.net core 微信支付-----下单

    支付这一块也算是项目开发常遇到的功能,尤其是电商这块,支付必不可少,而现在常用的两大支付:1,微信支付 2 ,支付宝支付。但是我这里我要说的就是这个坑爹的微信支付,因为整体来说他没有支付宝支付显得友好,为什么呢?因为他没有测试环境,如果要开发这块必须要在实际环境中进行,真他妈操蛋!而这一块支付宝就比较友好了,他给开发者提供了沙箱测试环境,是真的不错,至于怎末操作,这里就不多说了,后续会详细介绍支付宝的沙箱支付。

     按理说在实际环境下测试开发也没啥问题,但是对于好多想学微信支付的同僚们来说就显得不友好了,因为不是所有的软件都会包含支付功能这一块的,有的可能做了几年软件开发都没有接触到支付这一块,别不相信,这种情况普遍存在。所以呀,微信支付这一块官网还是得上点心,搞个测试环境,照顾照顾开发者。哈哈哈哈

    微信支付主要是商家申请账号的,要到微信商户平台上去注册账号,还有申请一个微信公众号平台账号。至于这些怎末操作,这里就不多说了,自己看官网,讲的还是比较细致的。看一下就了解了。

   微信支付有很多中类型,这里就说native支付的方式:

 

 

 我们看一下Native支付,官方给的接口:

 

本次就先看一下Native下单和支付成功之后的回调。

下面我们就根据官方的文档一步一步操作:

首先看接口:

 

 

 请求示例:

 

 

 返回参数:

 

 

 

 从上面由请求接口,请求参数,返回参数,我们可以根据官方要求去做:

微信支付需要的基本配置信息:

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading.Tasks;

namespace WeChat.Api.Common
{
注意:appi appSecret是申请微信公众号平台获取的 后面的几个是微信商户平台获取的。
public class WeChatConfig { /// <summary> /// 直连商户申请的公众号或移动应用appid。 /// </summary> public static string appid => ""; /// <summary> /// AppSecret,app端加密解密使用 /// </summary> public static string AppSecret => ""; /// <summary> /// 密钥,用商户平台上设置的APIv3密钥【微信商户平台—>账户设置—>API安全—>设置APIv3密钥】,记为key; /// </summary> public static string APIV3Key => ""; /// <summary> /// 直连商户的商户号,由微信支付生成并下发。 /// </summary> public static string mchid => ""; /// <summary> /// 证书序列号 /// 查看证书序列号: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 /// </summary> public static string serialNo => ""; /// <summary> /// key 私钥 /// 私钥和证书:https://wechatpay-api.gitbook.io/wechatpay-api-v3/ren-zheng/zheng-shu#sheng-ming-suo-shi-yong-de-zheng-shu /// </summary> public static string privateKey => FileHelper.ReadFile(Directory.GetCurrentDirectory()+"/UpFile/WeChatKey/key.txt", "utf-8"); } }

 

 

 远程请求微信接口的通用类:

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net.Http;
using System.Security.Cryptography;
using System.Threading;
using System.Threading.Tasks;

namespace WeChat.Api.Common
{
    public class HttpHandler : DelegatingHandler
    {
      
        public HttpHandler()
        {
            InnerHandler = new HttpClientHandler();  
        }

        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");
            request.Headers.Add("User-Agent", "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.2; .NET CLR 1.0.3705;)");

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

            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=\"{WeChatConfig.mchid}\",nonce_str=\"{nonce}\",timestamp=\"{timestamp}\",serial_no=\"{WeChatConfig.serialNo}\",signature=\"{signature}\"";
        }

        protected string Sign(string message)
        {
            // NOTE: 私钥不包括私钥文件起始的-----BEGIN PRIVATE KEY-----
            //        亦不包括结尾的-----END PRIVATE KEY-----
            string privateKey = WeChatConfig.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));
        }
    }
}

请求参数的Model:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace WeChat.Api.Model.CreateOrder
{
    public class PayOrder
    {
        /// <summary>
        /// 直连商户申请的公众号或移动应用appid。
        /// </summary>
        public string appid { set; get; }

        /// <summary>
        /// 直连商户的商户号,由微信支付生成并下发。
        /// </summary>
        public string mchid { set; get; }

        /// <summary>
        ///  商品描述
        /// </summary>
        public string description { set; get; }

        /// <summary>
        /// 商户系统内部订单号,只能是数字、大小写字母_-*且在同一个商户号下唯一
        /// </summary>
        public string out_trade_no { set; get; }
        /// <summary>
        /// 订单支付回调接口
        /// </summary>
        public string notify_url { set; get; }

        /// <summary>
        /// 订单金额信息
        /// </summary>
        public Amount amount { set; get; }

    }
    /// <summary>
    /// 微信支付金额实体
    /// </summary>
    public class Amount
    {
        /// <summary>
        /// 订单总金额,单位为分。
        /// </summary>
        public int total { set; get; }

        /// <summary>
        /// 货币类型,CNY:人民币,境内商户号仅支持人民币。
        /// </summary>
        public string currency { set; get; } = "CNY";
    }
}

