@tagadapay/plugin-sdk 2.6.4 → 2.6.8

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 (55) hide show
  1. package/README.md +39 -0
  2. package/dist/react/components/GooglePayButton.d.ts +12 -0
  3. package/dist/react/components/GooglePayButton.js +340 -0
  4. package/dist/react/components/index.d.ts +3 -2
  5. package/dist/react/components/index.js +3 -2
  6. package/dist/react/hooks/useApplePay.js +38 -10
  7. package/dist/react/hooks/useGoogleAutocomplete.d.ts +2 -0
  8. package/dist/react/hooks/useGoogleAutocomplete.js +18 -2
  9. package/dist/react/hooks/useGooglePay.d.ts +22 -0
  10. package/dist/react/hooks/useGooglePay.js +32 -0
  11. package/dist/react/index.d.ts +2 -1
  12. package/dist/react/index.js +3 -1
  13. package/dist/react/types/apple-pay.d.ts +0 -25
  14. package/dist/v2/core/googleAutocomplete.d.ts +2 -0
  15. package/dist/v2/core/googleAutocomplete.js +21 -8
  16. package/dist/v2/core/resources/checkout.d.ts +70 -2
  17. package/dist/v2/core/resources/discounts.d.ts +53 -0
  18. package/dist/v2/core/resources/discounts.js +29 -0
  19. package/dist/v2/core/resources/expressPaymentMethods.d.ts +56 -0
  20. package/dist/v2/core/resources/expressPaymentMethods.js +27 -0
  21. package/dist/v2/core/resources/index.d.ts +8 -4
  22. package/dist/v2/core/resources/index.js +8 -4
  23. package/dist/v2/core/resources/shippingRates.d.ts +36 -0
  24. package/dist/v2/core/resources/shippingRates.js +23 -0
  25. package/dist/v2/core/resources/storeConfig.d.ts +25 -0
  26. package/dist/v2/core/resources/storeConfig.js +16 -0
  27. package/dist/v2/core/resources/vipOffers.d.ts +37 -0
  28. package/dist/v2/core/resources/vipOffers.js +27 -0
  29. package/dist/v2/core/utils/order.d.ts +1 -0
  30. package/dist/v2/core/utils/pluginConfig.d.ts +6 -6
  31. package/dist/v2/index.d.ts +13 -9
  32. package/dist/v2/index.js +3 -3
  33. package/dist/v2/react/components/ApplePayButton.d.ts +141 -0
  34. package/dist/v2/react/components/ApplePayButton.js +320 -0
  35. package/dist/v2/react/components/GooglePayButton.d.ts +19 -0
  36. package/dist/v2/react/components/GooglePayButton.js +355 -0
  37. package/dist/v2/react/hooks/useApiQuery.d.ts +5 -1
  38. package/dist/v2/react/hooks/useApiQuery.js +5 -1
  39. package/dist/v2/react/hooks/useDiscountsQuery.d.ts +30 -0
  40. package/dist/v2/react/hooks/useDiscountsQuery.js +175 -0
  41. package/dist/v2/react/hooks/useExpressPaymentMethods.d.ts +12 -0
  42. package/dist/v2/react/hooks/useExpressPaymentMethods.js +17 -0
  43. package/dist/v2/react/hooks/useGoogleAutocomplete.d.ts +2 -0
  44. package/dist/v2/react/hooks/useGoogleAutocomplete.js +18 -2
  45. package/dist/v2/react/hooks/useShippingRatesQuery.d.ts +22 -0
  46. package/dist/v2/react/hooks/useShippingRatesQuery.js +134 -0
  47. package/dist/v2/react/hooks/useStoreConfigQuery.d.ts +17 -0
  48. package/dist/v2/react/hooks/useStoreConfigQuery.js +52 -0
  49. package/dist/v2/react/hooks/useVipOffersQuery.d.ts +72 -0
  50. package/dist/v2/react/hooks/useVipOffersQuery.js +140 -0
  51. package/dist/v2/react/index.d.ts +32 -17
  52. package/dist/v2/react/index.js +19 -10
  53. package/dist/v2/react/providers/ExpressPaymentMethodsProvider.d.ts +59 -0
  54. package/dist/v2/react/providers/ExpressPaymentMethodsProvider.js +165 -0
  55. package/package.json +1 -1
