前言
这次分享的是java对接微信的支付接口,实现电脑端扫码支付后,跳转支付成功页面的例子。之所以分享是微信的Api太坑了。留下的文档也少,对接过程中容易出现各种各样的问题,在实现这扫码支付功能的时候出现了太多问题。整整对接了5天,每天下班就解决点。仅此分享出来供大家参考,结合自己的业务实现支付功能,要是有问题及时交流哦。不过本次的demo是测试通过才分享的。此处的扫码支付是基于 微信模式二的扫码支付
一、微信扫码支付的条件
1、首先要有 appid ,mchId(微信支付商户号)、apiKey(用户的私钥)有这些才能去对接支付接口。想要获取以上参数前提就是有自己的营业执照去微信官方申请微信支付。
2、条件1是对接的第三方支付。所以才必须要有营业执照。如果只是个人账号则需要对接第四方支付了。不过论稳定性肯定是第三方支付好。因为第四方支付都是基于app监听来实现的。
3、找到微信支付的官方文档去参考,虽然内容不详细,但是也能起参考左右、地址如下:
微信扫码支付:https://pay.weixin.qq.com/wiki/doc/api/native.php?chapter=6_5
二、开始上扫码支付的效果图
- 支付入口
2、点击立即支付跳转到扫码页面
页面做到比较简陋,这里是基于链接生成二维码功能将请求微信下单接口返回的二维码链接转换成二维码。
使用技术:QRCode.js
参考地址:https://www.runoob.com/w3cnote/javascript-qrcodejs-library.html
3、然后拿起微信扫一扫,支付成功后跳转到自己定义的支付成功页面。
三、开始上代码
1、跳转支付页面
/**
* 跳转支付测试页面
*/
@RequestMapping("/toIndex")
public String toIndex(){
return "index";
}
2、拼装支付参数请求微信统一下单接口
此初的工具类都是基于微信官方sdk提供稍加改造,但是基本内容都没有变,
参考地址: https://pay.weixin.qq.com/wiki/doc/api/native.php?chapter=11_1
/**
* 跳转微信支付扫码页面
* @param orderId 是定位到某个商品的标识,用来点击商品后跳转支付页面
* @throws Exception
*/
@RequestMapping("weixinPay")
public String weixinPay(Model model, String orderId) throws Exception{
log.info("点击支付按钮跳转扫码页面商品Id",orderId);
//系统内部序列,此处是单机模式下使用的序列,如果分布式需要结合redis实现分布式序列
GenerateSequenceUtil sequenceUtil=new GenerateSequenceUtil();
String outTradeNo = sequenceUtil.generateSequenceNo();
//此处使用Map当做微信接口的入参,其他开发者可根据自己需求转成bean封装成支付req
Map map = getMap(outTradeNo);
/**
* 生成带有 sign 的 XML 格式字符串
* 微信支付是xml入参请求
*/
String xml=WXPayUtil.generateSignedXml(map,apiKey);
log.info("微信PC端扫码支付请求同一下单接口入参:{}",xml);
// 发送支付请求
String resultStr = WeixinUtil.postXml(payOrderUrl, xml);
log.info("result=" + resultStr);
// xml转map
Map<String, Object> resultMap = XmlUtil.parseXml(resultStr);
//签名验证,根据需要选择是否打开
// 校验返回结果 签名
/** String resultSign = SignUtil.sign(resultMap, apiKey);
if (resultMap.get("sign") == null || !resultMap.get("sign").equals(resultSign)) {
log.info("sign is not correct, " + resultMap.get("sign") + " " + resultSign);
throw new RuntimeException("签名校验不通过");
}**/
//支付结果表
PayOrderResult result = BeanUtil.map2Object(PayOrderResult.class, resultMap);
//封装页面返参
WeixinPageResult pageResult = WeixinPageResult.builder()
.codeUrl(result.getCodeUrl()).orderId(outTradeNo)
.weixinOrderId(result.getPrepayId()).build();
model.addAttribute("pageResult",pageResult);
/**
* 根据自己业务记录支付流水表,比如扫码时记录,
* 支付后将支付流水表的支付结果修改成已支付,
* 此处需要根据自己的业务定,给出自己的预支付流水表的表结构,仅供参考
*/
ResDetail resDetail=new ResDetail();
//int i = orderService.saveResDetail(resDetail);
return "weixinPay";
}
3、将微信统一下单接口中的返参中codeurl生成二维码
微信接口返参如下
参考地址微信官方Api: https://pay.weixin.qq.com/wiki/doc/api/native.php?chapter=9_1
4、如何扫码支付后去跳转支付页面
PS:这才是微信比较坑的地方,扫码支付后不会自动跳转,像支付宝就会有。这个分享的demo是页面写的定时器,定时去调用微信的查询订单接口,查到已支付后就跳转页面。
此方案引发的问题: 可能大家一定会说,
(1)、页面的参数是不安全的,万一有人改了参数,你这次支付就有风险了,
(2)、页面定时去调用后台很可能引发性能问题。
==解决方案:==针对以上问题也可以避免掉的。比如针对问题1:我们可以禁止页面调试,F12无效、不能复制、粘贴修改。但是这都是低层次的。这些手段也能被破解。此处是生成的唯一单号,只要修改页面参数扫码支付后就不会跳转支付成功页面,这样就造成了付完钱也拿不到资源。除非不差钱的兄弟会这样做。这样基本上对自己的业务没有影响。
针对问题2:此处性能可以调大定时的时间,但是因为频繁访问就规避了,那高并发就更不要尝试了,只有遇到瓶颈才能优化哦。
此处上前端定时的代码
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<meta name="viewport" content="width=device-width,initial-scale=1,user-scalable=no" />
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
<link rel="stylesheet" type="text/css" href="../css/appliystyle.css" />
<script type="text/javascript" src="../weixin/jquery.min.js"></script>
<script type="text/javascript" src="../weixin/qrcode.min.js"></script>
<script type="text/javascript" src="../jquery.min.js"></script>
<script type="text/javascript" src="../vector.js"></script>
</head>
<body>
<div id="container">
<div id="output">
<input id="text" type="hidden" th:value="${pageResult.codeUrl}" style="width:80%" /><br />
<input name="orderId" type="hidden" th:value="${pageResult.orderId}" />
<input name="weixinOrderId" type="hidden" th:value="${pageResult.weixinOrderId}" />
<div style=" background:#5cb85c; color:#5cb85c;border:1px solid blue;height:300px;width:300px;justify-content:center;align-items:center;display:-webkit-flex;width: 230px; height: 230px; position: absolute; left: 50%; top: 50%; margin: -50px 0 0 -50px;border:3px solid #000">
<div id="qrcode" style="border:1px solid blueviolet;height:200px;width:200px;"></div>
</div>
</div>
</div>
<script type="text/javascript" th:src="@{/weixin/weixinPay.js}"></script>
<script th:inline="javascript">
var orderOut = [[${pageResult.orderId}]];
var transactionId = [[${pageResult.weixinOrderId}]];
</script>
<script type="text/javascript">
var qrcode = new QRCode(document.getElementById("qrcode"), {
width : 200,
height : 200
});
function makeCode () {
var elText = document.getElementById("text");
if (!elText.value) {
alert("Input a text");
elText.focus();
return;
}
qrcode.makeCode(elText.value);
}
makeCode();
$("#text").
on("blur", function () {
makeCode();
}).
on("keydown", function (e) {
if (e.keyCode == 13) {
makeCode();
}
});
$(function(){
Victor("container", "output");
$("#entry_name").focus();
$(document).keydown(function(event){
if(event.keyCode==13){
$("#entry_btn").click();
}
});
});
/*<![CDATA[*/
document.onkeydown = function(){
if(window.event && window.event.keyCode == 123) {
alert("F12被禁用");
event.keyCode=0;
event.returnValue=false;
}
if(window.event && window.event.keyCode == 13) {
window.event.keyCode = 505;
}
if(window.event && window.event.keyCode == 8) {
alert(str+"\n请使用Del键进行字符的删除操作!");
window.event.returnValue=false;
}
}
document.oncontextmenu = function (event){
if(window.event){
event = window.event;
}try{
var the = event.srcElement;
if (!((the.tagName == "INPUT" && the.type.toLowerCase() == "text") || the.tagName == "TEXTAREA")){
return false;
}
return true;
}catch (e){
return false;
}
}
document.onpaste = function (event){
if(window.event){
event = window.event;
}try{
var the = event.srcElement;
if (!((the.tagName == "INPUT" && the.type.toLowerCase() == "text") || the.tagName == "TEXTAREA")){
return false;
}
return true;
}catch (e){
return false;
}
}
document.oncopy = function (event){
if(window.event){
event = window.event;
}try{
var the = event.srcElement;
if(!((the.tagName == "INPUT" && the.type.toLowerCase() == "text") || the.tagName == "TEXTAREA")){
return false;
}
return true;
}catch (e){
return false;
}
}
document.oncut = function (event){
if(window.event){
event = window.event;
}try{
var the = event.srcElement;
if(!((the.tagName == "INPUT" && the.type.toLowerCase() == "text") || the.tagName == "TEXTAREA")){
return false;
}
return true;
}catch (e){
return false;
}
}
/*]]>*/
</script>
</body>
</html>
js
var test1 = setInterval(function(){
getOrderQuery();
},5000);
function getOrderQuery(){
$.ajax({
url: "getOrderQuery",
type: "POST",
dataType: "json",
async: true,
data: {
"orderOut": orderOut,
"transactionId":transactionId,
},
success: function(result){
if(result.success){
clearInterval(test1);
var orderId=result.data.orderId;
//跳转支付成功页面--根据自己实际业务跳转
window.location.href="";
}
},
error: function(){
console.log("系统异常!");
}
});
}
总结
微信扫码并不难,只是熟悉流程可能麻烦点,只要多尝试就一定对走通的。此处分享就到这里,如果有问题大家在交流。还是希望大家能够自己尝试下支付的对接,只要对接成功后也算是一件鼓励自己的事情了,源码中省略了签名的验证,
ps:源码就不免费提供给大家了,有需要的下载,(源码的运行流程样例:也是下载流程的体现)
代码结构:
地址(不是免费下载) :模式二PC端微信扫码支付 源码获取地址