微信支付之支付码支付
<strong>一、获取微信支付码url</strong>(1)获取微信支付码url主方法 /// <summary>/// 获取微信支付二维码
/// </summary>
/// <param name="log">日志</param>
/// <param name="orderId">订单编号</param>
/// <returns></returns>
public static string GetPayUrl(string orderId, decimal totalPrice)
{
//errMsg = "";
//Log4Net.Log4Net.Info(log, "订单号:" + orderId + "发起Native的第二种支付方式");
WxPayData data = new WxPayData();
data.SetValue("body", "");//商品描述
data.SetValue("attach", "");//附加数据
data.SetValue("out_trade_no", orderId);//随机字符串
data.SetValue("total_fee", Convert.ToInt32(totalPrice * 100));//总金额
data.SetValue("time_start", DateTime.Now.ToString("yyyyMMddHHmmss"));//交易起始时间
data.SetValue("time_expire", DateTime.Now.AddMinutes(30).ToString("yyyyMMddHHmmss"));//交易结束时间,前端二维码有效期半小时
data.SetValue("goods_tag", "");//商品标记(可根据业务随便填)
data.SetValue("trade_type", "NATIVE");//交易类型
data.SetValue("product_id", orderId);//商品ID
WxPayData result = WxPayApi.UnifiedOrder(data);//调用统一下单接口
string url = string.Empty;
if (result.GetValue("return_code").ToString() == "SUCCESS")
{
if (result.GetValue("result_code").ToString() == "SUCCESS")
{
url = result.GetValue("code_url").ToString();//获得统一下单接口返回的二维码链接
}
else
{
//errMsg = result.GetValue("err_code_des").ToString();
}
}
else
{
//errMsg = result.GetValue("return_msg").ToString();
}
//Log4Net.Log4Net.Info(log, "订单号:" + orderId + "发起Native的第二种支付方式,生成支付链接:" + url);
return url;
}(2)支付辅助类/**
*
* 统一下单
* @param WxPaydata inputObj 提交给统一下单API的参数
* @param int timeOut 超时时间
* @throws WxPayException
* @return 成功时返回,其他抛异常
*/
public static WxPayData UnifiedOrder(WxPayData inputObj, int timeOut = 6)
{
string url = "https://api.mch.weixin.qq.com/pay/unifiedorder";
//检测必填参数
if (!inputObj.IsSet("out_trade_no"))
{
throw new Exception("缺少统一支付接口必填参数out_trade_no!");
}
else if (!inputObj.IsSet("body"))
{
throw new Exception("缺少统一支付接口必填参数body!");
}
else if (!inputObj.IsSet("total_fee"))
{
throw new Exception("缺少统一支付接口必填参数total_fee!");
}
else if (!inputObj.IsSet("trade_type"))
{
throw new Exception("缺少统一支付接口必填参数trade_type!");
}
//关联参数
if (inputObj.GetValue("trade_type").ToString() == "JSAPI" && !inputObj.IsSet("openid"))
{
throw new Exception("统一支付接口中,缺少必填参数openid!trade_type为JSAPI时,openid为必填参数!");
}
if (inputObj.GetValue("trade_type").ToString() == "NATIVE" && !inputObj.IsSet("product_id"))
{
throw new Exception("统一支付接口中,缺少必填参数product_id!trade_type为JSAPI时,product_id为必填参数!");
}
//异步通知url未设置,则使用配置文件中的url
if (!inputObj.IsSet("notify_url"))
{
inputObj.SetValue("notify_url", WxPayConfig.GetConfig().GetNotifyUrl());//异步通知url
}
inputObj.SetValue("appid", WxPayConfig.GetConfig().GetAppID());//appID
inputObj.SetValue("mch_id", WxPayConfig.GetConfig().GetMchID());//商户号
inputObj.SetValue("spbill_create_ip", WxPayConfig.GetConfig().GetIp());//终端ip
inputObj.SetValue("nonce_str", GenerateNonceStr());//随机字符串
inputObj.SetValue("sign_type", WxPayData.SIGN_TYPE_HMAC_SHA256);//签名类型
//签名
inputObj.SetValue("sign", inputObj.MakeSign());
string xml = inputObj.ToXml();
var start = DateTime.Now;
//Log4Net.Log4Net.Info(log, "WX UnfiedOrder request : " + xml);
string response = HttpService.Post(xml, url, false, timeOut);
//Log4Net.Log4Net.Info(log, "WX UnfiedOrder response : " + response);
var end = DateTime.Now;
int timeCost = (int)((end - start).TotalMilliseconds);
WxPayData result = new WxPayData();
result.FromXml(response);
ReportCostTime(url, timeCost, result);//测速上报
return result;
} public class WxPayData
{
public const string SIGN_TYPE_MD5 = "MD5";
public const string SIGN_TYPE_HMAC_SHA256 = "HMAC-SHA256";
public WxPayData()
{
}
//采用排序的Dictionary的好处是方便对数据包进行签名,不用再签名之前再做一次排序
private SortedDictionary<string, object> m_values = new SortedDictionary<string, object>();
/**
* 设置某个字段的值
* @param key 字段名
* @param value 字段值
*/
public void SetValue(string key, object value)
{
m_values = value;
}
/**
* 根据字段名获取某个字段的值
* @param key 字段名
* @return key对应的字段值
*/
public object GetValue(string key)
{
object o = null;
m_values.TryGetValue(key, out o);
return o;
}
/**
* 判断某个字段是否已设置
* @param key 字段名
* @return 若字段key已被设置,则返回true,否则返回false
*/
public bool IsSet(string key)
{
object o = null;
m_values.TryGetValue(key, out o);
if (null != o)
return true;
else
return false;
}
/**
* @将Dictionary转成xml
* @return 经转换得到的xml串
* @throws WxPayException
**/
public string ToXml()
{
//数据为空时不能转化为xml格式
if (0 == m_values.Count)
{
throw new Exception("WxPayData数据为空!");
}
string xml = "<xml>";
foreach (KeyValuePair<string, object> pair in m_values)
{
//字段值不能为null,会影响后续流程
if (pair.Value == null)
{
throw new Exception("WxPayData内部含有值为null的字段!");
}
if (pair.Value.GetType() == typeof(int))
{
xml += "<" + pair.Key + ">" + pair.Value + "</" + pair.Key + ">";
}
else if (pair.Value.GetType() == typeof(string))
{
xml += "<" + pair.Key + ">" + "<!]></" + pair.Key + ">";
}
else//除了string和int类型不能含有其他数据类型
{
throw new Exception("WxPayData字段数据类型错误!");
}
}
xml += "</xml>";
return xml;
}
/**
* @将xml转为WxPayData对象并返回对象内部的数据
* @param string 待转换的xml串
* @return 经转换得到的Dictionary
* @throws WxPayException
*/
public SortedDictionary<string, object> FromXml(string xml)
{
if (string.IsNullOrEmpty(xml))
{
throw new Exception("将空的xml串转换为WxPayData不合法!");
}
XmlDocument xmlDoc = new XmlDocument();
xmlDoc.LoadXml(xml);
XmlNode xmlNode = xmlDoc.FirstChild;//获取到根节点<xml>
XmlNodeList nodes = xmlNode.ChildNodes;
foreach (XmlNode xn in nodes)
{
XmlElement xe = (XmlElement)xn;
m_values = xe.InnerText;//获取xml的键值对到WxPayData内部的数据中
}
try
{
//2015-06-29 错误是没有签名
if (m_values["return_code"] != "SUCCESS")
{
return m_values;
}
CheckSign();//验证签名,不通过会抛异常
}
catch (Exception ex)
{
throw new Exception(ex.Message);
}
return m_values;
}
/**
* @Dictionary格式转化成url参数格式
* @ return url格式串, 该串不包含sign字段值
*/
public string ToUrl()
{
string buff = "";
foreach (KeyValuePair<string, object> pair in m_values)
{
if (pair.Value == null)
{
throw new Exception("WxPayData内部含有值为null的字段!");
}
if (pair.Key != "sign" && pair.Value.ToString() != "")
{
buff += pair.Key + "=" + pair.Value + "&";
}
}
buff = buff.Trim('&');
return buff;
}
/**
* @Dictionary格式化成Json
* @return json串数据
*/
public string ToJson()
{
//string jsonStr = JsonMapper.ToJson(m_values);
//return jsonStr;
return "";
}
/**
* @values格式化成能在Web页面上显示的结果(因为web页面上不能直接输出xml格式的字符串)
*/
public string ToPrintStr()
{
string str = "";
foreach (KeyValuePair<string, object> pair in m_values)
{
if (pair.Value == null)
{
throw new Exception("WxPayData内部含有值为null的字段!");
}
str += string.Format("{0}={1}\n", pair.Key, pair.Value.ToString());
}
str = HttpUtility.HtmlEncode(str);
return str;
}
/**
* @生成签名,详见签名生成算法
* @return 签名, sign字段不参加签名
*/
public string MakeSign(string signType)
{
//转url格式
string str = ToUrl();
//在string后加入API KEY
str += "&key=" + WxPayConfig.GetConfig().GetKey();
if (signType == SIGN_TYPE_MD5)
{
var md5 = MD5.Create();
var bs = md5.ComputeHash(Encoding.UTF8.GetBytes(str));
var sb = new StringBuilder();
foreach (byte b in bs)
{
sb.Append(b.ToString("x2"));
}
//所有字符转为大写
return sb.ToString().ToUpper();
}
else if (signType == SIGN_TYPE_HMAC_SHA256)
{
return CalcHMACSHA256Hash(str, WxPayConfig.GetConfig().GetKey());
}
else
{
throw new Exception("sign_type 不合法");
}
}
/**
* @生成签名,详见签名生成算法
* @return 签名, sign字段不参加签名 SHA256
*/
public string MakeSign()
{
return MakeSign(SIGN_TYPE_HMAC_SHA256);
}
/**
*
* 检测签名是否正确
* 正确返回true,错误抛异常
*/
public bool CheckSign(string signType)
{
//如果没有设置签名,则跳过检测
if (!IsSet("sign"))
{
throw new Exception("WxPayData签名存在但不合法!");
}
//如果设置了签名但是签名为空,则抛异常
else if (GetValue("sign") == null || GetValue("sign").ToString() == "")
{
throw new Exception("WxPayData签名存在但不合法!");
}
//获取接收到的签名
string return_sign = GetValue("sign").ToString();
//在本地计算新的签名
string cal_sign = MakeSign(signType);
if (cal_sign == return_sign)
{
return true;
}
throw new Exception("WxPayData签名验证错误!");
}
/**
*
* 检测签名是否正确
* 正确返回true,错误抛异常
*/
public bool CheckSign()
{
return CheckSign(SIGN_TYPE_HMAC_SHA256);
}
/**
* @获取Dictionary
*/
public SortedDictionary<string, object> GetValues()
{
return m_values;
}
private string CalcHMACSHA256Hash(string plaintext, string salt)
{
string result = "";
var enc = Encoding.Default;
byte[]
baText2BeHashed = enc.GetBytes(plaintext),
baSalt = enc.GetBytes(salt);
System.Security.Cryptography.HMACSHA256 hasher = new HMACSHA256(baSalt);
byte[] baHashedText = hasher.ComputeHash(baText2BeHashed);
result = string.Join("", baHashedText.ToList().Select(b => b.ToString("x2")).ToArray());
return result;
}
} /**
* 生成随机串,随机串包含字母或数字
* @return 随机串
*/
public static string GenerateNonceStr()
{
RandomGenerator randomGenerator = new RandomGenerator();
return randomGenerator.GetRandomUInt().ToString();
}public class RandomGenerator
{
readonly RNGCryptoServiceProvider csp;
public RandomGenerator()
{
csp = new RNGCryptoServiceProvider();
}
public int Next(int minValue, int maxExclusiveValue)
{
if (minValue >= maxExclusiveValue)
throw new ArgumentOutOfRangeException("minValue must be lower than maxExclusiveValue");
long diff = (long)maxExclusiveValue - minValue;
long upperBound = uint.MaxValue / diff * diff;
uint ui;
do
{
ui = GetRandomUInt();
} while (ui >= upperBound);
return (int)(minValue + (ui % diff));
}
public uint GetRandomUInt()
{
var randomBytes = GenerateRandomBytes(sizeof(uint));
return BitConverter.ToUInt32(randomBytes, 0);
}
private byte[] GenerateRandomBytes(int bytesNumber)
{
byte[] buffer = new byte;
csp.GetBytes(buffer);
return buffer;
}
} 二、微信支付结果回调接收
(1)支付回调主接收方法
/// <summary>
/// 微信支付回调函数
/// </summary>
/// <returns></returns>
public async Task WxPayNotify()
{
HttpContext context = _httpContextAccessor.HttpContext;
try
{
WxPayData notifyData = GetNotifyData(context);
Log.Information("GetNotifyData finished");
//检查支付结果中transaction_id是否存在
if (!notifyData.IsSet("transaction_id"))
{
//若transaction_id不存在,则立即返回结果给微信支付后台
WxPayData res = new WxPayData();
res.SetValue("return_code", "FAIL");
res.SetValue("return_msg", "支付结果中微信订单号不存在");
await context.Response.WriteAsync(res.ToXml());
}
string transaction_id = notifyData.GetValue("transaction_id").ToString();
string out_trade_no = notifyData.GetValue("out_trade_no").ToString();
//查询订单,判断订单真实性
if (!QueryOrder(transaction_id))
{
//若订单查询失败,则立即返回结果给微信支付后台
WxPayData res = new WxPayData();
res.SetValue("return_code", "FAIL");
res.SetValue("return_msg", "订单查询失败");
await context.Response.WriteAsync(res.ToXml());
}
//查询订单成功
else
{
//判断订单号和支付金额是否和数据库中一致
//修改订单状态,插入支付payment信息
string result_code = notifyData.GetValue("result_code").ToString();
string openid = notifyData.GetValue("openid").ToString();
string trade_type = notifyData.GetValue("trade_type").ToString();
string bank_type = notifyData.GetValue("bank_type").ToString();
int total_fee = Convert.ToInt32(notifyData.GetValue("total_fee"));
int cash_fee = Convert.ToInt32(notifyData.GetValue("cash_fee"));
string time_end = notifyData.GetValue("time_end").ToString();
Log.Information($"out_trade_no is {out_trade_no}");
var orderInfo = await _orderRepository.FindAsync(item => item.OrderNumber.ToString() == out_trade_no);
if (orderInfo == null)
{
//若订单查询失败,则立即返回结果给微信支付后台
WxPayData res = new WxPayData();
res.SetValue("return_code", "FAIL");
res.SetValue("return_msg", "商户订单不存在");
await context.Response.WriteAsync(res.ToXml());
}
Log.Information($"total_fee is {total_fee}");
Log.Information($"DiscountPrice*100 is {orderInfo.DiscountPrice * 100}");
if (orderInfo.DiscountPrice * 100 != total_fee)//订单支付金额不一致
{
WxPayData res = new WxPayData();
res.SetValue("return_code", "FAIL");
res.SetValue("return_msg", "订单支付金额不一致");
await context.Response.WriteAsync(res.ToXml());
}
if (orderInfo.PayStatus == PayStatus.Success && orderInfo.DiscountPrice * 100 == total_fee)//订单已支付
{
WxPayData res = new WxPayData();
res.SetValue("return_code", "SUCCESS");
res.SetValue("return_msg", "OK");
await context.Response.WriteAsync(res.ToXml());
}
else
{<br> //该部分主要是一些自定义逻辑了,可以根据自己的业务需求设置;
WXPayResult wPay = new WXPayResult();
wPay.OpenId = openid;
wPay.TradeState = result_code;
wPay.BankType = bank_type;
wPay.TotalFee = total_fee;
wPay.CashFee = cash_fee;
wPay.TransactionId = transaction_id;
wPay.TimeEnd = time_end;
wPay = await _wXPayResultRepository.InsertAsync(wPay, true);
if (null != wPay)
{
OrderWXPaymentMapping mapping = new OrderWXPaymentMapping();
mapping.OrderNumber = orderInfo.OrderNumber;
mapping.WXPayResultId = wPay.Id;
mapping = await _orderWXPaymentRepository.InsertAsync(mapping, true);
if (mapping != null)
{
Order orderEntity = null;
if (result_code == "SUCCESS")
{
orderInfo.PayStatus = PayStatus.Success;
orderEntity = await _orderRepository.UpdateAsync(orderInfo, true);//10001支付成功
}
else
{
orderInfo.PayStatus = PayStatus.Fail;
orderEntity = await _orderRepository.UpdateAsync(orderInfo, true); ;//10002支付失败
}
if (orderEntity != null)
{
WxPayData res = new WxPayData();
res.SetValue("return_code", "SUCCESS");
res.SetValue("return_msg", "OK");
await context.Response.WriteAsync(res.ToXml());
}
else
{
//若订单查询失败,则立即返回结果给微信支付后台
WxPayData res = new WxPayData();
res.SetValue("return_code", "FAIL");
res.SetValue("return_msg", "商户订单处理失败");
await context.Response.WriteAsync(res.ToXml());
}
}
else
{
//若订单查询失败,则立即返回结果给微信支付后台
WxPayData res = new WxPayData();
res.SetValue("return_code", "FAIL");
res.SetValue("return_msg", "商户订单处理失败");
await context.Response.WriteAsync(res.ToXml());
}
}
else
{
//若订单查询失败,则立即返回结果给微信支付后台
WxPayData res = new WxPayData();
res.SetValue("return_code", "FAIL");
res.SetValue("return_msg", "商户订单处理失败");
await context.Response.WriteAsync(res.ToXml());
}
}
}
}
catch (Exception ex)
{
Log.Information($"WxPayNotify error message is {ex.Message}");
//若订单查询失败,则立即返回结果给微信支付后台
WxPayData res = new WxPayData();
res.SetValue("return_code", "FAIL");
res.SetValue("return_msg", "商户订单处理失败");
await context.Response.WriteAsync(res.ToXml());
var currentInfo = MethodBase.GetCurrentMethod();
//Log4Net.Error(log, ex.Message, currentInfo.ReflectedType.FullName + "-" + currentInfo.Name);
}
}(2)辅助方法 /// <summary>
/// 接收从微信支付后台发送过来的数据并验证签名
/// </summary>
/// <returns>微信支付后台返回的数据</returns>
public WxPayData GetNotifyData(HttpContext context)
{
string message = string.Empty;
context.Request.EnableBuffering();
context.Request.Body.Seek(0, SeekOrigin.Begin);
Log.Information(context.Request.Method);
using (var reader = new StreamReader(context.Request.Body, Encoding.UTF8))
{
message = reader.ReadToEndAsync().Result;
}
Log.Information($"context result is {message}");
//Log4Net.Info(log, $"GetNotifyData Receive data from WeChat :{message}");
//转换数据格式并验证签名
WxPayData data = new WxPayData();
try
{
data.FromXml(message);
}
catch (Exception ex)
{
//若签名错误,则立即返回结果给微信支付后台
WxPayData res = new WxPayData();
res.SetValue("return_code", "FAIL");
res.SetValue("return_msg", ex.Message);
context.Response.WriteAsync(res.ToXml());
}
//Log4Net.Info(log, $"GetNotifyData Check sign success");
return data;
} 提示:
* APPID:绑定支付的APPID(必须配置)
* MCHID:商户号(必须配置)
* KEY:商户支付密钥,参考开户邮件设置(必须配置),请妥善保管,避免密钥泄露
出处:https://www.cnblogs.com/cby-love本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须在文章页面给出原文连接,否则保留追究法律责任的权利.如果您觉得文章对您有帮助,可以点击文章右下角"推荐".您的鼓励是作者坚持原创和持续写作的最大动力!
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!
页:
[1]