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#

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 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
}
}