let the Bazaar verify URL double as the OAuth redirect URI
The Bazaar account owner registered /api/payment/bazaar/verify as the redirect URI. POST stays the app's purchase check; GET now takes Bazaar's ?code=, exchanges it for a refresh token and stores it in the new BazaarCredential row, so connecting needs no SSH or .env edit. The GET is unauthenticated, so it only acts inside a window opened from the admin (or bazaar_authorize_link): 72 h by default, closed by the first successful exchange, state checked when Bazaar echoes it. Authorize/token URL shapes follow the known devapi v2 flow; not checked against Bazaar's current docs, which did not load. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -2,7 +2,8 @@ from urllib.parse import urlsplit
|
||||
|
||||
from django.conf import settings
|
||||
from django.db import transaction
|
||||
from django.http import HttpResponseRedirect
|
||||
from django.http import HttpResponse, HttpResponseRedirect
|
||||
from django.utils.html import escape
|
||||
from django.shortcuts import get_object_or_404
|
||||
from drf_spectacular.types import OpenApiTypes
|
||||
from drf_spectacular.utils import OpenApiParameter, extend_schema
|
||||
@@ -219,9 +220,24 @@ class ZarinpalCallbackView(APIView):
|
||||
return Response(ZarinpalCallbackResponseSerializer(data).data)
|
||||
|
||||
|
||||
def _bazaar_oauth_page(message: str, *, status: int) -> HttpResponse:
|
||||
# این را صاحبِ حسابِ بازار در مرورگر میبیند، نه اپ؛ پس HTML ساده.
|
||||
return HttpResponse(
|
||||
'<!doctype html><meta charset="utf-8"><title>اتصال به بازار</title>'
|
||||
f'<p dir="rtl" style="font:16px sans-serif;margin:2em">{escape(message)}</p>',
|
||||
status=status,
|
||||
)
|
||||
|
||||
|
||||
class BazaarVerifyPurchaseView(APIView):
|
||||
"""
|
||||
POST /api/payment/bazaar/verify — تایید پرداخت درونبرنامهای بازار
|
||||
GET /api/payment/bazaar/verify?code=… — Redirect URIِ OAuthِ بازار
|
||||
|
||||
همین آدرس در پنلِ بازار Redirect URI ثبت شده (خواستِ مدیرِ حساب،
|
||||
۲۰۲۶-۰۹-۲۶)، پس دو کار دارد که با متد از هم جدا میشوند: GET را مرورگرِ
|
||||
صاحبِ حساب بعد از «اجازه» میزند و بیاحراز است (فقط داخلِ پنجرهای که
|
||||
start_authorization باز کرده اثر دارد)؛ POST را اپِ اندروید بعد از خرید.
|
||||
|
||||
برخلاف درگاه معمولی که با webhook خودِ درگاه تایید میشه، در پرداخت
|
||||
درونبرنامهای بازار هیچ webhookی نیست: کلاینت اندروید بعد از خرید از
|
||||
@@ -241,6 +257,33 @@ class BazaarVerifyPurchaseView(APIView):
|
||||
throttle_scope = 'bazaar_verify'
|
||||
throttle_classes = [GuestAwareBazaarThrottle]
|
||||
|
||||
def get_permissions(self):
|
||||
if self.request.method == 'GET':
|
||||
return [AllowAny()]
|
||||
return super().get_permissions()
|
||||
|
||||
def get_throttles(self):
|
||||
if self.request.method == 'GET':
|
||||
self.throttle_scope = 'bazaar_oauth'
|
||||
return super().get_throttles()
|
||||
|
||||
@extend_schema(exclude=True)
|
||||
def get(self, request):
|
||||
if request.query_params.get('error'):
|
||||
return _bazaar_oauth_page(
|
||||
f'بازار اجازه نداد: {request.query_params.get("error")}', status=400,
|
||||
)
|
||||
code = request.query_params.get('code', '')
|
||||
if not code:
|
||||
return _bazaar_oauth_page(
|
||||
'این آدرس برای برگشت از بازار است و بدون code کاری نمیکند.', status=400,
|
||||
)
|
||||
try:
|
||||
bazaar.complete_authorization(code=code, state=request.query_params.get('state', ''))
|
||||
except bazaar.BazaarAuthorizationError as exc:
|
||||
return _bazaar_oauth_page(str(exc), status=400)
|
||||
return _bazaar_oauth_page('اتصال به بازار انجام شد. میتوانید این صفحه را ببندید.', status=200)
|
||||
|
||||
@extend_schema(
|
||||
request=BazaarVerifyRequestSerializer,
|
||||
responses=BazaarVerifyResponseSerializer,
|
||||
|
||||
Reference in New Issue
Block a user