@economist/web-apple-pay 1.10.2-beta.1 → 1.11.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.
package/README.md CHANGED
@@ -27,7 +27,10 @@ npm install --save @economist/web-apple-pay
27
27
  Call `configureApplePay(...)` once during app startup, before rendering Apple Pay UI:
28
28
 
29
29
  ```ts
30
- import { configureApplePay, defineApplePayElements } from '@economist/web-apple-pay';
30
+ import {
31
+ configureApplePay,
32
+ defineApplePayElements,
33
+ } from '@economist/web-apple-pay';
31
34
 
32
35
  configureApplePay({
33
36
  merchantId: 'merchant.com.economist.your-app',
@@ -54,56 +57,6 @@ container.appendChild(button);
54
57
 
55
58
  When clicked, the button creates and opens `teg-apple-pay-modal` automatically.
56
59
 
57
- ### 3) Gated creation — `createIfEnabled`
58
-
59
- Hosts that mount Apple Pay on a wall, banner, or popup should use
60
- `ApplePayButton.createIfEnabled(offer, country, entryPoint)` instead of
61
- `document.createElement`. It applies Amplitude experiment and placement rules
62
- per entry point, then returns a configured button or `null`.
63
-
64
- ```ts
65
- import {
66
- ApplePayButton,
67
- ApplePayEntryPoint,
68
- } from '@economist/web-apple-pay';
69
-
70
- const button = ApplePayButton.createIfEnabled(
71
- offer,
72
- 'US',
73
- ApplePayEntryPoint.FREE_TRIAL_POPUP,
74
- );
75
-
76
- if (button) {
77
- container.appendChild(button);
78
- await button.whenReady();
79
- if (button.style.display === 'none') {
80
- button.remove();
81
- }
82
- }
83
- ```
84
-
85
- #### Entry points
86
-
87
- | `ApplePayEntryPoint` | Value | Amplitude flag | Placement eligibility |
88
- | --- | --- | --- | --- |
89
- | `PAYWALL` | `article_paywall` | `apple-pay-express-checkout-on-paywalls` | Safari / Apple device |
90
- | `REGWALL` | `article_regwall` | `apple-pay-express-checkout-on-banners` | none |
91
- | `DUAL_OFFER_BANNER` (default if omitted) | `dual_offer_banner` | `apple-pay-express-checkout-on-banners` | homepage + Safari / iPhone |
92
- | `FREE_TRIAL_POPUP` | `free_trial_popup` | **none** — always shown | none |
93
-
94
- `FREE_TRIAL_POPUP` does not read `window.experimentIntegration` and does not
95
- emit `$exposure`. The button is created whenever the offer is valid (excluded
96
- variants still suppress it). The component still hides itself after mount if
97
- Apple Pay is unavailable on the device.
98
-
99
- For every other entry point:
100
-
101
- - Host must set `window.experimentIntegration.getVariant` before calling
102
- `createIfEnabled`.
103
- - `treatment` → button created; `control` / missing / anything else → `null`.
104
- - QA override: `?FEATURE_APPLE_PAY_EXPRESS_CHECKOUT` shows the button without
105
- emitting `$exposure`.
106
-
107
60
  ## Runtime configuration
108
61
 
109
62
  ### `configureApplePay(config)`
@@ -135,8 +88,8 @@ to the DOM. This is the supported, stable way to know whether Apple Pay is
135
88
  available without touching lifecycle internals.
136
89
 
137
90
  ```ts
138
- container.appendChild(btn); // spec-compliant — browser fires connectedCallback naturally
139
- await btn.whenReady(); // resolves when rendering is complete
91
+ container.appendChild(btn); // spec-compliant — browser fires connectedCallback naturally
92
+ await btn.whenReady(); // resolves when rendering is complete
140
93
  if (btn.style.display !== 'none') {
141
94
  // Apple Pay rendered successfully
142
95
  } else {
@@ -170,13 +123,21 @@ Consumers should listen for this event and continue payment orchestration.
170
123
  - `defineApplePayElements()`: registers both custom elements.
171
124
  - `configureApplePay(config)`: sets required runtime config (merchant ID and wallet API base URL, with optional debug bypass).
172
125
  - `ApplePayButton`: button component class.
173
- - `createIfEnabled(offer, country, entryPoint?)`: returns a configured button or `null` after applying Amplitude / placement rules for the entry point. `FREE_TRIAL_POPUP` is not experiment-gated.
174
126
  - `whenReady(): Promise<void>` — resolves when the current render lifecycle completes (available or hidden). Resolves immediately if called before the element is connected.
175
- - `ApplePayEntryPoint`: where the button is mounted (`PAYWALL`, `REGWALL`, `DUAL_OFFER_BANNER`, `FREE_TRIAL_POPUP`).
127
+ - `createIfEnabled(offer, country, entryPoint?)`: returns a configured button or `null`.
128
+ - `DUAL_OFFER_BANNER` (or omitted): always-on after offer exclusions — no homepage/Safari gate at `createIfEnabled` (device gating via `isApplePayAvailable` on mount); no banners Amplitude experiment / `$exposure`.
129
+ - `FREE_TRIAL_BANNER`: same always-on-after-exclusions gate as Dual; wallet impression `entry_point` is `free_trial_banner`.
130
+ - `PAYWALL`: experiment-gated via `apple-pay-express-checkout-on-paywalls` (`treatment` or QA URL).
131
+ - `REGWALL`: inert — always returns `null`.
176
132
  - `ApplePayModal`: modal component class.
177
133
  - `ensureApplePayLogoSymbol()`: logo symbol injection helper.
134
+ - `APPLE_PAY_EXPRESS_PAYWALLS_EXPERIMENT_KEY`: Amplitude flag key for paywalls experiment gating / `$exposure`.
178
135
  - `Offer`: offer contract type used by component props.
179
136
 
137
+ ### Breaking changes
138
+
139
+ - `APPLE_PAY_EXPRESS_EXPERIMENT_KEY` (banners experiment flag) is no longer exported. Use `APPLE_PAY_EXPRESS_PAYWALLS_EXPERIMENT_KEY` for paywalls, or drop the import if you only needed the retired banners key.
140
+
180
141
  ## Troubleshooting
181
142
 
182
143
  ### Button does not appear
@@ -184,9 +145,9 @@ Consumers should listen for this event and continue payment orchestration.
184
145
  Check:
185
146
 
186
147
  - `configureApplePay(...)` was called before rendering (with a valid `merchantId` and `walletApiBaseUrl`)
187
- - device/browser supports Apple Pay (Safari on Apple device required)
188
- - for paywall / regwall / dual-offer banner: Amplitude variant is `treatment`, or the `FEATURE_APPLE_PAY_EXPRESS_CHECKOUT` QA param is present
189
- - `FREE_TRIAL_POPUP` is not Amplitude-gated — if the button is missing, check offer variant and device support instead
148
+ - device/browser supports Apple Pay (Safari on Apple device required — enforced by `isApplePayAvailable` on mount)
149
+ - for paywalls: Amplitude treatment (or `?FEATURE_APPLE_PAY_EXPRESS_CHECKOUT`) is satisfied, plus Safari/Apple placement eligibility
150
+ - for banners: consumer kill switch off (no homepage/Safari gate at `createIfEnabled`)
190
151
  - offer variant is not excluded (`bundle`, `insider_print`)
191
152
 
192
153
  ### Logged-in checkbox copy looks wrong
@@ -237,4 +198,3 @@ Story coverage matrix:
237
198
  - `ApplePayButton` behavior and state variants are available as individual canvas stories in Storybook.
238
199
  - `ApplePayModal` docs page shows the stable default example.
239
200
  - `ApplePayModal` behavior and state variants are available as individual canvas stories in Storybook.
240
-
package/dist/index.js CHANGED
@@ -1308,10 +1308,9 @@ var ApplePayEntryPoint = /* @__PURE__ */ ((ApplePayEntryPoint2) => {
1308
1308
  ApplePayEntryPoint2["PAYWALL"] = "article_paywall";
1309
1309
  ApplePayEntryPoint2["REGWALL"] = "article_regwall";
1310
1310
  ApplePayEntryPoint2["DUAL_OFFER_BANNER"] = "dual_offer_banner";
1311
- ApplePayEntryPoint2["FREE_TRIAL_POPUP"] = "free_trial_popup";
1311
+ ApplePayEntryPoint2["FREE_TRIAL_BANNER"] = "free_trial_banner";
1312
1312
  return ApplePayEntryPoint2;
1313
1313
  })(ApplePayEntryPoint || {});
1314
- var APPLE_PAY_EXPRESS_EXPERIMENT_KEY = "apple-pay-express-checkout-on-banners";
1315
1314
  var APPLE_PAY_EXPRESS_PAYWALLS_EXPERIMENT_KEY = "apple-pay-express-checkout-on-paywalls";
1316
1315
  var APPLE_PAY_EXPRESS_VARIANTS = {
1317
1316
  CONTROL: "control",
@@ -1355,7 +1354,7 @@ var track = (action, extra) => {
1355
1354
  ...extra
1356
1355
  });
1357
1356
  };
