@economist/web-apple-pay 0.1.1-beta.0

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 +186 -0
  2. package/dist/index.d.ts +1 -0
  3. package/dist/index.js +1642 -0
  4. package/dist/src/apple-pay-button.d.ts +45 -0
  5. package/dist/src/apple-pay-button.js +1621 -0
  6. package/dist/src/apple-pay-button.stories.js +1934 -0
  7. package/dist/src/apple-pay-logo.d.ts +1 -0
  8. package/dist/src/apple-pay-logo.js +14 -0
  9. package/dist/src/apple-pay-modal.d.ts +12 -0
  10. package/dist/src/apple-pay-modal.js +442 -0
  11. package/dist/src/apple-pay-modal.stories.js +1778 -0
  12. package/dist/src/clients/payment-checkout/apple-session.d.ts +18 -0
  13. package/dist/src/clients/payment-checkout/apple-session.js +243 -0
  14. package/dist/src/clients/payment-checkout/billing-contact-details.d.ts +21 -0
  15. package/dist/src/clients/payment-checkout/billing-contact-details.js +109 -0
  16. package/dist/src/clients/payment-checkout/config.d.ts +9 -0
  17. package/dist/src/clients/payment-checkout/config.js +23 -0
  18. package/dist/src/clients/payment-checkout/contact-validation.d.ts +21 -0
  19. package/dist/src/clients/payment-checkout/contact-validation.js +50 -0
  20. package/dist/src/clients/payment-checkout/detects.d.ts +3 -0
  21. package/dist/src/clients/payment-checkout/detects.js +56 -0
  22. package/dist/src/clients/payment-checkout/index.d.ts +5 -0
  23. package/dist/src/clients/payment-checkout/index.js +83 -0
  24. package/dist/src/clients/payment-checkout/messages.d.ts +33 -0
  25. package/dist/src/clients/payment-checkout/messages.js +19 -0
  26. package/dist/src/clients/payment-checkout/payment-handler.d.ts +36 -0
  27. package/dist/src/clients/payment-checkout/payment-handler.js +389 -0
  28. package/dist/src/clients/payment-checkout/session.d.ts +2 -0
  29. package/dist/src/clients/payment-checkout/session.js +67 -0
  30. package/dist/src/clients/payment-checkout/shipping-contact-details.d.ts +10 -0
  31. package/dist/src/clients/payment-checkout/shipping-contact-details.js +83 -0
  32. package/dist/src/clients/payment-checkout/types.d.ts +118 -0
  33. package/dist/src/clients/payment-checkout/types.js +14 -0
  34. package/dist/src/index.d.ts +7 -0
  35. package/dist/src/index.js +1642 -0
  36. package/dist/src/stories/fixtures/offer.d.ts +2 -0
  37. package/dist/src/stories/fixtures/offer.js +28 -0
  38. package/dist/src/stories/utils/story-env.d.ts +11 -0
  39. package/dist/src/stories/utils/story-env.js +148 -0
  40. package/dist/src/types/html-template.d.js +0 -0
  41. package/dist/src/types/offer.types.d.ts +73 -0
  42. package/dist/src/types/offer.types.js +0 -0
  43. package/dist/src/utils/billing-countries.d.ts +19 -0
  44. package/dist/src/utils/billing-countries.js +263 -0
  45. package/dist/src/utils/define.d.ts +1 -0
  46. package/dist/src/utils/define.js +9 -0
  47. package/dist/src/utils/expressCheckoutRedirect.d.ts +55 -0
  48. package/dist/src/utils/expressCheckoutRedirect.js +46 -0
  49. package/dist/src/utils/expressCheckoutTracking.d.ts +64 -0
  50. package/dist/src/utils/expressCheckoutTracking.js +84 -0
  51. package/dist/src/utils/pricing.d.ts +1 -0
  52. package/dist/src/utils/pricing.js +60 -0
  53. package/dist/src/utils/recaptcha.d.ts +22 -0
  54. package/dist/src/utils/recaptcha.js +54 -0
  55. package/package.json +64 -0
