Core3.1 微信v3 JSAPI支付 退款

魏楊楊發表於2021-02-01

1、前言

上一篇寫了《Core3.1 微信v3 JSAPI支付》,這個屬於v3的介面規則,現在研究了下退款的介面我寫的時候它屬於v2介面規則文件。但凡微信支付文件裡面寫清楚點我也不會在這裡記錄一下。

2、乾貨

     介面文件https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter3_1_9.shtml 現在看看是v3介面的了 我的天,他們在逗我玩一樣,v2的寫好了v3就放出來了。命途多舛啊。 v2請求的是xml文件格式的,這裡又要溫習一下舊知識了。

     當時我記得清清楚楚 就說了請求要加簽名,證書雙向驗證。官網文件沒有寫。Net的怎麼載入證書 就說了一句windows下面直接安裝證書就好了。結果 都是Bad Request 還是人工諮詢吧 到最後才把程式碼找給我。我問人工客戶地址給我看看,他說沒有地址(肺都氣炸。。。)

3、程式碼

這是封裝的一個請求 ,程式碼加在上篇文章裡面的那個請求裡面

     /// <summary>
         ///postXML請求
        /// </summary>
        /// <param name="url">地址</param>
        /// <param name="requestString">引數(json格式)</param>
        /// <param name="path">p12檔案路徑</param>
        /// <param name="certPwd">密碼</param>
        /// <returns>string</returns>
        public string PostXml(string url, string requestString,string path, string certPwd)
        {
            var handler = new HttpClientHandler
            {
                ClientCertificateOptions = ClientCertificateOption.Manual,
                SslProtocols = SslProtocols.Tls12,
                ServerCertificateCustomValidationCallback = (x, y, z, m) => true,
            };

            //var path = Path.Combine(AppContext.BaseDirectory, "cert\\iot3rd.p12");
            handler.ClientCertificates.Add(new X509Certificate2(path, certPwd));

            var client = new HttpClient(handler);

            var content = new StringContent(requestString);
            content.Headers.ContentType = new MediaTypeHeaderValue("application/x-www-form-urlencoded");
            //GetAwaiter().GetResult();
            var httpResponseMessage = client.PostAsync(url, content).Result.Content.ReadAsStringAsync().Result;
            return httpResponseMessage;
        }

 

   最主要的就是加了認證檔案,p12檔案路徑跟密碼,一般密碼都是商戶號。這就是微信文件說的雙向驗證

    拼接請求引數,官網文件有驗證這個的。排序的話我自己手動固定排序的,就那幾個欄位ABCDEFG都可以看到0.0。

 

 /// <summary>
        /// 退費需要的字元
        /// </summary>
        /// <param name="hospInfo"></param>
        /// <param name="regOrder"></param>
        /// <returns></returns>
        private string RequestRetreatParmar(Entity.Models.HospInfo hospInfo, RegOrder regOrder)
        {
            var guid = Guid.NewGuid().ToString("N").Substring(0, 30);
            var retreatNo = OrderHelper.GenerateNo("CFTF");
            int money = Convert.ToInt32(regOrder.OwnFee * 100);
            //簽名
            string message = $"appid={hospInfo.WxAppid}&mch_id={hospInfo.WxMchid}&nonce_str={guid}&out_refund_no={retreatNo}&refund_desc=客戶預約掛號退款&refund_fee={money}&total_fee={money}&transaction_id={regOrder.PlatformTradeId}&key={hospInfo.WxKey}";
            var signMd5 = SecurityHelper.MD5EncrytString(message).ToUpper();
            var dto = new
            {
                xml= new
                {
                    appid = hospInfo.WxAppid,
                    mch_id = hospInfo.WxMchid,
                    nonce_str = guid,
                    sign = signMd5,
                    transaction_id = regOrder.PlatformTradeId,
                    out_refund_no = retreatNo,
                    total_fee = money,
                    refund_fee = money,
                    refund_desc = "客戶預約掛號退款",
                }
            };
            var json = JsonConvert.SerializeObject(dto);
            
            return json;
        }

  我把這裡MD5加密的程式碼也貼上

        /// md5加密
        /// </summary>
        /// <param name="inputString">字串</param>
        /// <returns>加密過的字串(不可以解密)</returns>
        public static string MD5EncrytString(string inputString)
        {
            MD5 md5 = System.Security.Cryptography.MD5.Create();
            byte[] buffer = Encoding.UTF8.GetBytes(inputString);
            byte[] md5Buffer = md5.ComputeHash(buffer);
            md5.Clear();
            StringBuilder sb = new StringBuilder();
            for (int i = 0; i < md5Buffer.Length; i++)
            {
                sb.Append(md5Buffer[i].ToString("x2"));
            }
            return sb.ToString();
        }

 請求這裡,只要Return_code等於SUCCESS下面就可以做自己的業務邏輯了。裡面返回的欄位肯定有一個是你自己寫了傳進去的。通過這個欄位查詢資料庫完成自己的邏輯。

 //微信退費操作
                    var url = RequestUrl.PAYREFUND;
                    var json = RequestRetreatParmar(hospInfoMoidel, regOrderModel);
                    var xml = JsonConvert.DeserializeXmlNode(json);
                    var postData = xml.InnerXml;
                    var pathfile = _webHostEnvironment.WebRootPath + "/arsjkll/apiclient_cert.p12";
                    var wxPostResult = _httpClientFactoryHelper.PostXml(url, postData, pathfile, hospInfoMoidel.WxSslcertpassword);
                    SaveLog("WeChatTuiFei", wxPostResult);
                    var resultRep = wxPostResult.Replace("<![CDATA[", "").Replace("]]>", "");
                    XmlDocument doc = new XmlDocument();
                    doc.LoadXml(resultRep);
                    string jsonText = JsonConvert.SerializeXmlNode(doc);
                    SaveLog("WeChatTuiFei", jsonText);
                    var resultModel = JsonConvert.DeserializeObject<RetreatNoResulDtoXml>(jsonText);
                    var resultModelXml = resultModel.Xml;
                    if (resultModelXml.Result_Code == "SUCCESS")
                    {
                        regOrderModel.RefundNo = resultModelXml.Out_Refund_No;
                        regOrderModel.RefundState = 1;
                        regOrderModel.RefundResult = resultModelXml.Return_Code;
                        regOrderModel.RefundTime = DateTime.Now;

                        payLogModel.RefundNo = resultModelXml.Out_Refund_No;
                        payLogModel.RefundState = 1;
                        payLogModel.RefundResult = resultModelXml.Return_Code;
                        payLogModel.RefundTime = DateTime.Now;
                        await _db.SaveChangesAsync();
                        return Result.ToSuccess(resultModel);
                    }
                    else
                        return Result.ToFail(resultModelXml.Return_Msg);

 

