send the Bazaar access token without "Bearer" and cap its cache at an hour
Tried against the real Bazaar API on 2026-09-26 after the first live authorization: "Authorization: Bearer <t>" gets 403 "Access token is not valid", the bare token is accepted, even though token_type says Bearer. The refresh grant also returned expires_in=3600000, which the old code would have cached for ~41 days; the TTL is now capped at one hour and 401/403 clear it. 404 invalid_value (wrong package or SKU) is our misconfiguration, so it now raises 503 instead of failing the user's order; only error=not_found means "not purchased", as Bazaar's docs say. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -166,11 +166,21 @@ def complete_authorization(*, code: str, state: str = '') -> None:
|
||||
raise BazaarAuthorizationError('این لینک همین الان یک بار استفاده شد.')
|
||||
cache.delete(ACCESS_TOKEN_CACHE_KEY)
|
||||
if data.get('access_token'):
|
||||
cache.set(ACCESS_TOKEN_CACHE_KEY, data['access_token'],
|
||||
timeout=max(int(data.get('expires_in', 3600)) - 60, 60))
|
||||
cache.set(ACCESS_TOKEN_CACHE_KEY, data['access_token'], timeout=_access_token_ttl(data))
|
||||
logger.info('Bazaar API authorized; refresh token stored')
|
||||
|
||||
|
||||
def _access_token_ttl(data: dict) -> int:
|
||||
# سند ۳۶۰۰ میگوید ولی بازارِ واقعی ۲۰۲۶-۰۹-۲۶ `expires_in: 3600000` داد
|
||||
# (احتمالاً میلیثانیه). کش را به یک ساعت سقف میزنیم تا ۴۱ روز توکنِ
|
||||
# مرده نگه نداریم؛ روی 401/403 هم پاک میشود.
|
||||
try:
|
||||
seconds = int(data.get('expires_in', 3600))
|
||||
except (TypeError, ValueError):
|
||||
seconds = 3600
|
||||
return max(min(seconds, 3600) - 60, 60)
|
||||
|
||||
|
||||
def _get_access_token() -> str:
|
||||
"""
|
||||
ACCESS_TOKEN رو کش میکنه (توی cache framework جنگو) تا هر verify یک
|
||||
@@ -214,9 +224,7 @@ def _get_access_token() -> str:
|
||||
logger.error('Bazaar access-token response malformed: status=%s', resp.status_code)
|
||||
raise BazaarAPIError()
|
||||
|
||||
# طبق سند بازار access_token معمولاً ~۱ ساعت اعتباره؛ کمی زودتر منقضی
|
||||
# میکنیم تا لبهی زمانی مشکل نسازه.
|
||||
cache.set(ACCESS_TOKEN_CACHE_KEY, token, timeout=int(data.get('expires_in', 3600)) - 60)
|
||||
cache.set(ACCESS_TOKEN_CACHE_KEY, token, timeout=_access_token_ttl(data))
|
||||
return token
|
||||
|
||||
|
||||
@@ -234,7 +242,10 @@ def _auth_headers() -> dict:
|
||||
"""
|
||||
if settings.BAZAAR_PISHKHAN_API_SECRET:
|
||||
return {'CAFEBAZAAR-PISHKHAN-API-SECRET': settings.BAZAAR_PISHKHAN_API_SECRET}
|
||||
return {'Authorization': f'Bearer {_get_access_token()}'}
|
||||
# بدونِ «Bearer»: با آن بازار 403 «Access token is not valid» میدهد، با
|
||||
# توکنِ خام قبول میکند (آزمایش روی بازارِ واقعی، ۲۰۲۶-۰۹-۲۶)، هرچند
|
||||
# token_typeِ خودش Bearer است.
|
||||
return {'Authorization': _get_access_token()}
|
||||
|
||||
|
||||
def verify_purchase(*, product_sku: str, purchase_token: str, is_subscription: bool = False) -> BazaarPurchaseResult:
|
||||
@@ -247,7 +258,7 @@ def verify_purchase(*, product_sku: str, purchase_token: str, is_subscription: b
|
||||
تکخرید: api/validate/<pkg>/inapp/<sku>/purchases/<token>/
|
||||
اشتراک: api/applications/<pkg>/subscriptions/<sku>/purchases/<token>
|
||||
به گفتهی سند «تنها اگر error برابر not_found باشد خرید انجام نشده»؛ پس
|
||||
فقط ۴۰۴ نامعتبر است و بقیهی خطاها (کلیدِ غلط، سقفِ روزانه، قطعی) ۵۰۳ اند
|
||||
فقط همان نامعتبر است و بقیهی خطاها (کلیدِ غلط، سقفِ روزانه، قطعی) ۵۰۳ اند
|
||||
تا یک خریدِ واقعی بهخاطرِ خطای ما failed نشود.
|
||||
"""
|
||||
if not is_configured():
|
||||
@@ -279,13 +290,16 @@ def verify_purchase(*, product_sku: str, purchase_token: str, is_subscription: b
|
||||
except ValueError:
|
||||
data = None
|
||||
|
||||
if resp.status_code == 404:
|
||||
return BazaarPurchaseResult(is_valid=False, raw=data if isinstance(data, dict) else {'status_code': 404})
|
||||
error = data.get('error') if isinstance(data, dict) else None
|
||||
if resp.status_code == 404 and error == 'not_found':
|
||||
return BazaarPurchaseResult(is_valid=False, raw=data)
|
||||
# 404 با invalid_value («Package name is invalid» / «Product is not found»)
|
||||
# یعنی تنظیمِ ما غلط است، نه خریدِ کاربر؛ مثلِ بقیهی خطاها ۵۰۳.
|
||||
if resp.status_code != 200 or not isinstance(data, dict):
|
||||
if resp.status_code == 401:
|
||||
if resp.status_code in (401, 403):
|
||||
cache.delete(ACCESS_TOKEN_CACHE_KEY)
|
||||
logger.error('Bazaar verify-purchase failed: status=%s error=%s', resp.status_code,
|
||||
data.get('error') if isinstance(data, dict) else None)
|
||||
logger.error('Bazaar verify-purchase failed: status=%s error=%s desc=%s', resp.status_code,
|
||||
error, data.get('error_description') if isinstance(data, dict) else None)
|
||||
raise BazaarAPIError()
|
||||
|
||||
if is_subscription:
|
||||
|
||||
Reference in New Issue
Block a user