@youidian/pay-sdk 1.1.1 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/client.cjs +118 -89
- package/dist/client.cjs.map +1 -1
- package/dist/client.d.cts +4 -8
- package/dist/client.d.ts +4 -8
- package/dist/client.js +117 -90
- package/dist/client.js.map +1 -1
- package/dist/hosted-modal-BZmYmXTU.d.cts +20 -0
- package/dist/hosted-modal-BZmYmXTU.d.ts +20 -0
- package/dist/index.cjs +129 -92
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +2 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.js +129 -92
- package/dist/index.js.map +1 -1
- package/dist/login.cjs +227 -0
- package/dist/login.cjs.map +1 -0
- package/dist/login.d.cts +57 -0
- package/dist/login.d.ts +57 -0
- package/dist/login.js +201 -0
- package/dist/login.js.map +1 -0
- package/dist/server.cjs +12 -2
- package/dist/server.cjs.map +1 -1
- package/dist/server.d.cts +22 -2
- package/dist/server.d.ts +22 -2
- package/dist/server.js +12 -2
- package/dist/server.js.map +1 -1
- package/package.json +6 -1
package/dist/client.js
CHANGED
|
@@ -2,61 +2,33 @@ 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/
|
|
6
|
-
|
|
5
|
+
// src/hosted-modal.ts
|
|
6
|
+
function applyLocaleToUrl(urlValue, locale) {
|
|
7
|
+
if (!locale) return urlValue;
|
|
8
|
+
try {
|
|
9
|
+
const url = new URL(urlValue);
|
|
10
|
+
const localePrefix = `/${locale}`;
|
|
11
|
+
if (!url.pathname.startsWith(`${localePrefix}/`) && url.pathname !== localePrefix) {
|
|
12
|
+
url.pathname = `${localePrefix}${url.pathname}`;
|
|
13
|
+
}
|
|
14
|
+
return url.toString();
|
|
15
|
+
} catch (_error) {
|
|
16
|
+
const localePrefix = `/${locale}`;
|
|
17
|
+
if (!urlValue.startsWith(`${localePrefix}/`) && urlValue !== localePrefix) {
|
|
18
|
+
return `${localePrefix}${urlValue.startsWith("/") ? "" : "/"}${urlValue}`;
|
|
19
|
+
}
|
|
20
|
+
return urlValue;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
var HostedFrameModal = class {
|
|
7
24
|
constructor() {
|
|
8
25
|
__publicField(this, "iframe", null);
|
|
9
26
|
__publicField(this, "modal", null);
|
|
10
27
|
__publicField(this, "messageHandler", null);
|
|
11
28
|
}
|
|
12
|
-
|
|
13
|
-
* Opens the payment checkout page in an iframe modal.
|
|
14
|
-
* @param urlOrParams - The checkout page URL or payment parameters
|
|
15
|
-
* @param options - UI options
|
|
16
|
-
*/
|
|
17
|
-
openPayment(urlOrParams, options) {
|
|
29
|
+
openHostedFrame(url, options) {
|
|
18
30
|
if (typeof document === "undefined") return;
|
|
19
31
|
if (this.modal) return;
|
|
20
|
-
let checkoutUrl;
|
|
21
|
-
if (typeof urlOrParams === "string") {
|
|
22
|
-
checkoutUrl = urlOrParams;
|
|
23
|
-
} else {
|
|
24
|
-
const {
|
|
25
|
-
appId,
|
|
26
|
-
productId,
|
|
27
|
-
priceId,
|
|
28
|
-
productCode,
|
|
29
|
-
userId,
|
|
30
|
-
checkoutUrl: checkoutUrlParam,
|
|
31
|
-
baseUrl = "https://pay.imgto.link"
|
|
32
|
-
} = urlOrParams;
|
|
33
|
-
const base = (checkoutUrlParam || baseUrl).replace(/\/$/, "");
|
|
34
|
-
if (productCode) {
|
|
35
|
-
checkoutUrl = `${base}/checkout/${appId}/code/${productCode}?userId=${encodeURIComponent(userId)}`;
|
|
36
|
-
} else if (productId && priceId) {
|
|
37
|
-
checkoutUrl = `${base}/checkout/${appId}/${productId}/${priceId}?userId=${encodeURIComponent(userId)}`;
|
|
38
|
-
} else {
|
|
39
|
-
throw new Error(
|
|
40
|
-
"Either productCode or both productId and priceId are required"
|
|
41
|
-
);
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
let finalUrl = checkoutUrl;
|
|
45
|
-
if (options?.locale) {
|
|
46
|
-
try {
|
|
47
|
-
const url = new URL(checkoutUrl);
|
|
48
|
-
const localePrefix = `/${options.locale}`;
|
|
49
|
-
if (!url.pathname.startsWith(`${localePrefix}/`) && url.pathname !== localePrefix) {
|
|
50
|
-
url.pathname = `${localePrefix}${url.pathname}`;
|
|
51
|
-
finalUrl = url.toString();
|
|
52
|
-
}
|
|
53
|
-
} catch (_e) {
|
|
54
|
-
const localePrefix = `/${options.locale}`;
|
|
55
|
-
if (!checkoutUrl.startsWith(`${localePrefix}/`) && checkoutUrl !== localePrefix) {
|
|
56
|
-
finalUrl = `${localePrefix}${checkoutUrl.startsWith("/") ? "" : "/"}${checkoutUrl}`;
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
32
|
this.modal = document.createElement("div");
|
|
61
33
|
Object.assign(this.modal.style, {
|
|
62
34
|
position: "fixed",
|
|
@@ -64,53 +36,66 @@ var PaymentUI = class {
|
|
|
64
36
|
left: "0",
|
|
65
37
|
width: "100%",
|
|
66
38
|
height: "100%",
|
|
67
|
-
backgroundColor: "rgba(
|
|
39
|
+
backgroundColor: "rgba(15,23,42,0.52)",
|
|
68
40
|
display: "flex",
|
|
69
41
|
alignItems: "center",
|
|
70
42
|
justifyContent: "center",
|
|
71
43
|
zIndex: "9999",
|
|
72
|
-
transition: "opacity 0.3s ease"
|
|
44
|
+
transition: "opacity 0.3s ease",
|
|
45
|
+
backdropFilter: "blur(14px)",
|
|
46
|
+
padding: "16px"
|
|
73
47
|
});
|
|
74
48
|
const container = document.createElement("div");
|
|
75
49
|
Object.assign(container.style, {
|
|
76
|
-
width: "450px",
|
|
77
|
-
height: "min(600px, 90vh)",
|
|
78
|
-
backgroundColor: "
|
|
79
|
-
borderRadius: "
|
|
80
|
-
overflow: "
|
|
50
|
+
width: options.width || "450px",
|
|
51
|
+
height: options.height || "min(600px, 90vh)",
|
|
52
|
+
backgroundColor: "transparent",
|
|
53
|
+
borderRadius: "28px",
|
|
54
|
+
overflow: "visible",
|
|
81
55
|
position: "relative",
|
|
82
|
-
boxShadow: "
|
|
56
|
+
boxShadow: "none",
|
|
57
|
+
maxWidth: "calc(100vw - 32px)",
|
|
58
|
+
maxHeight: "calc(100vh - 32px)",
|
|
59
|
+
transition: "height 180ms ease",
|
|
60
|
+
willChange: "height"
|
|
83
61
|
});
|
|
84
62
|
const closeBtn = document.createElement("button");
|
|
85
63
|
closeBtn.innerHTML = "\xD7";
|
|
86
64
|
Object.assign(closeBtn.style, {
|
|
87
65
|
position: "absolute",
|
|
88
|
-
right: "
|
|
89
|
-
top: "
|
|
90
|
-
fontSize: "
|
|
91
|
-
|
|
92
|
-
|
|
66
|
+
right: "-14px",
|
|
67
|
+
top: "-14px",
|
|
68
|
+
fontSize: "20px",
|
|
69
|
+
width: "36px",
|
|
70
|
+
height: "36px",
|
|
71
|
+
borderRadius: "9999px",
|
|
72
|
+
border: "1px solid rgba(255,255,255,0.5)",
|
|
73
|
+
background: "rgba(255,255,255,0.9)",
|
|
93
74
|
cursor: "pointer",
|
|
94
|
-
color: "#
|
|
95
|
-
zIndex: "
|
|
75
|
+
color: "#475569",
|
|
76
|
+
zIndex: "2",
|
|
77
|
+
boxShadow: "0 10px 30px rgba(15,23,42,0.16)"
|
|
96
78
|
});
|
|
97
79
|
closeBtn.onclick = () => {
|
|
98
80
|
this.close();
|
|
99
|
-
options?.
|
|
81
|
+
options.onCloseButton?.();
|
|
100
82
|
};
|
|
101
83
|
this.iframe = document.createElement("iframe");
|
|
102
|
-
this.iframe.src =
|
|
84
|
+
this.iframe.src = url;
|
|
103
85
|
Object.assign(this.iframe.style, {
|
|
104
86
|
width: "100%",
|
|
105
87
|
height: "100%",
|
|
106
|
-
border: "none"
|
|
88
|
+
border: "none",
|
|
89
|
+
borderRadius: "28px",
|
|
90
|
+
background: "transparent",
|
|
91
|
+
display: "block"
|
|
107
92
|
});
|
|
108
93
|
container.appendChild(closeBtn);
|
|
109
94
|
container.appendChild(this.iframe);
|
|
110
95
|
this.modal.appendChild(container);
|
|
111
96
|
document.body.appendChild(this.modal);
|
|
112
97
|
this.messageHandler = (event) => {
|
|
113
|
-
if (options
|
|
98
|
+
if (options.allowedOrigin && options.allowedOrigin !== "*") {
|
|
114
99
|
if (event.origin !== options.allowedOrigin) {
|
|
115
100
|
return;
|
|
116
101
|
}
|
|
@@ -119,43 +104,85 @@ var PaymentUI = class {
|
|
|
119
104
|
if (!data || typeof data !== "object" || !data.type) {
|
|
120
105
|
return;
|
|
121
106
|
}
|
|
122
|
-
|
|
123
|
-
case "PAYMENT_SUCCESS":
|
|
124
|
-
options?.onSuccess?.(data.orderId);
|
|
125
|
-
break;
|
|
126
|
-
case "PAYMENT_CANCELLED":
|
|
127
|
-
options?.onCancel?.(data.orderId);
|
|
128
|
-
break;
|
|
129
|
-
case "PAYMENT_RESIZE":
|
|
130
|
-
if (data.height && container) {
|
|
131
|
-
const maxHeight = window.innerHeight * 0.9;
|
|
132
|
-
const newHeight = Math.min(data.height, maxHeight);
|
|
133
|
-
container.style.height = `${newHeight}px`;
|
|
134
|
-
}
|
|
135
|
-
break;
|
|
136
|
-
case "PAYMENT_CLOSE":
|
|
137
|
-
this.close();
|
|
138
|
-
options?.onClose?.();
|
|
139
|
-
break;
|
|
140
|
-
}
|
|
107
|
+
options.onMessage(data, container);
|
|
141
108
|
};
|
|
142
109
|
window.addEventListener("message", this.messageHandler);
|
|
143
110
|
}
|
|
144
|
-
/**
|
|
145
|
-
* Close the payment modal
|
|
146
|
-
*/
|
|
147
111
|
close() {
|
|
148
112
|
if (typeof window === "undefined") return;
|
|
149
113
|
if (this.messageHandler) {
|
|
150
114
|
window.removeEventListener("message", this.messageHandler);
|
|
151
115
|
this.messageHandler = null;
|
|
152
116
|
}
|
|
153
|
-
if (this.modal
|
|
117
|
+
if (this.modal?.parentNode) {
|
|
154
118
|
this.modal.parentNode.removeChild(this.modal);
|
|
155
119
|
}
|
|
156
120
|
this.modal = null;
|
|
157
121
|
this.iframe = null;
|
|
158
122
|
}
|
|
123
|
+
};
|
|
124
|
+
|
|
125
|
+
// src/client.ts
|
|
126
|
+
var PaymentUI = class extends HostedFrameModal {
|
|
127
|
+
/**
|
|
128
|
+
* Opens the payment checkout page in an iframe modal.
|
|
129
|
+
* @param urlOrParams - The checkout page URL or payment parameters
|
|
130
|
+
* @param options - UI options
|
|
131
|
+
*/
|
|
132
|
+
openPayment(urlOrParams, options) {
|
|
133
|
+
if (typeof document === "undefined") return;
|
|
134
|
+
if (this.modal) return;
|
|
135
|
+
let checkoutUrl;
|
|
136
|
+
if (typeof urlOrParams === "string") {
|
|
137
|
+
checkoutUrl = urlOrParams;
|
|
138
|
+
} else {
|
|
139
|
+
const {
|
|
140
|
+
appId,
|
|
141
|
+
productId,
|
|
142
|
+
priceId,
|
|
143
|
+
productCode,
|
|
144
|
+
userId,
|
|
145
|
+
checkoutUrl: checkoutUrlParam,
|
|
146
|
+
baseUrl = "https://pay.imgto.link"
|
|
147
|
+
} = urlOrParams;
|
|
148
|
+
const base = (checkoutUrlParam || baseUrl).replace(/\/$/, "");
|
|
149
|
+
if (productCode) {
|
|
150
|
+
checkoutUrl = `${base}/checkout/${appId}/code/${productCode}?userId=${encodeURIComponent(userId)}`;
|
|
151
|
+
} else if (productId && priceId) {
|
|
152
|
+
checkoutUrl = `${base}/checkout/${appId}/${productId}/${priceId}?userId=${encodeURIComponent(userId)}`;
|
|
153
|
+
} else {
|
|
154
|
+
throw new Error(
|
|
155
|
+
"Either productCode or both productId and priceId are required"
|
|
156
|
+
);
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
const finalUrl = applyLocaleToUrl(checkoutUrl, options?.locale);
|
|
160
|
+
this.openHostedFrame(finalUrl, {
|
|
161
|
+
allowedOrigin: options?.allowedOrigin,
|
|
162
|
+
onCloseButton: () => options?.onCancel?.(),
|
|
163
|
+
onMessage: (data, container) => {
|
|
164
|
+
switch (data.type) {
|
|
165
|
+
case "PAYMENT_SUCCESS":
|
|
166
|
+
options?.onSuccess?.(data.orderId);
|
|
167
|
+
break;
|
|
168
|
+
case "PAYMENT_CANCELLED":
|
|
169
|
+
options?.onCancel?.(data.orderId);
|
|
170
|
+
break;
|
|
171
|
+
case "PAYMENT_RESIZE":
|
|
172
|
+
if (data.height) {
|
|
173
|
+
const maxHeight = window.innerHeight * 0.9;
|
|
174
|
+
const newHeight = Math.min(data.height, maxHeight);
|
|
175
|
+
container.style.height = `${newHeight}px`;
|
|
176
|
+
}
|
|
177
|
+
break;
|
|
178
|
+
case "PAYMENT_CLOSE":
|
|
179
|
+
this.close();
|
|
180
|
+
options?.onClose?.();
|
|
181
|
+
break;
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
});
|
|
185
|
+
}
|
|
159
186
|
/**
|
|
160
187
|
* Poll order status from integrator's API endpoint
|
|
161
188
|
* @param statusUrl - The integrator's API endpoint to check order status
|
package/dist/client.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/client.ts"],"sourcesContent":["/**\n * Youidian Payment SDK - Client Module\n * 用于浏览器端集成,包含支付弹窗、状态轮询等功能\n * 不依赖 Node.js crypto 模块\n */\n\n/**\n * Payment event types from checkout pages\n */\nexport type PaymentEventType =\n\t| \"PAYMENT_SUCCESS\"\n\t| \"PAYMENT_CANCELLED\"\n\t| \"PAYMENT_CLOSE\"\n\t| \"PAYMENT_RESIZE\"\n\n/**\n * Payment event data from postMessage\n */\nexport interface PaymentEventData {\n\ttype: PaymentEventType\n\torderId?: string\n\theight?: number\n}\n\n/**\n * Order status response\n */\nexport interface OrderStatus {\n\torderId: string\n\tstatus: \"PENDING\" | \"PAID\" | \"CANCELLED\" | \"REFUNDED\" | \"FAILED\"\n\tpaidAt?: string\n\tchannelTransactionId?: string\n}\n\n/**\n * Payment UI Options\n */\nexport interface PaymentUIOptions {\n\tlocale?: string\n\tonSuccess?: (orderId?: string) => void\n\tonCancel?: (orderId?: string) => void\n\tonClose?: () => void\n\t/** Origin to validate postMessage (defaults to '*', set for security) */\n\tallowedOrigin?: string\n}\n\n/**\n * Poll Options\n */\nexport interface PollOptions {\n\t/** Polling interval in ms (default: 3000) */\n\tinterval?: number\n\t/** Timeout in ms (default: 300000 = 5 minutes) */\n\ttimeout?: number\n\t/** Callback on each status check */\n\tonStatusChange?: (status: OrderStatus) => void\n}\n\n/**\n * Client-side Payment UI Helper\n * 浏览器端支付 UI 辅助类,用于弹出支付窗口和轮询订单状态\n */\n/**\n * Params for opening payment directly without manual URL construction\n */\nexport interface PaymentParams {\n\tappId: string\n\tuserId: string\n\t/** Product ID - use with priceId for direct ID-based payment */\n\tproductId?: string\n\t/** Price ID - use with productId for direct ID-based payment */\n\tpriceId?: string\n\t/** Product code - use with locale for code-based payment (alternative to productId/priceId) */\n\tproductCode?: string\n\n\t/**\n\t * @deprecated Use checkoutUrl instead\n\t * Defaults to https://pay.imgto.link\n\t */\n\tbaseUrl?: string\n\n\t/**\n\t * Checkout page URL (e.g. https://pay.youidian.com)\n\t * Defaults to https://pay.imgto.link\n\t */\n\tcheckoutUrl?: string\n}\n\n/**\n * Client-side Payment UI Helper\n * 浏览器端支付 UI 辅助类,用于弹出支付窗口和轮询订单状态\n */\nexport class PaymentUI {\n\tprivate iframe: HTMLIFrameElement | null = null\n\tprivate modal: HTMLDivElement | null = null\n\tprivate messageHandler: ((event: MessageEvent) => void) | null = null\n\n\t/**\n\t * Opens the payment checkout page in an iframe modal.\n\t * @param urlOrParams - The checkout page URL or payment parameters\n\t * @param options - UI options\n\t */\n\topenPayment(urlOrParams: string | PaymentParams, options?: PaymentUIOptions) {\n\t\tif (typeof document === \"undefined\") return // Server-side guard\n\t\tif (this.modal) return // Prevent multiple modals\n\n\t\tlet checkoutUrl: string\n\t\tif (typeof urlOrParams === \"string\") {\n\t\t\tcheckoutUrl = urlOrParams\n\t\t} else {\n\t\t\tconst {\n\t\t\t\tappId,\n\t\t\t\tproductId,\n\t\t\t\tpriceId,\n\t\t\t\tproductCode,\n\t\t\t\tuserId,\n\t\t\t\tcheckoutUrl: checkoutUrlParam,\n\t\t\t\tbaseUrl = \"https://pay.imgto.link\",\n\t\t\t} = urlOrParams\n\n\t\t\t// 优先使用 checkoutUrl,其次 baseUrl,默认 https://pay.imgto.link\n\t\t\tconst base = (checkoutUrlParam || baseUrl).replace(/\\/$/, \"\")\n\n\t\t\tif (productCode) {\n\t\t\t\t// Use product code route\n\t\t\t\tcheckoutUrl = `${base}/checkout/${appId}/code/${productCode}?userId=${encodeURIComponent(userId)}`\n\t\t\t} else if (productId && priceId) {\n\t\t\t\t// Use ID-based route\n\t\t\t\tcheckoutUrl = `${base}/checkout/${appId}/${productId}/${priceId}?userId=${encodeURIComponent(userId)}`\n\t\t\t} else {\n\t\t\t\tthrow new Error(\n\t\t\t\t\t\"Either productCode or both productId and priceId are required\",\n\t\t\t\t)\n\t\t\t}\n\t\t}\n\n\t\tlet finalUrl = checkoutUrl\n\t\tif (options?.locale) {\n\t\t\ttry {\n\t\t\t\tconst url = new URL(checkoutUrl)\n\t\t\t\t// Check if path already starts with /locale/ or is exactly /locale\n\t\t\t\tconst localePrefix = `/${options.locale}`\n\t\t\t\tif (\n\t\t\t\t\t!url.pathname.startsWith(`${localePrefix}/`) &&\n\t\t\t\t\turl.pathname !== localePrefix\n\t\t\t\t) {\n\t\t\t\t\turl.pathname = `${localePrefix}${url.pathname}`\n\t\t\t\t\tfinalUrl = url.toString()\n\t\t\t\t}\n\t\t\t} catch (_e) {\n\t\t\t\t// Fallback if URL is relative or invalid\n\t\t\t\tconst localePrefix = `/${options.locale}`\n\t\t\t\tif (\n\t\t\t\t\t!checkoutUrl.startsWith(`${localePrefix}/`) &&\n\t\t\t\t\tcheckoutUrl !== localePrefix\n\t\t\t\t) {\n\t\t\t\t\tfinalUrl = `${localePrefix}${checkoutUrl.startsWith(\"/\") ? \"\" : \"/\"}${checkoutUrl}`\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// Create Modal Overlay\n\t\tthis.modal = document.createElement(\"div\")\n\t\tObject.assign(this.modal.style, {\n\t\t\tposition: \"fixed\",\n\t\t\ttop: \"0\",\n\t\t\tleft: \"0\",\n\t\t\twidth: \"100%\",\n\t\t\theight: \"100%\",\n\t\t\tbackgroundColor: \"rgba(0,0,0,0.5)\",\n\t\t\tdisplay: \"flex\",\n\t\t\talignItems: \"center\",\n\t\t\tjustifyContent: \"center\",\n\t\t\tzIndex: \"9999\",\n\t\t\ttransition: \"opacity 0.3s ease\",\n\t\t})\n\n\t\t// Create Container\n\t\tconst container = document.createElement(\"div\")\n\t\tObject.assign(container.style, {\n\t\t\twidth: \"450px\",\n\t\t\theight: \"min(600px, 90vh)\",\n\t\t\tbackgroundColor: \"#fff\",\n\t\t\tborderRadius: \"20px\",\n\t\t\toverflow: \"hidden\",\n\t\t\tposition: \"relative\",\n\t\t\tboxShadow: \"0 25px 50px -12px rgba(0,0,0,0.25)\",\n\t\t})\n\n\t\t// Create Close Button\n\t\tconst closeBtn = document.createElement(\"button\")\n\t\tcloseBtn.innerHTML = \"×\"\n\t\tObject.assign(closeBtn.style, {\n\t\t\tposition: \"absolute\",\n\t\t\tright: \"15px\",\n\t\t\ttop: \"10px\",\n\t\t\tfontSize: \"24px\",\n\t\t\tborder: \"none\",\n\t\t\tbackground: \"none\",\n\t\t\tcursor: \"pointer\",\n\t\t\tcolor: \"#999\",\n\t\t\tzIndex: \"1\",\n\t\t})\n\t\tcloseBtn.onclick = () => {\n\t\t\tthis.close()\n\t\t\toptions?.onCancel?.()\n\t\t}\n\n\t\t// Create Iframe\n\t\tthis.iframe = document.createElement(\"iframe\")\n\t\tthis.iframe.src = finalUrl\n\t\tObject.assign(this.iframe.style, {\n\t\t\twidth: \"100%\",\n\t\t\theight: \"100%\",\n\t\t\tborder: \"none\",\n\t\t})\n\n\t\tcontainer.appendChild(closeBtn)\n\t\tcontainer.appendChild(this.iframe)\n\t\tthis.modal.appendChild(container)\n\t\tdocument.body.appendChild(this.modal)\n\n\t\t// Listen for messages from checkout page\n\t\tthis.messageHandler = (event: MessageEvent) => {\n\t\t\t// Validate origin if specified\n\t\t\tif (options?.allowedOrigin && options.allowedOrigin !== \"*\") {\n\t\t\t\tif (event.origin !== options.allowedOrigin) {\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tconst data = event.data as PaymentEventData\n\n\t\t\tif (!data || typeof data !== \"object\" || !data.type) {\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\tswitch (data.type) {\n\t\t\t\tcase \"PAYMENT_SUCCESS\":\n\t\t\t\t\toptions?.onSuccess?.(data.orderId)\n\t\t\t\t\t// Auto-close removed, waiting for explicit PAYMENT_CLOSE\n\t\t\t\t\tbreak\n\t\t\t\tcase \"PAYMENT_CANCELLED\":\n\t\t\t\t\toptions?.onCancel?.(data.orderId)\n\t\t\t\t\tbreak\n\t\t\t\tcase \"PAYMENT_RESIZE\":\n\t\t\t\t\tif (data.height && container) {\n\t\t\t\t\t\t// Limit max height to 90% of viewport\n\t\t\t\t\t\tconst maxHeight = window.innerHeight * 0.9\n\t\t\t\t\t\tconst newHeight = Math.min(data.height, maxHeight)\n\t\t\t\t\t\tcontainer.style.height = `${newHeight}px`\n\t\t\t\t\t}\n\t\t\t\t\tbreak\n\t\t\t\tcase \"PAYMENT_CLOSE\":\n\t\t\t\t\tthis.close()\n\t\t\t\t\toptions?.onClose?.()\n\t\t\t\t\tbreak\n\t\t\t}\n\t\t}\n\t\twindow.addEventListener(\"message\", this.messageHandler)\n\t}\n\n\t/**\n\t * Close the payment modal\n\t */\n\tclose() {\n\t\tif (typeof window === \"undefined\") return\n\n\t\tif (this.messageHandler) {\n\t\t\twindow.removeEventListener(\"message\", this.messageHandler)\n\t\t\tthis.messageHandler = null\n\t\t}\n\t\tif (this.modal && this.modal.parentNode) {\n\t\t\tthis.modal.parentNode.removeChild(this.modal)\n\t\t}\n\t\tthis.modal = null\n\t\tthis.iframe = null\n\t}\n\n\t/**\n\t * Poll order status from integrator's API endpoint\n\t * @param statusUrl - The integrator's API endpoint to check order status\n\t * @param options - Polling options\n\t * @returns Promise that resolves when order is paid or rejects on timeout/failure\n\t */\n\tasync pollOrderStatus(\n\t\tstatusUrl: string,\n\t\toptions?: PollOptions,\n\t): Promise<OrderStatus> {\n\t\tconst interval = options?.interval || 3000\n\t\tconst timeout = options?.timeout || 300000\n\t\tconst startTime = Date.now()\n\n\t\treturn new Promise((resolve, reject) => {\n\t\t\tconst poll = async () => {\n\t\t\t\ttry {\n\t\t\t\t\tconst response = await fetch(statusUrl)\n\t\t\t\t\tif (!response.ok) {\n\t\t\t\t\t\tthrow new Error(`Status check failed: ${response.status}`)\n\t\t\t\t\t}\n\n\t\t\t\t\tconst status: OrderStatus = await response.json()\n\t\t\t\t\toptions?.onStatusChange?.(status)\n\n\t\t\t\t\tif (status.status === \"PAID\") {\n\t\t\t\t\t\tresolve(status)\n\t\t\t\t\t\treturn\n\t\t\t\t\t}\n\n\t\t\t\t\tif (status.status === \"CANCELLED\" || status.status === \"FAILED\") {\n\t\t\t\t\t\treject(new Error(`Order ${status.status.toLowerCase()}`))\n\t\t\t\t\t\treturn\n\t\t\t\t\t}\n\n\t\t\t\t\t// Check timeout\n\t\t\t\t\tif (Date.now() - startTime > timeout) {\n\t\t\t\t\t\treject(new Error(\"Polling timeout\"))\n\t\t\t\t\t\treturn\n\t\t\t\t\t}\n\n\t\t\t\t\t// Continue polling\n\t\t\t\t\tsetTimeout(poll, interval)\n\t\t\t\t} catch (error) {\n\t\t\t\t\t// On network error, continue polling unless timeout\n\t\t\t\t\tif (Date.now() - startTime > timeout) {\n\t\t\t\t\t\treject(error)\n\t\t\t\t\t\treturn\n\t\t\t\t\t}\n\t\t\t\t\tsetTimeout(poll, interval)\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tpoll()\n\t\t})\n\t}\n}\n\n/**\n * Convenience function to create a PaymentUI instance\n */\nexport function createPaymentUI(): PaymentUI {\n\treturn new PaymentUI()\n}\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/hosted-modal.ts","../src/client.ts"],"sourcesContent":["export interface HostedFrameMessage {\n\ttype: string\n\theight?: number\n}\n\ntype HostedFrameOptions<TData extends HostedFrameMessage> = {\n\tallowedOrigin?: string\n\theight?: string\n\tonCloseButton?: () => void\n\tonMessage: (data: TData, container: HTMLDivElement) => void\n\twidth?: string\n}\n\nexport function applyLocaleToUrl(urlValue: string, locale?: string): string {\n\tif (!locale) return urlValue\n\n\ttry {\n\t\tconst url = new URL(urlValue)\n\t\tconst localePrefix = `/${locale}`\n\t\tif (\n\t\t\t!url.pathname.startsWith(`${localePrefix}/`) &&\n\t\t\turl.pathname !== localePrefix\n\t\t) {\n\t\t\turl.pathname = `${localePrefix}${url.pathname}`\n\t\t}\n\t\treturn url.toString()\n\t} catch (_error) {\n\t\tconst localePrefix = `/${locale}`\n\t\tif (!urlValue.startsWith(`${localePrefix}/`) && urlValue !== localePrefix) {\n\t\t\treturn `${localePrefix}${urlValue.startsWith(\"/\") ? \"\" : \"/\"}${urlValue}`\n\t\t}\n\t\treturn urlValue\n\t}\n}\n\nexport class HostedFrameModal<TData extends HostedFrameMessage> {\n\tprotected iframe: HTMLIFrameElement | null = null\n\tprotected modal: HTMLDivElement | null = null\n\tprotected messageHandler: ((event: MessageEvent) => void) | null = null\n\n\tprotected openHostedFrame(\n\t\turl: string,\n\t\toptions: HostedFrameOptions<TData>,\n\t): void {\n\t\tif (typeof document === \"undefined\") return\n\t\tif (this.modal) return\n\n\t\tthis.modal = document.createElement(\"div\")\n\t\tObject.assign(this.modal.style, {\n\t\t\tposition: \"fixed\",\n\t\t\ttop: \"0\",\n\t\t\tleft: \"0\",\n\t\t\twidth: \"100%\",\n\t\t\theight: \"100%\",\n\t\t\tbackgroundColor: \"rgba(15,23,42,0.52)\",\n\t\t\tdisplay: \"flex\",\n\t\t\talignItems: \"center\",\n\t\t\tjustifyContent: \"center\",\n\t\t\tzIndex: \"9999\",\n\t\t\ttransition: \"opacity 0.3s ease\",\n\t\t\tbackdropFilter: \"blur(14px)\",\n\t\t\tpadding: \"16px\",\n\t\t})\n\n\t\tconst container = document.createElement(\"div\")\n\t\tObject.assign(container.style, {\n\t\t\twidth: options.width || \"450px\",\n\t\t\theight: options.height || \"min(600px, 90vh)\",\n\t\t\tbackgroundColor: \"transparent\",\n\t\t\tborderRadius: \"28px\",\n\t\t\toverflow: \"visible\",\n\t\t\tposition: \"relative\",\n\t\t\tboxShadow: \"none\",\n\t\t\tmaxWidth: \"calc(100vw - 32px)\",\n\t\t\tmaxHeight: \"calc(100vh - 32px)\",\n\t\t\ttransition: \"height 180ms ease\",\n\t\t\twillChange: \"height\",\n\t\t})\n\n\t\tconst closeBtn = document.createElement(\"button\")\n\t\tcloseBtn.innerHTML = \"×\"\n\t\tObject.assign(closeBtn.style, {\n\t\t\tposition: \"absolute\",\n\t\t\tright: \"-14px\",\n\t\t\ttop: \"-14px\",\n\t\t\tfontSize: \"20px\",\n\t\t\twidth: \"36px\",\n\t\t\theight: \"36px\",\n\t\t\tborderRadius: \"9999px\",\n\t\t\tborder: \"1px solid rgba(255,255,255,0.5)\",\n\t\t\tbackground: \"rgba(255,255,255,0.9)\",\n\t\t\tcursor: \"pointer\",\n\t\t\tcolor: \"#475569\",\n\t\t\tzIndex: \"2\",\n\t\t\tboxShadow: \"0 10px 30px rgba(15,23,42,0.16)\",\n\t\t})\n\t\tcloseBtn.onclick = () => {\n\t\t\tthis.close()\n\t\t\toptions.onCloseButton?.()\n\t\t}\n\n\t\tthis.iframe = document.createElement(\"iframe\")\n\t\tthis.iframe.src = url\n\t\tObject.assign(this.iframe.style, {\n\t\t\twidth: \"100%\",\n\t\t\theight: \"100%\",\n\t\t\tborder: \"none\",\n\t\t\tborderRadius: \"28px\",\n\t\t\tbackground: \"transparent\",\n\t\t\tdisplay: \"block\",\n\t\t})\n\n\t\tcontainer.appendChild(closeBtn)\n\t\tcontainer.appendChild(this.iframe)\n\t\tthis.modal.appendChild(container)\n\t\tdocument.body.appendChild(this.modal)\n\n\t\tthis.messageHandler = (event: MessageEvent) => {\n\t\t\tif (options.allowedOrigin && options.allowedOrigin !== \"*\") {\n\t\t\t\tif (event.origin !== options.allowedOrigin) {\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tconst data = event.data as TData\n\t\t\tif (!data || typeof data !== \"object\" || !data.type) {\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\toptions.onMessage(data, container)\n\t\t}\n\n\t\twindow.addEventListener(\"message\", this.messageHandler)\n\t}\n\n\tclose() {\n\t\tif (typeof window === \"undefined\") return\n\n\t\tif (this.messageHandler) {\n\t\t\twindow.removeEventListener(\"message\", this.messageHandler)\n\t\t\tthis.messageHandler = null\n\t\t}\n\n\t\tif (this.modal?.parentNode) {\n\t\t\tthis.modal.parentNode.removeChild(this.modal)\n\t\t}\n\n\t\tthis.modal = null\n\t\tthis.iframe = null\n\t}\n}\n","/**\n * Youidian Payment SDK - Client Module\n * 用于浏览器端集成,包含支付弹窗、状态轮询等功能\n * 不依赖 Node.js crypto 模块\n */\n\nimport {\n\tapplyLocaleToUrl,\n\tHostedFrameModal,\n} from \"./hosted-modal\"\n\n/**\n * Payment event types from checkout pages\n */\nexport type PaymentEventType =\n\t| \"PAYMENT_SUCCESS\"\n\t| \"PAYMENT_CANCELLED\"\n\t| \"PAYMENT_CLOSE\"\n\t| \"PAYMENT_RESIZE\"\n\n/**\n * Payment event data from postMessage\n */\nexport interface PaymentEventData {\n\ttype: PaymentEventType\n\torderId?: string\n\theight?: number\n}\n\n/**\n * Order status response\n */\nexport interface OrderStatus {\n\torderId: string\n\tstatus: \"PENDING\" | \"PAID\" | \"CANCELLED\" | \"REFUNDED\" | \"FAILED\"\n\tpaidAt?: string\n\tchannelTransactionId?: string\n}\n\n/**\n * Payment UI Options\n */\nexport interface PaymentUIOptions {\n\tlocale?: string\n\tonSuccess?: (orderId?: string) => void\n\tonCancel?: (orderId?: string) => void\n\tonClose?: () => void\n\t/** Origin to validate postMessage (defaults to '*', set for security) */\n\tallowedOrigin?: string\n}\n\n/**\n * Poll Options\n */\nexport interface PollOptions {\n\t/** Polling interval in ms (default: 3000) */\n\tinterval?: number\n\t/** Timeout in ms (default: 300000 = 5 minutes) */\n\ttimeout?: number\n\t/** Callback on each status check */\n\tonStatusChange?: (status: OrderStatus) => void\n}\n\n/**\n * Client-side Payment UI Helper\n * 浏览器端支付 UI 辅助类,用于弹出支付窗口和轮询订单状态\n */\n/**\n * Params for opening payment directly without manual URL construction\n */\nexport interface PaymentParams {\n\tappId: string\n\tuserId: string\n\t/** Product ID - use with priceId for direct ID-based payment */\n\tproductId?: string\n\t/** Price ID - use with productId for direct ID-based payment */\n\tpriceId?: string\n\t/** Product code - use with locale for code-based payment (alternative to productId/priceId) */\n\tproductCode?: string\n\n\t/**\n\t * @deprecated Use checkoutUrl instead\n\t * Defaults to https://pay.imgto.link\n\t */\n\tbaseUrl?: string\n\n\t/**\n\t * Checkout page URL (e.g. https://pay.youidian.com)\n\t * Defaults to https://pay.imgto.link\n\t */\n\tcheckoutUrl?: string\n}\n\n/**\n * Client-side Payment UI Helper\n * 浏览器端支付 UI 辅助类,用于弹出支付窗口和轮询订单状态\n */\nexport class PaymentUI extends HostedFrameModal<PaymentEventData> {\n\n\t/**\n\t * Opens the payment checkout page in an iframe modal.\n\t * @param urlOrParams - The checkout page URL or payment parameters\n\t * @param options - UI options\n\t */\n\topenPayment(urlOrParams: string | PaymentParams, options?: PaymentUIOptions) {\n\t\tif (typeof document === \"undefined\") return // Server-side guard\n\t\tif (this.modal) return // Prevent multiple modals\n\n\t\tlet checkoutUrl: string\n\t\tif (typeof urlOrParams === \"string\") {\n\t\t\tcheckoutUrl = urlOrParams\n\t\t} else {\n\t\t\tconst {\n\t\t\t\tappId,\n\t\t\t\tproductId,\n\t\t\t\tpriceId,\n\t\t\t\tproductCode,\n\t\t\t\tuserId,\n\t\t\t\tcheckoutUrl: checkoutUrlParam,\n\t\t\t\tbaseUrl = \"https://pay.imgto.link\",\n\t\t\t} = urlOrParams\n\n\t\t\t// 优先使用 checkoutUrl,其次 baseUrl,默认 https://pay.imgto.link\n\t\t\tconst base = (checkoutUrlParam || baseUrl).replace(/\\/$/, \"\")\n\n\t\t\tif (productCode) {\n\t\t\t\t// Use product code route\n\t\t\t\tcheckoutUrl = `${base}/checkout/${appId}/code/${productCode}?userId=${encodeURIComponent(userId)}`\n\t\t\t} else if (productId && priceId) {\n\t\t\t\t// Use ID-based route\n\t\t\t\tcheckoutUrl = `${base}/checkout/${appId}/${productId}/${priceId}?userId=${encodeURIComponent(userId)}`\n\t\t\t} else {\n\t\t\t\tthrow new Error(\n\t\t\t\t\t\"Either productCode or both productId and priceId are required\",\n\t\t\t\t)\n\t\t\t}\n\t\t}\n\n\t\tconst finalUrl = applyLocaleToUrl(checkoutUrl, options?.locale)\n\n\t\tthis.openHostedFrame(finalUrl, {\n\t\t\tallowedOrigin: options?.allowedOrigin,\n\t\t\tonCloseButton: () => options?.onCancel?.(),\n\t\t\tonMessage: (data, container) => {\n\t\t\t\tswitch (data.type) {\n\t\t\t\t\tcase \"PAYMENT_SUCCESS\":\n\t\t\t\t\t\toptions?.onSuccess?.(data.orderId)\n\t\t\t\t\t\tbreak\n\t\t\t\t\tcase \"PAYMENT_CANCELLED\":\n\t\t\t\t\t\toptions?.onCancel?.(data.orderId)\n\t\t\t\t\t\tbreak\n\t\t\t\t\tcase \"PAYMENT_RESIZE\":\n\t\t\t\t\t\tif (data.height) {\n\t\t\t\t\t\t\tconst maxHeight = window.innerHeight * 0.9\n\t\t\t\t\t\t\tconst newHeight = Math.min(data.height, maxHeight)\n\t\t\t\t\t\t\tcontainer.style.height = `${newHeight}px`\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak\n\t\t\t\t\tcase \"PAYMENT_CLOSE\":\n\t\t\t\t\t\tthis.close()\n\t\t\t\t\t\toptions?.onClose?.()\n\t\t\t\t\t\tbreak\n\t\t\t\t}\n\t\t\t},\n\t\t})\n\t}\n\n\t/**\n\t * Poll order status from integrator's API endpoint\n\t * @param statusUrl - The integrator's API endpoint to check order status\n\t * @param options - Polling options\n\t * @returns Promise that resolves when order is paid or rejects on timeout/failure\n\t */\n\tasync pollOrderStatus(\n\t\tstatusUrl: string,\n\t\toptions?: PollOptions,\n\t): Promise<OrderStatus> {\n\t\tconst interval = options?.interval || 3000\n\t\tconst timeout = options?.timeout || 300000\n\t\tconst startTime = Date.now()\n\n\t\treturn new Promise((resolve, reject) => {\n\t\t\tconst poll = async () => {\n\t\t\t\ttry {\n\t\t\t\t\tconst response = await fetch(statusUrl)\n\t\t\t\t\tif (!response.ok) {\n\t\t\t\t\t\tthrow new Error(`Status check failed: ${response.status}`)\n\t\t\t\t\t}\n\n\t\t\t\t\tconst status: OrderStatus = await response.json()\n\t\t\t\t\toptions?.onStatusChange?.(status)\n\n\t\t\t\t\tif (status.status === \"PAID\") {\n\t\t\t\t\t\tresolve(status)\n\t\t\t\t\t\treturn\n\t\t\t\t\t}\n\n\t\t\t\t\tif (status.status === \"CANCELLED\" || status.status === \"FAILED\") {\n\t\t\t\t\t\treject(new Error(`Order ${status.status.toLowerCase()}`))\n\t\t\t\t\t\treturn\n\t\t\t\t\t}\n\n\t\t\t\t\t// Check timeout\n\t\t\t\t\tif (Date.now() - startTime > timeout) {\n\t\t\t\t\t\treject(new Error(\"Polling timeout\"))\n\t\t\t\t\t\treturn\n\t\t\t\t\t}\n\n\t\t\t\t\t// Continue polling\n\t\t\t\t\tsetTimeout(poll, interval)\n\t\t\t\t} catch (error) {\n\t\t\t\t\t// On network error, continue polling unless timeout\n\t\t\t\t\tif (Date.now() - startTime > timeout) {\n\t\t\t\t\t\treject(error)\n\t\t\t\t\t\treturn\n\t\t\t\t\t}\n\t\t\t\t\tsetTimeout(poll, interval)\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tpoll()\n\t\t})\n\t}\n}\n\n/**\n * Convenience function to create a PaymentUI instance\n */\nexport function createPaymentUI(): PaymentUI {\n\treturn new PaymentUI()\n}\n"],"mappings":"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","names":[]}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
interface HostedFrameMessage {
|
|
2
|
+
type: string;
|
|
3
|
+
height?: number;
|
|
4
|
+
}
|
|
5
|
+
type HostedFrameOptions<TData extends HostedFrameMessage> = {
|
|
6
|
+
allowedOrigin?: string;
|
|
7
|
+
height?: string;
|
|
8
|
+
onCloseButton?: () => void;
|
|
9
|
+
onMessage: (data: TData, container: HTMLDivElement) => void;
|
|
10
|
+
width?: string;
|
|
11
|
+
};
|
|
12
|
+
declare class HostedFrameModal<TData extends HostedFrameMessage> {
|
|
13
|
+
protected iframe: HTMLIFrameElement | null;
|
|
14
|
+
protected modal: HTMLDivElement | null;
|
|
15
|
+
protected messageHandler: ((event: MessageEvent) => void) | null;
|
|
16
|
+
protected openHostedFrame(url: string, options: HostedFrameOptions<TData>): void;
|
|
17
|
+
close(): void;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export { HostedFrameModal as H, type HostedFrameMessage as a };
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
interface HostedFrameMessage {
|
|
2
|
+
type: string;
|
|
3
|
+
height?: number;
|
|
4
|
+
}
|
|
5
|
+
type HostedFrameOptions<TData extends HostedFrameMessage> = {
|
|
6
|
+
allowedOrigin?: string;
|
|
7
|
+
height?: string;
|
|
8
|
+
onCloseButton?: () => void;
|
|
9
|
+
onMessage: (data: TData, container: HTMLDivElement) => void;
|
|
10
|
+
width?: string;
|
|
11
|
+
};
|
|
12
|
+
declare class HostedFrameModal<TData extends HostedFrameMessage> {
|
|
13
|
+
protected iframe: HTMLIFrameElement | null;
|
|
14
|
+
protected modal: HTMLDivElement | null;
|
|
15
|
+
protected messageHandler: ((event: MessageEvent) => void) | null;
|
|
16
|
+
protected openHostedFrame(url: string, options: HostedFrameOptions<TData>): void;
|
|
17
|
+
close(): void;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export { HostedFrameModal as H, type HostedFrameMessage as a };
|