@tagadapay/plugin-sdk 3.1.11 โ†’ 3.1.22

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.
Files changed (116) hide show
  1. package/README.md +1129 -1129
  2. package/build-cdn.js +231 -228
  3. package/dist/data/iso3166.d.ts +23 -33
  4. package/dist/data/iso3166.js +134 -198
  5. package/dist/data/languages.d.ts +5 -64
  6. package/dist/data/languages.js +23 -143
  7. package/dist/external-tracker.js +968 -102
  8. package/dist/external-tracker.min.js +2 -2
  9. package/dist/external-tracker.min.js.map +4 -4
  10. package/dist/react/hooks/useISOData.js +1 -1
  11. package/dist/react/hooks/usePaymentPolling.d.ts +3 -3
  12. package/dist/react/hooks/useShippingRates.d.ts +6 -0
  13. package/dist/react/hooks/useShippingRates.js +38 -0
  14. package/dist/react/providers/TagadaProvider.js +5 -5
  15. package/dist/react/services/apiService.d.ts +21 -0
  16. package/dist/react/services/apiService.js +10 -0
  17. package/dist/tagada-sdk.js +2079 -179
  18. package/dist/tagada-sdk.min.js +4 -2
  19. package/dist/tagada-sdk.min.js.map +4 -4
  20. package/dist/v2/core/client.d.ts +4 -2
  21. package/dist/v2/core/client.js +4 -3
  22. package/dist/v2/core/errors.d.ts +75 -0
  23. package/dist/v2/core/errors.js +104 -0
  24. package/dist/v2/core/funnelClient.d.ts +16 -15
  25. package/dist/v2/core/funnelClient.js +1 -1
  26. package/dist/v2/core/index.d.ts +1 -0
  27. package/dist/v2/core/index.js +2 -0
  28. package/dist/v2/core/pixelMapping.d.ts +49 -0
  29. package/dist/v2/core/pixelMapping.js +325 -0
  30. package/dist/v2/core/resources/apiClient.d.ts +2 -0
  31. package/dist/v2/core/resources/apiClient.js +52 -9
  32. package/dist/v2/core/resources/checkout.d.ts +89 -30
  33. package/dist/v2/core/resources/checkout.js +8 -0
  34. package/dist/v2/core/resources/customer.d.ts +20 -19
  35. package/dist/v2/core/resources/funnel.d.ts +17 -17
  36. package/dist/v2/core/resources/payments.d.ts +84 -13
  37. package/dist/v2/core/resources/payments.js +26 -9
  38. package/dist/v2/core/resources/shippingRates.d.ts +15 -0
  39. package/dist/v2/core/resources/shippingRates.js +11 -0
  40. package/dist/v2/core/types.d.ts +50 -12
  41. package/dist/v2/core/types.js +0 -3
  42. package/dist/v2/core/utils/checkout.d.ts +2 -2
  43. package/dist/v2/core/utils/checkout.js +7 -2
  44. package/dist/v2/core/utils/order.d.ts +11 -9
  45. package/dist/v2/core/utils/previewModeIndicator.js +101 -101
  46. package/dist/v2/index.d.ts +4 -2
  47. package/dist/v2/index.js +1 -1
  48. package/dist/v2/react/components/ApplePayButton.js +13 -4
  49. package/dist/v2/react/components/FunnelScriptInjector.js +51 -30
  50. package/dist/v2/react/components/WhopCheckout.d.ts +24 -0
  51. package/dist/v2/react/components/WhopCheckout.js +231 -0
  52. package/dist/v2/react/hooks/__examples__/FunnelContextExample.js +1 -1
  53. package/dist/v2/react/hooks/payment-actions/useAirwallexRadarAction.d.ts +14 -0
  54. package/dist/v2/react/hooks/payment-actions/useAirwallexRadarAction.js +181 -0
  55. package/dist/v2/react/hooks/payment-actions/useErrorAction.d.ts +9 -0
  56. package/dist/v2/react/hooks/payment-actions/useErrorAction.js +21 -0
  57. package/dist/v2/react/hooks/payment-actions/useFinixRadarAction.d.ts +14 -0
  58. package/dist/v2/react/hooks/payment-actions/useFinixRadarAction.js +187 -0
  59. package/dist/v2/react/hooks/payment-actions/useKessPayAction.d.ts +11 -0
  60. package/dist/v2/react/hooks/payment-actions/useKessPayAction.js +91 -0
  61. package/dist/v2/react/hooks/payment-actions/useMasterCardAction.d.ts +24 -0
  62. package/dist/v2/react/hooks/payment-actions/useMasterCardAction.js +221 -0
  63. package/dist/v2/react/hooks/payment-actions/usePaymentActionHandler.d.ts +15 -0
  64. package/dist/v2/react/hooks/payment-actions/usePaymentActionHandler.js +142 -0
  65. package/dist/v2/react/hooks/payment-actions/useProcessorAuthAction.d.ts +3 -0
  66. package/dist/v2/react/hooks/payment-actions/useProcessorAuthAction.js +13 -0
  67. package/dist/v2/react/hooks/payment-actions/useRedirectAction.d.ts +10 -0
  68. package/dist/v2/react/hooks/payment-actions/useRedirectAction.js +35 -0
  69. package/dist/v2/react/hooks/payment-actions/useStripeRadarAction.d.ts +14 -0
  70. package/dist/v2/react/hooks/payment-actions/useStripeRadarAction.js +192 -0
  71. package/dist/v2/react/hooks/payment-actions/useThreedsAuthAction.d.ts +14 -0
  72. package/dist/v2/react/hooks/payment-actions/useThreedsAuthAction.js +81 -0
  73. package/dist/v2/react/hooks/payment-actions/useTrustFlowAction.d.ts +11 -0
  74. package/dist/v2/react/hooks/payment-actions/useTrustFlowAction.js +84 -0
  75. package/dist/v2/react/hooks/payment-processing/usePaymentInstruments.d.ts +14 -0
  76. package/dist/v2/react/hooks/payment-processing/usePaymentInstruments.js +36 -0
  77. package/dist/v2/react/hooks/payment-processing/usePaymentProcessors.d.ts +31 -0
  78. package/dist/v2/react/hooks/payment-processing/usePaymentProcessors.js +212 -0
  79. package/dist/v2/react/hooks/payment-redirect/useAirwallex3dsReturn.d.ts +14 -0
  80. package/dist/v2/react/hooks/payment-redirect/useAirwallex3dsReturn.js +207 -0
  81. package/dist/v2/react/hooks/payment-redirect/useGenericPaymentReturn.d.ts +12 -0
  82. package/dist/v2/react/hooks/payment-redirect/useGenericPaymentReturn.js +101 -0
  83. package/dist/v2/react/hooks/useCheckoutQuery.d.ts +6 -0
  84. package/dist/v2/react/hooks/useCheckoutQuery.js +45 -0
  85. package/dist/v2/react/hooks/useFunnel.d.ts +1 -2
  86. package/dist/v2/react/hooks/useGeoLocation.d.ts +2 -1
  87. package/dist/v2/react/hooks/useGeoLocation.js +4 -2
  88. package/dist/v2/react/hooks/useGoogleAutocomplete.js +82 -33
  89. package/dist/v2/react/hooks/useISOData.js +1 -1
  90. package/dist/v2/react/hooks/usePaymentPolling.d.ts +3 -3
  91. package/dist/v2/react/hooks/usePaymentQuery.d.ts +18 -5
  92. package/dist/v2/react/hooks/usePaymentQuery.js +63 -1015
  93. package/dist/v2/react/hooks/usePaymentRetrieve.d.ts +3 -2
  94. package/dist/v2/react/hooks/usePaymentRetrieve.js +3 -1
  95. package/dist/v2/react/hooks/usePixelTracking.d.ts +5 -43
  96. package/dist/v2/react/hooks/usePixelTracking.js +213 -407
  97. package/dist/v2/react/hooks/useShippingRatesQuery.d.ts +6 -0
  98. package/dist/v2/react/hooks/useShippingRatesQuery.js +47 -4
  99. package/dist/v2/react/hooks/useStepConfig.d.ts +2 -8
  100. package/dist/v2/react/hooks/useStepConfig.js +1 -1
  101. package/dist/v2/react/hooks/useWhopPaymentPolling.d.ts +30 -0
  102. package/dist/v2/react/hooks/useWhopPaymentPolling.js +61 -0
  103. package/dist/v2/react/index.d.ts +7 -0
  104. package/dist/v2/react/index.js +4 -0
  105. package/dist/v2/react/providers/ExpressPaymentMethodsProvider.d.ts +2 -1
  106. package/dist/v2/react/providers/ExpressPaymentMethodsProvider.js +3 -1
  107. package/dist/v2/react/providers/TagadaProvider.js +76 -7
  108. package/dist/v2/standalone/external-tracker.d.ts +52 -46
  109. package/dist/v2/standalone/external-tracker.js +205 -98
  110. package/dist/v2/standalone/index.d.ts +22 -0
  111. package/dist/v2/standalone/index.js +125 -0
  112. package/package.json +112 -112
  113. package/dist/react/utils/__tests__/urlUtils.test.d.ts +0 -1
  114. package/dist/react/utils/__tests__/urlUtils.test.js +0 -189
  115. package/dist/v2/core/__tests__/pathRemapping.test.d.ts +0 -11
  116. package/dist/v2/core/__tests__/pathRemapping.test.js +0 -776