package/README.md CHANGED
@@ -15,6 +15,7 @@ A comprehensive React SDK for building plugins on the TagadaPay platform. Create
15
15
  - **[useCheckout (V2)](#usecheckout)** - TanStack Query-based checkout management
16
16
  - **[useOffers (V2)](#useoffers)** - Dynamic pricing with automatic caching
17
17
  - **[useProducts (V2)](#useproducts)** - Product data management
18
+ - **[useStoreConfig (V2)](#usestoreconfig)** - Store configuration with automatic caching
18
19
  - **[usePayment (V2)](#usepayment)** - Payment processing with 3DS support
19
20
  - **[V2 Utility Functions](#v2-utility-functions--advanced-features)** - Enhanced utilities and TanStack Query integration
20
21
 
@@ -708,6 +709,44 @@ const {
708
709
  } = useProducts({ storeId, enabled });
709
710
  ```
710
711
 
712
+ #### useStoreConfig()
713
+
714
+ Hook for fetching store configuration with automatic caching.
715
+
716
+ ```typescript
717
+ const {
718
+ // Query data
719
+ storeConfig, // StoreConfig | undefined
720
+ isLoading, // boolean
721
+ error, // Error | null
722
+ isSuccess, // boolean
723
+
724
+ // Actions
725
+ refetch, // () => Promise<void>
726
+ } = useStoreConfig({ storeId, enabled });
727
+ ```
728
+
729
+ **Example:**
730
+
731
+ ```tsx
732
+ import { useStoreConfig } from '@tagadapay/plugin-sdk/v2';
733
+
734
+ function StoreInfo() {
735
+ const { storeConfig, isLoading } = useStoreConfig();
736
+
737
+ if (isLoading) return <div>Loading...</div>;
738
+
739
+ return (
740
+ <div>
741
+ <h1>{storeConfig?.storeName}</h1>
742
+ <p>Currency: {storeConfig?.currency}</p>
743
+ </div>
744
+ );
745
+ }
746
+ ```
747
+
748
+ See [useStoreConfig documentation](./docs/README-useStoreConfig.md) for more details.
749
+
711
750
  #### useOrder()
712
751
 
713
752
  Hook for order management and tracking.
@@ -0,0 +1,12 @@
1
+ import React from 'react';
2
+ import { CheckoutData } from '../hooks/useCheckout';
3
+ interface GooglePayButtonProps {
4
+ className?: string;
5
+ disabled?: boolean;
6
+ onSuccess?: (payment: any) => void;
7
+ onError?: (error: string) => void;
8
+ onCancel?: () => void;
9
+ checkout: CheckoutData | null;
10
+ }
11
+ export declare const GooglePayButton: React.FC<GooglePayButtonProps>;
12
+ export default GooglePayButton;
@@ -0,0 +1,340 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import GooglePayButtonReact from '@google-pay/button-react';
3
+ import { useCallback, useEffect, useState } from 'react';
4
+ import { useExpressPaymentMethods } from '../hooks/useExpressPaymentMethods';
5
+ import { useOrderSummary } from '../hooks/useOrderSummary';
6
+ import { usePayment } from '../hooks/usePayment';
7
+ import { useShippingRates } from '../hooks/useShippingRates';
8
+ import { Button } from './Button';
9
+ const basistheoryPublicKey = process.env.NEXT_PUBLIC_BASIS_THEORY_PUBLIC_API_KEY || '';
10
+ const basistheoryTenantId = process.env.NEXT_PUBLIC_BASIS_THEORY_TENANT_ID || '0b283fa3-44a1-4535-adff-e99ad0a58a47';
11
+ export const GooglePayButton = ({ className = '', disabled = false, onSuccess, onError, onCancel, checkout, }) => {
12
+ const { googlePayPaymentMethod, reComputeOrderSummary, handleAddExpressId, updateCheckoutSessionValues, updateCustomerEmail, shippingMethods, setError: setContextError, } = useExpressPaymentMethods();
13
+ const [processingPayment, setProcessingPayment] = useState(false);
14
+ const [pendingPaymentData, setPendingPaymentData] = useState(null);
15
+ const [googlePayError, setGooglePayError] = useState(null);
16
+ const checkoutSessionId = checkout?.checkoutSession?.id;
17
+ const { processApplePayPayment: processExpressPayment } = usePayment(); // Will create processGooglePayPayment method
18
+ const { orderSummary } = useOrderSummary({ sessionId: checkoutSessionId });
19
+ const { selectRate } = useShippingRates({ checkout: checkout || undefined });
20
+ // Don't render if no Google Pay payment method is enabled
21
+ if (!googlePayPaymentMethod) {
22
+ return null;
23
+ }
24
+ // Register express payment method
25
+ useEffect(() => {
26
+ handleAddExpressId('google_pay');
27
+ }, [handleAddExpressId]);
28
+ // Convert Google Pay address to internal Address format
29
+ const googlePayAddressToAddress = useCallback((googlePayAddress) => {
30
+ return {
31
+ address1: googlePayAddress.address1 || '',
32
+ address2: googlePayAddress.address2 || '',
33
+ lastName: googlePayAddress.name?.split(' ').slice(-1)[0] || '',
34
+ firstName: googlePayAddress.name?.split(' ').slice(0, -1).join(' ') || '',
35
+ city: googlePayAddress.locality || '',
36
+ state: googlePayAddress.administrativeArea || '',
37
+ country: googlePayAddress.countryCode || '',
38
+ postal: googlePayAddress.postalCode || '',
39
+ phone: googlePayAddress.phoneNumber || '',
40
+ email: '',
41
+ };
42
+ }, []);
43
+ // Tokenize Google Pay data using Basis Theory
44
+ const tokenizeGooglePayTokenWithBasisTheory = useCallback(async (paymentData) => {
45
+ try {
46
+ const googlePayTokenString = paymentData.paymentMethodData.tokenizationData.token;
47
+ const googlePayToken = JSON.parse(googlePayTokenString);
48
+ const response = await fetch('https://api.basistheory.com/google-pay', {
49
+ method: 'POST',
50
+ headers: {
51
+ 'Content-Type': 'application/json',
52
+ 'BT-API-KEY': basistheoryPublicKey,
53
+ },
54
+ body: JSON.stringify({
55
+ google_payment_data: googlePayToken,
56
+ }),
57
+ });
58
+ if (!response.ok) {
59
+ const errorData = await response.json().catch(() => ({}));
60
+ console.error('Basis Theory API error:', errorData);
61
+ throw new Error(`HTTP error! Status: ${response.status}`);
62
+ }
63
+ const jsonResponse = await response.json();
64
+ return jsonResponse?.token_intent || jsonResponse?.google_pay;
65
+ }
66
+ catch (error) {
67
+ console.error('Error tokenizing Google Pay data:', error);
68
+ throw error;
69
+ }
70
+ }, []);
71
+ // Charge payment in backend
72
+ const chargePayment = useCallback(async (token) => {
73
+ if (!checkoutSessionId) {
74
+ throw new Error('Checkout session ID is not available');
75
+ }
76
+ setProcessingPayment(true);
77
+ try {
78
+ // Use the payment hook to process the Google Pay payment
79
+ // Note: You'll need to implement processGooglePayPayment similar to processApplePayPayment
80
+ await processExpressPayment(checkoutSessionId, token, {
81
+ onSuccess: (payment) => {
82
+ setProcessingPayment(false);
83
+ if (onSuccess) {
84
+ onSuccess(payment);
85
+ }
86
+ },
87
+ onFailure: (error) => {
88
+ setProcessingPayment(false);
89
+ const errorMessage = error || 'Payment Failed';
90
+ setGooglePayError(errorMessage);
91
+ setContextError(errorMessage);
92
+ if (onError) {
93
+ onError(errorMessage);
94
+ }
95
+ },
96
+ });
97
+ }
98
+ catch (error) {
99
+ console.error('Error charging payment:', error);
100
+ const errorMessage = error instanceof Error ? error.message : 'Payment Failed';
101
+ setProcessingPayment(false);
102
+ setGooglePayError(errorMessage);
103
+ setContextError(errorMessage);
104
+ if (onError) {
105
+ onError(errorMessage);
106
+ }
107
+ throw error;
108
+ }
109
+ }, [checkoutSessionId, processExpressPayment, onSuccess, onError, setContextError]);
110
+ // Process payment data
111
+ const onGooglePaymentData = useCallback(async (paymentData) => {
112
+ setProcessingPayment(true);
113
+ try {
114
+ // Extract billing address if available
115
+ let billingAddress;
116
+ if (paymentData.paymentMethodData.info?.billingAddress) {
117
+ billingAddress = googlePayAddressToAddress(paymentData.paymentMethodData.info.billingAddress);
118
+ }
119
+ // Extract shipping address if available
120
+ let shippingAddress;
121
+ if (paymentData.shippingAddress) {
122
+ shippingAddress = googlePayAddressToAddress(paymentData.shippingAddress);
123
+ }
124
+ // Update checkout session with addresses before processing payment
125
+ if (shippingAddress) {
126
+ await updateCheckoutSessionValues({
127
+ data: {
128
+ shippingAddress,
129
+ billingAddress: billingAddress ?? null,
130
+ },
131
+ });
132
+ }
133
+ // Update customer email if provided
134
+ if (paymentData.email) {
135
+ await updateCustomerEmail({
136
+ data: {
137
+ email: paymentData.email,
138
+ },
139
+ });
140
+ }
141
+ const payToken = await tokenizeGooglePayTokenWithBasisTheory(paymentData);
142
+ await chargePayment(payToken);
143
+ }
144
+ catch (error) {
145
+ console.error('Error processing Google Pay payment:', error);
146
+ const errorMessage = error instanceof Error ? error.message : 'Payment Failed';
147
+ setProcessingPayment(false);
148
+ setGooglePayError(errorMessage);
149
+ setContextError(errorMessage);
150
+ if (onError) {
151
+ onError(errorMessage);
152
+ }
153
+ }
154
+ }, [
155
+ googlePayAddressToAddress,
156
+ updateCheckoutSessionValues,
157
+ updateCustomerEmail,
158
+ tokenizeGooglePayTokenWithBasisTheory,
159
+ chargePayment,
160
+ onError,
161
+ setContextError,
162
+ ]);
163
+ // Effect to process payment data in background
164
+ useEffect(() => {
165
+ if (pendingPaymentData && !processingPayment) {
166
+ const processPaymentInBackground = async () => {
167
+ try {
168
+ await onGooglePaymentData(pendingPaymentData);
169
+ }
170
+ catch (error) {
171
+ console.error('Background payment processing failed:', error);
172
+ }
173
+ finally {
174
+ setPendingPaymentData(null);
175
+ }
176
+ };
177
+ void processPaymentInBackground();
178
+ }
179
+ }, [pendingPaymentData, processingPayment, onGooglePaymentData]);
180
+ // Handle payment data changes during the flow
181
+ const handleGooglePayDataChanged = useCallback((intermediatePaymentData) => {
182
+ return new Promise((resolve) => {
183
+ const processCallback = async () => {
184
+ try {
185
+ const paymentDataRequestUpdate = {};
186
+ if (intermediatePaymentData.callbackTrigger === 'SHIPPING_ADDRESS') {
187
+ const address = intermediatePaymentData.shippingAddress;
188
+ const shippingAddress = {
189
+ address1: address?.addressLines?.[0] || '',
190
+ address2: address?.addressLines?.[1] || '',
191
+ lastName: address?.name?.split(' ').slice(-1)[0] || '',
192
+ firstName: address?.name?.split(' ').slice(0, -1).join(' ') || '',
193
+ city: address?.locality || '',
194
+ state: address?.administrativeArea || '',
195
+ country: address?.countryCode || '',
196
+ postal: address?.postalCode || '',
197
+ phone: address?.phoneNumber || '',
198
+ email: '',
199
+ };
200
+ await updateCheckoutSessionValues({
201
+ data: { shippingAddress },
202
+ });
203
+ const newOrderSummary = await reComputeOrderSummary();
204
+ if (newOrderSummary) {
205
+ paymentDataRequestUpdate.newShippingOptionParameters = {
206
+ defaultSelectedOptionId: newOrderSummary.shippingMethods[0]?.identifier || '',
207
+ shippingOptions: newOrderSummary.shippingMethods.map((method) => ({
208
+ id: method.identifier,
209
+ label: method.label,
210
+ description: method.amount + ': ' + method.detail || '',
211
+ })),
212
+ };
213
+ paymentDataRequestUpdate.newTransactionInfo = {
214
+ totalPriceStatus: 'FINAL',
215
+ totalPrice: newOrderSummary.total.amount,
216
+ currencyCode: orderSummary?.currency || 'USD',
217
+ };
218
+ }
219
+ }
220
+ else if (intermediatePaymentData.callbackTrigger === 'SHIPPING_OPTION') {
221
+ // Update shipping rate
222
+ if (selectRate) {
223
+ await selectRate(intermediatePaymentData.shippingOptionData.id);
224
+ }
225
+ const newOrderSummary = await reComputeOrderSummary();
226
+ if (newOrderSummary) {
227
+ paymentDataRequestUpdate.newTransactionInfo = {
228
+ totalPriceStatus: 'FINAL',
229
+ totalPrice: newOrderSummary.total.amount,
230
+ currencyCode: orderSummary?.currency || 'USD',
231
+ };
232
+ }
233
+ }
234
+ else if (intermediatePaymentData.callbackTrigger === 'OFFER') {
235
+ console.log('OFFER callback triggered, no action needed');
236
+ }
237
+ else if (intermediatePaymentData.callbackTrigger === 'INITIALIZE') {
238
+ console.log('INITIALIZE callback triggered, no action needed');
239
+ }
240
+ resolve(paymentDataRequestUpdate);
241
+ }
242
+ catch (error) {
243
+ console.error('Error in onPaymentDataChanged:', error);
244
+ resolve({
245
+ error: {
246
+ reason: 'SHIPPING_ADDRESS_UNSERVICEABLE',
247
+ message: 'Unable to calculate shipping for this address',
248
+ intent: intermediatePaymentData.callbackTrigger,
249
+ },
250
+ });
251
+ }
252
+ };
253
+ void processCallback();
254
+ });
255
+ }, [updateCheckoutSessionValues, reComputeOrderSummary, selectRate, orderSummary]);
256
+ // Handle payment authorization
257
+ const handleGooglePayAuthorized = useCallback((paymentData) => {
258
+ setPendingPaymentData(paymentData);
259
+ return Promise.resolve({ transactionState: 'SUCCESS' });
260
+ }, []);
261
+ // Don't render if no order summary
262
+ if (!orderSummary) {
263
+ return null;
264
+ }
265
+ const minorUnitsToCurrencyString = (amountMinor, currency) => {
266
+ return (amountMinor / 100).toFixed(2);
267
+ };
268
+ const allowedCardNetworks = ['AMEX', 'DISCOVER', 'INTERAC', 'JCB', 'MASTERCARD', 'VISA'];
269
+ const allowedCardAuthMethods = ['PAN_ONLY', 'CRYPTOGRAM_3DS'];
270
+ const baseCardPaymentMethod = {
271
+ type: 'CARD',
272
+ parameters: {
273
+ allowedAuthMethods: allowedCardAuthMethods,
274
+ allowedCardNetworks: allowedCardNetworks,
275
+ billingAddressRequired: true,
276
+ billingAddressParameters: {
277
+ format: 'FULL',
278
+ phoneNumberRequired: true,
279
+ },
280
+ },
281
+ };
282
+ const tokenizationSpecification = {
283
+ type: 'PAYMENT_GATEWAY',
284
+ parameters: {
285
+ gateway: 'basistheory',
286
+ gatewayMerchantId: basistheoryTenantId,
287
+ },
288
+ };
289
+ const paymentRequest = {
290
+ apiVersion: 2,
291
+ apiVersionMinor: 0,
292
+ allowedPaymentMethods: [
293
+ {
294
+ ...baseCardPaymentMethod,
295
+ tokenizationSpecification,
296
+ },
297
+ ],
298
+ transactionInfo: {
299
+ totalPriceStatus: 'FINAL',
300
+ totalPrice: minorUnitsToCurrencyString(orderSummary.totalAdjustedAmount, orderSummary.currency),
301
+ currencyCode: orderSummary.currency,
302
+ },
303
+ merchantInfo: {
304
+ merchantName: googlePayPaymentMethod?.metadata?.merchantName ||
305
+ checkout?.checkoutSession?.store?.name ||
306
+ 'Store',
307
+ merchantId: googlePayPaymentMethod?.metadata?.sandboxed
308
+ ? '12345678901234567890'
309
+ : googlePayPaymentMethod?.metadata?.merchantId || '12345678901234567890',
310
+ },
311
+ shippingAddressRequired: true,
312
+ shippingOptionRequired: true,
313
+ emailRequired: true,
314
+ callbackIntents: ['SHIPPING_OPTION', 'SHIPPING_ADDRESS', 'PAYMENT_AUTHORIZATION'],
315
+ shippingOptionParameters: {
316
+ defaultSelectedOptionId: shippingMethods.length > 0 ? shippingMethods[0].identifier : '',
317
+ shippingOptions: shippingMethods.map((method) => ({
318
+ id: method.identifier,
319
+ label: method.label,
320
+ description: method.amount + ': ' + method.detail || '',
321
+ })),
322
+ },
323
+ };
324
+ const environment = googlePayPaymentMethod?.metadata?.sandboxed ? 'TEST' : 'PRODUCTION';
325
+ return (_jsxs("div", { className: "w-full", children: [!processingPayment ? (_jsx(GooglePayButtonReact, { environment: environment, paymentRequest: paymentRequest, onPaymentAuthorized: handleGooglePayAuthorized, onPaymentDataChanged: handleGooglePayDataChanged, onError: (error) => {
326
+ console.error('Google Pay error:', error);
327
+ const errorMessage = 'Google Pay error: ' + error.statusMessage;
328
+ setGooglePayError(errorMessage);
329
+ setContextError(errorMessage);
330
+ if (onError) {
331
+ onError(errorMessage);
332
+ }
333
+ }, onCancel: () => {
334
+ console.log('Google Pay payment cancelled');
335
+ if (onCancel) {
336
+ onCancel();
337
+ }
338
+ }, existingPaymentMethodRequired: false, buttonColor: "black", buttonType: "plain", buttonSizeMode: "fill", buttonLocale: "en", className: `m-0 h-10 w-full rounded-sm p-0 text-base text-white ${className}` })) : (_jsx(Button, { type: "button", variant: "outline", size: "lg", className: "h-10 w-full bg-black text-base text-white shadow-sm hover:bg-black/80", disabled: true, children: _jsxs("div", { className: "flex items-center justify-center gap-3", children: [_jsx("div", { className: "h-5 w-5 animate-spin rounded-full border-2 border-white border-t-transparent" }), _jsx("span", { className: "font-medium", children: "Processing..." })] }) })), googlePayError && (_jsx("div", { className: "mt-2 rounded border border-red-200 bg-red-50 p-2 text-sm text-red-600", children: googlePayError }))] }));
339
+ };
340
+ export default GooglePayButton;
@@ -1,3 +1,4 @@
1
- export { default as DebugDrawer } from './DebugDrawer';
2
- export { default as Button } from './Button';
3
1
  export { default as ApplePayButton } from './ApplePayButton';
2
+ export { default as Button } from './Button';
3
+ export { default as DebugDrawer } from './DebugDrawer';
4
+ export { default as GooglePayButton } from './GooglePayButton';
@@ -1,3 +1,4 @@
1
- export { default as DebugDrawer } from './DebugDrawer';
2
- export { default as Button } from './Button';
3
1
  export { default as ApplePayButton } from './ApplePayButton';
2
+ export { default as Button } from './Button';
3
+ export { default as DebugDrawer } from './DebugDrawer';
4
+ export { default as GooglePayButton } from './GooglePayButton';
@@ -282,7 +282,9 @@ export function useApplePay(options = {}) {
282
282
  // Tokenize the Apple Pay payment
283
283
  const applePayToken = await tokenizeApplePay(event);
284
284
  // Complete the Apple Pay session
285
- session.completePayment(window.ApplePaySession.STATUS_SUCCESS);
285
+ session.completePayment({
286
+ status: window.ApplePaySession.STATUS_SUCCESS,
287
+ });
286
288
  // Process the payment using the SDK's payment methods
287
289
  await processApplePayPayment(checkoutSessionId, applePayToken, {
288
290
  onSuccess: (payment) => {
@@ -298,7 +300,9 @@ export function useApplePay(options = {}) {
298
300
  }
299
301
  catch (error) {
300
302
  console.error('Payment processing failed:', error);
301
- session.completePayment(window.ApplePaySession.STATUS_FAILURE);
303
+ session.completePayment({
304
+ status: window.ApplePaySession.STATUS_FAILURE,
305
+ });
302
306
  setProcessingPayment(false);
303
307
  const errorMsg = error instanceof Error ? error.message : 'Payment processing failed';
304
308
  setError(errorMsg);
@@ -317,7 +321,18 @@ export function useApplePay(options = {}) {
317
321
  return;
318
322
  }
319
323
  const { lineItems: newLineItems, total: newTotal } = newOrderSummary;
320
- session.completeShippingMethodSelection(window.ApplePaySession.STATUS_SUCCESS, newTotal, newLineItems);
324
+ session.completeShippingMethodSelection({
325
+ newTotal: {
326
+ label: newTotal.label,
327
+ amount: newTotal.amount,
328
+ type: 'final',
329
+ },
330
+ newLineItems: newLineItems.map(item => ({
331
+ label: item.label,
332
+ amount: item.amount,
333
+ type: 'final',
334
+ })),
335
+ });
321
336
  }
322
337
  catch (error) {
323
338
  console.error('Shipping method selection failed:', error);
@@ -343,7 +358,24 @@ export function useApplePay(options = {}) {
343
358
  console.log('======= APPLE PAY ON SHIPPING CONTACT SELECTED ======');
344
359
  console.log('newOrderSummary', newOrderSummary);
345
360
  console.log(newLineItems, newTotal, newShippingMethods);
346
- session.completeShippingContactSelection(window.ApplePaySession.STATUS_SUCCESS, newShippingMethods, newTotal, newLineItems);
361
+ session.completeShippingContactSelection({
362
+ newShippingMethods: newShippingMethods.map(method => ({
363
+ label: method.label,
364
+ amount: method.amount,
365
+ detail: method.detail,
366
+ identifier: method.identifier,
367
+ })),
368
+ newTotal: {
369
+ label: newTotal.label,
370
+ amount: newTotal.amount,
371
+ type: 'final',
372
+ },
373
+ newLineItems: newLineItems.map(item => ({
374
+ label: item.label,
375
+ amount: item.amount,
376
+ type: 'final',
377
+ })),
378
+ });
347
379
  }
348
380
  catch (error) {
349
381
  console.error('Shipping contact selection failed:', error);
@@ -351,12 +383,8 @@ export function useApplePay(options = {}) {
351
383
  }
352
384
  })();
353
385
  };
354
- session.onerror = (event) => {
355
- console.error('Apple Pay Session Error:', event);
356
- const errorMsg = 'Apple Pay session error';
357
- setError(errorMsg);
358
- options.onError?.(errorMsg);
359
- };
386
+ // Note: ApplePaySession doesn't have an onerror handler
387
+ // Error handling is done in the specific event handlers above
360
388
  session.oncancel = () => {
361
389
  console.log('Payment cancelled by user');
362
390
  setProcessingPayment(false);
@@ -38,6 +38,8 @@ export interface ExtractedAddress {
38
38
  locality: string;
39
39
  administrativeAreaLevel1: string;
40
40
  administrativeAreaLevel1Long: string;
41
+ administrativeAreaLevel2: string;
42
+ administrativeAreaLevel2Long: string;
41
43
  country: string;
42
44
  postalCode: string;
43
45
  }
@@ -155,6 +155,8 @@ export function useGoogleAutocomplete(options) {
155
155
  locality: '',
156
156
  administrativeAreaLevel1: '',
157
157
  administrativeAreaLevel1Long: '',
158
+ administrativeAreaLevel2: '',
159
+ administrativeAreaLevel2Long: '',
158
160
  country: '',
159
161
  postalCode: '',
160
162
  };
@@ -170,8 +172,13 @@ export function useGoogleAutocomplete(options) {
170
172
  if (types.includes('locality')) {
171
173
  extracted.locality = component.long_name;
172
174
  }
173
- if (types.includes('administrative_area_level_2') && !!!extracted.locality) {
174
- extracted.locality = component.long_name;
175
+ if (types.includes('administrative_area_level_2')) {
176
+ extracted.administrativeAreaLevel2 = component.short_name;
177
+ extracted.administrativeAreaLevel2Long = component.long_name;
178
+ // Use level_2 as fallback for locality if locality is not set
179
+ if (!extracted.locality) {
180
+ extracted.locality = component.long_name;
181
+ }
175
182
  }
176
183
  if (types.includes('administrative_area_level_1')) {
177
184
  extracted.administrativeAreaLevel1 = component.short_name;
@@ -184,6 +191,15 @@ export function useGoogleAutocomplete(options) {
184
191
  extracted.postalCode = component.long_name;
185
192
  }
186
193
  });
194
+ // For countries like France where administrative_area_level_1 (région) may be missing,
195
+ // use administrative_area_level_2 (département) as the primary state/province value
196
+ // We prefer the long_name (e.g., "Bouches-du-Rhône") over short_name (e.g., "13")
197
+ // because it's more likely to match our state database entries
198
+ if (!extracted.administrativeAreaLevel1 && extracted.administrativeAreaLevel2) {
199
+ // Use long name as the primary value (e.g., "Bouches-du-Rhône" instead of "13")
200
+ extracted.administrativeAreaLevel1 = extracted.administrativeAreaLevel2Long || extracted.administrativeAreaLevel2;
201
+ extracted.administrativeAreaLevel1Long = extracted.administrativeAreaLevel2Long;
202
+ }
187
203
  console.log('🏗️ Extracted address components:', extracted);
188
204
  return extracted;
189
205
  }, []);
@@ -0,0 +1,22 @@
1
+ interface UseGooglePayOptions {
2
+ onSuccess?: (payment: any) => void;
3
+ onError?: (error: string) => void;
4
+ onCancel?: () => void;
5
+ checkoutSessionId?: string;
6
+ customerId?: string;
7
+ storeName?: string;
8
+ currencyCode?: string;
9
+ merchantId?: string;
10
+ merchantName?: string;
11
+ }
12
+ export declare const useGooglePay: ({ onSuccess, onError, onCancel, checkoutSessionId, customerId, storeName, currencyCode, merchantId, merchantName, }: UseGooglePayOptions) => {
13
+ handleGooglePayAuthorized: (paymentData: any) => Promise<any>;
14
+ handleGooglePayDataChanged: (intermediatePaymentData: any) => Promise<any>;
15
+ processingPayment: boolean;
16
+ setProcessingPayment: import("react").Dispatch<import("react").SetStateAction<boolean>>;
17
+ googlePayError: string | null;
18
+ setGooglePayError: import("react").Dispatch<import("react").SetStateAction<string | null>>;
19
+ isGooglePayAvailable: boolean;
20
+ paymentRequest: null;
21
+ };
22
+ export {};
@@ -0,0 +1,32 @@
1
+ import { useCallback, useEffect, useState } from 'react';
2
+ export const useGooglePay = ({ onSuccess, onError, onCancel, checkoutSessionId, customerId, storeName = 'Store', currencyCode = 'USD', merchantId = '12345678901234567890', merchantName = 'Store', }) => {
3
+ const [processingPayment, setProcessingPayment] = useState(false);
4
+ const [googlePayError, setGooglePayError] = useState(null);
5
+ const [isGooglePayAvailable, setIsGooglePayAvailable] = useState(false);
6
+ // Check if Google Pay is available on this device
7
+ useEffect(() => {
8
+ if (typeof window !== 'undefined' && window.google?.payments?.api) {
9
+ setIsGooglePayAvailable(true);
10
+ }
11
+ }, []);
12
+ // The actual implementation will be done by the consumer
13
+ // This hook just provides state management
14
+ const handleGooglePayAuthorized = useCallback((paymentData) => {
15
+ // This will be implemented by the consumer
16
+ return Promise.resolve({ transactionState: 'SUCCESS' });
17
+ }, []);
18
+ const handleGooglePayDataChanged = useCallback((intermediatePaymentData) => {
19
+ // This will be implemented by the consumer
20
+ return Promise.resolve({});
21
+ }, []);
22
+ return {
23
+ handleGooglePayAuthorized,
24
+ handleGooglePayDataChanged,
25
+ processingPayment,
26
+ setProcessingPayment,
27
+ googlePayError,
28
+ setGooglePayError,
29
+ isGooglePayAvailable,
30
+ paymentRequest: null, // Will be provided by consumer
31
+ };
32
+ };
@@ -43,6 +43,7 @@ export { usePaymentPolling } from './hooks/usePaymentPolling';
43
43
  export { useThreeds } from './hooks/useThreeds';
44
44
  export { useThreedsModal } from './hooks/useThreedsModal';
45
45
  export { useApplePay } from './hooks/useApplePay';
46
+ export { useGooglePay } from './hooks/useGooglePay';
46
47
  export { ExpressPaymentMethodsProvider, useExpressPaymentMethods } from './hooks/useExpressPaymentMethods';
47
48
  export type { ExpressPaymentMethodsContextType } from './hooks/useExpressPaymentMethods';
48
49
  export type { AuthState, Currency, Customer, CustomerInfos, Environment, EnvironmentConfig, Locale, Order, OrderAddress, OrderItem, OrderSummary, PickupPoint, Session, Store } from './types';
@@ -59,5 +60,5 @@ export type { Payment, PaymentPollingHook, PollingOptions } from './hooks/usePay
59
60
  export type { PaymentInstrument, ThreedsChallenge, ThreedsHook, ThreedsOptions, ThreedsProvider, ThreedsSession } from './hooks/useThreeds';
60
61
  export type { ApplePayToken, CardPaymentMethod, PaymentHook, PaymentInstrumentResponse, PaymentOptions, PaymentResponse } from './hooks/usePayment';
61
62
  export type { ApplePayAddress, ApplePayConfig, ApplePayLineItem, ApplePayPaymentAuthorizedEvent, ApplePayPaymentRequest, ApplePayPaymentToken, ApplePayValidateMerchantEvent, BasisTheorySessionRequest, BasisTheoryTokenizeRequest, PayToken, UseApplePayOptions, UseApplePayResult } from './types/apple-pay';
62
- export { ApplePayButton, Button } from './components';
63
+ export { ApplePayButton, Button, GooglePayButton } from './components';
63
64
  export { convertCurrency, formatMoney, formatMoneyWithoutSymbol, formatSimpleMoney, getCurrencyInfo, minorUnitsToMajorUnits, moneyStringOrNumberToMinorUnits } from './utils/money';
@@ -45,10 +45,12 @@ export { useThreeds } from './hooks/useThreeds';
45
45
  export { useThreedsModal } from './hooks/useThreedsModal';
46
46
  // Apple Pay hooks exports
47
47
  export { useApplePay } from './hooks/useApplePay';
48
+ // Google Pay hooks exports
49
+ export { useGooglePay } from './hooks/useGooglePay';
48
50
  // Express Payment context exports
49
51
  // Express Payment Methods (extended functionality)
50
52
  export { ExpressPaymentMethodsProvider, useExpressPaymentMethods } from './hooks/useExpressPaymentMethods';
51
53
  // Component exports
52
- export { ApplePayButton, Button } from './components';
54
+ export { ApplePayButton, Button, GooglePayButton } from './components';
53
55
  // Utility exports
54
56
  export { convertCurrency, formatMoney, formatMoneyWithoutSymbol, formatSimpleMoney, getCurrencyInfo, minorUnitsToMajorUnits, moneyStringOrNumberToMinorUnits } from './utils/money';