@payment-kit-js/vanilla 0.3.0 → 0.4.1

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 (67) hide show
  1. package/dist/airwallex-google-pay-adapter-BCmTZip5.mjs +167 -0
  2. package/dist/airwallex-google-pay-adapter-BCmTZip5.mjs.map +1 -0
  3. package/dist/airwallex-google-pay-adapter-Be2Af4N9.d.mts +140 -0
  4. package/dist/airwallex-google-pay-adapter-Be2Af4N9.d.mts.map +1 -0
  5. package/dist/cdn/paymentkit.js +5768 -83
  6. package/dist/cdn/paymentkit.js.map +4 -4
  7. package/dist/cdn/paymentkit.min.js +58 -3
  8. package/dist/cdn/paymentkit.min.js.map +4 -4
  9. package/dist/{connect-card-DO2EJxu6.mjs → connect-card-BrtCmsjz.mjs} +1 -1
  10. package/dist/{connect-card-DO2EJxu6.mjs.map → connect-card-BrtCmsjz.mjs.map} +1 -1
  11. package/dist/{connect-card-C582hcWw.d.mts → connect-card-DTfXuTsW.d.mts} +1 -1
  12. package/dist/{connect-card-C582hcWw.d.mts.map → connect-card-DTfXuTsW.d.mts.map} +1 -1
  13. package/dist/connect-tunnel-x-BhVAej5Q.mjs.map +1 -1
  14. package/dist/{connect-tunnel-x-B7iMQ7DX.d.mts → connect-tunnel-x-Dxcg5Y7Y.d.mts} +6 -5
  15. package/dist/connect-tunnel-x-Dxcg5Y7Y.d.mts.map +1 -0
  16. package/dist/index.d.mts +3 -3
  17. package/dist/index.d.mts.map +1 -1
  18. package/dist/index.mjs +337 -5
  19. package/dist/index.mjs.map +1 -1
  20. package/dist/next-action-handlers-BZs04hYb.mjs +271 -0
  21. package/dist/next-action-handlers-BZs04hYb.mjs.map +1 -0
  22. package/dist/payment-methods/airwallex-google-pay-adapter.d.mts +2 -0
  23. package/dist/payment-methods/airwallex-google-pay-adapter.mjs +3 -0
  24. package/dist/payment-methods/apple-pay.d.mts +78 -0
  25. package/dist/payment-methods/apple-pay.d.mts.map +1 -0
  26. package/dist/payment-methods/apple-pay.mjs +188 -0
  27. package/dist/payment-methods/apple-pay.mjs.map +1 -0
  28. package/dist/payment-methods/card.d.mts +3 -3
  29. package/dist/payment-methods/card.d.mts.map +1 -1
  30. package/dist/payment-methods/card.mjs +40 -12
  31. package/dist/payment-methods/card.mjs.map +1 -1
  32. package/dist/payment-methods/google-pay.d.mts +25 -7
  33. package/dist/payment-methods/google-pay.d.mts.map +1 -1
  34. package/dist/payment-methods/google-pay.mjs +165 -30
  35. package/dist/payment-methods/google-pay.mjs.map +1 -1
  36. package/dist/payment-methods/next-action-handlers.d.mts.map +1 -1
  37. package/dist/payment-methods/next-action-handlers.mjs +1 -1
  38. package/dist/payment-methods/paypal.d.mts +3 -3
  39. package/dist/payment-methods/paypal.d.mts.map +1 -1
  40. package/dist/payment-methods/paypal.mjs +8 -3
  41. package/dist/payment-methods/paypal.mjs.map +1 -1
  42. package/dist/payment-methods/stripe-apple-pay-adapter.d.mts +2 -0
  43. package/dist/payment-methods/stripe-apple-pay-adapter.mjs +3 -0
  44. package/dist/payment-methods/stripe-google-pay-adapter.d.mts +1 -1
  45. package/dist/payment-methods/stripe-google-pay-adapter.mjs +1 -1
  46. package/dist/penpal/connect-card.d.mts +1 -1
  47. package/dist/penpal/connect-card.mjs +1 -1
  48. package/dist/penpal/connect-tunnel-x.d.mts +1 -1
  49. package/dist/stripe-apple-pay-adapter-Bg7nCy3P.mjs +313 -0
  50. package/dist/stripe-apple-pay-adapter-Bg7nCy3P.mjs.map +1 -0
  51. package/dist/stripe-apple-pay-adapter-Bq3f1mqv.d.mts +141 -0
  52. package/dist/stripe-apple-pay-adapter-Bq3f1mqv.d.mts.map +1 -0
  53. package/dist/{stripe-google-pay-adapter-DMDArVp2.mjs → stripe-google-pay-adapter-DjrgDYWe.mjs} +1 -1
  54. package/dist/{stripe-google-pay-adapter-DMDArVp2.mjs.map → stripe-google-pay-adapter-DjrgDYWe.mjs.map} +1 -1
  55. package/dist/{stripe-google-pay-adapter-DUUB46SG.d.mts → stripe-google-pay-adapter-xktEycOD.d.mts} +1 -1
  56. package/dist/{stripe-google-pay-adapter-DUUB46SG.d.mts.map → stripe-google-pay-adapter-xktEycOD.d.mts.map} +1 -1
  57. package/dist/types-CPuloCtF.d.mts +129 -0
  58. package/dist/types-CPuloCtF.d.mts.map +1 -0
  59. package/dist/{utils-h0dxplHy.mjs → utils-Dgyk7RkM.mjs} +40 -2
  60. package/dist/utils-Dgyk7RkM.mjs.map +1 -0
  61. package/package.json +9 -3
  62. package/dist/connect-tunnel-x-B7iMQ7DX.d.mts.map +0 -1
  63. package/dist/next-action-handlers-DTsWjUIA.mjs +0 -53
  64. package/dist/next-action-handlers-DTsWjUIA.mjs.map +0 -1
  65. package/dist/types-DsVMq4jZ.d.mts +0 -64
  66. package/dist/types-DsVMq4jZ.d.mts.map +0 -1
  67. package/dist/utils-h0dxplHy.mjs.map +0 -1
@@ -1,8 +1,8 @@
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, r as createCheckoutIFrame, t as $ } from "../utils-h0dxplHy.mjs";
4
- import { t as connectToCardIframe } from "../connect-card-DO2EJxu6.mjs";
5
- import { t as handleNextAction } from "../next-action-handlers-DTsWjUIA.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";
6
6
 
7
7
  //#region src/payment-methods/card.ts
