7.8 KiB
نمایش درگاه تست در رابط کاربری هنگام فعال بودن حالت آزمایشی
زمینه
سیستم پرداخت یک حالت آزمایشی (payment_test_mode) دارد که در SiteConfig ذخیره میشود. وقتی این حالت فعال است، backend در resolveGateway() (هر دو PaymentController و SmsWalletController) بجای درگاه واقعی، درگاه MockGateway را بهکار میبرد و پول واقعی کسر نمیشود.
مشکل: Frontend هیچ اطلاعی از حالت تست ندارد. کاربر در SmsWalletPage و SubscriptionPage انتخاب درگاه میبیند (ملت / سپ)، حتی وقتی backend همه را به Mock هدایت میکند. این گیجکننده است.
هدف: وقتی payment_test_mode = '1' است:
- انتخابکننده درگاه واقعی پنهان شود
- بجای آن یک نوار زرد/نارنجی نمایش داده شود: «درگاه آزمایشی فعال است — پول واقعی کسر نخواهد شد»
مشکل / هدف
دو صفحه frontend نیاز به تغییر دارند:
SmsWalletPage.tsx— modal شارژ کیف پول پیامک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 تنظیمات فقط ادمین است
// 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 (خط ۳۴۸–۳۷۳)
// همیشه دو گزینه mellat و sep نمایش میدهد
{(['mellat', 'sep'] as const).map((gw) => (
<div key={gw} onClick={() => setGateway(gw)} ...>
<div>{gw === 'mellat' ? 'ملت' : 'سپ'}</div>
...
</div>
))}
Frontend — SubscriptionPage modal (خط ۳۰۷–۳۲۷)
// همیشه دو گزینه mellat و sep نمایش میدهد
{(['mellat', 'sep'] as const).map((gw) => (
<button key={gw} onClick={() => setSelectedGateway(gw)} ...>
<CreditCardIcon ... />
{GATEWAY_LABELS[gw]}
</button>
))}
وظایف
۱. Backend — endpoint عمومی GET /api/v1/payment/config
در src/Payment/Controller/PaymentController.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 اضافه کن:
const { data: paymentConfigData } = useQuery<ApiResponse<{ test_mode: boolean }>>({
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)، قسمت «انتخاب درگاه پرداخت» را با شرط جایگزین کن:
{isTestMode ? (
<div style={{
background: '#fef9c3',
border: '1px solid #fbbf24',
borderRadius: 10,
padding: '12px 16px',
display: 'flex', alignItems: 'center', gap: 10,
}}>
<span style={{ fontSize: 18 }}>⚠️</span>
<div>
<div style={{ fontWeight: 700, fontSize: 13.5, color: '#92400e' }}>
درگاه آزمایشی فعال است
</div>
<div style={{ fontSize: 12, color: '#b45309', marginTop: 2 }}>
پول واقعی کسر نخواهد شد — این تراکنش آزمایشی است
</div>
</div>
</div>
) : (
// کد فعلی انتخاب درگاه (mellat / sep)
<div>...</div>
)}
همچنین در mutation، وقتی isTestMode است، مقدار gateway را به 'mock' تنظیم کن (یا هر رشتهای — backend به هر حال Mock را استفاده میکند، ولی باید یک مقدار ارسال کنیم).
۴. Frontend — SubscriptionPage: پنهان کردن انتخابکننده درگاه در حالت تست
همان pattern را در SubscriptionPage.tsx در قسمت «انتخاب درگاه پرداخت» داخل modal اعمال کن.
همچنین در دکمه پرداخت زیر modal، متن را در حالت تست تغییر بده:
{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 خاصتر باید اول باشد.
ترتیب اجرا
- Backend: اضافه کردن
config()بهPaymentController - تست route:
ddev exec php bin/console debug:router | grep payment - Cache:
ddev exec php bin/console cache:clear - Frontend:
SmsWalletPage.tsx— اضافه کردن query و شرط حالت تست - Frontend:
SubscriptionPage.tsx— همان تغییر - Build:
ddev exec yarn dev - TypeScript check:
ddev exec npx tsc --noEmit --project tsconfig.json 2>&1 | head -20 - مستندات:
docs/api/payment.md