@@ -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
+ }
@@ -17,8 +17,14 @@ export interface UseCheckoutQueryResult {
17
17
  checkoutToken: string;
18
18
  }>;
19
19
  refresh: () => Promise<void>;
20
+ replaceSessionLineItems: (lineItems: CheckoutLineItem[]) => Promise<any>;
20
21
  updateLineItems: (lineItems: CheckoutLineItem[]) => Promise<any>;
21
22
  updateLineItemsOptimistic: (lineItems: CheckoutLineItem[]) => void;
23
+ addLineItems: (lineItems: CheckoutLineItem[]) => Promise<any>;
24
+ removeLineItems: (lineItems: {
25
+ variantId: string;
26
+ quantity?: number;
27
+ }[]) => Promise<any>;
22
28
  setItemQuantity: (variantId: string, quantity: number, priceId?: string) => Promise<any>;
23
29
  updateCustomer: (data: {
24
30
  email: string;
@@ -121,6 +121,20 @@ export function useCheckoutQuery(options = {}) {
121
121
  },
122
122
  });
123
123
  // Order bump functionality removed - use useOrderBumpQuery instead
124
+ // Replace session line items mutation (full rewrite)
125
+ const replaceSessionLineItemsMutation = useMutation({
126
+ mutationFn: ({ lineItems }) => {
127
+ if (!checkout?.checkoutSession?.id) {
128
+ throw new Error('No checkout session available');
129
+ }
130
+ return checkoutResource.replaceSessionLineItems(checkout.checkoutSession.id, lineItems);
131
+ },
132
+ onSuccess: () => {
133
+ if (checkoutToken) {
134
+ void queryClient.invalidateQueries({ queryKey: ['checkout', checkoutToken] });
135
+ }
136
+ },
137
+ });
124
138
  // Line items mutation with optimistic updates
