SpringBoot + Vue + WebSocket + NATAPP 实现 支付宝支付—沙箱环境

后端

1、pom文件
  • 引入WebSocket包
<dependency>
 <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-websocket</artifactId>
</dependency>
  • 引入支付宝sdk
<dependency>
   <groupId>com.alipay.sdk</groupId>
    <artifactId>alipay-sdk-java</artifactId>
    <version>4.22.67.ALL</version>
</dependency>
2、application.yml
AliPay:
  # 应用ID,您的APPID,收款账号既是您的APPID对应支付宝账号,在沙箱应用中获取
  appId: 
  # 商户私钥,您的PKCS8格式RSA2私钥,通过开发助手生成的应用私钥
  privateKey: 
  # 支付宝公钥,在沙箱应用获取,通过应用公钥生成支付宝公钥
  publicKey: 
  # 服务器异步通知页面路径需http://格式的完整路径,测试环境需要内网穿透natapp
  notifyUrl: http://cd8apv.natappfree.cc/alipay/call
  # 页面跳转同步通知页面路径 需http://格式的完整路径,测试环境需要内网穿透natapp
  returnUrl: http://cd8apv.natappfree.cc/alipay/call
  # 签名方式
  signType: RSA2
  # 字符编码格式
  charset: utf-8
  # 支付宝网关,在沙箱应用中获取
  gatewayUrl: https://openapi.alipaydev.com/gateway.do
3、创建AlipayController
package com.think.controller;

import com.alibaba.druid.util.StringUtils;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.alipay.api.AlipayApiException;
import com.alipay.api.AlipayClient;
import com.alipay.api.DefaultAlipayClient;
import com.alipay.api.request.AlipayTradePagePayRequest;
import com.alipay.api.request.AlipayTradePrecreateRequest;
import com.alipay.api.response.AlipayTradePrecreateResponse;
import com.think.pojo.AliReturnPay;
import com.think.util.ShareFuncUtil;
import com.think.util.WebSocket;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
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.util.*;

/**
 * @Program: IntelliJ IDEA
 * @Description: AlipayController
 * @Author: Li
 * @CreateDate: 2022-08-22 09:56
 * @Version: 1.1.0
 **/
@Slf4j
@Controller
@RequestMapping("/alipay")
public class AlipayController {

    //获取配置文件中的配置信息
    //应用ID,您的APPID,收款账号既是您的APPID对应支付宝账号
    @Value("${AliPay.appId}")
    private String appId;

    //商户私钥 您的PKCS8格式RSA2私钥
    @Value("${AliPay.privateKey}")
    private String privateKey;

    //支付宝公钥
    @Value("${AliPay.publicKey}")
    private String publicKey;

    //服务器异步通知页面路径
    @Value("${AliPay.notifyUrl}")
    private String notifyUrl;

    //页面跳转同步通知页面路径
    @Value("${AliPay.returnUrl}")
    private String returnUrl;

    //签名方式
    @Value("${AliPay.signType}")
    private String signType;

    //字符编码格式
    @Value("${AliPay.charset}")
    private String charset;

    //支付宝网关
    @Value("${AliPay.gatewayUrl}")
    private String gatewayUrl;

    private final String format = "json";

    @Autowired
    private WebSocket webSocket;

    @PostMapping("/sandboxPay")
    @ResponseBody
    public Object sandboxPay(AliReturnPay aliReturnPay) throws AlipayApiException{

        AlipayClient alipayClient = new DefaultAlipayClient(gatewayUrl,appId,privateKey,format,charset,publicKey,signType);
        AlipayTradePrecreateRequest alipayRequest = new AlipayTradePrecreateRequest();
        // 设置支付宝异步通知回调地址 (注意:这个网址必须是可以通过外网访问的网址)
        alipayRequest.setNotifyUrl(returnUrl);
        alipayRequest.setReturnUrl(returnUrl);
		// 订单信息
        aliReturnPay.setOut_trade_no(UUID.randomUUID().toString().replaceAll("-",""));
        aliReturnPay.setSubject("订单名称");
        aliReturnPay.setTotal_amount(String.valueOf(10));
        aliReturnPay.setBody("商品描述");
        aliReturnPay.setStore_id("公司名");
        aliReturnPay.setTimeout_express("90m");
        
        alipayRequest.setBizContent (JSON.toJSONString(aliReturnPay));
        
        AlipayTradePrecreateResponse response = alipayClient.execute (alipayRequest);
        // 返回支付宝支付网址,用于生成二维码
        return ShareFuncUtil.returnObject(response.getQrCode(),aliReturnPay);
    }


