278 lines
11 KiB
JavaScript
278 lines
11 KiB
JavaScript
|
|
|
|||
|
|
let alipay = require("alipay-sdk");
|
|||
|
|
const AlipaySdkConfig = {
|
|||
|
|
appId: process.env.CONFIG_ALIPAY_MINI_APPID,
|
|||
|
|
privateKey:process.env.CONFIG_ALIPAY_MINI_PRIVATE_KEY,
|
|||
|
|
alipayPublicKey:process.env.CONFIG_ALIPAY_MINI_PUBLIC_KEY,
|
|||
|
|
};
|
|||
|
|
const AlipaySdk = alipay.AlipaySdk;
|
|||
|
|
const alipaySdk = new AlipaySdk(AlipaySdkConfig);
|
|||
|
|
const notifyUrl = process.env.CONFIG_ALIPAY_PAY_NOTIFY
|
|||
|
|
|
|||
|
|
console.log('===============支付宝AppId')
|
|||
|
|
console.log(AlipaySdkConfig.appId)
|
|||
|
|
console.log('===============支付宝公钥')
|
|||
|
|
console.log(AlipaySdkConfig.alipayPublicKey)
|
|||
|
|
console.log('===============支付宝私钥')
|
|||
|
|
console.log(AlipaySdkConfig.privateKey)
|
|||
|
|
const TradeStatus = {
|
|||
|
|
WAIT_BUYER_PAY: 'WAIT_BUYER_PAY',//交易创建,等待买家付款
|
|||
|
|
TRADE_CLOSED: 'TRADE_CLOSED',//未付款交易超时关闭,或支付完成后全额退款
|
|||
|
|
TRADE_SUCCESS: 'TRADE_SUCCESS',//交易成功
|
|||
|
|
TRADE_FINISHED: 'TRADE_FINISHED',//交易完成
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
class aliplayApi {
|
|||
|
|
/**
|
|||
|
|
* 获取accToken
|
|||
|
|
* @param {obj} param0
|
|||
|
|
*/
|
|||
|
|
static async accToken({ code }) {
|
|||
|
|
try {
|
|||
|
|
let params = {
|
|||
|
|
grantType: 'authorization_code',
|
|||
|
|
code,
|
|||
|
|
};
|
|||
|
|
let options = {};
|
|||
|
|
let res = await alipaySdk.exec("alipay.system.oauth.token", params, options);
|
|||
|
|
const accessToken = res && res.accessToken ? res.accessToken : '';
|
|||
|
|
if (accessToken) return accessToken
|
|||
|
|
return Promise.reject({ message: res && res.subMsg ? res.subMsg : '获取token失败' });
|
|||
|
|
}
|
|||
|
|
catch (e) {
|
|||
|
|
return Promise.reject({ message: e.message || '获取token失败' });
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
/**
|
|||
|
|
* 获取用户信息
|
|||
|
|
* @param {obj} param0
|
|||
|
|
*/
|
|||
|
|
static async userInfo(auth_token) {
|
|||
|
|
try {
|
|||
|
|
let res = await alipaySdk.exec("alipay.user.info.share", { auth_token }, {});
|
|||
|
|
if (res && res.code === '10000') {
|
|||
|
|
const { avatar, certNo, mobile, userName, userId } = res;
|
|||
|
|
return { avatar, certNo, mobile, userName, userId };
|
|||
|
|
}
|
|||
|
|
return Promise.reject({ message: res && res.subMsg ? res.subMsg : '获取用户信息失败' });
|
|||
|
|
}
|
|||
|
|
catch (e) {
|
|||
|
|
return Promise.reject({ message: e.message || '获取用户信息失败' });
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
/**
|
|||
|
|
* 创建支付宝交易订单
|
|||
|
|
* @param {Object} params - 订单参数
|
|||
|
|
* @param {String} params.outTradeNo - 商户订单号,必填
|
|||
|
|
* @param {String} params.totalAmount - 订单总金额,单位为元,精确到小数点后两位,必填
|
|||
|
|
* @param {String} params.subject - 订单标题,必填
|
|||
|
|
* @param {String} params.buyerId - 买家支付宝用户ID,小程序场景下必填
|
|||
|
|
* @param {String} params.timeExpire - 订单绝对超时时间 【示例值】2021-12-31 10:05:00
|
|||
|
|
*/
|
|||
|
|
static async createTrade({ outTradeNo, totalAmount, subject, buyerId, timeExpire }) {
|
|||
|
|
try {
|
|||
|
|
// 参数校验
|
|||
|
|
if (!outTradeNo) {
|
|||
|
|
return Promise.reject({ message: '商户订单号不能为空' });
|
|||
|
|
}
|
|||
|
|
if (!totalAmount) {
|
|||
|
|
return Promise.reject({ message: '订单总金额不能为空' });
|
|||
|
|
}
|
|||
|
|
if (!subject) {
|
|||
|
|
return Promise.reject({ message: '订单标题不能为空' });
|
|||
|
|
}
|
|||
|
|
if (!buyerId) {
|
|||
|
|
return Promise.reject({ message: '买家支付宝用户ID不能为空' });
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 构建请求参数
|
|||
|
|
let bizContent = {
|
|||
|
|
out_trade_no: outTradeNo,
|
|||
|
|
total_amount: totalAmount,
|
|||
|
|
subject: subject,
|
|||
|
|
buyer_id: buyerId,
|
|||
|
|
product_code: 'JSAPI_PAY',
|
|||
|
|
time_expire: timeExpire,
|
|||
|
|
op_app_id: AlipaySdkConfig.appId,
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
let res = await alipaySdk.exec("alipay.trade.create", { notifyUrl, bizContent });
|
|||
|
|
console.log('支付宝创建交易订单响应===')
|
|||
|
|
console.log(res)
|
|||
|
|
console.log('notifyUrl', notifyUrl)
|
|||
|
|
// 检查响应结果
|
|||
|
|
if (res && res.code === '10000') {
|
|||
|
|
// 返回交易号
|
|||
|
|
return {
|
|||
|
|
notifyUrl,
|
|||
|
|
appId: AlipaySdkConfig.appId,
|
|||
|
|
tradeNo: res.tradeNo, // 支付宝交易号
|
|||
|
|
outTradeNo: res.outTradeNo, // 商户订单号
|
|||
|
|
};
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 返回错误信息
|
|||
|
|
return Promise.reject({
|
|||
|
|
message: res && res.subMsg ? res.subMsg : '创建交易失败',
|
|||
|
|
code: res && res.code ? res.code : '',
|
|||
|
|
});
|
|||
|
|
} catch (e) {
|
|||
|
|
return Promise.reject({ message: `创建交易失败: ${e.message}` });
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
static async queryTrade(trade_no) {
|
|||
|
|
try {
|
|||
|
|
let res = await alipaySdk.exec("alipay.trade.query", { bizContent: { trade_no } });
|
|||
|
|
if (res && res.code === '10000') {
|
|||
|
|
return res
|
|||
|
|
}
|
|||
|
|
return Promise.reject({
|
|||
|
|
message: res && res.subMsg ? res.subMsg : '创建交易失败',
|
|||
|
|
code: res && res.code ? res.code : '',
|
|||
|
|
});
|
|||
|
|
} catch (e) {
|
|||
|
|
return Promise.reject({ message: `查询交易失败: ${e.message}` });
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
/**
|
|||
|
|
* 解析支付宝支付成功回调
|
|||
|
|
* @param {Object} notifyData - 支付宝回调数据(可以是 req.body 或 req.query)
|
|||
|
|
* @returns {Object} 解析后的回调数据
|
|||
|
|
* @returns {String} outTradeNo - 商户订单号
|
|||
|
|
* @returns {String} tradeNo - 支付宝交易号
|
|||
|
|
* @returns {String} tradeStatus - 交易状态
|
|||
|
|
* @returns {String} totalAmount - 订单总金额
|
|||
|
|
* @returns {String} buyerId - 买家支付宝用户ID
|
|||
|
|
* @returns {String} gmtPayment - 支付时间
|
|||
|
|
*/
|
|||
|
|
static parsePayNotify(notifyData) {
|
|||
|
|
try {
|
|||
|
|
// 如果 notifyData 是对象,直接使用;如果是数组,取第一个元素
|
|||
|
|
const params = notifyData && typeof notifyData === 'object' ? notifyData : {};
|
|||
|
|
|
|||
|
|
// 验证签名
|
|||
|
|
// 支付宝SDK的签名验证方法可能因版本而异
|
|||
|
|
// 尝试使用 checkNotifySign 方法验证签名,如果不存在则使用 verify 方法
|
|||
|
|
let signVerified = false;
|
|||
|
|
if (typeof alipaySdk.checkNotifySign === 'function') {
|
|||
|
|
signVerified = alipaySdk.checkNotifySign(params, AlipaySdkConfig.alipayPublicKey);
|
|||
|
|
} else if (typeof alipaySdk.verify === 'function') {
|
|||
|
|
signVerified = alipaySdk.verify(params, AlipaySdkConfig.alipayPublicKey);
|
|||
|
|
} else if (typeof alipay.checkNotifySign === 'function') {
|
|||
|
|
// 尝试使用静态方法
|
|||
|
|
signVerified = alipay.checkNotifySign(params, AlipaySdkConfig.alipayPublicKey);
|
|||
|
|
} else {
|
|||
|
|
// 如果SDK没有提供验证方法,记录警告但继续处理(生产环境建议实现签名验证)
|
|||
|
|
console.warn('警告:支付宝SDK未找到签名验证方法,请确保实现签名验证逻辑');
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (!signVerified) {
|
|||
|
|
return Promise.reject({ message: '签名验证失败', verified: false });
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 解析回调数据
|
|||
|
|
const result = {
|
|||
|
|
outTradeNo: params.out_trade_no || params.outTradeNo, // 商户订单号
|
|||
|
|
tradeNo: params.trade_no || params.tradeNo, // 支付宝交易号
|
|||
|
|
tradeStatus: params.trade_status || params.tradeStatus, // 交易状态
|
|||
|
|
totalAmount: params.total_amount || params.totalAmount, // 订单总金额
|
|||
|
|
buyerId: params.buyer_id || params.buyerId, // 买家支付宝用户ID
|
|||
|
|
buyerLogonId: params.buyer_logon_id || params.buyerLogonId, // 买家支付宝账号
|
|||
|
|
sellerId: params.seller_id || params.sellerId, // 卖家支付宝用户ID
|
|||
|
|
sellerEmail: params.seller_email || params.sellerEmail, // 卖家支付宝账号
|
|||
|
|
gmtCreate: params.gmt_create || params.gmtCreate, // 交易创建时间
|
|||
|
|
gmtPayment: params.gmt_payment || params.gmtPayment, // 交易支付时间
|
|||
|
|
gmtClose: params.gmt_close || params.gmtClose, // 交易关闭时间
|
|||
|
|
subject: params.subject, // 订单标题
|
|||
|
|
body: params.body, // 订单描述
|
|||
|
|
receiptAmount: params.receipt_amount || params.receiptAmount, // 实收金额
|
|||
|
|
invoiceAmount: params.invoice_amount || params.invoiceAmount, // 开票金额
|
|||
|
|
pointAmount: params.point_amount || params.pointAmount, // 集分宝金额
|
|||
|
|
fundBillList: params.fund_bill_list || params.fundBillList, // 支付金额信息
|
|||
|
|
charset: params.charset, // 编码格式
|
|||
|
|
notifyId: params.notify_id || params.notifyId, // 通知校验ID
|
|||
|
|
notifyType: params.notify_type || params.notifyType, // 通知类型
|
|||
|
|
notifyTime: params.notify_time || params.notifyTime, // 通知时间
|
|||
|
|
version: params.version, // 接口版本
|
|||
|
|
signType: params.sign_type || params.signType, // 签名类型
|
|||
|
|
sign: params.sign, // 签名
|
|||
|
|
appId: params.app_id || params.appId, // 支付宝分配给开发者的应用Id
|
|||
|
|
verified: true, // 签名验证通过标识
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
// 检查交易状态
|
|||
|
|
const tradeStatus = result.tradeStatus;
|
|||
|
|
if (tradeStatus === 'TRADE_SUCCESS' || tradeStatus === 'TRADE_FINISHED') {
|
|||
|
|
result.isSuccess = true;
|
|||
|
|
} else {
|
|||
|
|
result.isSuccess = false;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return result;
|
|||
|
|
} catch (e) {
|
|||
|
|
return Promise.reject({
|
|||
|
|
message: `解析支付回调失败: ${e.message}`,
|
|||
|
|
verified: false,
|
|||
|
|
error: e
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
static parseRefundNotify(notifyData) {
|
|||
|
|
try {
|
|||
|
|
const params = notifyData && typeof notifyData === 'object' ? notifyData : {};
|
|||
|
|
let signVerified = false;
|
|||
|
|
if (typeof alipaySdk.checkNotifySign === 'function') {
|
|||
|
|
signVerified = alipaySdk.checkNotifySign(params, AlipaySdkConfig.alipayPublicKey);
|
|||
|
|
} else if (typeof alipaySdk.verify === 'function') {
|
|||
|
|
signVerified = alipaySdk.verify(params, AlipaySdkConfig.alipayPublicKey);
|
|||
|
|
} else if (typeof alipay.checkNotifySign === 'function') {
|
|||
|
|
signVerified = alipay.checkNotifySign(params, AlipaySdkConfig.alipayPublicKey);
|
|||
|
|
} else {
|
|||
|
|
console.warn('警告:支付宝SDK未找到签名验证方法,请确保实现签名验证逻辑');
|
|||
|
|
}
|
|||
|
|
if (!signVerified) {
|
|||
|
|
return Promise.reject({ message: '签名验证失败', verified: false });
|
|||
|
|
}
|
|||
|
|
return params;
|
|||
|
|
} catch (e) {
|
|||
|
|
return Promise.reject({ message: `解析退款回调失败: ${e.message}` });
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
static async refundTrade({ tradeNo, totalAmount: refundAmount, refundReason }) {
|
|||
|
|
try {
|
|||
|
|
let res = await alipaySdk.exec("alipay.trade.refund", {
|
|||
|
|
bizContent: {
|
|||
|
|
refund_amount: refundAmount,
|
|||
|
|
trade_no: tradeNo,
|
|||
|
|
refund_reason: refundReason
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
if (res && res.code === '10000') {
|
|||
|
|
return res
|
|||
|
|
}
|
|||
|
|
return Promise.reject({
|
|||
|
|
message: res && res.subMsg ? res.subMsg : '退款失败',
|
|||
|
|
code: res && res.code ? res.code : '',
|
|||
|
|
});
|
|||
|
|
} catch (e) {
|
|||
|
|
return Promise.reject({ message: `退款失败: ${e.message}` });
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
static async queryRefundDetail(trade_no, out_request_no) {
|
|||
|
|
try {
|
|||
|
|
let res = await alipaySdk.exec("alipay.trade.fastpay.refund.query", { bizContent: { trade_no, out_request_no, query_options: ['gmt_refund_pay'] } });
|
|||
|
|
if (res && res.code === '10000') {
|
|||
|
|
return { success: true, data: res }
|
|||
|
|
}
|
|||
|
|
return { success: false, message: res && res.subMsg ? res.subMsg : '查询退款失败', data: res };
|
|||
|
|
} catch (e) {
|
|||
|
|
return Promise.reject({ message: `查询退款详情失败: ${e.message}` });
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
module.exports = aliplayApi;
|