1358
- var trackApplePayExperimentExposure = (variant, experimentKey, flagKey = APPLE_PAY_EXPRESS_EXPERIMENT_KEY) => {
1357
+ var trackApplePayExperimentExposure = (variant, experimentKey, flagKey) => {
1359
1358
  const isActiveVariant = variant === APPLE_PAY_EXPRESS_VARIANTS.CONTROL || variant === APPLE_PAY_EXPRESS_VARIANTS.TREATMENT;
1360
1359
  if (!isActiveVariant) {
1361
1360
  return;
@@ -1384,8 +1383,11 @@ var trackExpressWalletImpression = (skuId, { entryPoint, country }) => {
1384
1383
  device: getDevice()
1385
1384
  });
1386
1385
  };
1387
- var trackExpressWalletClick = (skuId) => {
1388
- track(EXPRESS_CHECKOUT_EVENTS.BUTTON_CLICK, { sku_id: skuId });
1386
+ var trackExpressWalletClick = (skuId, { entryPoint } = {}) => {
1387
+ track(EXPRESS_CHECKOUT_EVENTS.BUTTON_CLICK, {
1388
+ sku_id: skuId,
1389
+ ...entryPoint ? { entry_point: entryPoint } : {}
1390
+ });
1389
1391
  };
1390
1392
  var trackWalletPaymentSheetOpen = (skuId) => {
1391
1393
  track(EXPRESS_CHECKOUT_EVENTS.SHEET_OPEN, { sku_id: skuId });
@@ -2006,15 +2008,17 @@ var getExperimentKeyFromNextData = (flagKey) => {
2006
2008
  };
2007
2009
 
2008
2010
  // src/utils/applePayExpressEligibility.ts
2009
- var isApplePayExpressHomepage = (pathname = window.location.pathname) => pathname === "/";
2010
- var isEligibleForApplePayExpressExperiment = () => isApplePayExpressHomepage() && isAppleDeviceOrSafari();
2011
2011
  var isEligibleForApplePayExpressPaywallExperiment = () => isAppleDeviceOrSafari();
2012
2012
 
2013
2013
  // src/apple-pay-button.ts
2014
2014
  var ENTRY_POINT_CONFIG = {
2015
2015
  ["dual_offer_banner" /* DUAL_OFFER_BANNER */]: {
2016
- isEligible: isEligibleForApplePayExpressExperiment,
2017
- experimentFlagKey: APPLE_PAY_EXPRESS_EXPERIMENT_KEY
2016
+ isEligible: () => true
2017
+ // no experimentFlagKey → always-on after offer exclusions
2018
+ },
2019
+ ["free_trial_banner" /* FREE_TRIAL_BANNER */]: {
2020
+ isEligible: () => true
2021
+ // Dual parity — no experimentFlagKey, no homepage gate here
2018
2022
  },
2019
2023
  ["article_paywall" /* PAYWALL */]: {
2020
2024
  isEligible: isEligibleForApplePayExpressPaywallExperiment,
@@ -2022,10 +2026,7 @@ var ENTRY_POINT_CONFIG = {
2022
2026
  },
2023
2027
  ["article_regwall" /* REGWALL */]: {
2024
2028
  isEligible: () => true,
2025
- experimentFlagKey: APPLE_PAY_EXPRESS_EXPERIMENT_KEY
2026
- },
2027
- ["free_trial_popup" /* FREE_TRIAL_POPUP */]: {
2028
- isEligible: () => true
2029
+ enabled: false
2029
2030
  }
2030
2031
  };
2031
2032
  var FEATURE_APPLE_PAY_EXPRESS_CHECKOUT = "FEATURE_APPLE_PAY_EXPRESS_CHECKOUT";
@@ -2044,28 +2045,29 @@ var ApplePayButton = class _ApplePayButton extends HTMLElement {
2044
2045
  define(_ApplePayButton.tag, _ApplePayButton);
2045
2046
  }
2046
2047
  /**
2047
- * Creates and configures an ApplePayButton when the Amplitude experiment is enabled.
2048
+ * Creates and configures an ApplePayButton when the entry point allows it.
2048
2049
  *
2049
- * Reads the experiment variant from `window.experimentIntegration.getVariant`:
2050
- * - {@link APPLE_PAY_EXPRESS_PAYWALLS_EXPERIMENT_KEY} for `PAYWALL`
2051
- * - {@link APPLE_PAY_EXPRESS_EXPERIMENT_KEY} for `REGWALL` / `DUAL_OFFER_BANNER`
2052
- * - no Amplitude gate for `FREE_TRIAL_POPUP` (always shown)
2050
+ * - `DUAL_OFFER_BANNER` (or omitted): always-on after offer exclusions —
2051
+ * no homepage/Safari gate here, no banners experiment read, and no banners
2052
+ * `$exposure`.
2053
+ * - `FREE_TRIAL_BANNER`: same always-on-after-exclusions gate as Dual; distinct
2054
+ * TEDL `entry_point` string (`free_trial_banner`) for wallet impression attribution.
2055
+ * - `PAYWALL`: experiment-gated via {@link APPLE_PAY_EXPRESS_PAYWALLS_EXPERIMENT_KEY}
2056
+ * (`treatment` or QA URL param). The host must set up
2057
+ * `window.experimentIntegration` before calling for this entry point.
2058
+ * - `REGWALL`: inert — always returns null.
2053
2059
  *
2054
- * The host page is responsible for setting up `window.experimentIntegration`
2055
- * before calling this method (except `FREE_TRIAL_POPUP`).
2056
- *
2057
- * - `treatment` → button created and returned
2058
- * - `control` / missing / anything else → returns null (button suppressed)
2059
- *
2060
- * **Transition period:** the legacy `FEATURE_APPLE_PAY_EXPRESS_CHECKOUT` URL param
2061
- * gate is also evaluated as a fallback until all host apps have integrated
2062
- * `window.experimentIntegration`. Once all consumers are migrated, the URL param
2063
- * fallback will be removed.
2060
+ * **PAYWALL only — transition period:** the legacy
2061
+ * `FEATURE_APPLE_PAY_EXPRESS_CHECKOUT` URL param can show the button without
2062
+ * treatment (and without inflating `$exposure`). For banners there is no
2063
+ * experiment gate; `BYPASS_APPLE_PAY_CHECKS` only skips device checks in
2064
+ * `isApplePayAvailable()` on mount.
2064
2065
  *
2065
2066
  * **Eligibility:**
2066
- * - `DUAL_OFFER_BANNER` (or omitted): homepage + Safari/iPhone (SMA-3301)
2067
+ * - `DUAL_OFFER_BANNER` / `FREE_TRIAL_BANNER` (or omitted): always eligible at
2068
+ * this gate (homepage placement is host-owned; Safari/Apple is enforced
2069
+ * later by `isApplePayAvailable()` on mount)
2067
2070
  * - `PAYWALL`: Safari/Apple device only (no homepage) — article paywalls
2068
- * - `REGWALL` / `FREE_TRIAL_POPUP`: no host-side placement gate
2069
2071
  *
2070
2072
  * @param offer - The offer to display in the Apple Pay flow.
2071
2073
  * @param country - The user's country code.
@@ -2088,6 +2090,9 @@ var ApplePayButton = class _ApplePayButton extends HTMLElement {
2088
2090
  }
2089
2091
  const { isEligible: eligibilityCheck, experimentFlagKey } = config;
2090
2092
  const isEligible = eligibilityCheck();
2093
+ if (config.enabled === false) {
2094
+ return null;
2095
+ }
2091
2096
  if (!hasUrlParamFlag && !bypassApplePayChecks() && !isEligible) {
2092
2097
  return null;
2093
2098
  }
@@ -2232,9 +2237,7 @@ var ApplePayButton = class _ApplePayButton extends HTMLElement {
2232
2237
  button.removeAttribute("aria-busy");
2233
2238
  button.getAnimations?.({ subtree: true }).forEach((animation) => animation.cancel());
2234
2239
  }
2235
- this.getAnimations?.({ subtree: true }).forEach(
2236
- (animation) => animation.cancel()
2237
- );
2240
+ this.getAnimations?.({ subtree: true }).forEach((animation) => animation.cancel());
2238
2241
  void this.offsetHeight;
2239
2242
  this.style.display = "none";
2240
2243
  this.querySelector(SEL_CONTAINER)?.remove();
@@ -2343,7 +2346,10 @@ var ApplePayButton = class _ApplePayButton extends HTMLElement {
2343
2346
  if (document.querySelector(ApplePayModal.tag)) {
2344
2347
  return;
2345
2348
  }
2346
- trackExpressWalletClick(this.#offer.sku_id);
2349
+ trackExpressWalletClick(
2350
+ this.#offer.sku_id,
2351
+ this.#entryPoint ? { entryPoint: this.#entryPoint } : void 0
2352
+ );
2347
2353
  ApplePayModal.define();
2348
2354
  const modal = document.createElement(ApplePayModal.tag);
2349
2355
  modal.offer = this.#offer;
@@ -2435,7 +2441,6 @@ function defineApplePayElements() {
2435
2441
  }
2436
2442
  export {
2437
2443
  APPLE_PAY_EXCLUDED_VARIANTS,
2438
- APPLE_PAY_EXPRESS_EXPERIMENT_KEY,
2439
2444
  APPLE_PAY_EXPRESS_PAYWALLS_EXPERIMENT_KEY,
2440
2445
  APPLE_PAY_EXPRESS_VARIANTS,
2441
2446
  ApplePayButton,
@@ -6,28 +6,29 @@ export declare class ApplePayButton extends HTMLElement {
6
6
  static readonly tag = "teg-apple-pay-button";
7
7
  static define(): void;
8
8
  /**
9
- * Creates and configures an ApplePayButton when the Amplitude experiment is enabled.
9
+ * Creates and configures an ApplePayButton when the entry point allows it.
10
10
  *
11
- * Reads the experiment variant from `window.experimentIntegration.getVariant`:
12
- * - {@link APPLE_PAY_EXPRESS_PAYWALLS_EXPERIMENT_KEY} for `PAYWALL`
13
- * - {@link APPLE_PAY_EXPRESS_EXPERIMENT_KEY} for `REGWALL` / `DUAL_OFFER_BANNER`
14
- * - no Amplitude gate for `FREE_TRIAL_POPUP` (always shown)
11
+ * - `DUAL_OFFER_BANNER` (or omitted): always-on after offer exclusions —
12
+ * no homepage/Safari gate here, no banners experiment read, and no banners
13
+ * `$exposure`.
14
+ * - `FREE_TRIAL_BANNER`: same always-on-after-exclusions gate as Dual; distinct
15
+ * TEDL `entry_point` string (`free_trial_banner`) for wallet impression attribution.
16
+ * - `PAYWALL`: experiment-gated via {@link APPLE_PAY_EXPRESS_PAYWALLS_EXPERIMENT_KEY}
17
+ * (`treatment` or QA URL param). The host must set up
18
+ * `window.experimentIntegration` before calling for this entry point.
19
+ * - `REGWALL`: inert — always returns null.
15
20
  *
16
- * The host page is responsible for setting up `window.experimentIntegration`
17
- * before calling this method (except `FREE_TRIAL_POPUP`).
18
- *
19
- * - `treatment` → button created and returned
20
- * - `control` / missing / anything else → returns null (button suppressed)
21
- *
22
- * **Transition period:** the legacy `FEATURE_APPLE_PAY_EXPRESS_CHECKOUT` URL param
23
- * gate is also evaluated as a fallback until all host apps have integrated
24
- * `window.experimentIntegration`. Once all consumers are migrated, the URL param
25
- * fallback will be removed.
21
+ * **PAYWALL only — transition period:** the legacy
22
+ * `FEATURE_APPLE_PAY_EXPRESS_CHECKOUT` URL param can show the button without
23
+ * treatment (and without inflating `$exposure`). For banners there is no
24
+ * experiment gate; `BYPASS_APPLE_PAY_CHECKS` only skips device checks in
25
+ * `isApplePayAvailable()` on mount.
26
26
  *
27
27
  * **Eligibility:**
28
- * - `DUAL_OFFER_BANNER` (or omitted): homepage + Safari/iPhone (SMA-3301)
28
+ * - `DUAL_OFFER_BANNER` / `FREE_TRIAL_BANNER` (or omitted): always eligible at
29
+ * this gate (homepage placement is host-owned; Safari/Apple is enforced
30
+ * later by `isApplePayAvailable()` on mount)
29
31
  * - `PAYWALL`: Safari/Apple device only (no homepage) — article paywalls
30
- * - `REGWALL` / `FREE_TRIAL_POPUP`: no host-side placement gate
31
32
  *
32
33
  * @param offer - The offer to display in the Apple Pay flow.
33
34
  * @param country - The user's country code.
@@ -1299,7 +1299,6 @@ var redirectToCheckoutFallback = (params) => {
1299
1299
  };
1300
1300
 
1301
1301
  // src/constants.ts
1302
- var APPLE_PAY_EXPRESS_EXPERIMENT_KEY = "apple-pay-express-checkout-on-banners";
1303
1302
  var APPLE_PAY_EXPRESS_PAYWALLS_EXPERIMENT_KEY = "apple-pay-express-checkout-on-paywalls";
1304
1303
  var APPLE_PAY_EXPRESS_VARIANTS = {
1305
1304
  CONTROL: "control",
@@ -1343,7 +1342,7 @@ var track = (action, extra) => {
1343
1342
  ...extra
1344
1343
  });
1345
1344
  };
1346
- var trackApplePayExperimentExposure = (variant, experimentKey, flagKey = APPLE_PAY_EXPRESS_EXPERIMENT_KEY) => {
1345
+ var trackApplePayExperimentExposure = (variant, experimentKey, flagKey) => {
1347
1346
  const isActiveVariant = variant === APPLE_PAY_EXPRESS_VARIANTS.CONTROL || variant === APPLE_PAY_EXPRESS_VARIANTS.TREATMENT;
1348
1347
  if (!isActiveVariant) {
1349
1348
  return;
@@ -1372,8 +1371,11 @@ var trackExpressWalletImpression = (skuId, { entryPoint, country }) => {
1372
1371
  device: getDevice()
1373
1372
  });
1374
1373
  };
1375
- var trackExpressWalletClick = (skuId) => {
1376
- track(EXPRESS_CHECKOUT_EVENTS.BUTTON_CLICK, { sku_id: skuId });
1374
+ var trackExpressWalletClick = (skuId, { entryPoint } = {}) => {
1375
+ track(EXPRESS_CHECKOUT_EVENTS.BUTTON_CLICK, {
1376
+ sku_id: skuId,
1377
+ ...entryPoint ? { entry_point: entryPoint } : {}
1378
+ });
1377
1379
  };
1378
1380
  var trackWalletPaymentSheetOpen = (skuId) => {
1379
1381
  track(EXPRESS_CHECKOUT_EVENTS.SHEET_OPEN, { sku_id: skuId });
@@ -1994,15 +1996,17 @@ var getExperimentKeyFromNextData = (flagKey) => {
1994
1996
  };
1995
1997
 
1996
1998
  // src/utils/applePayExpressEligibility.ts
1997
- var isApplePayExpressHomepage = (pathname = window.location.pathname) => pathname === "/";
1998
- var isEligibleForApplePayExpressExperiment = () => isApplePayExpressHomepage() && isAppleDeviceOrSafari();
1999
1999
  var isEligibleForApplePayExpressPaywallExperiment = () => isAppleDeviceOrSafari();
2000
2000
 
2001
2001
  // src/apple-pay-button.ts
2002
2002
  var ENTRY_POINT_CONFIG = {
2003
2003
  ["dual_offer_banner" /* DUAL_OFFER_BANNER */]: {
2004
- isEligible: isEligibleForApplePayExpressExperiment,
2005
- experimentFlagKey: APPLE_PAY_EXPRESS_EXPERIMENT_KEY
2004
+ isEligible: () => true
2005
+ // no experimentFlagKey → always-on after offer exclusions
2006
+ },
2007
+ ["free_trial_banner" /* FREE_TRIAL_BANNER */]: {
2008
+ isEligible: () => true
2009
+ // Dual parity — no experimentFlagKey, no homepage gate here
2006
2010
  },
2007
2011
  ["article_paywall" /* PAYWALL */]: {
2008
2012
  isEligible: isEligibleForApplePayExpressPaywallExperiment,
@@ -2010,10 +2014,7 @@ var ENTRY_POINT_CONFIG = {
2010
2014
  },
2011
2015
  ["article_regwall" /* REGWALL */]: {
2012
2016
  isEligible: () => true,
2013
- experimentFlagKey: APPLE_PAY_EXPRESS_EXPERIMENT_KEY
2014
- },
2015
- ["free_trial_popup" /* FREE_TRIAL_POPUP */]: {
2016
- isEligible: () => true
2017
+ enabled: false
2017
2018
  }
2018
2019
  };
2019
2020
  var FEATURE_APPLE_PAY_EXPRESS_CHECKOUT = "FEATURE_APPLE_PAY_EXPRESS_CHECKOUT";
@@ -2032,28 +2033,29 @@ var ApplePayButton = class _ApplePayButton extends HTMLElement {
2032
2033
  define(_ApplePayButton.tag, _ApplePayButton);
2033
2034
  }
2034
2035
  /**
2035
- * Creates and configures an ApplePayButton when the Amplitude experiment is enabled.
2036
+ * Creates and configures an ApplePayButton when the entry point allows it.
2036
2037
  *
2037
- * Reads the experiment variant from `window.experimentIntegration.getVariant`:
2038
- * - {@link APPLE_PAY_EXPRESS_PAYWALLS_EXPERIMENT_KEY} for `PAYWALL`
2039
- * - {@link APPLE_PAY_EXPRESS_EXPERIMENT_KEY} for `REGWALL` / `DUAL_OFFER_BANNER`
2040
- * - no Amplitude gate for `FREE_TRIAL_POPUP` (always shown)
2038
+ * - `DUAL_OFFER_BANNER` (or omitted): always-on after offer exclusions —
2039
+ * no homepage/Safari gate here, no banners experiment read, and no banners
2040
+ * `$exposure`.
2041
+ * - `FREE_TRIAL_BANNER`: same always-on-after-exclusions gate as Dual; distinct
2042
+ * TEDL `entry_point` string (`free_trial_banner`) for wallet impression attribution.
2043
+ * - `PAYWALL`: experiment-gated via {@link APPLE_PAY_EXPRESS_PAYWALLS_EXPERIMENT_KEY}
2044
+ * (`treatment` or QA URL param). The host must set up
2045
+ * `window.experimentIntegration` before calling for this entry point.
2046
+ * - `REGWALL`: inert — always returns null.
2041
2047
  *
2042
- * The host page is responsible for setting up `window.experimentIntegration`
2043
- * before calling this method (except `FREE_TRIAL_POPUP`).
2044
- *
2045
- * - `treatment` → button created and returned
2046
- * - `control` / missing / anything else → returns null (button suppressed)
2047
- *
2048
- * **Transition period:** the legacy `FEATURE_APPLE_PAY_EXPRESS_CHECKOUT` URL param
2049
- * gate is also evaluated as a fallback until all host apps have integrated
2050
- * `window.experimentIntegration`. Once all consumers are migrated, the URL param
2051
- * fallback will be removed.
2048
+ * **PAYWALL only — transition period:** the legacy
2049
+ * `FEATURE_APPLE_PAY_EXPRESS_CHECKOUT` URL param can show the button without
2050
+ * treatment (and without inflating `$exposure`). For banners there is no
2051
+ * experiment gate; `BYPASS_APPLE_PAY_CHECKS` only skips device checks in
2052
+ * `isApplePayAvailable()` on mount.
2052
2053
  *
2053
2054
  * **Eligibility:**
2054
- * - `DUAL_OFFER_BANNER` (or omitted): homepage + Safari/iPhone (SMA-3301)
2055
+ * - `DUAL_OFFER_BANNER` / `FREE_TRIAL_BANNER` (or omitted): always eligible at
2056
+ * this gate (homepage placement is host-owned; Safari/Apple is enforced
2057
+ * later by `isApplePayAvailable()` on mount)
2055
2058
  * - `PAYWALL`: Safari/Apple device only (no homepage) — article paywalls
2056
- * - `REGWALL` / `FREE_TRIAL_POPUP`: no host-side placement gate
2057
2059
  *
2058
2060
  * @param offer - The offer to display in the Apple Pay flow.
2059
2061
  * @param country - The user's country code.
@@ -2076,6 +2078,9 @@ var ApplePayButton = class _ApplePayButton extends HTMLElement {
2076
2078
  }
2077
2079
  const { isEligible: eligibilityCheck, experimentFlagKey } = config;
2078
2080
  const isEligible = eligibilityCheck();
2081
+ if (config.enabled === false) {
2082
+ return null;
2083
+ }
2079
2084
  if (!hasUrlParamFlag && !bypassApplePayChecks() && !isEligible) {
2080
2085
  return null;
2081
2086
  }
@@ -2220,9 +2225,7 @@ var ApplePayButton = class _ApplePayButton extends HTMLElement {
2220
2225
  button.removeAttribute("aria-busy");
2221
2226
  button.getAnimations?.({ subtree: true }).forEach((animation) => animation.cancel());
2222
2227
  }
2223
- this.getAnimations?.({ subtree: true }).forEach(
2224
- (animation) => animation.cancel()
2225
- );
2228
+ this.getAnimations?.({ subtree: true }).forEach((animation) => animation.cancel());
2226
2229
  void this.offsetHeight;
2227
2230
  this.style.display = "none";
2228
2231
  this.querySelector(SEL_CONTAINER)?.remove();
@@ -2331,7 +2334,10 @@ var ApplePayButton = class _ApplePayButton extends HTMLElement {
2331
2334
  if (document.querySelector(ApplePayModal.tag)) {
2332
2335
  return;
2333
2336
  }
2334
- trackExpressWalletClick(this.#offer.sku_id);
2337
+ trackExpressWalletClick(
2338
+ this.#offer.sku_id,
2339
+ this.#entryPoint ? { entryPoint: this.#entryPoint } : void 0
2340
+ );
2335
2341
  ApplePayModal.define();
2336
2342
  const modal = document.createElement(ApplePayModal.tag);
2337
2343
  modal.offer = this.#offer;
@@ -1307,7 +1307,6 @@ var redirectToCheckoutFallback = (params) => {
1307
1307
  };
1308
1308
 
1309
1309
  // src/constants.ts
1310
- var APPLE_PAY_EXPRESS_EXPERIMENT_KEY = "apple-pay-express-checkout-on-banners";
1311
1310
  var APPLE_PAY_EXPRESS_PAYWALLS_EXPERIMENT_KEY = "apple-pay-express-checkout-on-paywalls";
1312
1311
  var APPLE_PAY_EXPRESS_VARIANTS = {
1313
1312
  CONTROL: "control",
@@ -1351,7 +1350,7 @@ var track = (action, extra) => {
1351
1350
  ...extra
1352
1351
  });
1353
1352
  };
1354
- var trackApplePayExperimentExposure = (variant, experimentKey, flagKey = APPLE_PAY_EXPRESS_EXPERIMENT_KEY) => {
1353
+ var trackApplePayExperimentExposure = (variant, experimentKey, flagKey) => {
1355
1354
  const isActiveVariant = variant === APPLE_PAY_EXPRESS_VARIANTS.CONTROL || variant === APPLE_PAY_EXPRESS_VARIANTS.TREATMENT;
1356
1355
  if (!isActiveVariant) {
1357
1356
  return;
@@ -1380,8 +1379,11 @@ var trackExpressWalletImpression = (skuId, { entryPoint, country }) => {
1380
1379
  device: getDevice()
1381
1380
  });
1382
1381
  };
1383
- var trackExpressWalletClick = (skuId) => {
1384
- track(EXPRESS_CHECKOUT_EVENTS.BUTTON_CLICK, { sku_id: skuId });
1382
+ var trackExpressWalletClick = (skuId, { entryPoint } = {}) => {
1383
+ track(EXPRESS_CHECKOUT_EVENTS.BUTTON_CLICK, {
1384
+ sku_id: skuId,
1385
+ ...entryPoint ? { entry_point: entryPoint } : {}
1386
+ });
1385
1387
  };
1386
1388
  var trackWalletPaymentSheetOpen = (skuId) => {
1387
1389
  track(EXPRESS_CHECKOUT_EVENTS.SHEET_OPEN, { sku_id: skuId });
@@ -2002,15 +2004,17 @@ var getExperimentKeyFromNextData = (flagKey) => {
2002
2004
  };
2003
2005
 
2004
2006
  // src/utils/applePayExpressEligibility.ts
2005
- var isApplePayExpressHomepage = (pathname = window.location.pathname) => pathname === "/";
2006
- var isEligibleForApplePayExpressExperiment = () => isApplePayExpressHomepage() && isAppleDeviceOrSafari();
2007
2007
  var isEligibleForApplePayExpressPaywallExperiment = () => isAppleDeviceOrSafari();
2008
2008
 
2009
2009
  // src/apple-pay-button.ts
2010
2010
  var ENTRY_POINT_CONFIG = {
2011
2011
  ["dual_offer_banner" /* DUAL_OFFER_BANNER */]: {
2012
- isEligible: isEligibleForApplePayExpressExperiment,
2013
- experimentFlagKey: APPLE_PAY_EXPRESS_EXPERIMENT_KEY
2012
+ isEligible: () => true
2013
+ // no experimentFlagKey → always-on after offer exclusions
2014
+ },
2015
+ ["free_trial_banner" /* FREE_TRIAL_BANNER */]: {
2016
+ isEligible: () => true
2017
+ // Dual parity — no experimentFlagKey, no homepage gate here
2014
2018
  },
2015
2019
  ["article_paywall" /* PAYWALL */]: {
2016
2020
  isEligible: isEligibleForApplePayExpressPaywallExperiment,
@@ -2018,10 +2022,7 @@ var ENTRY_POINT_CONFIG = {
2018
2022
  },
2019
2023
  ["article_regwall" /* REGWALL */]: {
2020
2024
  isEligible: () => true,
2021
- experimentFlagKey: APPLE_PAY_EXPRESS_EXPERIMENT_KEY
2022
- },
2023
- ["free_trial_popup" /* FREE_TRIAL_POPUP */]: {
2024
- isEligible: () => true
2025
+ enabled: false
2025
2026
  }
2026
2027
  };
2027
2028
  var FEATURE_APPLE_PAY_EXPRESS_CHECKOUT = "FEATURE_APPLE_PAY_EXPRESS_CHECKOUT";
@@ -2040,28 +2041,29 @@ var ApplePayButton = class _ApplePayButton extends HTMLElement {
2040
2041
  define(_ApplePayButton.tag, _ApplePayButton);
2041
2042
  }
2042
2043
  /**
2043
- * Creates and configures an ApplePayButton when the Amplitude experiment is enabled.
2044
+ * Creates and configures an ApplePayButton when the entry point allows it.
2044
2045
  *
2045
- * Reads the experiment variant from `window.experimentIntegration.getVariant`:
2046
- * - {@link APPLE_PAY_EXPRESS_PAYWALLS_EXPERIMENT_KEY} for `PAYWALL`
2047
- * - {@link APPLE_PAY_EXPRESS_EXPERIMENT_KEY} for `REGWALL` / `DUAL_OFFER_BANNER`
2048
- * - no Amplitude gate for `FREE_TRIAL_POPUP` (always shown)
2046
+ * - `DUAL_OFFER_BANNER` (or omitted): always-on after offer exclusions —
2047
+ * no homepage/Safari gate here, no banners experiment read, and no banners
2048
+ * `$exposure`.
2049
+ * - `FREE_TRIAL_BANNER`: same always-on-after-exclusions gate as Dual; distinct
2050
+ * TEDL `entry_point` string (`free_trial_banner`) for wallet impression attribution.
2051
+ * - `PAYWALL`: experiment-gated via {@link APPLE_PAY_EXPRESS_PAYWALLS_EXPERIMENT_KEY}
2052
+ * (`treatment` or QA URL param). The host must set up
2053
+ * `window.experimentIntegration` before calling for this entry point.
2054
+ * - `REGWALL`: inert — always returns null.
2049
2055
  *
2050
- * The host page is responsible for setting up `window.experimentIntegration`
2051
- * before calling this method (except `FREE_TRIAL_POPUP`).
2052
- *
2053
- * - `treatment` → button created and returned
2054
- * - `control` / missing / anything else → returns null (button suppressed)
2055
- *
2056
- * **Transition period:** the legacy `FEATURE_APPLE_PAY_EXPRESS_CHECKOUT` URL param
2057
- * gate is also evaluated as a fallback until all host apps have integrated
2058
- * `window.experimentIntegration`. Once all consumers are migrated, the URL param
2059
- * fallback will be removed.
2056
+ * **PAYWALL only — transition period:** the legacy
2057
+ * `FEATURE_APPLE_PAY_EXPRESS_CHECKOUT` URL param can show the button without
2058
+ * treatment (and without inflating `$exposure`). For banners there is no
2059
+ * experiment gate; `BYPASS_APPLE_PAY_CHECKS` only skips device checks in
2060
+ * `isApplePayAvailable()` on mount.
2060
2061
  *
2061
2062
  * **Eligibility:**
2062
- * - `DUAL_OFFER_BANNER` (or omitted): homepage + Safari/iPhone (SMA-3301)
2063
+ * - `DUAL_OFFER_BANNER` / `FREE_TRIAL_BANNER` (or omitted): always eligible at
2064
+ * this gate (homepage placement is host-owned; Safari/Apple is enforced
2065
+ * later by `isApplePayAvailable()` on mount)
2063
2066
  * - `PAYWALL`: Safari/Apple device only (no homepage) — article paywalls
2064
- * - `REGWALL` / `FREE_TRIAL_POPUP`: no host-side placement gate
2065
2067
  *
2066
2068
  * @param offer - The offer to display in the Apple Pay flow.
2067
2069
  * @param country - The user's country code.
@@ -2084,6 +2086,9 @@ var ApplePayButton = class _ApplePayButton extends HTMLElement {
2084
2086
  }
2085
2087
  const { isEligible: eligibilityCheck, experimentFlagKey } = config;
2086
2088
  const isEligible = eligibilityCheck();
2089
+ if (config.enabled === false) {
2090
+ return null;
2091
+ }
2087
2092
  if (!hasUrlParamFlag && !bypassApplePayChecks() && !isEligible) {
2088
2093
  return null;
2089
2094
  }
@@ -2228,9 +2233,7 @@ var ApplePayButton = class _ApplePayButton extends HTMLElement {
2228
2233
  button.removeAttribute("aria-busy");
2229
2234
  button.getAnimations?.({ subtree: true }).forEach((animation) => animation.cancel());
2230
2235
  }
2231
- this.getAnimations?.({ subtree: true }).forEach(
2232
- (animation) => animation.cancel()
2233
- );
2236
+ this.getAnimations?.({ subtree: true }).forEach((animation) => animation.cancel());
2234
2237
  void this.offsetHeight;
2235
2238
  this.style.display = "none";
2236
2239
  this.querySelector(SEL_CONTAINER)?.remove();
@@ -2339,7 +2342,10 @@ var ApplePayButton = class _ApplePayButton extends HTMLElement {
2339
2342
  if (document.querySelector(ApplePayModal.tag)) {
2340
2343
  return;
2341
2344
  }
2342
- trackExpressWalletClick(this.#offer.sku_id);
2345
+ trackExpressWalletClick(
2346
+ this.#offer.sku_id,
2347
+ this.#entryPoint ? { entryPoint: this.#entryPoint } : void 0
2348
+ );
2343
2349
  ApplePayModal.define();
2344
2350
  const modal = document.createElement(ApplePayModal.tag);
2345
2351
  modal.offer = this.#offer;
@@ -1299,7 +1299,6 @@ var redirectToCheckoutFallback = (params) => {
1299
1299
  };
1300
1300
 
1301
1301
  // src/constants.ts
1302
- var APPLE_PAY_EXPRESS_EXPERIMENT_KEY = "apple-pay-express-checkout-on-banners";
1303
1302
  var APPLE_PAY_EXPRESS_PAYWALLS_EXPERIMENT_KEY = "apple-pay-express-checkout-on-paywalls";
1304
1303
  var APPLE_PAY_EXPRESS_VARIANTS = {
1305
1304
  CONTROL: "control",
@@ -1343,7 +1342,7 @@ var track = (action, extra) => {
1343
1342
  ...extra
1344
1343
  });
1345
1344
  };
1346
- var trackApplePayExperimentExposure = (variant, experimentKey, flagKey = APPLE_PAY_EXPRESS_EXPERIMENT_KEY) => {
1345
+ var trackApplePayExperimentExposure = (variant, experimentKey, flagKey) => {
1347
1346
  const isActiveVariant = variant === APPLE_PAY_EXPRESS_VARIANTS.CONTROL || variant === APPLE_PAY_EXPRESS_VARIANTS.TREATMENT;
1348
1347
  if (!isActiveVariant) {
1349
1348
  return;
@@ -1372,8 +1371,11 @@ var trackExpressWalletImpression = (skuId, { entryPoint, country }) => {
1372
1371
  device: getDevice()
1373
1372
  });
1374
1373
  };
1375
- var trackExpressWalletClick = (skuId) => {
1376
- track(EXPRESS_CHECKOUT_EVENTS.BUTTON_CLICK, { sku_id: skuId });
1374
+ var trackExpressWalletClick = (skuId, { entryPoint } = {}) => {
1375
+ track(EXPRESS_CHECKOUT_EVENTS.BUTTON_CLICK, {
1376
+ sku_id: skuId,
1377
+ ...entryPoint ? { entry_point: entryPoint } : {}
1378
+ });
1377
1379
  };
1378
1380
  var trackWalletPaymentSheetOpen = (skuId) => {
1379
1381
  track(EXPRESS_CHECKOUT_EVENTS.SHEET_OPEN, { sku_id: skuId });
@@ -1994,15 +1996,17 @@ var getExperimentKeyFromNextData = (flagKey) => {
1994
1996
  };
1995
1997
 
1996
1998
  // src/utils/applePayExpressEligibility.ts
1997
- var isApplePayExpressHomepage = (pathname = window.location.pathname) => pathname === "/";
1998
- var isEligibleForApplePayExpressExperiment = () => isApplePayExpressHomepage() && isAppleDeviceOrSafari();
1999
1999
  var isEligibleForApplePayExpressPaywallExperiment = () => isAppleDeviceOrSafari();
2000
2000
 
2001
2001
  // src/apple-pay-button.ts
2002
2002
  var ENTRY_POINT_CONFIG = {
2003
2003
  ["dual_offer_banner" /* DUAL_OFFER_BANNER */]: {
2004
- isEligible: isEligibleForApplePayExpressExperiment,
2005
- experimentFlagKey: APPLE_PAY_EXPRESS_EXPERIMENT_KEY
2004
+ isEligible: () => true
2005
+ // no experimentFlagKey → always-on after offer exclusions
2006
+ },
2007
+ ["free_trial_banner" /* FREE_TRIAL_BANNER */]: {
2008
+ isEligible: () => true
2009
+ // Dual parity — no experimentFlagKey, no homepage gate here
2006
2010
  },
2007
2011
  ["article_paywall" /* PAYWALL */]: {
2008
2012
  isEligible: isEligibleForApplePayExpressPaywallExperiment,
@@ -2010,10 +2014,7 @@ var ENTRY_POINT_CONFIG = {
2010
2014
  },
2011
2015
  ["article_regwall" /* REGWALL */]: {
2012
2016
  isEligible: () => true,
2013
- experimentFlagKey: APPLE_PAY_EXPRESS_EXPERIMENT_KEY
2014
- },
2015
- ["free_trial_popup" /* FREE_TRIAL_POPUP */]: {
2016
- isEligible: () => true
2017
+ enabled: false
2017
2018
  }
2018
2019
  };
2019
2020
  var FEATURE_APPLE_PAY_EXPRESS_CHECKOUT = "FEATURE_APPLE_PAY_EXPRESS_CHECKOUT";
@@ -2032,28 +2033,29 @@ var ApplePayButton = class _ApplePayButton extends HTMLElement {
2032
2033
  define(_ApplePayButton.tag, _ApplePayButton);
2033
2034
  }
2034
2035
  /**
2035
- * Creates and configures an ApplePayButton when the Amplitude experiment is enabled.
2036
+ * Creates and configures an ApplePayButton when the entry point allows it.
2036
2037
  *
2037
- * Reads the experiment variant from `window.experimentIntegration.getVariant`:
2038
- * - {@link APPLE_PAY_EXPRESS_PAYWALLS_EXPERIMENT_KEY} for `PAYWALL`
2039
- * - {@link APPLE_PAY_EXPRESS_EXPERIMENT_KEY} for `REGWALL` / `DUAL_OFFER_BANNER`
2040
- * - no Amplitude gate for `FREE_TRIAL_POPUP` (always shown)
2038
+ * - `DUAL_OFFER_BANNER` (or omitted): always-on after offer exclusions —
2039
+ * no homepage/Safari gate here, no banners experiment read, and no banners
2040
+ * `$exposure`.
2041
+ * - `FREE_TRIAL_BANNER`: same always-on-after-exclusions gate as Dual; distinct
2042
+ * TEDL `entry_point` string (`free_trial_banner`) for wallet impression attribution.
2043
+ * - `PAYWALL`: experiment-gated via {@link APPLE_PAY_EXPRESS_PAYWALLS_EXPERIMENT_KEY}
2044
+ * (`treatment` or QA URL param). The host must set up
2045
+ * `window.experimentIntegration` before calling for this entry point.
2046
+ * - `REGWALL`: inert — always returns null.
2041
2047
  *
2042
- * The host page is responsible for setting up `window.experimentIntegration`
2043
- * before calling this method (except `FREE_TRIAL_POPUP`).
2044
- *
2045
- * - `treatment` → button created and returned
2046
- * - `control` / missing / anything else → returns null (button suppressed)
2047
- *
2048
- * **Transition period:** the legacy `FEATURE_APPLE_PAY_EXPRESS_CHECKOUT` URL param
2049
- * gate is also evaluated as a fallback until all host apps have integrated
2050
- * `window.experimentIntegration`. Once all consumers are migrated, the URL param
2051
- * fallback will be removed.
2048
+ * **PAYWALL only — transition period:** the legacy
2049
+ * `FEATURE_APPLE_PAY_EXPRESS_CHECKOUT` URL param can show the button without
2050
+ * treatment (and without inflating `$exposure`). For banners there is no
2051
+ * experiment gate; `BYPASS_APPLE_PAY_CHECKS` only skips device checks in
2052
+ * `isApplePayAvailable()` on mount.
2052
2053
  *
2053
2054
  * **Eligibility:**
2054
- * - `DUAL_OFFER_BANNER` (or omitted): homepage + Safari/iPhone (SMA-3301)
2055
+ * - `DUAL_OFFER_BANNER` / `FREE_TRIAL_BANNER` (or omitted): always eligible at
2056
+ * this gate (homepage placement is host-owned; Safari/Apple is enforced
2057
+ * later by `isApplePayAvailable()` on mount)
2055
2058
  * - `PAYWALL`: Safari/Apple device only (no homepage) — article paywalls
2056
- * - `REGWALL` / `FREE_TRIAL_POPUP`: no host-side placement gate
2057
2059
  *
2058
2060
  * @param offer - The offer to display in the Apple Pay flow.
2059
2061
  * @param country - The user's country code.
@@ -2076,6 +2078,9 @@ var ApplePayButton = class _ApplePayButton extends HTMLElement {
2076
2078
  }
2077
2079
  const { isEligible: eligibilityCheck, experimentFlagKey } = config;
2078
2080
  const isEligible = eligibilityCheck();
2081
+ if (config.enabled === false) {
2082
+ return null;
2083
+ }
2079
2084
  if (!hasUrlParamFlag && !bypassApplePayChecks() && !isEligible) {
2080
2085
  return null;
2081
2086
  }
@@ -2220,9 +2225,7 @@ var ApplePayButton = class _ApplePayButton extends HTMLElement {
2220
2225
  button.removeAttribute("aria-busy");
2221
2226
  button.getAnimations?.({ subtree: true }).forEach((animation) => animation.cancel());
2222
2227
  }
2223
- this.getAnimations?.({ subtree: true }).forEach(
2224
- (animation) => animation.cancel()
2225
- );
2228
+ this.getAnimations?.({ subtree: true }).forEach((animation) => animation.cancel());
2226
2229
  void this.offsetHeight;
2227
2230
  this.style.display = "none";
2228
2231
  this.querySelector(SEL_CONTAINER)?.remove();
@@ -2331,7 +2334,10 @@ var ApplePayButton = class _ApplePayButton extends HTMLElement {
2331
2334
  if (document.querySelector(ApplePayModal.tag)) {
2332
2335
  return;
2333
2336
  }
2334
- trackExpressWalletClick(this.#offer.sku_id);
2337
+ trackExpressWalletClick(
2338
+ this.#offer.sku_id,
2339
+ this.#entryPoint ? { entryPoint: this.#entryPoint } : void 0
2340
+ );
2335
2341
  ApplePayModal.define();
2336
2342
  const modal = document.createElement(ApplePayModal.tag);
2337
2343
  modal.offer = this.#offer;
@@ -2,38 +2,26 @@ export declare enum ApplePayEntryPoint {
2
2
  PAYWALL = "article_paywall",
3
3
  REGWALL = "article_regwall",
4
4
  DUAL_OFFER_BANNER = "dual_offer_banner",
5
- /** Post-registration free-trial popup. Not Amplitude-gated. */
6
- FREE_TRIAL_POPUP = "free_trial_popup"
5
+ FREE_TRIAL_BANNER = "free_trial_banner"
7
6
  }
