# 支付宝支付
我们使用的就是 电脑网站支付能力,它的 快速接入文档 (opens new window),时序图如下
可以看得见,和微信支付是类似的:
- 在支付宝发起支付请求:预下单
- 确认支付
- 异步通知支付支付状态
# 前端代码分析
// 支付宝支付直接跳转
window.location.href = "alipay.html?orderId=" + orderId + "&amount="+this.totalAmount;
window.open("alipayTempTransit.html?orderId=" + orderId);
1
2
3
2
3
可以看到,先改变当前浏览器页面到 alipay.html 页面,然后再打开一个窗口 alipayTempTransit.html 页面。
这里支付,还是基于前面用户在购物车结算订单,也就是在我们系统创建完订单之后,发起的操作。
alipay.html:是一个中转页,等待支付成功,在这个页面会去轮询后端请求订单的支付状态
alipayTempTransit.html 是一个真正发起支付的表单页面
// 获得支付宝构建的支付提交form
getAliPayForm (orderId) {
var userInfo = this.userInfo
// 往支付中心发起请求
var paymentServerUrl = app.paymentServerUrl
axios.defaults.withCredentials = true
axios.post(
paymentServerUrl + '/payment/goAlipay?merchantUserId=' + userInfo.id + '&merchantOrderId=' + orderId,
{},
{
headers: {
'imoocUserId': '123',
'password': '123'
}
})
.then(res => {
if (res.data.status == 200) {
// 返回了一个 form 表单
var alipayForm = res.data.data
console.log(alipayForm)
// this.alipayForm = alipayForm;
document.write(alipayForm)
} else {
alert(res.data.msg)
}
})
},
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
# 支付中心支付宝预下单代码
/**
*
* @Description: 前往支付宝进行支付
* @return
* @throws Exception
*/
@ResponseBody
@RequestMapping(value="/goAlipay")
public IMOOCJSONResult goAlipay(String merchantOrderId, String merchantUserId) throws Exception{
// 查询订单详情
Orders waitPayOrder = paymentOrderService.queryOrderByStatus(merchantUserId, merchantOrderId, PaymentStatus.WAIT_PAY.type);
//获得初始化的AlipayClient
AlipayClient alipayClient = new DefaultAlipayClient(aliPayResource.getGatewayUrl(),
aliPayResource.getAppId(),
aliPayResource.getMerchantPrivateKey(),
"json",
aliPayResource.getCharset(),
aliPayResource.getAlipayPublicKey(),
aliPayResource.getSignType());
//设置请求参数
AlipayTradePagePayRequest alipayRequest = new AlipayTradePagePayRequest();
alipayRequest.setReturnUrl(aliPayResource.getReturnUrl());
alipayRequest.setNotifyUrl(aliPayResource.getNotifyUrl());
// 商户订单号, 商户网站订单系统中唯一订单号, 必填
String out_trade_no = merchantOrderId;
// 付款金额, 必填 单位元
String total_amount = CurrencyUtils.getFen2YuanWithPoint(waitPayOrder.getAmount());
// String total_amount = "0.01"; // 测试用 1分钱
// 订单名称, 必填
String subject = "天天吃货-付款用户[" + merchantUserId + "]";
// 商品描述, 可空, 目前先用订单名称
String body = subject;
// 该笔订单允许的最晚付款时间,逾期将关闭交易。取值范围:1m~15d。m-分钟,h-小时,d-天,1c-当天(1c-当天的情况下,无论交易何时创建,都在0点关闭)。 该参数数值不接受小数点, 如 1.5h,可转换为 90m。
String timeout_express = "1d";
alipayRequest.setBizContent("{\"out_trade_no\":\""+ out_trade_no +"\","
+ "\"total_amount\":\""+ total_amount +"\","
+ "\"subject\":\""+ subject +"\","
+ "\"body\":\""+ body +"\","
+ "\"timeout_express\":\""+ timeout_express +"\","
+ "\"product_code\":\"FAST_INSTANT_TRADE_PAY\"}");
//若想给BizContent增加其他可选请求参数, 以增加自定义超时时间参数timeout_express来举例说明
//alipayRequest.setBizContent("{\"out_trade_no\":\""+ out_trade_no +"\","
// + "\"total_amount\":\""+ total_amount +"\","
// + "\"subject\":\""+ subject +"\","
// + "\"body\":\""+ body +"\","
// + "\"timeout_express\":\"10m\","
// + "\"product_code\":\"FAST_INSTANT_TRADE_PAY\"}");
//请求参数可查阅【电脑网站支付的API文档-alipay.trade.page.pay-请求参数】章节
//请求
String alipayForm = "";
try {
alipayForm = alipayClient.pageExecute(alipayRequest).getBody();
} catch (AlipayApiException e) {
e.printStackTrace();
}
log.info("支付宝支付 - 前往支付页面, alipayForm: \n{}", alipayForm);
return IMOOCJSONResult.ok(alipayForm);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
支付中心,接受到前端发起的支付请求,然后使用 alipay 的 sdk 发起预下单请求后,得到一个 html 代码。
再由前端打开的窗口渲染这段 HTML 代码,完成后面的支付。
返回的 html 代码如下所示
上面的表单被加载之后,直接就提交了一个 post 表单,页面就被重定向到如下页面了
这个时候就可以使用支付宝支付功能了
# 支付中心-异步通知
/**
* @Description: 支付成功后的支付宝异步通知
*/
@RequestMapping(value="/alipay")
public String alipay(HttpServletRequest request, HttpServletResponse response) throws Exception {
log.info("支付成功后的支付宝异步通知");
//获取支付宝POST过来反馈信息
Map<String,String> params = new HashMap<String,String>();
Map<String,String[]> requestParams = request.getParameterMap();
for (Iterator<String> 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,
aliPayResource.getAlipayPublicKey(),
aliPayResource.getCharset(),
aliPayResource.getSignType()); //调用SDK验证签名
if(signVerified) {//验证成功
// 商户订单号
String out_trade_no = new String(request.getParameter("out_trade_no").getBytes("ISO-8859-1"),"UTF-8");
// 支付宝交易号
String trade_no = new String(request.getParameter("trade_no").getBytes("ISO-8859-1"),"UTF-8");
// 交易状态
String trade_status = new String(request.getParameter("trade_status").getBytes("ISO-8859-1"),"UTF-8");
// 付款金额
String total_amount = new String(request.getParameter("total_amount").getBytes("ISO-8859-1"),"UTF-8");
if (trade_status.equals("TRADE_SUCCESS")){
String merchantReturnUrl = paymentOrderService.updateOrderPaid(out_trade_no, CurrencyUtils.getYuan2Fen(total_amount));
// 在这里会往我们的后台发送回调通知
notifyFoodieShop(out_trade_no, merchantReturnUrl);
}
log.info("************* 支付成功(支付宝异步通知) - 时间: {} *************", DateUtil.getCurrentDateString(DateUtil.DATETIME_PATTERN));
log.info("* 订单号: {}", out_trade_no);
log.info("* 支付宝交易号: {}", trade_no);
log.info("* 实付金额: {}", total_amount);
log.info("* 交易状态: {}", trade_status);
log.info("*****************************************************************************");
return "success";
}else {
//验证失败
log.info("验签失败, 时间: {}", DateUtil.getCurrentDateString(DateUtil.DATETIME_PATTERN));
return "fail";
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
# 后端代码-异步通知
由于这里使用了支付中心,简化了我们的代码,我们的异步通知使用了与 wx 一样的通知代码。