@tagadapay/plugin-sdk 3.1.5 → 3.1.9
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/README.md +1129 -1129
- package/build-cdn.js +220 -113
- package/dist/external-tracker.js +1225 -558
- package/dist/external-tracker.min.js +2 -2
- package/dist/external-tracker.min.js.map +4 -4
- package/dist/react/hooks/useApplePay.js +25 -36
- package/dist/react/hooks/usePaymentPolling.d.ts +9 -3
- package/dist/react/providers/TagadaProvider.js +5 -5
- package/dist/react/utils/money.d.ts +4 -3
- package/dist/react/utils/money.js +39 -6
- package/dist/react/utils/trackingUtils.js +1 -0
- package/dist/tagada-sdk.js +10142 -0
- package/dist/tagada-sdk.min.js +43 -0
- package/dist/tagada-sdk.min.js.map +7 -0
- package/dist/v2/core/client.js +34 -2
- package/dist/v2/core/config/environment.js +9 -2
- package/dist/v2/core/funnelClient.d.ts +180 -2
- package/dist/v2/core/funnelClient.js +289 -6
- package/dist/v2/core/resources/apiClient.js +1 -1
- package/dist/v2/core/resources/checkout.d.ts +68 -0
- package/dist/v2/core/resources/funnel.d.ts +25 -0
- package/dist/v2/core/resources/payments.d.ts +70 -3
- package/dist/v2/core/resources/payments.js +72 -7
- package/dist/v2/core/utils/index.d.ts +1 -0
- package/dist/v2/core/utils/index.js +2 -0
- package/dist/v2/core/utils/pluginConfig.d.ts +8 -0
- package/dist/v2/core/utils/pluginConfig.js +68 -5
- package/dist/v2/core/utils/previewMode.d.ts +7 -0
- package/dist/v2/core/utils/previewMode.js +72 -14
- package/dist/v2/core/utils/previewModeIndicator.d.ts +19 -0
- package/dist/v2/core/utils/previewModeIndicator.js +414 -0
- package/dist/v2/core/utils/tokenStorage.d.ts +4 -0
- package/dist/v2/core/utils/tokenStorage.js +15 -1
- package/dist/v2/index.d.ts +9 -3
- package/dist/v2/index.js +8 -3
- package/dist/v2/react/components/ApplePayButton.d.ts +22 -123
- package/dist/v2/react/components/ApplePayButton.js +247 -317
- package/dist/v2/react/components/FunnelScriptInjector.d.ts +3 -1
- package/dist/v2/react/components/FunnelScriptInjector.js +255 -162
- package/dist/v2/react/components/GooglePayButton.d.ts +2 -0
- package/dist/v2/react/components/GooglePayButton.js +80 -64
- package/dist/v2/react/components/PreviewModeIndicator.d.ts +46 -0
- package/dist/v2/react/components/PreviewModeIndicator.js +113 -0
- package/dist/v2/react/hooks/useApplePayCheckout.d.ts +16 -0
- package/dist/v2/react/hooks/useApplePayCheckout.js +193 -0
- package/dist/v2/react/hooks/useFunnel.d.ts +48 -6
- package/dist/v2/react/hooks/useFunnel.js +25 -5
- package/dist/v2/react/hooks/useGoogleAutocomplete.d.ts +10 -0
- package/dist/v2/react/hooks/useGoogleAutocomplete.js +48 -0
- package/dist/v2/react/hooks/useGooglePayCheckout.d.ts +21 -0
- package/dist/v2/react/hooks/useGooglePayCheckout.js +198 -0
- package/dist/v2/react/hooks/usePaymentPolling.d.ts +15 -3
- package/dist/v2/react/hooks/usePaymentPolling.js +31 -9
- package/dist/v2/react/hooks/usePaymentQuery.d.ts +34 -2
- package/dist/v2/react/hooks/usePaymentQuery.js +731 -7
- package/dist/v2/react/hooks/usePaymentRetrieve.d.ts +26 -0
- package/dist/v2/react/hooks/usePaymentRetrieve.js +175 -0
- package/dist/v2/react/hooks/usePixelTracking.d.ts +56 -0
- package/dist/v2/react/hooks/usePixelTracking.js +508 -0
- package/dist/v2/react/hooks/useStepConfig.d.ts +64 -0
- package/dist/v2/react/hooks/useStepConfig.js +53 -0
- package/dist/v2/react/index.d.ts +15 -5
- package/dist/v2/react/index.js +8 -2
- package/dist/v2/react/providers/ExpressPaymentMethodsProvider.d.ts +1 -0
- package/dist/v2/react/providers/ExpressPaymentMethodsProvider.js +41 -13
- package/dist/v2/react/providers/TagadaProvider.js +24 -23
- package/dist/v2/standalone/external-tracker.d.ts +2 -0
- package/dist/v2/standalone/external-tracker.js +6 -3
- package/package.json +112 -112
- package/dist/v2/react/hooks/useApplePay.d.ts +0 -16
- package/dist/v2/react/hooks/useApplePay.js +0 -247
|
@@ -1,102 +1,70 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
/**
|
|
3
3
|
* Apple Pay Button Component for v2 Architecture
|
|
4
|
-
*
|
|
4
|
+
* Clean rewrite based on working CMS implementation
|
|
5
5
|
*/
|
|
6
|
-
import { useCallback, useEffect,
|
|
7
|
-
import { PaymentsResource } from '../../core/resources/payments';
|
|
8
|
-
import { OrdersResource } from '../../core/resources/orders';
|
|
9
|
-
import { useExpressPaymentMethods } from '../hooks/useExpressPaymentMethods';
|
|
10
|
-
import { getGlobalApiClient } from '../hooks/useApiQuery';
|
|
6
|
+
import { useCallback, useEffect, useMemo, useState } from 'react';
|
|
11
7
|
import { getBasisTheoryApiKey } from '../../../react/config/payment';
|
|
12
|
-
import {
|
|
13
|
-
|
|
14
|
-
|
|
8
|
+
import { getCurrencyInfo, minorUnitsToMajorUnits } from '../../../react/utils/money';
|
|
9
|
+
import { useExpressPaymentMethods } from '../hooks/useExpressPaymentMethods';
|
|
10
|
+
import { usePaymentQuery } from '../hooks/usePaymentQuery';
|
|
11
|
+
import { useShippingRatesQuery } from '../hooks/useShippingRatesQuery';
|
|
12
|
+
// Helper function to convert Apple Pay contact to Address (matches CMS)
|
|
13
|
+
const applePayContactToAddress = (contact) => {
|
|
14
|
+
return {
|
|
15
|
+
address1: contact?.addressLines?.[0] || '',
|
|
16
|
+
address2: contact?.addressLines?.[1] || '',
|
|
17
|
+
lastName: contact?.familyName || '',
|
|
18
|
+
firstName: contact?.givenName || '',
|
|
19
|
+
city: contact?.locality || '',
|
|
20
|
+
state: contact?.administrativeArea || '',
|
|
21
|
+
country: contact?.countryCode || '',
|
|
22
|
+
postal: contact?.postalCode || '',
|
|
23
|
+
phone: contact?.phoneNumber || '',
|
|
24
|
+
email: contact?.emailAddress || '',
|
|
25
|
+
};
|
|
26
|
+
};
|
|
27
|
+
export const ApplePayButton = ({ checkout, onSuccess, onError, onCancel }) => {
|
|
28
|
+
const { applePayPaymentMethod, reComputeOrderSummary, shippingMethods, lineItems, handleAddExpressId, updateCheckoutSessionValues, updateCustomerEmail, setError: setContextError, } = useExpressPaymentMethods();
|
|
15
29
|
const [processingPayment, setProcessingPayment] = useState(false);
|
|
16
30
|
const [isApplePayAvailable, setIsApplePayAvailable] = useState(false);
|
|
17
|
-
|
|
18
|
-
// Get Basis Theory API key from config (auto-detects environment)
|
|
31
|
+
// Get Basis Theory API key
|
|
19
32
|
const basistheoryPublicKey = useMemo(() => getBasisTheoryApiKey(), []);
|
|
20
|
-
//
|
|
21
|
-
const
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
}
|
|
25
|
-
catch (error) {
|
|
26
|
-
throw new Error('Failed to initialize payments resource: ' +
|
|
27
|
-
(error instanceof Error ? error.message : 'Unknown error'));
|
|
28
|
-
}
|
|
29
|
-
}, []);
|
|
30
|
-
const ordersResource = useMemo(() => {
|
|
31
|
-
try {
|
|
32
|
-
return new OrdersResource(getGlobalApiClient());
|
|
33
|
-
}
|
|
34
|
-
catch (error) {
|
|
35
|
-
throw new Error('Failed to initialize orders resource: ' + (error instanceof Error ? error.message : 'Unknown error'));
|
|
36
|
-
}
|
|
37
|
-
}, []);
|
|
33
|
+
// Use payment hook for proper payment processing
|
|
34
|
+
const { processApplePayPayment } = usePaymentQuery();
|
|
35
|
+
// Use shipping rates hook for selecting shipping rates
|
|
36
|
+
const { selectRate } = useShippingRatesQuery({ checkout });
|
|
38
37
|
// Don't render if no Apple Pay payment method is enabled
|
|
39
38
|
if (!applePayPaymentMethod) {
|
|
40
39
|
return null;
|
|
41
40
|
}
|
|
42
|
-
// Check Apple Pay availability
|
|
41
|
+
// Check Apple Pay availability (matches CMS pattern - useApplePayAvailable hook)
|
|
43
42
|
useEffect(() => {
|
|
44
|
-
const
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
setIsApplePayAvailable(canMakePaymentsWithActiveCard);
|
|
55
|
-
if (canMakePaymentsWithActiveCard) {
|
|
56
|
-
handleAddExpressId('apple_pay');
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
|
-
else {
|
|
60
|
-
setIsApplePayAvailable(false);
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
catch (error) {
|
|
64
|
-
console.error('Error checking Apple Pay availability:', error);
|
|
65
|
-
setIsApplePayAvailable(false);
|
|
66
|
-
}
|
|
67
|
-
};
|
|
68
|
-
checkApplePayAvailability();
|
|
69
|
-
}, [applePayPaymentMethod, handleAddExpressId]);
|
|
70
|
-
// Helper to convert minor units to currency string for Apple Pay
|
|
71
|
-
// Uses SDK's minorUnitsToMajorUnits which properly handles different currency decimal places
|
|
43
|
+
const addExpress = () => handleAddExpressId('apple_pay');
|
|
44
|
+
if (window?.ApplePaySession && ApplePaySession.canMakePayments()) {
|
|
45
|
+
setIsApplePayAvailable(true);
|
|
46
|
+
addExpress();
|
|
47
|
+
}
|
|
48
|
+
else {
|
|
49
|
+
setIsApplePayAvailable(false);
|
|
50
|
+
}
|
|
51
|
+
}, [handleAddExpressId]);
|
|
52
|
+
// Helper to convert minor units to currency string
|
|
72
53
|
const minorUnitsToCurrencyString = useCallback((amountMinor, currency) => {
|
|
73
|
-
|
|
74
|
-
|
|
54
|
+
// Fail safely - don't allow invalid data to become '0.00'
|
|
55
|
+
if (amountMinor === undefined || amountMinor === null || !currency) {
|
|
56
|
+
throw new Error(`Invalid currency data: amountMinor=${amountMinor}, currency=${currency}`);
|
|
57
|
+
}
|
|
75
58
|
const currencyInfo = getCurrencyInfo(currency);
|
|
76
59
|
if (!currencyInfo) {
|
|
77
|
-
// Fallback to simple division if currency info not found
|
|
78
60
|
console.warn(`Currency info not found for ${currency}, using fallback`);
|
|
79
61
|
return (amountMinor / 100).toFixed(2);
|
|
80
62
|
}
|
|
63
|
+
// 0 is a valid amount (e.g., free items)
|
|
81
64
|
const majorUnits = minorUnitsToMajorUnits(amountMinor, currency);
|
|
82
65
|
return majorUnits.toFixed(currencyInfo.ISOdigits);
|
|
83
66
|
}, []);
|
|
84
|
-
//
|
|
85
|
-
const applePayContactToAddress = useCallback((contact) => {
|
|
86
|
-
return {
|
|
87
|
-
address1: contact.addressLines?.[0] || '',
|
|
88
|
-
address2: contact.addressLines?.[1] || '',
|
|
89
|
-
lastName: contact.familyName || '',
|
|
90
|
-
firstName: contact.givenName || '',
|
|
91
|
-
city: contact.locality || '',
|
|
92
|
-
state: contact.administrativeArea || '',
|
|
93
|
-
country: contact.countryCode || '',
|
|
94
|
-
postal: contact.postalCode || '',
|
|
95
|
-
phone: contact.phoneNumber || '',
|
|
96
|
-
email: contact.emailAddress || '',
|
|
97
|
-
};
|
|
98
|
-
}, []);
|
|
99
|
-
// Validate merchant with Basis Theory
|
|
67
|
+
// Validate merchant with Basis Theory (matches CMS)
|
|
100
68
|
const validateMerchant = useCallback(async () => {
|
|
101
69
|
try {
|
|
102
70
|
const response = await fetch('https://api.basistheory.com/apple-pay/session', {
|
|
@@ -111,18 +79,18 @@ export const ApplePayButton = ({ className = '', disabled = false, onSuccess, on
|
|
|
111
79
|
}),
|
|
112
80
|
});
|
|
113
81
|
if (!response.ok) {
|
|
114
|
-
throw new Error(`
|
|
82
|
+
throw new Error(`HTTP error! Status: ${response.status}`);
|
|
115
83
|
}
|
|
116
84
|
const merchantSession = await response.json();
|
|
117
85
|
return merchantSession;
|
|
118
86
|
}
|
|
119
|
-
catch (
|
|
120
|
-
console.error('Merchant validation failed:',
|
|
121
|
-
throw
|
|
87
|
+
catch (err) {
|
|
88
|
+
console.error('[ApplePay] Merchant validation failed:', err);
|
|
89
|
+
throw err;
|
|
122
90
|
}
|
|
123
|
-
}, [checkout.checkoutSession.store?.name]);
|
|
124
|
-
// Tokenize Apple Pay payment with Basis Theory
|
|
125
|
-
const tokenizeApplePay = useCallback(async (
|
|
91
|
+
}, [basistheoryPublicKey, checkout.checkoutSession.store?.name]);
|
|
92
|
+
// Tokenize Apple Pay payment with Basis Theory (matches CMS)
|
|
93
|
+
const tokenizeApplePay = useCallback(async (event) => {
|
|
126
94
|
try {
|
|
127
95
|
const response = await fetch('https://api.basistheory.com/apple-pay', {
|
|
128
96
|
method: 'POST',
|
|
@@ -131,278 +99,240 @@ export const ApplePayButton = ({ className = '', disabled = false, onSuccess, on
|
|
|
131
99
|
'BT-API-KEY': basistheoryPublicKey,
|
|
132
100
|
},
|
|
133
101
|
body: JSON.stringify({
|
|
134
|
-
apple_payment_data: token,
|
|
102
|
+
apple_payment_data: event.payment.token,
|
|
135
103
|
}),
|
|
136
104
|
});
|
|
137
105
|
if (!response.ok) {
|
|
138
|
-
throw new Error(`
|
|
106
|
+
throw new Error(`HTTP error! Status: ${response.status}`);
|
|
139
107
|
}
|
|
140
108
|
const result = await response.json();
|
|
141
109
|
return result.apple_pay; // Basis Theory returns the Apple Pay token in the apple_pay field
|
|
142
110
|
}
|
|
143
111
|
catch (error) {
|
|
144
|
-
console.error('
|
|
112
|
+
console.error('[ApplePay] Tokenizing Apple Pay failed:', error);
|
|
145
113
|
throw error;
|
|
146
114
|
}
|
|
147
|
-
}, []);
|
|
148
|
-
// Process
|
|
149
|
-
const
|
|
115
|
+
}, [basistheoryPublicKey]);
|
|
116
|
+
// Process payment using the hook (handles order creation, instrument creation, payment processing, and polling)
|
|
117
|
+
const handleApplePayPayment = useCallback(async (payToken) => {
|
|
118
|
+
setProcessingPayment(true);
|
|
150
119
|
try {
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
}
|
|
163
|
-
// Update customer email
|
|
164
|
-
if (shippingContact?.emailAddress) {
|
|
165
|
-
await updateCustomerEmail({
|
|
166
|
-
data: {
|
|
167
|
-
email: shippingContact.emailAddress,
|
|
168
|
-
},
|
|
169
|
-
});
|
|
170
|
-
}
|
|
171
|
-
// 1. Tokenize with Basis Theory
|
|
172
|
-
const applePayToken = await tokenizeApplePay(token);
|
|
173
|
-
// 2. Create payment instrument from the tokenized Apple Pay payment
|
|
174
|
-
const paymentInstrumentData = {
|
|
175
|
-
type: 'apple_pay',
|
|
176
|
-
token: applePayToken.id,
|
|
177
|
-
dpanType: applePayToken.type,
|
|
178
|
-
card: {
|
|
179
|
-
bin: applePayToken.card.bin,
|
|
180
|
-
last4: applePayToken.card.last4,
|
|
181
|
-
expirationMonth: applePayToken.card.expiration_month,
|
|
182
|
-
expirationYear: applePayToken.card.expiration_year,
|
|
183
|
-
brand: applePayToken.card.brand,
|
|
120
|
+
// Use the hook's method which handles everything properly
|
|
121
|
+
const result = await processApplePayPayment(checkout.checkoutSession.id, payToken, {
|
|
122
|
+
// Callbacks are handled by the hook, but we can intercept for custom behavior
|
|
123
|
+
onPaymentSuccess: (response) => {
|
|
124
|
+
// Keep processingPayment true for loading state during navigation
|
|
125
|
+
// The hook already returns the response, we just need to return it
|
|
126
|
+
},
|
|
127
|
+
onPaymentFailed: (error) => {
|
|
128
|
+
console.error('[ApplePay] Payment failed:', error);
|
|
129
|
+
setProcessingPayment(false);
|
|
130
|
+
throw new Error(error.message);
|
|
184
131
|
},
|
|
185
|
-
};
|
|
186
|
-
const paymentInstrument = await paymentsResource.createPaymentInstrument(paymentInstrumentData);
|
|
187
|
-
if (!paymentInstrument?.id) {
|
|
188
|
-
throw new Error('Failed to create payment instrument');
|
|
189
|
-
}
|
|
190
|
-
// 3. Create order from checkout session
|
|
191
|
-
const orderResponse = await ordersResource.createOrder(checkout.checkoutSession.id);
|
|
192
|
-
if (!orderResponse?.success || !orderResponse?.order?.id) {
|
|
193
|
-
throw new Error('Failed to create order');
|
|
194
|
-
}
|
|
195
|
-
// 4. Process payment
|
|
196
|
-
const paymentResult = await paymentsResource.processPaymentDirect(checkout.checkoutSession.id, paymentInstrument.id, undefined, {
|
|
197
|
-
initiatedBy: 'customer',
|
|
198
|
-
source: 'checkout',
|
|
199
132
|
});
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
}
|
|
203
|
-
return paymentResult;
|
|
133
|
+
// Return the result (contains payment and order)
|
|
134
|
+
return result;
|
|
204
135
|
}
|
|
205
136
|
catch (error) {
|
|
206
|
-
console.error('
|
|
207
|
-
const errorMessage = error instanceof Error ? error.message : 'Apple Pay payment failed';
|
|
208
|
-
setApplePayError(errorMessage);
|
|
209
|
-
setContextError(errorMessage);
|
|
210
|
-
if (onError) {
|
|
211
|
-
onError(errorMessage);
|
|
212
|
-
}
|
|
213
|
-
throw error;
|
|
214
|
-
}
|
|
215
|
-
finally {
|
|
137
|
+
console.error('[ApplePay] Payment processing failed:', error);
|
|
216
138
|
setProcessingPayment(false);
|
|
139
|
+
throw error;
|
|
217
140
|
}
|
|
218
|
-
}, [
|
|
219
|
-
|
|
220
|
-
applePayContactToAddress,
|
|
221
|
-
updateCheckoutSessionValues,
|
|
222
|
-
updateCustomerEmail,
|
|
223
|
-
tokenizeApplePay,
|
|
224
|
-
paymentsResource,
|
|
225
|
-
ordersResource,
|
|
226
|
-
onSuccess,
|
|
227
|
-
onError,
|
|
228
|
-
setContextError,
|
|
229
|
-
]);
|
|
230
|
-
// Handle Apple Pay button click
|
|
141
|
+
}, [processApplePayPayment, checkout.checkoutSession.id]);
|
|
142
|
+
// Handle Apple Pay button click (matches CMS implementation)
|
|
231
143
|
const handleApplePayClick = useCallback(() => {
|
|
232
144
|
if (!isApplePayAvailable || processingPayment || !checkout.summary) {
|
|
233
145
|
return;
|
|
234
146
|
}
|
|
235
|
-
const
|
|
147
|
+
const storeName = checkout.checkoutSession.store?.name || 'Store';
|
|
148
|
+
const total = {
|
|
149
|
+
label: storeName,
|
|
150
|
+
amount: minorUnitsToCurrencyString(checkout.summary.totalAdjustedAmount, checkout.summary.currency),
|
|
151
|
+
type: 'final',
|
|
152
|
+
};
|
|
153
|
+
const request = {
|
|
236
154
|
countryCode: applePayPaymentMethod?.metadata?.country || 'US',
|
|
237
155
|
currencyCode: checkout.summary.currency,
|
|
238
156
|
supportedNetworks: ['visa', 'masterCard', 'amex', 'discover'],
|
|
239
157
|
merchantCapabilities: ['supports3DS'],
|
|
240
|
-
total
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
lineItems: lineItems.map((item) => ({
|
|
246
|
-
label: item.label,
|
|
247
|
-
amount: item.amount,
|
|
248
|
-
type: 'final',
|
|
249
|
-
})),
|
|
250
|
-
shippingType: 'shipping',
|
|
251
|
-
shippingMethods: shippingMethods.map((method) => ({
|
|
252
|
-
label: method.label,
|
|
253
|
-
amount: method.amount,
|
|
254
|
-
detail: method.detail,
|
|
255
|
-
identifier: method.identifier,
|
|
256
|
-
})),
|
|
257
|
-
requiredBillingContactFields: ['postalAddress', 'name', 'phone', 'email'],
|
|
258
|
-
requiredShippingContactFields: ['postalAddress', 'name', 'phone', 'email'],
|
|
158
|
+
total,
|
|
159
|
+
shippingMethods,
|
|
160
|
+
lineItems,
|
|
161
|
+
requiredShippingContactFields: ['name', 'phone', 'email', 'postalAddress'],
|
|
162
|
+
requiredBillingContactFields: ['postalAddress'],
|
|
259
163
|
};
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
}
|
|
313
|
-
|
|
314
|
-
});
|
|
315
|
-
}
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
164
|
+
try {
|
|
165
|
+
const session = new ApplePaySession(3, request);
|
|
166
|
+
// Merchant validation (matches CMS)
|
|
167
|
+
session.onvalidatemerchant = (event) => {
|
|
168
|
+
void (async () => {
|
|
169
|
+
try {
|
|
170
|
+
const merchantSession = await validateMerchant();
|
|
171
|
+
session.completeMerchantValidation(merchantSession);
|
|
172
|
+
}
|
|
173
|
+
catch (error) {
|
|
174
|
+
console.error('[ApplePay] Merchant validation failed:', error);
|
|
175
|
+
session.abort();
|
|
176
|
+
}
|
|
177
|
+
})();
|
|
178
|
+
};
|
|
179
|
+
// Payment authorized (matches CMS)
|
|
180
|
+
session.onpaymentauthorized = (event) => {
|
|
181
|
+
void (async () => {
|
|
182
|
+
try {
|
|
183
|
+
const shippingContact = event.payment.shippingContact;
|
|
184
|
+
const billingContact = event.payment.billingContact;
|
|
185
|
+
const shippingAddress = applePayContactToAddress(shippingContact);
|
|
186
|
+
const billingAddress = applePayContactToAddress(billingContact);
|
|
187
|
+
await updateCheckoutSessionValues({
|
|
188
|
+
data: {
|
|
189
|
+
shippingAddress,
|
|
190
|
+
billingAddress,
|
|
191
|
+
},
|
|
192
|
+
});
|
|
193
|
+
if (shippingContact.emailAddress) {
|
|
194
|
+
await updateCustomerEmail({
|
|
195
|
+
data: {
|
|
196
|
+
email: shippingContact.emailAddress,
|
|
197
|
+
},
|
|
198
|
+
});
|
|
199
|
+
}
|
|
200
|
+
const applePayToken = await tokenizeApplePay(event);
|
|
201
|
+
session.completePayment(ApplePaySession.STATUS_SUCCESS);
|
|
202
|
+
const result = await handleApplePayPayment(applePayToken);
|
|
203
|
+
// Call success callback (triggers navigation)
|
|
204
|
+
// Note: processingPayment stays true until component unmounts during navigation
|
|
205
|
+
if (onSuccess) {
|
|
206
|
+
onSuccess(result);
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
catch (error) {
|
|
210
|
+
console.error('[ApplePay] Payment processing failed:', error);
|
|
211
|
+
session.completePayment(ApplePaySession.STATUS_FAILURE);
|
|
212
|
+
const errorMessage = 'Payment Failed';
|
|
213
|
+
setContextError(errorMessage);
|
|
214
|
+
if (onError) {
|
|
215
|
+
onError(errorMessage);
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
})();
|
|
219
|
+
};
|
|
220
|
+
// Shipping method selected (matches CMS - abort if recompute fails)
|
|
221
|
+
session.onshippingmethodselected = (event) => {
|
|
222
|
+
void (async () => {
|
|
223
|
+
try {
|
|
224
|
+
// Update shipping method via client
|
|
225
|
+
await selectRate(event.shippingMethod.identifier);
|
|
226
|
+
const newOrderSummary = await reComputeOrderSummary();
|
|
227
|
+
if (!newOrderSummary) {
|
|
228
|
+
console.error('[ApplePay] No order summary returned, aborting session');
|
|
229
|
+
session.abort();
|
|
230
|
+
return;
|
|
231
|
+
}
|
|
232
|
+
const { lineItems: newLineItems, total: newTotal } = newOrderSummary;
|
|
233
|
+
// Ensure type: 'final' is present for Apple Pay (matches CMS)
|
|
234
|
+
const formattedTotal = {
|
|
235
|
+
...newTotal,
|
|
236
|
+
type: 'final',
|
|
237
|
+
};
|
|
238
|
+
const formattedLineItems = newLineItems.map((item) => ({
|
|
239
|
+
...item,
|
|
240
|
+
type: 'final',
|
|
241
|
+
}));
|
|
242
|
+
session.completeShippingMethodSelection(ApplePaySession.STATUS_SUCCESS, formattedTotal, formattedLineItems);
|
|
243
|
+
}
|
|
244
|
+
catch (error) {
|
|
245
|
+
console.error('[ApplePay] Shipping method selection failed:', error);
|
|
246
|
+
session.abort();
|
|
247
|
+
}
|
|
248
|
+
})();
|
|
249
|
+
};
|
|
250
|
+
// Shipping contact selected (matches CMS - abort if recompute fails)
|
|
251
|
+
session.onshippingcontactselected = (event) => {
|
|
252
|
+
void (async () => {
|
|
253
|
+
const shippingContact = event.shippingContact;
|
|
254
|
+
try {
|
|
255
|
+
await updateCheckoutSessionValues({
|
|
256
|
+
data: {
|
|
257
|
+
shippingAddress: applePayContactToAddress(shippingContact),
|
|
258
|
+
},
|
|
259
|
+
});
|
|
260
|
+
const newOrderSummary = await reComputeOrderSummary();
|
|
261
|
+
if (!newOrderSummary) {
|
|
262
|
+
console.error('[ApplePay] No order summary returned, aborting session');
|
|
263
|
+
session.abort();
|
|
264
|
+
setContextError('Payment Failed');
|
|
265
|
+
return;
|
|
266
|
+
}
|
|
267
|
+
const { lineItems: newLineItems, total: newTotal, shippingMethods: newShippingMethods, } = newOrderSummary;
|
|
268
|
+
// Ensure type: 'final' is present for Apple Pay (matches CMS)
|
|
269
|
+
const formattedTotal = {
|
|
270
|
+
...newTotal,
|
|
271
|
+
type: 'final',
|
|
272
|
+
};
|
|
273
|
+
const formattedLineItems = newLineItems.map((item) => ({
|
|
274
|
+
...item,
|
|
275
|
+
type: 'final',
|
|
276
|
+
}));
|
|
277
|
+
session.completeShippingContactSelection(ApplePaySession.STATUS_SUCCESS, newShippingMethods, formattedTotal, formattedLineItems);
|
|
278
|
+
}
|
|
279
|
+
catch (error) {
|
|
280
|
+
console.error('[ApplePay] Shipping contact selection failed:', error);
|
|
281
|
+
session.abort();
|
|
282
|
+
}
|
|
283
|
+
})();
|
|
284
|
+
};
|
|
285
|
+
session.onerror = (event) => {
|
|
286
|
+
console.error('[ApplePay] Session Error:', event);
|
|
287
|
+
};
|
|
288
|
+
session.oncancel = () => {
|
|
289
|
+
if (onCancel) {
|
|
290
|
+
onCancel();
|
|
332
291
|
}
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
},
|
|
340
|
-
newLineItems: newOrderSummary?.lineItems.map((item) => ({
|
|
341
|
-
label: item.label,
|
|
342
|
-
amount: item.amount,
|
|
343
|
-
type: 'final',
|
|
344
|
-
})) || [],
|
|
345
|
-
};
|
|
346
|
-
session.completeShippingMethodSelection(update);
|
|
347
|
-
}
|
|
348
|
-
catch (error) {
|
|
349
|
-
console.error('Shipping method selection failed:', error);
|
|
350
|
-
session.completeShippingMethodSelection({});
|
|
351
|
-
}
|
|
352
|
-
};
|
|
353
|
-
session.onpaymentauthorized = async (event) => {
|
|
354
|
-
try {
|
|
355
|
-
await processApplePayPayment(event.payment.token, event.payment.billingContact, event.payment.shippingContact);
|
|
356
|
-
session.completePayment({
|
|
357
|
-
status: window.ApplePaySession.STATUS_SUCCESS,
|
|
358
|
-
});
|
|
359
|
-
}
|
|
360
|
-
catch (error) {
|
|
361
|
-
console.error('Payment authorization failed:', error);
|
|
362
|
-
session.completePayment({
|
|
363
|
-
status: window.ApplePaySession.STATUS_FAILURE,
|
|
364
|
-
});
|
|
365
|
-
}
|
|
366
|
-
};
|
|
367
|
-
session.oncancel = () => {
|
|
368
|
-
if (onCancel) {
|
|
369
|
-
onCancel();
|
|
370
|
-
}
|
|
371
|
-
};
|
|
372
|
-
session.begin();
|
|
292
|
+
};
|
|
293
|
+
session.begin();
|
|
294
|
+
}
|
|
295
|
+
catch (error) {
|
|
296
|
+
console.error('[ApplePay] Failed to start Apple Pay session:', error);
|
|
297
|
+
}
|
|
373
298
|
}, [
|
|
374
299
|
isApplePayAvailable,
|
|
375
300
|
processingPayment,
|
|
376
301
|
checkout,
|
|
377
|
-
storeName,
|
|
378
|
-
lineItems,
|
|
379
302
|
shippingMethods,
|
|
303
|
+
lineItems,
|
|
304
|
+
applePayPaymentMethod,
|
|
305
|
+
minorUnitsToCurrencyString,
|
|
380
306
|
validateMerchant,
|
|
381
|
-
|
|
307
|
+
tokenizeApplePay,
|
|
308
|
+
processApplePayPayment,
|
|
382
309
|
updateCheckoutSessionValues,
|
|
310
|
+
updateCustomerEmail,
|
|
383
311
|
reComputeOrderSummary,
|
|
384
|
-
processApplePayPayment,
|
|
385
|
-
onCancel,
|
|
386
|
-
onError,
|
|
387
312
|
setContextError,
|
|
388
|
-
|
|
389
|
-
|
|
313
|
+
selectRate,
|
|
314
|
+
onSuccess,
|
|
315
|
+
onError,
|
|
316
|
+
onCancel,
|
|
390
317
|
]);
|
|
391
|
-
// Button size classes
|
|
392
|
-
const sizeClasses = {
|
|
393
|
-
sm: 'h-8 px-3 text-sm',
|
|
394
|
-
md: 'h-10 px-4 text-base',
|
|
395
|
-
lg: 'h-12 px-6 text-lg',
|
|
396
|
-
};
|
|
397
|
-
// Button variant classes
|
|
398
|
-
const variantClasses = {
|
|
399
|
-
default: 'bg-black text-white hover:bg-black/90',
|
|
400
|
-
outline: 'border border-black bg-white text-black hover:bg-black hover:text-white',
|
|
401
|
-
ghost: 'text-black hover:bg-black/10',
|
|
402
|
-
};
|
|
403
318
|
if (!isApplePayAvailable) {
|
|
404
319
|
return null;
|
|
405
320
|
}
|
|
406
|
-
return (_jsxs("
|
|
321
|
+
return (_jsxs("button", { onClick: handleApplePayClick, disabled: processingPayment, style: {
|
|
322
|
+
backgroundColor: '#000',
|
|
323
|
+
color: '#fff',
|
|
324
|
+
border: 'none',
|
|
325
|
+
borderRadius: '4px',
|
|
326
|
+
padding: '12px',
|
|
327
|
+
width: '100%',
|
|
328
|
+
fontSize: '16px',
|
|
329
|
+
fontWeight: '500',
|
|
330
|
+
cursor: processingPayment ? 'not-allowed' : 'pointer',
|
|
331
|
+
opacity: processingPayment ? 0.6 : 1,
|
|
332
|
+
display: 'flex',
|
|
333
|
+
alignItems: 'center',
|
|
334
|
+
justifyContent: 'center',
|
|
335
|
+
gap: '8px',
|
|
336
|
+
}, type: "button", children: [_jsx("svg", { width: "24", height: "24", viewBox: "0 0 24 24", fill: "currentColor", children: _jsx("path", { d: "M18.71 19.5c-.83 1.24-1.71 2.45-3.05 2.47-1.34.03-1.77-.79-3.29-.79-1.53 0-2 .77-3.27.82-1.31.05-2.3-1.32-3.14-2.53C4.25 17 2.94 12.45 4.7 9.39c.87-1.52 2.43-2.48 4.12-2.51 1.28-.02 2.5.87 3.29.87.78 0 2.26-1.07 3.81-.91.65.03 2.47.26 3.64 1.98-.09.06-2.17 1.28-2.15 3.81.03 3.02 2.65 4.03 2.68 4.04-.03.07-.42 1.44-1.38 2.83M13 3.5c.73-.83 1.94-1.46 2.94-1.5.13 1.17-.34 2.35-1.04 3.19-.69.85-1.83 1.51-2.95 1.42-.15-1.15.41-2.35 1.05-3.11z" }) }), processingPayment ? 'Processing...' : 'Pay with Apple Pay'] }));
|
|
407
337
|
};
|
|
408
338
|
export default ApplePayButton;
|