@economist/web-apple-pay 1.9.1 → 1.9.3

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/dist/index.js CHANGED
@@ -1303,6 +1303,19 @@ var redirectToCheckoutFallback = (params) => {
1303
1303
  return url.pathname + url.search;
1304
1304
  };
1305
1305
 
1306
+ // src/constants.ts
1307
+ var ApplePayEntryPoint = /* @__PURE__ */ ((ApplePayEntryPoint3) => {
1308
+ ApplePayEntryPoint3["PAYWALL"] = "article_paywall";
1309
+ ApplePayEntryPoint3["REGWALL"] = "article_regwall";
1310
+ ApplePayEntryPoint3["DUAL_OFFER_BANNER"] = "dual_offer_banner";
1311
+ return ApplePayEntryPoint3;
1312
+ })(ApplePayEntryPoint || {});
1313
+ var APPLE_PAY_EXPRESS_EXPERIMENT_KEY = "apple-pay-express-checkout-on-banners";
1314
+ var APPLE_PAY_EXPRESS_VARIANTS = {
1315
+ CONTROL: "control",
1316
+ TREATMENT: "treatment"
1317
+ };
1318
+
1306
1319
  // src/utils/expressCheckoutTracking.ts
1307
1320
  var EXPRESS_CHECKOUT_EVENTS = {
1308
1321
  /** Wallet button displayed */
@@ -1340,6 +1353,25 @@ var track = (action, extra) => {
1340
1353
  ...extra
1341
1354
  });
1342
1355
  };