8
- /**
9
- * Amplitude experiment key for Apple Pay Express Checkout on homepage banners
10
- * (dual-offer banner) and article banner regwalls.
11
- *
12
- * This package reads the variant for this key internally via
13
- * `window.experimentIntegration.getVariant()` — set up by the host page
14
- * before `createIfEnabled` is called.
15
- *
16
- * Fail-closed: missing integration, missing key, `control`, or Amplitude fetch
17
- * failure on the host → button is hidden (unless QA URL param is present).
18
- *
19
- * Must match the Feature experiment key in Amplitude and host app constants
20
- * (Engagement / Insider / Podcasts).
21
- */
22
- export declare const APPLE_PAY_EXPRESS_EXPERIMENT_KEY = "apple-pay-express-checkout-on-banners";
23
7
  /**
24
8
  * Amplitude experiment key for Apple Pay Express Checkout on article paywalls
25
9
  * (`ApplePayEntryPoint.PAYWALL`, e.g. single-offer-paywall / multi-offer paywall).
26
10
  *
27
- * Same fail-closed / QA-param behaviour as {@link APPLE_PAY_EXPRESS_EXPERIMENT_KEY}.
11
+ * Fail-closed when the variant is missing or not `treatment`; the QA URL param
12
+ * `FEATURE_APPLE_PAY_EXPRESS_CHECKOUT` can show the button without treatment.
28
13
  * Eligibility for this flag ignores homepage and requires Safari/Apple device
29
14
  * (plus `isApplePayAvailable` before `$exposure`).
15
+ *
16
+ * Must match the Feature experiment key in Amplitude and host app constants
17
+ * (Engagement / Insider / Podcasts) while those still bridge the experiment.
30
18
  */
