微信支付:C#计算签名


使用 HttpClient 来计算签名

HttpClient是.NET4.5引入的一个HTTP客户端库,其命名空间为 System.Net.Http ,.NET 4.5之前我们可能使用WebClient和HttpWebRequest来达到相同目的。HttpClient利用了最新的面向任务模式,使得处理异步请求非常容易。它适合用于多次请求操作,一般设置好默认头部后,可以进行重复多次的请求,基本上用一个实例可以提交任何的HTTP请求。HttpClient有预热机制,第一次进行访问时比较慢,所以不应该用到HttpClient就new一个出来,应该使用单例或其他方式获取HttpClient的实例

新建一个 请求处理类 HttpHandler

  // 使用方法
// HttpClient client = new HttpClient(new HttpHandler("{商户号}", "{商户证书序列号}"));
// ...
// var response = client.GetAsync("https://api.mch.weixin.qq.com/v3/certificates");
public class HttpHandler : DelegatingHandler
{
    private readonly string merchantId;
    private readonly string serialNo;

    public HttpHandler()
    {
        InnerHandler = new HttpClientHandler();

        this.merchantId = ConfigData.Intance.WXPaySetting.wx_mchid;
        this.serialNo = ConfigData.Intance.WXPaySetting.wx_serialNo;
    }

    protected async override Task<HttpResponseMessage> SendAsync(
        HttpRequestMessage request,
        CancellationToken cancellationToken)
    {
        var auth = await BuildAuthAsync(request);
        string value = $"WECHATPAY2-SHA256-RSA2048 {auth}";
        request.Headers.Add("Authorization", value);
        return await base.SendAsync(request, cancellationToken);
    }

    protected async Task<string> BuildAuthAsync(HttpRequestMessage request)
    {
        string method = request.Method.ToString();
        string body = "";
        if (method == "POST" || method == "PUT" || method == "PATCH")
        {
            var content = request.Content;
            body = await content.ReadAsStringAsync();
        }

        string uri = request.RequestUri.PathAndQuery;
        TimeSpan ts = DateTime.Now - new DateTime(1970, 1, 1, 0, 0, 0, 0);
        var timestamp = (int)ts.TotalSeconds - 28800;
        string nonce = Path.GetRandomFileName();

        string message = $"{method}\n{uri}\n{timestamp}\n{nonce}\n{body}\n";
        string signature = Sign(message);
        return $"mchid=\"{merchantId}\",nonce_str=\"{nonce}\",timestamp=\"{timestamp}\",serial_no=\"{serialNo}\",signature=\"{signature}\"";
    }

    protected string Sign(string message)
    {
        // NOTE: 私钥不包括私钥文件起始的-----BEGIN PRIVATE KEY-----
        //        亦不包括结尾的-----END PRIVATE KEY-----
        string privateKey = ConfigData.Intance.WXPaySetting.wx_private_key;


        //转换成适用于.Net的秘钥
        var netKey = RSAPrivateKeyJava2DotNet(privateKey);
        var rsa = new RSACryptoServiceProvider();
        rsa.FromXmlString(netKey);
        //创建一个空对象
        var rsaClear = new RSACryptoServiceProvider();
        var paras = rsa.ExportParameters(true);
        rsaClear.ImportParameters(paras);
        //签名返回
        using (var sha256 = new SHA256CryptoServiceProvider())
        {
            var signData = rsa.SignData(Encoding.UTF8.GetBytes(message), sha256);
            string s2 = Convert.ToBase64String(signData);
            return s2;
        }
    }

    string RSAPrivateKeyJava2DotNet(string privateKey)
    {
        RsaPrivateCrtKeyParameters privateKeyParam = (RsaPrivateCrtKeyParameters)PrivateKeyFactory.CreateKey(Convert.FromBase64String(privateKey));

        return string.Format("<RSAKeyValue><Modulus>{0}</Modulus><Exponent>{1}</Exponent><P>{2}</P><Q>{3}</Q><DP>{4}</DP><DQ>{5}</DQ><InverseQ>{6}</InverseQ><D>{7}</D></RSAKeyValue>",
            Convert.ToBase64String(privateKeyParam.Modulus.ToByteArrayUnsigned()),
            Convert.ToBase64String(privateKeyParam.PublicExponent.ToByteArrayUnsigned()),
            Convert.ToBase64String(privateKeyParam.P.ToByteArrayUnsigned()),
            Convert.ToBase64String(privateKeyParam.Q.ToByteArrayUnsigned()),
            Convert.ToBase64String(privateKeyParam.DP.ToByteArrayUnsigned()),
            Convert.ToBase64String(privateKeyParam.DQ.ToByteArrayUnsigned()),
            Convert.ToBase64String(privateKeyParam.QInv.ToByteArrayUnsigned()),
            Convert.ToBase64String(privateKeyParam.Exponent.ToByteArrayUnsigned()));
    }
}
GarsonZhang www.yesdotnet.com

