springboot+vue+对接支付宝接口+二维码扫描支付功能(沙箱环境)


Posted in Javascript onOctober 15, 2020

1. 什么是支付宝接口(沙箱环境)?

记录时间:2020年10月15日3:55

现如今,手机支付已相当普遍,而作为开发人员应该对手机支付操作有所了解。而支付宝接口是支付宝提供的一个接口,用来对接软件应用程序在进行金钱交易使用。然后对于编程爱好者而言,想学习这一点就有点难,因为要想使用支付宝接口,必须前提是使用软件应用程序,软件应用程序需要向支付宝申请,提交一系列资料,这一点是实现不了的。这就对开发者增加了一定的难度,因为产品没有上线,然后需要对接支付宝接口就是很大的问题,所以出现了沙箱环境,具有虚拟的用户和管理员账户,进行实验测试是否对接成功。接下来就根据我的经验,简单的介绍一下我的使用和学习过程。

使用技术+编程软件:

springboot(idea)vue + elementui(HBuilderX)+ vue-qr(vue生成二维码框架)NATAPP(连接外网,实现支付宝回调)websocket(实现前端响应)

步骤:

准备沙箱环境JAVA + springboot 中使用 SDK 连接支付宝接口配置前端使用vue+elementui页面设计需要注意点结果测试

首先创建应用,这里是沙箱环境,先准备沙箱环境:

百度搜索**支付宝蚂蚁金服**,登录支付包账号,出现如下显示:

springboot+vue+对接支付宝接口+二维码扫描支付功能(沙箱环境)

这里是创建应用的地方,也就是说有项目要上线时,在这里申请。使用沙箱环境的话,点击左上角开放平台,然后往下拉,会出现沙箱二字,点击进入即可:

springboot+vue+对接支付宝接口+二维码扫描支付功能(沙箱环境)

然后就可以看到支付宝官网介绍的沙箱环境的使用,其实官网已经介绍的非常详细了,如有小伙伴们看着嫌麻烦,可以根据我学习的步骤作为参考。在如何使用沙箱环境下点击沙箱应用链接,去配置自己的沙箱环境。

springboot+vue+对接支付宝接口+二维码扫描支付功能(沙箱环境)

点击 i 符号,上面有提示链接,如何生成RSA2密钥,这里就不详细介绍了,官网写的非常明白,我这么笨的人也可以学会,相信小伙伴们也可以学会的。按照步骤会生成两个文件:应用公钥和应用私钥。记住,是应用公钥,而不是支付宝公钥。这里学习过程中没有使用证书模式

springboot+vue+对接支付宝接口+二维码扫描支付功能(沙箱环境)

然后点击RSA2的设置/查看。将产生的应用公钥复制进去就可以了,而下面会生成支付宝公钥,在后续连接过程中非常重要,需要区分使用的是支付宝公钥还是应用公钥

springboot+vue+对接支付宝接口+二维码扫描支付功能(沙箱环境)

JAVA + springboot 中使用 SDK 连接支付宝接口配置

springboot+vue+对接支付宝接口+二维码扫描支付功能(沙箱环境) 

这里是官网使用SDK的方法,这里使用旧版的方式,新版的方式我使用过程中出现某些问题不会解决。
使用之前,先封装几个配置,第一个是连接支付宝的配置:

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.sdk</groupId>
 <artifactId>alipay-sdk-java</artifactId>
 <version>4.10.145.ALL</version>
</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");
 }
}

前端使用vue+elementui页面设计:
vue项目的搭建这里就不介绍,首先准备环境,添加 vue-qr 插件:

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>
			<!-- 使用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/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>

这样前后端代码就准备就绪了,再上面的代码中有两个需要注意的: 第一个问题是:notifyUrl使用的url是外网地址,并不是IP地址,否则会无法回调。但是在学习环境下使用不了外网地址。这就需要使用**NATAPP**。百度搜索NATAPp官网,点进去下载,它是一个natapp.exe 应用,这有这个。在官网注册账号,点击购买免费隧道(免费隧道只能购买两个,要珍惜哦。隧道协议写web,其他的就按要本地环境配置就可以了)。购买之后就会有下面一条自己的隧道

springboot+vue+对接支付宝接口+二维码扫描支付功能(沙箱环境)

双击natapp.exe ,进入控制台,输入以下命令,开启隧道:

# 这里的值是上面的authtoken的值
natapp --authtoken=值

之后如下显示:

springboot+vue+对接支付宝接口+二维码扫描支付功能(沙箱环境)

这里的外网连接地址,就是notifyUrl的地址,然后再加上方法mapping路径即可,如我的是:http://cv95x3.natappfree.cc/call

第二个问题是:支付成功后,回调函数执行了,如何告诉前端我已经支付成功,同时关闭扫描二维码按钮,这里就用到了websocket,这里不详细介绍websocket是什么,只要知道一点,它是可以监听到后端是否发送信息。首先在pom.xml中导入依赖:

<dependency>
 <groupId>org.springframework.boot</groupId>
 <artifactId>spring-boot-starter-websocket</artifactId>
 <version>2.3.4.RELEASE</version>
</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通道连接。当支付成功之后,后端向前端反馈支付成功信息,前端监控接收到消息后做处理,即关闭二维码对话框。

