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 CHANGED
@@ -218,11 +218,33 @@ function run3DSecure(args) {
218
218
  onOpened,
219
219
  onClosed
220
220
  } = args;
221
- onOpayoCancelled = onCancelled;
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
  }
@@ -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
- onOpayoCancelled = onCancelled;
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"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "orderopia-ordering-api-client-vue",
3
- "version": "0.0.5",
3
+ "version": "0.0.7",
4
4
  "description": "Vue bindings for Orderopia Ordering API Client",
5
5
  "license": "MIT",
6
6
  "type": "module",