@youidian/sdk 3.1.1 → 3.2.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/dist/client.cjs +547 -2
- package/dist/client.cjs.map +1 -1
- package/dist/client.d.cts +2 -1
- package/dist/client.d.ts +2 -1
- package/dist/client.js +543 -1
- package/dist/client.js.map +1 -1
- package/dist/index.cjs +339 -152
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1 -2
- package/dist/index.d.ts +1 -2
- package/dist/index.js +341 -155
- package/dist/index.js.map +1 -1
- package/dist/login-D_VaJqQX.d.cts +86 -0
- package/dist/login-D_VaJqQX.d.ts +86 -0
- package/dist/login.cjs +230 -43
- package/dist/login.cjs.map +1 -1
- package/dist/login.d.cts +1 -62
- package/dist/login.d.ts +1 -62
- package/dist/login.js +228 -42
- package/dist/login.js.map +1 -1
- package/package.json +36 -24
- package/dist/hosted-modal-BZmYmXTU.d.cts +0 -20
- package/dist/hosted-modal-BZmYmXTU.d.ts +0 -20
package/dist/index.cjs
CHANGED
|
@@ -36,7 +36,8 @@ __export(src_exports, {
|
|
|
36
36
|
PaymentClient: () => PaymentClient,
|
|
37
37
|
PaymentUI: () => PaymentUI,
|
|
38
38
|
createLoginUI: () => createLoginUI,
|
|
39
|
-
createPaymentUI: () => createPaymentUI
|
|
39
|
+
createPaymentUI: () => createPaymentUI,
|
|
40
|
+
handleLoginCallbackIfPresent: () => handleLoginCallbackIfPresent
|
|
40
41
|
});
|
|
41
42
|
module.exports = __toCommonJS(src_exports);
|
|
42
43
|
|
|
@@ -237,115 +238,6 @@ var HostedFrameModal = class {
|
|
|
237
238
|
}
|
|
238
239
|
};
|
|
239
240
|
|
|
240
|
-
// src/client.ts
|
|
241
|
-
var PaymentUI = class extends HostedFrameModal {
|
|
242
|
-
/**
|
|
243
|
-
* Opens the payment checkout page in an iframe modal.
|
|
244
|
-
* @param urlOrParams - The checkout page URL or payment parameters
|
|
245
|
-
* @param options - UI options
|
|
246
|
-
*/
|
|
247
|
-
openPayment(urlOrParams, options) {
|
|
248
|
-
if (typeof document === "undefined") return;
|
|
249
|
-
if (this.modal) return;
|
|
250
|
-
let checkoutUrl;
|
|
251
|
-
if (typeof urlOrParams === "string") {
|
|
252
|
-
checkoutUrl = urlOrParams;
|
|
253
|
-
} else {
|
|
254
|
-
const {
|
|
255
|
-
appId,
|
|
256
|
-
productId,
|
|
257
|
-
priceId,
|
|
258
|
-
productCode,
|
|
259
|
-
userId,
|
|
260
|
-
checkoutUrl: checkoutUrlParam,
|
|
261
|
-
baseUrl = "https://pay.imgto.link"
|
|
262
|
-
} = urlOrParams;
|
|
263
|
-
const base = (checkoutUrlParam || baseUrl).replace(/\/$/, "");
|
|
264
|
-
if (productCode) {
|
|
265
|
-
checkoutUrl = `${base}/checkout/${appId}/code/${productCode}?userId=${encodeURIComponent(userId)}`;
|
|
266
|
-
} else if (productId && priceId) {
|
|
267
|
-
checkoutUrl = `${base}/checkout/${appId}/${productId}/${priceId}?userId=${encodeURIComponent(userId)}`;
|
|
268
|
-
} else {
|
|
269
|
-
throw new Error(
|
|
270
|
-
"Either productCode or both productId and priceId are required"
|
|
271
|
-
);
|
|
272
|
-
}
|
|
273
|
-
}
|
|
274
|
-
const finalUrl = applyLocaleToUrl(checkoutUrl, options?.locale);
|
|
275
|
-
this.openHostedFrame(finalUrl, {
|
|
276
|
-
allowedOrigin: options?.allowedOrigin,
|
|
277
|
-
onCloseButton: () => options?.onCancel?.(),
|
|
278
|
-
onMessage: (data, container) => {
|
|
279
|
-
switch (data.type) {
|
|
280
|
-
case "PAYMENT_SUCCESS":
|
|
281
|
-
options?.onSuccess?.(data.orderId);
|
|
282
|
-
break;
|
|
283
|
-
case "PAYMENT_CANCELLED":
|
|
284
|
-
options?.onCancel?.(data.orderId);
|
|
285
|
-
break;
|
|
286
|
-
case "PAYMENT_RESIZE":
|
|
287
|
-
if (data.height) {
|
|
288
|
-
const maxHeight = window.innerHeight * 0.9;
|
|
289
|
-
const newHeight = Math.min(data.height, maxHeight);
|
|
290
|
-
container.style.height = `${newHeight}px`;
|
|
291
|
-
}
|
|
292
|
-
break;
|
|
293
|
-
case "PAYMENT_CLOSE":
|
|
294
|
-
this.close();
|
|
295
|
-
options?.onClose?.();
|
|
296
|
-
break;
|
|
297
|
-
}
|
|
298
|
-
}
|
|
299
|
-
});
|
|
300
|
-
}
|
|
301
|
-
/**
|
|
302
|
-
* Poll order status from integrator's API endpoint
|
|
303
|
-
* @param statusUrl - The integrator's API endpoint to check order status
|
|
304
|
-
* @param options - Polling options
|
|
305
|
-
* @returns Promise that resolves when order is paid or rejects on timeout/failure
|
|
306
|
-
*/
|
|
307
|
-
async pollOrderStatus(statusUrl, options) {
|
|
308
|
-
const interval = options?.interval || 3e3;
|
|
309
|
-
const timeout = options?.timeout || 3e5;
|
|
310
|
-
const startTime = Date.now();
|
|
311
|
-
return new Promise((resolve, reject) => {
|
|
312
|
-
const poll = async () => {
|
|
313
|
-
try {
|
|
314
|
-
const response = await fetch(statusUrl);
|
|
315
|
-
if (!response.ok) {
|
|
316
|
-
throw new Error(`Status check failed: ${response.status}`);
|
|
317
|
-
}
|
|
318
|
-
const status = await response.json();
|
|
319
|
-
options?.onStatusChange?.(status);
|
|
320
|
-
if (status.status === "PAID") {
|
|
321
|
-
resolve(status);
|
|
322
|
-
return;
|
|
323
|
-
}
|
|
324
|
-
if (status.status === "CANCELLED" || status.status === "FAILED") {
|
|
325
|
-
reject(new Error(`Order ${status.status.toLowerCase()}`));
|
|
326
|
-
return;
|
|
327
|
-
}
|
|
328
|
-
if (Date.now() - startTime > timeout) {
|
|
329
|
-
reject(new Error("Polling timeout"));
|
|
330
|
-
return;
|
|
331
|
-
}
|
|
332
|
-
setTimeout(poll, interval);
|
|
333
|
-
} catch (error) {
|
|
334
|
-
if (Date.now() - startTime > timeout) {
|
|
335
|
-
reject(error);
|
|
336
|
-
return;
|
|
337
|
-
}
|
|
338
|
-
setTimeout(poll, interval);
|
|
339
|
-
}
|
|
340
|
-
};
|
|
341
|
-
poll();
|
|
342
|
-
});
|
|
343
|
-
}
|
|
344
|
-
};
|
|
345
|
-
function createPaymentUI() {
|
|
346
|
-
return new PaymentUI();
|
|
347
|
-
}
|
|
348
|
-
|
|
349
241
|
// src/login.ts
|
|
350
242
|
function getOrigin(value) {
|
|
351
243
|
if (!value) return null;
|
|
@@ -355,10 +247,33 @@ function getOrigin(value) {
|
|
|
355
247
|
return null;
|
|
356
248
|
}
|
|
357
249
|
}
|
|
358
|
-
function
|
|
250
|
+
function createLoginCallbackState() {
|
|
251
|
+
if (typeof crypto !== "undefined" && crypto.randomUUID) {
|
|
252
|
+
return crypto.randomUUID().replace(/-/g, "");
|
|
253
|
+
}
|
|
254
|
+
return `${Date.now().toString(36)}${Math.random().toString(36).slice(2)}`;
|
|
255
|
+
}
|
|
256
|
+
function buildLoginCallbackUrl(callbackState, callbackUrl) {
|
|
257
|
+
const baseUrl = callbackUrl || window.location.href;
|
|
258
|
+
const url = new URL(baseUrl, window.location.href);
|
|
259
|
+
const returnHash = url.hash.replace(/^#/, "");
|
|
260
|
+
url.hash = "";
|
|
261
|
+
url.searchParams.set(LOGIN_CALLBACK_MARKER, "1");
|
|
262
|
+
url.searchParams.set(LOGIN_CALLBACK_STATE_PARAM, callbackState);
|
|
263
|
+
if (returnHash) {
|
|
264
|
+
url.searchParams.set(LOGIN_CALLBACK_RETURN_HASH_PARAM, returnHash);
|
|
265
|
+
} else {
|
|
266
|
+
url.searchParams.delete(LOGIN_CALLBACK_RETURN_HASH_PARAM);
|
|
267
|
+
}
|
|
268
|
+
return url.toString();
|
|
269
|
+
}
|
|
270
|
+
function buildLoginLaunchPayload(params, options, callbackState) {
|
|
359
271
|
const parentOrigin = typeof window !== "undefined" ? window.location.origin : void 0;
|
|
272
|
+
const callbackUrl = typeof window !== "undefined" && callbackState ? buildLoginCallbackUrl(callbackState, options?.callbackUrl) : void 0;
|
|
360
273
|
return {
|
|
361
274
|
autoClose: options?.autoClose ?? true,
|
|
275
|
+
callbackUrl,
|
|
276
|
+
callbackState,
|
|
362
277
|
origin: parentOrigin,
|
|
363
278
|
preferredChannel: params.preferredChannel
|
|
364
279
|
};
|
|
@@ -371,6 +286,9 @@ function appendLoginLaunchHash(urlValue, payload) {
|
|
|
371
286
|
if (payload.origin) {
|
|
372
287
|
hashParams.set("ydOrigin", payload.origin);
|
|
373
288
|
}
|
|
289
|
+
if (payload.callbackUrl) {
|
|
290
|
+
hashParams.set("ydCallbackUrl", payload.callbackUrl);
|
|
291
|
+
}
|
|
374
292
|
if (payload.preferredChannel) {
|
|
375
293
|
hashParams.set("ydPreferredChannel", payload.preferredChannel);
|
|
376
294
|
}
|
|
@@ -389,10 +307,9 @@ function appendLoginLaunchHash(urlValue, payload) {
|
|
|
389
307
|
function buildLoginPopupName(payload) {
|
|
390
308
|
return `youidian-login:${JSON.stringify(payload)}`;
|
|
391
309
|
}
|
|
392
|
-
function buildLoginUrl(params, options) {
|
|
310
|
+
function buildLoginUrl(params, options, launchPayload) {
|
|
393
311
|
const { appId, baseUrl = "https://pay.imgto.link", loginUrl } = params;
|
|
394
312
|
const rawBase = (loginUrl || baseUrl).replace(/\/$/, "");
|
|
395
|
-
const launchPayload = buildLoginLaunchPayload(params, options);
|
|
396
313
|
const parentOrigin = launchPayload.origin;
|
|
397
314
|
let finalUrl;
|
|
398
315
|
try {
|
|
@@ -423,6 +340,9 @@ function buildLoginUrl(params, options) {
|
|
|
423
340
|
if (parentOrigin) {
|
|
424
341
|
url.searchParams.set("origin", parentOrigin);
|
|
425
342
|
}
|
|
343
|
+
if (launchPayload.callbackUrl) {
|
|
344
|
+
url.searchParams.set("callbackUrl", launchPayload.callbackUrl);
|
|
345
|
+
}
|
|
426
346
|
return appendLoginLaunchHash(url.toString(), launchPayload);
|
|
427
347
|
} catch (_error) {
|
|
428
348
|
const searchParams = new URLSearchParams();
|
|
@@ -435,6 +355,9 @@ function buildLoginUrl(params, options) {
|
|
|
435
355
|
if (parentOrigin) {
|
|
436
356
|
searchParams.set("origin", parentOrigin);
|
|
437
357
|
}
|
|
358
|
+
if (launchPayload.callbackUrl) {
|
|
359
|
+
searchParams.set("callbackUrl", launchPayload.callbackUrl);
|
|
360
|
+
}
|
|
438
361
|
const query = searchParams.toString();
|
|
439
362
|
if (!query) {
|
|
440
363
|
return appendLoginLaunchHash(finalUrl, launchPayload);
|
|
@@ -461,6 +384,102 @@ function extractLoginResult(data) {
|
|
|
461
384
|
};
|
|
462
385
|
}
|
|
463
386
|
var LOGIN_UI_LOG_PREFIX = "[Youidian LoginUI]";
|
|
387
|
+
var LOGIN_CALLBACK_MARKER = "yd_login_callback";
|
|
388
|
+
var LOGIN_CALLBACK_RESULT_PARAM = "yd_login_result";
|
|
389
|
+
var LOGIN_CALLBACK_RETURN_HASH_PARAM = "yd_login_return_hash";
|
|
390
|
+
var LOGIN_CALLBACK_STATE_PARAM = "yd_login_state";
|
|
391
|
+
var LOGIN_CALLBACK_STORAGE_PREFIX = "yd-login-callback:";
|
|
392
|
+
function decodeCallbackPayload(value) {
|
|
393
|
+
try {
|
|
394
|
+
const padded = value.padEnd(
|
|
395
|
+
value.length + (4 - value.length % 4) % 4,
|
|
396
|
+
"="
|
|
397
|
+
);
|
|
398
|
+
const base64 = padded.replaceAll("-", "+").replaceAll("_", "/");
|
|
399
|
+
const binary = atob(base64);
|
|
400
|
+
const json = decodeURIComponent(
|
|
401
|
+
Array.from(
|
|
402
|
+
binary,
|
|
403
|
+
(char) => `%${char.charCodeAt(0).toString(16).padStart(2, "0")}`
|
|
404
|
+
).join("")
|
|
405
|
+
);
|
|
406
|
+
const parsed = JSON.parse(json);
|
|
407
|
+
return parsed && typeof parsed === "object" && parsed.type ? parsed : null;
|
|
408
|
+
} catch {
|
|
409
|
+
return null;
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
function getLoginCallbackChannelName(state) {
|
|
413
|
+
return `youidian-login:${state}`;
|
|
414
|
+
}
|
|
415
|
+
function getLoginCallbackStorageKey(state) {
|
|
416
|
+
return `${LOGIN_CALLBACK_STORAGE_PREFIX}${state}`;
|
|
417
|
+
}
|
|
418
|
+
function publishLoginCallback(state, data) {
|
|
419
|
+
try {
|
|
420
|
+
const channel = new BroadcastChannel(getLoginCallbackChannelName(state));
|
|
421
|
+
channel.postMessage(data);
|
|
422
|
+
channel.close();
|
|
423
|
+
} catch {
|
|
424
|
+
}
|
|
425
|
+
try {
|
|
426
|
+
const storageKey = getLoginCallbackStorageKey(state);
|
|
427
|
+
window.localStorage.setItem(storageKey, JSON.stringify(data));
|
|
428
|
+
window.setTimeout(() => {
|
|
429
|
+
try {
|
|
430
|
+
window.localStorage.removeItem(storageKey);
|
|
431
|
+
} catch {
|
|
432
|
+
}
|
|
433
|
+
}, 800);
|
|
434
|
+
} catch {
|
|
435
|
+
}
|
|
436
|
+
try {
|
|
437
|
+
window.opener?.postMessage(data, window.location.origin);
|
|
438
|
+
} catch {
|
|
439
|
+
}
|
|
440
|
+
}
|
|
441
|
+
function cleanLoginCallbackUrl(url) {
|
|
442
|
+
url.searchParams.delete(LOGIN_CALLBACK_MARKER);
|
|
443
|
+
url.searchParams.delete(LOGIN_CALLBACK_STATE_PARAM);
|
|
444
|
+
const returnHash = url.searchParams.get(LOGIN_CALLBACK_RETURN_HASH_PARAM);
|
|
445
|
+
url.searchParams.delete(LOGIN_CALLBACK_RETURN_HASH_PARAM);
|
|
446
|
+
url.hash = returnHash ? `#${returnHash}` : "";
|
|
447
|
+
return url.toString();
|
|
448
|
+
}
|
|
449
|
+
function handleLoginCallbackIfPresent() {
|
|
450
|
+
if (typeof window === "undefined") {
|
|
451
|
+
return false;
|
|
452
|
+
}
|
|
453
|
+
const url = new URL(window.location.href);
|
|
454
|
+
if (url.searchParams.get(LOGIN_CALLBACK_MARKER) !== "1") {
|
|
455
|
+
return false;
|
|
456
|
+
}
|
|
457
|
+
const state = url.searchParams.get(LOGIN_CALLBACK_STATE_PARAM) || "";
|
|
458
|
+
const hashParams = new URLSearchParams(url.hash.replace(/^#/, ""));
|
|
459
|
+
const rawResult = hashParams.get(LOGIN_CALLBACK_RESULT_PARAM);
|
|
460
|
+
const data = rawResult && state ? decodeCallbackPayload(rawResult) : {
|
|
461
|
+
type: "LOGIN_ERROR",
|
|
462
|
+
message: "Missing login callback result"
|
|
463
|
+
};
|
|
464
|
+
try {
|
|
465
|
+
document.documentElement.style.visibility = "hidden";
|
|
466
|
+
} catch {
|
|
467
|
+
}
|
|
468
|
+
if (state && data) {
|
|
469
|
+
publishLoginCallback(state, data);
|
|
470
|
+
}
|
|
471
|
+
try {
|
|
472
|
+
window.history.replaceState(null, "", cleanLoginCallbackUrl(url));
|
|
473
|
+
} catch {
|
|
474
|
+
}
|
|
475
|
+
window.setTimeout(() => {
|
|
476
|
+
try {
|
|
477
|
+
window.close();
|
|
478
|
+
} catch {
|
|
479
|
+
}
|
|
480
|
+
}, 30);
|
|
481
|
+
return true;
|
|
482
|
+
}
|
|
464
483
|
function logLoginDebug(message, details) {
|
|
465
484
|
if (typeof console === "undefined") return;
|
|
466
485
|
console.debug(LOGIN_UI_LOG_PREFIX, message, details || {});
|
|
@@ -476,14 +495,24 @@ function logLoginWarn(message, details) {
|
|
|
476
495
|
var LoginUI = class {
|
|
477
496
|
constructor() {
|
|
478
497
|
__publicField(this, "popup", null);
|
|
498
|
+
__publicField(this, "callbackChannel", null);
|
|
499
|
+
__publicField(this, "callbackStorageHandler", null);
|
|
479
500
|
__publicField(this, "messageHandler", null);
|
|
480
501
|
__publicField(this, "closeMonitor", null);
|
|
481
502
|
__publicField(this, "completed", false);
|
|
482
503
|
}
|
|
483
504
|
cleanup() {
|
|
505
|
+
if (this.callbackChannel) {
|
|
506
|
+
this.callbackChannel.close();
|
|
507
|
+
}
|
|
508
|
+
if (typeof window !== "undefined" && this.callbackStorageHandler) {
|
|
509
|
+
window.removeEventListener("storage", this.callbackStorageHandler);
|
|
510
|
+
}
|
|
484
511
|
if (typeof window !== "undefined" && this.messageHandler) {
|
|
485
512
|
window.removeEventListener("message", this.messageHandler);
|
|
486
513
|
}
|
|
514
|
+
this.callbackChannel = null;
|
|
515
|
+
this.callbackStorageHandler = null;
|
|
487
516
|
if (typeof window !== "undefined" && this.closeMonitor) {
|
|
488
517
|
window.clearInterval(this.closeMonitor);
|
|
489
518
|
}
|
|
@@ -492,6 +521,81 @@ var LoginUI = class {
|
|
|
492
521
|
this.popup = null;
|
|
493
522
|
this.completed = false;
|
|
494
523
|
}
|
|
524
|
+
handleLoginEvent(data, options) {
|
|
525
|
+
if (!data || typeof data !== "object" || !data.type) {
|
|
526
|
+
logLoginDebug("Ignored non-login event payload");
|
|
527
|
+
return;
|
|
528
|
+
}
|
|
529
|
+
logLoginInfo("Received login event", {
|
|
530
|
+
type: data.type
|
|
531
|
+
});
|
|
532
|
+
switch (data.type) {
|
|
533
|
+
case "LOGIN_SUCCESS":
|
|
534
|
+
if (this.completed) {
|
|
535
|
+
break;
|
|
536
|
+
}
|
|
537
|
+
this.completed = true;
|
|
538
|
+
logLoginInfo("Login succeeded", {
|
|
539
|
+
channel: data.channel || null,
|
|
540
|
+
userId: data.userId || null
|
|
541
|
+
});
|
|
542
|
+
options?.onSuccess?.(extractLoginResult(data));
|
|
543
|
+
break;
|
|
544
|
+
case "LOGIN_CANCELLED":
|
|
545
|
+
logLoginInfo("Login cancelled");
|
|
546
|
+
options?.onCancel?.();
|
|
547
|
+
break;
|
|
548
|
+
case "LOGIN_RESIZE":
|
|
549
|
+
break;
|
|
550
|
+
case "LOGIN_ERROR":
|
|
551
|
+
if (this.completed) {
|
|
552
|
+
break;
|
|
553
|
+
}
|
|
554
|
+
logLoginWarn("Login flow reported an error", {
|
|
555
|
+
message: data.message || data.error || null
|
|
556
|
+
});
|
|
557
|
+
options?.onError?.(data.message || data.error, data);
|
|
558
|
+
break;
|
|
559
|
+
case "LOGIN_CLOSE":
|
|
560
|
+
if (options?.autoClose === false) {
|
|
561
|
+
logLoginInfo(
|
|
562
|
+
"Login popup requested close; autoClose disabled, keeping popup open"
|
|
563
|
+
);
|
|
564
|
+
options?.onClose?.();
|
|
565
|
+
break;
|
|
566
|
+
}
|
|
567
|
+
logLoginInfo("Login popup requested close; autoClose enabled");
|
|
568
|
+
this.close();
|
|
569
|
+
options?.onClose?.();
|
|
570
|
+
break;
|
|
571
|
+
}
|
|
572
|
+
}
|
|
573
|
+
listenForLoginCallback(callbackState, options) {
|
|
574
|
+
try {
|
|
575
|
+
this.callbackChannel = new BroadcastChannel(
|
|
576
|
+
getLoginCallbackChannelName(callbackState)
|
|
577
|
+
);
|
|
578
|
+
this.callbackChannel.onmessage = (event) => {
|
|
579
|
+
this.handleLoginEvent(event.data, options);
|
|
580
|
+
this.handleLoginEvent({ type: "LOGIN_CLOSE" }, options);
|
|
581
|
+
};
|
|
582
|
+
} catch {
|
|
583
|
+
logLoginDebug("BroadcastChannel is unavailable for login callback");
|
|
584
|
+
}
|
|
585
|
+
this.callbackStorageHandler = (event) => {
|
|
586
|
+
if (event.key !== getLoginCallbackStorageKey(callbackState) || !event.newValue) {
|
|
587
|
+
return;
|
|
588
|
+
}
|
|
589
|
+
try {
|
|
590
|
+
const data = JSON.parse(event.newValue);
|
|
591
|
+
this.handleLoginEvent(data, options);
|
|
592
|
+
this.handleLoginEvent({ type: "LOGIN_CLOSE" }, options);
|
|
593
|
+
} catch {
|
|
594
|
+
logLoginWarn("Failed to parse login callback storage payload");
|
|
595
|
+
}
|
|
596
|
+
};
|
|
597
|
+
window.addEventListener("storage", this.callbackStorageHandler);
|
|
598
|
+
}
|
|
495
599
|
close() {
|
|
496
600
|
logLoginInfo("close() called", {
|
|
497
601
|
hasPopup: Boolean(this.popup),
|
|
@@ -505,7 +609,13 @@ var LoginUI = class {
|
|
|
505
609
|
openLogin(params, options) {
|
|
506
610
|
if (typeof window === "undefined") return;
|
|
507
611
|
if (this.popup && !this.popup.closed) return;
|
|
508
|
-
const
|
|
612
|
+
const callbackState = createLoginCallbackState();
|
|
613
|
+
const launchPayload = buildLoginLaunchPayload(
|
|
614
|
+
params,
|
|
615
|
+
options,
|
|
616
|
+
callbackState
|
|
617
|
+
);
|
|
618
|
+
const finalUrl = buildLoginUrl(params, options, launchPayload);
|
|
509
619
|
logLoginInfo("Opening hosted login popup", {
|
|
510
620
|
appId: params.appId,
|
|
511
621
|
loginUrl: finalUrl,
|
|
@@ -531,9 +641,7 @@ var LoginUI = class {
|
|
|
531
641
|
"resizable=yes",
|
|
532
642
|
"scrollbars=yes"
|
|
533
643
|
].join(",");
|
|
534
|
-
const popupName = buildLoginPopupName(
|
|
535
|
-
buildLoginLaunchPayload(params, options)
|
|
536
|
-
);
|
|
644
|
+
const popupName = buildLoginPopupName(launchPayload);
|
|
537
645
|
const popup = window.open(finalUrl, popupName, features);
|
|
538
646
|
if (!popup) {
|
|
539
647
|
logLoginWarn("Popup blocked by the browser");
|
|
@@ -542,6 +650,7 @@ var LoginUI = class {
|
|
|
542
650
|
}
|
|
543
651
|
this.popup = popup;
|
|
544
652
|
this.completed = false;
|
|
653
|
+
this.listenForLoginCallback(callbackState, options);
|
|
545
654
|
const allowedOrigin = options?.allowedOrigin;
|
|
546
655
|
const popupOrigin = getOrigin(finalUrl);
|
|
547
656
|
this.messageHandler = (event) => {
|
|
@@ -568,40 +677,7 @@ var LoginUI = class {
|
|
|
568
677
|
type: data.type,
|
|
569
678
|
eventOrigin: event.origin
|
|
570
679
|
});
|
|
571
|
-
|
|
572
|
-
case "LOGIN_SUCCESS":
|
|
573
|
-
this.completed = true;
|
|
574
|
-
logLoginInfo("Login succeeded", {
|
|
575
|
-
channel: data.channel || null,
|
|
576
|
-
userId: data.userId || null
|
|
577
|
-
});
|
|
578
|
-
options?.onSuccess?.(extractLoginResult(data));
|
|
579
|
-
break;
|
|
580
|
-
case "LOGIN_CANCELLED":
|
|
581
|
-
logLoginInfo("Login cancelled");
|
|
582
|
-
options?.onCancel?.();
|
|
583
|
-
break;
|
|
584
|
-
case "LOGIN_RESIZE":
|
|
585
|
-
break;
|
|
586
|
-
case "LOGIN_ERROR":
|
|
587
|
-
logLoginWarn("Login flow reported an error", {
|
|
588
|
-
message: data.message || data.error || null
|
|
589
|
-
});
|
|
590
|
-
options?.onError?.(data.message || data.error, data);
|
|
591
|
-
break;
|
|
592
|
-
case "LOGIN_CLOSE":
|
|
593
|
-
if (options?.autoClose === false) {
|
|
594
|
-
logLoginInfo(
|
|
595
|
-
"Login popup requested close; autoClose disabled, keeping popup open"
|
|
596
|
-
);
|
|
597
|
-
options?.onClose?.();
|
|
598
|
-
break;
|
|
599
|
-
}
|
|
600
|
-
logLoginInfo("Login popup requested close; autoClose enabled");
|
|
601
|
-
this.close();
|
|
602
|
-
options?.onClose?.();
|
|
603
|
-
break;
|
|
604
|
-
}
|
|
680
|
+
this.handleLoginEvent(data, options);
|
|
605
681
|
};
|
|
606
682
|
window.addEventListener("message", this.messageHandler);
|
|
607
683
|
this.closeMonitor = window.setInterval(() => {
|
|
@@ -622,6 +698,116 @@ var LoginUI = class {
|
|
|
622
698
|
function createLoginUI() {
|
|
623
699
|
return new LoginUI();
|
|
624
700
|
}
|
|
701
|
+
handleLoginCallbackIfPresent();
|
|
702
|
+
|
|
703
|
+
// src/client.ts
|
|
704
|
+
var PaymentUI = class extends HostedFrameModal {
|
|
705
|
+
/**
|
|
706
|
+
* Opens the payment checkout page in an iframe modal.
|
|
707
|
+
* @param urlOrParams - The checkout page URL or payment parameters
|
|
708
|
+
* @param options - UI options
|
|
709
|
+
*/
|
|
710
|
+
openPayment(urlOrParams, options) {
|
|
711
|
+
if (typeof document === "undefined") return;
|
|
712
|
+
if (this.modal) return;
|
|
713
|
+
let checkoutUrl;
|
|
714
|
+
if (typeof urlOrParams === "string") {
|
|
715
|
+
checkoutUrl = urlOrParams;
|
|
716
|
+
} else {
|
|
717
|
+
const {
|
|
718
|
+
appId,
|
|
719
|
+
productId,
|
|
720
|
+
priceId,
|
|
721
|
+
productCode,
|
|
722
|
+
userId,
|
|
723
|
+
checkoutUrl: checkoutUrlParam,
|
|
724
|
+
baseUrl = "https://pay.imgto.link"
|
|
725
|
+
} = urlOrParams;
|
|
726
|
+
const base = (checkoutUrlParam || baseUrl).replace(/\/$/, "");
|
|
727
|
+
if (productCode) {
|
|
728
|
+
checkoutUrl = `${base}/checkout/${appId}/code/${productCode}?userId=${encodeURIComponent(userId)}`;
|
|
729
|
+
} else if (productId && priceId) {
|
|
730
|
+
checkoutUrl = `${base}/checkout/${appId}/${productId}/${priceId}?userId=${encodeURIComponent(userId)}`;
|
|
731
|
+
} else {
|
|
732
|
+
throw new Error(
|
|
733
|
+
"Either productCode or both productId and priceId are required"
|
|
734
|
+
);
|
|
735
|
+
}
|
|
736
|
+
}
|
|
737
|
+
const finalUrl = applyLocaleToUrl(checkoutUrl, options?.locale);
|
|
738
|
+
this.openHostedFrame(finalUrl, {
|
|
739
|
+
allowedOrigin: options?.allowedOrigin,
|
|
740
|
+
onCloseButton: () => options?.onCancel?.(),
|
|
741
|
+
onMessage: (data, container) => {
|
|
742
|
+
switch (data.type) {
|
|
743
|
+
case "PAYMENT_SUCCESS":
|
|
744
|
+
options?.onSuccess?.(data.orderId);
|
|
745
|
+
break;
|
|
746
|
+
case "PAYMENT_CANCELLED":
|
|
747
|
+
options?.onCancel?.(data.orderId);
|
|
748
|
+
break;
|
|
749
|
+
case "PAYMENT_RESIZE":
|
|
750
|
+
if (data.height) {
|
|
751
|
+
const maxHeight = window.innerHeight * 0.9;
|
|
752
|
+
const newHeight = Math.min(data.height, maxHeight);
|
|
753
|
+
container.style.height = `${newHeight}px`;
|
|
754
|
+
}
|
|
755
|
+
break;
|
|
756
|
+
case "PAYMENT_CLOSE":
|
|
757
|
+
this.close();
|
|
758
|
+
options?.onClose?.();
|
|
759
|
+
break;
|
|
760
|
+
}
|
|
761
|
+
}
|
|
762
|
+
});
|
|
763
|
+
}
|
|
764
|
+
/**
|
|
765
|
+
* Poll order status from integrator's API endpoint
|
|
766
|
+
* @param statusUrl - The integrator's API endpoint to check order status
|
|
767
|
+
* @param options - Polling options
|
|
768
|
+
* @returns Promise that resolves when order is paid or rejects on timeout/failure
|
|
769
|
+
*/
|
|
770
|
+
async pollOrderStatus(statusUrl, options) {
|
|
771
|
+
const interval = options?.interval || 3e3;
|
|
772
|
+
const timeout = options?.timeout || 3e5;
|
|
773
|
+
const startTime = Date.now();
|
|
774
|
+
return new Promise((resolve, reject) => {
|
|
775
|
+
const poll = async () => {
|
|
776
|
+
try {
|
|
777
|
+
const response = await fetch(statusUrl);
|
|
778
|
+
if (!response.ok) {
|
|
779
|
+
throw new Error(`Status check failed: ${response.status}`);
|
|
780
|
+
}
|
|
781
|
+
const status = await response.json();
|
|
782
|
+
options?.onStatusChange?.(status);
|
|
783
|
+
if (status.status === "PAID") {
|
|
784
|
+
resolve(status);
|
|
785
|
+
return;
|
|
786
|
+
}
|
|
787
|
+
if (status.status === "CANCELLED" || status.status === "FAILED") {
|
|
788
|
+
reject(new Error(`Order ${status.status.toLowerCase()}`));
|
|
789
|
+
return;
|
|
790
|
+
}
|
|
791
|
+
if (Date.now() - startTime > timeout) {
|
|
792
|
+
reject(new Error("Polling timeout"));
|
|
793
|
+
return;
|
|
794
|
+
}
|
|
795
|
+
setTimeout(poll, interval);
|
|
796
|
+
} catch (error) {
|
|
797
|
+
if (Date.now() - startTime > timeout) {
|
|
798
|
+
reject(error);
|
|
799
|
+
return;
|
|
800
|
+
}
|
|
801
|
+
setTimeout(poll, interval);
|
|
802
|
+
}
|
|
803
|
+
};
|
|
804
|
+
poll();
|
|
805
|
+
});
|
|
806
|
+
}
|
|
807
|
+
};
|
|
808
|
+
function createPaymentUI() {
|
|
809
|
+
return new PaymentUI();
|
|
810
|
+
}
|
|
625
811
|
|
|
626
812
|
// src/server.ts
|
|
627
813
|
var import_crypto = __toESM(require("crypto"), 1);
|
|
@@ -879,6 +1065,7 @@ var PaymentClient = class {
|
|
|
879
1065
|
PaymentClient,
|
|
880
1066
|
PaymentUI,
|
|
881
1067
|
createLoginUI,
|
|
882
|
-
createPaymentUI
|
|
1068
|
+
createPaymentUI,
|
|
1069
|
+
handleLoginCallbackIfPresent
|
|
883
1070
|
});
|
|
884
1071
|
//# sourceMappingURL=index.cjs.map
|