epic-6-snap-hybrid-complete #15
|
|
@ -0,0 +1,61 @@
|
|||
#!/usr/bin/env node
|
||||
|
||||
const API_URL = 'http://localhost:8000/createtransaksi'
|
||||
const API_KEY = 'dev-key'
|
||||
|
||||
const orderId = `SNAPTEST-${Date.now()}`
|
||||
|
||||
const payload = {
|
||||
mercant_id: 'TESTMERCHANT',
|
||||
timestamp: Date.now(),
|
||||
deskripsi: 'Testing Snap Payment Mode',
|
||||
nominal: 150000,
|
||||
nama: 'Test Snap User',
|
||||
no_telepon: '081234567890',
|
||||
email: 'test@snap.com',
|
||||
item: [
|
||||
{
|
||||
item_id: orderId,
|
||||
nama: 'Test Product Snap',
|
||||
harga: 150000,
|
||||
qty: 1
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
try {
|
||||
const response = await fetch(API_URL, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'x-api-key': API_KEY
|
||||
},
|
||||
body: JSON.stringify(payload)
|
||||
})
|
||||
|
||||
if (!response.ok) {
|
||||
const error = await response.text()
|
||||
console.error('❌ Error:', response.status, error)
|
||||
process.exit(1)
|
||||
}
|
||||
|
||||
const data = await response.json()
|
||||
|
||||
const paymentUrl = data.data?.url || data.payment_url
|
||||
const token = paymentUrl ? paymentUrl.split('/pay/')[1] : null
|
||||
|
||||
console.log('✅ Payment link created successfully!')
|
||||
console.log('\n🔗 Snap Mode Payment Link:')
|
||||
console.log(paymentUrl.replace('https://midtrans-cifo.winteraccess.id', 'http://localhost:5173'))
|
||||
console.log('\n📋 Order ID:', orderId)
|
||||
console.log('💰 Amount: Rp 150,000')
|
||||
console.log('🔑 Mode: SNAP (Hosted UI)')
|
||||
|
||||
if (token) {
|
||||
console.log('\n📄 Token:', token.substring(0, 50) + '...')
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
console.error('❌ Failed to create payment link:', error.message)
|
||||
process.exit(1)
|
||||
}
|
||||
|
|
@ -1,10 +1,10 @@
|
|||
import { createBrowserRouter, RouterProvider } from 'react-router-dom'
|
||||
import { AppLayout } from './AppLayout'
|
||||
// import { CheckoutPage } from '../pages/CheckoutPage'
|
||||
import { CheckoutPage } from '../pages/CheckoutPage'
|
||||
import { PaymentStatusPage } from '../pages/PaymentStatusPage'
|
||||
import { PaymentHistoryPage } from '../pages/PaymentHistoryPage'
|
||||
import { NotFoundPage } from '../pages/NotFoundPage'
|
||||
// import { DemoStorePage } from '../pages/DemoStorePage'
|
||||
import { DemoStorePage } from '../pages/DemoStorePage'
|
||||
import { InitPage } from '../pages/InitialPage'
|
||||
import { PayPage } from '../pages/PayPage'
|
||||
|
||||
|
|
@ -15,7 +15,8 @@ const router = createBrowserRouter([
|
|||
errorElement: <div role="alert">Terjadi kesalahan. Coba muat ulang.</div>,
|
||||
children: [
|
||||
{ index: true, element: <InitPage /> },
|
||||
// { path: 'checkout', element: <CheckoutPage /> },
|
||||
{ path: 'checkout', element: <CheckoutPage /> },
|
||||
{ path: 'demo', element: <DemoStorePage /> },
|
||||
{ path: 'pay/:token', element: <PayPage /> },
|
||||
{ path: 'payments/:orderId/status', element: <PaymentStatusPage /> },
|
||||
{ path: 'history', element: <PaymentHistoryPage /> },
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ import { SnapTokenService } from './SnapTokenService'
|
|||
interface SnapPaymentTriggerProps {
|
||||
orderId: string
|
||||
amount: number
|
||||
customer?: { name?: string; phone?: string; email?: string }
|
||||
paymentMethod?: string
|
||||
onSuccess?: (result: any) => void
|
||||
onError?: (error: any) => void
|
||||
|
|
@ -19,6 +20,7 @@ interface SnapPaymentTriggerProps {
|
|||
export function SnapPaymentTrigger({
|
||||
orderId,
|
||||
amount,
|
||||
customer,
|
||||
paymentMethod,
|
||||
onSuccess,
|
||||
onError,
|
||||
|
|
@ -43,6 +45,7 @@ export function SnapPaymentTrigger({
|
|||
<SnapHostedPayment
|
||||
orderId={orderId}
|
||||
amount={amount}
|
||||
customer={customer}
|
||||
onSuccess={onSuccess}
|
||||
onError={onError}
|
||||
/>
|
||||
|
|
@ -105,7 +108,7 @@ function CorePaymentComponent({ paymentMethod, orderId, amount, onChargeInitiate
|
|||
return <Component orderId={orderId} amount={amount} onChargeInitiated={onChargeInitiated} />
|
||||
}
|
||||
|
||||
function SnapHostedPayment({ orderId, amount, onSuccess, onError }: Omit<SnapPaymentTriggerProps, 'paymentMethod' | 'onChargeInitiated'>) {
|
||||
function SnapHostedPayment({ orderId, amount, customer, onSuccess, onError }: Omit<SnapPaymentTriggerProps, 'paymentMethod' | 'onChargeInitiated'>) {
|
||||
const [loading, setLoading] = React.useState(false)
|
||||
const [error, setError] = React.useState('')
|
||||
|
||||
|
|
@ -114,7 +117,7 @@ function SnapHostedPayment({ orderId, amount, onSuccess, onError }: Omit<SnapPay
|
|||
setLoading(true)
|
||||
setError('')
|
||||
|
||||
Logger.paymentInfo('snap.payment.init', { orderId, amount })
|
||||
Logger.paymentInfo('snap.payment.init', { orderId, amount, customer })
|
||||
|
||||
// Create Snap transaction token using service
|
||||
const token = await SnapTokenService.createToken({
|
||||
|
|
@ -122,10 +125,17 @@ function SnapHostedPayment({ orderId, amount, onSuccess, onError }: Omit<SnapPay
|
|||
order_id: orderId,
|
||||
gross_amount: amount
|
||||
},
|
||||
// Add customer details if available
|
||||
customer_details: {
|
||||
// These would come from props or context
|
||||
}
|
||||
customer_details: customer ? {
|
||||
first_name: customer.name,
|
||||
email: customer.email,
|
||||
phone: customer.phone
|
||||
} : undefined,
|
||||
item_details: [{
|
||||
id: orderId,
|
||||
name: 'Payment',
|
||||
price: amount,
|
||||
quantity: 1
|
||||
}]
|
||||
})
|
||||
|
||||
Logger.paymentInfo('snap.token.received', { orderId, token: token.substring(0, 10) + '...' })
|
||||
|
|
|
|||
|
|
@ -22,7 +22,10 @@ export interface SnapTokenRequest {
|
|||
}
|
||||
|
||||
export interface SnapTokenResponse {
|
||||
token: string
|
||||
token: string | {
|
||||
token: string
|
||||
redirect_url: string
|
||||
}
|
||||
}
|
||||
|
||||
export class SnapTokenService {
|
||||
|
|
@ -38,16 +41,28 @@ export class SnapTokenService {
|
|||
|
||||
const response = await api.post<SnapTokenResponse>('/payments/snap/token', request)
|
||||
|
||||
if (!response.data?.token) {
|
||||
// Handle both response formats:
|
||||
// 1. Direct string: { token: "abc123" }
|
||||
// 2. Nested object: { token: { token: "abc123", redirect_url: "..." } }
|
||||
let tokenString: string
|
||||
if (typeof response.data?.token === 'string') {
|
||||
tokenString = response.data.token
|
||||
} else if (response.data?.token && typeof response.data.token === 'object') {
|
||||
tokenString = response.data.token.token
|
||||
} else {
|
||||
throw new Error('Invalid token response from server')
|
||||
}
|
||||
|
||||
if (!tokenString) {
|
||||
throw new Error('Empty token received from server')
|
||||
}
|
||||
|
||||
TransactionLogger.log('SNAP', 'token.created', {
|
||||
orderId: request.transaction_details.order_id,
|
||||
tokenLength: response.data.token.length
|
||||
tokenLength: tokenString.length
|
||||
})
|
||||
|
||||
return response.data.token
|
||||
return tokenString
|
||||
|
||||
} catch (error) {
|
||||
TransactionLogger.logPaymentError('SNAP', request.transaction_details.order_id, error)
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@ export function CheckoutPage() {
|
|||
const expireAt = Date.now() + 59 * 60 * 1000 + 32 * 1000 // 00:59:32
|
||||
const [selected, setSelected] = React.useState<PaymentMethod | null>(null)
|
||||
const [locked, setLocked] = React.useState(false)
|
||||
const [currentStep, setCurrentStep] = React.useState<1 | 2 | 3>(1)
|
||||
const [currentStep, setCurrentStep] = React.useState<1 | 2>(1)
|
||||
const [isBusy, setIsBusy] = React.useState(false)
|
||||
const [form, setForm] = React.useState<{ name: string; contact: string; address: string; notes: string }>({
|
||||
name: 'Demo User',
|
||||
|
|
@ -66,8 +66,8 @@ export function CheckoutPage() {
|
|||
</Alert>
|
||||
)}
|
||||
|
||||
<PaymentSheet merchantName="Simaya" orderId={orderId} amount={amount} expireAt={expireAt} showStatusCTA={currentStep === 3}>
|
||||
{/* Wizard 3 langkah: Step 1 (Form Dummy) → Step 2 (Pilih Metode) → Step 3 (Panel Metode) */}
|
||||
<PaymentSheet merchantName="Simaya" orderId={orderId} amount={amount} expireAt={expireAt} showStatusCTA={currentStep === 2}>
|
||||
{/* Wizard 2 langkah: Step 1 (Form Dummy) → Step 2 (Payment - Snap/Core auto-detect) */}
|
||||
{currentStep === 1 && (
|
||||
<div className="space-y-3">
|
||||
<div className="text-sm font-medium">Konfirmasi data checkout</div>
|
||||
|
|
@ -114,6 +114,8 @@ export function CheckoutPage() {
|
|||
disabled={isBusy}
|
||||
onClick={() => {
|
||||
setIsBusy(true)
|
||||
// Set default payment method (bank_transfer for demo)
|
||||
setSelected('bank_transfer')
|
||||
setTimeout(() => { setCurrentStep(2); setIsBusy(false) }, 400)
|
||||
}}
|
||||
>
|
||||
|
|
@ -122,74 +124,33 @@ export function CheckoutPage() {
|
|||
<span className="h-4 w-4 animate-spin rounded-full border-2 border-white/70 border-t-transparent" aria-hidden />
|
||||
Memuat…
|
||||
</span>
|
||||
) : 'Next'}
|
||||
) : 'Lanjut ke Pembayaran'}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{currentStep === 2 && (
|
||||
<div className="space-y-3">
|
||||
<PaymentMethodList
|
||||
selected={selected ?? undefined}
|
||||
onSelect={(m) => {
|
||||
setSelected(m)
|
||||
if (m === 'bank_transfer' || m === 'cstore') {
|
||||
// SnapPaymentTrigger will handle the bank/store selection internally
|
||||
setIsBusy(true)
|
||||
setTimeout(() => { setCurrentStep(3); setIsBusy(false) }, 300)
|
||||
} else if (m === 'cpay') {
|
||||
// Redirect ke aplikasi cPay (CIFO Token) di Play Store
|
||||
try {
|
||||
Logger.info('cpay.redirect.start')
|
||||
window.open('https://play.google.com/store/apps/details?id=com.cifo.walanja', '_blank')
|
||||
Logger.info('cpay.redirect.done')
|
||||
} catch (e) {
|
||||
Logger.error('cpay.redirect.error', { message: (e as Error)?.message })
|
||||
}
|
||||
} else {
|
||||
setIsBusy(true)
|
||||
setTimeout(() => { setCurrentStep(3); setIsBusy(false) }, 300)
|
||||
}
|
||||
}}
|
||||
disabled={locked}
|
||||
enabled={runtimeCfg?.paymentToggles
|
||||
? {
|
||||
bank_transfer: runtimeCfg.paymentToggles.bank_transfer,
|
||||
credit_card: runtimeCfg.paymentToggles.credit_card,
|
||||
gopay: runtimeCfg.paymentToggles.gopay,
|
||||
cstore: runtimeCfg.paymentToggles.cstore,
|
||||
cpay: !!runtimeCfg.paymentToggles.cpay,
|
||||
}
|
||||
: undefined}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{currentStep === 3 && (
|
||||
<div className="space-y-3" aria-live="polite">
|
||||
{selected && (
|
||||
<SnapPaymentTrigger
|
||||
orderId={orderId}
|
||||
amount={amount}
|
||||
paymentMethod={selected}
|
||||
onChargeInitiated={() => setLocked(true)}
|
||||
onSuccess={(result) => {
|
||||
Logger.info('checkout.payment.success', { orderId, result })
|
||||
// Handle successful payment
|
||||
}}
|
||||
onError={(error) => {
|
||||
Logger.error('checkout.payment.error', { orderId, error })
|
||||
// Handle payment error
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
{selected && !(runtimeCfg?.paymentToggles ?? defaultEnabled())[selected] && (
|
||||
<div className="mt-2">
|
||||
<Alert title="Metode nonaktif">Metode pembayaran ini dinonaktifkan di konfigurasi lingkungan.</Alert>
|
||||
</div>
|
||||
)}
|
||||
{/* No back/next controls on Step 3 as requested */}
|
||||
<SnapPaymentTrigger
|
||||
orderId={orderId}
|
||||
amount={amount}
|
||||
customer={{
|
||||
name: form.name,
|
||||
email: form.contact.includes('@') ? form.contact : undefined,
|
||||
phone: !form.contact.includes('@') ? form.contact : undefined
|
||||
}}
|
||||
paymentMethod={'bank_transfer'}
|
||||
onChargeInitiated={() => setLocked(true)}
|
||||
onSuccess={(result) => {
|
||||
Logger.info('checkout.payment.success', { orderId, result })
|
||||
// Handle successful payment
|
||||
}}
|
||||
onError={(error) => {
|
||||
Logger.error('checkout.payment.error', { orderId, error })
|
||||
// Handle payment error
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
</PaymentSheet>
|
||||
|
|
|
|||
|
|
@ -1,13 +1,8 @@
|
|||
import { useEffect, useMemo, useState } from 'react'
|
||||
import { useParams } from 'react-router-dom'
|
||||
import { PaymentSheet } from '../features/payments/components/PaymentSheet'
|
||||
import { PaymentMethodList } from '../features/payments/components/PaymentMethodList'
|
||||
import type { PaymentMethod } from '../features/payments/components/PaymentMethodList'
|
||||
import { BankTransferPanel } from '../features/payments/core/BankTransferPanel'
|
||||
import { CardPanel } from '../features/payments/core/CardPanel'
|
||||
import { GoPayPanel } from '../features/payments/core/GoPayPanel'
|
||||
import { CStorePanel } from '../features/payments/core/CStorePanel'
|
||||
import { BankLogo, type BankKey, LogoAlfamart, LogoIndomaret } from '../features/payments/components/PaymentLogos'
|
||||
import { SnapPaymentTrigger } from '../features/payments/snap/SnapPaymentTrigger'
|
||||
import { usePaymentConfig } from '../features/payments/lib/usePaymentConfig'
|
||||
import { Alert } from '../components/alert/Alert'
|
||||
import { Button } from '../components/ui/button'
|
||||
|
|
@ -23,15 +18,13 @@ export function PayPage() {
|
|||
const [orderId, setOrderId] = useState<string>('')
|
||||
const [amount, setAmount] = useState<number>(0)
|
||||
const [expireAt, setExpireAt] = useState<number>(Date.now() + 24 * 60 * 60 * 1000)
|
||||
const [selectedMethod, setSelectedMethod] = useState<Method>(null)
|
||||
const [selectedMethod] = useState<Method>(null)
|
||||
const [locked, setLocked] = useState<boolean>(false)
|
||||
const [selectedBank, setSelectedBank] = useState<BankKey | null>(null)
|
||||
const [selectedStore, setSelectedStore] = useState<'alfamart' | 'indomaret' | null>(null)
|
||||
const [customer, setCustomer] = useState<{ name?: string; phone?: string; email?: string } | undefined>(undefined)
|
||||
const [allowedMethods, setAllowedMethods] = useState<string[] | undefined>(undefined)
|
||||
const [error, setError] = useState<{ code?: string; message?: string } | null>(null)
|
||||
const { data: runtimeCfg } = usePaymentConfig()
|
||||
const [currentStep, setCurrentStep] = useState<2 | 3>(2)
|
||||
const [isBusy, setIsBusy] = useState(false)
|
||||
const currentStep = 2
|
||||
|
||||
useEffect(() => {
|
||||
let cancelled = false
|
||||
|
|
@ -43,6 +36,7 @@ export function PayPage() {
|
|||
setOrderId(payload.order_id)
|
||||
setAmount(payload.nominal)
|
||||
setExpireAt(payload.expire_at ?? Date.now() + 24 * 60 * 60 * 1000)
|
||||
setCustomer(payload.customer)
|
||||
setAllowedMethods(payload.allowed_methods)
|
||||
setError(null)
|
||||
if (isOrderLocked(payload.order_id)) setLocked(true)
|
||||
|
|
@ -58,26 +52,7 @@ export function PayPage() {
|
|||
}, [token])
|
||||
|
||||
const merchantName = useMemo(() => '', [])
|
||||
|
||||
const isExpired = expireAt ? Date.now() > expireAt : false
|
||||
const enabledMap: Record<PaymentMethod, boolean> = useMemo(() => {
|
||||
const base = runtimeCfg?.paymentToggles
|
||||
const allow = allowedMethods
|
||||
const all: Record<PaymentMethod, boolean> = {
|
||||
bank_transfer: base?.bank_transfer ?? true,
|
||||
credit_card: base?.credit_card ?? true,
|
||||
gopay: base?.gopay ?? true,
|
||||
cstore: base?.cstore ?? true,
|
||||
cpay: base?.cpay ?? false,
|
||||
}
|
||||
if (allow && Array.isArray(allow)) {
|
||||
for (const k of (Object.keys(all) as PaymentMethod[])) {
|
||||
if (k === 'cpay') continue
|
||||
all[k] = allow.includes(k) && all[k]
|
||||
}
|
||||
}
|
||||
return all
|
||||
}, [runtimeCfg, allowedMethods])
|
||||
|
||||
if (error || isExpired) {
|
||||
const title = isExpired ? 'Link pembayaran telah kedaluwarsa' : 'Link pembayaran tidak valid'
|
||||
|
|
@ -118,7 +93,7 @@ export function PayPage() {
|
|||
orderId={orderId}
|
||||
amount={amount}
|
||||
expireAt={expireAt}
|
||||
showStatusCTA={currentStep === 3}
|
||||
showStatusCTA={currentStep === 2}
|
||||
>
|
||||
<div className="space-y-4 px-4 py-6">
|
||||
{locked && currentStep === 2 && (
|
||||
|
|
@ -132,132 +107,26 @@ export function PayPage() {
|
|||
</Alert>
|
||||
)}
|
||||
{currentStep === 2 && (
|
||||
<div className="space-y-3">
|
||||
<PaymentMethodList
|
||||
selected={selectedMethod ?? undefined}
|
||||
onSelect={(m) => {
|
||||
setSelectedMethod(m as Method)
|
||||
if (m === 'bank_transfer' || m === 'cstore') {
|
||||
void 0
|
||||
} else if (m === 'cpay') {
|
||||
try {
|
||||
window.open('https://play.google.com/store/apps/details?id=com.cifo.walanja', '_blank')
|
||||
} catch { void 0 }
|
||||
} else {
|
||||
setIsBusy(true)
|
||||
setTimeout(() => { setCurrentStep(3); setIsBusy(false) }, 300)
|
||||
}
|
||||
<div className="space-y-3" aria-live="polite">
|
||||
<SnapPaymentTrigger
|
||||
orderId={orderId}
|
||||
amount={amount}
|
||||
customer={customer}
|
||||
paymentMethod={selectedMethod || 'bank_transfer'}
|
||||
onChargeInitiated={() => {
|
||||
lockOrder(orderId)
|
||||
setLocked(true)
|
||||
}}
|
||||
disabled={locked}
|
||||
enabled={enabledMap}
|
||||
renderPanel={(m) => {
|
||||
const enabled = enabledMap[m]
|
||||
if (!enabled) {
|
||||
return (
|
||||
<div className="p-2">
|
||||
<Alert title="Metode nonaktif">Metode pembayaran ini dinonaktifkan di konfigurasi lingkungan.</Alert>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
if (m === 'bank_transfer') {
|
||||
return (
|
||||
<div className="space-y-2" aria-live="polite">
|
||||
<div className="text-xs text-gray-600">Pilih bank untuk membuat Virtual Account</div>
|
||||
<div className={`grid grid-cols-2 md:grid-cols-3 gap-2 ${isBusy ? 'pointer-events-none opacity-60' : ''}`}>
|
||||
{(['bca', 'bni', 'bri', 'cimb', 'mandiri', 'permata'] as BankKey[]).map((bk) => (
|
||||
<button
|
||||
key={bk}
|
||||
type="button"
|
||||
onClick={() => {
|
||||
setSelectedBank(bk)
|
||||
setIsBusy(true)
|
||||
setTimeout(() => { setCurrentStep(3); setIsBusy(false) }, 300)
|
||||
}}
|
||||
className="rounded border border-gray-300 bg-white p-3 md:p-2 w-full flex items-center justify-center overflow-hidden hover:bg-gray-100"
|
||||
aria-label={`Pilih bank ${bk.toUpperCase()}`}
|
||||
>
|
||||
<BankLogo bank={bk} />
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
{isBusy && (
|
||||
<div className="text-xs text-gray-600 inline-flex items-center gap-2">
|
||||
<span className="h-3 w-3 animate-spin rounded-full border-2 border-gray-400 border-t-transparent" aria-hidden />
|
||||
Menyiapkan VA…
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
if (m === 'cstore') {
|
||||
return (
|
||||
<div className="space-y-2" aria-live="polite">
|
||||
<div className="text-xs text-gray-600">Pilih toko untuk membuat kode pembayaran</div>
|
||||
<div className={`grid grid-cols-2 gap-2 ${isBusy ? 'pointer-events-none opacity-60' : ''}`}>
|
||||
{/* {(['alfamart', 'indomaret'] as const).map((st) => ( */}
|
||||
{(['alfamart'] as const).map((st) => (
|
||||
<button
|
||||
key={st}
|
||||
type="button"
|
||||
onClick={() => {
|
||||
setSelectedStore(st)
|
||||
setIsBusy(true)
|
||||
setTimeout(() => { setCurrentStep(3); setIsBusy(false) }, 300)
|
||||
}}
|
||||
className="rounded border border-gray-300 bg-white p-3 md:p-2 w-full flex items-center justify-center hover:bg-gray-100"
|
||||
aria-label={`Pilih toko ${st.toUpperCase()}`}
|
||||
>
|
||||
{st === 'alfamart' ? <LogoAlfamart /> : <LogoIndomaret />}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
return null
|
||||
onSuccess={(result) => {
|
||||
console.log('[PayPage] Payment success:', result)
|
||||
nav.toStatus(orderId, selectedMethod || undefined)
|
||||
}}
|
||||
onError={(error) => {
|
||||
console.error('[PayPage] Payment error:', error)
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{currentStep === 3 && (
|
||||
<div className="space-y-3" aria-live="polite">
|
||||
{selectedMethod === 'bank_transfer' && (
|
||||
<BankTransferPanel
|
||||
locked={locked}
|
||||
onChargeInitiated={() => { lockOrder(orderId); setLocked(true) }}
|
||||
orderId={orderId}
|
||||
amount={amount}
|
||||
defaultBank={(selectedBank ?? 'bca')}
|
||||
/>
|
||||
)}
|
||||
{selectedMethod === 'credit_card' && (
|
||||
<CardPanel
|
||||
locked={locked}
|
||||
onChargeInitiated={() => { lockOrder(orderId); setLocked(true) }}
|
||||
orderId={orderId}
|
||||
amount={amount}
|
||||
/>
|
||||
)}
|
||||
{selectedMethod === 'gopay' && (
|
||||
<GoPayPanel
|
||||
locked={locked}
|
||||
onChargeInitiated={() => { lockOrder(orderId); setLocked(true) }}
|
||||
orderId={orderId}
|
||||
amount={amount}
|
||||
/>
|
||||
)}
|
||||
{selectedMethod === 'cstore' && (
|
||||
<CStorePanel
|
||||
locked={locked}
|
||||
onChargeInitiated={() => { lockOrder(orderId); setLocked(true) }}
|
||||
orderId={orderId}
|
||||
amount={amount}
|
||||
defaultStore={selectedStore ?? undefined}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</PaymentSheet>
|
||||
)
|
||||
|
|
|
|||
Loading…
Reference in New Issue