@economist/web-apple-pay 0.1.3-beta.30 → 0.1.3-beta.32

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
@@ -1145,7 +1145,7 @@ var isApplePayAvailable = async () => {
1145
1145
  }
1146
1146
  const hasActiveCard = await applePaySession.canMakePaymentsWithActiveCard(merchantId);
1147
1147
  return hasActiveCard;
1148
- } catch (error) {
1148
+ } catch {
1149
1149
  return false;
1150
1150
  }
1151
1151
  };
@@ -1233,13 +1233,25 @@ var getExpressCheckoutSession = () => {
1233
1233
  };
1234
1234
 
1235
1235
  // src/utils/expressCheckoutRedirect.ts
1236
- var captureOriginUrl = () => window.location.href;
1237
- var buildQueryString = () => {
1238
- const countryCode = new URLSearchParams(window.location.search).get(
1239
- "country"
1240
- );
1241
- return countryCode ? `&country=${encodeURIComponent(countryCode)}` : "";
1236
+ var CHECKOUT_REDIRECT_KEY = "checkout-redirect";
1237
+ var ALLOWED_REDIRECT_HOSTNAME = "economist.com";
1238
+ function isAllowedRedirectUrl(url) {
1239
+ if (url.startsWith("/")) return !url.startsWith("//");
1240
+ try {
1241
+ const { hostname } = new URL(url);
1242
+ return hostname === ALLOWED_REDIRECT_HOSTNAME || hostname.endsWith(`.${ALLOWED_REDIRECT_HOSTNAME}`);
1243
+ } catch {
1244
+ return false;
1245
+ }
1246
+ }
1247
+ var writeCheckoutRedirect = (originUrl) => {
1248
+ if (!isAllowedRedirectUrl(originUrl)) return;
1249
+ try {
1250
+ localStorage.setItem(CHECKOUT_REDIRECT_KEY, originUrl);
1251
+ } catch {
1252
+ }
1242
1253
  };
1254
+ var captureOriginUrl = () => window.location.href;
1243
1255
  var decodeSku = (sku) => {
1244
1256
  try {
1245
1257
  return atob(sku.substring(sku.indexOf("-") + 1));
@@ -1250,14 +1262,11 @@ var decodeSku = (sku) => {
1250
1262
  var redirectToCheckoutComplete = (params) => {
1251
1263
  const url = new URL("/checkout/complete", window.location.origin);
1252
1264
  url.searchParams.set("basketId", params.basketId);
1253
- if (params.queryString) {
1254
- const qs = params.queryString.startsWith("?") ? params.queryString.slice(1) : params.queryString;
1255
- new URLSearchParams(qs).forEach((v, k) => url.searchParams.set(k, v));
1256
- }
1257
1265
  url.searchParams.set("lmo_offer", params.skuId);
1258
1266
  url.searchParams.set("status", params.status);
1259
1267
  url.searchParams.set("redirectToCheckout", "true");
1260
- url.searchParams.set("returnUrl", params.returnUrl);
1268
+ if (params.email) url.searchParams.set("email", params.email);
1269
+ if (params.country) url.searchParams.set("country", params.country);
1261
1270
  return url.pathname + url.search;
1262
1271
  };
1263
1272
  var redirectToCheckoutFallback = (params) => {
@@ -1268,7 +1277,7 @@ var redirectToCheckoutFallback = (params) => {
1268
1277
  url.searchParams.set("inlineError", params.inlineError);
1269
1278
  if (params.email) url.searchParams.set("email", params.email);
1270
1279
  if (params.emailError) url.searchParams.set("emailError", params.emailError);
1271
- url.searchParams.set("returnUrl", params.returnUrl);
1280
+ if (params.country) url.searchParams.set("country", params.country);
1272
1281
  return url.pathname + url.search;
1273
1282
  };
1274
1283
 
@@ -1352,17 +1361,16 @@ var MSG_EMAIL_WRONG_FORMAT = "That email doesn\u2019t look right. Please enter a
1352
1361
  var MSG_EMAIL_TOO_LONG = "This field must be 80 characters or less.";
1353
1362
  var MSG_UNSUPPORTED_BILLING_COUNTRY = "Billing country does not match offer price zone";
1354
1363
  var MSG_PAYMENT_GENERIC_ERROR = "Something went wrong. Please check your payment and billing details and try again or contact us.";
1355
- var MSG_RECAPTCHA_VALIDATION_FAILED = "Security check failed. Please try again.";
1356
- var MSG_EMAIL_COLLISION = "This email address is already linked to an account. Please log in, or try a different email address.";
1357
1364
  var MSG_CONTACT_VALIDATION_INTERNAL = "Could not validate Apple Pay contact";
1358
1365
 
1359
1366
  // src/clients/payment-checkout/apple-session.ts
1360
1367
  function redirectToFallbackCheckout(config) {
1368
+ writeCheckoutRedirect(config.returnUrl);
1361
1369
  trackFallbackCheckout(config.skuId);
1362
1370
  window.location.href = redirectToCheckoutFallback({
1363
1371
  skuId: config.skuId,
1364
- returnUrl: config.returnUrl,
1365
- inlineError: MSG_PAYMENT_GENERIC_ERROR
1372
+ inlineError: MSG_PAYMENT_GENERIC_ERROR,
1373
+ country: config.countryCode
1366
1374
  });
1367
1375
  }
1368
1376
  async function handleMerchantValidation(event, session, config) {
@@ -1384,7 +1392,7 @@ async function handleMerchantValidation(event, session, config) {
1384
1392
  return;
1385
1393
  }
1386
1394
  session.completeMerchantValidation(response.merchantSession);
1387
- } catch (err) {
1395
+ } catch {
1388
1396
  session.abort();
1389
1397
  redirectToFallbackCheckout(config);
1390
1398
  }
@@ -1417,7 +1425,7 @@ function startApplePaySession(config) {
1417
1425
  };
1418
1426
  trackWalletPaymentSheetOpen(config.skuId);
1419
1427
  session.begin();
1420
- } catch (err) {
1428
+ } catch {
1421
1429
  redirectToFallbackCheckout(config);
1422
1430
  }
1423
1431
  }
@@ -1539,8 +1547,6 @@ var shippingContactDetails = (shippingContact) => {
1539
1547
  };
1540
1548
 
1541
1549
  // src/clients/payment-checkout/payment-handler.ts
1542
- var MSG_ELIGIBILITY = "Something went wrong, please try again";
1543
- var MSG_REGISTRATION = "We couldn't create your account. Please enter your email or try a different login method.";
1544
1550
  var failApplePaySession = (session, errors = []) => {
1545
1551
  session.completePayment({
1546
1552
  status: ApplePaySession.STATUS_FAILURE,
@@ -1564,37 +1570,42 @@ function validateContacts(event, sessionData, supportedBillingCountries) {
1564
1570
  }
1565
1571
  return void 0;
1566
1572
  }
1567
- function redirectFallback(skuId, returnUrl, options) {
1573
+ function redirectFallback(skuId, returnUrl, country, options) {
1574
+ writeCheckoutRedirect(returnUrl);
1568
1575
  trackFallbackCheckout(skuId);
1569
1576
  window.location.href = redirectToCheckoutFallback({
1570
1577
  skuId,
1571
- returnUrl,
1578
+ country,
1572
1579
  ...options
1573
1580
  });
1574
1581
  }
1575
1582
  var ERROR_HANDLERS = {
1576
- EMAIL_COLLISION: ({ skuId, returnUrl, validatedEmail }) => redirectFallback(skuId, returnUrl, {
1577
- emailError: MSG_EMAIL_COLLISION,
1583
+ // emailError flows as an inline field error on the email input.
1584
+ // email pre-populates the input so the user doesn't have to retype it.
1585
+ EMAIL_COLLISION: ({ skuId, returnUrl, country, validatedEmail, walletError }) => redirectFallback(skuId, returnUrl, country, {
1586
+ emailError: walletError.message,
1578
1587
  email: validatedEmail
1579
1588
  }),
1580
- PAYMENT_DECLINED: ({ skuId, returnUrl }) => (
1581
- // Matches sma-checkout catch-all: always redirect with generic inlineError.
1582
- redirectFallback(skuId, returnUrl, { inlineError: MSG_PAYMENT_GENERIC_ERROR })
1583
- ),
1584
- ELIGIBILITY_REJECTED: ({ skuId, returnUrl }) => redirectFallback(skuId, returnUrl, { inlineError: MSG_ELIGIBILITY }),
1585
- REGISTRATION_FAILED: ({ skuId, returnUrl }) => redirectFallback(skuId, returnUrl, { inlineError: MSG_REGISTRATION }),
1586
- VALIDATION_ERROR: ({ skuId, returnUrl }) => redirectFallback(skuId, returnUrl, { inlineError: MSG_RECAPTCHA_VALIDATION_FAILED })
1589
+ PAYMENT_DECLINED: ({ skuId, returnUrl, country, walletError }) => redirectFallback(skuId, returnUrl, country, { inlineError: walletError.message }),
1590
+ ELIGIBILITY_REJECTED: ({ skuId, returnUrl, country, walletError }) => redirectFallback(skuId, returnUrl, country, { inlineError: walletError.message }),
1591
+ REGISTRATION_FAILED: ({ skuId, returnUrl, country, walletError }) => redirectFallback(skuId, returnUrl, country, { inlineError: walletError.message }),
1592
+ ACCOUNT_CREATION_FAILED: ({ skuId, returnUrl, country, validatedEmail, walletError }) => redirectFallback(skuId, returnUrl, country, {
1593
+ emailError: walletError.message,
1594
+ email: validatedEmail
1595
+ }),
1596
+ VALIDATION_ERROR: ({ skuId, returnUrl, country, walletError }) => redirectFallback(skuId, returnUrl, country, { inlineError: walletError.message }),
1597
+ RECAPTCHA_FAILED: ({ skuId, returnUrl, country, walletError }) => redirectFallback(skuId, returnUrl, country, { inlineError: walletError.message })
1587
1598
  };
1588
1599
  function handleStructuredError(ctx) {
1589
1600
  const handler = ERROR_HANDLERS[ctx.walletError.errorType];
1590
1601
  if (handler) {
1591
1602
  handler(ctx);
1592
1603
  } else {
1593
- redirectFallback(ctx.skuId, ctx.returnUrl, { inlineError: MSG_PAYMENT_GENERIC_ERROR });
1604
+ redirectFallback(ctx.skuId, ctx.returnUrl, ctx.country, { inlineError: MSG_PAYMENT_GENERIC_ERROR });
1594
1605
  }
1595
1606
  }
1596
1607
  async function handlePaymentAuthorized(event, session, sessionData, config) {
1597
- const { skuId, returnUrl, queryString } = config;
1608
+ const { skuId, returnUrl, country } = config;
1598
1609
  let validatedEmail;
1599
1610
  try {
1600
1611
  validatedEmail = validateContacts(
@@ -1614,13 +1625,15 @@ async function handlePaymentAuthorized(event, session, sessionData, config) {
1614
1625
  });
1615
1626
  trackWalletPaymentSuccess(skuId, result.basketId);
1616
1627
  session.completePayment({ status: ApplePaySession.STATUS_SUCCESS });
1628
+ const successEmail = sessionData.loggedIn ? sessionData.emailAddress : validatedEmail;
1617
1629
  const userStatus = sessionData.loggedIn ? "lex" : "nex";
1630
+ writeCheckoutRedirect(returnUrl);
1618
1631
  window.location.href = redirectToCheckoutComplete({
1619
1632
  basketId: result.basketId,
1620
1633
  status: userStatus,
1621
1634
  skuId: decodeSku(skuId),
1622
- returnUrl,
1623
- queryString
1635
+ email: successEmail,
1636
+ country
1624
1637
  });
1625
1638
  } catch (err) {
1626
1639
  if (err instanceof ContactValidationError) {
@@ -1631,10 +1644,10 @@ async function handlePaymentAuthorized(event, session, sessionData, config) {
1631
1644
  trackWalletPaymentFailure(skuId, walletError?.errorType ?? "UNKNOWN");
1632
1645
  failApplePaySession(session);
1633
1646
  if (!walletError) {
1634
- redirectFallback(skuId, returnUrl, { inlineError: MSG_PAYMENT_GENERIC_ERROR });
1647
+ redirectFallback(skuId, returnUrl, country, { inlineError: MSG_PAYMENT_GENERIC_ERROR });
1635
1648
  return;
1636
1649
  }
1637
- handleStructuredError({ skuId, returnUrl, validatedEmail, walletError });
1650
+ handleStructuredError({ skuId, returnUrl, country, validatedEmail, walletError });
1638
1651
  }
1639
1652
  }
1640
1653
 
@@ -2027,18 +2040,203 @@ var ApplePayButton = class _ApplePayButton extends HTMLElement {
2027
2040
  // ─── Render pipeline ────────────────────────────────────────────────────────
2028
2041
  async #run() {
2029
2042
  try {
2030
- if (await this.#checkAvailability()) return;
2031
- if (this.#checkOffer()) return;
2043
+ console.log(
2044
+ "[ApplePayButton] #run start",
2045
+ JSON.stringify(
2046
+ {
2047
+ country: this.#country,
2048
+ offer_sku: this.#offer?.sku_id,
2049
+ entry_point: this.#entryPoint
2050
+ },
2051
+ null,
2052
+ 2
2053
+ )
2054
+ );
2032
2055
  this.#renderTemplate();
2033
- if (await this.#checkSession()) return;
2056
+ console.log(
2057
+ "[ApplePayButton] #renderTemplate done",
2058
+ JSON.stringify(
2059
+ {
2060
+ country: this.#country,
2061
+ offer_sku: this.#offer?.sku_id
2062
+ },
2063
+ null,
2064
+ 2
2065
+ )
2066
+ );
2067
+ const availabilityBlocked = await this.#checkAvailability();
2068
+ console.log(
2069
+ "[ApplePayButton] #checkAvailability result",
2070
+ JSON.stringify(
2071
+ {
2072
+ blocked: availabilityBlocked,
2073
+ country: this.#country,
2074
+ offer_sku: this.#offer?.sku_id
2075
+ },
2076
+ null,
2077
+ 2
2078
+ )
2079
+ );
2080
+ if (availabilityBlocked) {
2081
+ console.log(
2082
+ "[ApplePayButton] #run early return after #checkAvailability",
2083
+ JSON.stringify(
2084
+ {
2085
+ country: this.#country,
2086
+ offer_sku: this.#offer?.sku_id
2087
+ },
2088
+ null,
2089
+ 2
2090
+ )
2091
+ );
2092
+ return;
2093
+ }
2094
+ const offerMissing = this.#checkOffer();
2095
+ console.log(
2096
+ "[ApplePayButton] #checkOffer result",
2097
+ JSON.stringify(
2098
+ {
2099
+ blocked: offerMissing,
2100
+ country: this.#country,
2101
+ offer_sku: this.#offer?.sku_id
2102
+ },
2103
+ null,
2104
+ 2
2105
+ )
2106
+ );
2107
+ if (offerMissing) {
2108
+ console.log(
2109
+ "[ApplePayButton] #run early return after #checkOffer",
2110
+ JSON.stringify(
2111
+ {
2112
+ country: this.#country,
2113
+ offer_sku: this.#offer?.sku_id
2114
+ },
2115
+ null,
2116
+ 2
2117
+ )
2118
+ );
2119
+ return;
2120
+ }
2121
+ const sessionBlocked = await this.#checkSession();
2122
+ console.log(
2123
+ "[ApplePayButton] #checkSession result",
2124
+ JSON.stringify(
2125
+ {
2126
+ blocked: sessionBlocked,
2127
+ country: this.#country,
2128
+ offer_sku: this.#offer?.sku_id,
2129
+ session_user_type: this.#sessionData?.userType,
2130
+ session_logged_in: this.#sessionData?.loggedIn
2131
+ },
2132
+ null,
2133
+ 2
2134
+ )
2135
+ );
2136
+ if (sessionBlocked) {
2137
+ console.log(
2138
+ "[ApplePayButton] #run early return after #checkSession",
2139
+ JSON.stringify(
2140
+ {
2141
+ country: this.#country,
2142
+ offer_sku: this.#offer?.sku_id
2143
+ },
2144
+ null,
2145
+ 2
2146
+ )
2147
+ );
2148
+ return;
2149
+ }
2034
2150
  this.#captureReturnUrl();
2151
+ console.log(
2152
+ "[ApplePayButton] #captureReturnUrl done",
2153
+ JSON.stringify(
2154
+ {
2155
+ return_url: this.#returnUrl
2156
+ },
2157
+ null,
2158
+ 2
2159
+ )
2160
+ );
2035
2161
  this.#computeBillingCountries();
2036
- if (await this.#fetchAndCacheOptions()) return;
2162
+ console.log(
2163
+ "[ApplePayButton] #computeBillingCountries done",
2164
+ JSON.stringify(
2165
+ {
2166
+ supported_billing_countries: this.#supportedBillingCountries
2167
+ },
2168
+ null,
2169
+ 2
2170
+ )
2171
+ );
2172
+ const optionsBlocked = await this.#fetchAndCacheOptions();
2173
+ console.log(
2174
+ "[ApplePayButton] #fetchAndCacheOptions result",
2175
+ JSON.stringify(
2176
+ {
2177
+ blocked: optionsBlocked,
2178
+ has_apple_pay_option: Boolean(this.#paymentOptions?.options?.apple_pay)
2179
+ },
2180
+ null,
2181
+ 2
2182
+ )
2183
+ );
2184
+ if (optionsBlocked) {
2185
+ console.log(
2186
+ "[ApplePayButton] #run early return after #fetchAndCacheOptions",
2187
+ JSON.stringify(
2188
+ {
2189
+ country: this.#country,
2190
+ offer_sku: this.#offer?.sku_id
2191
+ },
2192
+ null,
2193
+ 2
2194
+ )
2195
+ );
2196
+ return;
2197
+ }
2037
2198
  this.#finishLoading();
2038
- } catch (err) {
2199
+ console.log(
2200
+ "[ApplePayButton] #finishLoading done",
2201
+ JSON.stringify(
2202
+ {
2203
+ country: this.#country,
2204
+ offer_sku: this.#offer?.sku_id
2205
+ },
2206
+ null,
2207
+ 2
2208
+ )
2209
+ );
2210
+ } catch (error) {
2211
+ console.log(
2212
+ "[ApplePayButton] #run failed",
2213
+ JSON.stringify(
2214
+ {
2215
+ country: this.#country,
2216
+ offer_sku: this.#offer?.sku_id,
2217
+ error: error instanceof Error ? {
2218
+ name: error.name,
2219
+ message: error.message,
2220
+ stack: error.stack
2221
+ } : error
2222
+ },
2223
+ null,
2224
+ 2
2225
+ )
2226
+ );
2039
2227
  this.style.display = "none";
2040
2228
  this.querySelector(SEL_CONTAINER)?.remove();
2041
2229
  } finally {
2230
+ console.log(
2231
+ "[ApplePayButton] #run finally",
2232
+ JSON.stringify(
2233
+ {
2234
+ rendering: this.#rendering
2235
+ },
2236
+ null,
2237
+ 2
2238
+ )
2239
+ );
2042
2240
  this.#rendering = false;
2043
2241
  }
2044
2242
  }
@@ -2047,6 +2245,7 @@ var ApplePayButton = class _ApplePayButton extends HTMLElement {
2047
2245
  const available = await isApplePayAvailable();
2048
2246
  if (!available) {
2049
2247
  this.style.display = "none";
2248
+ this.querySelector(SEL_CONTAINER)?.remove();
2050
2249
  return true;
2051
2250
  }
2052
2251
  return false;
@@ -2068,6 +2267,7 @@ var ApplePayButton = class _ApplePayButton extends HTMLElement {
2068
2267
  #checkOffer() {
2069
2268
  if (!this.#offer) {
2070
2269
  this.style.display = "none";
2270
+ this.querySelector(SEL_CONTAINER)?.remove();
2071
2271
  return true;
2072
2272
  }
2073
2273
  return false;
@@ -2174,7 +2374,7 @@ var ApplePayButton = class _ApplePayButton extends HTMLElement {
2174
2374
  */
2175
2375
  #scheduleRecaptchaTokens() {
2176
2376
  const attempt = getPurchaseRecaptchaTokens();
2177
- attempt.catch((err) => {
2377
+ attempt.catch(() => {
2178
2378
  if (this.#recaptchaTokensPromise === attempt) {
2179
2379
  this.#recaptchaTokensPromise = null;
2180
2380
  }
@@ -2217,7 +2417,6 @@ var ApplePayButton = class _ApplePayButton extends HTMLElement {
2217
2417
  country,
2218
2418
  currency: offer.price?.currency_code ?? DEFAULT_CURRENCY,
2219
2419
  returnUrl: this.#returnUrl,
2220
- queryString: buildQueryString(),
2221
2420
  supportedBillingCountries: this.#supportedBillingCountries,
2222
2421
  recaptchaTokens
2223
2422
  })
@@ -2236,7 +2435,7 @@ var ApplePayButton = class _ApplePayButton extends HTMLElement {
2236
2435
  recaptchaTokens
2237
2436
  )
2238
2437
  );
2239
- } catch (error) {
2438
+ } catch {
2240
2439
  }
2241
2440
  }
2242
2441
  };