@tagadapay/plugin-sdk 4.0.0 → 4.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1129 -1129
- package/build-cdn.js +499 -499
- package/dist/external-tracker.js +156 -2
- package/dist/external-tracker.min.js +2 -2
- package/dist/external-tracker.min.js.map +4 -4
- package/dist/react/providers/TagadaProvider.js +5 -5
- package/dist/tagada-react-sdk-minimal.min.js +2 -2
- package/dist/tagada-react-sdk-minimal.min.js.map +4 -4
- package/dist/tagada-react-sdk.js +696 -245
- package/dist/tagada-react-sdk.min.js +2 -2
- package/dist/tagada-react-sdk.min.js.map +4 -4
- package/dist/tagada-sdk.js +2908 -94
- package/dist/tagada-sdk.min.js +2 -2
- package/dist/tagada-sdk.min.js.map +4 -4
- package/dist/v2/core/funnelClient.d.ts +40 -0
- package/dist/v2/core/funnelClient.js +30 -0
- package/dist/v2/core/pixelTracker.d.ts +51 -0
- package/dist/v2/core/pixelTracker.js +425 -0
- package/dist/v2/core/resources/checkout.d.ts +45 -1
- package/dist/v2/core/resources/checkout.js +13 -3
- package/dist/v2/core/resources/offers.d.ts +1 -1
- package/dist/v2/core/resources/offers.js +3 -1
- package/dist/v2/core/resources/promotionEvents.d.ts +5 -0
- package/dist/v2/core/resources/promotionEvents.js +2 -0
- package/dist/v2/core/resources/promotions.d.ts +6 -1
- package/dist/v2/core/resources/promotions.js +6 -1
- package/dist/v2/core/resources/shippingRates.d.ts +18 -0
- package/dist/v2/core/resources/shippingRates.js +18 -0
- package/dist/v2/core/utils/clickIdResolver.d.ts +79 -0
- package/dist/v2/core/utils/clickIdResolver.js +169 -0
- package/dist/v2/core/utils/index.d.ts +2 -0
- package/dist/v2/core/utils/index.js +4 -0
- package/dist/v2/core/utils/metaEventId.d.ts +14 -0
- package/dist/v2/core/utils/metaEventId.js +16 -0
- package/dist/v2/core/utils/previewModeIndicator.js +101 -101
- package/dist/v2/index.d.ts +7 -0
- package/dist/v2/index.js +10 -0
- package/dist/v2/react/components/ApplePayButton.js +50 -0
- package/dist/v2/react/components/FunnelScriptInjector.js +9 -9
- package/dist/v2/react/components/GooglePayButton.js +39 -1
- package/dist/v2/react/components/StripeExpressButton.js +54 -2
- package/dist/v2/react/hooks/payment-actions/useNgeniusThreedsAction.js +11 -11
- package/dist/v2/react/hooks/useCheckoutQuery.js +41 -29
- package/dist/v2/react/hooks/useDiscountsQuery.js +4 -0
- package/dist/v2/react/hooks/useFunnel.d.ts +7 -0
- package/dist/v2/react/hooks/useFunnel.js +2 -1
- package/dist/v2/react/hooks/usePixelTracking.d.ts +10 -5
- package/dist/v2/react/hooks/usePixelTracking.js +32 -374
- package/dist/v2/react/hooks/usePreviewOffer.d.ts +3 -1
- package/dist/v2/react/hooks/usePreviewOffer.js +8 -2
- package/dist/v2/react/hooks/usePromotionsQuery.js +9 -3
- package/dist/v2/react/hooks/useShippingRatesQuery.js +36 -21
- package/dist/v2/react/hooks/useStepConfig.d.ts +9 -0
- package/dist/v2/react/hooks/useStepConfig.js +5 -1
- package/dist/v2/react/index.d.ts +4 -0
- package/dist/v2/react/index.js +8 -0
- package/dist/v2/react/providers/TagadaProvider.js +18 -5
- package/dist/v2/standalone/apple-pay-service.d.ts +1 -1
- package/dist/v2/standalone/index.d.ts +3 -0
- package/dist/v2/standalone/index.js +23 -0
- package/dist/v2/standalone/payment-service.d.ts +54 -1
- package/dist/v2/standalone/payment-service.js +228 -61
- package/package.json +115 -115
|
@@ -2,9 +2,11 @@
|
|
|
2
2
|
* Checkout Resource Client
|
|
3
3
|
* Axios-based API client for checkout endpoints
|
|
4
4
|
*/
|
|
5
|
+
import { PROMOTION_APPLIED, PROMOTION_REMOVED } from './promotionEvents';
|
|
5
6
|
export class CheckoutResource {
|
|
6
|
-
constructor(apiClient) {
|
|
7
|
+
constructor(apiClient, bus) {
|
|
7
8
|
this.apiClient = apiClient;
|
|
9
|
+
this.bus = bus;
|
|
8
10
|
}
|
|
9
11
|
/**
|
|
10
12
|
* Initialize a new checkout session (sync mode)
|
|
@@ -131,15 +133,23 @@ export class CheckoutResource {
|
|
|
131
133
|
* Apply promotion code
|
|
132
134
|
*/
|
|
133
135
|
async applyPromotionCode(checkoutSessionId, code) {
|
|
134
|
-
|
|
136
|
+
const result = await this.apiClient.post(`/api/v1/checkout-sessions/${checkoutSessionId}/promotions/apply`, {
|
|
135
137
|
code,
|
|
136
138
|
});
|
|
139
|
+
if (result.success) {
|
|
140
|
+
void this.bus?.emit(PROMOTION_APPLIED, { checkoutSessionId });
|
|
141
|
+
}
|
|
142
|
+
return result;
|
|
137
143
|
}
|
|
138
144
|
/**
|
|
139
145
|
* Remove promotion
|
|
140
146
|
*/
|
|
141
147
|
async removePromotion(checkoutSessionId, promotionId) {
|
|
142
|
-
|
|
148
|
+
const result = await this.apiClient.delete(`/api/v1/checkout-sessions/${checkoutSessionId}/promotions/${promotionId}`);
|
|
149
|
+
if (result.success) {
|
|
150
|
+
void this.bus?.emit(PROMOTION_REMOVED, { checkoutSessionId });
|
|
151
|
+
}
|
|
152
|
+
return result;
|
|
143
153
|
}
|
|
144
154
|
/**
|
|
145
155
|
* Get applied promotions
|
|
@@ -240,7 +240,7 @@ export declare class OffersResource {
|
|
|
240
240
|
productId?: string;
|
|
241
241
|
variantId: string;
|
|
242
242
|
quantity: number;
|
|
243
|
-
}>, returnUrl?: string, mainOrderId?: string): Promise<{
|
|
243
|
+
}>, returnUrl?: string, mainOrderId?: string, initiatedBy?: 'merchant' | 'customer'): Promise<{
|
|
244
244
|
preview: any;
|
|
245
245
|
checkout: {
|
|
246
246
|
checkoutUrl: string;
|
|
@@ -68,13 +68,14 @@ export class OffersResource {
|
|
|
68
68
|
* @param returnUrl - Optional return URL for checkout
|
|
69
69
|
* @param mainOrderId - Optional main order ID (for upsells)
|
|
70
70
|
*/
|
|
71
|
-
async payPreviewedOffer(offerId, currency = '', lineItems, returnUrl, mainOrderId) {
|
|
71
|
+
async payPreviewedOffer(offerId, currency = '', lineItems, returnUrl, mainOrderId, initiatedBy) {
|
|
72
72
|
console.log('💳 [OffersResource] Calling pay-preview API:', {
|
|
73
73
|
offerId,
|
|
74
74
|
currency,
|
|
75
75
|
lineItems,
|
|
76
76
|
returnUrl,
|
|
77
77
|
mainOrderId,
|
|
78
|
+
initiatedBy,
|
|
78
79
|
endpoint: `/api/v1/offers/${offerId}/pay-preview`,
|
|
79
80
|
});
|
|
80
81
|
const response = await this.apiClient.post(`/api/v1/offers/${offerId}/pay-preview`, {
|
|
@@ -83,6 +84,7 @@ export class OffersResource {
|
|
|
83
84
|
lineItems,
|
|
84
85
|
returnUrl: returnUrl || (typeof window !== 'undefined' ? window.location.href : ''),
|
|
85
86
|
mainOrderId,
|
|
87
|
+
...(initiatedBy ? { initiatedBy } : {}),
|
|
86
88
|
});
|
|
87
89
|
console.log('📥 [OffersResource] Pay-preview API response:', response);
|
|
88
90
|
return response;
|
|
@@ -4,6 +4,10 @@
|
|
|
4
4
|
*/
|
|
5
5
|
import { ApiClient } from './apiClient';
|
|
6
6
|
import { Promotion } from './checkout';
|
|
7
|
+
import { EventBus } from '../utils/eventBus';
|
|
8
|
+
import { PROMOTION_APPLIED, PROMOTION_REMOVED, PromotionEventPayload } from './promotionEvents';
|
|
9
|
+
export { PROMOTION_APPLIED, PROMOTION_REMOVED };
|
|
10
|
+
export type { PromotionEventPayload };
|
|
7
11
|
export interface PromotionCodeValidation {
|
|
8
12
|
isValid: boolean;
|
|
9
13
|
code: string;
|
|
@@ -18,7 +22,8 @@ export interface PromotionCodeValidation {
|
|
|
18
22
|
}
|
|
19
23
|
export declare class PromotionsResource {
|
|
20
24
|
private apiClient;
|
|
21
|
-
|
|
25
|
+
private bus?;
|
|
26
|
+
constructor(apiClient: ApiClient, bus?: EventBus | undefined);
|
|
22
27
|
/**
|
|
23
28
|
* Get applied promotions for a checkout session
|
|
24
29
|
*/
|
|
@@ -2,9 +2,12 @@
|
|
|
2
2
|
* Promotions Resource Client
|
|
3
3
|
* Axios-based API client for promotion endpoints
|
|
4
4
|
*/
|
|
5
|
+
import { PROMOTION_APPLIED, PROMOTION_REMOVED } from './promotionEvents';
|
|
6
|
+
export { PROMOTION_APPLIED, PROMOTION_REMOVED };
|
|
5
7
|
export class PromotionsResource {
|
|
6
|
-
constructor(apiClient) {
|
|
8
|
+
constructor(apiClient, bus) {
|
|
7
9
|
this.apiClient = apiClient;
|
|
10
|
+
this.bus = bus;
|
|
8
11
|
}
|
|
9
12
|
/**
|
|
10
13
|
* Get applied promotions for a checkout session
|
|
@@ -19,6 +22,7 @@ export class PromotionsResource {
|
|
|
19
22
|
try {
|
|
20
23
|
const response = await this.apiClient.post(`/api/v1/checkout-sessions/${checkoutSessionId}/promotions/apply`, { code: code.trim() });
|
|
21
24
|
if (response.success) {
|
|
25
|
+
void this.bus?.emit(PROMOTION_APPLIED, { checkoutSessionId });
|
|
22
26
|
return { success: true, promotion: response.promotion };
|
|
23
27
|
}
|
|
24
28
|
else {
|
|
@@ -47,6 +51,7 @@ export class PromotionsResource {
|
|
|
47
51
|
try {
|
|
48
52
|
const response = await this.apiClient.delete(`/api/v1/checkout-sessions/${checkoutSessionId}/promotions/${promotionId}`);
|
|
49
53
|
if (response.success) {
|
|
54
|
+
void this.bus?.emit(PROMOTION_REMOVED, { checkoutSessionId });
|
|
50
55
|
return { success: true };
|
|
51
56
|
}
|
|
52
57
|
else {
|
|
@@ -29,6 +29,11 @@ export interface ShippingRatesPreviewResponse {
|
|
|
29
29
|
apiKey: string | null;
|
|
30
30
|
};
|
|
31
31
|
}
|
|
32
|
+
export interface SelectPreviewShippingRateResponse {
|
|
33
|
+
success: boolean;
|
|
34
|
+
selectedRateId: string | null;
|
|
35
|
+
rates: ShippingRate[];
|
|
36
|
+
}
|
|
32
37
|
export declare class ShippingRatesResource {
|
|
33
38
|
private apiClient;
|
|
34
39
|
constructor(apiClient: ApiClient);
|
|
@@ -48,4 +53,17 @@ export declare class ShippingRatesResource {
|
|
|
48
53
|
* Useful for showing rates before user enters email
|
|
49
54
|
*/
|
|
50
55
|
previewShippingRates(sessionId: string, params: ShippingRatesPreviewParams): Promise<ShippingRatesPreviewResponse>;
|
|
56
|
+
/**
|
|
57
|
+
* Preview shipping rates for a country AND persist the auto-selected rate on the session.
|
|
58
|
+
*
|
|
59
|
+
* Difference with `previewShippingRates` (which is read-only):
|
|
60
|
+
* - the server picks the highlighted (or cheapest) rate for `countryCode`
|
|
61
|
+
* - it writes `checkoutSession.shippingRateId` to the DB
|
|
62
|
+
* - the next `getCheckout()` will return a summary that includes shipping cost
|
|
63
|
+
*
|
|
64
|
+
* Use this from the studio when geolocation (or any client-side hint) determines a country
|
|
65
|
+
* BEFORE the user submits the full address. Otherwise the wallet sheet (Apple Pay, etc.) will
|
|
66
|
+
* be opened with a stale total that doesn't include shipping fees.
|
|
67
|
+
*/
|
|
68
|
+
selectPreviewShippingRate(sessionId: string, params: ShippingRatesPreviewParams): Promise<SelectPreviewShippingRateResponse>;
|
|
51
69
|
}
|
|
@@ -31,4 +31,22 @@ export class ShippingRatesResource {
|
|
|
31
31
|
}
|
|
32
32
|
return this.apiClient.get(`/api/v1/checkout-sessions/${sessionId}/shipping-rates/preview?${queryParams.toString()}`);
|
|
33
33
|
}
|
|
34
|
+
/**
|
|
35
|
+
* Preview shipping rates for a country AND persist the auto-selected rate on the session.
|
|
36
|
+
*
|
|
37
|
+
* Difference with `previewShippingRates` (which is read-only):
|
|
38
|
+
* - the server picks the highlighted (or cheapest) rate for `countryCode`
|
|
39
|
+
* - it writes `checkoutSession.shippingRateId` to the DB
|
|
40
|
+
* - the next `getCheckout()` will return a summary that includes shipping cost
|
|
41
|
+
*
|
|
42
|
+
* Use this from the studio when geolocation (or any client-side hint) determines a country
|
|
43
|
+
* BEFORE the user submits the full address. Otherwise the wallet sheet (Apple Pay, etc.) will
|
|
44
|
+
* be opened with a stale total that doesn't include shipping fees.
|
|
45
|
+
*/
|
|
46
|
+
async selectPreviewShippingRate(sessionId, params) {
|
|
47
|
+
return this.apiClient.post(`/api/v1/checkout-sessions/${sessionId}/shipping-rates/select-preview`, {
|
|
48
|
+
countryCode: params.countryCode,
|
|
49
|
+
...(params.stateCode ? { stateCode: params.stateCode } : {}),
|
|
50
|
+
});
|
|
51
|
+
}
|
|
34
52
|
}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Click-ID resolver — pure, runtime-agnostic utility.
|
|
3
|
+
*
|
|
4
|
+
* Many ad-trackers (ClickFlare, Voluum, Binom, RedTrack, ClickMagick, …) all
|
|
5
|
+
* follow the same pattern: assign a `click_id` on the visitor's first ad-click
|
|
6
|
+
* on the lander, then expect that id to flow end-to-end (lander → checkout →
|
|
7
|
+
* thank-you) so they can match a server postback to the original visit.
|
|
8
|
+
*
|
|
9
|
+
* This module:
|
|
10
|
+
* - Resolves the click id from URL query params (preferred — survives the
|
|
11
|
+
* redirect chain) and first-party cookies (fallback — set by the tracker's
|
|
12
|
+
* lander script).
|
|
13
|
+
* - Publishes the resolved value to `window.TagadaPay.tracking` so merchant-
|
|
14
|
+
* authored postback snippets in `stepConfig.scripts` can read it without
|
|
15
|
+
* re-implementing URL/cookie scraping.
|
|
16
|
+
*
|
|
17
|
+
* No React, no SDK class dependencies — works from React provider, standalone
|
|
18
|
+
* SDK, the studio builder, the external-tracker bundle, or a raw <script>.
|
|
19
|
+
*
|
|
20
|
+
* The lists are intentionally explicit (not regexes) so we never accidentally
|
|
21
|
+
* promote a random param to a "click id" — they are the exact identifiers
|
|
22
|
+
* documented by each tracker / ad platform.
|
|
23
|
+
*/
|
|
24
|
+
/** URL query-param names ad trackers / ad platforms use for click ids.
|
|
25
|
+
*
|
|
26
|
+
* Order matters — the resolver returns the FIRST matching entry, so put
|
|
27
|
+
* tracker-specific canonical names BEFORE generic fallbacks like
|
|
28
|
+
* `click_id` or `clickid`. This is why ClickFlare's `cf_click_id` comes
|
|
29
|
+
* before `click_id`, and why the per-tracker canonical token (`cid` for
|
|
30
|
+
* Voluum, `rtkclickid` for RedTrack, `cmc_tid` for ClickMagick) comes
|
|
31
|
+
* first within its own block.
|
|
32
|
+
*/
|
|
33
|
+
export declare const CLICK_ID_URL_PARAMS: readonly ["cf_click_id", "cid", "rtkclickid", "cmc_tid", "cmc_id", "cmcid", "clickid", "click_id", "gclid", "gbraid", "wbraid", "fbclid", "msclkid", "ttclid", "twclid", "li_fat_id", "epik", "dclid", "yclid", "irclickid"];
|
|
34
|
+
/** Cookie names ad trackers store their click ids under.
|
|
35
|
+
*
|
|
36
|
+
* Same ordering rule as URL params: canonical first-party cookies first
|
|
37
|
+
* (e.g. `rtkclickid-store` for RedTrack), legacy fallbacks last.
|
|
38
|
+
*/
|
|
39
|
+
export declare const CLICK_ID_COOKIES: readonly ["cf_click_id", "cfclid", "rtkclickid-store", "_rtkclickid", "rtkclickid", "_voluum", "_voluumclickid", "_binom", "_binomclickid", "_mck", "cmcid", "skro-click-id", "click_id"];
|
|
40
|
+
export type ClickIdSource = 'url' | 'cookie';
|
|
41
|
+
export interface ResolvedClickId {
|
|
42
|
+
/** The first matching click id, or null. */
|
|
43
|
+
clickId: string | null;
|
|
44
|
+
/** Where it was sourced from. */
|
|
45
|
+
source: ClickIdSource | null;
|
|
46
|
+
/** Which key (param name or cookie name) matched. */
|
|
47
|
+
key: string | null;
|
|
48
|
+
/** Every click-id-shaped value found, namespaced as `url:foo` / `cookie:bar`. */
|
|
49
|
+
all: Record<string, string>;
|
|
50
|
+
}
|
|
51
|
+
export interface TagadaTrackingGlobal extends ResolvedClickId {
|
|
52
|
+
/** ms since epoch when the resolver last ran. */
|
|
53
|
+
resolvedAt: number;
|
|
54
|
+
}
|
|
55
|
+
declare global {
|
|
56
|
+
interface Window {
|
|
57
|
+
TagadaPay?: {
|
|
58
|
+
tracking?: TagadaTrackingGlobal;
|
|
59
|
+
[key: string]: unknown;
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Resolve the visitor's click id. Pure — no side effects, safe in SSR.
|
|
65
|
+
*
|
|
66
|
+
* Resolution order:
|
|
67
|
+
* 1. URL params (in CLICK_ID_URL_PARAMS order) — most authoritative.
|
|
68
|
+
* 2. Cookies (in CLICK_ID_COOKIES order) — fallback.
|
|
69
|
+
*/
|
|
70
|
+
export declare function resolveClickId(): ResolvedClickId;
|
|
71
|
+
/**
|
|
72
|
+
* Resolve and merge tracking metadata into `window.TagadaPay.tracking`.
|
|
73
|
+
*
|
|
74
|
+
* Idempotent and namespace-safe: we only ever touch the `tracking` key, never
|
|
75
|
+
* other namespaces (e.g. `order` set by the thank-you page).
|
|
76
|
+
*
|
|
77
|
+
* Returns the published value, or `null` when called outside a browser.
|
|
78
|
+
*/
|
|
79
|
+
export declare function publishTrackingGlobal(): TagadaTrackingGlobal | null;
|
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Click-ID resolver — pure, runtime-agnostic utility.
|
|
3
|
+
*
|
|
4
|
+
* Many ad-trackers (ClickFlare, Voluum, Binom, RedTrack, ClickMagick, …) all
|
|
5
|
+
* follow the same pattern: assign a `click_id` on the visitor's first ad-click
|
|
6
|
+
* on the lander, then expect that id to flow end-to-end (lander → checkout →
|
|
7
|
+
* thank-you) so they can match a server postback to the original visit.
|
|
8
|
+
*
|
|
9
|
+
* This module:
|
|
10
|
+
* - Resolves the click id from URL query params (preferred — survives the
|
|
11
|
+
* redirect chain) and first-party cookies (fallback — set by the tracker's
|
|
12
|
+
* lander script).
|
|
13
|
+
* - Publishes the resolved value to `window.TagadaPay.tracking` so merchant-
|
|
14
|
+
* authored postback snippets in `stepConfig.scripts` can read it without
|
|
15
|
+
* re-implementing URL/cookie scraping.
|
|
16
|
+
*
|
|
17
|
+
* No React, no SDK class dependencies — works from React provider, standalone
|
|
18
|
+
* SDK, the studio builder, the external-tracker bundle, or a raw <script>.
|
|
19
|
+
*
|
|
20
|
+
* The lists are intentionally explicit (not regexes) so we never accidentally
|
|
21
|
+
* promote a random param to a "click id" — they are the exact identifiers
|
|
22
|
+
* documented by each tracker / ad platform.
|
|
23
|
+
*/
|
|
24
|
+
// -----------------------------------------------------------------------------
|
|
25
|
+
// Configuration — explicit allow-lists
|
|
26
|
+
// -----------------------------------------------------------------------------
|
|
27
|
+
/** URL query-param names ad trackers / ad platforms use for click ids.
|
|
28
|
+
*
|
|
29
|
+
* Order matters — the resolver returns the FIRST matching entry, so put
|
|
30
|
+
* tracker-specific canonical names BEFORE generic fallbacks like
|
|
31
|
+
* `click_id` or `clickid`. This is why ClickFlare's `cf_click_id` comes
|
|
32
|
+
* before `click_id`, and why the per-tracker canonical token (`cid` for
|
|
33
|
+
* Voluum, `rtkclickid` for RedTrack, `cmc_tid` for ClickMagick) comes
|
|
34
|
+
* first within its own block.
|
|
35
|
+
*/
|
|
36
|
+
export const CLICK_ID_URL_PARAMS = Object.freeze([
|
|
37
|
+
// Postback ad-trackers (most specific first)
|
|
38
|
+
'cf_click_id', // ClickFlare canonical
|
|
39
|
+
'cid', // Voluum canonical (also: ClickFlare receiving-side)
|
|
40
|
+
'rtkclickid', // RedTrack canonical
|
|
41
|
+
'cmc_tid', // ClickMagick canonical (replaces legacy #S2#)
|
|
42
|
+
'cmc_id', // ClickMagick legacy
|
|
43
|
+
'cmcid', // ClickMagick alt
|
|
44
|
+
'clickid', // Generic affiliate-network token (Binom, RedTrack, Voluum, …)
|
|
45
|
+
'click_id', // Generic snake_case (ClickFlare, ClickMagick, …)
|
|
46
|
+
// Ad-platform native click ids
|
|
47
|
+
'gclid', // Google Ads
|
|
48
|
+
'gbraid', // Google Ads (iOS app)
|
|
49
|
+
'wbraid', // Google Ads (web→app)
|
|
50
|
+
'fbclid', // Meta
|
|
51
|
+
'msclkid', // Microsoft Ads
|
|
52
|
+
'ttclid', // TikTok
|
|
53
|
+
'twclid', // X / Twitter
|
|
54
|
+
'li_fat_id', // LinkedIn
|
|
55
|
+
'epik', // Pinterest
|
|
56
|
+
'dclid', // Display & Video 360
|
|
57
|
+
'yclid', // Yandex
|
|
58
|
+
'irclickid', // Impact
|
|
59
|
+
]);
|
|
60
|
+
/** Cookie names ad trackers store their click ids under.
|
|
61
|
+
*
|
|
62
|
+
* Same ordering rule as URL params: canonical first-party cookies first
|
|
63
|
+
* (e.g. `rtkclickid-store` for RedTrack), legacy fallbacks last.
|
|
64
|
+
*/
|
|
65
|
+
export const CLICK_ID_COOKIES = Object.freeze([
|
|
66
|
+
// ClickFlare
|
|
67
|
+
'cf_click_id',
|
|
68
|
+
'cfclid',
|
|
69
|
+
// RedTrack — `rtkclickid-store` is the canonical first-party cookie
|
|
70
|
+
// set by RedTrack's Universal Tracking Script.
|
|
71
|
+
'rtkclickid-store',
|
|
72
|
+
'_rtkclickid',
|
|
73
|
+
'rtkclickid',
|
|
74
|
+
// Voluum
|
|
75
|
+
'_voluum',
|
|
76
|
+
'_voluumclickid',
|
|
77
|
+
// Binom
|
|
78
|
+
'_binom',
|
|
79
|
+
'_binomclickid',
|
|
80
|
+
// ClickMagick
|
|
81
|
+
'_mck',
|
|
82
|
+
'cmcid',
|
|
83
|
+
// Other
|
|
84
|
+
'skro-click-id', // Skro
|
|
85
|
+
'click_id', // generic catch-all (last)
|
|
86
|
+
]);
|
|
87
|
+
// -----------------------------------------------------------------------------
|
|
88
|
+
// Pure resolution
|
|
89
|
+
// -----------------------------------------------------------------------------
|
|
90
|
+
function readCookie(name) {
|
|
91
|
+
if (typeof document === 'undefined' || !document.cookie)
|
|
92
|
+
return null;
|
|
93
|
+
const escaped = name.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
94
|
+
const m = document.cookie.match(new RegExp('(?:^|; )' + escaped + '=([^;]*)'));
|
|
95
|
+
if (!m)
|
|
96
|
+
return null;
|
|
97
|
+
try {
|
|
98
|
+
return decodeURIComponent(m[1]);
|
|
99
|
+
}
|
|
100
|
+
catch {
|
|
101
|
+
return m[1];
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
function readUrlParam(search, name) {
|
|
105
|
+
if (!search)
|
|
106
|
+
return null;
|
|
107
|
+
try {
|
|
108
|
+
return new URLSearchParams(search).get(name);
|
|
109
|
+
}
|
|
110
|
+
catch {
|
|
111
|
+
return null;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* Resolve the visitor's click id. Pure — no side effects, safe in SSR.
|
|
116
|
+
*
|
|
117
|
+
* Resolution order:
|
|
118
|
+
* 1. URL params (in CLICK_ID_URL_PARAMS order) — most authoritative.
|
|
119
|
+
* 2. Cookies (in CLICK_ID_COOKIES order) — fallback.
|
|
120
|
+
*/
|
|
121
|
+
export function resolveClickId() {
|
|
122
|
+
const all = {};
|
|
123
|
+
let clickId = null;
|
|
124
|
+
let source = null;
|
|
125
|
+
let key = null;
|
|
126
|
+
const search = typeof window !== 'undefined' ? window.location?.search ?? '' : '';
|
|
127
|
+
for (const name of CLICK_ID_URL_PARAMS) {
|
|
128
|
+
const v = readUrlParam(search, name);
|
|
129
|
+
if (v) {
|
|
130
|
+
all[`url:${name}`] = v;
|
|
131
|
+
if (clickId === null) {
|
|
132
|
+
clickId = v;
|
|
133
|
+
source = 'url';
|
|
134
|
+
key = name;
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
for (const name of CLICK_ID_COOKIES) {
|
|
139
|
+
const v = readCookie(name);
|
|
140
|
+
if (v) {
|
|
141
|
+
all[`cookie:${name}`] = v;
|
|
142
|
+
if (clickId === null) {
|
|
143
|
+
clickId = v;
|
|
144
|
+
source = 'cookie';
|
|
145
|
+
key = name;
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
return { clickId, source, key, all };
|
|
150
|
+
}
|
|
151
|
+
// -----------------------------------------------------------------------------
|
|
152
|
+
// Browser-side publication
|
|
153
|
+
// -----------------------------------------------------------------------------
|
|
154
|
+
/**
|
|
155
|
+
* Resolve and merge tracking metadata into `window.TagadaPay.tracking`.
|
|
156
|
+
*
|
|
157
|
+
* Idempotent and namespace-safe: we only ever touch the `tracking` key, never
|
|
158
|
+
* other namespaces (e.g. `order` set by the thank-you page).
|
|
159
|
+
*
|
|
160
|
+
* Returns the published value, or `null` when called outside a browser.
|
|
161
|
+
*/
|
|
162
|
+
export function publishTrackingGlobal() {
|
|
163
|
+
if (typeof window === 'undefined')
|
|
164
|
+
return null;
|
|
165
|
+
const resolved = resolveClickId();
|
|
166
|
+
const tracking = { ...resolved, resolvedAt: Date.now() };
|
|
167
|
+
window.TagadaPay = { ...(window.TagadaPay ?? {}), tracking };
|
|
168
|
+
return tracking;
|
|
169
|
+
}
|
|
@@ -14,3 +14,7 @@ export * from './sessionStorage';
|
|
|
14
14
|
export * from './funnelQueryKeys';
|
|
15
15
|
// Config hot reload for live preview editing
|
|
16
16
|
export * from './configHotReload';
|
|
17
|
+
// Meta CAPI / browser pixel deduplication helper.
|
|
18
|
+
export * from './metaEventId';
|
|
19
|
+
// Click-id resolver for ad-tracker integrations (ClickFlare, Voluum, Binom, …)
|
|
20
|
+
export * from './clickIdResolver';
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Stable event_id helper for Meta CAPI / browser pixel deduplication.
|
|
3
|
+
*
|
|
4
|
+
* The browser side (`fbq('track', name, params, { eventID })`) and the server
|
|
5
|
+
* side (`ServerEvent.setEventId(...)`) must publish the same value to let Meta
|
|
6
|
+
* dedup. This helper is the single source of truth for the formula, mirrored
|
|
7
|
+
* on the server in `src/app/services/meta/meta-event-id.ts`. A parity test in
|
|
8
|
+
* the server package keeps them byte-identical.
|
|
9
|
+
*
|
|
10
|
+
* Convention: `${eventName}_${entityId}`. Different event names get different
|
|
11
|
+
* IDs even when fired for the same entity (e.g. a Purchase + Subscribe pair on
|
|
12
|
+
* the same order), so each dedups independently.
|
|
13
|
+
*/
|
|
14
|
+
export declare function makeMetaEventId(eventName: string, entityId: string): string;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Stable event_id helper for Meta CAPI / browser pixel deduplication.
|
|
3
|
+
*
|
|
4
|
+
* The browser side (`fbq('track', name, params, { eventID })`) and the server
|
|
5
|
+
* side (`ServerEvent.setEventId(...)`) must publish the same value to let Meta
|
|
6
|
+
* dedup. This helper is the single source of truth for the formula, mirrored
|
|
7
|
+
* on the server in `src/app/services/meta/meta-event-id.ts`. A parity test in
|
|
8
|
+
* the server package keeps them byte-identical.
|
|
9
|
+
*
|
|
10
|
+
* Convention: `${eventName}_${entityId}`. Different event names get different
|
|
11
|
+
* IDs even when fired for the same entity (e.g. a Purchase + Subscribe pair on
|
|
12
|
+
* the same order), so each dedups independently.
|
|
13
|
+
*/
|
|
14
|
+
export function makeMetaEventId(eventName, entityId) {
|
|
15
|
+
return `${eventName}_${entityId}`;
|
|
16
|
+
}
|