feat(payments): implement server-side price resolution for subscription payments

Add a new function `resolve_fiat_offer_price_for_user` to determine the correct offer price for users based on their subscription choices and any applicable discounts. Update payment handlers for CryptoPay, FreeKassa, Platega, SeverPay, and YooKassa to utilize this function, ensuring callback price validation and improving error handling for price mismatches. This enhances security by preventing callback payload tampering and ensures accurate pricing for users.
This commit is contained in:
kavore
2026-02-12 15:09:04 +03:00
parent b5dfaf8188
commit d39e781203
7 changed files with 417 additions and 20 deletions
@@ -12,6 +12,8 @@ from config.settings import Settings
router = Router(name="user_subscription_payments_crypto_router")
from bot.handlers.user.subscription.payments_subscription import resolve_fiat_offer_price_for_user
@router.callback_query(F.data.startswith("pay_crypto:"))
async def pay_crypto_callback_handler(
callback: types.CallbackQuery,
@@ -43,7 +45,7 @@ async def pay_crypto_callback_handler(
_, data_payload = callback.data.split(":", 1)
parts = data_payload.split(":")
months = float(parts[0])
price_amount = float(parts[1])
callback_price_amount = float(parts[1])
sale_mode = parts[2] if len(parts) > 2 else "subscription"
except (ValueError, IndexError):
try:
@@ -53,6 +55,43 @@ async def pay_crypto_callback_handler(
return
user_id = callback.from_user.id
resolved_price_amount = await resolve_fiat_offer_price_for_user(
session=session,
settings=settings,
user_id=user_id,
months=months,
sale_mode=sale_mode,
promo_code_service=promo_code_service,
)
if resolved_price_amount is None:
logging.warning(
"CryptoPay: no server-side price for user %s, value=%s, mode=%s",
user_id,
months,
sale_mode,
)
try:
await callback.answer(get_text("error_try_again"), show_alert=True)
except Exception as exc:
logging.debug("Suppressed exception in bot/handlers/user/subscription/payments_crypto.py: %s", exc)
return
if abs(resolved_price_amount - callback_price_amount) > 0.01:
logging.warning(
"CryptoPay: callback price mismatch for user %s, value=%s, mode=%s, callback=%.2f, resolved=%.2f",
user_id,
months,
sale_mode,
callback_price_amount,
resolved_price_amount,
)
try:
await callback.answer(get_text("error_try_again"), show_alert=True)
except Exception as exc:
logging.debug("Suppressed exception in bot/handlers/user/subscription/payments_crypto.py: %s", exc)
return
price_amount = resolved_price_amount
human_value = str(int(months)) if float(months).is_integer() else f"{months:g}"
payment_description = (
get_text("payment_description_traffic", traffic_gb=human_value)