@youidian/sdk 3.1.1 → 3.2.1
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 +552 -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 +548 -1
- package/dist/client.js.map +1 -1
- package/dist/index.cjs +345 -153
- 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 +347 -156
- 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 +236 -44
- 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 +234 -43
- package/dist/login.js.map +1 -1
- package/package.json +1 -10
- 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,12 +609,23 @@ 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,
|
|
621
|
+
callbackState,
|
|
622
|
+
callbackUrl: launchPayload.callbackUrl || null,
|
|
623
|
+
hasCallbackUrl: Boolean(launchPayload.callbackUrl),
|
|
511
624
|
loginUrl: finalUrl,
|
|
512
625
|
allowedOrigin: options?.allowedOrigin || null,
|
|
513
|
-
autoClose: options?.autoClose ?? true
|
|
626
|
+
autoClose: options?.autoClose ?? true,
|
|
627
|
+
pageUrl: typeof window !== "undefined" ? window.location.href : null,
|
|
628
|
+
parentOrigin: launchPayload.origin || null
|
|
514
629
|
});
|
|
515
630
|
const popupWidth = 520;
|
|
516
631
|
const popupHeight = 720;
|
|
@@ -531,9 +646,7 @@ var LoginUI = class {
|
|
|
531
646
|
"resizable=yes",
|
|
532
647
|
"scrollbars=yes"
|
|
533
648
|
].join(",");
|
|
534
|
-
const popupName = buildLoginPopupName(
|
|
535
|
-
buildLoginLaunchPayload(params, options)
|
|
536
|
-
);
|
|
649
|
+
const popupName = buildLoginPopupName(launchPayload);
|
|
537
650
|
const popup = window.open(finalUrl, popupName, features);
|
|
538
651
|
if (!popup) {
|
|
539
652
|
logLoginWarn("Popup blocked by the browser");
|
|
@@ -542,6 +655,7 @@ var LoginUI = class {
|
|
|
542
655
|
}
|
|
543
656
|
this.popup = popup;
|
|
544
657
|
this.completed = false;
|
|
658
|
+
this.listenForLoginCallback(callbackState, options);
|
|
545
659
|
const allowedOrigin = options?.allowedOrigin;
|
|
546
660
|
const popupOrigin = getOrigin(finalUrl);
|
|
547
661
|
this.messageHandler = (event) => {
|
|
@@ -568,40 +682,7 @@ var LoginUI = class {
|
|
|
568
682
|
type: data.type,
|
|
569
683
|
eventOrigin: event.origin
|
|
570
684
|
});
|
|
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
|
-
}
|
|
685
|
+
this.handleLoginEvent(data, options);
|
|
605
686
|
};
|
|
606
687
|
window.addEventListener("message", this.messageHandler);
|
|
607
688
|
this.closeMonitor = window.setInterval(() => {
|
|
@@ -622,6 +703,116 @@ var LoginUI = class {
|
|
|
622
703
|
function createLoginUI() {
|
|
623
704
|
return new LoginUI();
|
|
624
705
|
}
|
|
706
|
+
handleLoginCallbackIfPresent();
|
|
707
|
+
|
|
708
|
+
// src/client.ts
|
|
709
|
+
var PaymentUI = class extends HostedFrameModal {
|
|
710
|
+
/**
|
|
711
|
+
* Opens the payment checkout page in an iframe modal.
|
|
712
|
+
* @param urlOrParams - The checkout page URL or payment parameters
|
|
713
|
+
* @param options - UI options
|
|
714
|
+
*/
|
|
715
|
+
openPayment(urlOrParams, options) {
|
|
716
|
+
if (typeof document === "undefined") return;
|
|
717
|
+
if (this.modal) return;
|
|
718
|
+
let checkoutUrl;
|
|
719
|
+
if (typeof urlOrParams === "string") {
|
|
720
|
+
checkoutUrl = urlOrParams;
|
|
721
|
+
} else {
|
|
722
|
+
const {
|
|
723
|
+
appId,
|
|
724
|
+
productId,
|
|
725
|
+
priceId,
|
|
726
|
+
productCode,
|
|
727
|
+
userId,
|
|
728
|
+
checkoutUrl: checkoutUrlParam,
|
|
729
|
+
baseUrl = "https://pay.imgto.link"
|
|
730
|
+
} = urlOrParams;
|
|
731
|
+
const base = (checkoutUrlParam || baseUrl).replace(/\/$/, "");
|
|
732
|
+
if (productCode) {
|
|
733
|
+
checkoutUrl = `${base}/checkout/${appId}/code/${productCode}?userId=${encodeURIComponent(userId)}`;
|
|
734
|
+
} else if (productId && priceId) {
|
|
735
|
+
checkoutUrl = `${base}/checkout/${appId}/${productId}/${priceId}?userId=${encodeURIComponent(userId)}`;
|
|
736
|
+
} else {
|
|
737
|
+
throw new Error(
|
|
738
|
+
"Either productCode or both productId and priceId are required"
|
|
739
|
+
);
|
|
740
|
+
}
|
|
741
|
+
}
|
|
742
|
+
const finalUrl = applyLocaleToUrl(checkoutUrl, options?.locale);
|
|
743
|
+
this.openHostedFrame(finalUrl, {
|
|
744
|
+
allowedOrigin: options?.allowedOrigin,
|
|
745
|
+
onCloseButton: () => options?.onCancel?.(),
|
|
746
|
+
onMessage: (data, container) => {
|
|
747
|
+
switch (data.type) {
|
|
748
|
+
case "PAYMENT_SUCCESS":
|
|
749
|
+
options?.onSuccess?.(data.orderId);
|
|
750
|
+
break;
|
|
751
|
+
case "PAYMENT_CANCELLED":
|
|
752
|
+
options?.onCancel?.(data.orderId);
|
|
753
|
+
break;
|
|
754
|
+
case "PAYMENT_RESIZE":
|
|
755
|
+
if (data.height) {
|
|
756
|
+
const maxHeight = window.innerHeight * 0.9;
|
|
757
|
+
const newHeight = Math.min(data.height, maxHeight);
|
|
758
|
+
container.style.height = `${newHeight}px`;
|
|
759
|
+
}
|
|
760
|
+
break;
|
|
761
|
+
case "PAYMENT_CLOSE":
|
|
762
|
+
this.close();
|
|
763
|
+
options?.onClose?.();
|
|
764
|
+
break;
|
|
765
|
+
}
|
|
766
|
+
}
|
|
767
|
+
});
|
|
768
|
+
}
|
|
769
|
+
/**
|
|
770
|
+
* Poll order status from integrator's API endpoint
|
|
771
|
+
* @param statusUrl - The integrator's API endpoint to check order status
|
|
772
|
+
* @param options - Polling options
|
|
773
|
+
* @returns Promise that resolves when order is paid or rejects on timeout/failure
|
|
774
|
+
*/
|
|
775
|
+
async pollOrderStatus(statusUrl, options) {
|
|
776
|
+
const interval = options?.interval || 3e3;
|
|
777
|
+
const timeout = options?.timeout || 3e5;
|
|
778
|
+
const startTime = Date.now();
|
|
779
|
+
return new Promise((resolve, reject) => {
|
|
780
|
+
const poll = async () => {
|
|
781
|
+
try {
|
|
782
|
+
const response = await fetch(statusUrl);
|
|
783
|
+
if (!response.ok) {
|
|
784
|
+
throw new Error(`Status check failed: ${response.status}`);
|
|
785
|
+
}
|
|
786
|
+
const status = await response.json();
|
|
787
|
+
options?.onStatusChange?.(status);
|
|
788
|
+
if (status.status === "PAID") {
|
|
789
|
+
resolve(status);
|
|
790
|
+
return;
|
|
791
|
+
}
|
|
792
|
+
if (status.status === "CANCELLED" || status.status === "FAILED") {
|
|
793
|
+
reject(new Error(`Order ${status.status.toLowerCase()}`));
|
|
794
|
+
return;
|
|
795
|
+
}
|
|
796
|
+
if (Date.now() - startTime > timeout) {
|
|
797
|
+
reject(new Error("Polling timeout"));
|
|
798
|
+
return;
|
|
799
|
+
}
|
|
800
|
+
setTimeout(poll, interval);
|
|
801
|
+
} catch (error) {
|
|
802
|
+
if (Date.now() - startTime > timeout) {
|
|
803
|
+
reject(error);
|
|
804
|
+
return;
|
|
805
|
+
}
|
|
806
|
+
setTimeout(poll, interval);
|
|
807
|
+
}
|
|
808
|
+
};
|
|
809
|
+
poll();
|
|
810
|
+
});
|
|
811
|
+
}
|
|
812
|
+
};
|
|
813
|
+
function createPaymentUI() {
|
|
814
|
+
return new PaymentUI();
|
|
815
|
+
}
|
|
625
816
|
|
|
626
817
|
// src/server.ts
|
|
627
818
|
var import_crypto = __toESM(require("crypto"), 1);
|
|
@@ -879,6 +1070,7 @@ var PaymentClient = class {
|
|
|
879
1070
|
PaymentClient,
|
|
880
1071
|
PaymentUI,
|
|
881
1072
|
createLoginUI,
|
|
882
|
-
createPaymentUI
|
|
1073
|
+
createPaymentUI,
|
|
1074
|
+
handleLoginCallbackIfPresent
|
|
883
1075
|
});
|
|
884
1076
|
//# sourceMappingURL=index.cjs.map
|