微信Native支付(用于网页端扫描二维码支付)

 一、相关描述

文档地址:微信支付-开发者文档

签名生成:签名生成-接口规则 | 微信支付商户平台文档中心

二、下单和商户订单号查询代码:

using Newtonsoft.Json;
using System;
using System.Configuration;
using System.Net.Http;
using System.Security.Cryptography;
using System.Text;
using System.Threading.Tasks;

namespace 微信的Native支付_用于网页端扫描二维码支付_
{
    class Program
    {
        #region 公用参数

        private readonly static string url = "https://api.mch.weixin.qq.com/v3/pay/transactions/native"; //请求地址
        private readonly static string appId = ConfigurationManager.AppSettings["appId"]; //应用ID
        private readonly static string mchId = ConfigurationManager.AppSettings["mchId"]; //直连商户号
        private readonly static QRCodeDBHelper _codeDb = new QRCodeDBHelper();
        #endregion

        static void Main(string[] args)
        {
            try
            {
                //string qrCodePath = PlaceGenerateQRCode("测试商品", "http://w.hrflag.cn/", "9.90").Result; //获取下单支付二维码
                SelectOrderInfo("O202307063473971331157"); //根据商户订单查找订单信息
                Console.ReadKey();
            }
            catch (Exception e)
            {
                Console.WriteLine("错误信息:" + e.Message);
            }

        }

        /// <summary>
        /// Native下单生成二维码
        /// </summary>
        /// <param name="descriptionText">商品描述</param>
        /// <param name="notify_url">通知地址</param>
        /// <param name="price">总金额</param>
        /// <returns></returns>
        private static async Task<string> PlaceGenerateQRCode(string descriptionText, string notify_urlText, string price)
        {
            string qrCodePath = string.Empty; //二维码路径
            decimal amountDecimal = decimal.Parse(price); // 将字符串金钱解析为 decimal 类型
            int amountInCents = (int)(amountDecimal * 100); // 将金额转换为分,使用整数类型存储
            string orderNo = GetOrderNo(); //订单号
            //接口请求参数
            var requestData = new
            {
                appid = appId,
                mchid = mchId,
                description = descriptionText,
                out_trade_no = orderNo,  //商户订单号
                notify_url = notify_urlText,
                amount = new
                {
                    total = amountInCents,//订单总金额,单位为分。
                    currency = "CNY"  // 货币类型
                }
            };
            string jsonStr = JsonConvert.SerializeObject(requestData); //json字符串参数
            string jsonResponse = await PostUrl(url, jsonStr); //获取微信支付链接
            qrCodePath = _codeDb.GenerateQRCode(jsonResponse, 320, 320);//生成二维码
            return qrCodePath;
        }

        /// <summary>
        /// 查询订单信息
        /// </summary>
        /// <param name="orderId">商户订单号</param>
        /// <returns></returns>
        public static async Task<string> SelectOrderInfo(string orderId)
        {
            string responesJsonStr = string.Empty; //订单返回信息
            string url = $"https://api.mch.weixin.qq.com/v3/pay/transactions/out-trade-no/{orderId}?mchid={mchId}"; //微信商户订单号查询订单接口
            try
            {
                string body = "";
                string timestamp = DateTimeOffset.Now.ToUnixTimeSeconds().ToString(); //获取本地时间戳
                string trade_no = GenerateSign(appId, timestamp, GenerateNonceStr()); //随机串  
                string message = $"GET\n/v3/pay/transactions/out-trade-no/{orderId}?mchid={mchId}\n{timestamp}\n{trade_no}\n{body}\n";  //构造签名串  注意每个支付类型需要修改一下路径:/v3/pay/transactions/native
                string signature = Sign(message);//获取签名值
                //请求接口
                using (HttpClient client = new HttpClient())
                {
                    // 创建HttpRequestMessage对象,并设置请求头
                    HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Get, url);
                    request.Headers.Accept.Clear();
                    request.Headers.Accept.ParseAdd("application/json");
                    // 设置User-Agent头
                    request.Headers.UserAgent.ParseAdd("Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36");
                    string authorization = $"WECHATPAY2-SHA256-RSA2048 mchid=\"{mchId}\",nonce_str=\"{trade_no}\",signature=\"{signature}\",timestamp=\"{timestamp}\",serial_no=\"{ConfigurationManager.AppSettings["serialno"]}\"";
                    request.Headers.Add("Authorization", authorization);
                    //request.Content = new StringContent(jsonStr, Encoding.UTF8, "application/json");
                    // 发送请求并获取响应   在ASP.NET WebAPI项目中,存在线程同步上下文(SynchronizationContext),这可能会导致异步操作被阻塞或死锁。尝试在WebAPI项目中使用ConfigureAwait(false)来避免上下文切换,例如:await client.SendAsync(request).ConfigureAwait(false);。这可以确保异步操作不会受到同步上下文的影响。
                    HttpResponseMessage response = await client.SendAsync(request);
                    // 读取响应内容  
                    responesJsonStr = await response.Content.ReadAsStringAsync();
                    dynamic prepayId = JsonConvert.DeserializeObject<dynamic>(responesJsonStr);
                }
                return responesJsonStr;
            }
            catch (Exception e)
            {
                throw new Exception(e.Message);
            }
        }

        #region 调用微信支付接口

