评论

ASP.NET Core 微信支付(一)【统一下单 APIV3】

本地调试时记得安装微信支付安全证书!!!发布到服务器上也要安装微信支付安全证书!!! 私钥从微信支付后台发放的证书中拷贝出来!!!!!

官方参考资料

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

统一下单接口: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 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 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}\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 = "{你的私钥}";
            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了

 三、注意

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

最后一次编辑于  2021-06-04  
点赞 2
收藏
评论

16 个评论

  • 大伟
    大伟
    2021-04-20
    请问这个方法,为啥我的运行不了呢?netcore3.1+win10,在 using (CngKey cngKey = CngKey.Import(keyData, CngKeyBlobFormat.Pkcs8PrivateBlob))这里就报错了
    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));
                }
            }
    


    2021-04-20
    赞同
    回复 6
    • 大稳·杨
      大稳·杨
      发表于小程序端
      2021-04-20

      什么错误?

      2021-04-20
      回复
    • 调调
      调调
      2021-06-04回复大稳·杨
      我也这里报错了:Internal.Cryptography.CryptoThrowHelper+WindowsCryptographicException: 在编码或解码时出错。
      2021-06-04
      回复
    • 大稳·杨
      大稳·杨
      2021-06-04回复调调
      你是说改这个方法行不行
      2021-06-04
      回复
    • 调调
      调调
      2021-06-04回复大稳·杨
      好了,感谢大神
      2021-06-04
      回复
    • 未来可期
      未来可期
      2021-09-07回复大稳·杨
      大神没有RSAKeyConvert咋整啊
      2021-09-07
      回复
    查看更多(1)
  • 大梦
    大梦
    2021-04-14

    大哥我想知道 .net 4 用什么代替这个 RSACng rsa = new RSACng(cngKey)这句代码。 开发工具是VS2010一直卡在签名处。已经两天了。


    2021-04-14
    赞同
    回复 4
  • 蔡刘聪
    蔡刘聪
    2021-03-26

    大佬 测试那个个get的方法是成功的 返回200了,但是post就是400

    2021-03-26
    赞同
    回复 2
    • 大稳·杨
      大稳·杨
      2021-03-27
      var respStr = await response.Content.ReadAsStringAsync();这地方写错了,应该是response ,这样可以读取到具体的错误信息
      2021-03-27
      回复
    • 大稳·杨
      大稳·杨
      2021-03-31
      你请求的接口支持post吗?
      2021-03-31
      回复
  • 阿刘
    阿刘
    2021-03-23

    测试过吗,这个代码


    2021-03-23
    赞同
    回复 7
    • 大稳·杨
      大稳·杨
      2021-03-24
      测试过,可以用的,你哪里出问题了?
      2021-03-24
      回复
    • 阿刘
      阿刘
      2021-03-24回复大稳·杨
      var response = client.GetAsync("https://api.mch.weixin.qq.com/v3/certificates");这句,是什么用处,和后面的var response = await client.PostAsync(url, bodyJson);没有问题吗? req.ToJson(),,这个应该是直接转不了的,我现在碰到还有没解决的是,提示appid,和公众号映射不上,我用的小程序的appid
      2021-03-24
      回复
    • 大稳·杨
      大稳·杨
      2021-03-25回复阿刘
      我这个只是写了两种请求方式,一个是GET,一个是POST方式。tojson是我自己封装的方法。你这个问题应该需要去微信开放平台关联起来,https://open.weixin.qq.com/
      2021-03-25
      回复
    • 阿刘
      阿刘
      2021-03-25回复大稳·杨
      开放平台,已经绑定了小程序,能给个指引吗,v2能正常支付了,
      2021-03-25
      回复
    • 阿刘
      阿刘
      2021-03-25回复大稳·杨
      {"code":"PARAM_ERROR","detail":{"location":null,"value":""},"message":"输入源“/appid”映射到字段“公众号ID”必填性规则校验失败,此字段为必填项"}
      2021-03-25
      回复
    查看更多(2)
  • 宝来
    宝来
    2021-03-18

    public HttpHandler(string merchantId, string merchantSerialNo)

    这两个参数都是什么东西,特别是后面这个merchantSerialNo,是什么东西?

    2021-03-18
    赞同
    回复 1
  • 风影 软件定向开发
    风影 软件定向开发
    2021-03-18

    感谢。。。。 帮了大忙... 希望大神 能继续 更新。。 包括支付通知的内容。

    2021-03-18
    赞同
    回复 1

正在加载...

登录 后发表内容