第一次提交
This commit is contained in:
@@ -0,0 +1,865 @@
|
||||
/**
|
||||
* 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,
|
||||
} = data;
|
||||
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]; // 获取支付方式
|
||||
let original_data = libs.common.getNotifyData({ httpInfo, provider }); // 获取原始回调数据
|
||||
// 初始化uniPayInstance
|
||||
let uniPayInstance = await this.initUniPayInstance({ provider, provider_pay_type });
|
||||
let notifyType = await uniPayInstance.checkNotifyType(httpInfo);
|
||||
if (notifyType !== "payment") {
|
||||
// 由于支付宝部分退款会触发支付成功的回调,但同时签名验证是算未通过的,为了避免支付宝重复推送,这里可以直接返回成功告知支付宝服务器,不用再推送过来了。
|
||||
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)); // 这一句代码有用,请勿删除。
|
||||
let {
|
||||
outTradeNo,
|
||||
totalFee,
|
||||
transactionId,
|
||||
resultCode, // 微信支付v2和支付宝支付判断成功的字段
|
||||
openid,
|
||||
appId,
|
||||
tradeState, // 微信支付v3判断支付成功的字段
|
||||
} = verifyResult;
|
||||
//console.log('verifyResult: ', 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,
|
||||
original_data,
|
||||
}
|
||||
});
|
||||
//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('用户自己的回调逻辑 - 开始执行');
|
||||
userOrderSuccess = await orderPaySuccess({
|
||||
verifyResult,
|
||||
data: payOrderInfo,
|
||||
});
|
||||
console.log('用户自己的回调逻辑 - 执行完成');
|
||||
}
|
||||
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 createOrder(data = {}) {
|
||||
let {
|
||||
provider, // 支付供应商
|
||||
total_fee, // 支付金额
|
||||
user_id, // 用户user_id(统计需要)
|
||||
openid, // 用户openid
|
||||
order_no, // 订单号
|
||||
out_trade_no, // 支付插件订单号
|
||||
description, // 订单描述
|
||||
type, // 回调类型
|
||||
qr_code, // 是否强制使用扫码支付
|
||||
clientInfo, // 客户端信息
|
||||
cloudInfo, // 云端信息
|
||||
|
||||
custom, // 自定义参数(不会发送给第三方支付服务器)
|
||||
other, // 其他请求参数(会发送给第三方支付服务器)
|
||||
} = 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 (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 currentNotifyUrl = notifyUrl[spaceId] || 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
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的参数
|
||||
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,
|
||||
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;
|
||||
}
|
||||
let queryRes = await uniPayInstance.orderQuery(orderQueryJson);
|
||||
if (queryRes.tradeState === 'SUCCESS' || queryRes.tradeState === 'FINISHED') {
|
||||
if (typeof payOrderInfo.user_order_success == "undefined" && await_notify) {
|
||||
let whileTime = 0; // 当前循环已执行的时间(毫秒)
|
||||
let whileInterval = 500; // 每次循环间隔时间(毫秒)
|
||||
let maxTime = 5000; // 循环执行时间超过此值则退出循环(毫秒)
|
||||
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);
|
||||
|
||||
console.log(`---- ${out_trade_no} -- ${outRefundNo} -- ${totalFee/100} -- ${refundFee/100}`)
|
||||
// 退款操作
|
||||
try {
|
||||
res.result = await uniPayInstance.refund({
|
||||
outTradeNo: out_trade_no,
|
||||
outRefundNo,
|
||||
totalFee,
|
||||
refundFee,
|
||||
refundDesc: refund_desc,
|
||||
refundFeeType: refund_fee_type
|
||||
});
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
let errMsg = err.message;
|
||||
if (errMsg && errMsg.indexOf("verify failure") > -1) {
|
||||
throw { errCode: ERROR[53005] };
|
||||
}
|
||||
return { errCode: -1, errMsg: errMsg, err }
|
||||
}
|
||||
if (res.result.refundFee) {
|
||||
res.errCode = 0;
|
||||
res.errMsg = "ok";
|
||||
// 修改数据库
|
||||
try {
|
||||
// 修改订单状态
|
||||
payOrderInfo = await dao.uniPayOrders.updateAndReturn({
|
||||
whereJson: {
|
||||
_id: payOrderInfo._id
|
||||
},
|
||||
dataJson: {
|
||||
status: 2,
|
||||
refund_fee: _.inc(refundFee),
|
||||
refund_count: refund_count,
|
||||
// 记录每次的退款详情
|
||||
refund_list: _.unshift({
|
||||
refund_date: Date.now(),
|
||||
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 {
|
||||
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
|
||||
};
|
||||
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
|
||||
});
|
||||
if ((provider === "wxpay" && closeOrderRes.resultCode === "SUCCESS") || (provider === "alipay" && closeOrderRes.code ===
|
||||
"10000")) {
|
||||
// 修改订单状态为已取消
|
||||
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 uniPayConifg = await this.getUniPayConfig({ provider, provider_pay_type });
|
||||
if (provider === "wxpay") {
|
||||
return await libs.wxpay.getOpenid({
|
||||
config: uniPayConifg,
|
||||
code,
|
||||
provider_pay_type,
|
||||
});
|
||||
} 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 transaction_id;
|
||||
let userOrderSuccess = false;
|
||||
let pay_date;
|
||||
if (verifyReceiptRes.tradeState !== "SUCCESS") {
|
||||
return {
|
||||
errCode: -1,
|
||||
errMsg: "未支付"
|
||||
};
|
||||
}
|
||||
// 支付成功
|
||||
pay_date = verifyReceiptRes.receipt.receipt_creation_date_ms;
|
||||
let inApp = verifyReceiptRes.receipt.in_app[0];
|
||||
let quantity = inApp.quantity; // 购买数量
|
||||
let product_id = inApp.product_id; // 对应的内购产品id
|
||||
transaction_id = inApp.transaction_id; // 本次交易id
|
||||
if (transaction_identifier !== transaction_id) {
|
||||
// 校验不通过
|
||||
return {
|
||||
errCode: -1,
|
||||
errMsg: "ios内购凭据校验不通过"
|
||||
};
|
||||
}
|
||||
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;
|
||||
try {
|
||||
// 加载自定义异步回调函数
|
||||
orderPaySuccess = require(`../notify/appleiap`);
|
||||
} 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('用户自己的回调逻辑 - 执行完成');
|
||||
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) {
|
||||
uniPayInstance = uniPay.initWeixinV3(uniPayConifg);
|
||||
} else {
|
||||
uniPayInstance = uniPay.initWeixin(uniPayConifg);
|
||||
}
|
||||
} else if (provider === "alipay") {
|
||||
// 支付宝
|
||||
uniPayInstance = uniPay.initAlipay(uniPayConifg);
|
||||
} else if (provider === "appleiap") {
|
||||
// ios内购
|
||||
uniPayInstance = uniPay.initAppleIapPayment(uniPayConifg);
|
||||
} else {
|
||||
throw new Error(`${pay_type} : 不支持的支付方式`);
|
||||
}
|
||||
return uniPayInstance;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
module.exports = new service();
|
||||
Reference in New Issue
Block a user