@economist/web-apple-pay 1.9.5 → 2.0.0-beta.1

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
@@ -121,10 +121,19 @@ Consumers should listen for this event and continue payment orchestration.
121
121
  - `configureApplePay(config)`: sets required runtime config (merchant ID and wallet API base URL, with optional debug bypass).
122
122
  - `ApplePayButton`: button component class.
123
123
  - `whenReady(): Promise<void>` — resolves when the current render lifecycle completes (available or hidden). Resolves immediately if called before the element is connected.
124
+ - `createIfEnabled(offer, country, entryPoint?)`: returns a configured button or `null`.
125
+ - `DUAL_OFFER_BANNER` (or omitted): always-on after homepage + Safari/Apple eligibility and offer exclusions — no banners Amplitude experiment / `$exposure`.
126
+ - `PAYWALL`: experiment-gated via `apple-pay-express-checkout-on-paywalls` (`treatment` or QA URL).
127
+ - `REGWALL`: inert — always returns `null`.
124
128
  - `ApplePayModal`: modal component class.
125
129
  - `ensureApplePayLogoSymbol()`: logo symbol injection helper.
130
+ - `APPLE_PAY_EXPRESS_PAYWALLS_EXPERIMENT_KEY`: Amplitude flag key for paywalls experiment gating / `$exposure`.
126
131
  - `Offer`: offer contract type used by component props.
127
132
 
133
+ ### Breaking changes
134
+
135
+ - `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.
136
+
128
137
  ## Troubleshooting
129
138
 
130
139
  ### Button does not appear
@@ -133,7 +142,8 @@ Check:
133
142
 
134
143
  - `configureApplePay(...)` was called before rendering (with a valid `merchantId` and `walletApiBaseUrl`)
135
144
  - device/browser supports Apple Pay (Safari on Apple device required)
136
- - query string/feature flag gating for your page is satisfied
145
+ - for paywalls: Amplitude treatment (or `?FEATURE_APPLE_PAY_EXPRESS_CHECKOUT`) is satisfied
146
+ - for banners: homepage + Safari/Apple eligibility (and consumer kill switch off)
137
147
  - offer variant is not excluded (`bundle`, `insider_print`)
138
148
 
139
149
  ### Logged-in checkbox copy looks wrong
package/dist/index.js CHANGED
@@ -1310,7 +1310,7 @@ var ApplePayEntryPoint = /* @__PURE__ */ ((ApplePayEntryPoint2) => {
1310
1310
  ApplePayEntryPoint2["DUAL_OFFER_BANNER"] = "dual_offer_banner";
1311
1311
  return ApplePayEntryPoint2;
1312
1312
  })(ApplePayEntryPoint || {});
1313
- var APPLE_PAY_EXPRESS_EXPERIMENT_KEY = "apple-pay-express-checkout-on-banners";
1313
+ var APPLE_PAY_EXPRESS_PAYWALLS_EXPERIMENT_KEY = "apple-pay-express-checkout-on-paywalls";
1314
1314
  var APPLE_PAY_EXPRESS_VARIANTS = {
1315
1315
  CONTROL: "control",
1316
1316
  TREATMENT: "treatment"
@@ -1353,7 +1353,7 @@ var track = (action, extra) => {
1353
1353
  ...extra
1354
1354
  });
1355
1355
  };
1356
- var trackApplePayExperimentExposure = (variant, experimentKey) => {
1356
+ var trackApplePayExperimentExposure = (variant, experimentKey, flagKey) => {
1357
1357
  const isActiveVariant = variant === APPLE_PAY_EXPRESS_VARIANTS.CONTROL || variant === APPLE_PAY_EXPRESS_VARIANTS.TREATMENT;
1358
1358
  if (!isActiveVariant) {
1359
1359
  return;
@@ -1361,14 +1361,14 @@ var trackApplePayExperimentExposure = (variant, experimentKey) => {
1361
1361
  const exposures = window.__applePayExperimentExposures ?? {};
1362
1362
  window.__applePayExperimentExposures = exposures;
1363
1363
  const pathname = window.location?.pathname ?? "";
1364
- const dedupeKey = `${pathname}:${APPLE_PAY_EXPRESS_EXPERIMENT_KEY}:${variant}`;
1364
+ const dedupeKey = `${pathname}:${flagKey}:${variant}`;
1365
1365
  if (exposures[dedupeKey]) {
1366
1366
  return;
1367
1367
  }
1368
1368
  exposures[dedupeKey] = true;
1369
1369
  emitTedlEvent({
1370
1370
  name: "$exposure",
1371
- flag_key: APPLE_PAY_EXPRESS_EXPERIMENT_KEY,
1371
+ flag_key: flagKey,
1372
1372
  variant,
1373
1373
  ...experimentKey ? { experiment_key: experimentKey } : {}
1374
1374
  });
@@ -2005,9 +2005,24 @@ var getExperimentKeyFromNextData = (flagKey) => {
2005
2005
 
2006
2006
  // src/utils/applePayExpressEligibility.ts
2007
2007
  var isApplePayExpressHomepage = (pathname = window.location.pathname) => pathname === "/";
2008
- var isEligibleForApplePayExpressExperiment = () => isApplePayExpressHomepage() && isAppleDeviceOrSafari();
2008
+ var isEligibleForApplePayExpressBanner = () => isApplePayExpressHomepage() && isAppleDeviceOrSafari();
2009
+ var isEligibleForApplePayExpressPaywallExperiment = () => isAppleDeviceOrSafari();
2009
2010
 
2010
2011
  // src/apple-pay-button.ts
2012
+ var ENTRY_POINT_CONFIG = {
2013
+ ["dual_offer_banner" /* DUAL_OFFER_BANNER */]: {
2014
+ isEligible: isEligibleForApplePayExpressBanner
2015
+ // no experimentFlagKey → always-on after eligibility / exclusions
2016
+ },
2017
+ ["article_paywall" /* PAYWALL */]: {
2018
+ isEligible: isEligibleForApplePayExpressPaywallExperiment,
2019
+ experimentFlagKey: APPLE_PAY_EXPRESS_PAYWALLS_EXPERIMENT_KEY
2020
+ },
2021
+ ["article_regwall" /* REGWALL */]: {
2022
+ isEligible: () => true,
2023
+ enabled: false
2024
+ }
2025
+ };
2011
2026
  var FEATURE_APPLE_PAY_EXPRESS_CHECKOUT = "FEATURE_APPLE_PAY_EXPRESS_CHECKOUT";
2012
2027
  var TOTAL_LABEL = "The Economist";
2013
2028
  var DEFAULT_CURRENCY = "USD";
@@ -2024,25 +2039,24 @@ var ApplePayButton = class _ApplePayButton extends HTMLElement {
2024
2039
  define(_ApplePayButton.tag, _ApplePayButton);
2025
2040
  }
2026
2041
  /**
2027
- * Creates and configures an ApplePayButton when the Amplitude experiment is enabled.
2028
- *
2029
- * Reads the experiment variant from `window.experimentIntegration.getVariant` using
2030
- * {@link APPLE_PAY_EXPRESS_EXPERIMENT_KEY}. The host page is responsible
2031
- * for setting up `window.experimentIntegration` before calling this method.
2042
+ * Creates and configures an ApplePayButton when the entry point allows it.
2032
2043
  *
2033
- * - `treatment` → button created and returned
2034
- * - `control` / missing / anything else → returns null (button suppressed)
2044
+ * - `DUAL_OFFER_BANNER` (or omitted): always-on after eligibility / exclusions —
2045
+ * no banners experiment read and no banners `$exposure`.
2046
+ * - `PAYWALL`: experiment-gated via {@link APPLE_PAY_EXPRESS_PAYWALLS_EXPERIMENT_KEY}
2047
+ * (`treatment` or QA URL param). The host must set up
2048
+ * `window.experimentIntegration` before calling for this entry point.
2049
+ * - `REGWALL`: inert — always returns null.
2035
2050
  *
2036
- * **Transition period:** the legacy `FEATURE_APPLE_PAY_EXPRESS_CHECKOUT` URL param
2037
- * gate is also evaluated as a fallback until all host apps have integrated
2038
- * `window.experimentIntegration`. Once all consumers are migrated, the URL param
2039
- * fallback will be removed.
2051
+ * **PAYWALL only — transition period:** the legacy
2052
+ * `FEATURE_APPLE_PAY_EXPRESS_CHECKOUT` URL param can show the button without
2053
+ * treatment (and without inflating `$exposure`). For banners, the same URL
2054
+ * param / `BYPASS_APPLE_PAY_CHECKS` only bypass **eligibility**, not an
2055
+ * experiment gate.
2040
2056
  *
2041
- * **SMA-3301 eligibility:** the homepage + Safari/iPhone eligibility gate
2042
- * only applies to the `DUAL_OFFER_BANNER` entry point (or when `entryPoint`
2043
- * is omitted). `PAYWALL`/`REGWALL` entry points render on article pages and
2044
- * keep their pre-SMA-3301 behaviour unaffected. Anonymous is assumed because
2045
- * the dual-offer banner is only mounted for anonymous visitors.
2057
+ * **Eligibility:**
2058
+ * - `DUAL_OFFER_BANNER` (or omitted): homepage + Safari/iPhone
2059
+ * - `PAYWALL`: Safari/Apple device only (no homepage) — article paywalls
2046
2060
  *
2047
2061
  * @param offer - The offer to display in the Apple Pay flow.
2048
2062
  * @param country - The user's country code.
@@ -2058,29 +2072,40 @@ var ApplePayButton = class _ApplePayButton extends HTMLElement {
2058
2072
  const hasUrlParamFlag = new URLSearchParams(window.location.search).has(
2059
2073
  FEATURE_APPLE_PAY_EXPRESS_CHECKOUT
2060
2074
  );
2061
- const isBannerEntryPoint = !entryPoint || entryPoint === "dual_offer_banner" /* DUAL_OFFER_BANNER */;
2062
- const isEligible = !isBannerEntryPoint || isEligibleForApplePayExpressExperiment();
2063
- if (isBannerEntryPoint && !hasUrlParamFlag && !bypassApplePayChecks() && !isEligible) {
2075
+ const resolvedEntryPoint = entryPoint || "dual_offer_banner" /* DUAL_OFFER_BANNER */;
2076
+ const config = ENTRY_POINT_CONFIG[resolvedEntryPoint];
2077
+ if (!config) {
2064
2078
  return null;
2065
2079
  }
2066
- const experimentValue = window.experimentIntegration?.getVariant?.(
2067
- APPLE_PAY_EXPRESS_EXPERIMENT_KEY
2068
- );
2069
- const isExperimentVariant = experimentValue === APPLE_PAY_EXPRESS_VARIANTS.CONTROL || experimentValue === APPLE_PAY_EXPRESS_VARIANTS.TREATMENT;
2070
- if (isExperimentVariant && isEligible && !bypassApplePayChecks() && !hasUrlParamFlag) {
2071
- void isApplePayAvailable().then((available) => {
2072
- if (available) {
2073
- trackApplePayExperimentExposure(
2074
- experimentValue,
2075
- getExperimentKeyFromNextData(APPLE_PAY_EXPRESS_EXPERIMENT_KEY)
2076
- );
2077
- }
2078
- });
2080
+ const { isEligible: eligibilityCheck, experimentFlagKey } = config;
2081
+ const isEligible = eligibilityCheck();
2082
+ if (config.enabled === false) {
2083
+ return null;
2079
2084
  }
2080
- const isTreatment = experimentValue === APPLE_PAY_EXPRESS_VARIANTS.TREATMENT;
2081
- if (!isTreatment && !hasUrlParamFlag) {
2085
+ if (!hasUrlParamFlag && !bypassApplePayChecks() && !isEligible) {
2082
2086
  return null;
2083
2087
  }
2088
+ if (experimentFlagKey) {
2089
+ const experimentValue = window.experimentIntegration?.getVariant?.(
2090
+ experimentFlagKey
2091
+ );
2092
+ const isExperimentVariant = experimentValue === APPLE_PAY_EXPRESS_VARIANTS.CONTROL || experimentValue === APPLE_PAY_EXPRESS_VARIANTS.TREATMENT;
2093
+ if (isExperimentVariant && isEligible && !bypassApplePayChecks() && !hasUrlParamFlag) {
2094
+ void isApplePayAvailable().then((available) => {
2095
+ if (available) {
2096
+ trackApplePayExperimentExposure(
2097
+ experimentValue,
2098
+ getExperimentKeyFromNextData(experimentFlagKey),
2099
+ experimentFlagKey
2100
+ );
2101
+ }
2102
+ });
2103
+ }
2104
+ const isTreatment = experimentValue === APPLE_PAY_EXPRESS_VARIANTS.TREATMENT;
2105
+ if (!isTreatment && !hasUrlParamFlag) {
2106
+ return null;
2107
+ }
2108
+ }
2084
2109
  _ApplePayButton.define();
2085
2110
  const button = document.createElement(_ApplePayButton.tag);
2086
2111
  button.offer = offer;
@@ -2393,6 +2418,8 @@ function defineApplePayElements() {
2393
2418
  }
2394
2419
  export {
2395
2420
  APPLE_PAY_EXCLUDED_VARIANTS,
2421
+ APPLE_PAY_EXPRESS_PAYWALLS_EXPERIMENT_KEY,
2422
+ APPLE_PAY_EXPRESS_VARIANTS,
2396
2423
  ApplePayButton,
2397
2424
  ApplePayEntryPoint,
2398
2425
  ApplePayModal,
@@ -6,25 +6,24 @@ 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` using
12
- * {@link APPLE_PAY_EXPRESS_EXPERIMENT_KEY}. The host page is responsible
13
- * for setting up `window.experimentIntegration` before calling this method.
11
+ * - `DUAL_OFFER_BANNER` (or omitted): always-on after eligibility / exclusions —
12
+ * no banners experiment read and no banners `$exposure`.
13
+ * - `PAYWALL`: experiment-gated via {@link APPLE_PAY_EXPRESS_PAYWALLS_EXPERIMENT_KEY}
14
+ * (`treatment` or QA URL param). The host must set up
15
+ * `window.experimentIntegration` before calling for this entry point.
16
+ * - `REGWALL`: inert — always returns null.
14
17
  *
