@klingis/sdk 0.0.0

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 ADDED
@@ -0,0 +1,62 @@
1
+ # @klingis/sdk
2
+
3
+ Embeddable checkout SDK for [Kling](https://kling.is) — a payments platform for Iceland.
4
+
5
+ ## Install
6
+
7
+ ```bash
8
+ npm install @klingis/sdk
9
+ ```
10
+
11
+ ## Usage
12
+
13
+ ```typescript
14
+ import { Kling } from '@klingis/sdk';
15
+
16
+ // 1. Initialize
17
+ const kling = Kling.init({ locale: 'is' });
18
+
19
+ // 2. Open checkout (sessionId comes from your server via the Kling API)
20
+ const result = await kling.checkout({
21
+ sessionId: 'cs_...',
22
+ onSuccess: (res) => console.log('Paid!', res.paymentIntentId),
23
+ onError: (err) => console.log('Failed', err.message),
24
+ onCancel: () => console.log('Cancelled'),
25
+ });
26
+ ```
27
+
28
+ ## Options
29
+
30
+ ### `Kling.init(options)`
31
+
32
+ | Option | Type | Default | Description |
33
+ |--------|------|---------|-------------|
34
+ | `locale` | `'is' \| 'en'` | auto-detect | Checkout UI language |
35
+ | `baseUrl` | `string` | `https://pay.kling.is` | Override checkout URL (for local dev) |
36
+ | `apiUrl` | `string` | `https://api.kling.is` | Override API URL |
37
+
38
+ ### `kling.checkout(options)`
39
+
40
+ | Option | Type | Description |
41
+ |--------|------|-------------|
42
+ | `sessionId` | `string` | **Required.** Checkout session ID from the API |
43
+ | `onSuccess` | `(result) => void` | Called on successful payment |
44
+ | `onError` | `(error) => void` | Called on payment failure |
45
+ | `onCancel` | `() => void` | Called when user closes checkout |
46
+ | `showSuccessScreen` | `boolean` | Show success screen after payment (default: `true`) |
47
+
48
+ ### CDN
49
+
50
+ You can also load the SDK via script tag:
51
+
52
+ ```html
53
+ <script src="https://api.kling.is/v1/kling.js"></script>
54
+ <script>
55
+ const kling = Kling.init();
56
+ kling.checkout({ sessionId: 'cs_...' });
57
+ </script>
58
+ ```
59
+
60
+ ## License
61
+
62
+ MIT
package/dist/index.cjs ADDED
@@ -0,0 +1,394 @@
1
+ "use strict";var c=Object.defineProperty;var p=Object.getOwnPropertyDescriptor;var u=Object.getOwnPropertyNames;var m=Object.prototype.hasOwnProperty;var k=(s,e)=>{for(var t in e)c(s,t,{get:e[t],enumerable:!0})},f=(s,e,t,i)=>{if(e&&typeof e=="object"||typeof e=="function")for(let n of u(e))!m.call(s,n)&&n!==t&&c(s,n,{get:()=>e[n],enumerable:!(i=p(e,n))||i.enumerable});return s};var y=s=>f(c({},"__esModule",{value:!0}),s);var x={};k(x,{Kling:()=>r,default:()=>r});module.exports=y(x);var d=`
2
+ * {
3
+ box-sizing: border-box;
4
+ }
5
+
6
+ .kling-overlay {
7
+ position: fixed;
8
+ inset: 0;
9
+ z-index: 2147483647;
10
+ display: flex;
11
+ align-items: center;
12
+ justify-content: center;
13
+ font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
14
+ }
15
+
16
+ .kling-backdrop {
17
+ position: absolute;
18
+ inset: 0;
19
+ background: rgba(0, 0, 0, 0.5);
20
+ backdrop-filter: blur(4px);
21
+ animation: kling-fade-in 0.2s ease-out;
22
+ }
23
+
24
+ .kling-modal-wrapper {
25
+ position: relative;
26
+ margin: 16px;
27
+ animation: kling-slide-up 0.3s ease-out;
28
+ }
29
+
30
+ .kling-modal {
31
+ position: relative;
32
+ width: min(400px, calc(100vw - 32px));
33
+ min-height: 300px;
34
+ max-height: 90vh;
35
+ background: white;
36
+ border-radius: 16px;
37
+ box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.25);
38
+ overflow: hidden;
39
+ }
40
+
41
+ .kling-modal-wrapper-mobile {
42
+ position: fixed;
43
+ bottom: 0;
44
+ left: 0;
45
+ right: 0;
46
+ margin: 0;
47
+ animation: kling-slide-up-mobile 0.3s ease-out;
48
+ }
49
+
50
+ .kling-modal-wrapper-mobile .kling-modal {
51
+ width: 100%;
52
+ max-width: 100%;
53
+ max-height: 95vh;
54
+ border-radius: 16px 16px 0 0;
55
+ }
56
+
57
+ .kling-modal-wrapper-mobile .kling-close-button {
58
+ top: -40px;
59
+ right: 12px;
60
+ }
61
+
62
+ .kling-close-button {
63
+ position: absolute;
64
+ top: -12px;
65
+ right: -12px;
66
+ width: 32px;
67
+ height: 32px;
68
+ border: none;
69
+ background: rgba(255, 255, 255, 0.9);
70
+ border-radius: 50%;
71
+ cursor: pointer;
72
+ display: flex;
73
+ align-items: center;
74
+ justify-content: center;
75
+ transition: background 0.15s;
76
+ z-index: 10;
77
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
78
+ }
79
+
80
+ .kling-close-button:hover {
81
+ background: rgba(255, 255, 255, 1);
82
+ }
83
+
84
+ .kling-close-button svg {
85
+ width: 16px;
86
+ height: 16px;
87
+ color: #374151;
88
+ }
89
+
90
+ .kling-iframe {
91
+ width: 100%;
92
+ height: 0;
93
+ border: none;
94
+ display: block;
95
+ opacity: 0;
96
+ transition: opacity 0.15s ease-out, height 0.2s ease-out;
97
+ }
98
+
99
+ .kling-iframe.loaded {
100
+ opacity: 1;
101
+ max-height: calc(90vh - 32px);
102
+ }
103
+
104
+ .kling-modal-mobile .kling-iframe {
105
+ height: calc(95vh - 32px);
106
+ max-height: calc(95vh - 32px);
107
+ }
108
+
109
+ .kling-loading {
110
+ display: flex;
111
+ flex-direction: column;
112
+ align-items: center;
113
+ justify-content: center;
114
+ height: 300px;
115
+ max-height: 400px;
116
+ gap: 16px;
117
+ }
118
+
119
+ .kling-spinner {
120
+ width: 32px;
121
+ height: 32px;
122
+ border: 3px solid #e2e8f0;
123
+ border-top-color: #6366f1;
124
+ border-radius: 50%;
125
+ animation: kling-spin 0.8s linear infinite;
126
+ }
127
+
128
+ .kling-loading-text {
129
+ color: #64748b;
130
+ font-size: 14px;
131
+ }
132
+
133
+ @keyframes kling-fade-in {
134
+ from { opacity: 0; }
135
+ to { opacity: 1; }
136
+ }
137
+
138
+ @keyframes kling-slide-up {
139
+ from {
140
+ opacity: 0;
141
+ transform: translateY(20px) scale(0.98);
142
+ }
143
+ to {
144
+ opacity: 1;
145
+ transform: translateY(0) scale(1);
146
+ }
147
+ }
148
+
149
+ @keyframes kling-slide-up-mobile {
150
+ from {
151
+ transform: translateY(100%);
152
+ }
153
+ to {
154
+ transform: translateY(0);
155
+ }
156
+ }
157
+
158
+ @keyframes kling-spin {
159
+ to { transform: rotate(360deg); }
160
+ }
161
+
162
+ /* Success state */
163
+ .kling-success {
164
+ display: flex;
165
+ flex-direction: column;
166
+ align-items: center;
167
+ justify-content: center;
168
+ padding: 48px 32px;
169
+ text-align: center;
170
+ }
171
+
172
+ .kling-success-icon {
173
+ width: 64px;
174
+ height: 64px;
175
+ border-radius: 50%;
176
+ background: linear-gradient(135deg, #10b981 0%, #059669 100%);
177
+ display: flex;
178
+ align-items: center;
179
+ justify-content: center;
180
+ margin-bottom: 24px;
181
+ animation: kling-success-pop 0.4s ease-out;
182
+ }
183
+
184
+ .kling-success-icon svg {
185
+ width: 32px;
186
+ height: 32px;
187
+ color: white;
188
+ }
189
+
190
+ .kling-success-title {
191
+ font-size: 24px;
192
+ font-weight: 700;
193
+ color: #0f172a;
194
+ margin: 0 0 8px 0;
195
+ }
196
+
197
+ .kling-success-amount {
198
+ font-size: 18px;
199
+ font-weight: 600;
200
+ color: #10b981;
201
+ margin: 0 0 8px 0;
202
+ }
203
+
204
+ .kling-success-message {
205
+ font-size: 14px;
206
+ color: #64748b;
207
+ margin: 0;
208
+ }
209
+
210
+ .kling-success-button {
211
+ background: linear-gradient(135deg, #6366f1 0%, #4f46e5 100%);
212
+ color: white;
213
+ border: none;
214
+ padding: 12px 32px;
215
+ border-radius: 8px;
216
+ font-size: 14px;
217
+ font-weight: 600;
218
+ cursor: pointer;
219
+ transition: transform 0.15s, box-shadow 0.15s;
220
+ }
221
+
222
+ .kling-success-button:hover {
223
+ transform: translateY(-1px);
224
+ box-shadow: 0 4px 12px rgba(99, 102, 241, 0.4);
225
+ }
226
+
227
+ @keyframes kling-success-pop {
228
+ 0% { transform: scale(0); opacity: 0; }
229
+ 50% { transform: scale(1.1); }
230
+ 100% { transform: scale(1); opacity: 1; }
231
+ }
232
+
233
+ /* Error state */
234
+ .kling-error {
235
+ display: flex;
236
+ flex-direction: column;
237
+ align-items: center;
238
+ justify-content: center;
239
+ padding: 48px 32px;
240
+ text-align: center;
241
+ }
242
+
243
+ .kling-error-icon {
244
+ width: 64px;
245
+ height: 64px;
246
+ border-radius: 50%;
247
+ background: linear-gradient(135deg, #ef4444 0%, #dc2626 100%);
248
+ display: flex;
249
+ align-items: center;
250
+ justify-content: center;
251
+ margin-bottom: 24px;
252
+ }
253
+
254
+ .kling-error-icon svg {
255
+ width: 32px;
256
+ height: 32px;
257
+ color: white;
258
+ }
259
+
260
+ .kling-error-title {
261
+ font-size: 24px;
262
+ font-weight: 700;
263
+ color: #0f172a;
264
+ margin: 0 0 8px 0;
265
+ }
266
+
267
+ .kling-error-message {
268
+ font-size: 14px;
269
+ color: #64748b;
270
+ margin: 0 0 32px 0;
271
+ max-width: 280px;
272
+ }
273
+
274
+ .kling-error-buttons {
275
+ display: flex;
276
+ gap: 12px;
277
+ }
278
+
279
+ .kling-error-retry {
280
+ background: linear-gradient(135deg, #6366f1 0%, #4f46e5 100%);
281
+ color: white;
282
+ border: none;
283
+ padding: 12px 24px;
284
+ border-radius: 8px;
285
+ font-size: 14px;
286
+ font-weight: 600;
287
+ cursor: pointer;
288
+ transition: transform 0.15s, box-shadow 0.15s;
289
+ }
290
+
291
+ .kling-error-retry:hover {
292
+ transform: translateY(-1px);
293
+ box-shadow: 0 4px 12px rgba(99, 102, 241, 0.4);
294
+ }
295
+
296
+ .kling-error-cancel {
297
+ background: transparent;
298
+ color: #64748b;
299
+ border: 1px solid #e2e8f0;
300
+ padding: 12px 24px;
301
+ border-radius: 8px;
302
+ font-size: 14px;
303
+ font-weight: 600;
304
+ cursor: pointer;
305
+ transition: background 0.15s;
306
+ }
307
+
308
+ .kling-error-cancel:hover {
309
+ background: #f8fafc;
310
+ }
311
+
312
+ /* Closing animation */
313
+ .kling-overlay.closing .kling-backdrop {
314
+ animation: kling-fade-out 0.2s ease-in forwards;
315
+ }
316
+
317
+ .kling-overlay.closing .kling-modal-wrapper {
318
+ animation: kling-slide-down 0.2s ease-in forwards;
319
+ }
320
+
321
+ .kling-overlay.closing .kling-modal-wrapper-mobile {
322
+ animation: kling-slide-down-mobile 0.2s ease-in forwards;
323
+ }
324
+
325
+ @keyframes kling-fade-out {
326
+ to { opacity: 0; }
327
+ }
328
+
329
+ @keyframes kling-slide-down {
330
+ to {
331
+ opacity: 0;
332
+ transform: translateY(20px) scale(0.98);
333
+ }
334
+ }
335
+
336
+ @keyframes kling-slide-down-mobile {
337
+ to {
338
+ transform: translateY(100%);
339
+ }
340
+ }
341
+ `,h=`
342
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
343
+ <line x1="18" y1="6" x2="6" y2="18"></line>
344
+ <line x1="6" y1="6" x2="18" y2="18"></line>
345
+ </svg>
346
+ `;var a=class{constructor(e){this.host=null;this.shadow=null;this.iframe=null;this.messageHandler=null;this.isClosing=!1;this.handleKeyDown=e=>{e.key==="Escape"&&this.handleCancel()};this.options=e}open(){if(this.host)return;this.host=document.createElement("div"),this.host.id="kling-checkout-host",document.body.appendChild(this.host),this.shadow=this.host.attachShadow({mode:"closed"});let e=window.innerWidth<640;this.shadow.innerHTML=`
347
+ <style>${d}</style>
348
+ <div class="kling-overlay" role="dialog" aria-modal="true" aria-label="Checkout">
349
+ <div class="kling-backdrop"></div>
350
+ <div class="kling-modal-wrapper ${e?"kling-modal-wrapper-mobile":""}">
351
+ <button class="kling-close-button" aria-label="Close checkout">
352
+ ${h}
353
+ </button>
354
+ <div class="kling-modal">
355
+ <div class="kling-loading">
356
+ <div class="kling-spinner"></div>
357
+ <div class="kling-loading-text">Loading checkout...</div>
358
+ </div>
359
+ </div>
360
+ </div>
361
+ </div>
362
+ `;let t=this.shadow.querySelector(".kling-backdrop"),i=this.shadow.querySelector(".kling-close-button");t?.addEventListener("click",()=>this.handleCancel()),i?.addEventListener("click",()=>this.handleCancel()),document.addEventListener("keydown",this.handleKeyDown),document.body.style.overflow="hidden",setTimeout(()=>this.loadIframe(),50),this.messageHandler=this.handleMessage.bind(this),window.addEventListener("message",this.messageHandler)}loadIframe(){if(!this.shadow)return;let e=this.shadow.querySelector(".kling-modal"),t=this.shadow.querySelector(".kling-loading");!e||!t||(this.iframe=document.createElement("iframe"),this.iframe.className="kling-iframe",this.iframe.src=this.options.embedUrl,this.iframe.allow="payment",this.iframe.setAttribute("loading","eager"),e.insertBefore(this.iframe,t))}handleMessage(e){let t=e.data;if(!(!t||typeof t!="object"||t.source!=="kling-checkout"))switch(t.type){case"checkout:success":this.handleSuccess(t.payload);break;case"checkout:error":this.handleError(t.payload);break;case"checkout:cancel":this.handleCancel(t.payload?.cancelUrl);break;case"checkout:resize":this.handleResize(t.payload);break;case"checkout:ready":this.handleReady();break;case"payment:success":this.showPaymentSuccess(t.payload);break;case"payment:error":this.showPaymentError(t.payload);break}}handleSuccess(e){this.showPaymentSuccess(e)}handleError(e){this.options.callbacks?.onError?.(e)}handleCancel(e){this.isClosing||(this.options.callbacks?.onCancel?.(),this.options.onCancel(),this.close(),e&&(window.location.href=e))}handleResize(e){this.iframe&&e.height&&(this.iframe.style.height=`${Math.min(e.height,window.innerHeight*.9)}px`)}handleReady(){this.iframe&&!this.iframe.classList.contains("loaded")&&(this.iframe.classList.add("loaded"),this.shadow?.querySelector(".kling-loading")?.remove())}showPaymentSuccess(e){if(!this.shadow)return;let t=this.shadow.querySelector(".kling-modal");if(!t)return;let i={status:"success",success:!0,paymentIntentId:e.paymentIntentId,paymentMethodId:e.paymentMethodId};this.options.callbacks?.onSuccess?.(e),this.iframe&&(this.iframe.style.display="none");let n=e.amount&&e.currency?`${e.amount.toLocaleString()} ${e.currency}`:"",o=!!e.successUrl;t.innerHTML=`
363
+ <div class="kling-success">
364
+ <div class="kling-success-icon">
365
+ <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
366
+ <path d="M22 11.08V12a10 10 0 1 1-5.93-9.14"/>
367
+ <polyline points="22 4 12 14.01 9 11.01"/>
368
+ </svg>
369
+ </div>
370
+ <h2 class="kling-success-title">Payment Successful</h2>
371
+ ${n?`<p class="kling-success-amount">${n}</p>`:""}
372
+ ${o?'<p class="kling-success-message">Redirecting...</p>':'<button class="kling-success-button" type="button">Done</button>'}
373
+ </div>
374
+ `,o?setTimeout(()=>{this.close(),this.options.onComplete(i),window.location.href=e.successUrl},1e3):t.querySelector(".kling-success-button")?.addEventListener("click",()=>{this.close(),this.options.onComplete(i)})}showPaymentError(e){if(!this.shadow)return;let t=this.shadow.querySelector(".kling-modal");if(!t)return;this.iframe&&(this.iframe.style.display="none"),t.innerHTML=`
375
+ <div class="kling-error">
376
+ <div class="kling-error-icon">
377
+ <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
378
+ <circle cx="12" cy="12" r="10"/>
379
+ <line x1="15" y1="9" x2="9" y2="15"/>
380
+ <line x1="9" y1="9" x2="15" y2="15"/>
381
+ </svg>
382
+ </div>
383
+ <h2 class="kling-error-title">Payment Failed</h2>
384
+ <p class="kling-error-message">${e.message||"Something went wrong. Please try again."}</p>
385
+ <div class="kling-error-buttons">
386
+ <button class="kling-error-retry" type="button">Try Again</button>
387
+ <button class="kling-error-cancel" type="button">Cancel</button>
388
+ </div>
389
+ </div>
390
+ `,t.querySelector(".kling-error-retry")?.addEventListener("click",()=>{if(this.iframe){this.iframe.style.display="block",t.innerHTML="",t.appendChild(this.iframe);let o=document.createElement("div");o.className="kling-loading",o.innerHTML=`
391
+ <div class="kling-spinner"></div>
392
+ <div class="kling-loading-text">Loading checkout...</div>
393
+ `,t.appendChild(o),this.iframe.src=this.iframe.src}}),t.querySelector(".kling-error-cancel")?.addEventListener("click",()=>{this.handleCancel()}),this.options.callbacks?.onError?.(e)}close(){if(this.isClosing||!this.shadow)return;this.isClosing=!0,this.shadow.querySelector(".kling-overlay")?.classList.add("closing"),setTimeout(()=>this.destroy(),200)}destroy(){document.removeEventListener("keydown",this.handleKeyDown),this.messageHandler&&window.removeEventListener("message",this.messageHandler),this.host&&(this.host.remove(),this.host=null),document.body.style.overflow="",this.shadow=null,this.iframe=null}};var v="https://api.kling.is",b="https://pay.kling.is",r=class s{constructor(e={}){this.currentOverlay=null;this.apiUrl=e.apiUrl||v,e.baseUrl?this.payUrl=e.baseUrl.replace(/\/$/,""):this.apiUrl.includes("localhost")?this.payUrl=this.apiUrl.replace(":3001",":3000"):this.payUrl=b,this.locale=e.locale||this.detectLocale()}static init(e={}){return new s(e)}checkout(e){if(!e.sessionId)throw new Error("Kling.checkout() requires a sessionId");this.currentOverlay&&(this.currentOverlay.close(),this.currentOverlay=null);let t=this.buildEmbedUrl(e),i,n=new Promise(l=>{i=l}),o=new a({embedUrl:t,onComplete:l=>{this.currentOverlay=null,i(l)},onCancel:()=>{this.currentOverlay=null,i({status:"canceled",success:!1,canceled:!0})},callbacks:{onSuccess:e.onSuccess,onError:e.onError,onCancel:e.onCancel}});return this.currentOverlay=o,o.open(),{then:n.then.bind(n),catch:n.catch.bind(n),finally:n.finally.bind(n),close:()=>{o.close(),i({status:"canceled",success:!1,canceled:!0})}}}buildEmbedUrl(e){let t=new URLSearchParams;return t.set("embed","1"),t.set("locale",this.locale),typeof window<"u"&&window.location?.href&&t.set("origin",window.location.href),`${this.payUrl}/checkout/${e.sessionId}?${t}`}detectLocale(){return typeof navigator>"u"?"en":(navigator.language?.toLowerCase()||"").startsWith("is")?"is":"en"}preload(e){let t=this.buildEmbedUrl(e),i=document.createElement("link");i.rel="prefetch",i.href=t,document.head.appendChild(i)}};0&&(module.exports={Kling});
394
+ //# sourceMappingURL=index.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/index.ts","../src/styles.ts","../src/overlay.ts","../src/kling.ts"],"sourcesContent":["/**\n * Kling Embeddable Checkout SDK\n *\n * @example\n * ```js\n * // Initialize\n * const kling = Kling.init();\n *\n * // Open checkout with a server-created session\n * const result = await kling.checkout({\n * sessionId: 'cs_xyz789',\n * });\n *\n * if (result.success) {\n * console.log('Payment successful:', result.paymentIntentId);\n * }\n * ```\n */\n\nexport { Kling } from \"./kling\";\nexport type {\n KlingInitOptions,\n CheckoutOptions,\n CheckoutResult,\n CheckoutController,\n CheckoutSuccessResult,\n CheckoutError,\n CheckoutStatus,\n} from \"./types\";\n\n// Also export Kling as default for UMD usage\nexport { Kling as default } from \"./kling\";\n","/**\n * Checkout overlay styles\n * Injected into Shadow DOM to avoid conflicts with host page\n */\n\nexport const overlayStyles = `\n * {\n box-sizing: border-box;\n }\n\n .kling-overlay {\n position: fixed;\n inset: 0;\n z-index: 2147483647;\n display: flex;\n align-items: center;\n justify-content: center;\n font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif;\n }\n\n .kling-backdrop {\n position: absolute;\n inset: 0;\n background: rgba(0, 0, 0, 0.5);\n backdrop-filter: blur(4px);\n animation: kling-fade-in 0.2s ease-out;\n }\n\n .kling-modal-wrapper {\n position: relative;\n margin: 16px;\n animation: kling-slide-up 0.3s ease-out;\n }\n\n .kling-modal {\n position: relative;\n width: min(400px, calc(100vw - 32px));\n min-height: 300px;\n max-height: 90vh;\n background: white;\n border-radius: 16px;\n box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.25);\n overflow: hidden;\n }\n\n .kling-modal-wrapper-mobile {\n position: fixed;\n bottom: 0;\n left: 0;\n right: 0;\n margin: 0;\n animation: kling-slide-up-mobile 0.3s ease-out;\n }\n\n .kling-modal-wrapper-mobile .kling-modal {\n width: 100%;\n max-width: 100%;\n max-height: 95vh;\n border-radius: 16px 16px 0 0;\n }\n\n .kling-modal-wrapper-mobile .kling-close-button {\n top: -40px;\n right: 12px;\n }\n\n .kling-close-button {\n position: absolute;\n top: -12px;\n right: -12px;\n width: 32px;\n height: 32px;\n border: none;\n background: rgba(255, 255, 255, 0.9);\n border-radius: 50%;\n cursor: pointer;\n display: flex;\n align-items: center;\n justify-content: center;\n transition: background 0.15s;\n z-index: 10;\n box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);\n }\n\n .kling-close-button:hover {\n background: rgba(255, 255, 255, 1);\n }\n\n .kling-close-button svg {\n width: 16px;\n height: 16px;\n color: #374151;\n }\n\n .kling-iframe {\n width: 100%;\n height: 0;\n border: none;\n display: block;\n opacity: 0;\n transition: opacity 0.15s ease-out, height 0.2s ease-out;\n }\n\n .kling-iframe.loaded {\n opacity: 1;\n max-height: calc(90vh - 32px);\n }\n\n .kling-modal-mobile .kling-iframe {\n height: calc(95vh - 32px);\n max-height: calc(95vh - 32px);\n }\n\n .kling-loading {\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n height: 300px;\n max-height: 400px;\n gap: 16px;\n }\n\n .kling-spinner {\n width: 32px;\n height: 32px;\n border: 3px solid #e2e8f0;\n border-top-color: #6366f1;\n border-radius: 50%;\n animation: kling-spin 0.8s linear infinite;\n }\n\n .kling-loading-text {\n color: #64748b;\n font-size: 14px;\n }\n\n @keyframes kling-fade-in {\n from { opacity: 0; }\n to { opacity: 1; }\n }\n\n @keyframes kling-slide-up {\n from {\n opacity: 0;\n transform: translateY(20px) scale(0.98);\n }\n to {\n opacity: 1;\n transform: translateY(0) scale(1);\n }\n }\n\n @keyframes kling-slide-up-mobile {\n from {\n transform: translateY(100%);\n }\n to {\n transform: translateY(0);\n }\n }\n\n @keyframes kling-spin {\n to { transform: rotate(360deg); }\n }\n\n /* Success state */\n .kling-success {\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n padding: 48px 32px;\n text-align: center;\n }\n\n .kling-success-icon {\n width: 64px;\n height: 64px;\n border-radius: 50%;\n background: linear-gradient(135deg, #10b981 0%, #059669 100%);\n display: flex;\n align-items: center;\n justify-content: center;\n margin-bottom: 24px;\n animation: kling-success-pop 0.4s ease-out;\n }\n\n .kling-success-icon svg {\n width: 32px;\n height: 32px;\n color: white;\n }\n\n .kling-success-title {\n font-size: 24px;\n font-weight: 700;\n color: #0f172a;\n margin: 0 0 8px 0;\n }\n\n .kling-success-amount {\n font-size: 18px;\n font-weight: 600;\n color: #10b981;\n margin: 0 0 8px 0;\n }\n\n .kling-success-message {\n font-size: 14px;\n color: #64748b;\n margin: 0;\n }\n\n .kling-success-button {\n background: linear-gradient(135deg, #6366f1 0%, #4f46e5 100%);\n color: white;\n border: none;\n padding: 12px 32px;\n border-radius: 8px;\n font-size: 14px;\n font-weight: 600;\n cursor: pointer;\n transition: transform 0.15s, box-shadow 0.15s;\n }\n\n .kling-success-button:hover {\n transform: translateY(-1px);\n box-shadow: 0 4px 12px rgba(99, 102, 241, 0.4);\n }\n\n @keyframes kling-success-pop {\n 0% { transform: scale(0); opacity: 0; }\n 50% { transform: scale(1.1); }\n 100% { transform: scale(1); opacity: 1; }\n }\n\n /* Error state */\n .kling-error {\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n padding: 48px 32px;\n text-align: center;\n }\n\n .kling-error-icon {\n width: 64px;\n height: 64px;\n border-radius: 50%;\n background: linear-gradient(135deg, #ef4444 0%, #dc2626 100%);\n display: flex;\n align-items: center;\n justify-content: center;\n margin-bottom: 24px;\n }\n\n .kling-error-icon svg {\n width: 32px;\n height: 32px;\n color: white;\n }\n\n .kling-error-title {\n font-size: 24px;\n font-weight: 700;\n color: #0f172a;\n margin: 0 0 8px 0;\n }\n\n .kling-error-message {\n font-size: 14px;\n color: #64748b;\n margin: 0 0 32px 0;\n max-width: 280px;\n }\n\n .kling-error-buttons {\n display: flex;\n gap: 12px;\n }\n\n .kling-error-retry {\n background: linear-gradient(135deg, #6366f1 0%, #4f46e5 100%);\n color: white;\n border: none;\n padding: 12px 24px;\n border-radius: 8px;\n font-size: 14px;\n font-weight: 600;\n cursor: pointer;\n transition: transform 0.15s, box-shadow 0.15s;\n }\n\n .kling-error-retry:hover {\n transform: translateY(-1px);\n box-shadow: 0 4px 12px rgba(99, 102, 241, 0.4);\n }\n\n .kling-error-cancel {\n background: transparent;\n color: #64748b;\n border: 1px solid #e2e8f0;\n padding: 12px 24px;\n border-radius: 8px;\n font-size: 14px;\n font-weight: 600;\n cursor: pointer;\n transition: background 0.15s;\n }\n\n .kling-error-cancel:hover {\n background: #f8fafc;\n }\n\n /* Closing animation */\n .kling-overlay.closing .kling-backdrop {\n animation: kling-fade-out 0.2s ease-in forwards;\n }\n\n .kling-overlay.closing .kling-modal-wrapper {\n animation: kling-slide-down 0.2s ease-in forwards;\n }\n\n .kling-overlay.closing .kling-modal-wrapper-mobile {\n animation: kling-slide-down-mobile 0.2s ease-in forwards;\n }\n\n @keyframes kling-fade-out {\n to { opacity: 0; }\n }\n\n @keyframes kling-slide-down {\n to {\n opacity: 0;\n transform: translateY(20px) scale(0.98);\n }\n }\n\n @keyframes kling-slide-down-mobile {\n to {\n transform: translateY(100%);\n }\n }\n`;\n\nexport const closeIconSvg = `\n <svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <line x1=\"18\" y1=\"6\" x2=\"6\" y2=\"18\"></line>\n <line x1=\"6\" y1=\"6\" x2=\"18\" y2=\"18\"></line>\n </svg>\n`;\n","import { overlayStyles, closeIconSvg } from \"./styles\";\nimport type { CheckoutResult, CheckoutOptions } from \"./types\";\n\nexport interface OverlayOptions {\n embedUrl: string;\n onComplete: (result: CheckoutResult) => void;\n onCancel: () => void;\n callbacks?: {\n onSuccess?: CheckoutOptions[\"onSuccess\"];\n onError?: CheckoutOptions[\"onError\"];\n onCancel?: CheckoutOptions[\"onCancel\"];\n };\n}\n\nexport class CheckoutOverlay {\n private host: HTMLDivElement | null = null;\n private shadow: ShadowRoot | null = null;\n private iframe: HTMLIFrameElement | null = null;\n private messageHandler: ((event: MessageEvent) => void) | null = null;\n private options: OverlayOptions;\n private isClosing = false;\n\n constructor(options: OverlayOptions) {\n this.options = options;\n }\n\n /**\n * Open the checkout overlay\n */\n open(): void {\n if (this.host) return;\n\n // Create host element\n this.host = document.createElement(\"div\");\n this.host.id = \"kling-checkout-host\";\n document.body.appendChild(this.host);\n\n // Attach Shadow DOM\n this.shadow = this.host.attachShadow({ mode: \"closed\" });\n\n // Detect mobile\n const isMobile = window.innerWidth < 640;\n\n // Build overlay HTML\n this.shadow.innerHTML = `\n <style>${overlayStyles}</style>\n <div class=\"kling-overlay\" role=\"dialog\" aria-modal=\"true\" aria-label=\"Checkout\">\n <div class=\"kling-backdrop\"></div>\n <div class=\"kling-modal-wrapper ${isMobile ? \"kling-modal-wrapper-mobile\" : \"\"}\">\n <button class=\"kling-close-button\" aria-label=\"Close checkout\">\n ${closeIconSvg}\n </button>\n <div class=\"kling-modal\">\n <div class=\"kling-loading\">\n <div class=\"kling-spinner\"></div>\n <div class=\"kling-loading-text\">Loading checkout...</div>\n </div>\n </div>\n </div>\n </div>\n `;\n\n // Set up event listeners\n const backdrop = this.shadow.querySelector(\".kling-backdrop\");\n const closeButton = this.shadow.querySelector(\".kling-close-button\");\n\n backdrop?.addEventListener(\"click\", () => this.handleCancel());\n closeButton?.addEventListener(\"click\", () => this.handleCancel());\n\n // Handle escape key\n document.addEventListener(\"keydown\", this.handleKeyDown);\n\n // Prevent body scroll\n document.body.style.overflow = \"hidden\";\n\n // Load iframe after a brief delay (allows animation to start)\n setTimeout(() => this.loadIframe(), 50);\n\n // Set up message listener\n this.messageHandler = this.handleMessage.bind(this);\n window.addEventListener(\"message\", this.messageHandler);\n }\n\n /**\n * Load the checkout iframe\n */\n private loadIframe(): void {\n if (!this.shadow) return;\n\n const modal = this.shadow.querySelector(\".kling-modal\");\n const loading = this.shadow.querySelector(\".kling-loading\");\n if (!modal || !loading) return;\n\n // Create iframe\n this.iframe = document.createElement(\"iframe\");\n this.iframe.className = \"kling-iframe\";\n this.iframe.src = this.options.embedUrl;\n this.iframe.allow = \"payment\";\n this.iframe.setAttribute(\"loading\", \"eager\");\n\n // Note: loading spinner is removed when we receive the first resize message\n // This ensures content is actually ready before showing\n\n // Insert iframe before loading spinner\n modal.insertBefore(this.iframe, loading);\n }\n\n /**\n * Handle postMessage from iframe\n */\n private handleMessage(event: MessageEvent): void {\n // Validate origin - should come from our checkout page\n // In production, validate against known Kling domains\n const data = event.data;\n\n if (!data || typeof data !== \"object\" || data.source !== \"kling-checkout\") {\n return;\n }\n\n switch (data.type) {\n case \"checkout:success\":\n this.handleSuccess(data.payload);\n break;\n\n case \"checkout:error\":\n this.handleError(data.payload);\n break;\n\n case \"checkout:cancel\":\n this.handleCancel(data.payload?.cancelUrl);\n break;\n\n case \"checkout:resize\":\n this.handleResize(data.payload);\n break;\n\n case \"checkout:ready\":\n this.handleReady();\n break;\n\n case \"payment:success\":\n this.showPaymentSuccess(data.payload);\n break;\n\n case \"payment:error\":\n this.showPaymentError(data.payload);\n break;\n }\n }\n\n /**\n * Handle successful payment (legacy checkout:success message)\n * Routes to the same success screen as payment:success.\n */\n private handleSuccess(payload: {\n paymentIntentId: string;\n paymentMethodId?: string;\n }): void {\n this.showPaymentSuccess(payload);\n }\n\n /**\n * Handle payment error\n */\n private handleError(payload: { code: string; message: string }): void {\n // Fire callback - don't close overlay, let user retry\n this.options.callbacks?.onError?.(payload);\n }\n\n /**\n * Handle cancel — close overlay and optionally redirect to cancelUrl\n */\n private handleCancel(cancelUrl?: string): void {\n if (this.isClosing) return;\n\n this.options.callbacks?.onCancel?.();\n this.options.onCancel();\n this.close();\n\n if (cancelUrl) {\n window.location.href = cancelUrl;\n }\n }\n\n /**\n * Handle iframe resize request\n */\n private handleResize(payload: { height: number }): void {\n if (this.iframe && payload.height) {\n this.iframe.style.height = `${Math.min(payload.height, window.innerHeight * 0.9)}px`;\n }\n }\n\n /**\n * Handle checkout ready signal - show iframe and hide spinner\n */\n private handleReady(): void {\n if (this.iframe && !this.iframe.classList.contains(\"loaded\")) {\n this.iframe.classList.add(\"loaded\");\n // Hide loading spinner\n const loading = this.shadow?.querySelector(\".kling-loading\");\n loading?.remove();\n }\n }\n\n /**\n * Show brief success animation, then redirect to successUrl or show \"Done\" button.\n */\n private showPaymentSuccess(payload: {\n paymentIntentId: string;\n paymentMethodId?: string;\n amount?: number;\n currency?: string;\n successUrl?: string | null;\n }): void {\n if (!this.shadow) return;\n\n const modal = this.shadow.querySelector(\".kling-modal\");\n if (!modal) return;\n\n const result: CheckoutResult = {\n status: \"success\",\n success: true,\n paymentIntentId: payload.paymentIntentId,\n paymentMethodId: payload.paymentMethodId,\n };\n\n // Fire success callback immediately\n this.options.callbacks?.onSuccess?.(payload);\n\n // Hide iframe and show success state\n if (this.iframe) {\n this.iframe.style.display = \"none\";\n }\n\n const amountText = payload.amount && payload.currency\n ? `${payload.amount.toLocaleString()} ${payload.currency}`\n : \"\";\n\n const hasRedirect = !!payload.successUrl;\n\n modal.innerHTML = `\n <div class=\"kling-success\">\n <div class=\"kling-success-icon\">\n <svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\n <path d=\"M22 11.08V12a10 10 0 1 1-5.93-9.14\"/>\n <polyline points=\"22 4 12 14.01 9 11.01\"/>\n </svg>\n </div>\n <h2 class=\"kling-success-title\">Payment Successful</h2>\n ${amountText ? `<p class=\"kling-success-amount\">${amountText}</p>` : \"\"}\n ${hasRedirect\n ? `<p class=\"kling-success-message\">Redirecting...</p>`\n : `<button class=\"kling-success-button\" type=\"button\">Done</button>`\n }\n </div>\n `;\n\n if (hasRedirect) {\n // Brief display then redirect parent page\n setTimeout(() => {\n this.close();\n this.options.onComplete(result);\n window.location.href = payload.successUrl!;\n }, 1000);\n } else {\n // No redirect — let user close manually\n const doneButton = modal.querySelector(\".kling-success-button\");\n doneButton?.addEventListener(\"click\", () => {\n this.close();\n this.options.onComplete(result);\n });\n }\n }\n\n /**\n * Show payment error UI in the overlay\n */\n private showPaymentError(payload: { code: string; message: string }): void {\n if (!this.shadow) return;\n\n const modal = this.shadow.querySelector(\".kling-modal\");\n if (!modal) return;\n\n // Hide iframe and show error state\n if (this.iframe) {\n this.iframe.style.display = \"none\";\n }\n\n modal.innerHTML = `\n <div class=\"kling-error\">\n <div class=\"kling-error-icon\">\n <svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\n <circle cx=\"12\" cy=\"12\" r=\"10\"/>\n <line x1=\"15\" y1=\"9\" x2=\"9\" y2=\"15\"/>\n <line x1=\"9\" y1=\"9\" x2=\"15\" y2=\"15\"/>\n </svg>\n </div>\n <h2 class=\"kling-error-title\">Payment Failed</h2>\n <p class=\"kling-error-message\">${payload.message || \"Something went wrong. Please try again.\"}</p>\n <div class=\"kling-error-buttons\">\n <button class=\"kling-error-retry\" type=\"button\">Try Again</button>\n <button class=\"kling-error-cancel\" type=\"button\">Cancel</button>\n </div>\n </div>\n `;\n\n // Handle retry button - reload iframe\n const retryButton = modal.querySelector(\".kling-error-retry\");\n retryButton?.addEventListener(\"click\", () => {\n if (this.iframe) {\n this.iframe.style.display = \"block\";\n modal.innerHTML = \"\";\n modal.appendChild(this.iframe);\n // Add loading spinner\n const loading = document.createElement(\"div\");\n loading.className = \"kling-loading\";\n loading.innerHTML = `\n <div class=\"kling-spinner\"></div>\n <div class=\"kling-loading-text\">Loading checkout...</div>\n `;\n modal.appendChild(loading);\n this.iframe.src = this.iframe.src; // Reload\n }\n });\n\n // Handle cancel button\n const cancelButton = modal.querySelector(\".kling-error-cancel\");\n cancelButton?.addEventListener(\"click\", () => {\n this.handleCancel();\n });\n\n // Fire error callback\n this.options.callbacks?.onError?.(payload);\n }\n\n /**\n * Handle escape key\n */\n private handleKeyDown = (event: KeyboardEvent): void => {\n if (event.key === \"Escape\") {\n this.handleCancel();\n }\n };\n\n /**\n * Close the overlay with animation\n */\n close(): void {\n if (this.isClosing || !this.shadow) return;\n this.isClosing = true;\n\n const overlay = this.shadow.querySelector(\".kling-overlay\");\n overlay?.classList.add(\"closing\");\n\n // Wait for animation to complete\n setTimeout(() => this.destroy(), 200);\n }\n\n /**\n * Clean up and remove overlay\n */\n private destroy(): void {\n // Remove event listeners\n document.removeEventListener(\"keydown\", this.handleKeyDown);\n if (this.messageHandler) {\n window.removeEventListener(\"message\", this.messageHandler);\n }\n\n // Remove host element\n if (this.host) {\n this.host.remove();\n this.host = null;\n }\n\n // Restore body scroll\n document.body.style.overflow = \"\";\n\n this.shadow = null;\n this.iframe = null;\n }\n}\n","import { CheckoutOverlay } from \"./overlay\";\nimport type {\n KlingInitOptions,\n CheckoutOptions,\n CheckoutResult,\n CheckoutController,\n} from \"./types\";\n\nconst DEFAULT_API_URL = \"https://api.kling.is\";\nconst DEFAULT_PAY_URL = \"https://pay.kling.is\";\n\nexport class Kling {\n private apiUrl: string;\n private payUrl: string;\n private locale: \"is\" | \"en\";\n private currentOverlay: CheckoutOverlay | null = null;\n\n private constructor(options: KlingInitOptions = {}) {\n this.apiUrl = options.apiUrl || DEFAULT_API_URL;\n // Use baseUrl if provided, otherwise derive from apiUrl\n if (options.baseUrl) {\n this.payUrl = options.baseUrl.replace(/\\/$/, \"\"); // Remove trailing slash\n } else if (this.apiUrl.includes(\"localhost\")) {\n this.payUrl = this.apiUrl.replace(\":3001\", \":3000\");\n } else {\n this.payUrl = DEFAULT_PAY_URL;\n }\n this.locale = options.locale || this.detectLocale();\n }\n\n /**\n * Initialize the Kling SDK\n */\n static init(options: KlingInitOptions = {}): Kling {\n return new Kling(options);\n }\n\n /**\n * Open checkout overlay\n */\n checkout(options: CheckoutOptions): CheckoutController {\n // Validate options\n if (!options.sessionId) {\n throw new Error(\"Kling.checkout() requires a sessionId\");\n }\n\n // Close any existing overlay\n if (this.currentOverlay) {\n this.currentOverlay.close();\n this.currentOverlay = null;\n }\n\n // Build embed URL\n const embedUrl = this.buildEmbedUrl(options);\n\n // Create promise for result\n let resolvePromise: (result: CheckoutResult) => void;\n const resultPromise = new Promise<CheckoutResult>((resolve) => {\n resolvePromise = resolve;\n });\n\n // Create overlay\n const overlay = new CheckoutOverlay({\n embedUrl,\n onComplete: (result) => {\n this.currentOverlay = null;\n resolvePromise(result);\n },\n onCancel: () => {\n this.currentOverlay = null;\n resolvePromise({\n status: \"canceled\",\n success: false,\n canceled: true,\n });\n },\n callbacks: {\n onSuccess: options.onSuccess,\n onError: options.onError,\n onCancel: options.onCancel,\n },\n });\n\n this.currentOverlay = overlay;\n overlay.open();\n\n // Return controller\n const controller: CheckoutController = {\n then: resultPromise.then.bind(resultPromise),\n catch: resultPromise.catch.bind(resultPromise),\n finally: resultPromise.finally.bind(resultPromise),\n close: () => {\n overlay.close();\n resolvePromise({\n status: \"canceled\",\n success: false,\n canceled: true,\n });\n },\n };\n\n return controller;\n }\n\n /**\n * Build the embed URL for the checkout iframe\n */\n private buildEmbedUrl(options: CheckoutOptions): string {\n const params = new URLSearchParams();\n params.set(\"embed\", \"1\");\n params.set(\"locale\", this.locale);\n\n // Pass the current page URL so the checkout can track where it was embedded\n if (typeof window !== \"undefined\" && window.location?.href) {\n params.set(\"origin\", window.location.href);\n }\n\n return `${this.payUrl}/checkout/${options.sessionId}?${params}`;\n }\n\n /**\n * Detect user's locale from browser\n */\n private detectLocale(): \"is\" | \"en\" {\n if (typeof navigator === \"undefined\") return \"en\";\n const lang = navigator.language?.toLowerCase() || \"\";\n return lang.startsWith(\"is\") ? \"is\" : \"en\";\n }\n\n /**\n * Preload checkout assets for faster opening\n */\n preload(options: { sessionId: string }): void {\n // Create a hidden iframe to preload the checkout\n const embedUrl = this.buildEmbedUrl(options as CheckoutOptions);\n const link = document.createElement(\"link\");\n link.rel = \"prefetch\";\n link.href = embedUrl;\n document.head.appendChild(link);\n }\n}\n"],"mappings":"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","names":["index_exports","__export","Kling","__toCommonJS","overlayStyles","closeIconSvg","CheckoutOverlay","options","event","isMobile","overlayStyles","closeIconSvg","backdrop","closeButton","modal","loading","data","payload","cancelUrl","result","amountText","hasRedirect","DEFAULT_API_URL","DEFAULT_PAY_URL","Kling","_Kling","options","embedUrl","resolvePromise","resultPromise","resolve","overlay","CheckoutOverlay","result","params","link"]}
@@ -0,0 +1,97 @@
1
+ /**
2
+ * Kling SDK Types
3
+ */
4
+ interface KlingInitOptions {
5
+ /**
6
+ * Override the API base URL (defaults to production)
7
+ */
8
+ apiUrl?: string;
9
+ /**
10
+ * Override the web app base URL for checkout (defaults to production)
11
+ * Use this for local testing: `baseUrl: 'http://localhost:3000'`
12
+ */
13
+ baseUrl?: string;
14
+ /**
15
+ * Locale for checkout UI
16
+ */
17
+ locale?: "is" | "en";
18
+ }
19
+ interface CheckoutOptions {
20
+ /**
21
+ * Checkout Session ID (created via API)
22
+ */
23
+ sessionId: string;
24
+ /**
25
+ * Callback fired on successful payment
26
+ */
27
+ onSuccess?: (result: CheckoutSuccessResult) => void;
28
+ /**
29
+ * Callback fired on payment error/decline
30
+ */
31
+ onError?: (error: CheckoutError) => void;
32
+ /**
33
+ * Callback fired when user cancels checkout
34
+ */
35
+ onCancel?: () => void;
36
+ }
37
+ interface CheckoutSuccessResult {
38
+ paymentIntentId: string;
39
+ paymentMethodId?: string;
40
+ }
41
+ interface CheckoutError {
42
+ code: string;
43
+ message: string;
44
+ }
45
+ type CheckoutStatus = "success" | "error" | "canceled";
46
+ interface CheckoutResult {
47
+ status: CheckoutStatus;
48
+ success: boolean;
49
+ paymentIntentId?: string;
50
+ paymentMethodId?: string;
51
+ error?: CheckoutError;
52
+ canceled?: boolean;
53
+ }
54
+ interface CheckoutController {
55
+ /**
56
+ * Promise that resolves when checkout completes
57
+ */
58
+ then: Promise<CheckoutResult>["then"];
59
+ catch: Promise<CheckoutResult>["catch"];
60
+ finally: Promise<CheckoutResult>["finally"];
61
+ /**
62
+ * Programmatically close the checkout overlay
63
+ */
64
+ close: () => void;
65
+ }
66
+
67
+ declare class Kling {
68
+ private apiUrl;
69
+ private payUrl;
70
+ private locale;
71
+ private currentOverlay;
72
+ private constructor();
73
+ /**
74
+ * Initialize the Kling SDK
75
+ */
76
+ static init(options?: KlingInitOptions): Kling;
77
+ /**
78
+ * Open checkout overlay
79
+ */
80
+ checkout(options: CheckoutOptions): CheckoutController;
81
+ /**
82
+ * Build the embed URL for the checkout iframe
83
+ */
84
+ private buildEmbedUrl;
85
+ /**
86
+ * Detect user's locale from browser
87
+ */
88
+ private detectLocale;
89
+ /**
90
+ * Preload checkout assets for faster opening
91
+ */
92
+ preload(options: {
93
+ sessionId: string;
94
+ }): void;
95
+ }
96
+
97
+ export { type CheckoutController, type CheckoutError, type CheckoutOptions, type CheckoutResult, type CheckoutStatus, type CheckoutSuccessResult, Kling, type KlingInitOptions, Kling as default };