        /// <summary>
        /// Post请求微信支付接口并返回接口响应内容
        /// </summary>
        /// <param name="url">微信接口</param>
        /// <param name="parment">参数 JSON字符串</param>
        /// <returns>返回支付二维码链接</returns>
        private static async Task<string> PostUrl(string url, string parment)
        {
            string jsonResponse = string.Empty; //读取接口响应内容
            string timestamp = DateTimeOffset.Now.ToUnixTimeSeconds().ToString(); //获取本地时间戳
            string trade_no = GenerateSign(appId, timestamp, GenerateNonceStr()); //随机串  
            string message = $"POST\n/v3/pay/transactions/native\n{timestamp}\n{trade_no}\n{parment}\n";  //构造签名串  注意每个支付类型需要修改一下路径:/v3/pay/transactions/native
            string signature = Sign(message);//获取签名值
            try
            {
                using (HttpClient client = new HttpClient())
                {
                    // 创建HttpRequestMessage对象,并设置请求头
                    HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Post, url);
                    request.Headers.Accept.Clear();
                    request.Headers.Accept.ParseAdd("application/json");
                    // 设置User-Agent头
                    request.Headers.UserAgent.ParseAdd("Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36");
                    string authorization = $"WECHATPAY2-SHA256-RSA2048 mchid=\"{mchId}\",nonce_str=\"{trade_no}\",signature=\"{signature}\",timestamp=\"{timestamp}\",serial_no=\"{ConfigurationManager.AppSettings["serialno"]}\"";
                    request.Headers.Add("Authorization", authorization);
                    request.Content = new StringContent(parment, Encoding.UTF8, "application/json");
                    // 发送请求并获取响应   在ASP.NET WebAPI项目中,存在线程同步上下文(SynchronizationContext),这可能会导致异步操作被阻塞或死锁。尝试在WebAPI项目中使用ConfigureAwait(false)来避免上下文切换,例如:await client.SendAsync(request).ConfigureAwait(false);。这可以确保异步操作不会受到同步上下文的影响。
                    HttpResponseMessage response = await client.SendAsync(request);
                    // 读取响应内容  
                    jsonResponse = await response.Content.ReadAsStringAsync();
                    dynamic prepayId = JsonConvert.DeserializeObject<dynamic>(jsonResponse);
                    jsonResponse = prepayId.code_url; //返回二维码链接
                }
                return jsonResponse;
            }
            catch (Exception e)
            {
                throw new Exception(e.Message);
            }
        }

        #endregion

        #region 微信签名

        /// <summary>
        /// 签名
        /// </summary>
        /// <param name="message">参数</param>
        /// <returns></returns>
        protected static string Sign(string message)
        {
            // 私钥
            string privateKey = ConfigurationManager.AppSettings["wxapiKey"];
            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));
            }
        }

        #endregion

        #region 微信生成随机数

        private static string GenerateSign(string appId, string timeStamp, string nonceStr)
        {
            string stringToSign = $"appId={appId}&nonceStr={nonceStr}&timeStamp={timeStamp}";
            stringToSign += "&key=" + ConfigurationManager.AppSettings["APIV3"]; // 替换为APIV3秘钥

            using (var md5 = MD5.Create())
            {
                byte[] hashBytes = md5.ComputeHash(Encoding.UTF8.GetBytes(stringToSign));
                StringBuilder sb = new StringBuilder();
                foreach (byte b in hashBytes)
                {
                    sb.Append(b.ToString("x2"));
                }
                return sb.ToString().ToUpper();
            }
        }

        #endregion

        #region 生成订单号

        /// <summary>
        /// 生成订单号
        /// </summary>
        /// <returns></returns>
        private static string GetOrderNo()
        {
            string strOrderNo = "O" + DateTime.Now.ToString("yyyyMMddfff") + GenerateNonceStr();
            return strOrderNo;
        }
        private static string GenerateNonceStr()
        {
            return GetRandomUInt().ToString();
        }

        private static uint GetRandomUInt()
        {
            var randomBytes = GenerateRandomBytes(sizeof(uint));
            return BitConverter.ToUInt32(randomBytes, 0);
        }
        private static byte[] GenerateRandomBytes(int bytesNumber)
        {
            RNGCryptoServiceProvider csp = new RNGCryptoServiceProvider();
            byte[] buffer = new byte[bytesNumber];
            csp.GetBytes(buffer);
            return buffer;
        }

        #endregion

    }
}

三、生成二维码封装代码

		/// <summary>
        /// 生成二维码(访问链接并设置二维码宽高)
        /// </summary>
        /// <param name="url">链接</param>
        /// <param name="width">宽</param>
        /// <param name="height">高</param>
        /// <returns>返回二维码路径</returns>
        public string GenerateQRCode(string url, int width, int height)
        {
            try
            {
                //生成二维码
                using (QRCodeGenerator qrCodeGenerator = new QRCodeGenerator())
                {
                    QRCodeData qrCodeData = qrCodeGenerator.CreateQrCode(url, QRCodeGenerator.ECCLevel.Q);
                    QRCode qrCode = new QRCode(qrCodeData);
                    Bitmap qrBitmap = qrCode.GetGraphic(100); //设置像素100
                    Bitmap resizeImage = new Bitmap(qrBitmap, new Size(width, height)); //设置宽度
                    string fileName = DateTimeOffset.Now.ToUnixTimeSeconds() + ".png"; //获取本地时间戳
                    string mlPath = Directory.GetCurrentDirectory() + "\\WxPayImage\\"; //目录地址
                    if (!Directory.Exists(mlPath)) Directory.CreateDirectory(mlPath); //没有当前目录则添加目录
                    string filePath = Path.Combine(mlPath + fileName); //拼接文件路径
                    resizeImage.Save(filePath, ImageFormat.Png);  //保存文件到指定路径
                    return filePath;//返回二维码路径
                }
            }
            catch (Exception e)
            {
                throw new Exception(e.Message);
            }
        }

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值