码云地址:
前言:
今天在一个公众号看到了这样一篇文章,写的是:SpringBoot接入支付宝,看了那篇文章发现写的比较一般,本着求知的心态,今天想着接触下接入支付宝,然后写个博客~
参考博客:
springboot接入支付宝支付-新版SDK(2020/11/03)
使用到的支付宝开发网址:
下面需要使用到的链接或者秘钥我都会用红色标注出来
一、环境搭建
1、应用公钥/私钥的获取
进入到支付宝开发助手-->MiniU研发工作台,以前的话应该是让下载软件什么的获取秘钥,现在可以直接通过网页进行获取了
复制下来生成的公钥跟私钥,记到小本子上面~
2、APPID、支付宝公钥的获取
进入到支付宝开放平台-沙箱环境,复制我们刚刚生成的公钥,放在对应的框框中即可生成"支付宝公钥"
复制你的APPID
复制生成的支付宝公钥~
记得复制下来支付宝提供的测试账号,下面测试要用:
3、内网穿透设置
我使用的是Windows环境,这里我就下载Windows版本的ngrok,这是国外的网站有的小伙伴下载比较慢,可以使用蓝奏云下载:https://wws.lanzoui.com/izXZZsuw9wb
下载完成之后,使用命令行进入到解压后的目录,复制内网穿透的链接:注意先别把窗口关闭了。
1、进入目录:鼠标放到导航栏上面,输入cmd,即可快速进入 2、启动应用:ngrok http 端口
二、代码整理
新建SpringBoot项目(只需添加SpringBootWeb依赖即可),项目结构如下:
pom.xml
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>com.alipay.sdk</groupId> <artifactId>alipay-easysdk</artifactId> <version>2.1.0</version> </dependency>
application.properties
## 端口 server.port=8080 ## 支付宝配置 # 应用ID alipay.appId=把你的应用id填到这里 # 应用私钥 alipay.privateKey=把你的应用私钥填到这里 # 支付宝公钥,注意不是生成的应用公钥 alipay.publicKey=把你的支付宝公钥填到这里 #支付网关配置,这一项是写死的,正式环境是openapi.alipay.com alipay.gateway=openapi.alipaydev.com # 支付宝前台跳转地址 alipay.returnUrl=生成的外网穿透链接/return_url.html # 支付宝后台通知地址 alipay.notifyUrl=生成的外网穿透链接/api/alipay/notify_url # 支付宝前台手机网页支付中途取消跳转地址 alipay.errorUrl=生成的外网穿透链接/error_url.html
config/AlipayConfig.java
/** * @author zhangzhixi * @version 1.0 * @date 2021-8-19 15:02 */ import com.alipay.easysdk.factory.Factory; import com.alipay.easysdk.kernel.Config; import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.ApplicationArguments; import org.springframework.boot.ApplicationRunner; import org.springframework.stereotype.Component; @Component public class AlipayConfig implements ApplicationRunner { // 应用id @Value("${alipay.appId}") private String appId; // 私钥 @Value("${alipay.privateKey}") private String privateKey; // 公钥 @Value("${alipay.publicKey}") private String publicKey; // 支付宝网关 @Value("${alipay.gateway}") private String gateway; // 支付成功后的接口回调地址,不是回调的友好页面,不要弄混了 @Value("${alipay.notifyUrl}") private String notifyUrl; /** * 项目初始化事件 * */ @Override public void run(ApplicationArguments args) throws Exception { //初始化支付宝SDK Factory.setOptions(getOptions()); System.out.println("**********支付宝SDK初始化完成**********"); } private Config getOptions() { //这里省略了一些不必要的配置,可参考文档的说明 Config config = new Config(); config.protocol = "https"; config.gatewayHost = this.gateway; config.signType = "RSA2"; config.appId = this.appId; // 为避免私钥随源码泄露,推荐从文件中读取私钥字符串而不是写入源码中 config.merchantPrivateKey = this.privateKey; // 注:如果采用非证书模式,则无需赋值上面的三个证书路径,改为赋值如下的支付宝公钥字符串即可 config.alipayPublicKey = this.publicKey; // 可设置异步通知接收服务地址(可选) config.notifyUrl = notifyUrl; return config; } }View Code
controller/AlipayController.java
/** * @author zhangzhixi * @version 1.0 * @date 2021-8-19 15:06 */ import com.alipay.easysdk.factory.Factory; import com.zhixi.aliyunpay.service.AlipayService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.*; import javax.servlet.http.HttpServletRequest; import java.util.HashMap; import java.util.Map; @RestController @RequestMapping("/api/alipay") public class AlipayController { private final AlipayService alipayService; @Autowired public AlipayController(AlipayService alipayService) { this.alipayService = alipayService; } /** * 支付宝电脑网页支付 * @param subject: 订单名称 * @param total: 金额 * @return java.lang.String */ @PostMapping("/page") public String page(String subject, String total) { subject = "测试支付"; total = "1000"; return alipayService.page(subject, total); } /** * 支付宝手机网页支付 * @param subject: 订单名称 * @param total: 金额 * @return java.lang.String */ @PostMapping("/wap") public String wap(String subject, String total) { subject = "测试支付"; total = "1000"; return alipayService.wap(subject, total); } /** * @param request: 请求 * @return java.lang.String * @description: 支付宝异步回调 * @date: 2020/11/3 */ @PostMapping("/notify_url") public String notify_url(HttpServletRequest request) throws Exception { if (request.getParameter("trade_status").equals("TRADE_SUCCESS")) { System.out.println("=========支付宝异步回调========"); Map<String, String> params = new HashMap<>(); Map<String, String[]> requestParams = request.getParameterMap(); for (String name : requestParams.keySet()) { params.put(name, request.getParameter(name)); // System.out.println(name + " = " + request.getParameter(name)); } // 支付宝验签 if (Factory.Payment.Common().verifyNotify(params)) { // 验签通过 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"; } /** * @param outTradeNo: 商家订单号 * @param refundAmount: 退款金额(不能大于交易金额) * @return java.lang.String * @description: 支付宝退款 * @date: 2020/11/3 */ @PostMapping("/refund") public String refund(String outTradeNo, String refundAmount) { return alipayService.refund(outTradeNo, refundAmount); } }View Code
service/AlipayService.java
/** * @author zhangzhixi * @version 1.0 * @date 2021-8-19 15:04 */ public interface AlipayService { /** * @description: 支付宝电脑网页支付 * @param subject: 订单名称 * @param total: 金额 * @date: 2020/11/3 * @return java.lang.String */ String page(String subject, String total); /** * @description: 支付宝手机网页支付 * @param subject: 订单名称 * @param total: 金额 * @date: 2020/11/3 * @return java.lang.String */ String wap(String subject, String total); /** * @description: 支付宝退款 * @param outTradeNo: 商家订单号 * @param refundAmount: 退款金额(不能大于交易金额) * @date: 2020/11/3 * @return java.lang.String */ String refund(String outTradeNo, String refundAmount); }View Code
service/impl/AlipayService.java
/** * @author zhangzhixi * @version 1.0 * @date 2021-8-19 15:04 */ import com.alipay.easysdk.factory.Factory; import com.alipay.easysdk.payment.common.models.AlipayTradeRefundResponse; import com.alipay.easysdk.payment.page.models.AlipayTradePagePayResponse; import com.alipay.easysdk.payment.wap.models.AlipayTradeWapPayResponse; import com.zhixi.aliyunpay.service.AlipayService; import com.zhixi.aliyunpay.util.OrderUtil; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Service; @Service public class AlipayServiceImpl implements AlipayService { // 支付成功后要跳转的页面 @Value("${alipay.returnUrl}") private String returnUrl; // 支付宝前台手机网页支付中途取消跳转地址 @Value("${alipay.errorUrl}") private String errorUrl; @Override public String page(String subject, String total) { try { AlipayTradePagePayResponse response = Factory.Payment // 选择电脑网站 .Page() // 调用支付方法(订单名称, 商家订单号, 金额, 成功页面) .pay(subject, OrderUtil.getOrderNo(), total, returnUrl); return response.body; } catch (Exception e) { e.printStackTrace(); } return null; } @Override public String wap(String subject, String total) { try { AlipayTradeWapPayResponse response = Factory.Payment //选择手机网站 .Wap() // 调用支付方法(订单名称, 商家订单号, 金额, 中途退出页面, 成功页面) .pay(subject, OrderUtil.getOrderNo(), total, errorUrl, returnUrl); return response.body; } catch (Exception e) { e.printStackTrace(); } return null; } @Override public String refund(String outTradeNo, String refundAmount) { try { AlipayTradeRefundResponse response = Factory.Payment .Common() // 调用交易退款(商家订单号, 退款金额) .refund(outTradeNo, refundAmount); if (response.getMsg().equals("Success")) { return "退款成功"; } } catch (Exception e) { e.printStackTrace(); } return "退款失败"; } }View Code
util/OrderUtil.java
/** * @author zhangzhixi * @version 1.0 * @date 2021-8-19 15:03 */ import java.time.Instant; import java.time.LocalDateTime; import java.time.ZoneOffset; import java.time.format.DateTimeFormatter; public class OrderUtil { /** * 根据时间戳生成订单号 */ public static String getOrderNo() { DateTimeFormatter df = DateTimeFormatter.ofPattern("yyyyMMddHHmmssSSS"); LocalDateTime localDateTime = Instant.ofEpochMilli(System.currentTimeMillis()).atZone(ZoneOffset.ofHours(8)).toLocalDateTime(); return df.format(localDateTime); } }View Code
html页面:
index.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>测试支付</title> </head> <body> <div> <h2>测试支付</h2> <form enctype="multipart/form-data" action="/api/alipay/page" method="post"> <button type="submit">电脑确认支付</button> </form> <form enctype="multipart/form-data" action="/api/alipay/wap" method="post"> <button type="submit">手机确认支付</button> </form> </div> </body> </html>View Code
return_url.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>支付成功</title> </head> <body> <div> <h2>支付成功</h2> <a href="http://78ff7c27cdb6.ngrok.io/return_url.html">主页</a> <a href="index.html">主页</a> </div> </body> </html>View Code
error_url.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>支付失败</title> </head> <body> <div> <h2>支付失败</h2> </div> </body> </html>View Code
三、测试
电脑测试访问:
手机测试访问: