zoukankan      html  css  js  c++  java
  • ASP.NET Core 微信支付(一)【统一下单 APIV3】

    官方参考资料

    签名:https://pay.weixin.qq.com/wiki/doc/apiv3/wechatpay/wechatpay4_0.shtml

    签名生成:https://wechatpay-api.gitbook.io/wechatpay-api-v3/qian-ming-zhi-nan-1/qian-ming-sheng-cheng

    统一下单接口:https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter3_2_1.shtml

    如何查看证书序列号:https://wechatpay-api.gitbook.io/wechatpay-api-v3/chang-jian-wen-ti/zheng-shu-xiang-guan#ru-he-cha-kan-zheng-shu-xu-lie-hao

    私钥和证书:https://wechatpay-api.gitbook.io/wechatpay-api-v3/ren-zheng/zheng-shu#sheng-ming-suo-shi-yong-de-zheng-shu

     

    首先声明,我这个是为APP支付提供的接口!!!其他端使用时仅供参考!!

     

    本地调试时记得安装微信支付安全证书!!!发布到服务器上也要安装微信支付安全证书!!!

     

    私钥从微信支付后台发放的证书中拷贝出来!!!!!

     

      一、签名

        生成签名

    参考资料里面讲的比较详细,也有官方的文档,不过文档不全,导致我的调试程序一直出现问题,请求微信的统一下单接口总是报400错误(Bad Request)。

    签名生成参考官方代码,代码如下,里面有我标注的请求接口报400错误原因的代码

    using System;
    using System.IO;
    using System.Net.Http;
    using System.Security.Cryptography;
    using System.Threading;
    using System.Threading.Tasks;
    
    namespace HttpHandlerDemo
    {
        // 使用方法
        // 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(string merchantId, string merchantSerialNo)
            {
                InnerHandler = new HttpClientHandler();
    
                this.merchantId = merchantId;
                this.serialNo = merchantSerialNo;
            }
    
            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);
                request.Headers.Add("Accept", "application/json");//如果缺少这句代码就会导致下单接口请求失败,报400错误(Bad Request)
                request.Headers.Add("User-Agent", "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.2; .NET CLR 1.0.3705;)");//如果缺少这句代码就会导致下单接口请求失败,报400错误(Bad Request)
    
                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();//debug的时候在这里打个断点,看看body的值是多少,如果跟你传入的参数不一致,说明是有问题的,一定参考我的方法
                }
    
                string uri = request.RequestUri.PathAndQuery;
                var timestamp = DateTimeOffset.Now.ToUnixTimeSeconds();
                string nonce = Path.GetRandomFileName();
    
                string message = $"{method}
    {uri}
    {timestamp}
    {nonce}
    {body}
    ";
                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 = "{你的私钥}";
                byte[] keyData = Convert.FromBase64String(privateKey);
                using (CngKey cngKey = CngKey.Import(keyData, CngKeyBlobFormat.Pkcs8PrivateBlob))
                using (RSACng rsa = new RSACng(cngKey))
                {
                    byte[] data = System.Text.Encoding.UTF8.GetBytes(message);
                    return Convert.ToBase64String(rsa.SignData(data, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1));
                }
            }
        }
    }

      二、统一下单

        使用方法

     var url = "https://api.mch.weixin.qq.com/v3/pay/transactions/app";
     var req = new GenerateOrderModelForWxPay
                    {
                        appid = WxPayConst.appid,
                        mchid = WxPayConst.mchid,
                        description = "商品名称",
                        amount = new WxPayAmountModel
                        {
                            total = 1
                        },
                        out_trade_no = orderNumber,
                        notify_url = "https://xxx.com/api/WxPayCallback"
                    };
    HttpClient client = new HttpClient(new HttpHandler("{商户号}", "{商户证书序列号}"));
    //GET 方式
    var response = client.GetAsync("https://api.mch.weixin.qq.com/v3/certificates");
    
    // POST 方式
     var bodyJson = new StringContent(req.ToJson(), Encoding.UTF8, "application/json"); //一定要这样传递参数,不然在加密签名的时候获取到的参数就是\u0这种形式的数据了,不是传递的这样的数据了,导致加密的结果不正确
    var response = await client.PostAsync(url, bodyJson);
    
    // 读取统一下单之后的返回结果,这样读取出来的直接就是结果,或者错误原因,大家一定要这么搞啊!!!多么痛的领悟,会有具体的错误信息的。
    var respStr = await response.Content.ReadAsStringAsync();//这里面就包含prepay_id了

     三、注意

      大家一定看接口规则里面的说明,我就是没有看,导致我搞了一天没有搞通,下面贴两个截图

     

  • 相关阅读:
    学习 Web 开发技术的16个最佳教程网站和博客
    将会改变未来IT世界的十种编程语言
    用来理解 Java 编程语言的 8 个图表
    花样Android ProgressBar
    Android控件TextProgressBar进度条上显文字
    ViewFlipper 在同一背景下 页面左右滑动
    Android自定义进度条-带文本(文字进度)的水平进度条(ProgressBar)
    android用户界面之GridView教程实例汇总
    禁止ScrollView在子控件的布局改变时自动滚动的的方法
    ListView中使用自定义Adapter及时更xin
  • 原文地址:https://www.cnblogs.com/dawenyang/p/14455543.html
Copyright © 2011-2022 走看看