8
8
  /**
@@ -34,7 +34,7 @@ const mapFieldsToCustomerInfo = (fields) => {
34
34
  };
35
35
  };
36
36
  const defCreateElement = (states) => {
37
- const { baseUrl, apiBaseUrl, cardInputConnections, secureToken } = states;
37
+ const { baseUrl, apiBaseUrl, cardInputConnections, secureToken, timingTracker } = states;
38
38
  return (type, options) => {
39
39
  const { style, onLoaded, onFocusChange } = options;
40
40
  const mountIFrame = (parentSelector) => {
@@ -47,6 +47,7 @@ const defCreateElement = (states) => {
47
47
  const iframe = createCheckoutIFrame(type.replace("_", "-"), baseUrl, params);
48
48
  parent.appendChild(iframe);
49
49
  iframe.onload = () => {
50
+ if (type === "card_pan") timingTracker.trackInputReady();
50
51
  const connection = connectToCardIframe(iframe, {
51
52
  onLoaded: onLoaded || (() => {}),
52
53
  onFocusChange: onFocusChange || (() => {})
@@ -65,16 +66,27 @@ const defCreateElement = (states) => {
65
66
  };
66
67
  const defSubmitPayment = (states) => {
67
68
  const submitPayment = async (fields) => {
69
+ const { timingTracker, environment } = states;
70
+ timingTracker.trackSubmit();
71
+ const checkoutRequestId = generateCheckoutRequestId(environment);
72
+ const requestOptions = withRequestId(checkoutRequestId);
73
+ console.log(`[Card] Generated checkout_request_id: ${checkoutRequestId}`);
68
74
  const tunnelX = await TunnelXManager.createFromPenpalConnection(states.tunnelXConnection);
69
75
  const validateCardResult = await validateCardFields(states);
70
76
  const validateFormResult = await validateFormFields(fields);
71
- if (!(validateCardResult.isSuccess && validateFormResult.isSuccess)) return { errors: {
72
- ...validateCardResult.errors,
73
- ...validateFormResult.errors
74
- } };
77
+ if (!(validateCardResult.isSuccess && validateFormResult.isSuccess)) {
78
+ timingTracker.trackFail(null, "validation_error", "Form validation failed");
79
+ return { errors: {
80
+ ...validateCardResult.errors,
81
+ ...validateFormResult.errors
82
+ } };
83
+ }
75
84
  if (!states.cardSetupIntentId) states.cardSetupIntentId = (await tunnelX.publicEndpoints.createCardSetupIntent({ checkoutToken: states.secureToken })).cardSetupIntentId;
76
85
  const submitCardResult = await submitCardFields(states);
77
- if (!submitCardResult.isSuccess) return { errors: submitCardResult.errors };
86
+ if (!submitCardResult.isSuccess) {
87
+ timingTracker.trackFail(null, "card_submit_error", "Card submission failed");
88
+ return { errors: submitCardResult.errors };
89
+ }
78
90
  const cardSetupIntent = await tunnelX.publicEndpoints.getCardSetupIntent({
79
91
  cardSetupIntentId: states.cardSetupIntentId,
80
92
  checkoutToken: states.secureToken
@@ -84,6 +96,7 @@ const defSubmitPayment = (states) => {
84
96
  if (!cardSetupIntent.isCardPanSet) errors.card_pan = "required";
85
97
  if (!cardSetupIntent.isCardExpSet) errors.card_exp = "required";
86
98
  if (!cardSetupIntent.isCardCvcSet) errors.card_cvc = "required";
99
+ timingTracker.trackFail(null, "card_setup_incomplete", "Card details incomplete");
87
100
  return { errors };
88
101
  }
89
102
  console.log("Card setup intent is set ✅", cardSetupIntent);
@@ -95,7 +108,7 @@ const defSubmitPayment = (states) => {
95
108
  customerInfo: mapFieldsToCustomerInfo(fields),
96
109
  fraudMetadata: collectFraudMetadata()
97
110
  }
98
- });
111
+ }, requestOptions);
99
112
  console.log("Card checkout result:", currentResult);
100
113
  const MAX_USER_ACTIONS = 5;
101
114
  let userActionCount = 0;
@@ -104,7 +117,7 @@ const defSubmitPayment = (states) => {
104
117
  console.log(`Handling user action ${userActionCount}/${MAX_USER_ACTIONS}...`);
105
118
  const actionResult = await handleNextAction(currentResult.nextAction);
106
119
  console.log("User action completed, verifying checkout...");
107
- const verifyResult = await tunnelX.publicEndpoints.cardCheckoutVerify({ checkoutToken: states.secureToken });
120
+ const verifyResult = await tunnelX.publicEndpoints.cardCheckoutVerify({ checkoutToken: states.secureToken }, requestOptions);
108
121
  if (verifyResult.nextAction) {
109
122
  if (!actionResult.success) console.log("3DS failed but cascade triggered new action, continuing loop...");
110
123
  else console.log("Another user action required, continuing loop...");
@@ -113,16 +126,31 @@ const defSubmitPayment = (states) => {
113
126
  }
114
127
  if (!actionResult.success) {
115
128
  console.log("3DS authentication failed, checkout concluded:", verifyResult);
116
- return { errors: { root: actionResult.error } };
129
+ timingTracker.trackFail(verifyResult.checkoutAttemptId || null, verifyResult.errorCode || "3ds_failed", verifyResult.errorMessageForCustomer || actionResult.error);
130
+ return { errors: {
131
+ root: verifyResult.errorMessageForCustomer || actionResult.error,
132
+ checkout_response: verifyResult
133
+ } };
117
134
  }
118
135
  console.log("Card checkout verified ✅", verifyResult);
136
+ timingTracker.trackSuccess(verifyResult.checkoutAttemptId || "unknown");
119
137
  return { data: verifyResult };
120
138
  }
121
139
  if (userActionCount >= MAX_USER_ACTIONS) {
122
140
  console.error("Max user actions exceeded");
141
+ timingTracker.trackFail(null, "max_actions_exceeded", "Too many authentication attempts");
123
142
  return { errors: { root: "Too many authentication attempts. Please try again." } };
124
143
  }
144
+ if (currentResult.state === "payment_failed" || currentResult.state === "checkout_failed") {
145
+ console.log("Card checkout failed:", currentResult);
146
+ timingTracker.trackFail(currentResult.checkoutAttemptId || null, currentResult.errorCode || "payment_failed", currentResult.errorMessageForCustomer || "Payment failed");
147
+ return { errors: {
148
+ root: currentResult.errorMessageForCustomer || "Payment failed",
149
+ checkout_response: currentResult
150
+ } };
151
+ }
125
152
  console.log("Card checkout completed ✅", currentResult);
153
+ timingTracker.trackSuccess(currentResult.checkoutAttemptId || "unknown");
126
154
  return { data: currentResult };
127
155
  };
128
156
  return submitPayment;
@@ -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 { $, collectFraudMetadata, createCheckoutIFrame, definePaymentMethod, validateFormFields } 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 } = 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 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 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 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 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 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 checkoutToken: states.secureToken,\n publicCardCheckoutRequest: {\n cardSetupIntentId: states.cardSetupIntentId,\n customerInfo: mapFieldsToCustomerInfo(fields),\n fraudMetadata: collectFraudMetadata(),\n },\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 checkoutToken: states.secureToken,\n });\n\n // Check if another action is required (e.g., cascade to next price tier 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 console.log(\"3DS authentication failed, checkout concluded:\", verifyResult);\n return { errors: { root: actionResult.error } };\n }\n\n console.log(\"Card checkout verified ✅\", verifyResult);\n\n // No more actions needed, return the result\n return { data: verifyResult as { [key: string]: unknown } };\n }\n\n if (userActionCount >= MAX_USER_ACTIONS) {\n console.error(\"Max user actions exceeded\");\n return { errors: { root: \"Too many authentication attempts. Please try again.\" } };\n }\n\n // Return checkout result - caller should check state for success/failure\n // States: checkout_succeeded, payment_failed, payment_cus_info_received (needs action)\n console.log(\"Card checkout completed ✅\", currentResult);\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":";;;;;;;;;;;AAoBA,MAAM,aAAa,aAA+D;CAChF,MAAM,UAAU,SAAS,MAAM;CAC/B,MAAM,aAAa,QAAQ,QAAQ,IAAI;AACvC,KAAI,eAAe,GACjB,QAAO,EAAE,WAAW,SAAS;AAE/B,QAAO;EACL,WAAW,QAAQ,UAAU,GAAG,WAAW;EAC3C,UAAU,QAAQ,UAAU,aAAa,EAAE,CAAC,MAAM,IAAI;EACvD;;;;;AAMH,MAAM,2BAA2B,WAA6B;CAC5D,MAAM,EAAE,WAAW,aAAa,UAAU,OAAO,cAAc;AAE/D,QAAO;EACL,OAAO,OAAO,kBAAkB;EAChC;EACA;EACA,gBACE,OAAO,oBAAoB,OAAO,oBAC9B;GACE,SAAS,OAAO,oBAAoB;GACpC,SAAS,OAAO,qBAAqB;GACtC,GACD;EACP;;AAYH,MAAM,oBAAoB,WAAuB;CAC/C,MAAM,EAAE,SAAS,YAAY,sBAAsB,gBAAgB;AAEnE,SAAQ,MAA2B,YAAkC;EACnE,MAAM,EAAE,OAAO,UAAU,kBAAkB;EAE3C,MAAM,eAAe,mBAA2B;GAC9C,MAAM,SAAS,EAAE,eAAe;GAChC,MAAMA,SAAiC;IACrC,gBAAgB;IAChB,cAAc;IACf;AAED,OAAI,MACF,QAAO,QAAQ,KAAK,UAAU,MAAM;GAGtC,MAAM,SAAS,qBAAqB,KAAK,QAAQ,KAAK,IAAI,EAAE,SAAS,OAAO;AAC5E,UAAO,YAAY,OAAO;AAE1B,UAAO,eAAe;IACpB,MAAM,aAAa,oBAAoB,QAAQ;KAC7C,UAAU,mBAAmB;KAC7B,eAAe,wBAAwB;KACxC,CAAC;AAEF,WAAO,qBAAqB,QAAQ;;GAGtC,MAAM,gBAAgB;AAEpB,IADmB,qBAAqB,OAC5B,SAAS;AACrB,WAAO,YAAY,OAAO;AAC1B,WAAO,qBAAqB,QAAQ;;AAGtC,UAAO,EAAE,SAAS;;AAGpB,SAAO,EAAE,OAAO,aAAa;;;AAIjC,MAAM,oBAAoB,WAAuB;CAC/C,MAAMC,gBAAiD,OAAO,WAAW;EACvE,MAAM,UAAU,MAAM,eAAe,2BAA2B,OAAO,kBAAkB;EAGzF,MAAM,qBAAqB,MAAM,mBAAmB,OAAO;EAC3D,MAAM,qBAAqB,MAAM,mBAAmB,OAAO;AAC3D,MAAI,EAAE,mBAAmB,aAAa,mBAAmB,WACvD,QAAO,EAAE,QAAQ;GAAE,GAAG,mBAAmB;GAAQ,GAAG,mBAAmB;GAAQ,EAAE;AAInF,MAAI,CAAC,OAAO,kBAIV,QAAO,qBAHK,MAAM,QAAQ,gBAAgB,sBAAsB,EAC9D,eAAe,OAAO,aACvB,CAAC,EAC6B;EAIjC,MAAM,mBAAmB,MAAM,iBAAiB,OAAO;AACvD,MAAI,CAAC,iBAAiB,UACpB,QAAO,EAAE,QAAQ,iBAAiB,QAAQ;EAI5C,MAAM,kBAAkB,MAAM,QAAQ,gBAAgB,mBAAmB;GACvE,mBAAmB,OAAO;GAC1B,eAAe,OAAO;GACvB,CAAC;AACF,MAAI,CAAC,gBAAgB,cAAc;GACjC,MAAM,SAAS,EAAE;AACjB,OAAI,CAAC,gBAAgB,aAAc,QAAO,WAAW;AACrD,OAAI,CAAC,gBAAgB,aAAc,QAAO,WAAW;AACrD,OAAI,CAAC,gBAAgB,aAAc,QAAO,WAAW;AACrD,UAAO,EAAE,QAAQ;;AAGnB,UAAQ,IAAI,8BAA8B,gBAAgB;AAC1D,UAAQ,IAAI,UAAU,OAAO;EAG7B,IAAI,gBAAgB,MAAM,QAAQ,gBAAgB,aAAa;GAC7D,eAAe,OAAO;GACtB,2BAA2B;IACzB,mBAAmB,OAAO;IAC1B,cAAc,wBAAwB,OAAO;IAC7C,eAAe,sBAAsB;IACtC;GACF,CAAC;AAEF,UAAQ,IAAI,yBAAyB,cAAc;EAKnD,MAAM,mBAAmB;EACzB,IAAI,kBAAkB;AAEtB,SAAO,cAAc,cAAc,kBAAkB,kBAAkB;AACrE;AACA,WAAQ,IAAI,wBAAwB,gBAAgB,GAAG,iBAAiB,KAAK;GAE7E,MAAM,eAAe,MAAM,iBAAiB,cAAc,WAAW;AAKrE,WAAQ,IAAI,+CAA+C;GAC3D,MAAM,eAAe,MAAM,QAAQ,gBAAgB,mBAAmB,EACpE,eAAe,OAAO,aACvB,CAAC;AAIF,OAAI,aAAa,YAAY;AAC3B,QAAI,CAAC,aAAa,QAChB,SAAQ,IAAI,kEAAkE;QAE9E,SAAQ,IAAI,mDAAmD;AAEjE,oBAAgB;AAChB;;AAGF,OAAI,CAAC,aAAa,SAAS;AAEzB,YAAQ,IAAI,kDAAkD,aAAa;AAC3E,WAAO,EAAE,QAAQ,EAAE,MAAM,aAAa,OAAO,EAAE;;AAGjD,WAAQ,IAAI,4BAA4B,aAAa;AAGrD,UAAO,EAAE,MAAM,cAA4C;;AAG7D,MAAI,mBAAmB,kBAAkB;AACvC,WAAQ,MAAM,4BAA4B;AAC1C,UAAO,EAAE,QAAQ,EAAE,MAAM,uDAAuD,EAAE;;AAKpF,UAAQ,IAAI,6BAA6B,cAAc;AACvD,SAAO,EAAE,MAAM,eAA6C;;AAE9D,QAAO;;AAGT,MAAM,mBAAmB,OAAO,WAAuB;CACrD,MAAM,SAAS,EAAE;CACjB,MAAM,EAAE,mBAAmB,yBAAyB;CAEpD,MAAM,iBAAiB,OAAO,QAAQ,qBAAqB,CAAC,IAAI,OAAO,CAAC,OAAO,gBAAgB;EAC7F,MAAM,OAAO;EAGb,MAAM,SAAS,OADA,MAAM,WAAW,SACJ,SAAS,qBAAqB,GAAG;AAE7D,MAAI,WAAW,OACb,QAAO,QAAQ,OAAO;GAExB;AAEF,OAAM,QAAQ,WAAW,eAAe;AAExC,QAAO;EACL;EACA,WAAW,OAAO,KAAK,OAAO,CAAC,WAAW;EAC3C;;AAGH,MAAM,qBAAqB,OAAO,WAAuB;CACvD,MAAMC,SAA2B,EAAE;CACnC,MAAM,EAAE,yBAAyB;CAEjC,MAAM,mBAAmB,OAAO,QAAQ,qBAAqB,CAAC,IAAI,OAAO,CAAC,OAAO,gBAAgB;EAC/F,MAAM,OAAO;AAEb,MAAI,CAAC,YAAY;AACf,UAAO,QAAQ;AACf;;EAIF,MAAM,WAAW,OADF,MAAM,WAAW,SACF,YAAY;AAE1C,MAAI,SACF,QAAO,QAAQ;GAEjB;AAEF,OAAM,QAAQ,WAAW,iBAAiB;AAE1C,QAAO;EACL;EACA,WAAW,OAAO,KAAK,OAAO,CAAC,WAAW;EAC3C;;AAGH,MAAM,oBAAoB,qBAAqB,qBAAqB;CAClE,MAAMC,cAA0B;EAAE,GAAG;EAAkB,sBAAsB,EAAE;EAAE;AAEjF,QAAO;EACL,MAAM;EACN,eAAe,EACb,eAAe,iBAAiB,YAAY,EAC7C;EACD,eAAe;GACb,eAAe,iBAAiB,YAAY;GAC5C,eAAe;AAEb,SAAK,MAAM,cAAc,OAAO,OAAO,YAAY,qBAAqB,CACtE,aAAY,SAAS;AAEvB,gBAAY,uBAAuB,EAAE;;GAExC;EACF;EACD;AAEF,mBAAe"}
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":";;;;;;;;;;;AA4BA,MAAM,aAAa,aAA+D;CAChF,MAAM,UAAU,SAAS,MAAM;CAC/B,MAAM,aAAa,QAAQ,QAAQ,IAAI;AACvC,KAAI,eAAe,GACjB,QAAO,EAAE,WAAW,SAAS;AAE/B,QAAO;EACL,WAAW,QAAQ,UAAU,GAAG,WAAW;EAC3C,UAAU,QAAQ,UAAU,aAAa,EAAE,CAAC,MAAM,IAAI;EACvD;;;;;AAMH,MAAM,2BAA2B,WAA6B;CAC5D,MAAM,EAAE,WAAW,aAAa,UAAU,OAAO,cAAc;AAE/D,QAAO;EACL,OAAO,OAAO,kBAAkB;EAChC;EACA;EACA,gBACE,OAAO,oBAAoB,OAAO,oBAC9B;GACE,SAAS,OAAO,oBAAoB;GACpC,SAAS,OAAO,qBAAqB;GACtC,GACD;EACP;;AAYH,MAAM,oBAAoB,WAAuB;CAC/C,MAAM,EAAE,SAAS,YAAY,sBAAsB,aAAa,kBAAkB;AAElF,SAAQ,MAA2B,YAAkC;EACnE,MAAM,EAAE,OAAO,UAAU,kBAAkB;EAE3C,MAAM,eAAe,mBAA2B;GAC9C,MAAM,SAAS,EAAE,eAAe;GAChC,MAAMA,SAAiC;IACrC,gBAAgB;IAChB,cAAc;IACf;AAED,OAAI,MACF,QAAO,QAAQ,KAAK,UAAU,MAAM;GAGtC,MAAM,SAAS,qBAAqB,KAAK,QAAQ,KAAK,IAAI,EAAE,SAAS,OAAO;AAC5E,UAAO,YAAY,OAAO;AAE1B,UAAO,eAAe;AAGpB,QAAI,SAAS,WACX,eAAc,iBAAiB;IAGjC,MAAM,aAAa,oBAAoB,QAAQ;KAC7C,UAAU,mBAAmB;KAC7B,eAAe,wBAAwB;KACxC,CAAC;AAEF,WAAO,qBAAqB,QAAQ;;GAGtC,MAAM,gBAAgB;AAEpB,IADmB,qBAAqB,OAC5B,SAAS;AACrB,WAAO,YAAY,OAAO;AAC1B,WAAO,qBAAqB,QAAQ;;AAGtC,UAAO,EAAE,SAAS;;AAGpB,SAAO,EAAE,OAAO,aAAa;;;AAIjC,MAAM,oBAAoB,WAAuB;CAC/C,MAAMC,gBAAiD,OAAO,WAAW;EACvE,MAAM,EAAE,eAAe,gBAAgB;AAGvC,gBAAc,aAAa;EAG3B,MAAM,oBAAoB,0BAA0B,YAAY;EAChE,MAAM,iBAAiB,cAAc,kBAAkB;AACvD,UAAQ,IAAI,yCAAyC,oBAAoB;EAEzE,MAAM,UAAU,MAAM,eAAe,2BAA2B,OAAO,kBAAkB;EAGzF,MAAM,qBAAqB,MAAM,mBAAmB,OAAO;EAC3D,MAAM,qBAAqB,MAAM,mBAAmB,OAAO;AAC3D,MAAI,EAAE,mBAAmB,aAAa,mBAAmB,YAAY;AACnE,iBAAc,UAAU,MAAM,oBAAoB,yBAAyB;AAC3E,UAAO,EAAE,QAAQ;IAAE,GAAG,mBAAmB;IAAQ,GAAG,mBAAmB;IAAQ,EAAE;;AAInF,MAAI,CAAC,OAAO,kBAIV,QAAO,qBAHK,MAAM,QAAQ,gBAAgB,sBAAsB,EAC9D,eAAe,OAAO,aACvB,CAAC,EAC6B;EAIjC,MAAM,mBAAmB,MAAM,iBAAiB,OAAO;AACvD,MAAI,CAAC,iBAAiB,WAAW;AAC/B,iBAAc,UAAU,MAAM,qBAAqB,yBAAyB;AAC5E,UAAO,EAAE,QAAQ,iBAAiB,QAAQ;;EAI5C,MAAM,kBAAkB,MAAM,QAAQ,gBAAgB,mBAAmB;GACvE,mBAAmB,OAAO;GAC1B,eAAe,OAAO;GACvB,CAAC;AACF,MAAI,CAAC,gBAAgB,cAAc;GACjC,MAAM,SAAS,EAAE;AACjB,OAAI,CAAC,gBAAgB,aAAc,QAAO,WAAW;AACrD,OAAI,CAAC,gBAAgB,aAAc,QAAO,WAAW;AACrD,OAAI,CAAC,gBAAgB,aAAc,QAAO,WAAW;AACrD,iBAAc,UAAU,MAAM,yBAAyB,0BAA0B;AACjF,UAAO,EAAE,QAAQ;;AAGnB,UAAQ,IAAI,8BAA8B,gBAAgB;AAC1D,UAAQ,IAAI,UAAU,OAAO;EAG7B,IAAI,gBAAgB,MAAM,QAAQ,gBAAgB,aAChD;GACE,eAAe,OAAO;GACtB,2BAA2B;IACzB,mBAAmB,OAAO;IAC1B,cAAc,wBAAwB,OAAO;IAC7C,eAAe,sBAAsB;IACtC;GACF,EACD,eACD;AAED,UAAQ,IAAI,yBAAyB,cAAc;EAKnD,MAAM,mBAAmB;EACzB,IAAI,kBAAkB;AAEtB,SAAO,cAAc,cAAc,kBAAkB,kBAAkB;AACrE;AACA,WAAQ,IAAI,wBAAwB,gBAAgB,GAAG,iBAAiB,KAAK;GAE7E,MAAM,eAAe,MAAM,iBAAiB,cAAc,WAAW;AAKrE,WAAQ,IAAI,+CAA+C;GAC3D,MAAM,eAAe,MAAM,QAAQ,gBAAgB,mBACjD,EACE,eAAe,OAAO,aACvB,EACD,eACD;AAID,OAAI,aAAa,YAAY;AAC3B,QAAI,CAAC,aAAa,QAChB,SAAQ,IAAI,kEAAkE;QAE9E,SAAQ,IAAI,mDAAmD;AAEjE,oBAAgB;AAChB;;AAGF,OAAI,CAAC,aAAa,SAAS;AAGzB,YAAQ,IAAI,kDAAkD,aAAa;AAC3E,kBAAc,UACZ,aAAa,qBAAqB,MAClC,aAAa,aAAa,cAC1B,aAAa,2BAA2B,aAAa,MACtD;AACD,WAAO,EACL,QAAQ;KACN,MAAM,aAAa,2BAA2B,aAAa;KAC3D,mBAAmB;KACpB,EACF;;AAGH,WAAQ,IAAI,4BAA4B,aAAa;AAGrD,iBAAc,aAAa,aAAa,qBAAqB,UAAU;AACvE,UAAO,EAAE,MAAM,cAA4C;;AAG7D,MAAI,mBAAmB,kBAAkB;AACvC,WAAQ,MAAM,4BAA4B;AAC1C,iBAAc,UAAU,MAAM,wBAAwB,mCAAmC;AACzF,UAAO,EAAE,QAAQ,EAAE,MAAM,uDAAuD,EAAE;;AAKpF,MAAI,cAAc,UAAU,oBAAoB,cAAc,UAAU,mBAAmB;AACzF,WAAQ,IAAI,yBAAyB,cAAc;AACnD,iBAAc,UACZ,cAAc,qBAAqB,MACnC,cAAc,aAAa,kBAC3B,cAAc,2BAA2B,iBAC1C;AAGD,UAAO,EACL,QAAQ;IACN,MAAM,cAAc,2BAA2B;IAC/C,mBAAmB;IACpB,EACF;;AAGH,UAAQ,IAAI,6BAA6B,cAAc;AACvD,gBAAc,aAAa,cAAc,qBAAqB,UAAU;AACxE,SAAO,EAAE,MAAM,eAA6C;;AAE9D,QAAO;;AAGT,MAAM,mBAAmB,OAAO,WAAuB;CACrD,MAAM,SAAS,EAAE;CACjB,MAAM,EAAE,mBAAmB,yBAAyB;CAEpD,MAAM,iBAAiB,OAAO,QAAQ,qBAAqB,CAAC,IAAI,OAAO,CAAC,OAAO,gBAAgB;EAC7F,MAAM,OAAO;EAGb,MAAM,SAAS,OADA,MAAM,WAAW,SACJ,SAAS,qBAAqB,GAAG;AAE7D,MAAI,WAAW,OACb,QAAO,QAAQ,OAAO;GAExB;AAEF,OAAM,QAAQ,WAAW,eAAe;AAExC,QAAO;EACL;EACA,WAAW,OAAO,KAAK,OAAO,CAAC,WAAW;EAC3C;;AAGH,MAAM,qBAAqB,OAAO,WAAuB;CACvD,MAAMC,SAA2B,EAAE;CACnC,MAAM,EAAE,yBAAyB;CAEjC,MAAM,mBAAmB,OAAO,QAAQ,qBAAqB,CAAC,IAAI,OAAO,CAAC,OAAO,gBAAgB;EAC/F,MAAM,OAAO;AAEb,MAAI,CAAC,YAAY;AACf,UAAO,QAAQ;AACf;;EAIF,MAAM,WAAW,OADF,MAAM,WAAW,SACF,YAAY;AAE1C,MAAI,SACF,QAAO,QAAQ;GAEjB;AAEF,OAAM,QAAQ,WAAW,iBAAiB;AAE1C,QAAO;EACL;EACA,WAAW,OAAO,KAAK,OAAO,CAAC,WAAW;EAC3C;;AAGH,MAAM,oBAAoB,qBAAqB,qBAAqB;CAClE,MAAMC,cAA0B;EAAE,GAAG;EAAkB,sBAAsB,EAAE;EAAE;AAEjF,QAAO;EACL,MAAM;EACN,eAAe,EACb,eAAe,iBAAiB,YAAY,EAC7C;EACD,eAAe;GACb,eAAe,iBAAiB,YAAY;GAC5C,eAAe;AAEb,SAAK,MAAM,cAAc,OAAO,OAAO,YAAY,qBAAqB,CACtE,aAAY,SAAS;AAEvB,gBAAY,uBAAuB,EAAE;;GAExC;EACF;EACD;AAEF,mBAAe"}
@@ -1,7 +1,8 @@
1
- import "../connect-card-C582hcWw.mjs";
2
- import "../connect-tunnel-x-B7iMQ7DX.mjs";
3
- import { r as PaymentMethod } from "../types-DsVMq4jZ.mjs";
4
- import { n as GooglePayMockScenario } from "../stripe-google-pay-adapter-DUUB46SG.mjs";
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";
5
6
 
6
7
  //#region src/payment-methods/google-pay.d.ts
7
8
  type GooglePayCustomerInfo = {
@@ -23,18 +24,35 @@ type GooglePayStartRequest = {
23
24
  mock_scenario?: string;
24
25
  };
25
26
  type GooglePayStartResponse = {
26
- client_secret: string;
27
- stripe_pk: string;
28
27
  checkout_attempt_id: string;
29
28
  amount: number;
29
+ amount_display: string;
30
30
  currency: string;
31
31
  country: string;
32
+ processor: "stripe" | "airwallex";
33
+ is_sandbox?: boolean;
34
+ client_secret?: string;
35
+ stripe_pk?: string;
36
+ merchant_name?: string;
37
+ airwallex_account_id?: string;
38
+ google_merchant_id?: string;
39
+ };
40
+ type GooglePayConfirmRequest = {
41
+ google_pay_token?: GooglePayEncryptedToken;
42
+ mock_scenario?: string;
43
+ };
44
+ type Airwallex3dsNextAction = {
45
+ type: "airwallex_3ds";
46
+ url: string;
47
+ method: string;
48
+ payment_intent_id: string;
32
49
  };
33
50
  type GooglePayConfirmResponse = {
34
51
  charge_status: "success" | "fail" | "pending";
35
52
  transaction_id?: string;
36
53
  error_message?: string;
37
54
  checkout_attempt_id: string;
55
+ next_action?: Airwallex3dsNextAction;
38
56
  };
39
57
  type GooglePaySubmitOptions = {
40
58
  processorId: string;
@@ -43,5 +61,5 @@ type GooglePaySubmitOptions = {
43
61
  };
44
62
  declare const GooglePayPaymentMethod: PaymentMethod<{}, "google_pay">;
45
63
  //#endregion
46
- export { GooglePayConfirmResponse, GooglePayCustomerInfo, GooglePayMockScenario, GooglePayStartRequest, GooglePayStartResponse, GooglePaySubmitOptions, GooglePayPaymentMethod as default };
64
+ export { Airwallex3dsNextAction, GooglePayConfirmRequest, GooglePayConfirmResponse, GooglePayCustomerInfo, GooglePayMockScenario, GooglePayStartRequest, GooglePayStartResponse, GooglePaySubmitOptions, GooglePayPaymentMethod as default };
47
65
  //# sourceMappingURL=google-pay.d.mts.map
@@ -1 +1 @@
1
- {"version":3,"file":"google-pay.d.mts","names":[],"sources":["../../src/payment-methods/google-pay.ts"],"sourcesContent":[],"mappings":";;;;;;KAKY,qBAAA;;;;KAKA,qBAAA;EALA,YAAA,EAAA,MAAA;EAKA,aAAA,EAEK,qBAAA;EASL,cAAA,EAAA;IASA,SAAA,CAAA,EAAA,MAAA;IAOA,WAAA,CAAA,EAAA;MAgNN,CAAA,GAAA,EAAA,MAAA,CAAA,EAAA,OASJ;;;;;;;;KAzOU,sBAAA;;;;;;;;KASA,wBAAA;;;;;;KAOA,sBAAA;;gBAEI;iBACC;;cA6MX,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;IAkeN,kBAAA,CAAA,EASJ;;;;;;KAvhBU,sBAAA;;;;;;;;;;;;;;KAuBA,uBAAA;qBACS;;;KAKT,sBAAA;;;;;;KAOA,wBAAA;;;;;gBAKI;;KAGJ,sBAAA;;gBAEI;iBACC;;cA+dX,wBASJ"}
@@ -1,9 +1,16 @@
1
- import { i as definePaymentMethod, n as collectFraudMetadata } from "../utils-h0dxplHy.mjs";
2
- import { n as StripeGooglePayAdapter, t as GooglePayMockScenario } from "../stripe-google-pay-adapter-DMDArVp2.mjs";
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";
3
5
 
4
6
  //#region src/payment-methods/google-pay.ts
5
- async function apiCall(url, options) {
6
- const response = await fetch(url, options);
7
+ async function apiCall(url, options, checkoutRequestId) {
8
+ const headers = new Headers(options.headers);
9
+ if (checkoutRequestId) headers.set("x-request-id", checkoutRequestId);
10
+ const response = await fetch(url, {
11
+ ...options,
12
+ headers
13
+ });
7
14
  if (!response.ok) {
8
15
  let errorMessage = `Request failed (${response.status})`;
9
16
  try {
@@ -23,7 +30,7 @@ function validateOptions(options) {
23
30
  function getMockScenarioStr(mockScenario) {
24
31
  return mockScenario && mockScenario !== GooglePayMockScenario.None ? mockScenario : void 0;
25
32
  }
26
- async function callStartEndpoint(apiBaseUrl, secureToken, options, mockScenarioStr) {
33
+ async function callStartEndpoint(apiBaseUrl, secureToken, options, mockScenarioStr, checkoutRequestId) {
27
34
  return apiCall(`${apiBaseUrl}/api/checkout/${secureToken}/google-pay/start`, {
28
35
  method: "POST",
29
36
  headers: { "Content-Type": "application/json" },
@@ -33,9 +40,9 @@ async function callStartEndpoint(apiBaseUrl, secureToken, options, mockScenarioS
33
40
  fraud_metadata: collectFraudMetadata(),
34
41
  mock_scenario: mockScenarioStr
35
42
  })
36
- });
43
+ }, checkoutRequestId);
37
44
  }
38
- function initializeAdapter(stripePk, startData, mockScenario) {
45
+ function initializeStripeAdapter(stripePk, startData, mockScenario) {
39
46
  const adapter = new StripeGooglePayAdapter(mockScenario);
40
47
  if (!adapter.initialize(stripePk)) return { error: "Stripe.js not loaded. Add <script src=\"https://js.stripe.com/v3/\"><\/script> to your page." };
41
48
  const prConfig = {
@@ -48,14 +55,20 @@ function initializeAdapter(stripePk, startData, mockScenario) {
48
55
  requestPayerName: true,
49
56
  requestPayerEmail: true
50
57
  };
51
- console.log("[GooglePay] PaymentRequest config:", prConfig);
52
58
  adapter.createPaymentRequest(prConfig);
53
59
  return { adapter };
54
60
  }
55
- async function showPaymentSheetAndConfirm(adapter, clientSecret) {
56
- const isAvailable = await adapter.canMakePayment();
57
- console.log("[GooglePay] canMakePayment result:", isAvailable);
58
- if (!isAvailable) return {
61
+ async function runStripeFlow(startData, mockScenario) {
62
+ if (!startData.stripe_pk || !startData.client_secret) return {
63
+ success: false,
64
+ error: "Stripe credentials not provided"
65
+ };
66
+ const { adapter, error: adapterError } = initializeStripeAdapter(startData.stripe_pk, startData, mockScenario);
67
+ if (!adapter) return {
68
+ success: false,
69
+ error: adapterError
70
+ };
71
+ if (!await adapter.canMakePayment()) return {
59
72
  success: false,
60
73
  error: "Google Pay not available on this device"
61
74
  };
@@ -70,48 +83,170 @@ async function showPaymentSheetAndConfirm(adapter, clientSecret) {
70
83
  error: "error" in paymentResult ? paymentResult.error : "Unknown error"
71
84
  };
72
85
  }
73
- const confirmResult = await adapter.confirmCardSetup(clientSecret, paymentResult.paymentMethodId);
86
+ const confirmResult = await adapter.confirmCardSetup(startData.client_secret, paymentResult.paymentMethodId);
74
87
  if (!confirmResult.success) {
75
88
  paymentResult.complete("fail");
76
89
  return {
77
90
  success: false,
78
- error: confirmResult.error
91
+ error: "error" in confirmResult ? confirmResult.error : "Card setup failed"
79
92
  };
80
93
  }
81
94
  paymentResult.complete("success");
82
95
  return { success: true };
83
96
  }
84
- async function callConfirmEndpoint(apiBaseUrl, secureToken, mockScenarioStr) {
97
+ function initializeAirwallexAdapter(startData, mockScenario) {
98
+ if (!startData.merchant_name || !startData.airwallex_account_id) return { error: "Airwallex Google Pay credentials not provided" };
99
+ let airwallexMockScenario = AirwallexGooglePayMockScenario.None;
100
+ if (mockScenario === GooglePayMockScenario.Success) airwallexMockScenario = AirwallexGooglePayMockScenario.Success;
101
+ else if (mockScenario === GooglePayMockScenario.Cancelled) airwallexMockScenario = AirwallexGooglePayMockScenario.Cancelled;
102
+ const adapter = new AirwallexGooglePayAdapter(airwallexMockScenario);
103
+ const config = {
104
+ merchantId: startData.merchant_name,
105
+ gatewayMerchantId: startData.airwallex_account_id,
106
+ amountDisplay: startData.amount_display,
107
+ currency: startData.currency,
108
+ country: startData.country,
109
+ isProduction: startData.is_sandbox === false,
110
+ googleMerchantId: startData.google_merchant_id
111
+ };
112
+ if (!adapter.initialize(config)) return { error: "Google Pay API not loaded. Add <script src=\"https://pay.google.com/gp/p/js/pay.js\"><\/script> to your page." };
113
+ return { adapter };
114
+ }
115
+ async function runAirwallexFlow(startData, mockScenario) {
116
+ const { adapter, error: adapterError } = initializeAirwallexAdapter(startData, mockScenario);
117
+ if (!adapter) return {
118
+ success: false,
119
+ error: adapterError
120
+ };
121
+ if (!await adapter.canMakePayment()) return {
122
+ success: false,
123
+ error: "Google Pay not available on this device"
124
+ };
125
+ const paymentResult = await adapter.showPaymentSheet();
126
+ if (!paymentResult.success) {
127
+ if ("cancelled" in paymentResult && paymentResult.cancelled) return {
128
+ success: false,
129
+ error: "Google Pay cancelled by user"
130
+ };
131
+ return {
132
+ success: false,
133
+ error: "error" in paymentResult ? paymentResult.error : "Unknown error"
134
+ };
135
+ }
136
+ return {
137
+ success: true,
138
+ token: paymentResult.token
139
+ };
140
+ }
141
+ /**
142
+ * Call /confirm endpoint - processes Google Pay token (Airwallex) or records setup (Stripe).
143
+ * For Airwallex, may return next_action if 3DS is required.
144
+ */
145
+ async function callConfirmEndpoint(apiBaseUrl, secureToken, googlePayToken, mockScenarioStr, checkoutRequestId) {
146
+ const requestBody = { mock_scenario: mockScenarioStr };
147
+ if (googlePayToken) requestBody.google_pay_token = googlePayToken;
85
148
  const result = await apiCall(`${apiBaseUrl}/api/checkout/${secureToken}/google-pay/confirm`, {
86
149
  method: "POST",
87
150
  headers: { "Content-Type": "application/json" },
88
- body: JSON.stringify({ mock_scenario: mockScenarioStr })
89
- });
90
- if (result.error || !result.data) return { errors: { google_pay: result.error || "Failed to confirm payment" } };
91
- const confirmData = result.data;
92
- if (confirmData.charge_status === "success") return { data: {
93
- id: confirmData.transaction_id,
94
- checkoutAttemptId: confirmData.checkout_attempt_id,
151
+ body: JSON.stringify(requestBody)
152
+ }, checkoutRequestId);
153
+ if (result.error || !result.data) return {
154
+ charge_status: "fail",
155
+ error_message: result.error || "Failed to confirm payment",
156
+ checkout_attempt_id: ""
157
+ };
158
+ return result.data;
159
+ }
160
+ /**
161
+ * Call /verify endpoint - verifies 3DS completion for Airwallex.
162
+ * Called after user completes 3DS challenge. May return another next_action for retry.
163
+ */
164
+ async function callVerifyEndpoint(apiBaseUrl, secureToken, checkoutRequestId) {
165
+ const result = await apiCall(`${apiBaseUrl}/api/checkout/${secureToken}/google-pay/verify`, {
166
+ method: "POST",
167
+ headers: { "Content-Type": "application/json" }
168
+ }, checkoutRequestId);
169
+ if (result.error || !result.data) return {
170
+ charge_status: "fail",
171
+ error_message: result.error || "Failed to verify payment",
172
+ checkout_attempt_id: ""
173
+ };
174
+ return result.data;
175
+ }
176
+ /**
177
+ * Convert GooglePayConfirmResponse to GooglePayResult for return to caller.
178
+ */
179
+ function toGooglePayResult(response, secureToken) {
180
+ if (response.charge_status === "success") return { data: {
181
+ id: response.transaction_id,
182
+ checkoutAttemptId: response.checkout_attempt_id,
95
183
  checkoutSessionId: secureToken,
96
184
  state: "checkout_succeeded"
97
185
  } };
98
- return { errors: { google_pay: confirmData.error_message || "Payment failed" } };
186
+ return { errors: { google_pay: response.error_message || "Payment failed" } };
187
+ }
188
+ /**
189
+ * Confirm Stripe Google Pay - simple flow without 3DS loop.
190
+ * Stripe.js handles 3DS internally during confirmCardSetup().
191
+ */
192
+ async function confirmStripeGooglePay(apiBaseUrl, secureToken, mockScenarioStr, checkoutRequestId) {
193
+ return toGooglePayResult(await callConfirmEndpoint(apiBaseUrl, secureToken, void 0, mockScenarioStr, checkoutRequestId), secureToken);
194
+ }
195
+ const MAX_USER_ACTIONS = 5;
196
+ /**
197
+ * Confirm Airwallex Google Pay with 3DS loop support.
198
+ *
199
+ * Flow:
200
+ * 1. Call /confirm with token → may return next_action (3DS)
201
+ * 2. If next_action: handle 3DS, then call /verify
202
+ * 3. Loop if /verify returns another next_action (3DS retry)
203
+ * 4. Return final result
204
+ *
205
+ * This mirrors the card.ts pattern for consistency.
206
+ */
207
+ async function confirmAirwallexGooglePay(apiBaseUrl, secureToken, googlePayToken, mockScenarioStr, checkoutRequestId) {
208
+ let userActionCount = 0;
209
+ let response = await callConfirmEndpoint(apiBaseUrl, secureToken, googlePayToken, mockScenarioStr, checkoutRequestId);
210
+ while (response.charge_status === "pending" && response.next_action && userActionCount < MAX_USER_ACTIONS) {
211
+ userActionCount++;
212
+ const actionResult = await handleNextAction(response.next_action);
213
+ const verifyResponse = await callVerifyEndpoint(apiBaseUrl, secureToken, checkoutRequestId);
214
+ if (verifyResponse.charge_status === "pending" && verifyResponse.next_action) {
215
+ if (!actionResult.success) console.log("[GooglePay:Airwallex] 3DS failed but retry available, continuing loop...");
216
+ response = verifyResponse;
217
+ continue;
218
+ }
219
+ if (!actionResult.success) return { errors: { google_pay: verifyResponse.error_message || actionResult.error || "3DS authentication failed" } };
220
+ return toGooglePayResult(verifyResponse, secureToken);
221
+ }
222
+ if (userActionCount >= MAX_USER_ACTIONS) return { errors: { google_pay: "Too many authentication attempts. Please try again." } };
223
+ return toGooglePayResult(response, secureToken);
99
224
  }
