@payment-kit-js/vanilla 0.5.8 → 0.5.10

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.
Files changed (40) hide show
  1. package/dist/{airwallex-apple-pay-adapter-I_pnNYdy.d.mts → airwallex-apple-pay-adapter-BCYt7Jzc.d.mts} +2 -1
  2. package/dist/airwallex-apple-pay-adapter-BCYt7Jzc.d.mts.map +1 -0
  3. package/dist/{airwallex-apple-pay-adapter-BE15xREr.mjs → airwallex-apple-pay-adapter-BFsoDoSf.mjs} +6 -2
  4. package/dist/airwallex-apple-pay-adapter-BFsoDoSf.mjs.map +1 -0
  5. package/dist/cdn/paymentkit.js +1250 -129
  6. package/dist/cdn/paymentkit.js.map +4 -4
  7. package/dist/cdn/paymentkit.min.js +10 -10
  8. package/dist/cdn/paymentkit.min.js.map +4 -4
  9. package/dist/index.d.mts.map +1 -1
  10. package/dist/index.mjs +7 -1
  11. package/dist/index.mjs.map +1 -1
  12. package/dist/payment-methods/airwallex-apple-pay-adapter.d.mts +1 -1
  13. package/dist/payment-methods/airwallex-apple-pay-adapter.mjs +1 -1
  14. package/dist/payment-methods/apple-pay.d.mts +15 -1
  15. package/dist/payment-methods/apple-pay.d.mts.map +1 -1
  16. package/dist/payment-methods/apple-pay.mjs +45 -13
  17. package/dist/payment-methods/apple-pay.mjs.map +1 -1
  18. package/dist/payment-methods/card.d.mts.map +1 -1
  19. package/dist/payment-methods/card.mjs +120 -21
  20. package/dist/payment-methods/card.mjs.map +1 -1
  21. package/dist/payment-methods/google-pay.d.mts +18 -1
  22. package/dist/payment-methods/google-pay.d.mts.map +1 -1
  23. package/dist/payment-methods/google-pay.mjs +28 -9
  24. package/dist/payment-methods/google-pay.mjs.map +1 -1
  25. package/dist/payment-methods/stripe-google-pay-adapter.d.mts +1 -1
  26. package/dist/payment-methods/stripe-google-pay-adapter.mjs +1 -1
  27. package/dist/payment-methods/vgs-collect-loader.d.mts +1 -1
  28. package/dist/payment-methods/vgs-collect-loader.mjs +1 -1
  29. package/dist/{stripe-google-pay-adapter-CqcUEoM3.mjs → stripe-google-pay-adapter-3cx0KNjK.mjs} +7 -2
  30. package/dist/stripe-google-pay-adapter-3cx0KNjK.mjs.map +1 -0
  31. package/dist/{stripe-google-pay-adapter-C3NCBSO3.d.mts → stripe-google-pay-adapter-Bdox4xBq.d.mts} +2 -1
  32. package/dist/stripe-google-pay-adapter-Bdox4xBq.d.mts.map +1 -0
  33. package/dist/{vgs-collect-loader-3Z4IWCA6.mjs → vgs-collect-loader-BZj36d8r.mjs} +28 -9
  34. package/dist/vgs-collect-loader-BZj36d8r.mjs.map +1 -0
  35. package/package.json +2 -2
  36. package/dist/airwallex-apple-pay-adapter-BE15xREr.mjs.map +0 -1
  37. package/dist/airwallex-apple-pay-adapter-I_pnNYdy.d.mts.map +0 -1
  38. package/dist/stripe-google-pay-adapter-C3NCBSO3.d.mts.map +0 -1
  39. package/dist/stripe-google-pay-adapter-CqcUEoM3.mjs.map +0 -1
  40. package/dist/vgs-collect-loader-3Z4IWCA6.mjs.map +0 -1
@@ -3,7 +3,7 @@ import { t as TunnelXManager } from "../connect-tunnel-x-BhVAej5Q.mjs";
3
3
  import { a as validateFormFields, i as definePaymentMethod, n as collectFraudMetadata, r as createCheckoutIFrame, s as withRequestId, t as $ } from "../utils-B70Y8YcZ.mjs";
4
4
  import { t as handleNextAction } from "../next-action-handlers-CTx8tRt0.mjs";
5
5
  import { t as connectToCardIframe } from "../connect-card-CMZkeppE.mjs";
6
- import { n as initVgsCollect, r as loadVgsCollectScript, t as createVgsCardFields } from "../vgs-collect-loader-3Z4IWCA6.mjs";
6
+ import { n as initVgsCollect, r as loadVgsCollectScript, t as createVgsCardFields } from "../vgs-collect-loader-BZj36d8r.mjs";
7
7
 
8
8
  //#region src/payment-methods/card.ts
9
9
  /**
@@ -114,7 +114,7 @@ const defCreateElement = (states) => {
114
114
  return;
115
115
  }
116
116
  console.log(`[PaymentKit] Creating VGS card fields — pan: ${parentSelector}, exp: ${expSelector}, cvc: ${cvcSelector}`);
117
- createVgsCardFields(form, {
117
+ states.vgsFieldsCleanup = createVgsCardFields(form, {
118
118
  pan: parentSelector,
119
119
  exp: expSelector,
120
120
  cvc: cvcSelector
@@ -130,7 +130,13 @@ const defCreateElement = (states) => {
130
130
  if (type !== "card_pan") onLoaded?.();
131
131
  cleanupVgs = () => {
132
132
  if (type === "card_pan" && states.vgsForm) {
133
- states.vgsForm.unmount();
133
+ try {
134
+ states.vgsFieldsCleanup?.();
135
+ } catch {}
136
+ states.vgsFieldsCleanup = void 0;
137
+ try {
138
+ states.vgsForm.unmount();
139
+ } catch {}
134
140
  states.vgsForm = void 0;
135
141
  }
136
142
  };
@@ -157,7 +163,9 @@ const defCreateElement = (states) => {
157
163
  iframe.style.inset = "0";
158
164
  cleanupDirect = () => {
159
165
  cardInputConnections[type]?.destroy();
160
- parent.removeChild(iframe);
166
+ try {
167
+ parent.removeChild(iframe);
168
+ } catch {}
161
169
  states.cardInputConnections[type] = void 0;
162
170
  };
163
171
  parent.appendChild(iframe);
@@ -208,7 +216,8 @@ const defSubmitPayment = (states) => {
208
216
  console.log("[PaymentKit] Submitting card via VGS CMP...");
209
217
  const vgsSubmitResult = await submitVgsCardFields(states, tunnelX);
210
218
  if (!vgsSubmitResult.isSuccess) {
211
- timingTracker.trackFail(null, "card_submit_error", "VGS card submission failed");
219
+ const errorDetail = Object.entries(vgsSubmitResult.errors).map(([k, v]) => `${k}: ${v}`).join(", ");
220
+ timingTracker.trackFail(null, "card_submit_error", errorDetail || "VGS card submission failed");
212
221
  return { errors: vgsSubmitResult.errors };
213
222
  }
214
223
  } else {
@@ -305,8 +314,36 @@ const fetchCollectToken = async (apiBaseUrl, secureToken) => {
305
314
  * 2. Call form.createCard() → VGS creates card, returns aliases
306
315
  * 3. Send aliases + fraud metadata to our backend via updateCardSetupIntent
307
316
  */
