@economist/web-apple-pay 0.1.3-beta.2 → 0.1.3-beta.21

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.
@@ -43,11 +43,18 @@ 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
+ });
46
51
  if (contentType.includes("application/json")) {
47
52
  const errorBody = await response.json();
53
+ console.warn("[web-apple-pay] apiFetch: parsed JSON error body", { errorBody });
48
54
  throw new ApiError(response.status, errorBody);
49
55
  }
50
56
  const errorText = await response.text();
57
+ console.warn("[web-apple-pay] apiFetch: non-JSON error body", { errorText });
51
58
  throw new Error(
52
59
  `API request failed: ${response.status} ${response.statusText} - ${errorText}`
53
60
  );
@@ -62,15 +69,37 @@ var validateAppleMerchant = (data) => {
62
69
  };
63
70
 
64
71
  // src/utils/expressCheckoutRedirect.ts
72
+ var CHECKOUT_REDIRECT_KEY = "checkout-redirect";
73
+ var ALLOWED_REDIRECT_HOSTNAME = "economist.com";
74
+ var writeCheckoutRedirect = (originUrl) => {
75
+ if (originUrl.startsWith("/")) {
76
+ localStorage.setItem(CHECKOUT_REDIRECT_KEY, originUrl);
77
+ return;
78
+ }
79
+ try {
80
+ const { hostname } = new URL(originUrl);
81
+ const isAllowed = hostname === ALLOWED_REDIRECT_HOSTNAME || hostname.endsWith(`.${ALLOWED_REDIRECT_HOSTNAME}`);
82
+ if (isAllowed) {
83
+ localStorage.setItem(CHECKOUT_REDIRECT_KEY, originUrl);
84
+ }
85
+ } catch {
86
+ }
87
+ };
88
+ var decodeSku = (sku) => {
89
+ try {
90
+ return atob(sku.substring(sku.indexOf("-") + 1));
91
+ } catch {
92
+ return sku;
93
+ }
94
+ };
65
95
  var redirectToCheckoutFallback = (params) => {
66
96
  const url = new URL(window.location.href);
67
97
  url.pathname = "/checkout";
68
- url.searchParams.set("lmo_offer", params.skuId);
98
+ url.searchParams.set("lmo_offer", decodeSku(params.skuId));
69
99
  if (params.inlineError)
70
100
  url.searchParams.set("inlineError", params.inlineError);
71
101
  if (params.email) url.searchParams.set("email", params.email);
72
102
  if (params.emailError) url.searchParams.set("emailError", params.emailError);
73
- url.searchParams.set("returnUrl", params.returnUrl);
74
103
  return url.pathname + url.search;
75
104
  };
76
105
 
@@ -159,72 +188,39 @@ var MSG_PAYMENT_GENERIC_ERROR = "Something went wrong. Please check your payment
159
188
 
160
189
  // src/clients/payment-checkout/apple-session.ts
161
190
  function redirectToFallbackCheckout(config) {
162
- console.warn("[web-apple-pay] redirectToFallbackCheckout", {
163
- skuId: config.skuId,
164
- returnUrl: config.returnUrl,
165
- inlineError: MSG_PAYMENT_GENERIC_ERROR
166
- });
191
+ writeCheckoutRedirect(config.returnUrl);
167
192
  trackFallbackCheckout(config.skuId);
168
193
  window.location.href = redirectToCheckoutFallback({
169
194
  skuId: config.skuId,
170
- returnUrl: config.returnUrl,
171
195
  inlineError: MSG_PAYMENT_GENERIC_ERROR
172
196
  });
173
197
  }
