Commit Graph

7874 Commits

Author SHA1 Message Date
Egor 6fbd0bff28 Merge pull request #2903 from BEDOLAGA-DEV/release-please--branches--main
chore(main): release 3.51.0
v3.51.0
2026-04-23 05:20:56 +03:00
github-actions[bot] dff3eb14a1 chore(main): release 3.51.0 2026-04-23 02:20:25 +00:00
Egor fab3d54f24 Merge pull request #2902 from BEDOLAGA-DEV/dev
Dev
2026-04-23 05:20:02 +03:00
Fringg 2c3ffc8c8a feat: integrate Overpay payment provider (pay.overpay.io)
Full integration across bot, cabinet, admin panel and landing pages:

- Config: 16 OVERPAY_* settings (API URL, credentials, P12 cert path,
  project ID, currency, amount limits, webhook path, payment methods)
- Service: overpay_service.py with httpx mTLS (P12 cert) + Basic Auth,
  create_payment, get_payment, refund_payment methods
- Payment mixin: OverpayPaymentMixin with create, webhook processing,
  finalize (balance credit + notifications), status check
- Model: OverpayPayment table + OVERPAY enum value
- CRUD: 9 standard operations (create, get_by_*, for_update, link)
- Bot handler: start_overpay_topup + process_overpay_payment_amount
- Handler registration: route_payment_by_method + callback registration
- Webhook: POST /overpay-webhook with DB-based anti-spoofing validation
- Cabinet: balance topup + guest payment dispatch
- Keyboard: payment method button in bot
- Admin: settings category + test payment button
- Infrastructure: payment_method_config_service, system_settings_service,
  payment_verification_service, payment_search_service
- Migration 0064: create overpay_payments table

Overpay API specifics: amount as string "100.00" (not kopeks),
success status "charged", HPP redirect via resultUrl
2026-04-23 04:36:56 +03:00
Fringg 6f87563789 fix: pad short RemnaWave usernames to meet 3-char minimum
RemnaWave API requires username >= 3 characters. Users with short
Telegram names (e.g. "Su") produced 2-char usernames that failed
validation, preventing subscription sync after payment.

Now format_remnawave_username pads short results with the user
identifier (telegram_id/email/user_id) to ensure minimum length.
2026-04-23 03:49:35 +03:00
Fringg 7005052156 fix: inactive user cleanup deletes users with paid subscriptions
The get_inactive_users query filtered only by last_activity (last bot
interaction), ignoring subscription end dates. Users who bought long
subscriptions (3-6-12 months) but didn't interact with the bot got
flagged as inactive and deleted+banned while their subscription was
still active or recently expired.

Fix: add SQL subquery excluding users who have ANY subscription with
end_date >= threshold_date. A user is now only deletable when BOTH
their last_activity AND their latest subscription end_date are older
than the configured inactivity period.
2026-04-23 03:40:45 +03:00
Fringg 29ae7089aa feat: respond to unknown media messages (photos, videos, documents)
Previously the bot only responded "Не понимаю эту команду" to text
messages. Photos, videos, documents and stickers sent outside of an
active FSM state were silently ignored, causing users to think their
media was received when it wasn't (e.g. support ticket screenshots
sent as separate messages).

