1、登录支付宝开放平台:https://open.alipay.com/platform/developerIndex.htm
2、点击研发服务:
3、设置密匙
由于我设置过了,里面有密匙,你们要点击那个支付宝密匙生成器,快捷链接:https://miniu.alipay.com/keytool/create
点确定之后,就和我这个一样了
好了,准备工作搞完了,页面先别关
为了通俗易懂,我就省略了业务层和持久层,直接伪造数据,防止大家对有些方法疑惑。
<dependency>
<groupId>com.alipay.sdkgroupId>
<artifactId>alipay-easysdkartifactId>
<version>2.2.0version>
dependency>
<dependency>
<groupId>org.projectlombokgroupId>
<artifactId>lombokartifactId>
dependency>
alipay:
appId:
privateKey:
publicKey:
gateway: openapi.alipaydev.com # 不用修改
returnUrl: http://127.0.0.1:8080/return.html # 支付成功后跳转的页面,可以为空,也可以根据回调接口的判断来返回信息
notifyUrl: http://域名/pay/notify # 当支付成功后,支付宝的回调接口,是你本地的接口,如果没有域名的话要内网穿透
内网穿透可以参考:https://blog.csdn.net/wulei2921625957/article/details/113776112?spm=1001.2014.3001.5501
解释:
/**
* 项目初始化
*/
@Component
public class ProjectInit 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) {
//初始化支付宝SDK
Factory.setOptions(getOptions());
}
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;
}
}
/**
* @program: alipay
* @create: 2021-06-25 14:44
*/
@RestController
public class PayController {
//支付成功后要跳转的页面
@Value("${alipay.returnUrl}")
private String returnUrl;
/**
* 下单支付
* 当用户点击支付的时候,应该是要传参的,我这里就直接省略了,免得修改麻烦,大家应该可以自己实现
*/
@SneakyThrows
@RequestMapping(value = "/pay", method = RequestMethod.POST)
public String pay() {
AlipayTradePagePayResponse response = Factory.Payment
//选择网页支付平台
.Page()
//调用支付方法:订单名称、订单号、金额、回调页面
.pay("测试商品", UUID.randomUUID().toString(), "5", returnUrl);
//这里可以加入业务代码:比如生成订单等等。。
return response.body;
}
/**
* 支付后异步回调的接口
* 这个接口需要内网穿透,因为要别人访问你的本地网站
*
* @param request
* @return
*/
@RequestMapping(value = "/notify", method = RequestMethod.POST)
public String notifyAsync(HttpServletRequest request) {
Map<String, String> map = new HashMap();
Enumeration<String> parameterNames = request.getParameterNames();
while (parameterNames.hasMoreElements()) {
String key = parameterNames.nextElement();
String value = request.getParameter(key);
map.put(key, value);
}
//验签
try {
if (Factory.Payment.Common().verifyNotify(map)) {
//验证用户的支付结果
String trade_status = request.getParameter("trade_status");
if ("TRADE_SUCCESS".equals(trade_status)) {
//这里可以更新订单的状态等等。。
}
} else {
return "fail";
}
} catch (Exception e) {
return "fail";
}
return "success";
}
}
index.html
<html lang="en">
<head>
<meta charset="UTF-8">
<title>确认订单title>
head>
<body>
<div>
您的订单信息如下,请确认无误后支付:<br/>
xxxxxxxxxx省略订单信息xxxxxxxxxx
<form action="/pay" method="post">
<button type="submit">确认支付button>
form>
div>
body>
html>
return.html
<html lang="en">
<head>
<meta charset="UTF-8">
<title>支付成功title>
head>
<body>
<div>
您已成功购买此商品,感谢您的支持
div>
body>
html>
至此,最简单的支付就搭建完成了,可以启动项目访问首页(劝大家换个浏览器访问,或者关闭当前浏览器所有页面,重新打开)点击支付后回调到下面的页面
那怎么支付呢?直接用账号或者扫码(不是我们日常的支付宝,是沙箱版支付宝),那账号呢?
支付成功后会跳到事先写好的return页面。
那要怎么体现前后端的交互呢?当前端发起请求的时候,我们后端应该要生成一条订单插入数据库,设置一个状态,然后用户支付成功后,根据回调函数更新数据库订单的状态即可,这个时候,前端就要定时监听了,监听我们后端写的获取订单的状态接口。如果支付成功,则结束监听,可以利用这一点替换掉yml文件里面设置的返回页面,根据这个来跳转即可。
那如果用户多次获取二维码,那我们会生成许多无用的订单,这个时候我觉得用Redis存储订单Id并设置有效时间,配合后端可以写一个定时任务来清理无用的订单即可。
我这里举例类似于上面的打赏功能,实现起来还是比较简单的。前端代码我就不展示了,等会说下思路即可。
重新搭建一个项目吧
和上面一样的
和上面一样的
和上面一样的
@Service
public class AlipayServiceImpl implements AlipayService {
//这个服务是订单服务,我就不展示了,主要的功能就是插入订单,更新订单状态。可以自己写
@Autowired
private OrderService orderService;
/**
* 生成二维码,当面付
*
* @param order 订单类,大家应该可以想到里面应该有哪些字段
*/
@SneakyThrows
@Override
public Map<String, Object> FaceTofACEPay(Order order) {
/**
* 订单的公共操作
*/
MyUtil myUtil = new MyUtil();
//获取二维码的同时要生成订单号
String orderId = myUtil.getUUID();
order.setOrderId(orderId);
//获取当前日期
String orderDate = myUtil.getCurrentDetailedTime();
order.setOrderDate(orderDate);
//设置订单为未支付状态
order.setOrderStatus(0);
Integer integer;
HashMap<String, Object> map = new HashMap<>();
AlipayTradePrecreateResponse response = Factory.Payment.FaceToFace().preCreate("树洞赞赏", orderId, "" + order.getOrderAmount());
if (ResponseChecker.success(response)) {
String qrCode = response.getQrCode();
map.put("qrCode", qrCode); //生成的二维码链接,不可直接打开,后面会说到
map.put("orderId", orderId); //生成的订单号应该要返回给前端
integer = orderService.insertOrder(order); //插入订单数据库
} else {
integer = 0;
map.put("qrCode", "");
map.put("orderId", "");
}
if (integer <= 0) {
//我自定义的异常类
throw new BusinessException(EnumBusinessError.UNKNOWN_ERROR, "生成二维码失败");
}
return map;
}
/**
* 支付异步回调
* 代码跟上面的案例大同小异
* @param mapParams
* @return
*/
@SneakyThrows
@Override
public String notifyUrl(HttpServletRequest request) {
Map<String, String> map = new HashMap();
Enumeration<String> parameterNames = request.getParameterNames();
while (parameterNames.hasMoreElements()) {
String key = parameterNames.nextElement();
String value = request.getParameter(key);
map.put(key, value);
}
//验签
try {
if (Factory.Payment.Common().verifyNotify(map)) {
//验证用户的支付结果
String trade_status = request.getParameter("trade_status");
if ("TRADE_SUCCESS".equals(trade_status)) {
//这里可以根据业务更新订单支付状态.....
Order order = new Order();
String orderId = map.get("out_trade_no");
order.setOrderId(orderId);
order.setOrderStatus(1);
orderService.updateOrder(order);
return "success";
}
} else {
return "fail";
}
return "fail";
}
}
@RestController
public class PayController {
@Autowired
private OrderService orderService;
@Autowired
private AlipayService alipayService;
/**
* 前端定时检查订单的支付状态
*
* @param order 订单类作为接收参数 用到了里面的 orderId
* CommonReturnType 这个就是统一返回前端的结果 code msg data
* @return
*/
@RequestMapping(value = "/checkOrderStatus", method = RequestMethod.POST)
public CommonReturnType checkOrderStatus(@RequestBody Order order) {
boolean b = orderService.checkOrderStatus(order);
return CommonReturnType.create(b);
}
/**
* 获取支付二维码
*
* @param order 订单类作为接收参数 用到了里面的 order金额 字段
* @return
* @throws Exception
*/
@RequestMapping(value = "/getQrCode", method = {
RequestMethod.POST})
public CommonReturnType FacetoFacePay(@RequestBody Order order) throws Exception {
Map<String, Object> map = alipayService.FaceTofACEPay(order);
return CommonReturnType.create(map);
}
/**
* 异步回调接口
*
*/
@RequestMapping(value = "/notifyUrl", method = RequestMethod.POST)
public String notifyInterface(HttpServletRequest request) throws Exception {
return alipayService.notifyUrl(request);
}
}
现在后端搭建完毕,前端就不写了,直接调用接口即可:
获取二维码接口: 在线的二维码转换网站:http://qrcode.cnaidc.com/
监听支付状态接口:监听的时候,前端要定时监听,一般为1s到2s的样子发一次请求,如果刷新了二维码,那就要重新带上新的订单ID重新监听。