# نمایش درگاه تست در رابط کاربری هنگام فعال بودن حالت آزمایشی ## زمینه سیستم پرداخت یک حالت آزمایشی (`payment_test_mode`) دارد که در `SiteConfig` ذخیره می‌شود. وقتی این حالت فعال است، backend در `resolveGateway()` (هر دو `PaymentController` و `SmsWalletController`) بجای درگاه واقعی، درگاه `MockGateway` را به‌کار می‌برد و پول واقعی کسر نمی‌شود. **مشکل:** Frontend هیچ اطلاعی از حالت تست ندارد. کاربر در `SmsWalletPage` و `SubscriptionPage` انتخاب درگاه می‌بیند (ملت / سپ)، حتی وقتی backend همه را به Mock هدایت می‌کند. این گیج‌کننده است. **هدف:** وقتی `payment_test_mode = '1'` است: - انتخاب‌کننده درگاه واقعی پنهان شود - بجای آن یک نوار زرد/نارنجی نمایش داده شود: «درگاه آزمایشی فعال است — پول واقعی کسر نخواهد شد» --- ## مشکل / هدف دو صفحه frontend نیاز به تغییر دارند: 1. `SmsWalletPage.tsx` — modal شارژ کیف پول پیامک 2. `SubscriptionPage.tsx` — modal خرید اشتراک همچنین یک endpoint عمومی (برای همه کاربران احراز هویت شده) باید ساخته شود چون `GET /api/v1/admin/settings` فقط برای `ROLE_ADMIN` است. --- ## فایل‌های مرتبط | فایل | نقش | |------|-----| | `src/Config/Controller/SiteConfigController.php` | endpoint تنظیمات — فعلاً فقط ROLE_ADMIN | | `src/Config/Repository/SiteConfigRepository.php` | خواندن `payment_test_mode` | | `src/Payment/Controller/PaymentController.php` | `resolveGateway()` — backend قبلاً درست است | | `assets/admin/pages/SmsWalletPage.tsx` | modal شارژ — نمایش‌دهنده انتخاب‌کننده درگاه | | `assets/admin/pages/SubscriptionPage.tsx` | modal اشتراک — نمایش‌دهنده انتخاب‌کننده درگاه | --- ## وضعیت فعلی ### Backend — endpoint تنظیمات فقط ادمین است ```php // src/Config/Controller/SiteConfigController.php #[IsGranted('ROLE_ADMIN')] class SiteConfigController extends BaseController { #[Route('/api/v1/admin/settings', methods: ['GET'])] public function get(): JsonResponse { return $this->success($this->configRepo->getAll()); } } ``` ### Frontend — SmsWalletPage modal (خط ۳۴۸–۳۷۳) ```tsx // همیشه دو گزینه mellat و sep نمایش می‌دهد {(['mellat', 'sep'] as const).map((gw) => (
setGateway(gw)} ...>
{gw === 'mellat' ? 'ملت' : 'سپ'}
...
))} ``` ### Frontend — SubscriptionPage modal (خط ۳۰۷–۳۲۷) ```tsx // همیشه دو گزینه mellat و sep نمایش می‌دهد {(['mellat', 'sep'] as const).map((gw) => ( ))} ``` --- ## وظایف ### ۱. Backend — endpoint عمومی `GET /api/v1/payment/config` در `src/Payment/Controller/PaymentController.php` یک متد جدید اضافه کن: ```php #[Route('/api/v1/payment/config', methods: ['GET'])] public function config(): JsonResponse { return $this->success([ 'test_mode' => $this->configRepo->get('payment_test_mode') === '1', ]); } ``` - **Permission:** `IS_AUTHENTICATED_FULLY` (کلاس `PaymentController` قبلاً این را دارد) - **مهم:** هرگز credential های درگاه (terminal_id، password، ...) را expose نکن - پاسخ فقط یک boolean کافی است - مستندات را در `docs/api/payment.md` اضافه کن ### ۲. Frontend — hook برای خواندن حالت تست در `assets/admin/pages/SmsWalletPage.tsx` و `assets/admin/pages/SubscriptionPage.tsx`، یک `useQuery` برای خواندن config اضافه کن: ```tsx const { data: paymentConfigData } = useQuery>({ queryKey: ['payment-config'], queryFn: () => api.get('/api/v1/payment/config'), staleTime: 5 * 60 * 1000, // 5 دقیقه cache }); const isTestMode = paymentConfigData?.data?.test_mode ?? false; ``` ### ۳. Frontend — SmsWalletPage: پنهان کردن انتخاب‌کننده درگاه در حالت تست در modal شارژ (`SmsWalletPage.tsx`)، قسمت «انتخاب درگاه پرداخت» را با شرط جایگزین کن: ```tsx {isTestMode ? (
⚠️
درگاه آزمایشی فعال است
پول واقعی کسر نخواهد شد — این تراکنش آزمایشی است
) : ( // کد فعلی انتخاب درگاه (mellat / sep)
...
)} ``` همچنین در mutation، وقتی `isTestMode` است، مقدار `gateway` را به `'mock'` تنظیم کن (یا هر رشته‌ای — backend به هر حال Mock را استفاده می‌کند، ولی باید یک مقدار ارسال کنیم). ### ۴. Frontend — SubscriptionPage: پنهان کردن انتخاب‌کننده درگاه در حالت تست همان pattern را در `SubscriptionPage.tsx` در قسمت «انتخاب درگاه پرداخت» داخل modal اعمال کن. همچنین در دکمه پرداخت زیر modal، متن را در حالت تست تغییر بده: ```tsx {isTestMode ? `پرداخت آزمایشی ${formatRial(purchaseTarget.period.price_rials)}` : `پرداخت ${formatRial(purchaseTarget.period.price_rials)}` } ``` --- ## نکات مهم - **Backend قبلاً درست است:** `resolveGateway()` در هر دو `PaymentController` و `SmsWalletController` حالت تست را بررسی می‌کند. فقط UI نیاز به تغییر دارد. - **Security:** endpoint جدید فقط یک boolean برمی‌گرداند — هیچ اطلاعات حساسی expose نمی‌شود. - **قرارداد API:** پاسخ باید `{ success: true, data: { test_mode: boolean } }` باشد. در frontend: `data?.data?.test_mode`. - **در حالت تست:** state انتخاب درگاه (`gateway` / `selectedGateway`) بی‌تأثیر است چون backend آن را نادیده می‌گیرد — ولی باید یک مقدار ارسال شود (از مقدار default `'mellat'` استفاده کن). - **Stale time:** این تنظیم خیلی نادر تغییر می‌کند، 5 دقیقه staleTime کافی است. - **Routing ترتیب:** مطمئن شو route `/api/v1/payment/config` قبل از `/api/v1/payment/{uuid}` تعریف شده تا با UUID conflict نکند. در Symfony اگر method‌ها مختلف است مشکلی نیست ولی اگر هر دو GET هستند، route خاص‌تر باید اول باشد. --- ## ترتیب اجرا 1. Backend: اضافه کردن `config()` به `PaymentController` 2. تست route: `ddev exec php bin/console debug:router | grep payment` 3. Cache: `ddev exec php bin/console cache:clear` 4. Frontend: `SmsWalletPage.tsx` — اضافه کردن query و شرط حالت تست 5. Frontend: `SubscriptionPage.tsx` — همان تغییر 6. Build: `ddev exec yarn dev` 7. TypeScript check: `ddev exec npx tsc --noEmit --project tsconfig.json 2>&1 | head -20` 8. مستندات: `docs/api/payment.md`