@paynext/sdk 0.0.165 → 0.0.167

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,6 +2,21 @@
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
+
5
20
  ## Installation
6
21
 
7
22
  ```bash
@@ -101,10 +116,11 @@ export const CheckoutComponent: FC<Readonly<IProps>> = (props) => {
101
116
  ## Configuration
102
117
 
103
118
  ```typescript
104
- interface PayNextConfig {
119
+ interface PayNextConfigExtended extends PayNextConfig {
105
120
  clientToken: string
106
121
  apiVersion: string
107
- environment: 'sandbox' | 'production',
122
+ environment: 'develop' | 'staging' | 'sandbox' | 'production'
123
+ iframeMode?: boolean // 🛡️ Enable PCI-compliant iframe mode (default: false)
108
124
  variant?: 'default' | 'compact'
109
125
  locale?: Locale
110
126
  translate?: CheckoutTranslate
@@ -194,4 +210,31 @@ The SDK supports recent versions of all major browsers:
194
210
 
195
211
  ## Security
196
212
 
197
- PCI DSS compliant with end-to-end encryption. No sensitive payment data is stored locally.
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)
@@ -0,0 +1,244 @@
1
+ <!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 - Iframe Mode</title>
7
+
8
+ <!-- Security headers -->
9
+ <meta http-equiv="X-Content-Type-Options" content="nosniff">
10
+ <meta http-equiv="X-Frame-Options" content="ALLOW-FROM *">
11
+ <meta http-equiv="Referrer-Policy" content="strict-origin-when-cross-origin">
12
+
13
+ <!-- CSP for iframe -->
14
+ <meta http-equiv="Content-Security-Policy"
15
+ content="default-src 'self'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline'; connect-src *; frame-ancestors *;">
16
+
17
+ <style>
18
+ * {
19
+ margin: 0;
20
+ padding: 0;
21
+ box-sizing: border-box;
22
+ }
23
+
24
+ html, body {
25
+ width: 100%;
26
+ height: 100%;
27
+ overflow: hidden;
28
+ }
29
+
30
+ body {
31
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, sans-serif;
32
+ }
33
+
34
+ #checkout-container {
35
+ width: 100%;
36
+ min-height: 100%;
37
+ }
38
+
39
+ /* Loading state */
40
+ .loading {
41
+ display: flex;
42
+ align-items: center;
43
+ justify-content: center;
44
+ min-height: 400px;
45
+ color: #666;
46
+ font-size: 14px;
47
+ }
48
+
49
+ .loading::after {
50
+ content: '...';
51
+ animation: dots 1.5s steps(4, end) infinite;
52
+ }
53
+
54
+ @keyframes dots {
55
+ 0%, 20% { content: '.'; }
56
+ 40% { content: '..'; }
57
+ 60%, 100% { content: '...'; }
58
+ }
59
+ </style>
60
+ </head>
61
+ <body>
62
+ <div id="checkout-container">
63
+ <div class="loading">Loading checkout</div>
64
+ </div>
65
+
66
+ <script type="module">
67
+ // Message types for parent communication
68
+ const MessageType = {
69
+ INIT: 'paynext:init',
70
+ MOUNT: 'paynext:mount',
71
+ UNMOUNT: 'paynext:unmount',
72
+ UPDATE_CONFIG: 'paynext:updateConfig',
73
+ READY: 'paynext:ready',
74
+ LOADED: 'paynext:loaded',
75
+ ATTEMPT: 'paynext:attempt',
76
+ COMPLETE: 'paynext:complete',
77
+ FAIL: 'paynext:fail',
78
+ ERROR: 'paynext:error',
79
+ RESIZE: 'paynext:resize'
80
+ }
81
+
82
+ let checkout = null
83
+ let parentOrigin = null
84
+ let config = null
85
+
86
+ // Send message to parent
87
+ function sendToParent(type, payload, requestId) {
88
+ if (!parentOrigin) {
89
+ console.error('[Iframe] Parent origin not set')
90
+ return
91
+ }
92
+
93
+ window.parent.postMessage(
94
+ { type, payload, requestId },
95
+ parentOrigin
96
+ )
97
+ }
98
+
99
+ // Handle messages from parent
100
+ window.addEventListener('message', async (event) => {
101
+ const data = event.data
102
+
103
+ // Store parent origin on first message
104
+ if (!parentOrigin) {
105
+ parentOrigin = event.origin
106
+ console.log('[Iframe] Parent origin set to:', parentOrigin)
107
+ }
108
+
109
+ // Validate origin
110
+ if (event.origin !== parentOrigin) {
111
+ console.warn('[Iframe] Message from unauthorized origin:', event.origin)
112
+ return
113
+ }
114
+
115
+ if (!data.type || !data.type.startsWith('paynext:')) {
116
+ return
117
+ }
118
+
119
+ try {
120
+ switch (data.type) {
121
+ case MessageType.MOUNT:
122
+ console.log('[Iframe] Mount request received')
123
+
124
+ if (data.payload?.config) {
125
+ config = data.payload.config
126
+
127
+ // Import and initialize SDK
128
+ const { PayNextCheckout } = await import('./index.cdn.js')
129
+
130
+ checkout = new PayNextCheckout()
131
+
132
+ // Mount checkout
133
+ await checkout.mount('checkout-container', {
134
+ ...config,
135
+ // Override callbacks to send messages to parent
136
+ onCheckoutLoaded: (result) => {
137
+ sendToParent(MessageType.LOADED, result, data.requestId)
138
+ notifyResize()
139
+ },
140
+ onCheckoutAttempt: (result) => {
141
+ sendToParent(MessageType.ATTEMPT, result)
142
+ },
143
+ onCheckoutComplete: (result) => {
144
+ sendToParent(MessageType.COMPLETE, result)
145
+ },
146
+ onCheckoutFail: (error) => {
147
+ sendToParent(MessageType.FAIL, error)
148
+ }
149
+ })
150
+
151
+ // Send success response
152
+ sendToParent(MessageType.READY, { success: true }, data.requestId)
153
+
154
+ // Setup resize observer
155
+ setupResizeObserver()
156
+ }
157
+ break
158
+
159
+ case MessageType.UNMOUNT:
160
+ console.log('[Iframe] Unmount request received')
161
+
162
+ if (checkout) {
163
+ await checkout.unmount()
164
+ checkout = null
165
+ }
166
+
167
+ sendToParent(MessageType.READY, { success: true }, data.requestId)
168
+ break
169
+
170
+ case MessageType.UPDATE_CONFIG:
171
+ console.log('[Iframe] Update config request received')
172
+
173
+ if (checkout && data.payload?.config) {
174
+ await checkout.updateConfig(data.payload.config)
175
+ sendToParent(MessageType.READY, { success: true }, data.requestId)
176
+ }
177
+ break
178
+
179
+ default:
180
+ console.warn('[Iframe] Unknown message type:', data.type)
181
+ }
182
+ } catch (error) {
183
+ console.error('[Iframe] Error handling message:', error)
184
+ sendToParent(MessageType.ERROR, {
185
+ message: error.message,
186
+ stack: error.stack
187
+ }, data.requestId)
188
+ }
189
+ })
190
+
191
+ // Setup resize observer to notify parent of height changes
192
+ function setupResizeObserver() {
193
+ const resizeObserver = new ResizeObserver((entries) => {
194
+ for (const entry of entries) {
195
+ const height = entry.contentRect.height
196
+ sendToParent(MessageType.RESIZE, { height })
197
+ }
198
+ })
199
+
200
+ resizeObserver.observe(document.body)
201
+ }
202
+
203
+ // Notify parent of current height
204
+ function notifyResize() {
205
+ const height = document.body.scrollHeight
206
+ sendToParent(MessageType.RESIZE, { height })
207
+ }
208
+
209
+ // Send ready message when iframe loads
210
+ window.addEventListener('load', () => {
211
+ console.log('[Iframe] Iframe loaded and ready')
212
+
213
+ // Wait a bit for parent to setup message listener
214
+ setTimeout(() => {
215
+ // Send ready to any origin initially
216
+ window.parent.postMessage(
217
+ { type: MessageType.READY, payload: { success: true } },
218
+ '*'
219
+ )
220
+ }, 100)
221
+ })
222
+
223
+ // Log errors
224
+ window.addEventListener('error', (event) => {
225
+ console.error('[Iframe] Runtime error:', event.error)
226
+ sendToParent(MessageType.ERROR, {
227
+ message: event.error?.message || 'Unknown error',
228
+ stack: event.error?.stack
229
+ })
230
+ })
231
+
232
+ window.addEventListener('unhandledrejection', (event) => {
233
+ console.error('[Iframe] Unhandled promise rejection:', event.reason)
234
+ sendToParent(MessageType.ERROR, {
235
+ message: event.reason?.message || 'Unhandled promise rejection',
236
+ stack: event.reason?.stack
237
+ })
238
+ })
239
+
240
+ console.log('[Iframe] PayNext Checkout iframe initialized')
241
+ </script>
242
+ </body>
243
+ </html>
244
+
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@paynext/sdk",
3
- "version": "0.0.165",
3
+ "version": "0.0.167",
4
4
  "description": "PayNext SDK - Payment processing with automatic CDN loading",
5
5
  "type": "module",
6
6
  "main": "dist/index.es.js",