import type { TCheckoutSessionExpanded, TPaymentMethodExpanded } from '@blocklet/payment-types'; import { useRequest, useSetState } from 'ahooks'; import noop from 'lodash/noop'; import { useEffect } from 'react'; import { joinURL } from 'ufo'; import { ReactGA } from '@arcblock/ux/lib/withTracker'; import type { PayFailedEvent, PaySuccessEvent } from '@arcblock/ux/lib/withTracker/action/pay'; import api from '../libs/api'; import { getPrefix, mergeExtraParams } from '../libs/util'; import Payment from '../payment'; import type { CheckoutContext, CheckoutProps } from '../types'; import { PaymentThemeProvider } from '../theme'; import DonationForm from '../payment/donation-form'; const promises: { [key: string]: Promise } = {}; const startFromPaymentLink = (id: string, params?: Record): Promise => { if (!promises[id]) { promises[id] = api .post(`/api/checkout-sessions/start/${id}?${mergeExtraParams(params)}`) .then((res: any) => res?.data) .finally(() => { setTimeout(() => { delete promises[id]; }, 3000); }); } return promises[id]; }; const fetchCheckoutSession = async (id: string): Promise => { const { data } = await api.get(`/api/checkout-sessions/retrieve/${id}`); return data; }; export default function CheckoutForm({ id, mode = 'inline', onPaid = noop, onError = console.error, onChange, goBack, extraParams = {}, action = '', theme = 'default', formType = 'payment', ...restProps }: CheckoutProps) { if (!id.startsWith('plink_') && !id.startsWith('cs_')) { throw new Error('Either a checkoutSession or a paymentLink id is required.'); } const type = id.startsWith('plink_') ? 'paymentLink' : 'checkoutSession'; const [state, setState] = useSetState({ completed: false, appError: null }); const { error: apiError, data } = useRequest(() => type === 'paymentLink' ? startFromPaymentLink(id, extraParams) : fetchCheckoutSession(id) ); useEffect(() => { if (type === 'paymentLink' && mode === 'standalone' && data) { window.history.replaceState( null, '', joinURL(getPrefix(), `/checkout/pay/${data.checkoutSession.id}?${mergeExtraParams(extraParams)}`) ); } }, [type, mode, data, extraParams]); const handlePaid = (result: CheckoutContext) => { setState({ completed: true }); onPaid?.(result as CheckoutContext); const paySuccessEvent: PaySuccessEvent = { action: 'paySuccess', // @ts-ignore 后续升级的话就会报错了,移除这个 lint 即可 mode: data?.checkoutSession?.mode!, success: true, }; ReactGA.event(paySuccessEvent.action, paySuccessEvent); }; const handleError = (err: any) => { console.error(err); setState({ appError: err }); onError?.(err); const payFailedEvent: PayFailedEvent = { action: 'payFailed', // @ts-ignore后续升级的话就会报错了,移除这个 lint 即可 mode: data?.checkoutSession?.mode!, errorMessage: err.message, success: false, }; ReactGA.event(payFailedEvent.action, payFailedEvent); }; const Checkout = formType === 'donation' ? ( ) : ( ); if (theme === 'inherit') { return Checkout; } if (theme && typeof theme === 'object') { return {Checkout}; } return {Checkout}; }