@tagadapay/plugin-sdk 3.1.22 → 3.1.24
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/build-cdn.js +274 -6
- package/dist/external-tracker.js +236 -3906
- package/dist/external-tracker.min.js +2 -25
- package/dist/external-tracker.min.js.map +4 -4
- package/dist/react/config/payment.d.ts +14 -4
- package/dist/react/config/payment.js +47 -9
- package/dist/react/hooks/useCheckout.d.ts +3 -0
- package/dist/react/hooks/useCheckout.js +4 -1
- package/dist/react/hooks/usePluginConfig.js +9 -10
- package/dist/react/providers/TagadaProvider.js +1 -1
- package/dist/tagada-react-sdk-minimal.min.js +36 -0
- package/dist/tagada-react-sdk-minimal.min.js.map +7 -0
- package/dist/tagada-react-sdk.js +37821 -0
- package/dist/tagada-react-sdk.min.js +78 -0
- package/dist/tagada-react-sdk.min.js.map +7 -0
- package/dist/tagada-sdk.js +10309 -6331
- package/dist/tagada-sdk.min.js +4 -22
- package/dist/tagada-sdk.min.js.map +4 -4
- package/dist/v2/cdn-react-minimal.d.ts +23 -0
- package/dist/v2/cdn-react-minimal.js +26 -0
- package/dist/v2/core/client.js +1 -1
- package/dist/v2/core/config/environment.js +2 -1
- package/dist/v2/core/funnelClient.d.ts +98 -10
- package/dist/v2/core/funnelClient.js +121 -27
- package/dist/v2/core/index.d.ts +0 -1
- package/dist/v2/core/index.js +0 -2
- package/dist/v2/core/isoData.d.ts +4 -4
- package/dist/v2/core/isoData.js +7 -7
- package/dist/v2/core/pixelMapping.js +64 -26
- package/dist/v2/core/resources/checkout.d.ts +10 -0
- package/dist/v2/core/resources/checkout.js +6 -0
- package/dist/v2/core/resources/expressPaymentMethods.d.ts +1 -0
- package/dist/v2/core/resources/payments.d.ts +7 -2
- package/dist/v2/core/resources/payments.js +1 -0
- package/dist/v2/core/resources/postPurchases.d.ts +17 -0
- package/dist/v2/core/resources/postPurchases.js +20 -0
- package/dist/v2/core/utils/deviceInfo.d.ts +0 -10
- package/dist/v2/core/utils/deviceInfo.js +152 -76
- package/dist/v2/core/utils/order.d.ts +2 -0
- package/dist/v2/core/utils/pluginConfig.js +18 -22
- package/dist/v2/index.d.ts +4 -3
- package/dist/v2/index.js +4 -2
- package/dist/v2/react/components/FunnelScriptInjector.js +145 -77
- package/dist/v2/react/components/StripeExpressButton.d.ts +13 -0
- package/dist/v2/react/components/StripeExpressButton.js +171 -0
- package/dist/v2/react/components/WhopCheckout.js +7 -1
- package/dist/v2/react/hooks/payment-actions/useProcessorAuthAction.js +21 -3
- package/dist/v2/react/hooks/useApplePayCheckout.js +8 -8
- package/dist/v2/react/hooks/useCheckoutQuery.d.ts +10 -0
- package/dist/v2/react/hooks/useCheckoutQuery.js +21 -13
- package/dist/v2/react/hooks/useFunnel.d.ts +15 -4
- package/dist/v2/react/hooks/useFunnel.js +8 -4
- package/dist/v2/react/hooks/useGoogleAutocomplete.d.ts +2 -0
- package/dist/v2/react/hooks/useGoogleAutocomplete.js +29 -15
- package/dist/v2/react/hooks/useISOData.d.ts +2 -5
- package/dist/v2/react/hooks/useISOData.js +25 -26
- package/dist/v2/react/hooks/usePaymentPolling.d.ts +2 -2
- package/dist/v2/react/hooks/usePixelTracking.js +151 -70
- package/dist/v2/react/hooks/usePostPurchasesQuery.js +34 -2
- package/dist/v2/react/hooks/useRemappableParams.d.ts +2 -6
- package/dist/v2/react/hooks/useRemappableParams.js +23 -23
- package/dist/v2/react/hooks/useSetPaymentMethod.d.ts +16 -0
- package/dist/v2/react/hooks/useSetPaymentMethod.js +33 -0
- package/dist/v2/react/hooks/useStepConfig.d.ts +23 -6
- package/dist/v2/react/hooks/useStepConfig.js +14 -7
- package/dist/v2/react/hooks/useTranslation.js +23 -8
- package/dist/v2/react/index.d.ts +8 -1
- package/dist/v2/react/index.js +3 -0
- package/dist/v2/react/providers/ExpressPaymentMethodsProvider.d.ts +1 -0
- package/dist/v2/react/providers/ExpressPaymentMethodsProvider.js +9 -1
- package/dist/v2/react/providers/TagadaProvider.js +4 -4
- package/dist/v2/standalone/index.d.ts +21 -3
- package/dist/v2/standalone/index.js +25 -3
- package/dist/v2/standalone/payment-service.d.ts +134 -0
- package/dist/v2/standalone/payment-service.js +928 -0
- package/package.json +4 -2
|
@@ -72,9 +72,43 @@ function convertValueToMajor(params) {
|
|
|
72
72
|
}
|
|
73
73
|
return params;
|
|
74
74
|
}
|
|
75
|
+
/** Convert top-level monetary fields (shipping, tax, subtotal, total_discount) from minor → major units. */
|
|
76
|
+
function convertMonetaryFieldsToMajor(params) {
|
|
77
|
+
const currency = params.currency ? String(params.currency) : null;
|
|
78
|
+
if (!currency)
|
|
79
|
+
return params;
|
|
80
|
+
const result = { ...params };
|
|
81
|
+
for (const field of ['shipping', 'tax', 'subtotal', 'total_discount']) {
|
|
82
|
+
if (result[field] != null) {
|
|
83
|
+
result[field] = minorToMajor(Number(result[field]), currency);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
return result;
|
|
87
|
+
}
|
|
88
|
+
/** Convert price fields inside contents[] items from minor → major units. */
|
|
89
|
+
function convertContentsPricesToMajor(params) {
|
|
90
|
+
if (!params.contents || !Array.isArray(params.contents))
|
|
91
|
+
return params;
|
|
92
|
+
const currency = params.currency ? String(params.currency) : null;
|
|
93
|
+
const result = { ...params };
|
|
94
|
+
result.contents = params.contents.map((item) => {
|
|
95
|
+
const itemCurrency = item.currency ? String(item.currency) : currency;
|
|
96
|
+
if (!itemCurrency)
|
|
97
|
+
return { ...item };
|
|
98
|
+
const converted = { ...item };
|
|
99
|
+
if (converted.price != null)
|
|
100
|
+
converted.price = minorToMajor(Number(converted.price), itemCurrency);
|
|
101
|
+
if (converted.original_price != null)
|
|
102
|
+
converted.original_price = minorToMajor(Number(converted.original_price), itemCurrency);
|
|
103
|
+
return converted;
|
|
104
|
+
});
|
|
105
|
+
return result;
|
|
106
|
+
}
|
|
75
107
|
function baseTransform(params) {
|
|
76
108
|
let p = ensureCurrencyUppercase(params);
|
|
77
109
|
p = convertValueToMajor(p);
|
|
110
|
+
p = convertMonetaryFieldsToMajor(p);
|
|
111
|
+
p = convertContentsPricesToMajor(p);
|
|
78
112
|
return p;
|
|
79
113
|
}
|
|
80
114
|
// ---------------------------------------------------------------------------
|
|
@@ -98,6 +132,14 @@ export function mapMetaEvent(eventName, parameters) {
|
|
|
98
132
|
if (params.contents && Array.isArray(params.contents)) {
|
|
99
133
|
params.content_ids = params.contents.map((i) => i.content_id).filter(Boolean);
|
|
100
134
|
params.content_type = params.content_type ?? 'product';
|
|
135
|
+
// Reshape contents to Meta's expected format (prices already converted by baseTransform)
|
|
136
|
+
params.contents = params.contents.map((item) => ({
|
|
137
|
+
id: item.content_id,
|
|
138
|
+
quantity: item.quantity != null ? Number(item.quantity) : undefined,
|
|
139
|
+
item_price: item.price != null ? Number(item.price) : undefined,
|
|
140
|
+
content_name: item.content_name,
|
|
141
|
+
content_category: item.content_category,
|
|
142
|
+
}));
|
|
101
143
|
}
|
|
102
144
|
return { name, params };
|
|
103
145
|
}
|
|
@@ -124,35 +166,29 @@ const TIKTOK_EVENT_MAP = {
|
|
|
124
166
|
export function mapTikTokEvent(eventName, parameters) {
|
|
125
167
|
const name = TIKTOK_EVENT_MAP[eventName] ?? eventName;
|
|
126
168
|
const params = baseTransform(parameters);
|
|
127
|
-
// Convert additional monetary fields from minor to major units
|
|
128
|
-
const currency = params.currency ? String(params.currency) : null;
|
|
129
|
-
if (currency) {
|
|
130
|
-
for (const field of ['shipping', 'tax', 'subtotal', 'total_discount']) {
|
|
131
|
-
if (params[field] != null) {
|
|
132
|
-
params[field] = minorToMajor(Number(params[field]), currency);
|
|
133
|
-
}
|
|
134
|
-
}
|
|
135
|
-
}
|
|
136
169
|
if (params.contents && Array.isArray(params.contents)) {
|
|
137
|
-
|
|
170
|
+
const ids = params.contents
|
|
171
|
+
.map((i) => i.content_id)
|
|
172
|
+
.filter(Boolean);
|
|
173
|
+
// TikTok requires content_id (singular) at the top level for VSA
|
|
174
|
+
if (!params.content_id && ids.length > 0) {
|
|
175
|
+
params.content_id = ids.length === 1 ? ids[0] : JSON.stringify(ids);
|
|
176
|
+
}
|
|
177
|
+
// Also keep content_ids for backwards compatibility
|
|
138
178
|
if (!params.content_ids) {
|
|
139
|
-
params.content_ids =
|
|
140
|
-
.map((i) => i.content_id)
|
|
141
|
-
.filter(Boolean);
|
|
179
|
+
params.content_ids = ids;
|
|
142
180
|
}
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
};
|
|
155
|
-
});
|
|
181
|
+
// Reshape contents to TikTok's expected format (prices already converted by baseTransform)
|
|
182
|
+
params.contents = params.contents.map((item) => ({
|
|
183
|
+
content_id: item.content_id,
|
|
184
|
+
content_name: item.content_name,
|
|
185
|
+
content_type: item.content_type ?? 'product',
|
|
186
|
+
content_category: item.content_category,
|
|
187
|
+
price: item.price != null ? Number(item.price) : undefined,
|
|
188
|
+
original_price: item.original_price != null ? Number(item.original_price) : undefined,
|
|
189
|
+
quantity: item.quantity != null ? Number(item.quantity) : undefined,
|
|
190
|
+
description: item.description,
|
|
191
|
+
}));
|
|
156
192
|
}
|
|
157
193
|
// Set required fields for InitiateCheckout
|
|
158
194
|
if (eventName === 'InitiateCheckout') {
|
|
@@ -224,6 +260,7 @@ export function mapPinterestEvent(eventName, parameters) {
|
|
|
224
260
|
params.order_quantity = Number(params.order_quantity);
|
|
225
261
|
}
|
|
226
262
|
if (params.contents && Array.isArray(params.contents)) {
|
|
263
|
+
// Reshape to Pinterest line_items format (prices already converted by baseTransform)
|
|
227
264
|
params.line_items = params.contents.map((item) => ({
|
|
228
265
|
product_id: item.content_id,
|
|
229
266
|
product_name: item.content_name,
|
|
@@ -259,6 +296,7 @@ export function mapGTMEvent(eventName, parameters) {
|
|
|
259
296
|
params.num_items = Number(params.num_items);
|
|
260
297
|
}
|
|
261
298
|
if (params.contents && Array.isArray(params.contents)) {
|
|
299
|
+
// Reshape to GA4 items format (prices already converted by baseTransform)
|
|
262
300
|
params.items = params.contents.map((item) => ({
|
|
263
301
|
item_id: item.content_id,
|
|
264
302
|
item_name: item.content_name,
|
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
*/
|
|
5
5
|
import type { OrderAddress } from '../types';
|
|
6
6
|
import { ApiClient } from './apiClient';
|
|
7
|
+
export type PaymentMethodName = 'paypal' | 'card' | 'klarna' | 'zelle';
|
|
7
8
|
export interface CheckoutLineItem {
|
|
8
9
|
externalProductId?: string | null;
|
|
9
10
|
externalVariantId?: string | null;
|
|
@@ -26,6 +27,7 @@ export interface CheckoutInitParams {
|
|
|
26
27
|
currency?: string;
|
|
27
28
|
locale?: string;
|
|
28
29
|
};
|
|
30
|
+
enabledOrderBumpOfferIds?: string[];
|
|
29
31
|
}
|
|
30
32
|
export interface UpsellTrigger {
|
|
31
33
|
id: string;
|
|
@@ -185,6 +187,7 @@ export interface CheckoutSummaryItem {
|
|
|
185
187
|
intervalCount?: number;
|
|
186
188
|
totalBillingCycles?: number;
|
|
187
189
|
unitAmountAfterFirstCycle?: number;
|
|
190
|
+
properties?: Record<string, unknown>;
|
|
188
191
|
orderLineItemProduct: {
|
|
189
192
|
name: string;
|
|
190
193
|
};
|
|
@@ -257,6 +260,7 @@ export interface CheckoutSessionPreviewItem {
|
|
|
257
260
|
totalBillingCycles?: number;
|
|
258
261
|
unitAmountAfterFirstCycle?: number;
|
|
259
262
|
subscriptionSettings?: Record<string, unknown>;
|
|
263
|
+
properties?: Record<string, unknown>;
|
|
260
264
|
orderLineItemProduct: {
|
|
261
265
|
name: string | null;
|
|
262
266
|
} | null;
|
|
@@ -519,4 +523,10 @@ export declare class CheckoutResource {
|
|
|
519
523
|
success: boolean;
|
|
520
524
|
preview: CheckoutSessionPreview;
|
|
521
525
|
}>;
|
|
526
|
+
/**
|
|
527
|
+
* Set selected payment method for a checkout session
|
|
528
|
+
*/
|
|
529
|
+
setPaymentMethod(checkoutSessionId: string, paymentMethodName: PaymentMethodName): Promise<{
|
|
530
|
+
success: boolean;
|
|
531
|
+
}>;
|
|
522
532
|
}
|
|
@@ -213,4 +213,10 @@ export class CheckoutResource {
|
|
|
213
213
|
async previewCheckoutSession(params) {
|
|
214
214
|
return this.apiClient.post('/api/v1/checkout-sessions/preview', params);
|
|
215
215
|
}
|
|
216
|
+
/**
|
|
217
|
+
* Set selected payment method for a checkout session
|
|
218
|
+
*/
|
|
219
|
+
async setPaymentMethod(checkoutSessionId, paymentMethodName) {
|
|
220
|
+
return this.apiClient.post(`/api/v1/checkout-sessions/${checkoutSessionId}/payment-method`, { paymentMethodName });
|
|
221
|
+
}
|
|
216
222
|
}
|
|
@@ -8,9 +8,9 @@ export interface Payment {
|
|
|
8
8
|
id: string;
|
|
9
9
|
status: string;
|
|
10
10
|
subStatus: string;
|
|
11
|
-
requireAction: 'none' | 'redirect' | 'redirect_to_payment' | 'error' | 'radar';
|
|
11
|
+
requireAction: 'none' | 'redirect' | 'redirect_to_payment' | 'error' | 'radar' | 'stripe_express_checkout';
|
|
12
12
|
requireActionData?: {
|
|
13
|
-
type: 'redirect' | 'redirect_to_payment' | 'threeds_auth' | 'processor_auth' | 'error' | 'stripe_radar' | 'finix_radar' | 'radar' | 'kesspay_auth' | 'trustflow_auth' | 'mastercard_auth';
|
|
13
|
+
type: 'redirect' | 'redirect_to_payment' | 'threeds_auth' | 'processor_auth' | 'error' | 'stripe_radar' | 'finix_radar' | 'radar' | 'kesspay_auth' | 'trustflow_auth' | 'mastercard_auth' | 'stripe_express_checkout';
|
|
14
14
|
url?: string;
|
|
15
15
|
processed: boolean;
|
|
16
16
|
processorId?: string;
|
|
@@ -32,6 +32,10 @@ export interface Payment {
|
|
|
32
32
|
orderId?: string;
|
|
33
33
|
publishableKey?: string;
|
|
34
34
|
};
|
|
35
|
+
stripeExpressCheckout?: {
|
|
36
|
+
clientSecret: string;
|
|
37
|
+
publishableKey?: string;
|
|
38
|
+
};
|
|
35
39
|
provider?: string;
|
|
36
40
|
isTest?: boolean;
|
|
37
41
|
};
|
|
@@ -242,6 +246,7 @@ export declare class PaymentsResource {
|
|
|
242
246
|
paymentFlowId?: string;
|
|
243
247
|
processorId?: string;
|
|
244
248
|
paymentMethod?: string;
|
|
249
|
+
isExpress?: boolean;
|
|
245
250
|
}): Promise<PaymentResponse>;
|
|
246
251
|
/**
|
|
247
252
|
* Get card payment instruments for customer
|
|
@@ -147,6 +147,7 @@ export class PaymentsResource {
|
|
|
147
147
|
...(options.paymentFlowId && { paymentFlowId: options.paymentFlowId }),
|
|
148
148
|
...(options.processorId && { processorId: options.processorId }),
|
|
149
149
|
...(options.paymentMethod && { paymentMethod: options.paymentMethod }),
|
|
150
|
+
...(options.isExpress && { isExpress: options.isExpress }),
|
|
150
151
|
};
|
|
151
152
|
console.log('[PaymentsResource] Request body being sent:', JSON.stringify(requestBody, null, 2));
|
|
152
153
|
const response = await this.apiClient.post('/api/public/v1/checkout/pay-v2', requestBody);
|
|
@@ -110,6 +110,7 @@ export interface CheckoutSessionState {
|
|
|
110
110
|
selectedVariants: Record<string, string>;
|
|
111
111
|
loadingVariants: Record<string, boolean>;
|
|
112
112
|
isUpdatingSummary: boolean;
|
|
113
|
+
isWhop?: boolean;
|
|
113
114
|
}
|
|
114
115
|
export declare class PostPurchasesResource {
|
|
115
116
|
private apiClient;
|
|
@@ -172,6 +173,22 @@ export declare class PostPurchasesResource {
|
|
|
172
173
|
* Get order summary for a checkout session
|
|
173
174
|
*/
|
|
174
175
|
getOrderSummary(sessionId: string, includeVariantOptions?: boolean): Promise<any>;
|
|
176
|
+
/**
|
|
177
|
+
* Get payment methods for a checkout session
|
|
178
|
+
*/
|
|
179
|
+
getPaymentMethods(checkoutSessionId: string): Promise<{
|
|
180
|
+
type: string;
|
|
181
|
+
}[]>;
|
|
182
|
+
/**
|
|
183
|
+
* Charge a Whop post-purchase using the stored payment method from the original order
|
|
184
|
+
*/
|
|
185
|
+
chargeWhopPostPurchase(params: {
|
|
186
|
+
checkoutSessionId: string;
|
|
187
|
+
orderId: string;
|
|
188
|
+
storeId: string;
|
|
189
|
+
}): Promise<{
|
|
190
|
+
checkoutRequestId: string;
|
|
191
|
+
}>;
|
|
175
192
|
/**
|
|
176
193
|
* Update line items for a checkout session
|
|
177
194
|
*/
|
|
@@ -108,6 +108,26 @@ export class PostPurchasesResource {
|
|
|
108
108
|
includeVariantOptions,
|
|
109
109
|
});
|
|
110
110
|
}
|
|
111
|
+
/**
|
|
112
|
+
* Get payment methods for a checkout session
|
|
113
|
+
*/
|
|
114
|
+
async getPaymentMethods(checkoutSessionId) {
|
|
115
|
+
return this.apiClient.get(`/api/v1/payment-methods?checkoutSessionId=${encodeURIComponent(checkoutSessionId)}`);
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* Charge a Whop post-purchase using the stored payment method from the original order
|
|
119
|
+
*/
|
|
120
|
+
async chargeWhopPostPurchase(params) {
|
|
121
|
+
return this.apiClient.post(`/api/v1/checkout-sessions/${params.checkoutSessionId}/whop-charge-payment`, {
|
|
122
|
+
checkoutSessionId: params.checkoutSessionId,
|
|
123
|
+
orderId: params.orderId,
|
|
124
|
+
storeId: params.storeId,
|
|
125
|
+
metadata: {
|
|
126
|
+
comingFromPostPurchase: true,
|
|
127
|
+
originalOrderId: params.orderId,
|
|
128
|
+
},
|
|
129
|
+
});
|
|
130
|
+
}
|
|
111
131
|
/**
|
|
112
132
|
* Update line items for a checkout session
|
|
113
133
|
*/
|
|
@@ -5,7 +5,6 @@ export interface DeviceInfo {
|
|
|
5
5
|
major: string;
|
|
6
6
|
name: string;
|
|
7
7
|
version: string;
|
|
8
|
-
type?: string;
|
|
9
8
|
};
|
|
10
9
|
os: {
|
|
11
10
|
name: string;
|
|
@@ -36,17 +35,8 @@ export interface DeviceInfo {
|
|
|
36
35
|
isAppleSilicon: boolean;
|
|
37
36
|
};
|
|
38
37
|
}
|
|
39
|
-
/**
|
|
40
|
-
* Get browser locale
|
|
41
|
-
*/
|
|
42
38
|
export declare function getBrowserLocale(): string;
|
|
43
|
-
/**
|
|
44
|
-
* Collect all device information using UAParser
|
|
45
|
-
*/
|
|
46
39
|
export declare function collectDeviceInfo(): DeviceInfo;
|
|
47
|
-
/**
|
|
48
|
-
* Get URL parameters for session initialization
|
|
49
|
-
*/
|
|
50
40
|
export declare function getUrlParams(): {
|
|
51
41
|
locale?: string;
|
|
52
42
|
currency?: string;
|
|
@@ -1,44 +1,159 @@
|
|
|
1
|
-
import { UAParser } from '@ua-parser-js/pro-enterprise';
|
|
2
|
-
import { isBot, isChromeFamily, isStandalonePWA, isAppleSilicon, } from '@ua-parser-js/pro-enterprise/helpers';
|
|
3
|
-
/**
|
|
4
|
-
* Get screen resolution
|
|
5
|
-
*/
|
|
6
1
|
function getScreenResolution() {
|
|
7
|
-
return {
|
|
8
|
-
width: window.screen.width,
|
|
9
|
-
height: window.screen.height,
|
|
10
|
-
};
|
|
2
|
+
return { width: window.screen.width, height: window.screen.height };
|
|
11
3
|
}
|
|
12
|
-
/**
|
|
13
|
-
* Get timezone
|
|
14
|
-
*/
|
|
15
4
|
function getTimeZone() {
|
|
16
5
|
try {
|
|
17
6
|
return Intl.DateTimeFormat().resolvedOptions().timeZone;
|
|
18
7
|
}
|
|
19
|
-
catch
|
|
20
|
-
console.error('Failed to get timezone:', error);
|
|
8
|
+
catch {
|
|
21
9
|
return 'UTC';
|
|
22
10
|
}
|
|
23
11
|
}
|
|
24
|
-
/**
|
|
25
|
-
* Get browser locale
|
|
26
|
-
*/
|
|
27
12
|
export function getBrowserLocale() {
|
|
28
13
|
try {
|
|
29
14
|
return navigator.language || 'en-US';
|
|
30
15
|
}
|
|
31
|
-
catch
|
|
32
|
-
console.error('Failed to get browser locale:', error);
|
|
16
|
+
catch {
|
|
33
17
|
return 'en-US';
|
|
34
18
|
}
|
|
35
19
|
}
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
20
|
+
// ---------------------------------------------------------------------------
|
|
21
|
+
// Lightweight UA parsing — no third-party library needed
|
|
22
|
+
// ---------------------------------------------------------------------------
|
|
23
|
+
const UA = typeof navigator !== 'undefined' ? navigator.userAgent : '';
|
|
24
|
+
/** Extract first match's groups from a UA string */
|
|
25
|
+
function match(ua, ...patterns) {
|
|
26
|
+
for (const p of patterns) {
|
|
27
|
+
const m = ua.match(p);
|
|
28
|
+
if (m)
|
|
29
|
+
return m;
|
|
30
|
+
}
|
|
31
|
+
return null;
|
|
32
|
+
}
|
|
33
|
+
function parseBrowser(ua) {
|
|
34
|
+
// Order matters — more specific first
|
|
35
|
+
const tests = [
|
|
36
|
+
[/EdgA?\/([\d.]+)/i, 'Edge'],
|
|
37
|
+
[/OPR\/([\d.]+)/i, 'Opera'],
|
|
38
|
+
[/Brave\/([\d.]+)/i, 'Brave'],
|
|
39
|
+
[/Vivaldi\/([\d.]+)/i, 'Vivaldi'],
|
|
40
|
+
[/SamsungBrowser\/([\d.]+)/i, 'Samsung Internet'],
|
|
41
|
+
[/UCBrowser\/([\d.]+)/i, 'UC Browser'],
|
|
42
|
+
[/Firefox\/([\d.]+)/i, 'Firefox'],
|
|
43
|
+
[/CriOS\/([\d.]+)/i, 'Chrome'], // Chrome on iOS
|
|
44
|
+
[/FxiOS\/([\d.]+)/i, 'Firefox'], // Firefox on iOS
|
|
45
|
+
[/Chrome\/([\d.]+)/i, 'Chrome'],
|
|
46
|
+
[/Version\/([\d.]+).*Safari/i, 'Safari'],
|
|
47
|
+
[/Safari\/([\d.]+)/i, 'Safari'],
|
|
48
|
+
];
|
|
49
|
+
for (const [re, name] of tests) {
|
|
50
|
+
const m = ua.match(re);
|
|
51
|
+
if (m) {
|
|
52
|
+
const version = m[1] || '';
|
|
53
|
+
return { name, version, major: version.split('.')[0] || '' };
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
return { name: '', version: '', major: '' };
|
|
57
|
+
}
|
|
58
|
+
function parseOS(ua) {
|
|
59
|
+
const m = match(ua, /Windows NT ([\d.]+)/i) ||
|
|
60
|
+
match(ua, /Mac OS X ([\d_.]+)/i) ||
|
|
61
|
+
match(ua, /Android ([\d.]+)/i) ||
|
|
62
|
+
match(ua, /iPhone OS ([\d_]+)/i) ||
|
|
63
|
+
match(ua, /iPad.*OS ([\d_]+)/i) ||
|
|
64
|
+
match(ua, /CrOS[\w ]*\/([\d.]+)/i) ||
|
|
65
|
+
match(ua, /Linux/i);
|
|
66
|
+
if (!m)
|
|
67
|
+
return { name: '', version: '' };
|
|
68
|
+
const raw = m[0];
|
|
69
|
+
const version = (m[1] || '').replace(/_/g, '.');
|
|
70
|
+
if (/Windows/i.test(raw)) {
|
|
71
|
+
const ntMap = {
|
|
72
|
+
'10.0': '10', '6.3': '8.1', '6.2': '8', '6.1': '7', '6.0': 'Vista', '5.1': 'XP',
|
|
73
|
+
};
|
|
74
|
+
return { name: 'Windows', version: ntMap[version] || version };
|
|
75
|
+
}
|
|
76
|
+
if (/Mac OS X/i.test(raw))
|
|
77
|
+
return { name: 'macOS', version };
|
|
78
|
+
if (/Android/i.test(raw))
|
|
79
|
+
return { name: 'Android', version };
|
|
80
|
+
if (/iPhone|iPad/i.test(raw))
|
|
81
|
+
return { name: 'iOS', version };
|
|
82
|
+
if (/CrOS/i.test(raw))
|
|
83
|
+
return { name: 'Chrome OS', version };
|
|
84
|
+
if (/Linux/i.test(raw))
|
|
85
|
+
return { name: 'Linux', version: '' };
|
|
86
|
+
return { name: '', version: '' };
|
|
87
|
+
}
|
|
88
|
+
function parseDevice(ua) {
|
|
89
|
+
if (/iPad/i.test(ua))
|
|
90
|
+
return { type: 'tablet', vendor: 'Apple', model: 'iPad' };
|
|
91
|
+
if (/iPhone/i.test(ua))
|
|
92
|
+
return { type: 'mobile', vendor: 'Apple', model: 'iPhone' };
|
|
93
|
+
if (/iPod/i.test(ua))
|
|
94
|
+
return { type: 'mobile', vendor: 'Apple', model: 'iPod' };
|
|
95
|
+
if (/Android/i.test(ua)) {
|
|
96
|
+
const m = ua.match(/Android[\s\d.]+;\s*([^)]+?)(?:\s+Build)/i);
|
|
97
|
+
const mobile = /Mobile/i.test(ua);
|
|
98
|
+
return { type: mobile ? 'mobile' : 'tablet', model: m?.[1]?.trim() };
|
|
99
|
+
}
|
|
100
|
+
return undefined;
|
|
101
|
+
}
|
|
102
|
+
function parseEngine(ua) {
|
|
103
|
+
const m = match(ua, /AppleWebKit\/([\d.]+)/i) ||
|
|
104
|
+
match(ua, /Gecko\/([\d.]+)/i) ||
|
|
105
|
+
match(ua, /Trident\/([\d.]+)/i) ||
|
|
106
|
+
match(ua, /Presto\/([\d.]+)/i);
|
|
107
|
+
if (!m)
|
|
108
|
+
return { name: '', version: '' };
|
|
109
|
+
const raw = m[0];
|
|
110
|
+
const version = m[1] || '';
|
|
111
|
+
if (/AppleWebKit/i.test(raw))
|
|
112
|
+
return { name: 'WebKit', version };
|
|
113
|
+
if (/Gecko/i.test(raw))
|
|
114
|
+
return { name: 'Gecko', version };
|
|
115
|
+
if (/Trident/i.test(raw))
|
|
116
|
+
return { name: 'Trident', version };
|
|
117
|
+
if (/Presto/i.test(raw))
|
|
118
|
+
return { name: 'Presto', version };
|
|
119
|
+
return { name: '', version: '' };
|
|
120
|
+
}
|
|
121
|
+
function parseCPU(ua) {
|
|
122
|
+
if (/x86_64|x64|amd64|Win64/i.test(ua))
|
|
123
|
+
return { architecture: 'amd64' };
|
|
124
|
+
if (/ia32|x86/i.test(ua))
|
|
125
|
+
return { architecture: 'ia32' };
|
|
126
|
+
if (/aarch64|arm64/i.test(ua))
|
|
127
|
+
return { architecture: 'arm64' };
|
|
128
|
+
if (/arm/i.test(ua))
|
|
129
|
+
return { architecture: 'arm' };
|
|
130
|
+
return { architecture: '' };
|
|
131
|
+
}
|
|
132
|
+
// ---------------------------------------------------------------------------
|
|
133
|
+
// Flags
|
|
134
|
+
// ---------------------------------------------------------------------------
|
|
135
|
+
function detectIsBot() {
|
|
136
|
+
if (navigator.webdriver)
|
|
137
|
+
return true;
|
|
138
|
+
return /bot|crawl|spider|slurp|Googlebot|bingbot|yandex|baidu|duckduck|facebookexternalhit|Twitterbot|linkedinbot|embedly|quora|pinterest|redditbot|Slackbot|Discordbot|WhatsApp|TelegramBot|Applebot/i.test(UA);
|
|
139
|
+
}
|
|
140
|
+
function detectIsChromeFamily(browserName) {
|
|
141
|
+
return /Chrome|Chromium|Edge|Brave|Opera|Vivaldi|Arc/i.test(browserName);
|
|
142
|
+
}
|
|
143
|
+
function detectIsStandalonePWA() {
|
|
144
|
+
return window.matchMedia('(display-mode: standalone)').matches
|
|
145
|
+
|| navigator.standalone === true;
|
|
146
|
+
}
|
|
147
|
+
function detectIsAppleSilicon(osName) {
|
|
148
|
+
if (!/mac/i.test(osName))
|
|
149
|
+
return false;
|
|
150
|
+
return navigator.maxTouchPoints > 0;
|
|
151
|
+
}
|
|
152
|
+
// ---------------------------------------------------------------------------
|
|
153
|
+
// Main
|
|
154
|
+
// ---------------------------------------------------------------------------
|
|
39
155
|
export function collectDeviceInfo() {
|
|
40
156
|
if (typeof window === 'undefined') {
|
|
41
|
-
// Server-side fallback
|
|
42
157
|
return {
|
|
43
158
|
userAgent: {
|
|
44
159
|
name: '',
|
|
@@ -50,75 +165,36 @@ export function collectDeviceInfo() {
|
|
|
50
165
|
},
|
|
51
166
|
screenResolution: { width: 0, height: 0 },
|
|
52
167
|
timeZone: 'UTC',
|
|
53
|
-
flags: {
|
|
54
|
-
isBot: false,
|
|
55
|
-
isChromeFamily: false,
|
|
56
|
-
isStandalonePWA: false,
|
|
57
|
-
isAppleSilicon: false,
|
|
58
|
-
},
|
|
168
|
+
flags: { isBot: false, isChromeFamily: false, isStandalonePWA: false, isAppleSilicon: false },
|
|
59
169
|
};
|
|
60
170
|
}
|
|
61
|
-
const
|
|
62
|
-
const
|
|
63
|
-
|
|
171
|
+
const browser = parseBrowser(UA);
|
|
172
|
+
const os = parseOS(UA);
|
|
173
|
+
const device = parseDevice(UA);
|
|
174
|
+
const engine = parseEngine(UA);
|
|
175
|
+
const cpu = parseCPU(UA);
|
|
64
176
|
let flags;
|
|
65
177
|
try {
|
|
66
178
|
flags = {
|
|
67
|
-
isBot:
|
|
68
|
-
isChromeFamily:
|
|
69
|
-
isStandalonePWA:
|
|
70
|
-
isAppleSilicon:
|
|
179
|
+
isBot: detectIsBot(),
|
|
180
|
+
isChromeFamily: detectIsChromeFamily(browser.name),
|
|
181
|
+
isStandalonePWA: detectIsStandalonePWA(),
|
|
182
|
+
isAppleSilicon: detectIsAppleSilicon(os.name),
|
|
71
183
|
};
|
|
72
184
|
}
|
|
73
|
-
catch
|
|
74
|
-
|
|
75
|
-
flags = {
|
|
76
|
-
isBot: false,
|
|
77
|
-
isChromeFamily: false,
|
|
78
|
-
isStandalonePWA: false,
|
|
79
|
-
isAppleSilicon: false,
|
|
80
|
-
};
|
|
185
|
+
catch {
|
|
186
|
+
flags = { isBot: false, isChromeFamily: false, isStandalonePWA: false, isAppleSilicon: false };
|
|
81
187
|
}
|
|
82
188
|
return {
|
|
83
|
-
userAgent: {
|
|
84
|
-
name: result.ua,
|
|
85
|
-
browser: {
|
|
86
|
-
major: result.browser.major || '',
|
|
87
|
-
name: result.browser.name || '',
|
|
88
|
-
version: result.browser.version || '',
|
|
89
|
-
type: result.browser.type,
|
|
90
|
-
},
|
|
91
|
-
os: {
|
|
92
|
-
name: result.os.name || '',
|
|
93
|
-
version: result.os.version || '',
|
|
94
|
-
},
|
|
95
|
-
device: result.device.model || result.device.type || result.device.vendor
|
|
96
|
-
? {
|
|
97
|
-
model: result.device.model,
|
|
98
|
-
type: result.device.type,
|
|
99
|
-
vendor: result.device.vendor,
|
|
100
|
-
}
|
|
101
|
-
: undefined,
|
|
102
|
-
engine: {
|
|
103
|
-
name: result.engine.name || '',
|
|
104
|
-
version: result.engine.version || '',
|
|
105
|
-
},
|
|
106
|
-
cpu: {
|
|
107
|
-
architecture: result.cpu.architecture || '',
|
|
108
|
-
},
|
|
109
|
-
},
|
|
189
|
+
userAgent: { name: UA, browser, os, device, engine, cpu },
|
|
110
190
|
screenResolution: getScreenResolution(),
|
|
111
191
|
timeZone: getTimeZone(),
|
|
112
192
|
flags,
|
|
113
193
|
};
|
|
114
194
|
}
|
|
115
|
-
/**
|
|
116
|
-
* Get URL parameters for session initialization
|
|
117
|
-
*/
|
|
118
195
|
export function getUrlParams() {
|
|
119
|
-
if (typeof window === 'undefined')
|
|
196
|
+
if (typeof window === 'undefined')
|
|
120
197
|
return {};
|
|
121
|
-
}
|
|
122
198
|
const params = new URLSearchParams(window.location.search);
|
|
123
199
|
return {
|
|
124
200
|
locale: params.get('locale') || undefined,
|
|
@@ -24,6 +24,7 @@ export interface OrderLineItem {
|
|
|
24
24
|
totalBillingCycles: number;
|
|
25
25
|
unitAmountAfterFirstCycle: number;
|
|
26
26
|
subscriptionSettings?: Record<string, unknown> | null;
|
|
27
|
+
properties?: Record<string, unknown>;
|
|
27
28
|
orderLineItemProduct: {
|
|
28
29
|
id: string;
|
|
29
30
|
name: string;
|
|
@@ -46,6 +47,7 @@ export interface OrderLineItem {
|
|
|
46
47
|
}
|
|
47
48
|
export interface Order {
|
|
48
49
|
id: string;
|
|
50
|
+
orderNumber: number;
|
|
49
51
|
storeId: string;
|
|
50
52
|
accountId: string;
|
|
51
53
|
createdAt: string;
|
|
@@ -159,6 +159,22 @@ const getMetaContent = (name) => {
|
|
|
159
159
|
const metaTag = document.querySelector(`meta[name="${name}"]`);
|
|
160
160
|
return metaTag?.getAttribute('content') || undefined;
|
|
161
161
|
};
|
|
162
|
+
/**
|
|
163
|
+
* Read config from window global (preferred) or fall back to x-plugin-config meta tag
|
|
164
|
+
* (retrocompat with old deployments that still inject the meta tag).
|
|
165
|
+
*/
|
|
166
|
+
const readWindowConfig = () => {
|
|
167
|
+
if (typeof window !== 'undefined' && window.__TAGADA_PLUGIN_CONFIG__) {
|
|
168
|
+
return window.__TAGADA_PLUGIN_CONFIG__;
|
|
169
|
+
}
|
|
170
|
+
try {
|
|
171
|
+
const encoded = getMetaContent('x-plugin-config');
|
|
172
|
+
if (encoded)
|
|
173
|
+
return JSON.parse(decodeURIComponent(encoded));
|
|
174
|
+
}
|
|
175
|
+
catch { /* ignore */ }
|
|
176
|
+
return {};
|
|
177
|
+
};
|
|
162
178
|
/**
|
|
163
179
|
* Synchronously get plugin config from meta tags
|
|
164
180
|
* This is a lightweight sync alternative to loadPluginConfig
|
|
@@ -173,16 +189,7 @@ export function getPluginConfig() {
|
|
|
173
189
|
const storeId = getMetaContent('x-plugin-store-id');
|
|
174
190
|
const accountId = getMetaContent('x-plugin-account-id');
|
|
175
191
|
const basePath = getMetaContent('x-plugin-base-path') || '/';
|
|
176
|
-
|
|
177
|
-
try {
|
|
178
|
-
const encodedConfig = getMetaContent('x-plugin-config');
|
|
179
|
-
if (encodedConfig) {
|
|
180
|
-
config = JSON.parse(decodeURIComponent(encodedConfig));
|
|
181
|
-
}
|
|
182
|
-
}
|
|
183
|
-
catch (error) {
|
|
184
|
-
console.warn('[TagadaSDK] Failed to parse plugin config from meta tag:', error);
|
|
185
|
-
}
|
|
192
|
+
const config = readWindowConfig();
|
|
186
193
|
return { storeId, accountId, basePath, config };
|
|
187
194
|
}
|
|
188
195
|
/**
|
|
@@ -206,18 +213,7 @@ const loadProductionConfig = async () => {
|
|
|
206
213
|
return null;
|
|
207
214
|
}
|
|
208
215
|
const basePath = getMetaContent('x-plugin-base-path') || '/';
|
|
209
|
-
|
|
210
|
-
let config = {};
|
|
211
|
-
try {
|
|
212
|
-
const encodedConfig = getMetaContent('x-plugin-config');
|
|
213
|
-
if (encodedConfig) {
|
|
214
|
-
const decodedConfig = decodeURIComponent(encodedConfig);
|
|
215
|
-
config = JSON.parse(decodedConfig);
|
|
216
|
-
}
|
|
217
|
-
}
|
|
218
|
-
catch (error) {
|
|
219
|
-
console.warn('⚠️ Failed to parse plugin config from meta tag:', error);
|
|
220
|
-
}
|
|
216
|
+
const config = readWindowConfig();
|
|
221
217
|
// Final validation
|
|
222
218
|
if (!accountId) {
|
|
223
219
|
console.warn('⚠️ Plugin config: Account ID not found in meta tags');
|