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

  • 相关阅读:
    centos 编码问题 编码转换 cd到对应目录 执行 中文解压
    centos 编码问题 编码转换 cd到对应目录 执行 中文解压
    centos 编码问题 编码转换 cd到对应目录 执行 中文解压
    Android MVP 十分钟入门!
    Android MVP 十分钟入门!
    Android MVP 十分钟入门!
    Android MVP 十分钟入门!
    mysql备份及恢复
    mysql备份及恢复
    mysql备份及恢复
  • 原文地址:https://www.cnblogs.com/buguge/p/5733243.html
Copyright © 2011-2022 走看看