@payment-kit-js/vanilla 0.5.18 → 0.5.19
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/README.md +14 -0
- package/dist/cdn/paymentkit.js +79 -12
- package/dist/cdn/paymentkit.js.map +3 -3
- package/dist/cdn/paymentkit.min.js +6 -6
- package/dist/cdn/paymentkit.min.js.map +3 -3
- package/dist/connect-card-CZhzK_Tp.d.mts.map +1 -1
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +65 -3
- package/dist/index.mjs.map +1 -1
- package/dist/{next-action-handlers-BZ5gynXe.mjs → next-action-handlers-DzhzSvEB.mjs} +2 -2
- package/dist/next-action-handlers-DzhzSvEB.mjs.map +1 -0
- package/dist/payment-methods/apple-pay.mjs +1 -1
- package/dist/payment-methods/card.d.mts.map +1 -1
- package/dist/payment-methods/card.mjs +7 -5
- package/dist/payment-methods/card.mjs.map +1 -1
- package/dist/payment-methods/google-pay.mjs +1 -1
- package/dist/payment-methods/next-action-handlers.mjs +1 -1
- package/package.json +2 -2
- package/dist/next-action-handlers-BZ5gynXe.mjs.map +0 -1
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"next-action-handlers-BZ5gynXe.mjs","names":[],"sources":["../src/utils/3ds-iframe-modal.ts","../src/payment-methods/next-action-handlers.ts"],"sourcesContent":["/**\n * 3DS iframe modal for displaying authentication pages.\n *\n * This module provides an iframe modal used by payment processors\n * that need to display a 3DS authentication page.\n */\n\ntype IframeModalResult = { success: true; data: Record<string, unknown> } | { success: false; error: string };\n\ntype ThreeDSCompleteMessage = {\n type: \"3ds_complete\";\n processor: string | null;\n isSuccess: boolean;\n error?: string;\n};\n\ntype IframeModalMessageHandler = (\n event: MessageEvent<ThreeDSCompleteMessage>,\n) => { handled: true; result: IframeModalResult } | { handled: false };\n\ninterface IframeModalOptions {\n /** URL to load in the iframe */\n url: string;\n /** Size of the iframe in pixels */\n size: { width: number; height: number };\n /** Border radius for the iframe in pixels (default: 16) */\n radius?: number;\n /** Handler to process postMessage events from the iframe */\n onMessage: IframeModalMessageHandler;\n}\n\n/**\n * Shows an iframe modal and waits for a result via postMessage.\n *\n * Features:\n * - Close/cancel button positioned outside the iframe\n * - Responsive design (works on mobile)\n * - Keyboard accessibility (Escape to close, focus trap)\n * - Fade-in/fade-out animations\n * - Iframe load error handling\n *\n * @param options - Modal configuration options\n * @returns Promise that resolves with success/failure result\n *\n * @example\n * ```ts\n * const result = await show3DSIframeModal({\n * url: \"https://bank.com/3ds-auth\",\n * size: { width: 605, height: 500 },\n * radius: 6,\n * onMessage: (event) => {\n * if (event.data?.type === \"auth_complete\") {\n * return {\n * handled: true,\n * result: event.data.succeeded\n * ? { success: true, data: { id: event.data.id } }\n * : { success: false, error: event.data.error }\n * };\n * }\n * return { handled: false };\n * },\n * });\n * ```\n */\n\nexport default function show3DSIframeModal(options: IframeModalOptions): Promise<IframeModalResult> {\n const { url, size, radius = 6, onMessage } = options;\n\n return new Promise((resolve) => {\n let resolved = false;\n\n // Create modal overlay with fade-in animation\n const overlay = document.createElement(\"div\");\n overlay.id = \"pk-iframe-modal-overlay\";\n overlay.setAttribute(\"role\", \"dialog\");\n overlay.setAttribute(\"aria-modal\", \"true\");\n overlay.setAttribute(\"aria-label\", \"3D Secure Verification\");\n overlay.style.cssText = `\n position: fixed;\n top: 0;\n left: 0;\n width: 100%;\n height: 100%;\n background: rgba(0, 0, 0, 0);\n display: flex;\n align-items: center;\n justify-content: center;\n z-index: 10000;\n transition: background 0.25s cubic-bezier(0.4, 0, 0.2, 1);\n `;\n\n // Create container for iframe and close button\n const container = document.createElement(\"div\");\n container.style.cssText = `\n position: relative;\n width: ${size.width}px;\n height: ${size.height}px;\n max-width: calc(100vw - 32px);\n max-height: calc(100vh - 64px);\n overflow: visible;\n opacity: 0;\n transform: scale(0.95) translateY(10px);\n transition: opacity 0.25s cubic-bezier(0.4, 0, 0.2, 1), transform 0.25s cubic-bezier(0.4, 0, 0.2, 1);\n `;\n\n // Create close button (positioned inside top-right of container)\n // Uses !important to prevent merchant CSS from hiding/repositioning the button\n const closeButton = document.createElement(\"button\");\n closeButton.id = \"pk-iframe-modal-close\";\n closeButton.setAttribute(\"aria-label\", \"Cancel verification\");\n closeButton.setAttribute(\"type\", \"button\");\n closeButton.style.cssText = `\n position: absolute !important;\n top: 8px !important;\n right: 8px !important;\n width: 32px !important;\n height: 32px !important;\n border: none !important;\n background: white !important;\n border-radius: 8px !important;\n cursor: pointer !important;\n display: flex !important;\n align-items: center !important;\n justify-content: center !important;\n z-index: 10 !important;\n transition: background 0.15s ease !important;\n box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15) !important;\n `;\n closeButton.innerHTML = `\n <svg width=\"12\" height=\"12\" viewBox=\"0 0 14 14\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\">\n <path d=\"M1 1L13 13M1 13L13 1\" stroke=\"#666\" stroke-width=\"2\" stroke-linecap=\"round\"/>\n </svg>\n `;\n closeButton.onmouseenter = (): void => {\n closeButton.style.setProperty(\"background\", \"#f5f5f5\", \"important\");\n };\n closeButton.onmouseleave = (): void => {\n closeButton.style.setProperty(\"background\", \"white\", \"important\");\n };\n\n // Create iframe (hidden initially until loaded)\n const iframe = document.createElement(\"iframe\");\n iframe.src = url;\n iframe.setAttribute(\"title\", \"3D Secure Verification\");\n iframe.style.cssText = `\n width: 100%;\n height: 100%;\n border: none;\n opacity: 0;\n border-radius: ${radius}px;\n background: white;\n transition: opacity 0.2s ease;\n position: relative;\n z-index: 1;\n `;\n\n // Handle iframe load success\n iframe.onload = (): void => {\n iframe.style.opacity = \"1\";\n };\n\n // Handle iframe load error\n iframe.onerror = (): void => {\n if (!resolved) {\n console.error(\"[IframeModal] Failed to load iframe\");\n cleanupWithAnimation();\n resolve({ success: false, error: \"Failed to load verification page. Please try again.\" });\n }\n };\n\n container.appendChild(closeButton);\n container.appendChild(iframe);\n overlay.appendChild(container);\n document.body.appendChild(overlay);\n\n // Trigger fade-in animation\n requestAnimationFrame(() => {\n overlay.style.background = \"rgba(0, 0, 0, 0.5)\";\n container.style.opacity = \"1\";\n container.style.transform = \"scale(1) translateY(0)\";\n });\n\n // Cleanup with fade-out animation\n const cleanupWithAnimation = (): void => {\n if (resolved) return;\n resolved = true;\n\n window.removeEventListener(\"message\", handleMessage);\n window.removeEventListener(\"keydown\", handleKeydown);\n\n // Use requestAnimationFrame to ensure transition is applied\n requestAnimationFrame(() => {\n overlay.style.background = \"rgba(0, 0, 0, 0)\";\n container.style.opacity = \"0\";\n container.style.transform = \"scale(0.95) translateY(10px)\";\n\n setTimeout(() => {\n if (overlay.parentNode) {\n overlay.parentNode.removeChild(overlay);\n }\n }, 250);\n });\n };\n\n // Handle close button click\n const handleCancel = (): void => {\n if (!resolved) {\n console.log(\"[IframeModal] User cancelled\");\n cleanupWithAnimation();\n resolve({ success: false, error: \"Verification cancelled\" });\n }\n };\n\n closeButton.onclick = handleCancel;\n\n // Handle Escape key\n const handleKeydown = (event: KeyboardEvent): void => {\n if (event.key === \"Escape\") {\n handleCancel();\n }\n // Trap focus within modal (Tab key cycles through close button and iframe)\n if (event.key === \"Tab\") {\n event.preventDefault();\n if (document.activeElement === closeButton) {\n iframe.focus();\n } else {\n closeButton.focus();\n }\n }\n };\n\n window.addEventListener(\"keydown\", handleKeydown);\n\n // Set initial focus to close button for accessibility\n closeButton.focus();\n\n const handleMessage = (event: MessageEvent): void => {\n const messageResult = onMessage(event);\n if (messageResult.handled) {\n cleanupWithAnimation();\n resolve(messageResult.result);\n }\n };\n\n window.addEventListener(\"message\", handleMessage);\n });\n}\n","/**\n * Next action handlers for processor-specific user actions (e.g., 3DS authentication).\n *\n * This module abstracts processor-specific logic away from the generic card payment flow.\n * Each handler implements the logic needed for a specific next action type.\n */\n\nimport type { PublicCardCheckoutResponse } from \"@pkg/sdk/models\";\nimport type { Stripe } from \"@stripe/stripe-js\";\nimport show3DSIframeModal from \"../utils/3ds-iframe-modal\";\n\n// Helper to wait for a condition\nconst sleep = (ms: number): Promise<void> => new Promise((resolve) => setTimeout(resolve, ms));\n\n// Check if Stripe.js is loaded via script tag\nconst isStripeJsPresent = (): boolean => \"Stripe\" in window;\n\n/**\n * Get the Stripe instance from the global window object.\n * Expects Stripe.js to be loaded via HTML script tag.\n * Polls for up to 5 seconds waiting for Stripe to be available.\n */\nconst getLoadedStripe = async (publishableKey: string): Promise<Stripe> => {\n // Poll for Stripe.js to be loaded (up to 5 seconds)\n for (let i = 0; i < 10; i++) {\n if (isStripeJsPresent()) {\n break;\n }\n await sleep(500);\n }\n\n if (!isStripeJsPresent()) {\n throw new Error(\n \"Stripe.js not loaded. Add this script tag to your HTML <head>:\\n\" +\n '<script src=\"https://js.stripe.com/v3/\"></script>',\n );\n }\n\n // @ts-expect-error Stripe is loaded globally via script tag\n const stripe: Stripe = new window.Stripe(publishableKey);\n return stripe;\n};\n\n/**\n * Result of handling a next action.\n */\nexport type NextActionResult = { success: true } | { success: false; error: string };\n\n/**\n * Handle Stripe 3DS authentication.\n */\nconst handleStripe3ds = async (\n nextAction: Extract<PublicCardCheckoutResponse[\"nextAction\"], { type: \"stripe_3ds\" }>,\n): Promise<NextActionResult> => {\n const { clientSecret, stripePk } = nextAction;\n\n const stripe = await getLoadedStripe(stripePk);\n\n // Show 3DS modal\n const { error: stripeError } = await stripe.confirmCardPayment(clientSecret);\n\n if (stripeError) {\n console.error(\"[3DS:Stripe] Authentication failed:\", stripeError.message);\n return { success: false, error: stripeError.message || \"3DS authentication failed\" };\n }\n\n return { success: true };\n};\n\n/**\n * Handle Airwallex 3DS authentication via iframe.\n *\n * Renders an iframe with the 3DS authentication URL. The iframe will redirect\n * to the 3DS callback page which sends a postMessage back to complete the flow.\n */\nconst handleAirwallex3ds = async (\n nextAction: Extract<PublicCardCheckoutResponse[\"nextAction\"], { type: \"airwallex_3ds\" }>,\n): Promise<NextActionResult> => {\n return show3DSIframeModal({\n url: nextAction.url,\n size: { width: 605, height: 550 },\n onMessage: (event) => {\n if (event.data.type !== \"3ds_complete\") {\n return { handled: false };\n }\n\n const { isSuccess, error } = event.data;\n\n if (isSuccess) {\n return { handled: true, result: { success: true, data: {} } };\n } else {\n console.error(\"[3DS:Airwallex] Authentication failed:\", error);\n return {\n handled: true,\n result: { success: false, error: error || \"3DS authentication failed\" },\n };\n }\n },\n });\n};\n\n/**\n * Handle a next action based on its type.\n * Routes to the appropriate processor-specific handler.\n *\n * @param nextAction - The next action from the checkout response\n * @returns Result indicating success or failure with error message\n */\nexport const handleNextAction = async (\n nextAction: NonNullable<PublicCardCheckoutResponse[\"nextAction\"]>,\n): Promise<NextActionResult> => {\n switch (nextAction.type) {\n case \"stripe_3ds\":\n return handleStripe3ds(nextAction);\n case \"airwallex_3ds\":\n return handleAirwallex3ds(nextAction);\n default:\n // TypeScript will catch if we miss a case when new action types are added\n return { success: false, error: `Unknown next action type: ${(nextAction as { type: string }).type}` };\n }\n};\n"],"mappings":"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"}
|