Core3.1 微信v3 JSAPI支付 退款

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

posted @ 2021-02-01 10:48  魏杨杨  阅读(647)  评论(3编辑  收藏
行到水穷处,坐看云起时!转载声明:技术需要共享,欢迎转载!但请注明版权及出处!