wechat-pay-v3
Version:
Wechat Pay V3 SDK for Node.js
1,136 lines (1,125 loc) • 35.3 kB
JavaScript
// src/base.ts
import crypto2 from "crypto";
import FormData from "form-data";
import axios from "axios";
import { join } from "path";
import { createWriteStream, existsSync, mkdirSync, readFileSync, statSync, unlink } from "fs";
// src/utils/index.ts
import crypto, { X509Certificate } from "crypto";
function urlExclueOrigin(url) {
const _url = new URL(url);
return url.replace(_url.origin, "");
}
function unixTimeStamp() {
return Math.floor(Date.now() / 1e3).toString();
}
function randomStr(length = 32) {
const chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
const maxPos = chars.length;
let noceStr = "";
for (let i = 0; i < (length || 32); i++) {
noceStr += chars.charAt(Math.floor(Math.random() * maxPos));
}
return noceStr;
}
function path2FileName(path) {
return path.replace(/^.*[\\\/]/, "");
}
function fileSha256(buffer) {
return crypto.createHash("sha256").update(buffer).digest("hex");
}
function getCertificateSerialNo(buf) {
const x509 = new X509Certificate(buf);
return x509.serialNumber;
}
function getCertificatePublicKey(certString) {
const x509 = new X509Certificate(certString);
return x509.publicKey;
}
function encrypt(data, key) {
return crypto.publicEncrypt(
{
key,
padding: crypto.constants.RSA_PKCS1_OAEP_PADDING
},
Buffer.from(data, "utf8")
).toString("base64");
}
function getToken(mchid, serial_no, nonce_str, timestamp, signature) {
return `mchid="${mchid}",nonce_str="${nonce_str}",signature="${signature}",serial_no="${serial_no}",timestamp="${timestamp}"`;
}
function decryptToString_AES(options) {
const { associated_data, ciphertext, key, nonce } = options;
const ciphertextBuffer = Buffer.from(ciphertext, "base64");
const authTag = ciphertextBuffer.slice(ciphertextBuffer.length - 16);
const data = ciphertextBuffer.slice(0, ciphertextBuffer.length - 16);
const decipherIv = crypto.createDecipheriv("aes-256-gcm", key, nonce);
decipherIv.setAuthTag(authTag);
decipherIv.setAAD(Buffer.from(associated_data));
const decryptBuf = decipherIv.update(data);
decipherIv.final();
return decryptBuf.toString("utf8");
}
function getPublicKey(certString) {
const x509 = new X509Certificate(certString);
return x509.publicKey;
}
function isUrl(url) {
const reg = /^((https|http|ftp)?:\/\/)[^\s]+/;
return reg.test(url);
}
function getSysTmpDir() {
return process.env.TMPDIR || process.env.TMP || process.env.TEMP || "/tmp";
}
function getPathValue(obj, path) {
return path.split(".").reduce((prev, curr) => prev[curr], obj);
}
function setPathValue(obj, path, value, options) {
let setFlag = true;
return path.split(".").reduce((prev, curr, index, array) => {
if (index === array.length - 1 && setFlag) {
prev[curr] = value;
}
if (!prev[curr]) {
if (options?.onSetFail)
options.onSetFail(array.slice(0, index + 1).join("."));
setFlag = false;
}
return prev[curr];
}, obj);
}
function replaceTagText(str, params, beforeToken = "{", afterToken = "}") {
const arr = Object.entries(params);
let result = str;
arr.forEach(([key, value]) => {
result = result.replace(`${beforeToken}${key}${afterToken}`, value);
});
return result;
}
// src/base.ts
var WechatPayV3Base = class {
constructor(options, events = {}) {
this.events = events;
this.mchid = options.mchid;
this.userAgent = options.userAgent ?? "wechatpay-sdk";
this.privateKey = options.apiclient_key;
this.apiCertSerialNo = getCertificateSerialNo(options.apiclient_cert);
this.apiV3Key = options.apiV3Key;
if (options.downloadDir) {
this.downloadDir = options.downloadDir;
} else {
const tempDir = getSysTmpDir();
const downloadDir = join(tempDir, "./wxpay-v3-downloads");
if (!existsSync(downloadDir)) {
mkdirSync(downloadDir);
}
this.downloadDir = downloadDir;
}
this.request = axios.create({
timeout: 1e3 * 60 * 5,
timeoutErrorMessage: "\u8BF7\u6C42\u8D85\u65F6",
headers: {
Accept: "application/json",
"User-Agent": this.userAgent,
"Content-Type": "application/json"
}
});
this.init();
}
/** pem私钥 */
privateKey;
/** 加密算法,固定值'WECHATPAY2-SHA256-RSA2048'.国密暂不支持 */
schema = "WECHATPAY2-SHA256-RSA2048";
/** axios请求示例 */
request;
/** 商户号 */
mchid;
/** header -> userAgent (微信可能会拒绝不带userAgent的请求) */
userAgent;
/** apiV3密钥 */
apiV3Key;
/** 平台证书列表 */
certificates = [];
/** 更新证书时间+12小时后的结果,注意此时间并非平台证书本身的过期时间,而是需要更新的时间 */
certExpiresTime;
/** 商户Api证书序列号 */
apiCertSerialNo;
/** 下载文件文件夹 */
downloadDir;
/**
* 初始化
*/
init() {
this.request.interceptors.response.use(
(response) => {
if (this.events.onResponse) {
this.events.onResponse(response, this);
}
return response;
},
(error) => {
if (error.response) {
return Promise.reject(error.response.data ?? error);
}
return Promise.reject(error);
}
);
this.request.interceptors.request.use(async (config) => {
if (this.events.onRequsetBefore) {
this.events.onRequsetBefore(config, this);
}
if (config.headers?.Authorization === void 0) {
if (config.method && config.url) {
const timestamp = unixTimeStamp();
const nonce = randomStr();
const signature = this.buildMessageSign(
config.method.toUpperCase(),
urlExclueOrigin(config.url),
timestamp,
nonce,
config.data || ""
);
const Authorization = this.getAuthorization(nonce, timestamp, signature);
config.headers.Authorization = Authorization;
}
}
if (config.url?.includes("/v3/certificates")) {
return config;
}
await this.updateCertificates();
if (this.events.onRequsetAfter) {
this.events.onRequsetAfter(config, this);
}
return config;
});
this.updateCertificates().catch((error) => {
console.log("init updateCertificates fail", error);
});
}
setEvents(events) {
this.events = events;
}
/**
* 更新平台证书
* @description 会判断缓存是否过期,如果过期则更新,否则不更新.
* @param forceUpdate 强制更新
*/
async updateCertificates(forceUpdate = false) {
if (forceUpdate === false) {
if (this.certExpiresTime && this.certExpiresTime > /* @__PURE__ */ new Date()) {
return;
}
}
const res = await this.getCertificates();
this.certificates = res;
this.certExpiresTime = new Date(Date.now() + 12 * 60 * 60 * 1e3);
}
/**
* 构造签名串并签名
* @param method 请求方法
* @param url 请求URL
* @param timestamp 时间戳
* @param nonce 随机字符串
* @param body 请求主体
*/
buildMessageSign(method, url, timestamp, nonce, body) {
const tmpBody = typeof body === "object" ? JSON.stringify(body) : body;
const data = method + "\n" + url + "\n" + timestamp + "\n" + nonce + "\n" + tmpBody + "\n";
return this.sha256WithRSA(data);
}
/**
* 构造验签名串
* @param timestamp 参数在响应头中对应
* @param nonce 参数在响应头中对应
* @param body 参数在响应头中对应
* @returns
*/
buildMessageVerify(timestamp, nonce, body) {
return [timestamp, nonce, body].join("\n") + "\n";
}
/**
* 构造Authorization
* @param nonce_str 随机字符串
* @param timestamp 时间戳
* @param signature 签名(buildMessage生成)
*/
getAuthorization(nonce_str, timestamp, signature) {
return `${this.schema} ${getToken(this.mchid, this.apiCertSerialNo, nonce_str, timestamp, signature)}`;
}
/**
* 私钥签名
* @param data 待签名数据
* @returns base64编码的签名
*/
sha256WithRSA(data) {
return crypto2.createSign("RSA-SHA256").update(data).sign(this.privateKey, "base64");
}
/**
* 平台证书公钥验签
* @param serial 证书序列号
* @param signature 签名
* @param data 待验签数据
*/
sha256WithRsaVerify(serial, signature, data) {
const cert = this.certificates.find((item) => item.serial_no === serial);
if (!cert) {
console.error({
cerificates: this.certificates,
certExpiresTime: this.certExpiresTime
});
throw new Error("\u8BC1\u4E66\u5E8F\u5217\u53F7\u9519\u8BEF");
}
return crypto2.createVerify("RSA-SHA256").update(data).verify(cert.publicKey, signature, "base64");
}
/**
* 解密平台响应
* @param nonce 随机字符串
* @param associated_data 附加数据
* @param ciphertext 密文
* @returns
*/
aesGcmDecrypt(options) {
return decryptToString_AES({
...options,
key: this.apiV3Key
});
}
/**
* 平台证书公钥加密,如果需要同时加密多个字段,请使用publicEncryptObjectPaths
* @param data 待加密数据
* @returns
*/
publicEncrypt(data) {
return encrypt(data, this.certificates[0].publicKey);
}
/**
* 平台证书公钥加密请求主体中指定的字段
* @description 对字符串类型的路径进行加密
* @param data 数据
* @param paths 路径数组
* @returns 该函数返回一个新的对象,不会修改原对象
* @example
* const data = { name: '张三' , idCard: {
* number:string //如果这个字段需要加密
* }}
* const newData = publicEncryptObjectPaths(data,['idCard.number'])
*/
publicEncryptObjectPaths(data, paths) {
const json = JSON.stringify(data);
const newData = JSON.parse(json);
paths.forEach((path) => {
const value = getPathValue(newData, path);
if (value && typeof value === "string") {
setPathValue(newData, path, this.publicEncrypt(value), {
onSetFail(failPath) {
console.log(`\u8BBE\u7F6E\u8DEF\u5F84:${failPath}\u5931\u8D25 : ${json}`);
}
});
}
});
return newData;
}
/**
* 下载文件
* @param url
* @param fileName 提供文件名,包括后缀。如果不提供,则从url中获取文件名
* @returns
*/
async downloadFile(url, fileName) {
let tmpFileName = fileName || "";
if (!fileName) {
tmpFileName = url.split("/").pop();
if (tmpFileName === "") {
throw new Error("\u6587\u4EF6\u540D\u83B7\u53D6\u5931\u8D25,\u8BF7\u624B\u52A8\u6307\u5B9A\u6587\u4EF6\u540D");
}
}
const response = await this.request.get(url, {
responseType: "stream"
});
const contentType = response.headers["content-type"];
const suffix = contentType.split("/").pop();
if (suffix) {
if (suffix === "jpeg") {
tmpFileName = `${tmpFileName}.jpg`;
} else {
tmpFileName = `${tmpFileName}.${suffix}`;
}
}
const filePath = join(this.downloadDir, tmpFileName);
const writer = createWriteStream(filePath);
response.data.pipe(writer);
return new Promise((resolve, reject) => {
writer.on("finish", () => {
writer.close();
resolve({
filePath,
fileName: tmpFileName
});
});
writer.on("error", reject);
});
}
/**
* 响应验签
* @description 该函数会验证签名,返回true表示验签成功,返回false表示验签失败
* @param headers 请求头
* @param body 请求体
*/
resVerify(headers, body) {
const {
"wechatpay-timestamp": timestamp,
"wechatpay-nonce": nonce,
"wechatpay-signature": signature,
"wechatpay-serial": serial
} = headers;
let bodyStr = "";
if (body) {
bodyStr = Object.keys(body).length !== 0 ? JSON.stringify(body) : "";
}
const signStr = this.buildMessageVerify(timestamp, nonce, bodyStr);
return this.sha256WithRsaVerify(serial, signature, signStr);
}
/**
* 处理回调 -验证签名,并使用AEAD_AES_256_GCM解密
* @param headers
* @param body
*/
async handleCallback(headers, body) {
if (!body.resource) {
throw new Error("\u56DE\u8C03\u6570\u636E\u683C\u5F0F\u9519\u8BEF");
}
const isOk = this.resVerify(headers, body);
if (!isOk) {
throw new Error("\u56DE\u8C03\u9A8C\u7B7E\u5931\u8D25");
}
const resource = this.aesGcmDecrypt(body.resource);
try {
return {
...body,
resource: JSON.parse(resource)
};
} catch (error) {
throw new Error("\u56DE\u8C03\u6570\u636EJSON\u89E3\u6790\u5931\u8D25");
}
}
//================ Base Api
/**
* 获取证书
* @description 获取商户当前可用的平台证书列表。
* @docUrl https://pay.weixin.qq.com/wiki/doc/apiv3_partner/apis/wechatpay5_1.shtml
*/
async getCertificates() {
const apiUrl = "https://api.mch.weixin.qq.com/v3/certificates";
const res = await this.request.get(apiUrl);
const certificates = res.data.data;
const decryptCertificates = certificates.map((item) => {
const { associated_data, ciphertext, nonce } = item.encrypt_certificate;
const certificate = this.aesGcmDecrypt({
nonce,
associated_data,
ciphertext
});
const publicKey = getPublicKey(certificate);
return {
expire_time: item.expire_time,
effective_time: item.effective_time,
serial_no: item.serial_no,
certificate,
publicKey
};
});
decryptCertificates.sort((a, b) => {
return new Date(b.expire_time).getTime() - new Date(a.expire_time).getTime();
});
return decryptCertificates;
}
//上传视频和图片的方法差不多,封装在一起
async _upload(path, options) {
const { fileName, type } = options;
const apiUrl = "https://api.mch.weixin.qq.com/v3/merchant/media/upload";
const realyFileName = fileName ?? path2FileName(path);
const fileSize = statSync(path).size;
if (type === "image") {
if (!realyFileName.match(/(jpg|png|bmp)$/i)) {
throw "\u56FE\u7247\u7ED3\u5C3E\u5FC5\u987B\u662Fjpg\u3001png\u3001bmp";
}
if (fileSize > 2 * 1024 * 1024) {
throw "\u56FE\u7247\u5927\u5C0F\u4E0D\u80FD\u8D85\u8FC72M";
}
} else if (type === "video") {
if (!realyFileName.match(/(avi|wmv|mpeg|mp4|mov|mkv|flv|f4v|m4v|rmvb)$/i)) {
throw "\u89C6\u9891\u7ED3\u5C3E\u5FC5\u987B\u662Favi\u3001wmv\u3001mpeg\u3001mp4\u3001mov\u3001mkv\u3001flv\u3001f4v\u3001m4v\u3001rmvb";
}
if (fileSize > 5 * 1024 * 1024) {
throw "\u89C6\u9891\u5927\u5C0F\u4E0D\u80FD\u8D85\u8FC75M";
}
}
let file;
try {
file = readFileSync(path);
} catch (error) {
throw "\u627E\u4E0D\u5230\u6587\u4EF6: " + path;
}
const body = {
filename: realyFileName,
sha256: fileSha256(file)
};
const json = JSON.stringify(body);
const timestamp = unixTimeStamp();
const nonce = randomStr();
const signature = this.buildMessageSign("POST", urlExclueOrigin(apiUrl), timestamp, nonce, json);
const Authorization = this.getAuthorization(nonce, timestamp, signature);
const formData = new FormData();
formData.append("meta", json, {
contentType: "application/json"
});
formData.append("file", file, {
filename: realyFileName
});
const res = await this.request.post(apiUrl, formData, {
headers: {
Authorization,
"Content-Type": "multipart/form-data;boundary=" + formData.getBoundary()
}
});
return res.data;
}
/**
* 图片上传
* @maxSize 2M
* @param pathOrUrl 图片路径可以是本地路径,也可以是网络路径
* @param fileName 商户上传的媒体图片的名称,商户自定义,必须以jpg、bmp、png为后缀,不区分大小写。
* @description 部分微信支付业务指定商户需要使用图片上传 API来上报图片信息,从而获得必传参数的值:图片MediaID 。
* @docUrl https://pay.weixin.qq.com/wiki/doc/apiv3_partner/apis/chapter2_1_1.shtml
*/
async uploadImage(pathOrUrl, fileName) {
if (isUrl(pathOrUrl)) {
const { filePath } = await this.downloadFile(pathOrUrl);
const result = await this._upload(filePath, { fileName, type: "image" });
unlink(filePath, (e) => {
if (e)
console.error("\u672C\u5730\u6587\u4EF6\u5220\u9664\u5931\u8D25", e);
});
return result;
} else {
return this._upload(pathOrUrl, { fileName, type: "image" });
}
}
/**
* 视频上传
* @maxSize 5M
* @param pathOrUrl 视频路径可以是本地路径,也可以是网络路径
* @param fileName 商户上传的媒体视频的名称,商户自定义,必须以avi、wmv、mpeg、mp4、mov、mkv、flv、f4v、m4v、rmvb为后缀,不区分大小写。
* @description 部分微信支付业务指定商户需要使用视频上传 API来上报视频信息,从而获得必传参数的值:视频MediaID 。
* @docUrl https://pay.weixin.qq.com/wiki/doc/apiv3_partner/apis/chapter2_1_2.shtml
*/
async uploadVideo(pathOrUrl, fileName) {
if (isUrl(pathOrUrl)) {
const { filePath } = await this.downloadFile(pathOrUrl);
const result = this._upload(filePath, { fileName, type: "video" });
unlink(filePath, (e) => {
if (e)
console.error("\u672C\u5730\u6587\u4EF6\u5220\u9664\u5931\u8D25", e);
});
return result;
} else {
return this._upload(pathOrUrl, { fileName, type: "video" });
}
}
};
var baseInstanceMap = /* @__PURE__ */ new Map();
var useInstanceMap = /* @__PURE__ */ new Map();
function apiContainer(options, events) {
const { singleton = true, ...wechatPayOptions } = options;
let base;
if (singleton) {
const key = wechatPayOptions.mchid;
if (baseInstanceMap.has(key)) {
base = baseInstanceMap.get(key);
} else {
base = new WechatPayV3Base(wechatPayOptions, events);
baseInstanceMap.set(key, base);
}
} else {
base = new WechatPayV3Base(wechatPayOptions, events);
}
function use(ApiClass) {
if (singleton) {
const key = ApiClass.name + wechatPayOptions.mchid;
if (useInstanceMap.has(key)) {
return useInstanceMap.get(key);
} else {
const instance = new ApiClass(base);
useInstanceMap.set(key, instance);
return instance;
}
}
return new ApiClass(base);
}
const {
downloadFile,
publicEncrypt,
publicEncryptObjectPaths,
uploadImage,
uploadVideo,
aesGcmDecrypt,
sha256WithRSA,
sha256WithRsaVerify,
handleCallback,
resVerify,
setEvents
} = base;
return {
use,
downloadFile: downloadFile.bind(base),
publicEncrypt: publicEncrypt.bind(base),
publicEncryptObjectPaths: publicEncryptObjectPaths.bind(base),
uploadImage: uploadImage.bind(base),
uploadVideo: uploadVideo.bind(base),
sha256WithRSA: sha256WithRSA.bind(base),
aesGcmDecrypt: aesGcmDecrypt.bind(base),
sha256WithRsaVerify: sha256WithRsaVerify.bind(base),
handleCallback: handleCallback.bind(base),
resVerify: resVerify.bind(base),
setEvents: setEvents.bind(base),
base
};
}
function getInstances() {
return {
baseInstanceMap,
useInstanceMap
};
}
// src/apis/applyment/applyment.ts
var Applyment = class {
constructor(base) {
this.base = base;
}
/**
* 提交申请单
* @notAutoEncrypt <不自动加密>
* @description 上传图片接口,this.uploadImage
* @description 加密接口,this.publicEncryptObjectPaths 或者 this.privateEncrypt
* @param body 请求主体,此接口及其复杂,提供得类型仅作参考,请参考官方文档
* @doc https://pay.weixin.qq.com/wiki/doc/apiv3_partner/apis/chapter11_1_1.shtml
*/
async submitApplications(body) {
const apiUrl = "https://api.mch.weixin.qq.com/v3/applyment4sub/applyment/";
const res = await this.base.request.post(apiUrl, body, {
headers: {
"Wechatpay-Serial": this.base.certificates[0].serial_no
}
});
return res.data;
}
/**
* 查询申请单状态
* @param businessCode 业务申请编号
*/
async queryApplymentState(businessCode) {
const apiUrl = `https://api.mch.weixin.qq.com/v3/applyment4sub/applyment/business_code/${businessCode}`;
const res = await this.base.request.get(apiUrl);
return res.data;
}
/**
* 修改结算账户
* @param sub_mchid 子商户号 特殊规则:长度最小8个字节。
* @param body 请求主体
* @returns 是否成功
*/
async modifySettlementAccount(sub_mchid, body) {
const apiUrl = `https://api.mch.weixin.qq.com/v3/apply4sub/sub_merchants/${sub_mchid}/modify-settlement`;
const res = await this.base.request.post(
apiUrl,
this.base.publicEncryptObjectPaths(body, ["account_name", "account_number"])
);
return +res.status === 204;
}
/**
* 查询结算账号
* @param sub_mchid 子商户号 特殊规则:长度最小8个字节。
* @returns 结算账号信息
*/
async querySettlementAccount(sub_mchid) {
const apiUrl = `https://api.mch.weixin.qq.com/v3/apply4sub/sub_merchants/${sub_mchid}/settlement`;
const res = await this.base.request.get(apiUrl);
return res.data;
}
};
// src/apis/basePay/basePay.ts
var UrlMap = {
order: {
provider: `https://api.mch.weixin.qq.com/v3/pay/partner/transactions/jsapi`,
business: `https://api.mch.weixin.qq.com/v3/pay/transactions/jsapi`
},
transactionIdQueryOrder: {
provider: "https://api.mch.weixin.qq.com/v3/pay/partner/transactions/id/{transaction_id}",
business: "https://api.mch.weixin.qq.com/v3/pay/transactions/id/{transaction_id}"
},
outTradeNoQueryOrder: {
provider: "https://api.mch.weixin.qq.com/v3/pay/partner/transactions/out-trade-no/{out_trade_no}",
business: "https://api.mch.weixin.qq.com/v3/pay/transactions/out-trade-no/{out_trade_no}"
},
closeOrder: {
provider: "https://api.mch.weixin.qq.com/v3/pay/partner/transactions/out-trade-no/{out_trade_no}/close",
business: "https://api.mch.weixin.qq.com/v3/pay/transactions/out-trade-no/{out_trade_no}/close"
},
refund: {
apiUrl: "https://api.mch.weixin.qq.com/v3/refund/domestic/refunds"
//退款都是一个
},
queryRefund: {
apiUrl: "https://api.mch.weixin.qq.com/v3/refund/domestic/refunds/{out_refund_no}"
//查询退款都是一个
},
applyTradeBill: {
apiUrl: "https://api.mch.weixin.qq.com/v3/bill/tradebill"
},
fundflowBill: {
apiUrl: "https://api.mch.weixin.qq.com/v3/bill/fundflowbill"
},
subFundflowBill: {
apiUrl: "https://api.mch.weixin.qq.com/v3/bill/sub-merchant-fundflowbill"
}
};
var BasePay = class {
constructor(base) {
this.base = base;
}
//=========================================下单
async _order(data) {
const isBusiness = data.appid !== void 0;
const apiUrl = isBusiness ? UrlMap.order.business : UrlMap.order.provider;
const result = await this.base.request.post(apiUrl, data);
return result.data;
}
/** 下单-直连商户 */
async order(data) {
return this._order(data);
}
/** 下单-服务商 */
async orderOnProvider(data) {
return this._order(data);
}
//=========================================查询订单_通过微信订单号
async _transactionIdQueryOrder(data) {
const { transaction_id, ...query } = data;
const isBusiness = data.mchid !== void 0;
const _ = isBusiness ? UrlMap.transactionIdQueryOrder.business + "?mchid=" + query.mchid : UrlMap.transactionIdQueryOrder.provider + "?sp_mchid=" + query.sp_mchid + "&sub_mchid=" + query.sub_mchid;
const apiUrl = replaceTagText(_, {
transaction_id
});
const result = await this.base.request.get(apiUrl);
return result.data;
}
/**
* 查询订单-通过微信订单号
*/
async transactionIdQueryOrder(data) {
return this._transactionIdQueryOrder(data);
}
/**
* 查询订单-服务商-通过微信订单号
*/
async transactionIdQueryOrderOnProvider(data) {
return this._transactionIdQueryOrder(data);
}
//=========================================查询订单_通过商户订单号
async _outTradeNoQueryOrder(data) {
const { out_trade_no, ...query } = data;
const isBusiness = data.mchid !== void 0;
const _ = isBusiness ? UrlMap.outTradeNoQueryOrder.business + "?mchid=" + query.mchid : UrlMap.outTradeNoQueryOrder.provider + "?sp_mchid=" + query.sp_mchid + "&sub_mchid=" + query.sub_mchid;
const apiUrl = replaceTagText(_, {
out_trade_no
});
const result = await this.base.request.get(apiUrl);
return result.data;
}
/**
* 查询订单-通过商户订单号
*/
async outTradeNoQueryOrder(data) {
return this._outTradeNoQueryOrder(data);
}
/**
* 查询订单-服务商-通过商户订单号
*/
async outTradeNoQueryOrderOnProvider(data) {
return this._outTradeNoQueryOrder(data);
}
//=========================================关闭订单
async _closeOrder(data) {
const { out_trade_no, ...body } = data;
const isBusiness = data.mchid !== void 0;
const _ = isBusiness ? UrlMap.closeOrder.business : UrlMap.closeOrder.provider;
const apiUrl = replaceTagText(_, {
out_trade_no
});
const result = await this.base.request.post(apiUrl, body);
return result.status;
}
/**
* 关闭订单-直连商户
* @returns status 如果为204,则关闭成功
*/
async closeOrder(data) {
return this._closeOrder(data);
}
/**
* 关闭订单-服务商
* @returns status 如果为204,则关闭成功
*/
async closeOrderOnProvider(data) {
return this._closeOrder(data);
}
//=========================================退款
async _refund(data) {
const { apiUrl } = UrlMap.refund;
const result = await this.base.request.post(apiUrl, data);
return result.data;
}
/**
* 退款-直连商户
*/
async refund(data) {
return this._refund(data);
}
/**
* 退款-服务商
*/
async refundOnProvider(data) {
return this._refund(data);
}
//=========================================查询退款
async _queryRefund(data) {
const { out_refund_no, sub_mchid } = data;
let apiUrl = replaceTagText(UrlMap.queryRefund.apiUrl, {
out_refund_no
});
if (sub_mchid) {
apiUrl += `?sub_mchid=${sub_mchid}`;
}
const result = await this.base.request.get(apiUrl);
return result.data;
}
/**
* 查询退款-直连商户
*/
async queryRefund(data) {
return this._queryRefund(data);
}
/**
* 查询退款-服务商
*/
async queryRefundOnProvider(data) {
return this._queryRefund(data);
}
//=========================================申请交易账单
async _applyTradeBill(data) {
const { apiUrl } = UrlMap.applyTradeBill;
const result = await this.base.request.get(apiUrl, {
params: data
});
return result.data;
}
/**
* 申请交易账单-直连商户
*/
async applyTradeBill(data) {
return this._applyTradeBill(data);
}
/**
* 申请交易账单-服务商
*/
async applyTradeBillOnProvider(data) {
return this._applyTradeBill(data);
}
//=========================================申请资金账单
async _applyFundFlowBill(data) {
const { apiUrl } = UrlMap.fundflowBill;
const result = await this.base.request.get(apiUrl, {
params: data
});
return result.data;
}
/**
* 申请资金账单-直连商户
*/
async applyFundFlowBill(data) {
return this._applyFundFlowBill(data);
}
/**
* 申请资金账单-服务商
*/
async applyFundFlowBillOnProvider(data) {
return this._applyFundFlowBill(data);
}
//=========================================申请单个子商户资金账单
/**
* 申请单个子商户资金账单 仅限服务商
*/
async applySubMerchantFundFlowBill(data) {
const { apiUrl } = UrlMap.subFundflowBill;
const result = await this.base.request.get(apiUrl, {
params: data
});
return result.data;
}
//=========================================下载账单
/**
* 下载账单(通用)
*/
async downloadBill(download_url) {
const result = await this.base.request.get(download_url, {
responseType: "arraybuffer"
});
return result.data;
}
};
// src/apis/basePay/AppPay.ts
var UrlMap2 = {
order: {
provider: `https://api.mch.weixin.qq.com/v3/pay/partner/transactions/app`,
business: `https://api.mch.weixin.qq.com/v3/pay/transactions/app`
}
};
var AppPay = class extends BasePay {
constructor(base) {
super(base);
}
async _exOrder(params) {
const isBusiness = params.appid !== void 0;
const apiUrl = isBusiness ? UrlMap2.order.business : UrlMap2.order.provider;
const result = await this.base.request.post(apiUrl, params);
return result.data;
}
/**
* App支付下单 直连
*/
async order(params) {
return this._exOrder(params);
}
/**
* App支付下单 服务商
*/
async orderOnProvider(data) {
return this._exOrder(data);
}
/**
* 获取App调起支付参数
* @param params
* @returns
*/
getPayParams(params) {
const { appId, prepay_id, partnerId } = params;
const timeStamp = unixTimeStamp();
const nonceStr = randomStr();
const packageStr = `Sign=WXPay`;
const message = [appId, timeStamp, nonceStr, prepay_id].join("\n") + "\n";
const paySign = this.base.sha256WithRSA(message);
return {
appId,
partnerId,
prepayId: prepay_id,
package: packageStr,
nonceStr,
timeStamp,
sign: paySign
};
}
};
// src/apis/basePay/JSPay.ts
var JSPay = class extends BasePay {
order(data) {
return super._order(data);
}
orderOnProvider(data) {
return super._order(data);
}
/**
* 获取调起支付参数
* @param params
* @returns
*/
getPayParams(params) {
const { appId, prepay_id } = params;
const timeStamp = unixTimeStamp();
const nonceStr = randomStr();
const packageStr = `prepay_id=${prepay_id}`;
const message = [appId, timeStamp, nonceStr, packageStr].join("\n") + "\n";
const paySign = this.base.sha256WithRSA(message);
return {
appId,
timeStamp,
nonceStr,
package: packageStr,
signType: "RSA",
paySign
};
}
};
// src/apis/basePay/MiniProgramPay.ts
var MiniProgramPay = class extends BasePay {
order(data) {
return super._order(data);
}
orderOnProvider(data) {
return super._order(data);
}
/**
* 获取调起支付参数
* @param params
* @returns
*/
getPayParams(params) {
const { appId, prepay_id } = params;
const timeStamp = unixTimeStamp();
const nonceStr = randomStr();
const packageStr = `prepay_id=${prepay_id}`;
const message = [appId, timeStamp, nonceStr, packageStr].join("\n") + "\n";
const paySign = this.base.sha256WithRSA(message);
return {
timeStamp,
nonceStr,
package: packageStr,
signType: "RSA",
paySign
};
}
};
// src/apis/basePay/NativePay.ts
var UrlMap3 = {
order: {
provider: `https://api.mch.weixin.qq.com/v3/pay/partner/transactions/native`,
business: `https://api.mch.weixin.qq.com/v3/pay/transactions/native`
}
};
var NativePay = class extends BasePay {
constructor(base) {
super(base);
}
async _exOrder(params) {
const isBusiness = params.appid !== void 0;
const apiUrl = isBusiness ? UrlMap3.order.business : UrlMap3.order.provider;
const result = await this.base.request.post(apiUrl, params);
return result.data;
}
/**
* Native支付下单 直连
*/
async order(params) {
return this._exOrder(params);
}
/**
* Native支付下单 服务商
*/
async orderOnProvider(data) {
return this._exOrder(data);
}
};
// src/apis/basePay/basePay.types.ts
var TradeTypeEnum = /* @__PURE__ */ ((TradeTypeEnum2) => {
TradeTypeEnum2["JSAPI"] = "JSAPI";
TradeTypeEnum2["NATIVE"] = "NATIVE";
TradeTypeEnum2["APP"] = "APP";
TradeTypeEnum2["MICROPAY"] = "MICROPAY";
TradeTypeEnum2["MWEB"] = "MWEB";
TradeTypeEnum2["FACEPAY"] = "FACEPAY";
return TradeTypeEnum2;
})(TradeTypeEnum || {});
var TradeStateEnum = /* @__PURE__ */ ((TradeStateEnum2) => {
TradeStateEnum2["SUCCESS"] = "SUCCESS";
TradeStateEnum2["REFUND"] = "REFUND";
TradeStateEnum2["NOTPAY"] = "NOTPAY";
TradeStateEnum2["CLOSED"] = "CLOSED";
TradeStateEnum2["REVOKED"] = "REVOKED";
TradeStateEnum2["USERPAYING"] = "USERPAYING";
TradeStateEnum2["PAYERROR"] = "PAYERROR";
return TradeStateEnum2;
})(TradeStateEnum || {});
var ResultRefundStatusEnum = /* @__PURE__ */ ((ResultRefundStatusEnum2) => {
ResultRefundStatusEnum2["SUCCESS"] = "SUCCESS";
ResultRefundStatusEnum2["CLOSED"] = "CLOSED";
ResultRefundStatusEnum2["PROCESSING"] = "PROCESSING";
ResultRefundStatusEnum2["ABNORMAL"] = "ABNORMAL";
return ResultRefundStatusEnum2;
})(ResultRefundStatusEnum || {});
var ResultRefundChannelEnum = /* @__PURE__ */ ((ResultRefundChannelEnum2) => {
ResultRefundChannelEnum2["ORIGINAL"] = "ORIGINAL";
ResultRefundChannelEnum2["BALANCE"] = "BALANCE";
ResultRefundChannelEnum2["OTHER_BALANCE"] = "OTHER_BALANCE";
ResultRefundChannelEnum2["OTHER_BANKCARD"] = "OTHER_BANKCARD";
return ResultRefundChannelEnum2;
})(ResultRefundChannelEnum || {});
var ResultFundsAccountEnum = /* @__PURE__ */ ((ResultFundsAccountEnum2) => {
ResultFundsAccountEnum2["UNSETTLED"] = "UNSETTLED";
ResultFundsAccountEnum2["AVAILABLE"] = "AVAILABLE";
ResultFundsAccountEnum2["UNAVAILABLE"] = "UNAVAILABLE";
ResultFundsAccountEnum2["OPERATION"] = "OPERATION";
ResultFundsAccountEnum2["BASIC"] = "BASIC";
return ResultFundsAccountEnum2;
})(ResultFundsAccountEnum || {});
// src/apis/basePay/h5Pay.ts
var UrlMap4 = {
order: {
provider: `https://api.mch.weixin.qq.com/v3/pay/partner/transactions/h5`,
business: `https://api.mch.weixin.qq.com/v3/pay/transactions/h5`
}
};
var H5Pay = class extends BasePay {
constructor(base) {
super(base);
}
async _exOrder(params) {
const isBusiness = params.appid !== void 0;
const apiUrl = isBusiness ? UrlMap4.order.business : UrlMap4.order.provider;
const result = await this.base.request.post(apiUrl, params);
return result.data;
}
/**
* H5支付下单 直连
*/
async order(params) {
return this._exOrder(params);
}
/**
* H5支付下单 服务商
*/
async orderOnProvider(data) {
return this._exOrder(data);
}
};
export {
AppPay,
Applyment,
BasePay,
H5Pay,
JSPay,
MiniProgramPay,
NativePay,
ResultFundsAccountEnum,
ResultRefundChannelEnum,
ResultRefundStatusEnum,
TradeStateEnum,
TradeTypeEnum,
WechatPayV3Base,
apiContainer,
decryptToString_AES,
encrypt,
fileSha256,
getCertificatePublicKey,
getCertificateSerialNo,
getInstances,
getPathValue,
getPublicKey,
getSysTmpDir,
getToken,
isUrl,
path2FileName,
randomStr,
replaceTagText,
setPathValue,
unixTimeStamp,
urlExclueOrigin
};