@economist/web-apple-pay 0.1.3-beta.37 → 0.1.3-beta.39

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
@@ -1120,52 +1120,32 @@ var isAppleDeviceOrSafari = () => {
1120
1120
  const isSafariBrowser = navigator.userAgent.indexOf("Safari") > -1 && navigator.userAgent.indexOf("Chrome") === -1;
1121
1121
  return isIOSOrMacTouchDevice || isSafariBrowser;
1122
1122
  };
1123
- var bypassApplePayChecks = () => new URLSearchParams(window.location.search).get("BYPASS_APPLE_PAY_CHECKS") !== null;
1123
+ var bypassApplePayChecks = () => new URLSearchParams(window.location.search).get("BYPASS_APPLE_PAY_CHECKS") !== null && getApplePayConfig()?.debugBypassEnabled === true;
1124
1124
  var isApplePayAvailable = async () => {
1125
1125
  if (bypassApplePayChecks()) {
1126
- console.log("[ApplePay] bypass active \u2014 displaying button");
1127
1126
  return true;
1128
1127
  }
1129
- const applePaySession = window.ApplePaySession;
1130
- const isAppleDevice = isAppleDeviceOrSafari();
1131
- console.log("[ApplePay] detection:", JSON.stringify({
1132
- applePaySessionExists: !!applePaySession,
1133
- isAppleDevice,
1134
- userAgent: navigator.userAgent,
1135
- maxTouchPoints: navigator.maxTouchPoints
1136
- }, null, 2));
1137
- if (!applePaySession && !isAppleDevice) {
1138
- console.log("[ApplePay] not available \u2014 no ApplePaySession and not Apple device");
1128
+ if (!isAppleDeviceOrSafari()) {
1139
1129
  return false;
1140
1130
  }
1131
+ const applePaySession = window.ApplePaySession;
1141
1132
  if (!applePaySession) {
1142
- console.log("[ApplePay] not available \u2014 Apple device but no ApplePaySession API");
1143
1133
  return false;
1144
1134
  }
1145
1135
  try {
1146
1136
  if (typeof applePaySession.canMakePayments !== "function") {
1147
- console.log("[ApplePay] not available \u2014 canMakePayments is not a function");
1148
1137
  return false;
1149
1138
  }
1150
1139
  if (!applePaySession.canMakePayments()) {
1151
- console.log("[ApplePay] not available \u2014 canMakePayments returned false");
1152
1140
  return false;
1153
1141
  }
1154
1142
  const merchantId = getApplePayConfig()?.merchantId;
1155
1143
  if (!merchantId) {
1156
- console.log("[ApplePay] not available \u2014 no merchantId configured");
1157
1144
  return false;
1158
1145
  }
1159
1146
  const hasActiveCard = await applePaySession.canMakePaymentsWithActiveCard(merchantId);
1160
- console.log("[ApplePay] canMakePaymentsWithActiveCard:", JSON.stringify({ merchantId, hasActiveCard }, null, 2));
1161
- if (!hasActiveCard) {
1162
- console.log("[ApplePay] not available \u2014 no active card for merchant");
1163
- return false;
1164
- }
1165
- console.log("[ApplePay] available \u2014 displaying button");
1166
- return true;
1167
- } catch (error) {
1168
- console.log("[ApplePay] not available \u2014 error:", JSON.stringify({ error: String(error) }, null, 2));
1147
+ return hasActiveCard;
1148
+ } catch {
1169
1149
  return false;
1170
1150
  }
1171
1151
  };
@@ -1338,17 +1318,21 @@ var track = (action, extra) => {
1338
1318
  ...extra
1339
1319
  });
1340
1320
  };
