diff --git a/src/pages/CheckoutPage.tsx b/src/pages/CheckoutPage.tsx index 1b4178f..8788a23 100644 --- a/src/pages/CheckoutPage.tsx +++ b/src/pages/CheckoutPage.tsx @@ -9,6 +9,118 @@ import { usePaymentConfig } from '../features/payments/lib/usePaymentConfig' import { Logger } from '../lib/logger' import React from 'react' +interface AutoSnapPaymentProps { + orderId: string + amount: number + customer?: { name?: string; phone?: string; email?: string } + onChargeInitiated?: () => void + onSuccess?: (result: any) => void + onError?: (error: any) => void +} + +function AutoSnapPayment({ orderId, amount, customer, onChargeInitiated, onSuccess, onError }: AutoSnapPaymentProps) { + const [loading, setLoading] = React.useState(false) + const [error, setError] = React.useState('') + const hasTriggered = React.useRef(false) + + React.useEffect(() => { + if (hasTriggered.current) return + hasTriggered.current = true + + const triggerPayment = async () => { + try { + setLoading(true) + setError('') + + Logger.paymentInfo('checkout.auto.snap.init', { orderId, amount, customer }) + + // Import SnapTokenService dynamically to avoid circular deps + const { SnapTokenService } = await import('../features/payments/snap/SnapTokenService') + + // 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('checkout.auto.snap.token.received', { orderId, token: token.substring(0, 10) + '...' }) + + // Auto-trigger Snap payment popup + if (window.snap && typeof window.snap.pay === 'function') { + window.snap.pay(token, { + onSuccess: (result: any) => { + Logger.paymentInfo('checkout.auto.snap.payment.success', { orderId, transactionId: result.transaction_id }) + onSuccess?.(result) + }, + onPending: (result: any) => { + Logger.paymentInfo('checkout.auto.snap.payment.pending', { orderId, transactionId: result.transaction_id }) + }, + onError: (result: any) => { + Logger.paymentError('checkout.auto.snap.payment.error', { orderId, error: result }) + const message = 'Pembayaran gagal. Silakan coba lagi.' + setError(message) + onError?.(result) + }, + onClose: () => { + Logger.paymentInfo('checkout.auto.snap.popup.closed', { orderId }) + } + }) + } else { + throw new Error('Snap.js not loaded') + } + + } catch (e: any) { + Logger.paymentError('checkout.auto.snap.payment.error', { orderId, error: e.message }) + const message = 'Gagal memuat pembayaran. Silakan refresh halaman.' + setError(message) + onError?.(e) + } finally { + setLoading(false) + } + } + + // Small delay to ensure UI is rendered + const timer = setTimeout(triggerPayment, 500) + return () => clearTimeout(timer) + }, [orderId, amount, customer, onChargeInitiated, onSuccess, onError]) + + return ( +
+ {error && ( + + {error} + + )} + +
+ {loading ? ( +
+
+

Menyiapkan pembayaran...

+
+ ) : ( +

+ Membuka halaman pembayaran Midtrans... +

+ )} +
+
+ ) +} + export function CheckoutPage() { const apiBase = Env.API_BASE_URL const clientKey = Env.MIDTRANS_CLIENT_KEY @@ -132,7 +244,7 @@ export function CheckoutPage() { {currentStep === 2 && (
- setLocked(true)} onSuccess={(result) => { Logger.info('checkout.payment.success', { orderId, result }) diff --git a/src/pages/PayPage.tsx b/src/pages/PayPage.tsx index ca64f8e..09c6f00 100644 --- a/src/pages/PayPage.tsx +++ b/src/pages/PayPage.tsx @@ -2,16 +2,128 @@ 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 { 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' import { getPaymentLinkPayload } from '../services/api' import { isOrderLocked, lockOrder } from '../features/payments/lib/chargeLock' import { usePaymentNavigation } from '../features/payments/lib/navigation' +import React from 'react' type Method = PaymentMethod | null +interface AutoSnapPaymentProps { + orderId: string + amount: number + customer?: { name?: string; phone?: string; email?: string } + onChargeInitiated?: () => void + onSuccess?: (result: any) => void + onError?: (error: any) => void +} + +function AutoSnapPayment({ orderId, amount, customer, onChargeInitiated, onSuccess, onError }: AutoSnapPaymentProps) { + const [loading, setLoading] = React.useState(false) + const [error, setError] = React.useState('') + const hasTriggered = React.useRef(false) + + React.useEffect(() => { + if (hasTriggered.current) return + hasTriggered.current = true + + const triggerPayment = async () => { + try { + setLoading(true) + setError('') + + console.log('[PayPage] Auto-triggering Snap payment:', { orderId, amount, customer }) + + // Import SnapTokenService dynamically to avoid circular deps + const { SnapTokenService } = await import('../features/payments/snap/SnapTokenService') + + // 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 + }] + }) + + console.log('[PayPage] Snap token received:', token.substring(0, 10) + '...') + + // Auto-trigger Snap payment popup + if (window.snap && typeof window.snap.pay === 'function') { + window.snap.pay(token, { + onSuccess: (result: any) => { + console.log('[PayPage] Payment success:', result) + onSuccess?.(result) + }, + onPending: (result: any) => { + console.log('[PayPage] Payment pending:', result) + }, + onError: (result: any) => { + console.error('[PayPage] Payment error:', result) + const message = 'Pembayaran gagal. Silakan coba lagi.' + setError(message) + onError?.(result) + }, + onClose: () => { + console.log('[PayPage] Snap popup closed') + } + }) + } else { + throw new Error('Snap.js not loaded') + } + + } catch (e: any) { + console.error('[PayPage] Auto-payment error:', e.message) + const message = 'Gagal memuat pembayaran. Silakan refresh halaman.' + setError(message) + onError?.(e) + } finally { + setLoading(false) + } + } + + // Small delay to ensure UI is rendered + const timer = setTimeout(triggerPayment, 500) + return () => clearTimeout(timer) + }, [orderId, amount, customer, onChargeInitiated, onSuccess, onError]) + + return ( +
+ {error && ( + + {error} + + )} + +
+ {loading ? ( +
+
+

Menyiapkan pembayaran...

+
+ ) : ( +

+ Membuka halaman pembayaran Midtrans... +

+ )} +
+
+ ) +} + export function PayPage() { const { token } = useParams() const nav = usePaymentNavigation() @@ -108,11 +220,10 @@ export function PayPage() { )} {currentStep === 2 && (
- { lockOrder(orderId) setLocked(true)