orderopia-ordering-api-client-vue 0.0.5 → 0.0.7
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/index.cjs +25 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +25 -1
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -218,11 +218,33 @@ function run3DSecure(args) {
|
|
|
218
218
|
onOpened,
|
|
219
219
|
onClosed
|
|
220
220
|
} = args;
|
|
221
|
-
|
|
221
|
+
console.log("[3DS] run3DSecure called", {
|
|
222
|
+
responseType,
|
|
223
|
+
acsUrl,
|
|
224
|
+
transactionId,
|
|
225
|
+
redirectUriProvided: !!redirectUri,
|
|
226
|
+
redirectUriFinal: redirectUri ?? getChallengeRedirectUri(),
|
|
227
|
+
cReqType: typeof cReq,
|
|
228
|
+
cReqPreview: typeof cReq === "string" ? cReq.slice(0, 80) : cReq && typeof cReq === "object" ? JSON.stringify(cReq).slice(0, 120) : cReq
|
|
229
|
+
});
|
|
230
|
+
onOpayoCancelled = () => {
|
|
231
|
+
console.log("[3DS] onOpayoCancelled fired");
|
|
232
|
+
try {
|
|
233
|
+
onCancelled?.();
|
|
234
|
+
} catch {
|
|
235
|
+
}
|
|
236
|
+
};
|
|
222
237
|
return new Promise((resolve) => {
|
|
223
238
|
const handleMessage = (event) => {
|
|
224
239
|
const msg = String(event.data || "");
|
|
240
|
+
console.log("[3DS] message received", {
|
|
241
|
+
msg,
|
|
242
|
+
origin: event.origin,
|
|
243
|
+
sourceIsWindow: event.source === window,
|
|
244
|
+
sourceIsTop: event.source === window.top
|
|
245
|
+
});
|
|
225
246
|
if (msg === "OPAYO_CALLBACK_SUCCESS") {
|
|
247
|
+
console.log("[3DS] SUCCESS -> closing + resolve(true)");
|
|
226
248
|
closeOpayoIframe();
|
|
227
249
|
window.removeEventListener("message", handleMessage);
|
|
228
250
|
try {
|
|
@@ -231,6 +253,7 @@ function run3DSecure(args) {
|
|
|
231
253
|
}
|
|
232
254
|
resolve(true);
|
|
233
255
|
} else if (msg === "OPAYO_CALLBACK_FAILED") {
|
|
256
|
+
console.log("[3DS] FAILED -> closing + resolve(false)");
|
|
234
257
|
closeOpayoIframe();
|
|
235
258
|
window.removeEventListener("message", handleMessage);
|
|
236
259
|
try {
|
|
@@ -246,6 +269,7 @@ function run3DSecure(args) {
|
|
|
246
269
|
} catch {
|
|
247
270
|
}
|
|
248
271
|
const termUrl = redirectUri ?? getChallengeRedirectUri();
|
|
272
|
+
console.log("[3DS] initializing iframe", { termUrl });
|
|
249
273
|
initializeIframe(responseType, acsUrl, pReq, cReq, termUrl, transactionId);
|
|
250
274
|
});
|
|
251
275
|
}
|
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/composables/useBasket.ts","../src/composables/useOpayo3ds.ts","../src/opayo/opayo3dsManager.ts","../src/install.ts"],"sourcesContent":["// src/index.ts\r\nexport { useBasket } from './composables/useBasket'\r\nexport { useOpayo3ds } from './composables/useOpayo3ds'\r\nexport { receiveOpayoResponse } from './opayo/opayo3dsManager'\r\nexport { OrderopiaVuePlugin } from './install'\r\n","// src/composables/useBasket.ts\r\nimport { ref, computed, onScopeDispose } from 'vue'\r\nimport {\r\n basketManager,\r\n type BasketState\r\n} from 'orderopia-ordering-api-client'\r\n\r\nexport function useBasket() {\r\n // Start with current basket immediately\r\n const basket = ref<BasketState>(basketManager.getBasket())\r\n\r\n // Subscribe immediately (NOT in onMounted)\r\n const unsubscribe = basketManager.subscribe(state => {\r\n basket.value = state\r\n })\r\n\r\n // Auto-cleanup when the component/composable scope is destroyed\r\n onScopeDispose(() => {\r\n unsubscribe()\r\n })\r\n\r\n const totalQuantity = computed(() =>\r\n basket.value.items.reduce((sum, i) => sum + (i.quantity ?? 0), 0)\r\n )\r\n\r\n return {\r\n basket,\r\n totalQuantity,\r\n\r\n // pass-through helpers (nice DX)\r\n addToBasket: basketManager.addToBasket.bind(basketManager),\r\n removeFromBasket: basketManager.removeFromBasket.bind(basketManager),\r\n clearBasket: basketManager.clearBasket.bind(basketManager)\r\n }\r\n}\r\n","// src/composables/useOpayo3ds.ts\r\nimport { ref, onScopeDispose } from 'vue'\r\nimport { run3DSecure, closeOpayoIframe, type Run3DSecureArgs } from '../opayo/opayo3dsManager'\r\n\r\nexport function useOpayo3ds() {\r\n const isOpen = ref(false)\r\n const isRunning = ref(false)\r\n\r\n let disposed = false\r\n\r\n onScopeDispose(() => {\r\n disposed = true\r\n isOpen.value = false\r\n isRunning.value = false\r\n closeOpayoIframe()\r\n })\r\n\r\n async function run(args: Run3DSecureArgs) {\r\n if (disposed) return false\r\n\r\n isOpen.value = true\r\n isRunning.value = true\r\n\r\n try {\r\n const result = await run3DSecure({\r\n ...args,\r\n onOpened: () => {\r\n if (disposed) return\r\n isOpen.value = true\r\n args.onOpened?.()\r\n },\r\n onClosed: () => {\r\n if (disposed) return\r\n isOpen.value = false\r\n args.onClosed?.()\r\n },\r\n onCancelled: () => {\r\n if (disposed) return\r\n isOpen.value = false\r\n args.onCancelled?.()\r\n }\r\n })\r\n\r\n return result\r\n } finally {\r\n if (!disposed) {\r\n isRunning.value = false\r\n }\r\n }\r\n }\r\n\r\n function close() {\r\n isOpen.value = false\r\n isRunning.value = false\r\n closeOpayoIframe()\r\n }\r\n\r\n return {\r\n isOpen,\r\n isRunning,\r\n run,\r\n close\r\n }\r\n}\r\n","// src/opayo/opayo3dsManager.ts\r\n\r\ntype ResponseType = 'CHALLENGE' | 'FALLBACK'\r\n\r\nfunction isBrowser() {\r\n return typeof window !== 'undefined' && typeof document !== 'undefined'\r\n}\r\n\r\nfunction ensureThreedsContainer() {\r\n if (!isBrowser()) return null\r\n\r\n let el = document.getElementById('threeds-container') as HTMLDivElement | null\r\n if (!el) {\r\n el = document.createElement('div')\r\n el.id = 'threeds-container'\r\n el.style.position = 'fixed'\r\n el.style.inset = '0'\r\n el.style.display = 'none'\r\n el.style.background = 'rgba(0,0,0,0.35)'\r\n el.style.zIndex = '99999'\r\n document.body.appendChild(el)\r\n }\r\n return el\r\n}\r\n\r\n// UTF-8 -> Base64URL (no padding)\r\nfunction utf8ToBase64Url(str: string) {\r\n const bytes = new TextEncoder().encode(str)\r\n let bin = ''\r\n for (let i = 0; i < bytes.length; i++) bin += String.fromCharCode(bytes[i])\r\n const b64 = btoa(bin)\r\n return b64.replace(/\\+/g, '-').replace(/\\//g, '_').replace(/=+$/g, '')\r\n}\r\n\r\n// Base64URL -> Base64 (and add padding)\r\nfunction toStdBase64(b64url: string) {\r\n let s = b64url.replace(/-/g, '+').replace(/_/g, '/')\r\n const pad = s.length % 4\r\n if (pad) s += '==='.slice(pad - 1)\r\n return s\r\n}\r\n\r\nfunction safeAtobMaybeJson(b64ish: string) {\r\n try {\r\n return atob(toStdBase64(b64ish))\r\n } catch {\r\n return null\r\n }\r\n}\r\n\r\n// Normalize creq to Base64URL without padding.\r\nfunction normalizeCreq(input: unknown) {\r\n if (input == null) return ''\r\n\r\n // Object -> JSON\r\n if (typeof input === 'object') {\r\n try {\r\n return utf8ToBase64Url(JSON.stringify(input))\r\n } catch {\r\n // ignore\r\n }\r\n }\r\n\r\n let creq = String(input).trim()\r\n\r\n // If obviously JSON text\r\n if (/^\\s*\\{[\\s\\S]*\\}\\s*$/.test(creq)) {\r\n return utf8ToBase64Url(creq)\r\n }\r\n\r\n // If it looks URL-encoded, attempt a decode\r\n if (/%[0-9A-Fa-f]{2}/.test(creq) || creq.includes('%')) {\r\n try {\r\n creq = decodeURIComponent(creq)\r\n } catch {\r\n // ignore\r\n }\r\n }\r\n\r\n // Remove whitespace, convert spaces to '+'\r\n creq = creq.replace(/\\s+/g, '').replace(/ /g, '+')\r\n\r\n // If decodes to JSON, re-encode properly as UTF-8 base64url\r\n const maybe = safeAtobMaybeJson(creq)\r\n if (maybe && maybe.charCodeAt(0) === 123 /* '{' */) {\r\n return utf8ToBase64Url(maybe)\r\n }\r\n\r\n // Otherwise normalize to base64url and strip padding\r\n creq = creq.replace(/\\+/g, '-').replace(/\\//g, '_').replace(/=+$/g, '')\r\n return creq\r\n}\r\n\r\nfunction hiddenInput(name: string, value: unknown) {\r\n const inp = document.createElement('input')\r\n inp.type = 'hidden'\r\n inp.name = name\r\n inp.value = value == null ? '' : String(value)\r\n return inp\r\n}\r\n\r\n/**\r\n * ✅ Pull env from the core client (prod/dev toggle you built)\r\n * NOTE: Adjust the import path to whatever you export publicly from the client package.\r\n */\r\nimport { isOrderopiaDevMode } from 'orderopia-ordering-api-client'\r\n\r\nfunction getChallengeRedirectUri(): string {\r\n // TEMP values for now (your ask) — but environment-aware.\r\n // Replace these when you confirm the final endpoint paths.\r\n return isOrderopiaDevMode()\r\n ? 'https://api-test.orderopia.com/Transactions/3ds/Challenge'\r\n : 'https://api.orderopia.com/Transactions/3ds/Challenge'\r\n}\r\n\r\nexport type Run3DSecureArgs = {\r\n responseType: ResponseType\r\n acsUrl: string\r\n pReq?: string | null\r\n cReq?: unknown\r\n redirectUri?: string | null\r\n transactionId?: string | null\r\n\r\n onCancelled?: () => void\r\n onOpened?: () => void\r\n onClosed?: () => void\r\n}\r\n\r\nlet onOpayoCancelled: (() => void) | undefined\r\n\r\nfunction initializeIframe(\r\n responseType: ResponseType,\r\n acsUrl: string,\r\n pReq?: string | null,\r\n cReq?: unknown,\r\n redirectUri?: string | null,\r\n transactionId?: string | null\r\n) {\r\n if (!isBrowser()) return\r\n\r\n const container = ensureThreedsContainer()\r\n if (!container) return\r\n\r\n container.style.display = 'block'\r\n container.textContent = ''\r\n\r\n const panel = document.createElement('div')\r\n panel.style.position = 'absolute'\r\n panel.style.top = '50%'\r\n panel.style.left = '50%'\r\n panel.style.transform = 'translate(-50%, -50%)'\r\n panel.style.width = 'min(100%, 500px)'\r\n panel.style.height = 'min(100%, 600px)'\r\n panel.style.background = 'var(--background, #fff)'\r\n panel.style.borderRadius = '10px'\r\n panel.style.boxShadow = '0 10px 30px rgba(0,0,0,0.2)'\r\n panel.style.display = 'flex'\r\n panel.style.flexDirection = 'column'\r\n panel.style.overflow = 'hidden'\r\n container.appendChild(panel)\r\n\r\n const msg = document.createElement('div')\r\n msg.style.padding = '16px'\r\n msg.style.fontFamily = 'sans-serif'\r\n msg.style.textAlign = 'center'\r\n msg.innerHTML =\r\n '<div style=\"font-weight:600;\">We’re redirecting you to your bank to complete the payment.</div>' +\r\n '<div style=\"margin-top:6px;\">This will only take a moment. Please <u>do not</u> refresh the page.</div>'\r\n panel.appendChild(msg)\r\n\r\n const iframeWrap = document.createElement('div')\r\n iframeWrap.style.flex = '1'\r\n iframeWrap.style.borderTop = '1px solid rgba(0,0,0,0.06)'\r\n panel.appendChild(iframeWrap)\r\n\r\n const iframeName = 'threeds_frame_' + Math.random().toString(36).slice(2)\r\n const iframe = document.createElement('iframe')\r\n iframe.name = iframeName\r\n iframe.title = '3-D Secure Authentication'\r\n iframe.style.width = '100%'\r\n iframe.style.height = '100%'\r\n iframe.style.border = '0'\r\n iframeWrap.appendChild(iframe)\r\n\r\n const form = document.createElement('form')\r\n form.style.display = 'none'\r\n form.method = 'POST'\r\n form.action = acsUrl\r\n form.target = iframeName\r\n document.body.appendChild(form)\r\n\r\n if (responseType === 'CHALLENGE') {\r\n // 3DS v2\r\n form.appendChild(hiddenInput('creq', normalizeCreq(cReq)))\r\n if (transactionId) {\r\n // Optional echo field (send as-is)\r\n form.appendChild(hiddenInput('threeDSSessionData', transactionId))\r\n }\r\n } else {\r\n // 3DS v1 fallback\r\n const termUrl = redirectUri || ''\r\n form.appendChild(hiddenInput('PaReq', pReq || ''))\r\n form.appendChild(hiddenInput('TermUrl', termUrl))\r\n form.appendChild(hiddenInput('MD', transactionId || ''))\r\n }\r\n\r\n form.submit()\r\n\r\n setTimeout(() => {\r\n try {\r\n document.body.removeChild(form)\r\n } catch {\r\n // ignore\r\n }\r\n }, 1000000)\r\n}\r\n\r\nexport function closeOpayoIframe() {\r\n if (!isBrowser()) return\r\n const container = document.getElementById('threeds-container') as HTMLDivElement | null\r\n if (!container) return\r\n\r\n container.innerHTML = ''\r\n container.style.display = 'none'\r\n\r\n try {\r\n onOpayoCancelled?.()\r\n } catch {\r\n // ignore\r\n }\r\n}\r\n\r\nexport function run3DSecure(args: Run3DSecureArgs) {\r\n if (!isBrowser()) return Promise.resolve(false)\r\n\r\n const {\r\n responseType,\r\n acsUrl,\r\n pReq,\r\n cReq,\r\n redirectUri,\r\n transactionId,\r\n onCancelled,\r\n onOpened,\r\n onClosed\r\n } = args\r\n\r\n onOpayoCancelled = onCancelled\r\n\r\n return new Promise<boolean>((resolve) => {\r\n const handleMessage = (event: MessageEvent) => {\r\n const msg = String((event as any).data || '')\r\n\r\n if (msg === 'OPAYO_CALLBACK_SUCCESS') {\r\n closeOpayoIframe()\r\n window.removeEventListener('message', handleMessage)\r\n try {\r\n onClosed?.()\r\n } catch {\r\n // ignore\r\n }\r\n resolve(true)\r\n } else if (msg === 'OPAYO_CALLBACK_FAILED') {\r\n closeOpayoIframe()\r\n window.removeEventListener('message', handleMessage)\r\n try {\r\n onClosed?.()\r\n } catch {\r\n // ignore\r\n }\r\n resolve(false)\r\n }\r\n }\r\n\r\n window.addEventListener('message', handleMessage)\r\n\r\n try {\r\n onOpened?.()\r\n } catch {\r\n // ignore\r\n }\r\n\r\n const termUrl = redirectUri ?? getChallengeRedirectUri()\r\n\r\n initializeIframe(responseType, acsUrl, pReq, cReq, termUrl, transactionId)\r\n })\r\n}\r\n\r\n// Call this from your callback/return page.\r\n// It posts a signal back to the parent checkout window.\r\nexport function receiveOpayoResponse(message: unknown) {\r\n if (!isBrowser()) return\r\n\r\n const m = String(message || '')\r\n if (m === 'OPAYO_CALLBACK_FAILED' || m === 'OPAYO_CALLBACK_SUCCESS') {\r\n try {\r\n window.top?.postMessage(m, '*')\r\n } catch {\r\n // ignore\r\n }\r\n\r\n try {\r\n onOpayoCancelled?.()\r\n } catch {\r\n // ignore\r\n }\r\n }\r\n}\r\n","// src/install.ts\r\nimport type { App } from 'vue'\r\n\r\nexport function OrderopiaVuePlugin() {\r\n return {\r\n install(app: App) {\r\n // nothing yet, but future-proof\r\n }\r\n }\r\n}\r\n"],"mappings":"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","names":["import_vue","import_orderopia_ordering_api_client"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/composables/useBasket.ts","../src/composables/useOpayo3ds.ts","../src/opayo/opayo3dsManager.ts","../src/install.ts"],"sourcesContent":["// src/index.ts\r\nexport { useBasket } from './composables/useBasket'\r\nexport { useOpayo3ds } from './composables/useOpayo3ds'\r\nexport { receiveOpayoResponse } from './opayo/opayo3dsManager'\r\nexport { OrderopiaVuePlugin } from './install'\r\n","// src/composables/useBasket.ts\r\nimport { ref, computed, onScopeDispose } from 'vue'\r\nimport {\r\n basketManager,\r\n type BasketState\r\n} from 'orderopia-ordering-api-client'\r\n\r\nexport function useBasket() {\r\n // Start with current basket immediately\r\n const basket = ref<BasketState>(basketManager.getBasket())\r\n\r\n // Subscribe immediately (NOT in onMounted)\r\n const unsubscribe = basketManager.subscribe(state => {\r\n basket.value = state\r\n })\r\n\r\n // Auto-cleanup when the component/composable scope is destroyed\r\n onScopeDispose(() => {\r\n unsubscribe()\r\n })\r\n\r\n const totalQuantity = computed(() =>\r\n basket.value.items.reduce((sum, i) => sum + (i.quantity ?? 0), 0)\r\n )\r\n\r\n return {\r\n basket,\r\n totalQuantity,\r\n\r\n // pass-through helpers (nice DX)\r\n addToBasket: basketManager.addToBasket.bind(basketManager),\r\n removeFromBasket: basketManager.removeFromBasket.bind(basketManager),\r\n clearBasket: basketManager.clearBasket.bind(basketManager)\r\n }\r\n}\r\n","// src/composables/useOpayo3ds.ts\r\nimport { ref, onScopeDispose } from 'vue'\r\nimport { run3DSecure, closeOpayoIframe, type Run3DSecureArgs } from '../opayo/opayo3dsManager'\r\n\r\nexport function useOpayo3ds() {\r\n const isOpen = ref(false)\r\n const isRunning = ref(false)\r\n\r\n let disposed = false\r\n\r\n onScopeDispose(() => {\r\n disposed = true\r\n isOpen.value = false\r\n isRunning.value = false\r\n closeOpayoIframe()\r\n })\r\n\r\n async function run(args: Run3DSecureArgs) {\r\n if (disposed) return false\r\n\r\n isOpen.value = true\r\n isRunning.value = true\r\n\r\n try {\r\n const result = await run3DSecure({\r\n ...args,\r\n onOpened: () => {\r\n if (disposed) return\r\n isOpen.value = true\r\n args.onOpened?.()\r\n },\r\n onClosed: () => {\r\n if (disposed) return\r\n isOpen.value = false\r\n args.onClosed?.()\r\n },\r\n onCancelled: () => {\r\n if (disposed) return\r\n isOpen.value = false\r\n args.onCancelled?.()\r\n }\r\n })\r\n\r\n return result\r\n } finally {\r\n if (!disposed) {\r\n isRunning.value = false\r\n }\r\n }\r\n }\r\n\r\n function close() {\r\n isOpen.value = false\r\n isRunning.value = false\r\n closeOpayoIframe()\r\n }\r\n\r\n return {\r\n isOpen,\r\n isRunning,\r\n run,\r\n close\r\n }\r\n}\r\n","// src/opayo/opayo3dsManager.ts\r\n\r\ntype ResponseType = 'CHALLENGE' | 'FALLBACK'\r\n\r\nfunction isBrowser() {\r\n return typeof window !== 'undefined' && typeof document !== 'undefined'\r\n}\r\n\r\nfunction ensureThreedsContainer() {\r\n if (!isBrowser()) return null\r\n\r\n let el = document.getElementById('threeds-container') as HTMLDivElement | null\r\n if (!el) {\r\n el = document.createElement('div')\r\n el.id = 'threeds-container'\r\n el.style.position = 'fixed'\r\n el.style.inset = '0'\r\n el.style.display = 'none'\r\n el.style.background = 'rgba(0,0,0,0.35)'\r\n el.style.zIndex = '99999'\r\n document.body.appendChild(el)\r\n }\r\n return el\r\n}\r\n\r\n// UTF-8 -> Base64URL (no padding)\r\nfunction utf8ToBase64Url(str: string) {\r\n const bytes = new TextEncoder().encode(str)\r\n let bin = ''\r\n for (let i = 0; i < bytes.length; i++) bin += String.fromCharCode(bytes[i])\r\n const b64 = btoa(bin)\r\n return b64.replace(/\\+/g, '-').replace(/\\//g, '_').replace(/=+$/g, '')\r\n}\r\n\r\n// Base64URL -> Base64 (and add padding)\r\nfunction toStdBase64(b64url: string) {\r\n let s = b64url.replace(/-/g, '+').replace(/_/g, '/')\r\n const pad = s.length % 4\r\n if (pad) s += '==='.slice(pad - 1)\r\n return s\r\n}\r\n\r\nfunction safeAtobMaybeJson(b64ish: string) {\r\n try {\r\n return atob(toStdBase64(b64ish))\r\n } catch {\r\n return null\r\n }\r\n}\r\n\r\n// Normalize creq to Base64URL without padding.\r\nfunction normalizeCreq(input: unknown) {\r\n if (input == null) return ''\r\n\r\n // Object -> JSON\r\n if (typeof input === 'object') {\r\n try {\r\n return utf8ToBase64Url(JSON.stringify(input))\r\n } catch {\r\n // ignore\r\n }\r\n }\r\n\r\n let creq = String(input).trim()\r\n\r\n // If obviously JSON text\r\n if (/^\\s*\\{[\\s\\S]*\\}\\s*$/.test(creq)) {\r\n return utf8ToBase64Url(creq)\r\n }\r\n\r\n // If it looks URL-encoded, attempt a decode\r\n if (/%[0-9A-Fa-f]{2}/.test(creq) || creq.includes('%')) {\r\n try {\r\n creq = decodeURIComponent(creq)\r\n } catch {\r\n // ignore\r\n }\r\n }\r\n\r\n // Remove whitespace, convert spaces to '+'\r\n creq = creq.replace(/\\s+/g, '').replace(/ /g, '+')\r\n\r\n // If decodes to JSON, re-encode properly as UTF-8 base64url\r\n const maybe = safeAtobMaybeJson(creq)\r\n if (maybe && maybe.charCodeAt(0) === 123 /* '{' */) {\r\n return utf8ToBase64Url(maybe)\r\n }\r\n\r\n // Otherwise normalize to base64url and strip padding\r\n creq = creq.replace(/\\+/g, '-').replace(/\\//g, '_').replace(/=+$/g, '')\r\n return creq\r\n}\r\n\r\nfunction hiddenInput(name: string, value: unknown) {\r\n const inp = document.createElement('input')\r\n inp.type = 'hidden'\r\n inp.name = name\r\n inp.value = value == null ? '' : String(value)\r\n return inp\r\n}\r\n\r\n/**\r\n * ✅ Pull env from the core client (prod/dev toggle you built)\r\n * NOTE: Adjust the import path to whatever you export publicly from the client package.\r\n */\r\nimport { isOrderopiaDevMode } from 'orderopia-ordering-api-client'\r\n\r\nfunction getChallengeRedirectUri(): string {\r\n // TEMP values for now (your ask) — but environment-aware.\r\n // Replace these when you confirm the final endpoint paths.\r\n return isOrderopiaDevMode()\r\n ? 'https://api-test.orderopia.com/Transactions/3ds/Challenge'\r\n : 'https://api.orderopia.com/Transactions/3ds/Challenge'\r\n}\r\n\r\nexport type Run3DSecureArgs = {\r\n responseType: ResponseType\r\n acsUrl: string\r\n pReq?: string | null\r\n cReq?: unknown\r\n redirectUri?: string | null\r\n transactionId?: string | null\r\n\r\n onCancelled?: () => void\r\n onOpened?: () => void\r\n onClosed?: () => void\r\n}\r\n\r\nlet onOpayoCancelled: (() => void) | undefined\r\n\r\nfunction initializeIframe(\r\n responseType: ResponseType,\r\n acsUrl: string,\r\n pReq?: string | null,\r\n cReq?: unknown,\r\n redirectUri?: string | null,\r\n transactionId?: string | null\r\n) {\r\n if (!isBrowser()) return\r\n\r\n const container = ensureThreedsContainer()\r\n if (!container) return\r\n\r\n container.style.display = 'block'\r\n container.textContent = ''\r\n\r\n const panel = document.createElement('div')\r\n panel.style.position = 'absolute'\r\n panel.style.top = '50%'\r\n panel.style.left = '50%'\r\n panel.style.transform = 'translate(-50%, -50%)'\r\n panel.style.width = 'min(100%, 500px)'\r\n panel.style.height = 'min(100%, 600px)'\r\n panel.style.background = 'var(--background, #fff)'\r\n panel.style.borderRadius = '10px'\r\n panel.style.boxShadow = '0 10px 30px rgba(0,0,0,0.2)'\r\n panel.style.display = 'flex'\r\n panel.style.flexDirection = 'column'\r\n panel.style.overflow = 'hidden'\r\n container.appendChild(panel)\r\n\r\n const msg = document.createElement('div')\r\n msg.style.padding = '16px'\r\n msg.style.fontFamily = 'sans-serif'\r\n msg.style.textAlign = 'center'\r\n msg.innerHTML =\r\n '<div style=\"font-weight:600;\">We’re redirecting you to your bank to complete the payment.</div>' +\r\n '<div style=\"margin-top:6px;\">This will only take a moment. Please <u>do not</u> refresh the page.</div>'\r\n panel.appendChild(msg)\r\n\r\n const iframeWrap = document.createElement('div')\r\n iframeWrap.style.flex = '1'\r\n iframeWrap.style.borderTop = '1px solid rgba(0,0,0,0.06)'\r\n panel.appendChild(iframeWrap)\r\n\r\n const iframeName = 'threeds_frame_' + Math.random().toString(36).slice(2)\r\n const iframe = document.createElement('iframe')\r\n iframe.name = iframeName\r\n iframe.title = '3-D Secure Authentication'\r\n iframe.style.width = '100%'\r\n iframe.style.height = '100%'\r\n iframe.style.border = '0'\r\n iframeWrap.appendChild(iframe)\r\n\r\n const form = document.createElement('form')\r\n form.style.display = 'none'\r\n form.method = 'POST'\r\n form.action = acsUrl\r\n form.target = iframeName\r\n document.body.appendChild(form)\r\n\r\n if (responseType === 'CHALLENGE') {\r\n // 3DS v2\r\n form.appendChild(hiddenInput('creq', normalizeCreq(cReq)))\r\n if (transactionId) {\r\n // Optional echo field (send as-is)\r\n form.appendChild(hiddenInput('threeDSSessionData', transactionId))\r\n }\r\n } else {\r\n // 3DS v1 fallback\r\n const termUrl = redirectUri || ''\r\n form.appendChild(hiddenInput('PaReq', pReq || ''))\r\n form.appendChild(hiddenInput('TermUrl', termUrl))\r\n form.appendChild(hiddenInput('MD', transactionId || ''))\r\n }\r\n\r\n form.submit()\r\n\r\n setTimeout(() => {\r\n try {\r\n document.body.removeChild(form)\r\n } catch {\r\n // ignore\r\n }\r\n }, 1000000)\r\n}\r\n\r\nexport function closeOpayoIframe() {\r\n if (!isBrowser()) return\r\n const container = document.getElementById('threeds-container') as HTMLDivElement | null\r\n if (!container) return\r\n\r\n container.innerHTML = ''\r\n container.style.display = 'none'\r\n\r\n try {\r\n onOpayoCancelled?.()\r\n } catch {\r\n // ignore\r\n }\r\n}\r\n\r\nexport function run3DSecure(args: Run3DSecureArgs) {\r\n if (!isBrowser()) return Promise.resolve(false)\r\n\r\n const {\r\n responseType,\r\n acsUrl,\r\n pReq,\r\n cReq,\r\n redirectUri,\r\n transactionId,\r\n onCancelled,\r\n onOpened,\r\n onClosed\r\n } = args\r\n\r\n console.log('[3DS] run3DSecure called', {\r\n responseType,\r\n acsUrl,\r\n transactionId,\r\n redirectUriProvided: !!redirectUri,\r\n redirectUriFinal: redirectUri ?? getChallengeRedirectUri(),\r\n cReqType: typeof cReq,\r\n cReqPreview:\r\n typeof cReq === 'string'\r\n ? cReq.slice(0, 80)\r\n : cReq && typeof cReq === 'object'\r\n ? JSON.stringify(cReq).slice(0, 120)\r\n : cReq\r\n })\r\n\r\n onOpayoCancelled = () => {\r\n console.log('[3DS] onOpayoCancelled fired')\r\n try {\r\n onCancelled?.()\r\n } catch {\r\n // ignore\r\n }\r\n }\r\n\r\n return new Promise<boolean>((resolve) => {\r\n const handleMessage = (event: MessageEvent) => {\r\n const msg = String((event as any).data || '')\r\n console.log('[3DS] message received', {\r\n msg,\r\n origin: event.origin,\r\n sourceIsWindow: event.source === window,\r\n sourceIsTop: event.source === window.top\r\n })\r\n\r\n if (msg === 'OPAYO_CALLBACK_SUCCESS') {\r\n console.log('[3DS] SUCCESS -> closing + resolve(true)')\r\n closeOpayoIframe()\r\n window.removeEventListener('message', handleMessage)\r\n try {\r\n onClosed?.()\r\n } catch {}\r\n resolve(true)\r\n } else if (msg === 'OPAYO_CALLBACK_FAILED') {\r\n console.log('[3DS] FAILED -> closing + resolve(false)')\r\n closeOpayoIframe()\r\n window.removeEventListener('message', handleMessage)\r\n try {\r\n onClosed?.()\r\n } catch {}\r\n resolve(false)\r\n }\r\n }\r\n\r\n window.addEventListener('message', handleMessage)\r\n\r\n try {\r\n onOpened?.()\r\n } catch {}\r\n\r\n const termUrl = redirectUri ?? getChallengeRedirectUri()\r\n console.log('[3DS] initializing iframe', { termUrl })\r\n\r\n initializeIframe(responseType, acsUrl, pReq, cReq, termUrl, transactionId)\r\n })\r\n}\r\n\r\n// Call this from your callback/return page.\r\n// It posts a signal back to the parent checkout window.\r\nexport function receiveOpayoResponse(message: unknown) {\r\n if (!isBrowser()) return\r\n\r\n const m = String(message || '')\r\n if (m === 'OPAYO_CALLBACK_FAILED' || m === 'OPAYO_CALLBACK_SUCCESS') {\r\n try {\r\n window.top?.postMessage(m, '*')\r\n } catch {\r\n // ignore\r\n }\r\n\r\n try {\r\n onOpayoCancelled?.()\r\n } catch {\r\n // ignore\r\n }\r\n }\r\n}\r\n","// src/install.ts\r\nimport type { App } from 'vue'\r\n\r\nexport function OrderopiaVuePlugin() {\r\n return {\r\n install(app: App) {\r\n // nothing yet, but future-proof\r\n }\r\n }\r\n}\r\n"],"mappings":"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","names":["import_vue","import_orderopia_ordering_api_client"]}
|
package/dist/index.js
CHANGED
|
@@ -191,11 +191,33 @@ function run3DSecure(args) {
|
|
|
191
191
|
onOpened,
|
|
192
192
|
onClosed
|
|
193
193
|
} = args;
|
|
194
|
-
|
|
194
|
+
console.log("[3DS] run3DSecure called", {
|
|
195
|
+
responseType,
|
|
196
|
+
acsUrl,
|
|
197
|
+
transactionId,
|
|
198
|
+
redirectUriProvided: !!redirectUri,
|
|
199
|
+
redirectUriFinal: redirectUri ?? getChallengeRedirectUri(),
|
|
200
|
+
cReqType: typeof cReq,
|
|
201
|
+
cReqPreview: typeof cReq === "string" ? cReq.slice(0, 80) : cReq && typeof cReq === "object" ? JSON.stringify(cReq).slice(0, 120) : cReq
|
|
202
|
+
});
|
|
203
|
+
onOpayoCancelled = () => {
|
|
204
|
+
console.log("[3DS] onOpayoCancelled fired");
|
|
205
|
+
try {
|
|
206
|
+
onCancelled?.();
|
|
207
|
+
} catch {
|
|
208
|
+
}
|
|
209
|
+
};
|
|
195
210
|
return new Promise((resolve) => {
|
|
196
211
|
const handleMessage = (event) => {
|
|
197
212
|
const msg = String(event.data || "");
|
|
213
|
+
console.log("[3DS] message received", {
|
|
214
|
+
msg,
|
|
215
|
+
origin: event.origin,
|
|
216
|
+
sourceIsWindow: event.source === window,
|
|
217
|
+
sourceIsTop: event.source === window.top
|
|
218
|
+
});
|
|
198
219
|
if (msg === "OPAYO_CALLBACK_SUCCESS") {
|
|
220
|
+
console.log("[3DS] SUCCESS -> closing + resolve(true)");
|
|
199
221
|
closeOpayoIframe();
|
|
200
222
|
window.removeEventListener("message", handleMessage);
|
|
201
223
|
try {
|
|
@@ -204,6 +226,7 @@ function run3DSecure(args) {
|
|
|
204
226
|
}
|
|
205
227
|
resolve(true);
|
|
206
228
|
} else if (msg === "OPAYO_CALLBACK_FAILED") {
|
|
229
|
+
console.log("[3DS] FAILED -> closing + resolve(false)");
|
|
207
230
|
closeOpayoIframe();
|
|
208
231
|
window.removeEventListener("message", handleMessage);
|
|
209
232
|
try {
|
|
@@ -219,6 +242,7 @@ function run3DSecure(args) {
|
|
|
219
242
|
} catch {
|
|
220
243
|
}
|
|
221
244
|
const termUrl = redirectUri ?? getChallengeRedirectUri();
|
|
245
|
+
console.log("[3DS] initializing iframe", { termUrl });
|
|
222
246
|
initializeIframe(responseType, acsUrl, pReq, cReq, termUrl, transactionId);
|
|
223
247
|
});
|
|
224
248
|
}
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/composables/useBasket.ts","../src/composables/useOpayo3ds.ts","../src/opayo/opayo3dsManager.ts","../src/install.ts"],"sourcesContent":["// src/composables/useBasket.ts\r\nimport { ref, computed, onScopeDispose } from 'vue'\r\nimport {\r\n basketManager,\r\n type BasketState\r\n} from 'orderopia-ordering-api-client'\r\n\r\nexport function useBasket() {\r\n // Start with current basket immediately\r\n const basket = ref<BasketState>(basketManager.getBasket())\r\n\r\n // Subscribe immediately (NOT in onMounted)\r\n const unsubscribe = basketManager.subscribe(state => {\r\n basket.value = state\r\n })\r\n\r\n // Auto-cleanup when the component/composable scope is destroyed\r\n onScopeDispose(() => {\r\n unsubscribe()\r\n })\r\n\r\n const totalQuantity = computed(() =>\r\n basket.value.items.reduce((sum, i) => sum + (i.quantity ?? 0), 0)\r\n )\r\n\r\n return {\r\n basket,\r\n totalQuantity,\r\n\r\n // pass-through helpers (nice DX)\r\n addToBasket: basketManager.addToBasket.bind(basketManager),\r\n removeFromBasket: basketManager.removeFromBasket.bind(basketManager),\r\n clearBasket: basketManager.clearBasket.bind(basketManager)\r\n }\r\n}\r\n","// src/composables/useOpayo3ds.ts\r\nimport { ref, onScopeDispose } from 'vue'\r\nimport { run3DSecure, closeOpayoIframe, type Run3DSecureArgs } from '../opayo/opayo3dsManager'\r\n\r\nexport function useOpayo3ds() {\r\n const isOpen = ref(false)\r\n const isRunning = ref(false)\r\n\r\n let disposed = false\r\n\r\n onScopeDispose(() => {\r\n disposed = true\r\n isOpen.value = false\r\n isRunning.value = false\r\n closeOpayoIframe()\r\n })\r\n\r\n async function run(args: Run3DSecureArgs) {\r\n if (disposed) return false\r\n\r\n isOpen.value = true\r\n isRunning.value = true\r\n\r\n try {\r\n const result = await run3DSecure({\r\n ...args,\r\n onOpened: () => {\r\n if (disposed) return\r\n isOpen.value = true\r\n args.onOpened?.()\r\n },\r\n onClosed: () => {\r\n if (disposed) return\r\n isOpen.value = false\r\n args.onClosed?.()\r\n },\r\n onCancelled: () => {\r\n if (disposed) return\r\n isOpen.value = false\r\n args.onCancelled?.()\r\n }\r\n })\r\n\r\n return result\r\n } finally {\r\n if (!disposed) {\r\n isRunning.value = false\r\n }\r\n }\r\n }\r\n\r\n function close() {\r\n isOpen.value = false\r\n isRunning.value = false\r\n closeOpayoIframe()\r\n }\r\n\r\n return {\r\n isOpen,\r\n isRunning,\r\n run,\r\n close\r\n }\r\n}\r\n","// src/opayo/opayo3dsManager.ts\r\n\r\ntype ResponseType = 'CHALLENGE' | 'FALLBACK'\r\n\r\nfunction isBrowser() {\r\n return typeof window !== 'undefined' && typeof document !== 'undefined'\r\n}\r\n\r\nfunction ensureThreedsContainer() {\r\n if (!isBrowser()) return null\r\n\r\n let el = document.getElementById('threeds-container') as HTMLDivElement | null\r\n if (!el) {\r\n el = document.createElement('div')\r\n el.id = 'threeds-container'\r\n el.style.position = 'fixed'\r\n el.style.inset = '0'\r\n el.style.display = 'none'\r\n el.style.background = 'rgba(0,0,0,0.35)'\r\n el.style.zIndex = '99999'\r\n document.body.appendChild(el)\r\n }\r\n return el\r\n}\r\n\r\n// UTF-8 -> Base64URL (no padding)\r\nfunction utf8ToBase64Url(str: string) {\r\n const bytes = new TextEncoder().encode(str)\r\n let bin = ''\r\n for (let i = 0; i < bytes.length; i++) bin += String.fromCharCode(bytes[i])\r\n const b64 = btoa(bin)\r\n return b64.replace(/\\+/g, '-').replace(/\\//g, '_').replace(/=+$/g, '')\r\n}\r\n\r\n// Base64URL -> Base64 (and add padding)\r\nfunction toStdBase64(b64url: string) {\r\n let s = b64url.replace(/-/g, '+').replace(/_/g, '/')\r\n const pad = s.length % 4\r\n if (pad) s += '==='.slice(pad - 1)\r\n return s\r\n}\r\n\r\nfunction safeAtobMaybeJson(b64ish: string) {\r\n try {\r\n return atob(toStdBase64(b64ish))\r\n } catch {\r\n return null\r\n }\r\n}\r\n\r\n// Normalize creq to Base64URL without padding.\r\nfunction normalizeCreq(input: unknown) {\r\n if (input == null) return ''\r\n\r\n // Object -> JSON\r\n if (typeof input === 'object') {\r\n try {\r\n return utf8ToBase64Url(JSON.stringify(input))\r\n } catch {\r\n // ignore\r\n }\r\n }\r\n\r\n let creq = String(input).trim()\r\n\r\n // If obviously JSON text\r\n if (/^\\s*\\{[\\s\\S]*\\}\\s*$/.test(creq)) {\r\n return utf8ToBase64Url(creq)\r\n }\r\n\r\n // If it looks URL-encoded, attempt a decode\r\n if (/%[0-9A-Fa-f]{2}/.test(creq) || creq.includes('%')) {\r\n try {\r\n creq = decodeURIComponent(creq)\r\n } catch {\r\n // ignore\r\n }\r\n }\r\n\r\n // Remove whitespace, convert spaces to '+'\r\n creq = creq.replace(/\\s+/g, '').replace(/ /g, '+')\r\n\r\n // If decodes to JSON, re-encode properly as UTF-8 base64url\r\n const maybe = safeAtobMaybeJson(creq)\r\n if (maybe && maybe.charCodeAt(0) === 123 /* '{' */) {\r\n return utf8ToBase64Url(maybe)\r\n }\r\n\r\n // Otherwise normalize to base64url and strip padding\r\n creq = creq.replace(/\\+/g, '-').replace(/\\//g, '_').replace(/=+$/g, '')\r\n return creq\r\n}\r\n\r\nfunction hiddenInput(name: string, value: unknown) {\r\n const inp = document.createElement('input')\r\n inp.type = 'hidden'\r\n inp.name = name\r\n inp.value = value == null ? '' : String(value)\r\n return inp\r\n}\r\n\r\n/**\r\n * ✅ Pull env from the core client (prod/dev toggle you built)\r\n * NOTE: Adjust the import path to whatever you export publicly from the client package.\r\n */\r\nimport { isOrderopiaDevMode } from 'orderopia-ordering-api-client'\r\n\r\nfunction getChallengeRedirectUri(): string {\r\n // TEMP values for now (your ask) — but environment-aware.\r\n // Replace these when you confirm the final endpoint paths.\r\n return isOrderopiaDevMode()\r\n ? 'https://api-test.orderopia.com/Transactions/3ds/Challenge'\r\n : 'https://api.orderopia.com/Transactions/3ds/Challenge'\r\n}\r\n\r\nexport type Run3DSecureArgs = {\r\n responseType: ResponseType\r\n acsUrl: string\r\n pReq?: string | null\r\n cReq?: unknown\r\n redirectUri?: string | null\r\n transactionId?: string | null\r\n\r\n onCancelled?: () => void\r\n onOpened?: () => void\r\n onClosed?: () => void\r\n}\r\n\r\nlet onOpayoCancelled: (() => void) | undefined\r\n\r\nfunction initializeIframe(\r\n responseType: ResponseType,\r\n acsUrl: string,\r\n pReq?: string | null,\r\n cReq?: unknown,\r\n redirectUri?: string | null,\r\n transactionId?: string | null\r\n) {\r\n if (!isBrowser()) return\r\n\r\n const container = ensureThreedsContainer()\r\n if (!container) return\r\n\r\n container.style.display = 'block'\r\n container.textContent = ''\r\n\r\n const panel = document.createElement('div')\r\n panel.style.position = 'absolute'\r\n panel.style.top = '50%'\r\n panel.style.left = '50%'\r\n panel.style.transform = 'translate(-50%, -50%)'\r\n panel.style.width = 'min(100%, 500px)'\r\n panel.style.height = 'min(100%, 600px)'\r\n panel.style.background = 'var(--background, #fff)'\r\n panel.style.borderRadius = '10px'\r\n panel.style.boxShadow = '0 10px 30px rgba(0,0,0,0.2)'\r\n panel.style.display = 'flex'\r\n panel.style.flexDirection = 'column'\r\n panel.style.overflow = 'hidden'\r\n container.appendChild(panel)\r\n\r\n const msg = document.createElement('div')\r\n msg.style.padding = '16px'\r\n msg.style.fontFamily = 'sans-serif'\r\n msg.style.textAlign = 'center'\r\n msg.innerHTML =\r\n '<div style=\"font-weight:600;\">We’re redirecting you to your bank to complete the payment.</div>' +\r\n '<div style=\"margin-top:6px;\">This will only take a moment. Please <u>do not</u> refresh the page.</div>'\r\n panel.appendChild(msg)\r\n\r\n const iframeWrap = document.createElement('div')\r\n iframeWrap.style.flex = '1'\r\n iframeWrap.style.borderTop = '1px solid rgba(0,0,0,0.06)'\r\n panel.appendChild(iframeWrap)\r\n\r\n const iframeName = 'threeds_frame_' + Math.random().toString(36).slice(2)\r\n const iframe = document.createElement('iframe')\r\n iframe.name = iframeName\r\n iframe.title = '3-D Secure Authentication'\r\n iframe.style.width = '100%'\r\n iframe.style.height = '100%'\r\n iframe.style.border = '0'\r\n iframeWrap.appendChild(iframe)\r\n\r\n const form = document.createElement('form')\r\n form.style.display = 'none'\r\n form.method = 'POST'\r\n form.action = acsUrl\r\n form.target = iframeName\r\n document.body.appendChild(form)\r\n\r\n if (responseType === 'CHALLENGE') {\r\n // 3DS v2\r\n form.appendChild(hiddenInput('creq', normalizeCreq(cReq)))\r\n if (transactionId) {\r\n // Optional echo field (send as-is)\r\n form.appendChild(hiddenInput('threeDSSessionData', transactionId))\r\n }\r\n } else {\r\n // 3DS v1 fallback\r\n const termUrl = redirectUri || ''\r\n form.appendChild(hiddenInput('PaReq', pReq || ''))\r\n form.appendChild(hiddenInput('TermUrl', termUrl))\r\n form.appendChild(hiddenInput('MD', transactionId || ''))\r\n }\r\n\r\n form.submit()\r\n\r\n setTimeout(() => {\r\n try {\r\n document.body.removeChild(form)\r\n } catch {\r\n // ignore\r\n }\r\n }, 1000000)\r\n}\r\n\r\nexport function closeOpayoIframe() {\r\n if (!isBrowser()) return\r\n const container = document.getElementById('threeds-container') as HTMLDivElement | null\r\n if (!container) return\r\n\r\n container.innerHTML = ''\r\n container.style.display = 'none'\r\n\r\n try {\r\n onOpayoCancelled?.()\r\n } catch {\r\n // ignore\r\n }\r\n}\r\n\r\nexport function run3DSecure(args: Run3DSecureArgs) {\r\n if (!isBrowser()) return Promise.resolve(false)\r\n\r\n const {\r\n responseType,\r\n acsUrl,\r\n pReq,\r\n cReq,\r\n redirectUri,\r\n transactionId,\r\n onCancelled,\r\n onOpened,\r\n onClosed\r\n } = args\r\n\r\n onOpayoCancelled = onCancelled\r\n\r\n return new Promise<boolean>((resolve) => {\r\n const handleMessage = (event: MessageEvent) => {\r\n const msg = String((event as any).data || '')\r\n\r\n if (msg === 'OPAYO_CALLBACK_SUCCESS') {\r\n closeOpayoIframe()\r\n window.removeEventListener('message', handleMessage)\r\n try {\r\n onClosed?.()\r\n } catch {\r\n // ignore\r\n }\r\n resolve(true)\r\n } else if (msg === 'OPAYO_CALLBACK_FAILED') {\r\n closeOpayoIframe()\r\n window.removeEventListener('message', handleMessage)\r\n try {\r\n onClosed?.()\r\n } catch {\r\n // ignore\r\n }\r\n resolve(false)\r\n }\r\n }\r\n\r\n window.addEventListener('message', handleMessage)\r\n\r\n try {\r\n onOpened?.()\r\n } catch {\r\n // ignore\r\n }\r\n\r\n const termUrl = redirectUri ?? getChallengeRedirectUri()\r\n\r\n initializeIframe(responseType, acsUrl, pReq, cReq, termUrl, transactionId)\r\n })\r\n}\r\n\r\n// Call this from your callback/return page.\r\n// It posts a signal back to the parent checkout window.\r\nexport function receiveOpayoResponse(message: unknown) {\r\n if (!isBrowser()) return\r\n\r\n const m = String(message || '')\r\n if (m === 'OPAYO_CALLBACK_FAILED' || m === 'OPAYO_CALLBACK_SUCCESS') {\r\n try {\r\n window.top?.postMessage(m, '*')\r\n } catch {\r\n // ignore\r\n }\r\n\r\n try {\r\n onOpayoCancelled?.()\r\n } catch {\r\n // ignore\r\n }\r\n }\r\n}\r\n","// src/install.ts\r\nimport type { App } from 'vue'\r\n\r\nexport function OrderopiaVuePlugin() {\r\n return {\r\n install(app: App) {\r\n // nothing yet, but future-proof\r\n }\r\n }\r\n}\r\n"],"mappings":"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","names":["ref","onScopeDispose","ref","onScopeDispose"]}
|
|
1
|
+
{"version":3,"sources":["../src/composables/useBasket.ts","../src/composables/useOpayo3ds.ts","../src/opayo/opayo3dsManager.ts","../src/install.ts"],"sourcesContent":["// src/composables/useBasket.ts\r\nimport { ref, computed, onScopeDispose } from 'vue'\r\nimport {\r\n basketManager,\r\n type BasketState\r\n} from 'orderopia-ordering-api-client'\r\n\r\nexport function useBasket() {\r\n // Start with current basket immediately\r\n const basket = ref<BasketState>(basketManager.getBasket())\r\n\r\n // Subscribe immediately (NOT in onMounted)\r\n const unsubscribe = basketManager.subscribe(state => {\r\n basket.value = state\r\n })\r\n\r\n // Auto-cleanup when the component/composable scope is destroyed\r\n onScopeDispose(() => {\r\n unsubscribe()\r\n })\r\n\r\n const totalQuantity = computed(() =>\r\n basket.value.items.reduce((sum, i) => sum + (i.quantity ?? 0), 0)\r\n )\r\n\r\n return {\r\n basket,\r\n totalQuantity,\r\n\r\n // pass-through helpers (nice DX)\r\n addToBasket: basketManager.addToBasket.bind(basketManager),\r\n removeFromBasket: basketManager.removeFromBasket.bind(basketManager),\r\n clearBasket: basketManager.clearBasket.bind(basketManager)\r\n }\r\n}\r\n","// src/composables/useOpayo3ds.ts\r\nimport { ref, onScopeDispose } from 'vue'\r\nimport { run3DSecure, closeOpayoIframe, type Run3DSecureArgs } from '../opayo/opayo3dsManager'\r\n\r\nexport function useOpayo3ds() {\r\n const isOpen = ref(false)\r\n const isRunning = ref(false)\r\n\r\n let disposed = false\r\n\r\n onScopeDispose(() => {\r\n disposed = true\r\n isOpen.value = false\r\n isRunning.value = false\r\n closeOpayoIframe()\r\n })\r\n\r\n async function run(args: Run3DSecureArgs) {\r\n if (disposed) return false\r\n\r\n isOpen.value = true\r\n isRunning.value = true\r\n\r\n try {\r\n const result = await run3DSecure({\r\n ...args,\r\n onOpened: () => {\r\n if (disposed) return\r\n isOpen.value = true\r\n args.onOpened?.()\r\n },\r\n onClosed: () => {\r\n if (disposed) return\r\n isOpen.value = false\r\n args.onClosed?.()\r\n },\r\n onCancelled: () => {\r\n if (disposed) return\r\n isOpen.value = false\r\n args.onCancelled?.()\r\n }\r\n })\r\n\r\n return result\r\n } finally {\r\n if (!disposed) {\r\n isRunning.value = false\r\n }\r\n }\r\n }\r\n\r\n function close() {\r\n isOpen.value = false\r\n isRunning.value = false\r\n closeOpayoIframe()\r\n }\r\n\r\n return {\r\n isOpen,\r\n isRunning,\r\n run,\r\n close\r\n }\r\n}\r\n","// src/opayo/opayo3dsManager.ts\r\n\r\ntype ResponseType = 'CHALLENGE' | 'FALLBACK'\r\n\r\nfunction isBrowser() {\r\n return typeof window !== 'undefined' && typeof document !== 'undefined'\r\n}\r\n\r\nfunction ensureThreedsContainer() {\r\n if (!isBrowser()) return null\r\n\r\n let el = document.getElementById('threeds-container') as HTMLDivElement | null\r\n if (!el) {\r\n el = document.createElement('div')\r\n el.id = 'threeds-container'\r\n el.style.position = 'fixed'\r\n el.style.inset = '0'\r\n el.style.display = 'none'\r\n el.style.background = 'rgba(0,0,0,0.35)'\r\n el.style.zIndex = '99999'\r\n document.body.appendChild(el)\r\n }\r\n return el\r\n}\r\n\r\n// UTF-8 -> Base64URL (no padding)\r\nfunction utf8ToBase64Url(str: string) {\r\n const bytes = new TextEncoder().encode(str)\r\n let bin = ''\r\n for (let i = 0; i < bytes.length; i++) bin += String.fromCharCode(bytes[i])\r\n const b64 = btoa(bin)\r\n return b64.replace(/\\+/g, '-').replace(/\\//g, '_').replace(/=+$/g, '')\r\n}\r\n\r\n// Base64URL -> Base64 (and add padding)\r\nfunction toStdBase64(b64url: string) {\r\n let s = b64url.replace(/-/g, '+').replace(/_/g, '/')\r\n const pad = s.length % 4\r\n if (pad) s += '==='.slice(pad - 1)\r\n return s\r\n}\r\n\r\nfunction safeAtobMaybeJson(b64ish: string) {\r\n try {\r\n return atob(toStdBase64(b64ish))\r\n } catch {\r\n return null\r\n }\r\n}\r\n\r\n// Normalize creq to Base64URL without padding.\r\nfunction normalizeCreq(input: unknown) {\r\n if (input == null) return ''\r\n\r\n // Object -> JSON\r\n if (typeof input === 'object') {\r\n try {\r\n return utf8ToBase64Url(JSON.stringify(input))\r\n } catch {\r\n // ignore\r\n }\r\n }\r\n\r\n let creq = String(input).trim()\r\n\r\n // If obviously JSON text\r\n if (/^\\s*\\{[\\s\\S]*\\}\\s*$/.test(creq)) {\r\n return utf8ToBase64Url(creq)\r\n }\r\n\r\n // If it looks URL-encoded, attempt a decode\r\n if (/%[0-9A-Fa-f]{2}/.test(creq) || creq.includes('%')) {\r\n try {\r\n creq = decodeURIComponent(creq)\r\n } catch {\r\n // ignore\r\n }\r\n }\r\n\r\n // Remove whitespace, convert spaces to '+'\r\n creq = creq.replace(/\\s+/g, '').replace(/ /g, '+')\r\n\r\n // If decodes to JSON, re-encode properly as UTF-8 base64url\r\n const maybe = safeAtobMaybeJson(creq)\r\n if (maybe && maybe.charCodeAt(0) === 123 /* '{' */) {\r\n return utf8ToBase64Url(maybe)\r\n }\r\n\r\n // Otherwise normalize to base64url and strip padding\r\n creq = creq.replace(/\\+/g, '-').replace(/\\//g, '_').replace(/=+$/g, '')\r\n return creq\r\n}\r\n\r\nfunction hiddenInput(name: string, value: unknown) {\r\n const inp = document.createElement('input')\r\n inp.type = 'hidden'\r\n inp.name = name\r\n inp.value = value == null ? '' : String(value)\r\n return inp\r\n}\r\n\r\n/**\r\n * ✅ Pull env from the core client (prod/dev toggle you built)\r\n * NOTE: Adjust the import path to whatever you export publicly from the client package.\r\n */\r\nimport { isOrderopiaDevMode } from 'orderopia-ordering-api-client'\r\n\r\nfunction getChallengeRedirectUri(): string {\r\n // TEMP values for now (your ask) — but environment-aware.\r\n // Replace these when you confirm the final endpoint paths.\r\n return isOrderopiaDevMode()\r\n ? 'https://api-test.orderopia.com/Transactions/3ds/Challenge'\r\n : 'https://api.orderopia.com/Transactions/3ds/Challenge'\r\n}\r\n\r\nexport type Run3DSecureArgs = {\r\n responseType: ResponseType\r\n acsUrl: string\r\n pReq?: string | null\r\n cReq?: unknown\r\n redirectUri?: string | null\r\n transactionId?: string | null\r\n\r\n onCancelled?: () => void\r\n onOpened?: () => void\r\n onClosed?: () => void\r\n}\r\n\r\nlet onOpayoCancelled: (() => void) | undefined\r\n\r\nfunction initializeIframe(\r\n responseType: ResponseType,\r\n acsUrl: string,\r\n pReq?: string | null,\r\n cReq?: unknown,\r\n redirectUri?: string | null,\r\n transactionId?: string | null\r\n) {\r\n if (!isBrowser()) return\r\n\r\n const container = ensureThreedsContainer()\r\n if (!container) return\r\n\r\n container.style.display = 'block'\r\n container.textContent = ''\r\n\r\n const panel = document.createElement('div')\r\n panel.style.position = 'absolute'\r\n panel.style.top = '50%'\r\n panel.style.left = '50%'\r\n panel.style.transform = 'translate(-50%, -50%)'\r\n panel.style.width = 'min(100%, 500px)'\r\n panel.style.height = 'min(100%, 600px)'\r\n panel.style.background = 'var(--background, #fff)'\r\n panel.style.borderRadius = '10px'\r\n panel.style.boxShadow = '0 10px 30px rgba(0,0,0,0.2)'\r\n panel.style.display = 'flex'\r\n panel.style.flexDirection = 'column'\r\n panel.style.overflow = 'hidden'\r\n container.appendChild(panel)\r\n\r\n const msg = document.createElement('div')\r\n msg.style.padding = '16px'\r\n msg.style.fontFamily = 'sans-serif'\r\n msg.style.textAlign = 'center'\r\n msg.innerHTML =\r\n '<div style=\"font-weight:600;\">We’re redirecting you to your bank to complete the payment.</div>' +\r\n '<div style=\"margin-top:6px;\">This will only take a moment. Please <u>do not</u> refresh the page.</div>'\r\n panel.appendChild(msg)\r\n\r\n const iframeWrap = document.createElement('div')\r\n iframeWrap.style.flex = '1'\r\n iframeWrap.style.borderTop = '1px solid rgba(0,0,0,0.06)'\r\n panel.appendChild(iframeWrap)\r\n\r\n const iframeName = 'threeds_frame_' + Math.random().toString(36).slice(2)\r\n const iframe = document.createElement('iframe')\r\n iframe.name = iframeName\r\n iframe.title = '3-D Secure Authentication'\r\n iframe.style.width = '100%'\r\n iframe.style.height = '100%'\r\n iframe.style.border = '0'\r\n iframeWrap.appendChild(iframe)\r\n\r\n const form = document.createElement('form')\r\n form.style.display = 'none'\r\n form.method = 'POST'\r\n form.action = acsUrl\r\n form.target = iframeName\r\n document.body.appendChild(form)\r\n\r\n if (responseType === 'CHALLENGE') {\r\n // 3DS v2\r\n form.appendChild(hiddenInput('creq', normalizeCreq(cReq)))\r\n if (transactionId) {\r\n // Optional echo field (send as-is)\r\n form.appendChild(hiddenInput('threeDSSessionData', transactionId))\r\n }\r\n } else {\r\n // 3DS v1 fallback\r\n const termUrl = redirectUri || ''\r\n form.appendChild(hiddenInput('PaReq', pReq || ''))\r\n form.appendChild(hiddenInput('TermUrl', termUrl))\r\n form.appendChild(hiddenInput('MD', transactionId || ''))\r\n }\r\n\r\n form.submit()\r\n\r\n setTimeout(() => {\r\n try {\r\n document.body.removeChild(form)\r\n } catch {\r\n // ignore\r\n }\r\n }, 1000000)\r\n}\r\n\r\nexport function closeOpayoIframe() {\r\n if (!isBrowser()) return\r\n const container = document.getElementById('threeds-container') as HTMLDivElement | null\r\n if (!container) return\r\n\r\n container.innerHTML = ''\r\n container.style.display = 'none'\r\n\r\n try {\r\n onOpayoCancelled?.()\r\n } catch {\r\n // ignore\r\n }\r\n}\r\n\r\nexport function run3DSecure(args: Run3DSecureArgs) {\r\n if (!isBrowser()) return Promise.resolve(false)\r\n\r\n const {\r\n responseType,\r\n acsUrl,\r\n pReq,\r\n cReq,\r\n redirectUri,\r\n transactionId,\r\n onCancelled,\r\n onOpened,\r\n onClosed\r\n } = args\r\n\r\n console.log('[3DS] run3DSecure called', {\r\n responseType,\r\n acsUrl,\r\n transactionId,\r\n redirectUriProvided: !!redirectUri,\r\n redirectUriFinal: redirectUri ?? getChallengeRedirectUri(),\r\n cReqType: typeof cReq,\r\n cReqPreview:\r\n typeof cReq === 'string'\r\n ? cReq.slice(0, 80)\r\n : cReq && typeof cReq === 'object'\r\n ? JSON.stringify(cReq).slice(0, 120)\r\n : cReq\r\n })\r\n\r\n onOpayoCancelled = () => {\r\n console.log('[3DS] onOpayoCancelled fired')\r\n try {\r\n onCancelled?.()\r\n } catch {\r\n // ignore\r\n }\r\n }\r\n\r\n return new Promise<boolean>((resolve) => {\r\n const handleMessage = (event: MessageEvent) => {\r\n const msg = String((event as any).data || '')\r\n console.log('[3DS] message received', {\r\n msg,\r\n origin: event.origin,\r\n sourceIsWindow: event.source === window,\r\n sourceIsTop: event.source === window.top\r\n })\r\n\r\n if (msg === 'OPAYO_CALLBACK_SUCCESS') {\r\n console.log('[3DS] SUCCESS -> closing + resolve(true)')\r\n closeOpayoIframe()\r\n window.removeEventListener('message', handleMessage)\r\n try {\r\n onClosed?.()\r\n } catch {}\r\n resolve(true)\r\n } else if (msg === 'OPAYO_CALLBACK_FAILED') {\r\n console.log('[3DS] FAILED -> closing + resolve(false)')\r\n closeOpayoIframe()\r\n window.removeEventListener('message', handleMessage)\r\n try {\r\n onClosed?.()\r\n } catch {}\r\n resolve(false)\r\n }\r\n }\r\n\r\n window.addEventListener('message', handleMessage)\r\n\r\n try {\r\n onOpened?.()\r\n } catch {}\r\n\r\n const termUrl = redirectUri ?? getChallengeRedirectUri()\r\n console.log('[3DS] initializing iframe', { termUrl })\r\n\r\n initializeIframe(responseType, acsUrl, pReq, cReq, termUrl, transactionId)\r\n })\r\n}\r\n\r\n// Call this from your callback/return page.\r\n// It posts a signal back to the parent checkout window.\r\nexport function receiveOpayoResponse(message: unknown) {\r\n if (!isBrowser()) return\r\n\r\n const m = String(message || '')\r\n if (m === 'OPAYO_CALLBACK_FAILED' || m === 'OPAYO_CALLBACK_SUCCESS') {\r\n try {\r\n window.top?.postMessage(m, '*')\r\n } catch {\r\n // ignore\r\n }\r\n\r\n try {\r\n onOpayoCancelled?.()\r\n } catch {\r\n // ignore\r\n }\r\n }\r\n}\r\n","// src/install.ts\r\nimport type { App } from 'vue'\r\n\r\nexport function OrderopiaVuePlugin() {\r\n return {\r\n install(app: App) {\r\n // nothing yet, but future-proof\r\n }\r\n }\r\n}\r\n"],"mappings":"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","names":["ref","onScopeDispose","ref","onScopeDispose"]}
|