一、使用所需技术
https://open.alipay.com/
- 前端:vue + element-ui + vue-qr + axios
- 后端:spring boot
- natapp 进行内网穿透
- websocket 前端响应
二、沙箱环境准备
前往支付宝开放平台:https://open.alipay.com/
记录好APPID和支付宝网关地址
公钥模式点击查看配置应用公钥
点击查看接入文档有详细教程(这里就不展开了)
手机端下载沙箱支付宝
二、构建spring boot项目
1. 引入依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--AliPay的SDK-->
<dependency>
<groupId>com.alipay.sdk</groupId>
<artifactId>alipay-sdk-java</artifactId>
<version>4.15.14.ALL</version>
</dependency>
<!-- websocket依赖 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-websocket</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.47</version>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>2.9.2</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
2.配置类
import com.alipay.api.AlipayClient;
import com.alipay.api.DefaultAlipayClient;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class AlipayConfig {
// 应用ID (填写你自己的)
public static final String APP_ID =
// 商户ID (填写你自己的,但是这里可以忽略)
public static final String sellerId =
// 支付宝网关
public static final String gateway_url = "https://openapi.alipaydev.com/gateway.do";
// 商户私钥 (填写你自己的)
public static final String PRIVATE_KEY =
// 支付宝公钥 (填写你自己的)
public static final String ALIPAY_PUBLIC_KEY =
// 签名方式
public static final String SIGN_TYPE = "RSA2";
// 字符编码格式
public static final String CHARSET = "UTF-8";
//返回格式
public static final String FORMAT = "json";
@Bean
public AlipayClient alipayClient() {
return new DefaultAlipayClient(gateway_url,APP_ID,PRIVATE_KEY,FORMAT,CHARSET,ALIPAY_PUBLIC_KEY,SIGN_TYPE);
}
}
3.封装回调参数
@Data
@ToString
@AllArgsConstructor
@NoArgsConstructor
public class AlipayBean implements Serializable {
private static final long serialVersionUID = 8003704888495570429L;
// 开发者的app_id
private String app_id;
// 商户订单号
private String out_trade_no;
// 签名
private String sign;
// 交易状态
private String trade_status;
// 支付宝交易号
private String trade_no;
// 交易的金额
private String total_amount;
}
4.控制层(连接支付宝)
@Controller
@Slf4j
public class AliPayController {
@Autowired
private WebSocket webSocket;
@Autowired
private AlipayClient alipayClient;
@RequestMapping("/pay")
@ResponseBody
public String send(HttpServletResponse httpServletResponse) throws AlipayApiException {
AlipayTradePrecreateRequest alipayRequest = new AlipayTradePrecreateRequest();
//该网址需通过外网访问(进行内网穿透或部署在服务器上)
alipayRequest.setNotifyUrl(""); //这里是要填的,用内网穿透开一次换一次(反正我是这样的)
alipayRequest.setBizContent("{" +
"\"out_trade_no\":\"999\"," +
"\"total_amount\":\"9999\"," +
"\"subject\":\"测试商品\"," +
"\"store_id\":\"测试公司\"," +
"\"timeout_express\":\"90m\"}"); //过期时间
AlipayTradePrecreateResponse response = alipayClient.execute(alipayRequest);
String qr = response.getQrCode();
return qr; //返回支付宝二维码的地址
}
@RequestMapping("/call")
public void call(HttpServletRequest request, HttpServletResponse response, AlipayBean returnPay) throws IOException {
response.setContentType("type=text/html;charset=UTF-8");
String orderNo = returnPay.getOut_trade_no();
if (("TRADE_SUCCESS").equals(returnPay.getTrade_status())) {
webSocket.sendMessage("true");
}
}
}
5.websocket的工具类
别问。问就是复制过来的哈哈哈,想了解可以点我
/**
* 服务端和客户端双向数据传输
* 可以监听到后端是否发送信息
*/
@ServerEndpoint("/bindingRecord")
@Component
@Slf4j
public class WebSocket {
private Session session;
private static CopyOnWriteArraySet<WebSocket> webSockets = new CopyOnWriteArraySet<>();
/**
* 新建webSocket配置类
* @return
*/
@Bean
public ServerEndpointExporter serverEndpointExporter() {
return new ServerEndpointExporter();
}
/**
* 建立连接
* @param session
*/
@OnOpen
public void onOpen(Session session) {
this.session = session;
webSockets.add(this);
log.info("【新建连接】,连接总数:{}", webSockets.size());
}
/**
* 断开连接
*/
@OnClose
public void onClose(){
webSockets.remove(this);
log.info("【断开连接】,连接总数:{}", webSockets.size());
}
/**
* 接收到信息
* @param message
*/
@OnMessage
public void onMessage(String message){
log.info("【收到】,客户端的信息:{},连接总数:{}", message, webSockets.size());
}
/**
* 发送消息
* @param message
*/
public void sendMessage(String message){
log.info("【广播发送】,信息:{},总连接数:{}", message, webSockets.size());
for (WebSocket webSocket : webSockets) {
try {
webSocket.session.getBasicRemote().sendText(message);
} catch (IOException e) {
log.info("【广播发送】,信息异常:{}", e.fillInStackTrace());
}
}
}
}
三、内网穿透
这里使用的是NATAPP,网址链接:https://natapp.cn/
注册并认证,可以购买2条免费的隧道
下载他的客户端打开
natapp --authtoken=****
四、前端的搭建
1、下载好element-ui、axios、vue-qr
vue install ** -s
<template>
<div>
<!-- 支付按钮,模拟支付操作 -->
<van-button type="primary" @click="pay">支付</van-button>
<el-dialog :title="paySucc?'支付成功':'扫码支付'" :visible.sync="dialogVisible" width="16%" center>
<!-- 生成二维码图片 -->
<vueQr :text="text" :size="200" v-if="!paySucc"></vueQr>
<!-- 使用websocket监控是否扫描,扫描成功显示成功并退出界面 -->
<span class="iconfont icon-success" style="position: relative;font-size: 100px;color:#42B983;margin-left: 50px;top:-10px;" v-else></span>
</el-dialog>
</div>
</template>
<script>
import vueQr from 'vue-qr'
export default {
data() {
return {
dialogVisible: false,
text: "",
paySucc: false
}
},
components: {
vueQr
},
methods: {
pay() {
let _this = this;
_this.paySucc = false;
_this.dialogVisible = true;
this.axios.request("http://localhost:8081/pay")
.then((response) => {
_this.text = response.data;
_this.dialogVisible = true;
//使用webSocket发送请求,下面会简单介绍websocket使用
if ("WebSocket" in window) {
// 打开一个 web socket
var ws = new WebSocket("ws://localhost:8081/bindingRecord");
ws.onopen = function() {
// Web Socket 已连接上,使用 send() 方法发送数据
// ws.send("data");
// alert("数据发送中...");
};
ws.onmessage = function(evt) {
var received_msg = evt.data;
// alert("数据已接收..." + evt.data);
if (Boolean(evt.data)) {
_this.paySucc = true;
setTimeout(() => {
_this.dialogVisible = false;
}, 3 * 1000);
}
ws.close();
};
ws.onclose = function() {
// // 关闭 websocket
console.log("连接已关闭...");
};
} else {
// 浏览器不支持 WebSocket
alert("您的浏览器不支持 WebSocket!");
}
}).catch((err) => {
console.log(err)
})
},
back(dataUrl, id) {
console.log(dataUrl, id)
}
}
}
</script>
<style>
.btn {
margin-left: 100px;
}
</style>