@youidian/sdk 3.4.1 → 3.4.2

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 CHANGED
@@ -23,14 +23,231 @@ var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "sy
23
23
  var client_exports = {};
24
24
  __export(client_exports, {
25
25
  LoginUI: () => LoginUI,
26
+ MessageUI: () => MessageUI,
26
27
  PaymentUI: () => PaymentUI,
27
28
  createLoginUI: () => createLoginUI,
29
+ createMessageUI: () => createMessageUI,
28
30
  createPaymentUI: () => createPaymentUI,
29
31
  detectLoginEnvironment: () => detectLoginEnvironment,
30
32
  handleLoginCallbackIfPresent: () => handleLoginCallbackIfPresent
31
33
  });
32
34
  module.exports = __toCommonJS(client_exports);
33
35
 
36
+ // src/brand-logo.ts
37
+ function ensureSdkBrandLogoStyles() {
38
+ if (document.getElementById?.("youidian-sdk-brand-logo-style")) {
39
+ return;
40
+ }
41
+ const style = document.createElement("style");
42
+ style.id = "youidian-sdk-brand-logo-style";
43
+ style.textContent = `
44
+ .youidian-sdk-brand-logo__ring,
45
+ .youidian-sdk-brand-logo__dot,
46
+ .youidian-sdk-brand-logo__copy,
47
+ .youidian-sdk-brand-logo__tagline {
48
+ transform-box: fill-box;
49
+ transform-origin: center;
50
+ will-change: opacity, transform;
51
+ }
52
+
53
+ .youidian-sdk-brand-logo[data-animated="true"] .youidian-sdk-brand-logo__ring {
54
+ animation: youidian-sdk-brand-logo-ring 1400ms cubic-bezier(0.4, 0, 0.2, 1) infinite;
55
+ }
56
+
57
+ .youidian-sdk-brand-logo[data-animated="true"] .youidian-sdk-brand-logo__dot {
58
+ animation: youidian-sdk-brand-logo-dot 1400ms cubic-bezier(0.4, 0, 0.2, 1) infinite;
59
+ }
60
+
61
+ .youidian-sdk-brand-logo[data-animated="true"] .youidian-sdk-brand-logo__copy {
62
+ animation: youidian-sdk-brand-logo-copy 1600ms cubic-bezier(0.4, 0, 0.2, 1) infinite;
63
+ }
64
+
65
+ .youidian-sdk-brand-logo[data-animated="true"] .youidian-sdk-brand-logo__tagline {
66
+ animation: youidian-sdk-brand-logo-tagline 1600ms cubic-bezier(0.4, 0, 0.2, 1) infinite;
67
+ }
68
+
69
+ @keyframes youidian-sdk-brand-logo-ring {
70
+ 0%,
71
+ 100% {
72
+ opacity: 0.72;
73
+ transform: rotate(0deg) scale(0.98);
74
+ }
75
+ 50% {
76
+ opacity: 1;
77
+ transform: rotate(8deg) scale(1.02);
78
+ }
79
+ }
80
+
81
+ @keyframes youidian-sdk-brand-logo-dot {
82
+ 0%,
83
+ 100% {
84
+ opacity: 0.58;
85
+ transform: scale(0.9);
86
+ }
87
+ 50% {
88
+ opacity: 1;
89
+ transform: scale(1.08);
90
+ }
91
+ }
92
+
93
+ @keyframes youidian-sdk-brand-logo-copy {
94
+ 0%,
95
+ 100% {
96
+ opacity: 0.86;
97
+ transform: translateY(0);
98
+ }
99
+ 50% {
100
+ opacity: 1;
101
+ transform: translateY(-1px);
102
+ }
103
+ }
104
+
105
+ @keyframes youidian-sdk-brand-logo-tagline {
106
+ 0%,
107
+ 100% {
108
+ opacity: 0.64;
109
+ }
110
+ 50% {
111
+ opacity: 0.92;
112
+ }
113
+ }
114
+
115
+ @media (prefers-reduced-motion: reduce) {
116
+ .youidian-sdk-brand-logo__ring,
117
+ .youidian-sdk-brand-logo__dot,
118
+ .youidian-sdk-brand-logo__copy,
119
+ .youidian-sdk-brand-logo__tagline {
120
+ animation: none;
121
+ opacity: 1;
122
+ transform: none;
123
+ }
124
+ }
125
+ `;
126
+ document.head?.appendChild(style);
127
+ }
128
+ function createSdkBrandLogoMark({
129
+ animated,
130
+ inverted,
131
+ size,
132
+ theme,
133
+ variant = "brand"
134
+ }) {
135
+ ensureSdkBrandLogoStyles();
136
+ const mark = document.createElementNS("http://www.w3.org/2000/svg", "svg");
137
+ mark.setAttribute("viewBox", "0 0 512 512");
138
+ mark.setAttribute("fill", "none");
139
+ mark.setAttribute("aria-hidden", "true");
140
+ Object.assign(mark.style, {
141
+ width: `${size}px`,
142
+ height: `${size}px`,
143
+ color: inverted ? "#ffffff" : theme.primary,
144
+ display: "block",
145
+ flex: "0 0 auto",
146
+ overflow: "visible"
147
+ });
148
+ const logoGroup = document.createElementNS("http://www.w3.org/2000/svg", "g");
149
+ logoGroup.setAttribute(
150
+ "transform",
151
+ variant === "mark" ? "matrix(0.74 0 0 0.74 -124 -114)" : "matrix(0.58 0 0 0.58 -40 -31)"
152
+ );
153
+ const ring = document.createElementNS("http://www.w3.org/2000/svg", "path");
154
+ ring.setAttribute("class", animated ? "youidian-sdk-brand-logo__ring" : "");
155
+ ring.setAttribute("fill", inverted ? "currentColor" : theme.primary);
156
+ ring.setAttribute(
157
+ "d",
158
+ "M704.298 679.54A264 264 0 1 1 704.298 309.46A49 49 0 0 1 634.4 378.149A166 166 0 1 0 634.4 610.851A49 49 0 0 1 704.298 679.54Z"
159
+ );
160
+ const dot = document.createElementNS("http://www.w3.org/2000/svg", "circle");
161
+ dot.setAttribute("class", animated ? "youidian-sdk-brand-logo__dot" : "");
162
+ dot.setAttribute("fill", inverted ? "currentColor" : theme.secondary);
163
+ dot.setAttribute("cx", "714.5");
164
+ dot.setAttribute("cy", "490.5");
165
+ dot.setAttribute("r", "68");
166
+ dot.setAttribute("opacity", inverted ? "0.78" : "1");
167
+ logoGroup.appendChild(ring);
168
+ logoGroup.appendChild(dot);
169
+ mark.appendChild(logoGroup);
170
+ return mark;
171
+ }
172
+ function createSdkBrandLogo({
173
+ animated,
174
+ brand,
175
+ brandWidth,
176
+ copyWidth,
177
+ gap = 10,
178
+ markMarginRight,
179
+ markSize,
180
+ nameSize,
181
+ subtitleMarginTop = 6,
182
+ subtitleSize,
183
+ subtitleWeight = "800",
184
+ theme,
185
+ variant = "brand"
186
+ }) {
187
+ ensureSdkBrandLogoStyles();
188
+ const wrapper = document.createElement("div");
189
+ wrapper.className = "youidian-sdk-brand-logo";
190
+ wrapper.setAttribute("data-animated", animated ? "true" : "false");
191
+ Object.assign(wrapper.style, {
192
+ display: "inline-flex",
193
+ alignItems: "center",
194
+ justifyContent: "center",
195
+ gap: `${gap}px`,
196
+ width: brandWidth ? `${brandWidth}px` : void 0
197
+ });
198
+ const copy = document.createElement("div");
199
+ copy.className = "youidian-sdk-brand-logo__copy";
200
+ copy.setAttribute("data-brand-locale", brand.language);
201
+ Object.assign(copy.style, {
202
+ display: "flex",
203
+ flexDirection: "column",
204
+ alignItems: "flex-start",
205
+ justifyContent: "center",
206
+ lineHeight: "1",
207
+ width: copyWidth ? `${copyWidth}px` : void 0
208
+ });
209
+ const name = document.createElement("div");
210
+ name.className = "youidian-sdk-brand-logo__name";
211
+ name.textContent = brand.name;
212
+ Object.assign(name.style, {
213
+ color: theme.foreground,
214
+ fontFamily: brand.language === "zh" ? '"Noto Sans TC", "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif' : '"Unbounded", "Space Grotesk", "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif',
215
+ fontSize: `${nameSize}px`,
216
+ fontWeight: brand.language === "zh" ? "900" : "800",
217
+ letterSpacing: brand.language === "zh" ? "0.08em" : "0.01em",
218
+ lineHeight: "1",
219
+ whiteSpace: "nowrap"
220
+ });
221
+ const subtitle = document.createElement("div");
222
+ subtitle.className = "youidian-sdk-brand-logo__tagline";
223
+ subtitle.textContent = brand.subtitle;
224
+ Object.assign(subtitle.style, {
225
+ color: theme.muted,
226
+ fontFamily: brand.language === "zh" ? '"Noto Sans TC", "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif' : '"Space Grotesk", "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif',
227
+ fontSize: `${subtitleSize}px`,
228
+ fontWeight: subtitleWeight,
229
+ letterSpacing: brand.language === "zh" ? "0.24em" : "0.07em",
230
+ lineHeight: "1",
231
+ marginTop: `${subtitleMarginTop}px`,
232
+ textTransform: brand.language === "zh" ? "none" : "uppercase",
233
+ whiteSpace: "nowrap"
234
+ });
235
+ copy.appendChild(name);
236
+ copy.appendChild(subtitle);
237
+ const mark = createSdkBrandLogoMark({
238
+ animated,
239
+ size: markSize,
240
+ theme,
241
+ variant
242
+ });
243
+ if (typeof markMarginRight === "number") {
244
+ mark.style.marginRight = `${markMarginRight}px`;
245
+ }
246
+ wrapper.appendChild(mark);
247
+ wrapper.appendChild(copy);
248
+ return wrapper;
249
+ }
250
+
34
251
  // src/hosted-modal.ts
