springboot接入支付宝的支付功能
了解支付宝
当接到这个需求,我就去找运营或产品要了登录的管理员账号,登录上去看看官方接入文档(对接的API文档不需要登录也可查看);主要看接入流程,以及需要用到哪些支付接口,接口中由哪些请求参数,返回参数,还有看支付宝的应用配置参数,密钥工具,应用私钥和支付宝公钥等信息;有了这些就可以开始准备代码开发和设计支付宝订单表结构(无论订单支付成功,还是失败,或者退订,我们自己数据库都要保存支付宝的订单信息,如:订单状态,订单支付时间,订单退订时间,订单支付金额等等);
设计表结构的思路
我是根据业务需求,和官方的支付订单接口文档的参数,去定义表的结构字段,类型;再根据之后业务量和需求去建立索引,如订单状态,支付订单号,这里就不做详细描述;
支付功能开发
前端设置个【测试支付】按钮功能调用后端支付功能,主要在于后端(java开发):
- 导入支付宝的maven依赖;
<dependency>
<groupId>com.alipay.sdk</groupId>
<artifactId>alipay-sdk-java</artifactId>
<version>4.39.2.ALL</version>
</dependency>
我是在test.yml文件配置支付宝参数,应用私钥和支付宝公钥可以通过支付宝工具生成获取;
- 回调路径-就是支付宝支付成功后,调用自己定义的【回调接口】,返回支付结果,可以根据回调的参数校验密钥,然后更改支付状态;
- 完成跳转路径-就是支付宝点击支付完成后,根据业务需求,跳转到自己定义的首页界面呢,还是XX页面给用户看;
注意:1、支付宝支付请求接口-使用方式
<!**
*请求方法是pageExecute,请求方式POST,返回的是表单
*请求方法是pageExecute,请求方式GET,返回的是url路径
*无论是表单还是URL路径打开都是支付界面
**>
AlipayTradeWapPayResponse response = alipayClient.pageExecute(request, "GET");
String pageRedirectionData = response.getBody();
前端接收打开支付界面方式:
//第一种方式:后端返回的字符串表单
var htmlData = res.data;
document.write(htmlData);//打开支付界面
//第二种方式:后端返回的字符串url
//var url = res.data;
// window.open(url);//打开支付界面
注意:2、支付宝支付退订请求接口-使用方式【只有响应参数是用execute方法,如果要返回表单或者url路径就用pageExecute】
<!**
*请求方法是【execute】,返回的是响应参数
**>
AlipayTradeWapPayResponse response = alipayClient.execute(request);
String pageRedirectionData = response.getBody();
注意:3、支付宝开发完成后,在支付宝的沙箱环境(登录后的控制台-最下面有个沙箱),用沙箱应用,沙箱账号去支付测试
接收支付宝的异步回调
/**
* 支付宝支付回调通知
*
* @param request 参数
* @param response 响应
* @return
* @throws Exception
*/
@AutoLog(value = "API-支付宝回调通知")
public Result aliPay_callback(HttpServletRequest request, HttpServletResponse response) throws Exception {
//方便手动回调测试,返回结果
Result result = new Result();
//获取支付宝回调参数
Map<String, String[]> requestParams = request.getParameterMap();
//转成支付宝-验签参数
Map<String, String> params = new HashMap<>();
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);
}
/*
* 注意:
* 在支付宝的业务通知中,只有交易通知状态为 TRADE_SUCCESS 或 TRADE_FINISHED 时,支付宝才会认定为买家付款成功。
* 如果签约的产品支持退款,并且对应的产品默认支持能收到 TRADE_SUCCESS 或 TRADE_FINISHED 状态,
* 该笔交易会先收到 TRADE_SUCCESS 交易状态,然后默认12个月该笔交易会再次收到 TRADE_FINISHED 状态。
* 实际该笔交易只支付了一次,切勿认为该笔交易支付两次
* */
/*
*异步接口响应response:fail获取消息失败,重发,success获取消息成功;
* 交易状态:WAIT_BUYER_PAY:交易创建,等待买家付款
* TRADE_CLOSED:在指定时间段内未支付时关闭的交易或在交易完成全额退款成功时关闭的交易
* TRADE_SUCCESS:商家签约的产品支持退款功能的前提下,买家付款成功。
* TRADE_FINISHED:商家签约的产品不支持退款功能的前提下,买家付款成功。
* 或者,商家签约的产品支持退款功能的前提下,交易已经成功并且已经超过可退款期限
*/
PrintWriter writer = response.getWriter();
//支付宝-验签
Boolean signVerified = alipay.signAlipay(params);
if (signVerified) {
// TODO 验签成功后
//业务请求
AlipayOrder alipayOrder = BeanUtil.copyProperties(params, AlipayOrder.class);
log.info("alipayOrder={}", alipayOrder);
QueryWrapper<AlipayOrder> alipayOrderQW = new QueryWrapper<>();
alipayOrderQW.lambda().eq(AlipayOrder::getTradeNo, alipayOrder.getTradeNo());
AlipayOrder alipayOrderObj = alipayOrderService.getOne(alipayOrderQW);
if (oConvertUtils.isNotEmpty(alipayOrderObj)) {
String id = alipayOrderObj.getId();
alipayOrder.setId(id);
alipayOrderService.updateById(alipayOrder);
} else {
alipayOrderService.save(alipayOrder);
}
//返回结果
result.setMessage("success");
writer.write("success");
} else {
result.setMessage("fail");
writer.write("fail");
}
result.setSuccess(signVerified);
result.setResult(JSON.toJSONString(params));
return result;
}
/**
* 支付宝验签-回调的待验签字符串
*
* @param resultInfo 验签参数
* @return
* @throws Exception
*/
public Boolean signAlipay(Map<String, String> resultInfo) throws Exception {
// 支付宝公钥
String publicKey = alipayPublicKey;
// 验签方法-支付宝官方提供
return AlipaySignature.rsaCheckV1(resultInfo, publicKey, charset, signType);
}
文档中,大部分都是本人参考支付宝官方文档,以及借鉴他人博客等其他资料,希望能对各位开发朋友,有所帮助,互相交流;如有什么问题,可以留言