@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.js CHANGED
@@ -2,6 +2,221 @@ var __defProp = Object.defineProperty;
2
2
  var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
3
3
  var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
4
4
 
5
+ // src/brand-logo.ts
6
+ function ensureSdkBrandLogoStyles() {
7
+ if (document.getElementById?.("youidian-sdk-brand-logo-style")) {
8
+ return;
9
+ }
10
+ const style = document.createElement("style");
11
+ style.id = "youidian-sdk-brand-logo-style";
12
+ style.textContent = `
13
+ .youidian-sdk-brand-logo__ring,
14
+ .youidian-sdk-brand-logo__dot,
15
+ .youidian-sdk-brand-logo__copy,
16
+ .youidian-sdk-brand-logo__tagline {
17
+ transform-box: fill-box;
18
+ transform-origin: center;
19
+ will-change: opacity, transform;
20
+ }
21
+
22
+ .youidian-sdk-brand-logo[data-animated="true"] .youidian-sdk-brand-logo__ring {
23
+ animation: youidian-sdk-brand-logo-ring 1400ms cubic-bezier(0.4, 0, 0.2, 1) infinite;
24
+ }
25
+
26
+ .youidian-sdk-brand-logo[data-animated="true"] .youidian-sdk-brand-logo__dot {
27
+ animation: youidian-sdk-brand-logo-dot 1400ms cubic-bezier(0.4, 0, 0.2, 1) infinite;
28
+ }
29
+
30
+ .youidian-sdk-brand-logo[data-animated="true"] .youidian-sdk-brand-logo__copy {
31
+ animation: youidian-sdk-brand-logo-copy 1600ms cubic-bezier(0.4, 0, 0.2, 1) infinite;
32
+ }
33
+
34
+ .youidian-sdk-brand-logo[data-animated="true"] .youidian-sdk-brand-logo__tagline {
35
+ animation: youidian-sdk-brand-logo-tagline 1600ms cubic-bezier(0.4, 0, 0.2, 1) infinite;
36
+ }
37
+
38
+ @keyframes youidian-sdk-brand-logo-ring {
39
+ 0%,
40
+ 100% {
41
+ opacity: 0.72;
42
+ transform: rotate(0deg) scale(0.98);
43
+ }
44
+ 50% {
45
+ opacity: 1;
46
+ transform: rotate(8deg) scale(1.02);
47
+ }
48
+ }
49
+
50
+ @keyframes youidian-sdk-brand-logo-dot {
51
+ 0%,
52
+ 100% {
53
+ opacity: 0.58;
54
+ transform: scale(0.9);
55
+ }
56
+ 50% {
57
+ opacity: 1;
58
+ transform: scale(1.08);
59
+ }
60
+ }
61
+
62
+ @keyframes youidian-sdk-brand-logo-copy {
63
+ 0%,
64
+ 100% {
65
+ opacity: 0.86;
66
+ transform: translateY(0);
67
+ }
68
+ 50% {
69
+ opacity: 1;
70
+ transform: translateY(-1px);
71
+ }
72
+ }
73
+
74
+ @keyframes youidian-sdk-brand-logo-tagline {
75
+ 0%,
76
+ 100% {
77
+ opacity: 0.64;
78
+ }
79
+ 50% {
80
+ opacity: 0.92;
81
+ }
82
+ }
83
+
84
+ @media (prefers-reduced-motion: reduce) {
85
+ .youidian-sdk-brand-logo__ring,
86
+ .youidian-sdk-brand-logo__dot,
87
+ .youidian-sdk-brand-logo__copy,
88
+ .youidian-sdk-brand-logo__tagline {
89
+ animation: none;
90
+ opacity: 1;
91
+ transform: none;
92
+ }
93
+ }
94
+ `;
95
+ document.head?.appendChild(style);
96
+ }
97
+ function createSdkBrandLogoMark({
98
+ animated,
99
+ inverted,
100
+ size,
101
+ theme,
102
+ variant = "brand"
103
+ }) {
104
+ ensureSdkBrandLogoStyles();
105
+ const mark = document.createElementNS("http://www.w3.org/2000/svg", "svg");
106
+ mark.setAttribute("viewBox", "0 0 512 512");
107
+ mark.setAttribute("fill", "none");
108
+ mark.setAttribute("aria-hidden", "true");
109
+ Object.assign(mark.style, {
110
+ width: `${size}px`,
111
+ height: `${size}px`,
112
+ color: inverted ? "#ffffff" : theme.primary,
113
+ display: "block",
114
+ flex: "0 0 auto",
115
+ overflow: "visible"
116
+ });
117
+ const logoGroup = document.createElementNS("http://www.w3.org/2000/svg", "g");
118
+ logoGroup.setAttribute(
119
+ "transform",
120
+ variant === "mark" ? "matrix(0.74 0 0 0.74 -124 -114)" : "matrix(0.58 0 0 0.58 -40 -31)"
121
+ );
122
+ const ring = document.createElementNS("http://www.w3.org/2000/svg", "path");
123
+ ring.setAttribute("class", animated ? "youidian-sdk-brand-logo__ring" : "");
124
+ ring.setAttribute("fill", inverted ? "currentColor" : theme.primary);
125
+ ring.setAttribute(
126
+ "d",
127
+ "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"
128
+ );
129
+ const dot = document.createElementNS("http://www.w3.org/2000/svg", "circle");
130
+ dot.setAttribute("class", animated ? "youidian-sdk-brand-logo__dot" : "");
131
+ dot.setAttribute("fill", inverted ? "currentColor" : theme.secondary);
132
+ dot.setAttribute("cx", "714.5");
133
+ dot.setAttribute("cy", "490.5");
134
+ dot.setAttribute("r", "68");
135
+ dot.setAttribute("opacity", inverted ? "0.78" : "1");
136
+ logoGroup.appendChild(ring);
137
+ logoGroup.appendChild(dot);
138
+ mark.appendChild(logoGroup);
139
+ return mark;
140
+ }
141
+ function createSdkBrandLogo({
142
+ animated,
143
+ brand,
144
+ brandWidth,
145
+ copyWidth,
146
+ gap = 10,
147
+ markMarginRight,
148
+ markSize,
149
+ nameSize,
150
+ subtitleMarginTop = 6,
151
+ subtitleSize,
152
+ subtitleWeight = "800",
153
+ theme,
154
+ variant = "brand"
155
+ }) {
156
+ ensureSdkBrandLogoStyles();
157
+ const wrapper = document.createElement("div");
158
+ wrapper.className = "youidian-sdk-brand-logo";
159
+ wrapper.setAttribute("data-animated", animated ? "true" : "false");
160
+ Object.assign(wrapper.style, {
161
+ display: "inline-flex",
162
+ alignItems: "center",
163
+ justifyContent: "center",
164
+ gap: `${gap}px`,
165
+ width: brandWidth ? `${brandWidth}px` : void 0
166
+ });
167
+ const copy = document.createElement("div");
168
+ copy.className = "youidian-sdk-brand-logo__copy";
169
+ copy.setAttribute("data-brand-locale", brand.language);
170
+ Object.assign(copy.style, {
171
+ display: "flex",
172
+ flexDirection: "column",
173
+ alignItems: "flex-start",
174
+ justifyContent: "center",
175
+ lineHeight: "1",
176
+ width: copyWidth ? `${copyWidth}px` : void 0
177
+ });
178
+ const name = document.createElement("div");
179
+ name.className = "youidian-sdk-brand-logo__name";
180
+ name.textContent = brand.name;
181
+ Object.assign(name.style, {
182
+ color: theme.foreground,
183
+ 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',
184
+ fontSize: `${nameSize}px`,
185
+ fontWeight: brand.language === "zh" ? "900" : "800",
186
+ letterSpacing: brand.language === "zh" ? "0.08em" : "0.01em",
187
+ lineHeight: "1",
188
+ whiteSpace: "nowrap"
189
+ });
190
+ const subtitle = document.createElement("div");
191
+ subtitle.className = "youidian-sdk-brand-logo__tagline";
192
+ subtitle.textContent = brand.subtitle;
193
+ Object.assign(subtitle.style, {
194
+ color: theme.muted,
195
+ 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',
196
+ fontSize: `${subtitleSize}px`,
197
+ fontWeight: subtitleWeight,
198
+ letterSpacing: brand.language === "zh" ? "0.24em" : "0.07em",
199
+ lineHeight: "1",
200
+ marginTop: `${subtitleMarginTop}px`,
201
+ textTransform: brand.language === "zh" ? "none" : "uppercase",
202
+ whiteSpace: "nowrap"
203
+ });
204
+ copy.appendChild(name);
205
+ copy.appendChild(subtitle);
206
+ const mark = createSdkBrandLogoMark({
207
+ animated,
208
+ size: markSize,
209
+ theme,
210
+ variant
211
+ });
212
+ if (typeof markMarginRight === "number") {
213
+ mark.style.marginRight = `${markMarginRight}px`;
214
+ }
215
+ wrapper.appendChild(mark);
216
+ wrapper.appendChild(copy);
217
+ return wrapper;
218
+ }
219
+
5
220
  // src/hosted-modal.ts