31
19
  export declare const APPLE_PAY_EXPRESS_PAYWALLS_EXPERIMENT_KEY = "apple-pay-express-checkout-on-paywalls";
32
20
  /**
33
21
  * Known Amplitude experiment flag keys for Apple Pay Express Checkout.
34
22
  * Narrowed so callers cannot pass an arbitrary typo'd string.
35
23
  */
36
- export type ApplePayExperimentFlagKey = typeof APPLE_PAY_EXPRESS_EXPERIMENT_KEY | typeof APPLE_PAY_EXPRESS_PAYWALLS_EXPERIMENT_KEY;
24
+ export type ApplePayExperimentFlagKey = typeof APPLE_PAY_EXPRESS_PAYWALLS_EXPERIMENT_KEY;
37
25
  /**
38
26
  * Amplitude variant values for the Apple Pay Express Checkout experiment.
39
27
  * CONTROL → button hidden (baseline)
@@ -3,17 +3,15 @@ var ApplePayEntryPoint = /* @__PURE__ */ ((ApplePayEntryPoint2) => {
3
3
  ApplePayEntryPoint2["PAYWALL"] = "article_paywall";
4
4
  ApplePayEntryPoint2["REGWALL"] = "article_regwall";
5
5
  ApplePayEntryPoint2["DUAL_OFFER_BANNER"] = "dual_offer_banner";
6
- ApplePayEntryPoint2["FREE_TRIAL_POPUP"] = "free_trial_popup";
6
+ ApplePayEntryPoint2["FREE_TRIAL_BANNER"] = "free_trial_banner";
7
7
  return ApplePayEntryPoint2;
8
8
  })(ApplePayEntryPoint || {});
