.Net Core 微信H5支付API V3【统一下单,回调】

官方参考文档

签名生成
证书和回调报文解密
h5下单接口
如何查看证书序列号
私钥和证书
常见问题和接口流程图
支付通知

统一下单的方法
public class H5MyWXPayUtil
    {
        public readonly static string appid = “”// 这里填写你自己的appid
        // 商户号
        public readonly static string mch_id = “”;// 这里填写你自己的商户号
        // 商户秘钥
        public readonly static string partnerkey = “”// 这里填写你自己的商户秘钥
        // 回调地址
        public readonly static string notifyurl = “”// 回调地址随便外网能访问就行  /tPosition/WX_Callback
        // 统一下单接口
        public readonly static string url = "https://api.mch.weixin.qq.com/v3/pay/transactions/h5";

        #region  H5请求的需要的参数
        /// <summary>
        /// h5支付需要的类
        /// </summary>
        public class bodyModel {
            /// <summary>
            /// 直连商户号
            /// </summary>
            public string mchid { get; set; }
            /// <summary>
            /// 应用ID
            /// </summary>
            public string appid { get; set; }
            /// <summary>
            /// 商品描述
            /// </summary>
            public string description { get; set; }
            
            /// <summary>
            /// 商户订单号
            /// </summary>
            public string out_trade_no { get; set; }
            /// <summary>
            /// 通知地址
            /// </summary>
            public string notify_url { get; set; }
            /// <summary>
            /// 订单金额
            /// </summary>
            public object amount { get; set; }
            /// <summary>
            /// 景信息
            /// </summary>
            public object  scene_info { get; set; }
           /// <summary>
           /// 附加数据
           /// </summary>
           public  string attach { get; set; }


        }
        public class H5amount {
            /// <summary>
            /// 金钱
            /// </summary>
            public int total { get; set; }
        }

        /// <summary>
        /// 场景信息
        /// </summary>
        public class H5scene_info {
            /// <summary>
            /// 客户端IP
            /// </summary>
            public string payer_client_ip { get; set; }
            /// <summary>
            /// H5场景信息
            /// </summary>
            public object h5_info { get; set; }

    }
        /// <summary>
        /// H5场景信息
        /// </summary>
        public class H5type {
            /// <summary>
            /// 场景类型
            /// </summary>
            public string type { get; set; }

        }

        #endregion

        #region 请求返回的参数

        public class returnParameters {
            /// <summary>
            /// 返回结果【Success/Error】
            /// </summary>
            public bool result { get; set; }
            /// <summary>
            /// 描述
            /// </summary>
            public string errmsg { get; set; }
            /// <summary>
            /// 返回成功的链接
            /// </summary>
            public string h5_url { get; set; }
        }

        #endregion
        /// <summary>
        /// H5支付封装
        /// </summary>
        /// <param name="description">商品的描述</param>
        /// <param name="total">金钱,分为单位</param>
        /// <param name="attach">附加数据</param>
        /// <param name="payer_client_ip">客户端的ip地址</param>
        /// <returns></returns>
        public static string  H5Getprepay(string description, decimal total, string attach,string payer_client_ip) {
            returnParameters returnParameters = new returnParameters();
            //body参数
            var formData = new bodyModel
            {
                appid=appid,//应用ID
                mchid=mch_id,//商户号
                description= description,//商品描述
                out_trade_no = getRandomTime(),//商户订单号
                attach =attach,//附加数据
                notify_url=notifyurl,//通知地址
                amount =new H5amount {
                      total= Convert.ToInt32(total * 100),//金额
                },
                scene_info =new H5scene_info {
                    payer_client_ip= payer_client_ip,//客户端Ip
                    h5_info=new H5type {
                        type= "Wap"
                    }
                }

            };

            HttpClient client = new HttpClient(new HttpHandlerH5("{商户号}", "{商户证书序列号}"));
            var bodyJson = new StringContent(formData.ToJson(), Encoding.UTF8, "application/json");
            var response =  client.PostAsync(url, bodyJson);
            var rep = response.Result;//在这里会等待task返回
            var respStr = rep.Content.ReadAsStringAsync();
            string responseBodyAsText = respStr.Result;
            JObject jo = (JObject)JsonConvert.DeserializeObject(responseBodyAsText);
            if (response.Result.IsSuccessStatusCode)
            {
                returnParameters.h5_url = jo["h5_url"].ToString();
                returnParameters.result = true;
            }
            else
            {
                returnParameters.errmsg = jo["message"].ToString();
                returnParameters.result = false;
            }
            return   JsonConvert.SerializeObject(returnParameters);

        }

        /// 生成订单号
        /// </summary>
        /// <returns></returns>
        private static string getRandomTime()
        {
            Random rd = new Random();//用于生成随机数
            string DateStr = DateTime.Now.ToString("yyyyMMddHHmmssMM");//日期
            string str = DateStr + rd.Next(10000).ToString().PadLeft(4, '0');//带日期的随机数
            return str;
        }
    }
