@payment-kit-js/vanilla 0.4.1 → 0.5.2

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 (73) hide show
  1. package/dist/airwallex-apple-pay-adapter-BE15xREr.mjs +237 -0
  2. package/dist/airwallex-apple-pay-adapter-BE15xREr.mjs.map +1 -0
  3. package/dist/airwallex-apple-pay-adapter-I_pnNYdy.d.mts +98 -0
  4. package/dist/airwallex-apple-pay-adapter-I_pnNYdy.d.mts.map +1 -0
  5. package/dist/{airwallex-google-pay-adapter-BCmTZip5.mjs → airwallex-google-pay-adapter-CHol_8f2.mjs} +1 -1
  6. package/dist/{airwallex-google-pay-adapter-BCmTZip5.mjs.map → airwallex-google-pay-adapter-CHol_8f2.mjs.map} +1 -1
  7. package/dist/{airwallex-google-pay-adapter-Be2Af4N9.d.mts → airwallex-google-pay-adapter-CY379Rre.d.mts} +1 -1
  8. package/dist/{airwallex-google-pay-adapter-Be2Af4N9.d.mts.map → airwallex-google-pay-adapter-CY379Rre.d.mts.map} +1 -1
  9. package/dist/cdn/paymentkit.js +789 -420
  10. package/dist/cdn/paymentkit.js.map +4 -4
  11. package/dist/cdn/paymentkit.min.js +6 -6
  12. package/dist/cdn/paymentkit.min.js.map +4 -4
  13. package/dist/{connect-card-BrtCmsjz.mjs → connect-card-CMZkeppE.mjs} +1 -1
  14. package/dist/{connect-card-BrtCmsjz.mjs.map → connect-card-CMZkeppE.mjs.map} +1 -1
  15. package/dist/{connect-card-DTfXuTsW.d.mts → connect-card-Cxy51W6t.d.mts} +1 -1
  16. package/dist/{connect-card-DTfXuTsW.d.mts.map → connect-card-Cxy51W6t.d.mts.map} +1 -1
  17. package/dist/connect-tunnel-x-BhVAej5Q.mjs.map +1 -1
  18. package/dist/{connect-tunnel-x-Dxcg5Y7Y.d.mts → connect-tunnel-x-lv6Wtdme.d.mts} +6 -1
  19. package/dist/connect-tunnel-x-lv6Wtdme.d.mts.map +1 -0
  20. package/dist/index.d.mts +3 -3
  21. package/dist/index.d.mts.map +1 -1
  22. package/dist/index.mjs +76 -14
  23. package/dist/index.mjs.map +1 -1
  24. package/dist/{next-action-handlers-BZs04hYb.mjs → next-action-handlers-CTx8tRt0.mjs} +1 -1
  25. package/dist/{next-action-handlers-BZs04hYb.mjs.map → next-action-handlers-CTx8tRt0.mjs.map} +1 -1
  26. package/dist/payment-methods/airwallex-apple-pay-adapter.d.mts +2 -0
  27. package/dist/payment-methods/airwallex-apple-pay-adapter.mjs +3 -0
  28. package/dist/payment-methods/airwallex-google-pay-adapter.d.mts +1 -1
  29. package/dist/payment-methods/airwallex-google-pay-adapter.mjs +1 -1
  30. package/dist/payment-methods/apple-pay.d.mts +37 -17
  31. package/dist/payment-methods/apple-pay.d.mts.map +1 -1
  32. package/dist/payment-methods/apple-pay.mjs +203 -37
  33. package/dist/payment-methods/apple-pay.mjs.map +1 -1
  34. package/dist/payment-methods/card.d.mts +3 -3
  35. package/dist/payment-methods/card.d.mts.map +1 -1
  36. package/dist/payment-methods/card.mjs +208 -41
  37. package/dist/payment-methods/card.mjs.map +1 -1
  38. package/dist/payment-methods/google-pay.d.mts +5 -5
  39. package/dist/payment-methods/google-pay.d.mts.map +1 -1
  40. package/dist/payment-methods/google-pay.mjs +6 -7
  41. package/dist/payment-methods/google-pay.mjs.map +1 -1
  42. package/dist/payment-methods/next-action-handlers.mjs +1 -1
  43. package/dist/payment-methods/paypal.d.mts +3 -3
  44. package/dist/payment-methods/paypal.mjs +3 -3
  45. package/dist/payment-methods/paypal.mjs.map +1 -1
  46. package/dist/payment-methods/stripe-apple-pay-adapter.d.mts +1 -1
  47. package/dist/payment-methods/stripe-apple-pay-adapter.mjs +1 -1
  48. package/dist/payment-methods/stripe-google-pay-adapter.d.mts +1 -1
  49. package/dist/payment-methods/stripe-google-pay-adapter.mjs +1 -1
  50. package/dist/payment-methods/vgs-collect-loader.d.mts +28 -0
  51. package/dist/payment-methods/vgs-collect-loader.d.mts.map +1 -0
  52. package/dist/payment-methods/vgs-collect-loader.mjs +3 -0
  53. package/dist/penpal/connect-card.d.mts +1 -1
  54. package/dist/penpal/connect-card.mjs +1 -1
  55. package/dist/penpal/connect-tunnel-x.d.mts +1 -1
  56. package/dist/{stripe-apple-pay-adapter-Bg7nCy3P.mjs → stripe-apple-pay-adapter-Cd9LVNnp.mjs} +1 -1
  57. package/dist/{stripe-apple-pay-adapter-Bg7nCy3P.mjs.map → stripe-apple-pay-adapter-Cd9LVNnp.mjs.map} +1 -1
  58. package/dist/{stripe-apple-pay-adapter-Bq3f1mqv.d.mts → stripe-apple-pay-adapter-DhpGclW9.d.mts} +1 -1
  59. package/dist/{stripe-apple-pay-adapter-Bq3f1mqv.d.mts.map → stripe-apple-pay-adapter-DhpGclW9.d.mts.map} +1 -1
  60. package/dist/{stripe-google-pay-adapter-xktEycOD.d.mts → stripe-google-pay-adapter-C3NCBSO3.d.mts} +1 -1
  61. package/dist/{stripe-google-pay-adapter-xktEycOD.d.mts.map → stripe-google-pay-adapter-C3NCBSO3.d.mts.map} +1 -1
  62. package/dist/{stripe-google-pay-adapter-DjrgDYWe.mjs → stripe-google-pay-adapter-CqcUEoM3.mjs} +1 -1
  63. package/dist/{stripe-google-pay-adapter-DjrgDYWe.mjs.map → stripe-google-pay-adapter-CqcUEoM3.mjs.map} +1 -1
  64. package/dist/{types-CPuloCtF.d.mts → types-CZk3V0Qt.d.mts} +24 -6
  65. package/dist/types-CZk3V0Qt.d.mts.map +1 -0
  66. package/dist/{utils-Dgyk7RkM.mjs → utils-B70Y8YcZ.mjs} +20 -9
  67. package/dist/utils-B70Y8YcZ.mjs.map +1 -0
  68. package/dist/vgs-collect-loader-D3_xOA-G.mjs +93 -0
  69. package/dist/vgs-collect-loader-D3_xOA-G.mjs.map +1 -0
  70. package/package.json +4 -2
  71. package/dist/connect-tunnel-x-Dxcg5Y7Y.d.mts.map +0 -1
  72. package/dist/types-CPuloCtF.d.mts.map +0 -1
  73. package/dist/utils-Dgyk7RkM.mjs.map +0 -1
@@ -1,8 +1,9 @@
1
1
  import "../penpal-BFKeZTVz.mjs";
2
2
  import { t as TunnelXManager } from "../connect-tunnel-x-BhVAej5Q.mjs";
3
- import { a as validateFormFields, i as definePaymentMethod, n as collectFraudMetadata, o as generateCheckoutRequestId, r as createCheckoutIFrame, s as withRequestId, t as $ } from "../utils-Dgyk7RkM.mjs";
4
- import { t as connectToCardIframe } from "../connect-card-BrtCmsjz.mjs";
5
- import { t as handleNextAction } from "../next-action-handlers-BZs04hYb.mjs";
3
+ import { a as validateFormFields, i as definePaymentMethod, n as collectFraudMetadata, r as createCheckoutIFrame, s as withRequestId, t as $ } from "../utils-B70Y8YcZ.mjs";
4
+ import { t as handleNextAction } from "../next-action-handlers-CTx8tRt0.mjs";
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-D3_xOA-G.mjs";
6
7
 
7
8
  //#region src/payment-methods/card.ts
