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.

531 lines
21 KiB
C#

9 months ago
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
{
/// <summary>
/// 异步支付
/// </summary>
/// <param name="payCode"></param>
/// <param name="payMode"></param>
/// <param name="order"></param>
public override void AsyncPayStart(string payCode, PayMode payMode, OrderObject order, Dictionary<string, string> value)
{
}
/// <summary>
/// 同步支付
/// </summary>
/// <returns></returns>
public override Tuple<bool, string, PayItem> PayStart(string payCode, PayMode payMode, string TradeNo, decimal PaidAmount, Dictionary<string, string> value)
{
var _data = this.PayResult(payCode, payMode, TradeNo, PaidAmount);
//Status
//0 支付中
//2 支付成功
//6 订单关闭
//8 支付失败
//10 退款中
//11 退款成功
//12 退款失败
if (!_data.Item1)
{
return new Tuple<bool, string, PayItem>(false, _data.Item2, null);
}
var _leshua = _data.Item3 as LeShuaPayAuthCodeData;
return ToPayObject(_leshua, payMode, TradeNo, PaidAmount);
}
/// <summary>
/// 支付查询
/// </summary>
/// <param name="payMode"></param>
/// <param name="args">1、商户2、乐刷订单号必须按顺序</param>
/// <returns></returns>
public override Tuple<bool, string, PayItem> QueryPay(PayMode payMode, string TradeNo, string OutTradeNo, Dictionary<string, string> value)
{
var _data = QueryPayResult(payMode, OutTradeNo, value);
if (!_data.Item1)
{
return new Tuple<bool, string, PayItem>(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<bool, string, PayItem>(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);
}
/// <summary>
/// 退款
/// </summary>
/// <param name="payMode"></param>
/// <param name="payItem"></param>
/// <param name="args"></param>
/// <returns></returns>
public override Tuple<bool, string, PayItem> RefundPay(PayMode payMode, PayItem payItem, Dictionary<string, string> value)
{
var _data = RefundPayResult(payMode, payItem, value);
if (!_data.Item1)
{
return new Tuple<bool, string, PayItem>(false, _data.Item2, null);
}
if (!_data.Item3.Status.Equals("10"))
{
return new Tuple<bool, string, PayItem>(false, _data.Item3.Error_msg == null ? _data.Item3.Resp_msg : _data.Item3.Error_msg, null);
}
return new Tuple<bool, string, PayItem>(true, "", null);
}
/// <summary>
/// 验证支付配置
/// </summary>
/// <param name="payCode"></param>
/// <param name="payMode"></param>
/// <returns></returns>
public override Tuple<bool, string, Dictionary<string, string>> 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<bool, string, Dictionary<string, string>>(false, "参数配置不完整...", null);
}
Dictionary<string, string> keyValues = new Dictionary<string, string>();
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<bool, string, Dictionary<string, string>>(true, "", keyValues);
}
catch (Exception ex)
{
logger.Error(ex);
return new Tuple<bool, string, Dictionary<string, string>>(false, ex.Message, null);
}
}
//================================内部方法================================
/// <summary>
/// 发起支付
/// </summary>
/// <returns></returns>
private Tuple<bool, string, object> PayResult(string payCode, PayMode payMode, string TradeNo, decimal PaidAmount)
{
try
{
var _verify = this.VerifyPay(payMode);
Dictionary<string, string> keyValues = new Dictionary<string, string>();
//接口名
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<string, string> keyValuePairs = new Dictionary<string, string>();
foreach (XmlNode xxNode in xxList)
{
var _nodes = xxNode.ChildNodes;
foreach (XmlNode item in _nodes)
{
keyValuePairs.Add(item.Name, item.InnerText);
}
}
var _LeShuaPaydata = JsonConvert.DeserializeObject<LeShuaPayAuthCodeData>(JsonConvert.SerializeObject(keyValuePairs));
if (_LeShuaPaydata.Resp_code.Equals("0") && _LeShuaPaydata.Result_code.Equals("0"))
{
return new Tuple<bool, string, object>(true, "", _LeShuaPaydata);
}
else
{
return new Tuple<bool, string, object>(false, _LeShuaPaydata.Error_msg == null ? _LeShuaPaydata.Resp_msg : _LeShuaPaydata.Error_msg, 0.00M);
}
}
catch (Exception ex)
{
logger.Error(ex);
return new Tuple<bool, string, object>(false, ex.Message, 0.00M);
}
}
/// <summary>
/// 发起支付查询
/// </summary>
/// <param name="payMode"></param>
/// <param name="args"></param>
/// <returns></returns>
private Tuple<bool, string, LeShuaPayAuthCodeData> QueryPayResult(PayMode payMode, string OutTradeNo, Dictionary<string, string> keys)
{
var _verify = this.VerifyPay(payMode);
Dictionary<string, string> keyValues = new Dictionary<string, string>();
//接口名
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<string, string> keyValuePairs = new Dictionary<string, string>();
foreach (XmlNode xxNode in xxList)
{
var _nodes = xxNode.ChildNodes;
foreach (XmlNode item in _nodes)
{
keyValuePairs.Add(item.Name, item.InnerText);
}
}
var _LeShuaPaydata = JsonConvert.DeserializeObject<LeShuaPayAuthCodeData>(JsonConvert.SerializeObject(keyValuePairs));
if (_LeShuaPaydata.Resp_code.Equals("0") && _LeShuaPaydata.Result_code.Equals("0"))
{
return new Tuple<bool, string, LeShuaPayAuthCodeData>(true, "", _LeShuaPaydata);
}
else
{
return new Tuple<bool, string, LeShuaPayAuthCodeData>(false, _LeShuaPaydata.Error_msg == null ? _LeShuaPaydata.Resp_msg : _LeShuaPaydata.Error_msg, null);
}
}
/// <summary>
/// 退款接口
/// </summary>
private Tuple<bool, string, LeShuaPayAuthCodeData> RefundPayResult(PayMode payMode, PayItem payItem, Dictionary<string, string> keys)
{
var _verify = this.VerifyPay(payMode);
Dictionary<string, string> keyValues = new Dictionary<string, string>();
//接口名
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<string, string> keyValuePairs = new Dictionary<string, string>();
foreach (XmlNode xxNode in xxList)
{
var _nodes = xxNode.ChildNodes;
foreach (XmlNode item in _nodes)
{
keyValuePairs.Add(item.Name, item.InnerText);
}
}
var _LeShuaPaydata = JsonConvert.DeserializeObject<LeShuaPayAuthCodeData>(JsonConvert.SerializeObject(keyValuePairs));
if (_LeShuaPaydata.Resp_code.Equals("0") && _LeShuaPaydata.Result_code.Equals("0"))
{
return new Tuple<bool, string, LeShuaPayAuthCodeData>(true, "", _LeShuaPaydata);
}
else
{
return new Tuple<bool, string, LeShuaPayAuthCodeData>(false, _LeShuaPaydata.Error_msg == null ? _LeShuaPaydata.Resp_msg : _LeShuaPaydata.Error_msg, null);
}
}
/// <summary>
/// 支付转换
/// </summary>
/// <param name="queryParam"></param>
/// <returns></returns>
protected override Tuple<bool, string, PayItem> ToPayObject(object queryParam, PayMode payMode, string TradeNo, decimal PaidAmount)
{
var _leshua = queryParam as LeShuaPayAuthCodeData;
if (_leshua == null)
{
return new Tuple<bool, string, PayItem>(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<bool, string, PayItem>(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<PayMode>(string.Format("where no = '{0}'", _leshua.ToPayWayNo));
if (_pay == null)
{
return new Tuple<bool, string, PayItem>(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<bool, string, PayItem>(true, "", payItem);
}
#region 签名方法
/// <summary>
/// MD5加密
/// </summary>
/// <param name="myString"></param>
/// <returns></returns>
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;
}
/// <summary>
/// 参数排序
/// </summary>
/// <param name="sArray"></param>
/// <returns></returns>
public static Dictionary<string, string> AsciiDictionary(Dictionary<string, string> sArray)
{
Dictionary<string, string> asciiDic = new Dictionary<string, string>();
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;
}
/// <summary>
/// 签名序列化
/// </summary>
/// <param name="paramsMap"></param>
/// <param name="key"></param>
/// <returns></returns>
public static string getParamSrc(Dictionary<string, string> paramsMap, string key)
{
var vDic = (from objDic in paramsMap orderby objDic.Key ascending select objDic);
StringBuilder str = new StringBuilder();
foreach (KeyValuePair<string, string> 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
}
}