@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 +801 -11
- package/dist/client.cjs.map +1 -1
- package/dist/client.d.cts +44 -2
- package/dist/client.d.ts +44 -2
- package/dist/client.js +799 -11
- package/dist/client.js.map +1 -1
- package/dist/index.cjs +872 -20
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +870 -20
- package/dist/index.js.map +1 -1
- package/dist/login.cjs +14 -3
- package/dist/login.cjs.map +1 -1
- package/dist/login.js +14 -3
- package/dist/login.js.map +1 -1
- package/dist/server.cjs +71 -9
- package/dist/server.cjs.map +1 -1
- package/dist/server.d.cts +106 -1
- package/dist/server.d.ts +106 -1
- package/dist/server.js +71 -9
- package/dist/server.js.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -33,10 +33,12 @@ var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "sy
|
|
|
33
33
|
var src_exports = {};
|
|
34
34
|
__export(src_exports, {
|
|
35
35
|
LoginUI: () => LoginUI,
|
|
36
|
+
MessageUI: () => MessageUI,
|
|
36
37
|
PaymentClient: () => PaymentClient,
|
|
37
38
|
PaymentUI: () => PaymentUI,
|
|
38
39
|
createFeedbackWidget: () => createFeedbackWidget,
|
|
39
40
|
createLoginUI: () => createLoginUI,
|
|
41
|
+
createMessageUI: () => createMessageUI,
|
|
40
42
|
createPaymentUI: () => createPaymentUI,
|
|
41
43
|
detectLoginEnvironment: () => detectLoginEnvironment,
|
|
42
44
|
getCustomAmountRechargeRule: () => getCustomAmountRechargeRule,
|
|
@@ -46,6 +48,221 @@ __export(src_exports, {
|
|
|
46
48
|
});
|
|
47
49
|
module.exports = __toCommonJS(src_exports);
|
|
48
50
|
|
|
51
|
+
// src/brand-logo.ts
|
|
52
|
+
function ensureSdkBrandLogoStyles() {
|
|
53
|
+
if (document.getElementById?.("youidian-sdk-brand-logo-style")) {
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
const style = document.createElement("style");
|
|
57
|
+
style.id = "youidian-sdk-brand-logo-style";
|
|
58
|
+
style.textContent = `
|
|
59
|
+
.youidian-sdk-brand-logo__ring,
|
|
60
|
+
.youidian-sdk-brand-logo__dot,
|
|
61
|
+
.youidian-sdk-brand-logo__copy,
|
|
62
|
+
.youidian-sdk-brand-logo__tagline {
|
|
63
|
+
transform-box: fill-box;
|
|
64
|
+
transform-origin: center;
|
|
65
|
+
will-change: opacity, transform;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
.youidian-sdk-brand-logo[data-animated="true"] .youidian-sdk-brand-logo__ring {
|
|
69
|
+
animation: youidian-sdk-brand-logo-ring 1400ms cubic-bezier(0.4, 0, 0.2, 1) infinite;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
.youidian-sdk-brand-logo[data-animated="true"] .youidian-sdk-brand-logo__dot {
|
|
73
|
+
animation: youidian-sdk-brand-logo-dot 1400ms cubic-bezier(0.4, 0, 0.2, 1) infinite;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
.youidian-sdk-brand-logo[data-animated="true"] .youidian-sdk-brand-logo__copy {
|
|
77
|
+
animation: youidian-sdk-brand-logo-copy 1600ms cubic-bezier(0.4, 0, 0.2, 1) infinite;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
.youidian-sdk-brand-logo[data-animated="true"] .youidian-sdk-brand-logo__tagline {
|
|
81
|
+
animation: youidian-sdk-brand-logo-tagline 1600ms cubic-bezier(0.4, 0, 0.2, 1) infinite;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
@keyframes youidian-sdk-brand-logo-ring {
|
|
85
|
+
0%,
|
|
86
|
+
100% {
|
|
87
|
+
opacity: 0.72;
|
|
88
|
+
transform: rotate(0deg) scale(0.98);
|
|
89
|
+
}
|
|
90
|
+
50% {
|
|
91
|
+
opacity: 1;
|
|
92
|
+
transform: rotate(8deg) scale(1.02);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
@keyframes youidian-sdk-brand-logo-dot {
|
|
97
|
+
0%,
|
|
98
|
+
100% {
|
|
99
|
+
opacity: 0.58;
|
|
100
|
+
transform: scale(0.9);
|
|
101
|
+
}
|
|
102
|
+
50% {
|
|
103
|
+
opacity: 1;
|
|
104
|
+
transform: scale(1.08);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
@keyframes youidian-sdk-brand-logo-copy {
|
|
109
|
+
0%,
|
|
110
|
+
100% {
|
|
111
|
+
opacity: 0.86;
|
|
112
|
+
transform: translateY(0);
|
|
113
|
+
}
|
|
114
|
+
50% {
|
|
115
|
+
opacity: 1;
|
|
116
|
+
transform: translateY(-1px);
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
@keyframes youidian-sdk-brand-logo-tagline {
|
|
121
|
+
0%,
|
|
122
|
+
100% {
|
|
123
|
+
opacity: 0.64;
|
|
124
|
+
}
|
|
125
|
+
50% {
|
|
126
|
+
opacity: 0.92;
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
@media (prefers-reduced-motion: reduce) {
|
|
131
|
+
.youidian-sdk-brand-logo__ring,
|
|
132
|
+
.youidian-sdk-brand-logo__dot,
|
|
133
|
+
.youidian-sdk-brand-logo__copy,
|
|
134
|
+
.youidian-sdk-brand-logo__tagline {
|
|
135
|
+
animation: none;
|
|
136
|
+
opacity: 1;
|
|
137
|
+
transform: none;
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
`;
|
|
141
|
+
document.head?.appendChild(style);
|
|
142
|
+
}
|
|
143
|
+
function createSdkBrandLogoMark({
|
|
144
|
+
animated,
|
|
145
|
+
inverted,
|
|
146
|
+
size,
|
|
147
|
+
theme,
|
|
148
|
+
variant = "brand"
|
|
149
|
+
}) {
|
|
150
|
+
ensureSdkBrandLogoStyles();
|
|
151
|
+
const mark = document.createElementNS("http://www.w3.org/2000/svg", "svg");
|
|
152
|
+
mark.setAttribute("viewBox", "0 0 512 512");
|
|
153
|
+
mark.setAttribute("fill", "none");
|
|
154
|
+
mark.setAttribute("aria-hidden", "true");
|
|
155
|
+
Object.assign(mark.style, {
|
|
156
|
+
width: `${size}px`,
|
|
157
|
+
height: `${size}px`,
|
|
158
|
+
color: inverted ? "#ffffff" : theme.primary,
|
|
159
|
+
display: "block",
|
|
160
|
+
flex: "0 0 auto",
|
|
161
|
+
overflow: "visible"
|
|
162
|
+
});
|
|
163
|
+
const logoGroup = document.createElementNS("http://www.w3.org/2000/svg", "g");
|
|
164
|
+
logoGroup.setAttribute(
|
|
165
|
+
"transform",
|
|
166
|
+
variant === "mark" ? "matrix(0.74 0 0 0.74 -124 -114)" : "matrix(0.58 0 0 0.58 -40 -31)"
|
|
167
|
+
);
|
|
168
|
+
const ring = document.createElementNS("http://www.w3.org/2000/svg", "path");
|
|
169
|
+
ring.setAttribute("class", animated ? "youidian-sdk-brand-logo__ring" : "");
|
|
170
|
+
ring.setAttribute("fill", inverted ? "currentColor" : theme.primary);
|
|
171
|
+
ring.setAttribute(
|
|
172
|
+
"d",
|
|
173
|
+
"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"
|
|
174
|
+
);
|
|
175
|
+
const dot = document.createElementNS("http://www.w3.org/2000/svg", "circle");
|
|
176
|
+
dot.setAttribute("class", animated ? "youidian-sdk-brand-logo__dot" : "");
|
|
177
|
+
dot.setAttribute("fill", inverted ? "currentColor" : theme.secondary);
|
|
178
|
+
dot.setAttribute("cx", "714.5");
|
|
179
|
+
dot.setAttribute("cy", "490.5");
|
|
180
|
+
dot.setAttribute("r", "68");
|
|
181
|
+
dot.setAttribute("opacity", inverted ? "0.78" : "1");
|
|
182
|
+
logoGroup.appendChild(ring);
|
|
183
|
+
logoGroup.appendChild(dot);
|
|
184
|
+
mark.appendChild(logoGroup);
|
|
185
|
+
return mark;
|
|
186
|
+
}
|
|
187
|
+
function createSdkBrandLogo({
|
|
188
|
+
animated,
|
|
189
|
+
brand,
|
|
190
|
+
brandWidth,
|
|
191
|
+
copyWidth,
|
|
192
|
+
gap = 10,
|
|
193
|
+
markMarginRight,
|
|
194
|
+
markSize,
|
|
195
|
+
nameSize,
|
|
196
|
+
subtitleMarginTop = 6,
|
|
197
|
+
subtitleSize,
|
|
198
|
+
subtitleWeight = "800",
|
|
199
|
+
theme,
|
|
200
|
+
variant = "brand"
|
|
201
|
+
}) {
|
|
202
|
+
ensureSdkBrandLogoStyles();
|
|
203
|
+
const wrapper = document.createElement("div");
|
|
204
|
+
wrapper.className = "youidian-sdk-brand-logo";
|
|
205
|
+
wrapper.setAttribute("data-animated", animated ? "true" : "false");
|
|
206
|
+
Object.assign(wrapper.style, {
|
|
207
|
+
display: "inline-flex",
|
|
208
|
+
alignItems: "center",
|
|
209
|
+
justifyContent: "center",
|
|
210
|
+
gap: `${gap}px`,
|
|
211
|
+
width: brandWidth ? `${brandWidth}px` : void 0
|
|
212
|
+
});
|
|
213
|
+
const copy = document.createElement("div");
|
|
214
|
+
copy.className = "youidian-sdk-brand-logo__copy";
|
|
215
|
+
copy.setAttribute("data-brand-locale", brand.language);
|
|
216
|
+
Object.assign(copy.style, {
|
|
217
|
+
display: "flex",
|
|
218
|
+
flexDirection: "column",
|
|
219
|
+
alignItems: "flex-start",
|
|
220
|
+
justifyContent: "center",
|
|
221
|
+
lineHeight: "1",
|
|
222
|
+
width: copyWidth ? `${copyWidth}px` : void 0
|
|
223
|
+
});
|
|
224
|
+
const name = document.createElement("div");
|
|
225
|
+
name.className = "youidian-sdk-brand-logo__name";
|
|
226
|
+
name.textContent = brand.name;
|
|
227
|
+
Object.assign(name.style, {
|
|
228
|
+
color: theme.foreground,
|
|
229
|
+
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',
|
|
230
|
+
fontSize: `${nameSize}px`,
|
|
231
|
+
fontWeight: brand.language === "zh" ? "900" : "800",
|
|
232
|
+
letterSpacing: brand.language === "zh" ? "0.08em" : "0.01em",
|
|
233
|
+
lineHeight: "1",
|
|
234
|
+
whiteSpace: "nowrap"
|
|
235
|
+
});
|
|
236
|
+
const subtitle = document.createElement("div");
|
|
237
|
+
subtitle.className = "youidian-sdk-brand-logo__tagline";
|
|
238
|
+
subtitle.textContent = brand.subtitle;
|
|
239
|
+
Object.assign(subtitle.style, {
|
|
240
|
+
color: theme.muted,
|
|
241
|
+
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',
|
|
242
|
+
fontSize: `${subtitleSize}px`,
|
|
243
|
+
fontWeight: subtitleWeight,
|
|
244
|
+
letterSpacing: brand.language === "zh" ? "0.24em" : "0.07em",
|
|
245
|
+
lineHeight: "1",
|
|
246
|
+
marginTop: `${subtitleMarginTop}px`,
|
|
247
|
+
textTransform: brand.language === "zh" ? "none" : "uppercase",
|
|
248
|
+
whiteSpace: "nowrap"
|
|
249
|
+
});
|
|
250
|
+
copy.appendChild(name);
|
|
251
|
+
copy.appendChild(subtitle);
|
|
252
|
+
const mark = createSdkBrandLogoMark({
|
|
253
|
+
animated,
|
|
254
|
+
size: markSize,
|
|
255
|
+
theme,
|
|
256
|
+
variant
|
|
257
|
+
});
|
|
258
|
+
if (typeof markMarginRight === "number") {
|
|
259
|
+
mark.style.marginRight = `${markMarginRight}px`;
|
|
260
|
+
}
|
|
261
|
+
wrapper.appendChild(mark);
|
|
262
|
+
wrapper.appendChild(copy);
|
|
263
|
+
return wrapper;
|
|
264
|
+
}
|
|
265
|
+
|
|
49
266
|
// src/hosted-modal.ts
|
|
50
267
|
var SDK_SUPPORTED_LOCALES = [
|
|
51
268
|
"en",
|
|
@@ -169,7 +386,7 @@ var HostedFrameModal = class {
|
|
|
169
386
|
const container = document.createElement("div");
|
|
170
387
|
Object.assign(container.style, {
|
|
171
388
|
width: options.width || "450px",
|
|
172
|
-
height: options.height || "min(
|
|
389
|
+
height: options.height || "min(760px, calc(100vh - 32px))",
|
|
173
390
|
backgroundColor: "transparent",
|
|
174
391
|
borderRadius: "28px",
|
|
175
392
|
overflow: "visible",
|
|
@@ -177,8 +394,7 @@ var HostedFrameModal = class {
|
|
|
177
394
|
boxShadow: "none",
|
|
178
395
|
maxWidth: "calc(100vw - 32px)",
|
|
179
396
|
maxHeight: "calc(100vh - 32px)",
|
|
180
|
-
transition: "
|
|
181
|
-
willChange: "height"
|
|
397
|
+
transition: "none"
|
|
182
398
|
});
|
|
183
399
|
const closeBtn = document.createElement("button");
|
|
184
400
|
closeBtn.innerHTML = "\xD7";
|
|
@@ -1019,6 +1235,7 @@ var LoginUI = class extends HostedFrameModal {
|
|
|
1019
1235
|
this.completed = false;
|
|
1020
1236
|
}
|
|
1021
1237
|
markIframeReady() {
|
|
1238
|
+
if (this.iframeReady) return;
|
|
1022
1239
|
this.iframeReady = true;
|
|
1023
1240
|
if (typeof window !== "undefined" && this.iframeLoadTimer) {
|
|
1024
1241
|
window.clearTimeout(this.iframeLoadTimer);
|
|
@@ -1263,6 +1480,17 @@ var LoginUI = class extends HostedFrameModal {
|
|
|
1263
1480
|
},
|
|
1264
1481
|
onMessage: (data, _container, event) => {
|
|
1265
1482
|
const isIframeEvent = event.source === this.iframe?.contentWindow;
|
|
1483
|
+
if (!isIframeEvent && (data.type === "LOGIN_READY" || data.type === "LOGIN_RESIZE")) {
|
|
1484
|
+
logLoginWarn(
|
|
1485
|
+
"Login lifecycle event ignored: event.source does not match iframe contentWindow",
|
|
1486
|
+
{
|
|
1487
|
+
type: data.type,
|
|
1488
|
+
eventOrigin: event.origin,
|
|
1489
|
+
hasIframe: Boolean(this.iframe),
|
|
1490
|
+
hasIframeContentWindow: Boolean(this.iframe?.contentWindow)
|
|
1491
|
+
}
|
|
1492
|
+
);
|
|
1493
|
+
}
|
|
1266
1494
|
if (isIframeEvent && (data.type === "LOGIN_READY" || data.type === "LOGIN_RESIZE") || data.type === "LOGIN_SUCCESS" || data.type === "LOGIN_ERROR") {
|
|
1267
1495
|
this.markIframeReady();
|
|
1268
1496
|
}
|
|
@@ -1397,6 +1625,406 @@ function createLoginUI() {
|
|
|
1397
1625
|
handleLoginCallbackIfPresent({ autoClose: false });
|
|
1398
1626
|
|
|
1399
1627
|
// src/client.ts
|
|
1628
|
+
var DEFAULT_PAYMENT_IFRAME_LOAD_TIMEOUT_MS = 8e3;
|
|
1629
|
+
var PAYMENT_UI_LOG_PREFIX = "[Youidian PaymentUI]";
|
|
1630
|
+
function hexToRgb(value) {
|
|
1631
|
+
const normalized = value.trim().replace(/^#/, "");
|
|
1632
|
+
if (!/^[0-9a-f]{3}([0-9a-f]{3})?$/i.test(normalized)) return null;
|
|
1633
|
+
const hex = normalized.length === 3 ? normalized.split("").map((char) => `${char}${char}`).join("") : normalized;
|
|
1634
|
+
const number = Number.parseInt(hex, 16);
|
|
1635
|
+
return {
|
|
1636
|
+
b: number & 255,
|
|
1637
|
+
g: number >> 8 & 255,
|
|
1638
|
+
r: number >> 16 & 255
|
|
1639
|
+
};
|
|
1640
|
+
}
|
|
1641
|
+
function alphaColor(value, alpha) {
|
|
1642
|
+
const rgb = hexToRgb(value);
|
|
1643
|
+
if (!rgb)
|
|
1644
|
+
return `color-mix(in srgb, ${value} ${Math.round(alpha * 100)}%, transparent)`;
|
|
1645
|
+
return `rgba(${rgb.r},${rgb.g},${rgb.b},${alpha})`;
|
|
1646
|
+
}
|
|
1647
|
+
function resolvePaymentTheme(options) {
|
|
1648
|
+
const primary = options?.theme?.primary || "#16a34a";
|
|
1649
|
+
const secondary = options?.theme?.secondary || "color-mix(in srgb, #16a34a 78%, white)";
|
|
1650
|
+
const foreground = options?.theme?.foreground || "#0f172a";
|
|
1651
|
+
const muted = options?.theme?.muted || "#64748b";
|
|
1652
|
+
const surface = options?.theme?.surface || "#ffffff";
|
|
1653
|
+
return {
|
|
1654
|
+
foreground,
|
|
1655
|
+
muted,
|
|
1656
|
+
primary,
|
|
1657
|
+
primary10: alphaColor(primary, 0.1),
|
|
1658
|
+
primary14: alphaColor(primary, 0.14),
|
|
1659
|
+
primary20: alphaColor(primary, 0.2),
|
|
1660
|
+
primary28: alphaColor(primary, 0.28),
|
|
1661
|
+
primary36: alphaColor(primary, 0.36),
|
|
1662
|
+
primary64: alphaColor(primary, 0.64),
|
|
1663
|
+
secondary,
|
|
1664
|
+
surface
|
|
1665
|
+
};
|
|
1666
|
+
}
|
|
1667
|
+
function logPaymentInfo(message, details) {
|
|
1668
|
+
if (typeof console === "undefined") return;
|
|
1669
|
+
console.info(PAYMENT_UI_LOG_PREFIX, message, details || {});
|
|
1670
|
+
}
|
|
1671
|
+
function logPaymentWarn(message, details) {
|
|
1672
|
+
if (typeof console === "undefined") return;
|
|
1673
|
+
console.warn(PAYMENT_UI_LOG_PREFIX, message, details || {});
|
|
1674
|
+
}
|
|
1675
|
+
function getPaymentIframeLoadTimeoutMs(options) {
|
|
1676
|
+
const timeout = options?.iframeLoadTimeoutMs;
|
|
1677
|
+
if (typeof timeout !== "number" || !Number.isFinite(timeout)) {
|
|
1678
|
+
return DEFAULT_PAYMENT_IFRAME_LOAD_TIMEOUT_MS;
|
|
1679
|
+
}
|
|
1680
|
+
return Math.max(0, timeout);
|
|
1681
|
+
}
|
|
1682
|
+
function isChinesePaymentLocale(locale) {
|
|
1683
|
+
return locale?.toLowerCase().startsWith("zh") || false;
|
|
1684
|
+
}
|
|
1685
|
+
function getPaymentFallbackText(options) {
|
|
1686
|
+
const isChinese = isChinesePaymentLocale(options?.locale);
|
|
1687
|
+
return {
|
|
1688
|
+
buttonText: options?.fallbackButtonText || (isChinese ? "\u5728\u65B0\u9875\u9762\u4E2D\u6253\u5F00" : "Open in new page"),
|
|
1689
|
+
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.",
|
|
1690
|
+
retryText: options?.fallbackRetryText || (isChinese ? "\u91CD\u65B0\u5C1D\u8BD5" : "Try again"),
|
|
1691
|
+
title: isChinese ? "\u652F\u4ED8\u9875\u9762\u6253\u5F00\u8F83\u6162" : "Payment page is taking longer"
|
|
1692
|
+
};
|
|
1693
|
+
}
|
|
1694
|
+
function getPaymentBrandText(options) {
|
|
1695
|
+
const isChinese = isChinesePaymentLocale(options?.locale);
|
|
1696
|
+
return {
|
|
1697
|
+
language: isChinese ? "zh" : "en",
|
|
1698
|
+
name: isChinese ? "\u4F18\u6613\u70B9" : "Youidian",
|
|
1699
|
+
subtitle: isChinese ? "\u5F00\u53D1\u8005\u670D\u52A1\u5E73\u53F0" : "Developer Service Platform"
|
|
1700
|
+
};
|
|
1701
|
+
}
|
|
1702
|
+
function getPaymentBrandLoadingText(options) {
|
|
1703
|
+
const isChinese = isChinesePaymentLocale(options?.locale);
|
|
1704
|
+
return {
|
|
1705
|
+
language: isChinese ? "zh" : "en",
|
|
1706
|
+
name: isChinese ? "\u4F18\u6613\u70B9" : "Youidian",
|
|
1707
|
+
subtitle: isChinese ? "\u5F00\u53D1\u8005\u670D\u52A1\u4E2D\u53F0" : "Developer Service"
|
|
1708
|
+
};
|
|
1709
|
+
}
|
|
1710
|
+
function getPaymentLoadingText(options) {
|
|
1711
|
+
const isChinese = isChinesePaymentLocale(options?.locale);
|
|
1712
|
+
return options?.loadingTitle || options?.loadingDescription || (isChinese ? "\u52A0\u8F7D\u4E2D..." : "Loading...");
|
|
1713
|
+
}
|
|
1714
|
+
function applyPaymentPanelStyle(panel, theme) {
|
|
1715
|
+
Object.assign(panel.style, {
|
|
1716
|
+
position: "absolute",
|
|
1717
|
+
inset: "0",
|
|
1718
|
+
display: "flex",
|
|
1719
|
+
alignItems: "center",
|
|
1720
|
+
justifyContent: "center",
|
|
1721
|
+
borderRadius: "28px",
|
|
1722
|
+
background: `linear-gradient(180deg, ${theme.surface} 0%, rgba(248,250,252,0.98) 100%)`,
|
|
1723
|
+
color: theme.foreground,
|
|
1724
|
+
padding: "28px 24px",
|
|
1725
|
+
textAlign: "center",
|
|
1726
|
+
zIndex: "1",
|
|
1727
|
+
boxShadow: "none",
|
|
1728
|
+
backdropFilter: "blur(16px)"
|
|
1729
|
+
});
|
|
1730
|
+
}
|
|
1731
|
+
function createPaymentIcon(type) {
|
|
1732
|
+
const svg = document.createElementNS("http://www.w3.org/2000/svg", "svg");
|
|
1733
|
+
svg.setAttribute("viewBox", "0 0 24 24");
|
|
1734
|
+
svg.setAttribute("fill", "none");
|
|
1735
|
+
svg.setAttribute("aria-hidden", "true");
|
|
1736
|
+
Object.assign(svg.style, {
|
|
1737
|
+
display: "block",
|
|
1738
|
+
flex: "0 0 auto"
|
|
1739
|
+
});
|
|
1740
|
+
const paths = type === "external" ? [
|
|
1741
|
+
"M7 17L17 7",
|
|
1742
|
+
"M10 7H17V14",
|
|
1743
|
+
"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"
|
|
1744
|
+
] : type === "refresh" ? [
|
|
1745
|
+
"M20 6V11H15",
|
|
1746
|
+
"M4 18V13H9",
|
|
1747
|
+
"M18.2 9A7 7 0 0 0 6.2 6.6L4 9",
|
|
1748
|
+
"M5.8 15A7 7 0 0 0 17.8 17.4L20 15"
|
|
1749
|
+
] : ["M12 7V12L15 14", "M21 12A9 9 0 1 1 3 12A9 9 0 0 1 21 12"];
|
|
1750
|
+
for (const value of paths) {
|
|
1751
|
+
const path = document.createElementNS("http://www.w3.org/2000/svg", "path");
|
|
1752
|
+
path.setAttribute("d", value);
|
|
1753
|
+
path.setAttribute("stroke", "currentColor");
|
|
1754
|
+
path.setAttribute("stroke-width", "2");
|
|
1755
|
+
path.setAttribute("stroke-linecap", "round");
|
|
1756
|
+
path.setAttribute("stroke-linejoin", "round");
|
|
1757
|
+
svg.appendChild(path);
|
|
1758
|
+
}
|
|
1759
|
+
return svg;
|
|
1760
|
+
}
|
|
1761
|
+
function createPaymentFallbackArtwork(theme) {
|
|
1762
|
+
const artwork = document.createElement("div");
|
|
1763
|
+
Object.assign(artwork.style, {
|
|
1764
|
+
height: "164px",
|
|
1765
|
+
margin: "0 auto 34px",
|
|
1766
|
+
position: "relative",
|
|
1767
|
+
width: "184px"
|
|
1768
|
+
});
|
|
1769
|
+
const wash = document.createElement("div");
|
|
1770
|
+
Object.assign(wash.style, {
|
|
1771
|
+
position: "absolute",
|
|
1772
|
+
left: "16px",
|
|
1773
|
+
top: "20px",
|
|
1774
|
+
width: "152px",
|
|
1775
|
+
height: "126px",
|
|
1776
|
+
borderRadius: "48% 52% 43% 57% / 54% 42% 58% 46%",
|
|
1777
|
+
background: `radial-gradient(circle at 48% 42%, ${theme.primary20}, ${theme.primary10} 52%, transparent 76%)`,
|
|
1778
|
+
filter: "blur(1px)",
|
|
1779
|
+
transform: "rotate(-10deg)"
|
|
1780
|
+
});
|
|
1781
|
+
const blob = document.createElement("div");
|
|
1782
|
+
Object.assign(blob.style, {
|
|
1783
|
+
position: "absolute",
|
|
1784
|
+
left: "26px",
|
|
1785
|
+
top: "28px",
|
|
1786
|
+
width: "132px",
|
|
1787
|
+
height: "116px",
|
|
1788
|
+
borderRadius: "42% 58% 53% 47% / 45% 39% 61% 55%",
|
|
1789
|
+
background: `linear-gradient(150deg, ${theme.primary14}, ${theme.primary10})`,
|
|
1790
|
+
boxShadow: `0 30px 64px ${theme.primary14}`,
|
|
1791
|
+
transform: "rotate(8deg)"
|
|
1792
|
+
});
|
|
1793
|
+
const tile = document.createElement("div");
|
|
1794
|
+
Object.assign(tile.style, {
|
|
1795
|
+
position: "absolute",
|
|
1796
|
+
left: "50%",
|
|
1797
|
+
top: "50%",
|
|
1798
|
+
transform: "translate(-50%, -50%) rotate(-4deg)",
|
|
1799
|
+
width: "112px",
|
|
1800
|
+
height: "106px",
|
|
1801
|
+
borderRadius: "38% 62% 50% 50% / 44% 43% 57% 56%",
|
|
1802
|
+
background: `linear-gradient(145deg, ${theme.primary}, ${theme.secondary})`,
|
|
1803
|
+
border: `1px solid ${theme.primary36}`,
|
|
1804
|
+
color: "#ffffff",
|
|
1805
|
+
display: "inline-flex",
|
|
1806
|
+
alignItems: "center",
|
|
1807
|
+
justifyContent: "center",
|
|
1808
|
+
boxShadow: `0 26px 54px ${theme.primary28}`
|
|
1809
|
+
});
|
|
1810
|
+
const icon = createPaymentIcon("slow");
|
|
1811
|
+
Object.assign(icon.style, {
|
|
1812
|
+
width: "52px",
|
|
1813
|
+
height: "52px",
|
|
1814
|
+
transform: "rotate(4deg)"
|
|
1815
|
+
});
|
|
1816
|
+
tile.appendChild(icon);
|
|
1817
|
+
artwork.appendChild(wash);
|
|
1818
|
+
artwork.appendChild(blob);
|
|
1819
|
+
artwork.appendChild(tile);
|
|
1820
|
+
return artwork;
|
|
1821
|
+
}
|
|
1822
|
+
function createPaymentBrandLoading(options, theme) {
|
|
1823
|
+
const brand = getPaymentBrandLoadingText(options);
|
|
1824
|
+
const wrapper = document.createElement("div");
|
|
1825
|
+
Object.assign(wrapper.style, {
|
|
1826
|
+
display: "flex",
|
|
1827
|
+
flexDirection: "column",
|
|
1828
|
+
alignItems: "center",
|
|
1829
|
+
justifyContent: "center",
|
|
1830
|
+
margin: "0 auto 16px"
|
|
1831
|
+
});
|
|
1832
|
+
wrapper.appendChild(
|
|
1833
|
+
createSdkBrandLogo({
|
|
1834
|
+
animated: true,
|
|
1835
|
+
brand,
|
|
1836
|
+
brandWidth: 292,
|
|
1837
|
+
copyWidth: 176,
|
|
1838
|
+
gap: 6,
|
|
1839
|
+
markMarginRight: -10,
|
|
1840
|
+
markSize: 112,
|
|
1841
|
+
nameSize: 39,
|
|
1842
|
+
subtitleMarginTop: 8,
|
|
1843
|
+
subtitleSize: 14,
|
|
1844
|
+
subtitleWeight: "700",
|
|
1845
|
+
theme,
|
|
1846
|
+
variant: "brand"
|
|
1847
|
+
})
|
|
1848
|
+
);
|
|
1849
|
+
return wrapper;
|
|
1850
|
+
}
|
|
1851
|
+
function createPaymentPlatformLogo(options, theme) {
|
|
1852
|
+
const brand = getPaymentBrandText(options);
|
|
1853
|
+
const wrapper = document.createElement("div");
|
|
1854
|
+
Object.assign(wrapper.style, {
|
|
1855
|
+
display: "flex",
|
|
1856
|
+
alignItems: "center",
|
|
1857
|
+
justifyContent: "center",
|
|
1858
|
+
margin: "0 auto 10px"
|
|
1859
|
+
});
|
|
1860
|
+
wrapper.appendChild(
|
|
1861
|
+
createSdkBrandLogo({
|
|
1862
|
+
brand,
|
|
1863
|
+
markSize: 48,
|
|
1864
|
+
nameSize: brand.language === "zh" ? 22 : 22,
|
|
1865
|
+
subtitleSize: brand.language === "zh" ? 9 : 9,
|
|
1866
|
+
theme
|
|
1867
|
+
})
|
|
1868
|
+
);
|
|
1869
|
+
return wrapper;
|
|
1870
|
+
}
|
|
1871
|
+
function createPaymentPanelContent() {
|
|
1872
|
+
const content = document.createElement("div");
|
|
1873
|
+
Object.assign(content.style, {
|
|
1874
|
+
display: "flex",
|
|
1875
|
+
flexDirection: "column",
|
|
1876
|
+
justifyContent: "center",
|
|
1877
|
+
maxWidth: "364px",
|
|
1878
|
+
minHeight: "100%",
|
|
1879
|
+
width: "100%"
|
|
1880
|
+
});
|
|
1881
|
+
return content;
|
|
1882
|
+
}
|
|
1883
|
+
function createPaymentPanelTitle(value, theme) {
|
|
1884
|
+
const title = document.createElement("div");
|
|
1885
|
+
title.textContent = value;
|
|
1886
|
+
Object.assign(title.style, {
|
|
1887
|
+
color: theme?.foreground || "#0f172a",
|
|
1888
|
+
fontSize: "24px",
|
|
1889
|
+
fontWeight: "900",
|
|
1890
|
+
lineHeight: "1.22",
|
|
1891
|
+
marginBottom: "16px"
|
|
1892
|
+
});
|
|
1893
|
+
return title;
|
|
1894
|
+
}
|
|
1895
|
+
function createPaymentPanelDescription(value, theme) {
|
|
1896
|
+
const description = document.createElement("div");
|
|
1897
|
+
description.textContent = value;
|
|
1898
|
+
Object.assign(description.style, {
|
|
1899
|
+
color: theme?.muted || "#64748b",
|
|
1900
|
+
fontSize: "14px",
|
|
1901
|
+
fontWeight: "600",
|
|
1902
|
+
lineHeight: "1.62",
|
|
1903
|
+
margin: "0 auto",
|
|
1904
|
+
maxWidth: "320px"
|
|
1905
|
+
});
|
|
1906
|
+
return description;
|
|
1907
|
+
}
|
|
1908
|
+
function createPaymentLoadingDescription(value, theme) {
|
|
1909
|
+
const description = document.createElement("div");
|
|
1910
|
+
description.textContent = value;
|
|
1911
|
+
Object.assign(description.style, {
|
|
1912
|
+
color: theme?.muted || "#64748b",
|
|
1913
|
+
fontSize: "14px",
|
|
1914
|
+
fontWeight: "500",
|
|
1915
|
+
lineHeight: "1.25",
|
|
1916
|
+
margin: "12px auto 0",
|
|
1917
|
+
maxWidth: "320px"
|
|
1918
|
+
});
|
|
1919
|
+
return description;
|
|
1920
|
+
}
|
|
1921
|
+
function createPaymentFallbackMessage(options) {
|
|
1922
|
+
const message = document.createElement("div");
|
|
1923
|
+
Object.assign(message.style, {
|
|
1924
|
+
margin: "0 auto",
|
|
1925
|
+
textAlign: "center"
|
|
1926
|
+
});
|
|
1927
|
+
message.appendChild(createPaymentFallbackArtwork(options.theme));
|
|
1928
|
+
message.appendChild(createPaymentPanelTitle(options.title, options.theme));
|
|
1929
|
+
message.appendChild(
|
|
1930
|
+
createPaymentPanelDescription(options.description, options.theme)
|
|
1931
|
+
);
|
|
1932
|
+
return message;
|
|
1933
|
+
}
|
|
1934
|
+
function createPaymentLoadingPanel(options) {
|
|
1935
|
+
const panel = document.createElement("div");
|
|
1936
|
+
const theme = resolvePaymentTheme(options);
|
|
1937
|
+
applyPaymentPanelStyle(panel, theme);
|
|
1938
|
+
const content = createPaymentPanelContent();
|
|
1939
|
+
content.appendChild(createPaymentBrandLoading(options, theme));
|
|
1940
|
+
content.appendChild(
|
|
1941
|
+
createPaymentLoadingDescription(getPaymentLoadingText(options), theme)
|
|
1942
|
+
);
|
|
1943
|
+
panel.appendChild(content);
|
|
1944
|
+
return panel;
|
|
1945
|
+
}
|
|
1946
|
+
function createPaymentButton(value, options) {
|
|
1947
|
+
const primary = options?.primary || false;
|
|
1948
|
+
const theme = options?.theme || resolvePaymentTheme();
|
|
1949
|
+
const button = document.createElement("button");
|
|
1950
|
+
button.type = "button";
|
|
1951
|
+
Object.assign(button.style, {
|
|
1952
|
+
width: "100%",
|
|
1953
|
+
height: "50px",
|
|
1954
|
+
borderRadius: "14px",
|
|
1955
|
+
border: primary ? `1px solid ${theme.primary}` : `1px solid ${theme.primary64}`,
|
|
1956
|
+
background: primary ? `linear-gradient(135deg, ${theme.primary}, ${theme.secondary})` : "#ffffff",
|
|
1957
|
+
color: primary ? "#ffffff" : theme.primary,
|
|
1958
|
+
cursor: "pointer",
|
|
1959
|
+
display: "inline-flex",
|
|
1960
|
+
alignItems: "center",
|
|
1961
|
+
justifyContent: "center",
|
|
1962
|
+
gap: "10px",
|
|
1963
|
+
fontSize: "15px",
|
|
1964
|
+
fontWeight: "800",
|
|
1965
|
+
marginTop: primary ? "12px" : "0",
|
|
1966
|
+
boxShadow: primary ? `0 14px 28px ${theme.primary20}` : "none"
|
|
1967
|
+
});
|
|
1968
|
+
if (options?.icon) {
|
|
1969
|
+
const icon = createPaymentIcon(options.icon);
|
|
1970
|
+
Object.assign(icon.style, {
|
|
1971
|
+
width: "17px",
|
|
1972
|
+
height: "17px"
|
|
1973
|
+
});
|
|
1974
|
+
button.appendChild(icon);
|
|
1975
|
+
}
|
|
1976
|
+
const label = document.createElement("span");
|
|
1977
|
+
label.textContent = value;
|
|
1978
|
+
button.appendChild(label);
|
|
1979
|
+
return button;
|
|
1980
|
+
}
|
|
1981
|
+
function createPaymentFallbackPanel(options) {
|
|
1982
|
+
const panel = document.createElement("div");
|
|
1983
|
+
const theme = resolvePaymentTheme(options.paymentOptions);
|
|
1984
|
+
applyPaymentPanelStyle(panel, theme);
|
|
1985
|
+
const content = createPaymentPanelContent();
|
|
1986
|
+
Object.assign(content.style, {
|
|
1987
|
+
justifyContent: "space-between"
|
|
1988
|
+
});
|
|
1989
|
+
const main = document.createElement("div");
|
|
1990
|
+
Object.assign(main.style, {
|
|
1991
|
+
display: "flex",
|
|
1992
|
+
flexDirection: "column",
|
|
1993
|
+
alignItems: "center",
|
|
1994
|
+
marginTop: "38px",
|
|
1995
|
+
width: "100%"
|
|
1996
|
+
});
|
|
1997
|
+
main.appendChild(
|
|
1998
|
+
createPaymentFallbackMessage({
|
|
1999
|
+
description: options.description,
|
|
2000
|
+
theme,
|
|
2001
|
+
title: options.title
|
|
2002
|
+
})
|
|
2003
|
+
);
|
|
2004
|
+
const actions = document.createElement("div");
|
|
2005
|
+
Object.assign(actions.style, {
|
|
2006
|
+
marginTop: "46px",
|
|
2007
|
+
width: "100%"
|
|
2008
|
+
});
|
|
2009
|
+
const retryButton = createPaymentButton(options.retryText, {
|
|
2010
|
+
icon: "refresh",
|
|
2011
|
+
theme
|
|
2012
|
+
});
|
|
2013
|
+
retryButton.onclick = options.onRetry;
|
|
2014
|
+
actions.appendChild(retryButton);
|
|
2015
|
+
const openButton = createPaymentButton(options.buttonText, {
|
|
2016
|
+
icon: "external",
|
|
2017
|
+
primary: true,
|
|
2018
|
+
theme
|
|
2019
|
+
});
|
|
2020
|
+
openButton.onclick = options.onOpen;
|
|
2021
|
+
actions.appendChild(openButton);
|
|
2022
|
+
main.appendChild(actions);
|
|
2023
|
+
content.appendChild(main);
|
|
2024
|
+
content.appendChild(createPaymentPlatformLogo(options.paymentOptions, theme));
|
|
2025
|
+
panel.appendChild(content);
|
|
2026
|
+
return panel;
|
|
2027
|
+
}
|
|
1400
2028
|
var PaymentUI = class extends HostedFrameModal {
|
|
1401
2029
|
constructor() {
|
|
1402
2030
|
super(...arguments);
|
|
@@ -1405,6 +2033,127 @@ var PaymentUI = class extends HostedFrameModal {
|
|
|
1405
2033
|
__publicField(this, "activeOrderId", null);
|
|
1406
2034
|
__publicField(this, "paymentCompleted", false);
|
|
1407
2035
|
__publicField(this, "cancelRequestedOrderId", null);
|
|
2036
|
+
__publicField(this, "iframeFallbackPanel", null);
|
|
2037
|
+
__publicField(this, "iframeLoadingPanel", null);
|
|
2038
|
+
__publicField(this, "iframeLoadTimer", null);
|
|
2039
|
+
__publicField(this, "iframeReady", false);
|
|
2040
|
+
}
|
|
2041
|
+
cleanupPaymentFrameState() {
|
|
2042
|
+
if (typeof window !== "undefined" && this.iframeLoadTimer) {
|
|
2043
|
+
window.clearTimeout(this.iframeLoadTimer);
|
|
2044
|
+
}
|
|
2045
|
+
if (this.iframeFallbackPanel?.parentNode) {
|
|
2046
|
+
this.iframeFallbackPanel.parentNode.removeChild(this.iframeFallbackPanel);
|
|
2047
|
+
}
|
|
2048
|
+
if (this.iframeLoadingPanel?.parentNode) {
|
|
2049
|
+
this.iframeLoadingPanel.parentNode.removeChild(this.iframeLoadingPanel);
|
|
2050
|
+
}
|
|
2051
|
+
this.iframeFallbackPanel = null;
|
|
2052
|
+
this.iframeLoadingPanel = null;
|
|
2053
|
+
this.iframeLoadTimer = null;
|
|
2054
|
+
this.iframeReady = false;
|
|
2055
|
+
}
|
|
2056
|
+
markPaymentIframeReady() {
|
|
2057
|
+
if (this.iframeReady) return;
|
|
2058
|
+
this.iframeReady = true;
|
|
2059
|
+
if (typeof window !== "undefined" && this.iframeLoadTimer) {
|
|
2060
|
+
window.clearTimeout(this.iframeLoadTimer);
|
|
2061
|
+
this.iframeLoadTimer = null;
|
|
2062
|
+
}
|
|
2063
|
+
if (this.iframeFallbackPanel?.parentNode) {
|
|
2064
|
+
this.iframeFallbackPanel.parentNode.removeChild(this.iframeFallbackPanel);
|
|
2065
|
+
}
|
|
2066
|
+
if (this.iframeLoadingPanel?.parentNode) {
|
|
2067
|
+
this.iframeLoadingPanel.parentNode.removeChild(this.iframeLoadingPanel);
|
|
2068
|
+
}
|
|
2069
|
+
this.iframeFallbackPanel = null;
|
|
2070
|
+
this.iframeLoadingPanel = null;
|
|
2071
|
+
}
|
|
2072
|
+
showPaymentIframeLoading(container, options) {
|
|
2073
|
+
if (this.iframeLoadingPanel || this.iframeReady) return;
|
|
2074
|
+
const panel = createPaymentLoadingPanel(options);
|
|
2075
|
+
this.iframeLoadingPanel = panel;
|
|
2076
|
+
container.appendChild(panel);
|
|
2077
|
+
}
|
|
2078
|
+
openFallbackPaymentPage(finalUrl, options) {
|
|
2079
|
+
if (typeof window === "undefined") return;
|
|
2080
|
+
const popup = window.open(
|
|
2081
|
+
finalUrl,
|
|
2082
|
+
"youidian-payment-checkout",
|
|
2083
|
+
"popup=yes,width=480,height=760,resizable=yes,scrollbars=yes"
|
|
2084
|
+
);
|
|
2085
|
+
if (popup) {
|
|
2086
|
+
logPaymentInfo("Opened hosted checkout fallback page", {
|
|
2087
|
+
checkoutUrl: finalUrl
|
|
2088
|
+
});
|
|
2089
|
+
options?.onFallbackOpen?.(finalUrl);
|
|
2090
|
+
try {
|
|
2091
|
+
popup.focus();
|
|
2092
|
+
} catch {
|
|
2093
|
+
}
|
|
2094
|
+
return;
|
|
2095
|
+
}
|
|
2096
|
+
logPaymentWarn("Hosted checkout fallback popup was blocked", {
|
|
2097
|
+
checkoutUrl: finalUrl
|
|
2098
|
+
});
|
|
2099
|
+
options?.onFallbackBlocked?.(finalUrl);
|
|
2100
|
+
window.location.assign(finalUrl);
|
|
2101
|
+
}
|
|
2102
|
+
showPaymentIframeFallback(options) {
|
|
2103
|
+
if (this.iframeReady || this.iframeFallbackPanel) return;
|
|
2104
|
+
const { container, finalUrl, paymentOptions } = options;
|
|
2105
|
+
logPaymentWarn("Hosted checkout iframe did not report ready in time", {
|
|
2106
|
+
checkoutUrl: finalUrl
|
|
2107
|
+
});
|
|
2108
|
+
if (this.iframeLoadingPanel?.parentNode) {
|
|
2109
|
+
this.iframeLoadingPanel.parentNode.removeChild(this.iframeLoadingPanel);
|
|
2110
|
+
}
|
|
2111
|
+
this.iframeLoadingPanel = null;
|
|
2112
|
+
const fallbackText = getPaymentFallbackText(paymentOptions);
|
|
2113
|
+
const panel = createPaymentFallbackPanel({
|
|
2114
|
+
buttonText: fallbackText.buttonText,
|
|
2115
|
+
description: fallbackText.description,
|
|
2116
|
+
onOpen: () => {
|
|
2117
|
+
this.openFallbackPaymentPage(finalUrl, paymentOptions);
|
|
2118
|
+
},
|
|
2119
|
+
onRetry: () => {
|
|
2120
|
+
this.iframeFallbackPanel = null;
|
|
2121
|
+
if (panel.parentNode) {
|
|
2122
|
+
panel.parentNode.removeChild(panel);
|
|
2123
|
+
}
|
|
2124
|
+
this.iframeReady = false;
|
|
2125
|
+
this.showPaymentIframeLoading(container, paymentOptions);
|
|
2126
|
+
if (this.iframe) {
|
|
2127
|
+
this.iframe.src = finalUrl;
|
|
2128
|
+
}
|
|
2129
|
+
this.startPaymentIframeWatchdog({
|
|
2130
|
+
container,
|
|
2131
|
+
finalUrl,
|
|
2132
|
+
paymentOptions
|
|
2133
|
+
});
|
|
2134
|
+
},
|
|
2135
|
+
paymentOptions,
|
|
2136
|
+
retryText: fallbackText.retryText,
|
|
2137
|
+
title: fallbackText.title
|
|
2138
|
+
});
|
|
2139
|
+
this.iframeFallbackPanel = panel;
|
|
2140
|
+
container.appendChild(panel);
|
|
2141
|
+
paymentOptions?.onFallbackVisible?.();
|
|
2142
|
+
}
|
|
2143
|
+
startPaymentIframeWatchdog(options) {
|
|
2144
|
+
if (typeof window === "undefined") return;
|
|
2145
|
+
if (this.iframeLoadTimer) {
|
|
2146
|
+
window.clearTimeout(this.iframeLoadTimer);
|
|
2147
|
+
}
|
|
2148
|
+
const timeoutMs = getPaymentIframeLoadTimeoutMs(options.paymentOptions);
|
|
2149
|
+
if (timeoutMs === 0) {
|
|
2150
|
+
this.showPaymentIframeFallback(options);
|
|
2151
|
+
return;
|
|
2152
|
+
}
|
|
2153
|
+
this.iframeLoadTimer = window.setTimeout(() => {
|
|
2154
|
+
this.iframeLoadTimer = null;
|
|
2155
|
+
this.showPaymentIframeFallback(options);
|
|
2156
|
+
}, timeoutMs);
|
|
1408
2157
|
}
|
|
1409
2158
|
cancelHostedOrder(reason = "user_cancel", orderId) {
|
|
1410
2159
|
const targetOrderId = orderId || this.activeOrderId;
|
|
@@ -1447,6 +2196,7 @@ var PaymentUI = class extends HostedFrameModal {
|
|
|
1447
2196
|
this.activeOrderId = null;
|
|
1448
2197
|
this.paymentCompleted = false;
|
|
1449
2198
|
this.cancelRequestedOrderId = null;
|
|
2199
|
+
this.cleanupPaymentFrameState();
|
|
1450
2200
|
if (typeof urlOrParams === "string") {
|
|
1451
2201
|
checkoutUrl = urlOrParams;
|
|
1452
2202
|
try {
|
|
@@ -1515,14 +2265,21 @@ var PaymentUI = class extends HostedFrameModal {
|
|
|
1515
2265
|
checkoutUrl,
|
|
1516
2266
|
getAutoResolvedLocale(options?.locale)
|
|
1517
2267
|
);
|
|
1518
|
-
this.openHostedFrame(finalUrl, {
|
|
2268
|
+
const hostedFrame = this.openHostedFrame(finalUrl, {
|
|
1519
2269
|
allowedOrigin: options?.allowedOrigin,
|
|
2270
|
+
height: "min(760px, calc(100vh - 32px))",
|
|
1520
2271
|
onCloseButton: () => {
|
|
1521
2272
|
this.cancelHostedOrder("modal_close");
|
|
1522
2273
|
options?.onCancel?.(this.activeOrderId || void 0);
|
|
1523
2274
|
},
|
|
1524
|
-
onMessage: (data, container) => {
|
|
2275
|
+
onMessage: (data, container, event) => {
|
|
2276
|
+
const isIframeMessage = event.source === this.iframe?.contentWindow;
|
|
2277
|
+
if (isIframeMessage && (data.type === "PAYMENT_READY" || data.type === "PAYMENT_RESIZE" || data.type === "PAYMENT_STARTED")) {
|
|
2278
|
+
this.markPaymentIframeReady();
|
|
2279
|
+
}
|
|
1525
2280
|
switch (data.type) {
|
|
2281
|
+
case "PAYMENT_READY":
|
|
2282
|
+
break;
|
|
1526
2283
|
case "PAYMENT_STARTED":
|
|
1527
2284
|
if (data.orderId) {
|
|
1528
2285
|
this.activeOrderId = data.orderId;
|
|
@@ -1537,11 +2294,6 @@ var PaymentUI = class extends HostedFrameModal {
|
|
|
1537
2294
|
options?.onCancel?.(data.orderId);
|
|
1538
2295
|
break;
|
|
1539
2296
|
case "PAYMENT_RESIZE":
|
|
1540
|
-
if (data.height) {
|
|
1541
|
-
const maxHeight = window.innerHeight * 0.9;
|
|
1542
|
-
const newHeight = Math.min(data.height, maxHeight);
|
|
1543
|
-
container.style.height = `${newHeight}px`;
|
|
1544
|
-
}
|
|
1545
2297
|
break;
|
|
1546
2298
|
case "PAYMENT_CLOSE":
|
|
1547
2299
|
this.cancelHostedOrder("payment_close", data.orderId);
|
|
@@ -1549,8 +2301,21 @@ var PaymentUI = class extends HostedFrameModal {
|
|
|
1549
2301
|
options?.onClose?.();
|
|
1550
2302
|
break;
|
|
1551
2303
|
}
|
|
1552
|
-
}
|
|
2304
|
+
},
|
|
2305
|
+
width: "min(450px, 100%)"
|
|
1553
2306
|
});
|
|
2307
|
+
if (hostedFrame) {
|
|
2308
|
+
this.showPaymentIframeLoading(hostedFrame.container, options);
|
|
2309
|
+
this.startPaymentIframeWatchdog({
|
|
2310
|
+
container: hostedFrame.container,
|
|
2311
|
+
finalUrl,
|
|
2312
|
+
paymentOptions: options
|
|
2313
|
+
});
|
|
2314
|
+
}
|
|
2315
|
+
}
|
|
2316
|
+
close() {
|
|
2317
|
+
super.close();
|
|
2318
|
+
this.cleanupPaymentFrameState();
|
|
1554
2319
|
}
|
|
1555
2320
|
/**
|
|
1556
2321
|
* Poll order status from integrator's API endpoint
|
|
@@ -1596,6 +2361,29 @@ var PaymentUI = class extends HostedFrameModal {
|
|
|
1596
2361
|
});
|
|
1597
2362
|
}
|
|
1598
2363
|
};
|
|
2364
|
+
var MessageUI = class {
|
|
2365
|
+
openWechatBinding(options) {
|
|
2366
|
+
const bindingUrl = options.bindingUrl?.trim();
|
|
2367
|
+
if (!bindingUrl) {
|
|
2368
|
+
throw new Error("bindingUrl is required");
|
|
2369
|
+
}
|
|
2370
|
+
if (options.displayMode === "popup") {
|
|
2371
|
+
const popup = window.open(
|
|
2372
|
+
bindingUrl,
|
|
2373
|
+
options.popupName || "youidian-wechat-message-binding",
|
|
2374
|
+
"width=480,height=720,noopener,noreferrer"
|
|
2375
|
+
);
|
|
2376
|
+
if (!popup) {
|
|
2377
|
+
window.location.href = bindingUrl;
|
|
2378
|
+
}
|
|
2379
|
+
return;
|
|
2380
|
+
}
|
|
2381
|
+
window.location.href = bindingUrl;
|
|
2382
|
+
}
|
|
2383
|
+
};
|
|
2384
|
+
function createMessageUI() {
|
|
2385
|
+
return new MessageUI();
|
|
2386
|
+
}
|
|
1599
2387
|
function createPaymentUI() {
|
|
1600
2388
|
return new PaymentUI();
|
|
1601
2389
|
}
|
|
@@ -2381,6 +3169,12 @@ var PaymentClient = class {
|
|
|
2381
3169
|
}
|
|
2382
3170
|
/**
|
|
2383
3171
|
* Decrypts the callback notification payload using AES-256-GCM.
|
|
3172
|
+
*
|
|
3173
|
+
* @deprecated This legacy encrypted-envelope protocol is NOT what the platform
|
|
3174
|
+
* actually delivers. The platform sends an HMAC-signed plaintext JSON webhook
|
|
3175
|
+
* with `X-UniPay-Signature` / `X-UniPay-Timestamp` headers; use
|
|
3176
|
+
* {@link PaymentClient.verifyWebhookSignature} to validate those instead.
|
|
3177
|
+
*
|
|
2384
3178
|
* @param notification - The encrypted notification from payment webhook
|
|
2385
3179
|
* @returns Decrypted payment callback data
|
|
2386
3180
|
*/
|
|
@@ -2403,6 +3197,39 @@ var PaymentClient = class {
|
|
|
2403
3197
|
);
|
|
2404
3198
|
}
|
|
2405
3199
|
}
|
|
3200
|
+
/**
|
|
3201
|
+
* Verify an inbound platform webhook signature.
|
|
3202
|
+
*
|
|
3203
|
+
* The platform delivers webhooks as plaintext JSON with two headers:
|
|
3204
|
+
* `X-UniPay-Signature` (HMAC-SHA256 of `${timestamp}.${rawBodyString}`) and
|
|
3205
|
+
* `X-UniPay-Timestamp`. The body itself is NOT encrypted; verify the signature
|
|
3206
|
+
* then `JSON.parse` the body to obtain a {@link WebhookPayload}.
|
|
3207
|
+
*
|
|
3208
|
+
* @param rawBody - The raw request body string (exactly as received)
|
|
3209
|
+
* @param signature - The `X-UniPay-Signature` header value (hex)
|
|
3210
|
+
* @param timestamp - The `X-UniPay-Timestamp` header value (ms epoch string)
|
|
3211
|
+
* @param options - Optional `toleranceMs` for timestamp freshness (default 5 min)
|
|
3212
|
+
* @returns `true` if the signature is valid and the timestamp is fresh
|
|
3213
|
+
*/
|
|
3214
|
+
verifyWebhookSignature(rawBody, signature, timestamp, options) {
|
|
3215
|
+
try {
|
|
3216
|
+
const toleranceMs = options?.toleranceMs ?? 5 * 60 * 1e3;
|
|
3217
|
+
const timestampMs = Number.parseInt(timestamp, 10);
|
|
3218
|
+
if (Number.isNaN(timestampMs) || Math.abs(Date.now() - timestampMs) > toleranceMs) {
|
|
3219
|
+
return false;
|
|
3220
|
+
}
|
|
3221
|
+
const rawString = `${timestamp}.${rawBody}`;
|
|
3222
|
+
const expected = import_crypto.default.createHmac("sha256", this.appSecret).update(rawString).digest("hex");
|
|
3223
|
+
const signatureBuffer = Buffer.from(signature);
|
|
3224
|
+
const expectedBuffer = Buffer.from(expected);
|
|
3225
|
+
if (signatureBuffer.length !== expectedBuffer.length) {
|
|
3226
|
+
return false;
|
|
3227
|
+
}
|
|
3228
|
+
return import_crypto.default.timingSafeEqual(signatureBuffer, expectedBuffer);
|
|
3229
|
+
} catch {
|
|
3230
|
+
return false;
|
|
3231
|
+
}
|
|
3232
|
+
}
|
|
2406
3233
|
/**
|
|
2407
3234
|
* Fetch products for the configured app.
|
|
2408
3235
|
*/
|
|
@@ -2609,15 +3436,11 @@ var PaymentClient = class {
|
|
|
2609
3436
|
* Useful when subscription credits and perpetual credits use separate keys.
|
|
2610
3437
|
*/
|
|
2611
3438
|
async consumeEntitlementPool(userId, keys, amount, options) {
|
|
2612
|
-
return this.request(
|
|
2613
|
-
|
|
2614
|
-
|
|
2615
|
-
|
|
2616
|
-
|
|
2617
|
-
amount,
|
|
2618
|
-
...options
|
|
2619
|
-
}
|
|
2620
|
-
);
|
|
3439
|
+
return this.request("POST", `/users/${userId}/entitlements/consume-pool`, {
|
|
3440
|
+
keys,
|
|
3441
|
+
amount,
|
|
3442
|
+
...options
|
|
3443
|
+
});
|
|
2621
3444
|
}
|
|
2622
3445
|
/**
|
|
2623
3446
|
* Get active numeric entitlement buckets ordered by expiration.
|
|
@@ -2709,14 +3532,43 @@ var PaymentClient = class {
|
|
|
2709
3532
|
}
|
|
2710
3533
|
);
|
|
2711
3534
|
}
|
|
3535
|
+
/**
|
|
3536
|
+
* Create a hosted WeChat official account binding URL for a user.
|
|
3537
|
+
*/
|
|
3538
|
+
async createWechatMessageBinding(params) {
|
|
3539
|
+
const loginExternalUserId = params.loginExternalUserId?.trim() || params.userId?.trim();
|
|
3540
|
+
const merchantUserId = params.merchantUserId?.trim();
|
|
3541
|
+
if (!loginExternalUserId && !merchantUserId) {
|
|
3542
|
+
throw new Error("userId or merchantUserId is required");
|
|
3543
|
+
}
|
|
3544
|
+
return this.request("POST", "/messages/wechat/bindings", {
|
|
3545
|
+
...params,
|
|
3546
|
+
loginExternalUserId,
|
|
3547
|
+
merchantUserId
|
|
3548
|
+
});
|
|
3549
|
+
}
|
|
3550
|
+
/**
|
|
3551
|
+
* Send an app-linked message template to a bound recipient.
|
|
3552
|
+
*/
|
|
3553
|
+
async sendMessage(params) {
|
|
3554
|
+
if (!params.templateId?.trim() && !params.templateCode?.trim()) {
|
|
3555
|
+
throw new Error("templateId or templateCode is required");
|
|
3556
|
+
}
|
|
3557
|
+
if (!params.data || typeof params.data !== "object") {
|
|
3558
|
+
throw new Error("data is required");
|
|
3559
|
+
}
|
|
3560
|
+
return this.request("POST", "/messages/send", params);
|
|
3561
|
+
}
|
|
2712
3562
|
};
|
|
2713
3563
|
// Annotate the CommonJS export names for ESM import in node:
|
|
2714
3564
|
0 && (module.exports = {
|
|
2715
3565
|
LoginUI,
|
|
3566
|
+
MessageUI,
|
|
2716
3567
|
PaymentClient,
|
|
2717
3568
|
PaymentUI,
|
|
2718
3569
|
createFeedbackWidget,
|
|
2719
3570
|
createLoginUI,
|
|
3571
|
+
createMessageUI,
|
|
2720
3572
|
createPaymentUI,
|
|
2721
3573
|
detectLoginEnvironment,
|
|
2722
3574
|
getCustomAmountRechargeRule,
|