使用:APIHelper

public class APIHelper
{
    /// <summary>
    /// Native支付 统一下单
    /// </summary>
    /// <param name="data"></param>
    /// <returns></returns>
    public async Task<Res_Native_TongYiXiaDan> Native_TongYiXiaDan(Req_Native_TongYiXiaDan data)
    {
        using (HttpClient client = GetClient())
        {
            string url = "https://api.mch.weixin.qq.com/v3/pay/partner/transactions/native";
            Newtonsoft.Json.JsonSerializerSettings settings = new Newtonsoft.Json.JsonSerializerSettings();
            settings.NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore;
            string json = Newtonsoft.Json.JsonConvert.SerializeObject(data, settings);
            HttpContent content = new StringContent(json);
            content.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue("application/json");
            HttpResponseMessage response = await client.PostAsync(url, content);
            string responseBody = await response.Content.ReadAsStringAsync();
            return Newtonsoft.Json.JsonConvert.DeserializeObject<Res_Native_TongYiXiaDan>(responseBody);
        }
    }

    /// <summary>
    /// Native支付 商户订单号查询
    /// </summary>
    /// <param name="data"></param>
    /// <returns></returns>
    public async Task<Res_Native_DingDanChaXun> Native_ChaXunDingDan(string out_trade_no)
    {
        using (HttpClient client = GetClient())
        {
            string url = $"https://api.mch.weixin.qq.com/v3/pay/partner/transactions/out-trade-no/{out_trade_no}?sp_mchid={Config.sp_mchid}&sub_mchid={Config.sub_mchid}";
            HttpResponseMessage response = await client.GetAsync(url);
            string responseBody = await response.Content.ReadAsStringAsync();
            return Newtonsoft.Json.JsonConvert.DeserializeObject<Res_Native_DingDanChaXun>(responseBody);
        }
    }


    HttpClient GetClient()
    {
        HttpClient client = new HttpClient(new HttpHandler());
        // 设置 Accept
        client.DefaultRequestHeaders.Accept.Clear();
        client.DefaultRequestHeaders.Accept.Add(new System.Net.Http.Headers.MediaTypeWithQualityHeaderValue("application/json"));
        client.DefaultRequestHeaders.UserAgent.Clear();

        // 设置 UserAgent
        var userAgent = new System.Net.Http.Headers.ProductInfoHeaderValue(new System.Net.Http.Headers.ProductHeaderValue("DataTook", "2.1.0"));
        client.DefaultRequestHeaders.UserAgent.Add(userAgent);
        return client;
    }
}
GarsonZhang www.yesdotnet.com

HttpWebRequest 方式签名

在某些条件下,如果使用HttpClient死循环,发生在 await client.PostAsync(url, content) 处,改用 HttpWebRequest 方式请求

 public class APIHelper
{
    ConfigWXPay Config;
    public APIHelper()
    {
        Config = ConfigData.Intance.WXPaySetting;
    }
    /// <summary>
    /// Native支付 统一下单
    /// </summary>
    /// <param name="data"></param>
    /// <returns></returns>
    public Res_Native_TongYiXiaDan Native_TongYiXiaDan(Req_Native_TongYiXiaDan data)
    {
        string url = "https://api.mch.weixin.qq.com/v3/pay/partner/transactions/native";

        Newtonsoft.Json.JsonSerializerSettings settings = new Newtonsoft.Json.JsonSerializerSettings();
        settings.NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore;
        string json = Newtonsoft.Json.JsonConvert.SerializeObject(data, settings);
        string responseBody = this.Post(url, json);
        return Newtonsoft.Json.JsonConvert.DeserializeObject<Res_Native_TongYiXiaDan>(responseBody);
    }

    /// <summary>
    /// Native支付 商户订单号查询
    /// </summary>
    /// <param name="data"></param>
    /// <returns></returns>
    public Res_Native_DingDanChaXun Native_ChaXunDingDan(string out_trade_no)
    {
        string url = $"https://api.mch.weixin.qq.com/v3/pay/partner/transactions/out-trade-no/{out_trade_no}?sp_mchid={Config.wx_mchid}&sub_mchid={Config.wx_sub_mchid}";
        string responseBody = get(url);
        return Newtonsoft.Json.JsonConvert.DeserializeObject<Res_Native_DingDanChaXun>(responseBody);
    }

