目录:
(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_id
String notifyId = paramsMap.get("notify_id");
// true:
if(signVerified){
// TODO 验签成功后,按照支付结果异步通知中的描述,对支付结果中的业务内容进行二次校验,校验成功后在response中返回success并继续商户自身业务处理,校验失败返回failure
if (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) {
// 删除key
this.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);
}
支付成功后数据库的状态会更改