1356
+ var trackApplePayExperimentExposure = (variant) => {
1357
+ const isActiveVariant = variant === APPLE_PAY_EXPRESS_VARIANTS.CONTROL || variant === APPLE_PAY_EXPRESS_VARIANTS.TREATMENT;
1358
+ if (!isActiveVariant) {
1359
+ return;
1360
+ }
1361
+ const exposures = window.__applePayExperimentExposures ?? {};
1362
+ window.__applePayExperimentExposures = exposures;
1363
+ const pathname = window.location?.pathname ?? "";
1364
+ const dedupeKey = `${pathname}:${APPLE_PAY_EXPRESS_EXPERIMENT_KEY}:${variant}`;
1365
+ if (exposures[dedupeKey]) {
1366
+ return;
1367
+ }
1368
+ exposures[dedupeKey] = true;
1369
+ emitTedlEvent({
1370
+ name: "$exposure",
1371
+ flag_key: APPLE_PAY_EXPRESS_EXPERIMENT_KEY,
1372
+ variant
1373
+ });
1374
+ };
1343
1375
  var trackExpressWalletImpression = (skuId, { entryPoint, country }) => {
1344
1376
  track(EXPRESS_CHECKOUT_EVENTS.IMPRESSION, {
1345
1377
  wallet_type: "apple_pay",
@@ -1382,6 +1414,20 @@ var MSG_MISSING_REQUIRED_FIELD = "Missing required field";
1382
1414
  var MSG_EMAIL_WRONG_FORMAT = "That email doesn\u2019t look right. Please enter a valid email address.";
1383
1415
  var MSG_EMAIL_TOO_LONG = "This field must be 80 characters or less.";
1384
1416
  var MSG_UNSUPPORTED_BILLING_COUNTRY = "Billing country does not match offer price zone";
1417
+ var BILLING_COUNTRY_MISMATCH_FALLBACK = "Country not supported for this purchase";
1418
+ var MSG_BILLING_COUNTRY_MISMATCH_REDIRECT = "This country isn\u2019t supported for this purchase. Try updating your billing address.";
1419
+ var buildCountryMismatchMessage = (countryCode) => {
1420
+ if (!countryCode) return BILLING_COUNTRY_MISMATCH_FALLBACK;
1421
+ try {
1422
+ const regionNames = new Intl.DisplayNames(["en"], { type: "region", fallback: "none" });
1423
+ const upper = countryCode.toUpperCase();
1424
+ const countryName = regionNames.of(upper);
1425
+ const isResolved = countryName && countryName !== upper && countryName !== "Unknown Region";
1426
+ return isResolved ? `${countryName} address required` : BILLING_COUNTRY_MISMATCH_FALLBACK;
1427
+ } catch {
1428
+ return BILLING_COUNTRY_MISMATCH_FALLBACK;
1429
+ }
1430
+ };
1385
1431
  var MSG_PAYMENT_GENERIC_ERROR = "Something went wrong. Please check your payment and billing details and try again or contact us.";
1386
1432
  var MSG_CONTACT_VALIDATION_INTERNAL = "Could not validate Apple Pay contact";
1387
1433
 
@@ -1662,7 +1708,19 @@ async function handlePaymentAuthorized(event, session, sessionData, config) {
1662
1708
  });
1663
1709
  } catch (err) {
1664
1710
  if (err instanceof ContactValidationError) {
1665
- failApplePaySession(session, err.errors);
1711
+ const isBillingCountryMismatch = err.errors.some(
1712
+ (e) => e.contactField === "countryCode"
1713
+ );
1714
+ if (isBillingCountryMismatch) {
1715
+ const countryCode = event.payment.billingContact?.countryCode;
1716
+ const userMessage = buildCountryMismatchMessage(countryCode);
1717
+ failApplePaySession(session, [
1718
+ new ApplePayError("billingContactInvalid", "countryCode", userMessage)
1719
+ ]);
1720
+ redirectFallback(skuId, returnUrl, country, { inlineError: MSG_BILLING_COUNTRY_MISMATCH_REDIRECT });
1721
+ } else {
1722
+ failApplePaySession(session, err.errors);
1723
+ }
1666
1724
  return;
1667
1725
  }
1668
1726
  const walletError = parseWalletApiError(err);
@@ -1937,19 +1995,6 @@ var getSupportedBillingCountries = (offerCountry, offerVariant) => {
1937
1995
  return [...zoneCountries];
1938
1996
  };
1939
1997
 
1940
- // src/constants.ts
1941
- var ApplePayEntryPoint = /* @__PURE__ */ ((ApplePayEntryPoint3) => {
1942
- ApplePayEntryPoint3["PAYWALL"] = "article_paywall";
1943
- ApplePayEntryPoint3["REGWALL"] = "article_regwall";
1944
- ApplePayEntryPoint3["DUAL_OFFER_BANNER"] = "dual_offer_banner";
1945
- return ApplePayEntryPoint3;
1946
- })(ApplePayEntryPoint || {});
1947
- var APPLE_PAY_EXPRESS_EXPERIMENT_KEY = "apple-pay-express-checkout-on-banners";
1948
- var APPLE_PAY_EXPRESS_VARIANTS = {
1949
- CONTROL: "control",
1950
- TREATMENT: "treatment"
1951
- };
1952
-
1953
1998
  // src/apple-pay-button.ts
1954
1999
  var FEATURE_APPLE_PAY_EXPRESS_CHECKOUT = "FEATURE_APPLE_PAY_EXPRESS_CHECKOUT";
1955
2000
  var TOTAL_LABEL = "The Economist";
@@ -1995,6 +2040,9 @@ var ApplePayButton = class _ApplePayButton extends HTMLElement {
1995
2040
  const experimentValue = window.experimentIntegration?.getVariant?.(
1996
2041
  APPLE_PAY_EXPRESS_EXPERIMENT_KEY
1997
2042
  );
2043
+ if (experimentValue) {
2044
+ trackApplePayExperimentExposure(experimentValue);
2045
+ }
1998
2046
  const isTreatment = experimentValue === APPLE_PAY_EXPRESS_VARIANTS.TREATMENT;
1999
2047
  const hasUrlParamFlag = new URLSearchParams(window.location.search).has(
2000
2048
  FEATURE_APPLE_PAY_EXPRESS_CHECKOUT
@@ -1298,6 +1298,13 @@ var redirectToCheckoutFallback = (params) => {
1298
1298
  return url.pathname + url.search;
1299
1299
  };
1300
1300
 
1301
+ // src/constants.ts
1302
+ var APPLE_PAY_EXPRESS_EXPERIMENT_KEY = "apple-pay-express-checkout-on-banners";
1303
+ var APPLE_PAY_EXPRESS_VARIANTS = {
1304
+ CONTROL: "control",
1305
+ TREATMENT: "treatment"
1306
+ };
1307
+
1301
1308
  // src/utils/expressCheckoutTracking.ts
1302
1309
  var EXPRESS_CHECKOUT_EVENTS = {
1303
1310
  /** Wallet button displayed */
@@ -1335,6 +1342,25 @@ var track = (action, extra) => {
1335
1342
  ...extra
1336
1343
  });
1337
1344
  };
1345
+ var trackApplePayExperimentExposure = (variant) => {
1346
+ const isActiveVariant = variant === APPLE_PAY_EXPRESS_VARIANTS.CONTROL || variant === APPLE_PAY_EXPRESS_VARIANTS.TREATMENT;
1347
+ if (!isActiveVariant) {
1348
+ return;
1349
+ }
1350
+ const exposures = window.__applePayExperimentExposures ?? {};
1351
+ window.__applePayExperimentExposures = exposures;
1352
+ const pathname = window.location?.pathname ?? "";
1353
+ const dedupeKey = `${pathname}:${APPLE_PAY_EXPRESS_EXPERIMENT_KEY}:${variant}`;
1354
+ if (exposures[dedupeKey]) {
1355
+ return;
1356
+ }
1357
+ exposures[dedupeKey] = true;
1358
+ emitTedlEvent({
1359
+ name: "$exposure",
1360
+ flag_key: APPLE_PAY_EXPRESS_EXPERIMENT_KEY,
1361
+ variant
1362
+ });
1363
+ };
1338
1364
  var trackExpressWalletImpression = (skuId, { entryPoint, country }) => {
1339
1365
  track(EXPRESS_CHECKOUT_EVENTS.IMPRESSION, {
1340
1366
  wallet_type: "apple_pay",
@@ -1377,6 +1403,20 @@ var MSG_MISSING_REQUIRED_FIELD = "Missing required field";
1377
1403
  var MSG_EMAIL_WRONG_FORMAT = "That email doesn\u2019t look right. Please enter a valid email address.";
1378
1404
  var MSG_EMAIL_TOO_LONG = "This field must be 80 characters or less.";
1379
1405
  var MSG_UNSUPPORTED_BILLING_COUNTRY = "Billing country does not match offer price zone";
1406
+ var BILLING_COUNTRY_MISMATCH_FALLBACK = "Country not supported for this purchase";
1407
+ var MSG_BILLING_COUNTRY_MISMATCH_REDIRECT = "This country isn\u2019t supported for this purchase. Try updating your billing address.";
1408
+ var buildCountryMismatchMessage = (countryCode) => {
1409
+ if (!countryCode) return BILLING_COUNTRY_MISMATCH_FALLBACK;
1410
+ try {
1411
+ const regionNames = new Intl.DisplayNames(["en"], { type: "region", fallback: "none" });
1412
+ const upper = countryCode.toUpperCase();
1413
+ const countryName = regionNames.of(upper);
1414
+ const isResolved = countryName && countryName !== upper && countryName !== "Unknown Region";
1415
+ return isResolved ? `${countryName} address required` : BILLING_COUNTRY_MISMATCH_FALLBACK;
1416
+ } catch {
1417
+ return BILLING_COUNTRY_MISMATCH_FALLBACK;
1418
+ }
1419
+ };
1380
1420
  var MSG_PAYMENT_GENERIC_ERROR = "Something went wrong. Please check your payment and billing details and try again or contact us.";
1381
1421
  var MSG_CONTACT_VALIDATION_INTERNAL = "Could not validate Apple Pay contact";
1382
1422
 
@@ -1657,7 +1697,19 @@ async function handlePaymentAuthorized(event, session, sessionData, config) {
1657
1697
  });
1658
1698
  } catch (err) {
1659
1699
  if (err instanceof ContactValidationError) {
1660
- failApplePaySession(session, err.errors);
1700
+ const isBillingCountryMismatch = err.errors.some(
1701
+ (e) => e.contactField === "countryCode"
1702
+ );
1703
+ if (isBillingCountryMismatch) {
1704
+ const countryCode = event.payment.billingContact?.countryCode;
1705
+ const userMessage = buildCountryMismatchMessage(countryCode);
1706
+ failApplePaySession(session, [
1707
+ new ApplePayError("billingContactInvalid", "countryCode", userMessage)
1708
+ ]);
1709
+ redirectFallback(skuId, returnUrl, country, { inlineError: MSG_BILLING_COUNTRY_MISMATCH_REDIRECT });
1710
+ } else {
1711
+ failApplePaySession(session, err.errors);
1712
+ }
1661
1713
  return;
1662
1714
  }
1663
1715
  const walletError = parseWalletApiError(err);
@@ -1932,13 +1984,6 @@ var getSupportedBillingCountries = (offerCountry, offerVariant) => {
1932
1984
  return [...zoneCountries];
1933
1985
  };
1934
1986
 
1935
- // src/constants.ts
1936
- var APPLE_PAY_EXPRESS_EXPERIMENT_KEY = "apple-pay-express-checkout-on-banners";
1937
- var APPLE_PAY_EXPRESS_VARIANTS = {
1938
- CONTROL: "control",
1939
- TREATMENT: "treatment"
1940
- };
1941
-
1942
1987
  // src/apple-pay-button.ts
1943
1988
  var FEATURE_APPLE_PAY_EXPRESS_CHECKOUT = "FEATURE_APPLE_PAY_EXPRESS_CHECKOUT";
1944
1989
  var TOTAL_LABEL = "The Economist";
@@ -1984,6 +2029,9 @@ var ApplePayButton = class _ApplePayButton extends HTMLElement {
1984
2029
  const experimentValue = window.experimentIntegration?.getVariant?.(
1985
2030
  APPLE_PAY_EXPRESS_EXPERIMENT_KEY
1986
2031
  );
2032
+ if (experimentValue) {
2033
+ trackApplePayExperimentExposure(experimentValue);
2034
+ }
1987
2035
  const isTreatment = experimentValue === APPLE_PAY_EXPRESS_VARIANTS.TREATMENT;
1988
2036
  const hasUrlParamFlag = new URLSearchParams(window.location.search).has(
1989
2037
  FEATURE_APPLE_PAY_EXPRESS_CHECKOUT
@@ -1306,6 +1306,13 @@ var redirectToCheckoutFallback = (params) => {
1306
1306
  return url.pathname + url.search;
1307
1307
  };
1308
1308
 
1309
+ // src/constants.ts
1310
+ var APPLE_PAY_EXPRESS_EXPERIMENT_KEY = "apple-pay-express-checkout-on-banners";
1311
+ var APPLE_PAY_EXPRESS_VARIANTS = {
1312
+ CONTROL: "control",
1313
+ TREATMENT: "treatment"
1314
+ };
1315
+
1309
1316
  // src/utils/expressCheckoutTracking.ts
1310
1317
  var EXPRESS_CHECKOUT_EVENTS = {
1311
1318
  /** Wallet button displayed */
@@ -1343,6 +1350,25 @@ var track = (action, extra) => {
1343
1350
  ...extra
1344
1351
  });
1345
1352
  };
1353
+ var trackApplePayExperimentExposure = (variant) => {
1354
+ const isActiveVariant = variant === APPLE_PAY_EXPRESS_VARIANTS.CONTROL || variant === APPLE_PAY_EXPRESS_VARIANTS.TREATMENT;
1355
+ if (!isActiveVariant) {
1356
+ return;
1357
+ }
1358
+ const exposures = window.__applePayExperimentExposures ?? {};
1359
+ window.__applePayExperimentExposures = exposures;
1360
+ const pathname = window.location?.pathname ?? "";
1361
+ const dedupeKey = `${pathname}:${APPLE_PAY_EXPRESS_EXPERIMENT_KEY}:${variant}`;
1362
+ if (exposures[dedupeKey]) {
1363
+ return;
1364
+ }
1365
+ exposures[dedupeKey] = true;
1366
+ emitTedlEvent({
1367
+ name: "$exposure",
1368
+ flag_key: APPLE_PAY_EXPRESS_EXPERIMENT_KEY,
1369
+ variant
1370
+ });
1371
+ };
1346
1372
  var trackExpressWalletImpression = (skuId, { entryPoint, country }) => {
1347
1373
  track(EXPRESS_CHECKOUT_EVENTS.IMPRESSION, {
1348
1374
  wallet_type: "apple_pay",
@@ -1385,6 +1411,20 @@ var MSG_MISSING_REQUIRED_FIELD = "Missing required field";
1385
1411
  var MSG_EMAIL_WRONG_FORMAT = "That email doesn\u2019t look right. Please enter a valid email address.";
1386
1412
  var MSG_EMAIL_TOO_LONG = "This field must be 80 characters or less.";
1387
1413
  var MSG_UNSUPPORTED_BILLING_COUNTRY = "Billing country does not match offer price zone";
1414
+ var BILLING_COUNTRY_MISMATCH_FALLBACK = "Country not supported for this purchase";
1415
+ var MSG_BILLING_COUNTRY_MISMATCH_REDIRECT = "This country isn\u2019t supported for this purchase. Try updating your billing address.";
1416
+ var buildCountryMismatchMessage = (countryCode) => {
1417
+ if (!countryCode) return BILLING_COUNTRY_MISMATCH_FALLBACK;
1418
+ try {
1419
+ const regionNames = new Intl.DisplayNames(["en"], { type: "region", fallback: "none" });
1420
+ const upper = countryCode.toUpperCase();
1421
+ const countryName = regionNames.of(upper);
1422
+ const isResolved = countryName && countryName !== upper && countryName !== "Unknown Region";
1423
+ return isResolved ? `${countryName} address required` : BILLING_COUNTRY_MISMATCH_FALLBACK;
1424
+ } catch {
1425
+ return BILLING_COUNTRY_MISMATCH_FALLBACK;
1426
+ }
1427
+ };
1388
1428
  var MSG_PAYMENT_GENERIC_ERROR = "Something went wrong. Please check your payment and billing details and try again or contact us.";
1389
1429
  var MSG_CONTACT_VALIDATION_INTERNAL = "Could not validate Apple Pay contact";
1390
1430
 
@@ -1665,7 +1705,19 @@ async function handlePaymentAuthorized(event, session, sessionData, config) {
1665
1705
  });
1666
1706
  } catch (err) {
1667
1707
  if (err instanceof ContactValidationError) {
1668
- failApplePaySession(session, err.errors);
1708
+ const isBillingCountryMismatch = err.errors.some(
1709
+ (e) => e.contactField === "countryCode"
1710
+ );
1711
+ if (isBillingCountryMismatch) {
1712
+ const countryCode = event.payment.billingContact?.countryCode;
1713
+ const userMessage = buildCountryMismatchMessage(countryCode);
1714
+ failApplePaySession(session, [
1715
+ new ApplePayError("billingContactInvalid", "countryCode", userMessage)
1716
+ ]);
1717
+ redirectFallback(skuId, returnUrl, country, { inlineError: MSG_BILLING_COUNTRY_MISMATCH_REDIRECT });
1718
+ } else {
1719
+ failApplePaySession(session, err.errors);
1720
+ }
1669
1721
  return;
1670
1722
  }
1671
1723
  const walletError = parseWalletApiError(err);
@@ -1940,13 +1992,6 @@ var getSupportedBillingCountries = (offerCountry, offerVariant) => {
1940
1992
  return [...zoneCountries];
1941
1993
  };
1942
1994
 
1943
- // src/constants.ts
1944
- var APPLE_PAY_EXPRESS_EXPERIMENT_KEY = "apple-pay-express-checkout-on-banners";
1945
- var APPLE_PAY_EXPRESS_VARIANTS = {
1946
- CONTROL: "control",
1947
- TREATMENT: "treatment"
1948
- };
1949
-
1950
1995
  // src/apple-pay-button.ts
1951
1996
  var FEATURE_APPLE_PAY_EXPRESS_CHECKOUT = "FEATURE_APPLE_PAY_EXPRESS_CHECKOUT";
1952
1997
  var TOTAL_LABEL = "The Economist";
@@ -1992,6 +2037,9 @@ var ApplePayButton = class _ApplePayButton extends HTMLElement {
1992
2037
  const experimentValue = window.experimentIntegration?.getVariant?.(
1993
2038
  APPLE_PAY_EXPRESS_EXPERIMENT_KEY
1994
2039
  );
2040
+ if (experimentValue) {
2041
+ trackApplePayExperimentExposure(experimentValue);
2042
+ }
1995
2043
  const isTreatment = experimentValue === APPLE_PAY_EXPRESS_VARIANTS.TREATMENT;
1996
2044
  const hasUrlParamFlag = new URLSearchParams(window.location.search).has(
1997
2045
  FEATURE_APPLE_PAY_EXPRESS_CHECKOUT
@@ -1298,6 +1298,13 @@ var redirectToCheckoutFallback = (params) => {
1298
1298
  return url.pathname + url.search;
1299
1299
  };
1300
1300
 
1301
+ // src/constants.ts
1302
+ var APPLE_PAY_EXPRESS_EXPERIMENT_KEY = "apple-pay-express-checkout-on-banners";
1303
+ var APPLE_PAY_EXPRESS_VARIANTS = {
1304
+ CONTROL: "control",
1305
+ TREATMENT: "treatment"
1306
+ };
1307
+
1301
1308
  // src/utils/expressCheckoutTracking.ts
1302
1309
  var EXPRESS_CHECKOUT_EVENTS = {
1303
1310
  /** Wallet button displayed */
@@ -1335,6 +1342,25 @@ var track = (action, extra) => {
1335
1342
  ...extra
1336
1343
  });
1337
1344
  };
1345
+ var trackApplePayExperimentExposure = (variant) => {
1346
+ const isActiveVariant = variant === APPLE_PAY_EXPRESS_VARIANTS.CONTROL || variant === APPLE_PAY_EXPRESS_VARIANTS.TREATMENT;
1347
+ if (!isActiveVariant) {
1348
+ return;
1349
+ }
1350
+ const exposures = window.__applePayExperimentExposures ?? {};
1351
+ window.__applePayExperimentExposures = exposures;
1352
+ const pathname = window.location?.pathname ?? "";
1353
+ const dedupeKey = `${pathname}:${APPLE_PAY_EXPRESS_EXPERIMENT_KEY}:${variant}`;
1354
+ if (exposures[dedupeKey]) {
1355
+ return;
1356
+ }
1357
+ exposures[dedupeKey] = true;
1358
+ emitTedlEvent({
1359
+ name: "$exposure",
1360
+ flag_key: APPLE_PAY_EXPRESS_EXPERIMENT_KEY,
1361
+ variant
1362
+ });
1363
+ };
1338
1364
  var trackExpressWalletImpression = (skuId, { entryPoint, country }) => {
1339
1365
  track(EXPRESS_CHECKOUT_EVENTS.IMPRESSION, {
1340
1366
  wallet_type: "apple_pay",
@@ -1377,6 +1403,20 @@ var MSG_MISSING_REQUIRED_FIELD = "Missing required field";
1377
1403
  var MSG_EMAIL_WRONG_FORMAT = "That email doesn\u2019t look right. Please enter a valid email address.";
1378
1404
  var MSG_EMAIL_TOO_LONG = "This field must be 80 characters or less.";
1379
1405
  var MSG_UNSUPPORTED_BILLING_COUNTRY = "Billing country does not match offer price zone";
1406
+ var BILLING_COUNTRY_MISMATCH_FALLBACK = "Country not supported for this purchase";
1407
+ var MSG_BILLING_COUNTRY_MISMATCH_REDIRECT = "This country isn\u2019t supported for this purchase. Try updating your billing address.";
1408
+ var buildCountryMismatchMessage = (countryCode) => {
1409
+ if (!countryCode) return BILLING_COUNTRY_MISMATCH_FALLBACK;
1410
+ try {
1411
+ const regionNames = new Intl.DisplayNames(["en"], { type: "region", fallback: "none" });
1412
+ const upper = countryCode.toUpperCase();
1413
+ const countryName = regionNames.of(upper);
1414
+ const isResolved = countryName && countryName !== upper && countryName !== "Unknown Region";
1415
+ return isResolved ? `${countryName} address required` : BILLING_COUNTRY_MISMATCH_FALLBACK;
1416
+ } catch {
1417
+ return BILLING_COUNTRY_MISMATCH_FALLBACK;
1418
+ }
1419
+ };
1380
1420
  var MSG_PAYMENT_GENERIC_ERROR = "Something went wrong. Please check your payment and billing details and try again or contact us.";
1381
1421
  var MSG_CONTACT_VALIDATION_INTERNAL = "Could not validate Apple Pay contact";
1382
1422
 
@@ -1657,7 +1697,19 @@ async function handlePaymentAuthorized(event, session, sessionData, config) {
1657
1697
  });
1658
1698
  } catch (err) {
1659
1699
  if (err instanceof ContactValidationError) {
1660
- failApplePaySession(session, err.errors);
1700
+ const isBillingCountryMismatch = err.errors.some(
1701
+ (e) => e.contactField === "countryCode"
1702
+ );
1703
+ if (isBillingCountryMismatch) {
1704
+ const countryCode = event.payment.billingContact?.countryCode;
1705
+ const userMessage = buildCountryMismatchMessage(countryCode);
1706
+ failApplePaySession(session, [
1707
+ new ApplePayError("billingContactInvalid", "countryCode", userMessage)
1708
+ ]);
1709
+ redirectFallback(skuId, returnUrl, country, { inlineError: MSG_BILLING_COUNTRY_MISMATCH_REDIRECT });
1710
+ } else {
1711
+ failApplePaySession(session, err.errors);
1712
+ }
1661
1713
  return;
1662
1714
  }
1663
1715
  const walletError = parseWalletApiError(err);
@@ -1932,13 +1984,6 @@ var getSupportedBillingCountries = (offerCountry, offerVariant) => {
1932
1984
  return [...zoneCountries];
1933
1985
  };
1934
1986
 
1935
- // src/constants.ts
1936
- var APPLE_PAY_EXPRESS_EXPERIMENT_KEY = "apple-pay-express-checkout-on-banners";
1937
- var APPLE_PAY_EXPRESS_VARIANTS = {
1938
- CONTROL: "control",
1939
- TREATMENT: "treatment"
1940
- };
1941
-
1942
1987
  // src/apple-pay-button.ts
1943
1988
  var FEATURE_APPLE_PAY_EXPRESS_CHECKOUT = "FEATURE_APPLE_PAY_EXPRESS_CHECKOUT";
1944
1989
  var TOTAL_LABEL = "The Economist";
@@ -1984,6 +2029,9 @@ var ApplePayButton = class _ApplePayButton extends HTMLElement {
1984
2029
  const experimentValue = window.experimentIntegration?.getVariant?.(
1985
2030
  APPLE_PAY_EXPRESS_EXPERIMENT_KEY
1986
2031
  );
2032
+ if (experimentValue) {
2033
+ trackApplePayExperimentExposure(experimentValue);
2034
+ }
1987
2035
  const isTreatment = experimentValue === APPLE_PAY_EXPRESS_VARIANTS.TREATMENT;
1988
2036
  const hasUrlParamFlag = new URLSearchParams(window.location.search).has(
1989
2037
  FEATURE_APPLE_PAY_EXPRESS_CHECKOUT
@@ -22,6 +22,25 @@ export declare const MSG_EMAIL_WRONG_FORMAT = "That email doesn\u2019t look righ
22
22
  export declare const MSG_EMAIL_TOO_LONG = "This field must be 80 characters or less.";
23
23
  /** Shown when the billing country is not in the supported list for the offer. */
24
24
  export declare const MSG_UNSUPPORTED_BILLING_COUNTRY = "Billing country does not match offer price zone";
25
+ /**
26
+ * Fallback user-facing message for billing country mismatch when the country
27
+ * name cannot be resolved from the country code.
28
+ */
29
+ export declare const BILLING_COUNTRY_MISMATCH_FALLBACK = "Country not supported for this purchase";
30
+ /**
31
+ * User-facing message shown as an inlineError on the checkout redirect page
32
+ * when the billing country does not match the offer price zone.
33
+ */
34
+ export declare const MSG_BILLING_COUNTRY_MISMATCH_REDIRECT = "This country isn\u2019t supported for this purchase. Try updating your billing address.";
35
+ /**
36
+ * Builds a short, user-facing message for a billing country mismatch, suitable
37
+ * for Apple's native payment sheet (~50 char limit).
38
+ *
39
+ * Returns `"[Country] address required"` (e.g. "Taiwan address required") when
40
+ * the country code resolves to a name, otherwise falls back to
41
+ * BILLING_COUNTRY_MISMATCH_FALLBACK.
42
+ */
43
+ export declare const buildCountryMismatchMessage: (countryCode: string | undefined) => string;
25
44
  /**
26
45
  * Generic fallback error shown to the user when merchant validation or the
27
46
  * purchase call fails. Redirected as an inlineError on the checkout fallback page.
@@ -3,13 +3,30 @@ var MSG_MISSING_REQUIRED_FIELD = "Missing required field";
3
3
  var MSG_EMAIL_WRONG_FORMAT = "That email doesn\u2019t look right. Please enter a valid email address.";
4
4
  var MSG_EMAIL_TOO_LONG = "This field must be 80 characters or less.";
5
5
  var MSG_UNSUPPORTED_BILLING_COUNTRY = "Billing country does not match offer price zone";
6
+ var BILLING_COUNTRY_MISMATCH_FALLBACK = "Country not supported for this purchase";
7
+ var MSG_BILLING_COUNTRY_MISMATCH_REDIRECT = "This country isn\u2019t supported for this purchase. Try updating your billing address.";
8
+ var buildCountryMismatchMessage = (countryCode) => {
9
+ if (!countryCode) return BILLING_COUNTRY_MISMATCH_FALLBACK;
10
+ try {
11
+ const regionNames = new Intl.DisplayNames(["en"], { type: "region", fallback: "none" });
12
+ const upper = countryCode.toUpperCase();
13
+ const countryName = regionNames.of(upper);
14
+ const isResolved = countryName && countryName !== upper && countryName !== "Unknown Region";
15
+ return isResolved ? `${countryName} address required` : BILLING_COUNTRY_MISMATCH_FALLBACK;
16
+ } catch {
17
+ return BILLING_COUNTRY_MISMATCH_FALLBACK;
18
+ }
19
+ };
6
20
  var MSG_PAYMENT_GENERIC_ERROR = "Something went wrong. Please check your payment and billing details and try again or contact us.";
7
21
  var MSG_CONTACT_VALIDATION_INTERNAL = "Could not validate Apple Pay contact";
8
22
  export {
23
+ BILLING_COUNTRY_MISMATCH_FALLBACK,
24
+ MSG_BILLING_COUNTRY_MISMATCH_REDIRECT,
9
25
  MSG_CONTACT_VALIDATION_INTERNAL,
10
26
  MSG_EMAIL_TOO_LONG,
11
27
  MSG_EMAIL_WRONG_FORMAT,
12
28
  MSG_MISSING_REQUIRED_FIELD,
13
29
  MSG_PAYMENT_GENERIC_ERROR,
14
- MSG_UNSUPPORTED_BILLING_COUNTRY
30
+ MSG_UNSUPPORTED_BILLING_COUNTRY,
31
+ buildCountryMismatchMessage
15
32
  };
@@ -117,6 +117,20 @@ var MSG_MISSING_REQUIRED_FIELD = "Missing required field";
117
117
  var MSG_EMAIL_WRONG_FORMAT = "That email doesn\u2019t look right. Please enter a valid email address.";
118
118
  var MSG_EMAIL_TOO_LONG = "This field must be 80 characters or less.";
119
119
  var MSG_UNSUPPORTED_BILLING_COUNTRY = "Billing country does not match offer price zone";
120
+ var BILLING_COUNTRY_MISMATCH_FALLBACK = "Country not supported for this purchase";
121
+ var MSG_BILLING_COUNTRY_MISMATCH_REDIRECT = "This country isn\u2019t supported for this purchase. Try updating your billing address.";
122
+ var buildCountryMismatchMessage = (countryCode) => {
123
+ if (!countryCode) return BILLING_COUNTRY_MISMATCH_FALLBACK;
124
+ try {
125
+ const regionNames = new Intl.DisplayNames(["en"], { type: "region", fallback: "none" });
126
+ const upper = countryCode.toUpperCase();
127
+ const countryName = regionNames.of(upper);
128
+ const isResolved = countryName && countryName !== upper && countryName !== "Unknown Region";
129
+ return isResolved ? `${countryName} address required` : BILLING_COUNTRY_MISMATCH_FALLBACK;
130
+ } catch {
131
+ return BILLING_COUNTRY_MISMATCH_FALLBACK;
132
+ }
133
+ };
120
134
  var MSG_PAYMENT_GENERIC_ERROR = "Something went wrong. Please check your payment and billing details and try again or contact us.";
121
135
  var MSG_CONTACT_VALIDATION_INTERNAL = "Could not validate Apple Pay contact";
122
136
 
@@ -375,7 +389,19 @@ async function handlePaymentAuthorized(event, session, sessionData, config) {
375
389
  });
376
390
  } catch (err) {
377
391
  if (err instanceof ContactValidationError) {
378
- failApplePaySession(session, err.errors);
392
+ const isBillingCountryMismatch = err.errors.some(
393
+ (e) => e.contactField === "countryCode"
394
+ );
395
+ if (isBillingCountryMismatch) {
396
+ const countryCode = event.payment.billingContact?.countryCode;
397
+ const userMessage = buildCountryMismatchMessage(countryCode);
398
+ failApplePaySession(session, [
399
+ new ApplePayError("billingContactInvalid", "countryCode", userMessage)
400
+ ]);
401
+ redirectFallback(skuId, returnUrl, country, { inlineError: MSG_BILLING_COUNTRY_MISMATCH_REDIRECT });
402
+ } else {
403
+ failApplePaySession(session, err.errors);
404
+ }
379
405
  return;
380
406
  }
381
407
  const walletError = parseWalletApiError(err);
package/dist/src/index.js CHANGED
@@ -1303,6 +1303,19 @@ var redirectToCheckoutFallback = (params) => {
1303
1303
  return url.pathname + url.search;
1304
1304
  };
1305
1305
 
1306
+ // src/constants.ts
1307
+ var ApplePayEntryPoint = /* @__PURE__ */ ((ApplePayEntryPoint3) => {
1308
+ ApplePayEntryPoint3["PAYWALL"] = "article_paywall";
1309
+ ApplePayEntryPoint3["REGWALL"] = "article_regwall";
1310
+ ApplePayEntryPoint3["DUAL_OFFER_BANNER"] = "dual_offer_banner";
1311
+ return ApplePayEntryPoint3;
1312
+ })(ApplePayEntryPoint || {});
1313
+ var APPLE_PAY_EXPRESS_EXPERIMENT_KEY = "apple-pay-express-checkout-on-banners";
1314
+ var APPLE_PAY_EXPRESS_VARIANTS = {
1315
+ CONTROL: "control",
1316
+ TREATMENT: "treatment"
1317
+ };
1318
+
1306
1319
  // src/utils/expressCheckoutTracking.ts
1307
1320
  var EXPRESS_CHECKOUT_EVENTS = {
1308
1321
  /** Wallet button displayed */
@@ -1340,6 +1353,25 @@ var track = (action, extra) => {
1340
1353
  ...extra
1341
1354
  });
1342
1355
  };
1356
+ var trackApplePayExperimentExposure = (variant) => {
1357
+ const isActiveVariant = variant === APPLE_PAY_EXPRESS_VARIANTS.CONTROL || variant === APPLE_PAY_EXPRESS_VARIANTS.TREATMENT;
1358
+ if (!isActiveVariant) {
1359
+ return;
1360
+ }
1361
+ const exposures = window.__applePayExperimentExposures ?? {};
1362
+ window.__applePayExperimentExposures = exposures;
1363
+ const pathname = window.location?.pathname ?? "";
1364
+ const dedupeKey = `${pathname}:${APPLE_PAY_EXPRESS_EXPERIMENT_KEY}:${variant}`;
1365
+ if (exposures[dedupeKey]) {
1366
+ return;
1367
+ }
1368
+ exposures[dedupeKey] = true;
1369
+ emitTedlEvent({
1370
+ name: "$exposure",
1371
+ flag_key: APPLE_PAY_EXPRESS_EXPERIMENT_KEY,
1372
+ variant
1373
+ });
1374
+ };
1343
1375
  var trackExpressWalletImpression = (skuId, { entryPoint, country }) => {
1344
1376
  track(EXPRESS_CHECKOUT_EVENTS.IMPRESSION, {
1345
1377
  wallet_type: "apple_pay",
@@ -1382,6 +1414,20 @@ var MSG_MISSING_REQUIRED_FIELD = "Missing required field";
1382
1414
  var MSG_EMAIL_WRONG_FORMAT = "That email doesn\u2019t look right. Please enter a valid email address.";
1383
1415
  var MSG_EMAIL_TOO_LONG = "This field must be 80 characters or less.";
1384
1416
  var MSG_UNSUPPORTED_BILLING_COUNTRY = "Billing country does not match offer price zone";
1417
+ var BILLING_COUNTRY_MISMATCH_FALLBACK = "Country not supported for this purchase";
1418
+ var MSG_BILLING_COUNTRY_MISMATCH_REDIRECT = "This country isn\u2019t supported for this purchase. Try updating your billing address.";
1419
+ var buildCountryMismatchMessage = (countryCode) => {
1420
+ if (!countryCode) return BILLING_COUNTRY_MISMATCH_FALLBACK;
1421
+ try {
1422
+ const regionNames = new Intl.DisplayNames(["en"], { type: "region", fallback: "none" });
1423
+ const upper = countryCode.toUpperCase();
1424
+ const countryName = regionNames.of(upper);
1425
+ const isResolved = countryName && countryName !== upper && countryName !== "Unknown Region";
1426
+ return isResolved ? `${countryName} address required` : BILLING_COUNTRY_MISMATCH_FALLBACK;
1427
+ } catch {
1428
+ return BILLING_COUNTRY_MISMATCH_FALLBACK;
1429
+ }
1430
+ };
1385
1431
  var MSG_PAYMENT_GENERIC_ERROR = "Something went wrong. Please check your payment and billing details and try again or contact us.";
1386
1432
  var MSG_CONTACT_VALIDATION_INTERNAL = "Could not validate Apple Pay contact";
1387
1433
 
@@ -1662,7 +1708,19 @@ async function handlePaymentAuthorized(event, session, sessionData, config) {
1662
1708
  });
1663
1709
  } catch (err) {
1664
1710
  if (err instanceof ContactValidationError) {
1665
- failApplePaySession(session, err.errors);
1711
+ const isBillingCountryMismatch = err.errors.some(
1712
+ (e) => e.contactField === "countryCode"
1713
+ );
1714
+ if (isBillingCountryMismatch) {
1715
+ const countryCode = event.payment.billingContact?.countryCode;
1716
+ const userMessage = buildCountryMismatchMessage(countryCode);
1717
+ failApplePaySession(session, [
1718
+ new ApplePayError("billingContactInvalid", "countryCode", userMessage)
1719
+ ]);
1720
+ redirectFallback(skuId, returnUrl, country, { inlineError: MSG_BILLING_COUNTRY_MISMATCH_REDIRECT });
1721
+ } else {
1722
+ failApplePaySession(session, err.errors);
1723
+ }
1666
1724
  return;
1667
1725
  }
1668
1726
  const walletError = parseWalletApiError(err);
@@ -1937,19 +1995,6 @@ var getSupportedBillingCountries = (offerCountry, offerVariant) => {
1937
1995
  return [...zoneCountries];
1938
1996
  };
1939
1997
 
1940
- // src/constants.ts
1941
- var ApplePayEntryPoint = /* @__PURE__ */ ((ApplePayEntryPoint3) => {
1942
- ApplePayEntryPoint3["PAYWALL"] = "article_paywall";
1943
- ApplePayEntryPoint3["REGWALL"] = "article_regwall";
1944
- ApplePayEntryPoint3["DUAL_OFFER_BANNER"] = "dual_offer_banner";
1945
- return ApplePayEntryPoint3;
1946
- })(ApplePayEntryPoint || {});
1947
- var APPLE_PAY_EXPRESS_EXPERIMENT_KEY = "apple-pay-express-checkout-on-banners";
1948
- var APPLE_PAY_EXPRESS_VARIANTS = {
1949
- CONTROL: "control",
1950
- TREATMENT: "treatment"
1951
- };
1952
-
1953
1998
  // src/apple-pay-button.ts
1954
1999
  var FEATURE_APPLE_PAY_EXPRESS_CHECKOUT = "FEATURE_APPLE_PAY_EXPRESS_CHECKOUT";
1955
2000
  var TOTAL_LABEL = "The Economist";
@@ -1995,6 +2040,9 @@ var ApplePayButton = class _ApplePayButton extends HTMLElement {
1995
2040
  const experimentValue = window.experimentIntegration?.getVariant?.(
1996
2041
  APPLE_PAY_EXPRESS_EXPERIMENT_KEY
1997
2042
  );
2043
+ if (experimentValue) {
2044
+ trackApplePayExperimentExposure(experimentValue);
2045
+ }
1998
2046
  const isTreatment = experimentValue === APPLE_PAY_EXPRESS_VARIANTS.TREATMENT;
1999
2047
  const hasUrlParamFlag = new URLSearchParams(window.location.search).has(
2000
2048
  FEATURE_APPLE_PAY_EXPRESS_CHECKOUT
@@ -17,6 +17,7 @@ declare global {
17
17
  events?: Record<string, unknown>[];
18
18
  [key: string]: unknown;
19
19
  };
20
+ __applePayExperimentExposures?: Record<string, boolean>;
20
21
  }
21
22
  }
22
23
  export declare const EXPRESS_CHECKOUT_EVENTS: {
@@ -46,6 +47,14 @@ export type ImpressionMetadata = {
46
47
  /** ISO 3166-1 alpha-2 country code of the offer (e.g. 'GB', 'US'). */
47
48
  country: string;
48
49
  };
50
+ /**
51
+ * Amplitude Experiment exposure for Apple Pay Express (SMA-3301).
52
+ *
53
+ * Fired when createIfEnabled evaluates an assigned variant (control or
54
+ * treatment). Deduped per pathname + variant so repeated banner renders on
55
+ * the same page do not over-count.
56
+ */
57
+ export declare const trackApplePayExperimentExposure: (variant: string) => void;
49
58
  /** Wallet button displayed — emits all 5 required analytics dimensions. */
50
59
  export declare const trackExpressWalletImpression: (skuId: string, { entryPoint, country }: ImpressionMetadata) => void;
51
60
  /** Wallet button clicked */
@@ -1,3 +1,10 @@
1
+ // src/constants.ts
2
+ var APPLE_PAY_EXPRESS_EXPERIMENT_KEY = "apple-pay-express-checkout-on-banners";
3
+ var APPLE_PAY_EXPRESS_VARIANTS = {
4
+ CONTROL: "control",
5
+ TREATMENT: "treatment"
6
+ };
7
+
1
8
  // src/utils/expressCheckoutTracking.ts
2
9
  var EXPRESS_CHECKOUT_EVENTS = {
3
10
  /** Wallet button displayed */
@@ -35,6 +42,25 @@ var track = (action, extra) => {
35
42
  ...extra
36
43
  });
37
44
  };
45
+ var trackApplePayExperimentExposure = (variant) => {
46
+ const isActiveVariant = variant === APPLE_PAY_EXPRESS_VARIANTS.CONTROL || variant === APPLE_PAY_EXPRESS_VARIANTS.TREATMENT;
47
+ if (!isActiveVariant) {
48
+ return;
49
+ }
50
+ const exposures = window.__applePayExperimentExposures ?? {};
51
+ window.__applePayExperimentExposures = exposures;
52
+ const pathname = window.location?.pathname ?? "";
53
+ const dedupeKey = `${pathname}:${APPLE_PAY_EXPRESS_EXPERIMENT_KEY}:${variant}`;
54
+ if (exposures[dedupeKey]) {
55
+ return;
56
+ }
57
+ exposures[dedupeKey] = true;
58
+ emitTedlEvent({
59
+ name: "$exposure",
60
+ flag_key: APPLE_PAY_EXPRESS_EXPERIMENT_KEY,
61
+ variant
62
+ });
63
+ };
38
64
  var trackExpressWalletImpression = (skuId, { entryPoint, country }) => {
39
65
  track(EXPRESS_CHECKOUT_EVENTS.IMPRESSION, {
40
66
  wallet_type: "apple_pay",
@@ -73,6 +99,7 @@ var trackValidationFieldError = (field, errorType) => {
73
99
  };
74
100
  export {
75
101
  EXPRESS_CHECKOUT_EVENTS,
102
+ trackApplePayExperimentExposure,
76
103
  trackExpressWalletClick,
77
104
  trackExpressWalletImpression,
78
105
  trackFallbackCheckout,
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.1",
4
+ "version": "1.9.3",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
7
7
  "types": "dist/index.d.ts",