diff --git a/payment/tests.py b/payment/tests.py index 8523530..dd1fb66 100644 --- a/payment/tests.py +++ b/payment/tests.py @@ -484,6 +484,22 @@ class PaymentRedirectViewTests(TestCase): idempotency_key='pay-view-order-1', ) + @override_settings( + ZARINPAL_MERCHANT_ID='merchant-xyz', ZARINPAL_CALLBACK_URL='https://api.example.com/cb', + ZARINPAL_STARTPAY_BASE='https://www.zarinpal.com/pg/StartPay', + ) + @patch('payment.views.zarinpal.request_payment') + def test_redirect_keeps_the_referrer_origin_for_zarinpal(self, mock_request_payment): + # با same-origin (پیش‌فرضِ جنگو) مرورگر Referer را روی ریدایرکت به + # زرین‌پال حذف می‌کرد و StartPay «دسترسی از این دامنه مجاز نمی‌باشد» می‌داد. + mock_request_payment.return_value = zarinpal.ZarinpalRequestResult( + authority='A-ref', payment_url='https://www.zarinpal.com/pg/StartPay/A-ref', + ) + response = self.client.get(f'/api/payment/orders/{self.order.id}/pay') + self.assertEqual(response.status_code, 302) + self.assertEqual(response['Referrer-Policy'], 'strict-origin-when-cross-origin') + self.assertEqual(self.client.get('/api/health')['Referrer-Policy'], 'same-origin') + @override_settings(ZARINPAL_MERCHANT_ID='', ZARINPAL_CALLBACK_URL='') def test_unconfigured_gateway_returns_503(self): response = self.client.get(f'/api/payment/orders/{self.order.id}/pay') diff --git a/payment/views.py b/payment/views.py index 19f735a..e4844dc 100644 --- a/payment/views.py +++ b/payment/views.py @@ -137,6 +137,17 @@ class PaymentRedirectView(APIView): throttle_scope = 'payment_pay' throttle_classes = [ScopedRateThrottle] + def dispatch(self, request, *args, **kwargs): + response = super().dispatch(request, *args, **kwargs) + # زرین‌پال در StartPay از روی Referer می‌سنجد کاربر از دامنه‌ی ثبت‌شده + # آمده یا نه. پیش‌فرضِ جنگو `same-origin` است و روی همین ریدایرکتِ + # بین‌دامنه‌ای Referer را حذف می‌کرد؛ زرین‌پال می‌گفت «دسترسی از این + # دامنه مجاز نمی‌باشد» (۲۰۲۶-۱۰-۰۱، پرداختِ آزمایشیِ صاحبِ پروژه). + # با این مقدار فقط origin صفحه‌ی فرانت (math.tadrisino2.ir) می‌رود، نه + # مسیر و کوئری. SecurityMiddleware با setdefault این را دست نمی‌زند. + response['Referrer-Policy'] = 'strict-origin-when-cross-origin' + return response + @extend_schema( responses={302: OpenApiTypes.NONE}, description='به درگاهِ زرین‌پال ریدایرکت می‌کند؛ بدنه‌ی JSON ندارد.',