目录:
(1)订单支付码有效时间
(2)支付后回调—同步回调
(3)支付宝回调—异步回调
(1)订单支付码有效时间
(2)支付后回调—同步回调
static修饰,不能通过Vuale注解获取配置文件的值,可以通过下面的方法进行获取值
实现类放开同步回调地址
成功页面:
修改配置类:
控制器AlipayController
/*** 支付宝回调* @return*/
@RequestMapping("callback/return")
public String callBack() {// 同步回调给用户展示信息return "redirect:" + AlipayConfig.return_order_url;
}
在web-all 项目中添加对应的返回控制器
/*** 支付成功页* @return*/
@GetMapping("pay/success.html")
public String success() {return "payment/success";
}
最终跳转到这个页面:
(3)支付宝回调—异步回调
...
异步回调有两个重要的职责:
确认并记录用户已付款,通知电商模块。新版本的支付接口已经取消了同步回调的支付结果传递。所以用户付款成功与否全看异步回调。
接收到回调要做的事情:
- 验证回调信息的真伪
- 验证用户付款的成功与否
- 把新的支付状态写入支付信息表{paymentInfo}中。
- 通知电商
- 给支付宝返回回执。
应用公网暴露
支付宝异步回调,需要我们的应用在公网上,怎么办呢?可以使用一个软件把我们的应用跟公网绑定,相当于我们的应用就在公网了
authttoken是申请的
双击启动:就实现了我们的应用进行了公网地址的绑定
这样就接受到了异步回调的数据了:
配置类:
控制器AlipayController
@Autowired
private PaymentService paymentService;
@PostMapping("/callback/notify")
@ResponseBody
public String callbackNotify(@RequestParam Map<String, String> paramsMap){System.out.println("你回来了.....");// Map<String, String> paramsMap = ... // 将异步通知中收到的所有参数都存放到map中//返回的异步结果进行验签boolean signVerified = false; //调用SDK验证签名try {//参数阿里公钥、字符编码、签名算法signVerified = AlipaySignature.rsaCheckV1(paramsMap, AlipayConfig.alipay_public_key, AlipayConfig.charset, AlipayConfig.sign_type);} catch (AlipayApiException e) {e.printStackTrace();}// 获取异步通知的参数中的订单号!String outTradeNo = paramsMap.get("out_trade_no");// 获取异步通知的参数中的订单总金额!String totalAmount = paramsMap.get("total_amount");// 获取异步通知的参数中的appId!String appId = paramsMap.get("app_id");// 获取异步通知的参数中的交易状态!String tradeStatus = paramsMap.get("trade_status");// 根据outTradeNo 查询数据!PaymentInfo paymentinfo = this.paymentService.getPaymentInfo(outTradeNo, PaymentType.ALIPAY.name());// 保证异步通知的幂等性!notify_idString notifyId = paramsMap.get("notify_id");// true:if(signVerified){// TODO 验签成功后,按照支付结果异步通知中的描述,对支付结果中的业务内容进行二次校验,校验成功后在response中返回success并继续商户自身业务处理,校验失败返回failureif (paymentinfo==null || new BigDecimal("0.01").compareTo(new BigDecimal(totalAmount))!=0|| AlipayConfig.app_id.equals(appId)){return "failure";}// 放入redis! setnx:当 key 不存在的时候生效!Boolean flag = this.redisTemplate.opsForValue().setIfAbsent(notifyId, notifyId, 1462, TimeUnit.MINUTES);// 说明已经处理过了!if (!flag){return "failure";}if ("TRADE_SUCCESS".equals(tradeStatus) || "TRADE_FINISHED".equals(tradeStatus)){// 修改交易记录状态!再订单状态!this.paymentService.paySuccess(outTradeNo,PaymentType.ALIPAY.name(),paramsMap);// this.paymentService.paySuccess(paymentinfo.getId(),paramsMap);return "success";}}else{// TODO 验签失败则记录异常日志,并在response中返回failure.return "failure";}return "failure";
}
幂等性:多次操作跟一次操作是一样的
这里更上面返回的不一样,返回sucess,支付宝就不会再发送了,如果返回faild支付宝会间隔一段的时间持续发送,返回啥都一样功能都能够实现
为了保证更新支付状态时出现异常,前面存不进去了,出现异常删掉前面Redis中的key
接口PaymentService
/*** 获取paymentInfo 对象* @param outTradeNo* @param name* @return*/
PaymentInfo getPaymentInfo(String outTradeNo, String name);/*** 支付成功更新交易记录方法* @param outTradeNo* @param name* @param paramMap*/
void paySuccess(String outTradeNo, String name, Map<String, String> paramMap);/*** 根据outTradeNo 支付方式name 更新数据* @param outTradeNo* @param name* @param paymentInfo*/
void updatePaymentInfo(String outTradeNo, String name, PaymentInfo paymentInfo);
实现类
@Override
public PaymentInfo getPaymentInfo(String outTradeNo, String name) {// select * from payment_info where out_trade_no = ? and payment_type = ?QueryWrapper<PaymentInfo> paymentInfoQueryWrapper = new QueryWrapper<>();paymentInfoQueryWrapper.eq("out_trade_no",outTradeNo);paymentInfoQueryWrapper.eq("payment_type",name);return paymentInfoMapper.selectOne(paymentInfoQueryWrapper);
}@Override
public void paySuccess(String outTradeNo, String paymentType, Map<String, String> paramsMap) {// 根据outTradeNo,paymentType 查询PaymentInfo paymentInfoQuery = this.getPaymentInfo(outTradeNo, paymentType);if (paymentInfoQuery==null){return;}try {// 改造一下更新的方法!PaymentInfo paymentInfo = new PaymentInfo();//回调时间paymentInfo.setCallbackTime(new Date());//支付状态paymentInfo.setPaymentStatus(PaymentStatus.PAID.name());/回调内容paymentInfo.setCallbackContent(paramsMap.toString());//交易编号paymentInfo.setTradeNo(paramsMap.get("trade_no"));// 查询条件也可以作为更新条件!this.updatePaymentInfo(outTradeNo, paymentType, paymentInfo);} catch (Exception e) {// 删除keythis.redisTemplate.delete(paramsMap.get("notify_id"));e.printStackTrace();}}// 更新交易状态记录!
public void updatePaymentInfo(String outTradeNo, String name, PaymentInfo paymentInfo) {QueryWrapper<PaymentInfo> paymentInfoQueryWrapper = new QueryWrapper<>();paymentInfoQueryWrapper.eq("out_trade_no",outTradeNo);paymentInfoQueryWrapper.eq("payment_type",name);paymentInfoMapper.update(paymentInfo,paymentInfoQueryWrapper);
}
支付成功后数据库的状态会更改