174
198
  async function handleMerchantValidation(event, session, config) {
175
- console.log("[web-apple-pay] handleMerchantValidation: start", {
176
- skuId: config.skuId,
177
- validationURL: event.validationURL
178
- });
179
199
  try {
180
200
  const validationHost = new URL(event.validationURL).host;
181
- console.log("[web-apple-pay] handleMerchantValidation: host parsed", {
182
- validationHost
183
- });
184
201
  if (!validationHost.endsWith("apple.com")) {
185
- console.warn(
186
- "[web-apple-pay] handleMerchantValidation: invalid validation host",
187
- { validationHost }
188
- );
189
202
  session.abort();
190
203
  redirectToFallbackCheckout(config);
191
204
  return;
192
205
  }
193
- console.log("[web-apple-pay] handleMerchantValidation: requesting recaptcha token");
194
206
  const recaptchaToken = await getRecaptchaToken("verifyMerchant");
195
- console.log("[web-apple-pay] handleMerchantValidation: recaptcha token received");
196
207
  const response = await validateAppleMerchant({
197
208
  validationURL: event.validationURL,
198
209
  recaptchaToken
199
210
  });
200
211
  if (!response?.merchantSession) {
201
- console.warn(
202
- "[web-apple-pay] handleMerchantValidation: missing merchantSession in response"
203
- );
204
212
  session.abort();
205
213
  redirectToFallbackCheckout(config);
206
214
  return;
207
215
  }
208
- console.log("[web-apple-pay] handleMerchantValidation: completing merchant validation");
209
216
  session.completeMerchantValidation(response.merchantSession);
210
- console.log("[web-apple-pay] handleMerchantValidation: success");
211
217
  } catch (err) {
212
- console.error("[web-apple-pay] handleMerchantValidation: failed", {
213
- skuId: config.skuId,
214
- err
215
- });
218
+ console.error("[web-apple-pay] onvalidatemerchant: error during validation", err);
216
219
  session.abort();
217
220
  redirectToFallbackCheckout(config);
218
221
  }
219
222
  }