6
221
  var SDK_SUPPORTED_LOCALES = [
7
222
  "en",
@@ -125,7 +340,7 @@ var HostedFrameModal = class {
125
340
  const container = document.createElement("div");
126
341
  Object.assign(container.style, {
127
342
  width: options.width || "450px",
128
- height: options.height || "min(600px, 90vh)",
343
+ height: options.height || "min(760px, calc(100vh - 32px))",
129
344
  backgroundColor: "transparent",
130
345
  borderRadius: "28px",
131
346
  overflow: "visible",
@@ -133,8 +348,7 @@ var HostedFrameModal = class {
133
348
  boxShadow: "none",
134
349
  maxWidth: "calc(100vw - 32px)",
135
350
  maxHeight: "calc(100vh - 32px)",
136
- transition: "height 180ms ease",
137
- willChange: "height"
351
+ transition: "none"
138
352
  });
139
353
  const closeBtn = document.createElement("button");
140
354
  closeBtn.innerHTML = "\xD7";
@@ -975,6 +1189,7 @@ var LoginUI = class extends HostedFrameModal {
975
1189
  this.completed = false;
976
1190
  }
977
1191
  markIframeReady() {
1192
+ if (this.iframeReady) return;
978
1193
  this.iframeReady = true;
979
1194
  if (typeof window !== "undefined" && this.iframeLoadTimer) {
980
1195
  window.clearTimeout(this.iframeLoadTimer);
@@ -1219,6 +1434,17 @@ var LoginUI = class extends HostedFrameModal {
1219
1434
  },
1220
1435
  onMessage: (data, _container, event) => {
1221
1436
  const isIframeEvent = event.source === this.iframe?.contentWindow;
1437
+ if (!isIframeEvent && (data.type === "LOGIN_READY" || data.type === "LOGIN_RESIZE")) {
1438
+ logLoginWarn(
1439
+ "Login lifecycle event ignored: event.source does not match iframe contentWindow",
1440
+ {
1441
+ type: data.type,
1442
+ eventOrigin: event.origin,
1443
+ hasIframe: Boolean(this.iframe),
1444
+ hasIframeContentWindow: Boolean(this.iframe?.contentWindow)
1445
+ }
1446
+ );
1447
+ }
1222
1448
  if (isIframeEvent && (data.type === "LOGIN_READY" || data.type === "LOGIN_RESIZE") || data.type === "LOGIN_SUCCESS" || data.type === "LOGIN_ERROR") {
1223
1449
  this.markIframeReady();
1224
1450
  }
@@ -1353,6 +1579,406 @@ function createLoginUI() {
1353
1579
  handleLoginCallbackIfPresent({ autoClose: false });
1354
1580
 
1355
1581
  // src/client.ts
1582
+ var DEFAULT_PAYMENT_IFRAME_LOAD_TIMEOUT_MS = 8e3;
1583
+ var PAYMENT_UI_LOG_PREFIX = "[Youidian PaymentUI]";
1584
+ function hexToRgb(value) {
1585
+ const normalized = value.trim().replace(/^#/, "");
1586
+ if (!/^[0-9a-f]{3}([0-9a-f]{3})?$/i.test(normalized)) return null;
1587
+ const hex = normalized.length === 3 ? normalized.split("").map((char) => `${char}${char}`).join("") : normalized;
1588
+ const number = Number.parseInt(hex, 16);
1589
+ return {
1590
+ b: number & 255,
1591
+ g: number >> 8 & 255,
1592
+ r: number >> 16 & 255
1593
+ };
1594
+ }
1595
+ function alphaColor(value, alpha) {
1596
+ const rgb = hexToRgb(value);
1597
+ if (!rgb)
1598
+ return `color-mix(in srgb, ${value} ${Math.round(alpha * 100)}%, transparent)`;
1599
+ return `rgba(${rgb.r},${rgb.g},${rgb.b},${alpha})`;
1600
+ }
1601
+ function resolvePaymentTheme(options) {
1602
+ const primary = options?.theme?.primary || "#16a34a";
1603
+ const secondary = options?.theme?.secondary || "color-mix(in srgb, #16a34a 78%, white)";
1604
+ const foreground = options?.theme?.foreground || "#0f172a";
1605
+ const muted = options?.theme?.muted || "#64748b";
1606
+ const surface = options?.theme?.surface || "#ffffff";
1607
+ return {
1608
+ foreground,
1609
+ muted,
1610
+ primary,
1611
+ primary10: alphaColor(primary, 0.1),
1612
+ primary14: alphaColor(primary, 0.14),
1613
+ primary20: alphaColor(primary, 0.2),
1614
+ primary28: alphaColor(primary, 0.28),
1615
+ primary36: alphaColor(primary, 0.36),
1616
+ primary64: alphaColor(primary, 0.64),
1617
+ secondary,
1618
+ surface
1619
+ };
1620
+ }
1621
+ function logPaymentInfo(message, details) {
1622
+ if (typeof console === "undefined") return;
1623
+ console.info(PAYMENT_UI_LOG_PREFIX, message, details || {});
1624
+ }
1625
+ function logPaymentWarn(message, details) {
1626
+ if (typeof console === "undefined") return;
1627
+ console.warn(PAYMENT_UI_LOG_PREFIX, message, details || {});
1628
+ }
1629
+ function getPaymentIframeLoadTimeoutMs(options) {
1630
+ const timeout = options?.iframeLoadTimeoutMs;
1631
+ if (typeof timeout !== "number" || !Number.isFinite(timeout)) {
1632
+ return DEFAULT_PAYMENT_IFRAME_LOAD_TIMEOUT_MS;
1633
+ }
1634
+ return Math.max(0, timeout);
1635
+ }
1636
+ function isChinesePaymentLocale(locale) {
1637
+ return locale?.toLowerCase().startsWith("zh") || false;
1638
+ }
1639
+ function getPaymentFallbackText(options) {
1640
+ const isChinese = isChinesePaymentLocale(options?.locale);
1641
+ return {
1642
+ buttonText: options?.fallbackButtonText || (isChinese ? "\u5728\u65B0\u9875\u9762\u4E2D\u6253\u5F00" : "Open in new page"),
1643
+ 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.",
1644
+ retryText: options?.fallbackRetryText || (isChinese ? "\u91CD\u65B0\u5C1D\u8BD5" : "Try again"),
1645
+ title: isChinese ? "\u652F\u4ED8\u9875\u9762\u6253\u5F00\u8F83\u6162" : "Payment page is taking longer"
1646
+ };
1647
+ }
1648
+ function getPaymentBrandText(options) {
1649
+ const isChinese = isChinesePaymentLocale(options?.locale);
1650
+ return {
1651
+ language: isChinese ? "zh" : "en",
1652
+ name: isChinese ? "\u4F18\u6613\u70B9" : "Youidian",
1653
+ subtitle: isChinese ? "\u5F00\u53D1\u8005\u670D\u52A1\u5E73\u53F0" : "Developer Service Platform"
1654
+ };
1655
+ }
1656
+ function getPaymentBrandLoadingText(options) {
1657
+ const isChinese = isChinesePaymentLocale(options?.locale);
1658
+ return {
1659
+ language: isChinese ? "zh" : "en",
1660
+ name: isChinese ? "\u4F18\u6613\u70B9" : "Youidian",
1661
+ subtitle: isChinese ? "\u5F00\u53D1\u8005\u670D\u52A1\u4E2D\u53F0" : "Developer Service"
1662
+ };
1663
+ }
1664
+ function getPaymentLoadingText(options) {
1665
+ const isChinese = isChinesePaymentLocale(options?.locale);
1666
+ return options?.loadingTitle || options?.loadingDescription || (isChinese ? "\u52A0\u8F7D\u4E2D..." : "Loading...");
1667
+ }
1668
+ function applyPaymentPanelStyle(panel, theme) {
1669
+ Object.assign(panel.style, {
1670
+ position: "absolute",
1671
+ inset: "0",
1672
+ display: "flex",
1673
+ alignItems: "center",
1674
+ justifyContent: "center",
1675
+ borderRadius: "28px",
1676
+ background: `linear-gradient(180deg, ${theme.surface} 0%, rgba(248,250,252,0.98) 100%)`,
1677
+ color: theme.foreground,
1678
+ padding: "28px 24px",
1679
+ textAlign: "center",
1680
+ zIndex: "1",
1681
+ boxShadow: "none",
1682
+ backdropFilter: "blur(16px)"
1683
+ });
1684
+ }
1685
+ function createPaymentIcon(type) {
1686
+ const svg = document.createElementNS("http://www.w3.org/2000/svg", "svg");
1687
+ svg.setAttribute("viewBox", "0 0 24 24");
1688
+ svg.setAttribute("fill", "none");
1689
+ svg.setAttribute("aria-hidden", "true");
1690
+ Object.assign(svg.style, {
1691
+ display: "block",
1692
+ flex: "0 0 auto"
1693
+ });
1694
+ const paths = type === "external" ? [
1695
+ "M7 17L17 7",
1696
+ "M10 7H17V14",
1697
+ "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"
1698
+ ] : type === "refresh" ? [
1699
+ "M20 6V11H15",
1700
+ "M4 18V13H9",
1701
+ "M18.2 9A7 7 0 0 0 6.2 6.6L4 9",
1702
+ "M5.8 15A7 7 0 0 0 17.8 17.4L20 15"
1703
+ ] : ["M12 7V12L15 14", "M21 12A9 9 0 1 1 3 12A9 9 0 0 1 21 12"];
1704
+ for (const value of paths) {
1705
+ const path = document.createElementNS("http://www.w3.org/2000/svg", "path");
1706
+ path.setAttribute("d", value);
1707
+ path.setAttribute("stroke", "currentColor");
1708
+ path.setAttribute("stroke-width", "2");
1709
+ path.setAttribute("stroke-linecap", "round");
1710
+ path.setAttribute("stroke-linejoin", "round");
1711
+ svg.appendChild(path);
1712
+ }
1713
+ return svg;
1714
+ }
1715
+ function createPaymentFallbackArtwork(theme) {
1716
+ const artwork = document.createElement("div");
1717
+ Object.assign(artwork.style, {
1718
+ height: "164px",
1719
+ margin: "0 auto 34px",
1720
+ position: "relative",
1721
+ width: "184px"
1722
+ });
1723
+ const wash = document.createElement("div");
1724
+ Object.assign(wash.style, {
1725
+ position: "absolute",
1726
+ left: "16px",
1727
+ top: "20px",
1728
+ width: "152px",
1729
+ height: "126px",
1730
+ borderRadius: "48% 52% 43% 57% / 54% 42% 58% 46%",
1731
+ background: `radial-gradient(circle at 48% 42%, ${theme.primary20}, ${theme.primary10} 52%, transparent 76%)`,
1732
+ filter: "blur(1px)",
1733
+ transform: "rotate(-10deg)"
1734
+ });
1735
+ const blob = document.createElement("div");
1736
+ Object.assign(blob.style, {
1737
+ position: "absolute",
1738
+ left: "26px",
1739
+ top: "28px",
1740
+ width: "132px",
1741
+ height: "116px",
1742
+ borderRadius: "42% 58% 53% 47% / 45% 39% 61% 55%",
1743
+ background: `linear-gradient(150deg, ${theme.primary14}, ${theme.primary10})`,
1744
+ boxShadow: `0 30px 64px ${theme.primary14}`,
1745
+ transform: "rotate(8deg)"
1746
+ });
1747
+ const tile = document.createElement("div");
1748
+ Object.assign(tile.style, {
1749
+ position: "absolute",
1750
+ left: "50%",
1751
+ top: "50%",
1752
+ transform: "translate(-50%, -50%) rotate(-4deg)",
1753
+ width: "112px",
1754
+ height: "106px",
1755
+ borderRadius: "38% 62% 50% 50% / 44% 43% 57% 56%",
1756
+ background: `linear-gradient(145deg, ${theme.primary}, ${theme.secondary})`,
1757
+ border: `1px solid ${theme.primary36}`,
1758
+ color: "#ffffff",
1759
+ display: "inline-flex",
1760
+ alignItems: "center",
1761
+ justifyContent: "center",
1762
+ boxShadow: `0 26px 54px ${theme.primary28}`
1763
+ });
1764
+ const icon = createPaymentIcon("slow");
1765
+ Object.assign(icon.style, {
1766
+ width: "52px",
1767
+ height: "52px",
1768
+ transform: "rotate(4deg)"
1769
+ });
1770
+ tile.appendChild(icon);
1771
+ artwork.appendChild(wash);
1772
+ artwork.appendChild(blob);
1773
+ artwork.appendChild(tile);
1774
+ return artwork;
1775
+ }
1776
+ function createPaymentBrandLoading(options, theme) {
1777
+ const brand = getPaymentBrandLoadingText(options);
1778
+ const wrapper = document.createElement("div");
1779
+ Object.assign(wrapper.style, {
1780
+ display: "flex",
1781
+ flexDirection: "column",
1782
+ alignItems: "center",
1783
+ justifyContent: "center",
1784
+ margin: "0 auto 16px"
1785
+ });
1786
+ wrapper.appendChild(
1787
+ createSdkBrandLogo({
1788
+ animated: true,
1789
+ brand,
1790
+ brandWidth: 292,
1791
+ copyWidth: 176,
1792
+ gap: 6,
1793
+ markMarginRight: -10,
1794
+ markSize: 112,
1795
+ nameSize: 39,
1796
+ subtitleMarginTop: 8,
1797
+ subtitleSize: 14,
1798
+ subtitleWeight: "700",
1799
+ theme,
1800
+ variant: "brand"
1801
+ })
1802
+ );
1803
+ return wrapper;
1804
+ }
1805
+ function createPaymentPlatformLogo(options, theme) {
1806
+ const brand = getPaymentBrandText(options);
1807
+ const wrapper = document.createElement("div");
1808
+ Object.assign(wrapper.style, {
1809
+ display: "flex",
1810
+ alignItems: "center",
1811
+ justifyContent: "center",
1812
+ margin: "0 auto 10px"
1813
+ });
1814
+ wrapper.appendChild(
1815
+ createSdkBrandLogo({
1816
+ brand,
1817
+ markSize: 48,
1818
+ nameSize: brand.language === "zh" ? 22 : 22,
1819
+ subtitleSize: brand.language === "zh" ? 9 : 9,
1820
+ theme
1821
+ })
1822
+ );
1823
+ return wrapper;
1824
+ }
1825
+ function createPaymentPanelContent() {
1826
+ const content = document.createElement("div");
1827
+ Object.assign(content.style, {
1828
+ display: "flex",
1829
+ flexDirection: "column",
1830
+ justifyContent: "center",
1831
+ maxWidth: "364px",
1832
+ minHeight: "100%",
1833
+ width: "100%"
1834
+ });
1835
+ return content;
1836
+ }
1837
+ function createPaymentPanelTitle(value, theme) {
1838
+ const title = document.createElement("div");
1839
+ title.textContent = value;
1840
+ Object.assign(title.style, {
1841
+ color: theme?.foreground || "#0f172a",
1842
+ fontSize: "24px",
1843
+ fontWeight: "900",
1844
+ lineHeight: "1.22",
1845
+ marginBottom: "16px"
1846
+ });
1847
+ return title;
1848
+ }
1849
+ function createPaymentPanelDescription(value, theme) {
1850
+ const description = document.createElement("div");
1851
+ description.textContent = value;
1852
+ Object.assign(description.style, {
1853
+ color: theme?.muted || "#64748b",
1854
+ fontSize: "14px",
1855
+ fontWeight: "600",
1856
+ lineHeight: "1.62",
1857
+ margin: "0 auto",
1858
+ maxWidth: "320px"
1859
+ });
1860
+ return description;
1861
+ }
1862
+ function createPaymentLoadingDescription(value, theme) {
1863
+ const description = document.createElement("div");
1864
+ description.textContent = value;
1865
+ Object.assign(description.style, {
1866
+ color: theme?.muted || "#64748b",
1867
+ fontSize: "14px",
1868
+ fontWeight: "500",
1869
+ lineHeight: "1.25",
1870
+ margin: "12px auto 0",
1871
+ maxWidth: "320px"
1872
+ });
1873
+ return description;
1874
+ }
1875
+ function createPaymentFallbackMessage(options) {
1876
+ const message = document.createElement("div");
1877
+ Object.assign(message.style, {
1878
+ margin: "0 auto",
1879
+ textAlign: "center"
1880
+ });
1881
+ message.appendChild(createPaymentFallbackArtwork(options.theme));
1882
+ message.appendChild(createPaymentPanelTitle(options.title, options.theme));
1883
+ message.appendChild(
1884
+ createPaymentPanelDescription(options.description, options.theme)
1885
+ );
1886
+ return message;
1887
+ }
1888
+ function createPaymentLoadingPanel(options) {
1889
+ const panel = document.createElement("div");
1890
+ const theme = resolvePaymentTheme(options);
1891
+ applyPaymentPanelStyle(panel, theme);
1892
+ const content = createPaymentPanelContent();
1893
+ content.appendChild(createPaymentBrandLoading(options, theme));
1894
+ content.appendChild(
1895
+ createPaymentLoadingDescription(getPaymentLoadingText(options), theme)
1896
+ );
1897
+ panel.appendChild(content);
1898
+ return panel;
1899
+ }
1900
+ function createPaymentButton(value, options) {
1901
+ const primary = options?.primary || false;
1902
+ const theme = options?.theme || resolvePaymentTheme();
1903
+ const button = document.createElement("button");
1904
+ button.type = "button";
1905
+ Object.assign(button.style, {
1906
+ width: "100%",
1907
+ height: "50px",
1908
+ borderRadius: "14px",
1909
+ border: primary ? `1px solid ${theme.primary}` : `1px solid ${theme.primary64}`,
1910
+ background: primary ? `linear-gradient(135deg, ${theme.primary}, ${theme.secondary})` : "#ffffff",
1911
+ color: primary ? "#ffffff" : theme.primary,
1912
+ cursor: "pointer",
1913
+ display: "inline-flex",
1914
+ alignItems: "center",
1915
+ justifyContent: "center",
1916
+ gap: "10px",
1917
+ fontSize: "15px",
1918
+ fontWeight: "800",
1919
+ marginTop: primary ? "12px" : "0",
1920
+ boxShadow: primary ? `0 14px 28px ${theme.primary20}` : "none"
1921
+ });
1922
+ if (options?.icon) {
1923
+ const icon = createPaymentIcon(options.icon);
1924
+ Object.assign(icon.style, {
1925
+ width: "17px",
1926
+ height: "17px"
1927
+ });
1928
+ button.appendChild(icon);
1929
+ }
1930
+ const label = document.createElement("span");
1931
+ label.textContent = value;
1932
+ button.appendChild(label);
1933
+ return button;
1934
+ }
1935
+ function createPaymentFallbackPanel(options) {
1936
+ const panel = document.createElement("div");
1937
+ const theme = resolvePaymentTheme(options.paymentOptions);
1938
+ applyPaymentPanelStyle(panel, theme);
1939
+ const content = createPaymentPanelContent();
1940
+ Object.assign(content.style, {
1941
+ justifyContent: "space-between"
1942
+ });
1943
+ const main = document.createElement("div");
1944
+ Object.assign(main.style, {
1945
+ display: "flex",
1946
+ flexDirection: "column",
1947
+ alignItems: "center",
1948
+ marginTop: "38px",
1949
+ width: "100%"
1950
+ });
1951
+ main.appendChild(
1952
+ createPaymentFallbackMessage({
1953
+ description: options.description,
1954
+ theme,
1955
+ title: options.title
1956
+ })
1957
+ );
1958
+ const actions = document.createElement("div");
1959
+ Object.assign(actions.style, {
1960
+ marginTop: "46px",
1961
+ width: "100%"
1962
+ });
1963
+ const retryButton = createPaymentButton(options.retryText, {
1964
+ icon: "refresh",
1965
+ theme
1966
+ });
1967
+ retryButton.onclick = options.onRetry;
1968
+ actions.appendChild(retryButton);
1969
+ const openButton = createPaymentButton(options.buttonText, {
1970
+ icon: "external",
1971
+ primary: true,
1972
+ theme
1973
+ });
1974
+ openButton.onclick = options.onOpen;
1975
+ actions.appendChild(openButton);
1976
+ main.appendChild(actions);
1977
+ content.appendChild(main);
1978
+ content.appendChild(createPaymentPlatformLogo(options.paymentOptions, theme));
1979
+ panel.appendChild(content);
1980
+ return panel;
1981
+ }
1356
1982
  var PaymentUI = class extends HostedFrameModal {
1357
1983
  constructor() {
1358
1984
  super(...arguments);
@@ -1361,6 +1987,127 @@ var PaymentUI = class extends HostedFrameModal {
1361
1987
  __publicField(this, "activeOrderId", null);
1362
1988
  __publicField(this, "paymentCompleted", false);
1363
1989
  __publicField(this, "cancelRequestedOrderId", null);
1990
+ __publicField(this, "iframeFallbackPanel", null);
1991
+ __publicField(this, "iframeLoadingPanel", null);
1992
+ __publicField(this, "iframeLoadTimer", null);
1993
+ __publicField(this, "iframeReady", false);
1994
+ }
1995
+ cleanupPaymentFrameState() {
1996
+ if (typeof window !== "undefined" && this.iframeLoadTimer) {
1997
+ window.clearTimeout(this.iframeLoadTimer);
1998
+ }
1999
+ if (this.iframeFallbackPanel?.parentNode) {
2000
+ this.iframeFallbackPanel.parentNode.removeChild(this.iframeFallbackPanel);
2001
+ }
2002
+ if (this.iframeLoadingPanel?.parentNode) {
2003
+ this.iframeLoadingPanel.parentNode.removeChild(this.iframeLoadingPanel);
2004
+ }
2005
+ this.iframeFallbackPanel = null;
2006
+ this.iframeLoadingPanel = null;
2007
+ this.iframeLoadTimer = null;
2008
+ this.iframeReady = false;
2009
+ }
2010
+ markPaymentIframeReady() {
2011
+ if (this.iframeReady) return;
2012
+ this.iframeReady = true;
2013
+ if (typeof window !== "undefined" && this.iframeLoadTimer) {
2014
+ window.clearTimeout(this.iframeLoadTimer);
2015
+ this.iframeLoadTimer = null;
2016
+ }
2017
+ if (this.iframeFallbackPanel?.parentNode) {
2018
+ this.iframeFallbackPanel.parentNode.removeChild(this.iframeFallbackPanel);
2019
+ }
2020
+ if (this.iframeLoadingPanel?.parentNode) {
2021
+ this.iframeLoadingPanel.parentNode.removeChild(this.iframeLoadingPanel);
2022
+ }
2023
+ this.iframeFallbackPanel = null;
2024
+ this.iframeLoadingPanel = null;
2025
+ }
2026
+ showPaymentIframeLoading(container, options) {
2027
+ if (this.iframeLoadingPanel || this.iframeReady) return;
2028
+ const panel = createPaymentLoadingPanel(options);
2029
+ this.iframeLoadingPanel = panel;
2030
+ container.appendChild(panel);
2031
+ }
2032
+ openFallbackPaymentPage(finalUrl, options) {
2033
+ if (typeof window === "undefined") return;
2034
+ const popup = window.open(
2035
+ finalUrl,
2036
+ "youidian-payment-checkout",
2037
+ "popup=yes,width=480,height=760,resizable=yes,scrollbars=yes"
2038
+ );
2039
+ if (popup) {
2040
+ logPaymentInfo("Opened hosted checkout fallback page", {
2041
+ checkoutUrl: finalUrl
2042
+ });
2043
+ options?.onFallbackOpen?.(finalUrl);
2044
+ try {
2045
+ popup.focus();
2046
+ } catch {
2047
+ }
2048
+ return;
2049
+ }
2050
+ logPaymentWarn("Hosted checkout fallback popup was blocked", {
2051
+ checkoutUrl: finalUrl
2052
+ });
2053
+ options?.onFallbackBlocked?.(finalUrl);
2054
+ window.location.assign(finalUrl);
2055
+ }
2056
+ showPaymentIframeFallback(options) {
2057
+ if (this.iframeReady || this.iframeFallbackPanel) return;
2058
+ const { container, finalUrl, paymentOptions } = options;
2059
+ logPaymentWarn("Hosted checkout iframe did not report ready in time", {
2060
+ checkoutUrl: finalUrl
2061
+ });
2062
+ if (this.iframeLoadingPanel?.parentNode) {
2063
+ this.iframeLoadingPanel.parentNode.removeChild(this.iframeLoadingPanel);
2064
+ }
2065
+ this.iframeLoadingPanel = null;
2066
+ const fallbackText = getPaymentFallbackText(paymentOptions);
2067
+ const panel = createPaymentFallbackPanel({
2068
+ buttonText: fallbackText.buttonText,
2069
+ description: fallbackText.description,
2070
+ onOpen: () => {
2071
+ this.openFallbackPaymentPage(finalUrl, paymentOptions);
2072
+ },
2073
+ onRetry: () => {
2074
+ this.iframeFallbackPanel = null;
2075
+ if (panel.parentNode) {
2076
+ panel.parentNode.removeChild(panel);
2077
+ }
2078
+ this.iframeReady = false;
2079
+ this.showPaymentIframeLoading(container, paymentOptions);
2080
+ if (this.iframe) {
2081
+ this.iframe.src = finalUrl;
2082
+ }
2083
+ this.startPaymentIframeWatchdog({
2084
+ container,
2085
+ finalUrl,
2086
+ paymentOptions
2087
+ });
2088
+ },
2089
+ paymentOptions,
2090
+ retryText: fallbackText.retryText,
2091
+ title: fallbackText.title
2092
+ });
2093
+ this.iframeFallbackPanel = panel;
2094
+ container.appendChild(panel);
2095
+ paymentOptions?.onFallbackVisible?.();
2096
+ }
2097
+ startPaymentIframeWatchdog(options) {
2098
+ if (typeof window === "undefined") return;
2099
+ if (this.iframeLoadTimer) {
2100
+ window.clearTimeout(this.iframeLoadTimer);
2101
+ }
2102
+ const timeoutMs = getPaymentIframeLoadTimeoutMs(options.paymentOptions);
2103
+ if (timeoutMs === 0) {
2104
+ this.showPaymentIframeFallback(options);
2105
+ return;
2106
+ }
2107
+ this.iframeLoadTimer = window.setTimeout(() => {
2108
+ this.iframeLoadTimer = null;
2109
+ this.showPaymentIframeFallback(options);
2110
+ }, timeoutMs);
1364
2111
  }
1365
2112
  cancelHostedOrder(reason = "user_cancel", orderId) {
1366
2113
  const targetOrderId = orderId || this.activeOrderId;
@@ -1403,6 +2150,7 @@ var PaymentUI = class extends HostedFrameModal {
1403
2150
  this.activeOrderId = null;
1404
2151
  this.paymentCompleted = false;
1405
2152
  this.cancelRequestedOrderId = null;
2153
+ this.cleanupPaymentFrameState();
1406
2154
  if (typeof urlOrParams === "string") {
1407
2155
  checkoutUrl = urlOrParams;
1408
2156
  try {
@@ -1471,14 +2219,21 @@ var PaymentUI = class extends HostedFrameModal {
1471
2219
  checkoutUrl,
1472
2220
  getAutoResolvedLocale(options?.locale)
1473
2221
  );
1474
- this.openHostedFrame(finalUrl, {
2222
+ const hostedFrame = this.openHostedFrame(finalUrl, {
1475
2223
  allowedOrigin: options?.allowedOrigin,
2224
+ height: "min(760px, calc(100vh - 32px))",
1476
2225
  onCloseButton: () => {
1477
2226
  this.cancelHostedOrder("modal_close");
1478
2227
  options?.onCancel?.(this.activeOrderId || void 0);
1479
2228
  },
1480
- onMessage: (data, container) => {
2229
+ onMessage: (data, container, event) => {
2230
+ const isIframeMessage = event.source === this.iframe?.contentWindow;
2231
+ if (isIframeMessage && (data.type === "PAYMENT_READY" || data.type === "PAYMENT_RESIZE" || data.type === "PAYMENT_STARTED")) {
2232
+ this.markPaymentIframeReady();
2233
+ }
1481
2234
  switch (data.type) {
2235
+ case "PAYMENT_READY":
2236
+ break;
1482
2237
  case "PAYMENT_STARTED":
1483
2238
  if (data.orderId) {
1484
2239
  this.activeOrderId = data.orderId;
@@ -1493,11 +2248,6 @@ var PaymentUI = class extends HostedFrameModal {
1493
2248
  options?.onCancel?.(data.orderId);
1494
2249
  break;
1495
2250
  case "PAYMENT_RESIZE":
1496
- if (data.height) {
1497
- const maxHeight = window.innerHeight * 0.9;
1498
- const newHeight = Math.min(data.height, maxHeight);
1499
- container.style.height = `${newHeight}px`;
1500
- }
1501
2251
  break;
1502
2252
  case "PAYMENT_CLOSE":
1503
2253
  this.cancelHostedOrder("payment_close", data.orderId);
@@ -1505,8 +2255,21 @@ var PaymentUI = class extends HostedFrameModal {
1505
2255
  options?.onClose?.();
1506
2256
  break;
1507
2257
  }
1508
- }
2258
+ },
2259
+ width: "min(450px, 100%)"
1509
2260
  });
2261
+ if (hostedFrame) {
2262
+ this.showPaymentIframeLoading(hostedFrame.container, options);
2263
+ this.startPaymentIframeWatchdog({
2264
+ container: hostedFrame.container,
2265
+ finalUrl,
2266
+ paymentOptions: options
2267
+ });
2268
+ }
2269
+ }
2270
+ close() {
2271
+ super.close();
2272
+ this.cleanupPaymentFrameState();
1510
2273
  }
1511
2274
  /**
1512
2275
  * Poll order status from integrator's API endpoint
@@ -1552,13 +2315,38 @@ var PaymentUI = class extends HostedFrameModal {
1552
2315
  });
1553
2316
  }
1554
2317
  };
2318
+ var MessageUI = class {
2319
+ openWechatBinding(options) {
2320
+ const bindingUrl = options.bindingUrl?.trim();
2321
+ if (!bindingUrl) {
2322
+ throw new Error("bindingUrl is required");
2323
+ }
2324
+ if (options.displayMode === "popup") {
2325
+ const popup = window.open(
2326
+ bindingUrl,
2327
+ options.popupName || "youidian-wechat-message-binding",
2328
+ "width=480,height=720,noopener,noreferrer"
2329
+ );
2330
+ if (!popup) {
2331
+ window.location.href = bindingUrl;
2332
+ }
2333
+ return;
2334
+ }
2335
+ window.location.href = bindingUrl;
2336
+ }
2337
+ };
2338
+ function createMessageUI() {
2339
+ return new MessageUI();
2340
+ }
1555
2341
  function createPaymentUI() {
1556
2342
  return new PaymentUI();
1557
2343
  }
1558
2344
  export {
1559
2345
  LoginUI,
2346
+ MessageUI,
1560
2347
  PaymentUI,
1561
2348
  createLoginUI,
2349
+ createMessageUI,
1562
2350
  createPaymentUI,
1563
2351
  detectLoginEnvironment,
1564
2352
  handleLoginCallbackIfPresent