SSM框架整合支付宝沙箱
如果你觉得这篇文章对你有用,不要白嫖,请点赞+评论+收藏+转发!
一、进入支付宝开放平台的管理中心的沙箱服务
网址:https://open.alipay.com/
二、设置RSA2(SHA256)密钥
1、下载支付宝开发助手
网址:https://opendocs.alipay.com/open/291/introduce
2、生成密钥
3、复制公钥到支付宝沙箱设置RSA2(SHA256)密钥
4、保存生成的支付宝公钥
三、在pom.xml中引入相关依赖
<dependency>
<groupId>com.alipay.sdk</groupId>
<artifactId>alipay-sdk-java</artifactId>
<version>4.13.50.ALL</version>
</dependency>
<dependency>
<groupId>com.alipay.sdk</groupId>
<artifactId>alipay-easysdk</artifactId>
<version>2.1.0</version>
</dependency>
四、在resources资源文件夹下,新建支付宝沙箱的配置文件:“apply.properties”
## 支付宝配置(不需要应用公钥,应用公钥只是为了生成支付宝公钥),以下配置均可在支付宝沙箱设置中找到
# 应用ID
alipay.appId =
# 应用私钥
alipay.privateKey =
# 支付宝公钥,注意不是生成的应用公钥
alipay.publicKey =
#支付网关配置,这一项是写死的,正式环境是openapi.alipay.com 注意这边不要加https或http等协议名称
alipay.gateway =
# 支付宝前台跳转地址(自己编写逻辑和页面)
alipay.returnUrl =
# 支付宝后台通知地址(自己编写逻辑和页面)
alipay.notifyUrl =
# 支付宝前台手机网页支付中途取消跳转地址(自己编写逻辑和页面)
alipay.errorUrl =
五、编写支付宝沙箱配置初始化类:AlipayConfig
编写此类的目的是:将支付宝的配置加载到Spring容器中,完成支付宝SDK的初始化。
其中,配置类需要实现ApplicationListener接口,其泛型为ContextRefreshedEvent,作用为当所有的bean都初始化完成并被成功装载后会触发ContextRefreshedEvent事件,ContextRefreshedEvent中提供有一个onApplicationEvent方法,也就是初始化完后将会执行onApplicationEvent方法,将初始化的逻辑编写在此方法中即可。
import com.alipay.easysdk.factory.Factory;
import com.alipay.easysdk.kernel.Config;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.ApplicationListener;
import org.springframework.context.annotation.PropertySource;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.stereotype.Component;
@PropertySource("classpath:/apply.properties") //引入支付宝配置文件
@Component
public class AlipayConfig implements ApplicationListener<ContextRefreshedEvent> {
// 应用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;
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;
}
@Override
public void onApplicationEvent(ContextRefreshedEvent event) {
//初始化支付宝SDK
Factory.setOptions(getOptions());
System.out.println("**********支付宝SDK初始化完成**********");
}
}
六、新建"AlipayService"类,编写支付宝服务层的逻辑代码
说明:这里只进行电脑支付和手机支付两种支付方式的模拟,其他方式请参考官方文档,自行测试!
package cn.bjwlxy.service;
import com.alipay.easysdk.factory.Factory;
import com.alipay.easysdk.payment.facetoface.models.AlipayTradePayResponse;
import com.alipay.easysdk.payment.page.models.AlipayTradePagePayResponse;
import com.alipay.easysdk.payment.wap.models.AlipayTradeWapPayResponse;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.PropertySource;
import org.springframework.stereotype.Service;
@PropertySource("classpath:/apply.properties")
@Service
public class AlipayService {
// 支付成功后要跳转的页面
@Value("${alipay.returnUrl}")
private String returnUrl;
// 支付宝前台手机网页支付中途取消跳转地址
@Value("${alipay.errorUrl}")
private String errorUrl;
/**
* 支付宝电脑网页支付
* @param total 金额
* @param orderId 订单编号
* @return
*/
public String page(String total, String orderId) {
try {
AlipayTradePagePayResponse response = Factory.Payment
// 选择电脑网站
.Page()
// 调用支付方法(订单名称, 商家订单号, 金额, 成功页面)
.pay("支付测试", orderId, total, returnUrl);
return response.body;
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
/**
* 支付宝手机网页支付
* @param total 金额
* @param orderId 订单编号
* @return
*/
public String wap(String total, String orderId) {
try {
AlipayTradeWapPayResponse response = Factory.Payment
//选择手机网站
.Wap()
// 调用支付方法(订单名称, 商家订单号, 金额, 中途退出页面, 成功页面)
.pay("支付测试", orderId, total, errorUrl, returnUrl);
return response.body;
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
}
六、新建"AlipayController"类,编写支付宝控制层的逻辑代码
package cn.bjwlxy.controller;
import cn.bjwlxy.service.AlipayService;
import com.alipay.easysdk.factory.Factory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpServletRequest;
import java.util.HashMap;
import java.util.Map;
@RestController
@RequestMapping("/api/alipay")
public class AlipayController {
@Autowired
private AlipayService alipayService;
/**
* @description: 支付宝电脑网页支付
* @param total: 金额
* @return java.lang.String
*/
//此处produces = {"text/html;charset=UTF-8"}设置返回数据的编码集,防止传递过程中出错
@PostMapping(value = "/page",produces = {"text/html;charset=UTF-8"})
public String page(String total,String orderId)
return alipayService.page(total,orderId);
}
/**
* @description: 支付宝手机网页支付
* @param orderId: 订单编号
* @param total: 金额
* @return java.lang.String
*/
//此处produces = {"text/html;charset=UTF-8"}设置返回数据的编码集,防止传递过程中出错
@PostMapping(value = "/wap",produces = {"text/html;charset=UTF-8"})
public String wap(String total,String orderId) {
return alipayService.wap(total, orderId);
}
/**
* @description: 支付宝异步回调
* @param request: 请求
* @return java.lang.String
*/
@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));
}
// 支付宝验签
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";
}
}
七、编写生成订单号方法
新建OrderUtil,编写生成订单号方法:
package cn.bjwlxy.common;
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);
}
}
八、编写前端测试页面
到此处后端代码编写完毕,下面开始编写前端测试页面,前端测试页面需要三个:购买页面(buy.jsp)、支付成功页面(return_url.html)、支付失败页面(error_url.html),这里只是为了测试,不做任何效果。
1、购买页面(buy.jsp)
<%@ page import="cn.bjwlxy.common.OrderUtil" %>
<%@ taglib prefix="security" uri="http://www.springframework.org/security/tags" %>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<h1>商品购买页面</h1>
商品一共${param.total_price}元
<form action="/api/alipay/page" method="post">
订单编号:<input type="text" value="<%=OrderUtil.getOrderNo()%>" name="orderId" readonly/>
订单总金额:<input type="text" value="${param.total_price}" name="total" readonly/>
<button type="submit">电脑确认支付</button>
</form>
<form action="/api/alipay/wap" method="post">
订单编号:<input type="text" value="<%=OrderUtil.getOrderNo()%>" name="orderId" readonly/>
订单总金额:<input type="text" value="${param.total_price}" name="total" readonly/>
<button type="submit">手机确认支付</button>
</form>
<script>
</script>
</html>
如果请求成功,会返回整个响应的支付页面的HTML元素,如果使用异步请求,可以使用doucument.write()或response.write()写入页面!
2、支付成功页面(return_url.html)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h2>支付成功</h2>
</body>
</html>
3、支付失败页面(error_url.html)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h2>支付失败</h2>
</body>
</html>
九、支付宝沙箱购买测试
1、购买页面
2、电脑支付测试
这里有两种测试方法:
第一种是下载支付宝沙箱版APP,扫码支付
第二种是在支付宝开放平台获取虚拟的账号和密码,手动输入
其实,支付宝沙箱模拟的就是一个买家和商家之间的互相交易,因为是是在沙箱环境中模拟的,所以不会产生真实交易。
支付宝官方给提供了一个买家账号和商家账号,我们付款时,使用买家账号,当付款成功后,买家账号中的金额相应的进行扣除,商家账号中的金额相应的增加,这样就模拟了一个在真实环境中的交易情况。
我们现在使用的网关地址为https://openapi.alipaydev.com/gateway.do,这个是支付宝沙箱的一个网关,当使用这个网关配置时,支付时,自动跳转到沙箱环境中。如果全部测试通过后,只需要将配置中的网关换为https://openapi.alipay.com就可以了,非常方便!
3、手机支付测试
手机测试的过程大致相同,只不过将支付的设备换为了手机,此处不再截图示意。