15
- * - `treatment` → button created and returned
16
- * - `control` / missing / anything else → returns null (button suppressed)
18
+ * **PAYWALL only — transition period:** the legacy
19
+ * `FEATURE_APPLE_PAY_EXPRESS_CHECKOUT` URL param can show the button without
20
+ * treatment (and without inflating `$exposure`). For banners, the same URL
21
+ * param / `BYPASS_APPLE_PAY_CHECKS` only bypass **eligibility**, not an
22
+ * experiment gate.
17
23
  *
18
- * **Transition period:** the legacy `FEATURE_APPLE_PAY_EXPRESS_CHECKOUT` URL param
19
- * gate is also evaluated as a fallback until all host apps have integrated
20
- * `window.experimentIntegration`. Once all consumers are migrated, the URL param
21
- * fallback will be removed.
22
- *
23
- * **SMA-3301 eligibility:** the homepage + Safari/iPhone eligibility gate
24
- * only applies to the `DUAL_OFFER_BANNER` entry point (or when `entryPoint`
25
- * is omitted). `PAYWALL`/`REGWALL` entry points render on article pages and
26
- * keep their pre-SMA-3301 behaviour unaffected. Anonymous is assumed because
27
- * the dual-offer banner is only mounted for anonymous visitors.
24
+ * **Eligibility:**
25
+ * - `DUAL_OFFER_BANNER` (or omitted): homepage + Safari/iPhone
26
+ * - `PAYWALL`: Safari/Apple device only (no homepage) — article paywalls
28
27
  *
29
28
  * @param offer - The offer to display in the Apple Pay flow.
30
29
  * @param country - The user's country code.
@@ -1299,7 +1299,7 @@ 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";
1302
+ var APPLE_PAY_EXPRESS_PAYWALLS_EXPERIMENT_KEY = "apple-pay-express-checkout-on-paywalls";
1303
1303
  var APPLE_PAY_EXPRESS_VARIANTS = {
1304
1304
  CONTROL: "control",
1305
1305
  TREATMENT: "treatment"
@@ -1342,7 +1342,7 @@ var track = (action, extra) => {
1342
1342
  ...extra
1343
1343
  });
1344
1344
  };
