@youidian/pay-sdk 1.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/server.js ADDED
@@ -0,0 +1,168 @@
1
+ "use strict";
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
9
+ var __export = (target, all) => {
10
+ for (var name in all)
11
+ __defProp(target, name, { get: all[name], enumerable: true });
12
+ };
13
+ var __copyProps = (to, from, except, desc) => {
14
+ if (from && typeof from === "object" || typeof from === "function") {
15
+ for (let key of __getOwnPropNames(from))
16
+ if (!__hasOwnProp.call(to, key) && key !== except)
17
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
18
+ }
19
+ return to;
20
+ };
21
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
22
+ // If the importer is in node compatibility mode or this is not an ESM
23
+ // file that has been converted to a CommonJS file using a Babel-
24
+ // compatible transform (i.e. "__esModule" has not been set), then set
25
+ // "default" to the CommonJS "module.exports" for node compatibility.
26
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
27
+ mod
28
+ ));
29
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
30
+ var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
31
+
32
+ // src/server.ts
33
+ var server_exports = {};
34
+ __export(server_exports, {
35
+ PaymentClient: () => PaymentClient
36
+ });
37
+ module.exports = __toCommonJS(server_exports);
38
+ var import_crypto = __toESM(require("crypto"));
39
+ var PaymentClient = class {
40
+ constructor(options) {
41
+ __publicField(this, "appId");
42
+ __publicField(this, "appSecret");
43
+ __publicField(this, "baseUrl");
44
+ if (!options.appId) throw new Error("appId is required");
45
+ if (!options.appSecret) throw new Error("appSecret is required");
46
+ if (!options.baseUrl) throw new Error("baseUrl is required");
47
+ this.appId = options.appId;
48
+ this.appSecret = options.appSecret;
49
+ this.baseUrl = options.baseUrl.replace(/\/$/, "");
50
+ }
51
+ /**
52
+ * Generate SHA256 signature for the request
53
+ * Logic: SHA256(appId + appSecret + timestamp)
54
+ */
55
+ generateSignature(timestamp) {
56
+ const str = `${this.appId}${this.appSecret}${timestamp}`;
57
+ return import_crypto.default.createHash("sha256").update(str).digest("hex");
58
+ }
59
+ /**
60
+ * Internal request helper for Gateway API
61
+ */
62
+ async request(method, path, body) {
63
+ const timestamp = Date.now();
64
+ const signature = this.generateSignature(timestamp);
65
+ const url = `${this.baseUrl}/api/v1/gateway/${this.appId}${path}`;
66
+ const headers = {
67
+ "Content-Type": "application/json",
68
+ "X-Pay-Timestamp": timestamp.toString(),
69
+ "X-Pay-Sign": signature
70
+ };
71
+ const options = {
72
+ method,
73
+ headers,
74
+ body: body ? JSON.stringify(body) : void 0
75
+ };
76
+ const response = await fetch(url, options);
77
+ if (!response.ok) {
78
+ const errorText = await response.text();
79
+ throw new Error(`Payment SDK Error (${response.status}): ${errorText}`);
80
+ }
81
+ const json = await response.json();
82
+ if (json.error) {
83
+ throw new Error(`Payment API Error: ${json.error}`);
84
+ }
85
+ return json.data;
86
+ }
87
+ /**
88
+ * Decrypts the callback notification payload using AES-256-GCM.
89
+ * @param notification - The encrypted notification from payment webhook
90
+ * @returns Decrypted payment callback data
91
+ */
92
+ decryptCallback(notification) {
93
+ try {
94
+ const { iv, encryptedData, authTag } = notification;
95
+ const key = import_crypto.default.createHash("sha256").update(this.appSecret).digest();
96
+ const decipher = import_crypto.default.createDecipheriv(
97
+ "aes-256-gcm",
98
+ key,
99
+ Buffer.from(iv, "hex")
100
+ );
101
+ decipher.setAuthTag(Buffer.from(authTag, "hex"));
102
+ let decrypted = decipher.update(encryptedData, "hex", "utf8");
103
+ decrypted += decipher.final("utf8");
104
+ return JSON.parse(decrypted);
105
+ } catch (error) {
106
+ throw new Error("Failed to decrypt payment callback: Invalid secret or tampered data.");
107
+ }
108
+ }
109
+ /**
110
+ * Fetch products for the configured app.
111
+ */
112
+ async getProducts(options) {
113
+ const params = new URLSearchParams();
114
+ params.append("appId", this.appId);
115
+ if (options?.locale) params.append("locale", options.locale);
116
+ if (options?.currency) params.append("currency", options.currency);
117
+ const url = `${this.baseUrl}/api/v1/pay/products?${params.toString()}`;
118
+ const res = await fetch(url, {
119
+ method: "GET",
120
+ headers: {
121
+ "Content-Type": "application/json",
122
+ "Accept": "application/json"
123
+ }
124
+ });
125
+ if (!res.ok) {
126
+ throw new Error(`Failed to fetch products: ${res.status} ${res.statusText}`);
127
+ }
128
+ const json = await res.json();
129
+ return json.data?.products || [];
130
+ }
131
+ /**
132
+ * Create a new order
133
+ * @param params - Order creation parameters
134
+ * @returns Order details with payment parameters
135
+ */
136
+ async createOrder(params) {
137
+ return this.request("POST", "/orders", params);
138
+ }
139
+ /**
140
+ * Pay for an existing order
141
+ * @param orderId - The order ID to pay
142
+ * @param params - Payment parameters including channel
143
+ */
144
+ async payOrder(orderId, params) {
145
+ return this.request("POST", `/orders/${orderId}/pay`, params);
146
+ }
147
+ /**
148
+ * Query order status
149
+ * @param orderId - The order ID to query
150
+ */
151
+ async getOrderStatus(orderId) {
152
+ return this.request("GET", `/orders/${orderId}`);
153
+ }
154
+ /**
155
+ * Generate checkout URL for client-side payment
156
+ * @param productId - Product ID
157
+ * @param priceId - Price ID
158
+ * @returns Checkout page URL
159
+ */
160
+ getCheckoutUrl(productId, priceId) {
161
+ return `${this.baseUrl}/checkout/${this.appId}/${productId}/${priceId}`;
162
+ }
163
+ };
164
+ // Annotate the CommonJS export names for ESM import in node:
165
+ 0 && (module.exports = {
166
+ PaymentClient
167
+ });
168
+ //# sourceMappingURL=server.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/server.ts"],"sourcesContent":["/**\n * Youidian Payment SDK - Server Module\n * 用于服务端集成,包含签名、订单创建、回调解密等功能\n */\n\nimport crypto from \"crypto\";\n\n/**\n * Order status response\n */\nexport interface OrderStatus {\n orderId: string;\n status: 'PENDING' | 'PAID' | 'CANCELLED' | 'REFUNDED' | 'FAILED';\n paidAt?: string;\n channelTransactionId?: string;\n}\n\n/**\n * Product Entitlements\n */\nexport interface ProductEntitlements {\n [key: string]: any;\n}\n\n/**\n * Product Price\n */\nexport interface ProductPrice {\n id: string;\n currency: string;\n amount: number;\n displayAmount: string;\n locale: string | null;\n isDefault: boolean;\n}\n\n/**\n * Product Data\n */\nexport interface Product {\n id: string;\n code: string;\n type: string;\n name: string;\n description?: string;\n entitlements: ProductEntitlements;\n prices: ProductPrice[];\n}\n\n/**\n * SDK Client Options\n */\nexport interface PaymentClientOptions {\n /** Application ID (Required) */\n appId: string;\n /** Application Secret (Required for server-side operations) */\n appSecret: string;\n /** API Base URL (e.g. https://pay.youidian.com) */\n baseUrl: string;\n}\n\n/**\n * Create Order Parameters\n */\nexport interface CreateOrderParams {\n productId?: string;\n priceId?: string;\n channel?: string;\n userId: string;\n returnUrl?: string;\n metadata?: Record<string, any>;\n merchantOrderId?: string;\n openid?: string;\n}\n\n/**\n * Create Order Response\n */\nexport interface CreateOrderResponse {\n orderId: string;\n internalId: string;\n amount: number;\n currency: string;\n payParams: any;\n}\n\n/**\n * Payment Callback Notification\n */\nexport interface PaymentNotification {\n iv: string;\n encryptedData: string;\n authTag: string;\n}\n\n/**\n * Decrypted Payment Callback Data\n */\nexport interface PaymentCallbackData {\n orderId: string;\n merchantOrderId?: string;\n status: 'PAID' | 'CANCELLED' | 'REFUNDED' | 'FAILED';\n amount: number;\n currency: string;\n paidAt: string;\n channelTransactionId?: string;\n metadata?: Record<string, any>;\n}\n\n/**\n * Server-side Payment Client\n * 服务端支付客户端,用于创建订单、查询状态、解密回调\n */\nexport class PaymentClient {\n private appId: string;\n private appSecret: string;\n private baseUrl: string;\n\n constructor(options: PaymentClientOptions) {\n if (!options.appId) throw new Error(\"appId is required\");\n if (!options.appSecret) throw new Error(\"appSecret is required\");\n if (!options.baseUrl) throw new Error(\"baseUrl is required\");\n\n this.appId = options.appId;\n this.appSecret = options.appSecret;\n this.baseUrl = options.baseUrl.replace(/\\/$/, ''); // Remove trailing slash\n }\n\n /**\n * Generate SHA256 signature for the request\n * Logic: SHA256(appId + appSecret + timestamp)\n */\n private generateSignature(timestamp: number): string {\n const str = `${this.appId}${this.appSecret}${timestamp}`;\n return crypto.createHash(\"sha256\").update(str).digest(\"hex\");\n }\n\n /**\n * Internal request helper for Gateway API\n */\n private async request<T>(method: string, path: string, body?: any): Promise<T> {\n const timestamp = Date.now();\n const signature = this.generateSignature(timestamp);\n\n const url = `${this.baseUrl}/api/v1/gateway/${this.appId}${path}`;\n\n const headers: HeadersInit = {\n \"Content-Type\": \"application/json\",\n \"X-Pay-Timestamp\": timestamp.toString(),\n \"X-Pay-Sign\": signature,\n };\n\n const options: RequestInit = {\n method,\n headers,\n body: body ? JSON.stringify(body) : undefined,\n };\n\n const response = await fetch(url, options);\n\n if (!response.ok) {\n const errorText = await response.text();\n throw new Error(`Payment SDK Error (${response.status}): ${errorText}`);\n }\n\n const json = await response.json();\n if (json.error) {\n throw new Error(`Payment API Error: ${json.error}`);\n }\n\n return json.data as T;\n }\n\n /**\n * Decrypts the callback notification payload using AES-256-GCM.\n * @param notification - The encrypted notification from payment webhook\n * @returns Decrypted payment callback data\n */\n decryptCallback(notification: PaymentNotification): PaymentCallbackData {\n try {\n const { iv, encryptedData, authTag } = notification;\n const key = crypto.createHash('sha256').update(this.appSecret).digest();\n const decipher = crypto.createDecipheriv(\n 'aes-256-gcm',\n key,\n Buffer.from(iv, 'hex')\n );\n\n decipher.setAuthTag(Buffer.from(authTag, 'hex'));\n\n let decrypted = decipher.update(encryptedData, 'hex', 'utf8');\n decrypted += decipher.final('utf8');\n\n return JSON.parse(decrypted);\n } catch (error) {\n throw new Error(\"Failed to decrypt payment callback: Invalid secret or tampered data.\");\n }\n }\n\n /**\n * Fetch products for the configured app.\n */\n async getProducts(options?: {\n locale?: string;\n currency?: string;\n }): Promise<Product[]> {\n const params = new URLSearchParams();\n params.append('appId', this.appId);\n if (options?.locale) params.append('locale', options.locale);\n if (options?.currency) params.append('currency', options.currency);\n\n const url = `${this.baseUrl}/api/v1/pay/products?${params.toString()}`;\n\n const res = await fetch(url, {\n method: 'GET',\n headers: {\n 'Content-Type': 'application/json',\n 'Accept': 'application/json'\n }\n });\n\n if (!res.ok) {\n throw new Error(`Failed to fetch products: ${res.status} ${res.statusText}`);\n }\n\n const json = await res.json();\n return json.data?.products || [];\n }\n\n /**\n * Create a new order\n * @param params - Order creation parameters\n * @returns Order details with payment parameters\n */\n async createOrder(params: CreateOrderParams): Promise<CreateOrderResponse> {\n return this.request(\"POST\", \"/orders\", params);\n }\n\n /**\n * Pay for an existing order\n * @param orderId - The order ID to pay\n * @param params - Payment parameters including channel\n */\n async payOrder(orderId: string, params: {\n channel: string;\n returnUrl?: string;\n openid?: string;\n [key: string]: any;\n }): Promise<CreateOrderResponse> {\n return this.request(\"POST\", `/orders/${orderId}/pay`, params);\n }\n\n /**\n * Query order status\n * @param orderId - The order ID to query\n */\n async getOrderStatus(orderId: string): Promise<OrderStatus> {\n return this.request(\"GET\", `/orders/${orderId}`);\n }\n\n /**\n * Generate checkout URL for client-side payment\n * @param productId - Product ID\n * @param priceId - Price ID\n * @returns Checkout page URL\n */\n getCheckoutUrl(productId: string, priceId: string): string {\n return `${this.baseUrl}/checkout/${this.appId}/${productId}/${priceId}`;\n }\n}\n"],"mappings":"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","names":["crypto"]}
@@ -0,0 +1,135 @@
1
+ var __defProp = Object.defineProperty;
2
+ var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
3
+ var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
4
+
5
+ // src/server.ts
6
+ import crypto from "crypto";
7
+ var PaymentClient = class {
8
+ constructor(options) {
9
+ __publicField(this, "appId");
10
+ __publicField(this, "appSecret");
11
+ __publicField(this, "baseUrl");
12
+ if (!options.appId) throw new Error("appId is required");
13
+ if (!options.appSecret) throw new Error("appSecret is required");
14
+ if (!options.baseUrl) throw new Error("baseUrl is required");
15
+ this.appId = options.appId;
16
+ this.appSecret = options.appSecret;
17
+ this.baseUrl = options.baseUrl.replace(/\/$/, "");
18
+ }
19
+ /**
20
+ * Generate SHA256 signature for the request
21
+ * Logic: SHA256(appId + appSecret + timestamp)
22
+ */
23
+ generateSignature(timestamp) {
24
+ const str = `${this.appId}${this.appSecret}${timestamp}`;
25
+ return crypto.createHash("sha256").update(str).digest("hex");
26
+ }
27
+ /**
28
+ * Internal request helper for Gateway API
29
+ */
30
+ async request(method, path, body) {
31
+ const timestamp = Date.now();
32
+ const signature = this.generateSignature(timestamp);
33
+ const url = `${this.baseUrl}/api/v1/gateway/${this.appId}${path}`;
34
+ const headers = {
35
+ "Content-Type": "application/json",
36
+ "X-Pay-Timestamp": timestamp.toString(),
37
+ "X-Pay-Sign": signature
38
+ };
39
+ const options = {
40
+ method,
41
+ headers,
42
+ body: body ? JSON.stringify(body) : void 0
43
+ };
44
+ const response = await fetch(url, options);
45
+ if (!response.ok) {
46
+ const errorText = await response.text();
47
+ throw new Error(`Payment SDK Error (${response.status}): ${errorText}`);
48
+ }
49
+ const json = await response.json();
50
+ if (json.error) {
51
+ throw new Error(`Payment API Error: ${json.error}`);
52
+ }
53
+ return json.data;
54
+ }
55
+ /**
56
+ * Decrypts the callback notification payload using AES-256-GCM.
57
+ * @param notification - The encrypted notification from payment webhook
58
+ * @returns Decrypted payment callback data
59
+ */
60
+ decryptCallback(notification) {
61
+ try {
62
+ const { iv, encryptedData, authTag } = notification;
63
+ const key = crypto.createHash("sha256").update(this.appSecret).digest();
64
+ const decipher = crypto.createDecipheriv(
65
+ "aes-256-gcm",
66
+ key,
67
+ Buffer.from(iv, "hex")
68
+ );
69
+ decipher.setAuthTag(Buffer.from(authTag, "hex"));
70
+ let decrypted = decipher.update(encryptedData, "hex", "utf8");
71
+ decrypted += decipher.final("utf8");
72
+ return JSON.parse(decrypted);
73
+ } catch (error) {
74
+ throw new Error("Failed to decrypt payment callback: Invalid secret or tampered data.");
75
+ }
76
+ }
77
+ /**
78
+ * Fetch products for the configured app.
79
+ */
80
+ async getProducts(options) {
81
+ const params = new URLSearchParams();
82
+ params.append("appId", this.appId);
83
+ if (options?.locale) params.append("locale", options.locale);
84
+ if (options?.currency) params.append("currency", options.currency);
85
+ const url = `${this.baseUrl}/api/v1/pay/products?${params.toString()}`;
86
+ const res = await fetch(url, {
87
+ method: "GET",
88
+ headers: {
89
+ "Content-Type": "application/json",
90
+ "Accept": "application/json"
91
+ }
92
+ });
93
+ if (!res.ok) {
94
+ throw new Error(`Failed to fetch products: ${res.status} ${res.statusText}`);
95
+ }
96
+ const json = await res.json();
97
+ return json.data?.products || [];
98
+ }
99
+ /**
100
+ * Create a new order
101
+ * @param params - Order creation parameters
102
+ * @returns Order details with payment parameters
103
+ */
104
+ async createOrder(params) {
105
+ return this.request("POST", "/orders", params);
106
+ }
107
+ /**
108
+ * Pay for an existing order
109
+ * @param orderId - The order ID to pay
110
+ * @param params - Payment parameters including channel
111
+ */
112
+ async payOrder(orderId, params) {
113
+ return this.request("POST", `/orders/${orderId}/pay`, params);
114
+ }
115
+ /**
116
+ * Query order status
117
+ * @param orderId - The order ID to query
118
+ */
119
+ async getOrderStatus(orderId) {
120
+ return this.request("GET", `/orders/${orderId}`);
121
+ }
122
+ /**
123
+ * Generate checkout URL for client-side payment
124
+ * @param productId - Product ID
125
+ * @param priceId - Price ID
126
+ * @returns Checkout page URL
127
+ */
128
+ getCheckoutUrl(productId, priceId) {
129
+ return `${this.baseUrl}/checkout/${this.appId}/${productId}/${priceId}`;
130
+ }
131
+ };
132
+ export {
133
+ PaymentClient
134
+ };
135
+ //# sourceMappingURL=server.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/server.ts"],"sourcesContent":["/**\n * Youidian Payment SDK - Server Module\n * 用于服务端集成,包含签名、订单创建、回调解密等功能\n */\n\nimport crypto from \"crypto\";\n\n/**\n * Order status response\n */\nexport interface OrderStatus {\n orderId: string;\n status: 'PENDING' | 'PAID' | 'CANCELLED' | 'REFUNDED' | 'FAILED';\n paidAt?: string;\n channelTransactionId?: string;\n}\n\n/**\n * Product Entitlements\n */\nexport interface ProductEntitlements {\n [key: string]: any;\n}\n\n/**\n * Product Price\n */\nexport interface ProductPrice {\n id: string;\n currency: string;\n amount: number;\n displayAmount: string;\n locale: string | null;\n isDefault: boolean;\n}\n\n/**\n * Product Data\n */\nexport interface Product {\n id: string;\n code: string;\n type: string;\n name: string;\n description?: string;\n entitlements: ProductEntitlements;\n prices: ProductPrice[];\n}\n\n/**\n * SDK Client Options\n */\nexport interface PaymentClientOptions {\n /** Application ID (Required) */\n appId: string;\n /** Application Secret (Required for server-side operations) */\n appSecret: string;\n /** API Base URL (e.g. https://pay.youidian.com) */\n baseUrl: string;\n}\n\n/**\n * Create Order Parameters\n */\nexport interface CreateOrderParams {\n productId?: string;\n priceId?: string;\n channel?: string;\n userId: string;\n returnUrl?: string;\n metadata?: Record<string, any>;\n merchantOrderId?: string;\n openid?: string;\n}\n\n/**\n * Create Order Response\n */\nexport interface CreateOrderResponse {\n orderId: string;\n internalId: string;\n amount: number;\n currency: string;\n payParams: any;\n}\n\n/**\n * Payment Callback Notification\n */\nexport interface PaymentNotification {\n iv: string;\n encryptedData: string;\n authTag: string;\n}\n\n/**\n * Decrypted Payment Callback Data\n */\nexport interface PaymentCallbackData {\n orderId: string;\n merchantOrderId?: string;\n status: 'PAID' | 'CANCELLED' | 'REFUNDED' | 'FAILED';\n amount: number;\n currency: string;\n paidAt: string;\n channelTransactionId?: string;\n metadata?: Record<string, any>;\n}\n\n/**\n * Server-side Payment Client\n * 服务端支付客户端,用于创建订单、查询状态、解密回调\n */\nexport class PaymentClient {\n private appId: string;\n private appSecret: string;\n private baseUrl: string;\n\n constructor(options: PaymentClientOptions) {\n if (!options.appId) throw new Error(\"appId is required\");\n if (!options.appSecret) throw new Error(\"appSecret is required\");\n if (!options.baseUrl) throw new Error(\"baseUrl is required\");\n\n this.appId = options.appId;\n this.appSecret = options.appSecret;\n this.baseUrl = options.baseUrl.replace(/\\/$/, ''); // Remove trailing slash\n }\n\n /**\n * Generate SHA256 signature for the request\n * Logic: SHA256(appId + appSecret + timestamp)\n */\n private generateSignature(timestamp: number): string {\n const str = `${this.appId}${this.appSecret}${timestamp}`;\n return crypto.createHash(\"sha256\").update(str).digest(\"hex\");\n }\n\n /**\n * Internal request helper for Gateway API\n */\n private async request<T>(method: string, path: string, body?: any): Promise<T> {\n const timestamp = Date.now();\n const signature = this.generateSignature(timestamp);\n\n const url = `${this.baseUrl}/api/v1/gateway/${this.appId}${path}`;\n\n const headers: HeadersInit = {\n \"Content-Type\": \"application/json\",\n \"X-Pay-Timestamp\": timestamp.toString(),\n \"X-Pay-Sign\": signature,\n };\n\n const options: RequestInit = {\n method,\n headers,\n body: body ? JSON.stringify(body) : undefined,\n };\n\n const response = await fetch(url, options);\n\n if (!response.ok) {\n const errorText = await response.text();\n throw new Error(`Payment SDK Error (${response.status}): ${errorText}`);\n }\n\n const json = await response.json();\n if (json.error) {\n throw new Error(`Payment API Error: ${json.error}`);\n }\n\n return json.data as T;\n }\n\n /**\n * Decrypts the callback notification payload using AES-256-GCM.\n * @param notification - The encrypted notification from payment webhook\n * @returns Decrypted payment callback data\n */\n decryptCallback(notification: PaymentNotification): PaymentCallbackData {\n try {\n const { iv, encryptedData, authTag } = notification;\n const key = crypto.createHash('sha256').update(this.appSecret).digest();\n const decipher = crypto.createDecipheriv(\n 'aes-256-gcm',\n key,\n Buffer.from(iv, 'hex')\n );\n\n decipher.setAuthTag(Buffer.from(authTag, 'hex'));\n\n let decrypted = decipher.update(encryptedData, 'hex', 'utf8');\n decrypted += decipher.final('utf8');\n\n return JSON.parse(decrypted);\n } catch (error) {\n throw new Error(\"Failed to decrypt payment callback: Invalid secret or tampered data.\");\n }\n }\n\n /**\n * Fetch products for the configured app.\n */\n async getProducts(options?: {\n locale?: string;\n currency?: string;\n }): Promise<Product[]> {\n const params = new URLSearchParams();\n params.append('appId', this.appId);\n if (options?.locale) params.append('locale', options.locale);\n if (options?.currency) params.append('currency', options.currency);\n\n const url = `${this.baseUrl}/api/v1/pay/products?${params.toString()}`;\n\n const res = await fetch(url, {\n method: 'GET',\n headers: {\n 'Content-Type': 'application/json',\n 'Accept': 'application/json'\n }\n });\n\n if (!res.ok) {\n throw new Error(`Failed to fetch products: ${res.status} ${res.statusText}`);\n }\n\n const json = await res.json();\n return json.data?.products || [];\n }\n\n /**\n * Create a new order\n * @param params - Order creation parameters\n * @returns Order details with payment parameters\n */\n async createOrder(params: CreateOrderParams): Promise<CreateOrderResponse> {\n return this.request(\"POST\", \"/orders\", params);\n }\n\n /**\n * Pay for an existing order\n * @param orderId - The order ID to pay\n * @param params - Payment parameters including channel\n */\n async payOrder(orderId: string, params: {\n channel: string;\n returnUrl?: string;\n openid?: string;\n [key: string]: any;\n }): Promise<CreateOrderResponse> {\n return this.request(\"POST\", `/orders/${orderId}/pay`, params);\n }\n\n /**\n * Query order status\n * @param orderId - The order ID to query\n */\n async getOrderStatus(orderId: string): Promise<OrderStatus> {\n return this.request(\"GET\", `/orders/${orderId}`);\n }\n\n /**\n * Generate checkout URL for client-side payment\n * @param productId - Product ID\n * @param priceId - Price ID\n * @returns Checkout page URL\n */\n getCheckoutUrl(productId: string, priceId: string): string {\n return `${this.baseUrl}/checkout/${this.appId}/${productId}/${priceId}`;\n }\n}\n"],"mappings":"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","names":[]}
package/package.json ADDED
@@ -0,0 +1,54 @@
1
+ {
2
+ "name": "@youidian/pay-sdk",
3
+ "version": "1.0.0",
4
+ "description": "Youidian Payment SDK - 统一支付集成 SDK",
5
+ "author": "Youidian",
6
+ "license": "MIT",
7
+ "main": "./dist/index.js",
8
+ "module": "./dist/index.mjs",
9
+ "types": "./dist/index.d.ts",
10
+ "publishConfig": {
11
+ "access": "public"
12
+ },
13
+ "exports": {
14
+ ".": {
15
+ "types": "./dist/index.d.ts",
16
+ "import": "./dist/index.mjs",
17
+ "require": "./dist/index.js"
18
+ },
19
+ "./client": {
20
+ "types": "./dist/client.d.ts",
21
+ "import": "./dist/client.mjs",
22
+ "require": "./dist/client.js"
23
+ },
24
+ "./server": {
25
+ "types": "./dist/server.d.ts",
26
+ "import": "./dist/server.mjs",
27
+ "require": "./dist/server.js"
28
+ }
29
+ },
30
+ "files": [
31
+ "dist"
32
+ ],
33
+ "scripts": {
34
+ "build": "tsup",
35
+ "prepublishOnly": "npm run build"
36
+ },
37
+ "dependencies": {},
38
+ "devDependencies": {
39
+ "@repo/typescript-config": "*",
40
+ "tsup": "^8.0.0",
41
+ "typescript": "^5.9.3"
42
+ },
43
+ "keywords": [
44
+ "payment",
45
+ "sdk",
46
+ "paypal",
47
+ "wechat-pay",
48
+ "alipay"
49
+ ],
50
+ "repository": {
51
+ "type": "git",
52
+ "url": "https://github.com/youidian/youidian-pay"
53
+ }
54
+ }