100
225
  const defSubmitPayment = (states) => {
101
226
  const submitPayment = async (_fields, options) => {
102
- const { apiBaseUrl, secureToken } = states;
227
+ const { apiBaseUrl, secureToken, environment } = states;
103
228
  const gpayOptions = options;
104
229
  const validationError = validateOptions(gpayOptions);
105
230
  if (validationError) return { errors: validationError };
106
231
  try {
107
232
  const mockScenarioStr = getMockScenarioStr(gpayOptions.mockScenario);
108
- const startResult = await callStartEndpoint(apiBaseUrl, secureToken, gpayOptions, mockScenarioStr);
233
+ const checkoutRequestId = generateCheckoutRequestId(environment);
234
+ console.log(`[GooglePay] Generated checkout_request_id: ${checkoutRequestId}`);
235
+ const startResult = await callStartEndpoint(apiBaseUrl, secureToken, gpayOptions, mockScenarioStr, checkoutRequestId);
109
236
  if (startResult.error || !startResult.data) return { errors: { google_pay: startResult.error || "Failed to start Google Pay" } };
110
- const { adapter, error: adapterError } = initializeAdapter(startResult.data.stripe_pk, startResult.data, gpayOptions.mockScenario);
111
- if (!adapter) return { errors: { google_pay: adapterError } };
112
- const paymentResult = await showPaymentSheetAndConfirm(adapter, startResult.data.client_secret);
113
- if (!paymentResult.success) return { errors: { google_pay: paymentResult.error } };
114
- return await callConfirmEndpoint(apiBaseUrl, secureToken, mockScenarioStr);
237
+ const startData = startResult.data;
238
+ if (startData.processor === "stripe") {
239
+ const stripeResult = await runStripeFlow(startData, gpayOptions.mockScenario);
240
+ if (!stripeResult.success) return { errors: { google_pay: stripeResult.error } };
241
+ return await confirmStripeGooglePay(apiBaseUrl, secureToken, mockScenarioStr, checkoutRequestId);
242
+ }
243
+ if (startData.processor === "airwallex") {
244
+ const airwallexResult = await runAirwallexFlow(startData, gpayOptions.mockScenario);
245
+ if (!airwallexResult.success) return { errors: { google_pay: airwallexResult.error } };
246
+ if (!airwallexResult.token) return { errors: { google_pay: "Google Pay token not received" } };
247
+ return await confirmAirwallexGooglePay(apiBaseUrl, secureToken, airwallexResult.token, mockScenarioStr, checkoutRequestId);
248
+ }
249
+ return { errors: { google_pay: `Unsupported processor: ${startData.processor}` } };
115
250
  } catch (error) {
116
251
  return { errors: { google_pay: `Google Pay error: ${error}` } };
117
252
  }
@@ -1 +1 @@
1
- {"version":3,"file":"google-pay.mjs","names":["submitPayment: TInternalFuncs[\"submitPayment\"]"],"sources":["../../src/payment-methods/google-pay.ts"],"sourcesContent":["import type { PaymentKitErrors, PaymentKitStates, TInternalFuncs } from \"../types\";\nimport { collectFraudMetadata, definePaymentMethod } from \"../utils\";\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 client_secret: string;\n stripe_pk: string;\n checkout_attempt_id: string;\n amount: number;\n currency: string;\n country: 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};\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>(url: string, options: RequestInit): Promise<{ data?: T; error?: string }> {\n const response = await fetch(url, options);\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): Promise<{ data?: GooglePayStartResponse; error?: string }> {\n return apiCall<GooglePayStartResponse>(`${apiBaseUrl}/api/checkout/${secureToken}/google-pay/start`, {\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}\n\nfunction initializeAdapter(\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 console.log(\"[GooglePay] PaymentRequest config:\", prConfig);\n\n adapter.createPaymentRequest(prConfig);\n return { adapter };\n}\n\nasync function showPaymentSheetAndConfirm(\n adapter: StripeGooglePayAdapter,\n clientSecret: string,\n): Promise<{ success: boolean; error?: string }> {\n // Check availability\n const isAvailable = await adapter.canMakePayment();\n console.log(\"[GooglePay] canMakePayment result:\", isAvailable);\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(clientSecret, paymentResult.paymentMethodId);\n\n if (!confirmResult.success) {\n paymentResult.complete(\"fail\");\n return { success: false, error: confirmResult.error };\n }\n\n paymentResult.complete(\"success\");\n return { success: true };\n}\n\nasync function callConfirmEndpoint(\n apiBaseUrl: string,\n secureToken: string,\n mockScenarioStr?: string,\n): Promise<GooglePayResult> {\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({ mock_scenario: mockScenarioStr }),\n },\n );\n\n if (result.error || !result.data) {\n return { errors: { google_pay: result.error || \"Failed to confirm payment\" } };\n }\n\n const confirmData = result.data;\n\n if (confirmData.charge_status === \"success\") {\n return {\n data: {\n id: confirmData.transaction_id,\n checkoutAttemptId: confirmData.checkout_attempt_id,\n checkoutSessionId: secureToken,\n state: \"checkout_succeeded\",\n },\n };\n }\n\n return { errors: { google_pay: confirmData.error_message || \"Payment failed\" } };\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 } = 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 // Step 1: Start Google Pay flow\n const startResult = await callStartEndpoint(apiBaseUrl, secureToken, gpayOptions, mockScenarioStr);\n if (startResult.error || !startResult.data) {\n return { errors: { google_pay: startResult.error || \"Failed to start Google Pay\" } };\n }\n\n // Step 2: Initialize Stripe adapter\n const { adapter, error: adapterError } = initializeAdapter(\n startResult.data.stripe_pk,\n startResult.data,\n gpayOptions.mockScenario,\n );\n if (!adapter) {\n return { errors: { google_pay: adapterError } };\n }\n\n // Step 3: Show payment sheet and confirm with Stripe\n const paymentResult = await showPaymentSheetAndConfirm(adapter, startResult.data.client_secret);\n if (!paymentResult.success) {\n return { errors: { google_pay: paymentResult.error } };\n }\n\n // Step 4: Confirm with backend\n return await callConfirmEndpoint(apiBaseUrl, secureToken, mockScenarioStr);\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, 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":";;;;;;AAsFA,eAAe,QACb,KACA,SACA,mBACuC;CAEvC,MAAM,UAAU,IAAI,QAAQ,QAAQ,QAAQ;AAC5C,KAAI,kBACF,SAAQ,IAAI,gBAAgB,kBAAkB;CAGhD,MAAM,WAAW,MAAM,MAAM,KAAK;EAAE,GAAG;EAAS;EAAS,CAAC;AAC1D,KAAI,CAAC,SAAS,IAAI;EAChB,IAAI,eAAe,mBAAmB,SAAS,OAAO;AACtD,MAAI;AAEF,mBADkB,MAAM,SAAS,MAAM,EACd,UAAU;UAC7B;AACN,kBAAe,SAAS,cAAc;;AAExC,SAAO,EAAE,OAAO,cAAc;;AAEhC,QAAO,EAAE,MAAM,MAAM,SAAS,MAAM,EAAE;;AAGxC,SAAS,gBAAgB,SAA0D;AACjF,KAAI,CAAC,SAAS,YACZ,QAAO,EAAE,cAAc,4BAA4B;AAErD,KAAI,CAAC,SAAS,cAAc,cAAc,CAAC,SAAS,cAAc,UAChE,QAAO,EAAE,eAAe,6CAA6C;AAEvE,QAAO;;AAGT,SAAS,mBAAmB,cAA0D;AACpF,QAAO,gBAAgB,iBAAiB,sBAAsB,OAAO,eAAe;;AAGtF,eAAe,kBACb,YACA,aACA,SACA,iBACA,mBAC4D;AAC5D,QAAO,QACL,GAAG,WAAW,gBAAgB,YAAY,oBAC1C;EACE,QAAQ;EACR,SAAS,EAAE,gBAAgB,oBAAoB;EAC/C,MAAM,KAAK,UAAU;GACnB,cAAc,QAAQ;GACtB,eAAe,QAAQ;GACvB,gBAAgB,sBAAsB;GACtC,eAAe;GAChB,CAA0B;EAC5B,EACD,kBACD;;AAOH,SAAS,wBACP,UACA,WACA,cACsD;CACtD,MAAM,UAAU,IAAI,uBAAuB,aAAa;AAExD,KAAI,CAAC,QAAQ,WAAW,SAAS,CAC/B,QAAO,EAAE,OAAO,gGAA6F;CAG/G,MAAM,WAAW;EACf,SAAS,UAAU;EACnB,UAAU,UAAU,SAAS,aAAa;EAC1C,OAAO;GAAE,OAAO;GAAS,QAAQ,UAAU;GAAQ;EACnD,kBAAkB;EAClB,mBAAmB;EACpB;AACD,SAAQ,qBAAqB,SAAS;AACtC,QAAO,EAAE,SAAS;;AAGpB,eAAe,cACb,WACA,cAC+C;AAC/C,KAAI,CAAC,UAAU,aAAa,CAAC,UAAU,cACrC,QAAO;EAAE,SAAS;EAAO,OAAO;EAAmC;CAIrE,MAAM,EAAE,SAAS,OAAO,iBAAiB,wBAAwB,UAAU,WAAW,WAAW,aAAa;AAC9G,KAAI,CAAC,QACH,QAAO;EAAE,SAAS;EAAO,OAAO;EAAc;AAMhD,KAAI,CAFgB,MAAM,QAAQ,gBAAgB,CAGhD,QAAO;EAAE,SAAS;EAAO,OAAO;EAA2C;CAI7E,MAAM,gBAAgB,MAAM,QAAQ,kBAAkB;AAEtD,KAAI,CAAC,cAAc,SAAS;AAC1B,MAAI,eAAe,iBAAiB,cAAc,UAChD,QAAO;GAAE,SAAS;GAAO,OAAO;GAAgC;AAGlE,SAAO;GAAE,SAAS;GAAO,OADJ,WAAW,gBAAgB,cAAc,QAAQ;GACxB;;CAIhD,MAAM,gBAAgB,MAAM,QAAQ,iBAAiB,UAAU,eAAe,cAAc,gBAAgB;AAE5G,KAAI,CAAC,cAAc,SAAS;AAC1B,gBAAc,SAAS,OAAO;AAC9B,SAAO;GAAE,SAAS;GAAO,OAAO,WAAW,gBAAgB,cAAc,QAAQ;GAAqB;;AAGxG,eAAc,SAAS,UAAU;AACjC,QAAO,EAAE,SAAS,MAAM;;AAO1B,SAAS,2BACP,WACA,cAIA;AACA,KAAI,CAAC,UAAU,iBAAiB,CAAC,UAAU,qBACzC,QAAO,EAAE,OAAO,iDAAiD;CAInE,IAAIA,wBAAwD,+BAA+B;AAC3F,KAAI,iBAAiB,sBAAsB,QACzC,yBAAwB,+BAA+B;UAC9C,iBAAiB,sBAAsB,UAChD,yBAAwB,+BAA+B;CAGzD,MAAM,UAAU,IAAI,0BAA0B,sBAAsB;CAEpE,MAAMC,SAAmC;EACvC,YAAY,UAAU;EACtB,mBAAmB,UAAU;EAC7B,eAAe,UAAU;EACzB,UAAU,UAAU;EACpB,SAAS,UAAU;EACnB,cAAc,UAAU,eAAe;EACvC,kBAAkB,UAAU;EAC7B;AAED,KAAI,CAAC,QAAQ,WAAW,OAAO,CAC7B,QAAO,EACL,OACE,iHACH;AAGH,QAAO,EAAE,SAAS;;AAGpB,eAAe,iBACb,WACA,cACgF;CAEhF,MAAM,EAAE,SAAS,OAAO,iBAAiB,2BAA2B,WAAW,aAAa;AAC5F,KAAI,CAAC,QACH,QAAO;EAAE,SAAS;EAAO,OAAO;EAAc;AAMhD,KAAI,CAFgB,MAAM,QAAQ,gBAAgB,CAGhD,QAAO;EAAE,SAAS;EAAO,OAAO;EAA2C;CAI7E,MAAM,gBAAgB,MAAM,QAAQ,kBAAkB;AAEtD,KAAI,CAAC,cAAc,SAAS;AAC1B,MAAI,eAAe,iBAAiB,cAAc,UAChD,QAAO;GAAE,SAAS;GAAO,OAAO;GAAgC;AAGlE,SAAO;GAAE,SAAS;GAAO,OADJ,WAAW,gBAAgB,cAAc,QAAQ;GACxB;;AAIhD,QAAO;EAAE,SAAS;EAAM,OAAO,cAAc;EAAO;;;;;;AAWtD,eAAe,oBACb,YACA,aACA,gBACA,iBACA,mBACmC;CACnC,MAAMC,cAAuC,EAC3C,eAAe,iBAChB;AAED,KAAI,eACF,aAAY,mBAAmB;CAGjC,MAAM,SAAS,MAAM,QACnB,GAAG,WAAW,gBAAgB,YAAY,sBAC1C;EACE,QAAQ;EACR,SAAS,EAAE,gBAAgB,oBAAoB;EAC/C,MAAM,KAAK,UAAU,YAAY;EAClC,EACD,kBACD;AAED,KAAI,OAAO,SAAS,CAAC,OAAO,KAC1B,QAAO;EACL,eAAe;EACf,eAAe,OAAO,SAAS;EAC/B,qBAAqB;EACtB;AAGH,QAAO,OAAO;;;;;;AAOhB,eAAe,mBACb,YACA,aACA,mBACmC;CACnC,MAAM,SAAS,MAAM,QACnB,GAAG,WAAW,gBAAgB,YAAY,qBAC1C;EACE,QAAQ;EACR,SAAS,EAAE,gBAAgB,oBAAoB;EAChD,EACD,kBACD;AAED,KAAI,OAAO,SAAS,CAAC,OAAO,KAC1B,QAAO;EACL,eAAe;EACf,eAAe,OAAO,SAAS;EAC/B,qBAAqB;EACtB;AAGH,QAAO,OAAO;;;;;AAMhB,SAAS,kBAAkB,UAAoC,aAAsC;AACnG,KAAI,SAAS,kBAAkB,UAC7B,QAAO,EACL,MAAM;EACJ,IAAI,SAAS;EACb,mBAAmB,SAAS;EAC5B,mBAAmB;EACnB,OAAO;EACR,EACF;AAEH,QAAO,EAAE,QAAQ,EAAE,YAAY,SAAS,iBAAiB,kBAAkB,EAAE;;;;;;AAW/E,eAAe,uBACb,YACA,aACA,iBACA,mBAC0B;AAE1B,QAAO,kBADU,MAAM,oBAAoB,YAAY,aAAa,QAAW,iBAAiB,kBAAkB,EAC/E,YAAY;;AAOjD,MAAM,mBAAmB;;;;;;;;;;;;AAazB,eAAe,0BACb,YACA,aACA,gBACA,iBACA,mBAC0B;CAC1B,IAAI,kBAAkB;CAGtB,IAAI,WAAW,MAAM,oBAAoB,YAAY,aAAa,gBAAgB,iBAAiB,kBAAkB;AAGrH,QAAO,SAAS,kBAAkB,aAAa,SAAS,eAAe,kBAAkB,kBAAkB;AACzG;EAGA,MAAM,eAAe,MAAM,iBAAiB,SAAS,YAAY;EAIjE,MAAM,iBAAiB,MAAM,mBAAmB,YAAY,aAAa,kBAAkB;AAG3F,MAAI,eAAe,kBAAkB,aAAa,eAAe,aAAa;AAC5E,OAAI,CAAC,aAAa,QAChB,SAAQ,IAAI,2EAA2E;AAEzF,cAAW;AACX;;AAIF,MAAI,CAAC,aAAa,QAEhB,QAAO,EACL,QAAQ,EACN,YAAY,eAAe,iBAAiB,aAAa,SAAS,6BACnE,EACF;AAIH,SAAO,kBAAkB,gBAAgB,YAAY;;AAIvD,KAAI,mBAAmB,iBACrB,QAAO,EAAE,QAAQ,EAAE,YAAY,uDAAuD,EAAE;AAI1F,QAAO,kBAAkB,UAAU,YAAY;;AAOjD,MAAM,oBAAoB,WAA6B;CACrD,MAAMC,gBAAiD,OAAO,SAAS,YAAY;EACjF,MAAM,EAAE,YAAY,aAAa,gBAAgB;EACjD,MAAM,cAAc;EAGpB,MAAM,kBAAkB,gBAAgB,YAAY;AACpD,MAAI,gBACF,QAAO,EAAE,QAAQ,iBAAiB;AAGpC,MAAI;GACF,MAAM,kBAAkB,mBAAmB,YAAY,aAAa;GAGpE,MAAM,oBAAoB,0BAA0B,YAAY;AAChE,WAAQ,IAAI,8CAA8C,oBAAoB;GAG9E,MAAM,cAAc,MAAM,kBACxB,YACA,aACA,aACA,iBACA,kBACD;AACD,OAAI,YAAY,SAAS,CAAC,YAAY,KACpC,QAAO,EAAE,QAAQ,EAAE,YAAY,YAAY,SAAS,8BAA8B,EAAE;GAGtF,MAAM,YAAY,YAAY;AAG9B,OAAI,UAAU,cAAc,UAAU;IAEpC,MAAM,eAAe,MAAM,cAAc,WAAW,YAAY,aAAa;AAC7E,QAAI,CAAC,aAAa,QAChB,QAAO,EAAE,QAAQ,EAAE,YAAY,aAAa,OAAO,EAAE;AAGvD,WAAO,MAAM,uBAAuB,YAAY,aAAa,iBAAiB,kBAAkB;;AAGlG,OAAI,UAAU,cAAc,aAAa;IAEvC,MAAM,kBAAkB,MAAM,iBAAiB,WAAW,YAAY,aAAa;AACnF,QAAI,CAAC,gBAAgB,QACnB,QAAO,EAAE,QAAQ,EAAE,YAAY,gBAAgB,OAAO,EAAE;AAE1D,QAAI,CAAC,gBAAgB,MACnB,QAAO,EAAE,QAAQ,EAAE,YAAY,iCAAiC,EAAE;AAGpE,WAAO,MAAM,0BACX,YACA,aACA,gBAAgB,OAChB,iBACA,kBACD;;AAGH,UAAO,EAAE,QAAQ,EAAE,YAAY,0BAA0B,UAAU,aAAa,EAAE;WAC3E,OAAO;AACd,UAAO,EAAE,QAAQ,EAAE,YAAY,qBAAqB,SAAS,EAAE;;;AAInE,QAAO;;AAOT,MAAM,yBAAyB,qBAAqB,qBAAqB;AACvE,QAAO;EACL,MAAM;EACN,eAAe,EAAE;EACjB,eAAe;GACb,eAAe,iBAAiB,iBAAiB;GACjD,eAAe;GAChB;EACF;EACD;AAIF,yBAAe"}
@@ -1 +1 @@
1
- {"version":3,"file":"next-action-handlers.d.mts","names":[],"sources":["../../src/payment-methods/next-action-handlers.ts"],"sourcesContent":[],"mappings":";;;;;KA6CY,gBAAA;;;;;;;;;;;;;cAgCC,+BACC,YAAY,8CACvB,QAAQ"}
1
+ {"version":3,"file":"next-action-handlers.d.mts","names":[],"sources":["../../src/payment-methods/next-action-handlers.ts"],"sourcesContent":[],"mappings":";;;;;KA8CY,gBAAA;;;;;;;;;;;;;cA8DC,+BACC,YAAY,8CACvB,QAAQ"}
@@ -1,3 +1,3 @@
1
- import { t as handleNextAction } from "../next-action-handlers-DTsWjUIA.mjs";
1
+ import { t as handleNextAction } from "../next-action-handlers-BZs04hYb.mjs";
2
2
 
3
3
  export { handleNextAction };
@@ -1,6 +1,6 @@
1
- import "../connect-card-C582hcWw.mjs";
2
- import "../connect-tunnel-x-B7iMQ7DX.mjs";
3
- import { r as PaymentMethod } from "../types-DsVMq4jZ.mjs";
1
+ import { r as PaymentMethod } from "../types-CPuloCtF.mjs";
2
+ import "../connect-card-DTfXuTsW.mjs";
3
+ import "../connect-tunnel-x-Dxcg5Y7Y.mjs";
4
4
 
5
5
  //#region src/payment-methods/paypal.d.ts
6
6
  type PayPalCustomerInfo = {