返回参数Model:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace WeChat.Api.Model.CreateOrder
{
    public class ReturnPayOrder
    {
     
        /// <summary>
        /// 生成二维码的链接
        /// </summary>
        public string code_url { get; set; }
    }
}

下单:

 /// <summary>
    /// 统一下单接口
    /// </summary>
    /// <param name="total">支付金额</param>
    /// <param name="OrderId">订单ID</param>
    /// <param name="pixel">像素大小</param>
        [HttpPost("PayOrder")]
        public async Task<ReturnPayOrder>  PayOrder(int total,string OrderId)
        {
            var orderNumber = $"{DateTime.Now:yyyyMMddHHmmssff}{CodeHelper.CreateNumCode(3)}";
            HttpClient client = new HttpClient(new HttpHandler());
            var req = new PayOrder
            {
                appid = WeChatConfig.appid,
                mchid =WeChatConfig.mchid,
                description = "nihao",
                out_trade_no = orderNumber,
                notify_url = "http://...../api/wechat/WxPayCallback",//这个是支付成功后的回调方法
                amount = new Amount
                {
                    total = total,
                    currency = "CNY"
                }
            };
            var bodyJson = new StringContent(req.ToJson(), Encoding.UTF8, "application/json");
            var resp = await client.PostAsync("https://api.mch.weixin.qq.com/v3/pay/transactions/native", bodyJson);
            // 注意!!! 这个resp只是http的结果,需要把接口具体返回的值读取出来,如果接口报错的话,这地方可以看到具体的错误信息,我就是在这里入坑的。
            var respStr = await resp.Content.ReadAsStringAsync();

            var respos = respStr.ToObject<ReturnPayOrder>();
            return respos;
          
           
        }

根据获取的回调的url动态生成支付二维码:

 /// <summary>
        /// 动态生成二维码(http://)
        /// </summary>
        /// <param name="url">下载路径</param>
        /// <param name="pixel">像素大小</param>
        [HttpGet("GetQRCode")]

        public void GetQRCode(string url, int pixel)
        {

            Response.ContentType = "image/jpeg";

            var bitmap = _qRCode.GetQRCode(url, pixel);

            MemoryStream ms = new MemoryStream();

            bitmap.Save(ms, ImageFormat.Jpeg);

            Response.Body.WriteAsync(ms.GetBuffer(), 0, Convert.ToInt32(ms.Length));

            Response.Body.Close();

        }

支付成功回调方法:

    /// <summary>
        /// 微信支付成功结果回调接口
        /// </summary>
        /// <returns>退款通知http应答码为200且返回状态码为SUCCESS才会当做商户接收成功,否则会重试。注意:重试过多会导致微信支付端积压过多通知而堵塞,影响其他正常通知。</returns>
        [HttpPost("WxPayCallback")]
        public async Task<ReturnNotifyModel> WxPayCallback()
        {
          
            FileHelper.AddLog("进入", "weixin1");
            FileHelper.AddLog(HttpContext.Request.Path.ToString(), "weixin1");

            try
            {
                using (StreamReader sr = new StreamReader(Request.Body, Encoding.UTF8))
                {

                    string strContent = sr.ReadToEndAsync().Result;
                    FileHelper.AddLog(strContent, "weixin1");
                    var wxPayNotifyModel = strContent.ToObject<WxPayNotifyModel>();
                    var decryptStr = AesGcmHelper.AesGcmDecrypt(wxPayNotifyModel.resource.associated_data, wxPayNotifyModel.resource.nonce, wxPayNotifyModel.resource.ciphertext, "89chulmpeQlk568752345UIcd891512a");
                    return decryptStr.ToString().ToObject<ReturnNotifyModel>();

                }



            }
            catch (Exception e)
            {
                FileHelper.AddLog("读取异常", "weixin1");
                throw;
            }
           
        

将这个api发布到服务器中,注意:回调方法一定是可以通过外网访问的,并且还要在微信商户平台中添加这个访问地址,不然回调会失败!

测试下单接口:

 

 然后通过code_url动态生成支付二维码:

 

 

然后将支付成功的回调方法发布到服务器上:

 

 

 然后,通过api接口生成的二维码模拟前端扫码支付:

 

后台接口回调日志:

 

 ciphertext字段解密后的数据:

源码:链接: https://pan.baidu.com/s/17QaZnIRrDCUoO2x4RvzhIA 提取码: zb44 

  微信支付这块其实gitee上有好多不错的开源的代码,比如paylink ,盛派,人家已经封装的很好了,但是对于初学者来说并不友好,还是自己根据官网一步一步的来清晰,不然过程云里雾里的。所以我做微信支付都是自己根据官网然后借鉴网上的一些理论代码,一步一步来实现的,这样自己对整个过程就比较清楚,后续自己也可以封装一下。

 

posted @ 2022-03-28 17:43  代码如风~~~  阅读(1254)  评论(2编辑  收藏  举报