fix: integrate Yandex Metrika offline conv + S2S postback hooks
Restore integration hooks dropped in PR #2851 merge: - PurchaseRequest accepts yandex_cid, referrer, subid from frontend - Cache yandex_cid and subid in Redis at purchase creation (24h TTL) - On fulfill_purchase: extract subid from cache, persist to DB - Save Yandex CID from Redis to yandex_client_id_map - Fire on_registration + S2S postback for new accounts - Fire on_purchase + S2S postback for all paid purchases - All hooks wrapped in try/except — failures never block delivery
This commit is contained in:
@@ -23,7 +23,7 @@ from app.services.guest_purchase_service import (
|
||||
)
|
||||
from app.services.payment_method_config_service import _get_method_defaults
|
||||
from app.services.payment_service import PaymentService
|
||||
from app.utils.cache import RateLimitCache
|
||||
from app.utils.cache import RateLimitCache, cache
|
||||
|
||||
|
||||
logger = structlog.get_logger(__name__)
|
||||
@@ -128,6 +128,9 @@ class PurchaseRequest(BaseModel):
|
||||
gift_recipient_type: str | None = Field(default=None, pattern=r'^(email|telegram)$')
|
||||
gift_recipient_value: str | None = Field(default=None, max_length=255)
|
||||
gift_message: str | None = Field(default=None, max_length=1000)
|
||||
yandex_cid: str | None = Field(default=None, max_length=128, pattern=r'^[A-Za-z0-9._:-]{4,128}$')
|
||||
referrer: str | None = Field(default=None, max_length=500)
|
||||
subid: str | None = Field(default=None, max_length=255)
|
||||
|
||||
@model_validator(mode='after')
|
||||
def validate_contacts(self) -> 'PurchaseRequest':
|
||||
@@ -654,6 +657,8 @@ async def create_landing_purchase(
|
||||
gift_recipient_type=body.gift_recipient_type,
|
||||
gift_recipient_value=body.gift_recipient_value,
|
||||
gift_message=body.gift_message,
|
||||
subid=body.subid,
|
||||
referrer=body.referrer,
|
||||
commit=False,
|
||||
)
|
||||
|
||||
@@ -706,6 +711,20 @@ async def create_landing_purchase(
|
||||
await db.commit()
|
||||
await db.refresh(purchase)
|
||||
|
||||
# Persist Yandex CID in cache so fulfill_purchase can link it to the user later
|
||||
if body.yandex_cid and settings.YANDEX_OFFLINE_CONV_ENABLED:
|
||||
try:
|
||||
await cache.set(f'yacid:purchase:{purchase.token}', body.yandex_cid, expire=86400)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
# Persist subid in cache for S2S postback
|
||||
if body.subid:
|
||||
try:
|
||||
await cache.set(f'subid:purchase:{purchase.token}', body.subid, expire=86400)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
return PurchaseResponse(
|
||||
purchase_token=purchase.token,
|
||||
payment_url=payment_url,
|
||||
|
||||
@@ -145,6 +145,8 @@ async def create_purchase(
|
||||
gift_recipient_value: str | None = None,
|
||||
gift_message: str | None = None,
|
||||
source: str = 'landing',
|
||||
subid: str | None = None,
|
||||
referrer: str | None = None,
|
||||
buyer_user_id: int | None = None,
|
||||
commit: bool = True,
|
||||
) -> GuestPurchase:
|
||||
@@ -152,6 +154,8 @@ async def create_purchase(
|
||||
purchase = await create_guest_purchase(
|
||||
db,
|
||||
commit=commit,
|
||||
subid=subid,
|
||||
referrer=referrer,
|
||||
landing_id=landing.id if landing else None,
|
||||
tariff_id=tariff.id,
|
||||
period_days=period_days,
|
||||
@@ -438,6 +442,20 @@ async def fulfill_purchase(
|
||||
purchase.status = GuestPurchaseStatus.DELIVERED.value
|
||||
purchase.user_id = user.id
|
||||
purchase.delivered_at = datetime.now(UTC)
|
||||
|
||||
# Extract subid from Redis cache (saved at purchase creation)
|
||||
try:
|
||||
from app.utils.cache import cache
|
||||
|
||||
_cached_subid = await cache.get(f'subid:purchase:{purchase.token}')
|
||||
if _cached_subid:
|
||||
purchase.subid = _cached_subid if isinstance(_cached_subid, str) else _cached_subid.decode()
|
||||
from app.database.crud.yandex_client_id import upsert_subid
|
||||
|
||||
await upsert_subid(db, user.id, purchase.subid, source='landing')
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
if recipient_type == 'email' and not purchase.is_gift and is_new_account:
|
||||
purchase.auto_login_token = create_auto_login_token(user.id)
|
||||
|
||||
@@ -463,6 +481,55 @@ async def fulfill_purchase(
|
||||
except Exception:
|
||||
logger.exception('Failed to create transaction for guest purchase', purchase_id=purchase.id)
|
||||
|
||||
# Save Yandex CID from Redis → DB (enables on_registration/on_purchase to use it)
|
||||
try:
|
||||
from app.services import yandex_offline_conv_service as yandex_conv
|
||||
from app.utils.cache import cache
|
||||
|
||||
_cached_cid = await cache.get(f'yacid:purchase:{purchase.token}')
|
||||
if _cached_cid:
|
||||
await yandex_conv.store_cid(db, user.id, _cached_cid, source='landing')
|
||||
await db.commit()
|
||||
except Exception:
|
||||
logger.debug('Failed to save CID from Redis')
|
||||
|
||||
# Registration event (new accounts only) + S2S postback
|
||||
if is_new_account:
|
||||
try:
|
||||
from app.services import yandex_offline_conv_service as yandex_conv
|
||||
|
||||
await yandex_conv.on_registration(db, user.id)
|
||||
except Exception:
|
||||
logger.debug('Yandex on_registration hook error')
|
||||
|
||||
try:
|
||||
from app.database.crud.yandex_client_id import get_subid
|
||||
from app.services.s2s_postback_service import send_postback
|
||||
|
||||
_subid = purchase.subid or await get_subid(db, user.id)
|
||||
if _subid:
|
||||
await send_postback('registration', _subid, user_id=user.id)
|
||||
except Exception:
|
||||
logger.debug('S2S postback registration hook error')
|
||||
|
||||
# Purchase event + S2S postback (always for paid purchases)
|
||||
try:
|
||||
from app.services import yandex_offline_conv_service as yandex_conv
|
||||
|
||||
await yandex_conv.on_purchase(db, user.id, purchase.amount_kopeks)
|
||||
except Exception:
|
||||
logger.debug('Yandex on_purchase hook error')
|
||||
|
||||
try:
|
||||
from app.database.crud.yandex_client_id import get_subid
|
||||
from app.services.s2s_postback_service import send_postback
|
||||
|
||||
_subid = purchase.subid or await get_subid(db, user.id)
|
||||
if _subid:
|
||||
await send_postback('purchase', _subid, amount=purchase.amount_kopeks / 100, user_id=user.id)
|
||||
except Exception:
|
||||
logger.debug('S2S postback purchase hook error')
|
||||
|
||||
try:
|
||||
await send_guest_notification(
|
||||
purchase,
|
||||
|
||||
Reference in New Issue
Block a user