/**
 * 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('用户自己的回调逻辑 - 开始执行');
					userOrderSuccess = await orderPaySuccess({
						verifyResult,
						data: payOrderInfo,
						clientInfo,
						cloudInfo
					});
					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 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") {
			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
			let cacheKey = {
				appId: uniPayConifg.appId,
				platform: "weixin-mp"
			}
			let cacheInfo = await dao.opendbOpenData.getAccessToken(cacheKey);
			if (cacheInfo) {
				// 缓存有值
				uniPayConifg.accessToken = cacheInfo.access_token;
			} else {
				// 缓存无值
				let getAccessTokenRes = await libs.wxpay.getAccessToken(uniPayConifg);
				uniPayConifg.accessToken = getAccessTokenRes.accessToken;
				// 缓存accessToken
				await dao.opendbOpenData.setAccessToken(cacheKey, {
					access_token: getAccessTokenRes.accessToken,
				}, getAccessTokenRes.expiresIn);
			}
			uniPayInstance = uniPay.initWeixinVirtualPayment(uniPayConifg);
		} else {
			throw new Error(`${provider} : 不支持的支付方式`);
		}
		return uniPayInstance;
	}

}


module.exports = new service();