    @RequestMapping("/call")
    public void call(HttpServletRequest request, HttpServletResponse response, AliReturnPay aliReturnPay) throws IOException {
        // 通知返回的数据会封装到 AliReturnPay 类中
        response.setContentType("type=text/html;charset=UTF-8");
        String orderNo = aliReturnPay.getOut_trade_no(); // 获得订单号,对数据进行修改
       
        // 支付成功的返回码
        if (("TRADE_SUCCESS").equals(aliReturnPay.getTrade_status())){
            // 向前端发送一条支付成功的通知
            webSocket.sendMessage(JSON.toJSONString(aliReturnPay));
        }
    }
}

4、创建WebSocket类
package com.think.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;

/**
 * @Program: IntelliJ IDEA
 * @Description: WebSocket
 * @Author: Li
 * @CreateDate: 2022-08-24 14:58
 * @Version: 1.1.0
 **/

@ServerEndpoint("/webSocket")
@Component
@Slf4j
public class WebSocket {

    private Session session;
    public 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());
            }
        }
    }

}

5、创建AliReturnPay Bean
package com.think.pojo;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.ToString;

import java.io.Serializable;

/**
 * @Program: IntelliJ IDEA
 * @Description: AliReturnPay\
 * @Author: Li 
 * @CreateDate: 2022-08-24 14:55
 * @Version: 1.1.0
 **/

@Data
@ToString
@AllArgsConstructor
@NoArgsConstructor
public class AliReturnPay implements Serializable {
    private static final long serialVersionUID = 8683949075381016639L;

    private String id;
    // 开发者的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;
    //用户
    private String user_tel;
    //ip
    private String host_ip;
    //外网ip
    private String external_ip;
    //订单名称
    private String subject;
    //订单内容
    private String body;
    //
    private String store_id;

    private String timeout_express;
}

6、Controller 的 return 方法 ShareFuncUtil 类
package com.think.util;

import java.io.IOException;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Random;

import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import com.alibaba.fastjson.JSONObject;
import com.fasterxml.jackson.databind.ObjectMapper;

public class ShareFuncUtil {
	
	public static Object returnObject(Object object) {
		JSONObject jsonObject = new JSONObject();
		jsonObject.put("code", 200);
		jsonObject.put("data", object);
		return jsonObject;
	}
	public static Object returnObject(Object object,Object object2) {
		JSONObject jsonObject = new JSONObject();
		jsonObject.put("code", 200);
		jsonObject.put("data1", object);
		jsonObject.put("data2", object2);
		return jsonObject;
	}
	
	public static Object returnNotObject(int code) {
		JSONObject jsonObject = new JSONObject();
		jsonObject.put("code", code);
		return jsonObject;
	}
	
	public static void writerPring(HttpServletRequest requests, HttpServletResponse responses, ServletResponse response, int code) throws IOException {
		responses.setHeader("Access-Control-Allow-Origin", requests.getHeader("Origin"));
		responses.setHeader("Access-Control-Allow-Credentials", "true");
		responses.setContentType("application/json; charset=utf-8");
		responses.setCharacterEncoding("UTF-8");
		PrintWriter out = response.getWriter();
		out.print(ShareFuncUtil.returnNotObject(code));
		out.flush();
		out.close();
	}


	private static ArrayList<String> arrList = new ArrayList<>(Arrays.asList("q","w","e","r","t","y","u","i","o","p","a","s","d","f","g","h","j","k","l","z","x","c","v","b","n","m","Q","W","E","R","T","Y","U","I","O","P","A","S","D","F","G","H","J","K","L","Z","X","C","V","B","N","M","0","1","2","3","4","5","6","7","8","9"));

