案例代码展示
后端代码
MyWXPayConfig.java(WXPayConfig接口实现类)
package org.example.config;
import com.github.wxpay.sdk.WXPayConfig;
import java.io.InputStream;
public class MyWXPayConfig implements WXPayConfig {
@Override
//商户账号AppID
public String getAppID() {
return "信息较为敏感(可自行找微信平台申请)";
}
@Override
//商户编号
public String getMchID() {
return "信息较为敏感(可自行找微信平台申请)";
}
@Override
//商户Key(秘钥)
public String getKey() {
return "信息较为敏感(可自行找微信平台申请)";
}
@Override
public InputStream getCertStream() {
return null;
}
@Override
public int getHttpConnectTimeoutMs() {
return 0;
}
@Override
public int getHttpReadTimeoutMs() {
return 0;
}
}
OrderController.java(前端访问此controller返回支付二维码)
package org.example.controller;
import com.github.wxpay.sdk.WXPay;
import org.example.config.MyWXPayConfig;
import org.example.model.Result;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.HashMap;
import java.util.Map;
@RestController
@RequestMapping("/order")
@CrossOrigin("*")
public class OrderController {
@PostMapping("/add")
public Result add(){
//微信支付:申请支付连接
WXPay wxPay = new WXPay(new MyWXPayConfig());
//通过当前时间和随机数结合生成订单编号outTradeNo
LocalDateTime dateTime=LocalDateTime.now();
DateTimeFormatter formatter=DateTimeFormatter.ofPattern("yyyyMMddHHmmss");
String outTradeNo = formatter.format(dateTime);
outTradeNo += (int)(Math.random()*899+100) + "";
//在data集合中存储生成微信二维码的重要参数
HashMap<String, String> data = new HashMap<>();
data.put("body","可乐"); //支付说明
data.put("out_trade_no",outTradeNo); //使用当前用户订单的编号作为当前支付交易的交易号
data.put("fee_type","CNY"); //支付模式为人民币
data.put("total_fee","1"); //设置支付金额为1分
data.put("trade_type","NATIVE"); //交易的类型
//需要注意的是这是需要自己配置内网穿透地址
data.put("notify_url","http://m39shb.natappfree.cc/pay/success"); //设置支付完成时回调方法的接口
//用于储存返回给前端必要数据的map集合(按需添加但是一般订单价格,订单编号,支付二维码编号都是必须的)
HashMap<String, String> map = new HashMap<>();
//储存订单编号
map.put("number",outTradeNo);
//储存订单价格
map.put("price","18.8");
try {
//通过data集合中的重要参数请求微信服务器,然后微信服务器将参数封装到resp中
Map<String, String> resp = wxPay.unifiedOrder(data);
//通过微信服务器返回的数据得到微信支付二维码,然后通过map集合返回给前端
map.put("resp",resp.get("code_url"));
} catch (Exception e) {
e.printStackTrace();
}
//封装返回给前端的信息
return Result.buildSuccess(map,"success");
}
}
PayController.java(微信支付成功后调用的接口)
package org.example.controller;
import com.github.wxpay.sdk.WXPayUtil;
import org.example.model.Result;
import org.example.websocket.WebSocketService;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import java.util.HashMap;
import java.util.Map;
@RestController
@RequestMapping("/pay")
public class PayController {
@PostMapping("/success")
public String success(HttpServletRequest request) throws Exception{
//获取请求输入流
ServletInputStream is = request.getInputStream();
//设置每次读取的大小为1024字节
byte[] bs = new byte[1024];
int len = -1;
//将流中信息封装到StringBuffer字符串中
StringBuffer buffer = new StringBuffer();
//循环读取
while ((len = is.read(bs)) != -1){
buffer.append(new String(bs,0,len));
}
String str = buffer.toString();
//通过此方法将规范的字符串转化为map集合
Map<String, String> map = WXPayUtil.xmlToMap(str);
//确认微信支付成功
if (map != null && map.get("result_code").equalsIgnoreCase("success")){
//支付成功后给前端反馈
WebSocketService.sendMsg(map.get("out_trade_no"),"1");
//封装返回给微信服务器的信息,理由是此订单已经支付完成了,不需要再对服务器请求了
HashMap<String, String> resp = new HashMap<>();
//以下是微信服务器所必要的信息
resp.put("return_code","success");
resp.put("return_msg","ok");
resp.put("appid",map.get("appid"));
resp.put("result_code","success");
//微信需要接受的是String类型,所以需要以下方法转化为字符串
return WXPayUtil.mapToXml(map);
}
return null;
}
}
WebSocketConfig.java (websocket服务节点配置)
package org.example.websocket;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.socket.server.standard.ServerEndpointExporter;
@Configuration
public class WebSocketConfig {
@Bean
//添加websocket服务节点配置(创建一个websocket对象放到spring容器)
public ServerEndpointExporter getServerEndpointExporter(){
return new ServerEndpointExporter();
}
}
WebSocketService.java(前端后端建立webSocket连接的业务类)
package org.example.websocket;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.stereotype.Component;
import org.springframework.web.socket.server.standard.ServerEndpointExporter;
import javax.websocket.OnClose;
import javax.websocket.OnOpen;
import javax.websocket.Session;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
@Component
@ServerEndpoint("/webSocket/{oid}")
public class WebSocketService {
//将所有订单的编号对应前端反馈记录下来
private static Map<String, Session> sessionMap = new ConcurrentHashMap<>();
@OnOpen
//WebSocket连接建立成功调用此方法
public void open(@PathParam("oid") String orderId, Session session){
System.out.println("open \t orderId = " + orderId);
sessionMap.put(orderId, session);
}
@OnClose
//WebSocket连接销毁成功调用此方法
public void close(@PathParam("oid") String orderId){
System.out.println("close \t orderId = " + orderId);
sessionMap.remove(orderId);
}
//支付成功后给前端反馈对应参数
public static void sendMsg(String orderId, String msg){
try {
System.out.print("sendMsg" + "\t");
System.out.println(sessionMap);
Session session = sessionMap.get(orderId);
session.getBasicRemote().sendText(msg);
}catch (Exception e){
e.printStackTrace();
}
}
}
前端代码
<template>
<div>
<h1 v-if="options">订单编号:{{number}}</h1>
<h1 v-if="options">订单价格:{{price}}</h1>
<div id="test" >
<div v-qr="options" v-if="options"></div>
<div v-html="html"></div>
</div>
</div>
</template>
<script>
import axios from 'axios'
export default {
data() {
return {
html: "",
options: null,
number: '',
price: ''
}
},
mounted: function() {
axios.post('http://localhost:8080/order/add')
.then(res => {
console.log(res.data);
//后端返回的订单价格
this.price = res.data.data.price
//后端返回的订单编号
this.number = res.data.data.number
//生成二维码所必须的信息其中text代表二维码内容
this.options = {
text: res.data.data.resp,
render: "canvas",
width: 256,
height: 256,
typeNumber: -1,
correctLevel: 2,
background: "#ffffff",
foreground: "#000000"
}
console.log(this.options)
//设置webSocket连接路径
var webSocketUrl = "ws://localhost:8080/webSocket/" + this.number
//webSocket建立webSocket连接
var webSocket = new WebSocket(webSocketUrl)
//保存当前vue对象(此后可通过此变量改变其中属性)
let tmp=this
webSocket.onmessage = function(event){
//后端返回给前端的支付结果
var msg = event.data
console.log("支付了")
//“1”是自己在后端定义支付成功的返回值
if(msg == '1'){
console.log("支付完成!")
//支付成功后在页面上进行修饰
tmp.html = "<h2 style='font-size:20px; color:green'>订单支付完成!</h2>"
console.log(tmp.html)
tmp.options = null
}
}
})
},
methods: {
addqrcode() {
document.getElementById("test").innerHTML("<h2 style='font-size:20px; color:green'>订单支付完成!</h2>")
}
}
}
</script>
<style>
.main {
width: 200px;
height: 200px;
background-color: #fff;
border: 1px solid black;
}
</style>
微信支付的大致步骤简要分析
1. 首先准备好微信支付的开发环境与准备工作
1.1 配置maven(导入依赖)
导入spring-book依赖
<parent>
<artifactId>spring-boot-starter-parent</artifactId>
<groupId>org.springframework.boot</groupId>
<version>2.3.5.RELEASE</version>
</parent>
导入微信核心依赖wxpay-sdk
<dependency>
<groupId>com.github.wxpay</groupId>
<artifactId>wxpay-sdk</artifactId>
<version>0.0.3</version>
</dependency>
导入websocket(前后端建立长连接来进行请求响应)
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-websocket</artifactId>
<version>2.5.2</version>
</dependency>
1.2. 准备好WXPayConfig接口的实现类
准备好实现类所需要的核心参数比如
- 商户编号
- 商户账号AppID
- 商户Key
原因:之所以需要这些重要参数就是因为申请微信连接的时候微信需要通过这些信息来确定商户。
2. 前端携带必要参数发送请求与相应
2.1 后端所需要的前端参数(基础)
- 订单编号
- 订单价格
2.2 前端所需要的后端参数(基础)
- 生成二维码的编码如(“weixin://wxpay/bizpayurl?pr=vyg9Deyzz”)
- 订单编号
- 订单价格
3. 前端请求后端后所需要的必要操作
3.1 申请支付连接对象
//其中的new MyWXPayConfig()对象是实现了WXPayConfig接口的实现类
WXPay wxPay = new WXPay(new MyWXPayConfig());
3.2 准备微信生成二维码所需的必要参数(可见官网详情)
官网地址:https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter3_4_1.shtml
通过查看官网地址可以发现,生成二维码等必要参数需要一个map集合
map集合的key值及其含义
key值 | 含义 |
---|---|
body | 支付说明 |
out_trade_no | 当前支付交易的交易号 |
fee_type | 设计支付模式(CNY表示人民币支付) |
total_fee | 设置支付金额 |
trade_type | 设置交易类型(如:NATIVE) |
notify_url | 设置支付完成的回调接口 |
3.3 通过微信服务器得到支付环境参数
//通过unifiedOrder()方法向微信服务器发送请求,然后返回如(微信二维码编码、订单编号等参数)
Map<String, String> resp = wxPay.unifiedOrder(data);
通过微信服务器得到参数之后,封装属性然后返回给前端
4. 前端生成二维码的必要准备
前端的开发环境是vue3.0,用的是node项目接口
生成二维码的工具是 “vue-qrcode-directive”
npm i vue-qrcode-directive #安装二维码生成工具
代码演示
# 微信二维码放置位置
<div v-qr="options" v-if="options"></div>
this.options = {
text: "二维码中的具体内容",
render: "canvas",
width: 256,
height: 256,
typeNumber: -1,
correctLevel: 2,
background: "#ffffff",
foreground: "#000000"
}
5. 用户扫描二维码到支付成功后相关步骤
5.1 使用webSocket与后端建立长连接(等待后端支付成功的反馈)
1. 前端部分:
var webSocketUrl = "ws://localhost:8080/webSocket/" + "从后端传递过来的订单编号"
var webSocket = new WebSocket(webSocketUrl)
webSocket.onmessage = function(event){
“支付成功之后会调用该方法”
}
2. 后端部分:
- 第一步:添加websocket服务节点配置(创建一个websocket对象放到spring容器)
- 第二步: 创建websocket连接,销毁websocket连接,对前端通知支付成功等功能
2. 编写微信支付成功后微信所访问的接口
在此接口中需要:
- 接收微信传输给服务器的信息(采用io的读取方式)
- 将读取到的字符串通过WXPayUtil.xmlToMap(str)方式转化为map集合
- 结合map集合中信息去调用支付成功的(websocket连接)返回给前端的方法