@@ -0,0 +1,2 @@
1
+ import type { Offer } from '../../types/offer.types';
2
+ export declare const STORYBOOK_SAMPLE_OFFER: Offer;
@@ -0,0 +1,28 @@
1
+ // src/stories/fixtures/offer.ts
2
+ var STORYBOOK_SAMPLE_OFFER = {
3
+ sku_id: "TEG-APPLEPAY-STORY-001",
4
+ display_name: "Economist Digital",
5
+ price: {
6
+ purchase: 12,
7
+ pre_discount: 24,
8
+ currency_code: "USD",
9
+ discounted: true,
10
+ tax: { inclusive: false }
11
+ },
12
+ product: {
13
+ type: "subscription",
14
+ variant: "digital",
15
+ offer_type: "standard"
16
+ },
17
+ subscription_options: {
18
+ free_trial: false,
19
+ renewal_interval: { duration: 1, unit: "month" },
20
+ renewal_price: 12,
21
+ auto_renewal: true,
22
+ variable_term: false,
23
+ display_name: "Monthly"
24
+ }
25
+ };
26
+ export {
27
+ STORYBOOK_SAMPLE_OFFER
28
+ };
@@ -0,0 +1,11 @@
1
+ import type { SessionResponse } from '../../clients/payment-checkout/types';
2
+ export type ApplePayStoryEnvironmentOptions = {
3
+ applePaySupported: boolean;
4
+ sessionResponse?: SessionResponse;
5
+ sessionStatus?: number;
6
+ debugBypassEnabled?: boolean;
7
+ bypassApplePayChecks?: boolean;
8
+ };
9
+ export declare const applyApplePayStoryEnvironment: ({ applePaySupported, sessionResponse, sessionStatus, debugBypassEnabled, bypassApplePayChecks, }: ApplePayStoryEnvironmentOptions) => void;
10
+ export declare const withApplePayStoryEnvironment: <TArgs>(options: ApplePayStoryEnvironmentOptions, renderFn: (args: TArgs) => HTMLElement) => (args: TArgs) => HTMLElement;
11
+ export declare const clickWhenElementReady: (root: ParentNode, selector: string, maxAttempts?: number) => void;
@@ -0,0 +1,148 @@
1
+ // src/clients/payment-checkout/config.ts
2
+ var runtimeConfig = null;
3
+ var configureApplePay = (config) => {
4
+ console.log("[web-apple-pay] configureApplePay called", {
5
+ merchantId: config.merchantId,
6
+ walletApiBaseUrl: config.walletApiBaseUrl,
7
+ debugBypassEnabled: config.debugBypassEnabled,
8
+ recaptchaSiteKey: config.recaptchaSiteKey ? "***" : void 0
9
+ });
10
+ runtimeConfig = {
11
+ ...config
12
+ };
13
+ };
14
+ var resetApplePayConfig = () => {
15
+ console.log("[web-apple-pay] resetApplePayConfig called");
16
+ runtimeConfig = null;
17
+ };
18
+
19
+ // src/stories/utils/story-env.ts
20
+ var STORYBOOK_WALLET_BASE_URL = "https://storybook-wallet.economist.com";
21
+ var STORYBOOK_SAFARI_UA = "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/16.0 Safari/605.1.15";
22
+ var defaultSessionResponse = {
23
+ loggedIn: false,
24
+ userType: "new-eligible-user"
25
+ };
26
+ var resetStoryEnvironment = null;
27
+ var createFetchMock = (sessionResponse, sessionStatus = 200) => {
28
+ return async (input) => {
29
+ const requestUrl = typeof input === "string" ? input : input instanceof URL ? input.toString() : input.url;
30
+ if (requestUrl.endsWith("/v1/wallet/session")) {
31
+ if (sessionStatus >= 400) {
32
+ return new Response("Session initialization failed", {
33
+ status: sessionStatus,
34
+ statusText: "Session Init Error"
35
+ });
36
+ }
37
+ return new Response(JSON.stringify(sessionResponse), {
38
+ status: 200,
39
+ headers: { "content-type": "application/json" }
40
+ });
41
+ }
42
+ return new Response("Not Found", {
43
+ status: 404,
44
+ statusText: "Not Found"
45
+ });
46
+ };
47
+ };
48
+ var applyApplePayStoryEnvironment = ({
49
+ applePaySupported,
50
+ sessionResponse = defaultSessionResponse,
51
+ sessionStatus = 200,
52
+ debugBypassEnabled = false,
53
+ bypassApplePayChecks = false
54
+ }) => {
55
+ if (resetStoryEnvironment) {
56
+ resetStoryEnvironment();
57
+ resetStoryEnvironment = null;
58
+ }
59
+ const originalFetch = globalThis.fetch;
60
+ const originalHref = window.location.href;
61
+ const originalUserAgent = navigator.userAgent;
62
+ const originalMaxTouchPoints = navigator.maxTouchPoints;
63
+ const originalApplePaySessionDescriptor = Object.getOwnPropertyDescriptor(
64
+ window,
65
+ "ApplePaySession"
66
+ );
67
+ const mockApplePaySession = {
68
+ canMakePayments: () => applePaySupported,
69
+ canMakePaymentsWithActiveCard: async () => applePaySupported
70
+ };
71
+ Object.defineProperty(window, "ApplePaySession", {
72
+ configurable: true,
73
+ writable: true,
74
+ value: mockApplePaySession
75
+ });
76
+ Object.defineProperty(navigator, "userAgent", {
77
+ configurable: true,
78
+ writable: true,
79
+ value: STORYBOOK_SAFARI_UA
80
+ });
81
+ Object.defineProperty(navigator, "maxTouchPoints", {
82
+ configurable: true,
83
+ writable: true,
84
+ value: 0
85
+ });
86
+ globalThis.fetch = createFetchMock(sessionResponse, sessionStatus);
87
+ const nextUrl = new URL(window.location.href);
88
+ if (bypassApplePayChecks) {
89
+ nextUrl.searchParams.set("BYPASS_APPLE_PAY_CHECKS", "1");
90
+ } else {
91
+ nextUrl.searchParams.delete("BYPASS_APPLE_PAY_CHECKS");
92
+ }
93
+ window.history.replaceState({}, "", nextUrl.toString());
94
+ configureApplePay({
95
+ merchantId: "merchant.com.economist.storybook",
96
+ walletApiBaseUrl: STORYBOOK_WALLET_BASE_URL,
97
+ debugBypassEnabled
98
+ });
99
+ resetStoryEnvironment = () => {
100
+ resetApplePayConfig();
101
+ globalThis.fetch = originalFetch;
102
+ window.history.replaceState({}, "", originalHref);
103
+ Object.defineProperty(navigator, "userAgent", {
104
+ configurable: true,
105
+ writable: true,
106
+ value: originalUserAgent
107
+ });
108
+ Object.defineProperty(navigator, "maxTouchPoints", {
109
+ configurable: true,
110
+ writable: true,
111
+ value: originalMaxTouchPoints
112
+ });
113
+ if (originalApplePaySessionDescriptor) {
114
+ Object.defineProperty(
115
+ window,
116
+ "ApplePaySession",
117
+ originalApplePaySessionDescriptor
118
+ );
119
+ return;
120
+ }
121
+ delete window.ApplePaySession;
122
+ };
123
+ };
124
+ var withApplePayStoryEnvironment = (options, renderFn) => (args) => {
125
+ applyApplePayStoryEnvironment(options);
126
+ return renderFn(args);
127
+ };
128
+ var clickWhenElementReady = (root, selector, maxAttempts = 5) => {
129
+ let attempts = 0;
130
+ const tryClick = () => {
131
+ const element = root.querySelector(selector);
132
+ if (element) {
133
+ element.click();
134
+ return;
135
+ }
136
+ if (attempts >= maxAttempts) {
137
+ return;
138
+ }
139
+ attempts += 1;
140
+ requestAnimationFrame(tryClick);
141
+ };
142
+ requestAnimationFrame(tryClick);
143
+ };
144
+ export {
145
+ applyApplePayStoryEnvironment,
146
+ clickWhenElementReady,
147
+ withApplePayStoryEnvironment
148
+ };
File without changes
@@ -0,0 +1,73 @@
1
+ export type ProductVariant = 'bundle' | 'digital' | 'espresso' | 'espressopod' | 'podcast' | 'insider_digital' | 'insider_print';
2
+ export type PeriodUnit = 'week' | 'month' | 'year';
3
+ export type Period = {
4
+ duration: number;
5
+ unit: PeriodUnit;
6
+ };
7
+ export type Tax = {
8
+ inclusive: boolean;
9
+ };
10
+ export type Price = {
11
+ purchase: number;
12
+ pre_discount?: number;
13
+ currency_code: string;
14
+ discounted: boolean;
15
+ tax?: Tax;
16
+ };
17
+ export type Product = {
18
+ type: string;
19
+ variant: ProductVariant;
20
+ offer_type: string;
21
+ };
22
+ export type TrialPeriod = {
23
+ duration: number;
24
+ unit: PeriodUnit;
25
+ };
26
+ export type RenewalInterval = {
27
+ duration: number;
28
+ unit: PeriodUnit;
29
+ };
30
+ export type SubscriptionOptions = {
31
+ free_trial: boolean;
32
+ trial_period?: TrialPeriod;
33
+ renewal_interval: RenewalInterval;
34
+ renewal_price?: number;
35
+ auto_renewal: boolean;
36
+ variable_term: boolean;
37
+ display_name: string;
38
+ introductory?: boolean;
39
+ sale_offer?: boolean;
40
+ bau_price?: string;
41
+ min_term_contract?: boolean;
42
+ min_term_contract_length?: string;
43
+ };
44
+ export type Restrictions = {
45
+ countries?: string[];
46
+ };
47
+ export type UnstructuredContent = {
48
+ features_html?: string;
49
+ custom_display_price?: string;
50
+ };
51
+ export type StructuredContent = {
52
+ custom_subscription_display_name?: string;
53
+ discount_percentages?: string[];
54
+ custom_detailed_display_price?: string;
55
+ };
56
+ export type Content = {
57
+ unstructured?: UnstructuredContent;
58
+ structured?: StructuredContent;
59
+ };
60
+ export type MetaData = {
61
+ can_checkout?: boolean;
62
+ product_path?: string;
63
+ };
64
+ export type Offer = {
65
+ sku_id: string;
66
+ price: Price;
67
+ display_name: string;
68
+ product: Product;
69
+ subscription_options: SubscriptionOptions;
70
+ restrictions?: Restrictions;
71
+ content?: Content;
72
+ meta_data?: MetaData;
73
+ };
File without changes
@@ -0,0 +1,19 @@
1
+ /**
2
+ * Returns the list of country codes that are valid billing countries for an offer.
3
+ *
4
+ * Mirrors sma-checkout `billingCountryOptions()` logic (options.ts:21):
5
+ * - `bundle` / `insider_print` offers → unrestricted (all countries)
6
+ * - EEA offer country → price-zone countries ∪ all EEA countries
7
+ * - Otherwise → price-zone countries only
8
+ *
9
+ * **Intentional deviation from sma-checkout:**
10
+ * The source (`get-zone-by-country.ts`) throws `Error('No price zone found for country …')`
11
+ * when the offer country is not in any price zone. This function returns all countries instead.
12
+ * Rationale: Web Components have no React error boundary — an uncaught throw would crash
13
+ * the host page silently. Returning all countries is the safe degradation: the Apple Pay
14
+ * session itself validates the billing address, so no invalid payment can succeed.
15
+ *
16
+ * @param offerCountry - ISO 3166-1 alpha-2 country code of the offer (e.g. `'GB'`)
17
+ * @param offerVariant - Product variant string (e.g. `'digital'`, `'bundle'`)
18
+ */
19
+ export declare const getSupportedBillingCountries: (offerCountry: string, offerVariant: string) => string[];
@@ -0,0 +1,263 @@
1
+ // src/data/price-zones.json
2
+ var price_zones_default = {
3
+ "AS-AS": [
4
+ "BD",
5
+ "BT",
6
+ "CK",
7
+ "FJ",
8
+ "ID",
9
+ "KH",
10
+ "KI",
11
+ "LA",
12
+ "LK",
13
+ "MH",
14
+ "MM",
15
+ "MN",
16
+ "MV",
17
+ "NC",
18
+ "NF",
19
+ "NP",
20
+ "NR",
21
+ "PF",
22
+ "PG",
23
+ "PK",
24
+ "PW",
25
+ "SB",
26
+ "TH",
27
+ "TL",
28
+ "TO",
29
+ "TV",
30
+ "VN",
31
+ "VU",
32
+ "WS"
33
+ ],
34
+ "AS-AU": ["AU"],
35
+ "AS-CN": ["CN"],
36
+ "AS-HK": ["HK", "MO"],
37
+ "AS-IN": ["IN"],
38
+ "AS-JP": ["JP"],
39
+ "AS-KR": ["KR"],
40
+ "AS-MY": ["MY"],
41
+ "AS-NZ": ["NZ"],
42
+ "AS-PH": ["PH"],
43
+ "AS-SG": ["BN", "SG"],
44
+ "AS-TW": ["TW"],
45
+ "CE-AF": [
46
+ "DZ",
47
+ "AO",
48
+ "BJ",
49
+ "BW",
50
+ "BF",
51
+ "BI",
52
+ "CM",
53
+ "CV",
54
+ "CF",
55
+ "TD",
56
+ "CG",
57
+ "CD",
58
+ "CI",
59
+ "DJ",
60
+ "EG",
61
+ "GQ",
62
+ "ER",
63
+ "ET",
64
+ "GA",
65
+ "GM",
66
+ "GH",
67
+ "GN",
68
+ "GW",
69
+ "IL",
70
+ "JO",
71
+ "KE",
72
+ "LB",
73
+ "LS",
74
+ "LR",
75
+ "LY",
76
+ "MG",
77
+ "MW",
78
+ "ML",
79
+ "MR",
80
+ "MU",
81
+ "MA",
82
+ "MZ",
83
+ "NA",
84
+ "NE",
85
+ "NG",
86
+ "PS",
87
+ "RE",
88
+ "RW",
89
+ "ST",
90
+ "SN",
91
+ "SC",
92
+ "SL",
93
+ "ZA",
94
+ "SZ",
95
+ "TZ",
96
+ "TG",
97
+ "TN",
98
+ "UG",
99
+ "ZM",
100
+ "ZW"
101
+ ],
102
+ "CE-CH": ["LI", "CH"],
103
+ "CE-DK": ["FO", "GL", "DK"],
104
+ "CE-EU": [
105
+ "AL",
106
+ "AD",
107
+ "AM",
108
+ "AT",
109
+ "AZ",
110
+ "BY",
111
+ "BE",
112
+ "BA",
113
+ "BG",
114
+ "HR",
115
+ "CY",
116
+ "CZ",
117
+ "EE",
118
+ "FI",
119
+ "FR",
120
+ "GE",
121
+ "DE",
122
+ "GI",
123
+ "GR",
124
+ "HU",
125
+ "IS",
126
+ "IE",
127
+ "IT",
128
+ "KZ",
129
+ "XK",
130
+ "KG",
131
+ "LV",
132
+ "LT",
133
+ "LU",
134
+ "MT",
135
+ "MD",
136
+ "MC",
137
+ "ME",
138
+ "NL",
139
+ "PL",
140
+ "PT",
141
+ "MK",
142
+ "RO",
143
+ "RS",
144
+ "SK",
145
+ "SI",
146
+ "ES",
147
+ "TJ",
148
+ "TR",
149
+ "TM",
150
+ "UA",
151
+ "UZ"
152
+ ],
153
+ "CE-ME": ["AE", "BH", "KW", "OM", "QA", "SA"],
154
+ "CE-NO": ["NO"],
155
+ "CE-SE": ["SE"],
156
+ "LA-LA": [
157
+ "AI",
158
+ "AG",
159
+ "AR",
160
+ "AW",
161
+ "BS",
162
+ "BB",
163
+ "BZ",
164
+ "BM",
165
+ "BO",
166
+ "BR",
167
+ "KY",
168
+ "CL",
169
+ "CO",
170
+ "CR",
171
+ "DM",
172
+ "DO",
173
+ "EC",
174
+ "SV",
175
+ "FK",
176
+ "GF",
177
+ "GD",
178
+ "GT",
179
+ "GY",
180
+ "HT",
181
+ "HN",
182
+ "JM",
183
+ "MQ",
184
+ "MX",
185
+ "MS",
186
+ "NI",
187
+ "MP",
188
+ "PA",
189
+ "PY",
190
+ "PE",
191
+ "KN",
192
+ "LC",
193
+ "VC",
194
+ "SR",
195
+ "TT",
196
+ "TC",
197
+ "UY",
198
+ "VG",
199
+ "VI"
200
+ ],
201
+ "NA-CA": ["CA"],
202
+ "NA-US": ["AS", "GU", "FM", "PR", "US"],
203
+ "UK-UK": ["GB"]
204
+ };
205
+
206
+ // src/data/eea-countries.json
207
+ var eea_countries_default = [
208
+ "AT",
209
+ "BE",
210
+ "BG",
211
+ "HR",
212
+ "CY",
213
+ "CZ",
214
+ "DK",
215
+ "EE",
216
+ "FO",
217
+ "FI",
218
+ "FR",
219
+ "DE",
220
+ "GR",
221
+ "GL",
222
+ "HU",
223
+ "IS",
224
+ "IE",
225
+ "IT",
226
+ "LV",
227
+ "LI",
228
+ "LT",
229
+ "LU",
230
+ "MT",
231
+ "NL",
232
+ "NO",
233
+ "PL",
234
+ "PT",
235
+ "RO",
236
+ "SK",
237
+ "SI",
238
+ "ES",
239
+ "SE"
240
+ ];
241
+
242
+ // src/utils/billing-countries.ts
243
+ var priceZones = price_zones_default;
244
+ var eeaCountries = eea_countries_default;
245
+ var ALL_COUNTRY_CODES = Object.values(priceZones).flat();
246
+ var getZoneByCountry = (country) => Object.keys(priceZones).find((key) => priceZones[key].includes(country)) ?? null;
247
+ var getSupportedBillingCountries = (offerCountry, offerVariant) => {
248
+ if (offerVariant === "bundle" || offerVariant === "insider_print") {
249
+ return [...ALL_COUNTRY_CODES];
250
+ }
251
+ const zone = getZoneByCountry(offerCountry);
252
+ if (!zone) {
253
+ return [...ALL_COUNTRY_CODES];
254
+ }
255
+ const zoneCountries = priceZones[zone];
256
+ if (eeaCountries.includes(offerCountry)) {
257
+ return Array.from(/* @__PURE__ */ new Set([...zoneCountries, ...eeaCountries]));
258
+ }
259
+ return [...zoneCountries];
260
+ };
261
+ export {
262
+ getSupportedBillingCountries
263
+ };
@@ -0,0 +1 @@
1
+ export declare const define: (component: string, instance: CustomElementConstructor) => void;
@@ -0,0 +1,9 @@
1
+ // src/utils/define.ts
2
+ var define = (component, instance) => {
3
+ if (!window.customElements.get(component)) {
4
+ window.customElements.define(component, instance);
5
+ }
6
+ };
7
+ export {
8
+ define
9
+ };
@@ -0,0 +1,55 @@
1
+ /**
2
+ * Centralized redirect URL builders for the Express Checkout payment handler.
3
+ *
4
+ * All redirects include a `returnUrl` parameter so downstream pages
5
+ * (/checkout/complete, onboarding) can return the user to the original article.
6
+ *
7
+ * Matches sma-checkout: src/lib/navigate-to-checkout/index.tsx
8
+ */
9
+ /**
10
+ * Captures the current page URL (the article the user is reading).
11
+ * Must be called before any redirect occurs.
12
+ */
13
+ export declare const captureOriginUrl: () => string;
14
+ /**
15
+ * Extracts only the `country` query param from the current page URL for
16
+ * forwarding to /checkout/complete. All other params are intentionally excluded.
17
+ *
18
+ * Matches sma-checkout: src/hooks/use-query-string/useCountryCodeQueryString
19
+ * Only `country` is forwarded — feature flags, UTM tags, and all other host-page
20
+ * params must NOT be propagated to /checkout/complete.
21
+ */
22
+ export declare const buildQueryString: () => string;
23
+ /**
24
+ * Decodes a Commerce Services encoded SKU to the raw offer path.
25
+ * e.g. LO-L29mZmVyczIvTkEt... → /offers2/NA-US.DIGITAL.MAAR...
26
+ *
27
+ * Matches sma-checkout: src/lib/commerce-services/decode-sku.ts
28
+ */
29
+ export declare const decodeSku: (sku: string) => string;
30
+ /**
31
+ * Builds the success redirect URL.
32
+ * /checkout/complete?basketId={id}&lmo_offer={sku}&status={userStatus}&redirectToCheckout=true&returnUrl=...
33
+ */
34
+ export declare const redirectToCheckoutComplete: (params: {
35
+ basketId: string;
36
+ status: "nex" | "lex";
37
+ skuId: string;
38
+ returnUrl: string;
39
+ queryString?: string;
40
+ }) => string;
41
+ /**
42
+ * Builds the fallback redirect URL for error scenarios.
43
+ * /checkout?lmo_offer={sku}&inlineError=...&returnUrl=...
44
+ *
45
+ * Builds on top of the current page URL (window.location.href) so that any
46
+ * existing query params (UTM tags, campaign flags) are preserved — matching
47
+ * sma-checkout: src/lib/navigate-to-checkout/index.tsx
48
+ */
49
+ export declare const redirectToCheckoutFallback: (params: {
50
+ skuId: string;
51
+ returnUrl: string;
52
+ inlineError?: string;
53
+ emailError?: string;
54
+ email?: string;
55
+ }) => string;
@@ -0,0 +1,46 @@
1
+ // src/utils/expressCheckoutRedirect.ts
2
+ var captureOriginUrl = () => window.location.href;
3
+ var buildQueryString = () => {
4
+ const countryCode = new URLSearchParams(window.location.search).get(
5
+ "country"
6
+ );
7
+ return countryCode ? `&country=${encodeURIComponent(countryCode)}` : "";
8
+ };
9
+ var decodeSku = (sku) => {
10
+ try {
11
+ return atob(sku.substring(sku.indexOf("-") + 1));
12
+ } catch {
13
+ return sku;
14
+ }
15
+ };
16
+ var redirectToCheckoutComplete = (params) => {
17
+ const url = new URL("/checkout/complete", window.location.origin);
18
+ url.searchParams.set("basketId", params.basketId);
19
+ if (params.queryString) {
20
+ const qs = params.queryString.startsWith("?") ? params.queryString.slice(1) : params.queryString;
21
+ new URLSearchParams(qs).forEach((v, k) => url.searchParams.set(k, v));
22
+ }
23
+ url.searchParams.set("lmo_offer", params.skuId);
24
+ url.searchParams.set("status", params.status);
25
+ url.searchParams.set("redirectToCheckout", "true");
26
+ url.searchParams.set("returnUrl", params.returnUrl);
27
+ return url.pathname + url.search;
28
+ };
29
+ var redirectToCheckoutFallback = (params) => {
30
+ const url = new URL(window.location.href);
31
+ url.pathname = "/checkout";
32
+ url.searchParams.set("lmo_offer", params.skuId);
33
+ if (params.inlineError)
34
+ url.searchParams.set("inlineError", params.inlineError);
35
+ if (params.email) url.searchParams.set("email", params.email);
36
+ if (params.emailError) url.searchParams.set("emailError", params.emailError);
37
+ url.searchParams.set("returnUrl", params.returnUrl);
38
+ return url.pathname + url.search;
39
+ };
40
+ export {
41
+ buildQueryString,
42
+ captureOriginUrl,
43
+ decodeSku,
44
+ redirectToCheckoutComplete,
45
+ redirectToCheckoutFallback
46
+ };