支付宝异步通知处理实现原理总结

支付宝有一个接口:实现支付请求(里面要提供一个订单号)
你有一个接口:实现支付结果的通知(通知里面会包含订单号)

后面就简单了

1)你生成订单 请求调用 支付宝接口 去支付(然后。。然后就没然后了)

2)你的接口:等着呗 支付宝会调用的接口 通知你 那个订单完成的结果

3) 支付系统的异步通知实质上是给给定的地址发送请求实现的,这个地址很可能是不会有页面而是一个接口地址。

4)通知地址实际上不是一个页面,而是一个接收通知的接口地址,上游给这个地址发请求,实际是调用我们的同志接口比如:
我们系统中的接受通道通知处理逻辑的本质是上游通过接口地址来调用接口。比如下面例子中就会通过“http://XXX.XXX.XXX.XXX:XXX/notify/XXXX”来调用接口。

5)支付通知,是用来接收来自银行或者第三方支付平台的订单支付结果通知,分为两种,一种是同步通知(又称前台通知),一种是异步通知(又称后台通知),简单的说,商户支付系统收到支付同步通知并且支付状态为已支付,我们需要将订单支付状态修改为支付确认中,商户支付系统收到支付异步通知并且支付状态为支付成功,我们需要将订单支付状态修改为已支付。再次强调下,商户支付系统要以异步通知的结果为准

    根据完成结果 实现 业务逻辑  

补充:
1、支付成功后 会有通知 但是哪个页面什么都么有 就是这个通知页面要怎么实现
1)支付成功 可以跳转一个页面 ,页面也是你写的 你想怎么写 就怎么写
2)所有的异步实现都可以通过spring的@Async注解调用spring的线程池完成。
举个例子,代码如下---个人处理结算异步通知接口(被回调对象):

  1. /**
  2. * 个人处理结算异步通知
  3. *
  4. * @param dto
  5. * @return
  6. */
  7. @RequestMapping(value = "/notify/{orderId}")
  8. @ResponseBody
  9. public Object personal(HttpServletRequest request, @PathVariable String orderId,HttpServletResponse response){
  10. Map<String, String> result = new HashMap<String, String>();
  11. result.put("respCode", "N");
  12. try {
  13. String content = this.getRequestBody(request);
  14. logger.info("收到结算系统结果回调:{}", content);
  15. WithdrawCallbackDto dto=JSON.parseObject(content, WithdrawCallbackDto.class);
  16. logger.info("个人:收到结算系统结果回调:{}", dto);
  17. if (!checkSmtSign(dto)) {
  18. result.put("respMsg", "验签失败");
  19. return result;
  20. }
  21. //查询个人提现记录流水
  22. WithdrawPersonalFlowEntity entity=withdrawPersonalFlowService.queryPersonalFlowByFlowNo(dto.getOutTradeNo());
  23. if(entity==null){
  24. result.put("respMsg", "操作失败:个人提现流水不存在");
  25. return result;
  26. }
  27. //校验提现流水状态是否支持退款和更新状态操作
  28. if(entity.getStatus()==WithdrawStatusEnum.ACCEPT.getStatus()){
  29. result.put("respCode", "Y");
  30. // 准备个人提现流水更新参数准备
  31. SettlementResponseDto responseDto = personalParametersPrepare(dto);
  32. // 代表结算成功
  33. if (dto.getRespCode().equals("100005")) {
  34. //更新个人提现流水操作
  35. withdrawPersonalFlowService.updateFlowBySmt(responseDto);
  36. }else{// 代表结算失败
  37. //更新个人提现流水操作
  38. withdrawPersonalFlowService.updateFlowBySmt(responseDto);
  39. //个人退还提现本金参数准备
  40. WithdrawRefundDto refundDto=personalRefundOfCommissionCharge(dto);
  41. //个人提现,退还本金处理
  42. withdrawPersonalFlowService.refund(refundDto);
  43. }
  44. result.put("respMsg","验签成功,个人退款成功");
  45. }else{
  46. result.put("respMsg", "操作失败:个人提现流水状态有误");
  47. return result;
  48. }
  49. } catch (PaycoreException e) {
  50. logger.error("个人处理结算系统异步回调失败, {}", e);
  51. result.put("respMsg", e.getErrorMsg());
  52. return result;
  53. } catch(Exception e){
  54. logger.error("个人处理结算系统异步回调失败, {}", e);
  55. result.put("respMsg","系统异常" );
  56. return result;
  57. }
  58. return result;
  59. }

回调验签

  1. private boolean checkSmtSign(WithdrawCallbackDto dto){
  2. String[] exceptParams = {"signCode", "signType"};
  3. return SignatureUtil.callBackVerify(dto, exceptParams);
  4. }
xml 配置
  1. <bean id="signUtil" class="com.qbao.signature.sign.client.CuratorZookeeperClient"
  2. init-method="init" lazy-init="false">
  3. <property name="connectString" value="${zk.hosts}" />
  4. <property name="needsAll" value="true" />
  5. </bean>
  6. <bean id="verifyUtil" class="com.qbao.signature.verify.client.CuratorZookeeperClient"
  7. init-method="init" lazy-init="false">
  8. <property name="connectString" value="${zk.hosts}"/>
  9. </bean>




2018-11-24更新

1、支付系统中异步通知的验签分两种情况

(1)第一种,会将我的通知地址(其实就是我们自己异步通知接口url)和和下单参数原样返回给我们,我们按照订单的签名针对订单重新对比签名是否相等。

(2)第二种,上游会将我们参数在异步通知中再次封装,然后验签的时候跟订单数据没有关系,直接做通知验签,比如:微信H5就是直接在返回结果中移除sign以后按照签名算法进行验签即可。

posted @ 2018-10-08 11:29  星朝  阅读(8104)  评论(0编辑  收藏