1345
- var trackApplePayExperimentExposure = (variant, experimentKey) => {
1345
+ var trackApplePayExperimentExposure = (variant, experimentKey, flagKey) => {
1346
1346
  const isActiveVariant = variant === APPLE_PAY_EXPRESS_VARIANTS.CONTROL || variant === APPLE_PAY_EXPRESS_VARIANTS.TREATMENT;
1347
1347
  if (!isActiveVariant) {
1348
1348
  return;
@@ -1350,14 +1350,14 @@ var trackApplePayExperimentExposure = (variant, experimentKey) => {
1350
1350
  const exposures = window.__applePayExperimentExposures ?? {};
1351
1351
  window.__applePayExperimentExposures = exposures;
1352
1352
  const pathname = window.location?.pathname ?? "";
1353
- const dedupeKey = `${pathname}:${APPLE_PAY_EXPRESS_EXPERIMENT_KEY}:${variant}`;
1353
+ const dedupeKey = `${pathname}:${flagKey}:${variant}`;
1354
1354
  if (exposures[dedupeKey]) {
1355
1355
  return;
1356
1356
  }
1357
1357
  exposures[dedupeKey] = true;
1358
1358
  emitTedlEvent({
1359
1359
  name: "$exposure",
1360
- flag_key: APPLE_PAY_EXPRESS_EXPERIMENT_KEY,
1360
+ flag_key: flagKey,
1361
1361
  variant,
1362
1362
  ...experimentKey ? { experiment_key: experimentKey } : {}
1363
1363
  });
@@ -1994,9 +1994,24 @@ var getExperimentKeyFromNextData = (flagKey) => {
1994
1994
 
1995
1995
  // src/utils/applePayExpressEligibility.ts
1996
1996
  var isApplePayExpressHomepage = (pathname = window.location.pathname) => pathname === "/";
1997
- var isEligibleForApplePayExpressExperiment = () => isApplePayExpressHomepage() && isAppleDeviceOrSafari();
1997
+ var isEligibleForApplePayExpressBanner = () => isApplePayExpressHomepage() && isAppleDeviceOrSafari();
1998
+ var isEligibleForApplePayExpressPaywallExperiment = () => isAppleDeviceOrSafari();
1998
1999
 
1999
2000
  // src/apple-pay-button.ts
2001
+ var ENTRY_POINT_CONFIG = {
2002
+ ["dual_offer_banner" /* DUAL_OFFER_BANNER */]: {
2003
+ isEligible: isEligibleForApplePayExpressBanner
2004
+ // no experimentFlagKey → always-on after eligibility / exclusions
2005
+ },
2006
+ ["article_paywall" /* PAYWALL */]: {
2007
+ isEligible: isEligibleForApplePayExpressPaywallExperiment,
2008
+ experimentFlagKey: APPLE_PAY_EXPRESS_PAYWALLS_EXPERIMENT_KEY
2009
+ },
2010
+ ["article_regwall" /* REGWALL */]: {
2011
+ isEligible: () => true,
2012
+ enabled: false
2013
+ }
2014
+ };
2000
2015
  var FEATURE_APPLE_PAY_EXPRESS_CHECKOUT = "FEATURE_APPLE_PAY_EXPRESS_CHECKOUT";
2001
2016
  var TOTAL_LABEL = "The Economist";
2002
2017
  var DEFAULT_CURRENCY = "USD";
@@ -2013,25 +2028,24 @@ var ApplePayButton = class _ApplePayButton extends HTMLElement {
2013
2028
  define(_ApplePayButton.tag, _ApplePayButton);
2014
2029
  }
2015
2030
  /**
2016
- * Creates and configures an ApplePayButton when the Amplitude experiment is enabled.
2017
- *
2018
- * Reads the experiment variant from `window.experimentIntegration.getVariant` using
2019
- * {@link APPLE_PAY_EXPRESS_EXPERIMENT_KEY}. The host page is responsible
2020
- * for setting up `window.experimentIntegration` before calling this method.
2031
+ * Creates and configures an ApplePayButton when the entry point allows it.
2021
2032
  *
2022
- * - `treatment` → button created and returned
2023
- * - `control` / missing / anything else → returns null (button suppressed)
2033
+ * - `DUAL_OFFER_BANNER` (or omitted): always-on after eligibility / exclusions —
2034
+ * no banners experiment read and no banners `$exposure`.
2035
+ * - `PAYWALL`: experiment-gated via {@link APPLE_PAY_EXPRESS_PAYWALLS_EXPERIMENT_KEY}
2036
+ * (`treatment` or QA URL param). The host must set up
2037
+ * `window.experimentIntegration` before calling for this entry point.
2038
+ * - `REGWALL`: inert — always returns null.
2024
2039
  *
2025
- * **Transition period:** the legacy `FEATURE_APPLE_PAY_EXPRESS_CHECKOUT` URL param
2026
- * gate is also evaluated as a fallback until all host apps have integrated
2027
- * `window.experimentIntegration`. Once all consumers are migrated, the URL param
2028
- * fallback will be removed.
2040
+ * **PAYWALL only — transition period:** the legacy
2041
+ * `FEATURE_APPLE_PAY_EXPRESS_CHECKOUT` URL param can show the button without
2042
+ * treatment (and without inflating `$exposure`). For banners, the same URL
2043
+ * param / `BYPASS_APPLE_PAY_CHECKS` only bypass **eligibility**, not an
2044
+ * experiment gate.
2029
2045
  *
2030
- * **SMA-3301 eligibility:** the homepage + Safari/iPhone eligibility gate
2031
- * only applies to the `DUAL_OFFER_BANNER` entry point (or when `entryPoint`
2032
- * is omitted). `PAYWALL`/`REGWALL` entry points render on article pages and
2033
- * keep their pre-SMA-3301 behaviour unaffected. Anonymous is assumed because
2034
- * the dual-offer banner is only mounted for anonymous visitors.
2046
+ * **Eligibility:**
2047
+ * - `DUAL_OFFER_BANNER` (or omitted): homepage + Safari/iPhone
2048
+ * - `PAYWALL`: Safari/Apple device only (no homepage) — article paywalls
2035
2049
  *
2036
2050
  * @param offer - The offer to display in the Apple Pay flow.
2037
2051
  * @param country - The user's country code.
@@ -2047,29 +2061,40 @@ var ApplePayButton = class _ApplePayButton extends HTMLElement {
2047
2061
  const hasUrlParamFlag = new URLSearchParams(window.location.search).has(
2048
2062
  FEATURE_APPLE_PAY_EXPRESS_CHECKOUT
2049
2063
  );
2050
- const isBannerEntryPoint = !entryPoint || entryPoint === "dual_offer_banner" /* DUAL_OFFER_BANNER */;
2051
- const isEligible = !isBannerEntryPoint || isEligibleForApplePayExpressExperiment();
2052
- if (isBannerEntryPoint && !hasUrlParamFlag && !bypassApplePayChecks() && !isEligible) {
2064
+ const resolvedEntryPoint = entryPoint || "dual_offer_banner" /* DUAL_OFFER_BANNER */;
2065
+ const config = ENTRY_POINT_CONFIG[resolvedEntryPoint];
2066
+ if (!config) {
2053
2067
  return null;
2054
2068
  }
2055
- const experimentValue = window.experimentIntegration?.getVariant?.(
2056
- APPLE_PAY_EXPRESS_EXPERIMENT_KEY
2057
- );
2058
- const isExperimentVariant = experimentValue === APPLE_PAY_EXPRESS_VARIANTS.CONTROL || experimentValue === APPLE_PAY_EXPRESS_VARIANTS.TREATMENT;
2059
- if (isExperimentVariant && isEligible && !bypassApplePayChecks() && !hasUrlParamFlag) {
2060
- void isApplePayAvailable().then((available) => {
2061
- if (available) {
2062
- trackApplePayExperimentExposure(
2063
- experimentValue,
2064
- getExperimentKeyFromNextData(APPLE_PAY_EXPRESS_EXPERIMENT_KEY)
2065
- );
2066
- }
2067
- });
2069
+ const { isEligible: eligibilityCheck, experimentFlagKey } = config;
2070
+ const isEligible = eligibilityCheck();
2071
+ if (config.enabled === false) {
2072
+ return null;
2068
2073
  }
2069
- const isTreatment = experimentValue === APPLE_PAY_EXPRESS_VARIANTS.TREATMENT;
2070
- if (!isTreatment && !hasUrlParamFlag) {
2074
+ if (!hasUrlParamFlag && !bypassApplePayChecks() && !isEligible) {
2071
2075
  return null;
2072
2076
  }
2077
+ if (experimentFlagKey) {
2078
+ const experimentValue = window.experimentIntegration?.getVariant?.(
2079
+ experimentFlagKey
2080
+ );
2081
+ const isExperimentVariant = experimentValue === APPLE_PAY_EXPRESS_VARIANTS.CONTROL || experimentValue === APPLE_PAY_EXPRESS_VARIANTS.TREATMENT;
2082
+ if (isExperimentVariant && isEligible && !bypassApplePayChecks() && !hasUrlParamFlag) {
2083
+ void isApplePayAvailable().then((available) => {
2084
+ if (available) {
2085
+ trackApplePayExperimentExposure(
2086
+ experimentValue,
2087
+ getExperimentKeyFromNextData(experimentFlagKey),
2088
+ experimentFlagKey
2089
+ );
2090
+ }
2091
+ });
2092
+ }
2093
+ const isTreatment = experimentValue === APPLE_PAY_EXPRESS_VARIANTS.TREATMENT;
2094
+ if (!isTreatment && !hasUrlParamFlag) {
2095
+ return null;
2096
+ }
2097
+ }
2073
2098
  _ApplePayButton.define();
2074
2099
  const button = document.createElement(_ApplePayButton.tag);
2075
2100
  button.offer = offer;
@@ -1307,7 +1307,7 @@ 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";
1310
+ var APPLE_PAY_EXPRESS_PAYWALLS_EXPERIMENT_KEY = "apple-pay-express-checkout-on-paywalls";
1311
1311
  var APPLE_PAY_EXPRESS_VARIANTS = {
1312
1312
  CONTROL: "control",
1313
1313
  TREATMENT: "treatment"
@@ -1350,7 +1350,7 @@ var track = (action, extra) => {
1350
1350
  ...extra
1351
1351
  });
1352
1352
  };
1353
- var trackApplePayExperimentExposure = (variant, experimentKey) => {
1353
+ var trackApplePayExperimentExposure = (variant, experimentKey, flagKey) => {
1354
1354
  const isActiveVariant = variant === APPLE_PAY_EXPRESS_VARIANTS.CONTROL || variant === APPLE_PAY_EXPRESS_VARIANTS.TREATMENT;
1355
1355
  if (!isActiveVariant) {
1356
1356
  return;
@@ -1358,14 +1358,14 @@ var trackApplePayExperimentExposure = (variant, experimentKey) => {
1358
1358
  const exposures = window.__applePayExperimentExposures ?? {};
1359
1359
  window.__applePayExperimentExposures = exposures;
1360
1360
  const pathname = window.location?.pathname ?? "";
1361
- const dedupeKey = `${pathname}:${APPLE_PAY_EXPRESS_EXPERIMENT_KEY}:${variant}`;
1361
+ const dedupeKey = `${pathname}:${flagKey}:${variant}`;
1362
1362
  if (exposures[dedupeKey]) {
1363
1363
  return;
1364
1364
  }
1365
1365
  exposures[dedupeKey] = true;
1366
1366
  emitTedlEvent({
1367
1367
  name: "$exposure",
1368
- flag_key: APPLE_PAY_EXPRESS_EXPERIMENT_KEY,
1368
+ flag_key: flagKey,
1369
1369
  variant,
1370
1370
  ...experimentKey ? { experiment_key: experimentKey } : {}
1371
1371
  });
@@ -2002,9 +2002,24 @@ var getExperimentKeyFromNextData = (flagKey) => {
2002
2002
 
2003
2003
  // src/utils/applePayExpressEligibility.ts
2004
2004
  var isApplePayExpressHomepage = (pathname = window.location.pathname) => pathname === "/";
2005
- var isEligibleForApplePayExpressExperiment = () => isApplePayExpressHomepage() && isAppleDeviceOrSafari();
2005
+ var isEligibleForApplePayExpressBanner = () => isApplePayExpressHomepage() && isAppleDeviceOrSafari();
2006
+ var isEligibleForApplePayExpressPaywallExperiment = () => isAppleDeviceOrSafari();
2006
2007
 
2007
2008
  // src/apple-pay-button.ts
2009
+ var ENTRY_POINT_CONFIG = {
2010
+ ["dual_offer_banner" /* DUAL_OFFER_BANNER */]: {
2011
+ isEligible: isEligibleForApplePayExpressBanner
2012
+ // no experimentFlagKey → always-on after eligibility / exclusions
2013
+ },
2014
+ ["article_paywall" /* PAYWALL */]: {
2015
+ isEligible: isEligibleForApplePayExpressPaywallExperiment,
2016
+ experimentFlagKey: APPLE_PAY_EXPRESS_PAYWALLS_EXPERIMENT_KEY
2017
+ },
2018
+ ["article_regwall" /* REGWALL */]: {
2019
+ isEligible: () => true,
2020
+ enabled: false
2021
+ }
2022
+ };
2008
2023
  var FEATURE_APPLE_PAY_EXPRESS_CHECKOUT = "FEATURE_APPLE_PAY_EXPRESS_CHECKOUT";
2009
2024
  var TOTAL_LABEL = "The Economist";
2010
2025
  var DEFAULT_CURRENCY = "USD";
@@ -2021,25 +2036,24 @@ var ApplePayButton = class _ApplePayButton extends HTMLElement {
2021
2036
  define(_ApplePayButton.tag, _ApplePayButton);
2022
2037
  }
2023
2038
  /**
2024
- * Creates and configures an ApplePayButton when the Amplitude experiment is enabled.
2039
+ * Creates and configures an ApplePayButton when the entry point allows it.
2025
2040
  *
2026
- * Reads the experiment variant from `window.experimentIntegration.getVariant` using
2027
- * {@link APPLE_PAY_EXPRESS_EXPERIMENT_KEY}. The host page is responsible
2028
- * for setting up `window.experimentIntegration` before calling this method.
2041
+ * - `DUAL_OFFER_BANNER` (or omitted): always-on after eligibility / exclusions —
2042
+ * no banners experiment read and no banners `$exposure`.
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.
2029
2047
  *
2030
- * - `treatment` → button created and returned
2031
- * - `control` / missing / anything else → returns null (button suppressed)
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, the same URL
2051
+ * param / `BYPASS_APPLE_PAY_CHECKS` only bypass **eligibility**, not an
2052
+ * experiment gate.
2032
2053
  *
2033
- * **Transition period:** the legacy `FEATURE_APPLE_PAY_EXPRESS_CHECKOUT` URL param
2034
- * gate is also evaluated as a fallback until all host apps have integrated
2035
- * `window.experimentIntegration`. Once all consumers are migrated, the URL param
2036
- * fallback will be removed.
2037
- *
2038
- * **SMA-3301 eligibility:** the homepage + Safari/iPhone eligibility gate
2039
- * only applies to the `DUAL_OFFER_BANNER` entry point (or when `entryPoint`
2040
- * is omitted). `PAYWALL`/`REGWALL` entry points render on article pages and
2041
- * keep their pre-SMA-3301 behaviour unaffected. Anonymous is assumed because
2042
- * the dual-offer banner is only mounted for anonymous visitors.
2054
+ * **Eligibility:**
2055
+ * - `DUAL_OFFER_BANNER` (or omitted): homepage + Safari/iPhone
2056
+ * - `PAYWALL`: Safari/Apple device only (no homepage) — article paywalls
2043
2057
  *
2044
2058
  * @param offer - The offer to display in the Apple Pay flow.
2045
2059
  * @param country - The user's country code.
@@ -2055,29 +2069,40 @@ var ApplePayButton = class _ApplePayButton extends HTMLElement {
2055
2069
  const hasUrlParamFlag = new URLSearchParams(window.location.search).has(
2056
2070
  FEATURE_APPLE_PAY_EXPRESS_CHECKOUT
2057
2071
  );
2058
- const isBannerEntryPoint = !entryPoint || entryPoint === "dual_offer_banner" /* DUAL_OFFER_BANNER */;
2059
- const isEligible = !isBannerEntryPoint || isEligibleForApplePayExpressExperiment();
2060
- if (isBannerEntryPoint && !hasUrlParamFlag && !bypassApplePayChecks() && !isEligible) {
2072
+ const resolvedEntryPoint = entryPoint || "dual_offer_banner" /* DUAL_OFFER_BANNER */;
2073
+ const config = ENTRY_POINT_CONFIG[resolvedEntryPoint];
2074
+ if (!config) {
2061
2075
  return null;
2062
2076
  }
2063
- const experimentValue = window.experimentIntegration?.getVariant?.(
2064
- APPLE_PAY_EXPRESS_EXPERIMENT_KEY
2065
- );
2066
- const isExperimentVariant = experimentValue === APPLE_PAY_EXPRESS_VARIANTS.CONTROL || experimentValue === APPLE_PAY_EXPRESS_VARIANTS.TREATMENT;
2067
- if (isExperimentVariant && isEligible && !bypassApplePayChecks() && !hasUrlParamFlag) {
2068
- void isApplePayAvailable().then((available) => {
2069
- if (available) {
2070
- trackApplePayExperimentExposure(
2071
- experimentValue,
2072
- getExperimentKeyFromNextData(APPLE_PAY_EXPRESS_EXPERIMENT_KEY)
2073
- );
2074
- }
2075
- });
2077
+ const { isEligible: eligibilityCheck, experimentFlagKey } = config;
2078
+ const isEligible = eligibilityCheck();
2079
+ if (config.enabled === false) {
2080
+ return null;
2076
2081
  }
2077
- const isTreatment = experimentValue === APPLE_PAY_EXPRESS_VARIANTS.TREATMENT;
2078
- if (!isTreatment && !hasUrlParamFlag) {
2082
+ if (!hasUrlParamFlag && !bypassApplePayChecks() && !isEligible) {
2079
2083
  return null;
2080
2084
  }
2085
+ if (experimentFlagKey) {
2086
+ const experimentValue = window.experimentIntegration?.getVariant?.(
2087
+ experimentFlagKey
2088
+ );
2089
+ const isExperimentVariant = experimentValue === APPLE_PAY_EXPRESS_VARIANTS.CONTROL || experimentValue === APPLE_PAY_EXPRESS_VARIANTS.TREATMENT;
2090
+ if (isExperimentVariant && isEligible && !bypassApplePayChecks() && !hasUrlParamFlag) {
2091
+ void isApplePayAvailable().then((available) => {
2092
+ if (available) {
2093
+ trackApplePayExperimentExposure(
2094
+ experimentValue,
2095
+ getExperimentKeyFromNextData(experimentFlagKey),
2096
+ experimentFlagKey
2097
+ );
2098
+ }
2099
+ });
2100
+ }
2101
+ const isTreatment = experimentValue === APPLE_PAY_EXPRESS_VARIANTS.TREATMENT;
2102
+ if (!isTreatment && !hasUrlParamFlag) {
2103
+ return null;
2104
+ }
2105
+ }
2081
2106
  _ApplePayButton.define();
2082
2107
  const button = document.createElement(_ApplePayButton.tag);
2083
2108
  button.offer = offer;
@@ -1299,7 +1299,7 @@ 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";
1302
+ var APPLE_PAY_EXPRESS_PAYWALLS_EXPERIMENT_KEY = "apple-pay-express-checkout-on-paywalls";
1303
1303
  var APPLE_PAY_EXPRESS_VARIANTS = {
1304
1304
  CONTROL: "control",
1305
1305
  TREATMENT: "treatment"
@@ -1342,7 +1342,7 @@ var track = (action, extra) => {
1342
1342
  ...extra
1343
1343
  });
1344
1344
  };
1345
- var trackApplePayExperimentExposure = (variant, experimentKey) => {
1345
+ var trackApplePayExperimentExposure = (variant, experimentKey, flagKey) => {
1346
1346
  const isActiveVariant = variant === APPLE_PAY_EXPRESS_VARIANTS.CONTROL || variant === APPLE_PAY_EXPRESS_VARIANTS.TREATMENT;
1347
1347
  if (!isActiveVariant) {
1348
1348
  return;
@@ -1350,14 +1350,14 @@ var trackApplePayExperimentExposure = (variant, experimentKey) => {
1350
1350
  const exposures = window.__applePayExperimentExposures ?? {};
1351
1351
  window.__applePayExperimentExposures = exposures;
1352
1352
  const pathname = window.location?.pathname ?? "";
1353
- const dedupeKey = `${pathname}:${APPLE_PAY_EXPRESS_EXPERIMENT_KEY}:${variant}`;
1353
+ const dedupeKey = `${pathname}:${flagKey}:${variant}`;
1354
1354
  if (exposures[dedupeKey]) {
1355
1355
  return;
1356
1356
  }
1357
1357
  exposures[dedupeKey] = true;
1358
1358
  emitTedlEvent({
1359
1359
  name: "$exposure",
1360
- flag_key: APPLE_PAY_EXPRESS_EXPERIMENT_KEY,
1360
+ flag_key: flagKey,
1361
1361
  variant,
1362
1362
  ...experimentKey ? { experiment_key: experimentKey } : {}
1363
1363
  });
@@ -1994,9 +1994,24 @@ var getExperimentKeyFromNextData = (flagKey) => {
1994
1994
 
1995
1995
  // src/utils/applePayExpressEligibility.ts
1996
1996
  var isApplePayExpressHomepage = (pathname = window.location.pathname) => pathname === "/";
1997
- var isEligibleForApplePayExpressExperiment = () => isApplePayExpressHomepage() && isAppleDeviceOrSafari();
1997
+ var isEligibleForApplePayExpressBanner = () => isApplePayExpressHomepage() && isAppleDeviceOrSafari();
1998
+ var isEligibleForApplePayExpressPaywallExperiment = () => isAppleDeviceOrSafari();
1998
1999
 
1999
2000
  // src/apple-pay-button.ts
2001
+ var ENTRY_POINT_CONFIG = {
2002
+ ["dual_offer_banner" /* DUAL_OFFER_BANNER */]: {
2003
+ isEligible: isEligibleForApplePayExpressBanner
2004
+ // no experimentFlagKey → always-on after eligibility / exclusions
2005
+ },
2006
+ ["article_paywall" /* PAYWALL */]: {
2007
+ isEligible: isEligibleForApplePayExpressPaywallExperiment,
2008
+ experimentFlagKey: APPLE_PAY_EXPRESS_PAYWALLS_EXPERIMENT_KEY
2009
+ },
2010
+ ["article_regwall" /* REGWALL */]: {
2011
+ isEligible: () => true,
2012
+ enabled: false
2013
+ }
2014
+ };
2000
2015
  var FEATURE_APPLE_PAY_EXPRESS_CHECKOUT = "FEATURE_APPLE_PAY_EXPRESS_CHECKOUT";
2001
2016
  var TOTAL_LABEL = "The Economist";
2002
2017
  var DEFAULT_CURRENCY = "USD";
@@ -2013,25 +2028,24 @@ var ApplePayButton = class _ApplePayButton extends HTMLElement {
2013
2028
  define(_ApplePayButton.tag, _ApplePayButton);
2014
2029
  }
2015
2030
  /**
2016
- * Creates and configures an ApplePayButton when the Amplitude experiment is enabled.
2031
+ * Creates and configures an ApplePayButton when the entry point allows it.
2017
2032
  *
2018
- * Reads the experiment variant from `window.experimentIntegration.getVariant` using
2019
- * {@link APPLE_PAY_EXPRESS_EXPERIMENT_KEY}. The host page is responsible
2020
- * for setting up `window.experimentIntegration` before calling this method.
2033
+ * - `DUAL_OFFER_BANNER` (or omitted): always-on after eligibility / exclusions —
2034
+ * no banners experiment read and no banners `$exposure`.
2035
+ * - `PAYWALL`: experiment-gated via {@link APPLE_PAY_EXPRESS_PAYWALLS_EXPERIMENT_KEY}
2036
+ * (`treatment` or QA URL param). The host must set up
2037
+ * `window.experimentIntegration` before calling for this entry point.
2038
+ * - `REGWALL`: inert — always returns null.
2021
2039
  *
2022
- * - `treatment` → button created and returned
2023
- * - `control` / missing / anything else → returns null (button suppressed)
2040
+ * **PAYWALL only — transition period:** the legacy
2041
+ * `FEATURE_APPLE_PAY_EXPRESS_CHECKOUT` URL param can show the button without
2042
+ * treatment (and without inflating `$exposure`). For banners, the same URL
2043
+ * param / `BYPASS_APPLE_PAY_CHECKS` only bypass **eligibility**, not an
2044
+ * experiment gate.
2024
2045
  *
2025
- * **Transition period:** the legacy `FEATURE_APPLE_PAY_EXPRESS_CHECKOUT` URL param
2026
- * gate is also evaluated as a fallback until all host apps have integrated
2027
- * `window.experimentIntegration`. Once all consumers are migrated, the URL param
2028
- * fallback will be removed.
2029
- *
2030
- * **SMA-3301 eligibility:** the homepage + Safari/iPhone eligibility gate
2031
- * only applies to the `DUAL_OFFER_BANNER` entry point (or when `entryPoint`
2032
- * is omitted). `PAYWALL`/`REGWALL` entry points render on article pages and
2033
- * keep their pre-SMA-3301 behaviour unaffected. Anonymous is assumed because
2034
- * the dual-offer banner is only mounted for anonymous visitors.
2046
+ * **Eligibility:**
2047
+ * - `DUAL_OFFER_BANNER` (or omitted): homepage + Safari/iPhone
2048
+ * - `PAYWALL`: Safari/Apple device only (no homepage) — article paywalls
2035
2049
  *
2036
2050
  * @param offer - The offer to display in the Apple Pay flow.
2037
2051
  * @param country - The user's country code.
@@ -2047,29 +2061,40 @@ var ApplePayButton = class _ApplePayButton extends HTMLElement {
2047
2061
  const hasUrlParamFlag = new URLSearchParams(window.location.search).has(
2048
2062
  FEATURE_APPLE_PAY_EXPRESS_CHECKOUT
2049
2063
  );
2050
- const isBannerEntryPoint = !entryPoint || entryPoint === "dual_offer_banner" /* DUAL_OFFER_BANNER */;
2051
- const isEligible = !isBannerEntryPoint || isEligibleForApplePayExpressExperiment();
2052
- if (isBannerEntryPoint && !hasUrlParamFlag && !bypassApplePayChecks() && !isEligible) {
2064
+ const resolvedEntryPoint = entryPoint || "dual_offer_banner" /* DUAL_OFFER_BANNER */;
2065
+ const config = ENTRY_POINT_CONFIG[resolvedEntryPoint];
2066
+ if (!config) {
2053
2067
  return null;
2054
2068
  }
2055
- const experimentValue = window.experimentIntegration?.getVariant?.(
2056
- APPLE_PAY_EXPRESS_EXPERIMENT_KEY
2057
- );
2058
- const isExperimentVariant = experimentValue === APPLE_PAY_EXPRESS_VARIANTS.CONTROL || experimentValue === APPLE_PAY_EXPRESS_VARIANTS.TREATMENT;
2059
- if (isExperimentVariant && isEligible && !bypassApplePayChecks() && !hasUrlParamFlag) {
2060
- void isApplePayAvailable().then((available) => {
2061
- if (available) {
2062
- trackApplePayExperimentExposure(
2063
- experimentValue,
2064
- getExperimentKeyFromNextData(APPLE_PAY_EXPRESS_EXPERIMENT_KEY)
2065
- );
2066
- }
2067
- });
2069
+ const { isEligible: eligibilityCheck, experimentFlagKey } = config;
2070
+ const isEligible = eligibilityCheck();
2071
+ if (config.enabled === false) {
2072
+ return null;
2068
2073
  }
2069
- const isTreatment = experimentValue === APPLE_PAY_EXPRESS_VARIANTS.TREATMENT;
2070
- if (!isTreatment && !hasUrlParamFlag) {
2074
+ if (!hasUrlParamFlag && !bypassApplePayChecks() && !isEligible) {
2071
2075
  return null;
2072
2076
  }
2077
+ if (experimentFlagKey) {
2078
+ const experimentValue = window.experimentIntegration?.getVariant?.(
2079
+ experimentFlagKey
2080
+ );
2081
+ const isExperimentVariant = experimentValue === APPLE_PAY_EXPRESS_VARIANTS.CONTROL || experimentValue === APPLE_PAY_EXPRESS_VARIANTS.TREATMENT;
2082
+ if (isExperimentVariant && isEligible && !bypassApplePayChecks() && !hasUrlParamFlag) {
2083
+ void isApplePayAvailable().then((available) => {
2084
+ if (available) {
2085
+ trackApplePayExperimentExposure(
2086
+ experimentValue,
2087
+ getExperimentKeyFromNextData(experimentFlagKey),
2088
+ experimentFlagKey
2089
+ );
2090
+ }
2091
+ });
2092
+ }
2093
+ const isTreatment = experimentValue === APPLE_PAY_EXPRESS_VARIANTS.TREATMENT;
2094
+ if (!isTreatment && !hasUrlParamFlag) {
2095
+ return null;
2096
+ }
2097
+ }
2073
2098
  _ApplePayButton.define();
2074
2099
  const button = document.createElement(_ApplePayButton.tag);
2075
2100
  button.offer = offer;
@@ -4,18 +4,23 @@ export declare enum ApplePayEntryPoint {
4
4
  DUAL_OFFER_BANNER = "dual_offer_banner"
5
5
  }
6
6
  /**
7
- * Amplitude experiment key for Apple Pay Express Checkout on banners.
8
- * This package reads the variant for this key internally via
9
- * `window.experimentIntegration.getVariant()` — set up by the host page
10
- * before `createIfEnabled` is called. Gates all entry points (paywall, regwall, banner).
7
+ * Amplitude experiment key for Apple Pay Express Checkout on article paywalls
8
+ * (`ApplePayEntryPoint.PAYWALL`, e.g. single-offer-paywall / multi-offer paywall).
11
9
  *
12
- * Fail-closed: missing integration, missing key, `control`, or Amplitude fetch
13
- * failure on the host → button is hidden (unless QA URL param is present).
10
+ * Fail-closed when the variant is missing or not `treatment`; the QA URL param
11
+ * `FEATURE_APPLE_PAY_EXPRESS_CHECKOUT` can show the button without treatment.
12
+ * Eligibility for this flag ignores homepage and requires Safari/Apple device
13
+ * (plus `isApplePayAvailable` before `$exposure`).
14
14
  *
15
15
  * Must match the Feature experiment key in Amplitude and host app constants
16
- * (Engagement / Insider / Podcasts).
16
+ * (Engagement / Insider / Podcasts) while those still bridge the experiment.
17
17
  */
18
- export declare const APPLE_PAY_EXPRESS_EXPERIMENT_KEY = "apple-pay-express-checkout-on-banners";
18
+ export declare const APPLE_PAY_EXPRESS_PAYWALLS_EXPERIMENT_KEY = "apple-pay-express-checkout-on-paywalls";
19
+ /**
20
+ * Known Amplitude experiment flag keys for Apple Pay Express Checkout.
21
+ * Narrowed so callers cannot pass an arbitrary typo'd string.
22
+ */
23
+ export type ApplePayExperimentFlagKey = typeof APPLE_PAY_EXPRESS_PAYWALLS_EXPERIMENT_KEY;
19
24
  /**
20
25
  * Amplitude variant values for the Apple Pay Express Checkout experiment.
21
26
  * CONTROL → button hidden (baseline)
@@ -5,13 +5,13 @@ var ApplePayEntryPoint = /* @__PURE__ */ ((ApplePayEntryPoint2) => {
5
5
  ApplePayEntryPoint2["DUAL_OFFER_BANNER"] = "dual_offer_banner";
6
6
  return ApplePayEntryPoint2;
7
7
  })(ApplePayEntryPoint || {});
8
- var APPLE_PAY_EXPRESS_EXPERIMENT_KEY = "apple-pay-express-checkout-on-banners";
8
+ var APPLE_PAY_EXPRESS_PAYWALLS_EXPERIMENT_KEY = "apple-pay-express-checkout-on-paywalls";
9
9
  var APPLE_PAY_EXPRESS_VARIANTS = {
10
10
  CONTROL: "control",
11
11
  TREATMENT: "treatment"
12
12
  };
13
13
  export {
14
- APPLE_PAY_EXPRESS_EXPERIMENT_KEY,
14
+ APPLE_PAY_EXPRESS_PAYWALLS_EXPERIMENT_KEY,
15
15
  APPLE_PAY_EXPRESS_VARIANTS,
16
16
  ApplePayEntryPoint
17
17
  };
@@ -2,7 +2,8 @@ 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 } from './constants';
5
+ export { ApplePayEntryPoint, APPLE_PAY_EXPRESS_PAYWALLS_EXPERIMENT_KEY, APPLE_PAY_EXPRESS_VARIANTS, } from './constants';
6
+ export type { ApplePayExperimentFlagKey } from './constants';
6
7
  export type { ApplePayRuntimeConfig } from './clients/payment-checkout/config';
7
8
  export type { Offer } from './types/offer.types';
8
9
  export declare function defineApplePayElements(): void;
package/dist/src/index.js CHANGED
@@ -1310,7 +1310,7 @@ var ApplePayEntryPoint = /* @__PURE__ */ ((ApplePayEntryPoint2) => {
1310
1310
  ApplePayEntryPoint2["DUAL_OFFER_BANNER"] = "dual_offer_banner";
1311
1311
  return ApplePayEntryPoint2;
1312
1312
  })(ApplePayEntryPoint || {});
1313
- var APPLE_PAY_EXPRESS_EXPERIMENT_KEY = "apple-pay-express-checkout-on-banners";
1313
+ var APPLE_PAY_EXPRESS_PAYWALLS_EXPERIMENT_KEY = "apple-pay-express-checkout-on-paywalls";
1314
1314
  var APPLE_PAY_EXPRESS_VARIANTS = {
1315
1315
  CONTROL: "control",
1316
1316
  TREATMENT: "treatment"
@@ -1353,7 +1353,7 @@ var track = (action, extra) => {
1353
1353
  ...extra
1354
1354
  });
1355
1355
  };
1356
- var trackApplePayExperimentExposure = (variant, experimentKey) => {
1356
+ var trackApplePayExperimentExposure = (variant, experimentKey, flagKey) => {
1357
1357
  const isActiveVariant = variant === APPLE_PAY_EXPRESS_VARIANTS.CONTROL || variant === APPLE_PAY_EXPRESS_VARIANTS.TREATMENT;
1358
1358
  if (!isActiveVariant) {
1359
1359
  return;
@@ -1361,14 +1361,14 @@ var trackApplePayExperimentExposure = (variant, experimentKey) => {
1361
1361
  const exposures = window.__applePayExperimentExposures ?? {};
1362
1362
  window.__applePayExperimentExposures = exposures;
1363
1363
  const pathname = window.location?.pathname ?? "";
1364
- const dedupeKey = `${pathname}:${APPLE_PAY_EXPRESS_EXPERIMENT_KEY}:${variant}`;
1364
+ const dedupeKey = `${pathname}:${flagKey}:${variant}`;
1365
1365
  if (exposures[dedupeKey]) {
1366
1366
  return;
1367
1367
  }
1368
1368
  exposures[dedupeKey] = true;
1369
1369
  emitTedlEvent({
1370
1370
  name: "$exposure",
1371
- flag_key: APPLE_PAY_EXPRESS_EXPERIMENT_KEY,
1371
+ flag_key: flagKey,
1372
1372
  variant,
1373
1373
  ...experimentKey ? { experiment_key: experimentKey } : {}
1374
1374
  });
@@ -2005,9 +2005,24 @@ var getExperimentKeyFromNextData = (flagKey) => {
2005
2005
 
2006
2006
  // src/utils/applePayExpressEligibility.ts
2007
2007
  var isApplePayExpressHomepage = (pathname = window.location.pathname) => pathname === "/";
2008
- var isEligibleForApplePayExpressExperiment = () => isApplePayExpressHomepage() && isAppleDeviceOrSafari();
2008
+ var isEligibleForApplePayExpressBanner = () => isApplePayExpressHomepage() && isAppleDeviceOrSafari();
2009
+ var isEligibleForApplePayExpressPaywallExperiment = () => isAppleDeviceOrSafari();
2009
2010
 
2010
2011
  // src/apple-pay-button.ts
2012
+ var ENTRY_POINT_CONFIG = {
2013
+ ["dual_offer_banner" /* DUAL_OFFER_BANNER */]: {
2014
+ isEligible: isEligibleForApplePayExpressBanner
2015
+ // no experimentFlagKey → always-on after eligibility / exclusions
2016
+ },
2017
+ ["article_paywall" /* PAYWALL */]: {
2018
+ isEligible: isEligibleForApplePayExpressPaywallExperiment,
2019
+ experimentFlagKey: APPLE_PAY_EXPRESS_PAYWALLS_EXPERIMENT_KEY
2020
+ },
2021
+ ["article_regwall" /* REGWALL */]: {
2022
+ isEligible: () => true,
2023
+ enabled: false
2024
+ }
2025
+ };
2011
2026
  var FEATURE_APPLE_PAY_EXPRESS_CHECKOUT = "FEATURE_APPLE_PAY_EXPRESS_CHECKOUT";
2012
2027
  var TOTAL_LABEL = "The Economist";
2013
2028
  var DEFAULT_CURRENCY = "USD";
@@ -2024,25 +2039,24 @@ var ApplePayButton = class _ApplePayButton extends HTMLElement {
2024
2039
  define(_ApplePayButton.tag, _ApplePayButton);
2025
2040
  }
2026
2041
  /**
2027
- * Creates and configures an ApplePayButton when the Amplitude experiment is enabled.
2028
- *
2029
- * Reads the experiment variant from `window.experimentIntegration.getVariant` using
2030
- * {@link APPLE_PAY_EXPRESS_EXPERIMENT_KEY}. The host page is responsible
2031
- * for setting up `window.experimentIntegration` before calling this method.
2042
+ * Creates and configures an ApplePayButton when the entry point allows it.
2032
2043
  *
2033
- * - `treatment` → button created and returned
2034
- * - `control` / missing / anything else → returns null (button suppressed)
2044
+ * - `DUAL_OFFER_BANNER` (or omitted): always-on after eligibility / exclusions —
2045
+ * no banners experiment read and no banners `$exposure`.
2046
+ * - `PAYWALL`: experiment-gated via {@link APPLE_PAY_EXPRESS_PAYWALLS_EXPERIMENT_KEY}
2047
+ * (`treatment` or QA URL param). The host must set up
2048
+ * `window.experimentIntegration` before calling for this entry point.
2049
+ * - `REGWALL`: inert — always returns null.
2035
2050
  *
2036
- * **Transition period:** the legacy `FEATURE_APPLE_PAY_EXPRESS_CHECKOUT` URL param
2037
- * gate is also evaluated as a fallback until all host apps have integrated
2038
- * `window.experimentIntegration`. Once all consumers are migrated, the URL param
2039
- * fallback will be removed.
2051
+ * **PAYWALL only — transition period:** the legacy
2052
+ * `FEATURE_APPLE_PAY_EXPRESS_CHECKOUT` URL param can show the button without
2053
+ * treatment (and without inflating `$exposure`). For banners, the same URL
2054
+ * param / `BYPASS_APPLE_PAY_CHECKS` only bypass **eligibility**, not an
2055
+ * experiment gate.
2040
2056
  *
2041
- * **SMA-3301 eligibility:** the homepage + Safari/iPhone eligibility gate
2042
- * only applies to the `DUAL_OFFER_BANNER` entry point (or when `entryPoint`
2043
- * is omitted). `PAYWALL`/`REGWALL` entry points render on article pages and
2044
- * keep their pre-SMA-3301 behaviour unaffected. Anonymous is assumed because
2045
- * the dual-offer banner is only mounted for anonymous visitors.
2057
+ * **Eligibility:**
2058
+ * - `DUAL_OFFER_BANNER` (or omitted): homepage + Safari/iPhone
2059
+ * - `PAYWALL`: Safari/Apple device only (no homepage) — article paywalls
2046
2060
  *
2047
2061
  * @param offer - The offer to display in the Apple Pay flow.
2048
2062
  * @param country - The user's country code.
@@ -2058,29 +2072,40 @@ var ApplePayButton = class _ApplePayButton extends HTMLElement {
2058
2072
  const hasUrlParamFlag = new URLSearchParams(window.location.search).has(
2059
2073
  FEATURE_APPLE_PAY_EXPRESS_CHECKOUT
2060
2074
  );
2061
- const isBannerEntryPoint = !entryPoint || entryPoint === "dual_offer_banner" /* DUAL_OFFER_BANNER */;
2062
- const isEligible = !isBannerEntryPoint || isEligibleForApplePayExpressExperiment();
2063
- if (isBannerEntryPoint && !hasUrlParamFlag && !bypassApplePayChecks() && !isEligible) {
2075
+ const resolvedEntryPoint = entryPoint || "dual_offer_banner" /* DUAL_OFFER_BANNER */;
2076
+ const config = ENTRY_POINT_CONFIG[resolvedEntryPoint];
2077
+ if (!config) {
2064
2078
  return null;
2065
2079
  }
2066
- const experimentValue = window.experimentIntegration?.getVariant?.(
2067
- APPLE_PAY_EXPRESS_EXPERIMENT_KEY
2068
- );
2069
- const isExperimentVariant = experimentValue === APPLE_PAY_EXPRESS_VARIANTS.CONTROL || experimentValue === APPLE_PAY_EXPRESS_VARIANTS.TREATMENT;
2070
- if (isExperimentVariant && isEligible && !bypassApplePayChecks() && !hasUrlParamFlag) {
2071
- void isApplePayAvailable().then((available) => {
2072
- if (available) {
2073
- trackApplePayExperimentExposure(
2074
- experimentValue,
2075
- getExperimentKeyFromNextData(APPLE_PAY_EXPRESS_EXPERIMENT_KEY)
2076
- );
2077
- }
2078
- });
2080
+ const { isEligible: eligibilityCheck, experimentFlagKey } = config;
2081
+ const isEligible = eligibilityCheck();
2082
+ if (config.enabled === false) {
2083
+ return null;
2079
2084
  }
2080
- const isTreatment = experimentValue === APPLE_PAY_EXPRESS_VARIANTS.TREATMENT;
2081
- if (!isTreatment && !hasUrlParamFlag) {
2085
+ if (!hasUrlParamFlag && !bypassApplePayChecks() && !isEligible) {
2082
2086
  return null;
2083
2087
  }
2088
+ if (experimentFlagKey) {
2089
+ const experimentValue = window.experimentIntegration?.getVariant?.(
2090
+ experimentFlagKey
2091
+ );
2092
+ const isExperimentVariant = experimentValue === APPLE_PAY_EXPRESS_VARIANTS.CONTROL || experimentValue === APPLE_PAY_EXPRESS_VARIANTS.TREATMENT;
2093
+ if (isExperimentVariant && isEligible && !bypassApplePayChecks() && !hasUrlParamFlag) {
2094
+ void isApplePayAvailable().then((available) => {
2095
+ if (available) {
2096
+ trackApplePayExperimentExposure(
2097
+ experimentValue,
2098
+ getExperimentKeyFromNextData(experimentFlagKey),
2099
+ experimentFlagKey
2100
+ );
2101
+ }
2102
+ });
2103
+ }
2104
+ const isTreatment = experimentValue === APPLE_PAY_EXPRESS_VARIANTS.TREATMENT;
2105
+ if (!isTreatment && !hasUrlParamFlag) {
2106
+ return null;
2107
+ }
2108
+ }
2084
2109
  _ApplePayButton.define();
2085
2110
  const button = document.createElement(_ApplePayButton.tag);
2086
2111
  button.offer = offer;
@@ -2393,6 +2418,8 @@ function defineApplePayElements() {
2393
2418
  }
2394
2419
  export {
2395
2420
  APPLE_PAY_EXCLUDED_VARIANTS,
2421
+ APPLE_PAY_EXPRESS_PAYWALLS_EXPERIMENT_KEY,
2422
+ APPLE_PAY_EXPRESS_VARIANTS,
2396
2423
  ApplePayButton,
2397
2424
  ApplePayEntryPoint,
2398
2425
  ApplePayModal,
@@ -1,16 +1,22 @@
1
1
  /**
2
- * Host-side eligibility for Apple Pay Express Feature Experiment (SMA-3301).
2
+ * Host-side placement eligibility for Apple Pay Express.
3
3
  *
4
- * Gates createIfEnabled before getVariant / $exposure so only the intended
5
- * population (homepage + Safari/iPhone) participates. Anonymous is assumed
6
- * because the dual-offer banner is only mounted for anonymous visitors.
7
- * Exposure is further gated on isApplePayAvailable() (BYPASS_APPLE_PAY_CHECKS
8
- * respected). Amplitude audience targeting uses generic user_properties from
9
- * Engagement.
4
+ * Used by createIfEnabled after offer exclusions. Enforces placement and
5
+ * device/browser eligibility (homepage + Safari/Apple for banners;
6
+ * Safari/Apple only for paywalls). Banner surfaces are always-on after
7
+ * eligibility; paywalls remain experiment-gated separately. Exposure
8
+ * (paywalls) is further gated on isApplePayAvailable()
9
+ * (BYPASS_APPLE_PAY_CHECKS respected).
10
10
  */
11
11
  export declare const isApplePayExpressHomepage: (pathname?: string) => boolean;
12
12
  /**
13
- * True when the visitor may enter the Apple Pay Express experiment path
14
- * (assignment read + $exposure + optional treatment button).
13
+ * Dual-offer / Free Trial homepage banners: homepage + Safari/iPhone.
14
+ * Anonymous is assumed because these banners are only mounted for
15
+ * anonymous visitors.
15
16
  */
16
- export declare const isEligibleForApplePayExpressExperiment: () => boolean;
17
+ export declare const isEligibleForApplePayExpressBanner: () => boolean;
18
+ /**
19
+ * Article paywalls (`ApplePayEntryPoint.PAYWALL`): Safari/Apple device only.
20
+ * Homepage targeting does not apply — paywalls render on article pages.
21
+ */
22
+ export declare const isEligibleForApplePayExpressPaywallExperiment: () => boolean;
@@ -7,8 +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
+ var isEligibleForApplePayExpressPaywallExperiment = () => isAppleDeviceOrSafari();
11
12
  export {
12
13
  isApplePayExpressHomepage,
13
- isEligibleForApplePayExpressExperiment
14
+ isEligibleForApplePayExpressBanner,
15
+ isEligibleForApplePayExpressPaywallExperiment
14
16
  };
@@ -11,6 +11,7 @@
11
11
  *
12
12
  * Event names are aligned to the PRD spec (section 10 — Tracking & Analytics).
13
13
  */
14
+ import { type ApplePayExperimentFlagKey } from '../constants';
14
15
  declare global {
15
16
  interface Window {
16
17
  tedl?: {
@@ -51,11 +52,15 @@ export type ImpressionMetadata = {
51
52
  * Amplitude Experiment exposure for Apple Pay Express (SMA-3301).
52
53
  *
53
54
  * Fired from createIfEnabled only after isApplePayAvailable() succeeds
54
- * (control or treatment). Deduped per pathname + variant so repeated banner
55
- * renders on the same page do not over-count. Includes experiment_key from
56
- * Engagement SSR (__NEXT_DATA__) when present for run attribution.
55
+ * (control or treatment). Deduped per pathname + flag_key + variant so
56
+ * repeated renders on the same page do not over-count. Includes
57
+ * experiment_key from Engagement SSR (__NEXT_DATA__) when present for run
58
+ * attribution.
59
+ *
60
+ * @param flagKey - Amplitude flag_key (required). Callers must pass an
61
+ * explicit key such as {@link APPLE_PAY_EXPRESS_PAYWALLS_EXPERIMENT_KEY}.
57
62
  */
58
- export declare const trackApplePayExperimentExposure: (variant: string, experimentKey?: string) => void;
63
+ export declare const trackApplePayExperimentExposure: (variant: string, experimentKey: string | undefined, flagKey: ApplePayExperimentFlagKey) => void;
59
64
  /** Wallet button displayed — emits all 5 required analytics dimensions. */
60
65
  export declare const trackExpressWalletImpression: (skuId: string, { entryPoint, country }: ImpressionMetadata) => void;
61
66
  /** Wallet button clicked */
@@ -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) => {
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;
@@ -50,14 +49,14 @@ var trackApplePayExperimentExposure = (variant, experimentKey) => {
50
49
  const exposures = window.__applePayExperimentExposures ?? {};
51
50
  window.__applePayExperimentExposures = exposures;
52
51
  const pathname = window.location?.pathname ?? "";
53
- const dedupeKey = `${pathname}:${APPLE_PAY_EXPRESS_EXPERIMENT_KEY}:${variant}`;
52
+ const dedupeKey = `${pathname}:${flagKey}:${variant}`;
54
53
  if (exposures[dedupeKey]) {
55
54
  return;
56
55
  }
57
56
  exposures[dedupeKey] = true;
58
57
  emitTedlEvent({
59
58
  name: "$exposure",
60
- flag_key: APPLE_PAY_EXPRESS_EXPERIMENT_KEY,
59
+ flag_key: flagKey,
61
60
  variant,
62
61
  ...experimentKey ? { experiment_key: experimentKey } : {}
63
62
  });
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.9.5",
4
+ "version": "2.0.0-beta.1",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
7
7
  "types": "dist/index.d.ts",