9
- var APPLE_PAY_EXPRESS_EXPERIMENT_KEY = "apple-pay-express-checkout-on-banners";
10
9
  var APPLE_PAY_EXPRESS_PAYWALLS_EXPERIMENT_KEY = "apple-pay-express-checkout-on-paywalls";
11
10
  var APPLE_PAY_EXPRESS_VARIANTS = {
12
11
  CONTROL: "control",
13
12
  TREATMENT: "treatment"
14
13
  };
15
14
  export {
16
- APPLE_PAY_EXPRESS_EXPERIMENT_KEY,
17
15
  APPLE_PAY_EXPRESS_PAYWALLS_EXPERIMENT_KEY,
18
16
  APPLE_PAY_EXPRESS_VARIANTS,
19
17
  ApplePayEntryPoint
@@ -2,7 +2,7 @@ export { ApplePayButton, APPLE_PAY_EXCLUDED_VARIANTS, } from './apple-pay-button
2
2
  export { ApplePayModal } from './apple-pay-modal';
3
3
  export { ensureApplePayLogoSymbol } from './apple-pay-logo';
4
4
  export { configureApplePay } from './clients/payment-checkout/config';
5
- export { ApplePayEntryPoint, APPLE_PAY_EXPRESS_EXPERIMENT_KEY, APPLE_PAY_EXPRESS_PAYWALLS_EXPERIMENT_KEY, APPLE_PAY_EXPRESS_VARIANTS, } from './constants';
5
+ export { ApplePayEntryPoint, APPLE_PAY_EXPRESS_PAYWALLS_EXPERIMENT_KEY, APPLE_PAY_EXPRESS_VARIANTS, } from './constants';
6
6
  export type { ApplePayExperimentFlagKey } from './constants';
7
7
  export type { ApplePayRuntimeConfig } from './clients/payment-checkout/config';
8
8
  export type { Offer } from './types/offer.types';
package/dist/src/index.js CHANGED
@@ -1308,10 +1308,9 @@ var ApplePayEntryPoint = /* @__PURE__ */ ((ApplePayEntryPoint2) => {
1308
1308
  ApplePayEntryPoint2["PAYWALL"] = "article_paywall";
1309
1309
  ApplePayEntryPoint2["REGWALL"] = "article_regwall";
1310
1310
  ApplePayEntryPoint2["DUAL_OFFER_BANNER"] = "dual_offer_banner";
1311
- ApplePayEntryPoint2["FREE_TRIAL_POPUP"] = "free_trial_popup";
1311
+ ApplePayEntryPoint2["FREE_TRIAL_BANNER"] = "free_trial_banner";
1312
1312
  return ApplePayEntryPoint2;
1313
1313
  })(ApplePayEntryPoint || {});
1314
- var APPLE_PAY_EXPRESS_EXPERIMENT_KEY = "apple-pay-express-checkout-on-banners";
1315
1314
  var APPLE_PAY_EXPRESS_PAYWALLS_EXPERIMENT_KEY = "apple-pay-express-checkout-on-paywalls";
1316
1315
  var APPLE_PAY_EXPRESS_VARIANTS = {
1317
1316
  CONTROL: "control",
@@ -1355,7 +1354,7 @@ var track = (action, extra) => {
1355
1354
  ...extra
1356
1355
  });
1357
1356
  };
