1、准备工作
支付宝开放平台 (alipay.com)
支付文档
登录支付宝开放平台获取沙箱环境信息

控制台

沙箱应用信息

密钥信息

2、整合SpringBoot
依赖
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| <dependency> <groupId>com.alipay.sdk</groupId> <artifactId>alipay-sdk-java</artifactId> <version>4.22.110.ALL</version> </dependency>
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional> </dependency>
|
在application.yaml中配置
1 2 3 4 5
| alipay: appId: appPrivateKey: alipayPublicKey: notifyUrl:
|
配置AliPayConfig
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
| @Data @Component @ConfigurationProperties(prefix = "alipay") public class AliPayConfig {
private String appId;
private String appPrivateKey;
private String alipayPublicKey;
private String notifyUrl;
private final String GATEWAY_URL = "https://openapi-sandbox.dl.alipaydev.com/gateway.do";
private final String FORMAT = "JSON";
private final String CHARSET = "UTF-8";
private final String SIGN_TYPE = "RSA2";
}
|
新建AliPayController
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
| @Slf4j @RestController @RequestMapping("/alipay") @RequiredArgsConstructor public class AliPayController {
private final AliPayConfig aliPayConfig;
@GetMapping("/pay") public void pay(HttpServletResponse response) throws IOException { AlipayClient alipayClient = new DefaultAlipayClient(aliPayConfig.getGATEWAY_URL(), aliPayConfig.getAppId(), aliPayConfig.getAppPrivateKey(), aliPayConfig.getFORMAT(), aliPayConfig.getCHARSET(), aliPayConfig.getAlipayPublicKey(), aliPayConfig.getSIGN_TYPE());
AlipayTradeWapPayRequest alipayRequest = new AlipayTradeWapPayRequest();
alipayRequest.setNotifyUrl(aliPayConfig.getNotifyUrl());
alipayRequest.setBizContent("{" + " \"out_trade_no\":\"1000120\"," + " \"total_amount\":11," + " \"subject\":\"iphone11\"," +
" \"product_code\":\"FAST_INSTANT_TRADE_PAY\"" + " }");
String form = ""; try { form = alipayClient.pageExecute(alipayRequest).getBody(); } catch (AlipayApiException e) { e.printStackTrace(); } response.setContentType("text/html;charset=" + aliPayConfig.getCHARSET()); response.getWriter().write(form); response.getWriter().flush(); response.getWriter().close(); }
}
|
测试
访问 localhost:8881/alipay/pay

搭建内网穿透
内网穿透是为了在沙箱支付的时候,在支付完成后,想回调自己项目的页面,支付宝不可以直接访问你本机的路径,必须要通过生成的外网来回到自己的项目页面,这个时候就需要内网穿透。这里使用natapp。
官网:https://natapp.cn/
下载natapp

购买隧道(免费)

配置端口和本地项目环境一致

在natapp同级目录新建config.ini

填写自己的authtoken
1 2 3 4 5 6 7 8 9
|
[default] authtoken= clienttoken= log=none loglevel=ERROR http_proxy=
|
双击natapp.exe运行

配置notify异步回调
支付完成后第三方支付系统会主动通知支付结果,要实现主动通知需要在请求支付系统下单时传入NotifyUrl,这里有两个url:NotifyUrl和ReturnUrl,ReturnUrl是支付完成后支付系统携带支付结果重定向到ReturnUrl地址,NotifyUrl是支付完成后支付系统在后台定时去通知,使用NotifyUrl比使用ReturnUrl有保证。
1 2 3 4
| alipay: notifyUrl: http://wf5mtk.natappfree.cc/alipay/notify
|
编写回调接口(被动接收支付结果)

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
| @PostMapping("/notify") public String payNotify(HttpServletRequest request) throws AlipayApiException { if (request.getParameter("trade_status").equals("TRADE_SUCCESS")) { log.info("=========支付宝异步回调============"); Map<String, String> params = new HashMap<>(); Map<String, String[]> requestParams = request.getParameterMap(); for (String name : requestParams.keySet()) { params.put(name, request.getParameter(name)); }
String outTradeNo = params.get("out_trade_no"); String gmtPayment = params.get("gmt_payment"); String alipayTradeNo = params.get("trade_no"); String sign = params.get("sign"); String content = AlipaySignature.getSignCheckContentV1(params); boolean checkSignature = AlipaySignature.rsa256CheckContent(content, sign, aliPayConfig.getAlipayPublicKey(), "UTF-8");
if (checkSignature) { System.out.println("交易名称: " + params.get("subject")); System.out.println("交易状态: " + params.get("trade_status")); System.out.println("支付宝交易凭证号: " + params.get("trade_no")); System.out.println("商户订单号: " + params.get("out_trade_no")); System.out.println("交易金额: " + params.get("total_amount")); System.out.println("买家在支付宝唯一id: " + params.get("buyer_id")); System.out.println("买家付款时间: " + params.get("gmt_payment")); System.out.println("买家付款金额: " + params.get("buyer_pay_amount"));
} }
return "success"; }
|
测试回调
访问 localhost:8881/alipay/pay
主动查询支付结果
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
| AlipayClient alipayClient = new DefaultAlipayClient(GATEWAY_URL, aliPayConfig.getAppId(), aliPayConfig.getAppPrivateKey(), FORMAT, CHARSET, aliPayConfig.getAlipayPublicKey(), SIGN_TYPE); AlipayTradeQueryRequest request = new AlipayTradeQueryRequest(); JSONObject bizContent = new JSONObject(); bizContent.put("out_trade_no", 10001230);
request.setBizContent(bizContent.toString()); AlipayTradeQueryResponse response = null; try { response = alipayClient.execute(request); if (!response.isSuccess()) { XczxPlusException.cast("请求支付查询查询失败"); } else { System.out.println("调用成功"); }
} catch (AlipayApiException e) { log.error("请求支付宝查询支付结果异常:{}", e.getMessage(), e); XczxPlusException.cast("请求支付查询支付结果异常"); } String body = response.getBody();
Map resultMap = JSON.parseObject(body, Map.class); Map map = (Map) resultMap.get("alipay_trade_query_response");
|
项目工程结构

