import { useEffect, useMemo, useState } from 'react' import { useParams } from 'react-router-dom' import { PaymentSheet } from '../features/payments/components/PaymentSheet' import type { PaymentMethod } from '../features/payments/components/PaymentMethodList' import { usePaymentConfig } from '../features/payments/lib/usePaymentConfig' import { Alert } from '../components/alert/Alert' import { Button } from '../components/ui/button' import { getPaymentLinkPayload } from '../services/api' import { isOrderLocked, lockOrder } from '../features/payments/lib/chargeLock' import { usePaymentNavigation } from '../features/payments/lib/navigation' import { Logger } from '../lib/logger' import { loadSnapScript } from '../lib/snapLoader' import { SnapTokenService } from '../features/payments/snap/SnapTokenService' import React from 'react' type Method = PaymentMethod | null interface AutoSnapPaymentProps { orderId: string amount: number customer?: { name?: string; phone?: string; email?: string } onSuccess?: (result: any) => void onError?: (error: any) => void } function AutoSnapPayment({ orderId, amount, customer, onSuccess, onError }: AutoSnapPaymentProps) { const [loading, setLoading] = React.useState(false) const [error, setError] = React.useState('') const [paymentTriggered, setPaymentTriggered] = React.useState(false) console.log('[PayPage] AutoSnapPayment mounted:', { orderId, amount, customer }) Logger.info('paypage.autosnapPayment.mount', { orderId, amount, hasCustomer: !!customer }) React.useEffect(() => { console.log('[PayPage] useEffect triggered', { orderId, amount, paymentTriggered }) if (!orderId || !amount || paymentTriggered) { console.log('[PayPage] Early return', { hasOrderId: !!orderId, hasAmount: !!amount, alreadyTriggered: paymentTriggered }) return } const triggerPayment = async () => { console.log('[PayPage] triggerPayment called') setPaymentTriggered(true) try { setLoading(true) setError('') Logger.paymentInfo('paypage.auto.snap.init', { orderId, amount, customer }) // Load Snap.js first await loadSnapScript() Logger.paymentInfo('paypage.auto.snap.script_loaded', { orderId, hasSnap: !!window.snap }) // Create Snap transaction token const token = await SnapTokenService.createToken({ transaction_details: { order_id: orderId, gross_amount: amount }, 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('paypage.auto.snap.token.received', { orderId, token: token.substring(0, 10) + '...' }) console.log('[PayPage] Token received:', token) // Store customer name in localStorage for status page if (customer?.name) { try { const customerCache = JSON.parse(localStorage.getItem('customerCache') || '{}') customerCache[orderId] = { name: customer.name, timestamp: Date.now() } localStorage.setItem('customerCache', JSON.stringify(customerCache)) } catch (e) { console.warn('Failed to cache customer name:', e) } } if (!window.snap || typeof window.snap.pay !== 'function') { throw new Error(`Snap.js not loaded: hasSnap=${!!window.snap}`) } console.log('[PayPage] Calling window.snap.pay') setLoading(false) window.snap.pay(token, { onSuccess: (result: any) => { Logger.paymentInfo('paypage.auto.snap.payment.success', { orderId, transactionId: result.transaction_id }) onSuccess?.(result) }, onPending: (result: any) => { Logger.paymentInfo('paypage.auto.snap.payment.pending', { orderId, transactionId: result.transaction_id }) }, onError: (result: any) => { Logger.paymentError('paypage.auto.snap.payment.error', { orderId, error: result }) setError('Pembayaran gagal. Silakan coba lagi.') setLoading(false) onError?.(result) }, onClose: () => { Logger.paymentInfo('paypage.auto.snap.popup.closed', { orderId }) setLoading(false) } }) } catch (e: any) { Logger.paymentError('paypage.auto.snap.payment.error', { orderId, error: e.message }) console.error('[PayPage] Error:', e) // Handle specific errors with user-friendly messages const errorMessage = e.response?.data?.message || e.message || '' const errorMessages = e.response?.data?.error_messages || [] // Check for "order_id already used" from Midtrans const isOrderIdUsed = errorMessage.includes('sudah digunakan') || errorMessage.includes('already been taken') || errorMessage.includes('order_id has already been taken') || errorMessages.some((msg: string) => msg.includes('sudah digunakan')) if (isOrderIdUsed) { // Order already has payment, redirect to status page Logger.paymentInfo('paypage.order.already_exists', { orderId }) console.log('[PayPage] Order already has payment, redirecting to status...') // Show user-friendly message then redirect setError('Pembayaran untuk pesanan ini sudah dibuat sebelumnya. Anda akan diarahkan ke halaman status pembayaran...') setTimeout(() => { window.location.href = `/payments/${orderId}/status` }, 2000) } else { // Generic error with user-friendly message const userMessage = 'Maaf, terjadi kesalahan saat memuat pembayaran. Silakan coba lagi atau hubungi customer service.' setError(userMessage) } onError?.(e) setLoading(false) } } console.log('[PayPage] Setting timeout') const timer = setTimeout(triggerPayment, 500) return () => clearTimeout(timer) }, [orderId, amount, customer, paymentTriggered, onSuccess, onError]) // Don't render anything until we have valid data if (!orderId || !amount) { return (
Memuat data pembayaran...
Menyiapkan pembayaran...
Gagal memuat pembayaran