    /// <summary>
    /// 客户端统一提交数据
    /// </summary>
    /// <param name="url">WebAPI核心URL地址</param>
    /// <param name="param">URL参数</param>
    /// <returns>返回数据</returns>
    string get(string url)
    {
        try
        {

            System.Net.HttpWebRequest request;

            if (url.StartsWith("https", StringComparison.OrdinalIgnoreCase))
            {
                ServicePointManager.SecurityProtocol = (SecurityProtocolType)192 | (SecurityProtocolType)768 | (SecurityProtocolType)3072;
                ServicePointManager.ServerCertificateValidationCallback = delegate { return true; };
                ServicePointManager.CheckCertificateRevocationList = false;
                ServicePointManager.DefaultConnectionLimit = 512;
                ServicePointManager.Expect100Continue = false;

                request = WebRequest.Create(url) as HttpWebRequest;
                request.ProtocolVersion = HttpVersion.Version10;
                request.KeepAlive = false;
            }
            else
            {
                request = (System.Net.HttpWebRequest)WebRequest.Create(url);
            }


            //request = (HttpWebRequest)WebRequest.Create(url + (param == "" ? "" : "?") + param);
            request.Method = "GET";
            request.ContentType = "text/html;charset=UTF-8";//text/html;charset=UTF-8;

            request.Accept = "application/json";
            request.UserAgent = "DataTook/2.1.0";
            var auth = this.BuildAuthAsync(request, "");
            string value = $"WECHATPAY2-SHA256-RSA2048 {auth}";
            request.Headers.Add("Authorization", value);

            #region 获取网页内容太大的话,就加下面这两句代码
            request.Headers["Accept-Encoding"] = "gzip,deflate";
            request.AutomaticDecompression = DecompressionMethods.GZip;
            #endregion

            request.Timeout = WebApiTools.TimeOut;

            HttpWebResponse response = (HttpWebResponse)request.GetResponse();
            Stream myResponseStream = response.GetResponseStream();
            StreamReader myStreamReader = new StreamReader(myResponseStream, Encoding.GetEncoding("utf-8"));
            string retString = myStreamReader.ReadToEnd();
            myStreamReader.Close();
            myResponseStream.Close();

            return retString;
        }
        catch (WebException ex)
        {
            string result = "GET:操作失败!\r\n" + ex.Message;
            if (ex.Response != null)
                result = result + "\r\n" + GetResponseText(ex.Response);
            throw new Exception(result);
        }
        catch (Exception ex)
        {
            throw new Exception("GET:操作失败!\r\n" + ex.Message);
        }
    }

    /// <summary>
    /// 客户端统一提交数据
    /// </summary>
    /// <param name="url">WebAPI核心URL地址</param>
    /// <param name="data">提交的数据</param>
    /// <returns>返回数据</returns>
    string Post(string url, string data)
    {
        string returnData = null;
        try
        {
            string strURL = url;
            System.Net.HttpWebRequest request;

            if (url.StartsWith("https", StringComparison.OrdinalIgnoreCase))
            {
                ServicePointManager.SecurityProtocol = (SecurityProtocolType)192 | (SecurityProtocolType)768 | (SecurityProtocolType)3072;
                ServicePointManager.ServerCertificateValidationCallback = delegate { return true; };
                ServicePointManager.CheckCertificateRevocationList = false;
                ServicePointManager.DefaultConnectionLimit = 512;
                ServicePointManager.Expect100Continue = false;

                request = WebRequest.Create(url) as HttpWebRequest;
                request.ProtocolVersion = HttpVersion.Version10;
                request.KeepAlive = false;
            }
            else
            {
                request = (System.Net.HttpWebRequest)WebRequest.Create(strURL);
            }

            request.Method = "POST";
            request.ContentType = "application/json";// "application/json;charset=UTF-8";//POST必须使用JSON格式

            request.Accept = "application/json";
            request.UserAgent = "DataTook/2.1.0";
            var auth = this.BuildAuthAsync(request, data);
            string value = $"WECHATPAY2-SHA256-RSA2048 {auth}";
            request.Headers.Add("Authorization", value);

            string paraUrlCoded = data;
            byte[] payload;
            payload = System.Text.Encoding.UTF8.GetBytes(paraUrlCoded);
            request.ContentLength = payload.Length;
            request.Timeout = WebApiTools.TimeOut;
            Stream writer = request.GetRequestStream();
            writer.Write(payload, 0, payload.Length);
            writer.Close();
            System.Net.HttpWebResponse response;



            response = (System.Net.HttpWebResponse)request.GetResponse();
            System.IO.Stream s;

            s = response.GetResponseStream();
            string StrDate = "";
            string strValue = "";
            StreamReader Reader = new StreamReader(s, Encoding.GetEncoding("utf-8"));
            while ((StrDate = Reader.ReadLine()) != null)
            {
                strValue += StrDate + "\r\n";
            }
            returnData = strValue;
            return returnData.Trim() + "\n";
        }
        catch (WebException ex)
        {
            string result = "POST:操作失败!\r\n" + ex.Message;
            if (ex.Response != null)
                result = result + "\r\n" + GetResponseText(ex.Response);
            throw new Exception(result);
        }
        catch (Exception ex)
        {
            throw new Exception("POST:操作失败!\r\n" + ex.Message);
        }
    }

