我们这里只看支付,其他跟支付无关的东西不看
准备页面
主要有三个页面
第一个页面,购票页面
第二个页面,详情支付页面
紫色的三个是跟我们支付有关的
第三个页面,支付成功跳转页面
准备二维码
首先导入一个JS,二维码生成插件qrious
创建一个页面,测试一下,注意一下,这个地址
准备微信参数、环境、HttpClient工具类、雪花算法工具类。
创建一个weixinpay.properties配置文件,配置微信支付需要的参数,这里的回调地址,我们没有用到。
在主XML配置文件加载我们的微信支付配置文件。
创建一个微信支付实体类,将我们的数据注入进去。
导入微信支付SDK,开发包。
导入我们的HttpClient工具类,后面调用微信支付接口会用到。
可能会跟其他的类重名,那就在后面加个Utils
加入雪花算法工具类,后面用来生成永远不会重复的订单编号。
开始支付功能编码
创建一个WeixinPayService,有两个参数。
编写WeixinPayServiceImpl实现类
package com.movie.service.impl;
import com.github.wxpay.sdk.WXPayUtil;
import com.movie.service.WeixinPayService;
import com.movie.utils.HttpClientUtils;
import com.movie.vo.WeiXin;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import sun.net.www.http.HttpClient;
import javax.annotation.Resource;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
/**
* @BelongsProject: MovieSystem
* @BelongsPackage: com.movie.service.impl
* @Description: TODO
*/
@Service
public class WeixinPayServiceImpl implements WeixinPayService {
@Resource
WeiXin weiXin;
/**
* 调用微信接口获取支付的地址
*
* @param out_trade_no 订单号
* @param total_fee 金额(分)
* @return
*/
@Override
public Map createNative(String out_trade_no, String total_fee) {
// 1.创建参数
Map param = new HashMap();// 创建参数
//调用微信支付统一下单的接口,必须转的参数
param.put('appid', weiXin.getAppid());// 公众号
param.put('mch_id', weiXin.getPartner());// 商户号
param.put('nonce_str', WXPayUtil.generateNonceStr());//调用工具类(微信SDK的)。随机字符串
param.put('body', '北海市影城电影票');// 商品描述
param.put('out_trade_no', out_trade_no);// 商户订单号
param.put('total_fee', total_fee);// 总金额(以分为单位)
param.put('spbill_create_ip', '127.0.0.1');// IP
param.put('notify_url', 'http://test.easyshop.cn');// 回调地址(随便写)
param.put('trade_type', 'NATIVE');// 交易类型,NATIVE类型表示扫码支持
try {
// 2.生成要发送的xml(将Map集合转为XML格式),因为微信要的是XML格式
String xmlParam = WXPayUtil.generateSignedXml(param, weiXin.getPartnerkey());
//使用HttpClient远程调用工具
//https://api.mch.weixin.qq.com/pay/unifiedorder 这个是微信支付的接口
HttpClientUtils client = new HttpClientUtils('https://api.mch.weixin.qq.com/pay/unifiedorder');
client.setHttps(true);
client.setXmlParam(xmlParam);
client.post();
// 3.获得结果
// result是服务器返回的结果,是XML格式的
String result = client.getContent();
//将服务器返回的结果,XML转为Map
Map resultMap = WXPayUtil.xmlToMap(result);
//封装返回值
Map map = new HashMap<>();
map.put('code_url', resultMap.get('code_url'));// 支付地址
map.put('total_fee', total_fee);// 总金额
map.put('out_trade_no', out_trade_no);// 订单号
return map;
} catch (Exception e) {
e.printStackTrace();
return new HashMap<>();
}
}
}
注释都写得很清楚了,就不多做解释了,我们调用这个方法需要给它传递两个参数,一个是订单号、一个是总金额。最后创建一个Map,把支付地址,总金额,订单号返回。
package com.movie.web;
import com.movie.bean.Users;
import com.movie.service.BuyTicketService;
import com.movie.service.TicketService;
import com.movie.service.WeixinPayService;
import com.movie.utils.CommomResult;
import com.movie.utils.IdWorker;
import com.movie.vo.PlayVo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
import javax.servlet.http.HttpSession;
import java.util.List;
import java.util.Map;
@Controller
public class BuyTicketController {
@Autowired
BuyTicketService buyTicketService;
@Autowired
WeixinPayService weixinPayService;
@Autowired
TicketService ticketService;
/**
* 跳转支付页面,生成二维码
*
* @param play_id
* @param seats
* @return
*/
@RequestMapping(value = '/buy_toPay', method = RequestMethod.POST)
public String toPay(Integer play_id, String seats, Model model, HttpSession session) {
//要购买的电影的座位号
session.setAttribute('seats',seats);
//根据电源id查找出电影的详细信息
PlayVo vo = buyTicketService.findPlayVoById(play_id);
//一张票的价格
Double money = vo.getMoney();
//以,进行分割,看看买了几张片
StringBuffer stringBuffer = new StringBuffer();
String[] arrays = seats.split(',');
//待支付金额,总金额
Double sumMoney = money * arrays.length;
//循环遍历,加上排、座
for (String s : arrays) {
s = s.replace('-', '排') + '座';
stringBuffer.append(s + ' ');
}
model.addAttribute('seats', stringBuffer.toString());
model.addAttribute('vo', vo);
model.addAttribute('sumMoney', sumMoney);
//2.生成一个永远不会重复的订单编号
IdWorker worker = new IdWorker();
long id = worker.nextId();
String orderId = id+'';
//调用微信接口,获取二维码支付地址
Map map = weixinPayService.createNative(orderId, '1'); //微信接口支付金额默认是以分为单位
//微信要支付的地址,把这个地址生成二维码
String code_url = map.get('code_url');
model.addAttribute('code_url', code_url);
//用户支付的订单号
model.addAttribute('orderId', orderId);
return 'user_film_pay';
}
}
注释都写得很清楚了,就不多做解释了,把得到的支付地址跟订单号存放在model,给它返回回去。
页面显示二维码
测试一下:
OK,没有问题
开始支付功能编码
我们扫码支付可以了,那么问题来了,我们扫码了之后,页面怎么知道它支付了还是没支付呢?
这就要查询订单的支付状态了,根据订单号查询订单的支付状态,看看它执行成功了还是失败。
在service下添加一个查询订单状态的方法
同上面一样,实现它
/**
* 根据订单号查询订单的支付状态
* @param out_trade_no
* @return
*/
@Override
public Map queryPayStatus(String out_trade_no) {
Map param = new HashMap();
param.put('appid', weiXin.getAppid());// 公众账号ID
param.put('mch_id', weiXin.getPartner());// 商户号
param.put('out_trade_no', out_trade_no);// ----->订单号
param.put('nonce_str', WXPayUtil.generateNonceStr());// 随机字符串
//查询订单支付状态接口
String url = 'https://api.mch.weixin.qq.com/pay/orderquery';
try {
String xmlParam = WXPayUtil.generateSignedXml(param, weiXin.getPartnerkey());
HttpClientUtils client = new HttpClientUtils(url);
client.setHttps(true);
client.setXmlParam(xmlParam);
//发送POS请求
client.post();
//调用接口返回的结果
String result = client.getContent();
//把返回的XML文件格式转为Map
Map map = WXPayUtil.xmlToMap(result);
System.out.println('---------------服务器端接口返回的支付结果:-----------------------');
if(map!=null){
Set> entries = map.entrySet();
for (Map.Entry entry : entries) {
System.out.println(entry.getKey()+'--->'+entry.getValue());
}
}
return map;
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
传递一个订单号,返回Map结果
编写Controller,跟之前的Controller是同一个类
流程是这样的:我们传递一个订单号,会调用service去查询订单的状态,会返回一个Map。我们对Map进行判断,如果Map不为空,并且里面的trade_state的值为SUCCESS,则说明支付成功。如果Map为空,则说明支付异常,失败。那么还有一个问题,如果用户打开了页面,但是他就是不扫码支付呢?我们的请求只发送一次查询,是不是不合适啊?我们可以给它来一个线程,让它每3秒钟执行一次,来一个循环,时间为5分钟,如果5分钟内没有扫码,则支付超时。
/**
* 根据订单号查询订单支付的状态--轮询订单支付状态接口
*
* @param out_trade_no
* @return
*/
@ResponseBody
@RequestMapping('/pay_queryPayStatus')
public CommomResult queryPayStatus(String out_trade_no,Integer play_id,HttpSession session) {
CommomResult commonResult = null;
try {
//查询支付的订单号是:1320615983324332000
System.out.println('查询支付的订单号是:'+out_trade_no);
System.out.println('查询支付的订单play_id:'+play_id);
String seats=(String)session.getAttribute('seats');// 3-4,5-6,7-8
Users loginUser=(Users) session.getAttribute('loginUser');
commonResult = null;
int x = 0;
while (true) {
//查询订单支付状态
Map map = weixinPayService.queryPayStatus(out_trade_no);
if (map != null) {
if (map.get('trade_state').equals('SUCCESS')) {// 如果成功
//支付成功,把支付成功的数据,存入数据中,后期可以查看订单
String[] seatNumers = seats.split(',');
for (String seatName : seatNumers) {
int seat_id = ticketService.getSeatIdBySeatName(seatName);
//插入到订单表 ticket表中
ticketService.addTicket(out_trade_no,play_id,loginUser.getUser_id(),seat_id);
}
commonResult = new CommomResult(200, '支付成功');
break;
}
}
if (map == null) {
commonResult = new CommomResult(500, '支付出现异常!失败');
break;
}
try {
Thread.sleep(3000);//睡眠3秒钟,每3秒钟执行一次
} catch (InterruptedException e) {
e.printStackTrace();
}
//为了不让循环无休止地运行,我们定义一个循环变量,如果这个变量超过了这个值则退出循环,设置时间为5分钟
x++;
if (x >= 60) {
commonResult = new CommomResult(502, '支付超时!');
break;
}
}
} catch (Exception e) {
commonResult = new CommomResult(500, '支付异常!');
e.printStackTrace();
} finally {
}
return commonResult;
}
编写页面
我们什么时候开始调用Controller查询订单状态的方法呢?那当然是在页面一打开,二维码出来就开始调用了
启动测试一下:
等到5分钟没有支付,超时
点击确认,页面刷新,扫码支付
支付成功,跳到成功页面
————————
文章为作者独立观点,不代表 股票程序化软件自动交易接口观点