220
223
  function startApplePaySession(config) {
221
- console.log("[web-apple-pay] startApplePaySession", {
222
- skuId: config.skuId,
223
- countryCode: config.countryCode,
224
- currencyCode: config.currencyCode,
225
- totalAmount: config.totalAmount,
226
- userLoggedIn: config.userLoggedIn
227
- });
228
224
  const paymentRequest = {
229
225
  countryCode: config.countryCode,
230
226
  currencyCode: config.currencyCode,
@@ -238,43 +234,22 @@ function startApplePaySession(config) {
238
234
  // Only request email from Apple Pay sheet if user is NOT logged in
239
235
  ...!config.userLoggedIn && { requiredShippingContactFields: ["email"] }
240
236
  };
241
- console.log("[web-apple-pay] startApplePaySession: payment request created", {
242
- skuId: config.skuId,
243
- requiredShippingContactFields: paymentRequest.requiredShippingContactFields
244
- });
245
237
  try {
246
238
  const session = new ApplePaySession(3, paymentRequest);
247
- console.log("[web-apple-pay] startApplePaySession: ApplePaySession created");
248
239
  session.onvalidatemerchant = async (event) => {
249
- console.log("[web-apple-pay] onvalidatemerchant: invoked", {
250
- skuId: config.skuId
251
- });
252
240
  await handleMerchantValidation(event, session, config);
253
241
  };
254
242
  session.onpaymentauthorized = (event) => {
255
- console.log("[web-apple-pay] onpaymentauthorized: invoked", {
256
- skuId: config.skuId
257
- });
258
243
  trackWalletPaymentAuthorised(config.skuId);
259
244
  config.onPaymentAuthorized(event, session);
260
245
  };
261
246
  session.oncancel = () => {
262
- console.log("[web-apple-pay] oncancel: invoked", {
263
- skuId: config.skuId
264
- });
265
247
  config.onCancel?.();
266
248
  };
267
- console.log("[web-apple-pay] startApplePaySession: opening payment sheet", {
268
- skuId: config.skuId
269
- });
270
249
  trackWalletPaymentSheetOpen(config.skuId);
271
250
  session.begin();
272
- console.log("[web-apple-pay] startApplePaySession: session begin called");
273
251
  } catch (err) {
274
- console.error("[web-apple-pay] startApplePaySession: failed to start session", {
275
- skuId: config.skuId,
276
- err
277
- });
252
+ console.error("[web-apple-pay] startApplePaySession: failed to create session", err);
278
253
  redirectToFallbackCheckout(config);
279
254
  }
280
255
  }
@@ -1,19 +1,12 @@
1
1
  // src/clients/payment-checkout/config.ts
2
2
  var runtimeConfig = null;
3
3
  var configureApplePay = (config) => {
4
- console.log("[web-apple-pay] configureApplePay called", {
5
- merchantId: config.merchantId,
6
- walletApiBaseUrl: config.walletApiBaseUrl,
7
- debugBypassEnabled: config.debugBypassEnabled,
8
- recaptchaSiteKey: config.recaptchaSiteKey ? "***" : void 0
9
- });
10
4
  runtimeConfig = {
11
5
  ...config
12
6
  };
13
7
  };
14
8
  var getApplePayConfig = () => runtimeConfig;
15
9
  var resetApplePayConfig = () => {
16
- console.log("[web-apple-pay] resetApplePayConfig called");
17
10
  runtimeConfig = null;
18
11
  };
19
12
  export {
@@ -10,27 +10,21 @@ var isAppleDeviceOrSafari = () => {
10
10
  };
11
11
  var bypassApplePayChecks = () => new URLSearchParams(window.location.search).get("BYPASS_APPLE_PAY_CHECKS") !== null && getApplePayConfig()?.debugBypassEnabled === true;
12
12
  var isApplePayAvailable = async () => {
13
- console.log("[web-apple-pay] isApplePayAvailable: checking availability");
14
13
  if (bypassApplePayChecks()) {
15
- console.log("[web-apple-pay] isApplePayAvailable: bypass active \u2192 true");
16
14
  return true;
17
15
  }
18
16
  if (!isAppleDeviceOrSafari()) {
19
- console.log("[web-apple-pay] isApplePayAvailable: not Apple device or Safari \u2192 false");
20
17
  return false;
21
18
  }
22
19
  const applePaySession = window.ApplePaySession;
23
20
  if (!applePaySession) {
24
- console.log("[web-apple-pay] isApplePayAvailable: ApplePaySession not found on window \u2192 false");
25
21
  return false;
26
22
  }
27
23
  try {
28
24
  if (typeof applePaySession.canMakePayments !== "function") {
29
- console.log("[web-apple-pay] isApplePayAvailable: canMakePayments is not a function \u2192 false");
30
25
  return false;
31
26
  }
32
27
  if (!applePaySession.canMakePayments()) {
33
- console.log("[web-apple-pay] isApplePayAvailable: canMakePayments() returned false \u2192 false");
34
28
  return false;
35
29
  }
36
30
  const merchantId = getApplePayConfig()?.merchantId;
@@ -40,10 +34,8 @@ var isApplePayAvailable = async () => {
40
34
  );
41
35
  return false;
42
36
  }
43
- console.log("[web-apple-pay] isApplePayAvailable : calling canMakePaymentsWithActiveCard", { merchantId });
44
- const result = await applePaySession.canMakePaymentsWithActiveCard(merchantId);
45
- console.log("[web-apple-pay] isApplePayAvailable: canMakePaymentsWithActiveCard \u2192", result);
46
- return result;
37
+ const hasActiveCard = await applePaySession.canMakePaymentsWithActiveCard(merchantId);
38
+ return hasActiveCard;
47
39
  } catch (error) {
48
40
  console.error("Error checking Apple Pay active card status:", error);
49
41
  return false;
@@ -43,11 +43,18 @@ 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
+ });
46
51
  if (contentType.includes("application/json")) {
47
52
  const errorBody = await response.json();
53
+ console.warn("[web-apple-pay] apiFetch: parsed JSON error body", { errorBody });
48
54
  throw new ApiError(response.status, errorBody);
49
55
  }
50
56
  const errorText = await response.text();
57
+ console.warn("[web-apple-pay] apiFetch: non-JSON error body", { errorText });
51
58
  throw new Error(
52
59
  `API request failed: ${response.status} ${response.statusText} - ${errorText}`
53
60
  );
@@ -17,6 +17,7 @@ import type { SessionResponse } from './types';
17
17
  export type PaymentHandlerConfig = {
18
18
  skuId: string;
19
19
  country: string;
20
+ currency: string;
20
21
  returnUrl: string;
21
22
  /**
22
23
  * Optional `&country=` query param captured from the page URL at the moment
@@ -43,11 +43,18 @@ 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
+ });
46
51
  if (contentType.includes("application/json")) {
47
52
  const errorBody = await response.json();
53
+ console.warn("[web-apple-pay] apiFetch: parsed JSON error body", { errorBody });
48
54
  throw new ApiError(response.status, errorBody);
49
55
  }
50
56
  const errorText = await response.text();
57
+ console.warn("[web-apple-pay] apiFetch: non-JSON error body", { errorText });
51
58
  throw new Error(
52
59
  `API request failed: ${response.status} ${response.statusText} - ${errorText}`
53
60
  );
@@ -208,25 +215,13 @@ var billingContactDetails = (contact, supportedBillingCountries) => {
208
215
  // src/clients/payment-checkout/shipping-contact-details.ts
209
216
  var EMAIL_FORMAT_REGEX = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
210
217
  var shippingContactDetails = (shippingContact) => {
211
- console.log("[web-apple-pay] shippingContactDetails: start", {
212
- hasShippingContact: Boolean(shippingContact),
213
- hasEmailAddress: shippingContact?.emailAddress !== void 0
214
- });
215
218
  const errors = [];
216
219
  let emailAddress = "";
217
220
  if (shippingContact?.emailAddress === void 0 || shippingContact?.emailAddress === "") {
218
- console.log(
219
- "[web-apple-pay] shippingContactDetails: email missing or empty"
220
- );
221
221
  pushRequiredFieldError(errors, "shipping", "emailAddress");
222
222
  } else {
223
- console.log("[web-apple-pay] shippingContactDetails: email provided");
224
223
  emailAddress = shippingContact.emailAddress.trim();
225
- console.log("[web-apple-pay] shippingContactDetails: email trimmed", {
226
- emailLength: emailAddress.length
227
- });
228
224
  if (emailAddress.length > 80) {
229
- console.log("[web-apple-pay] shippingContactDetails: email too long");
230
225
  pushFieldError(
231
226
  errors,
232
227
  "shipping",
@@ -235,9 +230,6 @@ var shippingContactDetails = (shippingContact) => {
235
230
  MSG_EMAIL_TOO_LONG
236
231
  );
237
232
  } else if (!EMAIL_FORMAT_REGEX.test(emailAddress)) {
238
- console.log(
239
- "[web-apple-pay] shippingContactDetails: email format invalid"
240
- );
241
233
  pushFieldError(
242
234
  errors,
243
235
  "shipping",
@@ -245,21 +237,31 @@ var shippingContactDetails = (shippingContact) => {
245
237
  "format",
246
238
  MSG_EMAIL_WRONG_FORMAT
247
239
  );
248
- } else {
249
- console.log("[web-apple-pay] shippingContactDetails: email valid");
250
240
  }
251
241
  }
252
242
  if (errors.length > 0) {
253
- console.log("[web-apple-pay] shippingContactDetails: throwing validation error", {
254
- errorCount: errors.length
255
- });
256
243
  throw new ContactValidationError(errors);
257
244
  }
258
- console.log("[web-apple-pay] shippingContactDetails: success");
259
245
  return { emailAddress };
260
246
  };
261
247
 
262
248
  // src/utils/expressCheckoutRedirect.ts
249
+ var CHECKOUT_REDIRECT_KEY = "checkout-redirect";
250
+ var ALLOWED_REDIRECT_HOSTNAME = "economist.com";
251
+ var writeCheckoutRedirect = (originUrl) => {
252
+ if (originUrl.startsWith("/")) {
253
+ localStorage.setItem(CHECKOUT_REDIRECT_KEY, originUrl);
254
+ return;
255
+ }
256
+ try {
257
+ const { hostname } = new URL(originUrl);
258
+ const isAllowed = hostname === ALLOWED_REDIRECT_HOSTNAME || hostname.endsWith(`.${ALLOWED_REDIRECT_HOSTNAME}`);
259
+ if (isAllowed) {
260
+ localStorage.setItem(CHECKOUT_REDIRECT_KEY, originUrl);
261
+ }
262
+ } catch {
263
+ }
264
+ };
263
265
  var decodeSku = (sku) => {
264
266
  try {
265
267
  return atob(sku.substring(sku.indexOf("-") + 1));
@@ -277,18 +279,16 @@ var redirectToCheckoutComplete = (params) => {
277
279
  url.searchParams.set("lmo_offer", params.skuId);
278
280
  url.searchParams.set("status", params.status);
279
281
  url.searchParams.set("redirectToCheckout", "true");
280
- url.searchParams.set("returnUrl", params.returnUrl);
281
282
  return url.pathname + url.search;
282
283
  };
283
284
  var redirectToCheckoutFallback = (params) => {
284
285
  const url = new URL(window.location.href);
285
286
  url.pathname = "/checkout";
286
- url.searchParams.set("lmo_offer", params.skuId);
287
+ url.searchParams.set("lmo_offer", decodeSku(params.skuId));
287
288
  if (params.inlineError)
288
289
  url.searchParams.set("inlineError", params.inlineError);
289
290
  if (params.email) url.searchParams.set("email", params.email);
290
291
  if (params.emailError) url.searchParams.set("emailError", params.emailError);
291
- url.searchParams.set("returnUrl", params.returnUrl);
292
292
  return url.pathname + url.search;
293
293
  };
294
294
 
@@ -303,9 +303,15 @@ var failApplePaySession = (session, errors = []) => {
303
303
  };
304
304
  function parseWalletApiError(err) {
305
305
  const body = err instanceof ApiError ? err.body : err;
306
+ console.warn("[web-apple-pay] parseWalletApiError: raw error", {
307
+ isApiError: err instanceof ApiError,
308
+ body
309
+ });
306
310
  if (body && typeof body === "object" && "errorType" in body && "message" in body) {
311
+ console.warn("[web-apple-pay] parseWalletApiError: structured BFF error", body);
307
312
  return body;
308
313
  }
314
+ console.warn("[web-apple-pay] parseWalletApiError: unstructured error \u2014 no errorType found");
309
315
  return null;
310
316
  }
311
317
  function validateContacts(event, sessionData, supportedBillingCountries) {
@@ -319,10 +325,10 @@ function validateContacts(event, sessionData, supportedBillingCountries) {
319
325
  return void 0;
320
326
  }
321
327
  function redirectFallback(skuId, returnUrl, options) {
328
+ writeCheckoutRedirect(returnUrl);
322
329
  trackFallbackCheckout(skuId);
323
330
  window.location.href = redirectToCheckoutFallback({
324
331
  skuId,
325
- returnUrl,
326
332
  ...options
327
333
  });
328
334
  }
@@ -350,58 +356,49 @@ function handleStructuredError(ctx) {
350
356
  async function handlePaymentAuthorized(event, session, sessionData, config) {
351
357
  const { skuId, returnUrl, queryString } = config;
352
358
  let validatedEmail;
353
- console.log("[web-apple-pay] handlePaymentAuthorized: starting", { skuId, userLoggedIn: sessionData.loggedIn });
354
359
  try {
355
- console.log("[web-apple-pay] handlePaymentAuthorized: step 1 \u2014 validating contacts");
356
360
  validatedEmail = validateContacts(
357
361
  event,
358
362
  sessionData,
359
363
  config.supportedBillingCountries
360
364
  );
361
- console.log("[web-apple-pay] handlePaymentAuthorized: contacts valid", { validatedEmail });
362
- console.log("[web-apple-pay] handlePaymentAuthorized: step 2 \u2014 calling performPurchase", { skuId });
363
365
  const result = await performPurchase({
364
366
  applePayToken: event.payment.token,
365
367
  skuId: config.skuId,
368
+ currency: config.currency,
366
369
  billingContact: event.payment.billingContact,
367
370
  ...event.payment.shippingContact && {
368
371
  shippingContact: event.payment.shippingContact
369
372
  },
370
373
  recaptchaTokens: config.recaptchaTokens
371
374
  });
372
- console.log("[web-apple-pay] handlePaymentAuthorized: performPurchase succeeded", { basketId: result.basketId });
373
- console.log("[web-apple-pay] handlePaymentAuthorized: step 3 \u2014 completing payment and redirecting");
374
375
  trackWalletPaymentSuccess(skuId, result.basketId);
375
376
  session.completePayment({ status: ApplePaySession.STATUS_SUCCESS });
376
377
  const userStatus = sessionData.loggedIn ? "lex" : "nex";
378
+ writeCheckoutRedirect(returnUrl);
377
379
  window.location.href = redirectToCheckoutComplete({
378
380
  basketId: result.basketId,
379
381
  status: userStatus,
380
382
  skuId: decodeSku(skuId),
381
- returnUrl,
382
383
  queryString
383
384
  });
384
385
  } catch (err) {
385
386
  if (err instanceof ContactValidationError) {
386
- console.warn("[web-apple-pay] handlePaymentAuthorized: contact validation failed", err.errors);
387
387
  failApplePaySession(session, err.errors);
388
388
  return;
389
389
  }
390
390
  console.error("[web-apple-pay] onpaymentauthorized error", { skuId, err });
391
391
  const walletError = parseWalletApiError(err);
392
- console.error("[web-apple-pay] handlePaymentAuthorized: BFF/network error", {
393
- skuId,
394
- errorType: walletError?.errorType ?? "UNKNOWN",
395
- message: walletError?.message
392
+ console.warn("[web-apple-pay] handlePaymentAuthorized: walletError result", {
393
+ walletError,
394
+ validatedEmail
396
395
  });
397
396
  trackWalletPaymentFailure(skuId, walletError?.errorType ?? "UNKNOWN");
398
397
  failApplePaySession(session);
399
398
  if (!walletError) {
400
- console.warn("[web-apple-pay] handlePaymentAuthorized: unstructured error, using generic fallback");
401
399
  redirectFallback(skuId, returnUrl, { inlineError: MSG_PAYMENT_GENERIC_ERROR });
402
400
  return;
403
401
  }
404
- console.log("[web-apple-pay] handlePaymentAuthorized: dispatching structured error handler", { errorType: walletError.errorType });
405
402
  handleStructuredError({ skuId, returnUrl, validatedEmail, walletError });
406
403
  }
407
404
  }
@@ -43,11 +43,18 @@ 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
+ });
46
51
  if (contentType.includes("application/json")) {
47
52
  const errorBody = await response.json();
53
+ console.warn("[web-apple-pay] apiFetch: parsed JSON error body", { errorBody });
48
54
  throw new ApiError(response.status, errorBody);
49
55
  }
50
56
  const errorText = await response.text();
57
+ console.warn("[web-apple-pay] apiFetch: non-JSON error body", { errorText });
51
58
  throw new Error(
52
59
  `API request failed: ${response.status} ${response.statusText} - ${errorText}`
53
60
  );
@@ -49,25 +49,13 @@ var ContactValidationError = class extends Error {
49
49
  // src/clients/payment-checkout/shipping-contact-details.ts
50
50
  var EMAIL_FORMAT_REGEX = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
51
51
  var shippingContactDetails = (shippingContact) => {
52
- console.log("[web-apple-pay] shippingContactDetails: start", {
53
- hasShippingContact: Boolean(shippingContact),
54
- hasEmailAddress: shippingContact?.emailAddress !== void 0
55
- });
56
52
  const errors = [];
57
53
  let emailAddress = "";
58
54
  if (shippingContact?.emailAddress === void 0 || shippingContact?.emailAddress === "") {
59
- console.log(
60
- "[web-apple-pay] shippingContactDetails: email missing or empty"
61
- );
62
55
  pushRequiredFieldError(errors, "shipping", "emailAddress");
63
56
  } else {
64
- console.log("[web-apple-pay] shippingContactDetails: email provided");
65
57
  emailAddress = shippingContact.emailAddress.trim();
66
- console.log("[web-apple-pay] shippingContactDetails: email trimmed", {
67
- emailLength: emailAddress.length
68
- });
69
58
  if (emailAddress.length > 80) {
70
- console.log("[web-apple-pay] shippingContactDetails: email too long");
71
59
  pushFieldError(
72
60
  errors,
73
61
  "shipping",
@@ -76,9 +64,6 @@ var shippingContactDetails = (shippingContact) => {
76
64
  MSG_EMAIL_TOO_LONG
77
65
  );
78
66
  } else if (!EMAIL_FORMAT_REGEX.test(emailAddress)) {
79
- console.log(
80
- "[web-apple-pay] shippingContactDetails: email format invalid"
81
- );
82
67
  pushFieldError(
83
68
  errors,
84
69
  "shipping",
@@ -86,17 +71,11 @@ var shippingContactDetails = (shippingContact) => {
86
71
  "format",
87
72
  MSG_EMAIL_WRONG_FORMAT
88
73
  );
89
- } else {
90
- console.log("[web-apple-pay] shippingContactDetails: email valid");
91
74
  }
92
75
  }
93
76
  if (errors.length > 0) {
94
- console.log("[web-apple-pay] shippingContactDetails: throwing validation error", {
95
- errorCount: errors.length
96
- });
97
77
  throw new ContactValidationError(errors);
98
78
  }
99
- console.log("[web-apple-pay] shippingContactDetails: success");
100
79
  return { emailAddress };
101
80
  };
102
81
  export {
@@ -87,6 +87,7 @@ export type PurchaseRequest = {
87
87
  * only provide a `skuId`. Frontend uses `skuId` directly — BFF must be aligned.
88
88
  */
89
89
  skuId: string;
90
+ currency: string;
90
91
  billingContact: ApplePayPaymentContact;
91
92
  /** Required if loggedIn: false — contains emailAddress from Apple Pay sheet. */
92
93
  shippingContact?: ApplePayPaymentContact;