@paynext/sdk 0.0.168 → 0.0.169

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -2,21 +2,6 @@
2
2
 
3
3
  TypeScript SDK for integrating PayNext payment processing with full type safety and multiple payment methods support.
4
4
 
5
- ## 🛡️ NEW: PCI-Compliant Iframe Mode
6
-
7
- Enable iframe mode for enhanced PCI compliance by adding one parameter:
8
-
9
- ```typescript
10
- await checkout.mount('checkout-container', {
11
- clientToken: 'your-token',
12
- environment: 'production',
13
- apiVersion: 'v1',
14
- iframeMode: true, // 🛡️ Isolates payment data in secure iframe
15
- })
16
- ```
17
-
18
- **Benefits:** Reduced PCI scope, isolated processing, XSS protection. See [Iframe Quick Start](./IFRAME_QUICKSTART.md) for details.
19
-
20
5
  ## Installation
21
6
 
22
7
  ```bash
@@ -116,11 +101,10 @@ export const CheckoutComponent: FC<Readonly<IProps>> = (props) => {
116
101
  ## Configuration
117
102
 
118
103
  ```typescript
119
- interface PayNextConfigExtended extends PayNextConfig {
104
+ interface PayNextConfig {
120
105
  clientToken: string
121
106
  apiVersion: string
122
- environment: 'develop' | 'staging' | 'sandbox' | 'production'
123
- iframeMode?: boolean // 🛡️ Enable PCI-compliant iframe mode (default: false)
107
+ environment: 'sandbox' | 'production',
124
108
  variant?: 'default' | 'compact'
125
109
  locale?: Locale
126
110
  translate?: CheckoutTranslate
@@ -210,31 +194,4 @@ The SDK supports recent versions of all major browsers:
210
194
 
211
195
  ## Security
212
196
 
213
- ### PCI Compliance
214
-
215
- **Iframe Mode (Recommended)**: Enables PCI-compliant iframe isolation for reduced compliance scope.
216
-
217
- ```typescript
218
- await checkout.mount('checkout-container', {
219
- iframeMode: true, // Isolates payment data processing
220
- // ... other config
221
- })
222
- ```
223
-
224
- ### Content Security Policy
225
-
226
- For iframe mode, add to your HTML:
227
-
228
- ```html
229
- <meta http-equiv="Content-Security-Policy"
230
- content="frame-src https://cdn-sdk.paynext.com https://cdn-sdk-dev.paynext.com;">
231
- ```
232
-
233
- ### Security Features
234
-
235
- - PCI DSS Level 1 compliant
236
- - End-to-end encryption (TLS 1.3)
237
- - Tokenization of sensitive data
238
- - No sensitive payment data stored locally
239
- - Optional iframe isolation for reduced PCI scope
240
- - Sandbox iframe security attributes (when iframe mode enabled)
197
+ PCI DSS compliant with end-to-end encryption. No sensitive payment data is stored locally.
package/dist/index.es.js CHANGED
@@ -1,11 +1,101 @@
1
- var f = Object.defineProperty;
2
- var p = (o, t, e) => t in o ? f(o, t, { enumerable: !0, configurable: !0, writable: !0, value: e }) : o[t] = e;
3
- var s = (o, t, e) => p(o, typeof t != "symbol" ? t + "" : t, e);
4
- const h = "https://cdn-sdk-dev.paynext.com/checkout.html", u = "https://cdn-sdk.paynext.com/checkout.html", w = {
1
+ var g = Object.defineProperty;
2
+ var w = (a, t, e) => t in a ? g(a, t, { enumerable: !0, configurable: !0, writable: !0, value: e }) : a[t] = e;
3
+ var s = (a, t, e) => w(a, typeof t != "symbol" ? t + "" : t, e);
4
+ const l = "https://cdn-sdk-dev.paynext.com/checkout.html", u = "https://cdn-sdk.paynext.com/checkout.html", x = {
5
5
  develop: "https://cdn-sdk-dev.paynext.com",
6
6
  production: "https://cdn-sdk.paynext.com"
7
7
  };
8
- class l {
8
+ function C(a) {
9
+ return `<!DOCTYPE html>
10
+ <html lang="en">
11
+ <head>
12
+ <meta charset="UTF-8">
13
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
14
+ <title>PayNext Checkout</title>
15
+ <style>
16
+ * { margin: 0; padding: 0; box-sizing: border-box; }
17
+ html, body { width: 100%; height: 100%; overflow: hidden; }
18
+ body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; }
19
+ #checkout-container { width: 100%; min-height: 100%; }
20
+ .loading { display: flex; align-items: center; justify-content: center; min-height: 400px; color: #666; font-size: 14px; }
21
+ .loading::after { content: '...'; animation: dots 1.5s steps(4, end) infinite; }
22
+ @keyframes dots { 0%, 20% { content: '.'; } 40% { content: '..'; } 60%, 100% { content: '...'; } }
23
+ </style>
24
+ </head>
25
+ <body>
26
+ <div id="checkout-container"><div class="loading">Loading checkout</div></div>
27
+ <script type="module">
28
+ const MessageType = {
29
+ MOUNT: 'paynext:mount', UNMOUNT: 'paynext:unmount', UPDATE_CONFIG: 'paynext:updateConfig',
30
+ READY: 'paynext:ready', LOADED: 'paynext:loaded', ATTEMPT: 'paynext:attempt',
31
+ COMPLETE: 'paynext:complete', FAIL: 'paynext:fail', ERROR: 'paynext:error', RESIZE: 'paynext:resize'
32
+ }
33
+ let checkout = null, parentOrigin = null, config = null
34
+
35
+ function sendToParent(type, payload, requestId) {
36
+ if (!parentOrigin) return
37
+ window.parent.postMessage({ type, payload, requestId }, parentOrigin)
38
+ }
39
+
40
+ window.addEventListener('message', async (event) => {
41
+ const data = event.data
42
+ if (!parentOrigin) parentOrigin = event.origin
43
+ if (event.origin !== parentOrigin || !data.type?.startsWith('paynext:')) return
44
+
45
+ try {
46
+ switch (data.type) {
47
+ case MessageType.MOUNT:
48
+ if (data.payload?.config) {
49
+ config = data.payload.config
50
+ const { PayNextCheckout } = await import('${a}')
51
+ checkout = new PayNextCheckout()
52
+ await checkout.mount('checkout-container', {
53
+ ...config,
54
+ onCheckoutLoaded: (result) => { sendToParent(MessageType.LOADED, result, data.requestId); notifyResize() },
55
+ onCheckoutAttempt: (result) => sendToParent(MessageType.ATTEMPT, result),
56
+ onCheckoutComplete: (result) => sendToParent(MessageType.COMPLETE, result),
57
+ onCheckoutFail: (error) => sendToParent(MessageType.FAIL, error)
58
+ })
59
+ sendToParent(MessageType.READY, { success: true }, data.requestId)
60
+ setupResizeObserver()
61
+ }
62
+ break
63
+ case MessageType.UNMOUNT:
64
+ if (checkout) { await checkout.unmount(); checkout = null }
65
+ sendToParent(MessageType.READY, { success: true }, data.requestId)
66
+ break
67
+ case MessageType.UPDATE_CONFIG:
68
+ if (checkout && data.payload?.config) {
69
+ await checkout.updateConfig(data.payload.config)
70
+ sendToParent(MessageType.READY, { success: true }, data.requestId)
71
+ }
72
+ break
73
+ }
74
+ } catch (error) {
75
+ sendToParent(MessageType.ERROR, { message: error.message, stack: error.stack }, data.requestId)
76
+ }
77
+ })
78
+
79
+ function setupResizeObserver() {
80
+ new ResizeObserver(entries => {
81
+ for (const entry of entries) sendToParent(MessageType.RESIZE, { height: entry.contentRect.height })
82
+ }).observe(document.body)
83
+ }
84
+
85
+ function notifyResize() { sendToParent(MessageType.RESIZE, { height: document.body.scrollHeight }) }
86
+
87
+ window.addEventListener('load', () => {
88
+ setTimeout(() => window.parent.postMessage({ type: MessageType.READY, payload: { success: true } }, '*'), 100)
89
+ })
90
+
91
+ window.addEventListener('error', (event) => {
92
+ sendToParent(MessageType.ERROR, { message: event.error?.message || 'Unknown error', stack: event.error?.stack })
93
+ })
94
+ <\/script>
95
+ </body>
96
+ </html>`;
97
+ }
98
+ class h {
9
99
  constructor() {
10
100
  s(this, "iframe", null);
11
101
  s(this, "container", null);
@@ -22,15 +112,15 @@ class l {
22
112
  }
23
113
  // send message to iframe
24
114
  sendMessage(t, e) {
25
- return new Promise((n, i) => {
115
+ return new Promise((o, n) => {
26
116
  if (!this.iframe || !this.iframe.contentWindow) {
27
- i(new Error("Iframe not initialized"));
117
+ n(new Error("Iframe not initialized"));
28
118
  return;
29
119
  }
30
- const a = this.generateRequestId(), c = { type: t, payload: e, requestId: a };
31
- this.pendingRequests.set(a, { resolve: n, reject: i }), setTimeout(() => {
32
- this.pendingRequests.has(a) && (this.pendingRequests.delete(a), i(new Error(`Request timeout: ${t}`)));
33
- }, 3e4), this.iframe.contentWindow.postMessage(c, this.allowedOrigin);
120
+ const i = this.generateRequestId(), r = { type: t, payload: e, requestId: i };
121
+ this.pendingRequests.set(i, { resolve: o, reject: n }), setTimeout(() => {
122
+ this.pendingRequests.has(i) && (this.pendingRequests.delete(i), n(new Error(`Request timeout: ${t}`)));
123
+ }, 3e4), this.iframe.contentWindow.postMessage(r, this.allowedOrigin);
34
124
  });
35
125
  }
36
126
  // handle messages from iframe
@@ -42,8 +132,8 @@ class l {
42
132
  const e = t.data;
43
133
  if (!(!e.type || !e.type.startsWith("paynext:"))) {
44
134
  if (e.requestId && this.pendingRequests.has(e.requestId)) {
45
- const { resolve: n } = this.pendingRequests.get(e.requestId);
46
- this.pendingRequests.delete(e.requestId), n(e.payload);
135
+ const { resolve: o } = this.pendingRequests.get(e.requestId);
136
+ this.pendingRequests.delete(e.requestId), o(e.payload);
47
137
  return;
48
138
  }
49
139
  switch (e.type) {
@@ -74,11 +164,11 @@ class l {
74
164
  }
75
165
  }
76
166
  // create and configure iframe
77
- createIframe(t) {
78
- const e = t.environment?.toLowerCase()?.includes("develop") || t.environment?.toLowerCase()?.includes("staging") ? "develop" : "production";
79
- this.allowedOrigin = w[e];
80
- const n = document.createElement("iframe"), i = e === "develop" ? h : u;
81
- return n.src = i, n.style.border = "none", n.style.width = "100%", n.style.minHeight = "400px", n.style.display = "block", n.allow = "payment *", n.sandbox.add(
167
+ createIframe(t, e = !1) {
168
+ const o = t.environment?.toLowerCase()?.includes("develop") || t.environment?.toLowerCase()?.includes("staging") ? "develop" : "production";
169
+ this.allowedOrigin = e ? window.location.origin : x[o];
170
+ const n = document.createElement("iframe"), i = o === "develop" ? l : u, r = o === "develop" ? "https://cdn-sdk-dev.paynext.com/index.cdn.js" : "https://cdn-sdk.paynext.com/index.cdn.js";
171
+ return e ? (console.log("[PayNext Iframe] Using inline iframe mode (embedded HTML)"), console.log("[PayNext Iframe] SDK will load from:", r), n.srcdoc = C(r)) : (console.log("[PayNext Iframe] Using CDN iframe mode"), console.log("[PayNext Iframe] Loading iframe from:", i), n.src = i), n.style.border = "none", n.style.width = "100%", n.style.minHeight = "400px", n.style.display = "block", n.allow = "payment *", n.sandbox.add(
82
172
  "allow-scripts",
83
173
  // required for SDK execution
84
174
  "allow-same-origin",
@@ -99,23 +189,35 @@ class l {
99
189
  for (; !this.isReady; ) {
100
190
  if (Date.now() - e > t)
101
191
  throw new Error("Iframe initialization timeout");
102
- await new Promise((n) => setTimeout(n, 100));
192
+ await new Promise((o) => setTimeout(o, 100));
103
193
  }
104
194
  }
105
195
  // mount checkout in iframe
106
196
  async mount(t, e) {
107
197
  try {
108
- const n = document.getElementById(t);
109
- if (!n)
198
+ const o = document.getElementById(t);
199
+ if (!o)
110
200
  throw new Error(`Container element "${t}" not found`);
111
- this.container = n, this.config = e, this.iframe && await this.unmount(), this.iframe = this.createIframe(e), window.addEventListener("message", this.handleMessage), n.innerHTML = "", n.appendChild(this.iframe), await new Promise((i, a) => {
112
- const c = setTimeout(() => {
113
- a(new Error("Iframe load timeout"));
201
+ this.container = o, this.config = e, this.iframe && await this.unmount();
202
+ const i = (e.environment?.toLowerCase()?.includes("develop") || e.environment?.toLowerCase()?.includes("staging") ? "develop" : "production") === "develop" ? l : u;
203
+ let r = !1;
204
+ try {
205
+ await fetch(i, {
206
+ method: "HEAD",
207
+ mode: "no-cors",
208
+ cache: "no-cache"
209
+ }), r = !1, console.log("[PayNext Iframe] CDN is available, using remote iframe");
210
+ } catch {
211
+ r = !0, console.warn("[PayNext Iframe] CDN not available, using inline fallback mode"), console.log("[PayNext Iframe] This is normal if CDN is not yet deployed or during local testing");
212
+ }
213
+ this.iframe = this.createIframe(e, r), window.addEventListener("message", this.handleMessage), o.innerHTML = "", o.appendChild(this.iframe), await new Promise((y, m) => {
214
+ const p = setTimeout(() => {
215
+ m(new Error("Iframe load timeout"));
114
216
  }, 1e4);
115
217
  this.iframe.onload = () => {
116
- clearTimeout(c), i();
218
+ clearTimeout(p), y();
117
219
  }, this.iframe.onerror = () => {
118
- clearTimeout(c), a(new Error("Iframe failed to load"));
220
+ clearTimeout(p), m(new Error("Iframe failed to load"));
119
221
  };
120
222
  }), await this.waitForReady(), await this.sendMessage("paynext:mount", {
121
223
  config: {
@@ -127,8 +229,8 @@ class l {
127
229
  onCheckoutFail: void 0
128
230
  }
129
231
  }), console.log("[PayNext Iframe] Checkout mounted successfully");
130
- } catch (n) {
131
- throw console.error("[PayNext Iframe] Mount failed:", n), await this.unmount(), n;
232
+ } catch (o) {
233
+ throw console.error("[PayNext Iframe] Mount failed:", o), await this.unmount(), o;
132
234
  }
133
235
  }
134
236
  // unmount checkout
@@ -159,45 +261,45 @@ class l {
159
261
  }
160
262
  // preload iframe (optional optimization)
161
263
  static async preload(t) {
162
- const n = (t?.toLowerCase()?.includes("develop") || t?.toLowerCase()?.includes("staging") ? "develop" : "production") === "develop" ? h : u, i = document.createElement("link");
163
- i.rel = "preconnect", i.href = new URL(n).origin, document.head.appendChild(i);
164
- const a = document.createElement("link");
165
- a.rel = "dns-prefetch", a.href = new URL(n).origin, document.head.appendChild(a);
264
+ const o = (t?.toLowerCase()?.includes("develop") || t?.toLowerCase()?.includes("staging") ? "develop" : "production") === "develop" ? l : u, n = document.createElement("link");
265
+ n.rel = "preconnect", n.href = new URL(o).origin, document.head.appendChild(n);
266
+ const i = document.createElement("link");
267
+ i.rel = "dns-prefetch", i.href = new URL(o).origin, document.head.appendChild(i);
166
268
  }
167
269
  }
168
- const P = {
169
- preload: l.preload
170
- }, y = "https://cdn-sdk-dev.paynext.com/index.cdn.js", g = "https://cdn-sdk.paynext.com/index.cdn.js";
171
- let r = null, d = null;
172
- async function m(o) {
270
+ const E = {
271
+ preload: h.preload
272
+ }, k = "https://cdn-sdk-dev.paynext.com/index.cdn.js", P = "https://cdn-sdk.paynext.com/index.cdn.js";
273
+ let d = null, c = null;
274
+ async function f(a) {
275
+ if (c)
276
+ return c;
173
277
  if (d)
174
278
  return d;
175
- if (r)
176
- return r;
177
- const t = o?.toLowerCase()?.includes("develop") || o?.toLowerCase()?.includes("staging") ? "develop" : "production";
178
- return r = new Promise((e, n) => {
279
+ const t = a?.toLowerCase()?.includes("develop") || a?.toLowerCase()?.includes("staging") ? "develop" : "production";
280
+ return d = new Promise((e, o) => {
179
281
  if (typeof window > "u") {
180
- n(new Error("PayNext SDK can only be loaded in browser environment"));
282
+ o(new Error("PayNext SDK can only be loaded in browser environment"));
181
283
  return;
182
284
  }
183
285
  if (window.PayNextSDK) {
184
- console.log("[PayNext CDN] SDK already loaded from cache"), d = window.PayNextSDK, e(d);
286
+ console.log("[PayNext CDN] SDK already loaded from cache"), c = window.PayNextSDK, e(c);
185
287
  return;
186
288
  }
187
- const i = document.createElement("script");
188
- i.src = t === "develop" ? y : g, i.crossOrigin = "*", i.async = !0, i.onload = () => {
189
- const a = window.PayNextSDK;
190
- if (!a) {
191
- console.error("[PayNext CDN] PayNextSDK not found in window after load"), r = null, document.head.removeChild(i), n(new Error("PayNextSDK not found in global scope after loading"));
289
+ const n = document.createElement("script");
290
+ n.src = t === "develop" ? k : P, n.crossOrigin = "*", n.async = !0, n.onload = () => {
291
+ const i = window.PayNextSDK;
292
+ if (!i) {
293
+ console.error("[PayNext CDN] PayNextSDK not found in window after load"), d = null, document.head.removeChild(n), o(new Error("PayNextSDK not found in global scope after loading"));
192
294
  return;
193
295
  }
194
- d = a, e(a);
195
- }, i.onerror = (a) => {
196
- console.error("[PayNext CDN] Failed to load script:", a), r = null, document.head.removeChild(i), n(new Error("Failed to load PayNext SDK script from CDN"));
197
- }, document.head.appendChild(i);
198
- }), r;
296
+ c = i, e(i);
297
+ }, n.onerror = (i) => {
298
+ console.error("[PayNext CDN] Failed to load script:", i), d = null, document.head.removeChild(n), o(new Error("Failed to load PayNext SDK script from CDN"));
299
+ }, document.head.appendChild(n);
300
+ }), d;
199
301
  }
200
- class C {
302
+ class I {
201
303
  constructor() {
202
304
  s(this, "cdnInstance", null);
203
305
  s(this, "iframeInstance", null);
@@ -207,7 +309,7 @@ class C {
207
309
  // initialize
208
310
  async initialize(t) {
209
311
  try {
210
- if (this.initPromise = await m(t), !this.initPromise.PayNextCheckout)
312
+ if (this.initPromise = await f(t), !this.initPromise.PayNextCheckout)
211
313
  throw new Error("PayNextCheckout not found in CDN module");
212
314
  this.cdnInstance = new this.initPromise.PayNextCheckout();
213
315
  } catch (e) {
@@ -223,7 +325,7 @@ class C {
223
325
  }
224
326
  // mount with iframe mode support
225
327
  async mount(t, e) {
226
- return e.iframeMode === !0 ? (this.mode = "iframe", this.iframeInstance || (this.iframeInstance = new l()), this.iframeInstance.mount(t, e)) : (this.mode = "direct", await this.initialize(e.environment), this.waitForReady(), this.cdnInstance?.mount(t, e));
328
+ return e.iframeMode === !0 ? (this.mode = "iframe", this.iframeInstance || (this.iframeInstance = new h()), this.iframeInstance.mount(t, e)) : (this.mode = "direct", await this.initialize(e.environment), this.waitForReady(), this.cdnInstance?.mount(t, e));
227
329
  }
228
330
  // unmount with mode detection
229
331
  async unmount() {
@@ -241,17 +343,17 @@ class C {
241
343
  }
242
344
  // preload (optional optimization)
243
345
  static async preload(t, e) {
244
- e?.iframeMode ? await l.preload(t) : await m(t);
346
+ e?.iframeMode ? await h.preload(t) : await f(t);
245
347
  }
246
348
  }
247
- const I = {
248
- preload: C.preload
349
+ const R = {
350
+ preload: I.preload
249
351
  };
250
- var x = /* @__PURE__ */ ((o) => (o.PAYPAL = "PAYPAL", o.APPLE_PAY = "APPLEPAY", o.GOOGLE_PAY = "GPAY", o.CARD = "CARD", o.VENMO = "VENMO", o))(x || {});
352
+ var v = /* @__PURE__ */ ((a) => (a.PAYPAL = "PAYPAL", a.APPLE_PAY = "APPLEPAY", a.GOOGLE_PAY = "GPAY", a.CARD = "CARD", a.VENMO = "VENMO", a))(v || {});
251
353
  export {
252
- C as PayNextCheckout,
253
- l as PayNextCheckoutIframe,
254
- I as PayNextSDK,
255
- P as PayNextSDKIframe,
256
- x as PaymentMethod
354
+ I as PayNextCheckout,
355
+ h as PayNextCheckoutIframe,
356
+ R as PayNextSDK,
357
+ E as PayNextSDKIframe,
358
+ v as PaymentMethod
257
359
  };
package/dist/index.umd.js CHANGED
@@ -1 +1,88 @@
1
- (function(a,s){typeof exports=="object"&&typeof module<"u"?s(exports):typeof define=="function"&&define.amd?define(["exports"],s):(a=typeof globalThis<"u"?globalThis:a||self,s(a["PayNext SDK"]={}))})(this,(function(a){"use strict";var k=Object.defineProperty;var I=(a,s,c)=>s in a?k(a,s,{enumerable:!0,configurable:!0,writable:!0,value:c}):a[s]=c;var r=(a,s,c)=>I(a,typeof s!="symbol"?s+"":s,c);const s="https://cdn-sdk-dev.paynext.com/checkout.html",c="https://cdn-sdk.paynext.com/checkout.html",w={develop:"https://cdn-sdk-dev.paynext.com",production:"https://cdn-sdk.paynext.com"};class u{constructor(){r(this,"iframe",null);r(this,"container",null);r(this,"config",null);r(this,"messageHandlers",new Map);r(this,"pendingRequests",new Map);r(this,"isReady",!1);r(this,"allowedOrigin","");this.handleMessage=this.handleMessage.bind(this)}generateRequestId(){return`${Date.now()}-${Math.random().toString(36).substr(2,9)}`}sendMessage(t,e){return new Promise((n,i)=>{if(!this.iframe||!this.iframe.contentWindow){i(new Error("Iframe not initialized"));return}const o=this.generateRequestId(),m={type:t,payload:e,requestId:o};this.pendingRequests.set(o,{resolve:n,reject:i}),setTimeout(()=>{this.pendingRequests.has(o)&&(this.pendingRequests.delete(o),i(new Error(`Request timeout: ${t}`)))},3e4),this.iframe.contentWindow.postMessage(m,this.allowedOrigin)})}handleMessage(t){if(t.origin!==this.allowedOrigin){console.warn("[PayNext Iframe] Message from unauthorized origin:",t.origin);return}const e=t.data;if(!(!e.type||!e.type.startsWith("paynext:"))){if(e.requestId&&this.pendingRequests.has(e.requestId)){const{resolve:n}=this.pendingRequests.get(e.requestId);this.pendingRequests.delete(e.requestId),n(e.payload);return}switch(e.type){case"paynext:ready":this.isReady=!0,console.log("[PayNext Iframe] SDK ready");break;case"paynext:loaded":this.config?.onCheckoutLoaded&&this.config.onCheckoutLoaded(e.payload);break;case"paynext:attempt":this.config?.onCheckoutAttempt&&this.config.onCheckoutAttempt(e.payload);break;case"paynext:complete":this.config?.onCheckoutComplete&&this.config.onCheckoutComplete(e.payload);break;case"paynext:fail":this.config?.onCheckoutFail&&this.config.onCheckoutFail(e.payload);break;case"paynext:error":console.error("[PayNext Iframe] Error:",e.payload);break;case"paynext:resize":this.iframe&&e.payload?.height&&(this.iframe.style.height=`${e.payload.height}px`);break;default:console.warn("[PayNext Iframe] Unknown message type:",e.type)}}}createIframe(t){const e=t.environment?.toLowerCase()?.includes("develop")||t.environment?.toLowerCase()?.includes("staging")?"develop":"production";this.allowedOrigin=w[e];const n=document.createElement("iframe"),i=e==="develop"?s:c;return n.src=i,n.style.border="none",n.style.width="100%",n.style.minHeight="400px",n.style.display="block",n.allow="payment *",n.sandbox.add("allow-scripts","allow-same-origin","allow-forms","allow-popups","allow-popups-to-escape-sandbox","allow-top-navigation-by-user-activation"),n.setAttribute("referrerpolicy","strict-origin-when-cross-origin"),n.setAttribute("importance","high"),n}async waitForReady(t=1e4){const e=Date.now();for(;!this.isReady;){if(Date.now()-e>t)throw new Error("Iframe initialization timeout");await new Promise(n=>setTimeout(n,100))}}async mount(t,e){try{const n=document.getElementById(t);if(!n)throw new Error(`Container element "${t}" not found`);this.container=n,this.config=e,this.iframe&&await this.unmount(),this.iframe=this.createIframe(e),window.addEventListener("message",this.handleMessage),n.innerHTML="",n.appendChild(this.iframe),await new Promise((i,o)=>{const m=setTimeout(()=>{o(new Error("Iframe load timeout"))},1e4);this.iframe.onload=()=>{clearTimeout(m),i()},this.iframe.onerror=()=>{clearTimeout(m),o(new Error("Iframe failed to load"))}}),await this.waitForReady(),await this.sendMessage("paynext:mount",{config:{...e,onCheckoutLoaded:void 0,onCheckoutAttempt:void 0,onCheckoutComplete:void 0,onCheckoutFail:void 0}}),console.log("[PayNext Iframe] Checkout mounted successfully")}catch(n){throw console.error("[PayNext Iframe] Mount failed:",n),await this.unmount(),n}}async unmount(){try{this.iframe&&(this.isReady&&await this.sendMessage("paynext:unmount").catch(()=>{}),this.iframe.parentNode&&this.iframe.parentNode.removeChild(this.iframe),this.iframe=null),window.removeEventListener("message",this.handleMessage),this.container&&(this.container.innerHTML="",this.container=null),this.isReady=!1,this.config=null,this.pendingRequests.clear(),this.messageHandlers.clear(),console.log("[PayNext Iframe] Checkout unmounted")}catch(t){console.error("[PayNext Iframe] Unmount failed:",t)}}async updateConfig(t){if(!this.isReady)throw new Error("Iframe not ready");this.config={...this.config,...t},await this.sendMessage("paynext:updateConfig",{config:{...t,onCheckoutLoaded:void 0,onCheckoutAttempt:void 0,onCheckoutComplete:void 0,onCheckoutFail:void 0}})}static async preload(t){const n=(t?.toLowerCase()?.includes("develop")||t?.toLowerCase()?.includes("staging")?"develop":"production")==="develop"?s:c,i=document.createElement("link");i.rel="preconnect",i.href=new URL(n).origin,document.head.appendChild(i);const o=document.createElement("link");o.rel="dns-prefetch",o.href=new URL(n).origin,document.head.appendChild(o)}}const g={preload:u.preload},C="https://cdn-sdk-dev.paynext.com/index.cdn.js",P="https://cdn-sdk.paynext.com/index.cdn.js";let l=null,h=null;async function f(d){if(h)return h;if(l)return l;const t=d?.toLowerCase()?.includes("develop")||d?.toLowerCase()?.includes("staging")?"develop":"production";return l=new Promise((e,n)=>{if(typeof window>"u"){n(new Error("PayNext SDK can only be loaded in browser environment"));return}if(window.PayNextSDK){console.log("[PayNext CDN] SDK already loaded from cache"),h=window.PayNextSDK,e(h);return}const i=document.createElement("script");i.src=t==="develop"?C:P,i.crossOrigin="*",i.async=!0,i.onload=()=>{const o=window.PayNextSDK;if(!o){console.error("[PayNext CDN] PayNextSDK not found in window after load"),l=null,document.head.removeChild(i),n(new Error("PayNextSDK not found in global scope after loading"));return}h=o,e(o)},i.onerror=o=>{console.error("[PayNext CDN] Failed to load script:",o),l=null,document.head.removeChild(i),n(new Error("Failed to load PayNext SDK script from CDN"))},document.head.appendChild(i)}),l}class p{constructor(){r(this,"cdnInstance",null);r(this,"iframeInstance",null);r(this,"initPromise",null);r(this,"mode","direct")}async initialize(t){try{if(this.initPromise=await f(t),!this.initPromise.PayNextCheckout)throw new Error("PayNextCheckout not found in CDN module");this.cdnInstance=new this.initPromise.PayNextCheckout}catch(e){throw console.error("[PayNext CDN] Failed to initialize:",e),e}}waitForReady(){if(!this.initPromise)throw new Error("SDK initialization not started");if(!this.cdnInstance)throw new Error("SDK instance not created")}async mount(t,e){return e.iframeMode===!0?(this.mode="iframe",this.iframeInstance||(this.iframeInstance=new u),this.iframeInstance.mount(t,e)):(this.mode="direct",await this.initialize(e.environment),this.waitForReady(),this.cdnInstance?.mount(t,e))}async unmount(){if(this.mode==="iframe"&&this.iframeInstance)return this.iframeInstance.unmount();if(this.waitForReady(),this.cdnInstance?.unmount)return this.cdnInstance.unmount()}async updateConfig(t){if(this.mode==="iframe"&&this.iframeInstance)return this.iframeInstance.updateConfig(t);if(this.waitForReady(),this.cdnInstance?.updateConfig)return this.cdnInstance.updateConfig(t)}static async preload(t,e){e?.iframeMode?await u.preload(t):await f(t)}}const x={preload:p.preload};var y=(d=>(d.PAYPAL="PAYPAL",d.APPLE_PAY="APPLEPAY",d.GOOGLE_PAY="GPAY",d.CARD="CARD",d.VENMO="VENMO",d))(y||{});a.PayNextCheckout=p,a.PayNextCheckoutIframe=u,a.PayNextSDK=x,a.PayNextSDKIframe=g,a.PaymentMethod=y,Object.defineProperty(a,Symbol.toStringTag,{value:"Module"})}));
1
+ (function(i,s){typeof exports=="object"&&typeof module<"u"?s(exports):typeof define=="function"&&define.amd?define(["exports"],s):(i=typeof globalThis<"u"?globalThis:i||self,s(i["PayNext SDK"]={}))})(this,(function(i){"use strict";var E=Object.defineProperty;var R=(i,s,c)=>s in i?E(i,s,{enumerable:!0,configurable:!0,writable:!0,value:c}):i[s]=c;var d=(i,s,c)=>R(i,typeof s!="symbol"?s+"":s,c);const s="https://cdn-sdk-dev.paynext.com/checkout.html",c="https://cdn-sdk.paynext.com/checkout.html",x={develop:"https://cdn-sdk-dev.paynext.com",production:"https://cdn-sdk.paynext.com"};function P(r){return`<!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>PayNext Checkout</title>
7
+ <style>
8
+ * { margin: 0; padding: 0; box-sizing: border-box; }
9
+ html, body { width: 100%; height: 100%; overflow: hidden; }
10
+ body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; }
11
+ #checkout-container { width: 100%; min-height: 100%; }
12
+ .loading { display: flex; align-items: center; justify-content: center; min-height: 400px; color: #666; font-size: 14px; }
13
+ .loading::after { content: '...'; animation: dots 1.5s steps(4, end) infinite; }
14
+ @keyframes dots { 0%, 20% { content: '.'; } 40% { content: '..'; } 60%, 100% { content: '...'; } }
15
+ </style>
16
+ </head>
17
+ <body>
18
+ <div id="checkout-container"><div class="loading">Loading checkout</div></div>
19
+ <script type="module">
20
+ const MessageType = {
21
+ MOUNT: 'paynext:mount', UNMOUNT: 'paynext:unmount', UPDATE_CONFIG: 'paynext:updateConfig',
22
+ READY: 'paynext:ready', LOADED: 'paynext:loaded', ATTEMPT: 'paynext:attempt',
23
+ COMPLETE: 'paynext:complete', FAIL: 'paynext:fail', ERROR: 'paynext:error', RESIZE: 'paynext:resize'
24
+ }
25
+ let checkout = null, parentOrigin = null, config = null
26
+
27
+ function sendToParent(type, payload, requestId) {
28
+ if (!parentOrigin) return
29
+ window.parent.postMessage({ type, payload, requestId }, parentOrigin)
30
+ }
31
+
32
+ window.addEventListener('message', async (event) => {
33
+ const data = event.data
34
+ if (!parentOrigin) parentOrigin = event.origin
35
+ if (event.origin !== parentOrigin || !data.type?.startsWith('paynext:')) return
36
+
37
+ try {
38
+ switch (data.type) {
39
+ case MessageType.MOUNT:
40
+ if (data.payload?.config) {
41
+ config = data.payload.config
42
+ const { PayNextCheckout } = await import('${r}')
43
+ checkout = new PayNextCheckout()
44
+ await checkout.mount('checkout-container', {
45
+ ...config,
46
+ onCheckoutLoaded: (result) => { sendToParent(MessageType.LOADED, result, data.requestId); notifyResize() },
47
+ onCheckoutAttempt: (result) => sendToParent(MessageType.ATTEMPT, result),
48
+ onCheckoutComplete: (result) => sendToParent(MessageType.COMPLETE, result),
49
+ onCheckoutFail: (error) => sendToParent(MessageType.FAIL, error)
50
+ })
51
+ sendToParent(MessageType.READY, { success: true }, data.requestId)
52
+ setupResizeObserver()
53
+ }
54
+ break
55
+ case MessageType.UNMOUNT:
56
+ if (checkout) { await checkout.unmount(); checkout = null }
57
+ sendToParent(MessageType.READY, { success: true }, data.requestId)
58
+ break
59
+ case MessageType.UPDATE_CONFIG:
60
+ if (checkout && data.payload?.config) {
61
+ await checkout.updateConfig(data.payload.config)
62
+ sendToParent(MessageType.READY, { success: true }, data.requestId)
63
+ }
64
+ break
65
+ }
66
+ } catch (error) {
67
+ sendToParent(MessageType.ERROR, { message: error.message, stack: error.stack }, data.requestId)
68
+ }
69
+ })
70
+
71
+ function setupResizeObserver() {
72
+ new ResizeObserver(entries => {
73
+ for (const entry of entries) sendToParent(MessageType.RESIZE, { height: entry.contentRect.height })
74
+ }).observe(document.body)
75
+ }
76
+
77
+ function notifyResize() { sendToParent(MessageType.RESIZE, { height: document.body.scrollHeight }) }
78
+
79
+ window.addEventListener('load', () => {
80
+ setTimeout(() => window.parent.postMessage({ type: MessageType.READY, payload: { success: true } }, '*'), 100)
81
+ })
82
+
83
+ window.addEventListener('error', (event) => {
84
+ sendToParent(MessageType.ERROR, { message: event.error?.message || 'Unknown error', stack: event.error?.stack })
85
+ })
86
+ <\/script>
87
+ </body>
88
+ </html>`}class m{constructor(){d(this,"iframe",null);d(this,"container",null);d(this,"config",null);d(this,"messageHandlers",new Map);d(this,"pendingRequests",new Map);d(this,"isReady",!1);d(this,"allowedOrigin","");this.handleMessage=this.handleMessage.bind(this)}generateRequestId(){return`${Date.now()}-${Math.random().toString(36).substr(2,9)}`}sendMessage(t,e){return new Promise((o,n)=>{if(!this.iframe||!this.iframe.contentWindow){n(new Error("Iframe not initialized"));return}const a=this.generateRequestId(),l={type:t,payload:e,requestId:a};this.pendingRequests.set(a,{resolve:o,reject:n}),setTimeout(()=>{this.pendingRequests.has(a)&&(this.pendingRequests.delete(a),n(new Error(`Request timeout: ${t}`)))},3e4),this.iframe.contentWindow.postMessage(l,this.allowedOrigin)})}handleMessage(t){if(t.origin!==this.allowedOrigin){console.warn("[PayNext Iframe] Message from unauthorized origin:",t.origin);return}const e=t.data;if(!(!e.type||!e.type.startsWith("paynext:"))){if(e.requestId&&this.pendingRequests.has(e.requestId)){const{resolve:o}=this.pendingRequests.get(e.requestId);this.pendingRequests.delete(e.requestId),o(e.payload);return}switch(e.type){case"paynext:ready":this.isReady=!0,console.log("[PayNext Iframe] SDK ready");break;case"paynext:loaded":this.config?.onCheckoutLoaded&&this.config.onCheckoutLoaded(e.payload);break;case"paynext:attempt":this.config?.onCheckoutAttempt&&this.config.onCheckoutAttempt(e.payload);break;case"paynext:complete":this.config?.onCheckoutComplete&&this.config.onCheckoutComplete(e.payload);break;case"paynext:fail":this.config?.onCheckoutFail&&this.config.onCheckoutFail(e.payload);break;case"paynext:error":console.error("[PayNext Iframe] Error:",e.payload);break;case"paynext:resize":this.iframe&&e.payload?.height&&(this.iframe.style.height=`${e.payload.height}px`);break;default:console.warn("[PayNext Iframe] Unknown message type:",e.type)}}}createIframe(t,e=!1){const o=t.environment?.toLowerCase()?.includes("develop")||t.environment?.toLowerCase()?.includes("staging")?"develop":"production";this.allowedOrigin=e?window.location.origin:x[o];const n=document.createElement("iframe"),a=o==="develop"?s:c,l=o==="develop"?"https://cdn-sdk-dev.paynext.com/index.cdn.js":"https://cdn-sdk.paynext.com/index.cdn.js";return e?(console.log("[PayNext Iframe] Using inline iframe mode (embedded HTML)"),console.log("[PayNext Iframe] SDK will load from:",l),n.srcdoc=P(l)):(console.log("[PayNext Iframe] Using CDN iframe mode"),console.log("[PayNext Iframe] Loading iframe from:",a),n.src=a),n.style.border="none",n.style.width="100%",n.style.minHeight="400px",n.style.display="block",n.allow="payment *",n.sandbox.add("allow-scripts","allow-same-origin","allow-forms","allow-popups","allow-popups-to-escape-sandbox","allow-top-navigation-by-user-activation"),n.setAttribute("referrerpolicy","strict-origin-when-cross-origin"),n.setAttribute("importance","high"),n}async waitForReady(t=1e4){const e=Date.now();for(;!this.isReady;){if(Date.now()-e>t)throw new Error("Iframe initialization timeout");await new Promise(o=>setTimeout(o,100))}}async mount(t,e){try{const o=document.getElementById(t);if(!o)throw new Error(`Container element "${t}" not found`);this.container=o,this.config=e,this.iframe&&await this.unmount();const a=(e.environment?.toLowerCase()?.includes("develop")||e.environment?.toLowerCase()?.includes("staging")?"develop":"production")==="develop"?s:c;let l=!1;try{await fetch(a,{method:"HEAD",mode:"no-cors",cache:"no-cache"}),l=!1,console.log("[PayNext Iframe] CDN is available, using remote iframe")}catch{l=!0,console.warn("[PayNext Iframe] CDN not available, using inline fallback mode"),console.log("[PayNext Iframe] This is normal if CDN is not yet deployed or during local testing")}this.iframe=this.createIframe(e,l),window.addEventListener("message",this.handleMessage),o.innerHTML="",o.appendChild(this.iframe),await new Promise((v,g)=>{const w=setTimeout(()=>{g(new Error("Iframe load timeout"))},1e4);this.iframe.onload=()=>{clearTimeout(w),v()},this.iframe.onerror=()=>{clearTimeout(w),g(new Error("Iframe failed to load"))}}),await this.waitForReady(),await this.sendMessage("paynext:mount",{config:{...e,onCheckoutLoaded:void 0,onCheckoutAttempt:void 0,onCheckoutComplete:void 0,onCheckoutFail:void 0}}),console.log("[PayNext Iframe] Checkout mounted successfully")}catch(o){throw console.error("[PayNext Iframe] Mount failed:",o),await this.unmount(),o}}async unmount(){try{this.iframe&&(this.isReady&&await this.sendMessage("paynext:unmount").catch(()=>{}),this.iframe.parentNode&&this.iframe.parentNode.removeChild(this.iframe),this.iframe=null),window.removeEventListener("message",this.handleMessage),this.container&&(this.container.innerHTML="",this.container=null),this.isReady=!1,this.config=null,this.pendingRequests.clear(),this.messageHandlers.clear(),console.log("[PayNext Iframe] Checkout unmounted")}catch(t){console.error("[PayNext Iframe] Unmount failed:",t)}}async updateConfig(t){if(!this.isReady)throw new Error("Iframe not ready");this.config={...this.config,...t},await this.sendMessage("paynext:updateConfig",{config:{...t,onCheckoutLoaded:void 0,onCheckoutAttempt:void 0,onCheckoutComplete:void 0,onCheckoutFail:void 0}})}static async preload(t){const o=(t?.toLowerCase()?.includes("develop")||t?.toLowerCase()?.includes("staging")?"develop":"production")==="develop"?s:c,n=document.createElement("link");n.rel="preconnect",n.href=new URL(o).origin,document.head.appendChild(n);const a=document.createElement("link");a.rel="dns-prefetch",a.href=new URL(o).origin,document.head.appendChild(a)}}const C={preload:m.preload},k="https://cdn-sdk-dev.paynext.com/index.cdn.js",I="https://cdn-sdk.paynext.com/index.cdn.js";let u=null,h=null;async function p(r){if(h)return h;if(u)return u;const t=r?.toLowerCase()?.includes("develop")||r?.toLowerCase()?.includes("staging")?"develop":"production";return u=new Promise((e,o)=>{if(typeof window>"u"){o(new Error("PayNext SDK can only be loaded in browser environment"));return}if(window.PayNextSDK){console.log("[PayNext CDN] SDK already loaded from cache"),h=window.PayNextSDK,e(h);return}const n=document.createElement("script");n.src=t==="develop"?k:I,n.crossOrigin="*",n.async=!0,n.onload=()=>{const a=window.PayNextSDK;if(!a){console.error("[PayNext CDN] PayNextSDK not found in window after load"),u=null,document.head.removeChild(n),o(new Error("PayNextSDK not found in global scope after loading"));return}h=a,e(a)},n.onerror=a=>{console.error("[PayNext CDN] Failed to load script:",a),u=null,document.head.removeChild(n),o(new Error("Failed to load PayNext SDK script from CDN"))},document.head.appendChild(n)}),u}class f{constructor(){d(this,"cdnInstance",null);d(this,"iframeInstance",null);d(this,"initPromise",null);d(this,"mode","direct")}async initialize(t){try{if(this.initPromise=await p(t),!this.initPromise.PayNextCheckout)throw new Error("PayNextCheckout not found in CDN module");this.cdnInstance=new this.initPromise.PayNextCheckout}catch(e){throw console.error("[PayNext CDN] Failed to initialize:",e),e}}waitForReady(){if(!this.initPromise)throw new Error("SDK initialization not started");if(!this.cdnInstance)throw new Error("SDK instance not created")}async mount(t,e){return e.iframeMode===!0?(this.mode="iframe",this.iframeInstance||(this.iframeInstance=new m),this.iframeInstance.mount(t,e)):(this.mode="direct",await this.initialize(e.environment),this.waitForReady(),this.cdnInstance?.mount(t,e))}async unmount(){if(this.mode==="iframe"&&this.iframeInstance)return this.iframeInstance.unmount();if(this.waitForReady(),this.cdnInstance?.unmount)return this.cdnInstance.unmount()}async updateConfig(t){if(this.mode==="iframe"&&this.iframeInstance)return this.iframeInstance.updateConfig(t);if(this.waitForReady(),this.cdnInstance?.updateConfig)return this.cdnInstance.updateConfig(t)}static async preload(t,e){e?.iframeMode?await m.preload(t):await p(t)}}const N={preload:f.preload};var y=(r=>(r.PAYPAL="PAYPAL",r.APPLE_PAY="APPLEPAY",r.GOOGLE_PAY="GPAY",r.CARD="CARD",r.VENMO="VENMO",r))(y||{});i.PayNextCheckout=f,i.PayNextCheckoutIframe=m,i.PayNextSDK=N,i.PayNextSDKIframe=C,i.PaymentMethod=y,Object.defineProperty(i,Symbol.toStringTag,{value:"Module"})}));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@paynext/sdk",
3
- "version": "0.0.168",
3
+ "version": "0.0.169",
4
4
  "description": "PayNext SDK - Payment processing with automatic CDN loading",
5
5
  "type": "module",
6
6
  "main": "dist/index.es.js",