1341
- var trackExpressWalletImpression = (skuId, { entryPoint, country }) => {
1321
+ var trackExpressWalletImpression = (skuId, { entryPoint, country }, buttonLoadDurationMs) => {
1342
1322
  track(EXPRESS_CHECKOUT_EVENTS.IMPRESSION, {
1343
1323
  wallet_type: "apple_pay",
1344
1324
  entry_point: entryPoint,
1345
1325
  sku_id: skuId,
1346
1326
  country,
1347
- device: getDevice()
1327
+ device: getDevice(),
1328
+ ...buttonLoadDurationMs !== void 0 && { button_load_duration_ms: buttonLoadDurationMs }
1348
1329
  });
1349
1330
  };
1350
- var trackExpressWalletClick = (skuId) => {
1351
- track(EXPRESS_CHECKOUT_EVENTS.BUTTON_CLICK, { sku_id: skuId });
1331
+ var trackExpressWalletClick = (skuId, modalRenderDurationMs) => {
1332
+ track(EXPRESS_CHECKOUT_EVENTS.BUTTON_CLICK, {
1333
+ sku_id: skuId,
1334
+ ...modalRenderDurationMs !== void 0 && { modal_render_duration_ms: modalRenderDurationMs }
1335
+ });
1352
1336
  };
1353
1337
  var trackWalletPaymentSheetOpen = (skuId) => {
1354
1338
  track(EXPRESS_CHECKOUT_EVENTS.SHEET_OPEN, { sku_id: skuId });
@@ -1356,16 +1340,18 @@ var trackWalletPaymentSheetOpen = (skuId) => {
1356
1340
  var trackWalletPaymentAuthorised = (skuId) => {
1357
1341
  track(EXPRESS_CHECKOUT_EVENTS.AUTHORISED, { sku_id: skuId });
1358
1342
  };
1359
- var trackWalletPaymentSuccess = (skuId, basketId) => {
1343
+ var trackWalletPaymentSuccess = (skuId, basketId, purchaseDurationMs) => {
1360
1344
  track(EXPRESS_CHECKOUT_EVENTS.SUCCESS, {
1361
1345
  sku_id: skuId,
1362
- basket_id: basketId
1346
+ basket_id: basketId,
1347
+ ...purchaseDurationMs !== void 0 && { purchase_duration_ms: purchaseDurationMs }
1363
1348
  });
1364
1349
  };
1365
- var trackWalletPaymentFailure = (skuId, reason) => {
1350
+ var trackWalletPaymentFailure = (skuId, reason, purchaseDurationMs) => {
1366
1351
  track(EXPRESS_CHECKOUT_EVENTS.FAILURE, {
1367
1352
  sku_id: skuId,
1368
- failure_reason: reason
1353
+ failure_reason: reason,
1354
+ ...purchaseDurationMs !== void 0 && { purchase_duration_ms: purchaseDurationMs }
1369
1355
  });
1370
1356
  };
1371
1357
  var trackFallbackCheckout = (skuId) => {
@@ -1627,12 +1613,14 @@ function handleStructuredError(ctx) {
1627
1613
  async function handlePaymentAuthorized(event, session, sessionData, config) {
1628
1614
  const { skuId, returnUrl, country } = config;
1629
1615
  let validatedEmail;
1616
+ let purchaseStartTime;
1630
1617
  try {
1631
1618
  validatedEmail = validateContacts(
1632
1619
  event,
1633
1620
  sessionData,
1634
1621
  config.supportedBillingCountries
1635
1622
  );
1623
+ purchaseStartTime = Date.now();
1636
1624
  const result = await performPurchase({
1637
1625
  applePayToken: event.payment.token,
1638
1626
  skuId: config.skuId,
@@ -1643,7 +1631,8 @@ async function handlePaymentAuthorized(event, session, sessionData, config) {
1643
1631
  },
1644
1632
  recaptchaTokens: config.recaptchaTokens
1645
1633
  });
1646
- trackWalletPaymentSuccess(skuId, result.basketId);
1634
+ const purchaseDurationMs = Date.now() - purchaseStartTime;
1635
+ trackWalletPaymentSuccess(skuId, result.basketId, purchaseDurationMs);
1647
1636
  session.completePayment({ status: ApplePaySession.STATUS_SUCCESS });
1648
1637
  const successEmail = sessionData.loggedIn ? sessionData.emailAddress : validatedEmail;
1649
1638
  const userStatus = sessionData.loggedIn ? "lex" : "nex";
@@ -1661,7 +1650,8 @@ async function handlePaymentAuthorized(event, session, sessionData, config) {
1661
1650
  return;
1662
1651
  }
1663
1652
  const walletError = parseWalletApiError(err);
1664
- trackWalletPaymentFailure(skuId, walletError?.errorType ?? "UNKNOWN");
1653
+ const purchaseDurationMs = purchaseStartTime !== void 0 ? Date.now() - purchaseStartTime : void 0;
1654
+ trackWalletPaymentFailure(skuId, walletError?.errorType ?? "UNKNOWN", purchaseDurationMs);
1665
1655
  failApplePaySession(session);
1666
1656
  if (!walletError) {
1667
1657
  redirectFallback(skuId, returnUrl, country, { inlineError: MSG_PAYMENT_GENERIC_ERROR });
@@ -1984,6 +1974,7 @@ var ApplePayButton = class _ApplePayButton extends HTMLElement {
1984
1974
  #country = "";
1985
1975
  #rendering = false;
1986
1976
  #readyPromise = null;
1977
+ #loadStartTime = 0;
1987
1978
  #sessionData = null;
1988
1979
  #paymentOptions = null;
1989
1980
  #returnUrl = "";
@@ -2059,6 +2050,7 @@ var ApplePayButton = class _ApplePayButton extends HTMLElement {
2059
2050
  }
2060
2051
  // ─── Render pipeline ────────────────────────────────────────────────────────
2061
2052
  async #run() {
2053
+ this.#loadStartTime = Date.now();
2062
2054
  try {
2063
2055
  this.#renderTemplate();
2064
2056
  if (await this.#checkAvailability()) return;
@@ -2158,10 +2150,12 @@ var ApplePayButton = class _ApplePayButton extends HTMLElement {
2158
2150
  button.classList.remove("wap-loading");
2159
2151
  this.#updateDisabledState();
2160
2152
  this.#scheduleRecaptchaTokens();
2161
- trackExpressWalletImpression(this.#offer.sku_id, {
2162
- entryPoint: this.#entryPoint,
2163
- country: this.#country
2164
- });
2153
+ const buttonLoadDurationMs = Date.now() - this.#loadStartTime;
2154
+ trackExpressWalletImpression(
2155
+ this.#offer.sku_id,
2156
+ { entryPoint: this.#entryPoint, country: this.#country },
2157
+ buttonLoadDurationMs
2158
+ );
2165
2159
  }
2166
2160
  }
2167
2161
  // ─── Private helpers ────────────────────────────────────────────────────────
@@ -2184,7 +2178,7 @@ var ApplePayButton = class _ApplePayButton extends HTMLElement {
2184
2178
  if (document.querySelector(ApplePayModal.tag)) {
2185
2179
  return;
2186
2180
  }
2187
- trackExpressWalletClick(this.#offer.sku_id);
2181
+ const modalOpenStart = Date.now();
2188
2182
  ApplePayModal.define();
2189
2183
  const modal = document.createElement(ApplePayModal.tag);
2190
2184
  modal.offer = this.#offer;
@@ -2199,6 +2193,8 @@ var ApplePayButton = class _ApplePayButton extends HTMLElement {
2199
2193
  this.#scheduleRecaptchaTokens();
2200
2194
  };
2201
2195
  modal.open(this.#recaptchaTokensPromise);
2196
+ const modalRenderDurationMs = Date.now() - modalOpenStart;
2197
+ trackExpressWalletClick(skuId, modalRenderDurationMs);
2202
2198
  }
2203
2199
  /**
2204
2200
  * Starts a fresh reCAPTCHA token pre-fetch and stores its promise.
@@ -1115,52 +1115,32 @@ var isAppleDeviceOrSafari = () => {
1115
1115
  const isSafariBrowser = navigator.userAgent.indexOf("Safari") > -1 && navigator.userAgent.indexOf("Chrome") === -1;
1116
1116
  return isIOSOrMacTouchDevice || isSafariBrowser;
1117
1117
  };
1118
- var bypassApplePayChecks = () => new URLSearchParams(window.location.search).get("BYPASS_APPLE_PAY_CHECKS") !== null;
1118
+ var bypassApplePayChecks = () => new URLSearchParams(window.location.search).get("BYPASS_APPLE_PAY_CHECKS") !== null && getApplePayConfig()?.debugBypassEnabled === true;
1119
1119
  var isApplePayAvailable = async () => {
1120
1120
  if (bypassApplePayChecks()) {
1121
- console.log("[ApplePay] bypass active \u2014 displaying button");
1122
1121
  return true;
1123
1122
  }
1124
- const applePaySession = window.ApplePaySession;
1125
- const isAppleDevice = isAppleDeviceOrSafari();
1126
- console.log("[ApplePay] detection:", JSON.stringify({
1127
- applePaySessionExists: !!applePaySession,
1128
- isAppleDevice,
1129
- userAgent: navigator.userAgent,
1130
- maxTouchPoints: navigator.maxTouchPoints
1131
- }, null, 2));
1132
- if (!applePaySession && !isAppleDevice) {
1133
- console.log("[ApplePay] not available \u2014 no ApplePaySession and not Apple device");
1123
+ if (!isAppleDeviceOrSafari()) {
1134
1124
  return false;
1135
1125
  }
1126
+ const applePaySession = window.ApplePaySession;
1136
1127
  if (!applePaySession) {
1137
- console.log("[ApplePay] not available \u2014 Apple device but no ApplePaySession API");
1138
1128
  return false;
1139
1129
  }
1140
1130
  try {
1141
1131
  if (typeof applePaySession.canMakePayments !== "function") {
1142
- console.log("[ApplePay] not available \u2014 canMakePayments is not a function");
1143
1132
  return false;
1144
1133
  }
1145
1134
  if (!applePaySession.canMakePayments()) {
1146
- console.log("[ApplePay] not available \u2014 canMakePayments returned false");
1147
1135
  return false;
1148
1136
  }
1149
1137
  const merchantId = getApplePayConfig()?.merchantId;
1150
1138
  if (!merchantId) {
1151
- console.log("[ApplePay] not available \u2014 no merchantId configured");
1152
1139
  return false;
1153
1140
  }
1154
1141
  const hasActiveCard = await applePaySession.canMakePaymentsWithActiveCard(merchantId);
1155
- console.log("[ApplePay] canMakePaymentsWithActiveCard:", JSON.stringify({ merchantId, hasActiveCard }, null, 2));
1156
- if (!hasActiveCard) {
1157
- console.log("[ApplePay] not available \u2014 no active card for merchant");
1158
- return false;
1159
- }
1160
- console.log("[ApplePay] available \u2014 displaying button");
1161
- return true;
1162
- } catch (error) {
1163
- console.log("[ApplePay] not available \u2014 error:", JSON.stringify({ error: String(error) }, null, 2));
1142
+ return hasActiveCard;
1143
+ } catch {
1164
1144
  return false;
1165
1145
  }
1166
1146
  };
@@ -1333,17 +1313,21 @@ var track = (action, extra) => {
1333
1313
  ...extra
1334
1314
  });
1335
1315
  };
1336
- var trackExpressWalletImpression = (skuId, { entryPoint, country }) => {
1316
+ var trackExpressWalletImpression = (skuId, { entryPoint, country }, buttonLoadDurationMs) => {
1337
1317
  track(EXPRESS_CHECKOUT_EVENTS.IMPRESSION, {
1338
1318
  wallet_type: "apple_pay",
1339
1319
  entry_point: entryPoint,
1340
1320
  sku_id: skuId,
1341
1321
  country,
1342
- device: getDevice()
1322
+ device: getDevice(),
1323
+ ...buttonLoadDurationMs !== void 0 && { button_load_duration_ms: buttonLoadDurationMs }
1343
1324
  });
1344
1325
  };
1345
- var trackExpressWalletClick = (skuId) => {
1346
- track(EXPRESS_CHECKOUT_EVENTS.BUTTON_CLICK, { sku_id: skuId });
1326
+ var trackExpressWalletClick = (skuId, modalRenderDurationMs) => {
1327
+ track(EXPRESS_CHECKOUT_EVENTS.BUTTON_CLICK, {
1328
+ sku_id: skuId,
1329
+ ...modalRenderDurationMs !== void 0 && { modal_render_duration_ms: modalRenderDurationMs }
1330
+ });
1347
1331
  };
1348
1332
  var trackWalletPaymentSheetOpen = (skuId) => {
1349
1333
  track(EXPRESS_CHECKOUT_EVENTS.SHEET_OPEN, { sku_id: skuId });
@@ -1351,16 +1335,18 @@ var trackWalletPaymentSheetOpen = (skuId) => {
1351
1335
  var trackWalletPaymentAuthorised = (skuId) => {
1352
1336
  track(EXPRESS_CHECKOUT_EVENTS.AUTHORISED, { sku_id: skuId });
1353
1337
  };
1354
- var trackWalletPaymentSuccess = (skuId, basketId) => {
1338
+ var trackWalletPaymentSuccess = (skuId, basketId, purchaseDurationMs) => {
1355
1339
  track(EXPRESS_CHECKOUT_EVENTS.SUCCESS, {
1356
1340
  sku_id: skuId,
1357
- basket_id: basketId
1341
+ basket_id: basketId,
1342
+ ...purchaseDurationMs !== void 0 && { purchase_duration_ms: purchaseDurationMs }
1358
1343
  });
1359
1344
  };
1360
- var trackWalletPaymentFailure = (skuId, reason) => {
1345
+ var trackWalletPaymentFailure = (skuId, reason, purchaseDurationMs) => {
1361
1346
  track(EXPRESS_CHECKOUT_EVENTS.FAILURE, {
1362
1347
  sku_id: skuId,
1363
- failure_reason: reason
1348
+ failure_reason: reason,
1349
+ ...purchaseDurationMs !== void 0 && { purchase_duration_ms: purchaseDurationMs }
1364
1350
  });
1365
1351
  };
1366
1352
  var trackFallbackCheckout = (skuId) => {
@@ -1622,12 +1608,14 @@ function handleStructuredError(ctx) {
1622
1608
  async function handlePaymentAuthorized(event, session, sessionData, config) {
1623
1609
  const { skuId, returnUrl, country } = config;
1624
1610
  let validatedEmail;
1611
+ let purchaseStartTime;
1625
1612
  try {
1626
1613
  validatedEmail = validateContacts(
1627
1614
  event,
1628
1615
  sessionData,
1629
1616
  config.supportedBillingCountries
1630
1617
  );
1618
+ purchaseStartTime = Date.now();
1631
1619
  const result = await performPurchase({
1632
1620
  applePayToken: event.payment.token,
1633
1621
  skuId: config.skuId,
@@ -1638,7 +1626,8 @@ async function handlePaymentAuthorized(event, session, sessionData, config) {
1638
1626
  },
1639
1627
  recaptchaTokens: config.recaptchaTokens
1640
1628
  });
1641
- trackWalletPaymentSuccess(skuId, result.basketId);
1629
+ const purchaseDurationMs = Date.now() - purchaseStartTime;
1630
+ trackWalletPaymentSuccess(skuId, result.basketId, purchaseDurationMs);
1642
1631
  session.completePayment({ status: ApplePaySession.STATUS_SUCCESS });
1643
1632
  const successEmail = sessionData.loggedIn ? sessionData.emailAddress : validatedEmail;
1644
1633
  const userStatus = sessionData.loggedIn ? "lex" : "nex";
@@ -1656,7 +1645,8 @@ async function handlePaymentAuthorized(event, session, sessionData, config) {
1656
1645
  return;
1657
1646
  }
1658
1647
  const walletError = parseWalletApiError(err);
1659
- trackWalletPaymentFailure(skuId, walletError?.errorType ?? "UNKNOWN");
1648
+ const purchaseDurationMs = purchaseStartTime !== void 0 ? Date.now() - purchaseStartTime : void 0;
1649
+ trackWalletPaymentFailure(skuId, walletError?.errorType ?? "UNKNOWN", purchaseDurationMs);
1660
1650
  failApplePaySession(session);
1661
1651
  if (!walletError) {
1662
1652
  redirectFallback(skuId, returnUrl, country, { inlineError: MSG_PAYMENT_GENERIC_ERROR });
@@ -1979,6 +1969,7 @@ var ApplePayButton = class _ApplePayButton extends HTMLElement {
1979
1969
  #country = "";
1980
1970
  #rendering = false;
1981
1971
  #readyPromise = null;
1972
+ #loadStartTime = 0;
1982
1973
  #sessionData = null;
1983
1974
  #paymentOptions = null;
1984
1975
  #returnUrl = "";
@@ -2054,6 +2045,7 @@ var ApplePayButton = class _ApplePayButton extends HTMLElement {
2054
2045
  }
2055
2046
  // ─── Render pipeline ────────────────────────────────────────────────────────
2056
2047
  async #run() {
2048
+ this.#loadStartTime = Date.now();
2057
2049
  try {
2058
2050
  this.#renderTemplate();
2059
2051
  if (await this.#checkAvailability()) return;
@@ -2153,10 +2145,12 @@ var ApplePayButton = class _ApplePayButton extends HTMLElement {
2153
2145
  button.classList.remove("wap-loading");
2154
2146
  this.#updateDisabledState();
2155
2147
  this.#scheduleRecaptchaTokens();
2156
- trackExpressWalletImpression(this.#offer.sku_id, {
2157
- entryPoint: this.#entryPoint,
2158
- country: this.#country
2159
- });
2148
+ const buttonLoadDurationMs = Date.now() - this.#loadStartTime;
2149
+ trackExpressWalletImpression(
2150
+ this.#offer.sku_id,
2151
+ { entryPoint: this.#entryPoint, country: this.#country },
2152
+ buttonLoadDurationMs
2153
+ );
2160
2154
  }
2161
2155
  }
2162
2156
  // ─── Private helpers ────────────────────────────────────────────────────────
@@ -2179,7 +2173,7 @@ var ApplePayButton = class _ApplePayButton extends HTMLElement {
2179
2173
  if (document.querySelector(ApplePayModal.tag)) {
2180
2174
  return;
2181
2175
  }
2182
- trackExpressWalletClick(this.#offer.sku_id);
2176
+ const modalOpenStart = Date.now();
2183
2177
  ApplePayModal.define();
2184
2178
  const modal = document.createElement(ApplePayModal.tag);
2185
2179
  modal.offer = this.#offer;
@@ -2194,6 +2188,8 @@ var ApplePayButton = class _ApplePayButton extends HTMLElement {
2194
2188
  this.#scheduleRecaptchaTokens();
2195
2189
  };
2196
2190
  modal.open(this.#recaptchaTokensPromise);
2191
+ const modalRenderDurationMs = Date.now() - modalOpenStart;
2192
+ trackExpressWalletClick(skuId, modalRenderDurationMs);
2197
2193
  }
2198
2194
  /**
2199
2195
  * Starts a fresh reCAPTCHA token pre-fetch and stores its promise.
@@ -1123,52 +1123,32 @@ var isAppleDeviceOrSafari = () => {
1123
1123
  const isSafariBrowser = navigator.userAgent.indexOf("Safari") > -1 && navigator.userAgent.indexOf("Chrome") === -1;
1124
1124
  return isIOSOrMacTouchDevice || isSafariBrowser;
1125
1125
  };
1126
- var bypassApplePayChecks = () => new URLSearchParams(window.location.search).get("BYPASS_APPLE_PAY_CHECKS") !== null;
1126
+ var bypassApplePayChecks = () => new URLSearchParams(window.location.search).get("BYPASS_APPLE_PAY_CHECKS") !== null && getApplePayConfig()?.debugBypassEnabled === true;
1127
1127
  var isApplePayAvailable = async () => {
1128
1128
  if (bypassApplePayChecks()) {
1129
- console.log("[ApplePay] bypass active \u2014 displaying button");
1130
1129
  return true;
1131
1130
  }
1132
- const applePaySession = window.ApplePaySession;
1133
- const isAppleDevice = isAppleDeviceOrSafari();
1134
- console.log("[ApplePay] detection:", JSON.stringify({
1135
- applePaySessionExists: !!applePaySession,
1136
- isAppleDevice,
1137
- userAgent: navigator.userAgent,
1138
- maxTouchPoints: navigator.maxTouchPoints
1139
- }, null, 2));
1140
- if (!applePaySession && !isAppleDevice) {
1141
- console.log("[ApplePay] not available \u2014 no ApplePaySession and not Apple device");
1131
+ if (!isAppleDeviceOrSafari()) {
1142
1132
  return false;
1143
1133
  }
1134
+ const applePaySession = window.ApplePaySession;
1144
1135
  if (!applePaySession) {
1145
- console.log("[ApplePay] not available \u2014 Apple device but no ApplePaySession API");
1146
1136
  return false;
1147
1137
  }
1148
1138
  try {
1149
1139
  if (typeof applePaySession.canMakePayments !== "function") {
1150
- console.log("[ApplePay] not available \u2014 canMakePayments is not a function");
1151
1140
  return false;
1152
1141
  }
1153
1142
  if (!applePaySession.canMakePayments()) {
1154
- console.log("[ApplePay] not available \u2014 canMakePayments returned false");
1155
1143
  return false;
1156
1144
  }
1157
1145
  const merchantId = getApplePayConfig()?.merchantId;
1158
1146
  if (!merchantId) {
1159
- console.log("[ApplePay] not available \u2014 no merchantId configured");
1160
1147
  return false;
1161
1148
  }
1162
1149
  const hasActiveCard = await applePaySession.canMakePaymentsWithActiveCard(merchantId);
1163
- console.log("[ApplePay] canMakePaymentsWithActiveCard:", JSON.stringify({ merchantId, hasActiveCard }, null, 2));
1164
- if (!hasActiveCard) {
1165
- console.log("[ApplePay] not available \u2014 no active card for merchant");
1166
- return false;
1167
- }
1168
- console.log("[ApplePay] available \u2014 displaying button");
1169
- return true;
1170
- } catch (error) {
1171
- console.log("[ApplePay] not available \u2014 error:", JSON.stringify({ error: String(error) }, null, 2));
1150
+ return hasActiveCard;
1151
+ } catch {
1172
1152
  return false;
1173
1153
  }
1174
1154
  };
@@ -1341,17 +1321,21 @@ var track = (action, extra) => {
1341
1321
  ...extra
1342
1322
  });
1343
1323
  };
1344
- var trackExpressWalletImpression = (skuId, { entryPoint, country }) => {
1324
+ var trackExpressWalletImpression = (skuId, { entryPoint, country }, buttonLoadDurationMs) => {
1345
1325
  track(EXPRESS_CHECKOUT_EVENTS.IMPRESSION, {
1346
1326
  wallet_type: "apple_pay",
1347
1327
  entry_point: entryPoint,
1348
1328
  sku_id: skuId,
1349
1329
  country,
1350
- device: getDevice()
1330
+ device: getDevice(),
1331
+ ...buttonLoadDurationMs !== void 0 && { button_load_duration_ms: buttonLoadDurationMs }
1351
1332
  });
1352
1333
  };
1353
- var trackExpressWalletClick = (skuId) => {
1354
- track(EXPRESS_CHECKOUT_EVENTS.BUTTON_CLICK, { sku_id: skuId });
1334
+ var trackExpressWalletClick = (skuId, modalRenderDurationMs) => {
1335
+ track(EXPRESS_CHECKOUT_EVENTS.BUTTON_CLICK, {
1336
+ sku_id: skuId,
1337
+ ...modalRenderDurationMs !== void 0 && { modal_render_duration_ms: modalRenderDurationMs }
1338
+ });
1355
1339
  };
1356
1340
  var trackWalletPaymentSheetOpen = (skuId) => {
1357
1341
  track(EXPRESS_CHECKOUT_EVENTS.SHEET_OPEN, { sku_id: skuId });
@@ -1359,16 +1343,18 @@ var trackWalletPaymentSheetOpen = (skuId) => {
1359
1343
  var trackWalletPaymentAuthorised = (skuId) => {
1360
1344
  track(EXPRESS_CHECKOUT_EVENTS.AUTHORISED, { sku_id: skuId });
1361
1345
  };
1362
- var trackWalletPaymentSuccess = (skuId, basketId) => {
1346
+ var trackWalletPaymentSuccess = (skuId, basketId, purchaseDurationMs) => {
1363
1347
  track(EXPRESS_CHECKOUT_EVENTS.SUCCESS, {
1364
1348
  sku_id: skuId,
1365
- basket_id: basketId
1349
+ basket_id: basketId,
1350
+ ...purchaseDurationMs !== void 0 && { purchase_duration_ms: purchaseDurationMs }
1366
1351
  });
1367
1352
  };
1368
- var trackWalletPaymentFailure = (skuId, reason) => {
1353
+ var trackWalletPaymentFailure = (skuId, reason, purchaseDurationMs) => {
1369
1354
  track(EXPRESS_CHECKOUT_EVENTS.FAILURE, {
1370
1355
  sku_id: skuId,
1371
- failure_reason: reason
1356
+ failure_reason: reason,
1357
+ ...purchaseDurationMs !== void 0 && { purchase_duration_ms: purchaseDurationMs }
1372
1358
  });
1373
1359
  };
1374
1360
  var trackFallbackCheckout = (skuId) => {
@@ -1630,12 +1616,14 @@ function handleStructuredError(ctx) {
1630
1616
  async function handlePaymentAuthorized(event, session, sessionData, config) {
1631
1617
  const { skuId, returnUrl, country } = config;
1632
1618
  let validatedEmail;
1619
+ let purchaseStartTime;
1633
1620
  try {
1634
1621
  validatedEmail = validateContacts(
1635
1622
  event,
1636
1623
  sessionData,
1637
1624
  config.supportedBillingCountries
1638
1625
  );
1626
+ purchaseStartTime = Date.now();
1639
1627
  const result = await performPurchase({
1640
1628
  applePayToken: event.payment.token,
1641
1629
  skuId: config.skuId,
@@ -1646,7 +1634,8 @@ async function handlePaymentAuthorized(event, session, sessionData, config) {
1646
1634
  },
1647
1635
  recaptchaTokens: config.recaptchaTokens
1648
1636
  });
1649
- trackWalletPaymentSuccess(skuId, result.basketId);
1637
+ const purchaseDurationMs = Date.now() - purchaseStartTime;
1638
+ trackWalletPaymentSuccess(skuId, result.basketId, purchaseDurationMs);
1650
1639
  session.completePayment({ status: ApplePaySession.STATUS_SUCCESS });
1651
1640
  const successEmail = sessionData.loggedIn ? sessionData.emailAddress : validatedEmail;
1652
1641
  const userStatus = sessionData.loggedIn ? "lex" : "nex";
@@ -1664,7 +1653,8 @@ async function handlePaymentAuthorized(event, session, sessionData, config) {
1664
1653
  return;
1665
1654
  }
1666
1655
  const walletError = parseWalletApiError(err);
1667
- trackWalletPaymentFailure(skuId, walletError?.errorType ?? "UNKNOWN");
1656
+ const purchaseDurationMs = purchaseStartTime !== void 0 ? Date.now() - purchaseStartTime : void 0;
1657
+ trackWalletPaymentFailure(skuId, walletError?.errorType ?? "UNKNOWN", purchaseDurationMs);
1668
1658
  failApplePaySession(session);
1669
1659
  if (!walletError) {
1670
1660
  redirectFallback(skuId, returnUrl, country, { inlineError: MSG_PAYMENT_GENERIC_ERROR });
@@ -1987,6 +1977,7 @@ var ApplePayButton = class _ApplePayButton extends HTMLElement {
1987
1977
  #country = "";
1988
1978
  #rendering = false;
1989
1979
  #readyPromise = null;
1980
+ #loadStartTime = 0;
1990
1981
  #sessionData = null;
1991
1982
  #paymentOptions = null;
1992
1983
  #returnUrl = "";
@@ -2062,6 +2053,7 @@ var ApplePayButton = class _ApplePayButton extends HTMLElement {
2062
2053
  }
2063
2054
  // ─── Render pipeline ────────────────────────────────────────────────────────
2064
2055
  async #run() {
2056
+ this.#loadStartTime = Date.now();
2065
2057
  try {
2066
2058
  this.#renderTemplate();
2067
2059
  if (await this.#checkAvailability()) return;
@@ -2161,10 +2153,12 @@ var ApplePayButton = class _ApplePayButton extends HTMLElement {
2161
2153
  button.classList.remove("wap-loading");
2162
2154
  this.#updateDisabledState();
2163
2155
  this.#scheduleRecaptchaTokens();
2164
- trackExpressWalletImpression(this.#offer.sku_id, {
2165
- entryPoint: this.#entryPoint,
2166
- country: this.#country
2167
- });
2156
+ const buttonLoadDurationMs = Date.now() - this.#loadStartTime;
2157
+ trackExpressWalletImpression(
2158
+ this.#offer.sku_id,
2159
+ { entryPoint: this.#entryPoint, country: this.#country },
2160
+ buttonLoadDurationMs
2161
+ );
2168
2162
  }
2169
2163
  }
2170
2164
  // ─── Private helpers ────────────────────────────────────────────────────────
@@ -2187,7 +2181,7 @@ var ApplePayButton = class _ApplePayButton extends HTMLElement {
2187
2181
  if (document.querySelector(ApplePayModal.tag)) {
2188
2182
  return;
2189
2183
  }
2190
- trackExpressWalletClick(this.#offer.sku_id);
2184
+ const modalOpenStart = Date.now();
2191
2185
  ApplePayModal.define();
2192
2186
  const modal = document.createElement(ApplePayModal.tag);
2193
2187
  modal.offer = this.#offer;
@@ -2202,6 +2196,8 @@ var ApplePayButton = class _ApplePayButton extends HTMLElement {
2202
2196
  this.#scheduleRecaptchaTokens();
2203
2197
  };
2204
2198
  modal.open(this.#recaptchaTokensPromise);
2199
+ const modalRenderDurationMs = Date.now() - modalOpenStart;
2200
+ trackExpressWalletClick(skuId, modalRenderDurationMs);
2205
2201
  }
2206
2202
  /**
2207
2203
  * Starts a fresh reCAPTCHA token pre-fetch and stores its promise.
@@ -1115,52 +1115,32 @@ var isAppleDeviceOrSafari = () => {
1115
1115
  const isSafariBrowser = navigator.userAgent.indexOf("Safari") > -1 && navigator.userAgent.indexOf("Chrome") === -1;
1116
1116
  return isIOSOrMacTouchDevice || isSafariBrowser;
1117
1117
  };
1118
- var bypassApplePayChecks = () => new URLSearchParams(window.location.search).get("BYPASS_APPLE_PAY_CHECKS") !== null;
1118
+ var bypassApplePayChecks = () => new URLSearchParams(window.location.search).get("BYPASS_APPLE_PAY_CHECKS") !== null && getApplePayConfig()?.debugBypassEnabled === true;
1119
1119
  var isApplePayAvailable = async () => {
1120
1120
  if (bypassApplePayChecks()) {
1121
- console.log("[ApplePay] bypass active \u2014 displaying button");
1122
1121
  return true;
1123
1122
  }
1124
- const applePaySession = window.ApplePaySession;
1125
- const isAppleDevice = isAppleDeviceOrSafari();
1126
- console.log("[ApplePay] detection:", JSON.stringify({
1127
- applePaySessionExists: !!applePaySession,
1128
- isAppleDevice,
1129
- userAgent: navigator.userAgent,
1130
- maxTouchPoints: navigator.maxTouchPoints
1131
- }, null, 2));
1132
- if (!applePaySession && !isAppleDevice) {
1133
- console.log("[ApplePay] not available \u2014 no ApplePaySession and not Apple device");
1123
+ if (!isAppleDeviceOrSafari()) {
1134
1124
  return false;
1135
1125
  }
1126
+ const applePaySession = window.ApplePaySession;
1136
1127
  if (!applePaySession) {
1137
- console.log("[ApplePay] not available \u2014 Apple device but no ApplePaySession API");
1138
1128
  return false;
1139
1129
  }
1140
1130
  try {
1141
1131
  if (typeof applePaySession.canMakePayments !== "function") {
1142
- console.log("[ApplePay] not available \u2014 canMakePayments is not a function");
1143
1132
  return false;
1144
1133
  }
1145
1134
  if (!applePaySession.canMakePayments()) {
1146
- console.log("[ApplePay] not available \u2014 canMakePayments returned false");
1147
1135
  return false;
1148
1136
  }
1149
1137
  const merchantId = getApplePayConfig()?.merchantId;
1150
1138
  if (!merchantId) {
1151
- console.log("[ApplePay] not available \u2014 no merchantId configured");
1152
1139
  return false;
1153
1140
  }
1154
1141
  const hasActiveCard = await applePaySession.canMakePaymentsWithActiveCard(merchantId);
1155
- console.log("[ApplePay] canMakePaymentsWithActiveCard:", JSON.stringify({ merchantId, hasActiveCard }, null, 2));
1156
- if (!hasActiveCard) {
1157
- console.log("[ApplePay] not available \u2014 no active card for merchant");
1158
- return false;
1159
- }
1160
- console.log("[ApplePay] available \u2014 displaying button");
1161
- return true;
1162
- } catch (error) {
1163
- console.log("[ApplePay] not available \u2014 error:", JSON.stringify({ error: String(error) }, null, 2));
1142
+ return hasActiveCard;
1143
+ } catch {
1164
1144
  return false;
1165
1145
  }
1166
1146
  };
@@ -1333,17 +1313,21 @@ var track = (action, extra) => {
1333
1313
  ...extra
1334
1314
  });
1335
1315
  };
1336
- var trackExpressWalletImpression = (skuId, { entryPoint, country }) => {
1316
+ var trackExpressWalletImpression = (skuId, { entryPoint, country }, buttonLoadDurationMs) => {
1337
1317
  track(EXPRESS_CHECKOUT_EVENTS.IMPRESSION, {
1338
1318
  wallet_type: "apple_pay",
1339
1319
  entry_point: entryPoint,
1340
1320
  sku_id: skuId,
1341
1321
  country,
1342
- device: getDevice()
1322
+ device: getDevice(),
1323
+ ...buttonLoadDurationMs !== void 0 && { button_load_duration_ms: buttonLoadDurationMs }
1343
1324
  });
1344
1325
  };
1345
- var trackExpressWalletClick = (skuId) => {
1346
- track(EXPRESS_CHECKOUT_EVENTS.BUTTON_CLICK, { sku_id: skuId });
1326
+ var trackExpressWalletClick = (skuId, modalRenderDurationMs) => {
1327
+ track(EXPRESS_CHECKOUT_EVENTS.BUTTON_CLICK, {
1328
+ sku_id: skuId,
1329
+ ...modalRenderDurationMs !== void 0 && { modal_render_duration_ms: modalRenderDurationMs }
1330
+ });
1347
1331
  };
1348
1332
  var trackWalletPaymentSheetOpen = (skuId) => {
1349
1333
  track(EXPRESS_CHECKOUT_EVENTS.SHEET_OPEN, { sku_id: skuId });
@@ -1351,16 +1335,18 @@ var trackWalletPaymentSheetOpen = (skuId) => {
1351
1335
  var trackWalletPaymentAuthorised = (skuId) => {
1352
1336
  track(EXPRESS_CHECKOUT_EVENTS.AUTHORISED, { sku_id: skuId });
1353
1337
  };
1354
- var trackWalletPaymentSuccess = (skuId, basketId) => {
1338
+ var trackWalletPaymentSuccess = (skuId, basketId, purchaseDurationMs) => {
1355
1339
  track(EXPRESS_CHECKOUT_EVENTS.SUCCESS, {
1356
1340
  sku_id: skuId,
1357
- basket_id: basketId
1341
+ basket_id: basketId,
1342
+ ...purchaseDurationMs !== void 0 && { purchase_duration_ms: purchaseDurationMs }
1358
1343
  });
1359
1344
  };
1360
- var trackWalletPaymentFailure = (skuId, reason) => {
1345
+ var trackWalletPaymentFailure = (skuId, reason, purchaseDurationMs) => {
1361
1346
  track(EXPRESS_CHECKOUT_EVENTS.FAILURE, {
1362
1347
  sku_id: skuId,
1363
- failure_reason: reason
1348
+ failure_reason: reason,
1349
+ ...purchaseDurationMs !== void 0 && { purchase_duration_ms: purchaseDurationMs }
1364
1350
  });
1365
1351
  };
1366
1352
  var trackFallbackCheckout = (skuId) => {
@@ -1622,12 +1608,14 @@ function handleStructuredError(ctx) {
1622
1608
  async function handlePaymentAuthorized(event, session, sessionData, config) {
1623
1609
  const { skuId, returnUrl, country } = config;
1624
1610
  let validatedEmail;
1611
+ let purchaseStartTime;
1625
1612
  try {
1626
1613
  validatedEmail = validateContacts(
1627
1614
  event,
1628
1615
  sessionData,
1629
1616
  config.supportedBillingCountries
1630
1617
  );
1618
+ purchaseStartTime = Date.now();
1631
1619
  const result = await performPurchase({
1632
1620
  applePayToken: event.payment.token,
1633
1621
  skuId: config.skuId,
@@ -1638,7 +1626,8 @@ async function handlePaymentAuthorized(event, session, sessionData, config) {
1638
1626
  },
1639
1627
  recaptchaTokens: config.recaptchaTokens
1640
1628
  });
1641
- trackWalletPaymentSuccess(skuId, result.basketId);
1629
+ const purchaseDurationMs = Date.now() - purchaseStartTime;
1630
+ trackWalletPaymentSuccess(skuId, result.basketId, purchaseDurationMs);
1642
1631
  session.completePayment({ status: ApplePaySession.STATUS_SUCCESS });
1643
1632
  const successEmail = sessionData.loggedIn ? sessionData.emailAddress : validatedEmail;
1644
1633
  const userStatus = sessionData.loggedIn ? "lex" : "nex";
@@ -1656,7 +1645,8 @@ async function handlePaymentAuthorized(event, session, sessionData, config) {
1656
1645
  return;
1657
1646
  }
1658
1647
  const walletError = parseWalletApiError(err);
1659
- trackWalletPaymentFailure(skuId, walletError?.errorType ?? "UNKNOWN");
1648
+ const purchaseDurationMs = purchaseStartTime !== void 0 ? Date.now() - purchaseStartTime : void 0;
1649
+ trackWalletPaymentFailure(skuId, walletError?.errorType ?? "UNKNOWN", purchaseDurationMs);
1660
1650
  failApplePaySession(session);
1661
1651
  if (!walletError) {
1662
1652
  redirectFallback(skuId, returnUrl, country, { inlineError: MSG_PAYMENT_GENERIC_ERROR });
@@ -1979,6 +1969,7 @@ var ApplePayButton = class _ApplePayButton extends HTMLElement {
1979
1969
  #country = "";
1980
1970
  #rendering = false;
1981
1971
  #readyPromise = null;
1972
+ #loadStartTime = 0;
1982
1973
  #sessionData = null;
1983
1974
  #paymentOptions = null;
1984
1975
  #returnUrl = "";
@@ -2054,6 +2045,7 @@ var ApplePayButton = class _ApplePayButton extends HTMLElement {
2054
2045
  }
2055
2046
  // ─── Render pipeline ────────────────────────────────────────────────────────
2056
2047
  async #run() {
2048
+ this.#loadStartTime = Date.now();
2057
2049
  try {
2058
2050
  this.#renderTemplate();
2059
2051
  if (await this.#checkAvailability()) return;
@@ -2153,10 +2145,12 @@ var ApplePayButton = class _ApplePayButton extends HTMLElement {
2153
2145
  button.classList.remove("wap-loading");
2154
2146
  this.#updateDisabledState();
2155
2147
  this.#scheduleRecaptchaTokens();
2156
- trackExpressWalletImpression(this.#offer.sku_id, {
2157
- entryPoint: this.#entryPoint,
2158
- country: this.#country
2159
- });
2148
+ const buttonLoadDurationMs = Date.now() - this.#loadStartTime;
2149
+ trackExpressWalletImpression(
2150
+ this.#offer.sku_id,
2151
+ { entryPoint: this.#entryPoint, country: this.#country },
2152
+ buttonLoadDurationMs
2153
+ );
2160
2154
  }
2161
2155
  }
2162
2156
  // ─── Private helpers ────────────────────────────────────────────────────────
@@ -2179,7 +2173,7 @@ var ApplePayButton = class _ApplePayButton extends HTMLElement {
2179
2173
  if (document.querySelector(ApplePayModal.tag)) {
2180
2174
  return;
2181
2175
  }
2182
- trackExpressWalletClick(this.#offer.sku_id);
2176
+ const modalOpenStart = Date.now();
2183
2177
  ApplePayModal.define();
2184
2178
  const modal = document.createElement(ApplePayModal.tag);
2185
2179
  modal.offer = this.#offer;
@@ -2194,6 +2188,8 @@ var ApplePayButton = class _ApplePayButton extends HTMLElement {
2194
2188
  this.#scheduleRecaptchaTokens();
2195
2189
  };
2196
2190
  modal.open(this.#recaptchaTokensPromise);
2191
+ const modalRenderDurationMs = Date.now() - modalOpenStart;
2192
+ trackExpressWalletClick(skuId, modalRenderDurationMs);
2197
2193
  }
2198
2194
  /**
2199
2195
  * Starts a fresh reCAPTCHA token pre-fetch and stores its promise.
@@ -8,52 +8,32 @@ var isAppleDeviceOrSafari = () => {
8
8
  const isSafariBrowser = navigator.userAgent.indexOf("Safari") > -1 && navigator.userAgent.indexOf("Chrome") === -1;
9
9
  return isIOSOrMacTouchDevice || isSafariBrowser;
10
10
  };
11
- var bypassApplePayChecks = () => new URLSearchParams(window.location.search).get("BYPASS_APPLE_PAY_CHECKS") !== null;
11
+ var bypassApplePayChecks = () => new URLSearchParams(window.location.search).get("BYPASS_APPLE_PAY_CHECKS") !== null && getApplePayConfig()?.debugBypassEnabled === true;
12
12
  var isApplePayAvailable = async () => {
13
13
  if (bypassApplePayChecks()) {
14
- console.log("[ApplePay] bypass active \u2014 displaying button");
15
14
  return true;
16
15
  }
17
- const applePaySession = window.ApplePaySession;
18
- const isAppleDevice = isAppleDeviceOrSafari();
19
- console.log("[ApplePay] detection:", JSON.stringify({
20
- applePaySessionExists: !!applePaySession,
21
- isAppleDevice,
22
- userAgent: navigator.userAgent,
23
- maxTouchPoints: navigator.maxTouchPoints
24
- }, null, 2));
25
- if (!applePaySession && !isAppleDevice) {
26
- console.log("[ApplePay] not available \u2014 no ApplePaySession and not Apple device");
16
+ if (!isAppleDeviceOrSafari()) {
27
17
  return false;
28
18
  }
19
+ const applePaySession = window.ApplePaySession;
29
20
  if (!applePaySession) {
30
- console.log("[ApplePay] not available \u2014 Apple device but no ApplePaySession API");
31
21
  return false;
32
22
  }
33
23
  try {
34
24
  if (typeof applePaySession.canMakePayments !== "function") {
35
- console.log("[ApplePay] not available \u2014 canMakePayments is not a function");
36
25
  return false;
37
26
  }
38
27
  if (!applePaySession.canMakePayments()) {
39
- console.log("[ApplePay] not available \u2014 canMakePayments returned false");
40
28
  return false;
41
29
  }
42
30
  const merchantId = getApplePayConfig()?.merchantId;
43
31
  if (!merchantId) {
44
- console.log("[ApplePay] not available \u2014 no merchantId configured");
45
32
  return false;
46
33
  }
47
34
  const hasActiveCard = await applePaySession.canMakePaymentsWithActiveCard(merchantId);
48
- console.log("[ApplePay] canMakePaymentsWithActiveCard:", JSON.stringify({ merchantId, hasActiveCard }, null, 2));
49
- if (!hasActiveCard) {
50
- console.log("[ApplePay] not available \u2014 no active card for merchant");
51
- return false;
52
- }
53
- console.log("[ApplePay] available \u2014 displaying button");
54
- return true;
55
- } catch (error) {
56
- console.log("[ApplePay] not available \u2014 error:", JSON.stringify({ error: String(error) }, null, 2));
35
+ return hasActiveCard;
36
+ } catch {
57
37
  return false;
58
38
  }
59
39
  };
@@ -93,16 +93,18 @@ var track = (action, extra) => {
93
93
  ...extra
94
94
  });
95
95
  };
96
- var trackWalletPaymentSuccess = (skuId, basketId) => {
96
+ var trackWalletPaymentSuccess = (skuId, basketId, purchaseDurationMs) => {
97
97
  track(EXPRESS_CHECKOUT_EVENTS.SUCCESS, {
98
98
  sku_id: skuId,
99
- basket_id: basketId
99
+ basket_id: basketId,
100
+ ...purchaseDurationMs !== void 0 && { purchase_duration_ms: purchaseDurationMs }
100
101
  });
101
102
  };
102
- var trackWalletPaymentFailure = (skuId, reason) => {
103
+ var trackWalletPaymentFailure = (skuId, reason, purchaseDurationMs) => {
103
104
  track(EXPRESS_CHECKOUT_EVENTS.FAILURE, {
104
105
  sku_id: skuId,
105
- failure_reason: reason
106
+ failure_reason: reason,
107
+ ...purchaseDurationMs !== void 0 && { purchase_duration_ms: purchaseDurationMs }
106
108
  });
107
109
  };
108
110
  var trackFallbackCheckout = (skuId) => {
@@ -345,12 +347,14 @@ function handleStructuredError(ctx) {
345
347
  async function handlePaymentAuthorized(event, session, sessionData, config) {
346
348
  const { skuId, returnUrl, country } = config;
347
349
  let validatedEmail;
350
+ let purchaseStartTime;
348
351
  try {
349
352
  validatedEmail = validateContacts(
350
353
  event,
351
354
  sessionData,
352
355
  config.supportedBillingCountries
353
356
  );
357
+ purchaseStartTime = Date.now();
354
358
  const result = await performPurchase({
355
359
  applePayToken: event.payment.token,
356
360
  skuId: config.skuId,
@@ -361,7 +365,8 @@ async function handlePaymentAuthorized(event, session, sessionData, config) {
361
365
  },
362
366
  recaptchaTokens: config.recaptchaTokens
363
367
  });
364
- trackWalletPaymentSuccess(skuId, result.basketId);
368
+ const purchaseDurationMs = Date.now() - purchaseStartTime;
369
+ trackWalletPaymentSuccess(skuId, result.basketId, purchaseDurationMs);
365
370
  session.completePayment({ status: ApplePaySession.STATUS_SUCCESS });
366
371
  const successEmail = sessionData.loggedIn ? sessionData.emailAddress : validatedEmail;
367
372
  const userStatus = sessionData.loggedIn ? "lex" : "nex";
@@ -379,7 +384,8 @@ async function handlePaymentAuthorized(event, session, sessionData, config) {
379
384
  return;
380
385
  }
381
386
  const walletError = parseWalletApiError(err);
382
- trackWalletPaymentFailure(skuId, walletError?.errorType ?? "UNKNOWN");
387
+ const purchaseDurationMs = purchaseStartTime !== void 0 ? Date.now() - purchaseStartTime : void 0;
388
+ trackWalletPaymentFailure(skuId, walletError?.errorType ?? "UNKNOWN", purchaseDurationMs);
383
389
  failApplePaySession(session);
384
390
  if (!walletError) {
385
391
  redirectFallback(skuId, returnUrl, country, { inlineError: MSG_PAYMENT_GENERIC_ERROR });
package/dist/src/index.js CHANGED
@@ -1120,52 +1120,32 @@ var isAppleDeviceOrSafari = () => {
1120
1120
  const isSafariBrowser = navigator.userAgent.indexOf("Safari") > -1 && navigator.userAgent.indexOf("Chrome") === -1;
1121
1121
  return isIOSOrMacTouchDevice || isSafariBrowser;
1122
1122
  };
1123
- var bypassApplePayChecks = () => new URLSearchParams(window.location.search).get("BYPASS_APPLE_PAY_CHECKS") !== null;
1123
+ var bypassApplePayChecks = () => new URLSearchParams(window.location.search).get("BYPASS_APPLE_PAY_CHECKS") !== null && getApplePayConfig()?.debugBypassEnabled === true;
1124
1124
  var isApplePayAvailable = async () => {
1125
1125
  if (bypassApplePayChecks()) {
1126
- console.log("[ApplePay] bypass active \u2014 displaying button");
1127
1126
  return true;
1128
1127
  }
1129
- const applePaySession = window.ApplePaySession;
1130
- const isAppleDevice = isAppleDeviceOrSafari();
1131
- console.log("[ApplePay] detection:", JSON.stringify({
1132
- applePaySessionExists: !!applePaySession,
1133
- isAppleDevice,
1134
- userAgent: navigator.userAgent,
1135
- maxTouchPoints: navigator.maxTouchPoints
1136
- }, null, 2));
1137
- if (!applePaySession && !isAppleDevice) {
1138
- console.log("[ApplePay] not available \u2014 no ApplePaySession and not Apple device");
1128
+ if (!isAppleDeviceOrSafari()) {
1139
1129
  return false;
1140
1130
  }
1131
+ const applePaySession = window.ApplePaySession;
1141
1132
  if (!applePaySession) {
1142
- console.log("[ApplePay] not available \u2014 Apple device but no ApplePaySession API");
1143
1133
  return false;
1144
1134
  }
1145
1135
  try {
1146
1136
  if (typeof applePaySession.canMakePayments !== "function") {
1147
- console.log("[ApplePay] not available \u2014 canMakePayments is not a function");
1148
1137
  return false;
1149
1138
  }
1150
1139
  if (!applePaySession.canMakePayments()) {
1151
- console.log("[ApplePay] not available \u2014 canMakePayments returned false");
1152
1140
  return false;
1153
1141
  }
1154
1142
  const merchantId = getApplePayConfig()?.merchantId;
1155
1143
  if (!merchantId) {
1156
- console.log("[ApplePay] not available \u2014 no merchantId configured");
1157
1144
  return false;
1158
1145
  }
1159
1146
  const hasActiveCard = await applePaySession.canMakePaymentsWithActiveCard(merchantId);
1160
- console.log("[ApplePay] canMakePaymentsWithActiveCard:", JSON.stringify({ merchantId, hasActiveCard }, null, 2));
1161
- if (!hasActiveCard) {
1162
- console.log("[ApplePay] not available \u2014 no active card for merchant");
1163
- return false;
1164
- }
1165
- console.log("[ApplePay] available \u2014 displaying button");
1166
- return true;
1167
- } catch (error) {
1168
- console.log("[ApplePay] not available \u2014 error:", JSON.stringify({ error: String(error) }, null, 2));
1147
+ return hasActiveCard;
1148
+ } catch {
1169
1149
  return false;
1170
1150
  }
1171
1151
  };
@@ -1338,17 +1318,21 @@ var track = (action, extra) => {
1338
1318
  ...extra
1339
1319
  });
1340
1320
  };
1341
- var trackExpressWalletImpression = (skuId, { entryPoint, country }) => {
1321
+ var trackExpressWalletImpression = (skuId, { entryPoint, country }, buttonLoadDurationMs) => {
1342
1322
  track(EXPRESS_CHECKOUT_EVENTS.IMPRESSION, {
1343
1323
  wallet_type: "apple_pay",
1344
1324
  entry_point: entryPoint,
1345
1325
  sku_id: skuId,
1346
1326
  country,
1347
- device: getDevice()
1327
+ device: getDevice(),
1328
+ ...buttonLoadDurationMs !== void 0 && { button_load_duration_ms: buttonLoadDurationMs }
1348
1329
  });
1349
1330
  };
1350
- var trackExpressWalletClick = (skuId) => {
1351
- track(EXPRESS_CHECKOUT_EVENTS.BUTTON_CLICK, { sku_id: skuId });
1331
+ var trackExpressWalletClick = (skuId, modalRenderDurationMs) => {
1332
+ track(EXPRESS_CHECKOUT_EVENTS.BUTTON_CLICK, {
1333
+ sku_id: skuId,
1334
+ ...modalRenderDurationMs !== void 0 && { modal_render_duration_ms: modalRenderDurationMs }
1335
+ });
1352
1336
  };
1353
1337
  var trackWalletPaymentSheetOpen = (skuId) => {
1354
1338
  track(EXPRESS_CHECKOUT_EVENTS.SHEET_OPEN, { sku_id: skuId });
@@ -1356,16 +1340,18 @@ var trackWalletPaymentSheetOpen = (skuId) => {
1356
1340
  var trackWalletPaymentAuthorised = (skuId) => {
1357
1341
  track(EXPRESS_CHECKOUT_EVENTS.AUTHORISED, { sku_id: skuId });
1358
1342
  };
1359
- var trackWalletPaymentSuccess = (skuId, basketId) => {
1343
+ var trackWalletPaymentSuccess = (skuId, basketId, purchaseDurationMs) => {
1360
1344
  track(EXPRESS_CHECKOUT_EVENTS.SUCCESS, {
1361
1345
  sku_id: skuId,
1362
- basket_id: basketId
1346
+ basket_id: basketId,
1347
+ ...purchaseDurationMs !== void 0 && { purchase_duration_ms: purchaseDurationMs }
1363
1348
  });
1364
1349
  };
1365
- var trackWalletPaymentFailure = (skuId, reason) => {
1350
+ var trackWalletPaymentFailure = (skuId, reason, purchaseDurationMs) => {
1366
1351
  track(EXPRESS_CHECKOUT_EVENTS.FAILURE, {
1367
1352
  sku_id: skuId,
1368
- failure_reason: reason
1353
+ failure_reason: reason,
1354
+ ...purchaseDurationMs !== void 0 && { purchase_duration_ms: purchaseDurationMs }
1369
1355
  });
1370
1356
  };
1371
1357
  var trackFallbackCheckout = (skuId) => {
@@ -1627,12 +1613,14 @@ function handleStructuredError(ctx) {
1627
1613
  async function handlePaymentAuthorized(event, session, sessionData, config) {
1628
1614
  const { skuId, returnUrl, country } = config;
1629
1615
  let validatedEmail;
1616
+ let purchaseStartTime;
1630
1617
  try {
1631
1618
  validatedEmail = validateContacts(
1632
1619
  event,
1633
1620
  sessionData,
1634
1621
  config.supportedBillingCountries
1635
1622
  );
1623
+ purchaseStartTime = Date.now();
1636
1624
  const result = await performPurchase({
1637
1625
  applePayToken: event.payment.token,
1638
1626
  skuId: config.skuId,
@@ -1643,7 +1631,8 @@ async function handlePaymentAuthorized(event, session, sessionData, config) {
1643
1631
  },
1644
1632
  recaptchaTokens: config.recaptchaTokens
1645
1633
  });
1646
- trackWalletPaymentSuccess(skuId, result.basketId);
1634
+ const purchaseDurationMs = Date.now() - purchaseStartTime;
1635
+ trackWalletPaymentSuccess(skuId, result.basketId, purchaseDurationMs);
1647
1636
  session.completePayment({ status: ApplePaySession.STATUS_SUCCESS });
1648
1637
  const successEmail = sessionData.loggedIn ? sessionData.emailAddress : validatedEmail;
1649
1638
  const userStatus = sessionData.loggedIn ? "lex" : "nex";
@@ -1661,7 +1650,8 @@ async function handlePaymentAuthorized(event, session, sessionData, config) {
1661
1650
  return;
1662
1651
  }
1663
1652
  const walletError = parseWalletApiError(err);
1664
- trackWalletPaymentFailure(skuId, walletError?.errorType ?? "UNKNOWN");
1653
+ const purchaseDurationMs = purchaseStartTime !== void 0 ? Date.now() - purchaseStartTime : void 0;
1654
+ trackWalletPaymentFailure(skuId, walletError?.errorType ?? "UNKNOWN", purchaseDurationMs);
1665
1655
  failApplePaySession(session);
1666
1656
  if (!walletError) {
1667
1657
  redirectFallback(skuId, returnUrl, country, { inlineError: MSG_PAYMENT_GENERIC_ERROR });
@@ -1984,6 +1974,7 @@ var ApplePayButton = class _ApplePayButton extends HTMLElement {
1984
1974
  #country = "";
1985
1975
  #rendering = false;
1986
1976
  #readyPromise = null;
1977
+ #loadStartTime = 0;
1987
1978
  #sessionData = null;
1988
1979
  #paymentOptions = null;
1989
1980
  #returnUrl = "";
@@ -2059,6 +2050,7 @@ var ApplePayButton = class _ApplePayButton extends HTMLElement {
2059
2050
  }
2060
2051
  // ─── Render pipeline ────────────────────────────────────────────────────────
2061
2052
  async #run() {
2053
+ this.#loadStartTime = Date.now();
2062
2054
  try {
2063
2055
  this.#renderTemplate();
2064
2056
  if (await this.#checkAvailability()) return;
@@ -2158,10 +2150,12 @@ var ApplePayButton = class _ApplePayButton extends HTMLElement {
2158
2150
  button.classList.remove("wap-loading");
2159
2151
  this.#updateDisabledState();
2160
2152
  this.#scheduleRecaptchaTokens();
2161
- trackExpressWalletImpression(this.#offer.sku_id, {
2162
- entryPoint: this.#entryPoint,
2163
- country: this.#country
2164
- });
2153
+ const buttonLoadDurationMs = Date.now() - this.#loadStartTime;
2154
+ trackExpressWalletImpression(
2155
+ this.#offer.sku_id,
2156
+ { entryPoint: this.#entryPoint, country: this.#country },
2157
+ buttonLoadDurationMs
2158
+ );
2165
2159
  }
2166
2160
  }
2167
2161
  // ─── Private helpers ────────────────────────────────────────────────────────
@@ -2184,7 +2178,7 @@ var ApplePayButton = class _ApplePayButton extends HTMLElement {
2184
2178
  if (document.querySelector(ApplePayModal.tag)) {
2185
2179
  return;
2186
2180
  }
2187
- trackExpressWalletClick(this.#offer.sku_id);
2181
+ const modalOpenStart = Date.now();
2188
2182
  ApplePayModal.define();
2189
2183
  const modal = document.createElement(ApplePayModal.tag);
2190
2184
  modal.offer = this.#offer;
@@ -2199,6 +2193,8 @@ var ApplePayButton = class _ApplePayButton extends HTMLElement {
2199
2193
  this.#scheduleRecaptchaTokens();
2200
2194
  };
2201
2195
  modal.open(this.#recaptchaTokensPromise);
2196
+ const modalRenderDurationMs = Date.now() - modalOpenStart;
2197
+ trackExpressWalletClick(skuId, modalRenderDurationMs);
2202
2198
  }
2203
2199
  /**
2204
2200
  * Starts a fresh reCAPTCHA token pre-fetch and stores its promise.
@@ -47,17 +47,17 @@ export type ImpressionMetadata = {
47
47
  country: string;
48
48
  };
49
49
  /** Wallet button displayed — emits all 5 required analytics dimensions. */
50
- export declare const trackExpressWalletImpression: (skuId: string, { entryPoint, country }: ImpressionMetadata) => void;
50
+ export declare const trackExpressWalletImpression: (skuId: string, { entryPoint, country }: ImpressionMetadata, buttonLoadDurationMs?: number) => void;
51
51
  /** Wallet button clicked */
52
- export declare const trackExpressWalletClick: (skuId: string) => void;
52
+ export declare const trackExpressWalletClick: (skuId: string, modalRenderDurationMs?: number) => void;
53
53
  /** Apple Pay sheet opened (session.begin() called) */
54
54
  export declare const trackWalletPaymentSheetOpen: (skuId: string) => void;
55
55
  /** Payment authorised by the user in the Apple Pay sheet */
56
56
  export declare const trackWalletPaymentAuthorised: (skuId: string) => void;
57
57
  /** Purchase completed — BFF returned success */
58
- export declare const trackWalletPaymentSuccess: (skuId: string, basketId: string) => void;
58
+ export declare const trackWalletPaymentSuccess: (skuId: string, basketId: string, purchaseDurationMs?: number) => void;
59
59
  /** Payment failed — BFF returned an error */
60
- export declare const trackWalletPaymentFailure: (skuId: string, reason: string) => void;
60
+ export declare const trackWalletPaymentFailure: (skuId: string, reason: string, purchaseDurationMs?: number) => void;
61
61
  /** Redirect to standard checkout triggered */
62
62
  export declare const trackFallbackCheckout: (skuId: string) => void;
63
63
  /** Billing/shipping field failed validation (emitted per-field when Apple Pay sheet stays open) */
@@ -35,17 +35,21 @@ var track = (action, extra) => {
35
35
  ...extra
36
36
  });
37
37
  };
38
- var trackExpressWalletImpression = (skuId, { entryPoint, country }) => {
38
+ var trackExpressWalletImpression = (skuId, { entryPoint, country }, buttonLoadDurationMs) => {
39
39
  track(EXPRESS_CHECKOUT_EVENTS.IMPRESSION, {
40
40
  wallet_type: "apple_pay",
41
41
  entry_point: entryPoint,
42
42
  sku_id: skuId,
43
43
  country,
44
- device: getDevice()
44
+ device: getDevice(),
45
+ ...buttonLoadDurationMs !== void 0 && { button_load_duration_ms: buttonLoadDurationMs }
45
46
  });
46
47
  };
47
- var trackExpressWalletClick = (skuId) => {
48
- track(EXPRESS_CHECKOUT_EVENTS.BUTTON_CLICK, { sku_id: skuId });
48
+ var trackExpressWalletClick = (skuId, modalRenderDurationMs) => {
49
+ track(EXPRESS_CHECKOUT_EVENTS.BUTTON_CLICK, {
50
+ sku_id: skuId,
51
+ ...modalRenderDurationMs !== void 0 && { modal_render_duration_ms: modalRenderDurationMs }
52
+ });
49
53
  };
50
54
  var trackWalletPaymentSheetOpen = (skuId) => {
51
55
  track(EXPRESS_CHECKOUT_EVENTS.SHEET_OPEN, { sku_id: skuId });
@@ -53,16 +57,18 @@ var trackWalletPaymentSheetOpen = (skuId) => {
53
57
  var trackWalletPaymentAuthorised = (skuId) => {
54
58
  track(EXPRESS_CHECKOUT_EVENTS.AUTHORISED, { sku_id: skuId });
55
59
  };
56
- var trackWalletPaymentSuccess = (skuId, basketId) => {
60
+ var trackWalletPaymentSuccess = (skuId, basketId, purchaseDurationMs) => {
57
61
  track(EXPRESS_CHECKOUT_EVENTS.SUCCESS, {
58
62
  sku_id: skuId,
59
- basket_id: basketId
63
+ basket_id: basketId,
64
+ ...purchaseDurationMs !== void 0 && { purchase_duration_ms: purchaseDurationMs }
60
65
  });
61
66
  };
62
- var trackWalletPaymentFailure = (skuId, reason) => {
67
+ var trackWalletPaymentFailure = (skuId, reason, purchaseDurationMs) => {
63
68
  track(EXPRESS_CHECKOUT_EVENTS.FAILURE, {
64
69
  sku_id: skuId,
65
- failure_reason: reason
70
+ failure_reason: reason,
71
+ ...purchaseDurationMs !== void 0 && { purchase_duration_ms: purchaseDurationMs }
66
72
  });
67
73
  };
68
74
  var trackFallbackCheckout = (skuId) => {
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@economist/web-apple-pay",
3
3
  "description": "Web component package for Apple Pay checkout UI",
4
- "version": "0.1.3-beta.37",
4
+ "version": "0.1.3-beta.39",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
7
7
  "types": "dist/index.d.ts",