    protected string BuildAuthAsync(HttpWebRequest request, string body)
    {
        string merchantId = ConfigData.Intance.WXPaySetting.wx_mchid;
        string serialNo = ConfigData.Intance.WXPaySetting.wx_serialNo;

        string method = request.Method.ToString();

        string uri = request.RequestUri.PathAndQuery;
        TimeSpan ts = DateTime.Now - new DateTime(1970, 1, 1, 0, 0, 0, 0);
        var timestamp = (int)ts.TotalSeconds - 28800;
        string nonce = Path.GetRandomFileName();

        string message = $"{method}\n{uri}\n{timestamp}\n{nonce}\n{body}\n";
        string signature = Sign(message);
        return $"mchid=\"{merchantId}\",nonce_str=\"{nonce}\",timestamp=\"{timestamp}\",serial_no=\"{serialNo}\",signature=\"{signature}\"";
    }

    protected string Sign(string message)
    {
        // NOTE: 私钥不包括私钥文件起始的-----BEGIN PRIVATE KEY-----
        //        亦不包括结尾的-----END PRIVATE KEY-----
        string privateKey = ConfigData.Intance.WXPaySetting.wx_private_key;


        //转换成适用于.Net的秘钥
        var netKey = RSAPrivateKeyJava2DotNet(privateKey);
        var rsa = new RSACryptoServiceProvider();
        rsa.FromXmlString(netKey);
        //创建一个空对象
        var rsaClear = new RSACryptoServiceProvider();
        var paras = rsa.ExportParameters(true);
        rsaClear.ImportParameters(paras);
        //签名返回
        using (var sha256 = new SHA256CryptoServiceProvider())
        {
            var signData = rsa.SignData(Encoding.UTF8.GetBytes(message), sha256);
            string s2 = Convert.ToBase64String(signData);
            return s2;
        }
    }

    string RSAPrivateKeyJava2DotNet(string privateKey)
    {
        RsaPrivateCrtKeyParameters privateKeyParam = (RsaPrivateCrtKeyParameters)PrivateKeyFactory.CreateKey(Convert.FromBase64String(privateKey));

        return string.Format("<RSAKeyValue><Modulus>{0}</Modulus><Exponent>{1}</Exponent><P>{2}</P><Q>{3}</Q><DP>{4}</DP><DQ>{5}</DQ><InverseQ>{6}</InverseQ><D>{7}</D></RSAKeyValue>",
            Convert.ToBase64String(privateKeyParam.Modulus.ToByteArrayUnsigned()),
            Convert.ToBase64String(privateKeyParam.PublicExponent.ToByteArrayUnsigned()),
            Convert.ToBase64String(privateKeyParam.P.ToByteArrayUnsigned()),
            Convert.ToBase64String(privateKeyParam.Q.ToByteArrayUnsigned()),
            Convert.ToBase64String(privateKeyParam.DP.ToByteArrayUnsigned()),
            Convert.ToBase64String(privateKeyParam.DQ.ToByteArrayUnsigned()),
            Convert.ToBase64String(privateKeyParam.QInv.ToByteArrayUnsigned()),
            Convert.ToBase64String(privateKeyParam.Exponent.ToByteArrayUnsigned()));
    }

    private static string GetResponseText(WebResponse response)
    {
        string text;
        using (StreamReader sr = new StreamReader(response.GetResponseStream()))
        {
            text = sr.ReadToEnd();
        }

        if ((response is HttpWebResponse))
        {
            int status = (int)(response as HttpWebResponse).StatusCode;
            if (status == 429) text = "流量访问限制!" + text;
        }

        return text;
    }

}
GarsonZhang www.yesdotnet.com

 

 

 

版权声明:本文为YES开发框架网发布内容,转载请附上原文出处连接
管理员
评论列表

发表评论

评论内容
昵称:
关联文章

支付C#计算签名
支付签名计算.net4.5
支付接口签名校验工具
支付C#计算签名
支付: API V3支付回调签名验证
【已解决】.NET 支付API V3中JSAPI支付发起wx.chooseWXPay时,提示 支付验证签名失败
支付签名计算.net4.5
支付:JSAPI支付 开发手册
支付官方相关工具下载
支付:header中的mchid与post payload中的mchid不匹配
支付:API v3 Postman脚本使用指南
/支付宝 在线支付测试工具
Asp.net H5唤起支付支付回调
支付,当面付,开发手册
支付:商户API私钥
JS 接口签名校验工具 (qq.com)
支付支付成功没有回调通知Notify_URL
支付接口签名校验工具
支付:受理机构必须传入sub_mch_id
支付:Http头缺少Accept或User-Agent