@payment-kit-js/vanilla 0.5.19 → 0.5.20
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cdn/paymentkit.js +19 -3
- package/dist/cdn/paymentkit.js.map +2 -2
- package/dist/cdn/paymentkit.min.js +6 -6
- package/dist/cdn/paymentkit.min.js.map +3 -3
- package/dist/index.mjs +1 -1
- package/dist/index.mjs.map +1 -1
- package/dist/payment-methods/card.d.mts.map +1 -1
- package/dist/payment-methods/card.mjs +22 -1
- package/dist/payment-methods/card.mjs.map +1 -1
- package/package.json +2 -2
package/dist/index.mjs
CHANGED
|
@@ -4,7 +4,7 @@ import { n as connectToTunnelXIframe } from "./connect-tunnel-x-5XOct5Tm.mjs";
|
|
|
4
4
|
import { o as getOrCreateCheckoutRequestId, r as createCheckoutIFrame } from "./utils-DIqrx4XF.mjs";
|
|
5
5
|
|
|
6
6
|
//#region package.json
|
|
7
|
-
var version = "0.5.
|
|
7
|
+
var version = "0.5.20";
|
|
8
8
|
|
|
9
9
|
//#endregion
|
|
10
10
|
//#region src/types.ts
|
package/dist/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.mjs","names":["STATIC_ENVIRONMENT_URLS: Record<Exclude<PaymentKitEnvironment, \"tunnel\">, EnvironmentUrls>","PaymentKit: PaymentKitType","PACKAGE_VERSION","loadPromises: Promise<void>[]","externalFuncsMapByPm: ExternalFuncsMapByPm<PaymentMethods>","submit: PaymentKitReturnType[\"submit\"]"],"sources":["../package.json","../src/types.ts","../src/index.ts"],"sourcesContent":["{\n \"name\": \"@payment-kit-js/vanilla\",\n \"version\": \"0.5.19\",\n \"main\": \"./dist/index.mjs\",\n \"types\": \"./dist/index.d.mts\",\n \"module\": \"./dist/index.mjs\",\n \"exports\": {\n \".\": \"./dist/index.mjs\",\n \"./payment-methods/affirm\": \"./dist/payment-methods/affirm.mjs\",\n \"./payment-methods/afterpay\": \"./dist/payment-methods/afterpay.mjs\",\n \"./payment-methods/airwallex-apple-pay-adapter\": \"./dist/payment-methods/airwallex-apple-pay-adapter.mjs\",\n \"./payment-methods/airwallex-google-pay-adapter\": \"./dist/payment-methods/airwallex-google-pay-adapter.mjs\",\n \"./payment-methods/apple-pay\": \"./dist/payment-methods/apple-pay.mjs\",\n \"./payment-methods/bnpl-shared\": \"./dist/payment-methods/bnpl-shared.mjs\",\n \"./payment-methods/card\": \"./dist/payment-methods/card.mjs\",\n \"./payment-methods/google-pay\": \"./dist/payment-methods/google-pay.mjs\",\n \"./payment-methods/klarna\": \"./dist/payment-methods/klarna.mjs\",\n \"./payment-methods/next-action-handlers\": \"./dist/payment-methods/next-action-handlers.mjs\",\n \"./payment-methods/paypal\": \"./dist/payment-methods/paypal.mjs\",\n \"./payment-methods/stripe-apple-pay-adapter\": \"./dist/payment-methods/stripe-apple-pay-adapter.mjs\",\n \"./payment-methods/stripe-google-pay-adapter\": \"./dist/payment-methods/stripe-google-pay-adapter.mjs\",\n \"./payment-methods/vgs-collect-loader\": \"./dist/payment-methods/vgs-collect-loader.mjs\",\n \"./penpal/connect-card\": \"./dist/penpal/connect-card.mjs\",\n \"./penpal/connect-tunnel-x\": \"./dist/penpal/connect-tunnel-x.mjs\",\n \"./package.json\": \"./package.json\"\n },\n \"license\": \"MIT\",\n \"description\": \"Vanilla package for PaymentKit\",\n \"files\": [\n \"dist\",\n \"dist/cdn\"\n ],\n \"scripts\": {\n \"dev\": \"yarn tsdown --watch ./src\",\n \"build:npm\": \"yarn tsdown\",\n \"build:cdn\": \"node build-cdn.mjs\",\n \"build\": \"yarn build:npm && yarn build:cdn\"\n },\n \"packageManager\": \"yarn@4.10.3\",\n \"dependencies\": {\n \"nanoid\": \"^5.0.7\",\n \"penpal\": \"^7.0.4\",\n \"posthog-js\": \"^1.196.0\",\n \"valibot\": \"^1.1.0\"\n },\n \"devDependencies\": {\n \"@pkg/sdk\": \"workspace:*\",\n \"@pkg/tsconfig\": \"workspace:*\",\n \"@stripe/stripe-js\": \"^8.6.1\",\n \"@types/applepayjs\": \"^14.0.9\",\n \"esbuild\": \"^0.24.2\",\n \"tsdown\": \"^0.15.10\",\n \"typescript\": \"^5.9.3\"\n },\n \"stableVersion\": \"0.5.19\"\n}\n","import type { CheckoutTimingTracker } from \"./analytics\";\nimport type { CardErrorCode } from \"./penpal/connect-card\";\nimport type { TunnelXIFrameConnection } from \"./penpal/connect-tunnel-x\";\n\nexport type PaymentKitEnvironment = \"local\" | \"tunnel\" | \"sandbox\" | \"staging\" | \"production\";\n\ntype EnvironmentUrls = {\n baseUrl: string;\n apiBaseUrl: string;\n};\n\n// TODO: Cleanup this after launch to only use SDK instead of plain fetch.\nconst STATIC_ENVIRONMENT_URLS: Record<Exclude<PaymentKitEnvironment, \"tunnel\">, EnvironmentUrls> = {\n local: {\n baseUrl: \"http://localhost:9101\",\n apiBaseUrl: \"http://localhost:9000\",\n },\n sandbox: {\n baseUrl: \"https://app.paymentkit.com/customer\",\n apiBaseUrl: \"https://app.paymentkit.com\",\n },\n staging: {\n baseUrl: \"https://staging.paymentkit.com/customer\",\n apiBaseUrl: \"https://staging.paymentkit.com\",\n },\n production: {\n baseUrl: \"https://app.paymentkit.com/customer\",\n apiBaseUrl: \"https://app.paymentkit.com\",\n },\n};\n\nfunction getTunnelUrls(): EnvironmentUrls {\n if (typeof window !== \"undefined\") {\n const params = new URLSearchParams(window.location.search);\n const apiBaseUrl = params.get(\"api_base_url\");\n if (apiBaseUrl) {\n return {\n baseUrl: window.location.origin,\n apiBaseUrl,\n };\n }\n // For loclx-style tunnels where API hostname is derived from the current hostname\n const hostname = window.location.hostname;\n if (hostname.includes(\"loclx.io\")) {\n const apiHostname = hostname.replace(/cportal|playground/, \"api\");\n return {\n baseUrl: `https://${hostname}`,\n apiBaseUrl: `https://${apiHostname}`,\n };\n }\n }\n // Fallback\n return STATIC_ENVIRONMENT_URLS.local;\n}\n\nexport function getUrlsForEnvironment(environment: PaymentKitEnvironment): EnvironmentUrls {\n if (environment === \"tunnel\") {\n return getTunnelUrls();\n }\n const urls = STATIC_ENVIRONMENT_URLS[environment];\n if (!urls) {\n console.warn(`[PaymentKit] Invalid environment \"${environment}\", falling back to local`);\n return STATIC_ENVIRONMENT_URLS.local;\n }\n return urls;\n}\n\ntype FormFieldNames =\n | \"customer_name\"\n | \"customer_email\"\n | \"customer_country\"\n | \"customer_zip_code\"\n | \"customer_business_name\"\n | \"customer_address_line1\"\n | \"customer_address_line2\"\n | \"customer_city\"\n | \"customer_state\"\n | \"customer_tax_ids\"\n | \"shipping_address_line1\"\n | \"shipping_address_line2\"\n | \"shipping_city\"\n | \"shipping_state\"\n | \"shipping_zip_code\"\n | \"shipping_country\";\n\nexport type FormErrorCodes = \"required\" | \"invalid\";\n\nexport type CardSubmitOptions = {\n skipCustomerValidation?: boolean;\n};\n\nexport type TInternalFuncs = {\n submitPayment: (\n fields: Partial<PaymentKitFields>,\n options?: unknown,\n ) => Promise<{ data: { [key: string]: unknown }; errors?: never } | { data?: never; errors: PaymentKitErrors }>;\n cleanup?: () => void;\n};\n\nexport type PaymentKit = <T extends readonly PaymentMethod<unknown>[]>(options: {\n environment?: PaymentKitEnvironment;\n secureToken: string;\n paymentMethods: T;\n /** Card tokenization mode from checkout session */\n cardTokenizationMode?: \"direct\" | \"vgs\";\n /** VGS vault ID from checkout session */\n vgsVaultId?: string;\n /** VGS environment from checkout session */\n vgsEnvironment?: string;\n /** @internal Enable analytics test mode for e2e testing */\n __enableAnalyticsTestMode?: boolean;\n}) => ExternalFuncsMapByPm<T> & {\n submit: PaymentKitSubmitHandler<T>;\n cleanup: () => void;\n};\n\ntype PaymentKitSubmitHandler<T extends readonly PaymentMethod<unknown>[]> = <\n N extends keyof ExternalFuncsMapByPm<T>,\n>(options: {\n fields: Partial<PaymentKitFields>;\n paymentMethod: N;\n options?: unknown;\n onError: (error: PaymentKitErrors) => void;\n onSuccess: (data: { [key: string]: unknown }) => void;\n}) => void;\n\nexport type PaymentKitStates = {\n baseUrl: string;\n apiBaseUrl: string;\n secureToken: string;\n environment: PaymentKitEnvironment;\n tunnelXConnection: TunnelXIFrameConnection;\n timingTracker: CheckoutTimingTracker;\n /** Checkout request ID for correlating all API calls and analytics events */\n checkoutRequestId: string;\n /** Card tokenization mode: 'direct' (KMS) or 'vgs' (VGS Collect + proxy) */\n cardTokenizationMode?: \"direct\" | \"vgs\";\n /** VGS vault ID (only when cardTokenizationMode is 'vgs') */\n vgsVaultId?: string;\n /** VGS environment: 'sandbox' or 'live' (only when cardTokenizationMode is 'vgs') */\n vgsEnvironment?: string;\n /** @internal Promise that resolves when session config (incl. VGS mode) is auto-detected */\n _sessionConfigReady?: Promise<void>;\n};\n\nexport type PaymentKitErrors = {\n root?: string;\n card_pan?: CardErrorCode;\n card_exp?: CardErrorCode;\n card_cvc?: CardErrorCode;\n paypal?: string;\n google_pay?: string;\n apple_pay?: string;\n processor_id?: string;\n amount?: string;\n currency?: string;\n country?: string;\n} & { [key in FormFieldNames]?: FormErrorCodes | string };\n\nexport type PaymentKitFields = { [key in FormFieldNames]: string };\n\nexport type PaymentMethod<TExternalFuncs = unknown, TName = string> = (paymentKitStates: PaymentKitStates) => {\n name: TName;\n externalFuncs: TExternalFuncs;\n internalFuncs: TInternalFuncs;\n};\n\nexport type ExternalFuncsMapByPm<T extends readonly PaymentMethod<unknown>[]> = {\n [K in T[number] as ReturnType<K>[\"name\"]]: ReturnType<K>[\"externalFuncs\"];\n};\n","import { version as PACKAGE_VERSION } from \"../package.json\";\nimport { AnalyticsService, CheckoutTimingTracker } from \"./analytics\";\nimport { connectToTunnelXIframe } from \"./penpal/connect-tunnel-x\";\nimport type { ExternalFuncsMapByPm, PaymentKit as PaymentKitType } from \"./types\";\nimport { getUrlsForEnvironment } from \"./types\";\nimport { createCheckoutIFrame } from \"./utils\";\nimport { getOrCreateCheckoutRequestId } from \"./utils/checkout-request-id\";\n\n/** Append script to DOM, falling back to head if body not ready */\nfunction appendScriptToDOM(script: HTMLScriptElement): void {\n const target = document.body ?? document.head;\n target.appendChild(script);\n}\n\n/**\n * Map fraud script URL to the script ID that the processor's script expects.\n * Airwallex's fingerprint script looks for `id=\"airwallex-fraud-api\"` via getElementById.\n * Other processors may have different ID requirements.\n */\nfunction getFraudScriptId(url: string): string {\n // Airwallex expects exactly \"airwallex-fraud-api\" as the script ID\n if (url.includes(\"airwallex.com\")) {\n return \"airwallex-fraud-api\";\n }\n // Fallback for other processors: use a hash-based ID\n const urlObj = new URL(url);\n const hash = btoa(urlObj.origin + urlObj.pathname)\n .replace(/[^a-zA-Z0-9]/g, \"\")\n .slice(0, 12);\n return `pk-fraud-script-${hash}`;\n}\n\ntype PaymentKitReturnType = ReturnType<PaymentKitType>;\n\nconst createTunnelXConnection = (baseUrl: string, apiBaseUrl: string, token: string) => {\n const iframe = createCheckoutIFrame(\"tunnel-x\", baseUrl, {\n checkout_token: token,\n api_base_url: apiBaseUrl,\n });\n // Tunnel-X is a hidden communication iframe, not a visible UI element\n Object.assign(iframe.style, { width: \"0\", height: \"0\", position: \"absolute\", visibility: \"hidden\" });\n document.body.appendChild(iframe);\n\n const connection = connectToTunnelXIframe(iframe, {});\n\n let destroyed = false;\n const unmount = () => {\n if (destroyed) return;\n destroyed = true;\n connection.destroy();\n iframe.remove();\n };\n\n return { unmount, connection };\n};\n\nconst PaymentKit: PaymentKitType = ({\n environment = \"production\",\n secureToken,\n paymentMethods,\n cardTokenizationMode,\n vgsVaultId,\n vgsEnvironment,\n __enableAnalyticsTestMode,\n}) => {\n type PaymentMethods = typeof paymentMethods;\n\n // Resolve URLs from environment\n const { baseUrl, apiBaseUrl } = getUrlsForEnvironment(environment);\n\n // Log version for debugging\n console.log(`[PaymentKit] v${PACKAGE_VERSION} initialized (env: ${environment})`);\n\n // Generate checkout request ID for correlating all API calls and analytics events\n // This is idempotent - same ID is reused across multiple PaymentKit instantiations\n const checkoutRequestId = getOrCreateCheckoutRequestId(environment);\n console.log(`[PaymentKit] checkout_request_id: ${checkoutRequestId}`);\n\n // Initialize analytics and identify with secure token for correlation\n AnalyticsService.init(apiBaseUrl, __enableAnalyticsTestMode);\n AnalyticsService.identify(secureToken);\n\n // Create timing tracker for this checkout session\n const timingTracker = new CheckoutTimingTracker(secureToken, checkoutRequestId);\n\n // Track page ready immediately\n timingTracker.trackPageReady();\n\n const { connection: tunnelXConnection, unmount: unmountTunnelX } = createTunnelXConnection(\n baseUrl,\n apiBaseUrl,\n secureToken,\n );\n\n const paymentKitStates = {\n baseUrl,\n apiBaseUrl,\n secureToken,\n environment,\n tunnelXConnection,\n timingTracker,\n checkoutRequestId,\n cardTokenizationMode,\n vgsVaultId,\n vgsEnvironment,\n _sessionConfigReady: undefined as Promise<void> | undefined,\n _fraudScriptIds: [] as string[],\n _fraudScriptsReady: undefined as Promise<void> | undefined,\n };\n\n // Auto-detect VGS mode from session API if not explicitly provided.\n // The promise resolves before any card mount or submit needs the mode.\n if (cardTokenizationMode === undefined) {\n const sessionConfigUrl = `${apiBaseUrl}/api/checkout-sessions/token/${secureToken}`;\n console.log(\n `[PaymentKit] cardTokenizationMode not provided — auto-detecting from session API: ${sessionConfigUrl}`,\n );\n paymentKitStates._sessionConfigReady = fetch(sessionConfigUrl)\n .then((resp) => {\n if (!resp.ok) {\n console.warn(`[PaymentKit] Session config fetch failed with status ${resp.status}`);\n return null;\n }\n return resp.json();\n })\n .then((session) => {\n if (session && typeof session === \"object\" && typeof session.card_tokenization_mode === \"string\") {\n paymentKitStates.cardTokenizationMode = session.card_tokenization_mode || \"direct\";\n console.log(\n `[PaymentKit] Session config detected — cardTokenizationMode: ${paymentKitStates.cardTokenizationMode}`,\n );\n\n // Validate VGS config from API before trusting it\n const vaultId = session.vgs_vault_id;\n const vgsEnv = session.vgs_environment;\n if (vaultId && /^tnt[a-z0-9]+$/.test(vaultId) && (vgsEnv === \"sandbox\" || vgsEnv === \"live\")) {\n paymentKitStates.vgsVaultId = vaultId;\n paymentKitStates.vgsEnvironment = vgsEnv;\n console.log(`[PaymentKit] VGS config valid — vaultId: ${vaultId}, environment: ${vgsEnv}`);\n } else if (paymentKitStates.cardTokenizationMode === \"vgs\") {\n console.warn(\n `[PaymentKit] Invalid VGS config from session API (vaultId: ${vaultId}, env: ${vgsEnv}), falling back to direct mode`,\n );\n paymentKitStates.cardTokenizationMode = \"direct\";\n }\n\n // Inject fraud fingerprinting scripts (for processors that support device fingerprinting)\n if (session?.fraud_script_urls?.length) {\n const loadPromises: Promise<void>[] = [];\n\n session.fraud_script_urls.forEach((url: string) => {\n const scriptId = getFraudScriptId(url);\n\n if (document.getElementById(scriptId)) {\n console.log(`[PaymentKit] Fraud script already present: ${scriptId}`);\n loadPromises.push(Promise.resolve());\n return;\n }\n\n const script = document.createElement(\"script\");\n script.type = \"text/javascript\";\n script.async = true;\n script.id = scriptId;\n script.src = url;\n script.dataset.orderSessionId = checkoutRequestId;\n\n const loadPromise = new Promise<void>((resolve) => {\n script.onload = () => {\n console.log(`[PaymentKit] Fraud script loaded: ${scriptId}`);\n resolve();\n };\n script.onerror = () => {\n console.error(\n `[PaymentKit] Failed to load fraud prevention script. If using Content-Security-Policy, add to script-src: ${new URL(url).origin}`,\n );\n resolve(); // Resolve anyway — don't block payment on optional fraud script\n };\n });\n\n loadPromises.push(loadPromise);\n paymentKitStates._fraudScriptIds.push(scriptId);\n appendScriptToDOM(script);\n });\n\n paymentKitStates._fraudScriptsReady = Promise.all(loadPromises).then(() => {\n console.log(`[PaymentKit] All ${loadPromises.length} fraud fingerprinting script(s) ready`);\n });\n\n // Log each injected script with its data-order-session-id for debugging\n session.fraud_script_urls.forEach((url: string) => {\n const scriptId = getFraudScriptId(url);\n console.log(\n `[PaymentKit] Fraud script injected: id=${scriptId}, data-order-session-id=${checkoutRequestId}, src=${url}`,\n );\n });\n console.log(`[PaymentKit] Injected ${session.fraud_script_urls.length} fraud fingerprinting script(s)`);\n }\n } else {\n console.log(\"[PaymentKit] Session response missing card_tokenization_mode — defaulting to direct mode\");\n paymentKitStates.cardTokenizationMode = \"direct\";\n }\n })\n .catch((err) => {\n console.error(\"[PaymentKit] Session config fetch error — falling back to direct mode:\", err);\n paymentKitStates.cardTokenizationMode = \"direct\";\n });\n } else {\n console.log(`[PaymentKit] cardTokenizationMode explicitly set: ${cardTokenizationMode}`);\n }\n\n const pmInstances = paymentMethods.map((paymentMethod) => paymentMethod(paymentKitStates));\n\n const externalFuncsMapByPm: ExternalFuncsMapByPm<PaymentMethods> = pmInstances.reduce(\n (acc, { name, externalFuncs }) => {\n // @ts-expect-error - typecase this better in future\n acc[name] = externalFuncs;\n return acc;\n },\n {} as ExternalFuncsMapByPm<PaymentMethods>,\n );\n\n const submit: PaymentKitReturnType[\"submit\"] = ({\n paymentMethod: paymentMethodName,\n fields,\n options,\n onSuccess,\n onError,\n }) => {\n const paymentMethod = pmInstances.find(({ name }) => name === paymentMethodName);\n if (!paymentMethod) {\n onError({ root: \"payment_method_not_found\" });\n return;\n }\n paymentMethod.internalFuncs\n .submitPayment(fields, options)\n .then(({ data, errors }) => {\n errors ? onError(errors) : onSuccess(data);\n })\n .catch((e) => {\n console.error(\"PaymentKit:submit:catch\", e);\n\n // Try to extract error message from response\n if (e?.response?.data) {\n onError(e.response.data);\n } else if (e?.message) {\n onError({ root: e.message });\n } else {\n onError({ root: \"unknown_error\" });\n }\n });\n };\n\n const cleanup = () => {\n // Clean up fraud fingerprinting scripts\n for (const scriptId of paymentKitStates._fraudScriptIds) {\n const script = document.getElementById(scriptId);\n if (script) {\n script.remove();\n }\n }\n paymentKitStates._fraudScriptIds = [];\n\n // Clean up all payment method instances\n for (const pm of pmInstances) {\n if (pm.internalFuncs.cleanup) {\n pm.internalFuncs.cleanup();\n }\n }\n unmountTunnelX();\n };\n\n return {\n submit,\n cleanup,\n ...externalFuncsMapByPm,\n };\n};\n\nexport type { PaymentKitEnvironment, PaymentKitFields } from \"./types\";\n\nexport default PaymentKit;\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.mjs","names":["STATIC_ENVIRONMENT_URLS: Record<Exclude<PaymentKitEnvironment, \"tunnel\">, EnvironmentUrls>","PaymentKit: PaymentKitType","PACKAGE_VERSION","loadPromises: Promise<void>[]","externalFuncsMapByPm: ExternalFuncsMapByPm<PaymentMethods>","submit: PaymentKitReturnType[\"submit\"]"],"sources":["../package.json","../src/types.ts","../src/index.ts"],"sourcesContent":["{\n \"name\": \"@payment-kit-js/vanilla\",\n \"version\": \"0.5.20\",\n \"main\": \"./dist/index.mjs\",\n \"types\": \"./dist/index.d.mts\",\n \"module\": \"./dist/index.mjs\",\n \"exports\": {\n \".\": \"./dist/index.mjs\",\n \"./payment-methods/affirm\": \"./dist/payment-methods/affirm.mjs\",\n \"./payment-methods/afterpay\": \"./dist/payment-methods/afterpay.mjs\",\n \"./payment-methods/airwallex-apple-pay-adapter\": \"./dist/payment-methods/airwallex-apple-pay-adapter.mjs\",\n \"./payment-methods/airwallex-google-pay-adapter\": \"./dist/payment-methods/airwallex-google-pay-adapter.mjs\",\n \"./payment-methods/apple-pay\": \"./dist/payment-methods/apple-pay.mjs\",\n \"./payment-methods/bnpl-shared\": \"./dist/payment-methods/bnpl-shared.mjs\",\n \"./payment-methods/card\": \"./dist/payment-methods/card.mjs\",\n \"./payment-methods/google-pay\": \"./dist/payment-methods/google-pay.mjs\",\n \"./payment-methods/klarna\": \"./dist/payment-methods/klarna.mjs\",\n \"./payment-methods/next-action-handlers\": \"./dist/payment-methods/next-action-handlers.mjs\",\n \"./payment-methods/paypal\": \"./dist/payment-methods/paypal.mjs\",\n \"./payment-methods/stripe-apple-pay-adapter\": \"./dist/payment-methods/stripe-apple-pay-adapter.mjs\",\n \"./payment-methods/stripe-google-pay-adapter\": \"./dist/payment-methods/stripe-google-pay-adapter.mjs\",\n \"./payment-methods/vgs-collect-loader\": \"./dist/payment-methods/vgs-collect-loader.mjs\",\n \"./penpal/connect-card\": \"./dist/penpal/connect-card.mjs\",\n \"./penpal/connect-tunnel-x\": \"./dist/penpal/connect-tunnel-x.mjs\",\n \"./package.json\": \"./package.json\"\n },\n \"license\": \"MIT\",\n \"description\": \"Vanilla package for PaymentKit\",\n \"files\": [\n \"dist\",\n \"dist/cdn\"\n ],\n \"scripts\": {\n \"dev\": \"yarn tsdown --watch ./src\",\n \"build:npm\": \"yarn tsdown\",\n \"build:cdn\": \"node build-cdn.mjs\",\n \"build\": \"yarn build:npm && yarn build:cdn\"\n },\n \"packageManager\": \"yarn@4.10.3\",\n \"dependencies\": {\n \"nanoid\": \"^5.0.7\",\n \"penpal\": \"^7.0.4\",\n \"posthog-js\": \"^1.196.0\",\n \"valibot\": \"^1.1.0\"\n },\n \"devDependencies\": {\n \"@pkg/sdk\": \"workspace:*\",\n \"@pkg/tsconfig\": \"workspace:*\",\n \"@stripe/stripe-js\": \"^8.6.1\",\n \"@types/applepayjs\": \"^14.0.9\",\n \"esbuild\": \"^0.24.2\",\n \"tsdown\": \"^0.15.10\",\n \"typescript\": \"^5.9.3\"\n },\n \"stableVersion\": \"0.5.20\"\n}\n","import type { CheckoutTimingTracker } from \"./analytics\";\nimport type { CardErrorCode } from \"./penpal/connect-card\";\nimport type { TunnelXIFrameConnection } from \"./penpal/connect-tunnel-x\";\n\nexport type PaymentKitEnvironment = \"local\" | \"tunnel\" | \"sandbox\" | \"staging\" | \"production\";\n\ntype EnvironmentUrls = {\n baseUrl: string;\n apiBaseUrl: string;\n};\n\n// TODO: Cleanup this after launch to only use SDK instead of plain fetch.\nconst STATIC_ENVIRONMENT_URLS: Record<Exclude<PaymentKitEnvironment, \"tunnel\">, EnvironmentUrls> = {\n local: {\n baseUrl: \"http://localhost:9101\",\n apiBaseUrl: \"http://localhost:9000\",\n },\n sandbox: {\n baseUrl: \"https://app.paymentkit.com/customer\",\n apiBaseUrl: \"https://app.paymentkit.com\",\n },\n staging: {\n baseUrl: \"https://staging.paymentkit.com/customer\",\n apiBaseUrl: \"https://staging.paymentkit.com\",\n },\n production: {\n baseUrl: \"https://app.paymentkit.com/customer\",\n apiBaseUrl: \"https://app.paymentkit.com\",\n },\n};\n\nfunction getTunnelUrls(): EnvironmentUrls {\n if (typeof window !== \"undefined\") {\n const params = new URLSearchParams(window.location.search);\n const apiBaseUrl = params.get(\"api_base_url\");\n if (apiBaseUrl) {\n return {\n baseUrl: window.location.origin,\n apiBaseUrl,\n };\n }\n // For loclx-style tunnels where API hostname is derived from the current hostname\n const hostname = window.location.hostname;\n if (hostname.includes(\"loclx.io\")) {\n const apiHostname = hostname.replace(/cportal|playground/, \"api\");\n return {\n baseUrl: `https://${hostname}`,\n apiBaseUrl: `https://${apiHostname}`,\n };\n }\n }\n // Fallback\n return STATIC_ENVIRONMENT_URLS.local;\n}\n\nexport function getUrlsForEnvironment(environment: PaymentKitEnvironment): EnvironmentUrls {\n if (environment === \"tunnel\") {\n return getTunnelUrls();\n }\n const urls = STATIC_ENVIRONMENT_URLS[environment];\n if (!urls) {\n console.warn(`[PaymentKit] Invalid environment \"${environment}\", falling back to local`);\n return STATIC_ENVIRONMENT_URLS.local;\n }\n return urls;\n}\n\ntype FormFieldNames =\n | \"customer_name\"\n | \"customer_email\"\n | \"customer_country\"\n | \"customer_zip_code\"\n | \"customer_business_name\"\n | \"customer_address_line1\"\n | \"customer_address_line2\"\n | \"customer_city\"\n | \"customer_state\"\n | \"customer_tax_ids\"\n | \"shipping_address_line1\"\n | \"shipping_address_line2\"\n | \"shipping_city\"\n | \"shipping_state\"\n | \"shipping_zip_code\"\n | \"shipping_country\";\n\nexport type FormErrorCodes = \"required\" | \"invalid\";\n\nexport type CardSubmitOptions = {\n skipCustomerValidation?: boolean;\n};\n\nexport type TInternalFuncs = {\n submitPayment: (\n fields: Partial<PaymentKitFields>,\n options?: unknown,\n ) => Promise<{ data: { [key: string]: unknown }; errors?: never } | { data?: never; errors: PaymentKitErrors }>;\n cleanup?: () => void;\n};\n\nexport type PaymentKit = <T extends readonly PaymentMethod<unknown>[]>(options: {\n environment?: PaymentKitEnvironment;\n secureToken: string;\n paymentMethods: T;\n /** Card tokenization mode from checkout session */\n cardTokenizationMode?: \"direct\" | \"vgs\";\n /** VGS vault ID from checkout session */\n vgsVaultId?: string;\n /** VGS environment from checkout session */\n vgsEnvironment?: string;\n /** @internal Enable analytics test mode for e2e testing */\n __enableAnalyticsTestMode?: boolean;\n}) => ExternalFuncsMapByPm<T> & {\n submit: PaymentKitSubmitHandler<T>;\n cleanup: () => void;\n};\n\ntype PaymentKitSubmitHandler<T extends readonly PaymentMethod<unknown>[]> = <\n N extends keyof ExternalFuncsMapByPm<T>,\n>(options: {\n fields: Partial<PaymentKitFields>;\n paymentMethod: N;\n options?: unknown;\n onError: (error: PaymentKitErrors) => void;\n onSuccess: (data: { [key: string]: unknown }) => void;\n}) => void;\n\nexport type PaymentKitStates = {\n baseUrl: string;\n apiBaseUrl: string;\n secureToken: string;\n environment: PaymentKitEnvironment;\n tunnelXConnection: TunnelXIFrameConnection;\n timingTracker: CheckoutTimingTracker;\n /** Checkout request ID for correlating all API calls and analytics events */\n checkoutRequestId: string;\n /** Card tokenization mode: 'direct' (KMS) or 'vgs' (VGS Collect + proxy) */\n cardTokenizationMode?: \"direct\" | \"vgs\";\n /** VGS vault ID (only when cardTokenizationMode is 'vgs') */\n vgsVaultId?: string;\n /** VGS environment: 'sandbox' or 'live' (only when cardTokenizationMode is 'vgs') */\n vgsEnvironment?: string;\n /** @internal Promise that resolves when session config (incl. VGS mode) is auto-detected */\n _sessionConfigReady?: Promise<void>;\n};\n\nexport type PaymentKitErrors = {\n root?: string;\n card_pan?: CardErrorCode;\n card_exp?: CardErrorCode;\n card_cvc?: CardErrorCode;\n paypal?: string;\n google_pay?: string;\n apple_pay?: string;\n processor_id?: string;\n amount?: string;\n currency?: string;\n country?: string;\n} & { [key in FormFieldNames]?: FormErrorCodes | string };\n\nexport type PaymentKitFields = { [key in FormFieldNames]: string };\n\nexport type PaymentMethod<TExternalFuncs = unknown, TName = string> = (paymentKitStates: PaymentKitStates) => {\n name: TName;\n externalFuncs: TExternalFuncs;\n internalFuncs: TInternalFuncs;\n};\n\nexport type ExternalFuncsMapByPm<T extends readonly PaymentMethod<unknown>[]> = {\n [K in T[number] as ReturnType<K>[\"name\"]]: ReturnType<K>[\"externalFuncs\"];\n};\n","import { version as PACKAGE_VERSION } from \"../package.json\";\nimport { AnalyticsService, CheckoutTimingTracker } from \"./analytics\";\nimport { connectToTunnelXIframe } from \"./penpal/connect-tunnel-x\";\nimport type { ExternalFuncsMapByPm, PaymentKit as PaymentKitType } from \"./types\";\nimport { getUrlsForEnvironment } from \"./types\";\nimport { createCheckoutIFrame } from \"./utils\";\nimport { getOrCreateCheckoutRequestId } from \"./utils/checkout-request-id\";\n\n/** Append script to DOM, falling back to head if body not ready */\nfunction appendScriptToDOM(script: HTMLScriptElement): void {\n const target = document.body ?? document.head;\n target.appendChild(script);\n}\n\n/**\n * Map fraud script URL to the script ID that the processor's script expects.\n * Airwallex's fingerprint script looks for `id=\"airwallex-fraud-api\"` via getElementById.\n * Other processors may have different ID requirements.\n */\nfunction getFraudScriptId(url: string): string {\n // Airwallex expects exactly \"airwallex-fraud-api\" as the script ID\n if (url.includes(\"airwallex.com\")) {\n return \"airwallex-fraud-api\";\n }\n // Fallback for other processors: use a hash-based ID\n const urlObj = new URL(url);\n const hash = btoa(urlObj.origin + urlObj.pathname)\n .replace(/[^a-zA-Z0-9]/g, \"\")\n .slice(0, 12);\n return `pk-fraud-script-${hash}`;\n}\n\ntype PaymentKitReturnType = ReturnType<PaymentKitType>;\n\nconst createTunnelXConnection = (baseUrl: string, apiBaseUrl: string, token: string) => {\n const iframe = createCheckoutIFrame(\"tunnel-x\", baseUrl, {\n checkout_token: token,\n api_base_url: apiBaseUrl,\n });\n // Tunnel-X is a hidden communication iframe, not a visible UI element\n Object.assign(iframe.style, { width: \"0\", height: \"0\", position: \"absolute\", visibility: \"hidden\" });\n document.body.appendChild(iframe);\n\n const connection = connectToTunnelXIframe(iframe, {});\n\n let destroyed = false;\n const unmount = () => {\n if (destroyed) return;\n destroyed = true;\n connection.destroy();\n iframe.remove();\n };\n\n return { unmount, connection };\n};\n\nconst PaymentKit: PaymentKitType = ({\n environment = \"production\",\n secureToken,\n paymentMethods,\n cardTokenizationMode,\n vgsVaultId,\n vgsEnvironment,\n __enableAnalyticsTestMode,\n}) => {\n type PaymentMethods = typeof paymentMethods;\n\n // Resolve URLs from environment\n const { baseUrl, apiBaseUrl } = getUrlsForEnvironment(environment);\n\n // Log version for debugging\n console.log(`[PaymentKit] v${PACKAGE_VERSION} initialized (env: ${environment})`);\n\n // Generate checkout request ID for correlating all API calls and analytics events\n // This is idempotent - same ID is reused across multiple PaymentKit instantiations\n const checkoutRequestId = getOrCreateCheckoutRequestId(environment);\n console.log(`[PaymentKit] checkout_request_id: ${checkoutRequestId}`);\n\n // Initialize analytics and identify with secure token for correlation\n AnalyticsService.init(apiBaseUrl, __enableAnalyticsTestMode);\n AnalyticsService.identify(secureToken);\n\n // Create timing tracker for this checkout session\n const timingTracker = new CheckoutTimingTracker(secureToken, checkoutRequestId);\n\n // Track page ready immediately\n timingTracker.trackPageReady();\n\n const { connection: tunnelXConnection, unmount: unmountTunnelX } = createTunnelXConnection(\n baseUrl,\n apiBaseUrl,\n secureToken,\n );\n\n const paymentKitStates = {\n baseUrl,\n apiBaseUrl,\n secureToken,\n environment,\n tunnelXConnection,\n timingTracker,\n checkoutRequestId,\n cardTokenizationMode,\n vgsVaultId,\n vgsEnvironment,\n _sessionConfigReady: undefined as Promise<void> | undefined,\n _fraudScriptIds: [] as string[],\n _fraudScriptsReady: undefined as Promise<void> | undefined,\n };\n\n // Auto-detect VGS mode from session API if not explicitly provided.\n // The promise resolves before any card mount or submit needs the mode.\n if (cardTokenizationMode === undefined) {\n const sessionConfigUrl = `${apiBaseUrl}/api/checkout-sessions/token/${secureToken}`;\n console.log(\n `[PaymentKit] cardTokenizationMode not provided — auto-detecting from session API: ${sessionConfigUrl}`,\n );\n paymentKitStates._sessionConfigReady = fetch(sessionConfigUrl)\n .then((resp) => {\n if (!resp.ok) {\n console.warn(`[PaymentKit] Session config fetch failed with status ${resp.status}`);\n return null;\n }\n return resp.json();\n })\n .then((session) => {\n if (session && typeof session === \"object\" && typeof session.card_tokenization_mode === \"string\") {\n paymentKitStates.cardTokenizationMode = session.card_tokenization_mode || \"direct\";\n console.log(\n `[PaymentKit] Session config detected — cardTokenizationMode: ${paymentKitStates.cardTokenizationMode}`,\n );\n\n // Validate VGS config from API before trusting it\n const vaultId = session.vgs_vault_id;\n const vgsEnv = session.vgs_environment;\n if (vaultId && /^tnt[a-z0-9]+$/.test(vaultId) && (vgsEnv === \"sandbox\" || vgsEnv === \"live\")) {\n paymentKitStates.vgsVaultId = vaultId;\n paymentKitStates.vgsEnvironment = vgsEnv;\n console.log(`[PaymentKit] VGS config valid — vaultId: ${vaultId}, environment: ${vgsEnv}`);\n } else if (paymentKitStates.cardTokenizationMode === \"vgs\") {\n console.warn(\n `[PaymentKit] Invalid VGS config from session API (vaultId: ${vaultId}, env: ${vgsEnv}), falling back to direct mode`,\n );\n paymentKitStates.cardTokenizationMode = \"direct\";\n }\n\n // Inject fraud fingerprinting scripts (for processors that support device fingerprinting)\n if (session?.fraud_script_urls?.length) {\n const loadPromises: Promise<void>[] = [];\n\n session.fraud_script_urls.forEach((url: string) => {\n const scriptId = getFraudScriptId(url);\n\n if (document.getElementById(scriptId)) {\n console.log(`[PaymentKit] Fraud script already present: ${scriptId}`);\n loadPromises.push(Promise.resolve());\n return;\n }\n\n const script = document.createElement(\"script\");\n script.type = \"text/javascript\";\n script.async = true;\n script.id = scriptId;\n script.src = url;\n script.dataset.orderSessionId = checkoutRequestId;\n\n const loadPromise = new Promise<void>((resolve) => {\n script.onload = () => {\n console.log(`[PaymentKit] Fraud script loaded: ${scriptId}`);\n resolve();\n };\n script.onerror = () => {\n console.error(\n `[PaymentKit] Failed to load fraud prevention script. If using Content-Security-Policy, add to script-src: ${new URL(url).origin}`,\n );\n resolve(); // Resolve anyway — don't block payment on optional fraud script\n };\n });\n\n loadPromises.push(loadPromise);\n paymentKitStates._fraudScriptIds.push(scriptId);\n appendScriptToDOM(script);\n });\n\n paymentKitStates._fraudScriptsReady = Promise.all(loadPromises).then(() => {\n console.log(`[PaymentKit] All ${loadPromises.length} fraud fingerprinting script(s) ready`);\n });\n\n // Log each injected script with its data-order-session-id for debugging\n session.fraud_script_urls.forEach((url: string) => {\n const scriptId = getFraudScriptId(url);\n console.log(\n `[PaymentKit] Fraud script injected: id=${scriptId}, data-order-session-id=${checkoutRequestId}, src=${url}`,\n );\n });\n console.log(`[PaymentKit] Injected ${session.fraud_script_urls.length} fraud fingerprinting script(s)`);\n }\n } else {\n console.log(\"[PaymentKit] Session response missing card_tokenization_mode — defaulting to direct mode\");\n paymentKitStates.cardTokenizationMode = \"direct\";\n }\n })\n .catch((err) => {\n console.error(\"[PaymentKit] Session config fetch error — falling back to direct mode:\", err);\n paymentKitStates.cardTokenizationMode = \"direct\";\n });\n } else {\n console.log(`[PaymentKit] cardTokenizationMode explicitly set: ${cardTokenizationMode}`);\n }\n\n const pmInstances = paymentMethods.map((paymentMethod) => paymentMethod(paymentKitStates));\n\n const externalFuncsMapByPm: ExternalFuncsMapByPm<PaymentMethods> = pmInstances.reduce(\n (acc, { name, externalFuncs }) => {\n // @ts-expect-error - typecase this better in future\n acc[name] = externalFuncs;\n return acc;\n },\n {} as ExternalFuncsMapByPm<PaymentMethods>,\n );\n\n const submit: PaymentKitReturnType[\"submit\"] = ({\n paymentMethod: paymentMethodName,\n fields,\n options,\n onSuccess,\n onError,\n }) => {\n const paymentMethod = pmInstances.find(({ name }) => name === paymentMethodName);\n if (!paymentMethod) {\n onError({ root: \"payment_method_not_found\" });\n return;\n }\n paymentMethod.internalFuncs\n .submitPayment(fields, options)\n .then(({ data, errors }) => {\n errors ? onError(errors) : onSuccess(data);\n })\n .catch((e) => {\n console.error(\"PaymentKit:submit:catch\", e);\n\n // Try to extract error message from response\n if (e?.response?.data) {\n onError(e.response.data);\n } else if (e?.message) {\n onError({ root: e.message });\n } else {\n onError({ root: \"unknown_error\" });\n }\n });\n };\n\n const cleanup = () => {\n // Clean up fraud fingerprinting scripts\n for (const scriptId of paymentKitStates._fraudScriptIds) {\n const script = document.getElementById(scriptId);\n if (script) {\n script.remove();\n }\n }\n paymentKitStates._fraudScriptIds = [];\n\n // Clean up all payment method instances\n for (const pm of pmInstances) {\n if (pm.internalFuncs.cleanup) {\n pm.internalFuncs.cleanup();\n }\n }\n unmountTunnelX();\n };\n\n return {\n submit,\n cleanup,\n ...externalFuncsMapByPm,\n };\n};\n\nexport type { PaymentKitEnvironment, PaymentKitFields } from \"./types\";\n\nexport default PaymentKit;\n"],"mappings":";;;;;;cAEa;;;;ACUb,MAAMA,0BAA6F;CACjG,OAAO;EACL,SAAS;EACT,YAAY;EACb;CACD,SAAS;EACP,SAAS;EACT,YAAY;EACb;CACD,SAAS;EACP,SAAS;EACT,YAAY;EACb;CACD,YAAY;EACV,SAAS;EACT,YAAY;EACb;CACF;AAED,SAAS,gBAAiC;AACxC,KAAI,OAAO,WAAW,aAAa;EAEjC,MAAM,aADS,IAAI,gBAAgB,OAAO,SAAS,OAAO,CAChC,IAAI,eAAe;AAC7C,MAAI,WACF,QAAO;GACL,SAAS,OAAO,SAAS;GACzB;GACD;EAGH,MAAM,WAAW,OAAO,SAAS;AACjC,MAAI,SAAS,SAAS,WAAW,EAAE;GACjC,MAAM,cAAc,SAAS,QAAQ,sBAAsB,MAAM;AACjE,UAAO;IACL,SAAS,WAAW;IACpB,YAAY,WAAW;IACxB;;;AAIL,QAAO,wBAAwB;;AAGjC,SAAgB,sBAAsB,aAAqD;AACzF,KAAI,gBAAgB,SAClB,QAAO,eAAe;CAExB,MAAM,OAAO,wBAAwB;AACrC,KAAI,CAAC,MAAM;AACT,UAAQ,KAAK,qCAAqC,YAAY,0BAA0B;AACxF,SAAO,wBAAwB;;AAEjC,QAAO;;;;;;ACvDT,SAAS,kBAAkB,QAAiC;AAE1D,EADe,SAAS,QAAQ,SAAS,MAClC,YAAY,OAAO;;;;;;;AAQ5B,SAAS,iBAAiB,KAAqB;AAE7C,KAAI,IAAI,SAAS,gBAAgB,CAC/B,QAAO;CAGT,MAAM,SAAS,IAAI,IAAI,IAAI;AAI3B,QAAO,mBAHM,KAAK,OAAO,SAAS,OAAO,SAAS,CAC/C,QAAQ,iBAAiB,GAAG,CAC5B,MAAM,GAAG,GAAG;;AAMjB,MAAM,2BAA2B,SAAiB,YAAoB,UAAkB;CACtF,MAAM,SAAS,qBAAqB,YAAY,SAAS;EACvD,gBAAgB;EAChB,cAAc;EACf,CAAC;AAEF,QAAO,OAAO,OAAO,OAAO;EAAE,OAAO;EAAK,QAAQ;EAAK,UAAU;EAAY,YAAY;EAAU,CAAC;AACpG,UAAS,KAAK,YAAY,OAAO;CAEjC,MAAM,aAAa,uBAAuB,QAAQ,EAAE,CAAC;CAErD,IAAI,YAAY;CAChB,MAAM,gBAAgB;AACpB,MAAI,UAAW;AACf,cAAY;AACZ,aAAW,SAAS;AACpB,SAAO,QAAQ;;AAGjB,QAAO;EAAE;EAAS;EAAY;;AAGhC,MAAMC,cAA8B,EAClC,cAAc,cACd,aACA,gBACA,sBACA,YACA,gBACA,gCACI;CAIJ,MAAM,EAAE,SAAS,eAAe,sBAAsB,YAAY;AAGlE,SAAQ,IAAI,iBAAiBC,QAAgB,qBAAqB,YAAY,GAAG;CAIjF,MAAM,oBAAoB,6BAA6B,YAAY;AACnE,SAAQ,IAAI,qCAAqC,oBAAoB;AAGrE,kBAAiB,KAAK,YAAY,0BAA0B;AAC5D,kBAAiB,SAAS,YAAY;CAGtC,MAAM,gBAAgB,IAAI,sBAAsB,aAAa,kBAAkB;AAG/E,eAAc,gBAAgB;CAE9B,MAAM,EAAE,YAAY,mBAAmB,SAAS,mBAAmB,wBACjE,SACA,YACA,YACD;CAED,MAAM,mBAAmB;EACvB;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA,qBAAqB;EACrB,iBAAiB,EAAE;EACnB,oBAAoB;EACrB;AAID,KAAI,yBAAyB,QAAW;EACtC,MAAM,mBAAmB,GAAG,WAAW,+BAA+B;AACtE,UAAQ,IACN,qFAAqF,mBACtF;AACD,mBAAiB,sBAAsB,MAAM,iBAAiB,CAC3D,MAAM,SAAS;AACd,OAAI,CAAC,KAAK,IAAI;AACZ,YAAQ,KAAK,wDAAwD,KAAK,SAAS;AACnF,WAAO;;AAET,UAAO,KAAK,MAAM;IAClB,CACD,MAAM,YAAY;AACjB,OAAI,WAAW,OAAO,YAAY,YAAY,OAAO,QAAQ,2BAA2B,UAAU;AAChG,qBAAiB,uBAAuB,QAAQ,0BAA0B;AAC1E,YAAQ,IACN,gEAAgE,iBAAiB,uBAClF;IAGD,MAAM,UAAU,QAAQ;IACxB,MAAM,SAAS,QAAQ;AACvB,QAAI,WAAW,iBAAiB,KAAK,QAAQ,KAAK,WAAW,aAAa,WAAW,SAAS;AAC5F,sBAAiB,aAAa;AAC9B,sBAAiB,iBAAiB;AAClC,aAAQ,IAAI,4CAA4C,QAAQ,iBAAiB,SAAS;eACjF,iBAAiB,yBAAyB,OAAO;AAC1D,aAAQ,KACN,8DAA8D,QAAQ,SAAS,OAAO,gCACvF;AACD,sBAAiB,uBAAuB;;AAI1C,QAAI,SAAS,mBAAmB,QAAQ;KACtC,MAAMC,eAAgC,EAAE;AAExC,aAAQ,kBAAkB,SAAS,QAAgB;MACjD,MAAM,WAAW,iBAAiB,IAAI;AAEtC,UAAI,SAAS,eAAe,SAAS,EAAE;AACrC,eAAQ,IAAI,8CAA8C,WAAW;AACrE,oBAAa,KAAK,QAAQ,SAAS,CAAC;AACpC;;MAGF,MAAM,SAAS,SAAS,cAAc,SAAS;AAC/C,aAAO,OAAO;AACd,aAAO,QAAQ;AACf,aAAO,KAAK;AACZ,aAAO,MAAM;AACb,aAAO,QAAQ,iBAAiB;MAEhC,MAAM,cAAc,IAAI,SAAe,YAAY;AACjD,cAAO,eAAe;AACpB,gBAAQ,IAAI,qCAAqC,WAAW;AAC5D,iBAAS;;AAEX,cAAO,gBAAgB;AACrB,gBAAQ,MACN,6GAA6G,IAAI,IAAI,IAAI,CAAC,SAC3H;AACD,iBAAS;;QAEX;AAEF,mBAAa,KAAK,YAAY;AAC9B,uBAAiB,gBAAgB,KAAK,SAAS;AAC/C,wBAAkB,OAAO;OACzB;AAEF,sBAAiB,qBAAqB,QAAQ,IAAI,aAAa,CAAC,WAAW;AACzE,cAAQ,IAAI,oBAAoB,aAAa,OAAO,uCAAuC;OAC3F;AAGF,aAAQ,kBAAkB,SAAS,QAAgB;MACjD,MAAM,WAAW,iBAAiB,IAAI;AACtC,cAAQ,IACN,0CAA0C,SAAS,0BAA0B,kBAAkB,QAAQ,MACxG;OACD;AACF,aAAQ,IAAI,yBAAyB,QAAQ,kBAAkB,OAAO,iCAAiC;;UAEpG;AACL,YAAQ,IAAI,2FAA2F;AACvG,qBAAiB,uBAAuB;;IAE1C,CACD,OAAO,QAAQ;AACd,WAAQ,MAAM,0EAA0E,IAAI;AAC5F,oBAAiB,uBAAuB;IACxC;OAEJ,SAAQ,IAAI,qDAAqD,uBAAuB;CAG1F,MAAM,cAAc,eAAe,KAAK,kBAAkB,cAAc,iBAAiB,CAAC;CAE1F,MAAMC,uBAA6D,YAAY,QAC5E,KAAK,EAAE,MAAM,oBAAoB;AAEhC,MAAI,QAAQ;AACZ,SAAO;IAET,EAAE,CACH;CAED,MAAMC,UAA0C,EAC9C,eAAe,mBACf,QACA,SACA,WACA,cACI;EACJ,MAAM,gBAAgB,YAAY,MAAM,EAAE,WAAW,SAAS,kBAAkB;AAChF,MAAI,CAAC,eAAe;AAClB,WAAQ,EAAE,MAAM,4BAA4B,CAAC;AAC7C;;AAEF,gBAAc,cACX,cAAc,QAAQ,QAAQ,CAC9B,MAAM,EAAE,MAAM,aAAa;AAC1B,YAAS,QAAQ,OAAO,GAAG,UAAU,KAAK;IAC1C,CACD,OAAO,MAAM;AACZ,WAAQ,MAAM,2BAA2B,EAAE;AAG3C,OAAI,GAAG,UAAU,KACf,SAAQ,EAAE,SAAS,KAAK;YACf,GAAG,QACZ,SAAQ,EAAE,MAAM,EAAE,SAAS,CAAC;OAE5B,SAAQ,EAAE,MAAM,iBAAiB,CAAC;IAEpC;;CAGN,MAAM,gBAAgB;AAEpB,OAAK,MAAM,YAAY,iBAAiB,iBAAiB;GACvD,MAAM,SAAS,SAAS,eAAe,SAAS;AAChD,OAAI,OACF,QAAO,QAAQ;;AAGnB,mBAAiB,kBAAkB,EAAE;AAGrC,OAAK,MAAM,MAAM,YACf,KAAI,GAAG,cAAc,QACnB,IAAG,cAAc,SAAS;AAG9B,kBAAgB;;AAGlB,QAAO;EACL;EACA;EACA,GAAG;EACJ;;AAKH,kBAAe"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"card.d.mts","names":[],"sources":["../../src/payment-methods/card.ts"],"sourcesContent":[],"mappings":";;;;;KAQY,aAAA,GAAgB;KAChB,gBAAA,GAAmB;KAsG1B,oBAAA,GAAuB,QAAQ,kBAAkB;UAC5C;EAxGE,WAAA,CAAA,EAAA,MAAa;AACzB,CAAA;AAAsD,
|
|
1
|
+
{"version":3,"file":"card.d.mts","names":[],"sources":["../../src/payment-methods/card.ts"],"sourcesContent":[],"mappings":";;;;;KAQY,aAAA,GAAgB;KAChB,gBAAA,GAAmB;KAsG1B,oBAAA,GAAuB,QAAQ,kBAAkB;UAC5C;EAxGE,WAAA,CAAA,EAAA,MAAa;AACzB,CAAA;AAAsD,cA22BhD,iBArwBmB,EAQyC,aARzC,CAAA;EAA6B,aAAA,EAAA,CAAA,IAAA,EAQtC,eARsC,EAAA,OAAA,EAQR,oBARQ,EAAA,GAAA;IAAlB,KAAA,EAAA,CAAA,cAAA,EAAA,MAAA,EAAA,GAAA;MAAR,OAAA,EAAA,GAAA,GAAA,IAAA;IAClB,CAAA;EAAM,CAAA;AAAA,CAAA,EAAA,MAowBV,CAAA"}
|
|
@@ -188,6 +188,19 @@ const defCreateElement = (states) => {
|
|
|
188
188
|
return { mount: mountIFrame };
|
|
189
189
|
};
|
|
190
190
|
};
|
|
191
|
+
/**
|
|
192
|
+
* Terminal failure states for a card checkout. Mirrors the backend checkout-attempt state machine
|
|
193
|
+
* (app/modules/checkout/checkout_attempt/checkout_attempt_state_machine.py — the only terminal
|
|
194
|
+
* success state is `checkout_succeeded`). A checkout in one of these states must be surfaced to the
|
|
195
|
+
* merchant as an error so `onError` — not `onSuccess` — fires.
|
|
196
|
+
*
|
|
197
|
+
* Note: 3DS authentication *passing* does NOT imply the charge succeeded — a pass-then-decline
|
|
198
|
+
* concludes with `payment_failed` and no further `nextAction`. Other non-terminal states (e.g.
|
|
199
|
+
* `payment_started` from an async "pending" charge that carries no `nextAction`) are intentionally
|
|
200
|
+
* left to the success/data path here, preserving existing behavior for the non-3DS flow.
|
|
201
|
+
*/
|
|
202
|
+
const CHECKOUT_FAILURE_STATES = new Set(["payment_failed", "checkout_failed"]);
|
|
203
|
+
const isCheckoutFailure = (result) => CHECKOUT_FAILURE_STATES.has(result.state);
|
|
191
204
|
const defSubmitPayment = (states) => {
|
|
192
205
|
const submitPayment = async (fields, options) => {
|
|
193
206
|
if (states._sessionConfigReady) await states._sessionConfigReady;
|
|
@@ -278,6 +291,14 @@ const defSubmitPayment = (states) => {
|
|
|
278
291
|
checkout_response: verifyResult
|
|
279
292
|
} };
|
|
280
293
|
}
|
|
294
|
+
if (isCheckoutFailure(verifyResult)) {
|
|
295
|
+
console.log("Card checkout concluded in a failure state after 3DS:", verifyResult);
|
|
296
|
+
timingTracker.trackFail(verifyResult.checkoutAttemptId || null, verifyResult.errorCode || "payment_failed", verifyResult.errorMessageForCustomer || "Payment failed");
|
|
297
|
+
return { errors: {
|
|
298
|
+
root: verifyResult.errorMessageForCustomer || "Payment failed",
|
|
299
|
+
checkout_response: verifyResult
|
|
300
|
+
} };
|
|
301
|
+
}
|
|
281
302
|
console.log("Card checkout verified ✅", verifyResult);
|
|
282
303
|
timingTracker.trackSuccess(verifyResult.checkoutAttemptId || "unknown");
|
|
283
304
|
return { data: verifyResult };
|
|
@@ -287,7 +308,7 @@ const defSubmitPayment = (states) => {
|
|
|
287
308
|
timingTracker.trackFail(null, "max_actions_exceeded", "Too many authentication attempts");
|
|
288
309
|
return { errors: { root: "Too many authentication attempts. Please try again." } };
|
|
289
310
|
}
|
|
290
|
-
if (currentResult
|
|
311
|
+
if (isCheckoutFailure(currentResult)) {
|
|
291
312
|
console.log("Card checkout failed:", currentResult);
|
|
292
313
|
timingTracker.trackFail(currentResult.checkoutAttemptId || null, currentResult.errorCode || "payment_failed", currentResult.errorMessageForCustomer || "Payment failed");
|
|
293
314
|
return { errors: {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"card.mjs","names":["taxIds: string[] | undefined","cleanupVgs: (() => void) | undefined","cleanupDirect: (() => void) | undefined","cdnTimeoutId: ReturnType<typeof setTimeout>","params: Record<string, string>","submitPayment: TInternalFuncs[\"submitPayment\"]","VGS_FIELD_MAP: Record<string, keyof PaymentKitErrors>","accessToken: string","cardResult: VGSCreateCardResponse","createCardTimeoutId: ReturnType<typeof setTimeout>","errors: PaymentKitErrors"],"sources":["../../src/payment-methods/card.ts"],"sourcesContent":["import {\n connectToCardIframe,\n type IFrameConnection,\n type CardInputType as PenpalCardInputType,\n type CheckoutResponse as PenpalCheckoutResponse,\n} from \"../penpal/connect-card\";\n\n// Re-export types for public API\nexport type CardInputType = PenpalCardInputType;\nexport type CheckoutResponse = PenpalCheckoutResponse;\n\nimport { AnalyticsService } from \"../analytics\";\nimport { TunnelXManager } from \"../penpal/connect-tunnel-x\";\nimport type { PaymentKitErrors, PaymentKitFields, PaymentKitStates, TInternalFuncs } from \"../types\";\nimport {\n $,\n collectFraudMetadata,\n createCheckoutIFrame,\n definePaymentMethod,\n validateFormFields,\n withRequestId,\n} from \"../utils\";\nimport { handleNextAction } from \"./next-action-handlers\";\nimport { createVgsCardFields, initVgsCollect, loadVgsCollectScript } from \"./vgs-collect-loader\";\n\n/**\n * Splits a full name into first name and last name.\n * First word becomes firstName, rest becomes lastName.\n */\nconst splitName = (fullName: string): { firstName: string; lastName?: string } => {\n const trimmed = fullName.trim();\n const spaceIndex = trimmed.indexOf(\" \");\n if (spaceIndex === -1) {\n return { firstName: trimmed };\n }\n return {\n firstName: trimmed.substring(0, spaceIndex),\n lastName: trimmed.substring(spaceIndex + 1).trim() || undefined,\n };\n};\n\n/**\n * Maps PaymentKitFields to the customerInfo structure expected by the API.\n */\nconst mapFieldsToCustomerInfo = (fields: PaymentKitFields) => {\n const { firstName, lastName } = splitName(fields.customer_name);\n\n const hasBillingAddress =\n fields.customer_country ||\n fields.customer_zip_code ||\n fields.customer_address_line1 ||\n fields.customer_address_line2 ||\n fields.customer_city ||\n fields.customer_state;\n\n const hasShippingAddress =\n fields.shipping_address_line1 ||\n fields.shipping_address_line2 ||\n fields.shipping_city ||\n fields.shipping_state ||\n fields.shipping_zip_code ||\n fields.shipping_country;\n\n let taxIds: string[] | undefined;\n if (fields.customer_tax_ids) {\n try {\n taxIds = JSON.parse(fields.customer_tax_ids);\n } catch {\n taxIds = undefined;\n }\n }\n\n return {\n email: fields.customer_email || undefined,\n firstName,\n lastName,\n businessName: fields.customer_business_name || undefined,\n taxIds: taxIds?.length ? taxIds : undefined,\n billingAddress: hasBillingAddress\n ? {\n country: fields.customer_country || undefined,\n zipCode: fields.customer_zip_code || undefined,\n line1: fields.customer_address_line1 || undefined,\n line2: fields.customer_address_line2 || undefined,\n city: fields.customer_city || undefined,\n state: fields.customer_state || undefined,\n }\n : undefined,\n shippingAddress: hasShippingAddress\n ? {\n line1: fields.shipping_address_line1 || undefined,\n line2: fields.shipping_address_line2 || undefined,\n city: fields.shipping_city || undefined,\n state: fields.shipping_state || undefined,\n zipCode: fields.shipping_zip_code || undefined,\n country: fields.shipping_country || undefined,\n }\n : undefined,\n };\n};\n\ntype CardStates = PaymentKitStates & {\n cardSetupIntentId?: string;\n cardInputConnections: Partial<Record<PenpalCardInputType, IFrameConnection>>;\n vgsForm?: VGSCollectForm;\n vgsFieldsCleanup?: () => void;\n vgsFocusCallbacks?: Partial<Record<PenpalCardInputType, (isFocused: boolean) => void>>;\n vgsMountSelectors?: Partial<Record<PenpalCardInputType, string>>;\n vgsPlaceholders?: Partial<Record<PenpalCardInputType, string>>;\n};\n\ntype CreateElementOptions = Partial<Parameters<typeof connectToCardIframe>[1]> & {\n style?: Record<string, string>;\n placeholder?: string;\n};\n\nconst defCreateElement = (states: CardStates) => {\n const { baseUrl, apiBaseUrl, cardInputConnections, secureToken, timingTracker } = states;\n\n return (type: PenpalCardInputType, options: CreateElementOptions) => {\n const { style, onLoaded, onFocusChange, placeholder } = options;\n\n const mountIFrame = (parentSelector: string) => {\n // Mutable cleanup callbacks — set once mode is determined and mount completes\n let cleanupVgs: (() => void) | undefined;\n let cleanupDirect: (() => void) | undefined;\n // Cancellation flag: prevents deferred doMount from executing after unmount.\n // Needed because React strict mode may call unmount before the deferred\n // _sessionConfigReady promise resolves.\n let cancelled = false;\n\n /**\n * Performs the actual DOM mount after the tokenization mode is known.\n * Runs synchronously when mode is explicitly provided, or after the\n * auto-detect fetch resolves.\n */\n // Store mount selector so VGS init (triggered by card_pan) can read\n // the exp/cvc selectors that were registered by their own createElement calls.\n if (!states.vgsMountSelectors) states.vgsMountSelectors = {};\n states.vgsMountSelectors[type] = parentSelector;\n\n const doMount = () => {\n if (cancelled) return;\n const isVgsMode = states.cardTokenizationMode === \"vgs\";\n console.log(\n `[PaymentKit] card.doMount(${type}) — mode: ${states.cardTokenizationMode}, vgsVaultId: ${states.vgsVaultId ?? \"unset\"}, vgsEnvironment: ${states.vgsEnvironment ?? \"unset\"}`,\n );\n\n // VGS mode: initialize VGS Collect form and mount fields on card_pan\n if (isVgsMode && states.vgsVaultId && states.vgsEnvironment) {\n // Store per-field focus callbacks and placeholders so createVgsCardFields\n // can wire each field to its own callback/placeholder. All three createElement\n // calls complete before the CDN script loads, so all values are available by then.\n if (onFocusChange) {\n if (!states.vgsFocusCallbacks) states.vgsFocusCallbacks = {};\n states.vgsFocusCallbacks[type] = onFocusChange;\n }\n if (placeholder) {\n if (!states.vgsPlaceholders) states.vgsPlaceholders = {};\n states.vgsPlaceholders[type] = placeholder;\n }\n\n if (type === \"card_pan\") {\n const vaultId = states.vgsVaultId;\n const vgsEnv = states.vgsEnvironment;\n\n console.log(`[PaymentKit] VGS mode active — loading VGS Collect CDN for vault ${vaultId} (${vgsEnv})...`);\n const VGS_CDN_TIMEOUT_MS = 15_000;\n let cdnTimeoutId: ReturnType<typeof setTimeout>;\n Promise.race([\n loadVgsCollectScript(),\n new Promise<never>((_, reject) => {\n cdnTimeoutId = setTimeout(() => {\n console.warn(`[PaymentKit] VGS Collect CDN load timed out after ${VGS_CDN_TIMEOUT_MS / 1000}s`);\n reject(new Error(\"VGS Collect CDN load timed out\"));\n }, VGS_CDN_TIMEOUT_MS);\n }),\n ])\n .finally(() => clearTimeout(cdnTimeoutId))\n .then(() => {\n if (cancelled) return;\n console.log(\"[PaymentKit] VGS Collect CDN loaded — initializing form...\");\n const form = initVgsCollect(vaultId, vgsEnv);\n if (cancelled) {\n form.unmount();\n return;\n }\n states.vgsForm = form;\n\n const css = style\n ? Object.fromEntries(\n Object.entries(style).map(([k, v]) => [k.replace(/([A-Z])/g, \"-$1\").toLowerCase(), v]),\n )\n : undefined;\n\n // Read exp/cvc selectors from shared state.\n // Each createElement(\"card_exp/card_cvc\").mount(selector) stores its\n // selector before this async callback runs, so they're available here.\n const expSelector = states.vgsMountSelectors?.card_exp;\n const cvcSelector = states.vgsMountSelectors?.card_cvc;\n if (!expSelector || !cvcSelector) {\n const msg =\n `[PaymentKit] VGS mode requires all three card fields to be mounted. ` +\n `Missing: ${!expSelector ? \"card_exp\" : \"\"}${!expSelector && !cvcSelector ? \", \" : \"\"}${!cvcSelector ? \"card_cvc\" : \"\"}`;\n console.error(msg);\n onLoaded?.();\n return;\n }\n console.log(\n `[PaymentKit] Creating VGS card fields — pan: ${parentSelector}, exp: ${expSelector}, cvc: ${cvcSelector}`,\n );\n states.vgsFieldsCleanup = createVgsCardFields(\n form,\n {\n pan: parentSelector,\n exp: expSelector,\n cvc: cvcSelector,\n },\n css,\n states.vgsFocusCallbacks,\n states.vgsPlaceholders,\n );\n\n console.log(\"[PaymentKit] VGS card fields created successfully\");\n timingTracker.trackInputReady();\n onLoaded?.();\n })\n .catch((err) => {\n console.error(\"[PaymentKit] Failed to load VGS Collect script. Card input will not be available:\", err);\n // Signal loaded so the form exits loading state rather than hanging forever.\n // Card fields won't render, but submit will fail with a clear error.\n onLoaded?.();\n });\n }\n\n // For non-pan fields in VGS mode, signal loaded immediately since\n // VGS handles all three fields from the card_pan mount.\n if (type !== \"card_pan\") {\n onLoaded?.();\n }\n\n cleanupVgs = () => {\n if (type === \"card_pan\" && states.vgsForm) {\n try {\n states.vgsFieldsCleanup?.();\n } catch {\n // VGS may try to remove iframes from containers already removed by React\n }\n states.vgsFieldsCleanup = undefined;\n try {\n states.vgsForm.unmount();\n } catch {\n // VGS unmount may fail if DOM nodes are already detached\n }\n states.vgsForm = undefined;\n }\n };\n return;\n }\n\n // If we got here and mode was \"vgs\", it means VGS config was incomplete\n if (isVgsMode) {\n console.warn(\n `[PaymentKit] VGS mode requested but config incomplete (vgsVaultId: ${states.vgsVaultId ?? \"unset\"}, vgsEnvironment: ${states.vgsEnvironment ?? \"unset\"}) — using direct mode`,\n );\n }\n\n // Direct mode: existing penpal iframe logic\n const parent = $(parentSelector) as HTMLElement;\n\n // Read container padding so the embed input matches the merchant's\n // visual intent. The iframe uses absolute positioning to fill the\n // full container, so the input needs its own padding for alignment.\n const computed = getComputedStyle(parent);\n const containerPadding = computed.padding !== \"0px\" ? computed.padding : undefined;\n const enhancedStyle = {\n ...(style ?? {}),\n height: \"100%\",\n boxSizing: \"border-box\",\n ...(containerPadding ? { padding: containerPadding } : {}),\n };\n\n const params: Record<string, string> = {\n checkout_token: secureToken,\n api_base_url: apiBaseUrl,\n style: JSON.stringify(enhancedStyle),\n };\n\n const iframe = createCheckoutIFrame(type.replace(\"_\", \"-\"), baseUrl, params);\n\n // Use absolute positioning so the iframe covers the full container\n // including any padding the merchant's CSS applies.\n if (computed.position === \"static\") {\n parent.style.position = \"relative\";\n }\n iframe.style.position = \"absolute\";\n iframe.style.inset = \"0\";\n\n // Set cleanup before DOM mutation to avoid leaking the iframe if\n // unmount() is called between appendChild and onload.\n cleanupDirect = () => {\n const connection = cardInputConnections[type];\n connection?.destroy();\n iframe.remove();\n states.cardInputConnections[type] = undefined;\n };\n\n parent.appendChild(iframe);\n\n iframe.onload = () => {\n if (type === \"card_pan\") {\n timingTracker.trackInputReady();\n }\n\n const connection = connectToCardIframe(iframe, {\n onLoaded: onLoaded || (() => {}),\n onFocusChange: onFocusChange || (() => {}),\n });\n\n states.cardInputConnections[type] = connection;\n };\n };\n\n // If session config needs auto-detection, wait for it; otherwise mount synchronously\n if (states._sessionConfigReady) {\n states._sessionConfigReady.then(doMount);\n } else {\n doMount();\n }\n\n return {\n unmount: () => {\n cancelled = true;\n cleanupVgs?.();\n cleanupDirect?.();\n },\n };\n };\n\n return { mount: mountIFrame };\n };\n};\n\nconst defSubmitPayment = (states: CardStates) => {\n const submitPayment: TInternalFuncs[\"submitPayment\"] = async (fields, options) => {\n // Ensure session config is loaded (no-op if explicitly provided)\n if (states._sessionConfigReady) await states._sessionConfigReady;\n const isVgsMode = states.cardTokenizationMode === \"vgs\";\n\n const { timingTracker, checkoutRequestId } = states;\n\n // Track submit event\n timingTracker.trackSubmit();\n\n // Use the checkout request ID from states (generated on page load)\n const requestOptions = withRequestId(checkoutRequestId);\n\n const tunnelX = await TunnelXManager.createFromPenpalConnection(states.tunnelXConnection);\n\n // Step 1. Validate form values.\n // Form field validation is shared across both modes.\n // In VGS mode, card field validation is handled by VGS Collect (built-in validators).\n // In direct mode, card field validation is handled by penpal iframes.\n const skipCustomerValidation = options?.skipCustomerValidation === true;\n const validateFormResult = skipCustomerValidation\n ? { isSuccess: true as const, errors: {} as PaymentKitErrors }\n : await validateFormFields(fields);\n if (!isVgsMode) {\n const validateCardResult = await validateCardFields(states);\n if (!(validateCardResult.isSuccess && validateFormResult.isSuccess)) {\n timingTracker.trackFail(null, \"validation_error\", \"Form validation failed\");\n return { errors: { ...validateCardResult.errors, ...validateFormResult.errors } };\n }\n } else if (!validateFormResult.isSuccess) {\n timingTracker.trackFail(null, \"validation_error\", \"Form validation failed\");\n return { errors: validateFormResult.errors };\n }\n\n // Step 2. Create card setup intent if not present\n if (!states.cardSetupIntentId) {\n const res = await tunnelX.publicEndpoints.createCardSetupIntent({\n checkoutToken: states.secureToken,\n });\n states.cardSetupIntentId = res.cardSetupIntentId;\n }\n\n // Step 3. Submit card values.\n if (isVgsMode) {\n // VGS mode: create card via CMP, then send aliases to backend\n console.log(\"[PaymentKit] Submitting card via VGS CMP...\");\n const vgsSubmitResult = await submitVgsCardFields(states, tunnelX);\n if (!vgsSubmitResult.isSuccess) {\n // Include field-level error details for PostHog visibility\n const errorDetail = Object.entries(vgsSubmitResult.errors)\n .map(([k, v]) => `${k}: ${v}`)\n .join(\", \");\n timingTracker.trackFail(null, \"card_submit_error\", errorDetail || \"VGS card submission failed\");\n return { errors: vgsSubmitResult.errors };\n }\n } else {\n // Direct mode: submit via penpal iframes\n const submitCardResult = await submitCardFields(states);\n if (!submitCardResult.isSuccess) {\n timingTracker.trackFail(null, \"card_submit_error\", \"Card submission failed\");\n return { errors: submitCardResult.errors };\n }\n }\n\n // Step 4. Get card setup intent.\n const cardSetupIntent = await tunnelX.publicEndpoints.getCardSetupIntent({\n cardSetupIntentId: states.cardSetupIntentId,\n checkoutToken: states.secureToken,\n });\n if (!cardSetupIntent.isCardAllSet) {\n const errors = {} as PaymentKitErrors;\n if (!cardSetupIntent.isCardPanSet) errors.card_pan = \"required\";\n if (!cardSetupIntent.isCardExpSet) errors.card_exp = \"required\";\n if (!cardSetupIntent.isCardCvcSet) errors.card_cvc = \"required\";\n timingTracker.trackFail(null, \"card_setup_incomplete\", \"Card details incomplete\");\n return { errors };\n }\n\n console.log(\"Card setup intent is set ✅\", cardSetupIntent);\n console.log(\"Fields\", fields);\n\n // Step 5. Submit card checkout with customer info and fraud metadata\n // Include processor_fraud_info with airwallex_device_id for fraud fingerprinting\n const fraudMetadata = {\n ...collectFraudMetadata(),\n processorFraudInfo: {\n airwallexDeviceId: states.checkoutRequestId,\n },\n };\n let currentResult = await tunnelX.publicEndpoints.cardCheckout(\n {\n checkoutToken: states.secureToken,\n publicCardCheckoutRequest: {\n cardSetupIntentId: states.cardSetupIntentId,\n customerInfo: mapFieldsToCustomerInfo(fields),\n fraudMetadata,\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 * Uses AbortController with 10s timeout to prevent indefinite hangs on slow networks.\n */\nconst FETCH_COLLECT_TOKEN_TIMEOUT_MS = 10_000;\n\nconst fetchCollectToken = async (apiBaseUrl: string, secureToken: string): Promise<string> => {\n const controller = new AbortController();\n const timeoutId = setTimeout(() => controller.abort(), FETCH_COLLECT_TOKEN_TIMEOUT_MS);\n\n try {\n const resp = await fetch(`${apiBaseUrl}/api/card-setup-intents/${secureToken}/collect-token`, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n signal: controller.signal,\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 } finally {\n clearTimeout(timeoutId);\n }\n};\n\n/**\n * Creates a card via VGS CMP and sends aliases to our backend.\n *\n * Flow:\n * 1. Fetch CMP access token from our backend\n * 2. Call form.createCard() → VGS creates card, returns aliases\n * 3. Send aliases + fraud metadata to our backend via updateCardSetupIntent\n */\n/**\n * Map VGS field-level error keys to PaymentKitErrors field keys.\n *\n * VGS CMP errorCallback uses short field names (\"pan\", \"cvc\", \"exp-date\")\n * matching the CMP field type, NOT the longer CSS-style names. Both forms\n * are mapped here for robustness.\n */\nconst VGS_FIELD_MAP: Record<string, keyof PaymentKitErrors> = {\n // Actual VGS CMP error keys (verified empirically)\n pan: \"card_pan\",\n cvc: \"card_cvc\",\n \"exp-date\": \"card_exp\",\n // Alternative key formats (VGS documentation references)\n \"card-number\": \"card_pan\",\n \"card-security-code\": \"card_cvc\",\n \"card-expiration-date\": \"card_exp\",\n};\n\n/**\n * Safely stringify an error for console logging.\n * VGS errors are plain objects that print as [object Object] without this.\n */\nconst stringifyError = (err: unknown): string => {\n if (err instanceof Error) return err.message;\n try {\n return JSON.stringify(err);\n } catch {\n return String(err);\n }\n};\n\nconst submitVgsCardFields = async (\n states: CardStates,\n tunnelX: Awaited<ReturnType<typeof TunnelXManager.createFromPenpalConnection>>,\n): Promise<{ errors: PaymentKitErrors; isSuccess: boolean }> => {\n const { vgsForm, cardSetupIntentId, secureToken, apiBaseUrl, checkoutRequestId } = states;\n const logPrefix = `[PaymentKit] [${checkoutRequestId}]`;\n const errors = {} as PaymentKitErrors;\n\n if (!vgsForm) {\n errors.card_pan = \"required\";\n return { errors, isSuccess: false };\n }\n\n if (!cardSetupIntentId) {\n errors.root = \"Card setup intent not created\";\n return { errors, isSuccess: false };\n }\n\n // Step 1: Get CMP access token from our backend\n let accessToken: string;\n const collectTokenStart = performance.now();\n try {\n accessToken = await fetchCollectToken(apiBaseUrl, secureToken);\n const collectTokenDuration = Math.round(performance.now() - collectTokenStart);\n AnalyticsService.capture(\"vgs_collect_token_timing\", {\n checkout_session_id: secureToken,\n request_id: checkoutRequestId,\n duration_ms: collectTokenDuration,\n success: true,\n });\n } catch (err) {\n const collectTokenDuration = Math.round(performance.now() - collectTokenStart);\n const isAbortError = err instanceof DOMException && err.name === \"AbortError\";\n AnalyticsService.capture(\"vgs_collect_token_timing\", {\n checkout_session_id: secureToken,\n request_id: checkoutRequestId,\n duration_ms: collectTokenDuration,\n success: false,\n error_type: isAbortError ? \"FETCH_TIMEOUT\" : \"FETCH_ERROR\",\n });\n console.error(`${logPrefix} VGS auth token fetch failed:`, stringifyError(err));\n errors.root = isAbortError\n ? \"Card authentication timed out — please try again\"\n : \"Card authentication failed — please try again\";\n return { errors, isSuccess: false };\n }\n\n // Step 2: Create card via VGS CMP — returns aliases for PAN/CVC, plain text expiry\n let cardResult: VGSCreateCardResponse;\n const VGS_CREATE_CARD_TIMEOUT_MS = 120_000;\n const createCardStart = performance.now();\n try {\n let createCardTimeoutId: ReturnType<typeof setTimeout>;\n cardResult = await Promise.race([\n new Promise<VGSCreateCardResponse>((resolve, reject) => {\n vgsForm.createCard(\n {\n auth: accessToken,\n data: { cardholder: {} },\n },\n (_status, cardObject) => resolve(cardObject),\n (error) => reject(error),\n );\n }),\n new Promise<never>((_, reject) => {\n createCardTimeoutId = setTimeout(() => reject(new Error(\"VGS_TIMEOUT\")), VGS_CREATE_CARD_TIMEOUT_MS);\n }),\n ]).finally(() => clearTimeout(createCardTimeoutId));\n\n // Emit timing event on success\n const createCardDuration = Math.round(performance.now() - createCardStart);\n AnalyticsService.capture(\"vgs_create_card_timing\", {\n checkout_session_id: secureToken,\n request_id: checkoutRequestId,\n duration_ms: createCardDuration,\n success: true,\n });\n } catch (err) {\n const createCardDuration = Math.round(performance.now() - createCardStart);\n const errStr = stringifyError(err);\n console.error(`${logPrefix} VGS createCard failed:`, errStr);\n\n // Distinguish timeout from VGS field validation errors\n if (err instanceof Error && err.message === \"VGS_TIMEOUT\") {\n AnalyticsService.capture(\"vgs_create_card_timing\", {\n checkout_session_id: secureToken,\n request_id: checkoutRequestId,\n duration_ms: createCardDuration,\n success: false,\n error_type: \"VGS_TIMEOUT\",\n });\n errors.root = \"Card processing timed out — please try again\";\n return { errors, isSuccess: false };\n }\n\n // VGS errorCallback returns field-level errors as a plain object,\n // e.g. { \"card-number\": { errorMessages: [\"Card number is not valid\"] } }\n // Map them to per-field errors so inline validation shows on the right field.\n if (err && typeof err === \"object\" && !(err instanceof Error)) {\n const vgsErrors = err as Record<string, unknown>;\n for (const [vgsKey, fieldKey] of Object.entries(VGS_FIELD_MAP)) {\n if (vgsKey in vgsErrors) {\n const fieldErr = vgsErrors[vgsKey] as Record<string, unknown> | undefined;\n const messages = fieldErr?.errorMessages;\n errors[fieldKey] = Array.isArray(messages) ? messages[0] : \"invalid\";\n }\n }\n if (Object.keys(errors).length > 0) {\n AnalyticsService.capture(\"vgs_create_card_timing\", {\n checkout_session_id: secureToken,\n request_id: checkoutRequestId,\n duration_ms: createCardDuration,\n success: false,\n error_type: \"VGS_VALIDATION_ERROR\",\n });\n return { errors, isSuccess: false };\n }\n }\n\n // Generic VGS error\n AnalyticsService.capture(\"vgs_create_card_timing\", {\n checkout_session_id: secureToken,\n request_id: checkoutRequestId,\n duration_ms: createCardDuration,\n success: false,\n error_type: \"VGS_ERROR\",\n });\n errors.root = \"Card validation failed — please check your card details and try again\";\n return { errors, isSuccess: false };\n }\n\n // Step 3: Extract aliases and expiry from CMP response\n if (!cardResult?.data?.attributes) {\n console.error(`${logPrefix} VGS createCard returned invalid response:`, stringifyError(cardResult));\n errors.root = \"Card processing returned an invalid response — please try again\";\n return { errors, isSuccess: false };\n }\n const { pan_alias, cvc_alias, exp_month, exp_year } = cardResult.data.attributes;\n\n if (!cvc_alias) {\n console.warn(\n `${logPrefix} VGS createCard response missing cvc_alias (303 dedup):`,\n JSON.stringify(cardResult.data.attributes),\n );\n }\n\n const fullYear = exp_year < 100 ? 2000 + exp_year : exp_year;\n const formattedExp = `${String(exp_month).padStart(2, \"0\")}/${fullYear}`;\n\n // Capture CMP card ID for network token tracking\n const cmpCardId = cardResult.data.id;\n\n // Step 4: Send aliases + fraud metadata to our backend.\n // CVC is sent as a VGS alias from createCard. Additionally, the CVC is\n // submitted via the vault inbound proxy (REDACT route) as a fallback for\n // the 303 deduplication path where VGS returns an existing card without\n // a fresh cvc_alias.\n try {\n const updateIntentCall = tunnelX.publicEndpoints.updateCardSetupIntent({\n checkoutToken: secureToken,\n cardSetupIntentId: cardSetupIntentId,\n updateCardSetupIntentReq: {\n cardPan: pan_alias,\n cardExp: formattedExp,\n ...(cvc_alias ? { cardCvc: cvc_alias } : {}),\n cmpCardId: cmpCardId,\n fraudMetadata: collectFraudMetadata(),\n },\n });\n\n const proxyCvcCall = new Promise<void>((resolve, reject) => {\n if (!vgsForm.submit) {\n reject(new Error(\"VGS form.submit not available\"));\n return;\n }\n vgsForm.submit(\n `/api/card-setup-intents/${secureToken}/${cardSetupIntentId}`,\n {\n method: \"post\",\n headers: { \"Content-Type\": \"application/json\" },\n data: (fields: Record<string, unknown>) => ({ card_cvc: fields.cvc }),\n },\n (_status: number) => resolve(),\n (err: unknown) => reject(err),\n );\n });\n\n await Promise.all([updateIntentCall, proxyCvcCall]);\n } catch (err) {\n console.error(`${logPrefix} updateCardSetupIntent failed:`, stringifyError(err));\n errors.root = \"Failed to save card details — please try again\";\n return { errors, isSuccess: false };\n }\n\n return { errors: {}, isSuccess: true };\n};\n\nconst submitCardFields = async (states: CardStates) => {\n const errors = {} as PaymentKitErrors;\n const { cardSetupIntentId, cardInputConnections } = states;\n\n const submitPromises = Object.entries(cardInputConnections).map(async ([_type, connection]) => {\n const type = _type as PenpalCardInputType;\n\n const remote = await connection.promise;\n const result = await remote.onSubmit(cardSetupIntentId || \"\");\n\n if (\"error\" in result) {\n errors[type] = result.error;\n }\n });\n\n await Promise.allSettled(submitPromises);\n\n return {\n errors,\n isSuccess: Object.keys(errors).length === 0,\n };\n};\n\nconst validateCardFields = async (states: CardStates) => {\n const errors: PaymentKitErrors = {};\n const { cardInputConnections } = states;\n\n const validatePromises = Object.entries(cardInputConnections).map(async ([_type, connection]) => {\n const type = _type as PenpalCardInputType;\n\n if (!connection) {\n errors[type] = \"penpal_not_connected\";\n return;\n }\n\n const remote = await connection.promise;\n const errorMsg = await remote.onValidate();\n\n if (errorMsg) {\n errors[type] = errorMsg;\n }\n });\n\n await Promise.allSettled(validatePromises);\n\n return {\n errors,\n isSuccess: Object.keys(errors).length === 0,\n };\n};\n\nconst CardPaymentMethod = definePaymentMethod((paymentKitStates) => {\n // Object.create() creates prototype delegation: reads fall through to the shared\n // paymentKitStates (preserving auto-detected VGS config), writes stay local.\n // This prevents card-specific properties (cardInputConnections, vgsForm, etc.)\n // from leaking onto the shared paymentKitStates object.\n const localStates = Object.create(paymentKitStates) as CardStates;\n localStates.cardInputConnections = {};\n\n return {\n name: \"card\",\n externalFuncs: {\n createElement: defCreateElement(localStates),\n },\n internalFuncs: {\n submitPayment: defSubmitPayment(localStates),\n cleanup: () => {\n // Clean up VGS form if present\n if (localStates.vgsForm) {\n localStates.vgsFieldsCleanup?.();\n localStates.vgsFieldsCleanup = undefined;\n localStates.vgsForm.unmount();\n localStates.vgsForm = undefined;\n }\n // Clean up all card input iframe connections\n for (const connection of Object.values(localStates.cardInputConnections)) {\n connection?.destroy();\n }\n localStates.cardInputConnections = {};\n },\n },\n };\n});\n\nexport default CardPaymentMethod;\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"card.mjs","names":["taxIds: string[] | undefined","cleanupVgs: (() => void) | undefined","cleanupDirect: (() => void) | undefined","cdnTimeoutId: ReturnType<typeof setTimeout>","params: Record<string, string>","submitPayment: TInternalFuncs[\"submitPayment\"]","VGS_FIELD_MAP: Record<string, keyof PaymentKitErrors>","accessToken: string","cardResult: VGSCreateCardResponse","createCardTimeoutId: ReturnType<typeof setTimeout>","errors: PaymentKitErrors"],"sources":["../../src/payment-methods/card.ts"],"sourcesContent":["import {\n connectToCardIframe,\n type IFrameConnection,\n type CardInputType as PenpalCardInputType,\n type CheckoutResponse as PenpalCheckoutResponse,\n} from \"../penpal/connect-card\";\n\n// Re-export types for public API\nexport type CardInputType = PenpalCardInputType;\nexport type CheckoutResponse = PenpalCheckoutResponse;\n\nimport { AnalyticsService } from \"../analytics\";\nimport { TunnelXManager } from \"../penpal/connect-tunnel-x\";\nimport type { PaymentKitErrors, PaymentKitFields, PaymentKitStates, TInternalFuncs } from \"../types\";\nimport {\n $,\n collectFraudMetadata,\n createCheckoutIFrame,\n definePaymentMethod,\n validateFormFields,\n withRequestId,\n} from \"../utils\";\nimport { handleNextAction } from \"./next-action-handlers\";\nimport { createVgsCardFields, initVgsCollect, loadVgsCollectScript } from \"./vgs-collect-loader\";\n\n/**\n * Splits a full name into first name and last name.\n * First word becomes firstName, rest becomes lastName.\n */\nconst splitName = (fullName: string): { firstName: string; lastName?: string } => {\n const trimmed = fullName.trim();\n const spaceIndex = trimmed.indexOf(\" \");\n if (spaceIndex === -1) {\n return { firstName: trimmed };\n }\n return {\n firstName: trimmed.substring(0, spaceIndex),\n lastName: trimmed.substring(spaceIndex + 1).trim() || undefined,\n };\n};\n\n/**\n * Maps PaymentKitFields to the customerInfo structure expected by the API.\n */\nconst mapFieldsToCustomerInfo = (fields: PaymentKitFields) => {\n const { firstName, lastName } = splitName(fields.customer_name);\n\n const hasBillingAddress =\n fields.customer_country ||\n fields.customer_zip_code ||\n fields.customer_address_line1 ||\n fields.customer_address_line2 ||\n fields.customer_city ||\n fields.customer_state;\n\n const hasShippingAddress =\n fields.shipping_address_line1 ||\n fields.shipping_address_line2 ||\n fields.shipping_city ||\n fields.shipping_state ||\n fields.shipping_zip_code ||\n fields.shipping_country;\n\n let taxIds: string[] | undefined;\n if (fields.customer_tax_ids) {\n try {\n taxIds = JSON.parse(fields.customer_tax_ids);\n } catch {\n taxIds = undefined;\n }\n }\n\n return {\n email: fields.customer_email || undefined,\n firstName,\n lastName,\n businessName: fields.customer_business_name || undefined,\n taxIds: taxIds?.length ? taxIds : undefined,\n billingAddress: hasBillingAddress\n ? {\n country: fields.customer_country || undefined,\n zipCode: fields.customer_zip_code || undefined,\n line1: fields.customer_address_line1 || undefined,\n line2: fields.customer_address_line2 || undefined,\n city: fields.customer_city || undefined,\n state: fields.customer_state || undefined,\n }\n : undefined,\n shippingAddress: hasShippingAddress\n ? {\n line1: fields.shipping_address_line1 || undefined,\n line2: fields.shipping_address_line2 || undefined,\n city: fields.shipping_city || undefined,\n state: fields.shipping_state || undefined,\n zipCode: fields.shipping_zip_code || undefined,\n country: fields.shipping_country || undefined,\n }\n : undefined,\n };\n};\n\ntype CardStates = PaymentKitStates & {\n cardSetupIntentId?: string;\n cardInputConnections: Partial<Record<PenpalCardInputType, IFrameConnection>>;\n vgsForm?: VGSCollectForm;\n vgsFieldsCleanup?: () => void;\n vgsFocusCallbacks?: Partial<Record<PenpalCardInputType, (isFocused: boolean) => void>>;\n vgsMountSelectors?: Partial<Record<PenpalCardInputType, string>>;\n vgsPlaceholders?: Partial<Record<PenpalCardInputType, string>>;\n};\n\ntype CreateElementOptions = Partial<Parameters<typeof connectToCardIframe>[1]> & {\n style?: Record<string, string>;\n placeholder?: string;\n};\n\nconst defCreateElement = (states: CardStates) => {\n const { baseUrl, apiBaseUrl, cardInputConnections, secureToken, timingTracker } = states;\n\n return (type: PenpalCardInputType, options: CreateElementOptions) => {\n const { style, onLoaded, onFocusChange, placeholder } = options;\n\n const mountIFrame = (parentSelector: string) => {\n // Mutable cleanup callbacks — set once mode is determined and mount completes\n let cleanupVgs: (() => void) | undefined;\n let cleanupDirect: (() => void) | undefined;\n // Cancellation flag: prevents deferred doMount from executing after unmount.\n // Needed because React strict mode may call unmount before the deferred\n // _sessionConfigReady promise resolves.\n let cancelled = false;\n\n /**\n * Performs the actual DOM mount after the tokenization mode is known.\n * Runs synchronously when mode is explicitly provided, or after the\n * auto-detect fetch resolves.\n */\n // Store mount selector so VGS init (triggered by card_pan) can read\n // the exp/cvc selectors that were registered by their own createElement calls.\n if (!states.vgsMountSelectors) states.vgsMountSelectors = {};\n states.vgsMountSelectors[type] = parentSelector;\n\n const doMount = () => {\n if (cancelled) return;\n const isVgsMode = states.cardTokenizationMode === \"vgs\";\n console.log(\n `[PaymentKit] card.doMount(${type}) — mode: ${states.cardTokenizationMode}, vgsVaultId: ${states.vgsVaultId ?? \"unset\"}, vgsEnvironment: ${states.vgsEnvironment ?? \"unset\"}`,\n );\n\n // VGS mode: initialize VGS Collect form and mount fields on card_pan\n if (isVgsMode && states.vgsVaultId && states.vgsEnvironment) {\n // Store per-field focus callbacks and placeholders so createVgsCardFields\n // can wire each field to its own callback/placeholder. All three createElement\n // calls complete before the CDN script loads, so all values are available by then.\n if (onFocusChange) {\n if (!states.vgsFocusCallbacks) states.vgsFocusCallbacks = {};\n states.vgsFocusCallbacks[type] = onFocusChange;\n }\n if (placeholder) {\n if (!states.vgsPlaceholders) states.vgsPlaceholders = {};\n states.vgsPlaceholders[type] = placeholder;\n }\n\n if (type === \"card_pan\") {\n const vaultId = states.vgsVaultId;\n const vgsEnv = states.vgsEnvironment;\n\n console.log(`[PaymentKit] VGS mode active — loading VGS Collect CDN for vault ${vaultId} (${vgsEnv})...`);\n const VGS_CDN_TIMEOUT_MS = 15_000;\n let cdnTimeoutId: ReturnType<typeof setTimeout>;\n Promise.race([\n loadVgsCollectScript(),\n new Promise<never>((_, reject) => {\n cdnTimeoutId = setTimeout(() => {\n console.warn(`[PaymentKit] VGS Collect CDN load timed out after ${VGS_CDN_TIMEOUT_MS / 1000}s`);\n reject(new Error(\"VGS Collect CDN load timed out\"));\n }, VGS_CDN_TIMEOUT_MS);\n }),\n ])\n .finally(() => clearTimeout(cdnTimeoutId))\n .then(() => {\n if (cancelled) return;\n console.log(\"[PaymentKit] VGS Collect CDN loaded — initializing form...\");\n const form = initVgsCollect(vaultId, vgsEnv);\n if (cancelled) {\n form.unmount();\n return;\n }\n states.vgsForm = form;\n\n const css = style\n ? Object.fromEntries(\n Object.entries(style).map(([k, v]) => [k.replace(/([A-Z])/g, \"-$1\").toLowerCase(), v]),\n )\n : undefined;\n\n // Read exp/cvc selectors from shared state.\n // Each createElement(\"card_exp/card_cvc\").mount(selector) stores its\n // selector before this async callback runs, so they're available here.\n const expSelector = states.vgsMountSelectors?.card_exp;\n const cvcSelector = states.vgsMountSelectors?.card_cvc;\n if (!expSelector || !cvcSelector) {\n const msg =\n `[PaymentKit] VGS mode requires all three card fields to be mounted. ` +\n `Missing: ${!expSelector ? \"card_exp\" : \"\"}${!expSelector && !cvcSelector ? \", \" : \"\"}${!cvcSelector ? \"card_cvc\" : \"\"}`;\n console.error(msg);\n onLoaded?.();\n return;\n }\n console.log(\n `[PaymentKit] Creating VGS card fields — pan: ${parentSelector}, exp: ${expSelector}, cvc: ${cvcSelector}`,\n );\n states.vgsFieldsCleanup = createVgsCardFields(\n form,\n {\n pan: parentSelector,\n exp: expSelector,\n cvc: cvcSelector,\n },\n css,\n states.vgsFocusCallbacks,\n states.vgsPlaceholders,\n );\n\n console.log(\"[PaymentKit] VGS card fields created successfully\");\n timingTracker.trackInputReady();\n onLoaded?.();\n })\n .catch((err) => {\n console.error(\"[PaymentKit] Failed to load VGS Collect script. Card input will not be available:\", err);\n // Signal loaded so the form exits loading state rather than hanging forever.\n // Card fields won't render, but submit will fail with a clear error.\n onLoaded?.();\n });\n }\n\n // For non-pan fields in VGS mode, signal loaded immediately since\n // VGS handles all three fields from the card_pan mount.\n if (type !== \"card_pan\") {\n onLoaded?.();\n }\n\n cleanupVgs = () => {\n if (type === \"card_pan\" && states.vgsForm) {\n try {\n states.vgsFieldsCleanup?.();\n } catch {\n // VGS may try to remove iframes from containers already removed by React\n }\n states.vgsFieldsCleanup = undefined;\n try {\n states.vgsForm.unmount();\n } catch {\n // VGS unmount may fail if DOM nodes are already detached\n }\n states.vgsForm = undefined;\n }\n };\n return;\n }\n\n // If we got here and mode was \"vgs\", it means VGS config was incomplete\n if (isVgsMode) {\n console.warn(\n `[PaymentKit] VGS mode requested but config incomplete (vgsVaultId: ${states.vgsVaultId ?? \"unset\"}, vgsEnvironment: ${states.vgsEnvironment ?? \"unset\"}) — using direct mode`,\n );\n }\n\n // Direct mode: existing penpal iframe logic\n const parent = $(parentSelector) as HTMLElement;\n\n // Read container padding so the embed input matches the merchant's\n // visual intent. The iframe uses absolute positioning to fill the\n // full container, so the input needs its own padding for alignment.\n const computed = getComputedStyle(parent);\n const containerPadding = computed.padding !== \"0px\" ? computed.padding : undefined;\n const enhancedStyle = {\n ...(style ?? {}),\n height: \"100%\",\n boxSizing: \"border-box\",\n ...(containerPadding ? { padding: containerPadding } : {}),\n };\n\n const params: Record<string, string> = {\n checkout_token: secureToken,\n api_base_url: apiBaseUrl,\n style: JSON.stringify(enhancedStyle),\n };\n\n const iframe = createCheckoutIFrame(type.replace(\"_\", \"-\"), baseUrl, params);\n\n // Use absolute positioning so the iframe covers the full container\n // including any padding the merchant's CSS applies.\n if (computed.position === \"static\") {\n parent.style.position = \"relative\";\n }\n iframe.style.position = \"absolute\";\n iframe.style.inset = \"0\";\n\n // Set cleanup before DOM mutation to avoid leaking the iframe if\n // unmount() is called between appendChild and onload.\n cleanupDirect = () => {\n const connection = cardInputConnections[type];\n connection?.destroy();\n iframe.remove();\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\n/**\n * Terminal failure states for a card checkout. Mirrors the backend checkout-attempt state machine\n * (app/modules/checkout/checkout_attempt/checkout_attempt_state_machine.py — the only terminal\n * success state is `checkout_succeeded`). A checkout in one of these states must be surfaced to the\n * merchant as an error so `onError` — not `onSuccess` — fires.\n *\n * Note: 3DS authentication *passing* does NOT imply the charge succeeded — a pass-then-decline\n * concludes with `payment_failed` and no further `nextAction`. Other non-terminal states (e.g.\n * `payment_started` from an async \"pending\" charge that carries no `nextAction`) are intentionally\n * left to the success/data path here, preserving existing behavior for the non-3DS flow.\n */\nconst CHECKOUT_FAILURE_STATES = new Set([\"payment_failed\", \"checkout_failed\"]);\n\nconst isCheckoutFailure = (result: CheckoutResponse): boolean => CHECKOUT_FAILURE_STATES.has(result.state);\n\nconst defSubmitPayment = (states: CardStates) => {\n const submitPayment: TInternalFuncs[\"submitPayment\"] = async (fields, options) => {\n // Ensure session config is loaded (no-op if explicitly provided)\n if (states._sessionConfigReady) await states._sessionConfigReady;\n const isVgsMode = states.cardTokenizationMode === \"vgs\";\n\n const { timingTracker, checkoutRequestId } = states;\n\n // Track submit event\n timingTracker.trackSubmit();\n\n // Use the checkout request ID from states (generated on page load)\n const requestOptions = withRequestId(checkoutRequestId);\n\n const tunnelX = await TunnelXManager.createFromPenpalConnection(states.tunnelXConnection);\n\n // Step 1. Validate form values.\n // Form field validation is shared across both modes.\n // In VGS mode, card field validation is handled by VGS Collect (built-in validators).\n // In direct mode, card field validation is handled by penpal iframes.\n const skipCustomerValidation = options?.skipCustomerValidation === true;\n const validateFormResult = skipCustomerValidation\n ? { isSuccess: true as const, errors: {} as PaymentKitErrors }\n : await validateFormFields(fields);\n if (!isVgsMode) {\n const validateCardResult = await validateCardFields(states);\n if (!(validateCardResult.isSuccess && validateFormResult.isSuccess)) {\n timingTracker.trackFail(null, \"validation_error\", \"Form validation failed\");\n return { errors: { ...validateCardResult.errors, ...validateFormResult.errors } };\n }\n } else if (!validateFormResult.isSuccess) {\n timingTracker.trackFail(null, \"validation_error\", \"Form validation failed\");\n return { errors: validateFormResult.errors };\n }\n\n // Step 2. Create card setup intent if not present\n if (!states.cardSetupIntentId) {\n const res = await tunnelX.publicEndpoints.createCardSetupIntent({\n checkoutToken: states.secureToken,\n });\n states.cardSetupIntentId = res.cardSetupIntentId;\n }\n\n // Step 3. Submit card values.\n if (isVgsMode) {\n // VGS mode: create card via CMP, then send aliases to backend\n console.log(\"[PaymentKit] Submitting card via VGS CMP...\");\n const vgsSubmitResult = await submitVgsCardFields(states, tunnelX);\n if (!vgsSubmitResult.isSuccess) {\n // Include field-level error details for PostHog visibility\n const errorDetail = Object.entries(vgsSubmitResult.errors)\n .map(([k, v]) => `${k}: ${v}`)\n .join(\", \");\n timingTracker.trackFail(null, \"card_submit_error\", errorDetail || \"VGS card submission failed\");\n return { errors: vgsSubmitResult.errors };\n }\n } else {\n // Direct mode: submit via penpal iframes\n const submitCardResult = await submitCardFields(states);\n if (!submitCardResult.isSuccess) {\n timingTracker.trackFail(null, \"card_submit_error\", \"Card submission failed\");\n return { errors: submitCardResult.errors };\n }\n }\n\n // Step 4. Get card setup intent.\n const cardSetupIntent = await tunnelX.publicEndpoints.getCardSetupIntent({\n cardSetupIntentId: states.cardSetupIntentId,\n checkoutToken: states.secureToken,\n });\n if (!cardSetupIntent.isCardAllSet) {\n const errors = {} as PaymentKitErrors;\n if (!cardSetupIntent.isCardPanSet) errors.card_pan = \"required\";\n if (!cardSetupIntent.isCardExpSet) errors.card_exp = \"required\";\n if (!cardSetupIntent.isCardCvcSet) errors.card_cvc = \"required\";\n timingTracker.trackFail(null, \"card_setup_incomplete\", \"Card details incomplete\");\n return { errors };\n }\n\n console.log(\"Card setup intent is set ✅\", cardSetupIntent);\n console.log(\"Fields\", fields);\n\n // Step 5. Submit card checkout with customer info and fraud metadata\n // Include processor_fraud_info with airwallex_device_id for fraud fingerprinting\n const fraudMetadata = {\n ...collectFraudMetadata(),\n processorFraudInfo: {\n airwallexDeviceId: states.checkoutRequestId,\n },\n };\n let currentResult = await tunnelX.publicEndpoints.cardCheckout(\n {\n checkoutToken: states.secureToken,\n publicCardCheckoutRequest: {\n cardSetupIntentId: states.cardSetupIntentId,\n customerInfo: mapFieldsToCustomerInfo(fields),\n fraudMetadata,\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 // 3DS auth passed AND no further action — but the charge may still have been declined.\n // Gate the success return on the checkout state so a pass-then-decline\n // (state=payment_failed) reports onError instead of a false onSuccess.\n if (isCheckoutFailure(verifyResult)) {\n console.log(\"Card checkout concluded in a failure state after 3DS:\", verifyResult);\n timingTracker.trackFail(\n verifyResult.checkoutAttemptId || null,\n verifyResult.errorCode || \"payment_failed\",\n verifyResult.errorMessageForCustomer || \"Payment failed\",\n );\n return {\n errors: {\n root: verifyResult.errorMessageForCustomer || \"Payment failed\",\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 (isCheckoutFailure(currentResult)) {\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 * Uses AbortController with 10s timeout to prevent indefinite hangs on slow networks.\n */\nconst FETCH_COLLECT_TOKEN_TIMEOUT_MS = 10_000;\n\nconst fetchCollectToken = async (apiBaseUrl: string, secureToken: string): Promise<string> => {\n const controller = new AbortController();\n const timeoutId = setTimeout(() => controller.abort(), FETCH_COLLECT_TOKEN_TIMEOUT_MS);\n\n try {\n const resp = await fetch(`${apiBaseUrl}/api/card-setup-intents/${secureToken}/collect-token`, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n signal: controller.signal,\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 } finally {\n clearTimeout(timeoutId);\n }\n};\n\n/**\n * Creates a card via VGS CMP and sends aliases to our backend.\n *\n * Flow:\n * 1. Fetch CMP access token from our backend\n * 2. Call form.createCard() → VGS creates card, returns aliases\n * 3. Send aliases + fraud metadata to our backend via updateCardSetupIntent\n */\n/**\n * Map VGS field-level error keys to PaymentKitErrors field keys.\n *\n * VGS CMP errorCallback uses short field names (\"pan\", \"cvc\", \"exp-date\")\n * matching the CMP field type, NOT the longer CSS-style names. Both forms\n * are mapped here for robustness.\n */\nconst VGS_FIELD_MAP: Record<string, keyof PaymentKitErrors> = {\n // Actual VGS CMP error keys (verified empirically)\n pan: \"card_pan\",\n cvc: \"card_cvc\",\n \"exp-date\": \"card_exp\",\n // Alternative key formats (VGS documentation references)\n \"card-number\": \"card_pan\",\n \"card-security-code\": \"card_cvc\",\n \"card-expiration-date\": \"card_exp\",\n};\n\n/**\n * Safely stringify an error for console logging.\n * VGS errors are plain objects that print as [object Object] without this.\n */\nconst stringifyError = (err: unknown): string => {\n if (err instanceof Error) return err.message;\n try {\n return JSON.stringify(err);\n } catch {\n return String(err);\n }\n};\n\nconst submitVgsCardFields = async (\n states: CardStates,\n tunnelX: Awaited<ReturnType<typeof TunnelXManager.createFromPenpalConnection>>,\n): Promise<{ errors: PaymentKitErrors; isSuccess: boolean }> => {\n const { vgsForm, cardSetupIntentId, secureToken, apiBaseUrl, checkoutRequestId } = states;\n const logPrefix = `[PaymentKit] [${checkoutRequestId}]`;\n const errors = {} as PaymentKitErrors;\n\n if (!vgsForm) {\n errors.card_pan = \"required\";\n return { errors, isSuccess: false };\n }\n\n if (!cardSetupIntentId) {\n errors.root = \"Card setup intent not created\";\n return { errors, isSuccess: false };\n }\n\n // Step 1: Get CMP access token from our backend\n let accessToken: string;\n const collectTokenStart = performance.now();\n try {\n accessToken = await fetchCollectToken(apiBaseUrl, secureToken);\n const collectTokenDuration = Math.round(performance.now() - collectTokenStart);\n AnalyticsService.capture(\"vgs_collect_token_timing\", {\n checkout_session_id: secureToken,\n request_id: checkoutRequestId,\n duration_ms: collectTokenDuration,\n success: true,\n });\n } catch (err) {\n const collectTokenDuration = Math.round(performance.now() - collectTokenStart);\n const isAbortError = err instanceof DOMException && err.name === \"AbortError\";\n AnalyticsService.capture(\"vgs_collect_token_timing\", {\n checkout_session_id: secureToken,\n request_id: checkoutRequestId,\n duration_ms: collectTokenDuration,\n success: false,\n error_type: isAbortError ? \"FETCH_TIMEOUT\" : \"FETCH_ERROR\",\n });\n console.error(`${logPrefix} VGS auth token fetch failed:`, stringifyError(err));\n errors.root = isAbortError\n ? \"Card authentication timed out — please try again\"\n : \"Card authentication failed — please try again\";\n return { errors, isSuccess: false };\n }\n\n // Step 2: Create card via VGS CMP — returns aliases for PAN/CVC, plain text expiry\n let cardResult: VGSCreateCardResponse;\n const VGS_CREATE_CARD_TIMEOUT_MS = 120_000;\n const createCardStart = performance.now();\n try {\n let createCardTimeoutId: ReturnType<typeof setTimeout>;\n cardResult = await Promise.race([\n new Promise<VGSCreateCardResponse>((resolve, reject) => {\n vgsForm.createCard(\n {\n auth: accessToken,\n data: { cardholder: {} },\n },\n (_status, cardObject) => resolve(cardObject),\n (error) => reject(error),\n );\n }),\n new Promise<never>((_, reject) => {\n createCardTimeoutId = setTimeout(() => reject(new Error(\"VGS_TIMEOUT\")), VGS_CREATE_CARD_TIMEOUT_MS);\n }),\n ]).finally(() => clearTimeout(createCardTimeoutId));\n\n // Emit timing event on success\n const createCardDuration = Math.round(performance.now() - createCardStart);\n AnalyticsService.capture(\"vgs_create_card_timing\", {\n checkout_session_id: secureToken,\n request_id: checkoutRequestId,\n duration_ms: createCardDuration,\n success: true,\n });\n } catch (err) {\n const createCardDuration = Math.round(performance.now() - createCardStart);\n const errStr = stringifyError(err);\n console.error(`${logPrefix} VGS createCard failed:`, errStr);\n\n // Distinguish timeout from VGS field validation errors\n if (err instanceof Error && err.message === \"VGS_TIMEOUT\") {\n AnalyticsService.capture(\"vgs_create_card_timing\", {\n checkout_session_id: secureToken,\n request_id: checkoutRequestId,\n duration_ms: createCardDuration,\n success: false,\n error_type: \"VGS_TIMEOUT\",\n });\n errors.root = \"Card processing timed out — please try again\";\n return { errors, isSuccess: false };\n }\n\n // VGS errorCallback returns field-level errors as a plain object,\n // e.g. { \"card-number\": { errorMessages: [\"Card number is not valid\"] } }\n // Map them to per-field errors so inline validation shows on the right field.\n if (err && typeof err === \"object\" && !(err instanceof Error)) {\n const vgsErrors = err as Record<string, unknown>;\n for (const [vgsKey, fieldKey] of Object.entries(VGS_FIELD_MAP)) {\n if (vgsKey in vgsErrors) {\n const fieldErr = vgsErrors[vgsKey] as Record<string, unknown> | undefined;\n const messages = fieldErr?.errorMessages;\n errors[fieldKey] = Array.isArray(messages) ? messages[0] : \"invalid\";\n }\n }\n if (Object.keys(errors).length > 0) {\n AnalyticsService.capture(\"vgs_create_card_timing\", {\n checkout_session_id: secureToken,\n request_id: checkoutRequestId,\n duration_ms: createCardDuration,\n success: false,\n error_type: \"VGS_VALIDATION_ERROR\",\n });\n return { errors, isSuccess: false };\n }\n }\n\n // Generic VGS error\n AnalyticsService.capture(\"vgs_create_card_timing\", {\n checkout_session_id: secureToken,\n request_id: checkoutRequestId,\n duration_ms: createCardDuration,\n success: false,\n error_type: \"VGS_ERROR\",\n });\n errors.root = \"Card validation failed — please check your card details and try again\";\n return { errors, isSuccess: false };\n }\n\n // Step 3: Extract aliases and expiry from CMP response\n if (!cardResult?.data?.attributes) {\n console.error(`${logPrefix} VGS createCard returned invalid response:`, stringifyError(cardResult));\n errors.root = \"Card processing returned an invalid response — please try again\";\n return { errors, isSuccess: false };\n }\n const { pan_alias, cvc_alias, exp_month, exp_year } = cardResult.data.attributes;\n\n if (!cvc_alias) {\n console.warn(\n `${logPrefix} VGS createCard response missing cvc_alias (303 dedup):`,\n JSON.stringify(cardResult.data.attributes),\n );\n }\n\n const fullYear = exp_year < 100 ? 2000 + exp_year : exp_year;\n const formattedExp = `${String(exp_month).padStart(2, \"0\")}/${fullYear}`;\n\n // Capture CMP card ID for network token tracking\n const cmpCardId = cardResult.data.id;\n\n // Step 4: Send aliases + fraud metadata to our backend.\n // CVC is sent as a VGS alias from createCard. Additionally, the CVC is\n // submitted via the vault inbound proxy (REDACT route) as a fallback for\n // the 303 deduplication path where VGS returns an existing card without\n // a fresh cvc_alias.\n try {\n const updateIntentCall = tunnelX.publicEndpoints.updateCardSetupIntent({\n checkoutToken: secureToken,\n cardSetupIntentId: cardSetupIntentId,\n updateCardSetupIntentReq: {\n cardPan: pan_alias,\n cardExp: formattedExp,\n ...(cvc_alias ? { cardCvc: cvc_alias } : {}),\n cmpCardId: cmpCardId,\n fraudMetadata: collectFraudMetadata(),\n },\n });\n\n const proxyCvcCall = new Promise<void>((resolve, reject) => {\n if (!vgsForm.submit) {\n reject(new Error(\"VGS form.submit not available\"));\n return;\n }\n vgsForm.submit(\n `/api/card-setup-intents/${secureToken}/${cardSetupIntentId}`,\n {\n method: \"post\",\n headers: { \"Content-Type\": \"application/json\" },\n data: (fields: Record<string, unknown>) => ({ card_cvc: fields.cvc }),\n },\n (_status: number) => resolve(),\n (err: unknown) => reject(err),\n );\n });\n\n await Promise.all([updateIntentCall, proxyCvcCall]);\n } catch (err) {\n console.error(`${logPrefix} updateCardSetupIntent failed:`, stringifyError(err));\n errors.root = \"Failed to save card details — please try again\";\n return { errors, isSuccess: false };\n }\n\n return { errors: {}, isSuccess: true };\n};\n\nconst submitCardFields = async (states: CardStates) => {\n const errors = {} as PaymentKitErrors;\n const { cardSetupIntentId, cardInputConnections } = states;\n\n const submitPromises = Object.entries(cardInputConnections).map(async ([_type, connection]) => {\n const type = _type as PenpalCardInputType;\n\n const remote = await connection.promise;\n const result = await remote.onSubmit(cardSetupIntentId || \"\");\n\n if (\"error\" in result) {\n errors[type] = result.error;\n }\n });\n\n await Promise.allSettled(submitPromises);\n\n return {\n errors,\n isSuccess: Object.keys(errors).length === 0,\n };\n};\n\nconst validateCardFields = async (states: CardStates) => {\n const errors: PaymentKitErrors = {};\n const { cardInputConnections } = states;\n\n const validatePromises = Object.entries(cardInputConnections).map(async ([_type, connection]) => {\n const type = _type as PenpalCardInputType;\n\n if (!connection) {\n errors[type] = \"penpal_not_connected\";\n return;\n }\n\n const remote = await connection.promise;\n const errorMsg = await remote.onValidate();\n\n if (errorMsg) {\n errors[type] = errorMsg;\n }\n });\n\n await Promise.allSettled(validatePromises);\n\n return {\n errors,\n isSuccess: Object.keys(errors).length === 0,\n };\n};\n\nconst CardPaymentMethod = definePaymentMethod((paymentKitStates) => {\n // Object.create() creates prototype delegation: reads fall through to the shared\n // paymentKitStates (preserving auto-detected VGS config), writes stay local.\n // This prevents card-specific properties (cardInputConnections, vgsForm, etc.)\n // from leaking onto the shared paymentKitStates object.\n const localStates = Object.create(paymentKitStates) as CardStates;\n localStates.cardInputConnections = {};\n\n return {\n name: \"card\",\n externalFuncs: {\n createElement: defCreateElement(localStates),\n },\n internalFuncs: {\n submitPayment: defSubmitPayment(localStates),\n cleanup: () => {\n // Clean up VGS form if present\n if (localStates.vgsForm) {\n localStates.vgsFieldsCleanup?.();\n localStates.vgsFieldsCleanup = undefined;\n localStates.vgsForm.unmount();\n localStates.vgsForm = undefined;\n }\n // Clean up all card input iframe connections\n for (const connection of Object.values(localStates.cardInputConnections)) {\n connection?.destroy();\n }\n localStates.cardInputConnections = {};\n },\n },\n };\n});\n\nexport default CardPaymentMethod;\n"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@payment-kit-js/vanilla",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.20",
|
|
4
4
|
"main": "./dist/index.mjs",
|
|
5
5
|
"types": "./dist/index.d.mts",
|
|
6
6
|
"module": "./dist/index.mjs",
|
|
@@ -52,5 +52,5 @@
|
|
|
52
52
|
"tsdown": "^0.15.10",
|
|
53
53
|
"typescript": "^5.9.3"
|
|
54
54
|
},
|
|
55
|
-
"stableVersion": "0.5.
|
|
55
|
+
"stableVersion": "0.5.20"
|
|
56
56
|
}
|