8
9
  /**
@@ -38,54 +39,145 @@ const defCreateElement = (states) => {
38
39
  return (type, options) => {
39
40
  const { style, onLoaded, onFocusChange } = options;
40
41
  const mountIFrame = (parentSelector) => {
41
- const parent = $(parentSelector);
42
- const params = {
43
- checkout_token: secureToken,
44
- api_base_url: apiBaseUrl
42
+ let cleanupVgs;
43
+ let cleanupDirect;
44
+ let cancelled = false;
45
+ /**
46
+ * Performs the actual DOM mount after the tokenization mode is known.
47
+ * Runs synchronously when mode is explicitly provided, or after the
48
+ * auto-detect fetch resolves.
49
+ */
50
+ const doMount = () => {
51
+ if (cancelled) return;
52
+ const isVgsMode = states.cardTokenizationMode === "vgs";
53
+ console.log(`[PaymentKit] card.doMount(${type}) — mode: ${states.cardTokenizationMode}, vgsVaultId: ${states.vgsVaultId ?? "unset"}, vgsEnvironment: ${states.vgsEnvironment ?? "unset"}`);
54
+ if (isVgsMode && states.vgsVaultId && states.vgsEnvironment) {
55
+ if (onFocusChange) {
56
+ if (!states.vgsFocusCallbacks) states.vgsFocusCallbacks = {};
57
+ states.vgsFocusCallbacks[type] = onFocusChange;
58
+ }
59
+ if (type === "card_pan") {
60
+ const vaultId = states.vgsVaultId;
61
+ const vgsEnv = states.vgsEnvironment;
62
+ console.log(`[PaymentKit] VGS mode active — loading VGS Collect CDN for vault ${vaultId} (${vgsEnv})...`);
63
+ const VGS_CDN_TIMEOUT_MS = 15e3;
64
+ let cdnTimeoutId;
65
+ Promise.race([loadVgsCollectScript(), new Promise((_, reject) => {
66
+ cdnTimeoutId = setTimeout(() => {
67
+ console.warn(`[PaymentKit] VGS Collect CDN load timed out after ${VGS_CDN_TIMEOUT_MS / 1e3}s`);
68
+ reject(/* @__PURE__ */ new Error("VGS Collect CDN load timed out"));
69
+ }, VGS_CDN_TIMEOUT_MS);
70
+ })]).finally(() => clearTimeout(cdnTimeoutId)).then(() => {
71
+ if (cancelled) return;
72
+ console.log("[PaymentKit] VGS Collect CDN loaded — initializing form...");
73
+ const form = initVgsCollect(vaultId, vgsEnv);
74
+ if (cancelled) {
75
+ form.unmount();
76
+ return;
77
+ }
78
+ states.vgsForm = form;
79
+ const css = style ? Object.fromEntries(Object.entries(style).map(([k, v]) => [k.replace(/([A-Z])/g, "-$1").toLowerCase(), v])) : void 0;
80
+ const expSelector = parentSelector.replace(/card[-_]pan/, (m) => m.replace("pan", "exp"));
81
+ const cvcSelector = parentSelector.replace(/card[-_]pan/, (m) => m.replace("pan", "cvc"));
82
+ if (expSelector === parentSelector || cvcSelector === parentSelector) {
83
+ const msg = `[PaymentKit] Failed to derive exp/cvc selectors from "${parentSelector}". Expected a selector containing "card-pan" or "card_pan".`;
84
+ console.error(msg);
85
+ onLoaded?.();
86
+ return;
87
+ }
88
+ console.log(`[PaymentKit] Creating VGS card fields — pan: ${parentSelector}, exp: ${expSelector}, cvc: ${cvcSelector}`);
89
+ createVgsCardFields(form, {
90
+ pan: parentSelector,
91
+ exp: expSelector,
92
+ cvc: cvcSelector
93
+ }, css, states.vgsFocusCallbacks);
94
+ console.log("[PaymentKit] VGS card fields created successfully");
95
+ timingTracker.trackInputReady();
96
+ onLoaded?.();
97
+ }).catch((err) => {
98
+ console.error("[PaymentKit] Failed to load VGS Collect script. Card input will not be available:", err);
99
+ onLoaded?.();
100
+ });
101
+ }
102
+ if (type !== "card_pan") onLoaded?.();
103
+ cleanupVgs = () => {
104
+ if (type === "card_pan" && states.vgsForm) {
105
+ states.vgsForm.unmount();
106
+ states.vgsForm = void 0;
107
+ }
108
+ };
109
+ return;
110
+ }
111
+ if (isVgsMode) console.warn(`[PaymentKit] VGS mode requested but config incomplete (vgsVaultId: ${states.vgsVaultId ?? "unset"}, vgsEnvironment: ${states.vgsEnvironment ?? "unset"}) — using direct mode`);
112
+ const parent = $(parentSelector);
113
+ const params = {
114
+ checkout_token: secureToken,
115
+ api_base_url: apiBaseUrl
116
+ };
117
+ if (style) params.style = JSON.stringify(style);
118
+ const iframe = createCheckoutIFrame(type.replace("_", "-"), baseUrl, params);
119
+ cleanupDirect = () => {
120
+ cardInputConnections[type]?.destroy();
121
+ parent.removeChild(iframe);
122
+ states.cardInputConnections[type] = void 0;
123
+ };
124
+ parent.appendChild(iframe);
125
+ iframe.onload = () => {
126
+ if (type === "card_pan") timingTracker.trackInputReady();
127
+ const connection = connectToCardIframe(iframe, {
128
+ onLoaded: onLoaded || (() => {}),
129
+ onFocusChange: onFocusChange || (() => {})
130
+ });
131
+ states.cardInputConnections[type] = connection;
132
+ };
45
133
  };
46
- if (style) params.style = JSON.stringify(style);
47
- const iframe = createCheckoutIFrame(type.replace("_", "-"), baseUrl, params);
48
- parent.appendChild(iframe);
49
- iframe.onload = () => {
50
- if (type === "card_pan") timingTracker.trackInputReady();
51
- const connection = connectToCardIframe(iframe, {
52
- onLoaded: onLoaded || (() => {}),
53
- onFocusChange: onFocusChange || (() => {})
54
- });
55
- states.cardInputConnections[type] = connection;
56
- };
57
- const unmount = () => {
58
- cardInputConnections[type]?.destroy();
59
- parent.removeChild(iframe);
60
- states.cardInputConnections[type] = void 0;
61
- };
62
- return { unmount };
134
+ if (states._sessionConfigReady) states._sessionConfigReady.then(doMount);
135
+ else doMount();
136
+ return { unmount: () => {
137
+ cancelled = true;
138
+ cleanupVgs?.();
139
+ cleanupDirect?.();
140
+ } };
63
141
  };
64
142
  return { mount: mountIFrame };
65
143
  };
66
144
  };
