.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 ,盛派,人家已經封裝的很好了,但是對於初學者來說並不友好,還是自己根據官網一步一步的來清晰,不然過程雲里霧里的。所以我做微信支付都是自己根據官網然后借鑒網上的一些理論代碼,一步一步來實現的,這樣自己對整個過程就比較清楚,后續自己也可以封裝一下。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM