使用微信支付,需要新建两个模块,一个是interface,另一个是service。
在service中引入微信支付的依赖。
<dependency>
<groupId>com.github.wxpay</groupId>
<artifactId>wxpay-sdk</artifactId>
<version>0.0.3</version>
</dependency>
QR二维码的生成
使用生成二维码的qrious二维码插件来生成二维码。
经过删减之后的index中生成二维码的代码如下:(单单只生成一个二维码)
<!DOCTYPE html>
<html lang="zh">
<head>
</head>
<body>
<img id="qrious">
<script src="./dist/umd/qrious.js"></script>
<script>
(function() {
var qr = window.qr = new QRious({
element: document.getElementById('qrious'),
size: 200, //这里是二维码的大小
value: 'http://www.baidu.com', //这里输入二维码
level:'H' //容错度,最低是L,最高是H(一共四级别)
})
})()
</script>
</body>
</html>
微信支付第三方平台
参考的是微信支付的第三方api。
统一下单思路说明
微信支付二维码实现思路
在cart-web模块中的controller中实现生成二维码的方法,传递需要的参数
在这里使用了定义的searchPayLogFromRedis方法来查找支付订单,获取订单中的金额
实现类如下:(在生成商品订单的同时生成了支付订单,同时存入了redis)
1、在interface中创建生成二维码的方法
2、在impl中实现方法
需要将配置文件中的信息引入到当前类中
通过httpclient请求来发送获取支付链接的请求,请求中包括微信支付的api所有需要的参数数据,将所有参数封装到requestMap中通过httpclient发送。
注意:微信支付api需要的数据是xml格式。需要使用微信提供的工具类来将map转成xml。
然后再通过工具类获取微信支付的返回值,返回的数据是xml格式,需要手动转成map,使用的还是微信提供的工具类。
返回的数据中包含很多信息。我们只需要三个:一个是商户订单号(我们传入的),一个是支付金额(我们传入的),还有一个是返回的支付链接。所以再次封装一个responseMap,存放这三个数据,返回。
@Override //商户订单号,支付金额
public Map createNative(String out_trade_no, String total_fee) {
try {
//通过我们的common模块发送http请求,请求地址是微信支付提供的支付链接请求的接口地址
HttpClientUtil clientUtil = new HttpClientUtil("https://api.mch.weixin.qq.com/pay/unifiedorder");
Map requestMap = new HashMap<>();
requestMap.put("appid", appid);
requestMap.put("mch_id", mch_id);
requestMap.put("nonce_str", WXPayUtil.generateNonceStr());
requestMap.put("body", "商城中的商品");
requestMap.put("out_trade_no", out_trade_no);
requestMap.put("total_fee", total_fee);
requestMap.put("spbill_create_ip","127.0.0.1");
requestMap.put("notify_url", notifyurl);
requestMap.put("trade_type", "NATIVE");
//微信工具类可以将map转xml的String,采用api:generateSignedXml
String requestXml = WXPayUtil.generateSignedXml(requestMap, partnerkey);
clientUtil.setXmlParam(requestXml);
//需要设置请求的协议
clientUtil.setHttps(true);
//发送请求
clientUtil.post();
//接收参数
String content = clientUtil.getContent();
System.out.println("========================收到的xml"+content);
Map<String, String> map = WXPayUtil.xmlToMap(content);
//map中的数据太多,我们暂时不需要,并且我们需要返回订单号和支付金额,所以再封装一个新的map。
Map responseMap = new HashMap<>();
responseMap.put("out_trade_no", out_trade_no);
responseMap.put("total_fee", total_fee);
responseMap.put("code_url", map.get("code_url"));
return responseMap;
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
返回到前端页面上(cart-web),在js文件中完成二维码的生成,在页面绑定名字为“qrious”的id。
支付查询状态(查询是否支付了)
前端在生成二维码的方法下调用查询状态的方法。
前端js文件controller中的查询状态的方法中,通过返回值判断状态,并做处理。
在后端(pay_service)模块中创建对应方法
还是使用微信支付的查询状态的api。
使用的依然是httpclientutil工具类。
将需要的参数封装到map中,转成xml格式发送请求。
接收到的xml转成map直接返回即可。
@Override
public Map queryPayStatus(String out_trade_no) {
try {
HttpClientUtil clientUtil = new HttpClientUtil("https://api.mch.weixin.qq.com/pay/orderquery");
Map requestMap = new HashMap<>();
requestMap.put("appid", appid);
requestMap.put("mch_id", mch_id);
requestMap.put("nonce_str", WXPayUtil.generateNonceStr());
requestMap.put("out_trade_no", out_trade_no);
//微信工具类可以将map转xml的String,采用api:generateSignedXml
String requestXml = WXPayUtil.generateSignedXml(requestMap, partnerkey);
clientUtil.setXmlParam(requestXml);
//需要设置请求的协议
clientUtil.setHttps(true);
//发送请求
clientUtil.post();
//接收参数
String content = clientUtil.getContent();
Map<String, String> responseMap = WXPayUtil.xmlToMap(content);
//直接将微信返回的内容往前端返
return responseMap;
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
在调用这个方法的controller(cart-web)中,使用死循环的方式,间断查询订单状态。
@RequestMapping("/queryPayStatus")
public Result queryPayStatus(String out_trade_no){
int timer = 1; //定义一个记录次数
while(true){
Map map = weixinPayService.queryPayStatus(out_trade_no);
if (map== null){
return new Result(false,"付款失败!");
}
//这里是微信支付的状态,参考微信支付api
if("SUCCESS".equals(map.get("trade_state"))){
weixinPayService.updateOrderStatus(out_trade_no, map.get("transaction_id").toString());
return new Result(true,"付款成功!");
}
//设置支付超出时间
if(timer > 6){ //判断30秒内需要完成支付动作,否则超时
System.out.println("=支付超时=");//如果关闭了客户端页面,显示完支付超时后,就不再执行查询订单状态操作,不会一直死循环
return new Result(false,"timeout");
}
timer ++;
try {
Thread.sleep(5000);//5秒查询一个订单状态
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
测试支付结果的演示
对接订单生成逻辑&生成支付单完成支付
思路说明:在生成商品订单时,要同时生成支付订单。(在order_service模块中的OrderServiceImpl类中的add方法中)
//生成支付单
TbPayLog payLog = new TbPayLog();
payLog.setTradeState("0"); //0未支付 1已支付
payLog.setUserId(order.getUserId());
payLog.setTotalFee((long) (payMoney * 100)); //支付总金额
payLog.setPayType("1"); //1.微信支付,2.货到付款
payLog.setCreateTime(new Date());
//去掉左括号和右括号,最好也去掉中间的空格
payLog.setOrderList(ordersIds.toString().replace("[", "").replace("]", "").replaceAll(" ", ""));
payLog.setOutTradeNo(idWorker.nextId() + ""); //支付单号采用uuid
//保存数据库
payLogMapper.insert(payLog);
//将payLog对象存到redis中
redisTemplate.boundHashOps("payLog").put(payLog.getUserId(), payLog);
付款成功后修改订单状态&支付单状态
传入两个参数,一个是订单id,一个是微信流水号,修改支付单的状态,再修改订单的状态(注意修改每一个商品的状态)。最后清空redis中的缓存。
@Override
public void updateOrderStatus(String out_trade_no, String transaction_id) {
//1.修改支付日志状态
TbPayLog payLog = payLogMapper.selectByPrimaryKey(out_trade_no);
payLog.setPayTime(new Date());
payLog.setTradeState("1");//已支付
payLog.setTransactionId(transaction_id);//微信内部交易号
payLogMapper.updateByPrimaryKey(payLog);
//2.修改本次支付所关联订单状态为"2"已付款
String orderList = payLog.getOrderList();//获取订单号列表
String[] orderIds = orderList.split(",");//获取订单号数组
//状态:1、未付款,2、已付款,3、未发货,4、已发货,5、交易成功,6、交易关闭,7、待评价
for(String orderId:orderIds){
TbOrder order = orderMapper.selectByPrimaryKey( Long.parseLong(orderId) );
if(order!=null){
order.setStatus("2");//已付款
order.setPaymentTime(new Date());//订单更新付款时间
orderMapper.updateByPrimaryKey(order);
}
}
//3.清除redis中保存的支付缓存数据
redisTemplate.boundHashOps("payLog").delete(payLog.getUserId());
}