You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

548 lines
27 KiB
C#

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

using NLog;
using POSV.Entity;
using POSV.Payment.AllinPay;
using POSV.Payment.AllinPay.Models.ParamModels;
using POSV.Payment.AllinPay.Models.ResponseModels;
using POSV.ShoppingCart;
using POSV.Utils;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace POSV.PayApi
{
#region subin 2023-07-01 add
/// <summary>
/// 通联支付API
/// </summary>
public class AllinApi
{
private static Logger logger = NLog.LogManager.GetCurrentClassLogger();
public static Tuple<bool, string, Dictionary<string, string>> GetAllinParameter(AccountTypeEnum accountType)
{
if (accountType == AccountTypeEnum.)
{
var payMode = OrderUtils.GetPayMode("50");
if (payMode.Body == null || payMode.Body.Count == 0)
{
var payModelBody = new Dictionary<string, object>();
payModelBody.Add("channel", "allinpay");
payModelBody.Add("appId", AllinpayLib.AppId);
payModelBody.Add("appKey", AllinpayLib.AppKey);
payModelBody.Add("gatewayUrl", AllinpayLib.Url);
payModelBody.Add("bizUserId", AllinpayLib.BizUserId);
payModelBody.Add("payuserid", AllinpayLib.Payuserid);
payModelBody.Add("vspCusid", AllinpayLib.VspCusid);
payMode.Body = payModelBody;
}
if (payMode != null)
{
var parameter = payMode.Body;
//通联支付参数
string appId = parameter.ContainsKey("appId") ? parameter["appId"].ToString() : "";
string appKey = parameter.ContainsKey("appKey") ? parameter["appKey"].ToString() : "";
string url = parameter.ContainsKey("gatewayUrl") ? parameter["gatewayUrl"].ToString() : "";
string payuserid = parameter.ContainsKey("payuserid") ? parameter["payuserid"].ToString() : "";
string vspCusid = parameter.ContainsKey("vspCusid") ? parameter["vspCusid"].ToString() : "";
string bizUserId = parameter.ContainsKey("bizUserId") ? parameter["bizUserId"].ToString() : "";
if (string.IsNullOrEmpty(appId) || string.IsNullOrEmpty(appKey) || string.IsNullOrEmpty(url))
{
return new Tuple<bool, string, Dictionary<string, string>>(false, "通联支付参数配置不完整", null);
}
Dictionary<string, string> dic = new Dictionary<string, string>();
dic.Add("appId", appId);
dic.Add("appKey", appKey);
dic.Add("url", url);
dic.Add("vspCusid", vspCusid);
dic.Add("payuserid", payuserid);
dic.Add("bizUserId", bizUserId);
return new Tuple<bool, string, Dictionary<string, string>>(true, "获取成功", dic);
}
else
{
return new Tuple<bool, string, Dictionary<string, string>>(false, "未启用通联扫码支付", null);
}
}
else
{
return new Tuple<bool, string, Dictionary<string, string>>(false, "暂不支持总部通联支付参数查询", null);
}
}
/// <summary>
/// 通联退款+查询结果
/// 备注:前端支付到统一账号,退款也从统一账号退款
/// </summary>
/// <param name="accountType"></param>
/// <param name="tradeNo">商户订单号</param>
/// <param name="paidAmount">原订单的实收金额</param>
/// <param name="refundAmount">退款金额</param>
/// <param name="reTradeNo">原交易的商户订单号</param>
/// <param name="reason">备注</param>
/// <param name="timeout"></param>
/// <param name="orderType">订单类型</param>
/// <param name="payuserId">付款账号</param>
/// <returns></returns>
public static Tuple<bool, string, string> RefundAndQuery(AccountTypeEnum accountType, string tradeNo, decimal paidAmount, decimal refundAmount, string reTradeNo, string reason, OrderType orderType, string payuserId, int timeout = 60)
{
try
{
var _refundList = new List<ApplyReciever>();
var info = GetAllinParameter(accountType);
if (info.Item1)
{
var parameter = info.Item3;
var _bizUserId = parameter["bizUserId"].ToString();
//付款账号为空时,使用配置参数的付款账号
if (string.IsNullOrEmpty(payuserId))
{
payuserId = parameter["payuserid"].ToString();
}
//全额退款refundList无需上送原路径返回;部分退款refundList必须上送.
if (paidAmount > refundAmount)
{
_refundList.Add(new ApplyReciever { bizUserId = _bizUserId, amount = Convert.ToInt16(Math.Abs(refundAmount)) });
}
}
var refundResponse = RefundByAllinpay(accountType, tradeNo, refundAmount, reTradeNo, paidAmount, reason, orderType, payuserId, _refundList);
if (refundResponse.Item1)
{
var refundResult = refundResponse.Item3;
if (refundResult.code == "10000")
{
int queryCount = 0;
int sleepTime = 3000;
if (timeout < 60)
{
timeout = 60;
}
queryCount = (60 * 1000 / sleepTime);
int i = 0; //60秒超时
while (i < queryCount)
{
var queryRefundResponse = QueryAllinpayRefundResult(accountType, tradeNo);
if (queryRefundResponse.Item1 && queryRefundResponse.Item3.code == "10000")
{
//logger.Info("订单[{0}]退款成功!", tradeNo);
//return new Tuple<bool, string>(true, "退款成功!");
var _data = JsonUtils.Deserialize<OrderDetail>(queryRefundResponse.Item3.data);
// logger.Info("订单[{0}]分店退款成功!", tradeNo);
// return new Tuple<bool, string>(true, "退款成功!");
var flg = false;
var msg = "";
switch (_data.status)
{
case "1":
flg = false;
msg = "未支付";
break;
case "3":
flg = false;
msg = "交易失败,交易过程中出现错误";
break;
case "4":
flg = true;
msg = "交易成功";
break;
case "5":
flg = true;
msg = "交易成功,但是发生了退款";
break;
case "6":
flg = true;
msg = "交易关闭";
break;
case "99":
flg = true;
msg = "进行中";
break;
}
logger.Info($"订单[{tradeNo}]分店退款交易{msg}");
return new Tuple<bool, string, string>(flg, msg, queryRefundResponse.Item3.data);
}
i++;
System.Threading.Thread.Sleep(3000);//睡眠3秒
}
if (i == queryCount)
{
logger.Info("订单[{0}]查询退款结果超时,退单失败!", tradeNo);
return new Tuple<bool, string, string>(false, "查询退款结果超时,退单失败!", "");
}
}
else
{
return new Tuple<bool, string, string>(false, refundResult.msg, refundResult.subMsg);
}
}
else
{
//请求失败
return new Tuple<bool, string, string>(false, refundResponse.Item2, "");
}
}
catch (Exception ex)
{
logger.Error(ex, "订单[" + tradeNo + "]通联退款发生异常");
return new Tuple<bool, string, string>(false, "退款发生异常!", ex.Message);
}
logger.Info("订单[{0}]退款失败!", tradeNo);
return new Tuple<bool, string, string>(false, "退款失败!", "");
}
/// <summary>
/// 通联退款+查询结果
/// 备注:前端支付到分店账号,退款也从各个分店账号退款
/// </summary>
/// <param name="accountType"></param>
/// <param name="backOrder">退款订单</param>
/// <param name="oldTradeNo">原交易订单号</param>
/// <param name="oldAmount">原交易订单总金额</param>
/// <param name="payuserId">原订单付款方userid当小程序付款时为微信的openid</param>
/// <param name="timeout"></param>
/// <returns></returns>
public static Tuple<bool, string, string> RefundAndQuery(AccountTypeEnum accountType, OrderObject backOrder, string oldTradeNo, int oldAmount, string payuserId, int timeout = 60)
{
try
{
var _refundList = new List<ApplyReciever>();
decimal _bizUserAmount = 0;
using (var db = Global.Instance.OpenDataBase)
{
var productIdList = backOrder.Items.Select(item => item.ProductId).Distinct();
var productIds = string.Join(",", productIdList);
StringBuilder sqlBuld = new StringBuilder();
sqlBuld.Append("SELECT ssa.bizUserId,sfa.goodId FROM pos_split_shop_account ssa ");
sqlBuld.Append("LEFT JOIN pos_split_food_account sfa ON sfa.shopId=ssa.id ");
sqlBuld.Append("WHERE sfa.goodId IN ('{0}'); ");
string sql = string.Format(sqlBuld.ToString(), productIds.Replace(",", "','"));
var orderSubStoreDetail = db.Query<OrderSubStoreDetail>(sql).ToList();
var subStoreProductIdList = orderSubStoreDetail.Select(item => item.GoodId).Distinct().ToList();
if (productIdList.Count() > orderSubStoreDetail.Count())
{
logger.Info($"订单[{backOrder.TradeNo}]分店退款失败!,退款菜品数量({productIds}>分店关联菜品数量({string.Join(",", subStoreProductIdList)})");
return new Tuple<bool, string, string>(false, "分店未关联退款菜品!", null);
}
var bizUseIdLst = orderSubStoreDetail.Select(item => item.BizUserId).Distinct().ToList();
foreach (var item in bizUseIdLst)
{
var goodLst = orderSubStoreDetail.Where(m => m.BizUserId == item).ToList().Select(m => m.GoodId);
var goodIds = string.Join(",", goodLst);
var total = backOrder.Items.Where(m => goodIds.Contains(m.ProductId)).Sum(m => m.TotalAmonut);
_bizUserAmount += Math.Abs(total * 100);
_refundList.Add(new ApplyReciever { bizUserId = item, amount = Convert.ToInt32(Math.Abs(total) * 100) });
}
}
var tradeNo = backOrder.StoreId + "_" + backOrder.TradeNo;
var refundAmount = Convert.ToInt32(backOrder.PaidAmount * 100);
var reason = backOrder.RefundCause;
var orderType = backOrder.OrderType;
var refundResponse = RefundByAllinpay(accountType, backOrder.TradeNo, _bizUserAmount, oldTradeNo, oldAmount, reason, orderType, payuserId, _refundList);
if (refundResponse.Item1)
{
var refundResult = refundResponse.Item3;
if (refundResult.code == "10000")
{
int queryCount = 0;
int sleepTime = 3000;
if (timeout < 60)
{
timeout = 60;
}
queryCount = (60 * 1000 / sleepTime);
int i = 0; //60秒超时
while (i < queryCount)
{
var queryRefundResponse = QueryAllinpayRefundResult(accountType, backOrder.TradeNo);
if (queryRefundResponse.Item1 && queryRefundResponse.Item3.code == "10000")
{
var _data = JsonUtils.Deserialize<OrderDetail>(queryRefundResponse.Item3.data);
// logger.Info("订单[{0}]分店退款成功!", tradeNo);
// return new Tuple<bool, string>(true, "退款成功!");
var flg = false;
var msg = "";
switch (_data.status)
{
case "1":
flg = false;
msg = "未支付";
break;
case "3":
flg = false;
msg = "交易失败,交易过程中出现错误";
break;
case "4":
flg = true;
msg = "交易成功";
break;
case "5":
flg = true;
msg = "交易成功,但是发生了退款";
break;
case "6":
flg = true;
msg = "交易关闭";
break;
case "99":
flg = true;
msg = "进行中";
break;
}
logger.Info($"订单[{tradeNo}]分店退款交易{msg}");
return new Tuple<bool, string, string>(flg, msg, queryRefundResponse.Item3.data);
}
i++;
System.Threading.Thread.Sleep(3000);//睡眠3秒
}
if (i == queryCount)
{
logger.Info("订单[{0}]查询分店退款结果超时,退单失败!", tradeNo);
return new Tuple<bool, string, string>(false, "查询退款结果超时,退单失败!", null);
}
}
else
{
return new Tuple<bool, string, string>(false, refundResult.msg, "");
}
}
else
{
//请求失败
return new Tuple<bool, string, string>(false, refundResponse.Item2, "");
}
}
catch (Exception ex)
{
logger.Error(ex, "订单[" + backOrder.TradeNo + "]分店账号通联退款发生异常");
return new Tuple<bool, string, string>(false, "退款发生异常!", ex.Message);
}
logger.Info("订单[{0}]分店账号退款失败!", backOrder.TradeNo);
return new Tuple<bool, string, string>(false, "退款失败!", "");
}
/// <summary>
/// 通联退款
/// </summary>
/// <param name="accountType"></param>
/// <param name="tradeNo">商户订单号</param>
/// <param name="totalAmount">退款金额</param>
/// <param name="refundNo">原交易订单号</param>
/// <param name="paidAmount">原交易订单实收金额</param>
/// <param name="reason">退款备注</param>
/// <param name="orderType">订单类型</param>
/// <param name="payuserId">付款账号</param>
/// <param name="refundList">托管代收订单中的收款人的退款金额</param>
/// <returns></returns>
public static Tuple<bool, string, OrderRefundResponseInfo> RefundByAllinpay(AccountTypeEnum accountType, string tradeNo, decimal totalAmount, string refundNo, decimal paidAmount, string reason, OrderType orderType, string payuserId, List<ApplyReciever> refundList)
{
lock (Global.Instance.SyncLock)
{
try
{
logger.Info("订单[{0}]开始发起通联退款......", tradeNo);
var info = GetAllinParameter(accountType);
if (info.Item1)
{
var parameter = info.Item3;
//付款账号为空时,使用配置参数的付款账号
if (string.IsNullOrEmpty(payuserId))
{
payuserId = parameter["payuserid"].ToString();
}
var refundModel = new OrderRefundModel
{
reqsn = tradeNo,
oldreqsn = refundNo,
payuserid = payuserId,
amount = Convert.ToInt32(totalAmount),
refundType = "D0",
backUrl = "https://www.baidu.com",
remark = reason
};
#region subin 20230918 由前端支付到统一账号,退款从统一账号退款改为分店账号收款分店账号退款
//subin 20230918 注释掉,由前端支付到统一账号,退款从统一账号退款改为分店账号收款分店账号退款
//if (paidAmount > totalAmount)
//{
// var _refundList = new List<ApplyReciever>();
// _refundList.Add(new ApplyReciever { bizUserId = AllinpayLib.BizUserId, amount = Convert.ToInt16(Math.Abs(totalAmount)) });
// refundModel.refundList = JsonUtils.Serialize(_refundList);
//}
//全额退款refundList无需上送原路径返回;部分退款refundList必须上送.
//if (refundList.Count() > 0)
//{
// refundModel.refundList = JsonUtils.Serialize(refundList);
//}
refundModel.refundList = JsonUtils.Serialize(refundList);
#endregion
var paramInfo = new OrderRefundParamInfo
{
bizContent = JsonUtils.Serialize(refundModel)
};
var _response = AllinpayHelper.OrderRefund(paramInfo);
if (_response.IsSuccess)
{
var _data = JsonUtils.Deserialize<OrderRefund>(_response.ResultInfo.data);
var flg = false;
string msg = null;
switch (_data.status)
{
case "success":
flg = true;
break;
case "pending":
flg = true;
break;
case "fail":
flg = false;
msg = "退款失败";
break;
}
logger.Info("订单[{0}]通联退款结果:{1}", tradeNo, _response.ResultInfo.data);
return new Tuple<bool, string, OrderRefundResponseInfo>(flg, msg, _response.ResultInfo);
}
else
{
logger.Info("订单[{0}]通联退款错误:{1}", tradeNo, _response.ShowUserMsg);
return new Tuple<bool, string, OrderRefundResponseInfo>(false, _response.ShowUserMsg, null);
}
}
else
{
return new Tuple<bool, string, OrderRefundResponseInfo>(false, info.Item2, null);
}
}
catch (Exception ex)
{
logger.Error(ex, "订单[{0}]调用通联退款接口异常", tradeNo);
return new Tuple<bool, string, OrderRefundResponseInfo>(false, "调用通联退款接口异常", null);
}
}
}
/// <summary>
/// 退款结果查询
/// </summary>
/// <param name="accountType"></param>
/// <param name="tradeNo">商户订单号</param>
/// <returns></returns>
public static Tuple<bool, string, GetOrderDetailResponseInfo> QueryAllinpayRefundResult(AccountTypeEnum accountType, string tradeNo)
{
lock (Global.Instance.SyncLock)
{
try
{
logger.Info("订单[{0}]开始发起通联退款查询请求......", tradeNo);
var info = GetAllinParameter(accountType);
if (info.Item1)
{
var parameter = info.Item3;
//AllinpayLib.AppId = parameter.ContainsKey("appId") ? parameter["appId"].ToString() : "";
//AllinpayLib.AppKey = parameter.ContainsKey("appKey") ? parameter["appKey"].ToString() : "";
//AllinpayLib.Url = parameter.ContainsKey("url") ? parameter["url"].ToString() : "";
//AllinpayLib.VspCusid = parameter.ContainsKey("vspCusid") ? parameter["vspCusid"].ToString() : "";
//AllinpayLib.Payuserid = parameter.ContainsKey("payuserid") ? parameter["payuserid"].ToString() : "";
//AllinpayLib.BizUserId = parameter.ContainsKey("bizUserId") ? parameter["bizUserId"].ToString() : "";
var orderDetailModel = new GetOrderDetailModel()
{
reqsn = tradeNo,
};
var paramInfo = new GetOrderDetailParamInfo
{
appId = parameter["appId"].ToString(),
key = parameter["appKey"].ToString(),
bizContent = JsonUtils.Serialize(orderDetailModel)
};
var _response = AllinpayHelper.GetOrderDetail(paramInfo);
if (_response.IsSuccess)
{
var _data = JsonUtils.Deserialize<OrderDetail>(_response.ResultInfo.data);
logger.Info("订单[{0}]通联退款查询请求结果:{1}", tradeNo, _response.ResultInfo.data);
var _statusDesc = "进行中";
var flg = false;
switch (_data.status)
{
case "1":
_statusDesc = "未支付";
break;
case "3":
_statusDesc = "交易失败";
break;
case "4":
_statusDesc = "交易成功";
break;
case "5":
_statusDesc = "交易成功-发生退款";
break;
case "6":
_statusDesc = "关闭";
break;
case "99":
_statusDesc = "进行中";
break;
}
if (!_statusDesc.Equals("交易成功"))
{
return new Tuple<bool, string, GetOrderDetailResponseInfo>(false, _statusDesc, null);
}
return new Tuple<bool, string, GetOrderDetailResponseInfo>(true, null, _response.ResultInfo);
}
else
{
logger.Info("订单[{0}]通联退款查询请求结果错误:{1}", tradeNo, _response.ShowUserMsg);
return new Tuple<bool, string, GetOrderDetailResponseInfo>(false, _response.ShowUserMsg, null);
}
}
else
{
return new Tuple<bool, string, GetOrderDetailResponseInfo>(false, info.Item2, null);
}
}
catch (Exception ex)
{
logger.Error(ex, "订单[{0}]调用通联退款查询接口异常", tradeNo);
return new Tuple<bool, string, GetOrderDetailResponseInfo>(false, "调用通联退款查询接口异常", null);
}
}
}
}
#endregion
}