评论

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-06-07

    部署到linux报错

    System.PlatformNotSupportedException: Operation is not supported on this platform.

    解决方案:

    protected string Sign(string message)
    {
        // NOTE: 私钥不包括私钥文件起始的-----BEGIN PRIVATE KEY-----
        //        亦不包括结尾的-----END PRIVATE KEY-----
        string privateKey = "{你的私钥}";
        var rsa = RSA.Create();
        rsa.ImportPkcs8PrivateKey(Convert.FromBase64String(privateKey), out _);
        var signbytes = rsa.SignData(Encoding.UTF8.GetBytes(message), HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1);
        return Convert.ToBase64String(signbytes);
    }
    
    2021-06-07
    赞同 1
    回复 1
    • 大稳·杨
      大稳·杨
      2021-06-08
      感谢分享
      2021-06-08
      回复
  • 健_
    健_
    2021-08-20

    大佬, 请问图片上传API 可以用这个吗, 需要怎么改

    2021-08-20
    赞同 1
    回复 1
    • 大稳·杨
      大稳·杨
      2021-08-21
      没有做过哎,文档应该有说明吧
      2021-08-21
      回复
  • 赵朋
    赵朋
    2021-07-25

    签名时报“指定的算法无效。”错误,麻烦大神看看是什么原因?本地可以,发布服务器后出现此问题。

    rsa.SignData(Encoding.UTF8.GetBytes(data), HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1)

    2021-07-25
    赞同 1
    回复
  • 一杯冰可乐Co.
    一杯冰可乐Co.
    2021-04-12

    .net 4.5 用什么代替这个 RSACng rsa = new RSACng(cngKey)


    2021-04-12
    赞同 1
    回复 1
  • 风平浪静
    风平浪静
    2021-10-25

    我使用的是RSA生成signType签名,但是一直报错,这个是怎么生成的

    2021-10-25
    赞同
    回复
  • 什么郎
    什么郎
    2021-07-30

    把你的代码粘贴过去直接报错了

    2021-07-30
    赞同
    回复
  • 什么郎
    什么郎
    2021-07-29

    这个和本地调试环境有什么关系吗?我现在觉得不是我代码的问题。。。

    2021-07-29
    赞同
    回复
  • 什么郎
    什么郎
    2021-07-29

    我完全copy了您的代码 结果还是request bad,之前用另一个方法也是400.。。。

    2021-07-29
    赞同
    回复 1
    • 大稳·杨
      大稳·杨
      2021-08-12
      接口没有返回具体错误信息描述吗?这句代码respStr对应的值什么,如果没有返回正确的值,那么就会返回具体的错误信息
      2021-08-12
      回复
  • 疯狂原始人
    疯狂原始人
    2021-05-31

    你好 ,

    我调用 https://api.mch.weixin.qq.com/v3/pay/transactions/jsapi 统一下单api

     HttpClient httpClient = new HttpClient(new HttpHandler(HelpClass.TenPayV3_MchId, WeiXinHelper.merchantSerialNo));

        // POST 方式

                    var bodyJson = new StringContent(postData, Encoding.UTF8, "application/json"); //一定要这样传递参数,不然在加密签名的时候获取到的参数就是\\u0这种形式的数据了,不是传递的这样的数据了,导致加密的结果不正确


    代码就卡在这里了 微信服务不返回任何消息回来

                    var response = await httpClient.PostAsync(url, bodyJson);


                    HelpClass.showlogtxt2("HTTP", string.Format(" response:{0} ", response.RequestMessage.ToString()));


                    var respStr = await response.Content.ReadAsStringAsync();//这里面就包含prepay_id了



    2021-05-31
    赞同
    回复 1
    • 大稳·杨
      大稳·杨
      2021-05-31
      没有返回任何信息的话,你看看是不是网络的问题了?不会没有信息返回的,就算是错误也会有错误信息返回的
      2021-05-31
      回复
  • 吴敌
    吴敌
    2021-05-08

    老大,在本机没问题,在服务器上报错“出现了内部错误”,应该是加密导致的错误?

    Internal.Cryptography.CryptoThrowHelper+WindowsCryptographicException: 出现了内部错误。\r\n   at System.Security.Cryptography.CngKey.Import(ReadOnlySpan`1 keyBlob, String curveName, CngKeyBlobFormat format, CngProvider provider)\r\n   at System.Security.Cryptography.CngKey.Import(Byte[] keyBlob, String curveName, CngKeyBlobFormat format, CngProvider provider)\r\n   at System.Security.Cryptography.CngKey.Import(Byte[] keyBlob, CngKeyBlobFormat format, CngProvider provider)\r\n   at System.Security.Cryptography.CngKey.Import(Byte[] keyBlob, CngKeyBlobFormat format)\r\n   at ArabMath.Service.HttpHandler.Sign(String message) in D:\\ArabMath\\ArabMathCore\\ArabMath.Service\\WxService\\WxPayService.cs:line 193\r\n   at ArabMath.Service.HttpHandler.BuildAuthAsync(HttpRequestMessage request) in D:\\ArabMath\\ArabMathCore\\ArabMath.Service\\WxService\\WxPayService.cs:line 185\r\n   at ArabMath.Service.HttpHandler.SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) in D:\\ArabMath\\ArabMathCore\\ArabMath.Service\\WxService\\WxPayService.cs:line 164\r\n   at System.Net.Http.HttpClient.FinishSendAsyncBuffered(Task`1 sendTask, HttpRequestMessage request, CancellationTokenSource cts, Boolean disposeCts)\r\n   at ArabMath.Service.WxPayService.WxPrePayAsync(Int32 total) in D:\\ArabMath\\ArabMathCore\\ArabMath.Service\\WxService\\WxPayService.cs:line 92",
    


    我照抄你的代码的,一模一样,本机ok,服务器就这个问题。谢谢呀。

    2021-05-08
    赞同
    回复 2

正在加载...

登录 后发表内容