paystream
Version:
PayStream V2 - 现代化的 TypeScript 统一支付回调处理库,采用面向对象架构
1,545 lines (1,539 loc) • 96.7 kB
JavaScript
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]}">`