35
252
  var SDK_SUPPORTED_LOCALES = [
36
253
  "en",
@@ -154,7 +371,7 @@ var HostedFrameModal = class {
154
371
  const container = document.createElement("div");
155
372
  Object.assign(container.style, {
156
373
  width: options.width || "450px",
157
- height: options.height || "min(600px, 90vh)",
374
+ height: options.height || "min(760px, calc(100vh - 32px))",
158
375
  backgroundColor: "transparent",
159
376
  borderRadius: "28px",
160
377
  overflow: "visible",
@@ -162,8 +379,7 @@ var HostedFrameModal = class {
162
379
  boxShadow: "none",
163
380
  maxWidth: "calc(100vw - 32px)",
164
381
  maxHeight: "calc(100vh - 32px)",
165
- transition: "height 180ms ease",
166
- willChange: "height"
382
+ transition: "none"
167
383
  });
168
384
  const closeBtn = document.createElement("button");
169
385
  closeBtn.innerHTML = "\xD7";
@@ -1004,6 +1220,7 @@ var LoginUI = class extends HostedFrameModal {
1004
1220
  this.completed = false;
1005
1221
  }
1006
1222
  markIframeReady() {
1223
+ if (this.iframeReady) return;
1007
1224
  this.iframeReady = true;
1008
1225
  if (typeof window !== "undefined" && this.iframeLoadTimer) {
1009
1226
  window.clearTimeout(this.iframeLoadTimer);
@@ -1248,6 +1465,17 @@ var LoginUI = class extends HostedFrameModal {
1248
1465
  },
1249
1466
  onMessage: (data, _container, event) => {
1250
1467
  const isIframeEvent = event.source === this.iframe?.contentWindow;
1468
+ if (!isIframeEvent && (data.type === "LOGIN_READY" || data.type === "LOGIN_RESIZE")) {
1469
+ logLoginWarn(
1470
+ "Login lifecycle event ignored: event.source does not match iframe contentWindow",
1471
+ {
1472
+ type: data.type,
1473
+ eventOrigin: event.origin,
1474
+ hasIframe: Boolean(this.iframe),
1475
+ hasIframeContentWindow: Boolean(this.iframe?.contentWindow)
1476
+ }
1477
+ );
1478
+ }
1251
1479
  if (isIframeEvent && (data.type === "LOGIN_READY" || data.type === "LOGIN_RESIZE") || data.type === "LOGIN_SUCCESS" || data.type === "LOGIN_ERROR") {
1252
1480
  this.markIframeReady();
1253
1481
  }
@@ -1382,6 +1610,406 @@ function createLoginUI() {
1382
1610
  handleLoginCallbackIfPresent({ autoClose: false });
1383
1611
 
1384
1612
  // src/client.ts
1613
+ var DEFAULT_PAYMENT_IFRAME_LOAD_TIMEOUT_MS = 8e3;
1614
+ var PAYMENT_UI_LOG_PREFIX = "[Youidian PaymentUI]";
1615
+ function hexToRgb(value) {
1616
+ const normalized = value.trim().replace(/^#/, "");
1617
+ if (!/^[0-9a-f]{3}([0-9a-f]{3})?$/i.test(normalized)) return null;
1618
+ const hex = normalized.length === 3 ? normalized.split("").map((char) => `${char}${char}`).join("") : normalized;
1619
+ const number = Number.parseInt(hex, 16);
1620
+ return {
1621
+ b: number & 255,
1622
+ g: number >> 8 & 255,
1623
+ r: number >> 16 & 255
1624
+ };
1625
+ }
1626
+ function alphaColor(value, alpha) {
1627
+ const rgb = hexToRgb(value);
1628
+ if (!rgb)
1629
+ return `color-mix(in srgb, ${value} ${Math.round(alpha * 100)}%, transparent)`;
1630
+ return `rgba(${rgb.r},${rgb.g},${rgb.b},${alpha})`;
1631
+ }
1632
+ function resolvePaymentTheme(options) {
1633
+ const primary = options?.theme?.primary || "#16a34a";
1634
+ const secondary = options?.theme?.secondary || "color-mix(in srgb, #16a34a 78%, white)";
1635
+ const foreground = options?.theme?.foreground || "#0f172a";
1636
+ const muted = options?.theme?.muted || "#64748b";
1637
+ const surface = options?.theme?.surface || "#ffffff";
1638
+ return {
1639
+ foreground,
1640
+ muted,
1641
+ primary,
1642
+ primary10: alphaColor(primary, 0.1),
1643
+ primary14: alphaColor(primary, 0.14),
1644
+ primary20: alphaColor(primary, 0.2),
1645
+ primary28: alphaColor(primary, 0.28),
1646
+ primary36: alphaColor(primary, 0.36),
1647
+ primary64: alphaColor(primary, 0.64),
1648
+ secondary,
1649
+ surface
1650
+ };
1651
+ }
1652
+ function logPaymentInfo(message, details) {
1653
+ if (typeof console === "undefined") return;
1654
+ console.info(PAYMENT_UI_LOG_PREFIX, message, details || {});
1655
+ }
1656
+ function logPaymentWarn(message, details) {
1657
+ if (typeof console === "undefined") return;
1658
+ console.warn(PAYMENT_UI_LOG_PREFIX, message, details || {});
1659
+ }
1660
+ function getPaymentIframeLoadTimeoutMs(options) {
1661
+ const timeout = options?.iframeLoadTimeoutMs;
1662
+ if (typeof timeout !== "number" || !Number.isFinite(timeout)) {
1663
+ return DEFAULT_PAYMENT_IFRAME_LOAD_TIMEOUT_MS;
1664
+ }
1665
+ return Math.max(0, timeout);
1666
+ }
1667
+ function isChinesePaymentLocale(locale) {
1668
+ return locale?.toLowerCase().startsWith("zh") || false;
1669
+ }
1670
+ function getPaymentFallbackText(options) {
1671
+ const isChinese = isChinesePaymentLocale(options?.locale);
1672
+ return {
1673
+ buttonText: options?.fallbackButtonText || (isChinese ? "\u5728\u65B0\u9875\u9762\u4E2D\u6253\u5F00" : "Open in new page"),
1674
+ description: isChinese ? "\u5F53\u524D\u7F51\u7EDC\u53EF\u80FD\u8F83\u6162\uFF0C\u5EFA\u8BAE\u91CD\u65B0\u5C1D\u8BD5\u6216\u5728\u65B0\u9875\u9762\u4E2D\u6253\u5F00\uFF0C\u4EE5\u83B7\u5F97\u66F4\u6D41\u7545\u7684\u652F\u4ED8\u4F53\u9A8C\u3002" : "Your network may be slow. Try again or open the payment page in a new page to continue.",
1675
+ retryText: options?.fallbackRetryText || (isChinese ? "\u91CD\u65B0\u5C1D\u8BD5" : "Try again"),
1676
+ title: isChinese ? "\u652F\u4ED8\u9875\u9762\u6253\u5F00\u8F83\u6162" : "Payment page is taking longer"
1677
+ };
1678
+ }
1679
+ function getPaymentBrandText(options) {
1680
+ const isChinese = isChinesePaymentLocale(options?.locale);
1681
+ return {
1682
+ language: isChinese ? "zh" : "en",
1683
+ name: isChinese ? "\u4F18\u6613\u70B9" : "Youidian",
1684
+ subtitle: isChinese ? "\u5F00\u53D1\u8005\u670D\u52A1\u5E73\u53F0" : "Developer Service Platform"
1685
+ };
1686
+ }
1687
+ function getPaymentBrandLoadingText(options) {
1688
+ const isChinese = isChinesePaymentLocale(options?.locale);
1689
+ return {
1690
+ language: isChinese ? "zh" : "en",
1691
+ name: isChinese ? "\u4F18\u6613\u70B9" : "Youidian",
1692
+ subtitle: isChinese ? "\u5F00\u53D1\u8005\u670D\u52A1\u4E2D\u53F0" : "Developer Service"
1693
+ };
1694
+ }
1695
+ function getPaymentLoadingText(options) {
1696
+ const isChinese = isChinesePaymentLocale(options?.locale);
1697
+ return options?.loadingTitle || options?.loadingDescription || (isChinese ? "\u52A0\u8F7D\u4E2D..." : "Loading...");
1698
+ }
1699
+ function applyPaymentPanelStyle(panel, theme) {
1700
+ Object.assign(panel.style, {
1701
+ position: "absolute",
1702
+ inset: "0",
1703
+ display: "flex",
1704
+ alignItems: "center",
1705
+ justifyContent: "center",
1706
+ borderRadius: "28px",
1707
+ background: `linear-gradient(180deg, ${theme.surface} 0%, rgba(248,250,252,0.98) 100%)`,
1708
+ color: theme.foreground,
1709
+ padding: "28px 24px",
1710
+ textAlign: "center",
1711
+ zIndex: "1",
1712
+ boxShadow: "none",
1713
+ backdropFilter: "blur(16px)"
1714
+ });
1715
+ }
1716
+ function createPaymentIcon(type) {
1717
+ const svg = document.createElementNS("http://www.w3.org/2000/svg", "svg");
1718
+ svg.setAttribute("viewBox", "0 0 24 24");
1719
+ svg.setAttribute("fill", "none");
1720
+ svg.setAttribute("aria-hidden", "true");
1721
+ Object.assign(svg.style, {
1722
+ display: "block",
1723
+ flex: "0 0 auto"
1724
+ });
1725
+ const paths = type === "external" ? [
1726
+ "M7 17L17 7",
1727
+ "M10 7H17V14",
1728
+ "M6.5 6.5H5.5A2.5 2.5 0 0 0 3 9V18.5A2.5 2.5 0 0 0 5.5 21H15A2.5 2.5 0 0 0 17.5 18.5V17.5"
1729
+ ] : type === "refresh" ? [
1730
+ "M20 6V11H15",
1731
+ "M4 18V13H9",
1732
+ "M18.2 9A7 7 0 0 0 6.2 6.6L4 9",
1733
+ "M5.8 15A7 7 0 0 0 17.8 17.4L20 15"
1734
+ ] : ["M12 7V12L15 14", "M21 12A9 9 0 1 1 3 12A9 9 0 0 1 21 12"];
1735
+ for (const value of paths) {
1736
+ const path = document.createElementNS("http://www.w3.org/2000/svg", "path");
1737
+ path.setAttribute("d", value);
1738
+ path.setAttribute("stroke", "currentColor");
1739
+ path.setAttribute("stroke-width", "2");
1740
+ path.setAttribute("stroke-linecap", "round");
1741
+ path.setAttribute("stroke-linejoin", "round");
1742
+ svg.appendChild(path);
1743
+ }
1744
+ return svg;
1745
+ }
1746
+ function createPaymentFallbackArtwork(theme) {
1747
+ const artwork = document.createElement("div");
1748
+ Object.assign(artwork.style, {
1749
+ height: "164px",
1750
+ margin: "0 auto 34px",
1751
+ position: "relative",
1752
+ width: "184px"
1753
+ });
1754
+ const wash = document.createElement("div");
1755
+ Object.assign(wash.style, {
1756
+ position: "absolute",
1757
+ left: "16px",
1758
+ top: "20px",
1759
+ width: "152px",
1760
+ height: "126px",
1761
+ borderRadius: "48% 52% 43% 57% / 54% 42% 58% 46%",
1762
+ background: `radial-gradient(circle at 48% 42%, ${theme.primary20}, ${theme.primary10} 52%, transparent 76%)`,
1763
+ filter: "blur(1px)",
1764
+ transform: "rotate(-10deg)"
1765
+ });
1766
+ const blob = document.createElement("div");
1767
+ Object.assign(blob.style, {
1768
+ position: "absolute",
1769
+ left: "26px",
1770
+ top: "28px",
1771
+ width: "132px",
1772
+ height: "116px",
1773
+ borderRadius: "42% 58% 53% 47% / 45% 39% 61% 55%",
1774
+ background: `linear-gradient(150deg, ${theme.primary14}, ${theme.primary10})`,
1775
+ boxShadow: `0 30px 64px ${theme.primary14}`,
1776
+ transform: "rotate(8deg)"
1777
+ });
1778
+ const tile = document.createElement("div");
1779
+ Object.assign(tile.style, {
1780
+ position: "absolute",
1781
+ left: "50%",
1782
+ top: "50%",
1783
+ transform: "translate(-50%, -50%) rotate(-4deg)",
1784
+ width: "112px",
1785
+ height: "106px",
1786
+ borderRadius: "38% 62% 50% 50% / 44% 43% 57% 56%",
1787
+ background: `linear-gradient(145deg, ${theme.primary}, ${theme.secondary})`,
1788
+ border: `1px solid ${theme.primary36}`,
1789
+ color: "#ffffff",
1790
+ display: "inline-flex",
1791
+ alignItems: "center",
1792
+ justifyContent: "center",
1793
+ boxShadow: `0 26px 54px ${theme.primary28}`
1794
+ });
1795
+ const icon = createPaymentIcon("slow");
1796
+ Object.assign(icon.style, {
1797
+ width: "52px",
1798
+ height: "52px",
1799
+ transform: "rotate(4deg)"
1800
+ });
1801
+ tile.appendChild(icon);
1802
+ artwork.appendChild(wash);
1803
+ artwork.appendChild(blob);
1804
+ artwork.appendChild(tile);
1805
+ return artwork;
1806
+ }
1807
+ function createPaymentBrandLoading(options, theme) {
1808
+ const brand = getPaymentBrandLoadingText(options);
1809
+ const wrapper = document.createElement("div");
1810
+ Object.assign(wrapper.style, {
1811
+ display: "flex",
1812
+ flexDirection: "column",
1813
+ alignItems: "center",
1814
+ justifyContent: "center",
1815
+ margin: "0 auto 16px"
1816
+ });
1817
+ wrapper.appendChild(
1818
+ createSdkBrandLogo({
1819
+ animated: true,
1820
+ brand,
1821
+ brandWidth: 292,
1822
+ copyWidth: 176,
1823
+ gap: 6,
1824
+ markMarginRight: -10,
1825
+ markSize: 112,
1826
+ nameSize: 39,
1827
+ subtitleMarginTop: 8,
1828
+ subtitleSize: 14,
1829
+ subtitleWeight: "700",
1830
+ theme,
1831
+ variant: "brand"
1832
+ })
1833
+ );
1834
+ return wrapper;
1835
+ }
1836
+ function createPaymentPlatformLogo(options, theme) {
1837
+ const brand = getPaymentBrandText(options);
1838
+ const wrapper = document.createElement("div");
1839
+ Object.assign(wrapper.style, {
1840
+ display: "flex",
1841
+ alignItems: "center",
1842
+ justifyContent: "center",
1843
+ margin: "0 auto 10px"
1844
+ });
1845
+ wrapper.appendChild(
1846
+ createSdkBrandLogo({
1847
+ brand,
1848
+ markSize: 48,
1849
+ nameSize: brand.language === "zh" ? 22 : 22,
1850
+ subtitleSize: brand.language === "zh" ? 9 : 9,
1851
+ theme
1852
+ })
1853
+ );
1854
+ return wrapper;
1855
+ }
1856
+ function createPaymentPanelContent() {
1857
+ const content = document.createElement("div");
1858
+ Object.assign(content.style, {
1859
+ display: "flex",
1860
+ flexDirection: "column",
1861
+ justifyContent: "center",
1862
+ maxWidth: "364px",
1863
+ minHeight: "100%",
1864
+ width: "100%"
1865
+ });
1866
+ return content;
1867
+ }
1868
+ function createPaymentPanelTitle(value, theme) {
1869
+ const title = document.createElement("div");
1870
+ title.textContent = value;
1871
+ Object.assign(title.style, {
1872
+ color: theme?.foreground || "#0f172a",
1873
+ fontSize: "24px",
1874
+ fontWeight: "900",
1875
+ lineHeight: "1.22",
1876
+ marginBottom: "16px"
1877
+ });
1878
+ return title;
1879
+ }
1880
+ function createPaymentPanelDescription(value, theme) {
1881
+ const description = document.createElement("div");
1882
+ description.textContent = value;
1883
+ Object.assign(description.style, {
1884
+ color: theme?.muted || "#64748b",
1885
+ fontSize: "14px",
1886
+ fontWeight: "600",
1887
+ lineHeight: "1.62",
1888
+ margin: "0 auto",
1889
+ maxWidth: "320px"
1890
+ });
1891
+ return description;
1892
+ }
1893
+ function createPaymentLoadingDescription(value, theme) {
1894
+ const description = document.createElement("div");
1895
+ description.textContent = value;
1896
+ Object.assign(description.style, {
1897
+ color: theme?.muted || "#64748b",
1898
+ fontSize: "14px",
1899
+ fontWeight: "500",
1900
+ lineHeight: "1.25",
1901
+ margin: "12px auto 0",
1902
+ maxWidth: "320px"
1903
+ });
1904
+ return description;
1905
+ }
1906
+ function createPaymentFallbackMessage(options) {
1907
+ const message = document.createElement("div");
1908
+ Object.assign(message.style, {
1909
+ margin: "0 auto",
1910
+ textAlign: "center"
1911
+ });
1912
+ message.appendChild(createPaymentFallbackArtwork(options.theme));
1913
+ message.appendChild(createPaymentPanelTitle(options.title, options.theme));
1914
+ message.appendChild(
1915
+ createPaymentPanelDescription(options.description, options.theme)
1916
+ );
1917
+ return message;
1918
+ }
1919
+ function createPaymentLoadingPanel(options) {
1920
+ const panel = document.createElement("div");
1921
+ const theme = resolvePaymentTheme(options);
1922
+ applyPaymentPanelStyle(panel, theme);
1923
+ const content = createPaymentPanelContent();
1924
+ content.appendChild(createPaymentBrandLoading(options, theme));
1925
+ content.appendChild(
1926
+ createPaymentLoadingDescription(getPaymentLoadingText(options), theme)
1927
+ );
1928
+ panel.appendChild(content);
1929
+ return panel;
1930
+ }
1931
+ function createPaymentButton(value, options) {
1932
+ const primary = options?.primary || false;
1933
+ const theme = options?.theme || resolvePaymentTheme();
1934
+ const button = document.createElement("button");
1935
+ button.type = "button";
1936
+ Object.assign(button.style, {
1937
+ width: "100%",
1938
+ height: "50px",
1939
+ borderRadius: "14px",
1940
+ border: primary ? `1px solid ${theme.primary}` : `1px solid ${theme.primary64}`,
1941
+ background: primary ? `linear-gradient(135deg, ${theme.primary}, ${theme.secondary})` : "#ffffff",
1942
+ color: primary ? "#ffffff" : theme.primary,
1943
+ cursor: "pointer",
1944
+ display: "inline-flex",
1945
+ alignItems: "center",
1946
+ justifyContent: "center",
1947
+ gap: "10px",
1948
+ fontSize: "15px",
1949
+ fontWeight: "800",
1950
+ marginTop: primary ? "12px" : "0",
1951
+ boxShadow: primary ? `0 14px 28px ${theme.primary20}` : "none"
1952
+ });
1953
+ if (options?.icon) {
1954
+ const icon = createPaymentIcon(options.icon);
1955
+ Object.assign(icon.style, {
1956
+ width: "17px",
1957
+ height: "17px"
1958
+ });
1959
+ button.appendChild(icon);
1960
+ }
1961
+ const label = document.createElement("span");
1962
+ label.textContent = value;
1963
+ button.appendChild(label);
1964
+ return button;
1965
+ }
1966
+ function createPaymentFallbackPanel(options) {
1967
+ const panel = document.createElement("div");
1968
+ const theme = resolvePaymentTheme(options.paymentOptions);
1969
+ applyPaymentPanelStyle(panel, theme);
1970
+ const content = createPaymentPanelContent();
1971
+ Object.assign(content.style, {
1972
+ justifyContent: "space-between"
1973
+ });
1974
+ const main = document.createElement("div");
1975
+ Object.assign(main.style, {
1976
+ display: "flex",
1977
+ flexDirection: "column",
1978
+ alignItems: "center",
1979
+ marginTop: "38px",
1980
+ width: "100%"
1981
+ });
1982
+ main.appendChild(
1983
+ createPaymentFallbackMessage({
1984
+ description: options.description,
1985
+ theme,
1986
+ title: options.title
1987
+ })
1988
+ );
1989
+ const actions = document.createElement("div");
1990
+ Object.assign(actions.style, {
1991
+ marginTop: "46px",
1992
+ width: "100%"
1993
+ });
1994
+ const retryButton = createPaymentButton(options.retryText, {
1995
+ icon: "refresh",
1996
+ theme
1997
+ });
1998
+ retryButton.onclick = options.onRetry;
1999
+ actions.appendChild(retryButton);
2000
+ const openButton = createPaymentButton(options.buttonText, {
2001
+ icon: "external",
2002
+ primary: true,
2003
+ theme
2004
+ });
2005
+ openButton.onclick = options.onOpen;
2006
+ actions.appendChild(openButton);
2007
+ main.appendChild(actions);
2008
+ content.appendChild(main);
2009
+ content.appendChild(createPaymentPlatformLogo(options.paymentOptions, theme));
2010
+ panel.appendChild(content);
2011
+ return panel;
2012
+ }
1385
2013
  var PaymentUI = class extends HostedFrameModal {
1386
2014
  constructor() {
1387
2015
  super(...arguments);
@@ -1390,6 +2018,127 @@ var PaymentUI = class extends HostedFrameModal {
1390
2018
  __publicField(this, "activeOrderId", null);
1391
2019
  __publicField(this, "paymentCompleted", false);
1392
2020
  __publicField(this, "cancelRequestedOrderId", null);
2021
+ __publicField(this, "iframeFallbackPanel", null);
2022
+ __publicField(this, "iframeLoadingPanel", null);
2023
+ __publicField(this, "iframeLoadTimer", null);
2024
+ __publicField(this, "iframeReady", false);
2025
+ }
2026
+ cleanupPaymentFrameState() {
2027
+ if (typeof window !== "undefined" && this.iframeLoadTimer) {
2028
+ window.clearTimeout(this.iframeLoadTimer);
2029
+ }
2030
+ if (this.iframeFallbackPanel?.parentNode) {
2031
+ this.iframeFallbackPanel.parentNode.removeChild(this.iframeFallbackPanel);
2032
+ }
2033
+ if (this.iframeLoadingPanel?.parentNode) {
2034
+ this.iframeLoadingPanel.parentNode.removeChild(this.iframeLoadingPanel);
2035
+ }
2036
+ this.iframeFallbackPanel = null;
2037
+ this.iframeLoadingPanel = null;
2038
+ this.iframeLoadTimer = null;
2039
+ this.iframeReady = false;
2040
+ }
2041
+ markPaymentIframeReady() {
2042
+ if (this.iframeReady) return;
2043
+ this.iframeReady = true;
2044
+ if (typeof window !== "undefined" && this.iframeLoadTimer) {
2045
+ window.clearTimeout(this.iframeLoadTimer);
2046
+ this.iframeLoadTimer = null;
2047
+ }
2048
+ if (this.iframeFallbackPanel?.parentNode) {
2049
+ this.iframeFallbackPanel.parentNode.removeChild(this.iframeFallbackPanel);
2050
+ }
2051
+ if (this.iframeLoadingPanel?.parentNode) {
2052
+ this.iframeLoadingPanel.parentNode.removeChild(this.iframeLoadingPanel);
2053
+ }
2054
+ this.iframeFallbackPanel = null;
2055
+ this.iframeLoadingPanel = null;
2056
+ }
2057
+ showPaymentIframeLoading(container, options) {
2058
+ if (this.iframeLoadingPanel || this.iframeReady) return;
2059
+ const panel = createPaymentLoadingPanel(options);
2060
+ this.iframeLoadingPanel = panel;
2061
+ container.appendChild(panel);
2062
+ }
2063
+ openFallbackPaymentPage(finalUrl, options) {
2064
+ if (typeof window === "undefined") return;
2065
+ const popup = window.open(
2066
+ finalUrl,
2067
+ "youidian-payment-checkout",
2068
+ "popup=yes,width=480,height=760,resizable=yes,scrollbars=yes"
2069
+ );
2070
+ if (popup) {
2071
+ logPaymentInfo("Opened hosted checkout fallback page", {
2072
+ checkoutUrl: finalUrl
2073
+ });
2074
+ options?.onFallbackOpen?.(finalUrl);
2075
+ try {
2076
+ popup.focus();
2077
+ } catch {
2078
+ }
2079
+ return;
2080
+ }
2081
+ logPaymentWarn("Hosted checkout fallback popup was blocked", {
2082
+ checkoutUrl: finalUrl
2083
+ });
2084
+ options?.onFallbackBlocked?.(finalUrl);
2085
+ window.location.assign(finalUrl);
2086
+ }
2087
+ showPaymentIframeFallback(options) {
2088
+ if (this.iframeReady || this.iframeFallbackPanel) return;
2089
+ const { container, finalUrl, paymentOptions } = options;
2090
+ logPaymentWarn("Hosted checkout iframe did not report ready in time", {
2091
+ checkoutUrl: finalUrl
2092
+ });
2093
+ if (this.iframeLoadingPanel?.parentNode) {
2094
+ this.iframeLoadingPanel.parentNode.removeChild(this.iframeLoadingPanel);
2095
+ }
2096
+ this.iframeLoadingPanel = null;
2097
+ const fallbackText = getPaymentFallbackText(paymentOptions);
2098
+ const panel = createPaymentFallbackPanel({
2099
+ buttonText: fallbackText.buttonText,
2100
+ description: fallbackText.description,
2101
+ onOpen: () => {
2102
+ this.openFallbackPaymentPage(finalUrl, paymentOptions);
2103
+ },
2104
+ onRetry: () => {
2105
+ this.iframeFallbackPanel = null;
2106
+ if (panel.parentNode) {
2107
+ panel.parentNode.removeChild(panel);
2108
+ }
2109
+ this.iframeReady = false;
2110
+ this.showPaymentIframeLoading(container, paymentOptions);
2111
+ if (this.iframe) {
2112
+ this.iframe.src = finalUrl;
2113
+ }
2114
+ this.startPaymentIframeWatchdog({
2115
+ container,
2116
+ finalUrl,
2117
+ paymentOptions
2118
+ });
2119
+ },
2120
+ paymentOptions,
2121
+ retryText: fallbackText.retryText,
2122
+ title: fallbackText.title
2123
+ });
2124
+ this.iframeFallbackPanel = panel;
2125
+ container.appendChild(panel);
2126
+ paymentOptions?.onFallbackVisible?.();
2127
+ }
2128
+ startPaymentIframeWatchdog(options) {
2129
+ if (typeof window === "undefined") return;
2130
+ if (this.iframeLoadTimer) {
2131
+ window.clearTimeout(this.iframeLoadTimer);
2132
+ }
2133
+ const timeoutMs = getPaymentIframeLoadTimeoutMs(options.paymentOptions);
2134
+ if (timeoutMs === 0) {
2135
+ this.showPaymentIframeFallback(options);
2136
+ return;
2137
+ }
2138
+ this.iframeLoadTimer = window.setTimeout(() => {
2139
+ this.iframeLoadTimer = null;
2140
+ this.showPaymentIframeFallback(options);
2141
+ }, timeoutMs);
1393
2142
  }
1394
2143
  cancelHostedOrder(reason = "user_cancel", orderId) {
1395
2144
  const targetOrderId = orderId || this.activeOrderId;
@@ -1432,6 +2181,7 @@ var PaymentUI = class extends HostedFrameModal {
1432
2181
  this.activeOrderId = null;
1433
2182
  this.paymentCompleted = false;
1434
2183
  this.cancelRequestedOrderId = null;
2184
+ this.cleanupPaymentFrameState();
1435
2185
  if (typeof urlOrParams === "string") {
1436
2186
  checkoutUrl = urlOrParams;
1437
2187
  try {
@@ -1500,14 +2250,21 @@ var PaymentUI = class extends HostedFrameModal {
1500
2250
  checkoutUrl,
1501
2251
  getAutoResolvedLocale(options?.locale)
1502
2252
  );
1503
- this.openHostedFrame(finalUrl, {
2253
+ const hostedFrame = this.openHostedFrame(finalUrl, {
1504
2254
  allowedOrigin: options?.allowedOrigin,
2255
+ height: "min(760px, calc(100vh - 32px))",
1505
2256
  onCloseButton: () => {
1506
2257
  this.cancelHostedOrder("modal_close");
1507
2258
  options?.onCancel?.(this.activeOrderId || void 0);
1508
2259
  },
1509
- onMessage: (data, container) => {
2260
+ onMessage: (data, container, event) => {
2261
+ const isIframeMessage = event.source === this.iframe?.contentWindow;
2262
+ if (isIframeMessage && (data.type === "PAYMENT_READY" || data.type === "PAYMENT_RESIZE" || data.type === "PAYMENT_STARTED")) {
2263
+ this.markPaymentIframeReady();
2264
+ }
1510
2265
  switch (data.type) {
2266
+ case "PAYMENT_READY":
2267
+ break;
1511
2268
  case "PAYMENT_STARTED":
1512
2269
  if (data.orderId) {
1513
2270
  this.activeOrderId = data.orderId;
@@ -1522,11 +2279,6 @@ var PaymentUI = class extends HostedFrameModal {
1522
2279
  options?.onCancel?.(data.orderId);
1523
2280
  break;
1524
2281
  case "PAYMENT_RESIZE":
1525
- if (data.height) {
1526
- const maxHeight = window.innerHeight * 0.9;
1527
- const newHeight = Math.min(data.height, maxHeight);
1528
- container.style.height = `${newHeight}px`;
1529
- }
1530
2282
  break;
1531
2283
  case "PAYMENT_CLOSE":
1532
2284
  this.cancelHostedOrder("payment_close", data.orderId);
@@ -1534,8 +2286,21 @@ var PaymentUI = class extends HostedFrameModal {
1534
2286
  options?.onClose?.();
1535
2287
  break;
1536
2288
  }
1537
- }
2289
+ },
2290
+ width: "min(450px, 100%)"
1538
2291
  });
2292
+ if (hostedFrame) {
2293
+ this.showPaymentIframeLoading(hostedFrame.container, options);
2294
+ this.startPaymentIframeWatchdog({
2295
+ container: hostedFrame.container,
2296
+ finalUrl,
2297
+ paymentOptions: options
2298
+ });
2299
+ }
2300
+ }
2301
+ close() {
2302
+ super.close();
2303
+ this.cleanupPaymentFrameState();
1539
2304
  }
1540
2305
  /**
1541
2306
  * Poll order status from integrator's API endpoint
@@ -1581,14 +2346,39 @@ var PaymentUI = class extends HostedFrameModal {
1581
2346
  });
1582
2347
  }
1583
2348
  };
2349
+ var MessageUI = class {
2350
+ openWechatBinding(options) {
2351
+ const bindingUrl = options.bindingUrl?.trim();
2352
+ if (!bindingUrl) {
2353
+ throw new Error("bindingUrl is required");
2354
+ }
2355
+ if (options.displayMode === "popup") {
2356
+ const popup = window.open(
2357
+ bindingUrl,
2358
+ options.popupName || "youidian-wechat-message-binding",
2359
+ "width=480,height=720,noopener,noreferrer"
2360
+ );
2361
+ if (!popup) {
2362
+ window.location.href = bindingUrl;
2363
+ }
2364
+ return;
2365
+ }
2366
+ window.location.href = bindingUrl;
2367
+ }
2368
+ };
2369
+ function createMessageUI() {
2370
+ return new MessageUI();
2371
+ }
1584
2372
  function createPaymentUI() {
1585
2373
  return new PaymentUI();
1586
2374
  }
1587
2375
  // Annotate the CommonJS export names for ESM import in node:
1588
2376
  0 && (module.exports = {
1589
2377
  LoginUI,
2378
+ MessageUI,
1590
2379
  PaymentUI,
1591
2380
  createLoginUI,
2381
+ createMessageUI,
1592
2382
  createPaymentUI,
1593
2383
  detectLoginEnvironment,
1594
2384
  handleLoginCallbackIfPresent