签名

直接复制微信官方文档的 签名生成

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);

            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();
            }

            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);
            
            var rsa = RSA.Create();
            rsa.ImportPkcs8PrivateKey(keyData, out _);
            byte[] data = System.Text.Encoding.UTF8.GetBytes(message);
            return Convert.ToBase64String(rsa.SignData(data, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1));
        }
    }
}

前端进行返回的url进行支付即可,常见问题和接口流程图

发布之后 iis报错 “系统找不到指定的文件”

  1. 找到网站对应的应用程序池
  2. 鼠标右键–>高级设置–>进程模型–>加载用户配置文件–>设置为true
    在这里插入图片描述
回调
 public async Task<string> PayH5NotifyUrl()
        {
           
           
            //获取回调POST过来的xml数据的代码
            Stream stream = HttpContext.Request.Body;
            byte[] buffer = new byte[HttpContext.Request.ContentLength.Value];
            await stream.ReadAsync(buffer, 0, buffer.Length); //.net core 3.1需要用ReadAsync才不会出错
           string str = System.Text.Encoding.UTF8.GetString(buffer);
            JObject jsonObj = JObject.Parse(str);
            string summary = jsonObj["summary"].ToString();//回调摘要
            if (summary== "支付成功")
            {
            
                string associatedData = ((JObject)jsonObj["resource"])["associated_data"].ToString();//附加数据
                string nonce = ((JObject)jsonObj["resource"])["nonce"].ToString();//随机串
                string ciphertext = ((JObject)jsonObj["resource"])["ciphertext"].ToString();//数据密文
                //调用解密
                var decryptStr = AesGcm.AesGcmDecrypt(associatedData, nonce, ciphertext);
                JObject resource = JObject.Parse(decryptStr);
                string attach = resource["attach"].ToString();//附加数据
                string  out_trade_no= resource["out_trade_no"].ToString();//商户订单号
                string transaction_id = resource["transaction_id"].ToString();//信支付订单号
                string total_fee = ((JObject)resource["amount"])["total"].ToString();//总金额
                string return_code = resource["trade_state"].ToString();//交易状态
               
                try
                {
                    //业务逻辑
                    if (return_code.ToUpper()== "SUCCESS")
                    {
                        return "{"
                        + "\"code\":\"SUCCESS\","
                        + "\"message\":\"成功\","
                        + "}";
                    }
                }
                catch (Exception e)
                {
                    throw;
                }
            }
            return "{"
                         + "\"code\":\"FAIL\","
                         + "\"message\":\"ERROR\","
                         + "}";

        }
解密类
public class AesGcm
{
    private static string ALGORITHM = "AES/GCM/NoPadding";
    private static int TAG_LENGTH_BIT = 128;
    private static int NONCE_LENGTH_BYTE = 12;
    private static string AES_KEY = "yourkeyhere";//APIv3密钥

    public static string AesGcmDecrypt(string associatedData, string nonce, string ciphertext)
    {
        GcmBlockCipher gcmBlockCipher = new GcmBlockCipher(new AesEngine());
        AeadParameters aeadParameters = new AeadParameters(
            new KeyParameter(Encoding.UTF8.GetBytes(AES_KEY)), 
            128, 
            Encoding.UTF8.GetBytes(nonce), 
            Encoding.UTF8.GetBytes(associatedData));
        gcmBlockCipher.Init(false, aeadParameters);

        byte[] data = Convert.FromBase64String(ciphertext);
        byte[] plaintext = new byte[gcmBlockCipher.GetOutputSize(data.Length)];
        int length = gcmBlockCipher.ProcessBytes(data, 0, data.Length, plaintext, 0);
        gcmBlockCipher.DoFinal(plaintext, length);
        return Encoding.UTF8.GetString(plaintext);
    }
}
  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值