1 开发前的准备
1.1 密钥的的生成
先去官网下载支付宝平台助手,下载地址:https://opendocs.alipay.com/open/291/105971 ,下载并安装平台助手,使用平台助手生成应用公钥
和应用私钥
。
1.2 创建应用配置账号信息
沙箱账号申请地址:https://openhome.alipay.com/platform/appDaily.htm
正式账号申请地址:https://open.alipay.com/platform/manageHome.htm
为了保证交易双方(商户和支付宝)的身份和数据安全,开发者在调用接口前,需要配置双方密钥,对交易数据进行双方校验。密钥包含应用私钥(APP_PRIVATE_KEY)
和应用公钥(APP_PUBLIC_KEY)
。生成密钥后,开发者需要在开放平台开发者中心进行密钥配置,配置完成后可以获取支付宝公钥(ALIPAY_PUBLIC_KEY)
应用在代码中,对请求内容进行签名。
注意:当设置了应用公钥后,才可以查看支付宝公钥
1.3 开发需要的参数
appid
: 应用ID
private_key
:由支付宝开发平台助手生成的应用私钥
alipay_public_key
:是支付宝公钥,支付宝开发平台助手生成的应用公钥
这里特别需要注意的是alipay_public_key
是蚂蚁金服开放平台获取的支付宝公钥,不是本地平台助手申请的应用公钥
,不要弄错了,否则会签名失败
2 支付介绍
2.1 支付逻辑
用户在商户系统下单
商户系统调用支付宝的支付接口,发起支付请求
用户登录,选择支付渠道,输入支付密码
支付宝get请求returnUrl
, 返回同步通知参数
支付宝post请求notifyUrl
,返回异步通知参数
商户可以通过 交易查询接口查询交易状态
如果用户或者商户想退款,可以发起退款请求
如果退款可以调用退款查询接口,查询退款信息
下载对账单
2.2 关于支付宝中同步通知和异步通知的理解
在调用支付宝接口的时候,确认支付以后我们需要请求两个通知接口:同步通知(returnUrl
)和异步通知(notifyUrl
)。刚开始看文档的时候也不是很清楚这两个接口有什么区别,从流程图里面描述到的是,同步通知(returnUrl
)是GET请求,“支付是否成功以异步通知为准”。异步通知(notifyUrl
)是POST请求,但是图里面还是说了一句“支付是否成功以查询接口返回为准”。所以最终支付是否成功还是要看查询接口的返回结果,那同步通知和异步通知有什么用呢?
个人的理解是:只要同步通知和异步通知返回成功一般情况下其实已经可以认为支付成功了,我们就已经可以进行下一步的操作了。同步通知用于前端界面及时反馈给用户,告诉用户支付操作已经完成,然后让前端页面跳转的需要跳转的页面;异步通知用于后台业务逻辑的处理。当然为了保险,后面最好还是调用查询接口,对支付账单进行核对。
需要注意的问题:
returnUrl
和returnUrl
这两个的接口地址必须放在公网上,需要外网能访问到,否则支付宝是调不到这两个接口的。本地测试可以使用 natapp 做内外网穿透。
- 经过测试异步通知和同步通知没有很明确的先后顺序,同步通知并不一定在异步通知之前被调用。
2.3 重点看官方的Demo
其实官方的Demo已经写的很详细了,每个接口的调用已经给出了详细的演示。所以后面的代码基本也是照着官方的Demo来的。
电脑网站支付Demo下载:https://opendocs.alipay.com/open/270/106291
3 支付接入代码
3.1 添加依赖
这里使用的是SpringBoot
,先在maven里引入官方的依赖:
1 2 3 4 5 6 7
|
<dependency> <groupId>com.alipay.sdk</groupId> <artifactId>alipay-sdk-java</artifactId> <version>4.9.124.ALL</version> </dependency>
|
3.2 添加配置类
接着添加已经配置类:
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
| package com.foochane.awpay.test.config;
public class MyAliPayConfig { public static final String natUrl = "http://znznca.natappfree.cc";
public static String app_id = "2016101800717168";
public static String merchant_private_key = "xxxxxxxxxxxxxx";
public static String alipay_public_key = "xxxxxxxxx";
public static String notify_url = natUrl + "/ali/pay/notify";
public static String return_url = natUrl + "/ali/pay/return";
public static String sign_type = "RSA2";
public static String charset = "utf-8";
public static String gatewayUrl = "https://openapi.alipaydev.com/gateway.do";
}
|
3.3 编写支付代码
然后就可以在Controller层编写支付代码了,这里只是为了演示,所以就之编写controller层。
这里引入的是电脑网站支付,代码流程如下:
- 创建
AlipayClient
类
- 设置请求参数(这里参数是写死的)
- 发起支付请求,获取支付表单
- 直接将支付表单输出到页面
具体代码如下:
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 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85
| package com.foochane.awpay.test.controller;
import com.alipay.api.AlipayApiException; import com.alipay.api.AlipayClient; import com.alipay.api.DefaultAlipayClient; import com.alipay.api.internal.util.AlipaySignature; import com.alipay.api.request.*; import com.foochane.awpay.test.config.MyAliPayConfig; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.ResponseBody;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.util.HashMap; import java.util.Iterator; import java.util.Map;
@RestController public class AlipayController {
@ResponseBody @RequestMapping(value = "ali/pay/order/create",method = RequestMethod.GET) public void pay (HttpServletRequest httpRequest, HttpServletResponse httpResponse) throws IOException { AlipayClient alipayClient = new DefaultAlipayClient( MyAliPayConfig.gatewayUrl, MyAliPayConfig.app_id, MyAliPayConfig.merchant_private_key, "json", MyAliPayConfig.charset, MyAliPayConfig.alipay_public_key, MyAliPayConfig.sign_type);
String out_trade_no = "P"+System.currentTimeMillis(); String total_amount = "80"; String subject = "商品购买"; String body = "购买商品的描述信息"; String timeout_express = "10m"; AlipayTradePagePayRequest alipayRequest = new AlipayTradePagePayRequest(); alipayRequest.setReturnUrl(MyAliPayConfig.return_url); alipayRequest.setNotifyUrl(MyAliPayConfig.notify_url); 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\"}");
String form = ""; try { form = alipayClient.pageExecute(alipayRequest).getBody(); } catch (AlipayApiException e) { e.printStackTrace(); }
System.out.println("*************************** 支付请求成功 *******************************"); System.out.println("返回表单:"); System.out.println(form); System.out.println("*************************** 支付请求成功 *******************************");
httpResponse.setContentType( "text/html;charset=" + MyAliPayConfig.charset); httpResponse.getWriter().write(form); httpResponse.getWriter().flush(); httpResponse.getWriter().close();
} }
|
4 同步通知代码
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
|
@ResponseBody @RequestMapping("ali/pay/return") public String returnUrl(HttpServletRequest httpRequest, HttpServletResponse httpResponse) throws Exception {
System.out.println("支付成功, 进入同步通知接口...");
Map<String, String> params = new HashMap<String, String>(); Map<String, String[]> requestParams = httpRequest.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] + ","; }
params.put(name, valueStr); }
boolean signVerified = AlipaySignature.rsaCheckV1(params, MyAliPayConfig.alipay_public_key, MyAliPayConfig.charset, MyAliPayConfig.sign_type);
if (signVerified) {
System.out.println("****************** 支付宝同步通知成功 ******************"); System.out.println("同步通知返回参数:" + params.toString()); System.out.println("****************** 支付宝同步通知成功 ******************");
} else { System.out.println("支付, 验签失败..."); }
return params.toString(); }
|
5 异步通知代码
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 69 70 71 72 73 74 75 76 77 78 79
|
@RequestMapping(value = "/ali/pay/notify") @ResponseBody public String notify(HttpServletRequest request, HttpServletRequest response) throws Exception {
System.out.println("支付成功, 进入异步通知接口...");
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] + ","; }
params.put(name, valueStr); }
boolean signVerified = AlipaySignature.rsaCheckV1(params, MyAliPayConfig.alipay_public_key, MyAliPayConfig.charset, MyAliPayConfig.sign_type);
if (signVerified) {
System.out.println("********************** 支付宝异步通知成功 **********************"); System.out.println("异步通知返回参数:" + params.toString()); System.out.println("********************** 支付宝异步通知成功 **********************");
if (params.get("trade_status").equals("TRADE_FINISHED")){
System.out.println("执行退款相关业务"); }else if (params.get("trade_status").equals("TRADE_SUCCESS")) {
System.out.println("执行相关业务"); }
} else { System.out.println("支付, 验签失败..."); }
return "success"; }
|
6 订单查询代码
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
|
@ResponseBody @RequestMapping(value = "/ali/pay/order/query") public String query(String out_trade_no,String trade_no){
System.out.println("订单查询...");
AlipayClient alipayClient = new DefaultAlipayClient(MyAliPayConfig.gatewayUrl, MyAliPayConfig.app_id, MyAliPayConfig.merchant_private_key, "json", MyAliPayConfig.charset, MyAliPayConfig.alipay_public_key, MyAliPayConfig.sign_type);
AlipayTradeQueryRequest alipayRequest = new AlipayTradeQueryRequest();
alipayRequest.setBizContent("{\"out_trade_no\":\""+ out_trade_no +"\","+"\"trade_no\":\""+ trade_no +"\"}");
String result = null; try { result = alipayClient.execute(alipayRequest).getBody(); } catch (AlipayApiException e) { e.printStackTrace(); }
System.out.println("订单查询结果:" +result);
return result; }
|
8 退款代码代码
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
|
@ResponseBody @RequestMapping(value = "/ali/refund/order/create") public String refund(String out_trade_no,String trade_no,String refund_amount, String refund_reason, String out_request_no){
System.out.println("退款...");
AlipayClient alipayClient = new DefaultAlipayClient(MyAliPayConfig.gatewayUrl, MyAliPayConfig.app_id, MyAliPayConfig.merchant_private_key, "json", MyAliPayConfig.charset, MyAliPayConfig.alipay_public_key, MyAliPayConfig.sign_type);
AlipayTradeRefundRequest alipayRequest = new AlipayTradeRefundRequest();
alipayRequest.setBizContent("{\"out_trade_no\":\""+ out_trade_no +"\"," + "\"trade_no\":\""+ trade_no +"\"," + "\"refund_amount\":\""+ refund_amount +"\"," + "\"refund_reason\":\""+ refund_reason +"\"," + "\"out_request_no\":\""+ out_request_no +"\"}");
String result = null; try { result = alipayClient.execute(alipayRequest).getBody(); } catch (AlipayApiException e) { e.printStackTrace(); }
System.out.println("退款结果:" + result);
return result; }
|
9 退款查询代码
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
|
@ResponseBody @RequestMapping(value = "/ali/refund/order/query") public String refundQuery(String out_trade_no, String trade_no, String out_request_no){ System.out.println("退款查询......"); AlipayClient alipayClient = new DefaultAlipayClient(MyAliPayConfig.gatewayUrl, MyAliPayConfig.app_id, MyAliPayConfig.merchant_private_key, "json", MyAliPayConfig.charset, MyAliPayConfig.alipay_public_key, MyAliPayConfig.sign_type);
AlipayTradeFastpayRefundQueryRequest alipayRequest = new AlipayTradeFastpayRefundQueryRequest(); alipayRequest.setBizContent("{\"out_trade_no\":\""+ out_trade_no +"\"," +"\"trade_no\":\""+ trade_no +"\"," +"\"out_request_no\":\""+ out_request_no +"\"}");
String result = null; try { result = alipayClient.execute(alipayRequest).getBody(); } catch (AlipayApiException e) { e.printStackTrace(); }
System.out.println("退款查询结果:"+result);;
return result; }
|
10 订单关闭代码
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
|
@ResponseBody @RequestMapping(value = "/ali/order/close") public String close(String out_trade_no, String trade_no){ System.out.println("交易关闭.....");
AlipayClient alipayClient = new DefaultAlipayClient(MyAliPayConfig.gatewayUrl, MyAliPayConfig.app_id, MyAliPayConfig.merchant_private_key, "json", MyAliPayConfig.charset, MyAliPayConfig.alipay_public_key, MyAliPayConfig.sign_type);
AlipayTradeCloseRequest alipayRequest = new AlipayTradeCloseRequest(); alipayRequest.setBizContent("{\"out_trade_no\":\""+ out_trade_no +"\"," +"\"trade_no\":\""+ trade_no +"\"}");
String result = null; try { result = alipayClient.execute(alipayRequest).getBody(); } catch (AlipayApiException e) { e.printStackTrace(); }
System.out.println("交易关闭结果:"+result);
return result; }
|
11 完整的代码地址
代码地址:https://github.com/foochane/aw-pay
这个代码只是简单的写controller层的代码,只是为了快速体验一下支付宝的接口调用,后期将进行具体的完善。