@youidian/sdk 3.1.0 → 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/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,70 @@ function getOrigin(value) {
355
247
  return null;
356
248
  }
357
249
  }
358
- function buildLoginUrl(params) {
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) {
271
+ const parentOrigin = typeof window !== "undefined" ? window.location.origin : void 0;
272
+ const callbackUrl = typeof window !== "undefined" && callbackState ? buildLoginCallbackUrl(callbackState, options?.callbackUrl) : void 0;
273
+ return {
274
+ autoClose: options?.autoClose ?? true,
275
+ callbackUrl,
276
+ callbackState,
277
+ origin: parentOrigin,
278
+ preferredChannel: params.preferredChannel
279
+ };
280
+ }
281
+ function appendLoginLaunchHash(urlValue, payload) {
282
+ const hashParams = new URLSearchParams();
283
+ if (payload.autoClose === false) {
284
+ hashParams.set("ydAutoClose", "false");
285
+ }
286
+ if (payload.origin) {
287
+ hashParams.set("ydOrigin", payload.origin);
288
+ }
289
+ if (payload.callbackUrl) {
290
+ hashParams.set("ydCallbackUrl", payload.callbackUrl);
291
+ }
292
+ if (payload.preferredChannel) {
293
+ hashParams.set("ydPreferredChannel", payload.preferredChannel);
294
+ }
295
+ const hash = hashParams.toString();
296
+ if (!hash) {
297
+ return urlValue;
298
+ }
299
+ try {
300
+ const url = new URL(urlValue);
301
+ url.hash = hash;
302
+ return url.toString();
303
+ } catch (_error) {
304
+ return `${urlValue}#${hash}`;
305
+ }
306
+ }
307
+ function buildLoginPopupName(payload) {
308
+ return `youidian-login:${JSON.stringify(payload)}`;
309
+ }
310
+ function buildLoginUrl(params, options, launchPayload) {
359
311
  const { appId, baseUrl = "https://pay.imgto.link", loginUrl } = params;
360
312
  const rawBase = (loginUrl || baseUrl).replace(/\/$/, "");
361
- const parentOrigin = typeof window !== "undefined" ? window.location.origin : void 0;
313
+ const parentOrigin = launchPayload.origin;
362
314
  let finalUrl;
363
315
  try {
364
316
  const url = new URL(rawBase);
@@ -382,24 +334,39 @@ function buildLoginUrl(params) {
382
334
  if (params.preferredChannel) {
383
335
  url.searchParams.set("preferredChannel", params.preferredChannel);
384
336
  }
337
+ if (options?.autoClose === false) {
338
+ url.searchParams.set("autoClose", "false");
339
+ }
385
340
  if (parentOrigin) {
386
341
  url.searchParams.set("origin", parentOrigin);
387
342
  }
388
- return url.toString();
343
+ if (launchPayload.callbackUrl) {
344
+ url.searchParams.set("callbackUrl", launchPayload.callbackUrl);
345
+ }
346
+ return appendLoginLaunchHash(url.toString(), launchPayload);
389
347
  } catch (_error) {
390
348
  const searchParams = new URLSearchParams();
391
349
  if (params.preferredChannel) {
392
350
  searchParams.set("preferredChannel", params.preferredChannel);
393
351
  }
352
+ if (options?.autoClose === false) {
353
+ searchParams.set("autoClose", "false");
354
+ }
394
355
  if (parentOrigin) {
395
356
  searchParams.set("origin", parentOrigin);
396
357
  }
358
+ if (launchPayload.callbackUrl) {
359
+ searchParams.set("callbackUrl", launchPayload.callbackUrl);
360
+ }
397
361
  const query = searchParams.toString();
398
362
  if (!query) {
399
- return finalUrl;
363
+ return appendLoginLaunchHash(finalUrl, launchPayload);
400
364
  }
401
365
  const separator = finalUrl.includes("?") ? "&" : "?";
402
- return `${finalUrl}${separator}${query}`;
366
+ return appendLoginLaunchHash(
367
+ `${finalUrl}${separator}${query}`,
368
+ launchPayload
369
+ );
403
370
  }
404
371
  }
405
372
  function extractLoginResult(data) {
@@ -417,6 +384,102 @@ function extractLoginResult(data) {
417
384
  };
418
385
  }
419
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
+ }
420
483
  function logLoginDebug(message, details) {
421
484
  if (typeof console === "undefined") return;
422
485
  console.debug(LOGIN_UI_LOG_PREFIX, message, details || {});
@@ -432,14 +495,24 @@ function logLoginWarn(message, details) {
432
495
  var LoginUI = class {
433
496
  constructor() {
434
497
  __publicField(this, "popup", null);
498
+ __publicField(this, "callbackChannel", null);
499
+ __publicField(this, "callbackStorageHandler", null);
435
500
  __publicField(this, "messageHandler", null);
436
501
  __publicField(this, "closeMonitor", null);
437
502
  __publicField(this, "completed", false);
438
503
  }
439
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
+ }
440
511
  if (typeof window !== "undefined" && this.messageHandler) {
441
512
  window.removeEventListener("message", this.messageHandler);
442
513
  }
514
+ this.callbackChannel = null;
515
+ this.callbackStorageHandler = null;
443
516
  if (typeof window !== "undefined" && this.closeMonitor) {
444
517
  window.clearInterval(this.closeMonitor);
445
518
  }
@@ -448,6 +521,81 @@ var LoginUI = class {
448
521
  this.popup = null;
449
522
  this.completed = false;
450
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
+ }
451
599
  close() {
452
600
  logLoginInfo("close() called", {
453
601
  hasPopup: Boolean(this.popup),
@@ -461,7 +609,13 @@ var LoginUI = class {
461
609
  openLogin(params, options) {
462
610
  if (typeof window === "undefined") return;
463
611
  if (this.popup && !this.popup.closed) return;
464
- const finalUrl = buildLoginUrl(params);
612
+ const callbackState = createLoginCallbackState();
613
+ const launchPayload = buildLoginLaunchPayload(
614
+ params,
615
+ options,
616
+ callbackState
617
+ );
618
+ const finalUrl = buildLoginUrl(params, options, launchPayload);
465
619
  logLoginInfo("Opening hosted login popup", {
466
620
  appId: params.appId,
467
621
  loginUrl: finalUrl,
@@ -487,7 +641,8 @@ var LoginUI = class {
487
641
  "resizable=yes",
488
642
  "scrollbars=yes"
489
643
  ].join(",");
490
- const popup = window.open(finalUrl, "youidian-login", features);
644
+ const popupName = buildLoginPopupName(launchPayload);
645
+ const popup = window.open(finalUrl, popupName, features);
491
646
  if (!popup) {
492
647
  logLoginWarn("Popup blocked by the browser");
493
648
  options?.onError?.("Login popup was blocked");
@@ -495,6 +650,7 @@ var LoginUI = class {
495
650
  }
496
651
  this.popup = popup;
497
652
  this.completed = false;
653
+ this.listenForLoginCallback(callbackState, options);
498
654
  const allowedOrigin = options?.allowedOrigin;
499
655
  const popupOrigin = getOrigin(finalUrl);
500
656
  this.messageHandler = (event) => {
@@ -521,38 +677,7 @@ var LoginUI = class {
521
677
  type: data.type,
522
678
  eventOrigin: event.origin
523
679
  });
524
- switch (data.type) {
525
- case "LOGIN_SUCCESS":
526
- this.completed = true;
527
- logLoginInfo("Login succeeded", {
528
- channel: data.channel || null,
529
- userId: data.userId || null
530
- });
531
- options?.onSuccess?.(extractLoginResult(data));
532
- break;
533
- case "LOGIN_CANCELLED":
534
- logLoginInfo("Login cancelled");
535
- options?.onCancel?.();
536
- break;
537
- case "LOGIN_RESIZE":
538
- break;
539
- case "LOGIN_ERROR":
540
- logLoginWarn("Login flow reported an error", {
541
- message: data.message || data.error || null
542
- });
543
- options?.onError?.(data.message || data.error, data);
544
- break;
545
- case "LOGIN_CLOSE":
546
- if (options?.autoClose === false) {
547
- logLoginInfo("Login popup requested close; autoClose disabled, keeping popup open");
548
- options?.onClose?.();
549
- break;
550
- }
551
- logLoginInfo("Login popup requested close; autoClose enabled");
552
- this.close();
553
- options?.onClose?.();
554
- break;
555
- }
680
+ this.handleLoginEvent(data, options);
556
681
  };
557
682
  window.addEventListener("message", this.messageHandler);
558
683
  this.closeMonitor = window.setInterval(() => {
@@ -573,6 +698,116 @@ var LoginUI = class {
573
698
  function createLoginUI() {
574
699
  return new LoginUI();
575
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
+ }
576
811
 
577
812
  // src/server.ts
578
813
  var import_crypto = __toESM(require("crypto"), 1);
@@ -830,6 +1065,7 @@ var PaymentClient = class {
830
1065
  PaymentClient,
831
1066
  PaymentUI,
832
1067
  createLoginUI,
833
- createPaymentUI
1068
+ createPaymentUI,
1069
+ handleLoginCallbackIfPresent
834
1070
  });
835
1071
  //# sourceMappingURL=index.cjs.map