1358
- var trackApplePayExperimentExposure = (variant, experimentKey, flagKey = APPLE_PAY_EXPRESS_EXPERIMENT_KEY) => {
1357
+ var trackApplePayExperimentExposure = (variant, experimentKey, flagKey) => {
1359
1358
  const isActiveVariant = variant === APPLE_PAY_EXPRESS_VARIANTS.CONTROL || variant === APPLE_PAY_EXPRESS_VARIANTS.TREATMENT;
1360
1359
  if (!isActiveVariant) {
1361
1360
  return;
@@ -1384,8 +1383,11 @@ var trackExpressWalletImpression = (skuId, { entryPoint, country }) => {
1384
1383
  device: getDevice()
1385
1384
  });
1386
1385
  };
1387
- var trackExpressWalletClick = (skuId) => {
1388
- track(EXPRESS_CHECKOUT_EVENTS.BUTTON_CLICK, { sku_id: skuId });
1386
+ var trackExpressWalletClick = (skuId, { entryPoint } = {}) => {
1387
+ track(EXPRESS_CHECKOUT_EVENTS.BUTTON_CLICK, {
1388
+ sku_id: skuId,
1389
+ ...entryPoint ? { entry_point: entryPoint } : {}
1390
+ });
1389
1391
  };
1390
1392
  var trackWalletPaymentSheetOpen = (skuId) => {
1391
1393
  track(EXPRESS_CHECKOUT_EVENTS.SHEET_OPEN, { sku_id: skuId });
@@ -2006,15 +2008,17 @@ var getExperimentKeyFromNextData = (flagKey) => {
2006
2008
  };
2007
2009
 
2008
2010
  // src/utils/applePayExpressEligibility.ts
2009
- var isApplePayExpressHomepage = (pathname = window.location.pathname) => pathname === "/";
2010
- var isEligibleForApplePayExpressExperiment = () => isApplePayExpressHomepage() && isAppleDeviceOrSafari();
2011
2011
  var isEligibleForApplePayExpressPaywallExperiment = () => isAppleDeviceOrSafari();
2012
2012
 
2013
2013
  // src/apple-pay-button.ts
2014
2014
  var ENTRY_POINT_CONFIG = {
2015
2015
  ["dual_offer_banner" /* DUAL_OFFER_BANNER */]: {
2016
- isEligible: isEligibleForApplePayExpressExperiment,
2017
- experimentFlagKey: APPLE_PAY_EXPRESS_EXPERIMENT_KEY
2016
+ isEligible: () => true
2017
+ // no experimentFlagKey → always-on after offer exclusions
2018
+ },
2019
+ ["free_trial_banner" /* FREE_TRIAL_BANNER */]: {
2020
+ isEligible: () => true
2021
+ // Dual parity — no experimentFlagKey, no homepage gate here
2018
2022
  },
2019
2023
  ["article_paywall" /* PAYWALL */]: {
2020
2024
  isEligible: isEligibleForApplePayExpressPaywallExperiment,
@@ -2022,10 +2026,7 @@ var ENTRY_POINT_CONFIG = {
2022
2026
  },
2023
2027
  ["article_regwall" /* REGWALL */]: {
2024
2028
  isEligible: () => true,
2025
- experimentFlagKey: APPLE_PAY_EXPRESS_EXPERIMENT_KEY
2026
- },
2027
- ["free_trial_popup" /* FREE_TRIAL_POPUP */]: {
2028
- isEligible: () => true
2029
+ enabled: false
2029
2030
  }
2030
2031
  };
2031
2032
  var FEATURE_APPLE_PAY_EXPRESS_CHECKOUT = "FEATURE_APPLE_PAY_EXPRESS_CHECKOUT";
@@ -2044,28 +2045,29 @@ var ApplePayButton = class _ApplePayButton extends HTMLElement {
2044
2045
  define(_ApplePayButton.tag, _ApplePayButton);
2045
2046
  }
2046
2047
  /**
2047
- * Creates and configures an ApplePayButton when the Amplitude experiment is enabled.
2048
+ * Creates and configures an ApplePayButton when the entry point allows it.
2048
2049
  *
2049
- * Reads the experiment variant from `window.experimentIntegration.getVariant`:
2050
- * - {@link APPLE_PAY_EXPRESS_PAYWALLS_EXPERIMENT_KEY} for `PAYWALL`
2051
- * - {@link APPLE_PAY_EXPRESS_EXPERIMENT_KEY} for `REGWALL` / `DUAL_OFFER_BANNER`
2052
- * - no Amplitude gate for `FREE_TRIAL_POPUP` (always shown)
2050
+ * - `DUAL_OFFER_BANNER` (or omitted): always-on after offer exclusions —
2051
+ * no homepage/Safari gate here, no banners experiment read, and no banners
2052
+ * `$exposure`.
2053
+ * - `FREE_TRIAL_BANNER`: same always-on-after-exclusions gate as Dual; distinct
2054
+ * TEDL `entry_point` string (`free_trial_banner`) for wallet impression attribution.
2055
+ * - `PAYWALL`: experiment-gated via {@link APPLE_PAY_EXPRESS_PAYWALLS_EXPERIMENT_KEY}
2056
+ * (`treatment` or QA URL param). The host must set up
2057
+ * `window.experimentIntegration` before calling for this entry point.
2058
+ * - `REGWALL`: inert — always returns null.
2053
2059
  *
2054
- * The host page is responsible for setting up `window.experimentIntegration`
2055
- * before calling this method (except `FREE_TRIAL_POPUP`).
2056
- *
2057
- * - `treatment` → button created and returned
2058
- * - `control` / missing / anything else → returns null (button suppressed)
2059
- *
2060
- * **Transition period:** the legacy `FEATURE_APPLE_PAY_EXPRESS_CHECKOUT` URL param
2061
- * gate is also evaluated as a fallback until all host apps have integrated
2062
- * `window.experimentIntegration`. Once all consumers are migrated, the URL param
2063
- * fallback will be removed.
2060
+ * **PAYWALL only — transition period:** the legacy
2061
+ * `FEATURE_APPLE_PAY_EXPRESS_CHECKOUT` URL param can show the button without
2062
+ * treatment (and without inflating `$exposure`). For banners there is no
2063
+ * experiment gate; `BYPASS_APPLE_PAY_CHECKS` only skips device checks in
2064
+ * `isApplePayAvailable()` on mount.
2064
2065
  *
2065
2066
  * **Eligibility:**
2066
- * - `DUAL_OFFER_BANNER` (or omitted): homepage + Safari/iPhone (SMA-3301)
2067
+ * - `DUAL_OFFER_BANNER` / `FREE_TRIAL_BANNER` (or omitted): always eligible at
2068
+ * this gate (homepage placement is host-owned; Safari/Apple is enforced
2069
+ * later by `isApplePayAvailable()` on mount)
2067
2070
  * - `PAYWALL`: Safari/Apple device only (no homepage) — article paywalls
2068
- * - `REGWALL` / `FREE_TRIAL_POPUP`: no host-side placement gate
2069
2071
  *
2070
2072
  * @param offer - The offer to display in the Apple Pay flow.
2071
2073
  * @param country - The user's country code.
@@ -2088,6 +2090,9 @@ var ApplePayButton = class _ApplePayButton extends HTMLElement {
2088
2090
  }
2089
2091
  const { isEligible: eligibilityCheck, experimentFlagKey } = config;
2090
2092
  const isEligible = eligibilityCheck();
2093
+ if (config.enabled === false) {
2094
+ return null;
2095
+ }
2091
2096
  if (!hasUrlParamFlag && !bypassApplePayChecks() && !isEligible) {
2092
2097
  return null;
2093
2098
  }
@@ -2232,9 +2237,7 @@ var ApplePayButton = class _ApplePayButton extends HTMLElement {
2232
2237
  button.removeAttribute("aria-busy");
2233
2238
  button.getAnimations?.({ subtree: true }).forEach((animation) => animation.cancel());
2234
2239
  }
2235
- this.getAnimations?.({ subtree: true }).forEach(
2236
- (animation) => animation.cancel()
2237
- );
2240
+ this.getAnimations?.({ subtree: true }).forEach((animation) => animation.cancel());
2238
2241
  void this.offsetHeight;
2239
2242
  this.style.display = "none";
2240
2243
  this.querySelector(SEL_CONTAINER)?.remove();
@@ -2343,7 +2346,10 @@ var ApplePayButton = class _ApplePayButton extends HTMLElement {
2343
2346
  if (document.querySelector(ApplePayModal.tag)) {
2344
2347
  return;
2345
2348
  }
2346
- trackExpressWalletClick(this.#offer.sku_id);
2349
+ trackExpressWalletClick(
2350
+ this.#offer.sku_id,
2351
+ this.#entryPoint ? { entryPoint: this.#entryPoint } : void 0
2352
+ );
2347
2353
  ApplePayModal.define();
2348
2354
  const modal = document.createElement(ApplePayModal.tag);
2349
2355
  modal.offer = this.#offer;
@@ -2435,7 +2441,6 @@ function defineApplePayElements() {
2435
2441
  }
2436
2442
  export {
2437
2443
  APPLE_PAY_EXCLUDED_VARIANTS,
2438
- APPLE_PAY_EXPRESS_EXPERIMENT_KEY,
2439
2444
  APPLE_PAY_EXPRESS_PAYWALLS_EXPERIMENT_KEY,
2440
2445
  APPLE_PAY_EXPRESS_VARIANTS,
2441
2446
  ApplePayButton,
@@ -1,18 +1,19 @@
1
1
  /**
2
- * Host-side eligibility for Apple Pay Express Feature Experiment (SMA-3301).
2
+ * Host-side placement eligibility helpers for Apple Pay Express.
3
3
  *
4
- * Gates createIfEnabled before getVariant / $exposure so only the intended
5
- * population participates. Exposure is further gated on isApplePayAvailable()
6
- * (BYPASS_APPLE_PAY_CHECKS respected). Amplitude audience targeting uses
7
- * generic user_properties from Engagement.
4
+ * Paywalls: used by createIfEnabled after offer exclusions so experiment
5
+ * $exposure is Safari/Apple-gated. Banners: createIfEnabled does not call
6
+ * the banner helper (always-on after offer exclusions); Safari/Apple still
7
+ * runs later via isApplePayAvailable() on mount. Exposure (paywalls) is
8
+ * further gated on isApplePayAvailable() (BYPASS_APPLE_PAY_CHECKS respected).
8
9
  */
9
10
  export declare const isApplePayExpressHomepage: (pathname?: string) => boolean;
10
11
  /**
11
- * Dual-offer homepage banner: homepage + Safari/iPhone.
12
- * Anonymous is assumed because the dual-offer banner is only mounted for
13
- * anonymous visitors.
12
+ * Dual-offer / Free Trial homepage banners: homepage + Safari/iPhone.
13
+ * Anonymous is assumed because these banners are only mounted for
14
+ * anonymous visitors. Not wired into createIfEnabled ENTRY_POINT_CONFIG.
14
15
  */
15
- export declare const isEligibleForApplePayExpressExperiment: () => boolean;
16
+ export declare const isEligibleForApplePayExpressBanner: () => boolean;
16
17
  /**
17
18
  * Article paywalls (`ApplePayEntryPoint.PAYWALL`): Safari/Apple device only.
18
19
  * Homepage targeting does not apply — paywalls render on article pages.
@@ -7,10 +7,10 @@ var isAppleDeviceOrSafari = () => {
7
7
 
8
8
  // src/utils/applePayExpressEligibility.ts
9
9
  var isApplePayExpressHomepage = (pathname = window.location.pathname) => pathname === "/";
10
- var isEligibleForApplePayExpressExperiment = () => isApplePayExpressHomepage() && isAppleDeviceOrSafari();
10
+ var isEligibleForApplePayExpressBanner = () => isApplePayExpressHomepage() && isAppleDeviceOrSafari();
11
11
  var isEligibleForApplePayExpressPaywallExperiment = () => isAppleDeviceOrSafari();
12
12
  export {
13
13
  isApplePayExpressHomepage,
14
- isEligibleForApplePayExpressExperiment,
14
+ isEligibleForApplePayExpressBanner,
15
15
  isEligibleForApplePayExpressPaywallExperiment
16
16
  };
@@ -57,14 +57,23 @@ export type ImpressionMetadata = {
57
57
  * experiment_key from Engagement SSR (__NEXT_DATA__) when present for run
58
58
  * attribution.
59
59
  *
60
- * @param flagKey - Amplitude flag_key. Defaults to the banners experiment;
61
- * paywalls pass {@link APPLE_PAY_EXPRESS_PAYWALLS_EXPERIMENT_KEY}.
60
+ * @param flagKey - Amplitude flag_key (required). Callers must pass an
61
+ * explicit key such as {@link APPLE_PAY_EXPRESS_PAYWALLS_EXPERIMENT_KEY}.
62
62
  */
63
- export declare const trackApplePayExperimentExposure: (variant: string, experimentKey?: string, flagKey?: ApplePayExperimentFlagKey) => void;
63
+ export declare const trackApplePayExperimentExposure: (variant: string, experimentKey: string | undefined, flagKey: ApplePayExperimentFlagKey) => void;
64
64
  /** Wallet button displayed — emits all 5 required analytics dimensions. */
65
65
  export declare const trackExpressWalletImpression: (skuId: string, { entryPoint, country }: ImpressionMetadata) => void;
66
+ /**
67
+ * Optional metadata for the wallet click event.
68
+ * `entry_point` is included only when provided so existing one-arg callers stay
69
+ * payload-compatible.
70
+ */
71
+ export type ClickMetadata = {
72
+ /** Entry point where the Apple Pay button was clicked — e.g. 'free_trial_banner'. */
73
+ entryPoint?: string;
74
+ };
66
75
  /** Wallet button clicked */
67
- export declare const trackExpressWalletClick: (skuId: string) => void;
76
+ export declare const trackExpressWalletClick: (skuId: string, { entryPoint }?: ClickMetadata) => void;
68
77
  /** Apple Pay sheet opened (session.begin() called) */
69
78
  export declare const trackWalletPaymentSheetOpen: (skuId: string) => void;
70
79
  /** Payment authorised by the user in the Apple Pay sheet */
@@ -1,5 +1,4 @@
1
1
  // src/constants.ts
2
- var APPLE_PAY_EXPRESS_EXPERIMENT_KEY = "apple-pay-express-checkout-on-banners";
3
2
  var APPLE_PAY_EXPRESS_VARIANTS = {
4
3
  CONTROL: "control",
5
4
  TREATMENT: "treatment"
@@ -42,7 +41,7 @@ var track = (action, extra) => {
42
41
  ...extra
43
42
  });
44
43
  };
45
- var trackApplePayExperimentExposure = (variant, experimentKey, flagKey = APPLE_PAY_EXPRESS_EXPERIMENT_KEY) => {
44
+ var trackApplePayExperimentExposure = (variant, experimentKey, flagKey) => {
46
45
  const isActiveVariant = variant === APPLE_PAY_EXPRESS_VARIANTS.CONTROL || variant === APPLE_PAY_EXPRESS_VARIANTS.TREATMENT;
47
46
  if (!isActiveVariant) {
48
47
  return;
@@ -71,8 +70,11 @@ var trackExpressWalletImpression = (skuId, { entryPoint, country }) => {
71
70
  device: getDevice()
72
71
  });
73
72
  };
74
- var trackExpressWalletClick = (skuId) => {
75
- track(EXPRESS_CHECKOUT_EVENTS.BUTTON_CLICK, { sku_id: skuId });
73
+ var trackExpressWalletClick = (skuId, { entryPoint } = {}) => {
74
+ track(EXPRESS_CHECKOUT_EVENTS.BUTTON_CLICK, {
75
+ sku_id: skuId,
76
+ ...entryPoint ? { entry_point: entryPoint } : {}
77
+ });
76
78
  };
77
79
  var trackWalletPaymentSheetOpen = (skuId) => {
78
80
  track(EXPRESS_CHECKOUT_EVENTS.SHEET_OPEN, { sku_id: skuId });
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@economist/web-apple-pay",
3
3
  "description": "Web component package for Apple Pay checkout UI",
4
- "version": "1.10.2-beta.1",
4
+ "version": "1.11.0",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
7
7
  "types": "dist/index.d.ts",