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 /// /// 通联支付API /// public class AllinApi { private static Logger logger = NLog.LogManager.GetCurrentClassLogger(); public static Tuple> GetAllinParameter(AccountTypeEnum accountType) { if (accountType == AccountTypeEnum.门店) { var payMode = OrderUtils.GetPayMode("50"); if (payMode.Body == null || payMode.Body.Count == 0) { var payModelBody = new Dictionary(); 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>(false, "通联支付参数配置不完整", null); } Dictionary dic = new Dictionary(); 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>(true, "获取成功", dic); } else { return new Tuple>(false, "未启用通联扫码支付", null); } } else { return new Tuple>(false, "暂不支持总部通联支付参数查询", null); } } /// /// 通联退款+查询结果 /// 备注:前端支付到统一账号,退款也从统一账号退款 /// /// /// 商户订单号 /// 原订单的实收金额 /// 退款金额 /// 原交易的商户订单号 /// 备注 /// /// 订单类型 /// 付款账号 /// public static Tuple 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(); 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(true, "退款成功!"); var _data = JsonUtils.Deserialize(queryRefundResponse.Item3.data); // logger.Info("订单[{0}]分店退款成功!", tradeNo); // return new Tuple(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(flg, msg, queryRefundResponse.Item3.data); } i++; System.Threading.Thread.Sleep(3000);//睡眠3秒 } if (i == queryCount) { logger.Info("订单[{0}]查询退款结果超时,退单失败!", tradeNo); return new Tuple(false, "查询退款结果超时,退单失败!", ""); } } else { return new Tuple(false, refundResult.msg, refundResult.subMsg); } } else { //请求失败 return new Tuple(false, refundResponse.Item2, ""); } } catch (Exception ex) { logger.Error(ex, "订单[" + tradeNo + "]通联退款发生异常"); return new Tuple(false, "退款发生异常!", ex.Message); } logger.Info("订单[{0}]退款失败!", tradeNo); return new Tuple(false, "退款失败!", ""); } /// /// 通联退款+查询结果 /// 备注:前端支付到分店账号,退款也从各个分店账号退款 /// /// /// 退款订单 /// 原交易订单号 /// 原交易订单总金额 /// 原订单付款方userid,当小程序付款时为微信的openid /// /// public static Tuple RefundAndQuery(AccountTypeEnum accountType, OrderObject backOrder, string oldTradeNo, int oldAmount, string payuserId, int timeout = 60) { try { var _refundList = new List(); 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(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(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(queryRefundResponse.Item3.data); // logger.Info("订单[{0}]分店退款成功!", tradeNo); // return new Tuple(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(flg, msg, queryRefundResponse.Item3.data); } i++; System.Threading.Thread.Sleep(3000);//睡眠3秒 } if (i == queryCount) { logger.Info("订单[{0}]查询分店退款结果超时,退单失败!", tradeNo); return new Tuple(false, "查询退款结果超时,退单失败!", null); } } else { return new Tuple(false, refundResult.msg, ""); } } else { //请求失败 return new Tuple(false, refundResponse.Item2, ""); } } catch (Exception ex) { logger.Error(ex, "订单[" + backOrder.TradeNo + "]分店账号通联退款发生异常"); return new Tuple(false, "退款发生异常!", ex.Message); } logger.Info("订单[{0}]分店账号退款失败!", backOrder.TradeNo); return new Tuple(false, "退款失败!", ""); } /// /// 通联退款 /// /// /// 商户订单号 /// 退款金额 /// 原交易订单号 /// 原交易订单实收金额 /// 退款备注 /// 订单类型 /// 付款账号 /// 托管代收订单中的收款人的退款金额 /// public static Tuple RefundByAllinpay(AccountTypeEnum accountType, string tradeNo, decimal totalAmount, string refundNo, decimal paidAmount, string reason, OrderType orderType, string payuserId, List 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(); // _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(_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(flg, msg, _response.ResultInfo); } else { logger.Info("订单[{0}]通联退款错误:{1}", tradeNo, _response.ShowUserMsg); return new Tuple(false, _response.ShowUserMsg, null); } } else { return new Tuple(false, info.Item2, null); } } catch (Exception ex) { logger.Error(ex, "订单[{0}]调用通联退款接口异常", tradeNo); return new Tuple(false, "调用通联退款接口异常", null); } } } /// /// 退款结果查询 /// /// /// 商户订单号 /// public static Tuple 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(_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(false, _statusDesc, null); } return new Tuple(true, null, _response.ResultInfo); } else { logger.Info("订单[{0}]通联退款查询请求结果错误:{1}", tradeNo, _response.ShowUserMsg); return new Tuple(false, _response.ShowUserMsg, null); } } else { return new Tuple(false, info.Item2, null); } } catch (Exception ex) { logger.Error(ex, "订单[{0}]调用通联退款查询接口异常", tradeNo); return new Tuple(false, "调用通联退款查询接口异常", null); } } } } #endregion }