wechatpay-nextjs-v3
Version:
微信支付v3版本工具包Next.js版本
859 lines (858 loc) • 36.9 kB
JavaScript
'use strict';
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.Payment = void 0;
const crypto_1 = __importDefault(require("crypto"));
const node_forge_1 = __importDefault(require("node-forge"));
const Base_1 = require("./Base");
const FetchPayRequest_1 = require("./FetchPayRequest");
class Payment extends Base_1.Base {
appid; // 直连商户申请的公众号或移动应用appid。
mchid; // 商户号
serial_no = ''; // 证书序列号
publicKey; // 公钥
privateKey; // 密钥
authType = 'WECHATPAY2-SHA256-RSA2048'; // 认证类型,目前为WECHATPAY2-SHA256-RSA2048
httpService;
key; // APIv3密钥
static certificates = {}; // 微信平台证书 key 是 serialNo, value 是 publicKey
constructor(arg1, mchid, publicKey, privateKey, options) {
super();
if (arg1 instanceof Object) {
this.appid = arg1.appid;
this.mchid = arg1.mchid;
if (arg1.serial_no)
this.serial_no = arg1.serial_no;
this.publicKey = arg1.publicKey;
if (!this.publicKey)
throw new Error('缺少公钥');
this.privateKey = arg1.privateKey;
if (!arg1.serial_no)
this.serial_no = this.getSN(this.publicKey);
this.authType = arg1.authType || 'WECHATPAY2-SHA256-RSA2048';
this.userAgent = arg1.userAgent || '127.0.0.1';
this.key = arg1.key;
}
else {
const _optipns = options || {};
this.appid = arg1;
this.mchid = mchid || '';
if (privateKey instanceof String) {
}
this.publicKey = publicKey;
this.privateKey = privateKey;
this.authType = _optipns.authType || 'WECHATPAY2-SHA256-RSA2048';
this.userAgent = _optipns.userAgent || '127.0.0.1';
this.key = _optipns.key;
this.serial_no = _optipns.serial_no || '';
if (!this.publicKey)
throw new Error('缺少公钥');
if (!this.serial_no)
this.serial_no = this.getSN(this.publicKey);
}
this.httpService = new FetchPayRequest_1.FetchPayRequest();
}
/**
* 自定义创建http 请求
*/
createHttp(service) {
this.httpService = service;
}
/**
* 获取微信平台key
* @param apiSecret APIv3密钥
* @returns
*/
async get_certificates(apiSecret) {
const url = 'https://api.mch.weixin.qq.com/v3/certificates';
const authorization = this.buildAuthorization('GET', url);
const headers = this.getHeaders(authorization);
const result = await this.httpService.get(url, headers);
if (result.status === 200) {
const data = result?.data?.data;
for (const item of data) {
const decryptCertificate = this.decipher_gcm(item.encrypt_certificate.ciphertext, item.encrypt_certificate.associated_data, item.encrypt_certificate.nonce, apiSecret);
const cert = node_forge_1.default.pki.certificateFromPem(decryptCertificate);
// 获取证书的公钥
const publicKey = cert.publicKey;
item.publicKey = node_forge_1.default.pki.publicKeyToPem(publicKey);
}
return data;
}
else {
throw new Error('拉取平台证书失败');
}
}
/**
* 拉取平台证书到 Pay.certificates 中
* @param apiSecret APIv3密钥
* https://pay.weixin.qq.com/wiki/doc/apiv3/apis/wechatpay5_1.shtml
*/
async fetchCertificates(apiSecret) {
const url = 'https://api.mch.weixin.qq.com/v3/certificates';
const authorization = this.buildAuthorization('GET', url);
const headers = this.getHeaders(authorization, { 'Content-Type': 'application/json' });
const result = await this.httpService.get(url, headers);
if (result.status === 200) {
const data = result?.data?.data;
const newCertificates = {};
data.forEach((item) => {
const decryptCertificate = this.decipher_gcm(item.encrypt_certificate.ciphertext, item.encrypt_certificate.associated_data, item.encrypt_certificate.nonce, apiSecret);
const cert = node_forge_1.default.pki.certificateFromPem(decryptCertificate);
// 获取证书的公钥
const publicKey = cert.publicKey;
newCertificates[item.serial_no] = node_forge_1.default.pki.publicKeyToPem(publicKey);
});
Payment.certificates = {
...Payment.certificates,
...newCertificates,
};
}
else {
throw new Error('拉取平台证书失败');
}
}
/**
* 验证签名,提醒:node 取头部信息时需要用小写,例如:req.headers['wechatpay-timestamp']
* @param params.timestamp HTTP头Wechatpay-Timestamp 中的应答时间戳
* @param params.nonce HTTP头Wechatpay-Nonce 中的应答随机串
* @param params.body 应答主体(response Body),需要按照接口返回的顺序进行验签,错误的顺序将导致验签失败。
* @param params.serial HTTP头Wechatpay-Serial 证书序列号
* @param params.signature HTTP头Wechatpay-Signature 签名
* @param params.apiSecret APIv3密钥,如果在 构造器 中有初始化该值(this.key),则可以不传入。当然传入也可以
*/
async verifySign(params) {
const { timestamp, nonce, body, serial, signature, apiSecret } = params;
let publicKey = Payment.certificates[serial];
if (!publicKey) {
await this.fetchCertificates(apiSecret);
}
publicKey = Payment.certificates[serial];
if (!publicKey) {
throw new Error('平台证书序列号不相符,未找到平台序列号');
}
const bodyStr = typeof body === 'string' ? body : JSON.stringify(body);
const data = `${timestamp}\n${nonce}\n${bodyStr}\n`;
const verify = crypto_1.default.createVerify('RSA-SHA256');
verify.update(data);
return verify.verify(publicKey, signature, 'base64');
}
/**
* 敏感信息加密
* @param str 敏感信息字段(如用户的住址、银行卡号、手机号码等)
* @returns
*/
publicEncrypt(str, wxPublicKey, padding = crypto_1.default.constants.RSA_PKCS1_OAEP_PADDING) {
if (![crypto_1.default.constants.RSA_PKCS1_PADDING, crypto_1.default.constants.RSA_PKCS1_OAEP_PADDING].includes(padding)) {
throw new Error(`Doesn't supported the padding mode(${padding}), here's only support RSA_PKCS1_OAEP_PADDING or RSA_PKCS1_PADDING.`);
}
const encrypted = crypto_1.default
.publicEncrypt({
key: wxPublicKey,
padding,
oaepHash: 'sha1',
}, Buffer.from(str, 'utf8'))
.toString('base64');
return encrypted;
}
/**
* 敏感信息解密
* @param str 敏感信息字段(如用户的住址、银行卡号、手机号码等)
* @returns
*/
privateDecrypt(str, padding = crypto_1.default.constants.RSA_PKCS1_OAEP_PADDING) {
if (![crypto_1.default.constants.RSA_PKCS1_PADDING, crypto_1.default.constants.RSA_PKCS1_OAEP_PADDING].includes(padding)) {
throw new Error(`Doesn't supported the padding mode(${padding}), here's only support RSA_PKCS1_OAEP_PADDING or RSA_PKCS1_PADDING.`);
}
const decrypted = crypto_1.default.privateDecrypt({
key: this.privateKey,
padding,
oaepHash: 'sha1',
}, Buffer.from(str, 'base64'));
return decrypted.toString('utf8');
}
/**
* 构建请求签名参数
* @param method Http 请求方式
* @param url 请求接口 例如/v3/certificates
* @param timestamp 获取发起请求时的系统当前时间戳
* @param nonceStr 随机字符串
* @param body 请求报文主体
*/
getSignature(method, nonce_str, timestamp, url, body) {
let str = method + '\n' + url + '\n' + timestamp + '\n' + nonce_str + '\n';
if (body && body instanceof Object)
body = JSON.stringify(body);
if (body)
str = str + body + '\n';
if (method === 'GET')
str = str + '\n';
return this.sha256WithRsa(str);
}
// jsapi 和 app 支付参数签名 加密自动顺序如下 不能错乱
// 应用id
// 时间戳
// 随机字符串
// 预支付交易会话ID
sign(str) {
return this.sha256WithRsa(str);
}
/**
* 获取证书序列号
* @param fileData 证书文件内容
* @returns 证书序列号
*/
getSN(fileData) {
if (!fileData && !this.publicKey)
throw new Error('缺少公钥');
if (!fileData)
fileData = this.publicKey;
let pemContent;
if (Buffer.isBuffer(fileData)) {
pemContent = fileData.toString('utf8');
}
else {
pemContent = fileData;
}
try {
const cert = node_forge_1.default.pki.certificateFromPem(pemContent);
// 确保序列号是十六进制格式
return cert.serialNumber.toUpperCase();
}
catch (error) {
throw new Error(`无法解析证书: ${error.message}`);
}
}
/**
* SHA256withRSA
* @param data 待加密字符
* @param privatekey 私钥key key.pem fs.readFileSync(keyPath)
*/
sha256WithRsa(data) {
if (!this.privateKey)
throw new Error('缺少秘钥文件');
return crypto_1.default.createSign('RSA-SHA256').update(data).sign(this.privateKey, 'base64');
}
/**
* 获取授权认证信息
* @param nonceStr 请求随机串
* @param timestamp 时间戳
* @param signature 签名值
*/
getAuthorization(nonce_str, timestamp, signature) {
const _authorization = 'mchid="' +
this.mchid +
'",' +
'nonce_str="' +
nonce_str +
'",' +
'timestamp="' +
timestamp +
'",' +
'serial_no="' +
this.serial_no +
'",' +
'signature="' +
signature +
'"';
return this.authType.concat(' ').concat(_authorization);
}
/**
* 回调解密
* @param ciphertext Base64编码后的开启/停用结果数据密文
* @param associated_data 附加数据
* @param nonce 加密使用的随机串
* @param key APIv3密钥
*/
decipher_gcm(ciphertext, associated_data, nonce, key) {
if (!key)
key = this.key;
if (!key)
throw new Error('缺少key');
const _ciphertext = Buffer.from(ciphertext, 'base64');
// 解密 ciphertext字符 AEAD_AES_256_GCM算法
const authTag = _ciphertext.slice(_ciphertext.length - 16);
const data = _ciphertext.slice(0, _ciphertext.length - 16);
const decipher = crypto_1.default.createDecipheriv('aes-256-gcm', key, nonce);
decipher.setAuthTag(authTag);
decipher.setAAD(Buffer.from(associated_data));
const decoded = decipher.update(data, undefined, 'utf8');
try {
return JSON.parse(decoded);
}
catch (e) {
return decoded;
}
}
/**
* 参数初始化
*/
buildAuthorization(method, url, params) {
const nonce_str = Math.random().toString(36).substr(2, 15), timestamp = parseInt(+new Date() / 1000 + '').toString();
const signature = this.getSignature(method, nonce_str, timestamp, url.replace('https://api.mch.weixin.qq.com', ''), params);
const authorization = this.getAuthorization(nonce_str, timestamp, signature);
return authorization;
}
//#region 支付相关接口
/**
* h5支付
* @param params 请求参数 object 参数介绍 请看文档https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter3_3_1.shtml
*/
async transactions_h5(params) {
// 请求参数
const _params = {
appid: this.appid,
mchid: this.mchid,
...params,
};
const url = 'https://api.mch.weixin.qq.com/v3/pay/transactions/h5';
const authorization = this.buildAuthorization('POST', url, _params);
const headers = this.getHeaders(authorization, { 'Content-Type': 'application/json' });
return await this.httpService.post(url, _params, headers);
}
/**
* 合单h5支付
* @param params 请求参数 object 参数介绍 请看文档https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter5_1_2.shtml
*/
async combine_transactions_h5(params) {
// 请求参数
const _params = {
combine_appid: this.appid,
combine_mchid: this.mchid,
...params,
};
const url = 'https://api.mch.weixin.qq.com/v3/combine-transactions/h5';
const authorization = this.buildAuthorization('POST', url, _params);
const headers = this.getHeaders(authorization, { 'Content-Type': 'application/json' });
return await this.httpService.post(url, _params, headers);
}
/**
* native支付
* @param params 请求参数 object 参数介绍 请看文档https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter3_4_1.shtml
*/
async transactions_native(params) {
// 请求参数
const _params = {
appid: this.appid,
mchid: this.mchid,
...params,
};
const url = 'https://api.mch.weixin.qq.com/v3/pay/transactions/native';
const authorization = this.buildAuthorization('POST', url, _params);
const headers = this.getHeaders(authorization, { 'Content-Type': 'application/json' });
return await this.httpService.post(url, _params, headers);
}
/**
* 合单native支付
* @param params 请求参数 object 参数介绍 请看文档https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter5_1_5.shtml
*/
async combine_transactions_native(params) {
// 请求参数
const _params = {
combine_appid: this.appid,
combine_mchid: this.mchid,
...params,
};
const url = 'https://api.mch.weixin.qq.com/v3/combine-transactions/native';
const authorization = this.buildAuthorization('POST', url, _params);
const headers = this.getHeaders(authorization, { 'Content-Type': 'application/json' });
return await this.httpService.post(url, _params, headers);
}
/**
* app支付
* @param params 请求参数 object 参数介绍 请看文档https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter3_2_1.shtml
*/
async transactions_app(params) {
// 请求参数
const _params = {
appid: this.appid,
mchid: this.mchid,
...params,
};
const url = 'https://api.mch.weixin.qq.com/v3/pay/transactions/app';
const authorization = this.buildAuthorization('POST', url, _params);
const headers = this.getHeaders(authorization, { 'Content-Type': 'application/json' });
const result = await this.httpService.post(url, _params, headers);
if (result.status === 200 && result.data.prepay_id) {
const data = {
appid: _params.appid,
partnerid: _params.mchid,
package: 'Sign=WXPay',
timestamp: parseInt(+new Date() / 1000 + '').toString(),
noncestr: Math.random().toString(36).substr(2, 15),
prepayid: result.data.prepay_id,
sign: '',
};
const str = [data.appid, data.timestamp, data.noncestr, data.prepayid, ''].join('\n');
data.sign = this.sign(str);
result.data = data;
}
return result;
}
/**
* 合单app支付
* @param params 请求参数 object 参数介绍 请看文档https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter5_1_1.shtml
*/
async combine_transactions_app(params) {
// 请求参数
const _params = {
combine_appid: this.appid,
combine_mchid: this.mchid,
...params,
};
const url = 'https://api.mch.weixin.qq.com/v3/combine-transactions/app';
const authorization = this.buildAuthorization('POST', url, _params);
const headers = this.getHeaders(authorization, { 'Content-Type': 'application/json' });
const result = await this.httpService.post(url, _params, headers);
if (result.status === 200 && result.data.prepay_id) {
const data = {
appid: _params.combine_appid,
partnerid: _params.combine_mchid,
package: 'Sign=WXPay',
timestamp: parseInt(+new Date() / 1000 + '').toString(),
noncestr: Math.random().toString(36).substr(2, 15),
prepayid: result.data.prepay_id,
sign: '',
};
const str = [data.appid, data.timestamp, data.noncestr, data.prepayid, ''].join('\n');
data.sign = this.sign(str);
result.data = data;
}
return result;
}
/**
* JSAPI支付 或者 小程序支付
* @param params 请求参数 object 参数介绍 请看文档https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter3_1_1.shtml
*/
async transactions_jsapi(params) {
// 请求参数
const _params = {
appid: this.appid,
mchid: this.mchid,
...params,
};
const url = 'https://api.mch.weixin.qq.com/v3/pay/transactions/jsapi';
const authorization = this.buildAuthorization('POST', url, _params);
const headers = this.getHeaders(authorization, { 'Content-Type': 'application/json' });
const result = await this.httpService.post(url, _params, headers);
if (result.status === 200 && result.data.prepay_id) {
const data = {
appId: _params.appid,
timeStamp: parseInt(+new Date() / 1000 + '').toString(),
nonceStr: Math.random().toString(36).substr(2, 15),
package: `prepay_id=${result.data.prepay_id}`,
signType: 'RSA',
paySign: '',
};
const str = [data.appId, data.timeStamp, data.nonceStr, data.package, ''].join('\n');
data.paySign = this.sign(str);
result.data = data;
}
return result;
}
/**
* 合单JSAPI支付 或者 小程序支付
* @param params 请求参数 object 参数介绍 请看文档https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter5_1_3.shtml
*/
async combine_transactions_jsapi(params) {
// 请求参数
const _params = {
combine_appid: this.appid,
combine_mchid: this.mchid,
...params,
};
const url = 'https://api.mch.weixin.qq.com/v3/combine-transactions/jsapi';
const authorization = this.buildAuthorization('POST', url, _params);
const headers = this.getHeaders(authorization, { 'Content-Type': 'application/json' });
const result = await this.httpService.post(url, _params, headers);
if (result.status === 200 && result.data.prepay_id) {
const data = {
appId: _params.combine_appid,
timeStamp: parseInt(+new Date() / 1000 + '').toString(),
nonceStr: Math.random().toString(36).substr(2, 15),
package: `prepay_id=${result.data.prepay_id}`,
signType: 'RSA',
paySign: '',
};
const str = [data.appId, data.timeStamp, data.nonceStr, data.package, ''].join('\n');
data.paySign = this.sign(str);
result.data = data;
}
return result;
}
/**
* 查询订单
* @param params 请求参数 object 参数介绍 请看文档https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter3_3_2.shtml
*/
async query(params) {
let url = '';
if (params.transaction_id) {
url = `https://api.mch.weixin.qq.com/v3/pay/transactions/id/${params.transaction_id}?mchid=${this.mchid}`;
}
else if (params.out_trade_no) {
url = `https://api.mch.weixin.qq.com/v3/pay/transactions/out-trade-no/${params.out_trade_no}?mchid=${this.mchid}`;
}
else {
throw new Error('缺少transaction_id或者out_trade_no');
}
const authorization = this.buildAuthorization('GET', url);
const headers = this.getHeaders(authorization);
return await this.httpService.get(url, headers);
}
/**
* 合单查询订单
* @param params 请求参数 object 参数介绍 请看文档https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter5_1_11.shtml
*/
async combine_query(combine_out_trade_no) {
if (!combine_out_trade_no)
throw new Error('缺少combine_out_trade_no');
const url = `https://api.mch.weixin.qq.com/v3/combine-transactions/out-trade-no/${combine_out_trade_no}`;
const authorization = this.buildAuthorization('GET', url);
const headers = this.getHeaders(authorization);
return await this.httpService.get(url, headers);
}
/**
* 关闭订单
* @param out_trade_no 请求参数 商户订单号 参数介绍 请看文档https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter3_3_3.shtml
*/
async close(out_trade_no) {
if (!out_trade_no)
throw new Error('缺少out_trade_no');
// 请求参数
const _params = {
mchid: this.mchid,
};
const url = `https://api.mch.weixin.qq.com/v3/pay/transactions/out-trade-no/${out_trade_no}/close`;
const authorization = this.buildAuthorization('POST', url, _params);
const headers = this.getHeaders(authorization, { 'Content-Type': 'application/json' });
return await this.httpService.post(url, _params, headers);
}
/**
* 合单关闭订单
* @param combine_out_trade_no 请求参数 总订单号 参数介绍 请看文档https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter5_1_12.shtml
* @param sub_orders array 子单信息
*/
async combine_close(combine_out_trade_no, sub_orders) {
if (!combine_out_trade_no)
throw new Error('缺少out_trade_no');
// 请求参数
const _params = {
combine_appid: this.appid,
sub_orders,
};
const url = `https://api.mch.weixin.qq.com/v3/combine-transactions/out-trade-no/${combine_out_trade_no}/close`;
const authorization = this.buildAuthorization('POST', url, _params);
const headers = this.getHeaders(authorization, { 'Content-Type': 'application/json' });
return await this.httpService.post(url, _params, headers);
}
/**
* 申请交易账单
* @param params 请求参数 object 参数介绍 请看文档https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter3_1_6.shtml
*/
async tradebill(params) {
let url = 'https://api.mch.weixin.qq.com/v3/bill/tradebill';
const _params = {
...params,
};
const querystring = Object.keys(_params)
.filter((key) => {
return !!_params[key];
})
.sort()
.map((key) => {
return key + '=' + _params[key];
})
.join('&');
url = url + `?${querystring}`;
const authorization = this.buildAuthorization('GET', url);
const headers = this.getHeaders(authorization);
return await this.httpService.get(url, headers);
}
/**
* 申请资金账单
* @param params 请求参数 object 参数介绍 请看文档https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter3_1_7.shtml
*/
async fundflowbill(params) {
let url = 'https://api.mch.weixin.qq.com/v3/bill/fundflowbill';
const _params = {
...params,
};
const querystring = Object.keys(_params)
.filter((key) => {
return !!_params[key];
})
.sort()
.map((key) => {
return key + '=' + _params[key];
})
.join('&');
url = url + `?${querystring}`;
const authorization = this.buildAuthorization('GET', url);
const headers = this.getHeaders(authorization);
return await this.httpService.get(url, headers);
}
/**
* 下载账单
* @param download_url 请求参数 路径 参数介绍 请看文档https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter3_1_8.shtml
*/
async downloadBill(download_url) {
const authorization = this.buildAuthorization('GET', download_url);
const headers = this.getHeaders(authorization);
return await this.httpService.get(download_url, headers);
}
/**
* 申请退款
* @param params 请求参数 路径 参数介绍 请看文档https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter3_2_9.shtml
*/
async refunds(params) {
const url = 'https://api.mch.weixin.qq.com/v3/refund/domestic/refunds';
// 请求参数
const _params = {
...params,
};
const authorization = this.buildAuthorization('POST', url, _params);
const headers = this.getHeaders(authorization, { 'Content-Type': 'application/json' });
return await this.httpService.post(url, _params, headers);
}
/**
* 查询单笔退款
* @documentation 请求参数 路径 参数介绍 请看文档https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter3_2_10.shtml
*/
async find_refunds(out_refund_no) {
if (!out_refund_no)
throw new Error('缺少out_refund_no');
const url = `https://api.mch.weixin.qq.com/v3/refund/domestic/refunds/${out_refund_no}`;
const authorization = this.buildAuthorization('GET', url);
const headers = this.getHeaders(authorization);
return await this.httpService.get(url, headers);
}
//#endregion 支付相关接口
//#region 商家转账到零钱
/**
* 发起商家转账零钱
* @documentation 请看文档https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter4_3_1.shtml
*/
async batches_transfer(params) {
const url = 'https://api.mch.weixin.qq.com/v3/transfer/batches';
// 请求参数
const _params = {
appid: this.appid,
...params,
};
const serial_no = _params?.wx_serial_no;
delete _params.wx_serial_no;
const authorization = this.buildAuthorization('POST', url, _params);
const headers = this.getHeaders(authorization, {
'Wechatpay-Serial': serial_no || this.serial_no,
'Content-Type': 'application/json',
});
return await this.httpService.post(url, _params, headers);
}
/**
* 微信批次单号查询批次单API
* @documentation 请看文档https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter4_3_2.shtml
*/
async query_batches_transfer_list_wx(params) {
const baseUrl = `https://api.mch.weixin.qq.com/v3/transfer/batches/batch-id/${params.batch_id}`;
const url = baseUrl + this.objectToQueryString(params, ['batch_id']);
const authorization = this.buildAuthorization('GET', url);
const headers = this.getHeaders(authorization);
return await this.httpService.get(url, headers);
}
/**
* 微信明细单号查询明细单API
* @documentation 请看文档https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter4_3_3.shtml
*/
async query_batches_transfer_detail_wx(params) {
const baseUrl = `https://api.mch.weixin.qq.com/v3/transfer/batches/batch-id/${params.batch_id}/details/detail-id/${params.detail_id}`;
const url = baseUrl + this.objectToQueryString(params, ['batch_id', 'detail_id']);
const authorization = this.buildAuthorization('GET', url);
const headers = this.getHeaders(authorization);
return await this.httpService.get(url, headers);
}
/**
* 商家批次单号查询批次单API
* @documentation 请看文档https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter4_3_5.shtml
*/
async query_batches_transfer_list(params) {
const baseUrl = `https://api.mch.weixin.qq.com/v3/transfer/batches/out-batch-no/${params.out_batch_no}`;
const url = baseUrl + this.objectToQueryString(params, ['out_batch_no']);
const authorization = this.buildAuthorization('GET', url);
const headers = this.getHeaders(authorization);
return await this.httpService.get(url, headers);
}
/**
* 商家明细单号查询明细单API
* @documentation 请看文档https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter4_3_6.shtml
*/
async query_batches_transfer_detail(params) {
const baseUrl = `https://api.mch.weixin.qq.com/v3/transfer/batches/out-batch-no/${params.out_batch_no}/details/out-detail-no/${params.out_detail_no}`;
const url = baseUrl + this.objectToQueryString(params, ['out_batch_no', 'out_detail_no']);
const authorization = this.buildAuthorization('GET', url);
const headers = this.getHeaders(authorization);
return await this.httpService.get(url, headers);
}
//#endregion 商家转账到零钱
//#region 分账
/**
* 请求分账API
* @param params
* @returns
* @documentation 请看文档https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter8_1_1.shtml
*/
async create_profitsharing_orders(params) {
const url = 'https://api.mch.weixin.qq.com/v3/profitsharing/orders';
// 请求参数
const _params = {
appid: this.appid,
...params,
};
const serial_no = _params?.wx_serial_no;
delete _params.wx_serial_no;
const authorization = this.buildAuthorization('POST', url, _params);
const headers = this.getHeaders(authorization, {
'Wechatpay-Serial': serial_no || this.serial_no,
'Content-Type': 'application/json',
});
return await this.httpService.post(url, _params, headers);
}
/**
* 查询分账结果API
* @documentation 请看文档https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter8_1_2.shtml
*/
async query_profitsharing_orders(transaction_id, out_order_no) {
if (!transaction_id)
throw new Error('缺少transaction_id');
if (!out_order_no)
throw new Error('缺少out_order_no');
let url = `https://api.mch.weixin.qq.com/v3/profitsharing/orders/${out_order_no}`;
url = url + this.objectToQueryString({ transaction_id });
const authorization = this.buildAuthorization('GET', url);
const headers = this.getHeaders(authorization);
return await this.httpService.get(url, headers);
}
/**
* 请求分账回退API
* @documentation 请看文档https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter8_1_3.shtml
*/
async profitsharing_return_orders(params) {
const url = 'https://api.mch.weixin.qq.com/v3/profitsharing/return-orders';
// 请求参数
const _params = {
...params,
};
const authorization = this.buildAuthorization('POST', url, _params);
const headers = this.getHeaders(authorization, { 'Content-Type': 'application/json' });
return await this.httpService.post(url, _params, headers);
}
/**
* 查询分账回退结果API
* @documentation 请看文档https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter8_1_4.shtml
*/
async query_profitsharing_return_orders(out_return_no, out_order_no) {
if (!out_return_no)
throw new Error('缺少out_return_no');
if (!out_order_no)
throw new Error('缺少out_order_no');
let url = `https://api.mch.weixin.qq.com/v3/profitsharing/return-orders/${out_return_no}`;
url = url + this.objectToQueryString({ out_order_no });
const authorization = this.buildAuthorization('GET', url);
const headers = this.getHeaders(authorization);
return await this.httpService.get(url, headers);
}
/**
* 解冻剩余资金API
* @documentation 请看文档https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter8_1_5.shtml
*/
async profitsharing_orders_unfreeze(params) {
const url = 'https://api.mch.weixin.qq.com/v3/profitsharing/orders/unfreeze';
// 请求参数
const _params = {
...params,
};
const authorization = this.buildAuthorization('POST', url, _params);
const headers = this.getHeaders(authorization, { 'Content-Type': 'application/json' });
return await this.httpService.post(url, _params, headers);
}
/**
* 查询剩余待分金额API
* @documentation 请看文档https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter8_1_6.shtml
*/
async query_profitsharing_amounts(transaction_id) {
if (!transaction_id)
throw new Error('缺少transaction_id');
const url = `https://api.mch.weixin.qq.com/v3/profitsharing/transactions/${transaction_id}/amounts`;
const authorization = this.buildAuthorization('GET', url);
const headers = this.getHeaders(authorization);
return await this.httpService.get(url, headers);
}
/**
* 添加分账接收方API
* @documentation https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter8_1_8.shtml
*/
async profitsharing_receivers_add(params) {
const url = 'https://api.mch.weixin.qq.com/v3/profitsharing/receivers/add';
// 请求参数
const _params = {
appid: this.appid,
...params,
};
const serial_no = _params?.wx_serial_no;
delete _params.wx_serial_no;
const authorization = this.buildAuthorization('POST', url, _params);
const headers = this.getHeaders(authorization, {
'Wechatpay-Serial': serial_no || this.serial_no,
'Content-Type': 'application/json',
});
return await this.httpService.post(url, _params, headers);
}
/**
* 删除分账接收方API
* @documentation https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter8_1_9.shtml
*/
async profitsharing_receivers_delete(params) {
const url = 'https://api.mch.weixin.qq.com/v3/profitsharing/receivers/delete';
// 请求参数
const _params = {
appid: this.appid,
...params,
};
const authorization = this.buildAuthorization('POST', url, _params);
const headers = this.getHeaders(authorization, { 'Content-Type': 'application/json' });
return await this.httpService.post(url, _params, headers);
}
/**
* 申请分账账单API
* @documentation https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter8_1_11.shtml
*/
async profitsharing_bills(bill_date, tar_type) {
if (!bill_date)
throw new Error('缺少bill_date');
let url = `https://api.mch.weixin.qq.com/v3/profitsharing/bills`;
url = url + this.objectToQueryString({ bill_date, ...(tar_type && { tar_type }) });
const authorization = this.buildAuthorization('GET', url);
const headers = this.getHeaders(authorization);
return await this.httpService.get(url, headers);
}
//#endregion 分账
async upload_images(pic_buffer, filename) {
//meta信息
const fileinfo = {
filename,
sha256: '',
};
const sign = crypto_1.default.createHash('sha256');
sign.update(pic_buffer);
fileinfo.sha256 = sign.digest('hex');
const url = '/v3/merchant/media/upload';
const authorization = this.buildAuthorization('POST', url, fileinfo);
const headers = this.getHeaders(authorization, { 'Content-Type': 'multipart/form-data;boundary=boundary' });
return await this.httpService.post(url, {
fileinfo,
pic_buffer,
}, headers);
}
}
exports.Payment = Payment;