UNPKG

paystream

Version:

PayStream V2 - 现代化的 TypeScript 统一支付回调处理库,采用面向对象架构

1,545 lines (1,539 loc) 96.7 kB
import { createDecipheriv, createVerify, createSign, randomBytes, createHmac, createHash } from 'crypto'; var __defProp = Object.defineProperty; var __getOwnPropDesc = Object.getOwnPropertyDescriptor; var __getOwnPropNames = Object.getOwnPropertyNames; var __hasOwnProp = Object.prototype.hasOwnProperty; var __esm = (fn, res) => function __init() { return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res; }; var __export = (target, all) => { for (var name in all) __defProp(target, name, { get: all[name], enumerable: true }); }; var __copyProps = (to, from, except, desc) => { if (from && typeof from === "object" || typeof from === "function") { for (let key of __getOwnPropNames(from)) if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); } return to; }; var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); // src/types/payment.ts var PaymentErrorCode, PaymentError, ExtendedPaymentErrorCode, PaymentMethodEnum; var init_payment = __esm({ "src/types/payment.ts"() { PaymentErrorCode = /* @__PURE__ */ ((PaymentErrorCode2) => { PaymentErrorCode2["VERIFY_FAILED"] = "VERIFY_FAILED"; PaymentErrorCode2["DECRYPT_FAILED"] = "DECRYPT_FAILED"; PaymentErrorCode2["CONFIG_ERROR"] = "CONFIG_ERROR"; PaymentErrorCode2["NETWORK_ERROR"] = "NETWORK_ERROR"; PaymentErrorCode2["UNKNOWN_PROVIDER"] = "UNKNOWN_PROVIDER"; PaymentErrorCode2["INVALID_PARAMS"] = "INVALID_PARAMS"; return PaymentErrorCode2; })(PaymentErrorCode || {}); PaymentError = class extends Error { constructor(code, message, details) { super(message); this.code = code; this.details = details; this.name = "PaymentError"; } }; ExtendedPaymentErrorCode = /* @__PURE__ */ ((ExtendedPaymentErrorCode2) => { ExtendedPaymentErrorCode2["VERIFY_FAILED"] = "VERIFY_FAILED"; ExtendedPaymentErrorCode2["DECRYPT_FAILED"] = "DECRYPT_FAILED"; ExtendedPaymentErrorCode2["CONFIG_ERROR"] = "CONFIG_ERROR"; ExtendedPaymentErrorCode2["NETWORK_ERROR"] = "NETWORK_ERROR"; ExtendedPaymentErrorCode2["UNKNOWN_PROVIDER"] = "UNKNOWN_PROVIDER"; ExtendedPaymentErrorCode2["INVALID_PARAMS"] = "INVALID_PARAMS"; ExtendedPaymentErrorCode2["INVALID_PARAMETER"] = "INVALID_PARAMETER"; ExtendedPaymentErrorCode2["CALLBACK_ERROR"] = "CALLBACK_ERROR"; ExtendedPaymentErrorCode2["CREATE_ORDER_FAILED"] = "CREATE_ORDER_FAILED"; ExtendedPaymentErrorCode2["QUERY_ORDER_FAILED"] = "QUERY_ORDER_FAILED"; ExtendedPaymentErrorCode2["REFUND_FAILED"] = "REFUND_FAILED"; return ExtendedPaymentErrorCode2; })(ExtendedPaymentErrorCode || {}); PaymentMethodEnum = /* @__PURE__ */ ((PaymentMethodEnum2) => { PaymentMethodEnum2["WECHAT_NATIVE"] = "wechat.native"; PaymentMethodEnum2["WECHAT_JSAPI"] = "wechat.jsapi"; PaymentMethodEnum2["WECHAT_H5"] = "wechat.h5"; PaymentMethodEnum2["WECHAT_APP"] = "wechat.app"; PaymentMethodEnum2["ALIPAY_QR"] = "alipay.qrcode"; PaymentMethodEnum2["ALIPAY_PC"] = "alipay.pc"; PaymentMethodEnum2["ALIPAY_H5"] = "alipay.h5"; PaymentMethodEnum2["ALIPAY_APP"] = "alipay.app"; return PaymentMethodEnum2; })(PaymentMethodEnum || {}); } }); // src/providers/base/BaseProvider.ts var BaseProvider; var init_BaseProvider = __esm({ "src/providers/base/BaseProvider.ts"() { init_payment(); BaseProvider = class { constructor(config, providerName) { this.config = config; this.providerName = providerName; this.validateConfig(); } /** * 获取提供商名称 */ getProviderName() { return this.providerName; } /** * 获取配置 */ getConfig() { return { ...this.config }; } /** * 更新配置 * @param newConfig 新配置 */ updateConfig(newConfig) { this.config = { ...this.config, ...newConfig }; this.validateConfig(); } /** * 检查是否启用 */ isEnabled() { return this.config.enabled; } /** * 处理支付回调通知(主入口) * @param payload 回调载荷 * @returns 统一格式的支付通知 */ async handleNotify(payload) { if (!this.isEnabled()) { throw new PaymentError( "CONFIG_ERROR" /* CONFIG_ERROR */, `${this.providerName} \u652F\u4ED8\u63D0\u4F9B\u5546\u672A\u542F\u7528` ); } try { this.validatePayload(payload); const verifyResult = await this.verifySignature(payload); if (!verifyResult.success) { throw new PaymentError( "VERIFY_FAILED" /* VERIFY_FAILED */, `${this.providerName} \u7B7E\u540D\u9A8C\u8BC1\u5931\u8D25: ${verifyResult.error || "\u672A\u77E5\u9519\u8BEF"}` ); } const notification = await this.transformNotification(payload); await this.postProcess(notification, payload); return notification; } catch (error) { if (error instanceof PaymentError) { throw error; } throw new PaymentError( "UNKNOWN_PROVIDER" /* UNKNOWN_PROVIDER */, `${this.providerName} \u56DE\u8C03\u5904\u7406\u5931\u8D25`, error ); } } /** * 后处理逻辑(可选重写) * @param notification 转换后的通知 * @param originalPayload 原始载荷 */ async postProcess(_notification, _originalPayload) { } /** * 验证支付方式是否支持 * @param method 支付方式 * @returns 是否支持 */ isSupportedMethod(method) { return this.getSupportedMethods().includes(method); } /** * 获取 Provider 状态信息 */ getStatus() { return { providerName: this.providerName, enabled: this.isEnabled(), sandbox: this.config.sandbox || false, supportedMethods: this.getSupportedMethods() }; } }; } }); function aesGcmDecrypt(ciphertext, nonce, associatedData, apiV3Key) { try { const buffer = Buffer.from(ciphertext, "base64"); const authTag = buffer.slice(buffer.length - 16); const data = buffer.slice(0, buffer.length - 16); const decipher = createDecipheriv("aes-256-gcm", apiV3Key, nonce); decipher.setAuthTag(authTag); decipher.setAAD(Buffer.from(associatedData)); const decrypted = Buffer.concat([decipher.update(data), decipher.final()]); return decrypted.toString("utf8"); } catch (error) { throw new PaymentError( "DECRYPT_FAILED" /* DECRYPT_FAILED */, "\u5FAE\u4FE1\u652F\u4ED8\u56DE\u8C03\u6570\u636E\u89E3\u5BC6\u5931\u8D25", error ); } } function verifyWithRSA2(data, signature, publicKey) { try { const verify = createVerify("RSA-SHA256"); verify.write(data); verify.end(); const formattedKey = formatPublicKey(publicKey); return verify.verify(formattedKey, signature, "base64"); } catch (error) { throw new PaymentError( "VERIFY_FAILED" /* VERIFY_FAILED */, "\u652F\u4ED8\u5B9D\u56DE\u8C03\u9A8C\u7B7E\u5931\u8D25", error ); } } function verifyWithRSA(data, signature, publicKey) { try { const verify = createVerify("RSA-SHA1"); verify.write(data); verify.end(); const formattedKey = formatPublicKey(publicKey); return verify.verify(formattedKey, signature, "base64"); } catch (error) { throw new PaymentError( "VERIFY_FAILED" /* VERIFY_FAILED */, "\u652F\u4ED8\u5B9D\u56DE\u8C03\u9A8C\u7B7E\u5931\u8D25", error ); } } function formatPublicKey(publicKey) { if (publicKey.includes("-----BEGIN PUBLIC KEY-----")) { return publicKey; } return `-----BEGIN PUBLIC KEY----- ${publicKey} -----END PUBLIC KEY-----`; } function signWithRSA(data, privateKey, algorithm = "RSA-SHA256") { try { const sign = createSign(algorithm); sign.write(data); sign.end(); const formattedKey = formatPrivateKey(privateKey); return sign.sign(formattedKey, "base64"); } catch (error) { throw new PaymentError( "CONFIG_ERROR" /* CONFIG_ERROR */, "\u751F\u6210\u7B7E\u540D\u5931\u8D25", error ); } } function formatPrivateKey(privateKey) { if (privateKey.includes("-----BEGIN PRIVATE KEY-----")) { return privateKey; } return `-----BEGIN PRIVATE KEY----- ${privateKey} -----END PRIVATE KEY-----`; } function verifyWechatSignature(timestamp, nonce, body, signature, publicKey) { try { const message = `${timestamp} ${nonce} ${body} `; const verify = createVerify("RSA-SHA256"); verify.write(message); verify.end(); const formattedKey = formatPublicKey(publicKey); return verify.verify(formattedKey, signature, "base64"); } catch (error) { throw new PaymentError( "VERIFY_FAILED" /* VERIFY_FAILED */, "\u5FAE\u4FE1\u652F\u4ED8\u7B7E\u540D\u9A8C\u8BC1\u5931\u8D25", error ); } } function generateNonce(length = 32) { const chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; let result = ""; for (let i = 0; i < length; i++) { result += chars.charAt(Math.floor(Math.random() * chars.length)); } return result; } function generateTimestamp() { return Math.floor(Date.now() / 1e3); } function generateOrderNo(prefix = "") { const timestamp = Date.now(); const random = randomBytes(4).toString("hex").toUpperCase(); return `${prefix}${timestamp}${random}`; } function formatAmount(amount) { return (amount / 100).toFixed(2); } function parseAmount(amount) { const num = typeof amount === "string" ? parseFloat(amount) : amount; return Math.round(num * 100); } function validateOrderNo(orderNo) { const regex = /^[a-zA-Z0-9_-]{6,32}$/; return regex.test(orderNo); } function generateUUID() { return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, function(c) { const r = Math.random() * 16 | 0; const v = c === "x" ? r : r & 3 | 8; return v.toString(16); }); } function safeCompare(a, b) { if (a.length !== b.length) { return false; } let result = 0; for (let i = 0; i < a.length; i++) { result |= a.charCodeAt(i) ^ b.charCodeAt(i); } return result === 0; } function base64UrlEncode(str) { return Buffer.from(str, "utf8").toString("base64").replace(/\+/g, "-").replace(/\//g, "_").replace(/=/g, ""); } function base64UrlDecode(str) { const padding = "=".repeat((4 - str.length % 4) % 4); const base64 = str.replace(/-/g, "+").replace(/_/g, "/") + padding; return Buffer.from(base64, "base64").toString("utf8"); } var init_crypto = __esm({ "src/utils/crypto.ts"() { init_payment(); } }); // src/utils/http.ts var HttpClient; var init_http = __esm({ "src/utils/http.ts"() { init_payment(); HttpClient = class { constructor(timeout = 3e4) { this.timeout = timeout; } /** * 发送 HTTP 请求 */ async request(config) { const { url, method, headers = {}, data, timeout = this.timeout } = config; try { const controller = new AbortController(); const timeoutId = setTimeout(() => controller.abort(), timeout); const fetchOptions = { method, headers: { "Content-Type": "application/json", ...headers }, signal: controller.signal }; if (data && (method === "POST" || method === "PUT")) { fetchOptions.body = typeof data === "string" ? data : JSON.stringify(data); } const response = await fetch(url, fetchOptions); clearTimeout(timeoutId); const responseHeaders = {}; response.headers.forEach((value, key) => { responseHeaders[key] = value; }); let responseData; const contentType = response.headers.get("content-type"); if (contentType && contentType.includes("application/json")) { responseData = await response.json(); } else { responseData = await response.text(); } return { status: response.status, statusText: response.statusText, headers: responseHeaders, data: responseData }; } catch (error) { if (error instanceof Error) { if (error.name === "AbortError") { throw new PaymentError( "NETWORK_ERROR" /* NETWORK_ERROR */, "\u8BF7\u6C42\u8D85\u65F6", error ); } throw new PaymentError( "NETWORK_ERROR" /* NETWORK_ERROR */, `\u7F51\u7EDC\u8BF7\u6C42\u5931\u8D25: ${error.message}`, error ); } throw new PaymentError( "NETWORK_ERROR" /* NETWORK_ERROR */, "\u672A\u77E5\u7F51\u7EDC\u9519\u8BEF", error ); } } /** * GET 请求 */ async get(url, headers) { return this.request({ url, method: "GET", headers }); } /** * POST 请求 */ async post(url, data, headers) { return this.request({ url, method: "POST", data, headers }); } }; } }); function generateJSAPIPayParams(appId, prepayId, privateKey) { const timeStamp = generateTimestamp().toString(); const nonceStr = generateNonce(); const packageStr = `prepay_id=${prepayId}`; const signType = "RSA"; const signString = [appId, timeStamp, nonceStr, packageStr].join("\n") + "\n"; const sign = createSign("RSA-SHA256"); sign.update(signString, "utf8"); const paySign = sign.sign(privateKey, "base64"); return { appId, timeStamp, nonceStr, package: packageStr, signType, paySign }; } var WECHAT_PAY_API_BASE, WechatPayV3Signer, WechatPayV3Client; var init_wechat = __esm({ "src/utils/wechat.ts"() { init_payment(); init_crypto(); init_http(); WECHAT_PAY_API_BASE = "https://api.mch.weixin.qq.com"; WechatPayV3Signer = class { constructor(privateKey, mchId, serialNo) { this.privateKey = privateKey; this.mchId = mchId; this.serialNo = serialNo; } /** * 生成微信支付V3签名 */ generateSignature(method, url, timestamp, nonce, body) { const signString = [method, url, timestamp, nonce, body].join("\n") + "\n"; try { const sign = createSign("RSA-SHA256"); sign.update(signString, "utf8"); return sign.sign(this.privateKey, "base64"); } catch (error) { throw new PaymentError( "CONFIG_ERROR" /* CONFIG_ERROR */, "\u5FAE\u4FE1\u652F\u4ED8\u7B7E\u540D\u751F\u6210\u5931\u8D25", error ); } } /** * 生成Authorization头 */ generateAuthorizationHeader(method, url, body = "") { const timestamp = generateTimestamp().toString(); const nonce = generateNonce(); const signature = this.generateSignature( method, url, timestamp, nonce, body ); return [ `WECHATPAY2-SHA256-RSA2048 mchid="${this.mchId}"`, `nonce_str="${nonce}"`, `signature="${signature}"`, `timestamp="${timestamp}"`, `serial_no="${this.serialNo}"` ].join(","); } }; WechatPayV3Client = class { constructor(privateKey, mchId, serialNo, sandbox = false) { this.httpClient = new HttpClient(); this.signer = new WechatPayV3Signer(privateKey, mchId, serialNo); this.sandbox = sandbox; } /** * 获取API基础URL */ getBaseUrl() { return this.sandbox ? "https://api.mch.weixin.qq.com" : WECHAT_PAY_API_BASE; } /** * 发送微信支付API请求 */ async request(method, path, data) { const url = `${this.getBaseUrl()}${path}`; const body = data ? JSON.stringify(data) : ""; const headers = { Authorization: this.signer.generateAuthorizationHeader( method, path, body ), "Content-Type": "application/json", "User-Agent": "PayStream/2.0.0" }; try { const response = await this.httpClient.request({ url, method, headers, data: data || void 0 }); if (response.status >= 400) { if (response.data && typeof response.data === "object" && "code" in response.data && "message" in response.data) { throw new PaymentError( "NETWORK_ERROR" /* NETWORK_ERROR */, `\u5FAE\u4FE1\u652F\u4ED8API\u8BF7\u6C42\u5931\u8D25: ${response.data.code} ${response.data.message}`, response.data.message ); } throw new PaymentError( "NETWORK_ERROR" /* NETWORK_ERROR */, `\u5FAE\u4FE1\u652F\u4ED8API\u8BF7\u6C42\u5931\u8D25: ${response.status} ${response.statusText}`, response.data ); } return response.data; } catch (error) { if (error instanceof PaymentError) { throw error; } throw new PaymentError( "NETWORK_ERROR" /* NETWORK_ERROR */, "\u5FAE\u4FE1\u652F\u4ED8API\u8BF7\u6C42\u5F02\u5E38", error ); } } /** * Native支付下单 */ async createNativeOrder(params) { return this.request( "POST", "/v3/pay/transactions/native", params ); } /** * JSAPI支付下单 */ async createJSAPIOrder(params) { return this.request( "POST", "/v3/pay/transactions/jsapi", params ); } /** * H5支付下单 */ async createH5Order(params) { return this.request( "POST", "/v3/pay/transactions/h5", params ); } /** * 查询订单(通过商户订单号) */ async queryOrderByOutTradeNo(outTradeNo, mchId) { return this.request( "GET", `/v3/pay/transactions/out-trade-no/${outTradeNo}?mchid=${mchId}` ); } /** * 查询订单(通过微信支付订单号) */ async queryOrderByTransactionId(transactionId, mchId) { return this.request( "GET", `/v3/pay/transactions/id/${transactionId}?mchid=${mchId}` ); } /** * 申请退款 */ async createRefund(params) { return this.request( "POST", "/v3/refund/domestic/refunds", params ); } /** * 查询退款 */ async queryRefund(outRefundNo) { return this.request( "GET", `/v3/refund/domestic/refunds/${outRefundNo}` ); } }; } }); var WECHAT_PAY_V2_API_BASE, WechatPayV2Client; var init_wechat_v2 = __esm({ "src/utils/wechat-v2.ts"() { init_payment(); init_crypto(); init_http(); WECHAT_PAY_V2_API_BASE = "https://api.mch.weixin.qq.com"; WechatPayV2Client = class { constructor(appId, mchId, apiKey, signType = "MD5", sandbox = false) { this.httpClient = new HttpClient(); this.sandbox = sandbox; this.appId = appId; this.mchId = mchId; this.apiKey = apiKey; this.signType = signType; } /** * 获取API基础URL */ getBaseUrl() { return this.sandbox ? WECHAT_PAY_V2_API_BASE : WECHAT_PAY_V2_API_BASE; } /** * 生成微信支付V2签名 */ generateSignature(params) { const filteredParams = Object.keys(params).filter( (key) => params[key] !== void 0 && params[key] !== "" && key !== "sign" ).sort().reduce( (obj, key) => { obj[key] = params[key]; return obj; }, {} ); const signString = Object.keys(filteredParams).map((key) => `${key}=${filteredParams[key]}`).join("&") + `&key=${this.apiKey}`; if (this.signType === "HMAC-SHA256") { return createHmac("sha256", this.apiKey).update(signString, "utf8").digest("hex").toUpperCase(); } else { return createHash("md5").update(signString, "utf8").digest("hex").toUpperCase(); } } /** * 验证微信支付V2签名 */ verifySignature(params) { const receivedSign = params.sign; const calculatedSign = this.generateSignature(params); return receivedSign === calculatedSign; } /** * 将对象转换为XML格式 */ objectToXml(obj) { let xml = "<xml>"; for (const [key, value] of Object.entries(obj)) { if (value !== void 0 && value !== "") { xml += `<${key}><![CDATA[${value}]]></${key}>`; } } xml += "</xml>"; return xml; } /** * 将XML转换为对象 */ xmlToObject(xml) { const obj = {}; const regex = /<(\w+)>(?:<!\[CDATA\[)?(.*?)(?:\]\]>)?<\/\1>/g; let match; while ((match = regex.exec(xml)) !== null) { const key = match[1]; const value = match[2]; obj[key] = value; } return obj; } /** * 发送微信支付V2 API请求 */ async request(path, data) { const url = `${this.getBaseUrl()}${path}`; const xmlData = this.objectToXml(data); try { const response = await this.httpClient.post(url, xmlData, { "Content-Type": "application/xml", "User-Agent": "PayStream/2.0.0" }); if (response.status >= 400) { throw new PaymentError( "NETWORK_ERROR" /* NETWORK_ERROR */, `\u5FAE\u4FE1\u652F\u4ED8V2 API\u8BF7\u6C42\u5931\u8D25: ${response.status} ${response.statusText}`, response.data ); } const result = this.xmlToObject(response.data); if (result.return_code === "SUCCESS" && !this.verifySignature(result)) { throw new PaymentError( "VERIFY_FAILED" /* VERIFY_FAILED */, "\u5FAE\u4FE1\u652F\u4ED8V2\u54CD\u5E94\u7B7E\u540D\u9A8C\u8BC1\u5931\u8D25", result ); } return result; } catch (error) { if (error instanceof PaymentError) { throw error; } throw new PaymentError( "NETWORK_ERROR" /* NETWORK_ERROR */, "\u5FAE\u4FE1\u652F\u4ED8V2 API\u8BF7\u6C42\u5F02\u5E38", error ); } } /** * 付款码支付 */ async micropay(params) { const requestData = { appid: this.appId, mch_id: this.mchId, nonce_str: generateNonce(), sign_type: this.signType, body: params.body, out_trade_no: params.out_trade_no, total_fee: params.total_fee, spbill_create_ip: params.spbill_create_ip, auth_code: params.auth_code, device_info: params.device_info, detail: params.detail, attach: params.attach, fee_type: params.fee_type || "CNY", goods_tag: params.goods_tag, limit_pay: params.limit_pay, time_start: params.time_start, time_expire: params.time_expire, receipt: params.receipt, profit_sharing: params.profit_sharing, scene_info: params.scene_info ? JSON.stringify(params.scene_info) : void 0, sign: "" // 临时占位,下面会生成真实签名 }; requestData.sign = this.generateSignature(requestData); return this.request("/pay/micropay", requestData); } /** * 查询订单 */ async queryOrder(params) { if (!params.transaction_id && !params.out_trade_no) { throw new PaymentError( "INVALID_PARAMS" /* INVALID_PARAMS */, "\u5FAE\u4FE1\u652F\u4ED8\u8BA2\u5355\u53F7\u548C\u5546\u6237\u8BA2\u5355\u53F7\u4E0D\u80FD\u540C\u65F6\u4E3A\u7A7A" ); } const requestData = { appid: this.appId, mch_id: this.mchId, transaction_id: params.transaction_id, out_trade_no: params.out_trade_no, nonce_str: generateNonce(), sign_type: this.signType, sign: "" // 临时占位,下面会生成真实签名 }; requestData.sign = this.generateSignature(requestData); return this.request( "/pay/orderquery", requestData ); } /** * 撤销订单 */ async reverseOrder(params) { if (!params.transaction_id && !params.out_trade_no) { throw new PaymentError( "INVALID_PARAMS" /* INVALID_PARAMS */, "\u5FAE\u4FE1\u652F\u4ED8\u8BA2\u5355\u53F7\u548C\u5546\u6237\u8BA2\u5355\u53F7\u4E0D\u80FD\u540C\u65F6\u4E3A\u7A7A" ); } const requestData = { appid: this.appId, mch_id: this.mchId, transaction_id: params.transaction_id, out_trade_no: params.out_trade_no, nonce_str: generateNonce(), sign_type: this.signType, sign: "" // 临时占位,下面会生成真实签名 }; requestData.sign = this.generateSignature(requestData); return this.request( "/secapi/pay/reverse", requestData ); } /** * 处理付款码支付流程(包含重试逻辑) */ async processMicropay(params) { try { const result = await this.micropay(params); if (result.return_code === "SUCCESS" && result.result_code === "SUCCESS") { return result; } if (result.err_code === "SYSTEMERROR") { await this.sleep(5e3); const queryResult = await this.queryOrder({ out_trade_no: params.out_trade_no }); if (queryResult.return_code === "SUCCESS" && queryResult.result_code === "SUCCESS") { return queryResult; } if (queryResult.trade_state === "NOTPAY" || queryResult.trade_state === "PAYERROR") { await this.reverseOrder({ out_trade_no: params.out_trade_no }); } throw new PaymentError( "NETWORK_ERROR" /* NETWORK_ERROR */, "\u7CFB\u7EDF\u9519\u8BEF\uFF0C\u652F\u4ED8\u72B6\u6001\u4E0D\u660E\u786E", result ); } if (result.err_code === "USERPAYING") { return this.pollPaymentResult(params.out_trade_no, 45e3); } throw new PaymentError( "NETWORK_ERROR" /* NETWORK_ERROR */, result.err_code_des || result.return_msg || "\u4ED8\u6B3E\u7801\u652F\u4ED8\u5931\u8D25", result ); } catch (error) { if (error instanceof PaymentError) { throw error; } throw new PaymentError( "NETWORK_ERROR" /* NETWORK_ERROR */, "\u4ED8\u6B3E\u7801\u652F\u4ED8\u5F02\u5E38", error ); } } /** * 轮询支付结果 */ async pollPaymentResult(outTradeNo, timeout = 45e3) { const startTime = Date.now(); const interval = 1e4; while (Date.now() - startTime < timeout) { await this.sleep(interval); try { const queryResult = await this.queryOrder({ out_trade_no: outTradeNo }); if (queryResult.return_code === "SUCCESS" && queryResult.result_code === "SUCCESS") { return queryResult; } if (queryResult.trade_state === "PAYERROR" || queryResult.trade_state === "CLOSED") { throw new PaymentError( "NETWORK_ERROR" /* NETWORK_ERROR */, queryResult.trade_state_desc || "\u652F\u4ED8\u5931\u8D25", queryResult ); } } catch (error) { console.warn("\u67E5\u8BE2\u8BA2\u5355\u5F02\u5E38:", error); } } try { await this.reverseOrder({ out_trade_no: outTradeNo }); } catch (error) { console.warn("\u64A4\u9500\u8BA2\u5355\u5931\u8D25:", error); } throw new PaymentError( "NETWORK_ERROR" /* NETWORK_ERROR */, "\u652F\u4ED8\u8D85\u65F6\uFF0C\u8BA2\u5355\u5DF2\u64A4\u9500", { out_trade_no: outTradeNo } ); } /** * 延时函数 */ sleep(ms) { return new Promise((resolve) => setTimeout(resolve, ms)); } }; } }); // src/providers/wechat/WechatProvider.ts var WechatProvider_exports = {}; __export(WechatProvider_exports, { WechatProvider: () => WechatProvider }); var WechatProvider; var init_WechatProvider = __esm({ "src/providers/wechat/WechatProvider.ts"() { init_BaseProvider(); init_payment(); init_crypto(); init_wechat(); init_wechat_v2(); WechatProvider = class extends BaseProvider { constructor(config) { super(config, "wechat"); this.supportedMethods = [ "native", "jsapi", "h5", "app" ]; this.wechatClient = new WechatPayV3Client( config.privateKey, config.mchId, config.serialNo, config.sandbox ); } /** * 验证配置 */ validateConfig() { const required = ["appId", "mchId", "apiV3Key", "privateKey", "serialNo"]; for (const key of required) { if (!this.config[key]) { throw new PaymentError( "CONFIG_ERROR" /* CONFIG_ERROR */, `\u5FAE\u4FE1\u652F\u4ED8\u914D\u7F6E\u7F3A\u5C11\u5FC5\u8981\u53C2\u6570: ${key}` ); } } if (!this.config.appId.startsWith("wx")) { throw new PaymentError( "CONFIG_ERROR" /* CONFIG_ERROR */, '\u5FAE\u4FE1\u652F\u4ED8 appId \u683C\u5F0F\u9519\u8BEF\uFF0C\u5E94\u4EE5 "wx" \u5F00\u5934' ); } if (!/^\d{10}$/.test(this.config.mchId)) { throw new PaymentError( "CONFIG_ERROR" /* CONFIG_ERROR */, "\u5FAE\u4FE1\u652F\u4ED8\u5546\u6237\u53F7\u683C\u5F0F\u9519\u8BEF\uFF0C\u5E94\u4E3A10\u4F4D\u6570\u5B57" ); } } /** * 验证载荷格式 */ validatePayload(payload) { if (!payload.raw) { throw new PaymentError( "INVALID_PARAMS" /* INVALID_PARAMS */, "\u5FAE\u4FE1\u652F\u4ED8\u56DE\u8C03\u6570\u636E\u4E3A\u7A7A" ); } const notification = payload.raw; if (!notification.id || !notification.resource) { throw new PaymentError( "INVALID_PARAMS" /* INVALID_PARAMS */, "\u5FAE\u4FE1\u652F\u4ED8\u56DE\u8C03\u6570\u636E\u683C\u5F0F\u9519\u8BEF" ); } const { ciphertext, nonce, associated_data } = notification.resource; if (!ciphertext || !nonce || !associated_data) { throw new PaymentError( "INVALID_PARAMS" /* INVALID_PARAMS */, "\u5FAE\u4FE1\u652F\u4ED8\u56DE\u8C03\u7F3A\u5C11\u52A0\u5BC6\u6570\u636E" ); } } /** * 验证签名 */ async verifySignature(payload) { try { const { headers, raw } = payload; if (!headers) { return { success: false, error: "\u7F3A\u5C11\u8BF7\u6C42\u5934\u4FE1\u606F" }; } const timestamp = headers["wechatpay-timestamp"]; const nonce = headers["wechatpay-nonce"]; const signature = headers["wechatpay-signature"]; const serial = headers["wechatpay-serial"]; if (!timestamp || !nonce || !signature || !serial) { return { success: false, error: "\u7F3A\u5C11\u5FC5\u8981\u7684\u7B7E\u540D\u5934\u90E8\u4FE1\u606F" }; } const body = typeof raw === "string" ? raw : JSON.stringify(raw); const isValid = verifyWechatSignature( timestamp, nonce, body, signature, this.config.platformCertificate || "" ); return { success: isValid, error: isValid ? void 0 : "\u7B7E\u540D\u9A8C\u8BC1\u5931\u8D25", details: { timestamp, nonce, serial } }; } catch (error) { return { success: false, error: `\u7B7E\u540D\u9A8C\u8BC1\u8FC7\u7A0B\u51FA\u9519: ${error}` }; } } /** * 转换通知数据为统一格式 */ async transformNotification(payload) { const notification = payload.raw; const decryptedData = aesGcmDecrypt( notification.resource.ciphertext, this.config.apiV3Key, notification.resource.nonce, notification.resource.associated_data ); const transactionData = JSON.parse(decryptedData); const result = { provider: "wechat", tradeStatus: this.mapTradeStatus(transactionData.trade_state), outTradeNo: transactionData.out_trade_no, tradeNo: transactionData.transaction_id, totalAmount: transactionData.amount.total, payerId: transactionData.payer.openid, raw: transactionData, timestamp: Date.now() }; return result; } /** * 映射微信支付交易状态到统一状态 */ mapTradeStatus(tradeState) { switch (tradeState) { case "SUCCESS": return "SUCCESS"; case "REFUND": case "CLOSED": case "REVOKED": case "PAYERROR": return "FAIL"; case "USERPAYING": case "NOTPAY": return "PENDING"; default: return "PENDING"; } } /** * 生成成功响应 */ generateSuccessResponse() { return { code: "SUCCESS", message: "\u6210\u529F" }; } /** * 生成失败响应 */ generateFailureResponse(error) { return { code: "FAIL", message: error || "\u5904\u7406\u5931\u8D25" }; } /** * 获取支持的支付方式 */ getSupportedMethods() { return this.supportedMethods.map((method) => `wechat.${method}`); } /** * 后处理逻辑 */ async postProcess(notification, _originalPayload) { if (this.config.sandbox) { console.log("\u{1F9EA} \u5FAE\u4FE1\u652F\u4ED8\u6C99\u7BB1\u73AF\u5883\u5904\u7406\u5B8C\u6210:", { outTradeNo: notification.outTradeNo, tradeNo: notification.tradeNo, status: notification.tradeStatus, amount: notification.totalAmount }); } } /** * 创建响应对象 */ createResponse(success, _error) { return { status: success ? 200 : 400, body: success ? this.generateSuccessResponse() : this.generateFailureResponse(), headers: { "Content-Type": "application/json" } }; } /** * 创建支付订单 */ async createOrder(method, request) { try { const wechatMethod = method.replace("wechat.", ""); if (!this.supportedMethods.includes(wechatMethod)) { throw new Error( `\u4E0D\u652F\u6301\u7684\u652F\u4ED8\u65B9\u5F0F: ${method}, \u652F\u6301\u7684\u652F\u4ED8\u65B9\u5F0F: ${this.supportedMethods.map((method2) => `wechat.${method2}`).join(", ")}` ); } if (!request.notifyUrl) { throw new Error("notify_url \u662F\u5FC5\u9700\u7684"); } let result; const paymentData = {}; switch (wechatMethod) { case "native": result = await this.createNativePayment(request); paymentData.qrCode = result.code_url; break; case "jsapi": result = await this.createJSAPIPayment(request); paymentData.payParams = generateJSAPIPayParams( this.config.appId, result.prepay_id, this.config.privateKey ); break; case "h5": result = await this.createH5Payment(request); paymentData.payUrl = result.h5_url; break; default: throw new Error(`\u4E0D\u652F\u6301\u7684\u5FAE\u4FE1\u652F\u4ED8\u65B9\u5F0F: ${wechatMethod}`); } return { success: true, tradeNo: request.outTradeNo, paymentData, raw: result }; } catch (error) { return { success: false, error: error instanceof Error ? error.message : "\u521B\u5EFA\u8BA2\u5355\u5931\u8D25" }; } } /** * 创建 Native 支付订单 */ async createNativePayment(request) { const params = { ...this.buildBaseOrderParams(request), scene_info: { payer_client_ip: request.clientIp || "127.0.0.1" } }; return await this.wechatClient.createNativeOrder(params); } /** * 利用v2接口来支持付款码条码付款 * 付款码支付必填参数:authCode、deviceInfo */ async createMicropay(request) { if (!this.config.apiV2Key) { throw new Error("\u626B\u7801\u652F\u4ED8\u5FC5\u987B\u914D\u7F6EapiV2Key"); } const client = new WechatPayV2Client( this.config.appId, this.config.mchId, this.config.apiV2Key, "MD5" ); if (!request.authCode) { throw new Error("authCode is required"); } if (!request.deviceInfo) { throw new Error("deviceInfo is required"); } return client.micropay({ auth_code: request.authCode, body: request.subject, out_trade_no: request.outTradeNo, total_fee: request.totalAmount, spbill_create_ip: request.clientIp || "127.0.0.1", device_info: request.deviceInfo, detail: request.body }); } /** * 创建 JSAPI 支付订单 */ async createJSAPIPayment(request) { if (!request.openid) { throw new Error("JSAPI\u652F\u4ED8\u9700\u8981\u63D0\u4F9Bopenid"); } const params = { ...this.buildBaseOrderParams(request), payer: { openid: request.openid }, scene_info: { payer_client_ip: request.clientIp || "127.0.0.1" } }; return await this.wechatClient.createJSAPIOrder(params); } /** * 创建 H5 支付订单 */ async createH5Payment(request) { const wechatRequest = request; const params = { ...this.buildBaseOrderParams(request), scene_info: { payer_client_ip: request.clientIp || "127.0.0.1", h5_info: { type: "Wap", app_name: wechatRequest.sceneInfo?.h5Info?.appName || "PayStream", app_url: wechatRequest.sceneInfo?.h5Info?.appUrl || "https://example.com" } } }; return await this.wechatClient.createH5Order(params); } /** * 构建基础订单参数 */ buildBaseOrderParams(request) { if (!request.notifyUrl) { throw new Error("notify_url is required"); } return { appid: this.config.appId, mchid: this.config.mchId, description: request.subject, out_trade_no: request.outTradeNo, notify_url: request.notifyUrl, amount: { total: request.totalAmount, // 单位为分 currency: "CNY" }, attach: request.body, // 处理时间过期,转换为 ISO 时间字符串 ...request.timeExpire && { time_expire: new Date( Date.now() + request.timeExpire * 60 * 1e3 ).toISOString() } }; } /** * 查询支付订单 */ async queryOrder(request) { try { let result; if (request.tradeNo) { result = await this.wechatClient.queryOrderByTransactionId( request.tradeNo, this.config.mchId ); } else if (request.outTradeNo) { result = await this.wechatClient.queryOrderByOutTradeNo( request.outTradeNo, this.config.mchId ); } else { throw new Error("\u5FC5\u987B\u63D0\u4F9B tradeNo \u6216 outTradeNo"); } const tradeStatus = this.mapTradeStatus(result.trade_state); return { success: true, orderInfo: { tradeStatus, outTradeNo: result.out_trade_no, tradeNo: result.transaction_id, totalAmount: result.amount.total, receiptAmount: result.amount.payer_total, payerId: result.payer.openid, createTime: (/* @__PURE__ */ new Date()).toISOString(), // 微信支付V3不返回创建时间 payTime: result.success_time || (/* @__PURE__ */ new Date()).toISOString() }, raw: result }; } catch (error) { return { success: false, error: error instanceof Error ? error.message : "\u67E5\u8BE2\u8BA2\u5355\u5931\u8D25" }; } } /** * 发起退款 */ async refund(request) { try { let totalAmount; if (request.tradeNo || request.outTradeNo) { const queryResult = await this.queryOrder({ tradeNo: request.tradeNo, outTradeNo: request.outTradeNo }); if (!queryResult.success || !queryResult.orderInfo) { throw new Error("\u67E5\u8BE2\u539F\u8BA2\u5355\u5931\u8D25\uFF0C\u65E0\u6CD5\u786E\u5B9A\u603B\u91D1\u989D"); } totalAmount = queryResult.orderInfo.totalAmount; } else { throw new Error("\u5FC5\u987B\u63D0\u4F9B tradeNo \u6216 outTradeNo"); } const params = { out_refund_no: request.outRefundNo, reason: request.refundReason || "\u5546\u6237\u9000\u6B3E", amount: { refund: request.refundAmount, total: totalAmount, currency: "CNY" }, notify_url: request.notifyUrl }; if (request.tradeNo) { params.transaction_id = request.tradeNo; } else if (request.outTradeNo) { params.out_trade_no = request.outTradeNo; } const result = await this.wechatClient.createRefund(params); let refundStatus = "PENDING"; if (result.status === "SUCCESS") { refundStatus = "SUCCESS"; } else if (result.status === "CLOSED") { refundStatus = "FAIL"; } return { success: true, refundInfo: { refundId: result.refund_id, outRefundNo: result.out_refund_no, refundAmount: result.amount.refund, refundStatus, refundTime: result.success_time || result.create_time }, raw: result }; } catch (error) { return { success: false, error: error instanceof Error ? error.message : "\u9000\u6B3E\u5931\u8D25" }; } } }; } }); var ALIPAY_API_BASE, ALIPAY_SANDBOX_API_BASE, AlipaySignature, AlipayClient; var init_alipay = __esm({ "src/utils/alipay.ts"() { init_payment(); init_http(); ALIPAY_API_BASE = "https://openapi.alipay.com/gateway.do"; ALIPAY_SANDBOX_API_BASE = "https://openapi.alipaydev.com/gateway.do"; AlipaySignature = class { constructor(privateKey, signType = "RSA2") { this.privateKey = privateKey; this.signType = signType; } /** * 生成支付宝签名 */ generateSignature(params) { const filteredParams = Object.keys(params).filter((key) => params[key] !== "" && params[key] != null).sort().reduce( (result, key) => { result[key] = params[key]; return result; }, {} ); const signString = Object.keys(filteredParams).map((key) => `${key}=${filteredParams[key]}`).join("&"); try { const algorithm = this.signType === "RSA2" ? "RSA-SHA256" : "RSA-SHA1"; const sign = createSign(algorithm); sign.update(signString, "utf8"); return sign.sign(this.privateKey, "base64"); } catch (error) { throw new PaymentError( "CONFIG_ERROR" /* CONFIG_ERROR */, "\u652F\u4ED8\u5B9D\u7B7E\u540D\u751F\u6210\u5931\u8D25", error ); } } /** * 验证支付宝签名 */ verifySignature(params, signature, publicKey) { const { sign, sign_type, ...filteredParams } = params; const signString = Object.keys(filteredParams).filter((key) => filteredParams[key] !== "" && filteredParams[key] != null).sort().map((key) => `${key}=${filteredParams[key]}`).join("&"); try { const algorithm = sign_type === "RSA2" ? "RSA-SHA256" : "RSA-SHA1"; const verify = createVerify(algorithm); verify.update(signString, "utf8"); return verify.verify(publicKey, signature, "base64"); } catch (error) { return false; } } }; AlipayClient = class { constructor(appId, privateKey, alipayPublicKey, signType = "RSA2", sandbox = false) { this.httpClient = new HttpClient(); this.signature = new AlipaySignature(privateKey, signType); this.appId = appId; this.privateKey = privateKey; this.alipayPublicKey = alipayPublicKey; this.sandbox = sandbox; this.signType = signType; } /** * 获取API基础URL */ getBaseUrl() { return this.sandbox ? ALIPAY_SANDBOX_API_BASE : ALIPAY_API_BASE; } /** * 构建公共参数 */ buildCommonParams(method, bizContent) { const params = { app_id: this.appId, method, format: "JSON", charset: "utf-8", sign_type: this.signType, timestamp: (/* @__PURE__ */ new Date()).toISOString().replace("T", " ").substring(0, 19), version: "1.0" }; if (bizContent) { params.biz_content = JSON.stringify(bizContent); } return params; } /** * 发送支付宝API请求 */ async request(method, bizContent, extraParams) { const params = { ...this.buildCommonParams(method, bizContent), ...extraParams }; params.sign = this.signature.generateSignature(params); try { const response = await this.httpClient.post( this.getBaseUrl(), new URLSearchParams(params).toString(), { "Content-Type": "application/x-www-form-urlencoded" } ); if (response.status >= 400) { throw new PaymentError( "NETWORK_ERROR" /* NETWORK_ERROR */, `\u652F\u4ED8\u5B9DAPI\u8BF7\u6C42\u5931\u8D25: ${response.status} ${response.statusText}`, response.data ); } const responseKey = method.replace(/\./g, "_") + "_response"; const result = response.data[responseKey]; if (!result) { throw new PaymentError( "NETWORK_ERROR" /* NETWORK_ERROR */, "\u652F\u4ED8\u5B9DAPI\u54CD\u5E94\u683C\u5F0F\u9519\u8BEF", response.data ); } if (result.code !== "10000") { throw new PaymentError( "NETWORK_ERROR" /* NETWORK_ERROR */, `\u652F\u4ED8\u5B9DAPI\u9519\u8BEF: ${result.code} ${result.msg || result.sub_msg}`, result ); } return result; } catch (error) { if (error instanceof PaymentError) { throw error; } throw new PaymentError( "NETWORK_ERROR" /* NETWORK_ERROR */, "\u652F\u4ED8\u5B9DAPI\u8BF7\u6C42\u5F02\u5E38", error ); } } /** * 统一收单交易创建接口(扫码支付) */ async tradeCreate(params) { return this.request( "alipay.trade.create", params ); } /** * 手机网站支付接口2.0(H5支付) */ async tradeWapPay(params) { const commonParams = this.buildCommonParams("alipay.trade.wap.pay", params); commonParams.sign = this.signature.generateSignature(commonParams); const queryString = new URLSearchParams(commonParams).toString(); return `${this.getBaseUrl()}?${queryString}`; } /** * 电脑网站支付(PC支付) */ async tradePagePay(params) { const commonParams = this.buildCommonParams( "alipay.trade.page.pay", params ); commonParams.sign = this.signature.generateSignature(commonParams); const formFields = Object.keys(commonParams).map( (key) => `<input type="hidden" name="${key}" value="${commonParams[key]}">`