当前位置 博文首页 > 魏杨杨:Core3.1 微信v3 JSAPI支付 退款

    魏杨杨:Core3.1 微信v3 JSAPI支付 退款

    作者:魏杨杨 时间:2021-02-01 12:25

    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]]></