67
145
  const defSubmitPayment = (states) => {
68
146
  const submitPayment = async (fields) => {
69
- const { timingTracker, environment } = states;
147
+ if (states._sessionConfigReady) await states._sessionConfigReady;
148
+ const isVgsMode = states.cardTokenizationMode === "vgs";
149
+ const { timingTracker, checkoutRequestId } = states;
70
150
  timingTracker.trackSubmit();
71
- const checkoutRequestId = generateCheckoutRequestId(environment);
72
151
  const requestOptions = withRequestId(checkoutRequestId);
73
- console.log(`[Card] Generated checkout_request_id: ${checkoutRequestId}`);
74
152
  const tunnelX = await TunnelXManager.createFromPenpalConnection(states.tunnelXConnection);
75
- const validateCardResult = await validateCardFields(states);
76
153
  const validateFormResult = await validateFormFields(fields);
77
- if (!(validateCardResult.isSuccess && validateFormResult.isSuccess)) {
154
+ if (!isVgsMode) {
155
+ const validateCardResult = await validateCardFields(states);
156
+ if (!(validateCardResult.isSuccess && validateFormResult.isSuccess)) {
157
+ timingTracker.trackFail(null, "validation_error", "Form validation failed");
158
+ return { errors: {
159
+ ...validateCardResult.errors,
160
+ ...validateFormResult.errors
161
+ } };
162
+ }
163
+ } else if (!validateFormResult.isSuccess) {
78
164
  timingTracker.trackFail(null, "validation_error", "Form validation failed");
79
- return { errors: {
80
- ...validateCardResult.errors,
81
- ...validateFormResult.errors
82
- } };
165
+ return { errors: validateFormResult.errors };
83
166
  }
84
167
  if (!states.cardSetupIntentId) states.cardSetupIntentId = (await tunnelX.publicEndpoints.createCardSetupIntent({ checkoutToken: states.secureToken })).cardSetupIntentId;
85
- const submitCardResult = await submitCardFields(states);
86
- if (!submitCardResult.isSuccess) {
87
- timingTracker.trackFail(null, "card_submit_error", "Card submission failed");
88
- return { errors: submitCardResult.errors };
168
+ if (isVgsMode) {
169
+ console.log("[PaymentKit] Submitting card via VGS CMP...");
170
+ const vgsSubmitResult = await submitVgsCardFields(states, tunnelX);
171
+ if (!vgsSubmitResult.isSuccess) {
172
+ timingTracker.trackFail(null, "card_submit_error", "VGS card submission failed");
173
+ return { errors: vgsSubmitResult.errors };
174
+ }
175
+ } else {
176
+ const submitCardResult = await submitCardFields(states);
177
+ if (!submitCardResult.isSuccess) {
178
+ timingTracker.trackFail(null, "card_submit_error", "Card submission failed");
179
+ return { errors: submitCardResult.errors };
180
+ }
89
181
  }
90
182
  const cardSetupIntent = await tunnelX.publicEndpoints.getCardSetupIntent({
91
183
  cardSetupIntentId: states.cardSetupIntentId,
@@ -155,6 +247,79 @@ const defSubmitPayment = (states) => {
155
247
  };
156
248
  return submitPayment;
157
249
  };
250
+ /**
251
+ * Fetches a VGS CMP access token from the backend.
252
+ */
253
+ const fetchCollectToken = async (apiBaseUrl, secureToken) => {
254
+ const resp = await fetch(`${apiBaseUrl}/api/card-setup-intents/${secureToken}/collect-token`, {
255
+ method: "POST",
256
+ headers: { "Content-Type": "application/json" }
257
+ });
258
+ if (!resp.ok) throw new Error(`Failed to get collect token: ${resp.status}`);
259
+ return (await resp.json()).access_token;
260
+ };
261
+ /**
262
+ * Creates a card via VGS CMP and sends aliases to our backend.
263
+ *
264
+ * Flow:
265
+ * 1. Fetch CMP access token from our backend
266
+ * 2. Call form.createCard() → VGS creates card, returns aliases
267
+ * 3. Send aliases + fraud metadata to our backend via updateCardSetupIntent
268
+ */
269
+ const submitVgsCardFields = async (states, tunnelX) => {
270
+ const { vgsForm, cardSetupIntentId, secureToken, apiBaseUrl } = states;
271
+ const errors = {};
272
+ if (!vgsForm) {
273
+ errors.card_pan = "required";
274
+ return {
275
+ errors,
276
+ isSuccess: false
277
+ };
278
+ }
279
+ if (!cardSetupIntentId) {
280
+ errors.root = "Card setup intent not created";
281
+ return {
282
+ errors,
283
+ isSuccess: false
284
+ };
285
+ }
286
+ try {
287
+ const accessToken = await fetchCollectToken(apiBaseUrl, secureToken);
288
+ const VGS_CREATE_CARD_TIMEOUT_MS = 3e4;
289
+ let createCardTimeoutId;
290
+ const { pan_alias, cvc_alias, exp_month, exp_year } = (await Promise.race([new Promise((resolve, reject) => {
291
+ vgsForm.createCard({
292
+ auth: accessToken,
293
+ data: { cardholder: {} }
294
+ }, (_status, cardObject) => resolve(cardObject), (error) => reject(error));
295
+ }), new Promise((_, reject) => {
296
+ createCardTimeoutId = setTimeout(() => reject(/* @__PURE__ */ new Error("VGS createCard timed out")), VGS_CREATE_CARD_TIMEOUT_MS);
297
+ })]).finally(() => clearTimeout(createCardTimeoutId))).data.attributes;
298
+ const fullYear = exp_year < 100 ? 2e3 + exp_year : exp_year;
299
+ const formattedExp = `${String(exp_month).padStart(2, "0")}/${fullYear}`;
300
+ await tunnelX.publicEndpoints.updateCardSetupIntent({
301
+ checkoutToken: secureToken,
302
+ cardSetupIntentId,
303
+ updateCardSetupIntentReq: {
304
+ cardPan: pan_alias,
305
+ cardExp: formattedExp,
306
+ cardCvc: cvc_alias,
307
+ fraudMetadata: collectFraudMetadata()
308
+ }
309
+ });
310
+ return {
311
+ errors: {},
312
+ isSuccess: true
313
+ };
314
+ } catch (err) {
315
+ console.error("[PaymentKit] VGS CMP createCard failed:", err);
316
+ errors.root = "Card submission failed";
317
+ return {
318
+ errors,
319
+ isSuccess: false
320
+ };
321
+ }
322
+ };
158
323
  const submitCardFields = async (states) => {
159
324
  const errors = {};
160
325
  const { cardSetupIntentId, cardInputConnections } = states;
@@ -188,16 +353,18 @@ const validateCardFields = async (states) => {
188
353
  };
189
354
  };
190
355
  const CardPaymentMethod = definePaymentMethod((paymentKitStates) => {
191
- const localStates = {
192
- ...paymentKitStates,
193
- cardInputConnections: {}
194
- };
356
+ const localStates = Object.create(paymentKitStates);
357
+ localStates.cardInputConnections = {};
195
358
  return {
196
359
  name: "card",
197
360
  externalFuncs: { createElement: defCreateElement(localStates) },
198
361
  internalFuncs: {
199
362
  submitPayment: defSubmitPayment(localStates),
200
363
  cleanup: () => {
364
+ if (localStates.vgsForm) {
365
+ localStates.vgsForm.unmount();
366
+ localStates.vgsForm = void 0;
367
+ }
201
368
  for (const connection of Object.values(localStates.cardInputConnections)) connection?.destroy();
202
369
  localStates.cardInputConnections = {};
203
370
  }
@@ -1 +1 @@
1
- {"version":3,"file":"card.mjs","names":["params: Record<string, string>","submitPayment: TInternalFuncs[\"submitPayment\"]","errors: PaymentKitErrors","localStates: CardStates"],"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 generateCheckoutRequestId,\n validateFormFields,\n withRequestId,\n} from \"../utils\";\nimport { handleNextAction } from \"./next-action-handlers\";\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 return {\n email: fields.customer_email || undefined,\n firstName,\n lastName,\n billingAddress:\n fields.customer_country || fields.customer_zip_code\n ? {\n country: fields.customer_country || undefined,\n zipCode: fields.customer_zip_code || undefined,\n }\n : undefined,\n };\n};\n\ntype CardStates = PaymentKitStates & {\n cardSetupIntentId?: string;\n cardInputConnections: Partial<Record<PenpalCardInputType, IFrameConnection>>;\n};\n\ntype CreateElementOptions = Partial<Parameters<typeof connectToCardIframe>[1]> & {\n style?: Record<string, 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 } = options;\n\n const mountIFrame = (parentSelector: string) => {\n const parent = $(parentSelector);\n const params: Record<string, string> = {\n checkout_token: secureToken,\n api_base_url: apiBaseUrl,\n };\n\n if (style) {\n params.style = JSON.stringify(style);\n }\n\n const iframe = createCheckoutIFrame(type.replace(\"_\", \"-\"), baseUrl, params);\n parent.appendChild(iframe);\n\n iframe.onload = () => {\n // Track input ready only for first iframe (card_pan)\n // This indicates the checkout form is ready for user input\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 const unmount = () => {\n const connection = cardInputConnections[type];\n connection?.destroy();\n parent.removeChild(iframe);\n states.cardInputConnections[type] = undefined;\n };\n\n return { unmount };\n };\n\n return { mount: mountIFrame };\n };\n};\n\nconst defSubmitPayment = (states: CardStates) => {\n const submitPayment: TInternalFuncs[\"submitPayment\"] = async (fields) => {\n const { timingTracker, environment } = states;\n\n // Track submit event\n timingTracker.trackSubmit();\n\n // Generate checkout request ID for correlating all API calls in this submission\n const checkoutRequestId = generateCheckoutRequestId(environment);\n const requestOptions = withRequestId(checkoutRequestId);\n console.log(`[Card] Generated checkout_request_id: ${checkoutRequestId}`);\n\n const tunnelX = await TunnelXManager.createFromPenpalConnection(states.tunnelXConnection);\n\n // Step 1. Validate card and form values.\n const validateCardResult = await validateCardFields(states);\n const validateFormResult = await validateFormFields(fields);\n if (!(validateCardResult.isSuccess && validateFormResult.isSuccess)) {\n timingTracker.trackFail(null, \"validation_error\", \"Form validation failed\");\n return { errors: { ...validateCardResult.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 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 // 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\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 const localStates: CardStates = { ...paymentKitStates, 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 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":["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 return {\n email: fields.customer_email || undefined,\n firstName,\n lastName,\n billingAddress:\n fields.customer_country || fields.customer_zip_code\n ? {\n country: fields.customer_country || undefined,\n zipCode: fields.customer_zip_code || 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};\n\ntype CreateElementOptions = Partial<Parameters<typeof connectToCardIframe>[1]> & {\n style?: Record<string, 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 } = 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 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 so createVgsCardFields can wire each\n // field to its own callback. All three createElement calls complete\n // before the CDN script loads, so all callbacks are available by then.\n if (onFocusChange) {\n if (!states.vgsFocusCallbacks) states.vgsFocusCallbacks = {};\n states.vgsFocusCallbacks[type] = onFocusChange;\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 // Derive exp/cvc selectors from the pan selector.\n // Handles both hyphenated (#pk-card-pan) and underscored (#card_pan) IDs.\n const expSelector = parentSelector.replace(/card[-_]pan/, (m) => m.replace(\"pan\", \"exp\"));\n const cvcSelector = parentSelector.replace(/card[-_]pan/, (m) => m.replace(\"pan\", \"cvc\"));\n if (expSelector === parentSelector || cvcSelector === parentSelector) {\n const msg =\n `[PaymentKit] Failed to derive exp/cvc selectors from \"${parentSelector}\". ` +\n `Expected a selector containing \"card-pan\" or \"card_pan\".`;\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 );\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);\n const params: Record<string, string> = {\n checkout_token: secureToken,\n api_base_url: apiBaseUrl,\n };\n\n if (style) {\n params.style = JSON.stringify(style);\n }\n\n const iframe = createCheckoutIFrame(type.replace(\"_\", \"-\"), baseUrl, params);\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) => {\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 validateFormResult = 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 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,8 +1,8 @@
1
- import { r as PaymentMethod } from "../types-CPuloCtF.mjs";
2
- import "../connect-card-DTfXuTsW.mjs";
3
- import "../connect-tunnel-x-Dxcg5Y7Y.mjs";
4
- import { a as GooglePayEncryptedToken } from "../airwallex-google-pay-adapter-Be2Af4N9.mjs";
5
- import { n as GooglePayMockScenario } from "../stripe-google-pay-adapter-xktEycOD.mjs";
1
+ import { r as PaymentMethod } from "../types-CZk3V0Qt.mjs";
2
+ import "../connect-card-Cxy51W6t.mjs";
3
+ import "../connect-tunnel-x-lv6Wtdme.mjs";
4
+ import { a as GooglePayEncryptedToken } from "../airwallex-google-pay-adapter-CY379Rre.mjs";
5
+ import { n as GooglePayMockScenario } from "../stripe-google-pay-adapter-C3NCBSO3.mjs";
6
6
 
7
7
  //#region src/payment-methods/google-pay.d.ts
8
8
  type GooglePayCustomerInfo = {
@@ -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;IAkeN,kBAAA,CAAA,EASJ;;;;;;KAvhBU,sBAAA;;;;;;;;;;;;;;KAuBA,uBAAA;qBACS;;;KAKT,sBAAA;;;;;;KAOA,wBAAA;;;;;gBAKI;;KAGJ,sBAAA;;gBAEI;iBACC;;cA+dX,wBASJ"}
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,7 +1,7 @@
1
- import { i as definePaymentMethod, n as collectFraudMetadata, o as generateCheckoutRequestId } from "../utils-Dgyk7RkM.mjs";
2
- import { n as AirwallexGooglePayMockScenario, t as AirwallexGooglePayAdapter } from "../airwallex-google-pay-adapter-BCmTZip5.mjs";
3
- import { t as handleNextAction } from "../next-action-handlers-BZs04hYb.mjs";
4
- import { n as StripeGooglePayAdapter, t as GooglePayMockScenario } from "../stripe-google-pay-adapter-DjrgDYWe.mjs";
1
+ import { i as definePaymentMethod, n as collectFraudMetadata, o as getOrCreateCheckoutRequestId } from "../utils-B70Y8YcZ.mjs";
2
+ import { n as AirwallexGooglePayMockScenario, t as AirwallexGooglePayAdapter } from "../airwallex-google-pay-adapter-CHol_8f2.mjs";
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";
5
5
 
6
6
  //#region src/payment-methods/google-pay.ts
7
7
  async function apiCall(url, options, checkoutRequestId) {
@@ -24,7 +24,6 @@ async function apiCall(url, options, checkoutRequestId) {
24
24
  }
25
25
  function validateOptions(options) {
26
26
  if (!options?.processorId) return { processor_id: "Processor ID is required" };
27
- if (!options?.customerInfo?.first_name || !options?.customerInfo?.last_name) return { customer_name: "Customer first and last name are required" };
28
27
  return null;
29
28
  }
30
29
  function getMockScenarioStr(mockScenario) {
@@ -230,8 +229,8 @@ const defSubmitPayment = (states) => {
230
229
  if (validationError) return { errors: validationError };
231
230
  try {
232
231
  const mockScenarioStr = getMockScenarioStr(gpayOptions.mockScenario);
233
- const checkoutRequestId = generateCheckoutRequestId(environment);
234
- console.log(`[GooglePay] Generated checkout_request_id: ${checkoutRequestId}`);
232
+ const checkoutRequestId = getOrCreateCheckoutRequestId(environment);
233
+ console.log(`[GooglePay] Using checkout_request_id: ${checkoutRequestId}`);
235
234
  const startResult = await callStartEndpoint(apiBaseUrl, secureToken, gpayOptions, mockScenarioStr, checkoutRequestId);
236
235
  if (startResult.error || !startResult.data) return { errors: { google_pay: startResult.error || "Failed to start Google Pay" } };
237
236
  const startData = startResult.data;
@@ -1 +1 @@
1
- {"version":3,"file":"google-pay.mjs","names":["airwallexMockScenario: AirwallexGooglePayMockScenario","config: AirwallexGooglePayConfig","requestBody: GooglePayConfirmRequest","submitPayment: TInternalFuncs[\"submitPayment\"]"],"sources":["../../src/payment-methods/google-pay.ts"],"sourcesContent":["import type { PaymentKitErrors, PaymentKitStates, TInternalFuncs } from \"../types\";\nimport { collectFraudMetadata, definePaymentMethod, generateCheckoutRequestId } from \"../utils\";\nimport {\n AirwallexGooglePayAdapter,\n type AirwallexGooglePayConfig,\n AirwallexGooglePayMockScenario,\n type GooglePayEncryptedToken,\n} from \"./airwallex-google-pay-adapter\";\nimport { handleNextAction } from \"./next-action-handlers\";\nimport { GooglePayMockScenario, StripeGooglePayAdapter } from \"./stripe-google-pay-adapter\";\n\n// Google Pay-specific types\nexport type GooglePayCustomerInfo = {\n first_name: string;\n last_name: string;\n};\n\nexport type GooglePayStartRequest = {\n processor_id: string;\n customer_info: GooglePayCustomerInfo;\n fraud_metadata: {\n ipAddress?: string;\n browserInfo?: { [key: string]: unknown };\n processorFraudInfo?: { [key: string]: unknown };\n };\n mock_scenario?: string;\n};\n\nexport type GooglePayStartResponse = {\n checkout_attempt_id: string;\n amount: number;\n amount_display: string; // Formatted for Google Pay (e.g., \"100.00\" for USD, \"1000\" for JPY)\n currency: string;\n country: string;\n\n // Processor discriminator\n processor: \"stripe\" | \"airwallex\";\n\n // Environment flag\n is_sandbox?: boolean;\n\n // Stripe-specific fields (when processor=\"stripe\")\n client_secret?: string;\n stripe_pk?: string;\n\n // Airwallex-specific fields (when processor=\"airwallex\")\n merchant_name?: string;\n airwallex_account_id?: string;\n google_merchant_id?: string; // Google-assigned merchant ID for production (BCR2DN...)\n};\n\nexport type GooglePayConfirmRequest = {\n google_pay_token?: GooglePayEncryptedToken; // Required for Airwallex\n mock_scenario?: string;\n};\n\n// Airwallex 3DS next action type (matches backend snake_case response)\nexport type Airwallex3dsNextAction = {\n type: \"airwallex_3ds\";\n url: string;\n method: string;\n payment_intent_id: string;\n};\n\nexport type GooglePayConfirmResponse = {\n charge_status: \"success\" | \"fail\" | \"pending\";\n transaction_id?: string;\n error_message?: string;\n checkout_attempt_id: string;\n next_action?: Airwallex3dsNextAction; // Present when charge_status=\"pending\" for 3DS\n};\n\nexport type GooglePaySubmitOptions = {\n processorId: string;\n customerInfo: GooglePayCustomerInfo;\n mockScenario?: GooglePayMockScenario;\n};\n\ntype GooglePayResult =\n | { data: { [key: string]: unknown }; errors?: never }\n | { data?: never; errors: PaymentKitErrors };\n\n// =============================================================================\n// Helper Functions\n// =============================================================================\n\nasync function apiCall<T>(\n url: string,\n options: RequestInit,\n checkoutRequestId?: string,\n): Promise<{ data?: T; error?: string }> {\n // Add x-request-id header if provided\n const headers = new Headers(options.headers);\n if (checkoutRequestId) {\n headers.set(\"x-request-id\", checkoutRequestId);\n }\n\n const response = await fetch(url, { ...options, headers });\n if (!response.ok) {\n let errorMessage = `Request failed (${response.status})`;\n try {\n const errorData = await response.json();\n errorMessage = errorData.detail || errorMessage;\n } catch {\n errorMessage = response.statusText || errorMessage;\n }\n return { error: errorMessage };\n }\n return { data: await response.json() };\n}\n\nfunction validateOptions(options: GooglePaySubmitOptions): PaymentKitErrors | null {\n if (!options?.processorId) {\n return { processor_id: \"Processor ID is required\" };\n }\n if (!options?.customerInfo?.first_name || !options?.customerInfo?.last_name) {\n return { customer_name: \"Customer first and last name are required\" };\n }\n return null;\n}\n\nfunction getMockScenarioStr(mockScenario?: GooglePayMockScenario): string | undefined {\n return mockScenario && mockScenario !== GooglePayMockScenario.None ? mockScenario : undefined;\n}\n\nasync function callStartEndpoint(\n apiBaseUrl: string,\n secureToken: string,\n options: GooglePaySubmitOptions,\n mockScenarioStr?: string,\n checkoutRequestId?: string,\n): Promise<{ data?: GooglePayStartResponse; error?: string }> {\n return apiCall<GooglePayStartResponse>(\n `${apiBaseUrl}/api/checkout/${secureToken}/google-pay/start`,\n {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({\n processor_id: options.processorId,\n customer_info: options.customerInfo,\n fraud_metadata: collectFraudMetadata(),\n mock_scenario: mockScenarioStr,\n } as GooglePayStartRequest),\n },\n checkoutRequestId,\n );\n}\n\n// =============================================================================\n// Stripe Flow\n// =============================================================================\n\nfunction initializeStripeAdapter(\n stripePk: string,\n startData: GooglePayStartResponse,\n mockScenario?: GooglePayMockScenario,\n): { adapter?: StripeGooglePayAdapter; error?: string } {\n const adapter = new StripeGooglePayAdapter(mockScenario);\n\n if (!adapter.initialize(stripePk)) {\n return { error: 'Stripe.js not loaded. Add <script src=\"https://js.stripe.com/v3/\"></script> to your page.' };\n }\n\n const prConfig = {\n country: startData.country,\n currency: startData.currency.toLowerCase(),\n total: { label: \"Total\", amount: startData.amount },\n requestPayerName: true,\n requestPayerEmail: true,\n };\n adapter.createPaymentRequest(prConfig);\n return { adapter };\n}\n\nasync function runStripeFlow(\n startData: GooglePayStartResponse,\n mockScenario?: GooglePayMockScenario,\n): Promise<{ success: boolean; error?: string }> {\n if (!startData.stripe_pk || !startData.client_secret) {\n return { success: false, error: \"Stripe credentials not provided\" };\n }\n\n // Initialize Stripe adapter\n const { adapter, error: adapterError } = initializeStripeAdapter(startData.stripe_pk, startData, mockScenario);\n if (!adapter) {\n return { success: false, error: adapterError };\n }\n\n // Check availability\n const isAvailable = await adapter.canMakePayment();\n\n if (!isAvailable) {\n return { success: false, error: \"Google Pay not available on this device\" };\n }\n\n // Show payment sheet\n const paymentResult = await adapter.showPaymentSheet();\n\n if (!paymentResult.success) {\n if (\"cancelled\" in paymentResult && paymentResult.cancelled) {\n return { success: false, error: \"Google Pay cancelled by user\" };\n }\n const errorMessage = \"error\" in paymentResult ? paymentResult.error : \"Unknown error\";\n return { success: false, error: errorMessage };\n }\n\n // Confirm with Stripe SDK\n const confirmResult = await adapter.confirmCardSetup(startData.client_secret, paymentResult.paymentMethodId);\n\n if (!confirmResult.success) {\n paymentResult.complete(\"fail\");\n return { success: false, error: \"error\" in confirmResult ? confirmResult.error : \"Card setup failed\" };\n }\n\n paymentResult.complete(\"success\");\n return { success: true };\n}\n\n// =============================================================================\n// Airwallex Flow\n// =============================================================================\n\nfunction initializeAirwallexAdapter(\n startData: GooglePayStartResponse,\n mockScenario?: GooglePayMockScenario,\n): {\n adapter?: AirwallexGooglePayAdapter;\n error?: string;\n} {\n if (!startData.merchant_name || !startData.airwallex_account_id) {\n return { error: \"Airwallex Google Pay credentials not provided\" };\n }\n\n // Convert GooglePayMockScenario to AirwallexGooglePayMockScenario\n let airwallexMockScenario: AirwallexGooglePayMockScenario = AirwallexGooglePayMockScenario.None;\n if (mockScenario === GooglePayMockScenario.Success) {\n airwallexMockScenario = AirwallexGooglePayMockScenario.Success;\n } else if (mockScenario === GooglePayMockScenario.Cancelled) {\n airwallexMockScenario = AirwallexGooglePayMockScenario.Cancelled;\n }\n\n const adapter = new AirwallexGooglePayAdapter(airwallexMockScenario);\n\n const config: AirwallexGooglePayConfig = {\n merchantId: startData.merchant_name,\n gatewayMerchantId: startData.airwallex_account_id,\n amountDisplay: startData.amount_display,\n currency: startData.currency,\n country: startData.country,\n isProduction: startData.is_sandbox === false,\n googleMerchantId: startData.google_merchant_id,\n };\n\n if (!adapter.initialize(config)) {\n return {\n error:\n 'Google Pay API not loaded. Add <script src=\"https://pay.google.com/gp/p/js/pay.js\"></script> to your page.',\n };\n }\n\n return { adapter };\n}\n\nasync function runAirwallexFlow(\n startData: GooglePayStartResponse,\n mockScenario?: GooglePayMockScenario,\n): Promise<{ success: boolean; token?: GooglePayEncryptedToken; error?: string }> {\n // Initialize Airwallex adapter\n const { adapter, error: adapterError } = initializeAirwallexAdapter(startData, mockScenario);\n if (!adapter) {\n return { success: false, error: adapterError };\n }\n\n // Check availability\n const isAvailable = await adapter.canMakePayment();\n\n if (!isAvailable) {\n return { success: false, error: \"Google Pay not available on this device\" };\n }\n\n // Show payment sheet\n const paymentResult = await adapter.showPaymentSheet();\n\n if (!paymentResult.success) {\n if (\"cancelled\" in paymentResult && paymentResult.cancelled) {\n return { success: false, error: \"Google Pay cancelled by user\" };\n }\n const errorMessage = \"error\" in paymentResult ? paymentResult.error : \"Unknown error\";\n return { success: false, error: errorMessage };\n }\n\n // Return the token for passing to backend\n return { success: true, token: paymentResult.token };\n}\n\n// =============================================================================\n// Confirm & Verify Endpoints\n// =============================================================================\n\n/**\n * Call /confirm endpoint - processes Google Pay token (Airwallex) or records setup (Stripe).\n * For Airwallex, may return next_action if 3DS is required.\n */\nasync function callConfirmEndpoint(\n apiBaseUrl: string,\n secureToken: string,\n googlePayToken?: GooglePayEncryptedToken,\n mockScenarioStr?: string,\n checkoutRequestId?: string,\n): Promise<GooglePayConfirmResponse> {\n const requestBody: GooglePayConfirmRequest = {\n mock_scenario: mockScenarioStr,\n };\n\n if (googlePayToken) {\n requestBody.google_pay_token = googlePayToken;\n }\n\n const result = await apiCall<GooglePayConfirmResponse>(\n `${apiBaseUrl}/api/checkout/${secureToken}/google-pay/confirm`,\n {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify(requestBody),\n },\n checkoutRequestId,\n );\n\n if (result.error || !result.data) {\n return {\n charge_status: \"fail\",\n error_message: result.error || \"Failed to confirm payment\",\n checkout_attempt_id: \"\",\n };\n }\n\n return result.data;\n}\n\n/**\n * Call /verify endpoint - verifies 3DS completion for Airwallex.\n * Called after user completes 3DS challenge. May return another next_action for retry.\n */\nasync function callVerifyEndpoint(\n apiBaseUrl: string,\n secureToken: string,\n checkoutRequestId?: string,\n): Promise<GooglePayConfirmResponse> {\n const result = await apiCall<GooglePayConfirmResponse>(\n `${apiBaseUrl}/api/checkout/${secureToken}/google-pay/verify`,\n {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n },\n checkoutRequestId,\n );\n\n if (result.error || !result.data) {\n return {\n charge_status: \"fail\",\n error_message: result.error || \"Failed to verify payment\",\n checkout_attempt_id: \"\",\n };\n }\n\n return result.data;\n}\n\n/**\n * Convert GooglePayConfirmResponse to GooglePayResult for return to caller.\n */\nfunction toGooglePayResult(response: GooglePayConfirmResponse, secureToken: string): GooglePayResult {\n if (response.charge_status === \"success\") {\n return {\n data: {\n id: response.transaction_id,\n checkoutAttemptId: response.checkout_attempt_id,\n checkoutSessionId: secureToken,\n state: \"checkout_succeeded\",\n },\n };\n }\n return { errors: { google_pay: response.error_message || \"Payment failed\" } };\n}\n\n// =============================================================================\n// Stripe Confirm (Simple - No 3DS Loop)\n// =============================================================================\n\n/**\n * Confirm Stripe Google Pay - simple flow without 3DS loop.\n * Stripe.js handles 3DS internally during confirmCardSetup().\n */\nasync function confirmStripeGooglePay(\n apiBaseUrl: string,\n secureToken: string,\n mockScenarioStr?: string,\n checkoutRequestId?: string,\n): Promise<GooglePayResult> {\n const response = await callConfirmEndpoint(apiBaseUrl, secureToken, undefined, mockScenarioStr, checkoutRequestId);\n return toGooglePayResult(response, secureToken);\n}\n\n// =============================================================================\n// Airwallex Confirm with 3DS Loop\n// =============================================================================\n\nconst MAX_USER_ACTIONS = 5;\n\n/**\n * Confirm Airwallex Google Pay with 3DS loop support.\n *\n * Flow:\n * 1. Call /confirm with token → may return next_action (3DS)\n * 2. If next_action: handle 3DS, then call /verify\n * 3. Loop if /verify returns another next_action (3DS retry)\n * 4. Return final result\n *\n * This mirrors the card.ts pattern for consistency.\n */\nasync function confirmAirwallexGooglePay(\n apiBaseUrl: string,\n secureToken: string,\n googlePayToken: GooglePayEncryptedToken,\n mockScenarioStr?: string,\n checkoutRequestId?: string,\n): Promise<GooglePayResult> {\n let userActionCount = 0;\n\n // Step 1: Initial confirm with token\n let response = await callConfirmEndpoint(apiBaseUrl, secureToken, googlePayToken, mockScenarioStr, checkoutRequestId);\n\n // Step 2: Handle 3DS loop (if required)\n while (response.charge_status === \"pending\" && response.next_action && userActionCount < MAX_USER_ACTIONS) {\n userActionCount++;\n\n // Handle 3DS challenge\n const actionResult = await handleNextAction(response.next_action);\n\n // Always call verify endpoint so backend can conclude properly\n // (even if 3DS failed, backend needs to know)\n const verifyResponse = await callVerifyEndpoint(apiBaseUrl, secureToken, checkoutRequestId);\n\n // Check if another 3DS action is required (retry scenario)\n if (verifyResponse.charge_status === \"pending\" && verifyResponse.next_action) {\n if (!actionResult.success) {\n console.log(\"[GooglePay:Airwallex] 3DS failed but retry available, continuing loop...\");\n }\n response = verifyResponse;\n continue;\n }\n\n // No more actions - check final result\n if (!actionResult.success) {\n // 3DS failed and no retry available\n return {\n errors: {\n google_pay: verifyResponse.error_message || actionResult.error || \"3DS authentication failed\",\n },\n };\n }\n\n // 3DS succeeded - return verify result\n return toGooglePayResult(verifyResponse, secureToken);\n }\n\n // Check for max attempts exceeded\n if (userActionCount >= MAX_USER_ACTIONS) {\n return { errors: { google_pay: \"Too many authentication attempts. Please try again.\" } };\n }\n\n // No 3DS required - return initial confirm result\n return toGooglePayResult(response, secureToken);\n}\n\n// =============================================================================\n// Main Submit Function\n// =============================================================================\n\nconst defSubmitPayment = (states: PaymentKitStates) => {\n const submitPayment: TInternalFuncs[\"submitPayment\"] = async (_fields, options) => {\n const { apiBaseUrl, secureToken, environment } = states;\n const gpayOptions = options as GooglePaySubmitOptions;\n\n // Validate options\n const validationError = validateOptions(gpayOptions);\n if (validationError) {\n return { errors: validationError };\n }\n\n try {\n const mockScenarioStr = getMockScenarioStr(gpayOptions.mockScenario);\n\n // Generate checkout request ID for correlating all API calls in this submission\n const checkoutRequestId = generateCheckoutRequestId(environment);\n console.log(`[GooglePay] Generated checkout_request_id: ${checkoutRequestId}`);\n\n // Step 1: Start Google Pay flow - backend selects processor\n const startResult = await callStartEndpoint(\n apiBaseUrl,\n secureToken,\n gpayOptions,\n mockScenarioStr,\n checkoutRequestId,\n );\n if (startResult.error || !startResult.data) {\n return { errors: { google_pay: startResult.error || \"Failed to start Google Pay\" } };\n }\n\n const startData = startResult.data;\n\n // Step 2 & 3: Run processor-specific flow and confirm\n if (startData.processor === \"stripe\") {\n // Stripe flow: Uses Stripe.js PaymentRequest API (handles 3DS internally)\n const stripeResult = await runStripeFlow(startData, gpayOptions.mockScenario);\n if (!stripeResult.success) {\n return { errors: { google_pay: stripeResult.error } };\n }\n // Confirm with backend (no token needed, Stripe handled it)\n return await confirmStripeGooglePay(apiBaseUrl, secureToken, mockScenarioStr, checkoutRequestId);\n }\n\n if (startData.processor === \"airwallex\") {\n // Airwallex flow: Uses Google Pay API directly, may need 3DS\n const airwallexResult = await runAirwallexFlow(startData, gpayOptions.mockScenario);\n if (!airwallexResult.success) {\n return { errors: { google_pay: airwallexResult.error } };\n }\n if (!airwallexResult.token) {\n return { errors: { google_pay: \"Google Pay token not received\" } };\n }\n // Confirm with 3DS loop support\n return await confirmAirwallexGooglePay(\n apiBaseUrl,\n secureToken,\n airwallexResult.token,\n mockScenarioStr,\n checkoutRequestId,\n );\n }\n\n return { errors: { google_pay: `Unsupported processor: ${startData.processor}` } };\n } catch (error) {\n return { errors: { google_pay: `Google Pay error: ${error}` } };\n }\n };\n\n return submitPayment;\n};\n\n// =============================================================================\n// Payment Method Definition\n// =============================================================================\n\nconst GooglePayPaymentMethod = definePaymentMethod((paymentKitStates) => {\n return {\n name: \"google_pay\",\n externalFuncs: {},\n internalFuncs: {\n submitPayment: defSubmitPayment(paymentKitStates),\n cleanup: () => {},\n },\n };\n});\n\nexport { GooglePayMockScenario };\n\nexport default GooglePayPaymentMethod;\n"],"mappings":"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"}
1
+ {"version":3,"file":"google-pay.mjs","names":["airwallexMockScenario: AirwallexGooglePayMockScenario","config: AirwallexGooglePayConfig","requestBody: GooglePayConfirmRequest","submitPayment: TInternalFuncs[\"submitPayment\"]"],"sources":["../../src/payment-methods/google-pay.ts"],"sourcesContent":["import type { PaymentKitErrors, PaymentKitStates, TInternalFuncs } from \"../types\";\nimport { collectFraudMetadata, definePaymentMethod, getOrCreateCheckoutRequestId } from \"../utils\";\nimport {\n AirwallexGooglePayAdapter,\n type AirwallexGooglePayConfig,\n AirwallexGooglePayMockScenario,\n type GooglePayEncryptedToken,\n} from \"./airwallex-google-pay-adapter\";\nimport { handleNextAction } from \"./next-action-handlers\";\nimport { GooglePayMockScenario, StripeGooglePayAdapter } from \"./stripe-google-pay-adapter\";\n\n// Google Pay-specific types\nexport type GooglePayCustomerInfo = {\n first_name: string;\n last_name: string;\n};\n\nexport type GooglePayStartRequest = {\n processor_id: string;\n customer_info: GooglePayCustomerInfo;\n fraud_metadata: {\n ipAddress?: string;\n browserInfo?: { [key: string]: unknown };\n processorFraudInfo?: { [key: string]: unknown };\n };\n mock_scenario?: string;\n};\n\nexport type GooglePayStartResponse = {\n checkout_attempt_id: string;\n amount: number;\n amount_display: string; // Formatted for Google Pay (e.g., \"100.00\" for USD, \"1000\" for JPY)\n currency: string;\n country: string;\n\n // Processor discriminator\n processor: \"stripe\" | \"airwallex\";\n\n // Environment flag\n is_sandbox?: boolean;\n\n // Stripe-specific fields (when processor=\"stripe\")\n client_secret?: string;\n stripe_pk?: string;\n\n // Airwallex-specific fields (when processor=\"airwallex\")\n merchant_name?: string;\n airwallex_account_id?: string;\n google_merchant_id?: string; // Google-assigned merchant ID for production (BCR2DN...)\n};\n\nexport type GooglePayConfirmRequest = {\n google_pay_token?: GooglePayEncryptedToken; // Required for Airwallex\n mock_scenario?: string;\n};\n\n// Airwallex 3DS next action type (matches backend snake_case response)\nexport type Airwallex3dsNextAction = {\n type: \"airwallex_3ds\";\n url: string;\n method: string;\n payment_intent_id: string;\n};\n\nexport type GooglePayConfirmResponse = {\n charge_status: \"success\" | \"fail\" | \"pending\";\n transaction_id?: string;\n error_message?: string;\n checkout_attempt_id: string;\n next_action?: Airwallex3dsNextAction; // Present when charge_status=\"pending\" for 3DS\n};\n\nexport type GooglePaySubmitOptions = {\n processorId: string;\n customerInfo: GooglePayCustomerInfo;\n mockScenario?: GooglePayMockScenario;\n};\n\ntype GooglePayResult =\n | { data: { [key: string]: unknown }; errors?: never }\n | { data?: never; errors: PaymentKitErrors };\n\n// =============================================================================\n// Helper Functions\n// =============================================================================\n\nasync function apiCall<T>(\n url: string,\n options: RequestInit,\n checkoutRequestId?: string,\n): Promise<{ data?: T; error?: string }> {\n // Add x-request-id header if provided\n const headers = new Headers(options.headers);\n if (checkoutRequestId) {\n headers.set(\"x-request-id\", checkoutRequestId);\n }\n\n const response = await fetch(url, { ...options, headers });\n if (!response.ok) {\n let errorMessage = `Request failed (${response.status})`;\n try {\n const errorData = await response.json();\n errorMessage = errorData.detail || errorMessage;\n } catch {\n errorMessage = response.statusText || errorMessage;\n }\n return { error: errorMessage };\n }\n return { data: await response.json() };\n}\n\nfunction validateOptions(options: GooglePaySubmitOptions): PaymentKitErrors | null {\n if (!options?.processorId) {\n return { processor_id: \"Processor ID is required\" };\n }\n // Customer name is optional — Google Pay collects it from the payment sheet\n return null;\n}\n\nfunction getMockScenarioStr(mockScenario?: GooglePayMockScenario): string | undefined {\n return mockScenario && mockScenario !== GooglePayMockScenario.None ? mockScenario : undefined;\n}\n\nasync function callStartEndpoint(\n apiBaseUrl: string,\n secureToken: string,\n options: GooglePaySubmitOptions,\n mockScenarioStr?: string,\n checkoutRequestId?: string,\n): Promise<{ data?: GooglePayStartResponse; error?: string }> {\n return apiCall<GooglePayStartResponse>(\n `${apiBaseUrl}/api/checkout/${secureToken}/google-pay/start`,\n {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({\n processor_id: options.processorId,\n customer_info: options.customerInfo,\n fraud_metadata: collectFraudMetadata(),\n mock_scenario: mockScenarioStr,\n } as GooglePayStartRequest),\n },\n checkoutRequestId,\n );\n}\n\n// =============================================================================\n// Stripe Flow\n// =============================================================================\n\nfunction initializeStripeAdapter(\n stripePk: string,\n startData: GooglePayStartResponse,\n mockScenario?: GooglePayMockScenario,\n): { adapter?: StripeGooglePayAdapter; error?: string } {\n const adapter = new StripeGooglePayAdapter(mockScenario);\n\n if (!adapter.initialize(stripePk)) {\n return { error: 'Stripe.js not loaded. Add <script src=\"https://js.stripe.com/v3/\"></script> to your page.' };\n }\n\n const prConfig = {\n country: startData.country,\n currency: startData.currency.toLowerCase(),\n total: { label: \"Total\", amount: startData.amount },\n requestPayerName: true,\n requestPayerEmail: true,\n };\n adapter.createPaymentRequest(prConfig);\n return { adapter };\n}\n\nasync function runStripeFlow(\n startData: GooglePayStartResponse,\n mockScenario?: GooglePayMockScenario,\n): Promise<{ success: boolean; error?: string }> {\n if (!startData.stripe_pk || !startData.client_secret) {\n return { success: false, error: \"Stripe credentials not provided\" };\n }\n\n // Initialize Stripe adapter\n const { adapter, error: adapterError } = initializeStripeAdapter(startData.stripe_pk, startData, mockScenario);\n if (!adapter) {\n return { success: false, error: adapterError };\n }\n\n // Check availability\n const isAvailable = await adapter.canMakePayment();\n\n if (!isAvailable) {\n return { success: false, error: \"Google Pay not available on this device\" };\n }\n\n // Show payment sheet\n const paymentResult = await adapter.showPaymentSheet();\n\n if (!paymentResult.success) {\n if (\"cancelled\" in paymentResult && paymentResult.cancelled) {\n return { success: false, error: \"Google Pay cancelled by user\" };\n }\n const errorMessage = \"error\" in paymentResult ? paymentResult.error : \"Unknown error\";\n return { success: false, error: errorMessage };\n }\n\n // Confirm with Stripe SDK\n const confirmResult = await adapter.confirmCardSetup(startData.client_secret, paymentResult.paymentMethodId);\n\n if (!confirmResult.success) {\n paymentResult.complete(\"fail\");\n return { success: false, error: \"error\" in confirmResult ? confirmResult.error : \"Card setup failed\" };\n }\n\n paymentResult.complete(\"success\");\n return { success: true };\n}\n\n// =============================================================================\n// Airwallex Flow\n// =============================================================================\n\nfunction initializeAirwallexAdapter(\n startData: GooglePayStartResponse,\n mockScenario?: GooglePayMockScenario,\n): {\n adapter?: AirwallexGooglePayAdapter;\n error?: string;\n} {\n if (!startData.merchant_name || !startData.airwallex_account_id) {\n return { error: \"Airwallex Google Pay credentials not provided\" };\n }\n\n // Convert GooglePayMockScenario to AirwallexGooglePayMockScenario\n let airwallexMockScenario: AirwallexGooglePayMockScenario = AirwallexGooglePayMockScenario.None;\n if (mockScenario === GooglePayMockScenario.Success) {\n airwallexMockScenario = AirwallexGooglePayMockScenario.Success;\n } else if (mockScenario === GooglePayMockScenario.Cancelled) {\n airwallexMockScenario = AirwallexGooglePayMockScenario.Cancelled;\n }\n\n const adapter = new AirwallexGooglePayAdapter(airwallexMockScenario);\n\n const config: AirwallexGooglePayConfig = {\n merchantId: startData.merchant_name,\n gatewayMerchantId: startData.airwallex_account_id,\n amountDisplay: startData.amount_display,\n currency: startData.currency,\n country: startData.country,\n isProduction: startData.is_sandbox === false,\n googleMerchantId: startData.google_merchant_id,\n };\n\n if (!adapter.initialize(config)) {\n return {\n error:\n 'Google Pay API not loaded. Add <script src=\"https://pay.google.com/gp/p/js/pay.js\"></script> to your page.',\n };\n }\n\n return { adapter };\n}\n\nasync function runAirwallexFlow(\n startData: GooglePayStartResponse,\n mockScenario?: GooglePayMockScenario,\n): Promise<{ success: boolean; token?: GooglePayEncryptedToken; error?: string }> {\n // Initialize Airwallex adapter\n const { adapter, error: adapterError } = initializeAirwallexAdapter(startData, mockScenario);\n if (!adapter) {\n return { success: false, error: adapterError };\n }\n\n // Check availability\n const isAvailable = await adapter.canMakePayment();\n\n if (!isAvailable) {\n return { success: false, error: \"Google Pay not available on this device\" };\n }\n\n // Show payment sheet\n const paymentResult = await adapter.showPaymentSheet();\n\n if (!paymentResult.success) {\n if (\"cancelled\" in paymentResult && paymentResult.cancelled) {\n return { success: false, error: \"Google Pay cancelled by user\" };\n }\n const errorMessage = \"error\" in paymentResult ? paymentResult.error : \"Unknown error\";\n return { success: false, error: errorMessage };\n }\n\n // Return the token for passing to backend\n return { success: true, token: paymentResult.token };\n}\n\n// =============================================================================\n// Confirm & Verify Endpoints\n// =============================================================================\n\n/**\n * Call /confirm endpoint - processes Google Pay token (Airwallex) or records setup (Stripe).\n * For Airwallex, may return next_action if 3DS is required.\n */\nasync function callConfirmEndpoint(\n apiBaseUrl: string,\n secureToken: string,\n googlePayToken?: GooglePayEncryptedToken,\n mockScenarioStr?: string,\n checkoutRequestId?: string,\n): Promise<GooglePayConfirmResponse> {\n const requestBody: GooglePayConfirmRequest = {\n mock_scenario: mockScenarioStr,\n };\n\n if (googlePayToken) {\n requestBody.google_pay_token = googlePayToken;\n }\n\n const result = await apiCall<GooglePayConfirmResponse>(\n `${apiBaseUrl}/api/checkout/${secureToken}/google-pay/confirm`,\n {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify(requestBody),\n },\n checkoutRequestId,\n );\n\n if (result.error || !result.data) {\n return {\n charge_status: \"fail\",\n error_message: result.error || \"Failed to confirm payment\",\n checkout_attempt_id: \"\",\n };\n }\n\n return result.data;\n}\n\n/**\n * Call /verify endpoint - verifies 3DS completion for Airwallex.\n * Called after user completes 3DS challenge. May return another next_action for retry.\n */\nasync function callVerifyEndpoint(\n apiBaseUrl: string,\n secureToken: string,\n checkoutRequestId?: string,\n): Promise<GooglePayConfirmResponse> {\n const result = await apiCall<GooglePayConfirmResponse>(\n `${apiBaseUrl}/api/checkout/${secureToken}/google-pay/verify`,\n {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n },\n checkoutRequestId,\n );\n\n if (result.error || !result.data) {\n return {\n charge_status: \"fail\",\n error_message: result.error || \"Failed to verify payment\",\n checkout_attempt_id: \"\",\n };\n }\n\n return result.data;\n}\n\n/**\n * Convert GooglePayConfirmResponse to GooglePayResult for return to caller.\n */\nfunction toGooglePayResult(response: GooglePayConfirmResponse, secureToken: string): GooglePayResult {\n if (response.charge_status === \"success\") {\n return {\n data: {\n id: response.transaction_id,\n checkoutAttemptId: response.checkout_attempt_id,\n checkoutSessionId: secureToken,\n state: \"checkout_succeeded\",\n },\n };\n }\n return { errors: { google_pay: response.error_message || \"Payment failed\" } };\n}\n\n// =============================================================================\n// Stripe Confirm (Simple - No 3DS Loop)\n// =============================================================================\n\n/**\n * Confirm Stripe Google Pay - simple flow without 3DS loop.\n * Stripe.js handles 3DS internally during confirmCardSetup().\n */\nasync function confirmStripeGooglePay(\n apiBaseUrl: string,\n secureToken: string,\n mockScenarioStr?: string,\n checkoutRequestId?: string,\n): Promise<GooglePayResult> {\n const response = await callConfirmEndpoint(apiBaseUrl, secureToken, undefined, mockScenarioStr, checkoutRequestId);\n return toGooglePayResult(response, secureToken);\n}\n\n// =============================================================================\n// Airwallex Confirm with 3DS Loop\n// =============================================================================\n\nconst MAX_USER_ACTIONS = 5;\n\n/**\n * Confirm Airwallex Google Pay with 3DS loop support.\n *\n * Flow:\n * 1. Call /confirm with token → may return next_action (3DS)\n * 2. If next_action: handle 3DS, then call /verify\n * 3. Loop if /verify returns another next_action (3DS retry)\n * 4. Return final result\n *\n * This mirrors the card.ts pattern for consistency.\n */\nasync function confirmAirwallexGooglePay(\n apiBaseUrl: string,\n secureToken: string,\n googlePayToken: GooglePayEncryptedToken,\n mockScenarioStr?: string,\n checkoutRequestId?: string,\n): Promise<GooglePayResult> {\n let userActionCount = 0;\n\n // Step 1: Initial confirm with token\n let response = await callConfirmEndpoint(apiBaseUrl, secureToken, googlePayToken, mockScenarioStr, checkoutRequestId);\n\n // Step 2: Handle 3DS loop (if required)\n while (response.charge_status === \"pending\" && response.next_action && userActionCount < MAX_USER_ACTIONS) {\n userActionCount++;\n\n // Handle 3DS challenge\n const actionResult = await handleNextAction(response.next_action);\n\n // Always call verify endpoint so backend can conclude properly\n // (even if 3DS failed, backend needs to know)\n const verifyResponse = await callVerifyEndpoint(apiBaseUrl, secureToken, checkoutRequestId);\n\n // Check if another 3DS action is required (retry scenario)\n if (verifyResponse.charge_status === \"pending\" && verifyResponse.next_action) {\n if (!actionResult.success) {\n console.log(\"[GooglePay:Airwallex] 3DS failed but retry available, continuing loop...\");\n }\n response = verifyResponse;\n continue;\n }\n\n // No more actions - check final result\n if (!actionResult.success) {\n // 3DS failed and no retry available\n return {\n errors: {\n google_pay: verifyResponse.error_message || actionResult.error || \"3DS authentication failed\",\n },\n };\n }\n\n // 3DS succeeded - return verify result\n return toGooglePayResult(verifyResponse, secureToken);\n }\n\n // Check for max attempts exceeded\n if (userActionCount >= MAX_USER_ACTIONS) {\n return { errors: { google_pay: \"Too many authentication attempts. Please try again.\" } };\n }\n\n // No 3DS required - return initial confirm result\n return toGooglePayResult(response, secureToken);\n}\n\n// =============================================================================\n// Main Submit Function\n// =============================================================================\n\nconst defSubmitPayment = (states: PaymentKitStates) => {\n const submitPayment: TInternalFuncs[\"submitPayment\"] = async (_fields, options) => {\n const { apiBaseUrl, secureToken, environment } = states;\n const gpayOptions = options as GooglePaySubmitOptions;\n\n // Validate options\n const validationError = validateOptions(gpayOptions);\n if (validationError) {\n return { errors: validationError };\n }\n\n try {\n const mockScenarioStr = getMockScenarioStr(gpayOptions.mockScenario);\n\n // Get or create checkout request ID for correlating all API calls\n const checkoutRequestId = getOrCreateCheckoutRequestId(environment);\n console.log(`[GooglePay] Using checkout_request_id: ${checkoutRequestId}`);\n\n // Step 1: Start Google Pay flow - backend selects processor\n const startResult = await callStartEndpoint(\n apiBaseUrl,\n secureToken,\n gpayOptions,\n mockScenarioStr,\n checkoutRequestId,\n );\n if (startResult.error || !startResult.data) {\n return { errors: { google_pay: startResult.error || \"Failed to start Google Pay\" } };\n }\n\n const startData = startResult.data;\n\n // Step 2 & 3: Run processor-specific flow and confirm\n if (startData.processor === \"stripe\") {\n // Stripe flow: Uses Stripe.js PaymentRequest API (handles 3DS internally)\n const stripeResult = await runStripeFlow(startData, gpayOptions.mockScenario);\n if (!stripeResult.success) {\n return { errors: { google_pay: stripeResult.error } };\n }\n // Confirm with backend (no token needed, Stripe handled it)\n return await confirmStripeGooglePay(apiBaseUrl, secureToken, mockScenarioStr, checkoutRequestId);\n }\n\n if (startData.processor === \"airwallex\") {\n // Airwallex flow: Uses Google Pay API directly, may need 3DS\n const airwallexResult = await runAirwallexFlow(startData, gpayOptions.mockScenario);\n if (!airwallexResult.success) {\n return { errors: { google_pay: airwallexResult.error } };\n }\n if (!airwallexResult.token) {\n return { errors: { google_pay: \"Google Pay token not received\" } };\n }\n // Confirm with 3DS loop support\n return await confirmAirwallexGooglePay(\n apiBaseUrl,\n secureToken,\n airwallexResult.token,\n mockScenarioStr,\n checkoutRequestId,\n );\n }\n\n return { errors: { google_pay: `Unsupported processor: ${startData.processor}` } };\n } catch (error) {\n return { errors: { google_pay: `Google Pay error: ${error}` } };\n }\n };\n\n return submitPayment;\n};\n\n// =============================================================================\n// Payment Method Definition\n// =============================================================================\n\nconst GooglePayPaymentMethod = definePaymentMethod((paymentKitStates) => {\n return {\n name: \"google_pay\",\n externalFuncs: {},\n internalFuncs: {\n submitPayment: defSubmitPayment(paymentKitStates),\n cleanup: () => {},\n },\n };\n});\n\nexport { GooglePayMockScenario };\n\nexport default GooglePayPaymentMethod;\n"],"mappings":"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"}
@@ -1,3 +1,3 @@
1
- import { t as handleNextAction } from "../next-action-handlers-BZs04hYb.mjs";
1
+ import { t as handleNextAction } from "../next-action-handlers-CTx8tRt0.mjs";
2
2
 
3
3
  export { handleNextAction };
@@ -1,6 +1,6 @@
1
- import { r as PaymentMethod } from "../types-CPuloCtF.mjs";
2
- import "../connect-card-DTfXuTsW.mjs";
3
- import "../connect-tunnel-x-Dxcg5Y7Y.mjs";
1
+ import { r as PaymentMethod } from "../types-CZk3V0Qt.mjs";
2
+ import "../connect-card-Cxy51W6t.mjs";
3
+ import "../connect-tunnel-x-lv6Wtdme.mjs";
4
4
 
5
5
  //#region src/payment-methods/paypal.d.ts
6
6
  type PayPalCustomerInfo = {
@@ -1,4 +1,4 @@
1
- import { i as definePaymentMethod, n as collectFraudMetadata, o as generateCheckoutRequestId } from "../utils-Dgyk7RkM.mjs";
1
+ import { i as definePaymentMethod, n as collectFraudMetadata, o as getOrCreateCheckoutRequestId } from "../utils-B70Y8YcZ.mjs";
2
2
 
3
3
  //#region src/payment-methods/paypal.ts
4
4
  const defSubmitPayment = (states) => {
@@ -8,8 +8,8 @@ const defSubmitPayment = (states) => {
8
8
  if (!paypalOptions?.processorId) return { errors: { processor_id: "Processor ID is required" } };
9
9
  if (!paypalOptions?.customerInfo?.first_name || !paypalOptions?.customerInfo?.last_name) return { errors: { customer_name: "Customer first and last name are required" } };
10
10
  try {
11
- const checkoutRequestId = generateCheckoutRequestId(environment);
12
- console.log(`[PayPal] Generated checkout_request_id: ${checkoutRequestId}`);
11
+ const checkoutRequestId = getOrCreateCheckoutRequestId(environment);
12
+ console.log(`[PayPal] Using checkout_request_id: ${checkoutRequestId}`);
13
13
  const startResponse = await fetch(`${apiBaseUrl}/api/checkout/${secureToken}/paypal/start`, {
14
14
  method: "POST",
15
15
  headers: {