GitHub源码下载:https://github.com/wshy0924/CarPark_Pay_Web/tree/master(不要忘了给个星星~~)
随着社会的发展,私家车的数量越来越多,不管是大型的商超或者是购物广场还是小型的停车收费站,如果仅靠人工收费的方式,无疑是浪费人力物力。因此,车牌照自动识别技术加上出入口扫描微信支付宝商户码支付的方式被众多停车场所采纳,虽然在一定程度上可以解决停车出入口拥挤,快速支付的问题,但并没有实现便捷支付的目的。另外,在大型的购物广场采用微信支付的方式,用户需扫码关注微信公众号,在微信公众号内通过绑定车牌号进行查询支付,这种方式实现了便捷支付的目的,也逐渐被社会各个停车场所接受。
如图所示为现场支付截图:
由于之前在整合支付宝和微信的“一码付”经验,故想要尝试在停车场应用一码付的方式进行支付,在效果上可以同以上微信公众号支付相同,并且在支付渠道上可以拓展为微信和支付宝两种方式,适用于更广泛的人群(毕竟不是每个人都只习惯使用微信支付,就算习惯使用,也不是一直有足够的钱,这个是最扎心的。。。。)。另外,这种方式不需要用户关注微信公众号,从用户的角度而言,也算是防止不休止的微信推送吧(当然,这对商家而言并不是一件好事,但毕竟只是一种方法和尝试,也要给予点鼓励吧)。下面不多说了,直接看看代码和效果图。
1.车牌查询页面
模拟市面上的查询页面,做了一个效果图,前端代码不做展示。
2.后端主要代码实现
一码付的精髓就在于这一小段代码,手机端扫描二维码,根据userAgent判断用户使用何种支付方式,其中微信为micromessenger,支付宝为alipayclient,然后根据相应的逻辑去调用相应的接口进行支付处理。
if (userAgent.indexOf("micromessenger") > 0) {
logger.info("----------微信支付----------");
String url = wxpayimpl.Authorize();
logger.info("url = " + url);
return "redirect:" + url;
} else if (userAgent.indexOf("alipayclient") > 0) {
logger.info("----------阿里支付----------");
String url = alipayimpl.Authorize();
logger.info("url = " + url);
return "redirect:" + url;
} else {
map.put("tips", "未匹配到APP类型");
return "failure";// 用了html不能使用反斜杠如"/tips"
}
}
然后,以微信支付为例,用户第一次扫描停车码时,需绑定车牌号,后端接收到用户请求后,执行插库操作记录当前用户绑定的车牌号,也许有人会问了,是不是每次扫码都需要输入车牌号,那不是很麻烦吗?
考虑到这种情况,提供的解决方案是根据每个微信/支付宝用户的openid和userid进行绑定,因为用户的openid和userid是唯一的,所以在第二次扫码的时候会先判断数据库中是否存在openid对应的车牌号,如果存在则在前端页面直接显示。
也许有人又会问了,我是富二代,家里不止一辆车,那只有一个微信怎么办?针对这个问题,在车牌显示页面加入“更换其他车牌”按钮,用户在审核是否是当日所停车辆时,可以更换车牌进行查询支付。这样也可以在手机忘带的情况下,帮别人或者别人帮自己支付。
下面是以微信为例的代码实现(支付宝与微信大同小异,openid和userid的区别,经过测试,二者效果一样):
@RequestMapping(value = "/wxcallback", method = RequestMethod.GET)
public String doWxCallBack(HttpServletRequest request, HttpServletResponse response, Map<String, Object> map) throws Exception {
logger.info("----------layer wxcallback----------");
//------------网页授权认证回调获取code---------------------
String code = request.getParameter("code");
String openid = wxpayimpl.GetOpenid(code);
logger.info("wxcallback code = " + code);
logger.info("wxcallback openid= " + openid);
String car_num = "";
Map<String,Object> selectmap = new HashMap<String,Object>();
selectmap.put("openid", openid);
//根据openid查询carnum_info数据库
List<Map> carnumlist = orderservice.selectOrder("tc_car.selectByOpenid",selectmap);
logger.info("carnumlist = " + carnumlist);
for (int i = 0; i < carnumlist.size(); i++) {
HashMap temp = (HashMap) carnumlist.get(i);
logger.info("temp = "+ temp);
car_num = temp.get("car_num").toString();
logger.info("carnum = " + car_num);
}
if(car_num != "") {
//map.put("tip", "请确认车牌号");
map.put("car_num", car_num);
map.put("openid", openid);
return "WXshow_carnum";
}else {
//map.put("tip", "请输入车牌号");
map.put("openid", openid);
return "WXcar_num";
}
}
用户通过点击查询费用按钮,即可通过车牌号去查询支付费用,最后就是调起微信支付的操作,通过调用微信下单支付接口即可实现:
@RequestMapping(value = "/dowxpay", method = RequestMethod.POST)
public Map<String, Object> doWxPay(@RequestParam("amount") String amount,@RequestParam("openid") String openid) throws Exception {
logger.info("----------data dowxpay----------");
logger.info("amount = " + amount);
logger.info("openid = " +openid);
Map<String, Object> map = new HashMap<String, Object>();
Map<String, Object> unifiedordermap = wxpayimpl.UnifiedOrder(amount,openid);
if (unifiedordermap.get("statuscode").equals("200")) {
String conResult = (String) unifiedordermap.get("conResult");
Map<String, Object> conResultmap = WXPayUtil.wxJsonToMapObj(conResult);
if (conResultmap.get("returnCode").equals("0000")) {
Map<String, Object> datamap = (Map<String, Object>) conResultmap.get("data");
String timeStamp = Long.toString(new Date().getTime()/1000);
String randomplus = WXPayUtil.wxgetRandomPlus(20);
//Map<String, Object> generatemd5map = wxpayimpl.GenerateMD5((String) datamap.get("prepay_id"),(String) datamap.get("appid"),timeStamp,randomplus);
Map<String, Object> generatemd5map = wxpayimpl.InsideGenerateMD5((String) datamap.get("prepay_id"),(String) datamap.get("appid"),timeStamp,randomplus);
if (generatemd5map.get("statuscode").equals("200")) {
conResult = (String) generatemd5map.get("conResult");
conResultmap = WXPayUtil.wxJsonToMapObj(conResult);
if (conResultmap.get("returnCode").equals("0000")) {
map.put("retcode", "0000");
map.put("msg", "支付成功!");
map.put("md5", conResultmap.get("data"));
map.put("appid", datamap.get("appid"));
map.put("mch_id", datamap.get("mch_id"));
map.put("nonce_str", randomplus);
map.put("prepay_id", "prepay_id="+datamap.get("prepay_id"));
map.put("timeStamp", timeStamp);
map.put("pay_orderid",unifiedordermap.get("pay_orderid"));
}else {
map.put("retcode", "1111");
map.put("msg", "支付失败!");
}
} else {
map.put("retcode", "1111");
map.put("msg", "支付失败!");
}
} else {
map.put("retcode", "1111");
map.put("msg", "支付失败!");
}
} else {
map.put("retcode", "1111");
map.put("msg", "支付失败!");
}
logger.info("ajax return map = "+map);
return map;
}
3.数据库设计
数据库设计主要包含两个表,一是opedid/userid和车牌号的关系表,二是车牌号与费用的关系表,比较简单,不做过多介绍。
数据使用MySQL数据库,使用springboot集成mybatis框架实现。
4.项目目录
5.支付结果图