4、附
生成二维码组件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| <dependency> <groupId>com.google.zxing</groupId> <artifactId>core</artifactId> <version>3.3.3</version> </dependency>
<dependency> <groupId>com.google.zxing</groupId> <artifactId>javase</artifactId> <version>3.3.3</version> </dependency> <dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-lang3</artifactId> </dependency>
|
工具类
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
| public class QRCodeUtil {
public String createQRCode(String content, int width, int height) throws IOException { String resultImage = ""; if (!StringUtils.isEmpty(content)) { ServletOutputStream stream = null; ByteArrayOutputStream os = new ByteArrayOutputStream(); @SuppressWarnings("rawtypes") HashMap<EncodeHintType, Comparable> hints = new HashMap<>(); hints.put(EncodeHintType.CHARACTER_SET, "utf-8"); hints.put(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.M); hints.put(EncodeHintType.MARGIN, 1);
try { QRCodeWriter writer = new QRCodeWriter(); BitMatrix bitMatrix = writer.encode(content, BarcodeFormat.QR_CODE, width, height, hints); BufferedImage bufferedImage = MatrixToImageWriter.toBufferedImage(bitMatrix); ImageIO.write(bufferedImage, "png", os);
resultImage = new String("data:image/png;base64," + EncryptUtil.encodeBase64(os.toByteArray())); return resultImage; } catch (Exception e) { e.printStackTrace(); throw new RuntimeException("生成二维码出错"); } finally { if (stream != null) { stream.flush(); stream.close(); } } } return null; }
public static void main(String[] args) throws IOException { QRCodeUtil qrCodeUtil = new QRCodeUtil();
System.out.println(qrCodeUtil.createQRCode("http://n2x659.natappfree.cc/orders/apipaytest", 200, 200)); }
}
|
运行会得到一个base63串
1
| data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMgAAADIAQAAAACFI5MzAAABQElEQVR42u2YPZKDMAyF5aFIuUfIUThafDSOwhEoUzC8fZKMySSbrVI8ZuICBX8uIvtZPxjeDfuSf8liPi7LFSgrzRTvV3XCKawXYLptFobviz6ZzB2xEfTjhyS9OwXB3A7jbMSngLOQ0I4v2AZf96wqTWJ9+9/dYEHSx2RYqfg/oqUgiX3nFBVfcCepcSbiJP67iwZ1G+5+Am7kyTzW9OcW/kRAX+QJ953+uCl8zO5PV5UsaffUp8rqP5+jiySJU8jtNxcNrysetCNK6A/V4lEQeU+xa0eZREE1tOTpFYod0VKXsKCqvRqMkW5pkza8Ggy3WgEuTvZcz0dcUBc+9MneL1DqkXjQz0eaZA1LqVtmzcMffTKPiPwz1mh2zkGyNwtT9kguTVI7LWv6ul7DCpOjX9iaGV66HDny/ZL1WfILfc/hMHLUpekAAAAASUVORK5CYII=
|
参数AliPay.java
1 2 3 4 5 6 7
| @Data public class AliPay { private String traceNo; private double totalAmount; private String subject; private String alipayTraceNo; }
|