准备工作:
支付宝开放平台注册认证自己的账号、去创建自己的应用(准备自己产品的相关图片、自己产品的宣传官网)、让自己的产品签约支付服务(APP、WEB),整理完成就可以去相应的编码了!
线上环境如果遇到问题,请先创建向导:
https://opensupport.alipay.com/support/codelab/home.htm
非常好用!
介绍编码前的准备工作
- APPID:每个应用都有自己的应用公钥!
- 应用私钥:由 支付宝开放平台开发助手 登录自己的账号 - 生成密钥得到!
- 支付宝公钥:由 支付宝开放平台助手 生成密钥 与 自己创建的应用绑定后,就可以得到!
- 应用公钥:没啥用! 由 支付宝开放平台开发助手 登录自己的账号 - 生成密钥得到!
编码
Maven依赖!
<!-- Hutool 工具 -->
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>5.5.7</version>
</dependency>
<!-- 支付宝SDK -->
<dependency>
<groupId>com.alipay.sdk</groupId>
<artifactId>alipay-sdk-java</artifactId>
<version>4.16.2.ALL</version>
</dependency>
创建一个SpringBean 将来用于注入
说明:生产环境与沙箱环境不一样的地方就是 服务地址 SERVER_URL_Test 是沙箱环境地址!APPID、 应用私钥、支付宝公钥都会变
里面的密钥需要生成好,再来写相关的代码!
private final String APP_ID = "你的AppID";
// 服务地址
private final String SERVER_URL = "https://openapi.alipay.com/gateway.do";
private final String SERVER_URL_Test = "https://openapi.alipaydev.com/gateway.do";
// 应用私钥 在"支付宝开放平台开发助手"里生成,然后更换支付宝的“应用公钥”即可,这样“应用私钥”就能生效
private final String APP_PRIVATE_KEY = "你的应用私钥";
// 支付宝公钥
private final String ALIPAY_PUBLIC_KEY = "你的支付宝公钥";
private final String CHARSET = "utf-8";
@Bean
public AlipayClient alipayClient() {
//实例化客户端
AlipayClient alipayClient = new DefaultAlipayClient(SERVER_URL, APP_ID, APP_PRIVATE_KEY, "json", CHARSET, ALIPAY_PUBLIC_KEY, "RSA2");
return alipayClient;
}
APP支付
// 注入 alipayClient
@Autowired
AlipayClient alipayClient;
/**
* 如果需要特别的要求,请去支付宝开放平台文档查看一些自定义参数 https://opendocs.alipay.com/apis/api_1/alipay.trade.app.pay#%E8%AF%B7%E6%B1%82%E5%8F%82%E6%95%B0
*
* 完全可以将String的Body作为Json直接返回给APP,APP可以唤起支付宝支付。如果用支付宝单独请求这个接口,只会得到Body字符串,不会唤起支付宝支付.
* 我们能做的就是给APP这个Body,由APP自己唤起手机系统底层调用支付宝,完成支付等!
* 为了更好的复习Servlet,练练手,还是用Servlet返回,注意使用httpServletResponse 不能加入@ResponseBody,否则会报错!
* Uniapp不支持支付宝沙箱环境
* @param httpServletResponse
*/
@GetMapping("/doapppay")
public void doAppPay(HttpServletResponse httpServletResponse) {
// 实例化具体API对应的request类,类名称和接口名称对应,当前调用接口名称:alipay.trade.app.pay
AlipayTradeAppPayRequest request = new AlipayTradeAppPayRequest();
// SDK已经封装掉了公共参数,这里只需要传入业务参数。以下方法为sdk的model入参方式(model和biz_content同时存在的情况下取biz_content)。
AlipayTradeAppPayModel model = new AlipayTradeAppPayModel();
// 自定义参数 依次是订单描述、订单标题、商户平台订单号、超时时间、总金额、公共回传参数(会返回到回调request里)、商家和支付宝签约的产品码
model.setBody("订单描述");
model.setSubject("订单标题");
model.setOutTradeNo(new Snowflake(1, 1).nextIdStr());
model.setTimeoutExpress("30m");
model.setTotalAmount("0.01");
model.setPassbackParams(UUID.fastUUID().toString());
model.setProductCode("QUICK_MSECURITY_PAY");
// 将自定义参数绑定到请求上
request.setBizModel(model);
// 设置回调地址 你可以参考我的文章内网穿透!简单作为自己的内网穿透!
request.setNotifyUrl("http://740969606.vaiwan.com/emailclock/pay/reback");
try {
// 这里和普通的接口调用不同,APP支付使用的是sdkExecute
AlipayTradeAppPayResponse response = alipayClient.sdkExecute(request);
// 指定ContentType
httpServletResponse.setContentType("text/html;charset=" + CHARSET);
// 给APP写入数据
if (response.isSuccess()) {
httpServletResponse.getWriter().write(response.getBody());
System.out.println("调用成功成功");
} else {
httpServletResponse.getWriter().write("抱歉APP支付唤起失败!请去Java后台查看相关报错信息!");
System.out.println("调用失败,原因:" + response.getMsg() + "," + response.getSubMsg());
}
httpServletResponse.getWriter().flush();
httpServletResponse.getWriter().close();
} catch (Exception e) {
e.printStackTrace();
// TODO 这里你可做任何事情
}
}
WEB支付
// 注入 alipayClient
@Autowired
AlipayClient alipayClient;
/**
* 官方文档:https://opendocs.alipay.com/apis/api_1/alipay.trade.page.pay#%E8%AF%B7%E6%B1%82%E5%8F%82%E6%95%B0
* 扫码支付,将会返回一个二维码地址,只需要让二维码跳转Java返回的地址即可!
* 回调地址会根据沙箱中的配置为准!
*
* @return 返回前端 制作的二维码内容 填写这个URL,支付宝扫码,就跳到支付页面了!
*/
@GetMapping("/dowebpay")
@ResponseBody
public String doWebPay() {
// 实例化具体API对应的request类,类名称和接口名称对应,当前调用接口名称:alipay.trade.app.pay
AlipayTradePrecreateRequest request = new AlipayTradePrecreateRequest();
// SDK已经封装掉了公共参数,这里只需要传入业务参数。以下方法为sdk的model入参方式(model和biz_content同时存在的情况下取biz_content)。
AlipayTradePrecreateModel model = new AlipayTradePrecreateModel();
// 自定义参数 以此是 订单信息、订单描述、自己平台订单号、交易超时时间、订单金额、公共回传参数(会返回到回调request里)
model.setSubject("卫龙辣条");
model.setBody("中国科学院小卖部");
model.setOutTradeNo(new Snowflake(1, 1).nextIdStr());
model.setTimeoutExpress("30m");
model.setTotalAmount("1000");
model.setPassbackParams(UUID.fastUUID().toString());
// 将自定义参数绑定到请求上
request.setBizModel(model);
// 设置回调地址 你可以参考我的文章内网穿透!简单作为自己的内网穿透!
request.setNotifyUrl("http://740969606.vaiwan.com/emailclock/pay/reback");
// 发起请求
try {
//这里和普通的接口调用不同,使用的是execute
AlipayTradePrecreateResponse response = alipayClient.execute(request);
if (response.isSuccess()) {
System.out.println("调用成功:" + response.getMsg() + "," + response.getSubMsg());
String qrCodeUrl = response.getQrCode();
return qrCodeUrl;
} else {
System.out.println("调用失败,原因:" + response.getMsg() + "," + response.getSubMsg());
return "老铁,下单失败,报错信息请在后端控制台查看";
}
} catch (Exception e) {
e.printStackTrace();
}
return "null";
}
支付宝回调接收(不确定支付宝回调的次数与间隔时间)
建议参考支付宝的回调机制:https://opendocs.alipay.com/open/58/103594
我们回调接受的时候,是请求支付的时候添加的一个自定义参数: notify_url,如果没有这个参数,将不会自己的触发回调。
支付宝官方文章中说到,重定向,也就是自定义参数:return_url,支付成功后,会自动跳转到这个网页(也就是发请求)! 如果没有这个参数,将不会自己的触发回调 。
/**
* 支付宝支付成功回调
* 不同的支付方式回调参数不完全相同,如果查看APP响应参数,请求查看响应的官方文档
* 例如:APP响应的参数有:https://opendocs.alipay.com/apis/api_1/alipay.trade.app.pay#%E5%93%8D%E5%BA%94%E5%8F%82%E6%95%B0
*
* @param httpServletRequest
*/
@RequestMapping("/reback")
@ResponseBody
public void payReback(HttpServletRequest httpServletRequest) {
System.out.println("回调来了,当前时间是:" + new Date());
// 最终的结果集
Map<String, String> params = new HashMap<>();
// 尚未整理的结果集合
Map<String, String[]> requestParams = httpServletRequest.getParameterMap();
for (Iterator<String> iter = requestParams.keySet().iterator(); iter.hasNext(); ) {
String name = iter.next();
String[] values = requestParams.get(name);
String valueStr = "";
for (int i = 0; i < values.length; i++) {
valueStr = (i == values.length - 1) ? valueStr + values[i] : valueStr + values[i] + ",";
}
params.put(name, valueStr);
}
boolean verifyResult = false;
try {
// 验证支付是否成功
verifyResult = AlipaySignature.rsaCheckV1(params, ALIPAY_PUBLIC_KEY, "UTF-8", "RSA2");
if (verifyResult) {
// TODO 请在这里加上商户的业务逻辑程序代码 异步通知可能出现订单重复通知 需要做去重处理
params.forEach((k, v) -> {
if (k.equals("out_trade_no")) {
System.out.println("支付成功的自己平台的订单号:" + v);
}
if (k.equals("total_amount")) {
System.out.println("收入的金额是:" + v + "元");
}
});
System.out.println("notify_url 验证成功succcess");
} else {
System.out.println("notify_url 验证失败");
// TODO 执行失败的相关逻辑
}
} catch (Exception e) {
e.printStackTrace();
}
}
这里给出一个线上版本的一个测试APP支付的回调参数,以便于参考!也可作为商户订单号 或者 支付宝订单号查询(红字展示),下面有查询代码
gmt_create : 2021-08-31 19:28:29
charset : utf-8
seller_email : 这是商家支付宝号我隐藏了
subject : 银卡充值
body : 56081689,1
buyer_id : 用户id号 2088开头的 我隐藏了
invoice_amount : 0.01
notify_id : 2021083100222192830050911412782255
fund_bill_list : [{"amount":"0.01","fundChannel":"ALIPAYACCOUNT"}]
notify_type : trade_status_sync
trade_status : TRADE_SUCCESS
receipt_amount : 0.01
app_id : 这个应用ID这里我隐藏了!
buyer_pay_amount : 0.01
seller_id : 2088241356978595
gmt_payment : 2021-08-31 19:28:29
notify_time : 2021-08-31 20:54:48
passback_params : f3551c9e-b888-4d07-9ffd-cf7bc8f6de7f
version : 1.0
out_trade_no : 20210831192824882346104249917440
total_amount : 0.01
trade_no : 2021083122001450911439518753
auth_app_id : 这个应用ID这里我隐藏了!
buyer_logon_id : 614***@qq.com
point_amount : 0.00
查询订单状态接口 (上面有参数,自己查询)
/**
* 交易查询 可以单独查询一个订单号中的所有信息 具体需要什么自己去变动
* 这里是APP支付的响应结果 https://opendocs.alipay.com/apis/028xq9#%E5%93%8D%E5%BA%94%E5%8F%82%E6%95%B0
* 不清楚WEB支付是否一样,建议去查看WEB的支付的查询接口文档
*
* 传入任意一个参数都可以查询,但是一个都不传入,报错!
* @param outTradeNo 商户订单号
* @param tradeNo 支付宝订单号
* @return
*/
@RequestMapping(value = "/query")
@ResponseBody
public String tradeQuery(@RequestParam(required = false, name = "outTradeNo") String outTradeNo,
@RequestParam(required = false, name = "tradeNo") String tradeNo) {
try {
AlipayTradeQueryRequest request = new AlipayTradeQueryRequest();
AlipayTradeQueryModel model = new AlipayTradeQueryModel();
if (StringUtils.isNotEmpty(outTradeNo)) {
model.setOutTradeNo(outTradeNo);
}
if (StringUtils.isNotEmpty(tradeNo)) {
model.setTradeNo(tradeNo);
}
request.setBizModel(model);
AlipayTradeQueryResponse execute = alipayClient.execute(request);
String info = "商户订单号:" + execute.getOutTradeNo() +
"<br>支付宝订单号:" + execute.getTradeNo() +
"<br>订单状态:" + execute.getTradeStatus() +
"<br>支付用户ID:" + execute.getBuyerUserId() +
"<br>支付用户名:" + execute.getBuyerUserName() +
"<br>订单支付时间(不确定订单支付时间):" + execute.getSendPayDate() +
"<br>订单支付金额:" + execute.getTotalAmount();
return info;
} catch (AlipayApiException e) {
e.printStackTrace();
}
return "啥也没查到!";
}
测试:http://127.0.0.1:18888/emailclock/pay/query?tradeNo=2021083122001450911439518753
其实说白了,一个支付系统不可能这么简单的。必须要有自己的消息队列来辅助订单的有效性!
特殊说明:
上述文章均是作者实际操作后产出。烦请各位,请勿直接盗用!转载记得标注原文链接:www.zanglikun.com
第三方平台不会及时更新本文最新内容。如果发现本文资料不全,可访问本人的Java博客搜索:标题关键字。以获取最新全部资料 ❤
第三方平台不会及时更新本文最新内容。如果发现本文资料不全,可访问本人的Java博客搜索:标题关键字。以获取最新全部资料 ❤
评论(0)