@economist/web-apple-pay 0.1.3-beta.22 → 0.1.3-beta.24

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
@@ -1156,18 +1156,11 @@ var apiFetch = async (path, options = {}) => {
1156
1156
  });
1157
1157
  if (!response.ok) {
1158
1158
  const contentType = response.headers.get("content-type") || "";
1159
- console.warn("[web-apple-pay] apiFetch: non-2xx response", {
1160
- path,
1161
- status: response.status,
1162
- contentType
1163
- });
1164
1159
  if (contentType.includes("application/json")) {
1165
1160
  const errorBody = await response.json();
1166
- console.warn("[web-apple-pay] apiFetch: parsed JSON error body", { errorBody });
1167
1161
  throw new ApiError(response.status, errorBody);
1168
1162
  }
1169
1163
  const errorText = await response.text();
1170
- console.warn("[web-apple-pay] apiFetch: non-JSON error body", { errorText });
1171
1164
  throw new Error(
1172
1165
  `API request failed: ${response.status} ${response.statusText} - ${errorText}`
1173
1166
  );
@@ -1245,6 +1238,8 @@ var redirectToCheckoutComplete = (params) => {
1245
1238
  url.searchParams.set("lmo_offer", params.skuId);
1246
1239
  url.searchParams.set("status", params.status);
1247
1240
  url.searchParams.set("redirectToCheckout", "true");
1241
+ if (params.email) url.searchParams.set("email", params.email);
1242
+ if (params.country) url.searchParams.set("country", params.country);
1248
1243
  return url.pathname + url.search;
1249
1244
  };
1250
1245
  var redirectToCheckoutFallback = (params) => {
@@ -1255,6 +1250,7 @@ var redirectToCheckoutFallback = (params) => {
1255
1250
  url.searchParams.set("inlineError", params.inlineError);
1256
1251
  if (params.email) url.searchParams.set("email", params.email);
1257
1252
  if (params.emailError) url.searchParams.set("emailError", params.emailError);
1253
+ if (params.country) url.searchParams.set("country", params.country);
1258
1254
  return url.pathname + url.search;
1259
1255
  };
1260
1256
 
@@ -1338,8 +1334,6 @@ var MSG_EMAIL_WRONG_FORMAT = "That email doesn\u2019t look right. Please enter a
1338
1334
  var MSG_EMAIL_TOO_LONG = "This field must be 80 characters or less.";
1339
1335
  var MSG_UNSUPPORTED_BILLING_COUNTRY = "Billing country does not match offer price zone";
1340
1336
  var MSG_PAYMENT_GENERIC_ERROR = "Something went wrong. Please check your payment and billing details and try again or contact us.";
1341
- var MSG_RECAPTCHA_VALIDATION_FAILED = "Security check failed. Please try again.";
1342
- var MSG_EMAIL_COLLISION = "This email address is already linked to an account. Please log in, or try a different email address.";
1343
1337
  var MSG_CONTACT_VALIDATION_INTERNAL = "Could not validate Apple Pay contact";
1344
1338
 
1345
1339
  // src/clients/payment-checkout/apple-session.ts
@@ -1348,7 +1342,8 @@ function redirectToFallbackCheckout(config) {
1348
1342
  trackFallbackCheckout(config.skuId);
1349
1343
  window.location.href = redirectToCheckoutFallback({
1350
1344
  skuId: config.skuId,
1351
- inlineError: MSG_PAYMENT_GENERIC_ERROR
1345
+ inlineError: MSG_PAYMENT_GENERIC_ERROR,
1346
+ country: config.countryCode
1352
1347
  });
1353
1348
  }
1354
1349
  async function handleMerchantValidation(event, session, config) {
@@ -1527,8 +1522,6 @@ var shippingContactDetails = (shippingContact) => {
1527
1522
  };
1528
1523
 
1529
1524
  // src/clients/payment-checkout/payment-handler.ts
1530
- var MSG_ELIGIBILITY = "Something went wrong, please try again";
1531
- var MSG_REGISTRATION = "We couldn't create your account. Please enter your email or try a different login method.";
1532
1525
  var failApplePaySession = (session, errors = []) => {
1533
1526
  session.completePayment({
1534
1527
  status: ApplePaySession.STATUS_FAILURE,
@@ -1558,42 +1551,42 @@ function validateContacts(event, sessionData, supportedBillingCountries) {
1558
1551
  }
1559
1552
  return void 0;
1560
1553
  }
1561
- function redirectFallback(skuId, returnUrl, options) {
1554
+ function redirectFallback(skuId, returnUrl, country, options) {
1562
1555
  writeCheckoutRedirect(returnUrl);
1563
1556
  trackFallbackCheckout(skuId);
1564
1557
  window.location.href = redirectToCheckoutFallback({
1565
1558
  skuId,
1559
+ country,
1566
1560
  ...options
1567
1561
  });
1568
1562
  }
1569
1563
  var ERROR_HANDLERS = {
1570
- EMAIL_COLLISION: ({ skuId, returnUrl, validatedEmail }) => redirectFallback(skuId, returnUrl, {
1571
- emailError: MSG_EMAIL_COLLISION,
1564
+ // emailError flows as an inline field error on the email input.
1565
+ // email pre-populates the input so the user doesn't have to retype it.
1566
+ EMAIL_COLLISION: ({ skuId, returnUrl, country, validatedEmail, walletError }) => redirectFallback(skuId, returnUrl, country, {
1567
+ emailError: walletError.message,
1572
1568
  email: validatedEmail
1573
1569
  }),
1574
- PAYMENT_DECLINED: ({ skuId, returnUrl }) => (
1575
- // Matches sma-checkout catch-all: always redirect with generic inlineError.
1576
- redirectFallback(skuId, returnUrl, { inlineError: MSG_PAYMENT_GENERIC_ERROR })
1577
- ),
1578
- ELIGIBILITY_REJECTED: ({ skuId, returnUrl }) => redirectFallback(skuId, returnUrl, { inlineError: MSG_ELIGIBILITY }),
1579
- REGISTRATION_FAILED: ({ skuId, returnUrl }) => redirectFallback(skuId, returnUrl, { inlineError: MSG_REGISTRATION }),
1580
- ACCOUNT_CREATION_FAILED: ({ skuId, returnUrl, validatedEmail }) => redirectFallback(skuId, returnUrl, {
1581
- emailError: MSG_REGISTRATION,
1570
+ PAYMENT_DECLINED: ({ skuId, returnUrl, country, walletError }) => redirectFallback(skuId, returnUrl, country, { inlineError: walletError.message }),
1571
+ ELIGIBILITY_REJECTED: ({ skuId, returnUrl, country, walletError }) => redirectFallback(skuId, returnUrl, country, { inlineError: walletError.message }),
1572
+ REGISTRATION_FAILED: ({ skuId, returnUrl, country, walletError }) => redirectFallback(skuId, returnUrl, country, { inlineError: walletError.message }),
1573
+ ACCOUNT_CREATION_FAILED: ({ skuId, returnUrl, country, validatedEmail, walletError }) => redirectFallback(skuId, returnUrl, country, {
1574
+ emailError: walletError.message,
1582
1575
  email: validatedEmail
1583
1576
  }),
1584
- VALIDATION_ERROR: ({ skuId, returnUrl }) => redirectFallback(skuId, returnUrl, { inlineError: MSG_RECAPTCHA_VALIDATION_FAILED }),
1585
- RECAPTCHA_FAILED: ({ skuId, returnUrl }) => redirectFallback(skuId, returnUrl, { inlineError: MSG_RECAPTCHA_VALIDATION_FAILED })
1577
+ VALIDATION_ERROR: ({ skuId, returnUrl, country, walletError }) => redirectFallback(skuId, returnUrl, country, { inlineError: walletError.message }),
1578
+ RECAPTCHA_FAILED: ({ skuId, returnUrl, country, walletError }) => redirectFallback(skuId, returnUrl, country, { inlineError: walletError.message })
1586
1579
  };
1587
1580
  function handleStructuredError(ctx) {
1588
1581
  const handler = ERROR_HANDLERS[ctx.walletError.errorType];
1589
1582
  if (handler) {
1590
1583
  handler(ctx);
1591
1584
  } else {
1592
- redirectFallback(ctx.skuId, ctx.returnUrl, { inlineError: MSG_PAYMENT_GENERIC_ERROR });
1585
+ redirectFallback(ctx.skuId, ctx.returnUrl, ctx.country, { inlineError: MSG_PAYMENT_GENERIC_ERROR });
1593
1586
  }
1594
1587
  }
1595
1588
  async function handlePaymentAuthorized(event, session, sessionData, config) {
1596
- const { skuId, returnUrl, queryString } = config;
1589
+ const { skuId, returnUrl, queryString, country } = config;
1597
1590
  let validatedEmail;
1598
1591
  try {
1599
1592
  validatedEmail = validateContacts(
@@ -1613,13 +1606,16 @@ async function handlePaymentAuthorized(event, session, sessionData, config) {
1613
1606
  });
1614
1607
  trackWalletPaymentSuccess(skuId, result.basketId);
1615
1608
  session.completePayment({ status: ApplePaySession.STATUS_SUCCESS });
1609
+ const successEmail = sessionData.loggedIn ? sessionData.emailAddress : validatedEmail;
1616
1610
  const userStatus = sessionData.loggedIn ? "lex" : "nex";
1617
1611
  writeCheckoutRedirect(returnUrl);
1618
1612
  window.location.href = redirectToCheckoutComplete({
1619
1613
  basketId: result.basketId,
1620
1614
  status: userStatus,
1621
1615
  skuId: decodeSku(skuId),
1622
- queryString
1616
+ queryString,
1617
+ email: successEmail,
1618
+ country
1623
1619
  });
1624
1620
  } catch (err) {
1625
1621
  if (err instanceof ContactValidationError) {
@@ -1628,17 +1624,13 @@ async function handlePaymentAuthorized(event, session, sessionData, config) {
1628
1624
  }
1629
1625
  console.error("[web-apple-pay] onpaymentauthorized error", { skuId, err });
1630
1626
  const walletError = parseWalletApiError(err);
1631
- console.warn("[web-apple-pay] handlePaymentAuthorized: walletError result", {
1632
- walletError,
1633
- validatedEmail
1634
- });
1635
1627
  trackWalletPaymentFailure(skuId, walletError?.errorType ?? "UNKNOWN");
1636
1628
  failApplePaySession(session);
1637
1629
  if (!walletError) {
1638
- redirectFallback(skuId, returnUrl, { inlineError: MSG_PAYMENT_GENERIC_ERROR });
1630
+ redirectFallback(skuId, returnUrl, country, { inlineError: MSG_PAYMENT_GENERIC_ERROR });
1639
1631
  return;
1640
1632
  }
1641
- handleStructuredError({ skuId, returnUrl, validatedEmail, walletError });
1633
+ handleStructuredError({ skuId, returnUrl, country, validatedEmail, walletError });
1642
1634
  }
1643
1635
  }
1644
1636
 
@@ -1151,18 +1151,11 @@ var apiFetch = async (path, options = {}) => {
1151
1151
  });
1152
1152
  if (!response.ok) {
1153
1153
  const contentType = response.headers.get("content-type") || "";
1154
- console.warn("[web-apple-pay] apiFetch: non-2xx response", {
1155
- path,
1156
- status: response.status,
1157
- contentType
1158
- });
1159
1154
  if (contentType.includes("application/json")) {
1160
1155
  const errorBody = await response.json();
1161
- console.warn("[web-apple-pay] apiFetch: parsed JSON error body", { errorBody });
1162
1156
  throw new ApiError(response.status, errorBody);
1163
1157
  }
1164
1158
  const errorText = await response.text();
1165
- console.warn("[web-apple-pay] apiFetch: non-JSON error body", { errorText });
1166
1159
  throw new Error(
1167
1160
  `API request failed: ${response.status} ${response.statusText} - ${errorText}`
1168
1161
  );
@@ -1240,6 +1233,8 @@ var redirectToCheckoutComplete = (params) => {
1240
1233
  url.searchParams.set("lmo_offer", params.skuId);
1241
1234
  url.searchParams.set("status", params.status);
1242
1235
  url.searchParams.set("redirectToCheckout", "true");
1236
+ if (params.email) url.searchParams.set("email", params.email);
1237
+ if (params.country) url.searchParams.set("country", params.country);
1243
1238
  return url.pathname + url.search;
1244
1239
  };
1245
1240
  var redirectToCheckoutFallback = (params) => {
@@ -1250,6 +1245,7 @@ var redirectToCheckoutFallback = (params) => {
1250
1245
  url.searchParams.set("inlineError", params.inlineError);
1251
1246
  if (params.email) url.searchParams.set("email", params.email);
1252
1247
  if (params.emailError) url.searchParams.set("emailError", params.emailError);
1248
+ if (params.country) url.searchParams.set("country", params.country);
1253
1249
  return url.pathname + url.search;
1254
1250
  };
1255
1251
 
@@ -1333,8 +1329,6 @@ var MSG_EMAIL_WRONG_FORMAT = "That email doesn\u2019t look right. Please enter a
1333
1329
  var MSG_EMAIL_TOO_LONG = "This field must be 80 characters or less.";
1334
1330
  var MSG_UNSUPPORTED_BILLING_COUNTRY = "Billing country does not match offer price zone";
1335
1331
  var MSG_PAYMENT_GENERIC_ERROR = "Something went wrong. Please check your payment and billing details and try again or contact us.";
1336
- var MSG_RECAPTCHA_VALIDATION_FAILED = "Security check failed. Please try again.";
1337
- var MSG_EMAIL_COLLISION = "This email address is already linked to an account. Please log in, or try a different email address.";
1338
1332
  var MSG_CONTACT_VALIDATION_INTERNAL = "Could not validate Apple Pay contact";
1339
1333
 
1340
1334
  // src/clients/payment-checkout/apple-session.ts
@@ -1343,7 +1337,8 @@ function redirectToFallbackCheckout(config) {
1343
1337
  trackFallbackCheckout(config.skuId);
1344
1338
  window.location.href = redirectToCheckoutFallback({
1345
1339
  skuId: config.skuId,
1346
- inlineError: MSG_PAYMENT_GENERIC_ERROR
1340
+ inlineError: MSG_PAYMENT_GENERIC_ERROR,
1341
+ country: config.countryCode
1347
1342
  });
1348
1343
  }
1349
1344
  async function handleMerchantValidation(event, session, config) {
@@ -1522,8 +1517,6 @@ var shippingContactDetails = (shippingContact) => {
1522
1517
  };
1523
1518
 
1524
1519
  // src/clients/payment-checkout/payment-handler.ts
1525
- var MSG_ELIGIBILITY = "Something went wrong, please try again";
1526
- var MSG_REGISTRATION = "We couldn't create your account. Please enter your email or try a different login method.";
1527
1520
  var failApplePaySession = (session, errors = []) => {
1528
1521
  session.completePayment({
1529
1522
  status: ApplePaySession.STATUS_FAILURE,
@@ -1553,42 +1546,42 @@ function validateContacts(event, sessionData, supportedBillingCountries) {
1553
1546
  }
1554
1547
  return void 0;
1555
1548
  }
1556
- function redirectFallback(skuId, returnUrl, options) {
1549
+ function redirectFallback(skuId, returnUrl, country, options) {
1557
1550
  writeCheckoutRedirect(returnUrl);
1558
1551
  trackFallbackCheckout(skuId);
1559
1552
  window.location.href = redirectToCheckoutFallback({
1560
1553
  skuId,
1554
+ country,
1561
1555
  ...options
1562
1556
  });
1563
1557
  }
1564
1558
  var ERROR_HANDLERS = {
1565
- EMAIL_COLLISION: ({ skuId, returnUrl, validatedEmail }) => redirectFallback(skuId, returnUrl, {
1566
- emailError: MSG_EMAIL_COLLISION,
1559
+ // emailError flows as an inline field error on the email input.
1560
+ // email pre-populates the input so the user doesn't have to retype it.
1561
+ EMAIL_COLLISION: ({ skuId, returnUrl, country, validatedEmail, walletError }) => redirectFallback(skuId, returnUrl, country, {
1562
+ emailError: walletError.message,
1567
1563
  email: validatedEmail
1568
1564
  }),
1569
- PAYMENT_DECLINED: ({ skuId, returnUrl }) => (
1570
- // Matches sma-checkout catch-all: always redirect with generic inlineError.
1571
- redirectFallback(skuId, returnUrl, { inlineError: MSG_PAYMENT_GENERIC_ERROR })
1572
- ),
1573
- ELIGIBILITY_REJECTED: ({ skuId, returnUrl }) => redirectFallback(skuId, returnUrl, { inlineError: MSG_ELIGIBILITY }),
1574
- REGISTRATION_FAILED: ({ skuId, returnUrl }) => redirectFallback(skuId, returnUrl, { inlineError: MSG_REGISTRATION }),
1575
- ACCOUNT_CREATION_FAILED: ({ skuId, returnUrl, validatedEmail }) => redirectFallback(skuId, returnUrl, {
1576
- emailError: MSG_REGISTRATION,
1565
+ PAYMENT_DECLINED: ({ skuId, returnUrl, country, walletError }) => redirectFallback(skuId, returnUrl, country, { inlineError: walletError.message }),
1566
+ ELIGIBILITY_REJECTED: ({ skuId, returnUrl, country, walletError }) => redirectFallback(skuId, returnUrl, country, { inlineError: walletError.message }),
1567
+ REGISTRATION_FAILED: ({ skuId, returnUrl, country, walletError }) => redirectFallback(skuId, returnUrl, country, { inlineError: walletError.message }),
1568
+ ACCOUNT_CREATION_FAILED: ({ skuId, returnUrl, country, validatedEmail, walletError }) => redirectFallback(skuId, returnUrl, country, {
1569
+ emailError: walletError.message,
1577
1570
  email: validatedEmail
1578
1571
  }),
1579
- VALIDATION_ERROR: ({ skuId, returnUrl }) => redirectFallback(skuId, returnUrl, { inlineError: MSG_RECAPTCHA_VALIDATION_FAILED }),
1580
- RECAPTCHA_FAILED: ({ skuId, returnUrl }) => redirectFallback(skuId, returnUrl, { inlineError: MSG_RECAPTCHA_VALIDATION_FAILED })
1572
+ VALIDATION_ERROR: ({ skuId, returnUrl, country, walletError }) => redirectFallback(skuId, returnUrl, country, { inlineError: walletError.message }),
1573
+ RECAPTCHA_FAILED: ({ skuId, returnUrl, country, walletError }) => redirectFallback(skuId, returnUrl, country, { inlineError: walletError.message })
1581
1574
  };
1582
1575
  function handleStructuredError(ctx) {
1583
1576
  const handler = ERROR_HANDLERS[ctx.walletError.errorType];
1584
1577
  if (handler) {
1585
1578
  handler(ctx);
1586
1579
  } else {
1587
- redirectFallback(ctx.skuId, ctx.returnUrl, { inlineError: MSG_PAYMENT_GENERIC_ERROR });
1580
+ redirectFallback(ctx.skuId, ctx.returnUrl, ctx.country, { inlineError: MSG_PAYMENT_GENERIC_ERROR });
1588
1581
  }
1589
1582
  }
1590
1583
  async function handlePaymentAuthorized(event, session, sessionData, config) {
1591
- const { skuId, returnUrl, queryString } = config;
1584
+ const { skuId, returnUrl, queryString, country } = config;
1592
1585
  let validatedEmail;
1593
1586
  try {
1594
1587
  validatedEmail = validateContacts(
@@ -1608,13 +1601,16 @@ async function handlePaymentAuthorized(event, session, sessionData, config) {
1608
1601
  });
1609
1602
  trackWalletPaymentSuccess(skuId, result.basketId);
1610
1603
  session.completePayment({ status: ApplePaySession.STATUS_SUCCESS });
1604
+ const successEmail = sessionData.loggedIn ? sessionData.emailAddress : validatedEmail;
1611
1605
  const userStatus = sessionData.loggedIn ? "lex" : "nex";
1612
1606
  writeCheckoutRedirect(returnUrl);
1613
1607
  window.location.href = redirectToCheckoutComplete({
1614
1608
  basketId: result.basketId,
1615
1609
  status: userStatus,
1616
1610
  skuId: decodeSku(skuId),
1617
- queryString
1611
+ queryString,
1612
+ email: successEmail,
1613
+ country
1618
1614
  });
1619
1615
  } catch (err) {
1620
1616
  if (err instanceof ContactValidationError) {
@@ -1623,17 +1619,13 @@ async function handlePaymentAuthorized(event, session, sessionData, config) {
1623
1619
  }
1624
1620
  console.error("[web-apple-pay] onpaymentauthorized error", { skuId, err });
1625
1621
  const walletError = parseWalletApiError(err);
1626
- console.warn("[web-apple-pay] handlePaymentAuthorized: walletError result", {
1627
- walletError,
1628
- validatedEmail
1629
- });
1630
1622
  trackWalletPaymentFailure(skuId, walletError?.errorType ?? "UNKNOWN");
1631
1623
  failApplePaySession(session);
1632
1624
  if (!walletError) {
1633
- redirectFallback(skuId, returnUrl, { inlineError: MSG_PAYMENT_GENERIC_ERROR });
1625
+ redirectFallback(skuId, returnUrl, country, { inlineError: MSG_PAYMENT_GENERIC_ERROR });
1634
1626
  return;
1635
1627
  }
1636
- handleStructuredError({ skuId, returnUrl, validatedEmail, walletError });
1628
+ handleStructuredError({ skuId, returnUrl, country, validatedEmail, walletError });
1637
1629
  }
1638
1630
  }
1639
1631
 
@@ -1159,18 +1159,11 @@ var apiFetch = async (path, options = {}) => {
1159
1159
  });
1160
1160
  if (!response.ok) {
1161
1161
  const contentType = response.headers.get("content-type") || "";
1162
- console.warn("[web-apple-pay] apiFetch: non-2xx response", {
1163
- path,
1164
- status: response.status,
1165
- contentType
1166
- });
1167
1162
  if (contentType.includes("application/json")) {
1168
1163
  const errorBody = await response.json();
1169
- console.warn("[web-apple-pay] apiFetch: parsed JSON error body", { errorBody });
1170
1164
  throw new ApiError(response.status, errorBody);
1171
1165
  }
1172
1166
  const errorText = await response.text();
1173
- console.warn("[web-apple-pay] apiFetch: non-JSON error body", { errorText });
1174
1167
  throw new Error(
1175
1168
  `API request failed: ${response.status} ${response.statusText} - ${errorText}`
1176
1169
  );
@@ -1248,6 +1241,8 @@ var redirectToCheckoutComplete = (params) => {
1248
1241
  url.searchParams.set("lmo_offer", params.skuId);
1249
1242
  url.searchParams.set("status", params.status);
1250
1243
  url.searchParams.set("redirectToCheckout", "true");
1244
+ if (params.email) url.searchParams.set("email", params.email);
1245
+ if (params.country) url.searchParams.set("country", params.country);
1251
1246
  return url.pathname + url.search;
1252
1247
  };
1253
1248
  var redirectToCheckoutFallback = (params) => {
@@ -1258,6 +1253,7 @@ var redirectToCheckoutFallback = (params) => {
1258
1253
  url.searchParams.set("inlineError", params.inlineError);
1259
1254
  if (params.email) url.searchParams.set("email", params.email);
1260
1255
  if (params.emailError) url.searchParams.set("emailError", params.emailError);
1256
+ if (params.country) url.searchParams.set("country", params.country);
1261
1257
  return url.pathname + url.search;
1262
1258
  };
1263
1259
 
@@ -1341,8 +1337,6 @@ var MSG_EMAIL_WRONG_FORMAT = "That email doesn\u2019t look right. Please enter a
1341
1337
  var MSG_EMAIL_TOO_LONG = "This field must be 80 characters or less.";
1342
1338
  var MSG_UNSUPPORTED_BILLING_COUNTRY = "Billing country does not match offer price zone";
1343
1339
  var MSG_PAYMENT_GENERIC_ERROR = "Something went wrong. Please check your payment and billing details and try again or contact us.";
1344
- var MSG_RECAPTCHA_VALIDATION_FAILED = "Security check failed. Please try again.";
1345
- var MSG_EMAIL_COLLISION = "This email address is already linked to an account. Please log in, or try a different email address.";
1346
1340
  var MSG_CONTACT_VALIDATION_INTERNAL = "Could not validate Apple Pay contact";
1347
1341
 
1348
1342
  // src/clients/payment-checkout/apple-session.ts
@@ -1351,7 +1345,8 @@ function redirectToFallbackCheckout(config) {
1351
1345
  trackFallbackCheckout(config.skuId);
1352
1346
  window.location.href = redirectToCheckoutFallback({
1353
1347
  skuId: config.skuId,
1354
- inlineError: MSG_PAYMENT_GENERIC_ERROR
1348
+ inlineError: MSG_PAYMENT_GENERIC_ERROR,
1349
+ country: config.countryCode
1355
1350
  });
1356
1351
  }
1357
1352
  async function handleMerchantValidation(event, session, config) {
@@ -1530,8 +1525,6 @@ var shippingContactDetails = (shippingContact) => {
1530
1525
  };
1531
1526
 
1532
1527
  // src/clients/payment-checkout/payment-handler.ts
1533
- var MSG_ELIGIBILITY = "Something went wrong, please try again";
1534
- var MSG_REGISTRATION = "We couldn't create your account. Please enter your email or try a different login method.";
1535
1528
  var failApplePaySession = (session, errors = []) => {
1536
1529
  session.completePayment({
1537
1530
  status: ApplePaySession.STATUS_FAILURE,
@@ -1561,42 +1554,42 @@ function validateContacts(event, sessionData, supportedBillingCountries) {
1561
1554
  }
1562
1555
  return void 0;
1563
1556
  }
1564
- function redirectFallback(skuId, returnUrl, options) {
1557
+ function redirectFallback(skuId, returnUrl, country, options) {
1565
1558
  writeCheckoutRedirect(returnUrl);
1566
1559
  trackFallbackCheckout(skuId);
1567
1560
  window.location.href = redirectToCheckoutFallback({
1568
1561
  skuId,
1562
+ country,
1569
1563
  ...options
1570
1564
  });
1571
1565
  }
1572
1566
  var ERROR_HANDLERS = {
1573
- EMAIL_COLLISION: ({ skuId, returnUrl, validatedEmail }) => redirectFallback(skuId, returnUrl, {
1574
- emailError: MSG_EMAIL_COLLISION,
1567
+ // emailError flows as an inline field error on the email input.
1568
+ // email pre-populates the input so the user doesn't have to retype it.
1569
+ EMAIL_COLLISION: ({ skuId, returnUrl, country, validatedEmail, walletError }) => redirectFallback(skuId, returnUrl, country, {
1570
+ emailError: walletError.message,
1575
1571
  email: validatedEmail
1576
1572
  }),
1577
- PAYMENT_DECLINED: ({ skuId, returnUrl }) => (
1578
- // Matches sma-checkout catch-all: always redirect with generic inlineError.
1579
- redirectFallback(skuId, returnUrl, { inlineError: MSG_PAYMENT_GENERIC_ERROR })
1580
- ),
1581
- ELIGIBILITY_REJECTED: ({ skuId, returnUrl }) => redirectFallback(skuId, returnUrl, { inlineError: MSG_ELIGIBILITY }),
1582
- REGISTRATION_FAILED: ({ skuId, returnUrl }) => redirectFallback(skuId, returnUrl, { inlineError: MSG_REGISTRATION }),
1583
- ACCOUNT_CREATION_FAILED: ({ skuId, returnUrl, validatedEmail }) => redirectFallback(skuId, returnUrl, {
1584
- emailError: MSG_REGISTRATION,
1573
+ PAYMENT_DECLINED: ({ skuId, returnUrl, country, walletError }) => redirectFallback(skuId, returnUrl, country, { inlineError: walletError.message }),
1574
+ ELIGIBILITY_REJECTED: ({ skuId, returnUrl, country, walletError }) => redirectFallback(skuId, returnUrl, country, { inlineError: walletError.message }),
1575
+ REGISTRATION_FAILED: ({ skuId, returnUrl, country, walletError }) => redirectFallback(skuId, returnUrl, country, { inlineError: walletError.message }),
1576
+ ACCOUNT_CREATION_FAILED: ({ skuId, returnUrl, country, validatedEmail, walletError }) => redirectFallback(skuId, returnUrl, country, {
1577
+ emailError: walletError.message,
1585
1578
  email: validatedEmail
1586
1579
  }),
1587
- VALIDATION_ERROR: ({ skuId, returnUrl }) => redirectFallback(skuId, returnUrl, { inlineError: MSG_RECAPTCHA_VALIDATION_FAILED }),
1588
- RECAPTCHA_FAILED: ({ skuId, returnUrl }) => redirectFallback(skuId, returnUrl, { inlineError: MSG_RECAPTCHA_VALIDATION_FAILED })
1580
+ VALIDATION_ERROR: ({ skuId, returnUrl, country, walletError }) => redirectFallback(skuId, returnUrl, country, { inlineError: walletError.message }),
1581
+ RECAPTCHA_FAILED: ({ skuId, returnUrl, country, walletError }) => redirectFallback(skuId, returnUrl, country, { inlineError: walletError.message })
1589
1582
  };
1590
1583
  function handleStructuredError(ctx) {
1591
1584
  const handler = ERROR_HANDLERS[ctx.walletError.errorType];
1592
1585
  if (handler) {
1593
1586
  handler(ctx);
1594
1587
  } else {
1595
- redirectFallback(ctx.skuId, ctx.returnUrl, { inlineError: MSG_PAYMENT_GENERIC_ERROR });
1588
+ redirectFallback(ctx.skuId, ctx.returnUrl, ctx.country, { inlineError: MSG_PAYMENT_GENERIC_ERROR });
1596
1589
  }
1597
1590
  }
1598
1591
  async function handlePaymentAuthorized(event, session, sessionData, config) {
1599
- const { skuId, returnUrl, queryString } = config;
1592
+ const { skuId, returnUrl, queryString, country } = config;
1600
1593
  let validatedEmail;
1601
1594
  try {
1602
1595
  validatedEmail = validateContacts(
@@ -1616,13 +1609,16 @@ async function handlePaymentAuthorized(event, session, sessionData, config) {
1616
1609
  });
1617
1610
  trackWalletPaymentSuccess(skuId, result.basketId);
1618
1611
  session.completePayment({ status: ApplePaySession.STATUS_SUCCESS });
1612
+ const successEmail = sessionData.loggedIn ? sessionData.emailAddress : validatedEmail;
1619
1613
  const userStatus = sessionData.loggedIn ? "lex" : "nex";
1620
1614
  writeCheckoutRedirect(returnUrl);
1621
1615
  window.location.href = redirectToCheckoutComplete({
1622
1616
  basketId: result.basketId,
1623
1617
  status: userStatus,
1624
1618
  skuId: decodeSku(skuId),
1625
- queryString
1619
+ queryString,
1620
+ email: successEmail,
1621
+ country
1626
1622
  });
1627
1623
  } catch (err) {
1628
1624
  if (err instanceof ContactValidationError) {
@@ -1631,17 +1627,13 @@ async function handlePaymentAuthorized(event, session, sessionData, config) {
1631
1627
  }
1632
1628
  console.error("[web-apple-pay] onpaymentauthorized error", { skuId, err });
1633
1629
  const walletError = parseWalletApiError(err);
1634
- console.warn("[web-apple-pay] handlePaymentAuthorized: walletError result", {
1635
- walletError,
1636
- validatedEmail
1637
- });
1638
1630
  trackWalletPaymentFailure(skuId, walletError?.errorType ?? "UNKNOWN");
1639
1631
  failApplePaySession(session);
1640
1632
  if (!walletError) {
1641
- redirectFallback(skuId, returnUrl, { inlineError: MSG_PAYMENT_GENERIC_ERROR });
1633
+ redirectFallback(skuId, returnUrl, country, { inlineError: MSG_PAYMENT_GENERIC_ERROR });
1642
1634
  return;
1643
1635
  }
1644
- handleStructuredError({ skuId, returnUrl, validatedEmail, walletError });
1636
+ handleStructuredError({ skuId, returnUrl, country, validatedEmail, walletError });
1645
1637
  }
1646
1638
  }
1647
1639
 
@@ -1151,18 +1151,11 @@ var apiFetch = async (path, options = {}) => {
1151
1151
  });
1152
1152
  if (!response.ok) {
1153
1153
  const contentType = response.headers.get("content-type") || "";
1154
- console.warn("[web-apple-pay] apiFetch: non-2xx response", {
1155
- path,
1156
- status: response.status,
1157
- contentType
1158
- });
1159
1154
  if (contentType.includes("application/json")) {
1160
1155
  const errorBody = await response.json();
1161
- console.warn("[web-apple-pay] apiFetch: parsed JSON error body", { errorBody });
1162
1156
  throw new ApiError(response.status, errorBody);
1163
1157
  }
1164
1158
  const errorText = await response.text();
1165
- console.warn("[web-apple-pay] apiFetch: non-JSON error body", { errorText });
1166
1159
  throw new Error(
1167
1160
  `API request failed: ${response.status} ${response.statusText} - ${errorText}`
1168
1161
  );
@@ -1240,6 +1233,8 @@ var redirectToCheckoutComplete = (params) => {
1240
1233
  url.searchParams.set("lmo_offer", params.skuId);
1241
1234
  url.searchParams.set("status", params.status);
1242
1235
  url.searchParams.set("redirectToCheckout", "true");
1236
+ if (params.email) url.searchParams.set("email", params.email);
1237
+ if (params.country) url.searchParams.set("country", params.country);
1243
1238
  return url.pathname + url.search;
1244
1239
  };
1245
1240
  var redirectToCheckoutFallback = (params) => {
@@ -1250,6 +1245,7 @@ var redirectToCheckoutFallback = (params) => {
1250
1245
  url.searchParams.set("inlineError", params.inlineError);
1251
1246
  if (params.email) url.searchParams.set("email", params.email);
1252
1247
  if (params.emailError) url.searchParams.set("emailError", params.emailError);
1248
+ if (params.country) url.searchParams.set("country", params.country);
1253
1249
  return url.pathname + url.search;
1254
1250
  };
1255
1251
 
@@ -1333,8 +1329,6 @@ var MSG_EMAIL_WRONG_FORMAT = "That email doesn\u2019t look right. Please enter a
1333
1329
  var MSG_EMAIL_TOO_LONG = "This field must be 80 characters or less.";
1334
1330
  var MSG_UNSUPPORTED_BILLING_COUNTRY = "Billing country does not match offer price zone";
1335
1331
  var MSG_PAYMENT_GENERIC_ERROR = "Something went wrong. Please check your payment and billing details and try again or contact us.";
1336
- var MSG_RECAPTCHA_VALIDATION_FAILED = "Security check failed. Please try again.";
1337
- var MSG_EMAIL_COLLISION = "This email address is already linked to an account. Please log in, or try a different email address.";
1338
1332
  var MSG_CONTACT_VALIDATION_INTERNAL = "Could not validate Apple Pay contact";
1339
1333
 
1340
1334
  // src/clients/payment-checkout/apple-session.ts
@@ -1343,7 +1337,8 @@ function redirectToFallbackCheckout(config) {
1343
1337
  trackFallbackCheckout(config.skuId);
1344
1338
  window.location.href = redirectToCheckoutFallback({
1345
1339
  skuId: config.skuId,
1346
- inlineError: MSG_PAYMENT_GENERIC_ERROR
1340
+ inlineError: MSG_PAYMENT_GENERIC_ERROR,
1341
+ country: config.countryCode
1347
1342
  });
1348
1343
  }
1349
1344
  async function handleMerchantValidation(event, session, config) {
@@ -1522,8 +1517,6 @@ var shippingContactDetails = (shippingContact) => {
1522
1517
  };
1523
1518
 
1524
1519
  // src/clients/payment-checkout/payment-handler.ts
1525
- var MSG_ELIGIBILITY = "Something went wrong, please try again";
1526
- var MSG_REGISTRATION = "We couldn't create your account. Please enter your email or try a different login method.";
1527
1520
  var failApplePaySession = (session, errors = []) => {
1528
1521
  session.completePayment({
1529
1522
  status: ApplePaySession.STATUS_FAILURE,
@@ -1553,42 +1546,42 @@ function validateContacts(event, sessionData, supportedBillingCountries) {
1553
1546
  }
1554
1547
  return void 0;
1555
1548
  }
1556
- function redirectFallback(skuId, returnUrl, options) {
1549
+ function redirectFallback(skuId, returnUrl, country, options) {
1557
1550
  writeCheckoutRedirect(returnUrl);
1558
1551
  trackFallbackCheckout(skuId);
1559
1552
  window.location.href = redirectToCheckoutFallback({
1560
1553
  skuId,
1554
+ country,
1561
1555
  ...options
1562
1556
  });
1563
1557
  }
1564
1558
  var ERROR_HANDLERS = {
1565
- EMAIL_COLLISION: ({ skuId, returnUrl, validatedEmail }) => redirectFallback(skuId, returnUrl, {
1566
- emailError: MSG_EMAIL_COLLISION,
1559
+ // emailError flows as an inline field error on the email input.
1560
+ // email pre-populates the input so the user doesn't have to retype it.
1561
+ EMAIL_COLLISION: ({ skuId, returnUrl, country, validatedEmail, walletError }) => redirectFallback(skuId, returnUrl, country, {
1562
+ emailError: walletError.message,
1567
1563
  email: validatedEmail
1568
1564
  }),
1569
- PAYMENT_DECLINED: ({ skuId, returnUrl }) => (
1570
- // Matches sma-checkout catch-all: always redirect with generic inlineError.
1571
- redirectFallback(skuId, returnUrl, { inlineError: MSG_PAYMENT_GENERIC_ERROR })
1572
- ),
1573
- ELIGIBILITY_REJECTED: ({ skuId, returnUrl }) => redirectFallback(skuId, returnUrl, { inlineError: MSG_ELIGIBILITY }),
1574
- REGISTRATION_FAILED: ({ skuId, returnUrl }) => redirectFallback(skuId, returnUrl, { inlineError: MSG_REGISTRATION }),
1575
- ACCOUNT_CREATION_FAILED: ({ skuId, returnUrl, validatedEmail }) => redirectFallback(skuId, returnUrl, {
1576
- emailError: MSG_REGISTRATION,
1565
+ PAYMENT_DECLINED: ({ skuId, returnUrl, country, walletError }) => redirectFallback(skuId, returnUrl, country, { inlineError: walletError.message }),
1566
+ ELIGIBILITY_REJECTED: ({ skuId, returnUrl, country, walletError }) => redirectFallback(skuId, returnUrl, country, { inlineError: walletError.message }),
1567
+ REGISTRATION_FAILED: ({ skuId, returnUrl, country, walletError }) => redirectFallback(skuId, returnUrl, country, { inlineError: walletError.message }),
1568
+ ACCOUNT_CREATION_FAILED: ({ skuId, returnUrl, country, validatedEmail, walletError }) => redirectFallback(skuId, returnUrl, country, {
1569
+ emailError: walletError.message,
1577
1570
  email: validatedEmail
1578
1571
  }),
1579
- VALIDATION_ERROR: ({ skuId, returnUrl }) => redirectFallback(skuId, returnUrl, { inlineError: MSG_RECAPTCHA_VALIDATION_FAILED }),
1580
- RECAPTCHA_FAILED: ({ skuId, returnUrl }) => redirectFallback(skuId, returnUrl, { inlineError: MSG_RECAPTCHA_VALIDATION_FAILED })
1572
+ VALIDATION_ERROR: ({ skuId, returnUrl, country, walletError }) => redirectFallback(skuId, returnUrl, country, { inlineError: walletError.message }),
1573
+ RECAPTCHA_FAILED: ({ skuId, returnUrl, country, walletError }) => redirectFallback(skuId, returnUrl, country, { inlineError: walletError.message })
1581
1574
  };
1582
1575
  function handleStructuredError(ctx) {
1583
1576
  const handler = ERROR_HANDLERS[ctx.walletError.errorType];
1584
1577
  if (handler) {
1585
1578
  handler(ctx);
1586
1579
  } else {
1587
- redirectFallback(ctx.skuId, ctx.returnUrl, { inlineError: MSG_PAYMENT_GENERIC_ERROR });
1580
+ redirectFallback(ctx.skuId, ctx.returnUrl, ctx.country, { inlineError: MSG_PAYMENT_GENERIC_ERROR });
1588
1581
  }
1589
1582
  }
1590
1583
  async function handlePaymentAuthorized(event, session, sessionData, config) {
1591
- const { skuId, returnUrl, queryString } = config;
1584
+ const { skuId, returnUrl, queryString, country } = config;
1592
1585
  let validatedEmail;
1593
1586
  try {
1594
1587
  validatedEmail = validateContacts(
@@ -1608,13 +1601,16 @@ async function handlePaymentAuthorized(event, session, sessionData, config) {
1608
1601
  });
1609
1602
  trackWalletPaymentSuccess(skuId, result.basketId);
1610
1603
  session.completePayment({ status: ApplePaySession.STATUS_SUCCESS });
1604
+ const successEmail = sessionData.loggedIn ? sessionData.emailAddress : validatedEmail;
1611
1605
  const userStatus = sessionData.loggedIn ? "lex" : "nex";
1612
1606
  writeCheckoutRedirect(returnUrl);
1613
1607
  window.location.href = redirectToCheckoutComplete({
1614
1608
  basketId: result.basketId,
1615
1609
  status: userStatus,
1616
1610
  skuId: decodeSku(skuId),
1617
- queryString
1611
+ queryString,
1612
+ email: successEmail,
1613
+ country
1618
1614
  });
1619
1615
  } catch (err) {
1620
1616
  if (err instanceof ContactValidationError) {
@@ -1623,17 +1619,13 @@ async function handlePaymentAuthorized(event, session, sessionData, config) {
1623
1619
  }
1624
1620
  console.error("[web-apple-pay] onpaymentauthorized error", { skuId, err });
1625
1621
  const walletError = parseWalletApiError(err);
1626
- console.warn("[web-apple-pay] handlePaymentAuthorized: walletError result", {
1627
- walletError,
1628
- validatedEmail
1629
- });
1630
1622
  trackWalletPaymentFailure(skuId, walletError?.errorType ?? "UNKNOWN");
1631
1623
  failApplePaySession(session);
1632
1624
  if (!walletError) {
1633
- redirectFallback(skuId, returnUrl, { inlineError: MSG_PAYMENT_GENERIC_ERROR });
1625
+ redirectFallback(skuId, returnUrl, country, { inlineError: MSG_PAYMENT_GENERIC_ERROR });
1634
1626
  return;
1635
1627
  }
1636
- handleStructuredError({ skuId, returnUrl, validatedEmail, walletError });
1628
+ handleStructuredError({ skuId, returnUrl, country, validatedEmail, walletError });
1637
1629
  }
1638
1630
  }
1639
1631
 
@@ -43,18 +43,11 @@ var apiFetch = async (path, options = {}) => {
43
43
  });
44
44
  if (!response.ok) {
45
45
  const contentType = response.headers.get("content-type") || "";
46
- console.warn("[web-apple-pay] apiFetch: non-2xx response", {
47
- path,
48
- status: response.status,
49
- contentType
50
- });
51
46
  if (contentType.includes("application/json")) {
52
47
  const errorBody = await response.json();
53
- console.warn("[web-apple-pay] apiFetch: parsed JSON error body", { errorBody });
54
48
  throw new ApiError(response.status, errorBody);
55
49
  }
56
50
  const errorText = await response.text();
57
- console.warn("[web-apple-pay] apiFetch: non-JSON error body", { errorText });
58
51
  throw new Error(
59
52
  `API request failed: ${response.status} ${response.statusText} - ${errorText}`
60
53
  );
@@ -100,6 +93,7 @@ var redirectToCheckoutFallback = (params) => {
100
93
  url.searchParams.set("inlineError", params.inlineError);
101
94
  if (params.email) url.searchParams.set("email", params.email);
102
95
  if (params.emailError) url.searchParams.set("emailError", params.emailError);
96
+ if (params.country) url.searchParams.set("country", params.country);
103
97
  return url.pathname + url.search;
104
98
  };
105
99
 
@@ -192,7 +186,8 @@ function redirectToFallbackCheckout(config) {
192
186
  trackFallbackCheckout(config.skuId);
193
187
  window.location.href = redirectToCheckoutFallback({
194
188
  skuId: config.skuId,
195
- inlineError: MSG_PAYMENT_GENERIC_ERROR
189
+ inlineError: MSG_PAYMENT_GENERIC_ERROR,
190
+ country: config.countryCode
196
191
  });
197
192
  }
198
193
  async function handleMerchantValidation(event, session, config) {
@@ -43,18 +43,11 @@ var apiFetch = async (path, options = {}) => {
43
43
  });
44
44
  if (!response.ok) {
45
45
  const contentType = response.headers.get("content-type") || "";
46
- console.warn("[web-apple-pay] apiFetch: non-2xx response", {
47
- path,
48
- status: response.status,
49
- contentType
50
- });
51
46
  if (contentType.includes("application/json")) {
52
47
  const errorBody = await response.json();
53
- console.warn("[web-apple-pay] apiFetch: parsed JSON error body", { errorBody });
54
48
  throw new ApiError(response.status, errorBody);
55
49
  }
56
50
  const errorText = await response.text();
57
- console.warn("[web-apple-pay] apiFetch: non-JSON error body", { errorText });
58
51
  throw new Error(
59
52
  `API request failed: ${response.status} ${response.statusText} - ${errorText}`
60
53
  );
@@ -1,9 +1,16 @@
1
1
  /**
2
- * Canonical user-facing error messages for the Apple Pay Express Checkout flow.
2
+ * Canonical user-facing strings for the Apple Pay Express Checkout flow.
3
3
  *
4
- * All copy here is verified against sma-checkout (src/actions/constants/index.ts
5
- * and handlers/create-on-payment-authorized-handler/) to ensure parity with the
6
- * reference implementation. Update both repos if copy changes.
4
+ * These constants cover two categories:
5
+ * 1. Local contact/field validation — fired before any BFF call, no API
6
+ * message is available.
7
+ * 2. Pre-purchase fallbacks — merchant validation failures and unstructured
8
+ * network errors where no BFF WalletApiError is present.
9
+ *
10
+ * Structured BFF errors (EMAIL_COLLISION, PAYMENT_DECLINED, RECAPTCHA_FAILED,
11
+ * etc.) are NOT represented here — their user-facing copy comes directly from
12
+ * walletError.message in the API response, so copy changes on the BFF side
13
+ * take effect without a frontend deploy.
7
14
  */
8
15
  /** Shown when a required Apple Pay contact field is absent. */
9
16
  export declare const MSG_MISSING_REQUIRED_FIELD = "Missing required field";
@@ -19,15 +26,5 @@ export declare const MSG_UNSUPPORTED_BILLING_COUNTRY = "Billing country does not
19
26
  * Matches sma-checkout GENERIC_ERROR_MSG pattern.
20
27
  */
21
28
  export declare const MSG_PAYMENT_GENERIC_ERROR = "Something went wrong. Please check your payment and billing details and try again or contact us.";
22
- /**
23
- * Shown when a reCAPTCHA security check fails.
24
- * Matches sma-checkout reference: handlers/create-on-payment-authorized-handler/subscribe.
25
- */
26
- export declare const MSG_RECAPTCHA_VALIDATION_FAILED = "Security check failed. Please try again.";
27
- /**
28
- * Shown when the BFF returns an EMAIL_COLLISION error (account already exists).
29
- * Matches sma-checkout L103-104.
30
- */
31
- export declare const MSG_EMAIL_COLLISION = "This email address is already linked to an account. Please log in, or try a different email address.";
32
29
  /** Internal ContactValidationError message — not shown to the user directly. */
33
30
  export declare const MSG_CONTACT_VALIDATION_INTERNAL = "Could not validate Apple Pay contact";
@@ -4,16 +4,12 @@ var MSG_EMAIL_WRONG_FORMAT = "That email doesn\u2019t look right. Please enter a
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
6
  var MSG_PAYMENT_GENERIC_ERROR = "Something went wrong. Please check your payment and billing details and try again or contact us.";
7
- var MSG_RECAPTCHA_VALIDATION_FAILED = "Security check failed. Please try again.";
8
- var MSG_EMAIL_COLLISION = "This email address is already linked to an account. Please log in, or try a different email address.";
9
7
  var MSG_CONTACT_VALIDATION_INTERNAL = "Could not validate Apple Pay contact";
10
8
  export {
11
9
  MSG_CONTACT_VALIDATION_INTERNAL,
12
- MSG_EMAIL_COLLISION,
13
10
  MSG_EMAIL_TOO_LONG,
14
11
  MSG_EMAIL_WRONG_FORMAT,
15
12
  MSG_MISSING_REQUIRED_FIELD,
16
13
  MSG_PAYMENT_GENERIC_ERROR,
17
- MSG_RECAPTCHA_VALIDATION_FAILED,
18
14
  MSG_UNSUPPORTED_BILLING_COUNTRY
19
15
  };
@@ -43,18 +43,11 @@ var apiFetch = async (path, options = {}) => {
43
43
  });
44
44
  if (!response.ok) {
45
45
  const contentType = response.headers.get("content-type") || "";
46
- console.warn("[web-apple-pay] apiFetch: non-2xx response", {
47
- path,
48
- status: response.status,
49
- contentType
50
- });
51
46
  if (contentType.includes("application/json")) {
52
47
  const errorBody = await response.json();
53
- console.warn("[web-apple-pay] apiFetch: parsed JSON error body", { errorBody });
54
48
  throw new ApiError(response.status, errorBody);
55
49
  }
56
50
  const errorText = await response.text();
57
- console.warn("[web-apple-pay] apiFetch: non-JSON error body", { errorText });
58
51
  throw new Error(
59
52
  `API request failed: ${response.status} ${response.statusText} - ${errorText}`
60
53
  );
@@ -125,8 +118,6 @@ var MSG_EMAIL_WRONG_FORMAT = "That email doesn\u2019t look right. Please enter a
125
118
  var MSG_EMAIL_TOO_LONG = "This field must be 80 characters or less.";
126
119
  var MSG_UNSUPPORTED_BILLING_COUNTRY = "Billing country does not match offer price zone";
127
120
  var MSG_PAYMENT_GENERIC_ERROR = "Something went wrong. Please check your payment and billing details and try again or contact us.";
128
- var MSG_RECAPTCHA_VALIDATION_FAILED = "Security check failed. Please try again.";
129
- var MSG_EMAIL_COLLISION = "This email address is already linked to an account. Please log in, or try a different email address.";
130
121
  var MSG_CONTACT_VALIDATION_INTERNAL = "Could not validate Apple Pay contact";
131
122
 
132
123
  // src/clients/payment-checkout/contact-validation.ts
@@ -279,6 +270,8 @@ var redirectToCheckoutComplete = (params) => {
279
270
  url.searchParams.set("lmo_offer", params.skuId);
280
271
  url.searchParams.set("status", params.status);
281
272
  url.searchParams.set("redirectToCheckout", "true");
273
+ if (params.email) url.searchParams.set("email", params.email);
274
+ if (params.country) url.searchParams.set("country", params.country);
282
275
  return url.pathname + url.search;
283
276
  };
284
277
  var redirectToCheckoutFallback = (params) => {
@@ -289,12 +282,11 @@ var redirectToCheckoutFallback = (params) => {
289
282
  url.searchParams.set("inlineError", params.inlineError);
290
283
  if (params.email) url.searchParams.set("email", params.email);
291
284
  if (params.emailError) url.searchParams.set("emailError", params.emailError);
285
+ if (params.country) url.searchParams.set("country", params.country);
292
286
  return url.pathname + url.search;
293
287
  };
294
288
 
295
289
  // src/clients/payment-checkout/payment-handler.ts
296
- var MSG_ELIGIBILITY = "Something went wrong, please try again";
297
- var MSG_REGISTRATION = "We couldn't create your account. Please enter your email or try a different login method.";
298
290
  var failApplePaySession = (session, errors = []) => {
299
291
  session.completePayment({
300
292
  status: ApplePaySession.STATUS_FAILURE,
@@ -324,42 +316,42 @@ function validateContacts(event, sessionData, supportedBillingCountries) {
324
316
  }
325
317
  return void 0;
326
318
  }
327
- function redirectFallback(skuId, returnUrl, options) {
319
+ function redirectFallback(skuId, returnUrl, country, options) {
328
320
  writeCheckoutRedirect(returnUrl);
329
321
  trackFallbackCheckout(skuId);
330
322
  window.location.href = redirectToCheckoutFallback({
331
323
  skuId,
324
+ country,
332
325
  ...options
333
326
  });
334
327
  }
335
328
  var ERROR_HANDLERS = {
336
- EMAIL_COLLISION: ({ skuId, returnUrl, validatedEmail }) => redirectFallback(skuId, returnUrl, {
337
- emailError: MSG_EMAIL_COLLISION,
329
+ // emailError flows as an inline field error on the email input.
330
+ // email pre-populates the input so the user doesn't have to retype it.
331
+ EMAIL_COLLISION: ({ skuId, returnUrl, country, validatedEmail, walletError }) => redirectFallback(skuId, returnUrl, country, {
332
+ emailError: walletError.message,
338
333
  email: validatedEmail
339
334
  }),
340
- PAYMENT_DECLINED: ({ skuId, returnUrl }) => (
341
- // Matches sma-checkout catch-all: always redirect with generic inlineError.
342
- redirectFallback(skuId, returnUrl, { inlineError: MSG_PAYMENT_GENERIC_ERROR })
343
- ),
344
- ELIGIBILITY_REJECTED: ({ skuId, returnUrl }) => redirectFallback(skuId, returnUrl, { inlineError: MSG_ELIGIBILITY }),
345
- REGISTRATION_FAILED: ({ skuId, returnUrl }) => redirectFallback(skuId, returnUrl, { inlineError: MSG_REGISTRATION }),
346
- ACCOUNT_CREATION_FAILED: ({ skuId, returnUrl, validatedEmail }) => redirectFallback(skuId, returnUrl, {
347
- emailError: MSG_REGISTRATION,
335
+ PAYMENT_DECLINED: ({ skuId, returnUrl, country, walletError }) => redirectFallback(skuId, returnUrl, country, { inlineError: walletError.message }),
336
+ ELIGIBILITY_REJECTED: ({ skuId, returnUrl, country, walletError }) => redirectFallback(skuId, returnUrl, country, { inlineError: walletError.message }),
337
+ REGISTRATION_FAILED: ({ skuId, returnUrl, country, walletError }) => redirectFallback(skuId, returnUrl, country, { inlineError: walletError.message }),
338
+ ACCOUNT_CREATION_FAILED: ({ skuId, returnUrl, country, validatedEmail, walletError }) => redirectFallback(skuId, returnUrl, country, {
339
+ emailError: walletError.message,
348
340
  email: validatedEmail
349
341
  }),
350
- VALIDATION_ERROR: ({ skuId, returnUrl }) => redirectFallback(skuId, returnUrl, { inlineError: MSG_RECAPTCHA_VALIDATION_FAILED }),
351
- RECAPTCHA_FAILED: ({ skuId, returnUrl }) => redirectFallback(skuId, returnUrl, { inlineError: MSG_RECAPTCHA_VALIDATION_FAILED })
342
+ VALIDATION_ERROR: ({ skuId, returnUrl, country, walletError }) => redirectFallback(skuId, returnUrl, country, { inlineError: walletError.message }),
343
+ RECAPTCHA_FAILED: ({ skuId, returnUrl, country, walletError }) => redirectFallback(skuId, returnUrl, country, { inlineError: walletError.message })
352
344
  };
353
345
  function handleStructuredError(ctx) {
354
346
  const handler = ERROR_HANDLERS[ctx.walletError.errorType];
355
347
  if (handler) {
356
348
  handler(ctx);
357
349
  } else {
358
- redirectFallback(ctx.skuId, ctx.returnUrl, { inlineError: MSG_PAYMENT_GENERIC_ERROR });
350
+ redirectFallback(ctx.skuId, ctx.returnUrl, ctx.country, { inlineError: MSG_PAYMENT_GENERIC_ERROR });
359
351
  }
360
352
  }
361
353
  async function handlePaymentAuthorized(event, session, sessionData, config) {
362
- const { skuId, returnUrl, queryString } = config;
354
+ const { skuId, returnUrl, queryString, country } = config;
363
355
  let validatedEmail;
364
356
  try {
365
357
  validatedEmail = validateContacts(
@@ -379,13 +371,16 @@ async function handlePaymentAuthorized(event, session, sessionData, config) {
379
371
  });
380
372
  trackWalletPaymentSuccess(skuId, result.basketId);
381
373
  session.completePayment({ status: ApplePaySession.STATUS_SUCCESS });
374
+ const successEmail = sessionData.loggedIn ? sessionData.emailAddress : validatedEmail;
382
375
  const userStatus = sessionData.loggedIn ? "lex" : "nex";
383
376
  writeCheckoutRedirect(returnUrl);
384
377
  window.location.href = redirectToCheckoutComplete({
385
378
  basketId: result.basketId,
386
379
  status: userStatus,
387
380
  skuId: decodeSku(skuId),
388
- queryString
381
+ queryString,
382
+ email: successEmail,
383
+ country
389
384
  });
390
385
  } catch (err) {
391
386
  if (err instanceof ContactValidationError) {
@@ -394,17 +389,13 @@ async function handlePaymentAuthorized(event, session, sessionData, config) {
394
389
  }
395
390
  console.error("[web-apple-pay] onpaymentauthorized error", { skuId, err });
396
391
  const walletError = parseWalletApiError(err);
397
- console.warn("[web-apple-pay] handlePaymentAuthorized: walletError result", {
398
- walletError,
399
- validatedEmail
400
- });
401
392
  trackWalletPaymentFailure(skuId, walletError?.errorType ?? "UNKNOWN");
402
393
  failApplePaySession(session);
403
394
  if (!walletError) {
404
- redirectFallback(skuId, returnUrl, { inlineError: MSG_PAYMENT_GENERIC_ERROR });
395
+ redirectFallback(skuId, returnUrl, country, { inlineError: MSG_PAYMENT_GENERIC_ERROR });
405
396
  return;
406
397
  }
407
- handleStructuredError({ skuId, returnUrl, validatedEmail, walletError });
398
+ handleStructuredError({ skuId, returnUrl, country, validatedEmail, walletError });
408
399
  }
409
400
  }
410
401
  export {
@@ -43,18 +43,11 @@ var apiFetch = async (path, options = {}) => {
43
43
  });
44
44
  if (!response.ok) {
45
45
  const contentType = response.headers.get("content-type") || "";
46
- console.warn("[web-apple-pay] apiFetch: non-2xx response", {
47
- path,
48
- status: response.status,
49
- contentType
50
- });
51
46
  if (contentType.includes("application/json")) {
52
47
  const errorBody = await response.json();
53
- console.warn("[web-apple-pay] apiFetch: parsed JSON error body", { errorBody });
54
48
  throw new ApiError(response.status, errorBody);
55
49
  }
56
50
  const errorText = await response.text();
57
- console.warn("[web-apple-pay] apiFetch: non-JSON error body", { errorText });
58
51
  throw new Error(
59
52
  `API request failed: ${response.status} ${response.statusText} - ${errorText}`
60
53
  );
package/dist/src/index.js CHANGED
@@ -1156,18 +1156,11 @@ var apiFetch = async (path, options = {}) => {
1156
1156
  });
1157
1157
  if (!response.ok) {
1158
1158
  const contentType = response.headers.get("content-type") || "";
1159
- console.warn("[web-apple-pay] apiFetch: non-2xx response", {
1160
- path,
1161
- status: response.status,
1162
- contentType
1163
- });
1164
1159
  if (contentType.includes("application/json")) {
1165
1160
  const errorBody = await response.json();
1166
- console.warn("[web-apple-pay] apiFetch: parsed JSON error body", { errorBody });
1167
1161
  throw new ApiError(response.status, errorBody);
1168
1162
  }
1169
1163
  const errorText = await response.text();
1170
- console.warn("[web-apple-pay] apiFetch: non-JSON error body", { errorText });
1171
1164
  throw new Error(
1172
1165
  `API request failed: ${response.status} ${response.statusText} - ${errorText}`
1173
1166
  );
@@ -1245,6 +1238,8 @@ var redirectToCheckoutComplete = (params) => {
1245
1238
  url.searchParams.set("lmo_offer", params.skuId);
1246
1239
  url.searchParams.set("status", params.status);
1247
1240
  url.searchParams.set("redirectToCheckout", "true");
1241
+ if (params.email) url.searchParams.set("email", params.email);
1242
+ if (params.country) url.searchParams.set("country", params.country);
1248
1243
  return url.pathname + url.search;
1249
1244
  };
1250
1245
  var redirectToCheckoutFallback = (params) => {
@@ -1255,6 +1250,7 @@ var redirectToCheckoutFallback = (params) => {
1255
1250
  url.searchParams.set("inlineError", params.inlineError);
1256
1251
  if (params.email) url.searchParams.set("email", params.email);
1257
1252
  if (params.emailError) url.searchParams.set("emailError", params.emailError);
1253
+ if (params.country) url.searchParams.set("country", params.country);
1258
1254
  return url.pathname + url.search;
1259
1255
  };
1260
1256
 
@@ -1338,8 +1334,6 @@ var MSG_EMAIL_WRONG_FORMAT = "That email doesn\u2019t look right. Please enter a
1338
1334
  var MSG_EMAIL_TOO_LONG = "This field must be 80 characters or less.";
1339
1335
  var MSG_UNSUPPORTED_BILLING_COUNTRY = "Billing country does not match offer price zone";
1340
1336
  var MSG_PAYMENT_GENERIC_ERROR = "Something went wrong. Please check your payment and billing details and try again or contact us.";
1341
- var MSG_RECAPTCHA_VALIDATION_FAILED = "Security check failed. Please try again.";
1342
- var MSG_EMAIL_COLLISION = "This email address is already linked to an account. Please log in, or try a different email address.";
1343
1337
  var MSG_CONTACT_VALIDATION_INTERNAL = "Could not validate Apple Pay contact";
1344
1338
 
1345
1339
  // src/clients/payment-checkout/apple-session.ts
@@ -1348,7 +1342,8 @@ function redirectToFallbackCheckout(config) {
1348
1342
  trackFallbackCheckout(config.skuId);
1349
1343
  window.location.href = redirectToCheckoutFallback({
1350
1344
  skuId: config.skuId,
1351
- inlineError: MSG_PAYMENT_GENERIC_ERROR
1345
+ inlineError: MSG_PAYMENT_GENERIC_ERROR,
1346
+ country: config.countryCode
1352
1347
  });
1353
1348
  }
1354
1349
  async function handleMerchantValidation(event, session, config) {
@@ -1527,8 +1522,6 @@ var shippingContactDetails = (shippingContact) => {
1527
1522
  };
1528
1523
 
1529
1524
  // src/clients/payment-checkout/payment-handler.ts
1530
- var MSG_ELIGIBILITY = "Something went wrong, please try again";
1531
- var MSG_REGISTRATION = "We couldn't create your account. Please enter your email or try a different login method.";
1532
1525
  var failApplePaySession = (session, errors = []) => {
1533
1526
  session.completePayment({
1534
1527
  status: ApplePaySession.STATUS_FAILURE,
@@ -1558,42 +1551,42 @@ function validateContacts(event, sessionData, supportedBillingCountries) {
1558
1551
  }
1559
1552
  return void 0;
1560
1553
  }
1561
- function redirectFallback(skuId, returnUrl, options) {
1554
+ function redirectFallback(skuId, returnUrl, country, options) {
1562
1555
  writeCheckoutRedirect(returnUrl);
1563
1556
  trackFallbackCheckout(skuId);
1564
1557
  window.location.href = redirectToCheckoutFallback({
1565
1558
  skuId,
1559
+ country,
1566
1560
  ...options
1567
1561
  });
1568
1562
  }
1569
1563
  var ERROR_HANDLERS = {
1570
- EMAIL_COLLISION: ({ skuId, returnUrl, validatedEmail }) => redirectFallback(skuId, returnUrl, {
1571
- emailError: MSG_EMAIL_COLLISION,
1564
+ // emailError flows as an inline field error on the email input.
1565
+ // email pre-populates the input so the user doesn't have to retype it.
1566
+ EMAIL_COLLISION: ({ skuId, returnUrl, country, validatedEmail, walletError }) => redirectFallback(skuId, returnUrl, country, {
1567
+ emailError: walletError.message,
1572
1568
  email: validatedEmail
1573
1569
  }),
1574
- PAYMENT_DECLINED: ({ skuId, returnUrl }) => (
1575
- // Matches sma-checkout catch-all: always redirect with generic inlineError.
1576
- redirectFallback(skuId, returnUrl, { inlineError: MSG_PAYMENT_GENERIC_ERROR })
1577
- ),
1578
- ELIGIBILITY_REJECTED: ({ skuId, returnUrl }) => redirectFallback(skuId, returnUrl, { inlineError: MSG_ELIGIBILITY }),
1579
- REGISTRATION_FAILED: ({ skuId, returnUrl }) => redirectFallback(skuId, returnUrl, { inlineError: MSG_REGISTRATION }),
1580
- ACCOUNT_CREATION_FAILED: ({ skuId, returnUrl, validatedEmail }) => redirectFallback(skuId, returnUrl, {
1581
- emailError: MSG_REGISTRATION,
1570
+ PAYMENT_DECLINED: ({ skuId, returnUrl, country, walletError }) => redirectFallback(skuId, returnUrl, country, { inlineError: walletError.message }),
1571
+ ELIGIBILITY_REJECTED: ({ skuId, returnUrl, country, walletError }) => redirectFallback(skuId, returnUrl, country, { inlineError: walletError.message }),
1572
+ REGISTRATION_FAILED: ({ skuId, returnUrl, country, walletError }) => redirectFallback(skuId, returnUrl, country, { inlineError: walletError.message }),
1573
+ ACCOUNT_CREATION_FAILED: ({ skuId, returnUrl, country, validatedEmail, walletError }) => redirectFallback(skuId, returnUrl, country, {
1574
+ emailError: walletError.message,
1582
1575
  email: validatedEmail
1583
1576
  }),
1584
- VALIDATION_ERROR: ({ skuId, returnUrl }) => redirectFallback(skuId, returnUrl, { inlineError: MSG_RECAPTCHA_VALIDATION_FAILED }),
1585
- RECAPTCHA_FAILED: ({ skuId, returnUrl }) => redirectFallback(skuId, returnUrl, { inlineError: MSG_RECAPTCHA_VALIDATION_FAILED })
1577
+ VALIDATION_ERROR: ({ skuId, returnUrl, country, walletError }) => redirectFallback(skuId, returnUrl, country, { inlineError: walletError.message }),
1578
+ RECAPTCHA_FAILED: ({ skuId, returnUrl, country, walletError }) => redirectFallback(skuId, returnUrl, country, { inlineError: walletError.message })
1586
1579
  };
1587
1580
  function handleStructuredError(ctx) {
1588
1581
  const handler = ERROR_HANDLERS[ctx.walletError.errorType];
1589
1582
  if (handler) {
1590
1583
  handler(ctx);
1591
1584
  } else {
1592
- redirectFallback(ctx.skuId, ctx.returnUrl, { inlineError: MSG_PAYMENT_GENERIC_ERROR });
1585
+ redirectFallback(ctx.skuId, ctx.returnUrl, ctx.country, { inlineError: MSG_PAYMENT_GENERIC_ERROR });
1593
1586
  }
1594
1587
  }
1595
1588
  async function handlePaymentAuthorized(event, session, sessionData, config) {
1596
- const { skuId, returnUrl, queryString } = config;
1589
+ const { skuId, returnUrl, queryString, country } = config;
1597
1590
  let validatedEmail;
1598
1591
  try {
1599
1592
  validatedEmail = validateContacts(
@@ -1613,13 +1606,16 @@ async function handlePaymentAuthorized(event, session, sessionData, config) {
1613
1606
  });
1614
1607
  trackWalletPaymentSuccess(skuId, result.basketId);
1615
1608
  session.completePayment({ status: ApplePaySession.STATUS_SUCCESS });
1609
+ const successEmail = sessionData.loggedIn ? sessionData.emailAddress : validatedEmail;
1616
1610
  const userStatus = sessionData.loggedIn ? "lex" : "nex";
1617
1611
  writeCheckoutRedirect(returnUrl);
1618
1612
  window.location.href = redirectToCheckoutComplete({
1619
1613
  basketId: result.basketId,
1620
1614
  status: userStatus,
1621
1615
  skuId: decodeSku(skuId),
1622
- queryString
1616
+ queryString,
1617
+ email: successEmail,
1618
+ country
1623
1619
  });
1624
1620
  } catch (err) {
1625
1621
  if (err instanceof ContactValidationError) {
@@ -1628,17 +1624,13 @@ async function handlePaymentAuthorized(event, session, sessionData, config) {
1628
1624
  }
1629
1625
  console.error("[web-apple-pay] onpaymentauthorized error", { skuId, err });
1630
1626
  const walletError = parseWalletApiError(err);
1631
- console.warn("[web-apple-pay] handlePaymentAuthorized: walletError result", {
1632
- walletError,
1633
- validatedEmail
1634
- });
1635
1627
  trackWalletPaymentFailure(skuId, walletError?.errorType ?? "UNKNOWN");
1636
1628
  failApplePaySession(session);
1637
1629
  if (!walletError) {
1638
- redirectFallback(skuId, returnUrl, { inlineError: MSG_PAYMENT_GENERIC_ERROR });
1630
+ redirectFallback(skuId, returnUrl, country, { inlineError: MSG_PAYMENT_GENERIC_ERROR });
1639
1631
  return;
1640
1632
  }
1641
- handleStructuredError({ skuId, returnUrl, validatedEmail, walletError });
1633
+ handleStructuredError({ skuId, returnUrl, country, validatedEmail, walletError });
1642
1634
  }
1643
1635
  }
1644
1636
 
@@ -43,6 +43,14 @@ export declare const decodeSku: (sku: string) => string;
43
43
  /**
44
44
  * Builds the success redirect URL.
45
45
  * /checkout/complete?basketId={id}&lmo_offer={sku}&status={userStatus}&redirectToCheckout=true
46
+ * &email={email}&country={country}
47
+ *
48
+ * `email` — the user's email address (from session for lex users, from Apple Pay
49
+ * shipping contact for nex users). Always appended when non-empty so checkout
50
+ * can pre-fill the account field.
51
+ *
52
+ * `country` — the offer country code forwarded from the payment handler config.
53
+ * Always appended when non-empty to preserve geo-context across the redirect.
46
54
  *
47
55
  * The origin article URL is written to localStorage by the caller via
48
56
  * writeCheckoutRedirect() before this redirect fires.
@@ -52,15 +60,23 @@ export declare const redirectToCheckoutComplete: (params: {
52
60
  status: "nex" | "lex";
53
61
  skuId: string;
54
62
  queryString?: string;
63
+ email?: string;
64
+ country?: string;
55
65
  }) => string;
56
66
  /**
57
67
  * Builds the fallback redirect URL for error scenarios.
58
- * /checkout?lmo_offer={offerPath}&inlineError=...
68
+ * /checkout?lmo_offer={offerPath}&inlineError=...&email={email}&country={country}
59
69
  *
60
70
  * The skuId is decoded via decodeSku() before being set as lmo_offer, producing
61
71
  * a Limio offer path (e.g. /offers2/UK-UK.DIGITAL.1YAR.X.DISC.30.NYT) as
62
72
  * checkout expects. If decoding fails (malformed SKU), the raw skuId is used.
63
73
  *
74
+ * `email` — always appended when non-empty so checkout can pre-fill the email
75
+ * field on the fallback page (e.g. for EMAIL_COLLISION flows).
76
+ *
77
+ * `country` — the offer country code, always appended when non-empty to preserve
78
+ * geo-context across the redirect.
79
+ *
64
80
  * Builds on top of the current page URL (window.location.href) so that any
65
81
  * existing query params (UTM tags, campaign flags) are preserved — matching
66
82
  * sma-checkout: src/lib/navigate-to-checkout/index.tsx
@@ -73,4 +89,5 @@ export declare const redirectToCheckoutFallback: (params: {
73
89
  inlineError?: string;
74
90
  emailError?: string;
75
91
  email?: string;
92
+ country?: string;
76
93
  }) => string;
@@ -39,6 +39,8 @@ var redirectToCheckoutComplete = (params) => {
39
39
  url.searchParams.set("lmo_offer", params.skuId);
40
40
  url.searchParams.set("status", params.status);
41
41
  url.searchParams.set("redirectToCheckout", "true");
42
+ if (params.email) url.searchParams.set("email", params.email);
43
+ if (params.country) url.searchParams.set("country", params.country);
42
44
  return url.pathname + url.search;
43
45
  };
44
46
  var redirectToCheckoutFallback = (params) => {
@@ -49,6 +51,7 @@ var redirectToCheckoutFallback = (params) => {
49
51
  url.searchParams.set("inlineError", params.inlineError);
50
52
  if (params.email) url.searchParams.set("email", params.email);
51
53
  if (params.emailError) url.searchParams.set("emailError", params.emailError);
54
+ if (params.country) url.searchParams.set("country", params.country);
52
55
  return url.pathname + url.search;
53
56
  };
54
57
  export {
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": "0.1.3-beta.22",
4
+ "version": "0.1.3-beta.24",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
7
7
  "types": "dist/index.d.ts",