<dependency>
<groupId>com.github.wxpaygroupId>
<artifactId>wxpay-sdkartifactId>
<version>0.0.3version>
dependency>
HashMap<String, String> map = new HashMap<>();
//商品描述
map.put("body","商品名字");
//交易号
map.put("out_trade_no","交易号 可以是订单号 或者其他的");
//交易币种
map.put("fee_type","CNY");
//交易金额 以分为单位 这里为了测试 用1分
map.put("total_fee","1");
//交易类型
map.put("trade_type","NATIVE");
//支付完成时的回调方法接口
map.put("notify_url","/pay/success");
WXPay wxPay = new WXPay(new WxPayConfig());
//发送支付请求
Map<String, String> resp = wxPay.unifiedOrder(map);
创建对象的过程中我们又被迫的需要创建交易成功后信息回调的接口以及微信支付中的商家信息配置
package com.llf.Config;
import com.github.wxpay.sdk.WXPayConfig;
import java.io.InputStream;
/**
创建wxpay config类实现wxpayconfig接口 重写其中的部分方法
其中用到的三个参数 都是在微信支付申请成功之后 微信官方给予的
**/
public class WxPayConfig implements WXPayConfig {
@Override
public String getAppID() {
// 返回的是商家的appId
return "wx632c8f211f8122c6";
}
@Override
public String getMchID() {
// 返回的是商家的id
return "1497984412";
}
@Override
public String getKey() {
//返回的是商家的密钥
return "sbNCm1JnevqI36LrEaxFwcaT0hkGxFnC";
}
@Override
public InputStream getCertStream() {
return null;
}
@Override
public int getHttpConnectTimeoutMs() {
return 0;
}
@Override
public int getHttpReadTimeoutMs() {
return 0;
}
}
信息回调的接口
package com.llf.Controller;
import io.swagger.annotations.Api;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/pay")
@Api(value = "微信支付方法回调",tags = "微信支付成功的方法回调")
public class PayController {
/**
* 微信支付成功的回调方法
*/
@PostMapping("/success")
public void success(){
}
}
一切信息都设置好以后 当我们访问成功后 微信官方会给我们返回一个map信息集合
Map<String, String> resp = wxPay.unifiedOrder(map);
/**
这个resp 就是返回的信息集合 其中有一个code_url的属性 就是进行支付的具体地址 可以将这个地址一起响应给前端 前端通过二维码生成 将链接生成二维码 完成支付
**/
// 将支付的url传给前端
orderInfo.put("payUrl",resp.get("code_url"));
resultVO=new ResultVO(ResStatus.OK,"提交订单成功",orderInfo);
生成二维码 我们需要用到两个js文件的帮助 一个是jquary 一个是qrcode 其中qrcode就是生成二维码的工具,
前端通过
localStorage.setItem("orderInfo",JSON.stringify(存储信息的对象));
方法将参数传送到支付页面 ,支付页面使用let jsonstr = localStorage.getItem("orderInfo");
方法 来获取支付需要的url
<div id="div1">
<div id="payQrcodeDiv" style="width: 200px; height: 200px;">div>
div>
// 接着对二维码进行模板渲染 由于我们使用vue进行数据渲染 因此需要创建mounted 钩子函数 实现渲染
mounted:function (){
// 定位到我们设置的div
let qrCode = new QRCode($("#payQrcodeDiv")[0],{
height:200,
width:200
});
//生成二维码 将支付的url放到这个方法中
qrCode.makeCode(this.orderInfo.payUrl);
}
DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no">
<title>购物车页面title>
<link href="static/css/amazeui.css" rel="stylesheet" type="text/css" />
<link href="static/css/demo.css" rel="stylesheet" type="text/css" />
<link href="static/css/cartstyle.css" rel="stylesheet" type="text/css" />
<link href="static/css/optstyle.css" rel="stylesheet" type="text/css" />
<script type="text/javascript" src="static/js/jquery.js">script>
head>
<body>
<div id="container">
<div class="am-container header">
<ul class="message-l">
<div class="topMessage">
<div class="menu-hd">
<a href="#" target="_top" class="h">亲,请登录a>
<a href="#" target="_top">免费注册a>
div>
div>
ul>
<ul class="message-r">
<div class="topMessage home">
<div class="menu-hd"><a href="index.html" target="_top" class="h">商城首页a>div>
div>
<div class="topMessage my-shangcheng">
<div class="menu-hd MyShangcheng"><a href="#" target="_top"><i class="am-icon-user am-icon-fw">i>个人中心a>div>
div>
<div class="topMessage mini-cart">
<div class="menu-hd"><a id="mc-menu-hd" href="shopcart.html" target="_top"><i class="am-icon-shopping-cart am-icon-fw">i><span>购物车span><strong id="J_MiniCartNum" class="h">0strong>a>div>
div>
<div class="topMessage favorite">
<div class="menu-hd"><a href="#" target="_top"><i class="am-icon-heart am-icon-fw">i><span>收藏夹span>a>div>
ul>
div>
<div class="nav white">
<div class="logo"><img src="static/images/logo.png" />div>
<div class="logoBig">
<li><img src="static/images/logobig.png" />li>
div>
<div class="search-bar pr">
<a name="index_none_header_sysc" href="#">a>
<form>
<input id="searchInput" name="index_none_header_sysc" type="text" placeholder="搜索" autocomplete="off">
<input id="ai-topsearch" class="submit am-btn" value="搜索" index="1" type="submit">
form>
div>
div>
<div class="clear">div>
<div style="background: lightgrey; height: 600px; margin-top: 40px; padding: 15px;">
<div style="width:65%; background: white; height: 540px; margin: auto; padding-left: 50px;">
======================================================================================== 关键渲染区域
<p> p>
<p> p>
<h3>订单编号:{
{orderInfo.orderId}}h3>
<p> p>
<h3>订单金额:¥ {
{orderInfo.totalPrice}}h3>
<hr/>
<div id="div1">
<div id="payQrcodeDiv" style="width: 200px; height: 200px;">div>
div>
================================================================================
div>
div>
<div class="theme-popover-mask">div>
<div class="theme-popover">
<div class="theme-span">div>
<div class="theme-poptit h-title">
<a href="javascript:;" title="关闭" class="close">×a>
div>
<div class="theme-popbod dform">
<form class="theme-signin" name="loginform" action="" method="post">
<div class="theme-signin-left">
<li class="theme-options">
<div class="cart-title">颜色:div>
<ul>
<li class="sku-line selected">12#川南玛瑙<i>i>li>
<li class="sku-line">10#蜜橘色+17#樱花粉<i>i>li>
ul>
li>
<li class="theme-options">
<div class="cart-title">包装:div>
<ul>
<li class="sku-line selected">包装:裸装<i>i>li>
<li class="sku-line">两支手袋装(送彩带)<i>i>li>
ul>
li>
<div class="theme-options">
<div class="cart-title number">数量div>
<dd>
<input class="min am-btn am-btn-default" name="" type="button" value="-" />
<input class="text_box" name="" type="text" value="1" style="width:30px;" />
<input class="add am-btn am-btn-default" name="" type="button" value="+" />
<span class="tb-hidden">库存<span class="stock">1000span>件span>
dd>
div>
<div class="clear">div>
<div class="btn-op">
<div class="btn am-btn am-btn-warning">确认div>
<div class="btn close am-btn am-btn-warning">取消div>
div>
div>
<div class="theme-signin-right">
<div class="img-info">
<img src="static/images/kouhong.jpg_80x80.jpg" />
div>
<div class="text-info">
<span class="J_Price price-now">¥39.00span>
<span id="Stock" class="tb-hidden">库存<span class="stock">1000span>件span>
div>
div>
form>
div>
div>
<div class="navCir">
<li><a href="home.html"><i class="am-icon-home ">i>首页a>li>
<li><a href="sort.html"><i class="am-icon-list">i>分类a>li>
<li class="active"><a href="shopcart.html"><i class="am-icon-shopping-basket">i>购物车a>li>
<li><a href="person/index.html"><i class="am-icon-user">i>我的a>li>
div>
div>
<script type="text/javascript" src="static/js/jquery-1.9.min.js" >script>
<script type="text/javascript" src="static/js/Cookie_Utils.js" >script>
<script type="text/javascript" src="static/js/vue.js" >script>
<script type="text/javascript" src="static/js/axios.min.js" >script>
<script type="text/javascript" src="static/js/qrcode.min.js" >script>
<script type="text/javascript">
let vm = new Vue({
el:"#container",
data:{
orderInfo:{
}
},
created:function(){
let jsonstr = localStorage.getItem("orderInfo");
if(jsonstr!=null){
localStorage.removeItem("orderInfo");
}
this.orderInfo = eval("("+jsonstr+")");
},
//二维码渲染要在模板渲染之后 因此需要用到mounted 钩子函数
mounted:function (){
let qrCode = new QRCode($("#payQrcodeDiv")[0],{
height:200,
width:200
});
qrCode.makeCode(this.orderInfo.payUrl);
}
});
script>
body>
html>
前端付款成功后 微信会向我们设置的信息回调接口发送反馈信息 ,我们要接收这些信息 进行处理
微信如何访问我们?
因为我们的程序在本地部署,因此微信是不能直接访问的,所以我们需要借助Ngrok来进行内网穿透,只有这样我们才能接收到微信反馈给我们的信息。
Ngrok的具体使用方法 可以参考
Ngrok内网穿透
这篇博客,里面介绍了使用方法
Ngrok配置完成后 把我们设置的信息回调地址转换成 Ngrok生成的地址
map.put("notify_url","/pay/success"); 换成
map.put("notify_url","http://0565-2408-8220-201b-ca00-488f-47a9-9a54-6964.ngrok.io/pay/success");
地址设置完成之后,对我们的回调接口进行设置
接收微信信息 并进行处理
package com.llf.Controller;
import com.github.wxpay.sdk.WXPayUtil;
import com.llf.Service.ServiceImpl.OrdersServiceImpl;
import com.llf.WebStocket.WebSocketServer;
import io.swagger.annotations.Api;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpRequest;
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 javax.servlet.http.HttpServletResponse;
import java.util.HashMap;
import java.util.Map;
@RestController
@RequestMapping("/pay")
@Api(value = "微信支付方法回调",tags = "微信支付成功的方法回调")
public class PayController {
@Autowired
private OrdersServiceImpl ordersService;
/**
* 微信支付成功的回调方法
*/
@RequestMapping("/success")
public String success(HttpServletRequest request) throws Exception{
//获得响应流
ServletInputStream inputStream = request.getInputStream();
//将响应的流文件转换成byte类型
byte[] bs = new byte[1024];
int len=-1;
StringBuilder stringBuilder = new StringBuilder();
while((len=inputStream.read(bs))!=-1){
stringBuilder.append(new String(bs,0,len));
}
//得到xml类型的响应流
String s = stringBuilder.toString();
//使用帮助类将xml文件还原为map文件
Map<String, String> map = WXPayUtil.xmlToMap(s);
//如果支付成功
if(map!=null&&"success".equalsIgnoreCase(map.get("result_code"))){
//修改订单状态为2 已支付
String orderId=map.get("out_trade_no");
int i = ordersService.updateStatusByOrderId(map.get("out_trade_no"), 2);
//如果i<0 表示支付状态改变失败 微信则会继续通知 如果改变成功 即告诉微信平台 已接收回调信息
if (i>0){
Map<String, String> resp = new HashMap<>();
//响应微信平台 响应码
resp.put("return_code","success");
//响应信息
resp.put("return_msg","OK");
//响应的商家id
resp.put("appid",map.get("appid"));
//结果信息
resp.put("result_code","success");
return WXPayUtil.mapToXml(resp);
}
}
return null;
}
}
前端付款成功后,我们需要进行支付结果提示,当付款成功后 我们应该显示付款成功的提示。那应该如何实现呢?
方法一、因为付款成功后 该订单的状态会变成2,那么我们设置一个定时任务 每隔1秒钟访问一次后端接口 知道得到该订单的状态为2时 再清除这个定时任务
优点:实现简单 前端设置定时任务 后端写接口 就可以完成
缺点:当并发量足够大的时候,每个用户每一秒钟都对该接口进行访问 加大服务器的访问量 容易使服务器崩溃
方法二、使用webSocket 让前端进入支付页面之后 就与后端建立一个长连接 当前端付款成功后 后端通过webSocket向前端发送消息 告诉前端支付成功 前端进行支付页面内容的改变
优点:对服务器的访问压力小 一次支付只需要访问一次就行
缺点:相较于方法一 实施起来较麻烦
方法一实现
<div id="div1">
// 通过v-if 判断状态的值 进行不同效果的显示
<div id="payQrcodeDiv" style="width: 200px; height: 200px;" v-if="status==1"></div>
<div v-else><span>支付成功</span></div>
</div>
//设置定时任务 一秒钟访问一次后端接口 直到支付状态为已支付 弊端是当并发量够多时,对后端接口访问量会很大
let i=setInterval(function (){
axios({
url:baseUrl+"orders/status/"+orderId,
method:"get",
headers:{
token:getCookieValue("token")
}
}).then((res)=>{
if (res.data.data==1){
//表示未支付
vm.status=1
}else if (res.data.data==2){
vm.status=2
//状态为二 表示以完成支付 清除定时任务
clearInterval(i);
}
})
},1000)
//controller
@GetMapping("status/{orderId}")
public ResultVO quarryStatusByOrderId(@PathVariable String orderId,@RequestHeader String token){
return ordersService.quarryStatusByOrderId(orderId);
}
//service
/**
* 根据订单编号 查询订单状态
* @param orderId
* @return
*/
ResultVO quarryStatusByOrderId(String orderId);
//serviceImpl
public ResultVO quarryStatusByOrderId(String orderId) {
Orders orders = ordersMapper.selectById(orderId);
if (orders!=null){
return new ResultVO(ResStatus.OK,"success",orders.getStatus());
}else {
return new ResultVO(ResStatus.NO,"ERROr",null);
}
}
方法二实现
使用webSocket首先需要导入websocket依赖
<!--webSocket 长连接-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-websocket</artifactId>
</dependency>
由于已经被springboot托管 因此这个依赖不需要导入版本号
配置WebSocket的conig类和服务器端
config配置类
@Configuration
public class WebSocketConfig {
@Bean
public ServerEndpointExporter getServerEndpointExporter(){
return new ServerEndpointExporter();
}
}
Server服务端
package com.llf.WebStocket;
import org.springframework.stereotype.Component;
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.concurrent.ConcurrentHashMap;
@Component
//前端进行访问的地址 类似于controller的地址
@ServerEndpoint("/webSocket/{orderId}")
public class WebSocketServer {
private static ConcurrentHashMap<String, Session> sessionMap = new ConcurrentHashMap<>();
/**前端发送请求建立websocket连接,就会执行@OnOpen方法**/
@OnOpen
public void open(@PathParam("orderId") String orderId, Session session){
System.out.println("------------建立连接:"+orderId);
sessionMap.put(orderId,session);
}
/**前端关闭页面或者主动关闭websocket连接,都会执行close**/
@OnClose
public void close(@PathParam("orderId") String orderId){
sessionMap.remove(orderId);
}
/**当微信支付成功调用该方法 向前端通知**/
public static void sendMsg(String orderId,String msg){
try {
Session session = sessionMap.get(orderId);
session.getBasicRemote().sendText(msg);
}catch (Exception e){
e.printStackTrace();
}
}
}
在回调信息接口类中引用server类 向前端进行消息推送
package com.llf.Controller;
import com.github.wxpay.sdk.WXPayUtil;
import com.llf.Service.ServiceImpl.OrdersServiceImpl;
import com.llf.WebStocket.WebSocketServer;
import io.swagger.annotations.Api;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpRequest;
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 javax.servlet.http.HttpServletResponse;
import java.util.HashMap;
import java.util.Map;
@RestController
@RequestMapping("/pay")
@Api(value = "微信支付方法回调",tags = "微信支付成功的方法回调")
public class PayController {
@Autowired
private OrdersServiceImpl ordersService;
/**
* 微信支付成功的回调方法
*/
@RequestMapping("/success")
public String success(HttpServletRequest request) throws Exception{
//1、获得响应流
ServletInputStream inputStream = request.getInputStream();
//将响应的流文件转换成byte类型
byte[] bs = new byte[1024];
int len=-1;
StringBuilder stringBuilder = new StringBuilder();
while((len=inputStream.read(bs))!=-1){
stringBuilder.append(new String(bs,0,len));
}
//得到xml类型的响应流
String s = stringBuilder.toString();
//使用帮助类将xml文件还原为map文件
Map<String, String> map = WXPayUtil.xmlToMap(s);
//如果支付成功
if(map!=null&&"success".equalsIgnoreCase(map.get("result_code"))){
//2、修改订单状态为已支付
String orderId=map.get("out_trade_no");
int i = ordersService.updateStatusByOrderId(map.get("out_trade_no"), 2);
//3、向前端websocket发送信息 1表示支付成功
添加这句话
WebSocketServer.sendMsg(orderId,"1");
//4、如果i<0 表示支付状态改变失败 微信则会继续通知 如果改变成功 即告诉微信平台 已接收回调信息
if (i>0){
Map<String, String> resp = new HashMap<>();
//响应微信平台 响应码
resp.put("return_code","success");
//响应信息
resp.put("return_msg","OK");
//响应的商家id
resp.put("appid",map.get("appid"));
//结果信息
resp.put("result_code","success");
return WXPayUtil.mapToXml(resp);
}
}
//支付失败 状态改变失败 都返回null
return null;
}
}
后端设置完成后 在前端也要进行简单设置
//方法二、 向后端建立webSocket链接
let socket = new WebSocket("ws://localhost:8080/webSocket/"+orderId);
//只要后端向前端发送消息 前端就接收它
socket.onmessage=function (event){
let msg = event.data;
if (msg=="1"){
vm.status=2
}
}
接收到信息 进行判断 如果为1 表示支付成功 则在div中显示状态为2的效果