317
+ /**
318
+ * Map VGS field-level error keys to PaymentKitErrors field keys.
319
+ *
320
+ * VGS CMP errorCallback uses short field names ("pan", "cvc", "exp-date")
321
+ * matching the CMP field type, NOT the longer CSS-style names. Both forms
322
+ * are mapped here for robustness.
323
+ */
324
+ const VGS_FIELD_MAP = {
325
+ pan: "card_pan",
326
+ cvc: "card_cvc",
327
+ "exp-date": "card_exp",
328
+ "card-number": "card_pan",
329
+ "card-security-code": "card_cvc",
330
+ "card-expiration-date": "card_exp"
331
+ };
332
+ /**
333
+ * Safely stringify an error for console logging.
334
+ * VGS errors are plain objects that print as [object Object] without this.
335
+ */
336
+ const stringifyError = (err) => {
337
+ if (err instanceof Error) return err.message;
338
+ try {
339
+ return JSON.stringify(err);
340
+ } catch {
341
+ return String(err);
342
+ }
343
+ };
308
344
  const submitVgsCardFields = async (states, tunnelX) => {
309
- const { vgsForm, cardSetupIntentId, secureToken, apiBaseUrl } = states;
345
+ const { vgsForm, cardSetupIntentId, secureToken, apiBaseUrl, checkoutRequestId } = states;
346
+ const logPrefix = `[PaymentKit] [${checkoutRequestId}]`;
310
347
  const errors = {};
311
348
  if (!vgsForm) {
312
349
  errors.card_pan = "required";
@@ -322,44 +359,104 @@ const submitVgsCardFields = async (states, tunnelX) => {
322
359
  isSuccess: false
323
360
  };
324
361
  }
362
+ let accessToken;
363
+ try {
364
+ accessToken = await fetchCollectToken(apiBaseUrl, secureToken);
365
+ } catch (err) {
366
+ console.error(`${logPrefix} VGS auth token fetch failed:`, stringifyError(err));
367
+ errors.root = "Card authentication failed — please try again";
368
+ return {
369
+ errors,
370
+ isSuccess: false
371
+ };
372
+ }
373
+ let cardResult;
374
+ const VGS_CREATE_CARD_TIMEOUT_MS = 3e4;
325
375
  try {
326
- const accessToken = await fetchCollectToken(apiBaseUrl, secureToken);
327
- const VGS_CREATE_CARD_TIMEOUT_MS = 3e4;
328
376
  let createCardTimeoutId;
329
- const cardResult = await Promise.race([new Promise((resolve, reject) => {
377
+ cardResult = await Promise.race([new Promise((resolve, reject) => {
330
378
  vgsForm.createCard({
331
379
  auth: accessToken,
332
380
  data: { cardholder: {} }
333
381
  }, (_status, cardObject) => resolve(cardObject), (error) => reject(error));
334
382
  }), new Promise((_, reject) => {
335
- createCardTimeoutId = setTimeout(() => reject(/* @__PURE__ */ new Error("VGS createCard timed out")), VGS_CREATE_CARD_TIMEOUT_MS);
383
+ createCardTimeoutId = setTimeout(() => reject(/* @__PURE__ */ new Error("VGS_TIMEOUT")), VGS_CREATE_CARD_TIMEOUT_MS);
336
384
  })]).finally(() => clearTimeout(createCardTimeoutId));
337
- if (!cardResult?.data?.attributes) throw new Error("VGS createCard returned an invalid response");
338
- const { pan_alias, cvc_alias, exp_month, exp_year } = cardResult.data.attributes;
339
- const fullYear = exp_year < 100 ? 2e3 + exp_year : exp_year;
340
- const formattedExp = `${String(exp_month).padStart(2, "0")}/${fullYear}`;
341
- await tunnelX.publicEndpoints.updateCardSetupIntent({
385
+ } catch (err) {
386
+ const errStr = stringifyError(err);
387
+ console.error(`${logPrefix} VGS createCard failed:`, errStr);
388
+ if (err instanceof Error && err.message === "VGS_TIMEOUT") {
389
+ errors.root = "Card processing timed out — please try again";
390
+ return {
391
+ errors,
392
+ isSuccess: false
393
+ };
394
+ }
395
+ if (err && typeof err === "object" && !(err instanceof Error)) {
396
+ const vgsErrors = err;
397
+ for (const [vgsKey, fieldKey] of Object.entries(VGS_FIELD_MAP)) if (vgsKey in vgsErrors) {
398
+ const messages = vgsErrors[vgsKey]?.errorMessages;
399
+ errors[fieldKey] = Array.isArray(messages) ? messages[0] : "invalid";
400
+ }
401
+ if (Object.keys(errors).length > 0) return {
402
+ errors,
403
+ isSuccess: false
404
+ };
405
+ }
406
+ errors.root = "Card validation failed — please check your card details and try again";
407
+ return {
408
+ errors,
409
+ isSuccess: false
410
+ };
411
+ }
412
+ if (!cardResult?.data?.attributes) {
413
+ console.error(`${logPrefix} VGS createCard returned invalid response:`, stringifyError(cardResult));
414
+ errors.root = "Card processing returned an invalid response — please try again";
415
+ return {
416
+ errors,
417
+ isSuccess: false
418
+ };
419
+ }
420
+ const { pan_alias, cvc_alias, exp_month, exp_year } = cardResult.data.attributes;
421
+ const fullYear = exp_year < 100 ? 2e3 + exp_year : exp_year;
422
+ const formattedExp = `${String(exp_month).padStart(2, "0")}/${fullYear}`;
423
+ const cmpCardId = cardResult.data.id;
424
+ try {
425
+ const updateIntentCall = tunnelX.publicEndpoints.updateCardSetupIntent({
342
426
  checkoutToken: secureToken,
343
427
  cardSetupIntentId,
344
428
  updateCardSetupIntentReq: {
345
429
  cardPan: pan_alias,
346
430
  cardExp: formattedExp,
347
431
  cardCvc: cvc_alias,
432
+ cmpCardId,
348
433
  fraudMetadata: collectFraudMetadata()
349
434
  }
350
435
  });
351
- return {
352
- errors: {},
353
- isSuccess: true
354
- };
436
+ const proxyCvcCall = new Promise((resolve, reject) => {
437
+ if (!vgsForm.submit) {
438
+ reject(/* @__PURE__ */ new Error("VGS form.submit not available"));
439
+ return;
440
+ }
441
+ vgsForm.submit(`/api/card-setup-intents/${secureToken}/${cardSetupIntentId}`, {
442
+ method: "post",
443
+ headers: { "Content-Type": "application/json" },
444
+ data: (fields) => ({ card_cvc: fields.cvc })
445
+ }, (_status) => resolve(), (err) => reject(err));
446
+ });
447
+ await Promise.all([updateIntentCall, proxyCvcCall]);
355
448
  } catch (err) {
356
- console.error("[PaymentKit] VGS CMP createCard failed:", err);
357
- errors.root = "Card submission failed";
449
+ console.error(`${logPrefix} updateCardSetupIntent failed:`, stringifyError(err));
450
+ errors.root = "Failed to save card details — please try again";
358
451
  return {
359
452
  errors,
360
453
  isSuccess: false
361
454
  };
362
455
  }
456
+ return {
457
+ errors: {},
458
+ isSuccess: true
459
+ };
363
460
  };
364
461
  const submitCardFields = async (states) => {
365
462
  const errors = {};
@@ -403,6 +500,8 @@ const CardPaymentMethod = definePaymentMethod((paymentKitStates) => {
403
500
  submitPayment: defSubmitPayment(localStates),
404
501
  cleanup: () => {
405
502
  if (localStates.vgsForm) {
503
+ localStates.vgsFieldsCleanup?.();
504
+ localStates.vgsFieldsCleanup = void 0;
406
505
  localStates.vgsForm.unmount();
407
506
  localStates.vgsForm = void 0;
408
507
  }
@@ -1 +1 @@
1
- {"version":3,"file":"card.mjs","names":["taxIds: string[] | undefined","cleanupVgs: (() => void) | undefined","cleanupDirect: (() => void) | undefined","cdnTimeoutId: ReturnType<typeof setTimeout>","params: Record<string, string>","submitPayment: TInternalFuncs[\"submitPayment\"]","createCardTimeoutId: ReturnType<typeof setTimeout>","errors: PaymentKitErrors"],"sources":["../../src/payment-methods/card.ts"],"sourcesContent":["import {\n connectToCardIframe,\n type IFrameConnection,\n type CardInputType as PenpalCardInputType,\n type CheckoutResponse as PenpalCheckoutResponse,\n} from \"../penpal/connect-card\";\n\n// Re-export types for public API\nexport type CardInputType = PenpalCardInputType;\nexport type CheckoutResponse = PenpalCheckoutResponse;\n\nimport { TunnelXManager } from \"../penpal/connect-tunnel-x\";\nimport type { PaymentKitErrors, PaymentKitFields, PaymentKitStates, TInternalFuncs } from \"../types\";\nimport {\n $,\n collectFraudMetadata,\n createCheckoutIFrame,\n definePaymentMethod,\n validateFormFields,\n withRequestId,\n} from \"../utils\";\nimport { handleNextAction } from \"./next-action-handlers\";\nimport { createVgsCardFields, initVgsCollect, loadVgsCollectScript } from \"./vgs-collect-loader\";\n\n/**\n * Splits a full name into first name and last name.\n * First word becomes firstName, rest becomes lastName.\n */\nconst splitName = (fullName: string): { firstName: string; lastName?: string } => {\n const trimmed = fullName.trim();\n const spaceIndex = trimmed.indexOf(\" \");\n if (spaceIndex === -1) {\n return { firstName: trimmed };\n }\n return {\n firstName: trimmed.substring(0, spaceIndex),\n lastName: trimmed.substring(spaceIndex + 1).trim() || undefined,\n };\n};\n\n/**\n * Maps PaymentKitFields to the customerInfo structure expected by the API.\n */\nconst mapFieldsToCustomerInfo = (fields: PaymentKitFields) => {\n const { firstName, lastName } = splitName(fields.customer_name);\n\n const hasBillingAddress =\n fields.customer_country ||\n fields.customer_zip_code ||\n fields.customer_address_line1 ||\n fields.customer_address_line2 ||\n fields.customer_city ||\n fields.customer_state;\n\n const hasShippingAddress =\n fields.shipping_address_line1 ||\n fields.shipping_address_line2 ||\n fields.shipping_city ||\n fields.shipping_state ||\n fields.shipping_zip_code ||\n fields.shipping_country;\n\n let taxIds: string[] | undefined;\n if (fields.customer_tax_ids) {\n try {\n taxIds = JSON.parse(fields.customer_tax_ids);\n } catch {\n taxIds = undefined;\n }\n }\n\n return {\n email: fields.customer_email || undefined,\n firstName,\n lastName,\n businessName: fields.customer_business_name || undefined,\n taxIds: taxIds?.length ? taxIds : undefined,\n billingAddress: hasBillingAddress\n ? {\n country: fields.customer_country || undefined,\n zipCode: fields.customer_zip_code || undefined,\n line1: fields.customer_address_line1 || undefined,\n line2: fields.customer_address_line2 || undefined,\n city: fields.customer_city || undefined,\n state: fields.customer_state || undefined,\n }\n : undefined,\n shippingAddress: hasShippingAddress\n ? {\n line1: fields.shipping_address_line1 || undefined,\n line2: fields.shipping_address_line2 || undefined,\n city: fields.shipping_city || undefined,\n state: fields.shipping_state || undefined,\n zipCode: fields.shipping_zip_code || undefined,\n country: fields.shipping_country || undefined,\n }\n : undefined,\n };\n};\n\ntype CardStates = PaymentKitStates & {\n cardSetupIntentId?: string;\n cardInputConnections: Partial<Record<PenpalCardInputType, IFrameConnection>>;\n vgsForm?: VGSCollectForm;\n vgsFocusCallbacks?: Partial<Record<PenpalCardInputType, (isFocused: boolean) => void>>;\n vgsMountSelectors?: Partial<Record<PenpalCardInputType, string>>;\n vgsPlaceholders?: Partial<Record<PenpalCardInputType, string>>;\n};\n\ntype CreateElementOptions = Partial<Parameters<typeof connectToCardIframe>[1]> & {\n style?: Record<string, string>;\n placeholder?: string;\n};\n\nconst defCreateElement = (states: CardStates) => {\n const { baseUrl, apiBaseUrl, cardInputConnections, secureToken, timingTracker } = states;\n\n return (type: PenpalCardInputType, options: CreateElementOptions) => {\n const { style, onLoaded, onFocusChange, placeholder } = options;\n\n const mountIFrame = (parentSelector: string) => {\n // Mutable cleanup callbacks — set once mode is determined and mount completes\n let cleanupVgs: (() => void) | undefined;\n let cleanupDirect: (() => void) | undefined;\n // Cancellation flag: prevents deferred doMount from executing after unmount.\n // Needed because React strict mode may call unmount before the deferred\n // _sessionConfigReady promise resolves.\n let cancelled = false;\n\n /**\n * Performs the actual DOM mount after the tokenization mode is known.\n * Runs synchronously when mode is explicitly provided, or after the\n * auto-detect fetch resolves.\n */\n // Store mount selector so VGS init (triggered by card_pan) can read\n // the exp/cvc selectors that were registered by their own createElement calls.\n if (!states.vgsMountSelectors) states.vgsMountSelectors = {};\n states.vgsMountSelectors[type] = parentSelector;\n\n const doMount = () => {\n if (cancelled) return;\n const isVgsMode = states.cardTokenizationMode === \"vgs\";\n console.log(\n `[PaymentKit] card.doMount(${type}) — mode: ${states.cardTokenizationMode}, vgsVaultId: ${states.vgsVaultId ?? \"unset\"}, vgsEnvironment: ${states.vgsEnvironment ?? \"unset\"}`,\n );\n\n // VGS mode: initialize VGS Collect form and mount fields on card_pan\n if (isVgsMode && states.vgsVaultId && states.vgsEnvironment) {\n // Store per-field focus callbacks and placeholders so createVgsCardFields\n // can wire each field to its own callback/placeholder. All three createElement\n // calls complete before the CDN script loads, so all values are available by then.\n if (onFocusChange) {\n if (!states.vgsFocusCallbacks) states.vgsFocusCallbacks = {};\n states.vgsFocusCallbacks[type] = onFocusChange;\n }\n if (placeholder) {\n if (!states.vgsPlaceholders) states.vgsPlaceholders = {};\n states.vgsPlaceholders[type] = placeholder;\n }\n\n if (type === \"card_pan\") {\n const vaultId = states.vgsVaultId;\n const vgsEnv = states.vgsEnvironment;\n\n console.log(`[PaymentKit] VGS mode active — loading VGS Collect CDN for vault ${vaultId} (${vgsEnv})...`);\n const VGS_CDN_TIMEOUT_MS = 15_000;\n let cdnTimeoutId: ReturnType<typeof setTimeout>;\n Promise.race([\n loadVgsCollectScript(),\n new Promise<never>((_, reject) => {\n cdnTimeoutId = setTimeout(() => {\n console.warn(`[PaymentKit] VGS Collect CDN load timed out after ${VGS_CDN_TIMEOUT_MS / 1000}s`);\n reject(new Error(\"VGS Collect CDN load timed out\"));\n }, VGS_CDN_TIMEOUT_MS);\n }),\n ])\n .finally(() => clearTimeout(cdnTimeoutId))\n .then(() => {\n if (cancelled) return;\n console.log(\"[PaymentKit] VGS Collect CDN loaded — initializing form...\");\n const form = initVgsCollect(vaultId, vgsEnv);\n if (cancelled) {\n form.unmount();\n return;\n }\n states.vgsForm = form;\n\n const css = style\n ? Object.fromEntries(\n Object.entries(style).map(([k, v]) => [k.replace(/([A-Z])/g, \"-$1\").toLowerCase(), v]),\n )\n : undefined;\n\n // Read exp/cvc selectors from shared state.\n // Each createElement(\"card_exp/card_cvc\").mount(selector) stores its\n // selector before this async callback runs, so they're available here.\n const expSelector = states.vgsMountSelectors?.card_exp;\n const cvcSelector = states.vgsMountSelectors?.card_cvc;\n if (!expSelector || !cvcSelector) {\n const msg =\n `[PaymentKit] VGS mode requires all three card fields to be mounted. ` +\n `Missing: ${!expSelector ? \"card_exp\" : \"\"}${!expSelector && !cvcSelector ? \", \" : \"\"}${!cvcSelector ? \"card_cvc\" : \"\"}`;\n console.error(msg);\n onLoaded?.();\n return;\n }\n console.log(\n `[PaymentKit] Creating VGS card fields — pan: ${parentSelector}, exp: ${expSelector}, cvc: ${cvcSelector}`,\n );\n createVgsCardFields(\n form,\n {\n pan: parentSelector,\n exp: expSelector,\n cvc: cvcSelector,\n },\n css,\n states.vgsFocusCallbacks,\n states.vgsPlaceholders,\n );\n\n console.log(\"[PaymentKit] VGS card fields created successfully\");\n timingTracker.trackInputReady();\n onLoaded?.();\n })\n .catch((err) => {\n console.error(\"[PaymentKit] Failed to load VGS Collect script. Card input will not be available:\", err);\n // Signal loaded so the form exits loading state rather than hanging forever.\n // Card fields won't render, but submit will fail with a clear error.\n onLoaded?.();\n });\n }\n\n // For non-pan fields in VGS mode, signal loaded immediately since\n // VGS handles all three fields from the card_pan mount.\n if (type !== \"card_pan\") {\n onLoaded?.();\n }\n\n cleanupVgs = () => {\n if (type === \"card_pan\" && states.vgsForm) {\n states.vgsForm.unmount();\n states.vgsForm = undefined;\n }\n };\n return;\n }\n\n // If we got here and mode was \"vgs\", it means VGS config was incomplete\n if (isVgsMode) {\n console.warn(\n `[PaymentKit] VGS mode requested but config incomplete (vgsVaultId: ${states.vgsVaultId ?? \"unset\"}, vgsEnvironment: ${states.vgsEnvironment ?? \"unset\"}) — using direct mode`,\n );\n }\n\n // Direct mode: existing penpal iframe logic\n const parent = $(parentSelector) as HTMLElement;\n\n // Read container padding so the embed input matches the merchant's\n // visual intent. The iframe uses absolute positioning to fill the\n // full container, so the input needs its own padding for alignment.\n const computed = getComputedStyle(parent);\n const containerPadding = computed.padding !== \"0px\" ? computed.padding : undefined;\n const enhancedStyle = {\n ...(style ?? {}),\n height: \"100%\",\n boxSizing: \"border-box\",\n ...(containerPadding ? { padding: containerPadding } : {}),\n };\n\n const params: Record<string, string> = {\n checkout_token: secureToken,\n api_base_url: apiBaseUrl,\n style: JSON.stringify(enhancedStyle),\n };\n\n const iframe = createCheckoutIFrame(type.replace(\"_\", \"-\"), baseUrl, params);\n\n // Use absolute positioning so the iframe covers the full container\n // including any padding the merchant's CSS applies.\n if (computed.position === \"static\") {\n parent.style.position = \"relative\";\n }\n iframe.style.position = \"absolute\";\n iframe.style.inset = \"0\";\n\n // Set cleanup before DOM mutation to avoid leaking the iframe if\n // unmount() is called between appendChild and onload.\n cleanupDirect = () => {\n const connection = cardInputConnections[type];\n connection?.destroy();\n parent.removeChild(iframe);\n states.cardInputConnections[type] = undefined;\n };\n\n parent.appendChild(iframe);\n\n iframe.onload = () => {\n if (type === \"card_pan\") {\n timingTracker.trackInputReady();\n }\n\n const connection = connectToCardIframe(iframe, {\n onLoaded: onLoaded || (() => {}),\n onFocusChange: onFocusChange || (() => {}),\n });\n\n states.cardInputConnections[type] = connection;\n };\n };\n\n // If session config needs auto-detection, wait for it; otherwise mount synchronously\n if (states._sessionConfigReady) {\n states._sessionConfigReady.then(doMount);\n } else {\n doMount();\n }\n\n return {\n unmount: () => {\n cancelled = true;\n cleanupVgs?.();\n cleanupDirect?.();\n },\n };\n };\n\n return { mount: mountIFrame };\n };\n};\n\nconst defSubmitPayment = (states: CardStates) => {\n const submitPayment: TInternalFuncs[\"submitPayment\"] = async (fields, options) => {\n // Ensure session config is loaded (no-op if explicitly provided)\n if (states._sessionConfigReady) await states._sessionConfigReady;\n const isVgsMode = states.cardTokenizationMode === \"vgs\";\n\n const { timingTracker, checkoutRequestId } = states;\n\n // Track submit event\n timingTracker.trackSubmit();\n\n // Use the checkout request ID from states (generated on page load)\n const requestOptions = withRequestId(checkoutRequestId);\n\n const tunnelX = await TunnelXManager.createFromPenpalConnection(states.tunnelXConnection);\n\n // Step 1. Validate form values.\n // Form field validation is shared across both modes.\n // In VGS mode, card field validation is handled by VGS Collect (built-in validators).\n // In direct mode, card field validation is handled by penpal iframes.\n const skipCustomerValidation = options?.skipCustomerValidation === true;\n const validateFormResult = skipCustomerValidation ? { isSuccess: true as const } : await validateFormFields(fields);\n if (!isVgsMode) {\n const validateCardResult = await validateCardFields(states);\n if (!(validateCardResult.isSuccess && validateFormResult.isSuccess)) {\n timingTracker.trackFail(null, \"validation_error\", \"Form validation failed\");\n return { errors: { ...validateCardResult.errors, ...validateFormResult.errors } };\n }\n } else if (!validateFormResult.isSuccess) {\n timingTracker.trackFail(null, \"validation_error\", \"Form validation failed\");\n return { errors: validateFormResult.errors };\n }\n\n // Step 2. Create card setup intent if not present\n if (!states.cardSetupIntentId) {\n const res = await tunnelX.publicEndpoints.createCardSetupIntent({\n checkoutToken: states.secureToken,\n });\n states.cardSetupIntentId = res.cardSetupIntentId;\n }\n\n // Step 3. Submit card values.\n if (isVgsMode) {\n // VGS mode: create card via CMP, then send aliases to backend\n console.log(\"[PaymentKit] Submitting card via VGS CMP...\");\n const vgsSubmitResult = await submitVgsCardFields(states, tunnelX);\n if (!vgsSubmitResult.isSuccess) {\n timingTracker.trackFail(null, \"card_submit_error\", \"VGS card submission failed\");\n return { errors: vgsSubmitResult.errors };\n }\n } else {\n // Direct mode: submit via penpal iframes\n const submitCardResult = await submitCardFields(states);\n if (!submitCardResult.isSuccess) {\n timingTracker.trackFail(null, \"card_submit_error\", \"Card submission failed\");\n return { errors: submitCardResult.errors };\n }\n }\n\n // Step 4. Get card setup intent.\n const cardSetupIntent = await tunnelX.publicEndpoints.getCardSetupIntent({\n cardSetupIntentId: states.cardSetupIntentId,\n checkoutToken: states.secureToken,\n });\n if (!cardSetupIntent.isCardAllSet) {\n const errors = {} as PaymentKitErrors;\n if (!cardSetupIntent.isCardPanSet) errors.card_pan = \"required\";\n if (!cardSetupIntent.isCardExpSet) errors.card_exp = \"required\";\n if (!cardSetupIntent.isCardCvcSet) errors.card_cvc = \"required\";\n timingTracker.trackFail(null, \"card_setup_incomplete\", \"Card details incomplete\");\n return { errors };\n }\n\n console.log(\"Card setup intent is set ✅\", cardSetupIntent);\n console.log(\"Fields\", fields);\n\n // Step 5. Submit card checkout with customer info and fraud metadata\n let currentResult = await tunnelX.publicEndpoints.cardCheckout(\n {\n checkoutToken: states.secureToken,\n publicCardCheckoutRequest: {\n cardSetupIntentId: states.cardSetupIntentId,\n customerInfo: mapFieldsToCustomerInfo(fields),\n fraudMetadata: collectFraudMetadata(),\n },\n },\n requestOptions,\n );\n\n console.log(\"Card checkout result:\", currentResult);\n\n // Step 6. Handle next actions in a loop (supports multiple 3DS challenges)\n // This loop handles the case where one processor fails and we try another\n // that also requires 3DS authentication.\n const MAX_USER_ACTIONS = 5; // Safety limit to prevent infinite loops\n let userActionCount = 0;\n\n while (currentResult.nextAction && userActionCount < MAX_USER_ACTIONS) {\n userActionCount++;\n console.log(`Handling user action ${userActionCount}/${MAX_USER_ACTIONS}...`);\n\n const actionResult = await handleNextAction(currentResult.nextAction);\n\n // Always call verify endpoint so backend can properly conclude the checkout.\n // This is needed even on 3DS failure so the backend can transition from\n // user_actions_requested to card_payment_concluded with proper failure state.\n console.log(\"User action completed, verifying checkout...\");\n const verifyResult = await tunnelX.publicEndpoints.cardCheckoutVerify(\n {\n checkoutToken: states.secureToken,\n },\n requestOptions,\n );\n\n // Check if another action is required (e.g., cascade to next processor with 3DS)\n // This must be checked BEFORE returning error, as cascade may offer a new 3DS challenge\n if (verifyResult.nextAction) {\n if (!actionResult.success) {\n console.log(\"3DS failed but cascade triggered new action, continuing loop...\");\n } else {\n console.log(\"Another user action required, continuing loop...\");\n }\n currentResult = verifyResult;\n continue;\n }\n\n if (!actionResult.success) {\n // 3DS failed and no cascade/retry available - return error\n // Include verifyResult as checkout_response so error_code is available to frontend\n console.log(\"3DS authentication failed, checkout concluded:\", verifyResult);\n timingTracker.trackFail(\n verifyResult.checkoutAttemptId || null,\n verifyResult.errorCode || \"3ds_failed\",\n verifyResult.errorMessageForCustomer || actionResult.error,\n );\n return {\n errors: {\n root: verifyResult.errorMessageForCustomer || actionResult.error,\n checkout_response: verifyResult,\n },\n };\n }\n\n console.log(\"Card checkout verified ✅\", verifyResult);\n\n // No more actions needed, return the result\n timingTracker.trackSuccess(verifyResult.checkoutAttemptId || \"unknown\");\n return { data: verifyResult as { [key: string]: unknown } };\n }\n\n if (userActionCount >= MAX_USER_ACTIONS) {\n console.error(\"Max user actions exceeded\");\n timingTracker.trackFail(null, \"max_actions_exceeded\", \"Too many authentication attempts\");\n return { errors: { root: \"Too many authentication attempts. Please try again.\" } };\n }\n\n // Check if checkout succeeded or failed\n // States: checkout_succeeded (success), payment_failed (failure)\n if (currentResult.state === \"payment_failed\" || currentResult.state === \"checkout_failed\") {\n console.log(\"Card checkout failed:\", currentResult);\n timingTracker.trackFail(\n currentResult.checkoutAttemptId || null,\n currentResult.errorCode || \"payment_failed\",\n currentResult.errorMessageForCustomer || \"Payment failed\",\n );\n // Return the full response as errors so frontend can display error details\n // including error_code, error_message_for_customer, error_message_for_debug\n return {\n errors: {\n root: currentResult.errorMessageForCustomer || \"Payment failed\",\n checkout_response: currentResult,\n },\n };\n }\n\n console.log(\"Card checkout completed ✅\", currentResult);\n timingTracker.trackSuccess(currentResult.checkoutAttemptId || \"unknown\");\n return { data: currentResult as { [key: string]: unknown } };\n };\n return submitPayment;\n};\n\n/**\n * Fetches a VGS CMP access token from the backend.\n */\nconst fetchCollectToken = async (apiBaseUrl: string, secureToken: string): Promise<string> => {\n const resp = await fetch(`${apiBaseUrl}/api/card-setup-intents/${secureToken}/collect-token`, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n });\n if (!resp.ok) {\n throw new Error(`Failed to get collect token: ${resp.status}`);\n }\n const data = await resp.json();\n return data.access_token;\n};\n\n/**\n * Creates a card via VGS CMP and sends aliases to our backend.\n *\n * Flow:\n * 1. Fetch CMP access token from our backend\n * 2. Call form.createCard() → VGS creates card, returns aliases\n * 3. Send aliases + fraud metadata to our backend via updateCardSetupIntent\n */\nconst submitVgsCardFields = async (\n states: CardStates,\n tunnelX: Awaited<ReturnType<typeof TunnelXManager.createFromPenpalConnection>>,\n): Promise<{ errors: PaymentKitErrors; isSuccess: boolean }> => {\n const { vgsForm, cardSetupIntentId, secureToken, apiBaseUrl } = states;\n const errors = {} as PaymentKitErrors;\n\n if (!vgsForm) {\n errors.card_pan = \"required\";\n return { errors, isSuccess: false };\n }\n\n if (!cardSetupIntentId) {\n errors.root = \"Card setup intent not created\";\n return { errors, isSuccess: false };\n }\n\n try {\n // Step 1: Get CMP access token from our backend\n const accessToken = await fetchCollectToken(apiBaseUrl, secureToken);\n\n // Step 2: Create card via VGS CMP — returns aliases for PAN/CVC, plain text expiry\n const VGS_CREATE_CARD_TIMEOUT_MS = 30_000;\n let createCardTimeoutId: ReturnType<typeof setTimeout>;\n const cardResult = await Promise.race([\n new Promise<VGSCreateCardResponse>((resolve, reject) => {\n vgsForm.createCard(\n {\n auth: accessToken,\n data: { cardholder: {} },\n },\n (_status, cardObject) => resolve(cardObject),\n (error) => reject(error),\n );\n }),\n new Promise<never>((_, reject) => {\n createCardTimeoutId = setTimeout(\n () => reject(new Error(\"VGS createCard timed out\")),\n VGS_CREATE_CARD_TIMEOUT_MS,\n );\n }),\n ]).finally(() => clearTimeout(createCardTimeoutId));\n\n // Step 3: Extract aliases and expiry from CMP response\n if (!cardResult?.data?.attributes) {\n throw new Error(\"VGS createCard returned an invalid response\");\n }\n const { pan_alias, cvc_alias, exp_month, exp_year } = cardResult.data.attributes;\n const fullYear = exp_year < 100 ? 2000 + exp_year : exp_year;\n const formattedExp = `${String(exp_month).padStart(2, \"0\")}/${fullYear}`;\n\n // Step 4: Send aliases + fraud metadata to our backend\n await tunnelX.publicEndpoints.updateCardSetupIntent({\n checkoutToken: secureToken,\n cardSetupIntentId: cardSetupIntentId,\n updateCardSetupIntentReq: {\n cardPan: pan_alias,\n cardExp: formattedExp,\n cardCvc: cvc_alias,\n fraudMetadata: collectFraudMetadata(),\n },\n });\n\n return { errors: {}, isSuccess: true };\n } catch (err) {\n console.error(\"[PaymentKit] VGS CMP createCard failed:\", err);\n errors.root = \"Card submission failed\";\n return { errors, isSuccess: false };\n }\n};\n\nconst submitCardFields = async (states: CardStates) => {\n const errors = {} as PaymentKitErrors;\n const { cardSetupIntentId, cardInputConnections } = states;\n\n const submitPromises = Object.entries(cardInputConnections).map(async ([_type, connection]) => {\n const type = _type as PenpalCardInputType;\n\n const remote = await connection.promise;\n const result = await remote.onSubmit(cardSetupIntentId || \"\");\n\n if (\"error\" in result) {\n errors[type] = result.error;\n }\n });\n\n await Promise.allSettled(submitPromises);\n\n return {\n errors,\n isSuccess: Object.keys(errors).length === 0,\n };\n};\n\nconst validateCardFields = async (states: CardStates) => {\n const errors: PaymentKitErrors = {};\n const { cardInputConnections } = states;\n\n const validatePromises = Object.entries(cardInputConnections).map(async ([_type, connection]) => {\n const type = _type as PenpalCardInputType;\n\n if (!connection) {\n errors[type] = \"penpal_not_connected\";\n return;\n }\n\n const remote = await connection.promise;\n const errorMsg = await remote.onValidate();\n\n if (errorMsg) {\n errors[type] = errorMsg;\n }\n });\n\n await Promise.allSettled(validatePromises);\n\n return {\n errors,\n isSuccess: Object.keys(errors).length === 0,\n };\n};\n\nconst CardPaymentMethod = definePaymentMethod((paymentKitStates) => {\n // Object.create() creates prototype delegation: reads fall through to the shared\n // paymentKitStates (preserving auto-detected VGS config), writes stay local.\n // This prevents card-specific properties (cardInputConnections, vgsForm, etc.)\n // from leaking onto the shared paymentKitStates object.\n const localStates = Object.create(paymentKitStates) as CardStates;\n localStates.cardInputConnections = {};\n\n return {\n name: \"card\",\n externalFuncs: {\n createElement: defCreateElement(localStates),\n },\n internalFuncs: {\n submitPayment: defSubmitPayment(localStates),\n cleanup: () => {\n // Clean up VGS form if present\n if (localStates.vgsForm) {\n localStates.vgsForm.unmount();\n localStates.vgsForm = undefined;\n }\n // Clean up all card input iframe connections\n for (const connection of Object.values(localStates.cardInputConnections)) {\n connection?.destroy();\n }\n localStates.cardInputConnections = {};\n },\n },\n };\n});\n\nexport default CardPaymentMethod;\n"],"mappings":"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"}
1
+ {"version":3,"file":"card.mjs","names":["taxIds: string[] | undefined","cleanupVgs: (() => void) | undefined","cleanupDirect: (() => void) | undefined","cdnTimeoutId: ReturnType<typeof setTimeout>","params: Record<string, string>","submitPayment: TInternalFuncs[\"submitPayment\"]","VGS_FIELD_MAP: Record<string, keyof PaymentKitErrors>","accessToken: string","cardResult: VGSCreateCardResponse","createCardTimeoutId: ReturnType<typeof setTimeout>","errors: PaymentKitErrors"],"sources":["../../src/payment-methods/card.ts"],"sourcesContent":["import {\n connectToCardIframe,\n type IFrameConnection,\n type CardInputType as PenpalCardInputType,\n type CheckoutResponse as PenpalCheckoutResponse,\n} from \"../penpal/connect-card\";\n\n// Re-export types for public API\nexport type CardInputType = PenpalCardInputType;\nexport type CheckoutResponse = PenpalCheckoutResponse;\n\nimport { TunnelXManager } from \"../penpal/connect-tunnel-x\";\nimport type { PaymentKitErrors, PaymentKitFields, PaymentKitStates, TInternalFuncs } from \"../types\";\nimport {\n $,\n collectFraudMetadata,\n createCheckoutIFrame,\n definePaymentMethod,\n validateFormFields,\n withRequestId,\n} from \"../utils\";\nimport { handleNextAction } from \"./next-action-handlers\";\nimport { createVgsCardFields, initVgsCollect, loadVgsCollectScript } from \"./vgs-collect-loader\";\n\n/**\n * Splits a full name into first name and last name.\n * First word becomes firstName, rest becomes lastName.\n */\nconst splitName = (fullName: string): { firstName: string; lastName?: string } => {\n const trimmed = fullName.trim();\n const spaceIndex = trimmed.indexOf(\" \");\n if (spaceIndex === -1) {\n return { firstName: trimmed };\n }\n return {\n firstName: trimmed.substring(0, spaceIndex),\n lastName: trimmed.substring(spaceIndex + 1).trim() || undefined,\n };\n};\n\n/**\n * Maps PaymentKitFields to the customerInfo structure expected by the API.\n */\nconst mapFieldsToCustomerInfo = (fields: PaymentKitFields) => {\n const { firstName, lastName } = splitName(fields.customer_name);\n\n const hasBillingAddress =\n fields.customer_country ||\n fields.customer_zip_code ||\n fields.customer_address_line1 ||\n fields.customer_address_line2 ||\n fields.customer_city ||\n fields.customer_state;\n\n const hasShippingAddress =\n fields.shipping_address_line1 ||\n fields.shipping_address_line2 ||\n fields.shipping_city ||\n fields.shipping_state ||\n fields.shipping_zip_code ||\n fields.shipping_country;\n\n let taxIds: string[] | undefined;\n if (fields.customer_tax_ids) {\n try {\n taxIds = JSON.parse(fields.customer_tax_ids);\n } catch {\n taxIds = undefined;\n }\n }\n\n return {\n email: fields.customer_email || undefined,\n firstName,\n lastName,\n businessName: fields.customer_business_name || undefined,\n taxIds: taxIds?.length ? taxIds : undefined,\n billingAddress: hasBillingAddress\n ? {\n country: fields.customer_country || undefined,\n zipCode: fields.customer_zip_code || undefined,\n line1: fields.customer_address_line1 || undefined,\n line2: fields.customer_address_line2 || undefined,\n city: fields.customer_city || undefined,\n state: fields.customer_state || undefined,\n }\n : undefined,\n shippingAddress: hasShippingAddress\n ? {\n line1: fields.shipping_address_line1 || undefined,\n line2: fields.shipping_address_line2 || undefined,\n city: fields.shipping_city || undefined,\n state: fields.shipping_state || undefined,\n zipCode: fields.shipping_zip_code || undefined,\n country: fields.shipping_country || undefined,\n }\n : undefined,\n };\n};\n\ntype CardStates = PaymentKitStates & {\n cardSetupIntentId?: string;\n cardInputConnections: Partial<Record<PenpalCardInputType, IFrameConnection>>;\n vgsForm?: VGSCollectForm;\n vgsFieldsCleanup?: () => void;\n vgsFocusCallbacks?: Partial<Record<PenpalCardInputType, (isFocused: boolean) => void>>;\n vgsMountSelectors?: Partial<Record<PenpalCardInputType, string>>;\n vgsPlaceholders?: Partial<Record<PenpalCardInputType, string>>;\n};\n\ntype CreateElementOptions = Partial<Parameters<typeof connectToCardIframe>[1]> & {\n style?: Record<string, string>;\n placeholder?: string;\n};\n\nconst defCreateElement = (states: CardStates) => {\n const { baseUrl, apiBaseUrl, cardInputConnections, secureToken, timingTracker } = states;\n\n return (type: PenpalCardInputType, options: CreateElementOptions) => {\n const { style, onLoaded, onFocusChange, placeholder } = options;\n\n const mountIFrame = (parentSelector: string) => {\n // Mutable cleanup callbacks — set once mode is determined and mount completes\n let cleanupVgs: (() => void) | undefined;\n let cleanupDirect: (() => void) | undefined;\n // Cancellation flag: prevents deferred doMount from executing after unmount.\n // Needed because React strict mode may call unmount before the deferred\n // _sessionConfigReady promise resolves.\n let cancelled = false;\n\n /**\n * Performs the actual DOM mount after the tokenization mode is known.\n * Runs synchronously when mode is explicitly provided, or after the\n * auto-detect fetch resolves.\n */\n // Store mount selector so VGS init (triggered by card_pan) can read\n // the exp/cvc selectors that were registered by their own createElement calls.\n if (!states.vgsMountSelectors) states.vgsMountSelectors = {};\n states.vgsMountSelectors[type] = parentSelector;\n\n const doMount = () => {\n if (cancelled) return;\n const isVgsMode = states.cardTokenizationMode === \"vgs\";\n console.log(\n `[PaymentKit] card.doMount(${type}) — mode: ${states.cardTokenizationMode}, vgsVaultId: ${states.vgsVaultId ?? \"unset\"}, vgsEnvironment: ${states.vgsEnvironment ?? \"unset\"}`,\n );\n\n // VGS mode: initialize VGS Collect form and mount fields on card_pan\n if (isVgsMode && states.vgsVaultId && states.vgsEnvironment) {\n // Store per-field focus callbacks and placeholders so createVgsCardFields\n // can wire each field to its own callback/placeholder. All three createElement\n // calls complete before the CDN script loads, so all values are available by then.\n if (onFocusChange) {\n if (!states.vgsFocusCallbacks) states.vgsFocusCallbacks = {};\n states.vgsFocusCallbacks[type] = onFocusChange;\n }\n if (placeholder) {\n if (!states.vgsPlaceholders) states.vgsPlaceholders = {};\n states.vgsPlaceholders[type] = placeholder;\n }\n\n if (type === \"card_pan\") {\n const vaultId = states.vgsVaultId;\n const vgsEnv = states.vgsEnvironment;\n\n console.log(`[PaymentKit] VGS mode active — loading VGS Collect CDN for vault ${vaultId} (${vgsEnv})...`);\n const VGS_CDN_TIMEOUT_MS = 15_000;\n let cdnTimeoutId: ReturnType<typeof setTimeout>;\n Promise.race([\n loadVgsCollectScript(),\n new Promise<never>((_, reject) => {\n cdnTimeoutId = setTimeout(() => {\n console.warn(`[PaymentKit] VGS Collect CDN load timed out after ${VGS_CDN_TIMEOUT_MS / 1000}s`);\n reject(new Error(\"VGS Collect CDN load timed out\"));\n }, VGS_CDN_TIMEOUT_MS);\n }),\n ])\n .finally(() => clearTimeout(cdnTimeoutId))\n .then(() => {\n if (cancelled) return;\n console.log(\"[PaymentKit] VGS Collect CDN loaded — initializing form...\");\n const form = initVgsCollect(vaultId, vgsEnv);\n if (cancelled) {\n form.unmount();\n return;\n }\n states.vgsForm = form;\n\n const css = style\n ? Object.fromEntries(\n Object.entries(style).map(([k, v]) => [k.replace(/([A-Z])/g, \"-$1\").toLowerCase(), v]),\n )\n : undefined;\n\n // Read exp/cvc selectors from shared state.\n // Each createElement(\"card_exp/card_cvc\").mount(selector) stores its\n // selector before this async callback runs, so they're available here.\n const expSelector = states.vgsMountSelectors?.card_exp;\n const cvcSelector = states.vgsMountSelectors?.card_cvc;\n if (!expSelector || !cvcSelector) {\n const msg =\n `[PaymentKit] VGS mode requires all three card fields to be mounted. ` +\n `Missing: ${!expSelector ? \"card_exp\" : \"\"}${!expSelector && !cvcSelector ? \", \" : \"\"}${!cvcSelector ? \"card_cvc\" : \"\"}`;\n console.error(msg);\n onLoaded?.();\n return;\n }\n console.log(\n `[PaymentKit] Creating VGS card fields — pan: ${parentSelector}, exp: ${expSelector}, cvc: ${cvcSelector}`,\n );\n states.vgsFieldsCleanup = createVgsCardFields(\n form,\n {\n pan: parentSelector,\n exp: expSelector,\n cvc: cvcSelector,\n },\n css,\n states.vgsFocusCallbacks,\n states.vgsPlaceholders,\n );\n\n console.log(\"[PaymentKit] VGS card fields created successfully\");\n timingTracker.trackInputReady();\n onLoaded?.();\n })\n .catch((err) => {\n console.error(\"[PaymentKit] Failed to load VGS Collect script. Card input will not be available:\", err);\n // Signal loaded so the form exits loading state rather than hanging forever.\n // Card fields won't render, but submit will fail with a clear error.\n onLoaded?.();\n });\n }\n\n // For non-pan fields in VGS mode, signal loaded immediately since\n // VGS handles all three fields from the card_pan mount.\n if (type !== \"card_pan\") {\n onLoaded?.();\n }\n\n cleanupVgs = () => {\n if (type === \"card_pan\" && states.vgsForm) {\n try {\n states.vgsFieldsCleanup?.();\n } catch {\n // VGS may try to remove iframes from containers already removed by React\n }\n states.vgsFieldsCleanup = undefined;\n try {\n states.vgsForm.unmount();\n } catch {\n // VGS unmount may fail if DOM nodes are already detached\n }\n states.vgsForm = undefined;\n }\n };\n return;\n }\n\n // If we got here and mode was \"vgs\", it means VGS config was incomplete\n if (isVgsMode) {\n console.warn(\n `[PaymentKit] VGS mode requested but config incomplete (vgsVaultId: ${states.vgsVaultId ?? \"unset\"}, vgsEnvironment: ${states.vgsEnvironment ?? \"unset\"}) — using direct mode`,\n );\n }\n\n // Direct mode: existing penpal iframe logic\n const parent = $(parentSelector) as HTMLElement;\n\n // Read container padding so the embed input matches the merchant's\n // visual intent. The iframe uses absolute positioning to fill the\n // full container, so the input needs its own padding for alignment.\n const computed = getComputedStyle(parent);\n const containerPadding = computed.padding !== \"0px\" ? computed.padding : undefined;\n const enhancedStyle = {\n ...(style ?? {}),\n height: \"100%\",\n boxSizing: \"border-box\",\n ...(containerPadding ? { padding: containerPadding } : {}),\n };\n\n const params: Record<string, string> = {\n checkout_token: secureToken,\n api_base_url: apiBaseUrl,\n style: JSON.stringify(enhancedStyle),\n };\n\n const iframe = createCheckoutIFrame(type.replace(\"_\", \"-\"), baseUrl, params);\n\n // Use absolute positioning so the iframe covers the full container\n // including any padding the merchant's CSS applies.\n if (computed.position === \"static\") {\n parent.style.position = \"relative\";\n }\n iframe.style.position = \"absolute\";\n iframe.style.inset = \"0\";\n\n // Set cleanup before DOM mutation to avoid leaking the iframe if\n // unmount() is called between appendChild and onload.\n cleanupDirect = () => {\n const connection = cardInputConnections[type];\n connection?.destroy();\n try {\n parent.removeChild(iframe);\n } catch {\n // Parent may already be detached from DOM by React\n }\n states.cardInputConnections[type] = undefined;\n };\n\n parent.appendChild(iframe);\n\n iframe.onload = () => {\n if (type === \"card_pan\") {\n timingTracker.trackInputReady();\n }\n\n const connection = connectToCardIframe(iframe, {\n onLoaded: onLoaded || (() => {}),\n onFocusChange: onFocusChange || (() => {}),\n });\n\n states.cardInputConnections[type] = connection;\n };\n };\n\n // If session config needs auto-detection, wait for it; otherwise mount synchronously\n if (states._sessionConfigReady) {\n states._sessionConfigReady.then(doMount);\n } else {\n doMount();\n }\n\n return {\n unmount: () => {\n cancelled = true;\n cleanupVgs?.();\n cleanupDirect?.();\n },\n };\n };\n\n return { mount: mountIFrame };\n };\n};\n\nconst defSubmitPayment = (states: CardStates) => {\n const submitPayment: TInternalFuncs[\"submitPayment\"] = async (fields, options) => {\n // Ensure session config is loaded (no-op if explicitly provided)\n if (states._sessionConfigReady) await states._sessionConfigReady;\n const isVgsMode = states.cardTokenizationMode === \"vgs\";\n\n const { timingTracker, checkoutRequestId } = states;\n\n // Track submit event\n timingTracker.trackSubmit();\n\n // Use the checkout request ID from states (generated on page load)\n const requestOptions = withRequestId(checkoutRequestId);\n\n const tunnelX = await TunnelXManager.createFromPenpalConnection(states.tunnelXConnection);\n\n // Step 1. Validate form values.\n // Form field validation is shared across both modes.\n // In VGS mode, card field validation is handled by VGS Collect (built-in validators).\n // In direct mode, card field validation is handled by penpal iframes.\n const skipCustomerValidation = options?.skipCustomerValidation === true;\n const validateFormResult = skipCustomerValidation ? { isSuccess: true as const } : await validateFormFields(fields);\n if (!isVgsMode) {\n const validateCardResult = await validateCardFields(states);\n if (!(validateCardResult.isSuccess && validateFormResult.isSuccess)) {\n timingTracker.trackFail(null, \"validation_error\", \"Form validation failed\");\n return { errors: { ...validateCardResult.errors, ...validateFormResult.errors } };\n }\n } else if (!validateFormResult.isSuccess) {\n timingTracker.trackFail(null, \"validation_error\", \"Form validation failed\");\n return { errors: validateFormResult.errors };\n }\n\n // Step 2. Create card setup intent if not present\n if (!states.cardSetupIntentId) {\n const res = await tunnelX.publicEndpoints.createCardSetupIntent({\n checkoutToken: states.secureToken,\n });\n states.cardSetupIntentId = res.cardSetupIntentId;\n }\n\n // Step 3. Submit card values.\n if (isVgsMode) {\n // VGS mode: create card via CMP, then send aliases to backend\n console.log(\"[PaymentKit] Submitting card via VGS CMP...\");\n const vgsSubmitResult = await submitVgsCardFields(states, tunnelX);\n if (!vgsSubmitResult.isSuccess) {\n // Include field-level error details for PostHog visibility\n const errorDetail = Object.entries(vgsSubmitResult.errors)\n .map(([k, v]) => `${k}: ${v}`)\n .join(\", \");\n timingTracker.trackFail(null, \"card_submit_error\", errorDetail || \"VGS card submission failed\");\n return { errors: vgsSubmitResult.errors };\n }\n } else {\n // Direct mode: submit via penpal iframes\n const submitCardResult = await submitCardFields(states);\n if (!submitCardResult.isSuccess) {\n timingTracker.trackFail(null, \"card_submit_error\", \"Card submission failed\");\n return { errors: submitCardResult.errors };\n }\n }\n\n // Step 4. Get card setup intent.\n const cardSetupIntent = await tunnelX.publicEndpoints.getCardSetupIntent({\n cardSetupIntentId: states.cardSetupIntentId,\n checkoutToken: states.secureToken,\n });\n if (!cardSetupIntent.isCardAllSet) {\n const errors = {} as PaymentKitErrors;\n if (!cardSetupIntent.isCardPanSet) errors.card_pan = \"required\";\n if (!cardSetupIntent.isCardExpSet) errors.card_exp = \"required\";\n if (!cardSetupIntent.isCardCvcSet) errors.card_cvc = \"required\";\n timingTracker.trackFail(null, \"card_setup_incomplete\", \"Card details incomplete\");\n return { errors };\n }\n\n console.log(\"Card setup intent is set ✅\", cardSetupIntent);\n console.log(\"Fields\", fields);\n\n // Step 5. Submit card checkout with customer info and fraud metadata\n let currentResult = await tunnelX.publicEndpoints.cardCheckout(\n {\n checkoutToken: states.secureToken,\n publicCardCheckoutRequest: {\n cardSetupIntentId: states.cardSetupIntentId,\n customerInfo: mapFieldsToCustomerInfo(fields),\n fraudMetadata: collectFraudMetadata(),\n },\n },\n requestOptions,\n );\n\n console.log(\"Card checkout result:\", currentResult);\n\n // Step 6. Handle next actions in a loop (supports multiple 3DS challenges)\n // This loop handles the case where one processor fails and we try another\n // that also requires 3DS authentication.\n const MAX_USER_ACTIONS = 5; // Safety limit to prevent infinite loops\n let userActionCount = 0;\n\n while (currentResult.nextAction && userActionCount < MAX_USER_ACTIONS) {\n userActionCount++;\n console.log(`Handling user action ${userActionCount}/${MAX_USER_ACTIONS}...`);\n\n const actionResult = await handleNextAction(currentResult.nextAction);\n\n // Always call verify endpoint so backend can properly conclude the checkout.\n // This is needed even on 3DS failure so the backend can transition from\n // user_actions_requested to card_payment_concluded with proper failure state.\n console.log(\"User action completed, verifying checkout...\");\n const verifyResult = await tunnelX.publicEndpoints.cardCheckoutVerify(\n {\n checkoutToken: states.secureToken,\n },\n requestOptions,\n );\n\n // Check if another action is required (e.g., cascade to next processor with 3DS)\n // This must be checked BEFORE returning error, as cascade may offer a new 3DS challenge\n if (verifyResult.nextAction) {\n if (!actionResult.success) {\n console.log(\"3DS failed but cascade triggered new action, continuing loop...\");\n } else {\n console.log(\"Another user action required, continuing loop...\");\n }\n currentResult = verifyResult;\n continue;\n }\n\n if (!actionResult.success) {\n // 3DS failed and no cascade/retry available - return error\n // Include verifyResult as checkout_response so error_code is available to frontend\n console.log(\"3DS authentication failed, checkout concluded:\", verifyResult);\n timingTracker.trackFail(\n verifyResult.checkoutAttemptId || null,\n verifyResult.errorCode || \"3ds_failed\",\n verifyResult.errorMessageForCustomer || actionResult.error,\n );\n return {\n errors: {\n root: verifyResult.errorMessageForCustomer || actionResult.error,\n checkout_response: verifyResult,\n },\n };\n }\n\n console.log(\"Card checkout verified ✅\", verifyResult);\n\n // No more actions needed, return the result\n timingTracker.trackSuccess(verifyResult.checkoutAttemptId || \"unknown\");\n return { data: verifyResult as { [key: string]: unknown } };\n }\n\n if (userActionCount >= MAX_USER_ACTIONS) {\n console.error(\"Max user actions exceeded\");\n timingTracker.trackFail(null, \"max_actions_exceeded\", \"Too many authentication attempts\");\n return { errors: { root: \"Too many authentication attempts. Please try again.\" } };\n }\n\n // Check if checkout succeeded or failed\n // States: checkout_succeeded (success), payment_failed (failure)\n if (currentResult.state === \"payment_failed\" || currentResult.state === \"checkout_failed\") {\n console.log(\"Card checkout failed:\", currentResult);\n timingTracker.trackFail(\n currentResult.checkoutAttemptId || null,\n currentResult.errorCode || \"payment_failed\",\n currentResult.errorMessageForCustomer || \"Payment failed\",\n );\n // Return the full response as errors so frontend can display error details\n // including error_code, error_message_for_customer, error_message_for_debug\n return {\n errors: {\n root: currentResult.errorMessageForCustomer || \"Payment failed\",\n checkout_response: currentResult,\n },\n };\n }\n\n console.log(\"Card checkout completed ✅\", currentResult);\n timingTracker.trackSuccess(currentResult.checkoutAttemptId || \"unknown\");\n return { data: currentResult as { [key: string]: unknown } };\n };\n return submitPayment;\n};\n\n/**\n * Fetches a VGS CMP access token from the backend.\n */\nconst fetchCollectToken = async (apiBaseUrl: string, secureToken: string): Promise<string> => {\n const resp = await fetch(`${apiBaseUrl}/api/card-setup-intents/${secureToken}/collect-token`, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n });\n if (!resp.ok) {\n throw new Error(`Failed to get collect token: ${resp.status}`);\n }\n const data = await resp.json();\n return data.access_token;\n};\n\n/**\n * Creates a card via VGS CMP and sends aliases to our backend.\n *\n * Flow:\n * 1. Fetch CMP access token from our backend\n * 2. Call form.createCard() → VGS creates card, returns aliases\n * 3. Send aliases + fraud metadata to our backend via updateCardSetupIntent\n */\n/**\n * Map VGS field-level error keys to PaymentKitErrors field keys.\n *\n * VGS CMP errorCallback uses short field names (\"pan\", \"cvc\", \"exp-date\")\n * matching the CMP field type, NOT the longer CSS-style names. Both forms\n * are mapped here for robustness.\n */\nconst VGS_FIELD_MAP: Record<string, keyof PaymentKitErrors> = {\n // Actual VGS CMP error keys (verified empirically)\n pan: \"card_pan\",\n cvc: \"card_cvc\",\n \"exp-date\": \"card_exp\",\n // Alternative key formats (VGS documentation references)\n \"card-number\": \"card_pan\",\n \"card-security-code\": \"card_cvc\",\n \"card-expiration-date\": \"card_exp\",\n};\n\n/**\n * Safely stringify an error for console logging.\n * VGS errors are plain objects that print as [object Object] without this.\n */\nconst stringifyError = (err: unknown): string => {\n if (err instanceof Error) return err.message;\n try {\n return JSON.stringify(err);\n } catch {\n return String(err);\n }\n};\n\nconst submitVgsCardFields = async (\n states: CardStates,\n tunnelX: Awaited<ReturnType<typeof TunnelXManager.createFromPenpalConnection>>,\n): Promise<{ errors: PaymentKitErrors; isSuccess: boolean }> => {\n const { vgsForm, cardSetupIntentId, secureToken, apiBaseUrl, checkoutRequestId } = states;\n const logPrefix = `[PaymentKit] [${checkoutRequestId}]`;\n const errors = {} as PaymentKitErrors;\n\n if (!vgsForm) {\n errors.card_pan = \"required\";\n return { errors, isSuccess: false };\n }\n\n if (!cardSetupIntentId) {\n errors.root = \"Card setup intent not created\";\n return { errors, isSuccess: false };\n }\n\n // Step 1: Get CMP access token from our backend\n let accessToken: string;\n try {\n accessToken = await fetchCollectToken(apiBaseUrl, secureToken);\n } catch (err) {\n console.error(`${logPrefix} VGS auth token fetch failed:`, stringifyError(err));\n errors.root = \"Card authentication failed — please try again\";\n return { errors, isSuccess: false };\n }\n\n // Step 2: Create card via VGS CMP — returns aliases for PAN/CVC, plain text expiry\n let cardResult: VGSCreateCardResponse;\n const VGS_CREATE_CARD_TIMEOUT_MS = 30_000;\n try {\n let createCardTimeoutId: ReturnType<typeof setTimeout>;\n cardResult = await Promise.race([\n new Promise<VGSCreateCardResponse>((resolve, reject) => {\n vgsForm.createCard(\n {\n auth: accessToken,\n data: { cardholder: {} },\n },\n (_status, cardObject) => resolve(cardObject),\n (error) => reject(error),\n );\n }),\n new Promise<never>((_, reject) => {\n createCardTimeoutId = setTimeout(() => reject(new Error(\"VGS_TIMEOUT\")), VGS_CREATE_CARD_TIMEOUT_MS);\n }),\n ]).finally(() => clearTimeout(createCardTimeoutId));\n } catch (err) {\n const errStr = stringifyError(err);\n console.error(`${logPrefix} VGS createCard failed:`, errStr);\n\n // Distinguish timeout from VGS field validation errors\n if (err instanceof Error && err.message === \"VGS_TIMEOUT\") {\n errors.root = \"Card processing timed out — please try again\";\n return { errors, isSuccess: false };\n }\n\n // VGS errorCallback returns field-level errors as a plain object,\n // e.g. { \"card-number\": { errorMessages: [\"Card number is not valid\"] } }\n // Map them to per-field errors so inline validation shows on the right field.\n if (err && typeof err === \"object\" && !(err instanceof Error)) {\n const vgsErrors = err as Record<string, unknown>;\n for (const [vgsKey, fieldKey] of Object.entries(VGS_FIELD_MAP)) {\n if (vgsKey in vgsErrors) {\n const fieldErr = vgsErrors[vgsKey] as Record<string, unknown> | undefined;\n const messages = fieldErr?.errorMessages;\n errors[fieldKey] = Array.isArray(messages) ? messages[0] : \"invalid\";\n }\n }\n if (Object.keys(errors).length > 0) {\n return { errors, isSuccess: false };\n }\n }\n\n errors.root = \"Card validation failed — please check your card details and try again\";\n return { errors, isSuccess: false };\n }\n\n // Step 3: Extract aliases and expiry from CMP response\n if (!cardResult?.data?.attributes) {\n console.error(`${logPrefix} VGS createCard returned invalid response:`, stringifyError(cardResult));\n errors.root = \"Card processing returned an invalid response — please try again\";\n return { errors, isSuccess: false };\n }\n const { pan_alias, cvc_alias, exp_month, exp_year } = cardResult.data.attributes;\n const fullYear = exp_year < 100 ? 2000 + exp_year : exp_year;\n const formattedExp = `${String(exp_month).padStart(2, \"0\")}/${fullYear}`;\n\n // Capture CMP card ID for network token tracking\n const cmpCardId = cardResult.data.id;\n\n // Step 4: Send aliases + fraud metadata to our backend.\n // CVC is sent as a VGS alias from createCard. Additionally, the CVC is\n // submitted via the vault inbound proxy (REDACT route) as a fallback for\n // the 303 deduplication path where VGS returns an existing card without\n // a fresh cvc_alias.\n try {\n const updateIntentCall = tunnelX.publicEndpoints.updateCardSetupIntent({\n checkoutToken: secureToken,\n cardSetupIntentId: cardSetupIntentId,\n updateCardSetupIntentReq: {\n cardPan: pan_alias,\n cardExp: formattedExp,\n cardCvc: cvc_alias,\n cmpCardId: cmpCardId,\n fraudMetadata: collectFraudMetadata(),\n },\n });\n\n const proxyCvcCall = new Promise<void>((resolve, reject) => {\n if (!vgsForm.submit) {\n reject(new Error(\"VGS form.submit not available\"));\n return;\n }\n vgsForm.submit(\n `/api/card-setup-intents/${secureToken}/${cardSetupIntentId}`,\n {\n method: \"post\",\n headers: { \"Content-Type\": \"application/json\" },\n data: (fields: Record<string, unknown>) => ({ card_cvc: fields.cvc }),\n },\n (_status: number) => resolve(),\n (err: unknown) => reject(err),\n );\n });\n\n await Promise.all([updateIntentCall, proxyCvcCall]);\n } catch (err) {\n console.error(`${logPrefix} updateCardSetupIntent failed:`, stringifyError(err));\n errors.root = \"Failed to save card details — please try again\";\n return { errors, isSuccess: false };\n }\n\n return { errors: {}, isSuccess: true };\n};\n\nconst submitCardFields = async (states: CardStates) => {\n const errors = {} as PaymentKitErrors;\n const { cardSetupIntentId, cardInputConnections } = states;\n\n const submitPromises = Object.entries(cardInputConnections).map(async ([_type, connection]) => {\n const type = _type as PenpalCardInputType;\n\n const remote = await connection.promise;\n const result = await remote.onSubmit(cardSetupIntentId || \"\");\n\n if (\"error\" in result) {\n errors[type] = result.error;\n }\n });\n\n await Promise.allSettled(submitPromises);\n\n return {\n errors,\n isSuccess: Object.keys(errors).length === 0,\n };\n};\n\nconst validateCardFields = async (states: CardStates) => {\n const errors: PaymentKitErrors = {};\n const { cardInputConnections } = states;\n\n const validatePromises = Object.entries(cardInputConnections).map(async ([_type, connection]) => {\n const type = _type as PenpalCardInputType;\n\n if (!connection) {\n errors[type] = \"penpal_not_connected\";\n return;\n }\n\n const remote = await connection.promise;\n const errorMsg = await remote.onValidate();\n\n if (errorMsg) {\n errors[type] = errorMsg;\n }\n });\n\n await Promise.allSettled(validatePromises);\n\n return {\n errors,\n isSuccess: Object.keys(errors).length === 0,\n };\n};\n\nconst CardPaymentMethod = definePaymentMethod((paymentKitStates) => {\n // Object.create() creates prototype delegation: reads fall through to the shared\n // paymentKitStates (preserving auto-detected VGS config), writes stay local.\n // This prevents card-specific properties (cardInputConnections, vgsForm, etc.)\n // from leaking onto the shared paymentKitStates object.\n const localStates = Object.create(paymentKitStates) as CardStates;\n localStates.cardInputConnections = {};\n\n return {\n name: \"card\",\n externalFuncs: {\n createElement: defCreateElement(localStates),\n },\n internalFuncs: {\n submitPayment: defSubmitPayment(localStates),\n cleanup: () => {\n // Clean up VGS form if present\n if (localStates.vgsForm) {\n localStates.vgsFieldsCleanup?.();\n localStates.vgsFieldsCleanup = undefined;\n localStates.vgsForm.unmount();\n localStates.vgsForm = undefined;\n }\n // Clean up all card input iframe connections\n for (const connection of Object.values(localStates.cardInputConnections)) {\n connection?.destroy();\n }\n localStates.cardInputConnections = {};\n },\n },\n };\n});\n\nexport default CardPaymentMethod;\n"],"mappings":"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"}
@@ -2,12 +2,13 @@ import { r as PaymentMethod } from "../types-D88-nhWu.mjs";
2
2
  import "../connect-card-Cxy51W6t.mjs";
3
3
  import "../connect-tunnel-x-lv6Wtdme.mjs";
4
4
  import { a as GooglePayEncryptedToken } from "../airwallex-google-pay-adapter-CY379Rre.mjs";
5
- import { n as GooglePayMockScenario } from "../stripe-google-pay-adapter-C3NCBSO3.mjs";
5
+ import { n as GooglePayMockScenario } from "../stripe-google-pay-adapter-Bdox4xBq.mjs";
6
6
 
7
7
  //#region src/payment-methods/google-pay.d.ts
8
8
  type GooglePayCustomerInfo = {
9
9
  first_name: string;
10
10
  last_name: string;
11
+ email?: string;
11
12
  };
12
13
  type GooglePayStartRequest = {
13
14
  processor_id: string;
@@ -39,6 +40,7 @@ type GooglePayStartResponse = {
39
40
  };
40
41
  type GooglePayConfirmRequest = {
41
42
  google_pay_token?: GooglePayEncryptedToken;
43
+ payer_email?: string;
42
44
  mock_scenario?: string;
43
45
  };
44
46
  type Airwallex3dsNextAction = {
@@ -50,9 +52,24 @@ type Airwallex3dsNextAction = {
50
52
  type GooglePayConfirmResponse = {
51
53
  charge_status: "success" | "fail" | "pending";
52
54
  transaction_id?: string;
55
+ error_code?: string;
53
56
  error_message?: string;
57
+ error_message_for_customer?: string;
58
+ error_message_for_debug?: string;
54
59
  checkout_attempt_id: string;
60
+ checkout_session_id?: string;
55
61
  next_action?: Airwallex3dsNextAction;
62
+ payment_intent_id?: string;
63
+ customer_id?: string;
64
+ payment_method_id?: string;
65
+ processor_used?: string;
66
+ subscription_id?: string;
67
+ invoice_id?: string;
68
+ invoice_number?: number;
69
+ card_brand?: string;
70
+ card_last4?: string;
71
+ card_exp_month?: number;
72
+ card_exp_year?: number;
56
73
  };
57
74
  type GooglePaySubmitOptions = {
58
75
  processorId: string;
@@ -1 +1 @@
1
- {"version":3,"file":"google-pay.d.mts","names":[],"sources":["../../src/payment-methods/google-pay.ts"],"sourcesContent":[],"mappings":";;;;;;;KAYY,qBAAA;;;;KAKA,qBAAA;EALA,YAAA,EAAA,MAAA;EAKA,aAAA,EAEK,qBAAA;EASL,cAAA,EAAA;IAuBA,SAAA,CAAA,EAAA,MAAA;IAMA,WAAA,CAAA,EAAA;MAOA,CAAA,GAAA,EAAA,MAAA,CAAA,EAAA,OAAwB;IAQxB,CAAA;IAgeN,kBAAA,CAAA,EASJ;;;;;;KArhBU,sBAAA;;;;;;;;;;;;;;KAuBA,uBAAA;qBACS;;;KAKT,sBAAA;;;;;;KAOA,wBAAA;;;;;gBAKI;;KAGJ,sBAAA;;gBAEI;iBACC;;cA6dX,wBASJ"}
1
+ {"version":3,"file":"google-pay.d.mts","names":[],"sources":["../../src/payment-methods/google-pay.ts"],"sourcesContent":[],"mappings":";;;;;;;KAYY,qBAAA;;;;;AAAA,KAMA,qBAAA,GANqB;EAMrB,YAAA,EAAA,MAAA;EAWA,aAAA,EATK,qBASiB;EAuBtB,cAAA,EAAA;IAOA,SAAA,CAAA,EAAA,MAAA;IAOA,WAAA,CAAA,EAAA;MAuBA,CAAA,GAAA,EAAA,MAAA,CAAA,EAAA,OAAsB;IA0gB5B,CAAA;;;;;;;KAtkBM,sBAAA;;;;;;;;;;;;;;KAuBA,uBAAA;qBACS;;;;KAMT,sBAAA;;;;;;KAOA,wBAAA;;;;;;;;;gBASI;;;;;;;;;;;;;KAcJ,sBAAA;;gBAEI;iBACC;;cAugBX,wBASJ"}
@@ -1,7 +1,7 @@
1
1
  import { i as definePaymentMethod, n as collectFraudMetadata, o as getOrCreateCheckoutRequestId } from "../utils-B70Y8YcZ.mjs";
2
2
  import { n as AirwallexGooglePayMockScenario, t as AirwallexGooglePayAdapter } from "../airwallex-google-pay-adapter-CHol_8f2.mjs";
3
3
  import { t as handleNextAction } from "../next-action-handlers-CTx8tRt0.mjs";
4
- import { n as StripeGooglePayAdapter, t as GooglePayMockScenario } from "../stripe-google-pay-adapter-CqcUEoM3.mjs";
4
+ import { n as StripeGooglePayAdapter, t as GooglePayMockScenario } from "../stripe-google-pay-adapter-3cx0KNjK.mjs";
5
5
 
6
6
  //#region src/payment-methods/google-pay.ts
7
7
  async function apiCall(url, options, checkoutRequestId) {
@@ -91,7 +91,10 @@ async function runStripeFlow(startData, mockScenario) {
91
91
  };
92
92
  }
93
93
  paymentResult.complete("success");
94
- return { success: true };
94
+ return {
95
+ success: true,
96
+ payerEmail: paymentResult.payerEmail
97
+ };
95
98
  }
96
99
  function initializeAirwallexAdapter(startData, mockScenario) {
97
100
  if (!startData.merchant_name || !startData.airwallex_account_id) return { error: "Airwallex Google Pay credentials not provided" };
@@ -141,9 +144,10 @@ async function runAirwallexFlow(startData, mockScenario) {
141
144
  * Call /confirm endpoint - processes Google Pay token (Airwallex) or records setup (Stripe).
142
145
  * For Airwallex, may return next_action if 3DS is required.
143
146
  */
144
- async function callConfirmEndpoint(apiBaseUrl, secureToken, googlePayToken, mockScenarioStr, checkoutRequestId) {
147
+ async function callConfirmEndpoint(apiBaseUrl, secureToken, googlePayToken, mockScenarioStr, checkoutRequestId, payerEmail) {
145
148
  const requestBody = { mock_scenario: mockScenarioStr };
146
149
  if (googlePayToken) requestBody.google_pay_token = googlePayToken;
150
+ if (payerEmail) requestBody.payer_email = payerEmail;
147
151
  const result = await apiCall(`${apiBaseUrl}/api/checkout/${secureToken}/google-pay/confirm`, {
148
152
  method: "POST",
149
153
  headers: { "Content-Type": "application/json" },
@@ -179,17 +183,32 @@ function toGooglePayResult(response, secureToken) {
179
183
  if (response.charge_status === "success") return { data: {
180
184
  id: response.transaction_id,
181
185
  checkoutAttemptId: response.checkout_attempt_id,
182
- checkoutSessionId: secureToken,
183
- state: "checkout_succeeded"
186
+ checkoutSessionId: response.checkout_session_id ?? secureToken,
187
+ state: "checkout_succeeded",
188
+ paymentIntentId: response.payment_intent_id,
189
+ customerId: response.customer_id,
190
+ paymentMethodId: response.payment_method_id,
191
+ processorUsed: response.processor_used,
192
+ subscriptionId: response.subscription_id,
193
+ invoiceId: response.invoice_id,
194
+ invoiceNumber: response.invoice_number,
195
+ cardBrand: response.card_brand,
196
+ cardLast4: response.card_last4,
197
+ cardExpMonth: response.card_exp_month,
198
+ cardExpYear: response.card_exp_year,
199
+ errorCode: response.error_code,
200
+ errorMessageForCustomer: response.error_message_for_customer,
201
+ errorMessageForDebug: response.error_message_for_debug,
202
+ nextAction: response.next_action
184
203
  } };
185
- return { errors: { google_pay: response.error_message || "Payment failed" } };
204
+ return { errors: { google_pay: response.error_message_for_customer || response.error_message_for_debug || response.error_message || "Payment failed" } };
186
205
  }
187
206
  /**
188
207
  * Confirm Stripe Google Pay - simple flow without 3DS loop.
189
208
  * Stripe.js handles 3DS internally during confirmCardSetup().
190
209
  */
191
- async function confirmStripeGooglePay(apiBaseUrl, secureToken, mockScenarioStr, checkoutRequestId) {
192
- return toGooglePayResult(await callConfirmEndpoint(apiBaseUrl, secureToken, void 0, mockScenarioStr, checkoutRequestId), secureToken);
210
+ async function confirmStripeGooglePay(apiBaseUrl, secureToken, mockScenarioStr, checkoutRequestId, payerEmail) {
211
+ return toGooglePayResult(await callConfirmEndpoint(apiBaseUrl, secureToken, void 0, mockScenarioStr, checkoutRequestId, payerEmail), secureToken);
193
212
  }
194
213
  const MAX_USER_ACTIONS = 5;
195
214
  /**
@@ -237,7 +256,7 @@ const defSubmitPayment = (states) => {
237
256
  if (startData.processor === "stripe") {
238
257
  const stripeResult = await runStripeFlow(startData, gpayOptions.mockScenario);
239
258
  if (!stripeResult.success) return { errors: { google_pay: stripeResult.error } };
240
- return await confirmStripeGooglePay(apiBaseUrl, secureToken, mockScenarioStr, checkoutRequestId);
259
+ return await confirmStripeGooglePay(apiBaseUrl, secureToken, mockScenarioStr, checkoutRequestId, stripeResult.payerEmail);
241
260
  }
242
261
  if (startData.processor === "airwallex") {
243
262
  const airwallexResult = await runAirwallexFlow(startData, gpayOptions.mockScenario);