zoukankan      html  css  js  c++  java
  • 不知道张(zhāng)雱(pāng)是谁?你out了!

    张(zhāng)雱(pāng)是谁?也许你已经听说过了,也许你还没听说过呢,不过你一定听说过老刘——刘强东,没错,这二人是有关系的,什么关系,京东是老刘的,而张雱呢?张雱是京东旗下52家关联公司法人代表或执行董事。


    好了,爆料至此,本文也不是为了要说这些事。 只是为了找个话题营销一下,目的是让你点进来,为什么这么玩呢?也许你会开骂,不过求你放过我,我辛辛苦苦写了这么长的文字,是想求一名观众的。每个人都渴望被关注,不是么?
    言归正传,本文的主题是一个订单退款处理问题的解决过程。


    近期,运营人员经常反映,在线支付的订单在做退票处理时,总是停滞在TPZ(退票中)状态,无法改成YTP(已退票)。

    系统部署方面,支付中心是一个单独的应用,负责内部支付请求与外部的第三方支付平台的信息桥接。商旅的saas是发布给客户用来预定商旅产品的平台。

    支付中心退款流程如下:

    查看业务系统支付发起的日志,如下:

    时间:2016-07-18 23:56:03
    请求调用支付中心,目标地址:http://paycenter.***.cn/PayAndRefund/RefundPage.aspx,参数:{"BusinessSystemId":1,"BusinessCode":1,"ThirdPayPlatform":3,"PayType":1,"PaySource":1,"OrderAmount":"880","OrderNo":"AZDOW1607131356","UserName":"李佳祺"}
    ----------------------------------------------------------------------------------------
    
    时间:2016-07-18 23:56:05
    调用支付中心返回结果:{"Code":"9999","MsgStr":"已有退款单不可以重复发起!"}
    ----------------------------------------------------------------------------------------
    

    哦,支付中心返回的不是退款成功,而是9999(“已有退款单不可以重复发起”)。

    那接下来,去支付中心来进一步排障。支付中心针对这张订单的日志记录如下:

    23:56:26.175	---------------类:ASP.payandrefund_refundpage_aspx
    23:56:26.691	接收到退款申请,反序列化正常,原始退款消息是{"BusinessSystemId":1,"BusinessCode":1,"ThirdPayPlatform":3,"PayType":1,"PaySource":1,"OrderAmount":"880","OrderNo":"AZDOW1607131356","UserName":"李佳祺"}---类名:ASP.payandrefund_refundpage_aspx
    23:56:26.691	请求的域名(paycenter.***.cn)与Partner支付协议使用的域名(pay.***.cn)不同,用后者域名发起流请求---类名:ASP.payandrefund_refundpage_aspx
    23:56:26.785	---------------类:ASP.payandrefund_refundpage_aspx
    23:56:26.785	接收到退款申请,反序列化正常,原始退款消息是{"BusinessSystemId":1,"BusinessCode":1,"ThirdPayPlatform":3,"PayType":1,"PaySource":1,"OrderAmount":"880","OrderNo":"AZDOW1607131356","UserName":"李佳祺"}---类名:ASP.payandrefund_refundpage_aspx
    23:56:26.863	持久化正常共添加1条数据---类名:ASP.payandrefund_refundpage_aspx
    23:56:26.863	接收支付数据成功并发送数据---类名:ASP.payandrefund_refundpage_aspx
    23:56:26.879	---------------类:ASP.yibao_refundform_aspx
    23:56:27.160	进入易宝退款,发起消息: p0_Cmd=RefundExt&p1_MerId=10012414996&p1_RequestId=AZDOW1607131356Refund2&p2_TrxId=218565910862142I&p3_Desc=nothing&p4_Details=&p5_Amt=880&hmac=d1dc1eab0d9553c99788587b20405bdd<=GMX=>返回消息:r0_Cmd=RefundExt
    r1_Code=1
    r1_RequestId=AZDOW1607131356Refund2
    r1_Time=20160718235637
    errorMsg=
    r2_OrderAmount=
    r3_SrcAmount=
    r4_RefundAmount=
    r5_MerRefundAmount=
    r6_Details=
    r7_Fee=0.0
    r9_BType=2
    hmac=f76ac405de03233ad6e92eef9dea2b8e---类:ASP.yibao_refundform_aspx
    

    分析这日志,可以看出,支付中心接收到这笔订单的退款请求,然后向第三方支付平台(易宝)接口发起了退款请求,并得到了退款成功的响应。貌似没什么问题。 但,为什么业务系统接收到支付的响应不是退款成功反而是“已有退款单”呢?

    这时不得不说支付中心的日志记录得不够完整。没有记录给业务系统回写了什么。只记录了一个易宝返回退款成功的日志,之后就什么都没有了。

    那么,想查明原因,接下来只能分析程序代码了。

    退款代码比较多,而且涉及到页面跳转,伪代码是这样子的:

     1 public partial class RefundPage : System.Web.UI.Page
     2 {
     3         
     4         /// <summary>
     5         /// 根据Request字符串反序列化得到的实体对象
     6         /// </summary>
     7         private CommonModel.Domains.RefundApply _refundApply;
     8 
     9         protected void Page_Load(object sender, EventArgs e)
    10         {
    11             验证请求参数,反序列化成_refundApply
    12             ValidDomainNameAndRedirect(); //验证支付中心域名
    13             if(没有该订单的退款记录)
    14             {
    15                         持久化退款申请记录
    16                         拼接易宝退款请求报文
    17                         Hmac加密
    18                         拿到易宝退款接口url
    19                         流请求方式调用易宝接口
    20                         对响应值做处理
    21                         if(是退款成功)
    22                         {
    23                                     回写“退款成功”
    24                         }
    25                         else
    26                         {
    27                                     回写“退款失败”
    28                         }
    29             }
    30             else
    31             {
    32                         回写9999"已有退款单不可以重复发起!"
    33             }
    34         }
    35 
    36         /// <summary>
    37         /// 判断请求的域名是否与Partner支付协议使用的域名相同。
    38         /// 如果不同,则发起流请求,修改为Partner支付协议使用的域名
    39         /// </summary>
    40         private void ValidDomainNameAndRedirect()
    41         {
    42                 string domainName = CommonModel.PayCenterConfig.PayAddress.PartnerDomainNameMap[_refundApply.Partner];
    43                 if (Request.Url.Authority != new Uri(domainName).Authority)
    44                 {
    45                     string url = string.Format("{0}/PayAndRefund/RefundPage.aspx", domainName);
    46                     CommonUtils.LogHelper.Write(this,
    47                        string.Format("请求的域名({0})与Partner支付协议使用的域名({1})不同,用后者域名发起流请求",
    48                         Request.Url.Authority, new Uri(domainName).Authority));
    49                     CommonModel.WebCommon.SendStreamStr(_requestStringInStream, url);
    50                 }
    51         }
    52 }

     需要再次强调的是ValidDomainNameAndRedirect()方法,这是什么意思呢? 支付中心是我司的一个通用的支付平台,对内公开的域名统一为paycenter.sbh.cn(即内部所有系统对接支付中心统一用这个域名), 而支付中心在与第3方支付平台对接时,是协议商定的域名(我司与连连支付协议域名是pay.yqs.cn,我司与易宝协议域名是payment.sbh.cn,与连连的代付协议域名是payagent.yqs.cn)。 支付中心作为一个站点部署在服务器的iis上,并绑定了这些域名。 基于这个情况,支付中心网站在接收到内部系统的支付请求后,首先要判断域名决定跳到相应的协议域名上(同一个iis站点),然后再做后续的处理。

    通过进一步分析上面的代码,问题正是出现在了这个方法上,见如上代码的第49行:用协议域名发起了流请求。那么针对这种情况下的退款操作,最终的回写值应该是流请求的返回值才对。 调整完后的代码如下:

     1 public partial class RefundPage : System.Web.UI.Page
     2 {
     3          
     4         /// <summary>
     5         /// 根据Request字符串反序列化得到的实体对象
     6         /// </summary>
     7         private CommonModel.Domains.RefundApply _refundApply;
     8  
     9         protected void Page_Load(object sender, EventArgs e)
    10         {
    11              验证请求参数,反序列化成_refundApply
    12              var model = ValidDomainNameAndRedirect(); //验证支付中心域名
    13              if(model != null)
    14              {
    15                     回写序列化model后的字符串 // 即 “退款成功” or “退款失败”
    16              }
    17              ...... //下面代码同上
    18         }
    19          
    20         private ReturnValue ValidDomainNameAndRedirect()
    21         {
    22                 string domainName = CommonModel.PayCenterConfig.PayAddress.PartnerDomainNameMap[_refundApply.Partner];
    23                 if (Request.Url.Host != new Uri(domainName).Host)
    24                 {
    25                     string url = string.Format("{0}/PayAndRefund/RefundPage.aspx", domainName);
    26                     CommonUtils.LogCommon.instance.writePay(this,
    27                        string.Format("请求的域名({0})与Partner支付协议使用的域名({1})不同,用后者域名发起流请求",
    28                         Request.Url.Host, new Uri(domainName).Host));
    29                     // 发起流请求,并获取返回值
    30                     string retValue = CommonModel.WebCommon.SendStreamStr(_requestStringInStream, url);
    31                     if (!string.IsNullOrEmpty(retValue))
    32                     {
    33                         return JsonConvert.DeserializeObject<ReturnValue>(retValue);
    34                     }
    35                 }
    36                 return null;
    37         }
    38 }

    经过仔细的推敲,这么改的逻辑应该是比较严谨了。 支付中心是去年上线的项目,因为缺乏测试用例,加上测试环境无法调用3方支付平台,给这次测试增加了难度。加上时间久远,加上

    经测试,整个退款过程终于正常了。发布到生产环境,搞定!

    下班后,走在路上,突然想起来文章开始的问题,为什么支付中心返回给业务系统的结果是已有退款单呢?

    通过下面的demo就能看出来: 新建2个空的web窗体,.cs里的代码如下:

     1     public partial class SendStreamTest1 : System.Web.UI.Page
     2     {
     3         protected void Page_Load(object sender, EventArgs e)
     4         {
     5             Response.Write("Application["SendStreamStr"] is: " + Application["SendStreamStr"]);
     6             Application["SendStreamStr"] = null;
     7 
     8             Uri url = new Uri(string.Format("http://{0}/Test/SendStreamTest2.aspx", Request.Url.Authority));
     9             CommonModel.WebCommon.SendStreamStr("", url.ToString());
    10             Response.Write("the value is:" + Application["SendStreamStr"]);
    11         }
    12     }
    1     public partial class SendStreamTest2 : System.Web.UI.Page
    2     {
    3         protected void Page_Load(object sender, EventArgs e)
    4         {
    5             Application["SendStreamStr"] = "111";
    6             Response.Write("Application["SendStreamStr"] is initialized as " + Application["SendStreamStr"]);
    7         }
    8     }

    用ie访问SendStreamTest1.aspx,可以看出来,流请求方式调用SendStreamTest2.aspx以后,Application["SendStreamStr"]被赋值了。(ps:注意是用Application来模拟测试的,不能用Session测试哦~你懂的)

    通过这个demo不难理解,上面之所以返回已有退款单,就是流请求调用之后,退款申请记录被持久化了,所以这里判断是否已有申请单自然是有了,那么,按照逻辑就会返回已有退款单了。

  • 相关阅读:
    中风后遗症
    慢性湿疹半年
    女子脚背痒肿案
    肾盂肾炎病案
    鼻衄二则
    糖尿病病案
    慢性肠炎2例
    子宫肌瘤病案2例
    眩晕病案
    前列腺炎病案3例
  • 原文地址:https://www.cnblogs.com/buguge/p/5733243.html
Copyright © 2011-2022 走看看