测试结果:

springboot+vue+对接支付宝接口+二维码扫描支付功能(沙箱环境)

然后下载支付宝沙箱版手机即可扫描模拟支付,在蚂蚁金服的沙箱环境中就有二维码下载,如下图:

springboot+vue+对接支付宝接口+二维码扫描支付功能(沙箱环境)

这里指记录了支付到回调处理的操作,再这之上还可进行进一步的封装。

搞定,记录结束。

结束时间:2020年10月15日6:12

到此这篇关于springboot+vue+对接支付宝接口+二维码扫描支付(沙箱环境)的文章就介绍到这了,更多相关springboot对接支付宝支付接口内容请搜索三水点靠木以前的文章或继续浏览下面的相关文章希望大家以后多多支持三水点靠木!

Javascript 相关文章推荐
jQuery中文入门指南,翻译加实例,jQuery的起点教程
Jan 13 Javascript
[原创]保存的js无法执行的解决办法
Feb 25 Javascript
cnblogs中在闪存中屏蔽某人的实现代码
Nov 14 Javascript
一个JQuery写的点击上下滚动的小例子
Aug 27 Javascript
jQuery EasyUI API 中文文档 - Documentation 文档
Sep 29 Javascript
JavaScript中Number.NEGATIVE_INFINITY值的使用详解
Jun 05 Javascript
Radio 单选JS动态添加的选项onchange事件无效的解决方法
Dec 12 Javascript
ES6新特性之Object的变化分析
Mar 31 Javascript
基于vue实现移动端圆形旋钮插件效果
Nov 28 Javascript
实例介绍JavaScript中多种组合继承
Jan 20 Javascript
微信小程序自定义toast组件的方法详解【含动画】
May 11 Javascript
vue Tab切换以及缓存页面处理的几种方式
Nov 05 Javascript
angular共享依赖的解决方案分享
Oct 15 #Javascript
ES11新增的这9个新特性,你都掌握了吗
Oct 15 #Javascript
js实现点击烟花特效
Oct 14 #Javascript
vue使用Sass时报错问题的解决方法
Oct 14 #Javascript
JavaScript原生数组函数实例汇总
Oct 14 #Javascript
如何通过JS实现日历简单算法
Oct 14 #Javascript
浅谈js数组splice删除某个元素爬坑
Oct 14 #Javascript
You might like
PHP获取POST数据的几种方法汇总
2015/03/03 PHP
PHP实现的登录,注册及密码修改功能分析
2016/11/25 PHP
纯JavaScript实现HTML5 Canvas六种特效滤镜示例
2013/06/28 Javascript
js下拉菜单语言选项简单实现
2013/09/23 Javascript
js实现动画特效的文字链接鼠标悬停提示的方法
2015/03/02 Javascript
javascript中call和apply的用法示例分析
2015/04/02 Javascript
异步安全加载javascript文件的方法
2015/07/21 Javascript
jquery实现九宫格大转盘抽奖
2015/11/13 Javascript
JavaScript中removeChild 方法开发示例代码
2016/08/15 Javascript
Vue.js快速入门教程
2016/09/07 Javascript
JS判断来路是否是百度等搜索索引进行弹窗或自动跳转的实现代码
2016/10/09 Javascript
JS封装的三级联动菜单(使用时只需要一行js代码)
2016/10/24 Javascript
js实现截图保存图片功能的代码示例
2017/02/16 Javascript
angularJS利用ng-repeat遍历二维数组的实例代码
2017/06/03 Javascript
ReactJs实现树形结构的数据显示的组件的示例
2017/08/18 Javascript
JavaScript通过mouseover()实现图片变大效果的示例
2017/12/20 Javascript
Vue-component全局注册实例
2018/09/06 Javascript
JS实现动态添加外部js、css到head标签的方法
2019/06/05 Javascript
微信小程序 导入图标实现过程详解
2019/10/11 Javascript
JavaScript 生成唯一ID的几种方式
2021/02/19 Javascript
k8s node节点重新加入master集群的实现
2021/02/22 Javascript
[14:51]DOTA2 HEROS教学视频教你分分钟做大人-卓尔游侠
2014/06/13 DOTA
Python求正态分布曲线下面积实例
2019/11/20 Python
关于pytorch中全连接神经网络搭建两种模式详解
2020/01/14 Python
关于win10在tensorflow的安装及在pycharm中运行步骤详解
2020/03/16 Python
matlab中二维插值函数interp2的使用详解
2020/04/22 Python
纽约复古灵感的现代珠宝品牌:Lulu Frost
2018/03/03 全球购物
国贸类专业毕业生的求职信分享
2013/12/08 职场文书
2014党委书记四风问题对照检查材料思想汇报
2014/09/22 职场文书
高中校园广播稿3篇
2014/09/29 职场文书
个人作风建设自查报告
2014/10/22 职场文书
2014年党风廉政工作总结
2014/12/03 职场文书
雨花台导游词
2015/02/06 职场文书
继续教育个人总结
2015/03/03 职场文书
基于CSS制作创意端午节专属加载特效
2022/06/01 HTML / CSS
win server2012 r2服务器共享文件夹如何设置
2022/06/21 Servers