我這裡沒有用notify_url返回介面 直接得到結果。下面是接收的Dto

public class RetreatNoResulDtoXml
    {
        public RetreatNoResulDto Xml { get; set; }
    }
    /// <summary>
    /// 退號|退費結果
    /// </summary>
    public class RetreatNoResulDto
    {
        /// <summary>
        /// 返回狀態碼
        /// SUCCESS:退款申請接收成功,結果通過退款查詢介面查詢
        /// FAIL:提交業務失敗
        /// 此欄位是通訊標識,非交易標識,交易是否成功需要檢視result_code來判斷
        /// 示例值:SUCCESS
        /// </summary>
        public string Return_Code { get; set; }
        /// <summary>
        /// 返回資訊
        /// 返回資訊,如非空,為錯誤原因 
        /// 簽名失敗
        /// 引數格式校驗錯誤
        /// 示例值:簽名失敗
        /// </summary>
        public string Return_Msg { get; set; }

        //返回狀態碼(return_code)為SUCCESS的時候,包含以下欄位


        /// <summary>
        /// 業務結果
        /// SUCCESS/FAIL
        /// 示例值:SUCCESS
        /// </summary>
        public string Result_Code { get; set; }
        /// <summary>
        /// 錯誤程式碼
        /// 示例值:SYSTEMERROR
        /// </summary>
        public string Err_Code { get; set; }
        /// <summary>
        /// 錯誤程式碼描述
        /// 結果資訊描述
        ///示例值:系統錯誤
        /// </summary>
        public string Err_Code_Des { get; set; }
        public string Appid { get; set; }
        public string Mch_Id { get; set; }
        public string Sub_Appid { get; set; }
        public string Sub_Mch_Id { get; set; }
        public string Nonce_Str { get; set; }
        /// <summary>
        /// 簽名
        /// </summary>
        public string Sign { get; set; }
        public string Transaction_Id { get; set; }
        public string Out_Trade_No { get; set; }
        public string Out_Refund_No { get; set; }
        public string Refund_Id { get; set; }
        public int Refund_Fee { get; set; }
        public int Settlement_Refund_Fee { get; set; }
        public int Total_Fee { get; set; }
        public int Settlement_Total_Fee { get; set; }
        public string Fee_Type { get; set; }
        public int Cash_Fee { get; set; }
       
    }

 

返回的結果我存了日誌  真不知道中間<![CDATA[SUCCESS]]> 這個花裡胡哨的是咋想的 我直接 全部替換 ""  了。

<xml><return_code><![CDATA[SUCCESS]]></return_code>
<return_msg><![CDATA[OK]]></return_msg>
<appid><![CDATA[wx6ed9112323b1f1d3e04]]></appid>
<mch_id><![CDATA[16045678769]]></mch_id>
<nonce_str><![CDATA[9xonPINPBGO4y8WW]]></nonce_str>
<sign><![CDATA[DABC2420B7B03FBA4D0027A9EBBF54D7]]></sign>
<result_code><![CDATA[SUCCESS]]></result_code>
<transaction_id><![CDATA[42000009嗚嗚嗚嗚276477024]]></transaction_id>
<out_trade_no><![CDATA[YYGH20210129164746001]]></out_trade_no>
<out_refund_no><![CDATA[CFTF20210129164806002]]></out_refund_no>
<refund_id><![CDATA[50300407262021012905921959108]]></refund_id>
<refund_channel><![CDATA[]]></refund_channel>
<refund_fee>440</refund_fee>
<coupon_refund_fee>0</coupon_refund_fee>
<total_fee>440</total_fee>
<cash_fee>440</cash_fee>
<coupon_refund_count>0</coupon_refund_count>
<cash_refund_fee>440</cash_refund_fee>
</xml>

4、總結

看著幾行程式碼 還給我排隊等了好久終於問人工解決的,之前都沒說請求頭裡面載入使用者證書,可能是v3出來了沒太在意吧。後面看看 v3介面 看樣子應該跟統一下單一樣了吧只要呼叫請求那個方法就可以了! 

時間,抓起了就是黃金,虛度了就是流水;書,看了就是知識,沒看就是廢紙;理想,努力了才叫夢想,放棄了那只是妄想。努力,雖然未必會收穫,但放棄,就一定一無所獲。

本文地址:https://www.cnblogs.com/w5942066/p/14346015.html

相關文章