	/** 生成邀请码
	 * @param num 位数
	 * @return 邀请码
	 */
	public static String makeRandomArr(int num){

		if(num >= arrList.size()){
			return "";
		}
		Random random = new Random();
		int temRandomNum = 0;
		ArrayList<String> newArrList = new ArrayList<>();

		for(int i = 0; i < num; i++){
			temRandomNum = random.nextInt(arrList.size()-1);
			newArrList.add(arrList.get(temRandomNum));
		}
		return newArrList.toString().replace("[", "").replace("]", "").replaceAll(", ", "");
	}
	
}

7、NATAPP 内网穿透
  • 进入官网点击右上角注册
    在这里插入图片描述

  • 注册登录后一系列信息填写完成后点击左上角购买隧道 免费的就行
    在这里插入图片描述

  • 填写信息
    在这里插入图片描述

  • 购买完成后下载对应自己的客户端
    在这里插入图片描述

  • 在NATAPP同级目录创建 config.ini
    在这里插入图片描述

#将本文件放置于natapp同级目录 程序将读取 [default] 段
#在命令行参数模式如 natapp -authtoken=xxx 等相同参数将会覆盖掉此配置
#命令行参数 -config= 可以指定任意config.ini文件
[default]
authtoken=                      #对应一条隧道的authtoken
clienttoken=                    #对应客户端的clienttoken,将会忽略authtoken,若无请留空,
log=none                        #log 日志文件,可指定本地文件, none=不做记录,stdout=直接屏幕输出 ,默认为none
loglevel=ERROR                  #日志等级 DEBUG, INFO, WARNING, ERROR 默认为 DEBUG
http_proxy=                     #代理设置 如 http://10.123.10.10:3128 非代理上网用户请务必留空
  • 点击我的隧道复制 authtoken 粘贴到 config.ini 文件里面,只需要填写 authtoken ,其他不用填
    在这里插入图片描述
  • 双击 natapp.exe 运行
    在这里插入图片描述
    此处的 http://cd8apv.natappfree.cc 就代表 127.0.0.1:8080
    如:需要支付成功需要访问 AlipayController 里面的 call 方法
    则:http://127.0.0.1:8080/alipay/call
    换:http://cd8apv.natappfree.cc/alipay/call
    就可以访问到

前端

安装vue-qr

npm i vue-qr -s

引入

import vueQr from 'vue-qr';

定义组件

components: {
	vueQr,
}

HTML标签

<vueQr :text="qr" :size="200" :margin="10" :correctLevel="0" :whiteMargin="false" :logoSrc="logoSrc" :logoMargin="5"></vueQr>
  • 封装的 axios 方法 正常请求就行
  • 请求 sandboxPay 方法
sandboxPay().then(res => {
	//返回的二维码数据
	this.qr = res.data1;
	//返回的支付信息
	this.orderInfo = res.data2;
	if ('WebSocket' in window) {
		// 打开一个 web socket
		// 使用store防止多次创建连接
		let ws = this.$store.state.app.webSocket;
		if(!ws){
			// 新建webSocket连接
			ws = new WebSocket('ws://localhost:8080/webSocket');
			this.$store.commit('app/SET_WEBSOCKET', ws);
		}
		ws.onopen = function() {
			// Web Socket 已连接上,使用 send() 方法发送数据
			// alert('数据发送中...');
		};
		ws.onmessage = function(evt) {
			var data = JSON.parse(evt.data);
			// 接收后台推送的数据
			if (data.trade_status == 'TRADE_SUCCESS') {
				console.log('支付成功');
				_this.paySucc = true;
				// 这里支付成功操作业务
			}
			ws.close();
			_this.$store.commit('app/SET_WEBSOCKET', null);
		};
		ws.onclose = function() {
			ws.close();
			_this.$store.commit('app/SET_WEBSOCKET', null);
		};
	} else {
		// 浏览器不支持 WebSocket
		alert('您的浏览器不支持 WebSocket!');
	}
});
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值