Harden auth, AI tutor, payments, and subscriptions

This commit is contained in:
2026-08-22 04:42:36 +03:30
parent f945f960c9
commit cb18c93a3e
41 changed files with 2202 additions and 595 deletions

View File

@@ -15,9 +15,11 @@
⚠️ کافه‌بازار روش صحت‌سنجی رو تغییر داده (نگاه کنید به
developers.cafebazaar.ir/fa/guidelines/in-app-billing/api/postman-and-bazaar-api-new)؛
قبل از اتصال واقعی به production، دقیق‌ترین مسیر endpoint را از همون صفحه یا
پنل devapi خودتون تایید کنید — این فایل فقط اسکلتِ همون جریان رسمی رو
پیاده‌سازی کرده تا بدون کلید واقعی هم (مثل call_payment_gateway در
services.py) از طریق Swagger قابل تست باشه.
پنل devapi خودتون تایید کنید.
🔒 بدون BAZAAR_REFRESH_TOKEN (کلیدهای بازار ست نشده)، verify_purchase همیشه
fail-closed عمل می‌کنه و invalid برمی‌گردونه؛ هیچ‌وقت به purchaseToken
ارسالیِ کلاینت، بدون صحت‌سنجیِ واقعی با سرور بازار، اعتماد نمی‌شه.
"""
from dataclasses import dataclass
@@ -48,10 +50,13 @@ def _get_access_token() -> str:
return cached
if not settings.BAZAAR_REFRESH_TOKEN:
# بدون کلید واقعی (مثلاً محیط توسعه)، استاب: دقیقاً مثل
# call_payment_gateway در payment/services.py، تا جریان کامل بدون
# کلید واقعی بازار هم قابل تست باشه.
return 'stub-access-token'
# بدون REFRESH_TOKEN، امکان گرفتن access_token واقعی از بازار نیست؛
# قبلاً اینجا یک 'stub-access-token' برمی‌گشت که باعث می‌شد
# verify_purchase هر purchaseToken غیرخالی رو valid فرض کنه
# (باگ fail-open). این تابع نباید در این حالت صدا زده بشه —
# verify_purchase قبل از فراخوانیِ این تابع، خودش fail-closed
# برمی‌گرده — ولی برای دفاع در عمق، اینجا هم صراحتاً خطا می‌ده.
raise RuntimeError('BAZAAR_REFRESH_TOKEN is not configured; cannot obtain a Bazaar access token.')
resp = requests.post(
f'{settings.BAZAAR_API_BASE}/auth/token/',
@@ -79,10 +84,17 @@ def verify_purchase(*, product_sku: str, purchase_token: str, is_subscription: b
تک‌محصولی)، طبق سند رسمی.
"""
if not settings.BAZAAR_REFRESH_TOKEN:
# استاب توسعه: هر token غیرخالی معتبر فرض می‌شه تا کل جریان
# (ساخت سفارش bazaar → verify → فعال‌سازی اشتراک) بدون کلید واقعی
# از طریق Swagger قابل تست باشه.
return BazaarPurchaseResult(is_valid=bool(purchase_token), purchase_state=0)
# 🔒 fail-closed: بدون BAZAAR_REFRESH_TOKEN (کلید سرور-به-سرورِ
# بازار)، امکان صحت‌سنجیِ واقعیِ purchaseToken با سرور بازار وجود
# نداره. قبلاً اینجا هر purchaseToken غیرخالیِ ارسالی از کلاینت
# مستقیماً valid فرض می‌شد (باگ fail-open) که یعنی یک کلاینت
# می‌تونست با فرستادن هر مقدار غیرخالی، بدون خرید واقعی، سفارش رو
# paid کنه. حالا در نبودِ کلیدهای بازار، هیچ‌وقت به گفته‌ی کلاینت
# اعتماد نمی‌کنیم و صریحاً invalid برمی‌گردونیم.
return BazaarPurchaseResult(
is_valid=False,
raw={'error': 'bazaar_credentials_not_configured'},
)
access_token = _get_access_token()
kind = 'subscriptions' if is_subscription else 'purchases'