using Newtonsoft.Json; using POSV.Entity; using POSV.Helper; using POSV.LeShuaPay; using POSV.Proxy.ThirdPartyPayMent.PayApiBase; using POSV.ShoppingCart; using POSV.Utils; using System; using System.Collections.Generic; using System.Linq; using System.Security.Cryptography; using System.Text; using System.Xml; namespace POSV.Proxy.ThirdPartyPayMent.LeShuaPay { public class LeShuaPay : PayBase { /// /// 异步支付 /// /// /// /// public override void AsyncPayStart(string payCode, PayMode payMode, OrderObject order, Dictionary value) { } /// /// 同步支付 /// /// public override Tuple PayStart(string payCode, PayMode payMode, string TradeNo, decimal PaidAmount, Dictionary value) { var _data = this.PayResult(payCode, payMode, TradeNo, PaidAmount); //Status //0 支付中 //2 支付成功 //6 订单关闭 //8 支付失败 //10 退款中 //11 退款成功 //12 退款失败 if (!_data.Item1) { return new Tuple(false, _data.Item2, null); } var _leshua = _data.Item3 as LeShuaPayAuthCodeData; return ToPayObject(_leshua, payMode, TradeNo, PaidAmount); } /// /// 支付查询 /// /// /// 1、商户2、乐刷订单号,必须按顺序 /// public override Tuple QueryPay(PayMode payMode, string TradeNo, string OutTradeNo, Dictionary value) { var _data = QueryPayResult(payMode, OutTradeNo, value); if (!_data.Item1) { return new Tuple(false, _data.Item2, null); } if (!_data.Item3.Status.Equals("2")) { if (string.IsNullOrEmpty(_data.Item3.Error_msg)) { switch (_data.Item3.Status) { case "0": _data.Item3.Error_msg = "支付中"; break; case "6": _data.Item3.Error_msg = "订单关闭"; break; case "8": _data.Item3.Error_msg = "支付失败"; break; case "10": _data.Item3.Error_msg = "退款中"; break; case "12": _data.Item3.Error_msg = "退款失败"; break; default: break; } } return new Tuple(false, _data.Item3.Error_msg == null ? _data.Item3.Resp_msg : _data.Item3.Error_msg, null); } return ToPayObject(_data.Item3, payMode, TradeNo, decimal.Parse(_data.Item3.Buyer_pay_amount) / 100); } /// /// 退款 /// /// /// /// /// public override Tuple RefundPay(PayMode payMode, PayItem payItem, Dictionary value) { var _data = RefundPayResult(payMode, payItem, value); if (!_data.Item1) { return new Tuple(false, _data.Item2, null); } if (!_data.Item3.Status.Equals("10")) { return new Tuple(false, _data.Item3.Error_msg == null ? _data.Item3.Resp_msg : _data.Item3.Error_msg, null); } return new Tuple(true, "", null); } /// /// 验证支付配置 /// /// /// /// public override Tuple> VerifyPay(PayMode payMode) { try { var parameter = payMode.Body; //支付参数 string channel = parameter.ContainsKey("channel") ? parameter["channel"].ToString() : ""; string merchant_id = parameter.ContainsKey("merchant_id") ? parameter["merchant_id"].ToString() : ""; string payKey = parameter.ContainsKey("payKey") ? parameter["payKey"].ToString() : ""; string notifyKey = parameter.ContainsKey("notifyKey") ? parameter["notifyKey"].ToString() : ""; string gatewayUrl = parameter.ContainsKey("gatewayUrl") ? parameter["gatewayUrl"].ToString() : ""; if (string.IsNullOrEmpty(channel) || string.IsNullOrEmpty(merchant_id) || string.IsNullOrEmpty(payKey) || string.IsNullOrEmpty(notifyKey) || string.IsNullOrEmpty(gatewayUrl)) { return new Tuple>(false, "参数配置不完整...", null); } Dictionary keyValues = new Dictionary(); keyValues.Add("merchant_id", merchant_id); keyValues.Add("channel", channel); keyValues.Add("payKey", payKey); keyValues.Add("notifyKey", notifyKey); keyValues.Add("gatewayUrl", gatewayUrl); return new Tuple>(true, "", keyValues); } catch (Exception ex) { logger.Error(ex); return new Tuple>(false, ex.Message, null); } } //================================内部方法================================ /// /// 发起支付 /// /// private Tuple PayResult(string payCode, PayMode payMode, string TradeNo, decimal PaidAmount) { try { var _verify = this.VerifyPay(payMode); Dictionary keyValues = new Dictionary(); //接口名 keyValues.Add("service", "upload_authcode"); //付款码 keyValues.Add("auth_code", payCode); //商户号 keyValues.Add("merchant_id", _verify.Item3.FirstOrDefault(f => f.Key.Equals("merchant_id")).Value); //商户订单号 keyValues.Add("third_order_id", DateTime.Now.ToString("yyyyMMddHHmmss") + TradeNo); //订单金额 keyValues.Add("amount", ((int)(PaidAmount * 100)).ToString("0")); ////通知地址 //keyValues.Add("notify_url", ""); ////用户IP地址 //keyValues.Add("client_ip", DeviceUtils.Instance.IPAddress); ////商品描述 //keyValues.Add("body", "餐饮"); ////商户门店编号 //keyValues.Add("shop_no", order.StoreNo); ////商户终端编号 //keyValues.Add("pos_no", order.PosNo); ////附加数据 //keyValues.Add("attach", ""); ////指定支付方式 //keyValues.Add("limit_pay", "0"); ////订单优惠标记 //keyValues.Add("goods_tag", ""); ////商品详情 //keyValues.Add("goods_detail", ""); ////订单有效时间 //keyValues.Add("order_expiration", "60"); ////花呗分期数 //keyValues.Add("hb_fq_num", "0"); //随机字符串 keyValues.Add("nonce_str", new Random().Next(100000, 999999).ToString()); //签名 keyValues.Add("sign", getParamSrc(keyValues, _verify.Item3.FirstOrDefault(f => f.Key == "payKey").Value)); logger.Info(JsonConvert.SerializeObject(keyValues)); var _data = HttpHelper.Post(_verify.Item3.FirstOrDefault(f => f.Key.Equals("gatewayUrl")).Value, keyValues); logger.Info(_data); XmlDocument xx = new XmlDocument(); xx.LoadXml(_data);//加载xml XmlNodeList xxList = xx.GetElementsByTagName("leshua"); //取得节点名为row的XmlNode集合 Dictionary keyValuePairs = new Dictionary(); foreach (XmlNode xxNode in xxList) { var _nodes = xxNode.ChildNodes; foreach (XmlNode item in _nodes) { keyValuePairs.Add(item.Name, item.InnerText); } } var _LeShuaPaydata = JsonConvert.DeserializeObject(JsonConvert.SerializeObject(keyValuePairs)); if (_LeShuaPaydata.Resp_code.Equals("0") && _LeShuaPaydata.Result_code.Equals("0")) { return new Tuple(true, "", _LeShuaPaydata); } else { return new Tuple(false, _LeShuaPaydata.Error_msg == null ? _LeShuaPaydata.Resp_msg : _LeShuaPaydata.Error_msg, 0.00M); } } catch (Exception ex) { logger.Error(ex); return new Tuple(false, ex.Message, 0.00M); } } /// /// 发起支付查询 /// /// /// /// private Tuple QueryPayResult(PayMode payMode, string OutTradeNo, Dictionary keys) { var _verify = this.VerifyPay(payMode); Dictionary keyValues = new Dictionary(); //接口名 keyValues.Add("service", "query_status"); //商户号 keyValues.Add("merchant_id", _verify.Item3.FirstOrDefault(f => f.Key.Equals("merchant_id")).Value); //商户订单号 keyValues.Add("third_order_id", OutTradeNo); ////乐刷订单号 //keyValues.Add("leshua_order_id", args[1]); //随机字符串 keyValues.Add("nonce_str", new Random().Next(100000, 999999).ToString()); //签名 keyValues.Add("sign", getParamSrc(keyValues, _verify.Item3.FirstOrDefault(f => f.Key == "payKey").Value)); logger.Info(JsonConvert.SerializeObject(keyValues)); var _data = HttpHelper.Post(_verify.Item3.FirstOrDefault(f => f.Key.Equals("gatewayUrl")).Value, keyValues); logger.Info(_data); XmlDocument xx = new XmlDocument(); xx.LoadXml(_data);//加载xml XmlNodeList xxList = xx.GetElementsByTagName("leshua"); //取得节点名为row的XmlNode集合 Dictionary keyValuePairs = new Dictionary(); foreach (XmlNode xxNode in xxList) { var _nodes = xxNode.ChildNodes; foreach (XmlNode item in _nodes) { keyValuePairs.Add(item.Name, item.InnerText); } } var _LeShuaPaydata = JsonConvert.DeserializeObject(JsonConvert.SerializeObject(keyValuePairs)); if (_LeShuaPaydata.Resp_code.Equals("0") && _LeShuaPaydata.Result_code.Equals("0")) { return new Tuple(true, "", _LeShuaPaydata); } else { return new Tuple(false, _LeShuaPaydata.Error_msg == null ? _LeShuaPaydata.Resp_msg : _LeShuaPaydata.Error_msg, null); } } /// /// 退款接口 /// private Tuple RefundPayResult(PayMode payMode, PayItem payItem, Dictionary keys) { var _verify = this.VerifyPay(payMode); Dictionary keyValues = new Dictionary(); //接口名 keyValues.Add("service", "unified_refund"); //商户号 keyValues.Add("merchant_id", _verify.Item3.FirstOrDefault(f => f.Key.Equals("merchant_id")).Value); //商户订单号 keyValues.Add("third_order_id", payItem.TradeVoucherNo); //商户退款id //keyValues.Add("merchant_refund_id", "T" + DateTime.Now.ToString("yyyyMMddHHmmss") + payItem.OutTradeNo); keyValues.Add("merchant_refund_id", "T" + DateTime.Now.ToString("yyyyMMddHHmmss") + payItem.PayNo); //退款金额 keyValues.Add("refund_amount", ((int)(Math.Abs(payItem.PaidAmount)* 100)).ToString()); //随机字符串 keyValues.Add("nonce_str", new Random().Next(100000, 999999).ToString()); //签名 keyValues.Add("sign", getParamSrc(keyValues, _verify.Item3.FirstOrDefault(f => f.Key == "payKey").Value)); var _data = HttpHelper.Post(_verify.Item3.FirstOrDefault(f => f.Key.Equals("gatewayUrl")).Value, keyValues); XmlDocument xx = new XmlDocument(); xx.LoadXml(_data);//加载xml XmlNodeList xxList = xx.GetElementsByTagName("leshua"); //取得节点名为row的XmlNode集合 Dictionary keyValuePairs = new Dictionary(); foreach (XmlNode xxNode in xxList) { var _nodes = xxNode.ChildNodes; foreach (XmlNode item in _nodes) { keyValuePairs.Add(item.Name, item.InnerText); } } var _LeShuaPaydata = JsonConvert.DeserializeObject(JsonConvert.SerializeObject(keyValuePairs)); if (_LeShuaPaydata.Resp_code.Equals("0") && _LeShuaPaydata.Result_code.Equals("0")) { return new Tuple(true, "", _LeShuaPaydata); } else { return new Tuple(false, _LeShuaPaydata.Error_msg == null ? _LeShuaPaydata.Resp_msg : _LeShuaPaydata.Error_msg, null); } } /// /// 支付转换 /// /// /// protected override Tuple ToPayObject(object queryParam, PayMode payMode, string TradeNo, decimal PaidAmount) { var _leshua = queryParam as LeShuaPayAuthCodeData; if (_leshua == null) { return new Tuple(false, "返回的数据解析错误", null); } //Status //0 支付中 //2 支付成功 //6 订单关闭 //8 支付失败 //10 退款中 //11 退款成功 //12 退款失败 if (!_leshua.Status.Equals("2")) { if (string.IsNullOrEmpty(_leshua.Error_msg)) { switch (_leshua.Status) { case "0": _leshua.Error_msg = "支付中"; break; case "6": _leshua.Error_msg = "订单关闭"; break; case "8": _leshua.Error_msg = "支付失败"; break; case "10": _leshua.Error_msg = "退款中"; break; case "12": _leshua.Error_msg = "退款失败"; break; default: break; } } return new Tuple(false, _leshua.Error_msg, new PayItem() { OutTradeNo = _leshua.Third_order_id }); } PayItem payItem = new PayItem(); payItem.CreateDate = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"); payItem.CreateUser = Global.Instance.Worker.CreateUser; payItem.Id = IdWorkerUtils.Instance.NextId(); using (var _db = Global.Instance.OpenDataBase) { var _pay = _db.FirstOrDefault(string.Format("where no = '{0}'", _leshua.ToPayWayNo)); if (_pay == null) { return new Tuple(false, "支付成功但程序组合数据时失败,查询不到对应支付方式", null); } payItem.Name = _pay.Name; payItem.No = _pay.No; //payItem.OrderId = order.Id; //payItem.Order = order.Pays.Count; payItem.OtherRate = _pay.OtherRateValue; payItem.OtherRateType = _pay.OtherRateType; payItem.OutTradeNo = _leshua.Out_transaction_id; payItem.PaidAmount = PaidAmount; //zhangy 2020-03-07 Add 解决乐刷支付实收金额的Bug //已收金额,真正意义上实际收款金额 payItem.Amount = payItem.PaidAmount; payItem.PayChannel = PayChannelEnum.乐刷支付; //zhangy 2020-05-29 Edit BusNo不合法 //payItem.PayNo = _leshua.Third_order_id; //zhangy 2020-05-29 Add 解决数据上送引发的BusNo不合法Bug payItem.PayNo = OrderUtils.Instance.GeneratePayNo(); payItem.PayTime = _leshua.Channel_datetime; payItem.ShiftName = Global.Instance.BusinessPlanLog.Name; payItem.ShiftNo = Global.Instance.BusinessPlanLog.No; payItem.Status = 1; payItem.TenantId = Global.Instance.Worker.TenantId; payItem.TradeNo = TradeNo; payItem.TradeVoucherNo = _leshua.Third_order_id; } return new Tuple(true, "", payItem); } #region 签名方法 /// /// MD5加密 /// /// /// public static string GetMD5(string myString) { MD5 md5 = new MD5CryptoServiceProvider(); byte[] fromData = System.Text.Encoding.UTF8.GetBytes(myString); byte[] targetData = md5.ComputeHash(fromData); string byte2String = null; for (int i = 0; i < targetData.Length; i++) { byte2String = byte2String + targetData[i].ToString("x2"); } return byte2String; } /// /// 参数排序 /// /// /// public static Dictionary AsciiDictionary(Dictionary sArray) { Dictionary asciiDic = new Dictionary(); string[] arrKeys = sArray.Keys.ToArray(); Array.Sort(arrKeys, string.CompareOrdinal); foreach (var key in arrKeys) { string value = sArray[key]; asciiDic.Add(key, value); } return asciiDic; } /// /// 签名序列化 /// /// /// /// public static string getParamSrc(Dictionary paramsMap, string key) { var vDic = (from objDic in paramsMap orderby objDic.Key ascending select objDic); StringBuilder str = new StringBuilder(); foreach (KeyValuePair item in vDic) { if (string.IsNullOrEmpty(item.Value) || item.Key.Equals("leshua") || item.Key.Equals("resp_code") || item.Key.Equals("sign")) { continue; } string pkey = item.Key; string pvalue = item.Value; str.Append(pkey + "=" + pvalue + "&"); } string result = str.ToString().Substring(0, str.ToString().Length - 1); return GetMD5(result + "&key=" + key); } #endregion } }