1128 lines
35 KiB
JavaScript
1128 lines
35 KiB
JavaScript
|
/**
|
|||
|
* uni-pay-co 统一支付服务实现
|
|||
|
*/
|
|||
|
|
|||
|
const crypto = require("crypto");
|
|||
|
|
|||
|
const uniPay = require("uni-pay");
|
|||
|
|
|||
|
const configCenter = require("uni-config-center");
|
|||
|
|
|||
|
const config = configCenter({ pluginId: 'uni-pay' }).requireFile('config.js');
|
|||
|
|
|||
|
const dao = require('../dao');
|
|||
|
|
|||
|
const libs = require('../libs');
|
|||
|
|
|||
|
const { UniCloudError, isUniPayError, ERROR } = require('../common/error')
|
|||
|
|
|||
|
const db = uniCloud.database();
|
|||
|
const _ = db.command;
|
|||
|
|
|||
|
const notifyPath = "/payNotify/";
|
|||
|
|
|||
|
class service {
|
|||
|
constructor(obj) {
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
/**
|
|||
|
* 获取支付插件的完整配置
|
|||
|
*/
|
|||
|
getConfig() {
|
|||
|
return config;
|
|||
|
}
|
|||
|
|
|||
|
/**
|
|||
|
* 支付成功 - 异步通知
|
|||
|
*/
|
|||
|
async paymentNotify(data = {}) {
|
|||
|
let {
|
|||
|
httpInfo,
|
|||
|
clientInfo,
|
|||
|
cloudInfo,
|
|||
|
isWxpayVirtual
|
|||
|
} = data;
|
|||
|
console.log('httpInfo: ', httpInfo);
|
|||
|
let path = httpInfo.path;
|
|||
|
let pay_type = path.substring(notifyPath.length);
|
|||
|
let provider = pay_type.split("-")[0]; // 获取支付供应商
|
|||
|
let provider_pay_type = pay_type.split("-")[1]; // 获取支付方式
|
|||
|
if (isWxpayVirtual) {
|
|||
|
// 微信虚拟支付固定参数
|
|||
|
provider = "wxpay-virtual";
|
|||
|
provider_pay_type = "mp";
|
|||
|
}
|
|||
|
// 初始化uniPayInstance
|
|||
|
let uniPayInstance = await this.initUniPayInstance({ provider, provider_pay_type });
|
|||
|
let notifyType = await uniPayInstance.checkNotifyType(httpInfo);
|
|||
|
console.log('notifyType: ', notifyType)
|
|||
|
if (notifyType === "token") {
|
|||
|
let verifyResult = await uniPayInstance.verifyTokenNotify(httpInfo);
|
|||
|
console.log('verifyResult: ', verifyResult)
|
|||
|
if (!verifyResult) {
|
|||
|
console.log('---------!签名验证未通过!---------');
|
|||
|
return;
|
|||
|
}
|
|||
|
// 校验token的测试接口,直接返回echostr
|
|||
|
return verifyResult.echostr;
|
|||
|
}
|
|||
|
if (notifyType !== "payment") {
|
|||
|
// 非支付通知直接返回成功
|
|||
|
console.log(`---------!非支付通知!---------`);
|
|||
|
return libs.common.returnNotifySUCCESS({ provider, provider_pay_type });
|
|||
|
}
|
|||
|
// 支付通知,验证签名
|
|||
|
let verifyResult = await uniPayInstance.verifyPaymentNotify(httpInfo);
|
|||
|
if (!verifyResult) {
|
|||
|
console.log('---------!签名验证未通过!---------');
|
|||
|
console.log('---------!签名验证未通过!---------');
|
|||
|
console.log('---------!签名验证未通过!---------');
|
|||
|
return {}
|
|||
|
}
|
|||
|
console.log('---------!签名验证通过!---------');
|
|||
|
verifyResult = JSON.parse(JSON.stringify(verifyResult)); // 这一句代码有用,请勿删除。
|
|||
|
console.log('verifyResult: ', verifyResult)
|
|||
|
let {
|
|||
|
outTradeNo,
|
|||
|
totalFee,
|
|||
|
transactionId,
|
|||
|
resultCode, // 微信支付v2和支付宝支付判断成功的字段
|
|||
|
openid,
|
|||
|
appId,
|
|||
|
tradeState, // 微信支付v3和微信虚拟支付判断支付成功的字段
|
|||
|
} = verifyResult;
|
|||
|
if (resultCode == "SUCCESS" || tradeState === "SUCCESS") {
|
|||
|
let time = Date.now();
|
|||
|
let payOrderInfo = await dao.uniPayOrders.updateAndReturn({
|
|||
|
whereJson: {
|
|||
|
status: 0, // status:0 为必须条件,防止重复推送时的错误
|
|||
|
out_trade_no: outTradeNo, // 商户订单号
|
|||
|
},
|
|||
|
dataJson: {
|
|||
|
status: 1, // 设置为已付款
|
|||
|
transaction_id: transactionId, // 第三方支付单号
|
|||
|
pay_date: time, // 更新支付时间(暂无法准确获取到支付时间,故用通知时间代替支付时间)
|
|||
|
notify_date: time, // 更新通知时间
|
|||
|
openid,
|
|||
|
provider, // 更新provider
|
|||
|
provider_pay_type, // 更新provider_pay_type
|
|||
|
original_data: httpInfo, // http回调信息,便于丢单时手动触发回调
|
|||
|
}
|
|||
|
});
|
|||
|
//console.log('payOrderInfo: ', payOrderInfo)
|
|||
|
if (payOrderInfo) {
|
|||
|
// 只有首次推送才执行用户自己的逻辑处理。
|
|||
|
// 用户自己的逻辑处理 开始-----------------------------------------------------------
|
|||
|
let userOrderSuccess = false;
|
|||
|
let orderPaySuccess;
|
|||
|
try {
|
|||
|
// 加载自定义异步回调函数
|
|||
|
orderPaySuccess = require(`../notify/${payOrderInfo.type}`);
|
|||
|
} catch (err) {
|
|||
|
console.log(err);
|
|||
|
}
|
|||
|
if (typeof orderPaySuccess === "function") {
|
|||
|
console.log('用户自己的回调逻辑 - 开始执行');
|
|||
|
try {
|
|||
|
userOrderSuccess = await orderPaySuccess({
|
|||
|
verifyResult,
|
|||
|
data: payOrderInfo,
|
|||
|
clientInfo,
|
|||
|
cloudInfo
|
|||
|
});
|
|||
|
console.log('用户自己的回调逻辑 - 执行完成');
|
|||
|
} catch(err){
|
|||
|
userOrderSuccess = false;
|
|||
|
console.log('用户自己的回调逻辑 - 执行异常', err);
|
|||
|
}
|
|||
|
}
|
|||
|
console.log('userOrderSuccess', userOrderSuccess);
|
|||
|
// 用户自己的逻辑处理 结束-----------------------------------------------------------
|
|||
|
|
|||
|
await dao.uniPayOrders.updateAndReturn({
|
|||
|
whereJson: {
|
|||
|
status: 1,
|
|||
|
out_trade_no: outTradeNo,
|
|||
|
},
|
|||
|
dataJson: {
|
|||
|
user_order_success: userOrderSuccess,
|
|||
|
}
|
|||
|
});
|
|||
|
|
|||
|
} else {
|
|||
|
console.log('---------!注意:本次回调非首次回调,已被插件拦截,插件不会执行你的回调函数!---------');
|
|||
|
console.log('---------!注意:本次回调非首次回调,已被插件拦截,插件不会执行你的回调函数!---------');
|
|||
|
console.log('---------!注意:本次回调非首次回调,已被插件拦截,插件不会执行你的回调函数!---------');
|
|||
|
console.log('verifyResult:', verifyResult);
|
|||
|
}
|
|||
|
} else {
|
|||
|
console.log('verifyResult:', verifyResult);
|
|||
|
}
|
|||
|
|
|||
|
return libs.common.returnNotifySUCCESS({ provider, provider_pay_type });
|
|||
|
}
|
|||
|
|
|||
|
/**
|
|||
|
* 微信虚拟支付异步通知
|
|||
|
*/
|
|||
|
async wxpayVirtualNotify(data = {}) {
|
|||
|
return this.paymentNotify({
|
|||
|
...data,
|
|||
|
isWxpayVirtual: true
|
|||
|
});
|
|||
|
}
|
|||
|
|
|||
|
/**
|
|||
|
* 统一支付 - 创建支付订单
|
|||
|
*/
|
|||
|
async createOrder(data = {}) {
|
|||
|
let {
|
|||
|
provider, // 支付供应商
|
|||
|
total_fee, // 支付金额
|
|||
|
user_id, // 用户user_id(统计需要)
|
|||
|
openid, // 用户openid
|
|||
|
order_no, // 订单号
|
|||
|
out_trade_no, // 支付插件订单号
|
|||
|
description, // 订单描述
|
|||
|
type, // 回调类型
|
|||
|
qr_code, // 是否强制使用扫码支付
|
|||
|
custom, // 自定义参数(不会发送给第三方支付服务器)
|
|||
|
other, // 其他请求参数(会发送给第三方支付服务器)
|
|||
|
clientInfo, // 客户端信息
|
|||
|
cloudInfo, // 云端信息
|
|||
|
wxpay_virtual, // 仅用于微信虚拟支付
|
|||
|
} = data;
|
|||
|
let subject = description;
|
|||
|
let body = description;
|
|||
|
if (!out_trade_no) out_trade_no = libs.common.createOrderNo();
|
|||
|
if (!order_no || typeof order_no !== "string") {
|
|||
|
throw { errCode: ERROR[51003] };
|
|||
|
}
|
|||
|
if (!type || typeof type !== "string") {
|
|||
|
throw { errCode: ERROR[51004] };
|
|||
|
}
|
|||
|
if (provider === "wxpay-virtual") {
|
|||
|
if (typeof wxpay_virtual !== "object") {
|
|||
|
throw { errCode: ERROR[51011] };
|
|||
|
}
|
|||
|
if (typeof wxpay_virtual.buy_quantity !== "number" || wxpay_virtual.buy_quantity <= 0) {
|
|||
|
throw { errCode: ERROR[51012] };
|
|||
|
}
|
|||
|
} else {
|
|||
|
if (typeof total_fee !== "number" || total_fee <= 0 || total_fee % 1 !== 0) {
|
|||
|
throw { errCode: ERROR[51005] };
|
|||
|
}
|
|||
|
}
|
|||
|
if (!description || typeof description !== "string") {
|
|||
|
throw { errCode: ERROR[51006] };
|
|||
|
}
|
|||
|
if (!provider || typeof provider !== "string") {
|
|||
|
throw { errCode: ERROR[51007] };
|
|||
|
}
|
|||
|
if (!clientInfo) {
|
|||
|
throw { errCode: ERROR[51008] };
|
|||
|
}
|
|||
|
if (!cloudInfo) {
|
|||
|
throw { errCode: ERROR[51009] };
|
|||
|
}
|
|||
|
let res = { errCode: 0, errMsg: 'ok', order_no, out_trade_no, provider };
|
|||
|
|
|||
|
let {
|
|||
|
clientIP: client_ip,
|
|||
|
userAgent: ua,
|
|||
|
appId: appid,
|
|||
|
deviceId: device_id,
|
|||
|
platform
|
|||
|
} = clientInfo;
|
|||
|
let {
|
|||
|
spaceId, // 服务空间ID
|
|||
|
} = cloudInfo;
|
|||
|
let {
|
|||
|
notifyUrl = {}
|
|||
|
} = config;
|
|||
|
// 业务逻辑开始-----------------------------------------------------------
|
|||
|
// 以下代码是为了兼容公测版迁移到正式版的空间
|
|||
|
let notifySpaceId = spaceId;
|
|||
|
if (!notifyUrl[notifySpaceId]) {
|
|||
|
if (notifySpaceId.indexOf("mp-") === 0) {
|
|||
|
notifySpaceId = notifySpaceId.substring(3);
|
|||
|
} else {
|
|||
|
notifySpaceId = `mp-${notifySpaceId}`
|
|||
|
}
|
|||
|
}
|
|||
|
// 以上代码是为了兼容公测版迁移到正式版的空间
|
|||
|
let currentNotifyUrl = notifyUrl[notifySpaceId] || notifyUrl["default"]; // 异步回调地址
|
|||
|
if (!currentNotifyUrl || currentNotifyUrl.indexOf("http") !== 0) {
|
|||
|
throw { errCode: ERROR[52002] };
|
|||
|
}
|
|||
|
platform = libs.common.getPlatform(platform);
|
|||
|
// 如果需要二维码支付模式,则清空下openid
|
|||
|
if (qr_code) {
|
|||
|
openid = undefined;
|
|||
|
res.qr_code = qr_code;
|
|||
|
}
|
|||
|
// 获取并自动匹配支付供应商的支付类型
|
|||
|
let provider_pay_type = libs.common.getProviderPayType({
|
|||
|
platform,
|
|||
|
provider,
|
|||
|
ua,
|
|||
|
qr_code
|
|||
|
});
|
|||
|
res.provider_pay_type = provider_pay_type;
|
|||
|
// 拼接实际异步回调地址
|
|||
|
let finalNotifyUrl = `${currentNotifyUrl}${notifyPath}${provider}-${provider_pay_type}`;
|
|||
|
|
|||
|
// 获取uniPay交易类型
|
|||
|
let tradeType = libs.common.getTradeType({ provider, provider_pay_type });
|
|||
|
|
|||
|
let uniPayConifg = await this.getUniPayConfig({ provider, provider_pay_type });
|
|||
|
// 初始化uniPayInstance
|
|||
|
let uniPayInstance = await this.initUniPayInstance({ provider, provider_pay_type });
|
|||
|
|
|||
|
// 获取支付信息
|
|||
|
let getOrderInfoParam = {
|
|||
|
openid: openid,
|
|||
|
subject: subject,
|
|||
|
body: body,
|
|||
|
outTradeNo: out_trade_no,
|
|||
|
totalFee: total_fee,
|
|||
|
notifyUrl: finalNotifyUrl,
|
|||
|
tradeType: tradeType
|
|||
|
};
|
|||
|
if (provider === "wxpay" && provider_pay_type === "mweb") {
|
|||
|
getOrderInfoParam.spbillCreateIp = client_ip;
|
|||
|
if (uniPayConifg.version !== 3) {
|
|||
|
// v2版本
|
|||
|
getOrderInfoParam.sceneInfo = uniPayConifg.sceneInfo;
|
|||
|
} else {
|
|||
|
// v3版本特殊处理
|
|||
|
getOrderInfoParam.sceneInfo = JSON.parse(JSON.stringify(uniPayConifg.sceneInfo));
|
|||
|
if (getOrderInfoParam.sceneInfo.h5_info.wap_url) {
|
|||
|
getOrderInfoParam.sceneInfo.h5_info.app_url = getOrderInfoParam.sceneInfo.h5_info.wap_url;
|
|||
|
delete getOrderInfoParam.sceneInfo.h5_info.wap_url;
|
|||
|
}
|
|||
|
if (getOrderInfoParam.sceneInfo.h5_info.wap_name) {
|
|||
|
getOrderInfoParam.sceneInfo.h5_info.app_name = getOrderInfoParam.sceneInfo.h5_info.wap_name;
|
|||
|
delete getOrderInfoParam.sceneInfo.h5_info.wap_name;
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
let expand_data;
|
|||
|
try {
|
|||
|
// 如果是苹果内购,不需要执行uniPayInstance.getOrderInfo等操作
|
|||
|
if (provider !== "appleiap") {
|
|||
|
// 第三方支付服务器返回的订单信息
|
|||
|
let orderInfo;
|
|||
|
if (other) {
|
|||
|
// other 内的键名转驼峰
|
|||
|
other = libs.common.snake2camelJson(other);
|
|||
|
getOrderInfoParam = Object.assign(getOrderInfoParam, other);
|
|||
|
}
|
|||
|
getOrderInfoParam = JSON.parse(JSON.stringify(getOrderInfoParam)); // 此为去除undefined的参数
|
|||
|
if (provider === "wxpay-virtual") {
|
|||
|
// 微信虚拟支付扩展数据
|
|||
|
expand_data = {
|
|||
|
mode: wxpay_virtual.mode, // short_series_coin 代币充值; short_series_goods 道具直购
|
|||
|
buy_quantity: wxpay_virtual.buy_quantity,
|
|||
|
rate: uniPayConifg.rate || 100,
|
|||
|
sandbox: uniPayConifg.sandbox,
|
|||
|
};
|
|||
|
if (wxpay_virtual.mode === "short_series_goods") {
|
|||
|
expand_data.product_id = wxpay_virtual.product_id;
|
|||
|
expand_data.goods_price = wxpay_virtual.goods_price;
|
|||
|
}
|
|||
|
// 获取用户的sessionKey
|
|||
|
let { session_key } = await dao.opendbOpenData.getSessionKey({
|
|||
|
appId: uniPayConifg.appId,
|
|||
|
platform: "weixin-mp",
|
|||
|
openid: openid
|
|||
|
});
|
|||
|
getOrderInfoParam.sessionKey = session_key;
|
|||
|
getOrderInfoParam.mode = expand_data.mode;
|
|||
|
getOrderInfoParam.buyQuantity = expand_data.buy_quantity;
|
|||
|
getOrderInfoParam.productId = expand_data.product_id;
|
|||
|
getOrderInfoParam.goodsPrice = expand_data.goods_price;
|
|||
|
if (getOrderInfoParam.mode === "short_series_coin") {
|
|||
|
// 计算支付金额
|
|||
|
total_fee = expand_data.buy_quantity / (expand_data.rate || 100) * 100;
|
|||
|
} else if (getOrderInfoParam.mode === "short_series_goods") {
|
|||
|
// 计算支付金额
|
|||
|
total_fee = expand_data.buy_quantity * expand_data.goods_price;
|
|||
|
}
|
|||
|
}
|
|||
|
orderInfo = await uniPayInstance.getOrderInfo(getOrderInfoParam);
|
|||
|
if (qr_code && orderInfo.codeUrl) {
|
|||
|
res.qr_code_image = await libs.qrcode.toDataURL(orderInfo.codeUrl, {
|
|||
|
type: "image/png",
|
|||
|
width: 200,
|
|||
|
margin: 1,
|
|||
|
scale: 1,
|
|||
|
color: {
|
|||
|
dark: "#000000",
|
|||
|
light: "#ffffff",
|
|||
|
},
|
|||
|
errorCorrectionLevel: "Q",
|
|||
|
quality: 1
|
|||
|
});
|
|||
|
}
|
|||
|
// 支付宝支付参数特殊处理
|
|||
|
if (provider === "alipay") {
|
|||
|
if (typeof orderInfo === "object" && orderInfo.code && orderInfo.code !== "10000") {
|
|||
|
res.errCode = orderInfo.code;
|
|||
|
res.errMsg = orderInfo.subMsg;
|
|||
|
}
|
|||
|
}
|
|||
|
res.order = orderInfo;
|
|||
|
}
|
|||
|
} catch (err) {
|
|||
|
let errMsg = err.errorMessage || err.message;
|
|||
|
console.error("data: ", data);
|
|||
|
console.error("getOrderInfoParam: ", getOrderInfoParam);
|
|||
|
console.error("err: ", err);
|
|||
|
console.error("errMsg: ", errMsg);
|
|||
|
throw { errCode: ERROR[53001], errMsg };
|
|||
|
}
|
|||
|
// 尝试获取下订单信息
|
|||
|
let payOrderInfo = await dao.uniPayOrders.find({
|
|||
|
order_no,
|
|||
|
out_trade_no
|
|||
|
});
|
|||
|
let create_date = Date.now();
|
|||
|
// 如果订单不存在,则添加
|
|||
|
if (!payOrderInfo) {
|
|||
|
// 添加数据库(数据库的out_trade_no字段需设置为唯一索引)
|
|||
|
let stat_platform = clientInfo.platform;
|
|||
|
if (stat_platform === "app") {
|
|||
|
stat_platform = clientInfo.os;
|
|||
|
}
|
|||
|
let nickname;
|
|||
|
if (user_id) {
|
|||
|
// 获取nickname(冗余昵称)
|
|||
|
let userInfo = await dao.uniIdUsers.findById(user_id);
|
|||
|
if (userInfo) nickname = userInfo.nickname;
|
|||
|
}
|
|||
|
await dao.uniPayOrders.add({
|
|||
|
provider,
|
|||
|
provider_pay_type,
|
|||
|
uni_platform: platform,
|
|||
|
status: 0,
|
|||
|
type,
|
|||
|
order_no,
|
|||
|
out_trade_no,
|
|||
|
user_id,
|
|||
|
nickname,
|
|||
|
device_id,
|
|||
|
client_ip,
|
|||
|
openid,
|
|||
|
description,
|
|||
|
total_fee,
|
|||
|
refund_fee: 0,
|
|||
|
refund_count: 0,
|
|||
|
provider_appid: uniPayConifg.appId,
|
|||
|
appid,
|
|||
|
custom,
|
|||
|
create_date,
|
|||
|
expand_data,
|
|||
|
stat_data: {
|
|||
|
platform: stat_platform,
|
|||
|
app_version: clientInfo.appVersion,
|
|||
|
app_version_code: clientInfo.appVersionCode,
|
|||
|
app_wgt_version: clientInfo.appWgtVersion,
|
|||
|
os: clientInfo.os,
|
|||
|
ua: clientInfo.ua,
|
|||
|
channel: clientInfo.channel ? clientInfo.channel : String(clientInfo.scene),
|
|||
|
scene: clientInfo.scene
|
|||
|
}
|
|||
|
});
|
|||
|
} else {
|
|||
|
// 如果订单已经存在,则修改下支付方式(用户可能先点微信支付,未付款,又点了支付宝支付)
|
|||
|
await dao.uniPayOrders.updateById(payOrderInfo._id, {
|
|||
|
provider,
|
|||
|
provider_pay_type,
|
|||
|
});
|
|||
|
}
|
|||
|
// 自动删除3天前的订单(未付款订单)
|
|||
|
// await dao.uniPayOrders.deleteExpPayOrders();
|
|||
|
// 业务逻辑结束-----------------------------------------------------------
|
|||
|
return res;
|
|||
|
}
|
|||
|
/**
|
|||
|
* 统一支付结果查询
|
|||
|
* @description 根据商户订单号或者平台订单号查询订单信息,主要用于未接收到支付通知时可以使用此接口进行支付结果验证
|
|||
|
*/
|
|||
|
async getOrder(data = {}) {
|
|||
|
let {
|
|||
|
out_trade_no, // 支付插件订单号
|
|||
|
transaction_id, // 支付平台的交易单号
|
|||
|
await_notify = false, // 是否需要等待异步通知执行完成才返回前端支付结果
|
|||
|
} = data;
|
|||
|
let res = { errCode: 0, errMsg: 'ok' };
|
|||
|
// 业务逻辑开始-----------------------------------------------------------
|
|||
|
if (!out_trade_no && !transaction_id) {
|
|||
|
throw { errCode: ERROR[51010] };
|
|||
|
}
|
|||
|
let payOrderInfo;
|
|||
|
if (transaction_id) {
|
|||
|
payOrderInfo = await dao.uniPayOrders.find({
|
|||
|
transaction_id
|
|||
|
});
|
|||
|
} else if (out_trade_no) {
|
|||
|
payOrderInfo = await dao.uniPayOrders.find({
|
|||
|
out_trade_no
|
|||
|
});
|
|||
|
}
|
|||
|
if (!payOrderInfo) {
|
|||
|
throw { errCode: ERROR[52001] };
|
|||
|
}
|
|||
|
// 初始化uniPayInstance
|
|||
|
let uniPayInstance = await this.initUniPayInstance(payOrderInfo);
|
|||
|
let orderQueryJson = {};
|
|||
|
if (out_trade_no) {
|
|||
|
orderQueryJson.outTradeNo = out_trade_no;
|
|||
|
} else {
|
|||
|
orderQueryJson.transactionId = transaction_id;
|
|||
|
}
|
|||
|
if (payOrderInfo.provider === "wxpay-virtual") {
|
|||
|
orderQueryJson.openid = payOrderInfo.openid;
|
|||
|
}
|
|||
|
let queryRes;
|
|||
|
if (typeof uniPayInstance.orderQuery === "function") {
|
|||
|
queryRes = await uniPayInstance.orderQuery(orderQueryJson);
|
|||
|
console.log('queryRes: ', queryRes)
|
|||
|
} else {
|
|||
|
// 无uniPayInstance.orderQuery函数时的兼容处理
|
|||
|
if ([1, 2].indexOf(payOrderInfo.status) > -1) {
|
|||
|
queryRes = {
|
|||
|
tradeState: "SUCCESS",
|
|||
|
tradeStateDesc: "订单已支付"
|
|||
|
};
|
|||
|
} else if ([3].indexOf(payOrderInfo.status) > -1) {
|
|||
|
queryRes = {
|
|||
|
tradeState: "REFUNDED",
|
|||
|
tradeStateDesc: "订单已退款"
|
|||
|
};
|
|||
|
} else {
|
|||
|
queryRes = {
|
|||
|
tradeState: "NOPAY",
|
|||
|
tradeStateDesc: "订单未支付"
|
|||
|
};
|
|||
|
}
|
|||
|
}
|
|||
|
if (queryRes.tradeState === 'SUCCESS' || queryRes.tradeState === 'FINISHED') {
|
|||
|
if (typeof payOrderInfo.user_order_success == "undefined" && await_notify) {
|
|||
|
let whileTime = 0; // 当前循环已执行的时间(毫秒)
|
|||
|
let whileInterval = 500; // 每次循环间隔时间(毫秒)
|
|||
|
let maxTime = 20000; // 循环执行时间超过此值则退出循环(毫秒)
|
|||
|
while (typeof payOrderInfo.user_order_success == "undefined" && whileTime <= maxTime) {
|
|||
|
await libs.common.sleep(whileInterval);
|
|||
|
whileTime += whileInterval;
|
|||
|
payOrderInfo = await dao.uniPayOrders.find({
|
|||
|
out_trade_no
|
|||
|
});
|
|||
|
}
|
|||
|
}
|
|||
|
res = {
|
|||
|
errCode: 0,
|
|||
|
errMsg: "ok",
|
|||
|
has_paid: true, // 标记用户是否已付款成功(此参数只能表示用户确实付款了,但系统的异步回调逻辑可能还未执行完成)
|
|||
|
out_trade_no, // 支付插件订单号
|
|||
|
transaction_id, // 支付平台订单号
|
|||
|
status: payOrderInfo.status, // 标记当前支付订单状态 -1:已关闭 0:未支付 1:已支付 2:已部分退款 3:已全额退款
|
|||
|
user_order_success: payOrderInfo.user_order_success, // 用户异步通知逻辑是否全部执行完成,且无异常(建议前端通过此参数是否为true来判断是否支付成功)
|
|||
|
pay_order: payOrderInfo,
|
|||
|
}
|
|||
|
} else {
|
|||
|
let errMsg = queryRes.tradeStateDesc || "未支付或已退款";
|
|||
|
if (errMsg.indexOf("订单发生过退款") > -1) {
|
|||
|
errMsg = "订单已退款";
|
|||
|
}
|
|||
|
res = {
|
|||
|
errCode: -1,
|
|||
|
errMsg: errMsg,
|
|||
|
has_paid: false,
|
|||
|
out_trade_no, // 支付插件订单号
|
|||
|
transaction_id, // 支付平台订单号
|
|||
|
}
|
|||
|
}
|
|||
|
// 业务逻辑结束-----------------------------------------------------------
|
|||
|
return res;
|
|||
|
}
|
|||
|
|
|||
|
/**
|
|||
|
* 统一退款
|
|||
|
* @description 当交易发生之后一段时间内,由于买家或者卖家的原因需要退款时,卖家可以通过退款接口将支付款退还给买家。
|
|||
|
*/
|
|||
|
async refund(data = {}) {
|
|||
|
let {
|
|||
|
out_trade_no, // 插件支付单号
|
|||
|
out_refund_no, // 退款单号(若不传,则自动生成)
|
|||
|
refund_desc = "用户申请退款",
|
|||
|
refund_fee: myRefundFee,
|
|||
|
refund_fee_type = "CNY"
|
|||
|
} = data;
|
|||
|
|
|||
|
let res = { errCode: 0, errMsg: 'ok' };
|
|||
|
// 业务逻辑开始-----------------------------------------------------------
|
|||
|
if (!out_trade_no) {
|
|||
|
throw { errCode: ERROR[51001] };
|
|||
|
}
|
|||
|
let payOrderInfo = await dao.uniPayOrders.find({
|
|||
|
out_trade_no
|
|||
|
});
|
|||
|
if (!payOrderInfo) {
|
|||
|
throw { errCode: ERROR[52001] };
|
|||
|
}
|
|||
|
let refund_count = payOrderInfo.refund_count || 0;
|
|||
|
refund_count++;
|
|||
|
// 生成退款订单号
|
|||
|
let outRefundNo = out_refund_no ? out_refund_no : `${out_trade_no}-${refund_count}`;
|
|||
|
// 订单总金额
|
|||
|
let totalFee = payOrderInfo.total_fee;
|
|||
|
// 退款总金额
|
|||
|
let refundFee = myRefundFee || totalFee;
|
|||
|
let provider = payOrderInfo.provider;
|
|||
|
let uniPayConifg = await this.getUniPayConfig(payOrderInfo);
|
|||
|
let uniPayInstance = await this.initUniPayInstance(payOrderInfo);
|
|||
|
let refundParams = {
|
|||
|
outTradeNo: out_trade_no,
|
|||
|
outRefundNo,
|
|||
|
totalFee,
|
|||
|
refundFee,
|
|||
|
refundDesc: refund_desc,
|
|||
|
refundFeeType: refund_fee_type
|
|||
|
};
|
|||
|
if (payOrderInfo.provider === "wxpay-virtual") {
|
|||
|
refundParams.openid = payOrderInfo.openid;
|
|||
|
refundParams.refundReason = "3"; // 0-暂无描述 1-产品问题,影响使用或效果不佳 2-售后问题,无法满足需求 3-意愿问题,用户主动退款 4-价格问题 5-其他原因
|
|||
|
refundParams.reqFrom = "2"; // 当前只支持"1"-人工客服退款,即用户电话给客服,由客服发起退款流程 "2"-用户自己发起退款流程 "3"-其他
|
|||
|
// 查询当前可退款金额
|
|||
|
refundParams.leftFee = totalFee - (payOrderInfo.refund_fee || 0);
|
|||
|
// 实时查询当前可退款金额(此处注释可省一次请求)
|
|||
|
// const orderQueryRes = await uniPayInstance.orderQuery({
|
|||
|
// openid: payOrderInfo.openid,
|
|||
|
// outTradeNo: payOrderInfo.out_trade_no
|
|||
|
// });
|
|||
|
// refundParams.leftFee = orderQueryRes.leftFee;
|
|||
|
}
|
|||
|
console.log(`---- ${out_trade_no} -- ${outRefundNo} -- ${totalFee/100} -- ${refundFee/100}`)
|
|||
|
// 退款操作
|
|||
|
try {
|
|||
|
res.result = await uniPayInstance.refund(refundParams);
|
|||
|
} catch (err) {
|
|||
|
console.error(err);
|
|||
|
let errMsg = err.message;
|
|||
|
if (errMsg) {
|
|||
|
if (errMsg.indexOf("verify failure") > -1) {
|
|||
|
throw { errCode: ERROR[53005] };
|
|||
|
}
|
|||
|
if (errMsg.indexOf("header too long") > -1) {
|
|||
|
throw { errCode: ERROR[53005] };
|
|||
|
}
|
|||
|
}
|
|||
|
return { errCode: -1, errMsg: errMsg, err }
|
|||
|
}
|
|||
|
if (res.result.refundFee) {
|
|||
|
res.errCode = 0;
|
|||
|
res.errMsg = "ok";
|
|||
|
// 修改数据库
|
|||
|
try {
|
|||
|
let time = Date.now();
|
|||
|
// 修改订单状态
|
|||
|
payOrderInfo = await dao.uniPayOrders.updateAndReturn({
|
|||
|
whereJson: {
|
|||
|
_id: payOrderInfo._id,
|
|||
|
"refund_list.out_refund_no": _.neq(outRefundNo)
|
|||
|
},
|
|||
|
dataJson: {
|
|||
|
status: 2,
|
|||
|
refund_fee: _.inc(refundFee),
|
|||
|
refund_count: refund_count,
|
|||
|
refund_date: time, // 更新最近一次退款时间
|
|||
|
// 记录每次的退款详情
|
|||
|
refund_list: _.unshift({
|
|||
|
refund_date: time,
|
|||
|
refund_fee: refundFee,
|
|||
|
out_refund_no: outRefundNo,
|
|||
|
refund_desc
|
|||
|
})
|
|||
|
}
|
|||
|
});
|
|||
|
if (payOrderInfo && payOrderInfo.refund_fee >= payOrderInfo.total_fee) {
|
|||
|
// 修改订单状态为已全额退款
|
|||
|
await dao.uniPayOrders.updateById(payOrderInfo._id, {
|
|||
|
status: 3,
|
|||
|
refund_fee: payOrderInfo.total_fee,
|
|||
|
});
|
|||
|
}
|
|||
|
} catch (err) {
|
|||
|
console.error(err);
|
|||
|
}
|
|||
|
} else {
|
|||
|
console.log('res.result: ', res.result);
|
|||
|
throw { errCode: ERROR[53002] };
|
|||
|
}
|
|||
|
// 业务逻辑结束-----------------------------------------------------------
|
|||
|
return res;
|
|||
|
}
|
|||
|
|
|||
|
/**
|
|||
|
* 查询退款(查询退款情况)
|
|||
|
* @description 提交退款申请后,通过调用该接口查询退款状态。
|
|||
|
*/
|
|||
|
async getRefund(data = {}) {
|
|||
|
let {
|
|||
|
out_trade_no, // 插件支付单号
|
|||
|
} = data;
|
|||
|
if (!out_trade_no) {
|
|||
|
throw { errCode: ERROR[51001] };
|
|||
|
}
|
|||
|
let payOrderInfo = await dao.uniPayOrders.find({
|
|||
|
out_trade_no
|
|||
|
});
|
|||
|
if (!payOrderInfo) {
|
|||
|
throw { errCode: ERROR[52001] };
|
|||
|
}
|
|||
|
let provider = payOrderInfo.provider;
|
|||
|
let uniPayInstance = await this.initUniPayInstance(payOrderInfo);
|
|||
|
let queryRes;
|
|||
|
try {
|
|||
|
let refundQueryJson = {
|
|||
|
outTradeNo: out_trade_no,
|
|||
|
outRefundNo: payOrderInfo.refund_list[0].out_refund_no
|
|||
|
};
|
|||
|
if (provider === "wxpay-virtual") {
|
|||
|
refundQueryJson.openid = payOrderInfo.openid;
|
|||
|
}
|
|||
|
queryRes = await uniPayInstance.refundQuery(refundQueryJson);
|
|||
|
} catch (err) {
|
|||
|
throw { errCode: ERROR[53003], errMsg: err.errMsg };
|
|||
|
}
|
|||
|
let orderInfo = {
|
|||
|
total_fee: payOrderInfo.total_fee,
|
|||
|
refund_fee: payOrderInfo.refund_fee,
|
|||
|
refund_count: payOrderInfo.refund_count,
|
|||
|
refund_list: payOrderInfo.refund_list,
|
|||
|
provider: payOrderInfo.provider,
|
|||
|
provider_pay_type: payOrderInfo.provider_pay_type,
|
|||
|
status: payOrderInfo.status,
|
|||
|
type: payOrderInfo.type,
|
|||
|
out_trade_no: payOrderInfo.out_trade_no,
|
|||
|
transaction_id: payOrderInfo.transaction_id,
|
|||
|
};
|
|||
|
if (queryRes.refundFee > 0) {
|
|||
|
let msg = "ok";
|
|||
|
if (payOrderInfo.refund_list && payOrderInfo.refund_list.length > 0) {
|
|||
|
msg = `合计退款 ${payOrderInfo.refund_fee/100}\r\n`;
|
|||
|
for (let i in payOrderInfo.refund_list) {
|
|||
|
let item = payOrderInfo.refund_list[i];
|
|||
|
let index = Number(i) + 1;
|
|||
|
let timeStr = libs.common.timeFormat(item.refund_date, "yyyy-MM-dd hh:mm:ss");
|
|||
|
msg += `${index}、 ${timeStr} \r\n退款 ${item.refund_fee/100} \r\n`;
|
|||
|
}
|
|||
|
}
|
|||
|
return {
|
|||
|
errCode: 0,
|
|||
|
errMsg: msg,
|
|||
|
pay_order: orderInfo,
|
|||
|
result: queryRes
|
|||
|
}
|
|||
|
} else {
|
|||
|
throw { errCode: ERROR[53003] };
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
/**
|
|||
|
* 关闭订单
|
|||
|
* @description 用于交易创建后,用户在一定时间内未进行支付,可调用该接口直接将未付款的交易进行关闭,避免重复支付。
|
|||
|
* 注意
|
|||
|
* 微信支付:订单生成后不能马上调用关单接口,最短调用时间间隔为 5 分钟。
|
|||
|
* 微信虚拟支付:不支持关闭订单
|
|||
|
*/
|
|||
|
async closeOrder(data = {}) {
|
|||
|
let {
|
|||
|
out_trade_no, // 插件支付单号
|
|||
|
} = data;
|
|||
|
if (!out_trade_no) {
|
|||
|
throw { errCode: ERROR[51001] };
|
|||
|
}
|
|||
|
let payOrderInfo = await dao.uniPayOrders.find({
|
|||
|
out_trade_no
|
|||
|
});
|
|||
|
if (!payOrderInfo) {
|
|||
|
throw { errCode: ERROR[52001] };
|
|||
|
}
|
|||
|
let { provider } = payOrderInfo;
|
|||
|
let uniPayInstance = await this.initUniPayInstance(payOrderInfo);
|
|||
|
let closeOrderRes = await uniPayInstance.closeOrder({
|
|||
|
outTradeNo: out_trade_no
|
|||
|
});
|
|||
|
let wxpayResult = (provider === "wxpay" && closeOrderRes.resultCode === "SUCCESS");
|
|||
|
let alipayResult = (provider === "alipay" && closeOrderRes.code === "10000");
|
|||
|
|
|||
|
if (wxpayResult || alipayResult) {
|
|||
|
// 修改订单状态为已取消
|
|||
|
await dao.uniPayOrders.update({
|
|||
|
whereJson: {
|
|||
|
_id: payOrderInfo._id,
|
|||
|
status: 0
|
|||
|
},
|
|||
|
dataJson: {
|
|||
|
status: -1,
|
|||
|
cancel_date: Date.now()
|
|||
|
}
|
|||
|
});
|
|||
|
return {
|
|||
|
errCode: 0,
|
|||
|
errMsg: "订单关闭成功",
|
|||
|
result: closeOrderRes
|
|||
|
}
|
|||
|
} else {
|
|||
|
throw { errCode: ERROR[53004] };
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
/**
|
|||
|
* 根据code获取openid
|
|||
|
*/
|
|||
|
async getOpenid(data = {}) {
|
|||
|
let {
|
|||
|
provider, // 支付供应商
|
|||
|
code, // 用户登录获取的code
|
|||
|
clientInfo, // 客户端环境
|
|||
|
} = data;
|
|||
|
if (!code) {
|
|||
|
throw { errCode: ERROR[51002] };
|
|||
|
}
|
|||
|
let { platform, ua } = clientInfo;
|
|||
|
// 获取并自动匹配支付供应商的支付类型
|
|||
|
let provider_pay_type = libs.common.getProviderPayType({
|
|||
|
provider,
|
|||
|
platform,
|
|||
|
ua
|
|||
|
});
|
|||
|
let needCacheSessionKey = false;
|
|||
|
let uniPayConifg = await this.getUniPayConfig({ provider, provider_pay_type });
|
|||
|
if (provider === "wxpay") {
|
|||
|
try {
|
|||
|
// 如果配置了微信虚拟支付,则使用微信虚拟支付的配置作为微信获取openid的配置
|
|||
|
let wxpayVirtualPayConifg = await this.getUniPayConfig({ provider: "wxpay-virtual", provider_pay_type: "mp" });
|
|||
|
if (wxpayVirtualPayConifg && wxpayVirtualPayConifg.appId && wxpayVirtualPayConifg.secret) {
|
|||
|
uniPayConifg = wxpayVirtualPayConifg;
|
|||
|
needCacheSessionKey = true;
|
|||
|
}
|
|||
|
} catch (err) {}
|
|||
|
|
|||
|
let res = await libs.wxpay.getOpenid({
|
|||
|
config: uniPayConifg,
|
|||
|
code,
|
|||
|
provider_pay_type,
|
|||
|
});
|
|||
|
if (needCacheSessionKey) {
|
|||
|
// 将session_key保存到缓存表中
|
|||
|
let cacheKey = {
|
|||
|
appId: uniPayConifg.appId,
|
|||
|
platform: "weixin-mp",
|
|||
|
openid: res.openid
|
|||
|
}
|
|||
|
let session_key = res.session_key;
|
|||
|
delete res.session_key;
|
|||
|
await dao.opendbOpenData.setSessionKey(cacheKey, { session_key }, 30 * 24 * 60 * 60);
|
|||
|
}
|
|||
|
return res;
|
|||
|
} else if (provider === "alipay") {
|
|||
|
return await libs.alipay.getOpenid({
|
|||
|
config: uniPayConifg,
|
|||
|
code,
|
|||
|
});
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
/**
|
|||
|
* 获取支持的支付方式
|
|||
|
* let payTypes = await service.pay.getPayProviderFromCloud();
|
|||
|
*/
|
|||
|
async getPayProviderFromCloud() {
|
|||
|
let wxpay = config.wxpay && config.wxpay.enable ? true : false;
|
|||
|
let alipay = config.alipay && config.alipay.enable ? true : false;
|
|||
|
let provider = [];
|
|||
|
if (wxpay) provider.push("wxpay");
|
|||
|
if (alipay) provider.push("alipay");
|
|||
|
return {
|
|||
|
errCode: 0,
|
|||
|
errMsg: "ok",
|
|||
|
wxpay,
|
|||
|
alipay,
|
|||
|
provider
|
|||
|
};
|
|||
|
}
|
|||
|
|
|||
|
/**
|
|||
|
* 验证iosIap苹果内购支付凭据
|
|||
|
* let payTypes = await service.pay.verifyReceiptFromAppleiap();
|
|||
|
*/
|
|||
|
async verifyReceiptFromAppleiap(data) {
|
|||
|
let {
|
|||
|
out_trade_no,
|
|||
|
transaction_receipt,
|
|||
|
transaction_identifier,
|
|||
|
} = data;
|
|||
|
if (!out_trade_no) {
|
|||
|
throw { errCode: ERROR[51001] };
|
|||
|
}
|
|||
|
// 初始化uniPayInstance
|
|||
|
let uniPayInstance = await this.initUniPayInstance({ provider: "appleiap", provider_pay_type: "app" });
|
|||
|
let verifyReceiptRes = await uniPayInstance.verifyReceipt({
|
|||
|
receiptData: transaction_receipt
|
|||
|
});
|
|||
|
let userOrderSuccess = false;
|
|||
|
let pay_date;
|
|||
|
if (verifyReceiptRes.tradeState !== "SUCCESS") {
|
|||
|
// 尝试使用相反的环境再次验证
|
|||
|
let uniPayConifg = await this.getUniPayConfig({ provider: "appleiap", provider_pay_type: "app" });
|
|||
|
uniPayInstance = uniPay.initAppleIapPayment({
|
|||
|
...uniPayConifg,
|
|||
|
sandbox: !uniPayConifg.sandbox,
|
|||
|
});
|
|||
|
verifyReceiptRes = await uniPayInstance.verifyReceipt({
|
|||
|
receiptData: transaction_receipt
|
|||
|
});
|
|||
|
if (verifyReceiptRes.tradeState !== "SUCCESS") {
|
|||
|
// 如果还是不成功,则校验不通过
|
|||
|
throw { errCode: ERROR[54002] };
|
|||
|
}
|
|||
|
}
|
|||
|
// 支付成功
|
|||
|
pay_date = Number(verifyReceiptRes.receipt.receipt_creation_date_ms);
|
|||
|
let inAppList = verifyReceiptRes.receipt.in_app;
|
|||
|
let inApp = inAppList.find((item) => {
|
|||
|
return item.transaction_id === transaction_identifier;
|
|||
|
});
|
|||
|
if (!inApp) {
|
|||
|
// 校验不通过
|
|||
|
throw { errCode: ERROR[54002] };
|
|||
|
}
|
|||
|
let quantity = inApp.quantity; // 购买数量
|
|||
|
let product_id = inApp.product_id; // 对应的内购产品id
|
|||
|
let transaction_id = inApp.transaction_id; // 本次交易id
|
|||
|
|
|||
|
if ((Date.now() - 1000 * 3600 * 24) > pay_date) {
|
|||
|
// 订单已超24小时,不做处理,通知前端直接关闭订单。
|
|||
|
return {
|
|||
|
errCode: 0,
|
|||
|
errMsg: "ok"
|
|||
|
};
|
|||
|
}
|
|||
|
// 查询该transaction_id是否使用过,如果已使用,则不做处理,通知前端直接关闭订单。
|
|||
|
let findOrderInfo = await dao.uniPayOrders.find({
|
|||
|
transaction_id,
|
|||
|
});
|
|||
|
if (findOrderInfo) {
|
|||
|
return {
|
|||
|
errCode: 0,
|
|||
|
errMsg: "ok"
|
|||
|
};
|
|||
|
}
|
|||
|
// 否则,执行用户回调
|
|||
|
// 用户自己的逻辑处理 开始-----------------------------------------------------------
|
|||
|
let orderPaySuccess;
|
|||
|
let payOrderInfo = await dao.uniPayOrders.find({
|
|||
|
out_trade_no,
|
|||
|
});
|
|||
|
if (!payOrderInfo) {
|
|||
|
throw { errCode: ERROR[52001] };
|
|||
|
}
|
|||
|
try {
|
|||
|
// 加载自定义异步回调函数
|
|||
|
orderPaySuccess = require(`../notify/${payOrderInfo.type}`);
|
|||
|
} catch (err) {
|
|||
|
console.log(err);
|
|||
|
}
|
|||
|
if (typeof orderPaySuccess === "function") {
|
|||
|
payOrderInfo = await dao.uniPayOrders.updateAndReturn({
|
|||
|
whereJson: {
|
|||
|
status: 0, // status:0 为必须条件,防止重复推送时的错误
|
|||
|
out_trade_no: out_trade_no, // 商户订单号
|
|||
|
},
|
|||
|
dataJson: {
|
|||
|
status: 1, // 设置为已付款
|
|||
|
transaction_id: transaction_id, // 第三方支付单号
|
|||
|
pay_date: pay_date,
|
|||
|
notify_date: pay_date,
|
|||
|
original_data: verifyReceiptRes
|
|||
|
}
|
|||
|
});
|
|||
|
console.log('用户自己的回调逻辑 - 开始执行');
|
|||
|
userOrderSuccess = await orderPaySuccess({
|
|||
|
verifyResult: verifyReceiptRes,
|
|||
|
data: payOrderInfo,
|
|||
|
});
|
|||
|
console.log('用户自己的回调逻辑 - 执行完成');
|
|||
|
payOrderInfo = await dao.uniPayOrders.updateAndReturn({
|
|||
|
whereJson: {
|
|||
|
status: 1,
|
|||
|
out_trade_no,
|
|||
|
},
|
|||
|
dataJson: {
|
|||
|
user_order_success: userOrderSuccess,
|
|||
|
}
|
|||
|
});
|
|||
|
} else {
|
|||
|
payOrderInfo = await dao.uniPayOrders.find({
|
|||
|
out_trade_no,
|
|||
|
});
|
|||
|
}
|
|||
|
console.log('userOrderSuccess', userOrderSuccess);
|
|||
|
// 用户自己的逻辑处理 结束-----------------------------------------------------------
|
|||
|
|
|||
|
//console.log('verifyReceiptRes: ', verifyReceiptRes);
|
|||
|
return {
|
|||
|
errCode: 0,
|
|||
|
errMsg: "ok",
|
|||
|
has_paid: true, // 标记用户是否已付款成功(此参数只能表示用户确实付款了,但系统的异步回调逻辑可能还未执行完成)
|
|||
|
out_trade_no, // 支付插件订单号
|
|||
|
transaction_id, // 支付平台订单号
|
|||
|
status: payOrderInfo.status, // 标记当前支付订单状态 -1:已关闭 0:未支付 1:已支付 2:已部分退款 3:已全额退款
|
|||
|
user_order_success: payOrderInfo.user_order_success, // 用户异步通知逻辑是否全部执行完成,且无异常(建议前端通过此参数是否为true来判断是否支付成功)
|
|||
|
pay_order: payOrderInfo,
|
|||
|
};
|
|||
|
}
|
|||
|
|
|||
|
/**
|
|||
|
* 获取对应支付配置
|
|||
|
* let uniPayConifg = await this.getUniPayConfig({ provider, provider_pay_type });
|
|||
|
*/
|
|||
|
async getUniPayConfig(data = {}) {
|
|||
|
let {
|
|||
|
provider,
|
|||
|
provider_pay_type,
|
|||
|
} = data;
|
|||
|
if (config && config[provider] && config[provider][provider_pay_type]) {
|
|||
|
let uniPayConfig = config[provider][provider_pay_type];
|
|||
|
if (!uniPayConfig.appId && provider !== "appleiap") {
|
|||
|
throw new Error(`uni-pay配置${provider}.${provider_pay_type}节点下的appId不能为空`);
|
|||
|
}
|
|||
|
return uniPayConfig;
|
|||
|
} else {
|
|||
|
throw new Error(`${provider}_${provider_pay_type} : 商户支付配置错误`);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
/**
|
|||
|
* 初始化uniPayInstance
|
|||
|
* let uniPayInstance = await service.pay.initUniPayInstance({ provider, provider_pay_type });
|
|||
|
*/
|
|||
|
async initUniPayInstance(data = {}) {
|
|||
|
let {
|
|||
|
provider,
|
|||
|
} = data;
|
|||
|
let uniPayConifg = await this.getUniPayConfig(data);
|
|||
|
let uniPayInstance;
|
|||
|
if (provider === "wxpay") {
|
|||
|
// 微信
|
|||
|
if (uniPayConifg.version === 3) {
|
|||
|
try {
|
|||
|
uniPayInstance = uniPay.initWeixinV3(uniPayConifg);
|
|||
|
} catch (err) {
|
|||
|
console.error(err);
|
|||
|
let errMsg = err.message;
|
|||
|
if (errMsg && errMsg.indexOf("invalid base64 body") > -1) {
|
|||
|
throw { errCode: ERROR[53005] };
|
|||
|
}
|
|||
|
throw err;
|
|||
|
}
|
|||
|
} else {
|
|||
|
uniPayInstance = uniPay.initWeixin(uniPayConifg);
|
|||
|
}
|
|||
|
} else if (provider === "alipay") {
|
|||
|
// 支付宝
|
|||
|
uniPayInstance = uniPay.initAlipay(uniPayConifg);
|
|||
|
} else if (provider === "appleiap") {
|
|||
|
// ios内购
|
|||
|
uniPayInstance = uniPay.initAppleIapPayment(uniPayConifg);
|
|||
|
} else if (provider === "wxpay-virtual") {
|
|||
|
// 微信虚拟支付
|
|||
|
// 还需要额外传accessToken
|
|||
|
uniPayConifg.accessToken = await this.getAccessToken(data);
|
|||
|
uniPayInstance = uniPay.initWeixinVirtualPayment(uniPayConifg);
|
|||
|
} else {
|
|||
|
throw new Error(`${provider} : 不支持的支付方式`);
|
|||
|
}
|
|||
|
return uniPayInstance;
|
|||
|
}
|
|||
|
|
|||
|
/**
|
|||
|
* 获取accessToken
|
|||
|
* let uniPayInstance = await service.pay.getAccessToken({ provider, provider_pay_type });
|
|||
|
*/
|
|||
|
async getAccessToken(data = {}) {
|
|||
|
let uniPayConifg = await this.getUniPayConfig(data);
|
|||
|
let cacheKey = {
|
|||
|
appId: uniPayConifg.appId,
|
|||
|
platform: "weixin-mp"
|
|||
|
}
|
|||
|
let cacheInfo = await dao.opendbOpenData.getAccessToken(cacheKey);
|
|||
|
if (cacheInfo) {
|
|||
|
// 缓存有值
|
|||
|
return cacheInfo.access_token;
|
|||
|
} else {
|
|||
|
// 缓存无值
|
|||
|
let getAccessTokenRes = await libs.wxpay.getAccessToken(uniPayConifg);
|
|||
|
let accessToken = getAccessTokenRes.accessToken;
|
|||
|
// 缓存accessToken
|
|||
|
await dao.opendbOpenData.setAccessToken(cacheKey, {
|
|||
|
access_token: getAccessTokenRes.accessToken,
|
|||
|
}, getAccessTokenRes.expiresIn);
|
|||
|
return accessToken;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
/**
|
|||
|
* 获取sessionKey
|
|||
|
* let sessionKey = await service.pay.getSessionKey({ provider, provider_pay_type, openid });
|
|||
|
*/
|
|||
|
async getSessionKey(data = {}) {
|
|||
|
let {
|
|||
|
openid,
|
|||
|
} = data;
|
|||
|
// 获取用户的sessionKey
|
|||
|
let uniPayConifg = await this.getUniPayConfig(data);
|
|||
|
let { session_key } = await dao.opendbOpenData.getSessionKey({
|
|||
|
appId: uniPayConifg.appId,
|
|||
|
platform: "weixin-mp",
|
|||
|
openid
|
|||
|
});
|
|||
|
return session_key;
|
|||
|
}
|
|||
|
|
|||
|
/**
|
|||
|
* 请求微信小程序虚拟支付API
|
|||
|
* let res = await service.pay.requestWxpayVirtualApi(data);
|
|||
|
*/
|
|||
|
async requestWxpayVirtualApi(options = {}) {
|
|||
|
let {
|
|||
|
method,
|
|||
|
data = {}
|
|||
|
} = options;
|
|||
|
// 微信虚拟支付固定参数
|
|||
|
let provider = "wxpay-virtual";
|
|||
|
let provider_pay_type = "mp";
|
|||
|
// 获得微信小程序虚拟支付实例
|
|||
|
let uniPayInstance = await this.initUniPayInstance({ provider, provider_pay_type });
|
|||
|
// 调用微信小程序虚拟支付云端API
|
|||
|
|
|||
|
if (["currencyPay"].indexOf(method) > -1) {
|
|||
|
if (!data.sessionKey) {
|
|||
|
data.sessionKey = await this.getSessionKey({ ...data, provider, provider_pay_type });
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
let res = await uniPayInstance[method](data);
|
|||
|
return res;
|
|||
|
}
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
module.exports = new service();
|