# entry پرداخت به‌صورت ریدایرکت خالص به Backend (بدون XHR) — سایت عمومی ## پروژه `nobat724_front` (سایت عمومی). **cross-repo** — پرامپت همتا (Backend، اول اجرا شود): `clinicpro/.claude/prompt/payment-single-flow-consolidation.md`. ## زمینه طبق معماری واحد پرداخت، سایت عمومی نباید هیچ API برای **شروع** پرداخت صدا بزند؛ فقط مرورگر را به Backend ریدایرکت کند. Backend یک entry ریدایرکتِ خالص دارد: ``` GET {API}/api/v1/payment/order/{appointmentUuid}?gateway=&return= ``` Backend این آدرس را می‌گیرد، سفارش را اعتبارسنجی می‌کند، `Payment` می‌سازد، به بانک وصل می‌شود و کاربر را به شاپرک می‌فرستد؛ در پایان به `return` (همان دامنهٔ مبدأ) با `?status=...` برمی‌گردد. وضعیت فعلی سایت: `components/appointment/paying/index.js` هنوز با **XHR** (`request.postAppointmentPayment`) پرداخت را شروع می‌کند و بعد به `pay_url` می‌رود. طبق نیاز جدید باید این XHR حذف شود و دکمهٔ پرداخت مستقیماً به `payment/order/{uuid}` ریدایرکت کند. ## مشکل / هدف حذف کامل فراخوانی API برای شروع پرداخت در سایت عمومی؛ دکمهٔ پرداخت = ریدایرکت مرورگر به `GET {API}/api/v1/payment/order/{appointmentUuid}?gateway=...&return=...`. انتخاب درگاه (در صورت چند درگاه) قبل از ریدایرکت انجام شود؛ اگر یک درگاه فعال باشد، خودکار. ## فایل‌های مرتبط | فایل | نقش | |------|-----| | `components/appointment/paying/index.js` | دکمهٔ پرداخت + انتخاب درگاه (از `getPaymentConfig`) | | `services/response.js` | `getPaymentConfig` (برای فهرست درگاه‌های فعال) — بدون تغییر | | `app/payment/result/page.js` | بازگشت از Backend با `status` — بدون تغییر | | `app/payment/[uuid]/page.js` | نمایش نتیجه — بدون تغییر | ## وضعیت فعلی ```js // components/appointment/paying/index.js const handlePayment = async () => { if (!appointmentId) return; if (!testMode && !selectedBank) return; setLoading(true); try { const res = await request.postAppointmentPayment({ // ❌ XHR برای شروع پرداخت appointment_uuid: appointmentId, gateway: testMode ? "mellat" : selectedBank, frontend_address: `${window.location.origin}/payment/result`, }); const payUrl = res?.data?.pay_url; if (payUrl) { window.location.href = payUrl; return; } throw new Error("pay_url missing"); } catch (error) { alert("خطا در شروع پرداخت..."); setLoading(false); } }; ``` `getPaymentConfig` از قبل درگاه‌های فعال را در `gateways` می‌دهد و `selectedBank`/`testMode` ست می‌شوند (بدون تغییر می‌مانند). ## وظایف ### ۱. تبدیل دکمهٔ پرداخت به ریدایرکتِ خالص (حذف XHR) `handlePayment` را طوری بازنویسی کن که به‌جای XHR، مستقیماً مرورگر را به entry بک‌اند ببرد: ```js const handlePayment = () => { if (!appointmentId) return; const gateway = testMode ? "mellat" : selectedBank; if (!gateway) return; // باید یک درگاه انتخاب شده باشد setLoading(true); const apiBase = process.env.NEXT_PUBLIC_API_URL; const ret = encodeURIComponent(`${window.location.origin}/payment/result`); window.location.href = `${apiBase}/api/v1/payment/order/${appointmentId}` + `?gateway=${encodeURIComponent(gateway)}&return=${ret}`; }; ``` - `handlePayment` دیگر `async` نیست و هیچ `request.*` صدا نمی‌زند. - `NEXT_PUBLIC_API_URL` همان base بک‌اند است (مثل بقیهٔ سایت). - `appointmentId` همان `appointment_uuid` است (capability؛ در URL می‌رود). ### ۲. حفظ انتخاب درگاه (مرحلهٔ ۲ نیاز) - `getPaymentConfig` و state `gateways`/`selectedBank`/`testMode` بدون تغییر بمانند؛ Select فقط وقتی چند درگاه فعال است نمایش داده شود. - اگر فقط یک درگاه فعال باشد، `selectedBank` از قبل روی همان ست است (پیش‌فرض `gateways[0]`), پس مرحله خودکار است. - دکمه وقتی `!testMode && !selectedBank` است `disabled` بماند (از قبل هست). ### ۳. پاک‌سازی - اگر `request.postAppointmentPayment` دیگر جای دیگری استفاده نمی‌شود، آن را از `services/response.js` حذف کن (بررسی با grep). اگر جای دیگری استفاده می‌شود، نگه‌دار. - `app/payment/result/page.js` و `app/payment/[uuid]/page.js` بدون تغییر (بازگشت با `status` را از قبل مدیریت می‌کنند). ## نکات مهم - **هیچ فراخوانی API برای شروع پرداخت نباید بماند** — فقط ریدایرکت full-page به `{API}/api/v1/payment/order/{uuid}`. Backend همهٔ ارتباط با بانک را انجام می‌دهد. - **بازگشت به دامنهٔ مبدأ:** `return=${window.location.origin}/payment/result` تضمین می‌کند Backend بعد از پرداخت به همین دامنهٔ چند-شهری برگردد؛ Backend این آدرس را در برابر `payment_allowed_frontend_hosts` اعتبارسنجی می‌کند (host دامنه باید whitelist باشد). - انتخاب درگاه همچنان از `GET /api/v1/payment/config` (`gateways` فعال) است — درگاه هاردکد نکن. - App Router؛ `paying` یک client component است (`use client`). فونت/استایل موجود (MUI + Tailwind, RTL). - بعد از تغییر: `npm run build`. تست دستی: کلیک پرداخت → مرورگر به `{API}/api/v1/payment/order/...` می‌رود (نه XHR)، سپس شاپرک، سپس بازگشت به `/payment/result?status=...`.