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介面 看樣子應該跟統一下單一樣了吧只要呼叫請求那個方法就可以了!
時間,抓起了就是黃金,虛度了就是流水;書,看了就是知識,沒看就是廢紙;理想,努力了才叫夢想,放棄了那只是妄想。努力,雖然未必會收穫,但放棄,就一定一無所獲。