connect Zarinpal as the real payment gateway
payment.services.call_payment_gateway was a stub returning a fake
payment.gateway/pay/{id} URL. It's replaced with a real Zarinpal
integration (payment/zarinpal.py, Payment API v4):
- create_order/CreateOrderView still never call Zarinpal directly —
paymentUrl points at our own GET /api/payment/orders/{id}/pay
(PaymentRedirectView), which requests the real Zarinpal authority on
first visit and redirects to StartPay. This keeps order creation from
depending on Zarinpal's uptime/latency, and means none of the existing
purchase/idempotency tests needed to change.
- GET /api/payment/zarinpal/callback (ZarinpalCallbackView) is where
Zarinpal redirects the user back to; it always re-verifies
server-to-server via zarinpal.verify_payment before marking an order
paid, so a forged Status=OK in the query string alone does nothing.
- zarinpal.py fails closed (503 / invalid) when ZARINPAL_MERCHANT_ID
isn't configured, matching payment/bazaar.py's existing convention —
never a fake successful payment.
Adds Order.gateway_authority, PUBLIC_BASE_URL/ZARINPAL_* settings, and
a payment_pay throttle scope.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -278,6 +278,7 @@ REST_FRAMEWORK = {
|
||||
'user_profile_write': '60/hour',
|
||||
'affiliate_track': '10/hour',
|
||||
'payment_webhook': '60/hour',
|
||||
'payment_pay': '30/hour',
|
||||
},
|
||||
}
|
||||
|
||||
@@ -389,6 +390,35 @@ BAZAAR_CLIENT_SECRET = os.environ.get('BAZAAR_CLIENT_SECRET', '')
|
||||
BAZAAR_REFRESH_TOKEN = os.environ.get('BAZAAR_REFRESH_TOKEN', '')
|
||||
BAZAAR_API_BASE = os.environ.get('BAZAAR_API_BASE', 'https://pardakht.cafebazaar.ir/devapi/v2')
|
||||
|
||||
# -----------------------------------------------------------------------
|
||||
# Zarinpal (درگاهِ اصلیِ پرداختِ ما — Payment API v4)
|
||||
# -----------------------------------------------------------------------
|
||||
# آدرسِ عمومیِ خودِ بکاند؛ هم برای ساختنِ paymentUrlی که به کلاینت برمیگردد
|
||||
# (PaymentRedirectView) و هم برای callback_url که به زرینپال میفرستیم لازم
|
||||
# است. بدونش این دو URL شکسته میمانند — سیگنالِ واضحی که هنوز ست نشده.
|
||||
PUBLIC_BASE_URL = os.environ.get('PUBLIC_BASE_URL', '').rstrip('/')
|
||||
|
||||
ZARINPAL_MERCHANT_ID = os.environ.get('ZARINPAL_MERCHANT_ID', '')
|
||||
ZARINPAL_SANDBOX = os.environ.get('ZARINPAL_SANDBOX') == '1'
|
||||
ZARINPAL_API_BASE = os.environ.get(
|
||||
'ZARINPAL_API_BASE',
|
||||
'https://sandbox.zarinpal.com/pg/v4/payment' if ZARINPAL_SANDBOX
|
||||
else 'https://api.zarinpal.com/pg/v4/payment',
|
||||
)
|
||||
ZARINPAL_STARTPAY_BASE = os.environ.get(
|
||||
'ZARINPAL_STARTPAY_BASE',
|
||||
'https://sandbox.zarinpal.com/pg/StartPay' if ZARINPAL_SANDBOX
|
||||
else 'https://www.zarinpal.com/pg/StartPay',
|
||||
)
|
||||
ZARINPAL_CALLBACK_URL = os.environ.get(
|
||||
'ZARINPAL_CALLBACK_URL',
|
||||
f'{PUBLIC_BASE_URL}/api/payment/zarinpal/callback' if PUBLIC_BASE_URL else '',
|
||||
)
|
||||
# اختیاری: بعدِ verify، مرورگر/کلاینت را به این URL (مثلاً یک deep link اپ)
|
||||
# با ?orderId=...&status=... ریدایرکت میکنیم. اگه خالی باشه، همونجا یک
|
||||
# پاسخِ JSON ساده برمیگرده.
|
||||
ZARINPAL_REDIRECT_URL = os.environ.get('ZARINPAL_REDIRECT_URL', '')
|
||||
|
||||
# -----------------------------------------------------------------------
|
||||
# RAG / AI provider (اپ rag + ai_tutor.generate_tutor_answer)
|
||||
# -----------------------------------------------------------------------
|
||||
|
||||
Reference in New Issue
Block a user