Files
clinicpro/.claude/prompt/payment-test-mode-ui.md

7.8 KiB
Raw Permalink Blame History

نمایش درگاه تست در رابط کاربری هنگام فعال بودن حالت آزمایشی

زمینه

سیستم پرداخت یک حالت آزمایشی (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 تنظیمات فقط ادمین است

// 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 خاص‌تر باید اول باشد.

ترتیب اجرا

  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