feat(payment): add endpoint for payment configuration and update UI for test mode
This commit is contained in:
@@ -43,10 +43,17 @@ export default function SmsWalletPage() {
|
||||
queryFn: () => api.get('/api/v1/sms/settings'),
|
||||
});
|
||||
|
||||
const balance = balanceData?.data;
|
||||
const logs = logsData?.data ?? EMPTY_LOGS;
|
||||
const total = logsData?.meta?.totalRecords ?? 0;
|
||||
const settings = settingsData?.data;
|
||||
const { data: paymentConfigData } = useQuery<ApiResponse<{ test_mode: boolean }>>({
|
||||
queryKey: ['payment-config'],
|
||||
queryFn: () => api.get('/api/v1/payment/config'),
|
||||
staleTime: 5 * 60 * 1000,
|
||||
});
|
||||
|
||||
const balance = balanceData?.data;
|
||||
const logs = logsData?.data ?? EMPTY_LOGS;
|
||||
const total = logsData?.meta?.totalRecords ?? 0;
|
||||
const settings = settingsData?.data;
|
||||
const isTestMode = paymentConfigData?.data?.test_mode ?? false;
|
||||
|
||||
const chargeForm = useForm<ChargeForm>({ resolver: zodResolver(chargeSchema) });
|
||||
const watchAmount = chargeForm.watch('amount_rials');
|
||||
@@ -345,32 +352,48 @@ export default function SmsWalletPage() {
|
||||
<Modal open={chargeOpen} onClose={() => setChargeOpen(false)} title="شارژ کیف پول پیامک">
|
||||
<form onSubmit={chargeForm.handleSubmit((d) => chargeMutation.mutate(d))}>
|
||||
<div style={{ display: 'flex', flexDirection: 'column', gap: 14 }}>
|
||||
{/* انتخاب درگاه — radio cards */}
|
||||
<div>
|
||||
<div style={{ fontSize: 13, fontWeight: 600, marginBottom: 8, color: 'var(--text-2)' }}>انتخاب درگاه پرداخت</div>
|
||||
<div style={{ display: 'flex', gap: 10 }}>
|
||||
{(['mellat', 'sep'] as const).map((gw) => (
|
||||
<div
|
||||
key={gw}
|
||||
onClick={() => setGateway(gw)}
|
||||
style={{
|
||||
flex: 1, textAlign: 'center', padding: '12px',
|
||||
border: `2px solid ${gateway === gw ? 'var(--primary)' : 'var(--border)'}`,
|
||||
borderRadius: 10, cursor: 'pointer',
|
||||
background: gateway === gw ? 'var(--primary-subtle)' : 'transparent',
|
||||
transition: 'all 0.15s',
|
||||
}}
|
||||
>
|
||||
<div style={{ fontWeight: 700, fontSize: 15, color: gateway === gw ? 'var(--primary)' : 'var(--text-1)' }}>
|
||||
{gw === 'mellat' ? 'ملت' : 'سپ'}
|
||||
</div>
|
||||
<div style={{ fontSize: 12, color: 'var(--text-3)', marginTop: 3 }}>
|
||||
{gw === 'mellat' ? 'بانک ملت' : 'بانک صادرات'}
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
{/* انتخاب درگاه */}
|
||||
{isTestMode ? (
|
||||
<div style={{
|
||||
background: '#fef9c3',
|
||||
border: '1px solid #fbbf24',
|
||||
borderRadius: 10,
|
||||
padding: '12px 16px',
|
||||
display: 'flex', alignItems: 'center', gap: 10,
|
||||
}}>
|
||||
<span style={{ fontSize: 20 }}>⚠️</span>
|
||||
<div>
|
||||
<div style={{ fontWeight: 700, fontSize: 13.5, color: '#92400e' }}>درگاه آزمایشی فعال است</div>
|
||||
<div style={{ fontSize: 12, color: '#b45309', marginTop: 2 }}>پول واقعی کسر نخواهد شد — این تراکنش آزمایشی است</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
) : (
|
||||
<div>
|
||||
<div style={{ fontSize: 13, fontWeight: 600, marginBottom: 8, color: 'var(--text-2)' }}>انتخاب درگاه پرداخت</div>
|
||||
<div style={{ display: 'flex', gap: 10 }}>
|
||||
{(['mellat', 'sep'] as const).map((gw) => (
|
||||
<div
|
||||
key={gw}
|
||||
onClick={() => setGateway(gw)}
|
||||
style={{
|
||||
flex: 1, textAlign: 'center', padding: '12px',
|
||||
border: `2px solid ${gateway === gw ? 'var(--primary)' : 'var(--border)'}`,
|
||||
borderRadius: 10, cursor: 'pointer',
|
||||
background: gateway === gw ? 'var(--primary-subtle)' : 'transparent',
|
||||
transition: 'all 0.15s',
|
||||
}}
|
||||
>
|
||||
<div style={{ fontWeight: 700, fontSize: 15, color: gateway === gw ? 'var(--primary)' : 'var(--text-1)' }}>
|
||||
{gw === 'mellat' ? 'ملت' : 'سپ'}
|
||||
</div>
|
||||
<div style={{ fontSize: 12, color: 'var(--text-3)', marginTop: 3 }}>
|
||||
{gw === 'mellat' ? 'بانک ملت' : 'بانک صادرات'}
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className="field">
|
||||
<label>مبلغ (ریال)</label>
|
||||
@@ -386,12 +409,15 @@ export default function SmsWalletPage() {
|
||||
|
||||
{watchAmount && Number(watchAmount) >= 10000 && (
|
||||
<div style={{
|
||||
background: 'var(--primary-subtle)',
|
||||
border: '1px solid oklch(0.85 0.06 256)',
|
||||
background: isTestMode ? '#fef9c3' : 'var(--primary-subtle)',
|
||||
border: `1px solid ${isTestMode ? '#fbbf24' : 'oklch(0.85 0.06 256)'}`,
|
||||
borderRadius: 8, padding: '10px 14px',
|
||||
fontSize: 13, color: 'var(--primary)', fontWeight: 500,
|
||||
fontSize: 13, color: isTestMode ? '#92400e' : 'var(--primary)', fontWeight: 500,
|
||||
}}>
|
||||
پرداخت {formatRial(Number(watchAmount))} از طریق {gateway === 'mellat' ? 'بانک ملت' : 'سپ'}
|
||||
{isTestMode
|
||||
? `پرداخت آزمایشی ${formatRial(Number(watchAmount))}`
|
||||
: `پرداخت ${formatRial(Number(watchAmount))} از طریق ${gateway === 'mellat' ? 'بانک ملت' : 'سپ'}`
|
||||
}
|
||||
</div>
|
||||
)}
|
||||
|
||||
|
||||
@@ -60,10 +60,17 @@ export default function SubscriptionPage() {
|
||||
queryFn: () => api.get('/api/v1/subscription/my'),
|
||||
});
|
||||
|
||||
const plans = plansData?.data ?? [];
|
||||
const myRaw = myData?.data;
|
||||
const my = myRaw?.subscription ?? null;
|
||||
const usedTrial = myRaw?.used_trial ?? false;
|
||||
const { data: paymentConfigData } = useQuery<ApiResponse<{ test_mode: boolean }>>({
|
||||
queryKey: ['payment-config'],
|
||||
queryFn: () => api.get('/api/v1/payment/config'),
|
||||
staleTime: 5 * 60 * 1000,
|
||||
});
|
||||
|
||||
const plans = plansData?.data ?? [];
|
||||
const myRaw = myData?.data;
|
||||
const my = myRaw?.subscription ?? null;
|
||||
const usedTrial = myRaw?.used_trial ?? false;
|
||||
const isTestMode = paymentConfigData?.data?.test_mode ?? false;
|
||||
|
||||
const trialMutation = useMutation({
|
||||
mutationFn: () => api.post('/api/v1/subscription/trial', {}),
|
||||
@@ -300,31 +307,47 @@ export default function SubscriptionPage() {
|
||||
</div>
|
||||
|
||||
{/* انتخاب درگاه */}
|
||||
<div>
|
||||
<div style={{ fontSize: 12.5, fontWeight: 600, color: 'var(--text-2)', marginBottom: 10 }}>
|
||||
انتخاب درگاه پرداخت
|
||||
{isTestMode ? (
|
||||
<div style={{
|
||||
background: '#fef9c3',
|
||||
border: '1px solid #fbbf24',
|
||||
borderRadius: 10,
|
||||
padding: '12px 16px',
|
||||
display: 'flex', alignItems: 'center', gap: 10,
|
||||
}}>
|
||||
<span style={{ fontSize: 20 }}>⚠️</span>
|
||||
<div>
|
||||
<div style={{ fontWeight: 700, fontSize: 13.5, color: '#92400e' }}>درگاه آزمایشی فعال است</div>
|
||||
<div style={{ fontSize: 12, color: '#b45309', marginTop: 2 }}>پول واقعی کسر نخواهد شد — این تراکنش آزمایشی است</div>
|
||||
</div>
|
||||
</div>
|
||||
<div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 10 }}>
|
||||
{(['mellat', 'sep'] as const).map((gw) => (
|
||||
<button
|
||||
key={gw}
|
||||
onClick={() => setSelectedGateway(gw)}
|
||||
style={{
|
||||
padding: '12px 16px', borderRadius: 'var(--r-sm)', cursor: 'pointer',
|
||||
border: `2px solid ${selectedGateway === gw ? 'var(--primary)' : 'var(--border)'}`,
|
||||
background: selectedGateway === gw ? 'var(--primary-soft)' : 'var(--surface)',
|
||||
color: selectedGateway === gw ? 'var(--primary-700)' : 'var(--text-2)',
|
||||
fontWeight: selectedGateway === gw ? 700 : 500,
|
||||
fontSize: 13, transition: '.14s', fontFamily: 'inherit',
|
||||
display: 'flex', alignItems: 'center', gap: 8,
|
||||
}}
|
||||
>
|
||||
<CreditCardIcon style={{ width: 17, flexShrink: 0 }} />
|
||||
{GATEWAY_LABELS[gw]}
|
||||
</button>
|
||||
))}
|
||||
) : (
|
||||
<div>
|
||||
<div style={{ fontSize: 12.5, fontWeight: 600, color: 'var(--text-2)', marginBottom: 10 }}>
|
||||
انتخاب درگاه پرداخت
|
||||
</div>
|
||||
<div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 10 }}>
|
||||
{(['mellat', 'sep'] as const).map((gw) => (
|
||||
<button
|
||||
key={gw}
|
||||
onClick={() => setSelectedGateway(gw)}
|
||||
style={{
|
||||
padding: '12px 16px', borderRadius: 'var(--r-sm)', cursor: 'pointer',
|
||||
border: `2px solid ${selectedGateway === gw ? 'var(--primary)' : 'var(--border)'}`,
|
||||
background: selectedGateway === gw ? 'var(--primary-soft)' : 'var(--surface)',
|
||||
color: selectedGateway === gw ? 'var(--primary-700)' : 'var(--text-2)',
|
||||
fontWeight: selectedGateway === gw ? 700 : 500,
|
||||
fontSize: 13, transition: '.14s', fontFamily: 'inherit',
|
||||
display: 'flex', alignItems: 'center', gap: 8,
|
||||
}}
|
||||
>
|
||||
<CreditCardIcon style={{ width: 17, flexShrink: 0 }} />
|
||||
{GATEWAY_LABELS[gw]}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* دکمهها */}
|
||||
<div style={{ display: 'flex', gap: 10, marginTop: 4 }}>
|
||||
@@ -338,7 +361,9 @@ export default function SubscriptionPage() {
|
||||
>
|
||||
{purchaseMutation.isPending
|
||||
? 'در حال انتقال...'
|
||||
: `پرداخت ${formatRial(purchaseTarget.period.price_rials)}`
|
||||
: isTestMode
|
||||
? `پرداخت آزمایشی ${formatRial(purchaseTarget.period.price_rials)}`
|
||||
: `پرداخت ${formatRial(purchaseTarget.period.price_rials)}`
|
||||
}
|
||||
</button>
|
||||
<button className="btn ghost" style={{ height: 44 }} onClick={() => setPurchaseTarget(null)}>
|
||||
|
||||
Reference in New Issue
Block a user