1、前端-使用window.location.href来请求接口,不要使用ajax,我们请求后台支付接口后要跳转到支付宝的支付页面,安全起见,这里入参传订单ID就够了,使用订单ID从后台拿订单数据
2、控制器
@ApiOperation("根据订单ID生成支付宝支付二维码")
@GetMapping("/alipay")
public String alipay(String orderId, HttpServletResponse response) throws AlipayApiException {
return orderService.alipay(orderId, response);
}
3、支付业务,支付宝接口文档参考“电脑网址支付——alipay.trade.page.pay(统一收单下单并支付页面接口)”
@Override
public String alipay(String orderId, HttpServletResponse servletResponse) throws AlipayApiException {
ServiceOrder order = orderMapper.selectById(orderId);//订单校验?如剔除订单项中已被置为删除的
AlipayVo vo = new AlipayVo();
vo.setOut_trade_no(order.getOrderNo());
vo.setTotal_amount(String.valueOf(order.getPayPrice()));
vo.setSubject("jwolf测试");
vo.setProduct_code("FAST_INSTANT_TRADE_PAY"); //这个是固定的
AlipayTradePagePayRequest request = new AlipayTradePagePayRequest();
request.setBizContent(JSON.toJSONString(vo));
request.setReturnUrl(alipayReturnUrl);
request.setNotifyUrl(alipayNotifyUrl);
// 执行支付宝请求,其实就是httpclient请求
// 注意这里使用的是AlipayTradePagePayRequest,返回的是response.getBody(),与自定义二维码不同
AlipayTradePagePayResponse response = alipayClient.pageExecute(request);
if (!response.isSuccess()) {
throw new CommonException("支付异常");
}
return response.getBody();
}
4、返回给页面的是如下字符串,就是一个表单,表单会立即提交然后生成二维码到支付宝的固定页面,下面的form表单是带有returnURL的。支付宝扫描支付成功后进入显示支付宝支付成功,然后页面定时器重定向到商户定义的returnURL接口。这就是同步回调。支付宝还会调用配置给它的异步回调接口告知商户后台,商户后台接收到回调请求开始处理自身业务,然后返回给支付宝“SUCCESS”,如果支付宝一直没有及时收到响应会进行衰减请求。
5、同步通知。前后端分离需要自己拼接HTML页面,比较麻烦,然后response响应回去,没分离则直接跳视图填充数据就OK了
public void alipayReturnUrl(HttpServletRequest request, HttpServletResponse response) throws IOException, AlipayApiException {
log.info("支付宝同步回调");
//获取支付宝GET过来反馈信息
Map params = new HashMap();
Map requestParams = request.getParameterMap();
for (Iterator iter = requestParams.keySet().iterator(); iter.hasNext(); ) {
String name = (String) iter.next();
String[] values = (String[]) requestParams.get(name);
String valueStr = "";
for (int i = 0; i < values.length; i++) {
valueStr = (i == values.length - 1) ? valueStr + values[i]
: valueStr + values[i] + ",";
}
//乱码解决,这段代码在出现乱码时使用。
//valueStr = new String(valueStr.getBytes("ISO-8859-1"), "utf-8");
params.put(name, valueStr);
}
boolean signVerified = AlipaySignature.rsaCheckV1(params, zfbPublicKey, AlipayClientConfiguration.charset, AlipayClientConfiguration.signType);
response.setContentType("text/html;charset=utf-8");
PrintWriter out = response.getWriter();
if (!signVerified) {
out.println("验签失败");
}
//商户订单号
String out_trade_no = new String(request.getParameter("out_trade_no").getBytes("ISO-8859-1"), "UTF-8");
//支付宝交易号
String tradeNo = new String(request.getParameter("trade_no").getBytes("ISO-8859-1"), "UTF-8");
String totalAmount = new String(request.getParameter("total_amount").getBytes("ISO-8859-1"), "UTF-8");
String tradeStatus = new String(request.getParameter("trade_status").getBytes("ISO-8859-1"), "UTF-8");
ServiceOrder order = orderMapper.selectOne(new QueryWrapper().eq("order_no", out_trade_no));
String msg = tradeStatus.equals("TRADE_SUCCESS") ? "支付成功" : "支付失败";
msg += JSON.toJSONString(order);
//前后端分离需要自己拼接HTML页面,比较麻烦,然后response响应回去,没分离则直接跳视图就可以了
out.println("" + msg + "");
}
6、异步通知,请求参数处理和验签都和同步通知一样,不同的是同步通知是返回一个页面,这里要修改订单状态,即支付后续逻辑,注意幂等,不要出现两次“发货”。商户的自身业务最好异步执行,可以避免执行失败或超时导致支付宝再次回调,可能会有幂等问题。最好是异步处理支付成功后的业务。异步业务最好多打印日志以备对账使用,这时的订单状态应该是支付成功与订单完成中间的一个“发货”过渡状态,异步业务正常执行完就修改订单状态为完成,而处理失败了可采取人工干预等
@Override
public String alipayNotifyUrl(HttpServletRequest request) throws AlipayApiException {
log.info("支付宝异步回调");
Map params = new HashMap();
Map requestParams = request.getParameterMap();
for (Iterator iter = requestParams.keySet().iterator(); iter.hasNext(); ) {
String name = (String) iter.next();
String[] values = (String[]) requestParams.get(name);
String valueStr = "";
for (int i = 0; i < values.length; i++) {
valueStr = (i == values.length - 1) ? valueStr + values[i]
: valueStr + values[i] + ",";
}
//乱码解决,这段代码在出现乱码时使用。
//valueStr = new String(valueStr.getBytes("ISO-8859-1"), "utf-8");
params.put(name, valueStr);
}
boolean flag = AlipaySignature.rsaCheckV1(params, zfbPublicKey, AlipayClientConfiguration.charset, AlipayClientConfiguration.signType);
// 获取支付宝的通知返回参数,可参考技术文档中页面跳转同步通知参数列表(以上仅供参考)//
if (flag == true) {// 验证成功
String trade_status = request.getParameter("trade_status"); // 交易状态
String out_trade_no = request.getParameter("out_trade_no"); // 获取订单号
log.info("回调过程获取的订单编号:{},订单状态:{}", out_trade_no, trade_status);
if (!"TRADE_SUCCESS".equals(trade_status)) {
return "";
}
log.info("支付宝订单支付成功!开始本地支付成功后业务");
int update = orderMapper.update(new ServiceOrder().setStatus(Constants.OrderStatus.PAY_OK.getOrderStatus()), new QueryWrapper().eq("order_no", out_trade_no).eq("status", Constants.OrderStatus.NO_PAY.getOrderStatus()));
if (update != 1) {
log.error("支付成功,但跟新订单状态为处理出现失败,订单号:{}", out_trade_no);
return "";
} else {
//异步处理该订单。收了钱不能退款,告诉用户成功。异步处理订单如有错误需要人工处理。
asyncProcessOrder(out_trade_no);
return "SUCCESS";
}
}
return "";
}