记录时间:2020年10月15日3:55
现如今,手机支付已相当普遍,而作为开发人员应该对手机支付操作有所了解。而支付宝接口是支付宝提供的一个接口,用来对接软件应用程序在进行金钱交易使用。然后对于编程爱好者而言,想学习这一点就有点难,因为要想使用支付宝接口,必须前提是使用软件应用程序,软件应用程序需要向支付宝申请,提交一系列资料,这一点是实现不了的。这就对开发者增加了一定的难度,因为产品没有上线,然后需要对接支付宝接口就是很大的问题,所以出现了沙箱环境,具有虚拟的用户和管理员账户,进行实验测试是否对接成功。接下来就根据我的经验,简单的介绍一下我的使用和学习过程。
使用技术+编程软件:
- springboot(idea)
- vue + elementui(HBuilderX)+ vue-qr(vue生成二维码框架)
- NATAPP(连接外网,实现支付宝回调)
- websocket(实现前端响应)
步骤:
- 准备沙箱环境
- JAVA + springboot 中使用 SDK 连接支付宝接口配置
- 前端使用vue+elementui页面设计
- 需要注意点
- 结果测试
这里是创建应用的地方,也就是说有项目要上线时,在这里申请。使用沙箱环境的话,点击左上角开放平台,然后往下拉,会出现沙箱二字,点击进入即可:
import com.alipay.api.AlipayApiException;
import com.alipay.api.AlipayClient;
import com.alipay.api.DefaultAlipayClient;
import com.alipay.api.internal.util.AlipaySignature;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import javax.servlet.http.HttpServletRequest;
import java.util.HashMap;
import java.util.Map;
/**
* 支付宝接口配置类
*/
@Configuration
public class PayConfig {
// 请填写您的AppId,例如:2019091767145019(必填)
private static final String appID = "2016102500758313";
//应用私钥,这里修改生成的私钥即可(必填)
private static final String privateKey = "";
//支付宝公钥,而非应用公钥(必填)
public static final String publicKey = "";
//默认即可(必填)
public static final String charset = "utf-8";
//默认即可(必填)
public static final String signType = "RSA2";
@Bean
public AlipayClient alipayClient(){
//沙箱环境使用https://openapi.alipaydev.com/gateway.do,线上环境使用https://openapi.alipay.com/gateway.do
return new DefaultAlipayClient("https://openapi.alipaydev.com/gateway.do", appID, privateKey, "json", charset, publicKey, signType);
}
/**
* 验签,是否正确
*/
public static boolean checkSign(HttpServletRequest request){
Map<String, String[]> requestMap = request.getParameterMap();
Map<String, String> paramsMap = new HashMap<>();
requestMap.forEach((key, values) -> {
String strs = "";
for(String value : values) {
strs = strs + value;
}
System.out.println(key +"===>"+strs);
paramsMap.put(key, strs);
});
System.out.println();
//调用SDK验证签名
try {
return AlipaySignature.rsaCheckV1(paramsMap, PayConfig.publicKey, PayConfig.charset, PayConfig.signType);
} catch (AlipayApiException e) {
// TODO Auto-generated catch block
e.printStackTrace();
System.out.println("*********************验签失败********************");
return false;
}
}
}
然后封装一个支付宝回调的参数对象,这里就不需要自己手动去获取参数了
import java.io.Serializable;
/**
* 支付宝回调参数
*/
public class AliReturnPayBean implements Serializable {
/**
*
*/
private static final long serialVersionUID = 1L;
/**
* 开发者的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;
public String getTotal_amount() {
return total_amount;
}
public void setTotal_amount(String total_amount) {
this.total_amount = total_amount;
}
public String getApp_id() {
return app_id;
}
public void setApp_id(String app_id) {
this.app_id = app_id;
}
public String getOut_trade_no() {
return out_trade_no;
}
public void setOut_trade_no(String out_trade_no) {
this.out_trade_no = out_trade_no;
}
public String getSign() {
return sign;
}
public void setSign(String sign) {
this.sign = sign;
}
public String getTrade_status() {
return trade_status;
}
public void setTrade_status(String trade_status) {
this.trade_status = trade_status;
}
public String getTrade_no() {
return trade_no;
}
public void setTrade_no(String trade_no) {
this.trade_no = trade_no;
}
@Override
public String toString() {
return "AliReturnPayBean [app_id=" + app_id + ", out_trade_no=" + out_trade_no + ", sign=" + sign
+ ", trade_status=" + trade_status + ", trade_no=" + trade_no + "]";
}
}
然后写一个控制层去连接支付宝,控制层必须是@Controller修饰,而不是@RestController修饰,因为支付宝的回调函数里面返回的是请求。具体事例如下:
前提:在pom.xml 中导入SDK依赖:
<dependency>
<groupId>com.alipay.sdkgroupId>
<artifactId>alipay-sdk-javaartifactId>
<version>4.10.145.ALLversion>
dependency>
package com.example.zhifubaozhifu.controller;
import com.alibaba.fastjson.JSON;
import com.alipay.api.AlipayApiException;
import com.alipay.api.AlipayClient;
import com.alipay.api.request.AlipayTradePrecreateRequest;
import com.alipay.api.response.AlipayTradePrecreateResponse;
import com.example.zhifubaozhifu.config.PayConfig;
import com.example.zhifubaozhifu.util.AliReturnPayBean;
import com.example.zhifubaozhifu.util.Shop;
import com.example.zhifubaozhifu.util.WebSocket;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.math.BigDecimal;
@Controller
@Slf4j
public class Test {
@Autowired
private AlipayClient alipayClient;
@Autowired
private WebSocket webSocket;
@RequestMapping("/createQR")
@ResponseBody
public String send() throws AlipayApiException {
AlipayTradePrecreateRequest request = new AlipayTradePrecreateRequest(); //创建API对应的request类
// 在下面会介绍notifyUrl怎么来的
request.setNotifyUrl("http://cv95x3.natappfree.cc/call");
//同步回调地址
// request.setReturnUrl("");
request.setBizContent(" {" +
" \"primary_industry_name\":\"IT科技/IT软件与服务\"," +
" \"primary_industry_code\":\"10001/20102\"," +
" \"secondary_industry_code\":\"10001/20102\"," +
" \"secondary_industry_name\":\"IT科技/IT软件与服务\"" +
" }");;
AlipayTradePrecreateResponse response = alipayClient.execute(request);
String path = "zhifu.jpg";
if (response.isSuccess()) {
System.out.println("调用成功");
return response.getQrCode();
} else {
System.out.println("调用失败");
}
return "";
}
/**
* 支付宝回调函数
* @param request
* @param response
* @param returnPay
* @throws IOException
*/
@RequestMapping("/call")
public void call(HttpServletRequest request, HttpServletResponse response, AliReturnPayBean returnPay) throws IOException {
response.setContentType("type=text/html;charset=UTF-8");
log.info("支付宝的的回调函数被调用");
if (!PayConfig.checkSign(request)) {
log.info("验签失败");
response.getWriter().write("failture");
return;
}
if (returnPay == null) {
log.info("支付宝的returnPay返回为空");
response.getWriter().write("success");
return;
}
log.info("支付宝的returnPay" + returnPay.toString());
//表示支付成功状态下的操作
if (returnPay.getTrade_status().equals("TRADE_SUCCESS")) {
log.info("支付宝的支付状态为TRADE_SUCCESS");
//业务逻辑处理 ,webSocket在下面会有介绍配置
webSocket.sendMessage("true");
}
response.getWriter().write("success");
}
}
npm install vue-qr --save
前端代码:
<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>
<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/createQR")
.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>
# 这里的值是上面的authtoken的值
natapp --authtoken=值
之后如下显示:
这里的外网连接地址,就是notifyUrl的地址,然后再加上方法mapping路径即可,如我的是:http://cv95x3.natappfree.cc/call
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-websocketartifactId>
<version>2.3.4.RELEASEversion>
dependency>
然后创建一个webconfig的配置类:
package com.example.zhifubaozhifu.util;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Component;
import org.springframework.web.socket.server.standard.ServerEndpointExporter;
import javax.websocket.OnClose;
import javax.websocket.OnMessage;
import javax.websocket.OnOpen;
import javax.websocket.Session;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
import java.util.concurrent.CopyOnWriteArraySet;
/**
* @desc: WebSocket服务
*
**/
//连接webSocket服务的URL
@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());
}
}
}
}
然后使用的时候调用方法onMessage即可接收消息,使用onMessage即可广发消息。
前端使用步骤:
<script type="text/javascript">
function WebSocketTest()
{
if ("WebSocket" in window)
{
alert("您的浏览器支持 WebSocket!");
// 打开一个 web socket
var ws = new WebSocket("ws://localhost:9998/echo");
ws.onopen = function()
{
// Web Socket 已连接上,使用 send() 方法发送数据
ws.send("发送数据");
alert("数据发送中...");
};
ws.onmessage = function (evt)
{
var received_msg = evt.data;
alert("数据已接收...");
};
ws.onclose = function()
{
// 关闭 websocket
alert("连接已关闭...");
};
}
else
{
// 浏览器不支持 WebSocket
alert("您的浏览器不支持 WebSocket!");
}
}
</script>
想详细了解的话,可以去菜鸟教程学习。
使用思路: 前端先创建websocket , 连接到后端websocket ,这样才能将websocket通道连接。当支付成功之后,后端向前端反馈支付成功信息,前端监控接收到消息后做处理,即关闭二维码对话框。
搞定,记录结束。
结束时间:2020年10月15日6:12