/* eslint-disable react/require-default-props */ import { useLocaleContext } from '@arcblock/ux/lib/Locale/context'; import Toast from '@arcblock/ux/lib/Toast'; import Dialog from '@arcblock/ux/lib/Dialog/dialog'; import type { Customer, TPaymentMethod, TInvoiceExpanded } from '@blocklet/payment-types'; import { Button, Typography } from '@mui/material'; import { useSetState } from 'ahooks'; import { useEffect, useRef } from 'react'; import StripeForm from '../payment/form/stripe'; import api from '../libs/api'; import { formatError } from '../libs/util'; export interface StripePaymentActionProps { invoice?: TInvoiceExpanded; invoiceIds?: string[]; subscriptionId?: string; customerId?: string; currencyId?: string; paymentMethod?: TPaymentMethod; autoTrigger?: boolean; onExternalPayment?: (invoiceId?: string) => void; onSuccess?: () => void; onError?: (error: Error) => void; onClose?: () => void; children?: (onPay: () => void, loading: boolean) => React.ReactNode; } export default function StripePaymentAction(props: StripePaymentActionProps) { const { invoice, invoiceIds, subscriptionId, customerId, currencyId, paymentMethod, autoTrigger = false, onExternalPayment, onSuccess, onError, onClose, children, } = props; const { t } = useLocaleContext(); const [state, setState] = useSetState<{ paying: boolean; confirmDialog: boolean; stripeDialog: boolean; clientSecret: string | null; publishableKey: string | null; customer: Customer | null; }>({ paying: false, confirmDialog: false, stripeDialog: false, clientSecret: null, publishableKey: null, customer: null, }); const autoTriggerRef = useRef(false); useEffect(() => { if (autoTrigger && !autoTriggerRef.current) { autoTriggerRef.current = true; handlePay(); } }, [autoTrigger]); // eslint-disable-line react-hooks/exhaustive-deps const handlePay = async () => { if (state.paying) { return; } const hasSubscription = !!(subscriptionId || invoice?.subscription_id); const method = paymentMethod || invoice?.paymentMethod; const shouldShowConfirm = hasSubscription && method?.type === 'stripe'; if (shouldShowConfirm) { setState({ confirmDialog: true }); return; } await proceedWithPayment(); }; const proceedWithPayment = async () => { setState({ paying: true, confirmDialog: false }); const derivedCurrencyId = currencyId || invoice?.currency_id || invoice?.paymentCurrency?.id; const derivedPaymentMethod = paymentMethod || invoice?.paymentMethod; const isStripePayment = derivedPaymentMethod?.type === 'stripe'; if (isStripePayment && derivedCurrencyId) { const stripePayload: Record = {}; if (invoiceIds && invoiceIds.length > 0) { stripePayload.invoice_ids = invoiceIds; } else if (invoice) { stripePayload.invoice_ids = [invoice.id]; } else if (subscriptionId) { stripePayload.subscription_id = subscriptionId; } else if (customerId) { stripePayload.customer_id = customerId; } if (derivedCurrencyId) { stripePayload.currency_id = derivedCurrencyId; } try { const { data: paymentData } = await api.post('/api/invoices/pay-stripe', stripePayload); setState({ paying: false, stripeDialog: true, clientSecret: paymentData.client_secret, publishableKey: paymentData.publishable_key, customer: paymentData.customer || null, }); return; } catch (err: any) { const error = formatError(err); Toast.error(error); setState({ paying: false }); onError?.(error); return; } } setState({ paying: false }); if (onExternalPayment) { onExternalPayment(invoice?.id); return; } Toast.error(t('payment.customer.invoice.payError')); onError?.(new Error('EXTERNAL_PAYMENT_HANDLER_NOT_PROVIDED')); }; const handleConfirmCancel = () => { setState({ confirmDialog: false, paying: false }); onClose?.(); }; const handleStripeConfirm = () => { Toast.success(t('payment.customer.invoice.payProcessing')); setState({ paying: false, stripeDialog: false, clientSecret: null, publishableKey: null, customer: null, }); setTimeout(() => { onSuccess?.(); }, 2000); }; const handleStripeCancel = () => { setState({ paying: false, stripeDialog: false, clientSecret: null, publishableKey: null, customer: null, }); onClose?.(); }; return ( <> {children?.(handlePay, state.paying)} {state.confirmDialog && ( {t('common.cancel')} , , ]}> {t('payment.customer.invoice.paymentConfirmDescription')} )} {state.stripeDialog && state.clientSecret && state.publishableKey && state.customer && ( )} ); }