在第三方应用里开发者的身份大多是服务商,需要代商家调用接口完成收款业务。之前我以为支付业务只需要传交易金额,订单号,交易双方的支付宝账号和在支付宝开放平台申请到的appId, 应用私钥, 支付宝公钥调用接口就可以了。仔细阅读过官方API文档后我发现订单交易并不是通过传入交易双方的支付宝账号。传入交易双方支付宝账号通常适用于支付宝双方转账场景(提现,分佣等)。商家自研应用需要传入appId实现收款,应用私钥和支付宝公钥在数据传输过程中对接口信息进行加解密(防止被人截获和篡改数据)。在第三方应用中存在一个非常重要的问题,相对于商家自研应用,服务商需要代商家调用支付接口,不能通过传入支付宝账号,怎么才能满足每个商家的支付需求,把款打到商家对应的账户上呢?支付宝开放平台给出了相关文档说明。
小程序文档 - 支付宝文档中心 通过用户授权获取用户信息以及拿到openId
图一 第三方应用邀请商家授权
图二 获取app_auth_token业务流程图
在找到上面的文档研究过后,我的思路清晰了很多,我们进入到第三方应用后台按照流程完整的进行一遍操作。
1.我们进入第三方应用后台依次点击"商家授权"->邀请商家授权按钮
2.点击后我们进入到"邀请商家授权"的页面,可以看到有通过二维码和链接的两种方式,我们可以把二维码和PC端链接渲染到我们应用当中,让商家扫码进行授权。这里我们以在浏览器输入PC端链接为例。
3.打开PC端链接,我们选择要授权应用,勾选服务条款,点击"确认处理代办"按钮
4.商家授权成功后会自动打开授权回调地址,app_auth_code会同时拼接到路由中。授权回调地址在第三方应用后台开发设置中进行配置,商家授权后会自动跳转到回调地址,并且以路由传参的方式把参数传过来。因此该授权回调地址必须存在且部署到服务器上。
5.在邀请商家授权后我们能看到app_auth_code已经传到了回调地址中。接下来我们需要写好程序在页面中拿到这些路径参数,页面打开时将这些参数传到后端换取app_auth_token。
下面附上我的页面源代码:
<script setup lang="ts">
import { useRoute } from 'vue-router'
import { onMounted, ref } from 'vue';
import request from '../utils/request'
let sellerId = ref('1807335124124164098')
const route = useRoute()
onMounted(() => {
console.log('code:' +route.query.app_auth_code)
console.log('appId:' +route.query.app_id)
console.log('source:' +route.query.source)
// 调用后端接口, 以app_auth_code换取app_auth_token
request.get('/updateToken', { params: { authCode: route.query.app_auth_code, sellerId: sellerId.value } })
})
</script>
<template>
<div class="getCode">
商家授权成功
</div>
</template>
<style scoped lang="scss">
.getCode {
width: 100vw;
}
</style>
6.后端拿到app_auth_code后需要参照官方文档换取app_auth_token,拿到app_auth_token后我们还需要将app_auth_token存入数据库中便于调用支付接口。下面附上我的后端源代码:
6.1.Maven依赖:
<dependency>
<groupId>com.alipay.sdk</groupId>
<artifactId>alipay-sdk-java</artifactId>
<version>4.39.113.ALL</version>
</dependency>
6.2.控制层代码
/**
*
* @param authCode
* @param sellerId 对应我数据库中表的主键ID,便于更新app_auth_token到商家表中
* @return
* @throws AlipayApiException
*/
@GetMapping("/updateToken")
public Result updateToken (String authCode, Long sellerId) throws AlipayApiException {
sellerService.updateToken(authCode, sellerId);
return Result.success();
}
6.3.业务层代码
public void updateToken(String authCode, Long sellerId) throws AlipayApiException {
AliPayConfig aliPayConfig = new AliPayConfig();
AlipayClient alipayClient = new
DefaultAlipayClient("https://openapi.alipay.com/gateway.do", aliPayConfig.getAppId(), aliPayConfig.getPrivateKey(), "json", "GBK", aliPayConfig.getPublicKey(), "RSA2");
// 构造请求参数以调用接口
AlipayOpenAuthTokenAppRequest request = new AlipayOpenAuthTokenAppRequest();
AlipayOpenAuthTokenAppModel model = new AlipayOpenAuthTokenAppModel();
// 设置授权方式
model.setGrantType("authorization_code");
// 设置应用授权码
model.setCode(authCode);
// 设置刷新令牌
// model.setRefreshToken("201509BBdcba1e3347de4e75ba3fed2c9abebE36");
request.setBizModel(model);
// 第三方代调用模式下请设置app_auth_token
// request.putOtherTextParam("app_auth_token", "<-- 请填写应用授权令牌 -->");
AlipayOpenAuthTokenAppResponse response = alipayClient.execute(request);
System.out.println(response.getBody());
if (response.isSuccess()) {
System.out.println("调用成功");
sellerMapper.updateToken(response.getAppAuthToken(), response.getAuthAppId(), sellerId);
} else {
System.out.println("调用失败");
// sdk版本是"4.38.0.ALL"及以上,可以参考下面的示例获取诊断链接
// String diagnosisUrl = DiagnosisUtils.getDiagnosisUrl(response);
// System.out.println(diagnosisUrl);
}
}
6.4.dao层代码
void updateToken(@Param("appAuthToken") String appAuthToken, @Param("authAppId") String authAppId, @Param("sellerId") Long sellerId);
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace= "com.example.springboot.mapper.SellerMapper">
<update id="updateToken">
update seller set auth_token = #{appAuthToken}, app_id = #{authAppId}
where id = #{sellerId}
</update>
</mapper>
7.通过上面的演示我们已经获取到了app_auth_token并且存到了数据库中。现在思考一个问题,为什么能把app_auth_token存到数据库里呢,这么重要的参数不会一直更新嘛?
app_auth_token有效期说明
根据官方对app_auth_token的解释,可见把app_auth_token插入数据库是必要也是可行的。
有一种情况就是服务商向商家提供新的业务,需要商家再授予新的权限。商家再次扫码对应用重新授权,这个时候原来的app_auth_token会失效,生成一个新的app_auth_token。但是我们每次都能通过商家登录账号拿到商家ID,商家重新授权后也是可以马上对数据库进行更新的,只是把上面的流程重复走了一遍。
有了以上的铺垫我们就可以在代商家调用接口的时候通过设置入参app_auth_token完成业务需求啦。
request.putOtherTextParam("app_auth_token", authToken);
最后简单介绍一下我自己吧。我,是一名本科生。在暑期实习期间因为公司有支付业务的需求以服务商的身份学习了支付宝开放平台的相关文档,在实践中遇到了很多困惑也真正有了很多体会。发布这篇文章希望可以真正帮助到大家。同时如果有不严谨的地方也欢迎各位指正。
星光不负赶路人。不论梦想有多遥远,只要坚持不懈,就一定能实现自己的理想。加油!