@tagadapay/plugin-sdk 3.1.12 → 3.1.24
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/build-cdn.js +397 -11
- package/dist/data/iso3166.d.ts +23 -33
- package/dist/data/iso3166.js +134 -198
- package/dist/data/languages.d.ts +5 -64
- package/dist/data/languages.js +23 -143
- package/dist/external-tracker.js +623 -3426
- package/dist/external-tracker.min.js +2 -25
- package/dist/external-tracker.min.js.map +4 -4
- package/dist/react/config/payment.d.ts +14 -4
- package/dist/react/config/payment.js +47 -9
- package/dist/react/hooks/useCheckout.d.ts +3 -0
- package/dist/react/hooks/useCheckout.js +4 -1
- package/dist/react/hooks/useISOData.js +1 -1
- package/dist/react/hooks/usePaymentPolling.d.ts +3 -3
- package/dist/react/hooks/usePluginConfig.js +9 -10
- package/dist/react/providers/TagadaProvider.js +1 -1
- package/dist/tagada-react-sdk-minimal.min.js +36 -0
- package/dist/tagada-react-sdk-minimal.min.js.map +7 -0
- package/dist/tagada-react-sdk.js +37821 -0
- package/dist/tagada-react-sdk.min.js +78 -0
- package/dist/tagada-react-sdk.min.js.map +7 -0
- package/dist/tagada-sdk.js +16044 -0
- package/dist/tagada-sdk.min.js +32 -0
- package/dist/tagada-sdk.min.js.map +7 -0
- package/dist/v2/cdn-react-minimal.d.ts +23 -0
- package/dist/v2/cdn-react-minimal.js +26 -0
- package/dist/v2/core/client.d.ts +4 -2
- package/dist/v2/core/client.js +5 -4
- package/dist/v2/core/config/environment.js +2 -1
- package/dist/v2/core/errors.d.ts +75 -0
- package/dist/v2/core/errors.js +104 -0
- package/dist/v2/core/funnelClient.d.ts +100 -10
- package/dist/v2/core/funnelClient.js +121 -27
- package/dist/v2/core/isoData.d.ts +4 -4
- package/dist/v2/core/isoData.js +7 -7
- package/dist/v2/core/pixelMapping.d.ts +49 -0
- package/dist/v2/core/pixelMapping.js +363 -0
- package/dist/v2/core/resources/apiClient.d.ts +2 -0
- package/dist/v2/core/resources/apiClient.js +52 -9
- package/dist/v2/core/resources/checkout.d.ts +99 -30
- package/dist/v2/core/resources/checkout.js +14 -0
- package/dist/v2/core/resources/customer.d.ts +20 -19
- package/dist/v2/core/resources/expressPaymentMethods.d.ts +1 -0
- package/dist/v2/core/resources/funnel.d.ts +17 -17
- package/dist/v2/core/resources/payments.d.ts +89 -13
- package/dist/v2/core/resources/payments.js +27 -9
- package/dist/v2/core/resources/postPurchases.d.ts +17 -0
- package/dist/v2/core/resources/postPurchases.js +20 -0
- package/dist/v2/core/types.d.ts +50 -12
- package/dist/v2/core/types.js +0 -3
- package/dist/v2/core/utils/checkout.d.ts +2 -2
- package/dist/v2/core/utils/checkout.js +7 -2
- package/dist/v2/core/utils/currency.d.ts +14 -0
- package/dist/v2/core/utils/currency.js +40 -0
- package/dist/v2/core/utils/deviceInfo.d.ts +0 -10
- package/dist/v2/core/utils/deviceInfo.js +152 -76
- package/dist/v2/core/utils/index.d.ts +1 -0
- package/dist/v2/core/utils/index.js +2 -0
- package/dist/v2/core/utils/order.d.ts +13 -9
- package/dist/v2/core/utils/pluginConfig.d.ts +8 -0
- package/dist/v2/core/utils/pluginConfig.js +36 -12
- package/dist/v2/index.d.ts +6 -3
- package/dist/v2/index.js +4 -2
- package/dist/v2/react/components/FunnelScriptInjector.js +166 -77
- package/dist/v2/react/components/StripeExpressButton.d.ts +13 -0
- package/dist/v2/react/components/StripeExpressButton.js +171 -0
- package/dist/v2/react/components/WhopCheckout.d.ts +24 -0
- package/dist/v2/react/components/WhopCheckout.js +237 -0
- package/dist/v2/react/hooks/__examples__/FunnelContextExample.js +1 -1
- package/dist/v2/react/hooks/payment-actions/useAirwallexRadarAction.d.ts +14 -0
- package/dist/v2/react/hooks/payment-actions/useAirwallexRadarAction.js +181 -0
- package/dist/v2/react/hooks/payment-actions/useErrorAction.d.ts +9 -0
- package/dist/v2/react/hooks/payment-actions/useErrorAction.js +21 -0
- package/dist/v2/react/hooks/payment-actions/useFinixRadarAction.d.ts +14 -0
- package/dist/v2/react/hooks/payment-actions/useFinixRadarAction.js +187 -0
- package/dist/v2/react/hooks/payment-actions/useKessPayAction.d.ts +11 -0
- package/dist/v2/react/hooks/payment-actions/useKessPayAction.js +91 -0
- package/dist/v2/react/hooks/payment-actions/useMasterCardAction.d.ts +24 -0
- package/dist/v2/react/hooks/payment-actions/useMasterCardAction.js +221 -0
- package/dist/v2/react/hooks/payment-actions/usePaymentActionHandler.d.ts +15 -0
- package/dist/v2/react/hooks/payment-actions/usePaymentActionHandler.js +142 -0
- package/dist/v2/react/hooks/payment-actions/useProcessorAuthAction.d.ts +3 -0
- package/dist/v2/react/hooks/payment-actions/useProcessorAuthAction.js +31 -0
- package/dist/v2/react/hooks/payment-actions/useRedirectAction.d.ts +10 -0
- package/dist/v2/react/hooks/payment-actions/useRedirectAction.js +35 -0
- package/dist/v2/react/hooks/payment-actions/useStripeRadarAction.d.ts +14 -0
- package/dist/v2/react/hooks/payment-actions/useStripeRadarAction.js +192 -0
- package/dist/v2/react/hooks/payment-actions/useThreedsAuthAction.d.ts +14 -0
- package/dist/v2/react/hooks/payment-actions/useThreedsAuthAction.js +81 -0
- package/dist/v2/react/hooks/payment-actions/useTrustFlowAction.d.ts +11 -0
- package/dist/v2/react/hooks/payment-actions/useTrustFlowAction.js +84 -0
- package/dist/v2/react/hooks/payment-processing/usePaymentInstruments.d.ts +14 -0
- package/dist/v2/react/hooks/payment-processing/usePaymentInstruments.js +36 -0
- package/dist/v2/react/hooks/payment-processing/usePaymentProcessors.d.ts +31 -0
- package/dist/v2/react/hooks/payment-processing/usePaymentProcessors.js +212 -0
- package/dist/v2/react/hooks/payment-redirect/useAirwallex3dsReturn.d.ts +14 -0
- package/dist/v2/react/hooks/payment-redirect/useAirwallex3dsReturn.js +207 -0
- package/dist/v2/react/hooks/payment-redirect/useGenericPaymentReturn.d.ts +12 -0
- package/dist/v2/react/hooks/payment-redirect/useGenericPaymentReturn.js +101 -0
- package/dist/v2/react/hooks/useApplePayCheckout.js +8 -8
- package/dist/v2/react/hooks/useCheckoutQuery.d.ts +16 -0
- package/dist/v2/react/hooks/useCheckoutQuery.js +63 -10
- package/dist/v2/react/hooks/useFunnel.d.ts +15 -4
- package/dist/v2/react/hooks/useFunnel.js +8 -4
- package/dist/v2/react/hooks/useGeoLocation.d.ts +2 -1
- package/dist/v2/react/hooks/useGeoLocation.js +4 -2
- package/dist/v2/react/hooks/useGoogleAutocomplete.d.ts +2 -0
- package/dist/v2/react/hooks/useGoogleAutocomplete.js +29 -15
- package/dist/v2/react/hooks/useISOData.d.ts +2 -5
- package/dist/v2/react/hooks/useISOData.js +26 -27
- package/dist/v2/react/hooks/usePaymentPolling.d.ts +3 -3
- package/dist/v2/react/hooks/usePaymentQuery.d.ts +18 -5
- package/dist/v2/react/hooks/usePaymentQuery.js +63 -1015
- package/dist/v2/react/hooks/usePaymentRetrieve.d.ts +3 -2
- package/dist/v2/react/hooks/usePaymentRetrieve.js +3 -1
- package/dist/v2/react/hooks/usePixelTracking.d.ts +5 -48
- package/dist/v2/react/hooks/usePixelTracking.js +283 -504
- package/dist/v2/react/hooks/usePostPurchasesQuery.js +34 -2
- package/dist/v2/react/hooks/useRemappableParams.d.ts +2 -6
- package/dist/v2/react/hooks/useRemappableParams.js +23 -23
- package/dist/v2/react/hooks/useSetPaymentMethod.d.ts +16 -0
- package/dist/v2/react/hooks/useSetPaymentMethod.js +33 -0
- package/dist/v2/react/hooks/useShippingRatesQuery.js +13 -5
- package/dist/v2/react/hooks/useStepConfig.d.ts +23 -6
- package/dist/v2/react/hooks/useStepConfig.js +14 -7
- package/dist/v2/react/hooks/useTranslation.js +23 -8
- package/dist/v2/react/hooks/useWhopPaymentPolling.d.ts +30 -0
- package/dist/v2/react/hooks/useWhopPaymentPolling.js +61 -0
- package/dist/v2/react/index.d.ts +15 -1
- package/dist/v2/react/index.js +7 -0
- package/dist/v2/react/providers/ExpressPaymentMethodsProvider.d.ts +3 -1
- package/dist/v2/react/providers/ExpressPaymentMethodsProvider.js +12 -2
- package/dist/v2/react/providers/TagadaProvider.js +74 -5
- package/dist/v2/standalone/external-tracker.d.ts +52 -46
- package/dist/v2/standalone/external-tracker.js +205 -98
- package/dist/v2/standalone/index.d.ts +40 -0
- package/dist/v2/standalone/index.js +148 -1
- package/dist/v2/standalone/payment-service.d.ts +134 -0
- package/dist/v2/standalone/payment-service.js +928 -0
- package/package.json +6 -4
- package/dist/react/utils/__tests__/urlUtils.test.d.ts +0 -1
- package/dist/react/utils/__tests__/urlUtils.test.js +0 -189
- package/dist/v2/core/__tests__/pathRemapping.test.d.ts +0 -11
- package/dist/v2/core/__tests__/pathRemapping.test.js +0 -776
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Hook for managing payment instruments
|
|
3
|
+
*/
|
|
4
|
+
import { useCallback } from 'react';
|
|
5
|
+
export function usePaymentInstruments({ basisTheory, paymentsResource, setError, }) {
|
|
6
|
+
// Create card payment instrument
|
|
7
|
+
const createCardPaymentInstrument = useCallback((cardData) => {
|
|
8
|
+
return paymentsResource.createCardPaymentInstrument(basisTheory, cardData);
|
|
9
|
+
}, [basisTheory, paymentsResource]);
|
|
10
|
+
// Create Apple Pay payment instrument
|
|
11
|
+
const createApplePayPaymentInstrument = useCallback((applePayToken) => {
|
|
12
|
+
return paymentsResource.createApplePayPaymentInstrument(basisTheory, applePayToken);
|
|
13
|
+
}, [basisTheory, paymentsResource]);
|
|
14
|
+
// Create Google Pay payment instrument
|
|
15
|
+
const createGooglePayPaymentInstrument = useCallback((googlePayToken) => {
|
|
16
|
+
return paymentsResource.createGooglePayPaymentInstrument(basisTheory, googlePayToken);
|
|
17
|
+
}, [basisTheory, paymentsResource]);
|
|
18
|
+
// Get card payment instruments
|
|
19
|
+
const getCardPaymentInstruments = useCallback(async () => {
|
|
20
|
+
try {
|
|
21
|
+
const response = await paymentsResource.getCardPaymentInstruments();
|
|
22
|
+
return response;
|
|
23
|
+
}
|
|
24
|
+
catch (error) {
|
|
25
|
+
const errorMsg = error instanceof Error ? error.message : 'Failed to fetch payment instruments';
|
|
26
|
+
setError(errorMsg);
|
|
27
|
+
throw error;
|
|
28
|
+
}
|
|
29
|
+
}, [paymentsResource, setError]);
|
|
30
|
+
return {
|
|
31
|
+
createCardPaymentInstrument,
|
|
32
|
+
createApplePayPaymentInstrument,
|
|
33
|
+
createGooglePayPaymentInstrument,
|
|
34
|
+
getCardPaymentInstruments,
|
|
35
|
+
};
|
|
36
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import type { CardPaymentMethod, ApplePayToken, GooglePayToken, PaymentOptions, PaymentResponse, PaymentInstrumentResponse } from '../../../core/resources/payments';
|
|
2
|
+
import type { PaymentsResource } from '../../../core/resources/payments';
|
|
3
|
+
import type { UsePaymentOptions } from '../usePaymentQuery';
|
|
4
|
+
interface UsePaymentProcessorsParams {
|
|
5
|
+
paymentsResource: PaymentsResource;
|
|
6
|
+
createCardPaymentInstrument: (cardData: CardPaymentMethod) => Promise<PaymentInstrumentResponse>;
|
|
7
|
+
createApplePayPaymentInstrument: (applePayToken: ApplePayToken) => Promise<PaymentInstrumentResponse>;
|
|
8
|
+
createGooglePayPaymentInstrument: (googlePayToken: GooglePayToken) => Promise<PaymentInstrumentResponse>;
|
|
9
|
+
createSession: any;
|
|
10
|
+
handlePaymentAction: any;
|
|
11
|
+
startPolling: any;
|
|
12
|
+
setIsLoading: (loading: boolean) => void;
|
|
13
|
+
setError: (error: string | null) => void;
|
|
14
|
+
setCurrentPaymentId: (id: string | null) => void;
|
|
15
|
+
hookOptionsRef: React.MutableRefObject<UsePaymentOptions | undefined>;
|
|
16
|
+
storeConfig: any;
|
|
17
|
+
}
|
|
18
|
+
export declare function usePaymentProcessors({ paymentsResource, createCardPaymentInstrument, createApplePayPaymentInstrument, createGooglePayPaymentInstrument, createSession, handlePaymentAction, startPolling, setIsLoading, setError, setCurrentPaymentId, hookOptionsRef, storeConfig, }: UsePaymentProcessorsParams): {
|
|
19
|
+
processCardPayment: (checkoutSessionId: string, cardData: CardPaymentMethod, options?: PaymentOptions) => Promise<PaymentResponse>;
|
|
20
|
+
processApplePayPayment: (checkoutSessionId: string, applePayToken: ApplePayToken, options?: PaymentOptions) => Promise<PaymentResponse>;
|
|
21
|
+
processGooglePayPayment: (checkoutSessionId: string, googlePayToken: GooglePayToken, options?: PaymentOptions) => Promise<PaymentResponse>;
|
|
22
|
+
processPaymentWithInstrument: (checkoutSessionId: string, paymentInstrumentId: string, options?: PaymentOptions) => Promise<PaymentResponse>;
|
|
23
|
+
processApmPayment: (checkoutSessionId: string, options: {
|
|
24
|
+
processorId: string;
|
|
25
|
+
paymentMethod: string;
|
|
26
|
+
initiatedBy?: "customer" | "merchant";
|
|
27
|
+
source?: "upsell" | "checkout" | "offer" | "missing_club" | "forced";
|
|
28
|
+
paymentFlowId?: string;
|
|
29
|
+
} & Pick<PaymentOptions, "onFailure" | "onPaymentFailed" | "onSuccess" | "onPaymentSuccess">) => Promise<PaymentResponse>;
|
|
30
|
+
};
|
|
31
|
+
export {};
|
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Hook for payment processing methods
|
|
3
|
+
*/
|
|
4
|
+
import { useCallback } from 'react';
|
|
5
|
+
import { getAssignedPaymentFlowId } from '../../../core/funnelClient';
|
|
6
|
+
export function usePaymentProcessors({ paymentsResource, createCardPaymentInstrument, createApplePayPaymentInstrument, createGooglePayPaymentInstrument, createSession, handlePaymentAction, startPolling, setIsLoading, setError, setCurrentPaymentId, hookOptionsRef, storeConfig, }) {
|
|
7
|
+
// Process payment directly with checkout session
|
|
8
|
+
const processPaymentDirect = useCallback(async (checkoutSessionId, paymentInstrumentId, threedsSessionId, options = {}) => {
|
|
9
|
+
try {
|
|
10
|
+
const paymentFlowId = options.paymentFlowId || getAssignedPaymentFlowId();
|
|
11
|
+
const response = await paymentsResource.processPaymentDirect(checkoutSessionId, paymentInstrumentId, threedsSessionId, {
|
|
12
|
+
initiatedBy: options.initiatedBy,
|
|
13
|
+
source: options.source,
|
|
14
|
+
paymentFlowId,
|
|
15
|
+
processorId: options.processorId,
|
|
16
|
+
paymentMethod: options.paymentMethod,
|
|
17
|
+
});
|
|
18
|
+
setCurrentPaymentId(response.payment?.id);
|
|
19
|
+
if (response.payment.requireAction !== 'none') {
|
|
20
|
+
await handlePaymentAction(response.payment, options);
|
|
21
|
+
}
|
|
22
|
+
else if (response.payment.status === 'succeeded') {
|
|
23
|
+
setIsLoading(false);
|
|
24
|
+
const successResponse = {
|
|
25
|
+
...response,
|
|
26
|
+
order: response.order || response.payment.order,
|
|
27
|
+
};
|
|
28
|
+
if (hookOptionsRef.current?.onPaymentCompleted) {
|
|
29
|
+
await hookOptionsRef.current.onPaymentCompleted(response.payment, {
|
|
30
|
+
isRedirectReturn: false,
|
|
31
|
+
order: successResponse.order,
|
|
32
|
+
checkoutSessionId,
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
options.onSuccess?.(successResponse);
|
|
36
|
+
options.onPaymentSuccess?.(successResponse);
|
|
37
|
+
}
|
|
38
|
+
else {
|
|
39
|
+
startPolling(response.payment?.id, {
|
|
40
|
+
onRequireAction: (payment) => {
|
|
41
|
+
void handlePaymentAction(payment, options);
|
|
42
|
+
},
|
|
43
|
+
onSuccess: async (payment) => {
|
|
44
|
+
setIsLoading(false);
|
|
45
|
+
const successResponse = {
|
|
46
|
+
paymentId: payment.id,
|
|
47
|
+
payment,
|
|
48
|
+
order: payment.order,
|
|
49
|
+
};
|
|
50
|
+
if (hookOptionsRef.current?.onPaymentCompleted) {
|
|
51
|
+
await hookOptionsRef.current.onPaymentCompleted(payment, {
|
|
52
|
+
isRedirectReturn: false,
|
|
53
|
+
order: successResponse.order,
|
|
54
|
+
checkoutSessionId,
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
options.onSuccess?.(successResponse);
|
|
58
|
+
options.onPaymentSuccess?.(successResponse);
|
|
59
|
+
},
|
|
60
|
+
onFailure: async (errorMsg) => {
|
|
61
|
+
setError(errorMsg);
|
|
62
|
+
setIsLoading(false);
|
|
63
|
+
if (hookOptionsRef.current?.onPaymentFailed) {
|
|
64
|
+
await hookOptionsRef.current.onPaymentFailed(errorMsg, {
|
|
65
|
+
isRedirectReturn: false,
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
options.onFailure?.(errorMsg);
|
|
69
|
+
options.onPaymentFailed?.({
|
|
70
|
+
code: 'PAYMENT_FAILED',
|
|
71
|
+
message: errorMsg,
|
|
72
|
+
payment: response.payment,
|
|
73
|
+
});
|
|
74
|
+
},
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
return response;
|
|
78
|
+
}
|
|
79
|
+
catch (_error) {
|
|
80
|
+
const errorMsg = _error instanceof Error ? _error.message : 'Payment failed';
|
|
81
|
+
setError(errorMsg);
|
|
82
|
+
setIsLoading(false);
|
|
83
|
+
options.onFailure?.(errorMsg);
|
|
84
|
+
options.onPaymentFailed?.({
|
|
85
|
+
code: 'PAYMENT_PROCESSING_ERROR',
|
|
86
|
+
message: errorMsg,
|
|
87
|
+
});
|
|
88
|
+
throw _error;
|
|
89
|
+
}
|
|
90
|
+
}, [paymentsResource, handlePaymentAction, startPolling, setIsLoading, setError, setCurrentPaymentId, hookOptionsRef]);
|
|
91
|
+
// Process APM payment (Alternative Payment Methods)
|
|
92
|
+
const processApmPayment = useCallback(async (checkoutSessionId, options) => {
|
|
93
|
+
setIsLoading(true);
|
|
94
|
+
setError(null);
|
|
95
|
+
try {
|
|
96
|
+
console.log('[processApmPayment] Processing APM payment directly:', options);
|
|
97
|
+
// For APM payments, pass empty paymentInstrumentId and let backend handle it with processorId/paymentMethod
|
|
98
|
+
return await processPaymentDirect(checkoutSessionId, '', undefined, options);
|
|
99
|
+
}
|
|
100
|
+
catch (_error) {
|
|
101
|
+
setIsLoading(false);
|
|
102
|
+
const errorMsg = _error instanceof Error ? _error.message : 'APM payment failed';
|
|
103
|
+
setError(errorMsg);
|
|
104
|
+
options.onFailure?.(errorMsg);
|
|
105
|
+
options.onPaymentFailed?.({
|
|
106
|
+
code: 'APM_PAYMENT_ERROR',
|
|
107
|
+
message: errorMsg,
|
|
108
|
+
});
|
|
109
|
+
throw _error;
|
|
110
|
+
}
|
|
111
|
+
}, [processPaymentDirect, setIsLoading, setError]);
|
|
112
|
+
// Process card payment
|
|
113
|
+
const processCardPayment = useCallback(async (checkoutSessionId, cardData, options = {}) => {
|
|
114
|
+
setIsLoading(true);
|
|
115
|
+
setError(null);
|
|
116
|
+
try {
|
|
117
|
+
// Create payment instrument and handle 3DS for card payments
|
|
118
|
+
const paymentInstrument = await createCardPaymentInstrument(cardData);
|
|
119
|
+
const shouldCreateThreedsSession = storeConfig?.computed?.threedsEnabled;
|
|
120
|
+
let threedsSessionId;
|
|
121
|
+
if (shouldCreateThreedsSession) {
|
|
122
|
+
try {
|
|
123
|
+
const threedsSession = await createSession(paymentInstrument, {
|
|
124
|
+
provider: options.threedsProvider || 'basis_theory',
|
|
125
|
+
});
|
|
126
|
+
threedsSessionId = threedsSession.id;
|
|
127
|
+
}
|
|
128
|
+
catch (_error) {
|
|
129
|
+
// Continue without 3DS session
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
return await processPaymentDirect(checkoutSessionId, paymentInstrument.id, threedsSessionId, options);
|
|
133
|
+
}
|
|
134
|
+
catch (_error) {
|
|
135
|
+
setIsLoading(false);
|
|
136
|
+
const errorMsg = _error instanceof Error ? _error.message : 'Payment failed';
|
|
137
|
+
setError(errorMsg);
|
|
138
|
+
options.onFailure?.(errorMsg);
|
|
139
|
+
options.onPaymentFailed?.({
|
|
140
|
+
code: 'CARD_PAYMENT_ERROR',
|
|
141
|
+
message: errorMsg,
|
|
142
|
+
});
|
|
143
|
+
throw _error;
|
|
144
|
+
}
|
|
145
|
+
}, [createCardPaymentInstrument, createSession, processPaymentDirect, storeConfig, setIsLoading, setError]);
|
|
146
|
+
// Process Apple Pay payment
|
|
147
|
+
const processApplePayPayment = useCallback(async (checkoutSessionId, applePayToken, options = {}) => {
|
|
148
|
+
setIsLoading(true);
|
|
149
|
+
setError(null);
|
|
150
|
+
try {
|
|
151
|
+
const paymentInstrument = await createApplePayPaymentInstrument(applePayToken);
|
|
152
|
+
return await processPaymentDirect(checkoutSessionId, paymentInstrument.id, undefined, options);
|
|
153
|
+
}
|
|
154
|
+
catch (_error) {
|
|
155
|
+
setIsLoading(false);
|
|
156
|
+
const errorMsg = _error instanceof Error ? _error.message : 'Apple Pay payment failed';
|
|
157
|
+
setError(errorMsg);
|
|
158
|
+
options.onFailure?.(errorMsg);
|
|
159
|
+
options.onPaymentFailed?.({
|
|
160
|
+
code: 'APPLE_PAY_ERROR',
|
|
161
|
+
message: errorMsg,
|
|
162
|
+
});
|
|
163
|
+
throw _error;
|
|
164
|
+
}
|
|
165
|
+
}, [createApplePayPaymentInstrument, processPaymentDirect, setIsLoading, setError]);
|
|
166
|
+
// Process Google Pay payment
|
|
167
|
+
const processGooglePayPayment = useCallback(async (checkoutSessionId, googlePayToken, options = {}) => {
|
|
168
|
+
setIsLoading(true);
|
|
169
|
+
setError(null);
|
|
170
|
+
try {
|
|
171
|
+
const paymentInstrument = await createGooglePayPaymentInstrument(googlePayToken);
|
|
172
|
+
return await processPaymentDirect(checkoutSessionId, paymentInstrument.id, undefined, options);
|
|
173
|
+
}
|
|
174
|
+
catch (_error) {
|
|
175
|
+
setIsLoading(false);
|
|
176
|
+
const errorMsg = _error instanceof Error ? _error.message : 'Google Pay payment failed';
|
|
177
|
+
setError(errorMsg);
|
|
178
|
+
options.onFailure?.(errorMsg);
|
|
179
|
+
options.onPaymentFailed?.({
|
|
180
|
+
code: 'GOOGLE_PAY_ERROR',
|
|
181
|
+
message: errorMsg,
|
|
182
|
+
});
|
|
183
|
+
throw _error;
|
|
184
|
+
}
|
|
185
|
+
}, [createGooglePayPaymentInstrument, processPaymentDirect, setIsLoading, setError]);
|
|
186
|
+
// Process payment with existing instrument
|
|
187
|
+
const processPaymentWithInstrument = useCallback(async (checkoutSessionId, paymentInstrumentId, options = {}) => {
|
|
188
|
+
setIsLoading(true);
|
|
189
|
+
setError(null);
|
|
190
|
+
try {
|
|
191
|
+
return await processPaymentDirect(checkoutSessionId, paymentInstrumentId, undefined, options);
|
|
192
|
+
}
|
|
193
|
+
catch (_error) {
|
|
194
|
+
setIsLoading(false);
|
|
195
|
+
const errorMsg = _error instanceof Error ? _error.message : 'Payment failed';
|
|
196
|
+
setError(errorMsg);
|
|
197
|
+
options.onFailure?.(errorMsg);
|
|
198
|
+
options.onPaymentFailed?.({
|
|
199
|
+
code: 'PAYMENT_INSTRUMENT_ERROR',
|
|
200
|
+
message: errorMsg,
|
|
201
|
+
});
|
|
202
|
+
throw _error;
|
|
203
|
+
}
|
|
204
|
+
}, [processPaymentDirect, setIsLoading, setError]);
|
|
205
|
+
return {
|
|
206
|
+
processCardPayment,
|
|
207
|
+
processApplePayPayment,
|
|
208
|
+
processGooglePayPayment,
|
|
209
|
+
processPaymentWithInstrument,
|
|
210
|
+
processApmPayment,
|
|
211
|
+
};
|
|
212
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { PaymentsResource } from '../../../core/resources/payments';
|
|
2
|
+
import type { UsePaymentOptions } from '../usePaymentQuery';
|
|
3
|
+
interface UseAirwallex3dsReturnParams {
|
|
4
|
+
paymentsResource: PaymentsResource;
|
|
5
|
+
startPolling: any;
|
|
6
|
+
handlePaymentAction: any;
|
|
7
|
+
setIsLoading: (loading: boolean) => void;
|
|
8
|
+
setError: (error: string | null) => void;
|
|
9
|
+
setCurrentPaymentId: (id: string | null) => void;
|
|
10
|
+
hookOptionsRef: React.MutableRefObject<UsePaymentOptions | undefined>;
|
|
11
|
+
redirectReturnProcessedRef: React.MutableRefObject<boolean>;
|
|
12
|
+
}
|
|
13
|
+
export declare function useAirwallex3dsReturn({ paymentsResource, startPolling, handlePaymentAction, setIsLoading, setError, setCurrentPaymentId, hookOptionsRef, redirectReturnProcessedRef, }: UseAirwallex3dsReturnParams): void;
|
|
14
|
+
export {};
|
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Hook for handling Airwallex 3DS redirect returns
|
|
3
|
+
*/
|
|
4
|
+
import { useEffect } from 'react';
|
|
5
|
+
export function useAirwallex3dsReturn({ paymentsResource, startPolling, handlePaymentAction, setIsLoading, setError, setCurrentPaymentId, hookOptionsRef, redirectReturnProcessedRef, }) {
|
|
6
|
+
useEffect(() => {
|
|
7
|
+
if (typeof window === 'undefined')
|
|
8
|
+
return;
|
|
9
|
+
if (redirectReturnProcessedRef.current)
|
|
10
|
+
return;
|
|
11
|
+
const urlParams = new URLSearchParams(window.location.search);
|
|
12
|
+
const paymentIdFromUrl = urlParams.get('paymentId');
|
|
13
|
+
const paymentIntentId = urlParams.get('payment_intent_id');
|
|
14
|
+
const succeeded = urlParams.get('succeeded');
|
|
15
|
+
const processorType = urlParams.get('processorType');
|
|
16
|
+
// Check for Airwallex 3DS return
|
|
17
|
+
const hasAirwallexParams = paymentIntentId || succeeded !== null;
|
|
18
|
+
if (!hasAirwallexParams || processorType !== 'airwallex' || !paymentIdFromUrl) {
|
|
19
|
+
return;
|
|
20
|
+
}
|
|
21
|
+
console.log('✅ [usePayment] Airwallex 3DS return detected!', {
|
|
22
|
+
paymentIntentId,
|
|
23
|
+
succeeded,
|
|
24
|
+
paymentId: paymentIdFromUrl,
|
|
25
|
+
});
|
|
26
|
+
redirectReturnProcessedRef.current = true;
|
|
27
|
+
const handleAirwallex3dsReturn = async () => {
|
|
28
|
+
setIsLoading(true);
|
|
29
|
+
setCurrentPaymentId(paymentIdFromUrl);
|
|
30
|
+
if (succeeded === 'true') {
|
|
31
|
+
try {
|
|
32
|
+
// Update 3DS session status
|
|
33
|
+
await paymentsResource.updateThreedsStatus({
|
|
34
|
+
paymentId: paymentIdFromUrl,
|
|
35
|
+
status: 'succeeded',
|
|
36
|
+
paymentIntentId: paymentIntentId || '',
|
|
37
|
+
});
|
|
38
|
+
console.log('✅ [usePayment] Airwallex 3DS status updated successfully');
|
|
39
|
+
// Clean up URL parameters
|
|
40
|
+
const cleanParams = new URLSearchParams(window.location.search);
|
|
41
|
+
const airwallexParams = ['payment_intent_id', 'succeeded', 'processorType', 'paymentId', 'paymentAction', 'paymentActionStatus', 'error_code', 'error_message', 'mode'];
|
|
42
|
+
airwallexParams.forEach(param => cleanParams.delete(param));
|
|
43
|
+
const newUrl = cleanParams.toString()
|
|
44
|
+
? `${window.location.pathname}?${cleanParams.toString()}`
|
|
45
|
+
: window.location.pathname;
|
|
46
|
+
window.history.replaceState({}, document.title, newUrl);
|
|
47
|
+
// Retrieve payment to trigger server-side check with Airwallex and finalize
|
|
48
|
+
console.log('🔄 [usePayment] Retrieving payment to finalize after Airwallex 3DS...');
|
|
49
|
+
const retrieveResult = await paymentsResource.retrievePayment(paymentIdFromUrl);
|
|
50
|
+
console.log('📊 [usePayment] Retrieve result:', retrieveResult);
|
|
51
|
+
const retrieveStatus = retrieveResult?.retrieveResult?.status || retrieveResult?.status;
|
|
52
|
+
if (retrieveResult?.retrieveResult?.success && retrieveStatus === 'succeeded') {
|
|
53
|
+
const payment = await paymentsResource.getPaymentStatus(paymentIdFromUrl);
|
|
54
|
+
console.log('✅ [usePayment] Payment succeeded after Airwallex 3DS!', payment);
|
|
55
|
+
setIsLoading(false);
|
|
56
|
+
if (hookOptionsRef.current?.onPaymentCompleted) {
|
|
57
|
+
try {
|
|
58
|
+
await hookOptionsRef.current.onPaymentCompleted(payment, {
|
|
59
|
+
isRedirectReturn: true,
|
|
60
|
+
order: payment.order,
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
catch (error) {
|
|
64
|
+
console.error('❌ [usePayment] Error in onPaymentCompleted callback:', error);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
else if (retrieveStatus === 'declined' || retrieveStatus === 'error') {
|
|
69
|
+
const errorMsg = retrieveResult?.retrieveResult?.message || retrieveResult?.message || 'Payment failed';
|
|
70
|
+
console.error('❌ [usePayment] Payment failed after Airwallex 3DS:', errorMsg);
|
|
71
|
+
setError(errorMsg);
|
|
72
|
+
setIsLoading(false);
|
|
73
|
+
if (hookOptionsRef.current?.onPaymentFailed) {
|
|
74
|
+
try {
|
|
75
|
+
await hookOptionsRef.current.onPaymentFailed(errorMsg, {
|
|
76
|
+
isRedirectReturn: true,
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
catch (error) {
|
|
80
|
+
console.error('❌ [usePayment] Error in onPaymentFailed callback:', error);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
else {
|
|
85
|
+
const payment = await paymentsResource.getPaymentStatus(paymentIdFromUrl);
|
|
86
|
+
console.log('📊 [usePayment] Payment status after retrieve:', payment);
|
|
87
|
+
if (payment.status === 'declined' || payment.status === 'failed') {
|
|
88
|
+
const errorMsg = payment.error?.message || payment.error?.processorMessage || 'Payment declined';
|
|
89
|
+
console.error('❌ [usePayment] Payment declined after Airwallex 3DS:', errorMsg);
|
|
90
|
+
setError(errorMsg);
|
|
91
|
+
setIsLoading(false);
|
|
92
|
+
if (hookOptionsRef.current?.onPaymentFailed) {
|
|
93
|
+
try {
|
|
94
|
+
await hookOptionsRef.current.onPaymentFailed(errorMsg, {
|
|
95
|
+
isRedirectReturn: true,
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
catch (error) {
|
|
99
|
+
console.error('❌ [usePayment] Error in onPaymentFailed callback:', error);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
else if (payment.requireAction !== 'none' && payment.requireActionData && !payment.requireActionData.processed) {
|
|
104
|
+
console.log('⚠️ [usePayment] Payment requires new action after Airwallex 3DS', payment);
|
|
105
|
+
void handlePaymentAction(payment, {});
|
|
106
|
+
}
|
|
107
|
+
else if (payment.status === 'succeeded' || (payment.status === 'pending' && payment.subStatus === 'authorized')) {
|
|
108
|
+
console.log('✅ [usePayment] Payment succeeded after Airwallex 3DS!', payment);
|
|
109
|
+
setIsLoading(false);
|
|
110
|
+
if (hookOptionsRef.current?.onPaymentCompleted) {
|
|
111
|
+
try {
|
|
112
|
+
await hookOptionsRef.current.onPaymentCompleted(payment, {
|
|
113
|
+
isRedirectReturn: true,
|
|
114
|
+
order: payment.order,
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
catch (error) {
|
|
118
|
+
console.error('❌ [usePayment] Error in onPaymentCompleted callback:', error);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
else {
|
|
123
|
+
console.log('⏳ [usePayment] Payment still pending, starting polling...');
|
|
124
|
+
startPolling(paymentIdFromUrl, {
|
|
125
|
+
onRequireAction: (polledPayment) => {
|
|
126
|
+
console.log('⚠️ [usePayment] Payment requires new action after Airwallex 3DS', polledPayment);
|
|
127
|
+
void handlePaymentAction(polledPayment, {});
|
|
128
|
+
},
|
|
129
|
+
onSuccess: async (polledPayment) => {
|
|
130
|
+
console.log('✅ [usePayment] Payment succeeded after Airwallex 3DS polling!', polledPayment);
|
|
131
|
+
setIsLoading(false);
|
|
132
|
+
if (hookOptionsRef.current?.onPaymentCompleted) {
|
|
133
|
+
try {
|
|
134
|
+
await hookOptionsRef.current.onPaymentCompleted(polledPayment, {
|
|
135
|
+
isRedirectReturn: true,
|
|
136
|
+
order: polledPayment.order,
|
|
137
|
+
});
|
|
138
|
+
}
|
|
139
|
+
catch (error) {
|
|
140
|
+
console.error('❌ [usePayment] Error in onPaymentCompleted callback:', error);
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
},
|
|
144
|
+
onFailure: async (errorMsg) => {
|
|
145
|
+
console.error('❌ [usePayment] Payment failed after Airwallex 3DS polling:', errorMsg);
|
|
146
|
+
setError(errorMsg);
|
|
147
|
+
setIsLoading(false);
|
|
148
|
+
if (hookOptionsRef.current?.onPaymentFailed) {
|
|
149
|
+
try {
|
|
150
|
+
await hookOptionsRef.current.onPaymentFailed(errorMsg, {
|
|
151
|
+
isRedirectReturn: true,
|
|
152
|
+
});
|
|
153
|
+
}
|
|
154
|
+
catch (error) {
|
|
155
|
+
console.error('❌ [usePayment] Error in onPaymentFailed callback:', error);
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
},
|
|
159
|
+
});
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
catch (error) {
|
|
164
|
+
console.error('❌ [usePayment] Failed to update Airwallex 3DS status:', error);
|
|
165
|
+
setError('Failed to process 3DS authentication');
|
|
166
|
+
setIsLoading(false);
|
|
167
|
+
if (hookOptionsRef.current?.onPaymentFailed) {
|
|
168
|
+
try {
|
|
169
|
+
await hookOptionsRef.current.onPaymentFailed('Failed to process 3DS authentication', {
|
|
170
|
+
isRedirectReturn: true,
|
|
171
|
+
});
|
|
172
|
+
}
|
|
173
|
+
catch (cbError) {
|
|
174
|
+
console.error('❌ [usePayment] Error in onPaymentFailed callback:', cbError);
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
else {
|
|
180
|
+
// 3DS authentication failed
|
|
181
|
+
console.error('❌ [usePayment] Airwallex 3DS authentication failed');
|
|
182
|
+
const errorMsg = urlParams.get('error_message') || 'Authentication failed';
|
|
183
|
+
setError(errorMsg);
|
|
184
|
+
setIsLoading(false);
|
|
185
|
+
// Clean up URL parameters
|
|
186
|
+
const cleanParams = new URLSearchParams(window.location.search);
|
|
187
|
+
const airwallexParams = ['payment_intent_id', 'succeeded', 'processorType', 'paymentId', 'paymentAction', 'paymentActionStatus', 'error_code', 'error_message', 'mode'];
|
|
188
|
+
airwallexParams.forEach(param => cleanParams.delete(param));
|
|
189
|
+
const newUrl = cleanParams.toString()
|
|
190
|
+
? `${window.location.pathname}?${cleanParams.toString()}`
|
|
191
|
+
: window.location.pathname;
|
|
192
|
+
window.history.replaceState({}, document.title, newUrl);
|
|
193
|
+
if (hookOptionsRef.current?.onPaymentFailed) {
|
|
194
|
+
try {
|
|
195
|
+
await hookOptionsRef.current.onPaymentFailed(errorMsg, {
|
|
196
|
+
isRedirectReturn: true,
|
|
197
|
+
});
|
|
198
|
+
}
|
|
199
|
+
catch (error) {
|
|
200
|
+
console.error('❌ [usePayment] Error in onPaymentFailed callback:', error);
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
};
|
|
205
|
+
void handleAirwallex3dsReturn();
|
|
206
|
+
}, [paymentsResource, startPolling, handlePaymentAction, setIsLoading, setError, setCurrentPaymentId, hookOptionsRef, redirectReturnProcessedRef]);
|
|
207
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { UsePaymentOptions } from '../usePaymentQuery';
|
|
2
|
+
interface UseGenericPaymentReturnParams {
|
|
3
|
+
startPolling: any;
|
|
4
|
+
handlePaymentAction: any;
|
|
5
|
+
setIsLoading: (loading: boolean) => void;
|
|
6
|
+
setError: (error: string | null) => void;
|
|
7
|
+
setCurrentPaymentId: (id: string | null) => void;
|
|
8
|
+
hookOptionsRef: React.MutableRefObject<UsePaymentOptions | undefined>;
|
|
9
|
+
redirectReturnProcessedRef: React.MutableRefObject<boolean>;
|
|
10
|
+
}
|
|
11
|
+
export declare function useGenericPaymentReturn({ startPolling, handlePaymentAction, setIsLoading, setError, setCurrentPaymentId, hookOptionsRef, redirectReturnProcessedRef, }: UseGenericPaymentReturnParams): void;
|
|
12
|
+
export {};
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Hook for handling generic payment redirect returns
|
|
3
|
+
*/
|
|
4
|
+
import { useEffect } from 'react';
|
|
5
|
+
export function useGenericPaymentReturn({ startPolling, handlePaymentAction, setIsLoading, setError, setCurrentPaymentId, hookOptionsRef, redirectReturnProcessedRef, }) {
|
|
6
|
+
useEffect(() => {
|
|
7
|
+
if (typeof window === 'undefined')
|
|
8
|
+
return;
|
|
9
|
+
if (redirectReturnProcessedRef.current)
|
|
10
|
+
return;
|
|
11
|
+
const urlParams = new URLSearchParams(window.location.search);
|
|
12
|
+
const paymentAction = urlParams.get('paymentAction');
|
|
13
|
+
const paymentActionStatus = urlParams.get('paymentActionStatus');
|
|
14
|
+
const paymentIdFromUrl = urlParams.get('paymentId');
|
|
15
|
+
const paymentMode = urlParams.get('mode');
|
|
16
|
+
// Skip if in retrieve mode
|
|
17
|
+
if (paymentMode === 'retrieve') {
|
|
18
|
+
console.log('⏭️ [usePayment] Skipping - retrieve mode detected (handled by usePaymentRetrieve)');
|
|
19
|
+
return;
|
|
20
|
+
}
|
|
21
|
+
// Check if returning from a payment redirect
|
|
22
|
+
if (paymentAction !== 'requireAction' || paymentActionStatus !== 'completed' || !paymentIdFromUrl) {
|
|
23
|
+
console.log('⏭️ [usePayment] No payment redirect detected - normal page load');
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
console.log('✅ [usePayment] Payment redirect return detected! Starting auto-polling...', {
|
|
27
|
+
paymentId: paymentIdFromUrl,
|
|
28
|
+
});
|
|
29
|
+
redirectReturnProcessedRef.current = true;
|
|
30
|
+
setIsLoading(true);
|
|
31
|
+
setCurrentPaymentId(paymentIdFromUrl);
|
|
32
|
+
// Start polling for the payment status
|
|
33
|
+
startPolling(paymentIdFromUrl, {
|
|
34
|
+
onRequireAction: (payment) => {
|
|
35
|
+
console.log('⚠️ [usePayment] Payment requires new action', payment);
|
|
36
|
+
void handlePaymentAction(payment, {});
|
|
37
|
+
},
|
|
38
|
+
onSuccess: async (payment) => {
|
|
39
|
+
console.log('✅ [usePayment] Payment succeeded after redirect!', {
|
|
40
|
+
paymentId: payment.id,
|
|
41
|
+
status: payment.status,
|
|
42
|
+
hasOrder: !!payment.order,
|
|
43
|
+
});
|
|
44
|
+
setIsLoading(false);
|
|
45
|
+
// Clean up ONLY payment-related query parameters (preserve funnel/checkout params)
|
|
46
|
+
const urlParams = new URLSearchParams(window.location.search);
|
|
47
|
+
const paymentParams = ['paymentAction', 'paymentActionStatus', 'paymentId', 'payment_intent', 'payment_intent_client_secret', 'source_type', 'redirect_status'];
|
|
48
|
+
paymentParams.forEach(param => urlParams.delete(param));
|
|
49
|
+
const newUrl = urlParams.toString()
|
|
50
|
+
? `${window.location.pathname}?${urlParams.toString()}`
|
|
51
|
+
: window.location.pathname;
|
|
52
|
+
window.history.replaceState({}, document.title, newUrl);
|
|
53
|
+
console.log('🧹 [usePayment] Payment URL parameters cleaned up (preserved funnel/checkout params)');
|
|
54
|
+
// Call hook-level onPaymentCompleted callback (if provided)
|
|
55
|
+
if (hookOptionsRef.current?.onPaymentCompleted) {
|
|
56
|
+
console.log('📞 [usePayment] Calling onPaymentCompleted callback...', {
|
|
57
|
+
isRedirectReturn: true,
|
|
58
|
+
});
|
|
59
|
+
try {
|
|
60
|
+
await hookOptionsRef.current.onPaymentCompleted(payment, {
|
|
61
|
+
isRedirectReturn: true,
|
|
62
|
+
order: payment.order,
|
|
63
|
+
});
|
|
64
|
+
console.log('✅ [usePayment] onPaymentCompleted callback completed successfully');
|
|
65
|
+
}
|
|
66
|
+
catch (error) {
|
|
67
|
+
console.error('❌ [usePayment] Error in onPaymentCompleted callback:', error);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
else {
|
|
71
|
+
console.warn('⚠️ [usePayment] No onPaymentCompleted callback provided');
|
|
72
|
+
}
|
|
73
|
+
},
|
|
74
|
+
onFailure: async (errorMsg) => {
|
|
75
|
+
console.error('❌ [usePayment] Payment failed after redirect:', errorMsg);
|
|
76
|
+
setError(errorMsg);
|
|
77
|
+
setIsLoading(false);
|
|
78
|
+
// Clean up ONLY payment-related query parameters (preserve funnel/checkout params)
|
|
79
|
+
const urlParams = new URLSearchParams(window.location.search);
|
|
80
|
+
const paymentParams = ['paymentAction', 'paymentActionStatus', 'paymentId', 'payment_intent', 'payment_intent_client_secret', 'source_type', 'redirect_status'];
|
|
81
|
+
paymentParams.forEach(param => urlParams.delete(param));
|
|
82
|
+
const newUrl = urlParams.toString()
|
|
83
|
+
? `${window.location.pathname}?${urlParams.toString()}`
|
|
84
|
+
: window.location.pathname;
|
|
85
|
+
window.history.replaceState({}, document.title, newUrl);
|
|
86
|
+
// Call hook-level onPaymentFailed callback (if provided)
|
|
87
|
+
if (hookOptionsRef.current?.onPaymentFailed) {
|
|
88
|
+
console.log('📞 [usePayment] Calling onPaymentFailed callback...');
|
|
89
|
+
try {
|
|
90
|
+
await hookOptionsRef.current.onPaymentFailed(errorMsg, {
|
|
91
|
+
isRedirectReturn: true,
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
catch (error) {
|
|
95
|
+
console.error('❌ [usePayment] Error in onPaymentFailed callback:', error);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
},
|
|
99
|
+
});
|
|
100
|
+
}, [startPolling, handlePaymentAction, setIsLoading, setError, setCurrentPaymentId, hookOptionsRef, redirectReturnProcessedRef]);
|
|
101
|
+
}
|
|
@@ -120,12 +120,17 @@ export function useApplePayCheckout({ checkout, onSuccess, onError, onCancel, })
|
|
|
120
120
|
setProcessingPayment(true);
|
|
121
121
|
// Tokenize payment
|
|
122
122
|
const applePayToken = await tokenizeApplePay(event);
|
|
123
|
-
// Complete Apple Pay sheet
|
|
123
|
+
// Complete Apple Pay sheet immediately after tokenization
|
|
124
|
+
// Apple Pay requires completePayment within ~30s of authorization
|
|
124
125
|
session.completePayment(window.ApplePaySession.STATUS_SUCCESS);
|
|
125
126
|
// Process payment via SDK hook
|
|
126
|
-
|
|
127
|
+
// onSuccess/redirect is gated through onPaymentSuccess callback only,
|
|
128
|
+
// so we never redirect until the backend confirms success
|
|
129
|
+
await processApplePayPayment(checkout.checkoutSession.id, applePayToken, {
|
|
127
130
|
onPaymentSuccess: (response) => {
|
|
128
|
-
|
|
131
|
+
if (onSuccess) {
|
|
132
|
+
onSuccess(response);
|
|
133
|
+
}
|
|
129
134
|
},
|
|
130
135
|
onPaymentFailed: (err) => {
|
|
131
136
|
setProcessingPayment(false);
|
|
@@ -135,14 +140,9 @@ export function useApplePayCheckout({ checkout, onSuccess, onError, onCancel, })
|
|
|
135
140
|
}
|
|
136
141
|
},
|
|
137
142
|
});
|
|
138
|
-
// Call success callback
|
|
139
|
-
if (onSuccess) {
|
|
140
|
-
onSuccess(result);
|
|
141
|
-
}
|
|
142
143
|
}
|
|
143
144
|
catch (error) {
|
|
144
145
|
console.error('Payment failed:', error);
|
|
145
|
-
session.completePayment(window.ApplePaySession.STATUS_FAILURE);
|
|
146
146
|
setProcessingPayment(false);
|
|
147
147
|
const errorMsg = error instanceof Error ? error.message : 'Payment failed';
|
|
148
148
|
setError(errorMsg);
|