@youidian/sdk 3.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.
@@ -0,0 +1,263 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
7
+ var __export = (target, all) => {
8
+ for (var name in all)
9
+ __defProp(target, name, { get: all[name], enumerable: true });
10
+ };
11
+ var __copyProps = (to, from, except, desc) => {
12
+ if (from && typeof from === "object" || typeof from === "function") {
13
+ for (let key of __getOwnPropNames(from))
14
+ if (!__hasOwnProp.call(to, key) && key !== except)
15
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
16
+ }
17
+ return to;
18
+ };
19
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
20
+ var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
21
+
22
+ // src/client.ts
23
+ var client_exports = {};
24
+ __export(client_exports, {
25
+ PaymentUI: () => PaymentUI,
26
+ createPaymentUI: () => createPaymentUI
27
+ });
28
+ module.exports = __toCommonJS(client_exports);
29
+
30
+ // src/hosted-modal.ts
31
+ function applyLocaleToUrl(urlValue, locale) {
32
+ if (!locale) return urlValue;
33
+ try {
34
+ const url = new URL(urlValue);
35
+ const localePrefix = `/${locale}`;
36
+ if (!url.pathname.startsWith(`${localePrefix}/`) && url.pathname !== localePrefix) {
37
+ url.pathname = `${localePrefix}${url.pathname}`;
38
+ }
39
+ return url.toString();
40
+ } catch (_error) {
41
+ const localePrefix = `/${locale}`;
42
+ if (!urlValue.startsWith(`${localePrefix}/`) && urlValue !== localePrefix) {
43
+ return `${localePrefix}${urlValue.startsWith("/") ? "" : "/"}${urlValue}`;
44
+ }
45
+ return urlValue;
46
+ }
47
+ }
48
+ var HostedFrameModal = class {
49
+ constructor() {
50
+ __publicField(this, "iframe", null);
51
+ __publicField(this, "modal", null);
52
+ __publicField(this, "messageHandler", null);
53
+ }
54
+ openHostedFrame(url, options) {
55
+ if (typeof document === "undefined") return;
56
+ if (this.modal) return;
57
+ this.modal = document.createElement("div");
58
+ Object.assign(this.modal.style, {
59
+ position: "fixed",
60
+ top: "0",
61
+ left: "0",
62
+ width: "100%",
63
+ height: "100%",
64
+ backgroundColor: "rgba(15,23,42,0.52)",
65
+ display: "flex",
66
+ alignItems: "center",
67
+ justifyContent: "center",
68
+ zIndex: "9999",
69
+ transition: "opacity 0.3s ease",
70
+ backdropFilter: "blur(14px)",
71
+ padding: "16px"
72
+ });
73
+ const container = document.createElement("div");
74
+ Object.assign(container.style, {
75
+ width: options.width || "450px",
76
+ height: options.height || "min(600px, 90vh)",
77
+ backgroundColor: "transparent",
78
+ borderRadius: "28px",
79
+ overflow: "visible",
80
+ position: "relative",
81
+ boxShadow: "none",
82
+ maxWidth: "calc(100vw - 32px)",
83
+ maxHeight: "calc(100vh - 32px)",
84
+ transition: "height 180ms ease",
85
+ willChange: "height"
86
+ });
87
+ const closeBtn = document.createElement("button");
88
+ closeBtn.innerHTML = "\xD7";
89
+ Object.assign(closeBtn.style, {
90
+ position: "absolute",
91
+ right: "-14px",
92
+ top: "-14px",
93
+ fontSize: "20px",
94
+ width: "36px",
95
+ height: "36px",
96
+ borderRadius: "9999px",
97
+ border: "1px solid rgba(255,255,255,0.5)",
98
+ background: "rgba(255,255,255,0.9)",
99
+ cursor: "pointer",
100
+ color: "#475569",
101
+ zIndex: "2",
102
+ boxShadow: "0 10px 30px rgba(15,23,42,0.16)"
103
+ });
104
+ closeBtn.onclick = () => {
105
+ this.close();
106
+ options.onCloseButton?.();
107
+ };
108
+ this.iframe = document.createElement("iframe");
109
+ this.iframe.src = url;
110
+ Object.assign(this.iframe.style, {
111
+ width: "100%",
112
+ height: "100%",
113
+ border: "none",
114
+ borderRadius: "28px",
115
+ background: "transparent",
116
+ display: "block"
117
+ });
118
+ container.appendChild(closeBtn);
119
+ container.appendChild(this.iframe);
120
+ this.modal.appendChild(container);
121
+ document.body.appendChild(this.modal);
122
+ this.messageHandler = (event) => {
123
+ if (options.allowedOrigin && options.allowedOrigin !== "*") {
124
+ if (event.origin !== options.allowedOrigin) {
125
+ return;
126
+ }
127
+ }
128
+ const data = event.data;
129
+ if (!data || typeof data !== "object" || !data.type) {
130
+ return;
131
+ }
132
+ options.onMessage(data, container);
133
+ };
134
+ window.addEventListener("message", this.messageHandler);
135
+ }
136
+ close() {
137
+ if (typeof window === "undefined") return;
138
+ if (this.messageHandler) {
139
+ window.removeEventListener("message", this.messageHandler);
140
+ this.messageHandler = null;
141
+ }
142
+ if (this.modal?.parentNode) {
143
+ this.modal.parentNode.removeChild(this.modal);
144
+ }
145
+ this.modal = null;
146
+ this.iframe = null;
147
+ }
148
+ };
149
+
150
+ // src/client.ts
151
+ var PaymentUI = class extends HostedFrameModal {
152
+ /**
153
+ * Opens the payment checkout page in an iframe modal.
154
+ * @param urlOrParams - The checkout page URL or payment parameters
155
+ * @param options - UI options
156
+ */
157
+ openPayment(urlOrParams, options) {
158
+ if (typeof document === "undefined") return;
159
+ if (this.modal) return;
160
+ let checkoutUrl;
161
+ if (typeof urlOrParams === "string") {
162
+ checkoutUrl = urlOrParams;
163
+ } else {
164
+ const {
165
+ appId,
166
+ productId,
167
+ priceId,
168
+ productCode,
169
+ userId,
170
+ checkoutUrl: checkoutUrlParam,
171
+ baseUrl = "https://pay.imgto.link"
172
+ } = urlOrParams;
173
+ const base = (checkoutUrlParam || baseUrl).replace(/\/$/, "");
174
+ if (productCode) {
175
+ checkoutUrl = `${base}/checkout/${appId}/code/${productCode}?userId=${encodeURIComponent(userId)}`;
176
+ } else if (productId && priceId) {
177
+ checkoutUrl = `${base}/checkout/${appId}/${productId}/${priceId}?userId=${encodeURIComponent(userId)}`;
178
+ } else {
179
+ throw new Error(
180
+ "Either productCode or both productId and priceId are required"
181
+ );
182
+ }
183
+ }
184
+ const finalUrl = applyLocaleToUrl(checkoutUrl, options?.locale);
185
+ this.openHostedFrame(finalUrl, {
186
+ allowedOrigin: options?.allowedOrigin,
187
+ onCloseButton: () => options?.onCancel?.(),
188
+ onMessage: (data, container) => {
189
+ switch (data.type) {
190
+ case "PAYMENT_SUCCESS":
191
+ options?.onSuccess?.(data.orderId);
192
+ break;
193
+ case "PAYMENT_CANCELLED":
194
+ options?.onCancel?.(data.orderId);
195
+ break;
196
+ case "PAYMENT_RESIZE":
197
+ if (data.height) {
198
+ const maxHeight = window.innerHeight * 0.9;
199
+ const newHeight = Math.min(data.height, maxHeight);
200
+ container.style.height = `${newHeight}px`;
201
+ }
202
+ break;
203
+ case "PAYMENT_CLOSE":
204
+ this.close();
205
+ options?.onClose?.();
206
+ break;
207
+ }
208
+ }
209
+ });
210
+ }
211
+ /**
212
+ * Poll order status from integrator's API endpoint
213
+ * @param statusUrl - The integrator's API endpoint to check order status
214
+ * @param options - Polling options
215
+ * @returns Promise that resolves when order is paid or rejects on timeout/failure
216
+ */
217
+ async pollOrderStatus(statusUrl, options) {
218
+ const interval = options?.interval || 3e3;
219
+ const timeout = options?.timeout || 3e5;
220
+ const startTime = Date.now();
221
+ return new Promise((resolve, reject) => {
222
+ const poll = async () => {
223
+ try {
224
+ const response = await fetch(statusUrl);
225
+ if (!response.ok) {
226
+ throw new Error(`Status check failed: ${response.status}`);
227
+ }
228
+ const status = await response.json();
229
+ options?.onStatusChange?.(status);
230
+ if (status.status === "PAID") {
231
+ resolve(status);
232
+ return;
233
+ }
234
+ if (status.status === "CANCELLED" || status.status === "FAILED") {
235
+ reject(new Error(`Order ${status.status.toLowerCase()}`));
236
+ return;
237
+ }
238
+ if (Date.now() - startTime > timeout) {
239
+ reject(new Error("Polling timeout"));
240
+ return;
241
+ }
242
+ setTimeout(poll, interval);
243
+ } catch (error) {
244
+ if (Date.now() - startTime > timeout) {
245
+ reject(error);
246
+ return;
247
+ }
248
+ setTimeout(poll, interval);
249
+ }
250
+ };
251
+ poll();
252
+ });
253
+ }
254
+ };
255
+ function createPaymentUI() {
256
+ return new PaymentUI();
257
+ }
258
+ // Annotate the CommonJS export names for ESM import in node:
259
+ 0 && (module.exports = {
260
+ PaymentUI,
261
+ createPaymentUI
262
+ });
263
+ //# sourceMappingURL=client.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/client.ts","../src/hosted-modal.ts"],"sourcesContent":["/**\n * Youidian Payment SDK - Client Module\n * 用于浏览器端集成,包含支付弹窗、状态轮询等功能\n * 不依赖 Node.js crypto 模块\n */\n\nimport { applyLocaleToUrl, HostedFrameModal } 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\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","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\nconst SDK_SUPPORTED_LOCALES = [\n\t\"en\",\n\t\"zh-CN\",\n\t\"zh-Hant\",\n\t\"fr\",\n\t\"de\",\n\t\"ja\",\n\t\"es\",\n\t\"ko\",\n\t\"nl\",\n\t\"it\",\n\t\"pt\",\n] as const\n\nconst SDK_DEFAULT_LOCALE = \"zh-CN\"\n\nconst SDK_LOCALE_ALIASES: Record<string, string> = {\n\ten: \"en\",\n\t\"en-us\": \"en\",\n\t\"en-gb\": \"en\",\n\tzh: \"zh-CN\",\n\t\"zh-cn\": \"zh-CN\",\n\t\"zh-tw\": \"zh-Hant\",\n\t\"zh-hk\": \"zh-Hant\",\n\t\"zh-hant\": \"zh-Hant\",\n\t\"zh-hans\": \"zh-CN\",\n\tfr: \"fr\",\n\tde: \"de\",\n\tja: \"ja\",\n\tes: \"es\",\n\tko: \"ko\",\n\tnl: \"nl\",\n\tit: \"it\",\n\tpt: \"pt\",\n}\n\nfunction matchSupportedBaseLocale(locale: string): string | undefined {\n\tconst base = locale.toLowerCase().split(\"-\")[0]\n\treturn SDK_SUPPORTED_LOCALES.find((item) => item.toLowerCase() === base)\n}\n\nexport function normalizeSdkLocale(locale?: string): string | undefined {\n\tif (!locale) return\n\n\tconst sanitized = locale.trim().replace(/_/g, \"-\")\n\tif (!sanitized) return\n\n\tconst lower = sanitized.toLowerCase()\n\tif (SDK_LOCALE_ALIASES[lower]) {\n\t\treturn SDK_LOCALE_ALIASES[lower]\n\t}\n\n\tlet canonical: string | undefined\n\ttry {\n\t\tcanonical = Intl.getCanonicalLocales(sanitized)[0]\n\t} catch {\n\t\tcanonical = undefined\n\t}\n\n\tconst candidates = [canonical, sanitized].filter(Boolean) as string[]\n\n\tfor (const candidate of candidates) {\n\t\tconst candidateLower = candidate.toLowerCase()\n\n\t\tif (SDK_LOCALE_ALIASES[candidateLower]) {\n\t\t\treturn SDK_LOCALE_ALIASES[candidateLower]\n\t\t}\n\t\tif ((SDK_SUPPORTED_LOCALES as readonly string[]).includes(candidate)) {\n\t\t\treturn candidate\n\t\t}\n\n\t\tconst baseMatch = matchSupportedBaseLocale(candidate)\n\t\tif (baseMatch) {\n\t\t\treturn baseMatch\n\t\t}\n\t}\n}\n\nexport function getBrowserLocale(): string | undefined {\n\tif (typeof navigator === \"undefined\") return\n\n\tfor (const candidate of navigator.languages || []) {\n\t\tconst normalized = normalizeSdkLocale(candidate)\n\t\tif (normalized) return normalized\n\t}\n\n\treturn normalizeSdkLocale(navigator.language)\n}\n\nexport function getAutoResolvedLocale(locale?: string): string {\n\treturn normalizeSdkLocale(locale) || getBrowserLocale() || SDK_DEFAULT_LOCALE\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"],"mappings":";;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;AC0GO,SAAS,iBAAiB,UAAkB,QAAyB;AAC3E,MAAI,CAAC,OAAQ,QAAO;AAEpB,MAAI;AACH,UAAM,MAAM,IAAI,IAAI,QAAQ;AAC5B,UAAM,eAAe,IAAI,MAAM;AAC/B,QACC,CAAC,IAAI,SAAS,WAAW,GAAG,YAAY,GAAG,KAC3C,IAAI,aAAa,cAChB;AACD,UAAI,WAAW,GAAG,YAAY,GAAG,IAAI,QAAQ;AAAA,IAC9C;AACA,WAAO,IAAI,SAAS;AAAA,EACrB,SAAS,QAAQ;AAChB,UAAM,eAAe,IAAI,MAAM;AAC/B,QAAI,CAAC,SAAS,WAAW,GAAG,YAAY,GAAG,KAAK,aAAa,cAAc;AAC1E,aAAO,GAAG,YAAY,GAAG,SAAS,WAAW,GAAG,IAAI,KAAK,GAAG,GAAG,QAAQ;AAAA,IACxE;AACA,WAAO;AAAA,EACR;AACD;AAEO,IAAM,mBAAN,MAAyD;AAAA,EAAzD;AACN,wBAAU,UAAmC;AAC7C,wBAAU,SAA+B;AACzC,wBAAU,kBAAyD;AAAA;AAAA,EAEzD,gBACT,KACA,SACO;AACP,QAAI,OAAO,aAAa,YAAa;AACrC,QAAI,KAAK,MAAO;AAEhB,SAAK,QAAQ,SAAS,cAAc,KAAK;AACzC,WAAO,OAAO,KAAK,MAAM,OAAO;AAAA,MAC/B,UAAU;AAAA,MACV,KAAK;AAAA,MACL,MAAM;AAAA,MACN,OAAO;AAAA,MACP,QAAQ;AAAA,MACR,iBAAiB;AAAA,MACjB,SAAS;AAAA,MACT,YAAY;AAAA,MACZ,gBAAgB;AAAA,MAChB,QAAQ;AAAA,MACR,YAAY;AAAA,MACZ,gBAAgB;AAAA,MAChB,SAAS;AAAA,IACV,CAAC;AAED,UAAM,YAAY,SAAS,cAAc,KAAK;AAC9C,WAAO,OAAO,UAAU,OAAO;AAAA,MAC9B,OAAO,QAAQ,SAAS;AAAA,MACxB,QAAQ,QAAQ,UAAU;AAAA,MAC1B,iBAAiB;AAAA,MACjB,cAAc;AAAA,MACd,UAAU;AAAA,MACV,UAAU;AAAA,MACV,WAAW;AAAA,MACX,UAAU;AAAA,MACV,WAAW;AAAA,MACX,YAAY;AAAA,MACZ,YAAY;AAAA,IACb,CAAC;AAED,UAAM,WAAW,SAAS,cAAc,QAAQ;AAChD,aAAS,YAAY;AACrB,WAAO,OAAO,SAAS,OAAO;AAAA,MAC7B,UAAU;AAAA,MACV,OAAO;AAAA,MACP,KAAK;AAAA,MACL,UAAU;AAAA,MACV,OAAO;AAAA,MACP,QAAQ;AAAA,MACR,cAAc;AAAA,MACd,QAAQ;AAAA,MACR,YAAY;AAAA,MACZ,QAAQ;AAAA,MACR,OAAO;AAAA,MACP,QAAQ;AAAA,MACR,WAAW;AAAA,IACZ,CAAC;AACD,aAAS,UAAU,MAAM;AACxB,WAAK,MAAM;AACX,cAAQ,gBAAgB;AAAA,IACzB;AAEA,SAAK,SAAS,SAAS,cAAc,QAAQ;AAC7C,SAAK,OAAO,MAAM;AAClB,WAAO,OAAO,KAAK,OAAO,OAAO;AAAA,MAChC,OAAO;AAAA,MACP,QAAQ;AAAA,MACR,QAAQ;AAAA,MACR,cAAc;AAAA,MACd,YAAY;AAAA,MACZ,SAAS;AAAA,IACV,CAAC;AAED,cAAU,YAAY,QAAQ;AAC9B,cAAU,YAAY,KAAK,MAAM;AACjC,SAAK,MAAM,YAAY,SAAS;AAChC,aAAS,KAAK,YAAY,KAAK,KAAK;AAEpC,SAAK,iBAAiB,CAAC,UAAwB;AAC9C,UAAI,QAAQ,iBAAiB,QAAQ,kBAAkB,KAAK;AAC3D,YAAI,MAAM,WAAW,QAAQ,eAAe;AAC3C;AAAA,QACD;AAAA,MACD;AAEA,YAAM,OAAO,MAAM;AACnB,UAAI,CAAC,QAAQ,OAAO,SAAS,YAAY,CAAC,KAAK,MAAM;AACpD;AAAA,MACD;AAEA,cAAQ,UAAU,MAAM,SAAS;AAAA,IAClC;AAEA,WAAO,iBAAiB,WAAW,KAAK,cAAc;AAAA,EACvD;AAAA,EAEA,QAAQ;AACP,QAAI,OAAO,WAAW,YAAa;AAEnC,QAAI,KAAK,gBAAgB;AACxB,aAAO,oBAAoB,WAAW,KAAK,cAAc;AACzD,WAAK,iBAAiB;AAAA,IACvB;AAEA,QAAI,KAAK,OAAO,YAAY;AAC3B,WAAK,MAAM,WAAW,YAAY,KAAK,KAAK;AAAA,IAC7C;AAEA,SAAK,QAAQ;AACb,SAAK,SAAS;AAAA,EACf;AACD;;;ADrJO,IAAM,YAAN,cAAwB,iBAAmC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAMjE,YAAY,aAAqC,SAA4B;AAC5E,QAAI,OAAO,aAAa,YAAa;AACrC,QAAI,KAAK,MAAO;AAEhB,QAAI;AACJ,QAAI,OAAO,gBAAgB,UAAU;AACpC,oBAAc;AAAA,IACf,OAAO;AACN,YAAM;AAAA,QACL;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA,aAAa;AAAA,QACb,UAAU;AAAA,MACX,IAAI;AAGJ,YAAM,QAAQ,oBAAoB,SAAS,QAAQ,OAAO,EAAE;AAE5D,UAAI,aAAa;AAEhB,sBAAc,GAAG,IAAI,aAAa,KAAK,SAAS,WAAW,WAAW,mBAAmB,MAAM,CAAC;AAAA,MACjG,WAAW,aAAa,SAAS;AAEhC,sBAAc,GAAG,IAAI,aAAa,KAAK,IAAI,SAAS,IAAI,OAAO,WAAW,mBAAmB,MAAM,CAAC;AAAA,MACrG,OAAO;AACN,cAAM,IAAI;AAAA,UACT;AAAA,QACD;AAAA,MACD;AAAA,IACD;AAEA,UAAM,WAAW,iBAAiB,aAAa,SAAS,MAAM;AAE9D,SAAK,gBAAgB,UAAU;AAAA,MAC9B,eAAe,SAAS;AAAA,MACxB,eAAe,MAAM,SAAS,WAAW;AAAA,MACzC,WAAW,CAAC,MAAM,cAAc;AAC/B,gBAAQ,KAAK,MAAM;AAAA,UAClB,KAAK;AACJ,qBAAS,YAAY,KAAK,OAAO;AACjC;AAAA,UACD,KAAK;AACJ,qBAAS,WAAW,KAAK,OAAO;AAChC;AAAA,UACD,KAAK;AACJ,gBAAI,KAAK,QAAQ;AAChB,oBAAM,YAAY,OAAO,cAAc;AACvC,oBAAM,YAAY,KAAK,IAAI,KAAK,QAAQ,SAAS;AACjD,wBAAU,MAAM,SAAS,GAAG,SAAS;AAAA,YACtC;AACA;AAAA,UACD,KAAK;AACJ,iBAAK,MAAM;AACX,qBAAS,UAAU;AACnB;AAAA,QACF;AAAA,MACD;AAAA,IACD,CAAC;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,MAAM,gBACL,WACA,SACuB;AACvB,UAAM,WAAW,SAAS,YAAY;AACtC,UAAM,UAAU,SAAS,WAAW;AACpC,UAAM,YAAY,KAAK,IAAI;AAE3B,WAAO,IAAI,QAAQ,CAAC,SAAS,WAAW;AACvC,YAAM,OAAO,YAAY;AACxB,YAAI;AACH,gBAAM,WAAW,MAAM,MAAM,SAAS;AACtC,cAAI,CAAC,SAAS,IAAI;AACjB,kBAAM,IAAI,MAAM,wBAAwB,SAAS,MAAM,EAAE;AAAA,UAC1D;AAEA,gBAAM,SAAsB,MAAM,SAAS,KAAK;AAChD,mBAAS,iBAAiB,MAAM;AAEhC,cAAI,OAAO,WAAW,QAAQ;AAC7B,oBAAQ,MAAM;AACd;AAAA,UACD;AAEA,cAAI,OAAO,WAAW,eAAe,OAAO,WAAW,UAAU;AAChE,mBAAO,IAAI,MAAM,SAAS,OAAO,OAAO,YAAY,CAAC,EAAE,CAAC;AACxD;AAAA,UACD;AAGA,cAAI,KAAK,IAAI,IAAI,YAAY,SAAS;AACrC,mBAAO,IAAI,MAAM,iBAAiB,CAAC;AACnC;AAAA,UACD;AAGA,qBAAW,MAAM,QAAQ;AAAA,QAC1B,SAAS,OAAO;AAEf,cAAI,KAAK,IAAI,IAAI,YAAY,SAAS;AACrC,mBAAO,KAAK;AACZ;AAAA,UACD;AACA,qBAAW,MAAM,QAAQ;AAAA,QAC1B;AAAA,MACD;AAEA,WAAK;AAAA,IACN,CAAC;AAAA,EACF;AACD;AAKO,SAAS,kBAA6B;AAC5C,SAAO,IAAI,UAAU;AACtB;","names":[]}
@@ -0,0 +1,103 @@
1
+ import { H as HostedFrameModal } from './hosted-modal-BZmYmXTU.cjs';
2
+
3
+ /**
4
+ * Youidian Payment SDK - Client Module
5
+ * 用于浏览器端集成,包含支付弹窗、状态轮询等功能
6
+ * 不依赖 Node.js crypto 模块
7
+ */
8
+
9
+ /**
10
+ * Payment event types from checkout pages
11
+ */
12
+ type PaymentEventType = "PAYMENT_SUCCESS" | "PAYMENT_CANCELLED" | "PAYMENT_CLOSE" | "PAYMENT_RESIZE";
13
+ /**
14
+ * Payment event data from postMessage
15
+ */
16
+ interface PaymentEventData {
17
+ type: PaymentEventType;
18
+ orderId?: string;
19
+ height?: number;
20
+ }
21
+ /**
22
+ * Order status response
23
+ */
24
+ interface OrderStatus {
25
+ orderId: string;
26
+ status: "PENDING" | "PAID" | "CANCELLED" | "REFUNDED" | "FAILED";
27
+ paidAt?: string;
28
+ channelTransactionId?: string;
29
+ }
30
+ /**
31
+ * Payment UI Options
32
+ */
33
+ interface PaymentUIOptions {
34
+ locale?: string;
35
+ onSuccess?: (orderId?: string) => void;
36
+ onCancel?: (orderId?: string) => void;
37
+ onClose?: () => void;
38
+ /** Origin to validate postMessage (defaults to '*', set for security) */
39
+ allowedOrigin?: string;
40
+ }
41
+ /**
42
+ * Poll Options
43
+ */
44
+ interface PollOptions {
45
+ /** Polling interval in ms (default: 3000) */
46
+ interval?: number;
47
+ /** Timeout in ms (default: 300000 = 5 minutes) */
48
+ timeout?: number;
49
+ /** Callback on each status check */
50
+ onStatusChange?: (status: OrderStatus) => void;
51
+ }
52
+ /**
53
+ * Client-side Payment UI Helper
54
+ * 浏览器端支付 UI 辅助类,用于弹出支付窗口和轮询订单状态
55
+ */
56
+ /**
57
+ * Params for opening payment directly without manual URL construction
58
+ */
59
+ interface PaymentParams {
60
+ appId: string;
61
+ userId: string;
62
+ /** Product ID - use with priceId for direct ID-based payment */
63
+ productId?: string;
64
+ /** Price ID - use with productId for direct ID-based payment */
65
+ priceId?: string;
66
+ /** Product code - use with locale for code-based payment (alternative to productId/priceId) */
67
+ productCode?: string;
68
+ /**
69
+ * @deprecated Use checkoutUrl instead
70
+ * Defaults to https://pay.imgto.link
71
+ */
72
+ baseUrl?: string;
73
+ /**
74
+ * Checkout page URL (e.g. https://pay.youidian.com)
75
+ * Defaults to https://pay.imgto.link
76
+ */
77
+ checkoutUrl?: string;
78
+ }
79
+ /**
80
+ * Client-side Payment UI Helper
81
+ * 浏览器端支付 UI 辅助类,用于弹出支付窗口和轮询订单状态
82
+ */
83
+ declare class PaymentUI extends HostedFrameModal<PaymentEventData> {
84
+ /**
85
+ * Opens the payment checkout page in an iframe modal.
86
+ * @param urlOrParams - The checkout page URL or payment parameters
87
+ * @param options - UI options
88
+ */
89
+ openPayment(urlOrParams: string | PaymentParams, options?: PaymentUIOptions): void;
90
+ /**
91
+ * Poll order status from integrator's API endpoint
92
+ * @param statusUrl - The integrator's API endpoint to check order status
93
+ * @param options - Polling options
94
+ * @returns Promise that resolves when order is paid or rejects on timeout/failure
95
+ */
96
+ pollOrderStatus(statusUrl: string, options?: PollOptions): Promise<OrderStatus>;
97
+ }
98
+ /**
99
+ * Convenience function to create a PaymentUI instance
100
+ */
101
+ declare function createPaymentUI(): PaymentUI;
102
+
103
+ export { type OrderStatus, type PaymentEventData, type PaymentEventType, type PaymentParams, PaymentUI, type PaymentUIOptions, type PollOptions, createPaymentUI };
@@ -0,0 +1,103 @@
1
+ import { H as HostedFrameModal } from './hosted-modal-BZmYmXTU.js';
2
+
3
+ /**
4
+ * Youidian Payment SDK - Client Module
5
+ * 用于浏览器端集成,包含支付弹窗、状态轮询等功能
6
+ * 不依赖 Node.js crypto 模块
7
+ */
8
+
9
+ /**
10
+ * Payment event types from checkout pages
11
+ */
12
+ type PaymentEventType = "PAYMENT_SUCCESS" | "PAYMENT_CANCELLED" | "PAYMENT_CLOSE" | "PAYMENT_RESIZE";
13
+ /**
14
+ * Payment event data from postMessage
15
+ */
16
+ interface PaymentEventData {
17
+ type: PaymentEventType;
18
+ orderId?: string;
19
+ height?: number;
20
+ }
21
+ /**
22
+ * Order status response
23
+ */
24
+ interface OrderStatus {
25
+ orderId: string;
26
+ status: "PENDING" | "PAID" | "CANCELLED" | "REFUNDED" | "FAILED";
27
+ paidAt?: string;
28
+ channelTransactionId?: string;
29
+ }
30
+ /**
31
+ * Payment UI Options
32
+ */
33
+ interface PaymentUIOptions {
34
+ locale?: string;
35
+ onSuccess?: (orderId?: string) => void;
36
+ onCancel?: (orderId?: string) => void;
37
+ onClose?: () => void;
38
+ /** Origin to validate postMessage (defaults to '*', set for security) */
39
+ allowedOrigin?: string;
40
+ }
41
+ /**
42
+ * Poll Options
43
+ */
44
+ interface PollOptions {
45
+ /** Polling interval in ms (default: 3000) */
46
+ interval?: number;
47
+ /** Timeout in ms (default: 300000 = 5 minutes) */
48
+ timeout?: number;
49
+ /** Callback on each status check */
50
+ onStatusChange?: (status: OrderStatus) => void;
51
+ }
52
+ /**
53
+ * Client-side Payment UI Helper
54
+ * 浏览器端支付 UI 辅助类,用于弹出支付窗口和轮询订单状态
55
+ */
56
+ /**
57
+ * Params for opening payment directly without manual URL construction
58
+ */
59
+ interface PaymentParams {
60
+ appId: string;
61
+ userId: string;
62
+ /** Product ID - use with priceId for direct ID-based payment */
63
+ productId?: string;
64
+ /** Price ID - use with productId for direct ID-based payment */
65
+ priceId?: string;
66
+ /** Product code - use with locale for code-based payment (alternative to productId/priceId) */
67
+ productCode?: string;
68
+ /**
69
+ * @deprecated Use checkoutUrl instead
70
+ * Defaults to https://pay.imgto.link
71
+ */
72
+ baseUrl?: string;
73
+ /**
74
+ * Checkout page URL (e.g. https://pay.youidian.com)
75
+ * Defaults to https://pay.imgto.link
76
+ */
77
+ checkoutUrl?: string;
78
+ }
79
+ /**
80
+ * Client-side Payment UI Helper
81
+ * 浏览器端支付 UI 辅助类,用于弹出支付窗口和轮询订单状态
82
+ */
83
+ declare class PaymentUI extends HostedFrameModal<PaymentEventData> {
84
+ /**
85
+ * Opens the payment checkout page in an iframe modal.
86
+ * @param urlOrParams - The checkout page URL or payment parameters
87
+ * @param options - UI options
88
+ */
89
+ openPayment(urlOrParams: string | PaymentParams, options?: PaymentUIOptions): void;
90
+ /**
91
+ * Poll order status from integrator's API endpoint
92
+ * @param statusUrl - The integrator's API endpoint to check order status
93
+ * @param options - Polling options
94
+ * @returns Promise that resolves when order is paid or rejects on timeout/failure
95
+ */
96
+ pollOrderStatus(statusUrl: string, options?: PollOptions): Promise<OrderStatus>;
97
+ }
98
+ /**
99
+ * Convenience function to create a PaymentUI instance
100
+ */
101
+ declare function createPaymentUI(): PaymentUI;
102
+
103
+ export { type OrderStatus, type PaymentEventData, type PaymentEventType, type PaymentParams, PaymentUI, type PaymentUIOptions, type PollOptions, createPaymentUI };