@tagadapay/plugin-sdk 2.6.2 → 2.6.6
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 +1090 -623
- package/dist/react/components/GooglePayButton.d.ts +12 -0
- package/dist/react/components/GooglePayButton.js +340 -0
- package/dist/react/components/index.d.ts +3 -2
- package/dist/react/components/index.js +3 -2
- package/dist/react/hooks/useApplePay.js +38 -10
- package/dist/react/hooks/{useExpressPayment.d.ts → useExpressPaymentMethods.d.ts} +13 -10
- package/dist/react/hooks/{useExpressPayment.js → useExpressPaymentMethods.js} +17 -8
- package/dist/react/hooks/useGoogleAutocomplete.d.ts +2 -0
- package/dist/react/hooks/useGoogleAutocomplete.js +18 -2
- package/dist/react/hooks/useGooglePay.d.ts +22 -0
- package/dist/react/hooks/useGooglePay.js +32 -0
- package/dist/react/index.d.ts +9 -7
- package/dist/react/index.js +8 -5
- package/dist/react/providers/TagadaProvider.js +5 -5
- package/dist/react/types/apple-pay.d.ts +0 -25
- package/dist/v2/core/googleAutocomplete.d.ts +2 -0
- package/dist/v2/core/googleAutocomplete.js +23 -4
- package/dist/v2/core/resources/checkout.d.ts +70 -2
- package/dist/v2/core/resources/discounts.d.ts +53 -0
- package/dist/v2/core/resources/discounts.js +29 -0
- package/dist/v2/core/resources/expressPaymentMethods.d.ts +56 -0
- package/dist/v2/core/resources/expressPaymentMethods.js +27 -0
- package/dist/v2/core/resources/index.d.ts +7 -4
- package/dist/v2/core/resources/index.js +7 -4
- package/dist/v2/core/resources/shippingRates.d.ts +36 -0
- package/dist/v2/core/resources/shippingRates.js +23 -0
- package/dist/v2/core/resources/vipOffers.d.ts +37 -0
- package/dist/v2/core/resources/vipOffers.js +27 -0
- package/dist/v2/core/utils/order.d.ts +1 -0
- package/dist/v2/core/utils/pluginConfig.d.ts +6 -6
- package/dist/v2/index.d.ts +12 -9
- package/dist/v2/index.js +3 -3
- package/dist/v2/react/components/ApplePayButton.d.ts +141 -0
- package/dist/v2/react/components/ApplePayButton.js +320 -0
- package/dist/v2/react/components/GooglePayButton.d.ts +19 -0
- package/dist/v2/react/components/GooglePayButton.js +355 -0
- package/dist/v2/react/hooks/useApiQuery.d.ts +4 -1
- package/dist/v2/react/hooks/useApiQuery.js +4 -1
- package/dist/v2/react/hooks/useDiscountsQuery.d.ts +30 -0
- package/dist/v2/react/hooks/useDiscountsQuery.js +175 -0
- package/dist/v2/react/hooks/useExpressPaymentMethods.d.ts +12 -0
- package/dist/v2/react/hooks/useExpressPaymentMethods.js +17 -0
- package/dist/v2/react/hooks/useGoogleAutocomplete.d.ts +2 -0
- package/dist/v2/react/hooks/useGoogleAutocomplete.js +18 -2
- package/dist/v2/react/hooks/useShippingRatesQuery.d.ts +22 -0
- package/dist/v2/react/hooks/useShippingRatesQuery.js +134 -0
- package/dist/v2/react/hooks/useVipOffersQuery.d.ts +72 -0
- package/dist/v2/react/hooks/useVipOffersQuery.js +140 -0
- package/dist/v2/react/index.d.ts +30 -17
- package/dist/v2/react/index.js +18 -10
- package/dist/v2/react/providers/ExpressPaymentMethodsProvider.d.ts +59 -0
- package/dist/v2/react/providers/ExpressPaymentMethodsProvider.js +165 -0
- package/dist/v2/react/providers/TagadaProvider.js +5 -5
- package/package.json +90 -90
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* VIP Offers Resource Client
|
|
3
|
+
* Axios-based API client for VIP offer endpoints
|
|
4
|
+
*/
|
|
5
|
+
export class VipOffersResource {
|
|
6
|
+
constructor(apiClient) {
|
|
7
|
+
this.apiClient = apiClient;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Get VIP preview showing potential savings
|
|
11
|
+
*/
|
|
12
|
+
async getVipPreview(sessionId, vipOfferIds) {
|
|
13
|
+
return this.apiClient.post(`/api/v1/checkout-sessions/${sessionId}/vip-preview`, {
|
|
14
|
+
orderBumpOfferIds: vipOfferIds,
|
|
15
|
+
orderBumpType: 'vip',
|
|
16
|
+
});
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Toggle an order bump (used for VIP offers)
|
|
20
|
+
*/
|
|
21
|
+
async toggleOrderBump(sessionId, orderBumpOfferId, selected) {
|
|
22
|
+
return this.apiClient.post(`/api/v1/checkout-sessions/${sessionId}/toggle-order-bump`, {
|
|
23
|
+
orderBumpOfferId,
|
|
24
|
+
selected,
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
}
|
|
@@ -2,11 +2,11 @@
|
|
|
2
2
|
* Plugin Configuration Utility Functions
|
|
3
3
|
* Pure functions for plugin configuration management
|
|
4
4
|
*/
|
|
5
|
-
export
|
|
5
|
+
export type PluginConfig<TConfig = Record<string, any>> = {
|
|
6
6
|
storeId?: string;
|
|
7
7
|
accountId?: string;
|
|
8
8
|
basePath?: string;
|
|
9
|
-
config?:
|
|
9
|
+
config?: any;
|
|
10
10
|
productId?: string;
|
|
11
11
|
variants?: Record<string, string>;
|
|
12
12
|
prices?: Record<string, any>;
|
|
@@ -15,13 +15,13 @@ export interface PluginConfig<TConfig = Record<string, any>> {
|
|
|
15
15
|
upsellId?: string;
|
|
16
16
|
googleApiKey?: string;
|
|
17
17
|
branding?: any;
|
|
18
|
-
}
|
|
19
|
-
export
|
|
18
|
+
} & TConfig;
|
|
19
|
+
export type RawPluginConfig<TConfig = Record<string, any>> = {
|
|
20
20
|
storeId?: string;
|
|
21
21
|
accountId?: string;
|
|
22
22
|
basePath?: string;
|
|
23
|
-
config?:
|
|
24
|
-
}
|
|
23
|
+
config?: any;
|
|
24
|
+
} & TConfig;
|
|
25
25
|
/**
|
|
26
26
|
* Core plugin config loading function
|
|
27
27
|
* Handles local dev, production, and raw config
|
package/dist/v2/index.d.ts
CHANGED
|
@@ -5,15 +5,18 @@
|
|
|
5
5
|
* - core: Pure functions without React dependencies
|
|
6
6
|
* - react: React hooks and components using core functions
|
|
7
7
|
*/
|
|
8
|
-
export * from './core/utils/pluginConfig';
|
|
9
|
-
export * from './core/utils/products';
|
|
10
8
|
export * from './core/googleAutocomplete';
|
|
11
9
|
export * from './core/isoData';
|
|
12
10
|
export * from './core/utils/currency';
|
|
13
|
-
export
|
|
14
|
-
export
|
|
15
|
-
export type {
|
|
16
|
-
export type {
|
|
17
|
-
export type {
|
|
18
|
-
export type {
|
|
19
|
-
export {
|
|
11
|
+
export * from './core/utils/pluginConfig';
|
|
12
|
+
export * from './core/utils/products';
|
|
13
|
+
export type { CheckoutData, CheckoutInitParams, CheckoutLineItem, CheckoutSession, Promotion } from './core/resources/checkout';
|
|
14
|
+
export type { Order, OrderLineItem } from './core/utils/order';
|
|
15
|
+
export type { PostPurchaseOffer, PostPurchaseOfferItem, PostPurchaseOfferSummary } from './core/resources/postPurchases';
|
|
16
|
+
export type { Offer, OfferItem, OfferSummary } from './core/resources/offers';
|
|
17
|
+
export type { OrderBumpOffer, OrderBumpPreview } from './core/utils/orderBump';
|
|
18
|
+
export type { ApplePayToken, CardPaymentMethod, Payment, PaymentInstrumentCustomer, PaymentInstrumentCustomerResponse, PaymentInstrumentResponse, PaymentOptions, PaymentResponse } from './core/resources/payments';
|
|
19
|
+
export type { ShippingRate, ShippingRatesResponse } from './core/resources/shippingRates';
|
|
20
|
+
export type { ApplyDiscountResponse, Discount, DiscountCodeValidation, RemoveDiscountResponse } from './core/resources/discounts';
|
|
21
|
+
export type { ToggleOrderBumpResponse, VipOffer, VipPreviewResponse } from './core/resources/vipOffers';
|
|
22
|
+
export { ApplePayButton, ExpressPaymentMethodsProvider, formatMoney, getAvailableLanguages, GooglePayButton, queryKeys, TagadaProvider, useApiMutation, useApiQuery, useCheckout, useCheckoutToken, useCountryOptions, useCurrency, useDiscounts, useExpressPaymentMethods, useGeoLocation, useGoogleAutocomplete, useInvalidateQuery, useISOData, useLanguageImport, useOffers, useOrder, useOrderBump, usePayment, usePluginConfig, usePostPurchases, usePreloadQuery, useProducts, usePromotions, useRegionOptions, useShippingRates, useTagadaContext, useThreeds, useThreedsModal, useVipOffers } from './react';
|
package/dist/v2/index.js
CHANGED
|
@@ -6,10 +6,10 @@
|
|
|
6
6
|
* - react: React hooks and components using core functions
|
|
7
7
|
*/
|
|
8
8
|
// Core exports (selective to avoid conflicts)
|
|
9
|
-
export * from './core/utils/pluginConfig';
|
|
10
|
-
export * from './core/utils/products';
|
|
11
9
|
export * from './core/googleAutocomplete';
|
|
12
10
|
export * from './core/isoData';
|
|
13
11
|
export * from './core/utils/currency';
|
|
12
|
+
export * from './core/utils/pluginConfig';
|
|
13
|
+
export * from './core/utils/products';
|
|
14
14
|
// React exports (hooks and components only, types are exported above)
|
|
15
|
-
export {
|
|
15
|
+
export { ApplePayButton, ExpressPaymentMethodsProvider, formatMoney, getAvailableLanguages, GooglePayButton, queryKeys, TagadaProvider, useApiMutation, useApiQuery, useCheckout, useCheckoutToken, useCountryOptions, useCurrency, useDiscounts, useExpressPaymentMethods, useGeoLocation, useGoogleAutocomplete, useInvalidateQuery, useISOData, useLanguageImport, useOffers, useOrder, useOrderBump, usePayment, usePluginConfig, usePostPurchases, usePreloadQuery, useProducts, usePromotions, useRegionOptions, useShippingRates, useTagadaContext, useThreeds, useThreedsModal, useVipOffers } from './react';
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Apple Pay Button Component for v2 Architecture
|
|
3
|
+
* Uses v2 useExpressPaymentMethods hook and follows clean architecture principles
|
|
4
|
+
*/
|
|
5
|
+
import React from 'react';
|
|
6
|
+
import { CheckoutData } from '../../core/resources/checkout';
|
|
7
|
+
declare class ApplePaySession {
|
|
8
|
+
static canMakePayments(): boolean;
|
|
9
|
+
static canMakePaymentsWithActiveCard(merchantId: string): Promise<boolean>;
|
|
10
|
+
static STATUS_SUCCESS: number;
|
|
11
|
+
static STATUS_FAILURE: number;
|
|
12
|
+
static STATUS_INVALID_BILLING_POSTAL_ADDRESS: number;
|
|
13
|
+
static STATUS_INVALID_SHIPPING_POSTAL_ADDRESS: number;
|
|
14
|
+
static STATUS_INVALID_SHIPPING_CONTACT: number;
|
|
15
|
+
static STATUS_PIN_REQUIRED: number;
|
|
16
|
+
static STATUS_PIN_INCORRECT: number;
|
|
17
|
+
static STATUS_PIN_LOCKOUT: number;
|
|
18
|
+
constructor(version: number, paymentRequest: ApplePayPaymentRequest);
|
|
19
|
+
begin(): void;
|
|
20
|
+
abort(): void;
|
|
21
|
+
completeMerchantValidation(merchantSession: any): void;
|
|
22
|
+
completePayment(result: ApplePayPaymentAuthorizationResult): void;
|
|
23
|
+
completeShippingContactSelection(update: ApplePayShippingContactUpdate): void;
|
|
24
|
+
completeShippingMethodSelection(update: ApplePayShippingMethodUpdate): void;
|
|
25
|
+
completePaymentMethodSelection(update: ApplePayPaymentMethodUpdate): void;
|
|
26
|
+
onvalidatemerchant: ((event: ApplePayValidateMerchantEvent) => void) | null;
|
|
27
|
+
onpaymentauthorized: ((event: ApplePayPaymentAuthorizedEvent) => void) | null;
|
|
28
|
+
onshippingcontactselected: ((event: ApplePayShippingContactSelectionEvent) => void) | null;
|
|
29
|
+
onshippingmethodselected: ((event: ApplePayShippingMethodSelectionEvent) => void) | null;
|
|
30
|
+
onpaymentmethodselected: ((event: ApplePayPaymentMethodSelectionEvent) => void) | null;
|
|
31
|
+
oncancel: ((event: Event) => void) | null;
|
|
32
|
+
}
|
|
33
|
+
interface ApplePayPaymentRequest {
|
|
34
|
+
countryCode: string;
|
|
35
|
+
currencyCode: string;
|
|
36
|
+
supportedNetworks: string[];
|
|
37
|
+
merchantCapabilities: string[];
|
|
38
|
+
total: ApplePayLineItem;
|
|
39
|
+
lineItems?: ApplePayLineItem[];
|
|
40
|
+
shippingType?: 'shipping' | 'delivery' | 'storePickup' | 'servicePickup';
|
|
41
|
+
shippingMethods?: ApplePayShippingMethod[];
|
|
42
|
+
requiredBillingContactFields?: string[];
|
|
43
|
+
requiredShippingContactFields?: string[];
|
|
44
|
+
applicationData?: string;
|
|
45
|
+
}
|
|
46
|
+
interface ApplePayLineItem {
|
|
47
|
+
label: string;
|
|
48
|
+
amount: string;
|
|
49
|
+
type?: 'final' | 'pending';
|
|
50
|
+
}
|
|
51
|
+
interface ApplePayShippingMethod {
|
|
52
|
+
label: string;
|
|
53
|
+
amount: string;
|
|
54
|
+
detail?: string;
|
|
55
|
+
identifier: string;
|
|
56
|
+
}
|
|
57
|
+
interface ApplePayPaymentToken {
|
|
58
|
+
paymentData: any;
|
|
59
|
+
paymentMethod: {
|
|
60
|
+
displayName: string;
|
|
61
|
+
network: string;
|
|
62
|
+
type: string;
|
|
63
|
+
paymentPass: any;
|
|
64
|
+
billingContact?: ApplePayPaymentContact;
|
|
65
|
+
};
|
|
66
|
+
transactionIdentifier: string;
|
|
67
|
+
}
|
|
68
|
+
interface ApplePayPaymentContact {
|
|
69
|
+
phoneNumber?: string;
|
|
70
|
+
emailAddress?: string;
|
|
71
|
+
givenName?: string;
|
|
72
|
+
familyName?: string;
|
|
73
|
+
phoneticGivenName?: string;
|
|
74
|
+
phoneticFamilyName?: string;
|
|
75
|
+
addressLines?: string[];
|
|
76
|
+
locality?: string;
|
|
77
|
+
administrativeArea?: string;
|
|
78
|
+
postalCode?: string;
|
|
79
|
+
countryCode?: string;
|
|
80
|
+
}
|
|
81
|
+
interface ApplePayValidateMerchantEvent {
|
|
82
|
+
validationURL: string;
|
|
83
|
+
}
|
|
84
|
+
interface ApplePayPaymentAuthorizedEvent {
|
|
85
|
+
payment: {
|
|
86
|
+
token: ApplePayPaymentToken;
|
|
87
|
+
billingContact?: ApplePayPaymentContact;
|
|
88
|
+
shippingContact?: ApplePayPaymentContact;
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
interface ApplePayShippingContactSelectionEvent {
|
|
92
|
+
shippingContact: ApplePayPaymentContact;
|
|
93
|
+
}
|
|
94
|
+
interface ApplePayShippingMethodSelectionEvent {
|
|
95
|
+
shippingMethod: ApplePayShippingMethod;
|
|
96
|
+
}
|
|
97
|
+
interface ApplePayPaymentMethodSelectionEvent {
|
|
98
|
+
paymentMethod: any;
|
|
99
|
+
}
|
|
100
|
+
interface ApplePayPaymentAuthorizationResult {
|
|
101
|
+
status: number;
|
|
102
|
+
errors?: ApplePayError[];
|
|
103
|
+
}
|
|
104
|
+
interface ApplePayShippingContactUpdate {
|
|
105
|
+
errors?: ApplePayError[];
|
|
106
|
+
newShippingMethods?: ApplePayShippingMethod[];
|
|
107
|
+
newTotal?: ApplePayLineItem;
|
|
108
|
+
newLineItems?: ApplePayLineItem[];
|
|
109
|
+
}
|
|
110
|
+
interface ApplePayShippingMethodUpdate {
|
|
111
|
+
newTotal?: ApplePayLineItem;
|
|
112
|
+
newLineItems?: ApplePayLineItem[];
|
|
113
|
+
}
|
|
114
|
+
interface ApplePayPaymentMethodUpdate {
|
|
115
|
+
newTotal?: ApplePayLineItem;
|
|
116
|
+
newLineItems?: ApplePayLineItem[];
|
|
117
|
+
}
|
|
118
|
+
interface ApplePayError {
|
|
119
|
+
code: string;
|
|
120
|
+
message: string;
|
|
121
|
+
contactField?: string;
|
|
122
|
+
}
|
|
123
|
+
declare global {
|
|
124
|
+
interface Window {
|
|
125
|
+
ApplePaySession: typeof ApplePaySession;
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
export interface ApplePayButtonProps {
|
|
129
|
+
className?: string;
|
|
130
|
+
disabled?: boolean;
|
|
131
|
+
onSuccess?: (payment: any) => void;
|
|
132
|
+
onError?: (error: string) => void;
|
|
133
|
+
onCancel?: () => void;
|
|
134
|
+
storeName?: string;
|
|
135
|
+
currencyCode?: string;
|
|
136
|
+
variant?: 'default' | 'outline' | 'ghost';
|
|
137
|
+
size?: 'sm' | 'md' | 'lg';
|
|
138
|
+
checkout: CheckoutData;
|
|
139
|
+
}
|
|
140
|
+
export declare const ApplePayButton: React.FC<ApplePayButtonProps>;
|
|
141
|
+
export default ApplePayButton;
|
|
@@ -0,0 +1,320 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
/**
|
|
3
|
+
* Apple Pay Button Component for v2 Architecture
|
|
4
|
+
* Uses v2 useExpressPaymentMethods hook and follows clean architecture principles
|
|
5
|
+
*/
|
|
6
|
+
import { useCallback, useEffect, useState } from 'react';
|
|
7
|
+
import { useExpressPaymentMethods } from '../hooks/useExpressPaymentMethods';
|
|
8
|
+
export const ApplePayButton = ({ className = '', disabled = false, onSuccess, onError, onCancel, storeName, currencyCode = 'USD', variant = 'outline', size = 'lg', checkout, }) => {
|
|
9
|
+
const { applePayPaymentMethod, shippingMethods, lineItems, handleAddExpressId, updateCheckoutSessionValues, updateCustomerEmail, reComputeOrderSummary, setError: setContextError, } = useExpressPaymentMethods();
|
|
10
|
+
const [processingPayment, setProcessingPayment] = useState(false);
|
|
11
|
+
const [isApplePayAvailable, setIsApplePayAvailable] = useState(false);
|
|
12
|
+
const [applePayError, setApplePayError] = useState(null);
|
|
13
|
+
// Don't render if no Apple Pay payment method is enabled
|
|
14
|
+
if (!applePayPaymentMethod) {
|
|
15
|
+
return null;
|
|
16
|
+
}
|
|
17
|
+
// Check Apple Pay availability
|
|
18
|
+
useEffect(() => {
|
|
19
|
+
const checkApplePayAvailability = async () => {
|
|
20
|
+
if (typeof window === 'undefined' || !window.ApplePaySession) {
|
|
21
|
+
setIsApplePayAvailable(false);
|
|
22
|
+
return;
|
|
23
|
+
}
|
|
24
|
+
try {
|
|
25
|
+
const canMakePayments = window.ApplePaySession.canMakePayments();
|
|
26
|
+
if (canMakePayments) {
|
|
27
|
+
const merchantId = applePayPaymentMethod?.metadata?.merchantId || '';
|
|
28
|
+
const canMakePaymentsWithActiveCard = await window.ApplePaySession.canMakePaymentsWithActiveCard(merchantId);
|
|
29
|
+
setIsApplePayAvailable(canMakePaymentsWithActiveCard);
|
|
30
|
+
if (canMakePaymentsWithActiveCard) {
|
|
31
|
+
handleAddExpressId('apple_pay');
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
else {
|
|
35
|
+
setIsApplePayAvailable(false);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
catch (error) {
|
|
39
|
+
console.error('Error checking Apple Pay availability:', error);
|
|
40
|
+
setIsApplePayAvailable(false);
|
|
41
|
+
}
|
|
42
|
+
};
|
|
43
|
+
checkApplePayAvailability();
|
|
44
|
+
}, [applePayPaymentMethod, handleAddExpressId]);
|
|
45
|
+
// Convert Apple Pay contact to internal Address format
|
|
46
|
+
const applePayContactToAddress = useCallback((contact) => {
|
|
47
|
+
return {
|
|
48
|
+
address1: contact.addressLines?.[0] || '',
|
|
49
|
+
address2: contact.addressLines?.[1] || '',
|
|
50
|
+
lastName: contact.familyName || '',
|
|
51
|
+
firstName: contact.givenName || '',
|
|
52
|
+
city: contact.locality || '',
|
|
53
|
+
state: contact.administrativeArea || '',
|
|
54
|
+
country: contact.countryCode || '',
|
|
55
|
+
postal: contact.postalCode || '',
|
|
56
|
+
phone: contact.phoneNumber || '',
|
|
57
|
+
email: contact.emailAddress || '',
|
|
58
|
+
};
|
|
59
|
+
}, []);
|
|
60
|
+
// Validate merchant with backend
|
|
61
|
+
const validateMerchant = useCallback(async (validationURL) => {
|
|
62
|
+
const response = await fetch('/api/v1/apple-pay/validate-merchant', {
|
|
63
|
+
method: 'POST',
|
|
64
|
+
headers: {
|
|
65
|
+
'Content-Type': 'application/json',
|
|
66
|
+
},
|
|
67
|
+
body: JSON.stringify({
|
|
68
|
+
validationURL,
|
|
69
|
+
checkoutSessionId: checkout.checkoutSession.id,
|
|
70
|
+
}),
|
|
71
|
+
});
|
|
72
|
+
if (!response.ok) {
|
|
73
|
+
throw new Error('Failed to validate Apple Pay merchant');
|
|
74
|
+
}
|
|
75
|
+
return response.json();
|
|
76
|
+
}, [checkout.checkoutSession.id]);
|
|
77
|
+
// Process Apple Pay payment
|
|
78
|
+
const processApplePayPayment = useCallback(async (token, billingContact, shippingContact) => {
|
|
79
|
+
try {
|
|
80
|
+
setProcessingPayment(true);
|
|
81
|
+
// Update addresses before processing payment
|
|
82
|
+
if (shippingContact) {
|
|
83
|
+
const shippingAddress = applePayContactToAddress(shippingContact);
|
|
84
|
+
const billingAddress = billingContact ? applePayContactToAddress(billingContact) : null;
|
|
85
|
+
await updateCheckoutSessionValues({
|
|
86
|
+
data: {
|
|
87
|
+
shippingAddress,
|
|
88
|
+
billingAddress,
|
|
89
|
+
},
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
// Update customer email
|
|
93
|
+
if (shippingContact?.emailAddress) {
|
|
94
|
+
await updateCustomerEmail({
|
|
95
|
+
data: {
|
|
96
|
+
email: shippingContact.emailAddress,
|
|
97
|
+
},
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
// Process payment with backend API
|
|
101
|
+
const response = await fetch('/api/v1/apple-pay/process-payment', {
|
|
102
|
+
method: 'POST',
|
|
103
|
+
headers: {
|
|
104
|
+
'Content-Type': 'application/json',
|
|
105
|
+
},
|
|
106
|
+
body: JSON.stringify({
|
|
107
|
+
checkoutSessionId: checkout.checkoutSession.id,
|
|
108
|
+
paymentToken: token,
|
|
109
|
+
billingContact,
|
|
110
|
+
shippingContact,
|
|
111
|
+
}),
|
|
112
|
+
});
|
|
113
|
+
if (!response.ok) {
|
|
114
|
+
throw new Error('Failed to process Apple Pay payment');
|
|
115
|
+
}
|
|
116
|
+
const paymentResult = await response.json();
|
|
117
|
+
if (onSuccess) {
|
|
118
|
+
onSuccess(paymentResult);
|
|
119
|
+
}
|
|
120
|
+
return paymentResult;
|
|
121
|
+
}
|
|
122
|
+
catch (error) {
|
|
123
|
+
console.error('Error processing Apple Pay payment:', error);
|
|
124
|
+
const errorMessage = error instanceof Error ? error.message : 'Apple Pay payment failed';
|
|
125
|
+
setApplePayError(errorMessage);
|
|
126
|
+
setContextError(errorMessage);
|
|
127
|
+
if (onError) {
|
|
128
|
+
onError(errorMessage);
|
|
129
|
+
}
|
|
130
|
+
throw error;
|
|
131
|
+
}
|
|
132
|
+
finally {
|
|
133
|
+
setProcessingPayment(false);
|
|
134
|
+
}
|
|
135
|
+
}, [
|
|
136
|
+
checkout.checkoutSession.id,
|
|
137
|
+
applePayContactToAddress,
|
|
138
|
+
updateCheckoutSessionValues,
|
|
139
|
+
updateCustomerEmail,
|
|
140
|
+
onSuccess,
|
|
141
|
+
onError,
|
|
142
|
+
setContextError,
|
|
143
|
+
]);
|
|
144
|
+
// Handle Apple Pay button click
|
|
145
|
+
const handleApplePayClick = useCallback(() => {
|
|
146
|
+
if (!isApplePayAvailable || processingPayment || !checkout.summary) {
|
|
147
|
+
return;
|
|
148
|
+
}
|
|
149
|
+
const paymentRequest = {
|
|
150
|
+
countryCode: 'US', // TODO: Get from checkout session
|
|
151
|
+
currencyCode: currencyCode,
|
|
152
|
+
supportedNetworks: ['visa', 'masterCard', 'amex', 'discover'],
|
|
153
|
+
merchantCapabilities: ['supports3DS'],
|
|
154
|
+
total: {
|
|
155
|
+
label: storeName || checkout.checkoutSession.store?.name || 'Total',
|
|
156
|
+
amount: (checkout.summary.totalAdjustedAmount / 100).toFixed(2),
|
|
157
|
+
type: 'final',
|
|
158
|
+
},
|
|
159
|
+
lineItems: lineItems.map((item) => ({
|
|
160
|
+
label: item.label,
|
|
161
|
+
amount: item.amount,
|
|
162
|
+
type: 'final',
|
|
163
|
+
})),
|
|
164
|
+
shippingType: 'shipping',
|
|
165
|
+
shippingMethods: shippingMethods.map((method) => ({
|
|
166
|
+
label: method.label,
|
|
167
|
+
amount: method.amount,
|
|
168
|
+
detail: method.detail,
|
|
169
|
+
identifier: method.identifier,
|
|
170
|
+
})),
|
|
171
|
+
requiredBillingContactFields: ['postalAddress', 'name', 'phone', 'email'],
|
|
172
|
+
requiredShippingContactFields: ['postalAddress', 'name', 'phone', 'email'],
|
|
173
|
+
};
|
|
174
|
+
const session = new window.ApplePaySession(3, paymentRequest);
|
|
175
|
+
session.onvalidatemerchant = async (event) => {
|
|
176
|
+
try {
|
|
177
|
+
const merchantSession = await validateMerchant(event.validationURL);
|
|
178
|
+
session.completeMerchantValidation(merchantSession);
|
|
179
|
+
}
|
|
180
|
+
catch (error) {
|
|
181
|
+
console.error('Merchant validation failed:', error);
|
|
182
|
+
session.abort();
|
|
183
|
+
const errorMessage = 'Apple Pay setup failed';
|
|
184
|
+
setApplePayError(errorMessage);
|
|
185
|
+
setContextError(errorMessage);
|
|
186
|
+
if (onError) {
|
|
187
|
+
onError(errorMessage);
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
};
|
|
191
|
+
session.onshippingcontactselected = async (event) => {
|
|
192
|
+
try {
|
|
193
|
+
const shippingAddress = applePayContactToAddress(event.shippingContact);
|
|
194
|
+
await updateCheckoutSessionValues({
|
|
195
|
+
data: { shippingAddress },
|
|
196
|
+
});
|
|
197
|
+
const newOrderSummary = await reComputeOrderSummary();
|
|
198
|
+
const update = {
|
|
199
|
+
newShippingMethods: newOrderSummary?.shippingMethods.map((method) => ({
|
|
200
|
+
label: method.label,
|
|
201
|
+
amount: method.amount,
|
|
202
|
+
detail: method.detail,
|
|
203
|
+
identifier: method.identifier,
|
|
204
|
+
})) || [],
|
|
205
|
+
newTotal: {
|
|
206
|
+
label: storeName || checkout.checkoutSession.store?.name || 'Total',
|
|
207
|
+
amount: newOrderSummary?.total.amount || '0.00',
|
|
208
|
+
type: 'final',
|
|
209
|
+
},
|
|
210
|
+
newLineItems: newOrderSummary?.lineItems.map((item) => ({
|
|
211
|
+
label: item.label,
|
|
212
|
+
amount: item.amount,
|
|
213
|
+
type: 'final',
|
|
214
|
+
})) || [],
|
|
215
|
+
};
|
|
216
|
+
session.completeShippingContactSelection(update);
|
|
217
|
+
}
|
|
218
|
+
catch (error) {
|
|
219
|
+
console.error('Shipping contact selection failed:', error);
|
|
220
|
+
session.completeShippingContactSelection({
|
|
221
|
+
errors: [
|
|
222
|
+
{
|
|
223
|
+
code: 'shippingContactInvalid',
|
|
224
|
+
message: 'Unable to ship to this address',
|
|
225
|
+
},
|
|
226
|
+
],
|
|
227
|
+
});
|
|
228
|
+
}
|
|
229
|
+
};
|
|
230
|
+
session.onshippingmethodselected = async (event) => {
|
|
231
|
+
try {
|
|
232
|
+
// Update shipping method via API
|
|
233
|
+
const response = await fetch('/api/v1/shipping-rates/select', {
|
|
234
|
+
method: 'POST',
|
|
235
|
+
headers: {
|
|
236
|
+
'Content-Type': 'application/json',
|
|
237
|
+
},
|
|
238
|
+
body: JSON.stringify({
|
|
239
|
+
checkoutSessionId: checkout.checkoutSession.id,
|
|
240
|
+
shippingRateId: event.shippingMethod.identifier,
|
|
241
|
+
}),
|
|
242
|
+
});
|
|
243
|
+
if (!response.ok) {
|
|
244
|
+
throw new Error('Failed to update shipping method');
|
|
245
|
+
}
|
|
246
|
+
const newOrderSummary = await reComputeOrderSummary();
|
|
247
|
+
const update = {
|
|
248
|
+
newTotal: {
|
|
249
|
+
label: storeName || checkout.checkoutSession.store?.name || 'Total',
|
|
250
|
+
amount: newOrderSummary?.total.amount || '0.00',
|
|
251
|
+
type: 'final',
|
|
252
|
+
},
|
|
253
|
+
newLineItems: newOrderSummary?.lineItems.map((item) => ({
|
|
254
|
+
label: item.label,
|
|
255
|
+
amount: item.amount,
|
|
256
|
+
type: 'final',
|
|
257
|
+
})) || [],
|
|
258
|
+
};
|
|
259
|
+
session.completeShippingMethodSelection(update);
|
|
260
|
+
}
|
|
261
|
+
catch (error) {
|
|
262
|
+
console.error('Shipping method selection failed:', error);
|
|
263
|
+
session.completeShippingMethodSelection({});
|
|
264
|
+
}
|
|
265
|
+
};
|
|
266
|
+
session.onpaymentauthorized = async (event) => {
|
|
267
|
+
try {
|
|
268
|
+
await processApplePayPayment(event.payment.token, event.payment.billingContact, event.payment.shippingContact);
|
|
269
|
+
session.completePayment({
|
|
270
|
+
status: window.ApplePaySession.STATUS_SUCCESS,
|
|
271
|
+
});
|
|
272
|
+
}
|
|
273
|
+
catch (error) {
|
|
274
|
+
console.error('Payment authorization failed:', error);
|
|
275
|
+
session.completePayment({
|
|
276
|
+
status: window.ApplePaySession.STATUS_FAILURE,
|
|
277
|
+
});
|
|
278
|
+
}
|
|
279
|
+
};
|
|
280
|
+
session.oncancel = () => {
|
|
281
|
+
if (onCancel) {
|
|
282
|
+
onCancel();
|
|
283
|
+
}
|
|
284
|
+
};
|
|
285
|
+
session.begin();
|
|
286
|
+
}, [
|
|
287
|
+
isApplePayAvailable,
|
|
288
|
+
processingPayment,
|
|
289
|
+
checkout,
|
|
290
|
+
currencyCode,
|
|
291
|
+
storeName,
|
|
292
|
+
lineItems,
|
|
293
|
+
shippingMethods,
|
|
294
|
+
validateMerchant,
|
|
295
|
+
applePayContactToAddress,
|
|
296
|
+
updateCheckoutSessionValues,
|
|
297
|
+
reComputeOrderSummary,
|
|
298
|
+
processApplePayPayment,
|
|
299
|
+
onCancel,
|
|
300
|
+
onError,
|
|
301
|
+
setContextError,
|
|
302
|
+
]);
|
|
303
|
+
// Button size classes
|
|
304
|
+
const sizeClasses = {
|
|
305
|
+
sm: 'h-8 px-3 text-sm',
|
|
306
|
+
md: 'h-10 px-4 text-base',
|
|
307
|
+
lg: 'h-12 px-6 text-lg',
|
|
308
|
+
};
|
|
309
|
+
// Button variant classes
|
|
310
|
+
const variantClasses = {
|
|
311
|
+
default: 'bg-black text-white hover:bg-black/90',
|
|
312
|
+
outline: 'border border-black bg-white text-black hover:bg-black hover:text-white',
|
|
313
|
+
ghost: 'text-black hover:bg-black/10',
|
|
314
|
+
};
|
|
315
|
+
if (!isApplePayAvailable) {
|
|
316
|
+
return null;
|
|
317
|
+
}
|
|
318
|
+
return (_jsxs("div", { className: "w-full", children: [_jsx("button", { type: "button", onClick: handleApplePayClick, disabled: disabled || processingPayment, className: `relative w-full rounded-md font-medium transition-colors focus:outline-none focus:ring-2 focus:ring-black focus:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 ${sizeClasses[size]} ${variantClasses[variant]} ${className} `, children: processingPayment ? (_jsxs("div", { className: "flex items-center justify-center gap-2", children: [_jsx("div", { className: "h-4 w-4 animate-spin rounded-full border-2 border-current border-t-transparent" }), _jsx("span", { children: "Processing..." })] })) : (_jsxs("span", { className: "flex items-center justify-center gap-2", children: [_jsx("svg", { className: "h-6 w-6", 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" }) }), "Pay with Apple Pay"] })) }), applePayError && (_jsx("div", { className: "mt-2 rounded border border-red-200 bg-red-50 p-2 text-sm text-red-600", children: applePayError }))] }));
|
|
319
|
+
};
|
|
320
|
+
export default ApplePayButton;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Google Pay Button Component for v2 Architecture
|
|
3
|
+
* Uses v2 useExpressPaymentMethods hook and follows clean architecture principles
|
|
4
|
+
*/
|
|
5
|
+
import React from 'react';
|
|
6
|
+
import { CheckoutData } from '../../core/resources/checkout';
|
|
7
|
+
export interface GooglePayButtonProps {
|
|
8
|
+
className?: string;
|
|
9
|
+
disabled?: boolean;
|
|
10
|
+
onSuccess?: (payment: any) => void;
|
|
11
|
+
onError?: (error: string) => void;
|
|
12
|
+
onCancel?: () => void;
|
|
13
|
+
checkout: CheckoutData;
|
|
14
|
+
size?: 'sm' | 'md' | 'lg';
|
|
15
|
+
buttonColor?: 'default' | 'black' | 'white';
|
|
16
|
+
buttonType?: 'buy' | 'plain' | 'donate' | 'pay';
|
|
17
|
+
}
|
|
18
|
+
export declare const GooglePayButton: React.FC<GooglePayButtonProps>;
|
|
19
|
+
export default GooglePayButton;
|