125
139
  const lineItemsMutation = useMutation({
126
140
  mutationFn: ({ lineItems }) => {
@@ -166,6 +180,34 @@ export function useCheckoutQuery(options = {}) {
166
180
  }
167
181
  },
168
182
  });
183
+ // Add line items mutation
184
+ const addLineItemsMutation = useMutation({
185
+ mutationFn: ({ lineItems }) => {
186
+ if (!checkout?.checkoutSession?.id) {
187
+ throw new Error('No checkout session available');
188
+ }
189
+ return checkoutResource.addLineItems(checkout.checkoutSession.id, lineItems);
190
+ },
191
+ onSuccess: () => {
192
+ if (checkoutToken) {
193
+ void queryClient.invalidateQueries({ queryKey: ['checkout', checkoutToken] });
194
+ }
195
+ },
196
+ });
197
+ // Remove line items mutation
198
+ const removeLineItemsMutation = useMutation({
199
+ mutationFn: ({ lineItems }) => {
200
+ if (!checkout?.checkoutSession?.id) {
201
+ throw new Error('No checkout session available');
202
+ }
203
+ return checkoutResource.removeLineItems(checkout.checkoutSession.id, lineItems);
204
+ },
205
+ onSuccess: () => {
206
+ if (checkoutToken) {
207
+ void queryClient.invalidateQueries({ queryKey: ['checkout', checkoutToken] });
208
+ }
209
+ },
210
+ });
169
211
  // Item quantity mutation
170
212
  const quantityMutation = useMutation({
171
213
  mutationFn: ({ variantId, quantity, priceId }) => {
@@ -274,8 +316,11 @@ export function useCheckoutQuery(options = {}) {
274
316
  },
275
317
  refresh,
276
318
  // Checkout operations
319
+ replaceSessionLineItems: (lineItems) => replaceSessionLineItemsMutation.mutateAsync({ lineItems }),
277
320
  updateLineItems: (lineItems) => lineItemsMutation.mutateAsync({ lineItems }),
278
321
  updateLineItemsOptimistic: (lineItems) => lineItemsMutation.mutate({ lineItems }),
322
+ addLineItems: (lineItems) => addLineItemsMutation.mutateAsync({ lineItems }),
323
+ removeLineItems: (lineItems) => removeLineItemsMutation.mutateAsync({ lineItems }),
279
324
  setItemQuantity: (variantId, quantity, priceId) => quantityMutation.mutateAsync({ variantId, quantity, priceId }),
280
325
  updateCustomer: (data) => customerMutation.mutateAsync(data),
281
326
  updateCustomerAndSessionInfo: (data) => customerAndSessionMutation.mutateAsync(data),