@paynext/sdk 0.0.168 → 0.0.170
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 +3 -46
- package/dist/index.es.js +169 -65
- package/dist/index.umd.js +88 -1
- package/package.json +1 -1
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
|
|
104
|
+
interface PayNextConfig {
|
|
120
105
|
clientToken: string
|
|
121
106
|
apiVersion: string
|
|
122
|
-
environment: '
|
|
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
|
-
|
|
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
|
|
2
|
-
var
|
|
3
|
-
var s = (
|
|
4
|
-
const
|
|
1
|
+
var w = Object.defineProperty;
|
|
2
|
+
var x = (a, t, e) => t in a ? w(a, t, { enumerable: !0, configurable: !0, writable: !0, value: e }) : a[t] = e;
|
|
3
|
+
var s = (a, t, e) => x(a, typeof t != "symbol" ? t + "" : t, e);
|
|
4
|
+
const m = "https://cdn-sdk-dev.paynext.com/checkout.html", p = "https://cdn-sdk.paynext.com/checkout.html", C = {
|
|
5
5
|
develop: "https://cdn-sdk-dev.paynext.com",
|
|
6
6
|
production: "https://cdn-sdk.paynext.com"
|
|
7
7
|
};
|
|
8
|
-
|
|
8
|
+
function k(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 f {
|
|
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((
|
|
115
|
+
return new Promise((o, n) => {
|
|
26
116
|
if (!this.iframe || !this.iframe.contentWindow) {
|
|
27
|
-
|
|
117
|
+
n(new Error("Iframe not initialized"));
|
|
28
118
|
return;
|
|
29
119
|
}
|
|
30
|
-
const
|
|
31
|
-
this.pendingRequests.set(
|
|
32
|
-
this.pendingRequests.has(
|
|
33
|
-
}, 3e4), this.iframe.contentWindow.postMessage(
|
|
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:
|
|
46
|
-
this.pendingRequests.delete(e.requestId),
|
|
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
|
|
79
|
-
this.allowedOrigin =
|
|
80
|
-
const n = document.createElement("iframe"), i =
|
|
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 : C[o];
|
|
170
|
+
const n = document.createElement("iframe"), i = o === "develop" ? m : p, 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 = k(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,37 @@ 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((
|
|
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
|
|
109
|
-
if (!
|
|
198
|
+
const o = document.getElementById(t);
|
|
199
|
+
if (!o)
|
|
110
200
|
throw new Error(`Container element "${t}" not found`);
|
|
111
|
-
this.container =
|
|
112
|
-
|
|
113
|
-
|
|
201
|
+
this.container = o, this.config = e, this.iframe && await this.unmount();
|
|
202
|
+
const n = e.environment?.toLowerCase()?.includes("develop") || e.environment?.toLowerCase()?.includes("staging") ? "develop" : "production", i = n === "develop" ? m : p, r = window.location.hostname === "localhost" || window.location.hostname === "127.0.0.1" || window.location.hostname.startsWith("192.168.") || window.location.hostname.endsWith(".local");
|
|
203
|
+
let l = r;
|
|
204
|
+
if (!r && n === "develop")
|
|
205
|
+
try {
|
|
206
|
+
const u = new AbortController(), h = setTimeout(() => u.abort(), 2e3);
|
|
207
|
+
await fetch(i, {
|
|
208
|
+
method: "HEAD",
|
|
209
|
+
signal: u.signal,
|
|
210
|
+
cache: "no-cache"
|
|
211
|
+
}), clearTimeout(h), l = !1, console.log("[PayNext Iframe] CDN is available, using remote iframe");
|
|
212
|
+
} catch {
|
|
213
|
+
l = !0, console.warn("[PayNext Iframe] CDN not available, using inline fallback mode");
|
|
214
|
+
}
|
|
215
|
+
l && (console.log("[PayNext Iframe] Using inline fallback mode"), console.log("[PayNext Iframe] Reason:", r ? "localhost detected" : "CDN not available")), this.iframe = this.createIframe(e, l), window.addEventListener("message", this.handleMessage), o.innerHTML = "", o.appendChild(this.iframe), await new Promise((u, h) => {
|
|
216
|
+
const y = setTimeout(() => {
|
|
217
|
+
h(new Error("Iframe load timeout"));
|
|
114
218
|
}, 1e4);
|
|
115
219
|
this.iframe.onload = () => {
|
|
116
|
-
clearTimeout(
|
|
220
|
+
clearTimeout(y), u();
|
|
117
221
|
}, this.iframe.onerror = () => {
|
|
118
|
-
clearTimeout(
|
|
222
|
+
clearTimeout(y), h(new Error("Iframe failed to load"));
|
|
119
223
|
};
|
|
120
224
|
}), await this.waitForReady(), await this.sendMessage("paynext:mount", {
|
|
121
225
|
config: {
|
|
@@ -127,8 +231,8 @@ class l {
|
|
|
127
231
|
onCheckoutFail: void 0
|
|
128
232
|
}
|
|
129
233
|
}), console.log("[PayNext Iframe] Checkout mounted successfully");
|
|
130
|
-
} catch (
|
|
131
|
-
throw console.error("[PayNext Iframe] Mount failed:",
|
|
234
|
+
} catch (o) {
|
|
235
|
+
throw console.error("[PayNext Iframe] Mount failed:", o), await this.unmount(), o;
|
|
132
236
|
}
|
|
133
237
|
}
|
|
134
238
|
// unmount checkout
|
|
@@ -159,45 +263,45 @@ class l {
|
|
|
159
263
|
}
|
|
160
264
|
// preload iframe (optional optimization)
|
|
161
265
|
static async preload(t) {
|
|
162
|
-
const
|
|
163
|
-
|
|
164
|
-
const
|
|
165
|
-
|
|
266
|
+
const o = (t?.toLowerCase()?.includes("develop") || t?.toLowerCase()?.includes("staging") ? "develop" : "production") === "develop" ? m : p, n = document.createElement("link");
|
|
267
|
+
n.rel = "preconnect", n.href = new URL(o).origin, document.head.appendChild(n);
|
|
268
|
+
const i = document.createElement("link");
|
|
269
|
+
i.rel = "dns-prefetch", i.href = new URL(o).origin, document.head.appendChild(i);
|
|
166
270
|
}
|
|
167
271
|
}
|
|
168
|
-
const
|
|
169
|
-
preload:
|
|
170
|
-
},
|
|
171
|
-
let
|
|
172
|
-
async function
|
|
272
|
+
const E = {
|
|
273
|
+
preload: f.preload
|
|
274
|
+
}, P = "https://cdn-sdk-dev.paynext.com/index.cdn.js", I = "https://cdn-sdk.paynext.com/index.cdn.js";
|
|
275
|
+
let d = null, c = null;
|
|
276
|
+
async function g(a) {
|
|
277
|
+
if (c)
|
|
278
|
+
return c;
|
|
173
279
|
if (d)
|
|
174
280
|
return d;
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
const t = o?.toLowerCase()?.includes("develop") || o?.toLowerCase()?.includes("staging") ? "develop" : "production";
|
|
178
|
-
return r = new Promise((e, n) => {
|
|
281
|
+
const t = a?.toLowerCase()?.includes("develop") || a?.toLowerCase()?.includes("staging") ? "develop" : "production";
|
|
282
|
+
return d = new Promise((e, o) => {
|
|
179
283
|
if (typeof window > "u") {
|
|
180
|
-
|
|
284
|
+
o(new Error("PayNext SDK can only be loaded in browser environment"));
|
|
181
285
|
return;
|
|
182
286
|
}
|
|
183
287
|
if (window.PayNextSDK) {
|
|
184
|
-
console.log("[PayNext CDN] SDK already loaded from cache"),
|
|
288
|
+
console.log("[PayNext CDN] SDK already loaded from cache"), c = window.PayNextSDK, e(c);
|
|
185
289
|
return;
|
|
186
290
|
}
|
|
187
|
-
const
|
|
188
|
-
|
|
189
|
-
const
|
|
190
|
-
if (!
|
|
191
|
-
console.error("[PayNext CDN] PayNextSDK not found in window after load"),
|
|
291
|
+
const n = document.createElement("script");
|
|
292
|
+
n.src = t === "develop" ? P : I, n.crossOrigin = "*", n.async = !0, n.onload = () => {
|
|
293
|
+
const i = window.PayNextSDK;
|
|
294
|
+
if (!i) {
|
|
295
|
+
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
296
|
return;
|
|
193
297
|
}
|
|
194
|
-
|
|
195
|
-
},
|
|
196
|
-
console.error("[PayNext CDN] Failed to load script:",
|
|
197
|
-
}, document.head.appendChild(
|
|
198
|
-
}),
|
|
298
|
+
c = i, e(i);
|
|
299
|
+
}, n.onerror = (i) => {
|
|
300
|
+
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"));
|
|
301
|
+
}, document.head.appendChild(n);
|
|
302
|
+
}), d;
|
|
199
303
|
}
|
|
200
|
-
class
|
|
304
|
+
class v {
|
|
201
305
|
constructor() {
|
|
202
306
|
s(this, "cdnInstance", null);
|
|
203
307
|
s(this, "iframeInstance", null);
|
|
@@ -207,7 +311,7 @@ class C {
|
|
|
207
311
|
// initialize
|
|
208
312
|
async initialize(t) {
|
|
209
313
|
try {
|
|
210
|
-
if (this.initPromise = await
|
|
314
|
+
if (this.initPromise = await g(t), !this.initPromise.PayNextCheckout)
|
|
211
315
|
throw new Error("PayNextCheckout not found in CDN module");
|
|
212
316
|
this.cdnInstance = new this.initPromise.PayNextCheckout();
|
|
213
317
|
} catch (e) {
|
|
@@ -223,7 +327,7 @@ class C {
|
|
|
223
327
|
}
|
|
224
328
|
// mount with iframe mode support
|
|
225
329
|
async mount(t, e) {
|
|
226
|
-
return e.iframeMode === !0 ? (this.mode = "iframe", this.iframeInstance || (this.iframeInstance = new
|
|
330
|
+
return e.iframeMode === !0 ? (this.mode = "iframe", this.iframeInstance || (this.iframeInstance = new f()), this.iframeInstance.mount(t, e)) : (this.mode = "direct", await this.initialize(e.environment), this.waitForReady(), this.cdnInstance?.mount(t, e));
|
|
227
331
|
}
|
|
228
332
|
// unmount with mode detection
|
|
229
333
|
async unmount() {
|
|
@@ -241,17 +345,17 @@ class C {
|
|
|
241
345
|
}
|
|
242
346
|
// preload (optional optimization)
|
|
243
347
|
static async preload(t, e) {
|
|
244
|
-
e?.iframeMode ? await
|
|
348
|
+
e?.iframeMode ? await f.preload(t) : await g(t);
|
|
245
349
|
}
|
|
246
350
|
}
|
|
247
|
-
const
|
|
248
|
-
preload:
|
|
351
|
+
const T = {
|
|
352
|
+
preload: v.preload
|
|
249
353
|
};
|
|
250
|
-
var
|
|
354
|
+
var N = /* @__PURE__ */ ((a) => (a.PAYPAL = "PAYPAL", a.APPLE_PAY = "APPLEPAY", a.GOOGLE_PAY = "GPAY", a.CARD = "CARD", a.VENMO = "VENMO", a))(N || {});
|
|
251
355
|
export {
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
356
|
+
v as PayNextCheckout,
|
|
357
|
+
f as PayNextCheckoutIframe,
|
|
358
|
+
T as PayNextSDK,
|
|
359
|
+
E as PayNextSDKIframe,
|
|
360
|
+
N as PaymentMethod
|
|
257
361
|
};
|
package/dist/index.umd.js
CHANGED
|
@@ -1 +1,88 @@
|
|
|
1
|
-
(function(
|
|
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 T=(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)=>T(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",C={develop:"https://cdn-sdk-dev.paynext.com",production:"https://cdn-sdk.paynext.com"};function k(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:C[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=k(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 n=e.environment?.toLowerCase()?.includes("develop")||e.environment?.toLowerCase()?.includes("staging")?"develop":"production",a=n==="develop"?s:c,l=window.location.hostname==="localhost"||window.location.hostname==="127.0.0.1"||window.location.hostname.startsWith("192.168.")||window.location.hostname.endsWith(".local");let p=l;if(!l&&n==="develop")try{const f=new AbortController,y=setTimeout(()=>f.abort(),2e3);await fetch(a,{method:"HEAD",signal:f.signal,cache:"no-cache"}),clearTimeout(y),p=!1,console.log("[PayNext Iframe] CDN is available, using remote iframe")}catch{p=!0,console.warn("[PayNext Iframe] CDN not available, using inline fallback mode")}p&&(console.log("[PayNext Iframe] Using inline fallback mode"),console.log("[PayNext Iframe] Reason:",l?"localhost detected":"CDN not available")),this.iframe=this.createIframe(e,p),window.addEventListener("message",this.handleMessage),o.innerHTML="",o.appendChild(this.iframe),await new Promise((f,y)=>{const P=setTimeout(()=>{y(new Error("Iframe load timeout"))},1e4);this.iframe.onload=()=>{clearTimeout(P),f()},this.iframe.onerror=()=>{clearTimeout(P),y(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 I={preload:m.preload},N="https://cdn-sdk-dev.paynext.com/index.cdn.js",v="https://cdn-sdk.paynext.com/index.cdn.js";let u=null,h=null;async function g(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"?N:v,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 w{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 g(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 g(t)}}const R={preload:w.preload};var x=(r=>(r.PAYPAL="PAYPAL",r.APPLE_PAY="APPLEPAY",r.GOOGLE_PAY="GPAY",r.CARD="CARD",r.VENMO="VENMO",r))(x||{});i.PayNextCheckout=w,i.PayNextCheckoutIframe=m,i.PayNextSDK=R,i.PayNextSDKIframe=I,i.PaymentMethod=x,Object.defineProperty(i,Symbol.toStringTag,{value:"Module"})}));
|