Now the bot replies with the same "use menu buttons" message for any
unhandled media when no FSM state is active.
2026-04-23 03:31:21 +03:00
Egor 80e953a07b Merge pull request #2901 from BEDOLAGA-DEV/release-please--branches--main
chore(main): release 3.50.0
v3.50.0
2026-04-22 06:18:07 +03:00
github-actions[bot] aebf58068f chore(main): release 3.50.0 2026-04-22 03:17:36 +00:00
Egor a491fe34bd Merge pull request #2900 from BEDOLAGA-DEV/dev
feat: v3.50.0 release
2026-04-22 06:17:09 +03:00
Egor 830e64afe0 Dev (#2899)
* fix: устранить MissingGreenlet в автоплатежах и починить traceback в логах

- subtract_user_balance: пишем promo_offer_log в отдельной сессии вместо rollback после commit, который экспайрил объекты основной сессии и ломал последующие обращения к subscription/user attrs
- monitoring_service._process_autopayments: перезагружаем subscription с eager-load user/tariff после списания, оборачиваем каждую итерацию в try/except + rollback, чтобы одна ошибка не валила весь батч
- logging_config: новый processor _auto_capture_exc_info автоматически подтягивает traceback из sys.exc_info() или error-kwarg → полный traceback в файле, консоли и Telegram без exc_info=True на каждом вызове
- logging_handler: дублирующая логика захвата exc_info в TelegramNotifierProcessor как резерв

* fix: устранить root cause MissingGreenlet в автоплатежах через refetch по id

Трейс показал: subscription.user падает на lazy-load → pool._checkout →
do_ping → await_ → MissingGreenlet. SQLAlchemy 2.0 async session не
поддерживает sync-lazy-load для relationships. Причина рассинхрона:
lock_user_for_pricing делает populate_existing=True + selectinload(
User.subscriptions).selectinload(Subscription.tariff), что разгружает
Subscription.user backref для сестринских подписок того же user.
Последующее обращение sub.user у другой подписки падает.

Фикс: захватываем (sub_id, user_id) пары ДО цикла, каждую итерацию
делаем fresh refetch через async select с eager load user+tariff+
promo_group. Никаких lazy access в горячем пути. В except используем
локально захваченные id вместо getattr(subscription, ...), чтобы
логирование не падало каскадом на expired объекте.

* fix: grant all available squads for unrestricted trials (#2897)

* feat: add WEBHOOK_IP to allow Telegram bypass DNS lookup for webhook (#2894)

* feat: add WEBHOOK_IP to allow Telegram bypass DNS lookup for webhook

* style: ruff format main.py

---------

Co-authored-by: Dmitry Lunin <br@slack.ru>

* fix: do not update first_name/last_name from OIDC claims (#2892)

Co-authored-by: Dmitry Lunin <br@slack.ru>

* fix: do not reset subscription_crypto_link when cryptoLink absent in webhook (#2891)

Co-authored-by: Dmitry Lunin <br@slack.ru>

* fix: FSM state loss on balance topup, PayPear confirmation_url, hidden trial tariff in renewal

- balance/platega: re-set FSM state after min/max validation errors,
  set state before pending_amount path, use balance_topup callback for back button
- balance/main: set FSM state and payment_method in handle_topup_amount_callback
  for all providers before routing, use balance_topup callback in validation errors
- payment/paypear: fix confirmation_url key (was 'url'), add fallback,
  store charged amount with commission for correct webhook amount comparison
- tariff_purchase: redirect to active tariff list when current tariff is
  inactive (hidden trial after promo code activation)
- cabinet/renewal: check tariff.is_active in both GET and POST endpoints
  to prevent hidden trial tariff periods from appearing

* fix: tariff switch pricing showing free for upgrades, admin duplicate subscription guard

- pricing_engine: use shortest period for daily rate comparison instead
  of period closest to remaining_days — fixes incorrect free/zero cost
  for upgrades when tariffs have different period sets
- pricing_engine: remove unused target_days parameter from
  get_tariff_daily_rate_fraction
- admin_users: add duplicate subscription check before create,
  change_tariff and activate actions to prevent UniqueViolationError
  on uq_subscriptions_user_tariff_active constraint
- admin_users: add IntegrityError fallback on create as TOCTOU safety net

* feat: tariff switch direction control, fix device pricing within tariff limit

Tariff switch direction:
- Add TARIFF_SWITCH_UPGRADE_ENABLED and TARIFF_SWITCH_DOWNGRADE_ENABLED
  settings to control allowed switch directions
- Guard all 10 entry points: instant switch (list, preview, confirm),
  legacy switch (list, select, confirm, daily confirm), cabinet (preview,
  execute), purchase-options API
- Filter tariff lists by allowed direction, show "unavailable" when
  both directions disabled
- Expose settings in cabinet purchase-options response for frontend

Device pricing fix:
- Devices within tariff.device_limit are now free when restoring
  (was charging for all devices regardless of tariff inclusion)
- Fix max(100, price) minimum enforcing 1 RUB even when
  chargeable_devices is 0
- Apply fix across all endpoints: bot handlers (confirm_change,
  execute_change, confirm_add), cabinet API (legacy purchase,
  modern purchase, get-price, save-cart), inline keyboard display

* fix: classic mode renewal resets device_limit to 1 via cart key mismatch

- Fix cart key mismatch: extend cart saved 'device_limit' but
  confirm_purchase read 'devices' key, falling back to DEFAULT=1.
  Now both keys are saved in both cart-save paths
- Fix confirm_purchase device resolution: use explicit is None checks
  instead of or-chain to avoid falsy-zero trap
- Fix return_to_saved_cart display: fall back to 'device_limit' and
  'traffic_limit_gb' keys when 'devices'/'traffic_gb' are absent
- Fix second cart-save path in _extend_existing_subscription with
  same dual-key pattern
- Fix RemnaWaveService import path in renewal service
- Add RESET_DEVICES_ON_RENEWAL setting: resets all connected devices
  (hwid) via RemnaWave API on each subscription renewal

* fix: menu layout schema icon limit, traffic_topup_enabled condition, shadowing imports

- Increase icon max_length from 10 to 100 in all three schemas
  (MenuButtonConfig, ButtonUpdateRequest, AddCustomButtonRequest)
  to support Telegram Custom Emoji IDs
- Add traffic_topup_enabled condition to ButtonConditions schema
- Remove shadowing local imports of MenuLayoutService in
  routes/menu_layout.py (top-level import already provides access)

* feat(tickets): multi-media message gallery (media_items JSONB)

- Add media_items JSONB column to TicketMessage model for multi-media
  gallery support (photos/videos/documents in one bubble)
- Add TicketMediaItem schema with type validation and shared
  _validate_media_bundle helper (max 10 items, legacy field compat)
- Update admin and user ticket handlers to store media_items and
  back-fill legacy media_type/media_file_id/media_caption from first
  item for backward compatibility
- Update _message_to_response in both admin and user routes to include
  media_items in API responses
- Allow empty message text when media is attached (message field now
  defaults to empty string with model validator ensuring text or media)
- Add migration 0061 with idempotent column check

Based on PR #2869 by @smediainfo — CI/CD workflow changes excluded
(hardcoded version strings would regress dynamic manifest reading)

* fix: ticket media_items review fixes

- Add if has_media else None guards in user-side ticket handlers
  (create_ticket, add_message) matching admin handler pattern
- Fix Telegram notification using resolved primary_file_id/primary_type
  instead of raw request fields for gallery messages
- Narrow except Exception to (TypeError, KeyError, ValueError) in
  _message_to_response with warning log for debugging
- Add media_items parameter to TicketCRUD.create_ticket and
  TicketCRUD.add_message for CRUD layer parity
- Add TicketMediaItemResponse and media_items field to webapi
  TicketMessageResponse to prevent data loss on read

* feat: landing page analytics goals and sticky pay button

- Add sticky_pay_button, analytics_view_enabled, analytics_view_goal,
  analytics_click_enabled, analytics_click_goal columns to LandingPage
- Add fields to CRUD updatable fields, admin create/update/detail
  schemas, create_landing() kwargs, _landing_to_detail() response
- Expose sticky_pay_button and analytics fields in public landing
  config response for frontend Yandex Metrika integration
- Add migration 0062 with idempotent column checks

Based on PR #2852 by @smediainfo — CI/CD workflow changes excluded
(hardcoded version strings would regress dynamic manifest reading)

* fix: validate analytics goal is set when analytics is enabled on landing

Prevent enabling analytics_view/click without providing the
corresponding goal identifier, which would result in empty
Yandex Metrika calls on the frontend.

* feat: Yandex Metrika offline conversions + S2S postbacks

- Add YandexClientIdMap model for user → yandex_cid mapping with
  upsert-safe CRUD (ON CONFLICT DO UPDATE)
- Add yandex_cid, subid, referrer columns to GuestPurchase
- Add yandex_offline_conv_service: Measurement Protocol integration
  with mc.yandex.ru/collect (registration, trial, purchase events),
  background task management, CID parsing from /start params
- Add s2s_postback_service: server-to-server affiliate postbacks
  with URL template placeholders and URL-safe encoding
- Add analytics offline conversion info to branding API (masked secret)
- Add POST /analytics/yandex-cid endpoint for cabinet CID capture
- Add 11 config settings (YANDEX_OFFLINE_CONV_*, S2S_POSTBACK_*)
- Add migration 0063 (yandex_client_id_map table + guest_purchases cols)
- Fix: mask measurement secret aggressively (show only last 4 chars)
- Fix: always replace {user_id} placeholder in S2S postback URLs
- Fix: use structlog kwargs instead of f-strings with LOG_PREFIX

Based on PR #2851 by @smediainfo — CI/CD workflow changes excluded

---------

Co-authored-by: c0mrade <killmy666@gmail.com>
Co-authored-by: Danila Yudin <danyayudin2012@gmail.com>
Co-authored-by: Dmitry V. Lunin <49199230+BlackRaincoat@users.noreply.github.com>
Co-authored-by: Dmitry Lunin <br@slack.ru>
2026-04-22 06:08:26 +03:00
Fringg 1068c1387a feat: Yandex Metrika offline conversions + S2S postbacks
- Add YandexClientIdMap model for user → yandex_cid mapping with
  upsert-safe CRUD (ON CONFLICT DO UPDATE)
- Add yandex_cid, subid, referrer columns to GuestPurchase
- Add yandex_offline_conv_service: Measurement Protocol integration
  with mc.yandex.ru/collect (registration, trial, purchase events),
  background task management, CID parsing from /start params
- Add s2s_postback_service: server-to-server affiliate postbacks
  with URL template placeholders and URL-safe encoding
- Add analytics offline conversion info to branding API (masked secret)
- Add POST /analytics/yandex-cid endpoint for cabinet CID capture
- Add 11 config settings (YANDEX_OFFLINE_CONV_*, S2S_POSTBACK_*)
- Add migration 0063 (yandex_client_id_map table + guest_purchases cols)
- Fix: mask measurement secret aggressively (show only last 4 chars)
- Fix: always replace {user_id} placeholder in S2S postback URLs
- Fix: use structlog kwargs instead of f-strings with LOG_PREFIX

Based on PR #2851 by @smediainfo — CI/CD workflow changes excluded
2026-04-22 05:57:46 +03:00
Fringg d31632534b fix: validate analytics goal is set when analytics is enabled on landing
Prevent enabling analytics_view/click without providing the
corresponding goal identifier, which would result in empty
Yandex Metrika calls on the frontend.
2026-04-22 05:43:54 +03:00
Fringg 3272b4bb05 feat: landing page analytics goals and sticky pay button
- Add sticky_pay_button, analytics_view_enabled, analytics_view_goal,
  analytics_click_enabled, analytics_click_goal columns to LandingPage
- Add fields to CRUD updatable fields, admin create/update/detail
  schemas, create_landing() kwargs, _landing_to_detail() response
- Expose sticky_pay_button and analytics fields in public landing
  config response for frontend Yandex Metrika integration
- Add migration 0062 with idempotent column checks

Based on PR #2852 by @smediainfo — CI/CD workflow changes excluded
(hardcoded version strings would regress dynamic manifest reading)
2026-04-22 05:36:41 +03:00
Fringg dd177101f7 fix: ticket media_items review fixes
- Add if has_media else None guards in user-side ticket handlers
  (create_ticket, add_message) matching admin handler pattern
- Fix Telegram notification using resolved primary_file_id/primary_type
  instead of raw request fields for gallery messages
- Narrow except Exception to (TypeError, KeyError, ValueError) in
  _message_to_response with warning log for debugging
- Add media_items parameter to TicketCRUD.create_ticket and
  TicketCRUD.add_message for CRUD layer parity
- Add TicketMediaItemResponse and media_items field to webapi
  TicketMessageResponse to prevent data loss on read
2026-04-22 05:29:51 +03:00
Fringg 36571c4275 feat(tickets): multi-media message gallery (media_items JSONB)
- Add media_items JSONB column to TicketMessage model for multi-media
  gallery support (photos/videos/documents in one bubble)
- Add TicketMediaItem schema with type validation and shared
  _validate_media_bundle helper (max 10 items, legacy field compat)
- Update admin and user ticket handlers to store media_items and
  back-fill legacy media_type/media_file_id/media_caption from first
  item for backward compatibility
- Update _message_to_response in both admin and user routes to include
  media_items in API responses
- Allow empty message text when media is attached (message field now
  defaults to empty string with model validator ensuring text or media)
- Add migration 0061 with idempotent column check

Based on PR #2869 by @smediainfo — CI/CD workflow changes excluded
(hardcoded version strings would regress dynamic manifest reading)
2026-04-22 05:23:48 +03:00
Fringg 66f8577448 fix: menu layout schema icon limit, traffic_topup_enabled condition, shadowing imports
- Increase icon max_length from 10 to 100 in all three schemas
  (MenuButtonConfig, ButtonUpdateRequest, AddCustomButtonRequest)
  to support Telegram Custom Emoji IDs
- Add traffic_topup_enabled condition to ButtonConditions schema
- Remove shadowing local imports of MenuLayoutService in
  routes/menu_layout.py (top-level import already provides access)
2026-04-22 05:17:56 +03:00
Fringg 9ca3320a02 fix: classic mode renewal resets device_limit to 1 via cart key mismatch
- Fix cart key mismatch: extend cart saved 'device_limit' but
  confirm_purchase read 'devices' key, falling back to DEFAULT=1.
  Now both keys are saved in both cart-save paths
- Fix confirm_purchase device resolution: use explicit is None checks
  instead of or-chain to avoid falsy-zero trap
- Fix return_to_saved_cart display: fall back to 'device_limit' and
  'traffic_limit_gb' keys when 'devices'/'traffic_gb' are absent
- Fix second cart-save path in _extend_existing_subscription with
  same dual-key pattern
- Fix RemnaWaveService import path in renewal service
- Add RESET_DEVICES_ON_RENEWAL setting: resets all connected devices
  (hwid) via RemnaWave API on each subscription renewal
2026-04-22 05:12:27 +03:00
Fringg 9ed4f086b0 feat: tariff switch direction control, fix device pricing within tariff limit
Tariff switch direction:
- Add TARIFF_SWITCH_UPGRADE_ENABLED and TARIFF_SWITCH_DOWNGRADE_ENABLED
  settings to control allowed switch directions
- Guard all 10 entry points: instant switch (list, preview, confirm),
  legacy switch (list, select, confirm, daily confirm), cabinet (preview,
  execute), purchase-options API
- Filter tariff lists by allowed direction, show "unavailable" when
  both directions disabled
- Expose settings in cabinet purchase-options response for frontend

Device pricing fix:
- Devices within tariff.device_limit are now free when restoring
  (was charging for all devices regardless of tariff inclusion)
- Fix max(100, price) minimum enforcing 1 RUB even when
  chargeable_devices is 0
- Apply fix across all endpoints: bot handlers (confirm_change,
  execute_change, confirm_add), cabinet API (legacy purchase,
  modern purchase, get-price, save-cart), inline keyboard display
2026-04-22 04:54:17 +03:00
Fringg da855a7c89 fix: tariff switch pricing showing free for upgrades, admin duplicate subscription guard
- pricing_engine: use shortest period for daily rate comparison instead
  of period closest to remaining_days — fixes incorrect free/zero cost
  for upgrades when tariffs have different period sets
- pricing_engine: remove unused target_days parameter from
  get_tariff_daily_rate_fraction
- admin_users: add duplicate subscription check before create,
  change_tariff and activate actions to prevent UniqueViolationError
  on uq_subscriptions_user_tariff_active constraint
- admin_users: add IntegrityError fallback on create as TOCTOU safety net
2026-04-22 04:25:42 +03:00
Fringg 7be404b918 fix: FSM state loss on balance topup, PayPear confirmation_url, hidden trial tariff in renewal
- balance/platega: re-set FSM state after min/max validation errors,
  set state before pending_amount path, use balance_topup callback for back button
- balance/main: set FSM state and payment_method in handle_topup_amount_callback
  for all providers before routing, use balance_topup callback in validation errors
- payment/paypear: fix confirmation_url key (was 'url'), add fallback,
  store charged amount with commission for correct webhook amount comparison
- tariff_purchase: redirect to active tariff list when current tariff is
  inactive (hidden trial after promo code activation)
- cabinet/renewal: check tariff.is_active in both GET and POST endpoints
  to prevent hidden trial tariff periods from appearing
2026-04-22 04:05:46 +03:00
Dmitry V. Lunin b71e58c8d2 fix: do not reset subscription_crypto_link when cryptoLink absent in webhook (#2891)
Co-authored-by: Dmitry Lunin <br@slack.ru>
2026-04-21 08:05:11 +03:00
Dmitry V. Lunin 1696e6f884 fix: do not update first_name/last_name from OIDC claims (#2892)
Co-authored-by: Dmitry Lunin <br@slack.ru>
2026-04-21 08:04:00 +03:00
Dmitry V. Lunin 7093d368d3 feat: add WEBHOOK_IP to allow Telegram bypass DNS lookup for webhook (#2894)
* feat: add WEBHOOK_IP to allow Telegram bypass DNS lookup for webhook

* style: ruff format main.py

---------

Co-authored-by: Dmitry Lunin <br@slack.ru>
2026-04-21 08:02:53 +03:00
Danila Yudin 905cea68b4 fix: grant all available squads for unrestricted trials (#2897) 2026-04-21 08:01:28 +03:00
Egor dc5442223d Merge pull request #2898 from BEDOLAGA-DEV/main
w
2026-04-21 07:53:33 +03:00
c0mrade 3b03c253cc fix: устранить root cause MissingGreenlet в автоплатежах через refetch по id
Трейс показал: subscription.user падает на lazy-load → pool._checkout →
do_ping → await_ → MissingGreenlet. SQLAlchemy 2.0 async session не
поддерживает sync-lazy-load для relationships. Причина рассинхрона:
lock_user_for_pricing делает populate_existing=True + selectinload(
User.subscriptions).selectinload(Subscription.tariff), что разгружает
Subscription.user backref для сестринских подписок того же user.
Последующее обращение sub.user у другой подписки падает.

Фикс: захватываем (sub_id, user_id) пары ДО цикла, каждую итерацию
делаем fresh refetch через async select с eager load user+tariff+
promo_group. Никаких lazy access в горячем пути. В except используем
локально захваченные id вместо getattr(subscription, ...), чтобы
логирование не падало каскадом на expired объекте.
2026-04-19 12:08:34 +03:00
c0mrade db79cc9eb0 fix: устранить MissingGreenlet в автоплатежах и починить traceback в логах
- subtract_user_balance: пишем promo_offer_log в отдельной сессии вместо rollback после commit, который экспайрил объекты основной сессии и ломал последующие обращения к subscription/user attrs
- monitoring_service._process_autopayments: перезагружаем subscription с eager-load user/tariff после списания, оборачиваем каждую итерацию в try/except + rollback, чтобы одна ошибка не валила весь батч
- logging_config: новый processor _auto_capture_exc_info автоматически подтягивает traceback из sys.exc_info() или error-kwarg → полный traceback в файле, консоли и Telegram без exc_info=True на каждом вызове
- logging_handler: дублирующая логика захвата exc_info в TelegramNotifierProcessor как резерв
2026-04-19 11:50:40 +03:00
Egor 1b94d9e700 Merge pull request #2890 from BEDOLAGA-DEV/release-please--branches--main
chore(main): release 3.49.0
v3.49.0
2026-04-18 04:00:56 +03:00
github-actions[bot] 39a7c92cd4 chore(main): release 3.49.0 2026-04-18 00:58:34 +00:00
Egor 81ebec676c Merge pull request #2889 from BEDOLAGA-DEV/dev
Dev
2026-04-18 03:58:10 +03:00
Fringg 25ea5c60fd docs: add AuraPay to README with partner block 2026-04-18 03:56:48 +03:00
Fringg 29877fc93b fix: handle edge case when all tariffs are daily in legacy renewal 2026-04-18 01:09:53 +03:00
Fringg 5986c00fab fix: redirect legacy users without tariff to tariff selection on renewal
Users created before tariffs were introduced (tariff_id=NULL) got
"Тариф не найден" when pressing "Продлить подписку". Now they see
a tariff selection list instead, allowing them to pick a tariff
and renew with proper parameters (traffic, devices, etc).
2026-04-18 01:05:48 +03:00
Fringg ecc4a6147d fix: rate-limit daily subscription insufficient balance notifications to 6 hours
Users with daily subscriptions and low balance were getting
"Подписка приостановлена" notification every 30 minutes (on each
charge cycle). Now rate-limited via Redis cache to max 1 notification
per 6 hours per subscription.
2026-04-18 00:59:09 +03:00
Fringg 16bc1d4198 fix: align campaign top registrations revenue with period comparison
Top registrations list was summing DEPOSIT + SUBSCRIPTION_PAYMENT
transactions (total user spending), while period comparison revenue
only counted DEPOSIT with real payment methods (actual money paid in).

Now both use the same calculation: only DEPOSIT transactions with
real payment methods. This fixes the discrepancy where a user showed
500₽ in the list but total revenue was 250₽.
2026-04-18 00:50:23 +03:00
Fringg 0f814be1b7 fix: add missing RollyPay CRUD wrappers and guest payment flow
RollyPay was missing:
- 7 CRUD wrapper functions in payment_service.py (create, get, update, link)
- Guest payment block in create_guest_payment()

Both were present for PayPear and AuraPay but omitted for RollyPay.
2026-04-18 00:23:38 +03:00
Fringg 97179360c0 feat: integrate AuraPay payment provider
Full integration following PayPear/RollyPay patterns:
- API client with X-ApiKey + X-ShopId auth, HMAC-SHA256 webhook verification
  (sorted keys, concatenated values, secret key #2)
- AuraPayPaymentMixin with create, webhook, finalize (all side effects), status check
- CRUD, model, migration (0060), PaymentMethod.AURAPAY enum
- Webhook endpoint, cabinet topup, bot handler with sub-options (card, sbp)
- Admin panel: AURAPAY category in bot_configuration + system_settings_service
- Config: AURAPAY_ENABLED, AURAPAY_API_KEY, AURAPAY_SHOP_ID, AURAPAY_SECRET_KEY
2026-04-18 00:02:49 +03:00
Fringg 2aa5927433 fix: register PayPear and RollyPay in admin panel settings
- bot_configuration.py: added PAYPEAR/ROLLYPAY to payment categories
  and test payment buttons
- system_settings_service.py: added category titles, descriptions,
  and prefix mappings for PAYPEAR_* and ROLLYPAY_* settings
2026-04-17 23:44:35 +03:00
Egor 1c696c69e3 Merge pull request #2887 from BEDOLAGA-DEV/dev
docs: add PayPear and RollyPay to README with partner blocks
2026-04-16 06:26:19 +03:00
Fringg b531959982 docs: add PayPear and RollyPay to README with partner blocks 2026-04-16 06:25:33 +03:00
Egor ead0fc99d3 Merge pull request #2886 from BEDOLAGA-DEV/release-please--branches--main
chore(main): release 3.48.0
v3.48.0
2026-04-16 06:11:21 +03:00
github-actions[bot] c09ae7436c chore(main): release 3.48.0 2026-04-16 03:10:42 +00:00
Egor 1ea76575ce Merge pull request #2885 from BEDOLAGA-DEV/dev
Dev
2026-04-16 06:10:19 +03:00
Fringg 25447edc9e docs: add SEVERPAY, PAYPEAR, ROLLYPAY to .env.example 2026-04-16 06:08:10 +03:00
Fringg a59858227f fix: support payment_method selection for RollyPay (sbp/card/crypto)
- Mixin accepts payment_method_type parameter (None = show all on form)
- API service sends payment_method only when specified
- Cabinet route passes payment_option to mixin
- Config service has sub_options: sbp, card, crypto
- Keyboard label no longer hardcodes "СБП"
2026-04-16 06:03:33 +03:00
Fringg ccc2f4efec feat: integrate RollyPay payment provider (SBP via USDT)
Full integration following PayPear/SeverPay patterns:
- API client with X-API-Key + X-Nonce auth, HMAC-SHA256 webhook verification
- RollyPayPaymentMixin with create, webhook, finalize (all side effects), status check
- CRUD, model, migration (0059), PaymentMethod.ROLLYPAY enum
- Webhook endpoint, cabinet topup, bot handler
- Statuses: created, processing, paid, expired, canceled, chargeback
- Config: ROLLYPAY_ENABLED, ROLLYPAY_API_KEY, ROLLYPAY_SIGNING_SECRET, etc.
2026-04-16 05:52:25 +03:00
Fringg a18f6caa9b feat: integrate PayPear payment provider
Full integration following SeverPay patterns:
- API client with Basic Auth, idempotency, HMAC webhook verification
- PayPearPaymentMixin with create, webhook, finalize (all side effects), status check
- CRUD, model, migration (0058), PaymentMethod.PAYPEAR enum
- Webhook endpoint, cabinet topup, bot handler, payment verification
- Sub-options: bank_card, sbp, sberpay, tpay
- Config: PAYPEAR_ENABLED, PAYPEAR_SHOP_ID, PAYPEAR_SECRET_KEY, etc.
2026-04-16 05:36:48 +03:00
Fringg 92eaf45311 fix: increase nalogo receipt queue retry window to 12 hours
Changed defaults from 10 attempts × 5min (50min total) to
72 attempts × 10min (12 hours total). When nalog.ru is temporarily
down, receipts now retry for 12 hours before being dropped,
giving the service enough time to recover.
2026-04-16 04:58:54 +03:00
Fringg 61cf495fc5 fix: show menu buttons for limited subscriptions in back-to-menu paths
The previous fix only covered /start command paths. The more common
show_main_menu and handle_back_to_menu in menu.py used their own
is_active check which returned False for limited status.

Now both paths treat limited subscriptions as active for UI, matching
the _calculate_subscription_flags fix in start.py.
2026-04-16 04:54:57 +03:00