作者:陈惠,叩丁狼教育高级讲师。原创文章,转载请注明出处。

上一篇微信支付文章:https://www.jianshu.com/p/9c322b1a5274
实现了微信公众号内H5页面进行支付的功能,但是这种方式的缺点就是必须在微信中打开付款页面才能实现,所以并不适合所有的场景。那么本篇文章,会以另外一种方式实现,使用扫码的方式来进行支付。

需要注意的是,扫码支付分两种形式:

线下的扫码支付:
这种方式非常简单,商户在微信申请付款二维码贴纸,贴到收银台附近位置,客户购买商品直接使用手机扫码二维码,输入付款金额即可支付,这种方式不需要编程人员,大大减少商户成本,比如常见的便利店,商场线下店等等。

叩丁狼教育.png

线上的扫码支付:
这是本文选择实现的方式,也叫做Native支付,这种方式适合PC端的网站,比如大众点评、携程、优酷等,如果使用的是电脑来访问网页,并需要支付相关费用,比如商品付款,充值VIP这些都是比较常见的场景,很多网站都会选用这种方式,总之最后付款的那一刻,就在网页上展示付款码,让用户去扫并付款即可,因为这种方式不是面对面付款,所以必须要保证客户付款的金额是准确的,所以这个二维码不是固定的,是根据账单金额生成的,用户扫码之后就可以马上看到需要付款的金额,确认无误再进行付款。

叩丁狼教育.png

本文使用的是线上的扫码支付方式,框架使用SpringMVC来实现。

准备工作

一.注册商户号
到微信支付商户平台https://pay.weixin.qq.com
提交商家相关资料,注册一个商户账号,并开通Native支付功能。

叩丁狼教育.png

二.绑定公众号或小程序
目的是要得到一个授权的APPID。

叩丁狼教育.png

三.设置API密钥,登录商户平台——>账户中心——>API安全——>API密钥
该密钥在后面的代码中计算支付签名的时候需要使用到。

叩丁狼教育.png

Native支付实现模式

官方提供了两种模式实现,第一种模式比较复杂,还需要自己处理二维码地址相关信息,第二种比较简单,微信可以直接把生成好的二维码地址返回给我们,所以我们使用第二种实现。
具体区别可参考:https://pay.weixin.qq.com/wiki/doc/api/native.php?chapter=6_3

实现的大致流程可参考官方提供的时序图:

叩丁狼教育.png

但是流程有很多,不一一演示,我们选取核心的部分来实现即可。

开发流程

文中的例子为:开通叩丁狼VIP会员服务,并实现扫码支付。

一.准备一个可以触发下单操作的页面

叩丁狼教育.png

二.点击"开通VIP"按钮后进入controller的方法,接收商品参数并调用微信支付统一下单接口
正常的业务流程是在该方法中,获取商品id,再通过id去查询数据库该商品的相关属性,比如名称,价格等等,然后再创建应用自身的业务订单,再去调用微信支付的统一下单接口(让微信生成预支付单,后续才可以进行支付),但此处重点在支付流程,商品的属性值和订单相关值,暂且先使用假数据。

接口以及参数可参考微信官方提供的统一下单文档:https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_1

根据文档介绍,使用Native方式调用统一下单接口时需要带上相关必填的参数如下:

叩丁狼教育.png

代码如下:
把必填的参数封装成对应的实体类:

/*** 微信统一下单实体类*/
@Setter
@Getter
@XmlRootElement(name = "xml")
@XmlAccessorType(XmlAccessType.FIELD)
public class WxOrderEntity {private String appid;private String body;private String device_info;private String mch_id;private String nonce_str;private String sign;private String out_trade_no;private int total_fee;private String trade_type;private String spbill_create_ip;private String openid;private String notify_url;private Long product_id;
}

调用接口成功后返回的结果也封装成实体类:

/*** 微信统一下单返回结果实体类*/
@Setter
@Getter
@XmlRootElement(name = "xml")
@XmlAccessorType(XmlAccessType.FIELD)
public class WxOrderResultEntity {private String return_code;private String return_msg;private String result_code;private String appid;private String nonce_str;private String sign;private String trade_type;private String prepay_id;private String code_url;
}

该结果中最重要的是code_url参数,在生成付款二维码时需要用到。

叩丁狼教育.png

注意:下单的业务逻辑,正常是需要抽取到业务层的,但是此处为了方便阅读代码,直接写到了控制器上。

@Controller
public class OrderController {@RequestMapping("order")public String save(Long productId, Model model, HttpServletRequest request) throws Exception {//根据商品id查询商品详细信息(假数据)double price = 0.01;//(0.01元)String productName = "叩丁狼VIP会员";//生成订单编号int number = (int)((Math.random()*9)*1000);//随机数DateFormat dateFormat = new SimpleDateFormat("yyyyMMddHHmmss");//时间String orderNumber = dateFormat.format(new Date()) + number;//准备调用接口需要的参数WxOrderEntity order = new WxOrderEntity();//appidorder.setAppid(WeChatUtil.APPID);//商户号order.setMch_id(WeChatUtil.MCH_ID);//商品描述order.setBody(productName);//交易类型order.setTrade_type("NATIVE");//商户订单号order.setOut_trade_no(orderNumber);//支付金额(单位:分)order.setTotal_fee((int)(price*100));//用户ip地址order.setSpbill_create_ip(RequestUtil.getIPAddress(request));//设置商品idorder.setProduct_id(productId);//接收支付结果的地址order.setNotify_url("http:/www.xxxx.com/receive.do");//32位随机数(UUID去掉-就是32位的)String uuid = UUID.randomUUID().toString().replace("-", "");order.setNonce_str(uuid);//生成签名String sign = WeChatUtil.getPaySign(order);order.setSign(sign);//调用微信支付统一下单接口,让微信也生成一个预支付订单String xmlResult = HttpUtil.post(GET_PAY_URL, XMLUtil.toXmlString(order));//把返回的xml字符串转成对象WxOrderResultEntity entity = XMLUtil.toObject(xmlResult,WxOrderResultEntity.class);//微信预支付单成功创建if(entity.getReturn_code().equals("SUCCESS")&&entity.getResult_code().equals("SUCCESS")){//使用二维码生成工具,把微信返回的codeUrl转为二维码图片,保存到磁盘String codeUrl = entity.getCode_url();//使用订单号来作为二维码的图片名称File file = new File(QRCodeUtil.PAY_PATH,orderNumber+".jpg");QRCodeUtil.createImage(codeUrl,new FileOutputStream(file));//把订单号传到支付页面model.addAttribute("orderNumber",orderNumber);}//跳转到支付页进行支付return  "pay";}
}

统一下单接口调用成功后,微信会创建一个预支付单,此时即为未付款状态:

叩丁狼教育.png

同时,微信还会返回codeUrl给我们,这个就是二维码的链接,我们需要利用工具把该链接转为二维码

叩丁狼教育.png

二维码生成工具,我使用的是google的Zxing
maven依赖:

 <dependency><groupId>com.google.zxing</groupId><artifactId>core</artifactId><version>3.3.3</version></dependency>

工具类:

public class QRCodeUtil {// 二维码尺寸public static final int QRCODE_SIZE = 300;// 存放二维码的路径public static final String PAY_PATH = "c://pay";/*** 生成二维码* @param content   源内容* @param outputStream 输出流* @throws Exception*/public static void createImage(String content, OutputStream outputStream) throws Exception {Hashtable hints = new Hashtable();hints.put(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.H);hints.put(EncodeHintType.CHARACTER_SET, "utf-8");hints.put(EncodeHintType.MARGIN, 1);BitMatrix bitMatrix = new MultiFormatWriter().encode(content, BarcodeFormat.QR_CODE, QRCODE_SIZE, QRCODE_SIZE,hints);int width = bitMatrix.getWidth();int height = bitMatrix.getHeight();BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);for (int x = 0; x < width; x++) {for (int y = 0; y < height; y++) {image.setRGB(x, y, bitMatrix.get(x, y) ? 0xFF000000 : 0xFFFFFFFF);}}// 存到磁盘ImageIO.write(image, "jpg", outputStream);}}

生成二维码网上也有很多工具,有的人还会在二维码中间加入logo,自己去找就可以啦。

下面是刚才统一下单的接口使用到的签名算法代码。

官方文档参考:
pay签名:https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=4_3

 /*** 计算微信支付的签名* @param obj* @return* @throws IllegalAccessException*/public static String getPaySign(Object obj) throws IllegalAccessException, IOException {StringBuilder sb = new StringBuilder();//把对象转为TreeMap集合(按照key的ASCII 码从小到大排序)TreeMap<String, Object> map;if(!(obj instanceof Map)) {map = ObjectUtils.objectToMap(obj);}else{map = (TreeMap)obj;}Set<Map.Entry<String, Object>> entrySet = map.entrySet();//遍历键值对for (Map.Entry<String, Object> entry : entrySet) {//如果值为空,不参与签名if(entry.getValue()!=null) {//格式key1=value1&key2=value2…sb.append(entry.getKey() + "=" + entry.getValue() + "&");}}//最后拼接商户的API密钥String stringSignTemp = sb.toString()+"key="+WeChatUtil.KEY;//进行md5加密并转为大写return SecurityUtil.MD5(stringSignTemp).toUpperCase();}

三.支付页面,展示付款二维码
效果如下:

叩丁狼教育.png

页面代码:

<div class="mod_layer_wxopen" style="display:block;"><iframe frameborder="0" class="iframe_mask"></iframe><div class="ly_content"><div class="ly_bd cf"><div class="ly_ct"><div class="qr_list"><h3 class="qr_tit">正在给微信帐号<span class="user js_wx_name">H</span><span class="js_txt">开通</span>VIP会员</h3><div class="qr_pic"><img id="qr_img"  src="/getQrCode.do?orderNumber=${orderNumber}"><span class="icon_wx"></span></div><p class="qr_txt js_qr_txt">使用微信扫描二维码</p><p class="qr_tips">请使用微信扫码支付</p></div></div></div></div>
</div>

重点的是img标签,二维码图片是经过controller去找的。

获取付款二维码:

    @RequestMapping("getQrCode")public void getQrCode(String orderNumber,HttpServletResponse response) throws IOException {//从磁盘中获取付款二维码并输出给responseFile file = new File(QRCodeUtil.PAY_PATH,orderNumber+".jpg");if(file.exists()){IOUtils.copy(new FileInputStream(file),response.getOutputStream());}}

有了付款二维码,客户就可以使用微信扫码,并支付了。

四.支付结果的处理
当用户支付后,微信会把支付结果发送到我们之前指定的notify_url地址,我们可以根据支付结果来做相关的业务逻辑

把微信支付通知结果封装成实体类

@Setter
@Getter
@XmlRootElement(name="xml")
@XmlAccessorType(XmlAccessType.FIELD)
public class WxPayResultEntity {private String return_code;private String result_code;private String return_msg;private String transaction_id;//微信支付订单号private String out_trade_no;//商户订单号private String total_fee;//订单金额private String cash_fee;//现金支付金额
}

具体支付通知结果的参数可参考官方文章:
https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_7&index=8

接收支付结果并处理业务:

    @RequestMapping("receive")@ResponseBodypublic BaseResult receive(@RequestBody WxPayResultEntity result) throws IOException {//判断是否支付成功if(result.getReturn_code().equals("SUCCESS")&&result.getResult_code().equals("SUCCESS")){//避免结果出现差异,安全起见,会再调用预支付订单查询的接口,检查该订单的状态是否是已支付//代码省略//.....}//通知微信我们收到了,如果微信没有收到回复,会间隔一段时间又通知一遍,这样的话容易出现业务重复处理操作BaseResult resp = new BaseResult();resp.setReturn_code("SUCCESS");resp.setReturn_msg("OK");return resp;}

主要逻辑是:
判断微信返回的支付结果是否支付成功,如果是支付成功,还应调用查询预支付订单的接口,再次确认支付结果,如果确认无误,我们就可以执行支付成功的业务逻辑,比如设置业务订单状态为已付款,商品发货,或者设置为VIP会员等等,最后需要给微信返回应答,通知微信我们收到并处理了这个结果,如果微信没有收到我们的回复,会间隔一段时间又再次通知一遍,这样的话容易出现业务重复处理的问题,可能导致商家资金损失。

查询预支付订单参考:https://pay.weixin.qq.com/wiki/doc/api/native.php?chapter=9_2
处理支付结果参考:https://pay.weixin.qq.com/wiki/doc/api/native.php?chapter=9_7&index=8

五.页面轮询检查支付结果
为了用户体验更好,页面需要定时去获取支付的结果,及时跳转页面或者显示支付结果给客户。

页面带上订单号轮询检查:

  <script type="text/javascript">//每两秒检查一次setInterval(function() {$.get("/checkOrder.do?orderNumber=${orderNumber}",function(data) {if (data.success) {alert("支付成功!");}});},2000)</script>

后台查询该订单的支付状态:
在该方法中,应查询数据库,检查该业务订单是否为已支付的状态,如果是,返回success:true,页面接收到结果,即可马上提示支付成功或者跳转到我的订单页面之类的业务处理。

    @RequestMapping("checkOrder")@ResponseBodypublic JSONObject checkOrder(String orderNumber) throws IOException {//检查订单状态,确认已支付,返回success:true//逻辑省略....JSONObject json = new JSONObject();json.put("success",true);return json;}

想获取更多技术干货,请前往叩丁狼官网:http://www.wolfcode.cn/all_article.html

微信支付之Native扫码支付功能相关推荐

  1. ThinkPHP整合微信支付之Native 扫码支付 模式二

    大家好,这篇文章是继微信支付之Native 扫码支付 模式一之后的微信支付系列教程第三篇:扫码支付之模式二 介绍下扫码支付目前有两种模式,模式一比模式二稍微复杂点,至于模式一与模式二的具体内容,流程, ...

  2. ThinkPHP整合微信支付之Native 扫码支付 模式一

    大家好,这篇文章是继微信支付jsapi篇之后的微信支付系列教程第二篇:扫码支付之模式一 介绍下扫码支付目前有两种模式,模式一比模式二稍微复杂点,至于模式一与模式二的具体内容,流程,微信开发文档都有详细 ...

  3. thinkphp3.2 微信 Native扫码支付功能

    Native 扫码支付之模式二 (模式一好像是不再支持了) 准备工作 认证过的服务号  商户号  业务域名 WxPay.Config.php 目录下面的这个文件记得要配置appid 这些 注意: 扫码 ...

  4. Java 微信native扫码支付 亲测有用

    最近在网上总结了spring cloud 微信扫码支付的流程, 本人是刚入行的小白,有不对的地方请大家指出 也欢迎大家来多多交流 我的商户APPID和秘钥的一些配置信息,是公司的 这些需要微信的商户认 ...

  5. 微信和支付宝H5扫码支付开发记录

    微信和支付宝H5扫码支付开发记录 微信支付 支付宝支付 简单前端实现的一下方法,服务端方法未记录: 微信支付 微信流程步骤简介 1.获取用户code 2.拉起微信支付 支付宝支付 支付宝流程步骤简介: ...

  6. 微信pc Native支付 支付宝pc扫码支付 Demo PHP tp5

    官方sdk Demo 支付宝 请求支付代码 public function alipay(){$arr =input('param.');vendor('alipay-trade-page-pay/a ...

  7. 微信支付开发(2) 扫码支付模式一

    关键字:微信支付 微信支付v3 native支付 扫码支付模式一 统一支付 Native支付 prepay_id  作者:方倍工作室 原文: http://www.cnblogs.com/txw195 ...

  8. 对支付宝微信们来说,扫码支付限额 500 元新规意味着什么?

    点击上方"CSDN",选择"置顶公众号" 关键时刻,第一时间送达! 12月27日下午,据中国人民银行(下称央行)网站消息,将于近日发布关于印发<条码支付业 ...

  9. 商户接入微信支付方法(扫码支付、刷卡、app)

    微信支付的几种方式解读: 微信支付商户平台的付款方式有以下几种: 公众号支付:在微信内的商家页面上完成公众号支付 APP支付:在APP中,调起微信进行APP支付 扫码支付:扫描二维码(包含PC网站)进 ...

  10. PHP 支付PC端扫码支付、APP接口调起支付宝支付、微信公众号接入支付宝支付

    第一:第三方支付原理 第二:支付接口申请流程 地址:https://docs.open.alipay.com/270/105899/ : 参考地址:https://blog.csdn.net/nove ...

最新文章

  1. Java代码统计某个字符串出现的次数
  2. python第三方库有哪些常用的、请列举15个-你想要的Python面试都在这里了【315+道题】...
  3. c语言位运算负数的实例_一招教你学会C语言中位运算
  4. php protobuf 性能,php中使用protobuffer
  5. [分享]Windows Phone 7 For Dummies
  6. IIS ARR设置HTTP跳转到HTTPS
  7. 管理软件公司与互联网公司的区别
  8. 实现电子词典_它是一部电子词典,一部翻译机,一部口语机还是一部出游的随身WiFi...
  9. 利用cloudera manager搭建Hadoop集群
  10. DevExpress控件TExtLookupComboBox实现多列模糊匹配输入的方法
  11. NYOJ题目71-独木舟上的旅行(贪心)
  12. python对象转为字符串
  13. 网络调试分析的工具①
  14. tiny6410裸机实验第0章--------------开发环境的搭建(USB转串口)
  15. Intel SGX论文调研
  16. 百度收录提交工具-免费主动提交百度快速收录软件
  17. Kosaraju(科萨拉朱)求强连通分量 (2021-8-5)
  18. 结合MVC.NET相关知识实现在线卖酒销售项目(一)
  19. 有些疑问,阿里云到底是做啥子的呢?
  20. c语言中余数恒等于1,费马小定理_KANGMANG201102_新浪博客

热门文章

  1. 2019年04月02日清明上海迪士尼游
  2. 皮卡丘为什么不进化_皮卡丘为什么不进化?
  3. 自制冰箱,冰柜蒸发器和毛细管的速算
  4. python小孩的报酬_孩子怎么区分报酬与奖励
  5. java后端要会写前端吗_后端开发有必要学习前端吗,如何入门呢
  6. matlab中readwav的用法,使用matlab的wavread函数总提示出错,那位高手指点下啊
  7. git merge冲突解决
  8. flink写hive hdfs一直挂在.inprogress状态
  9. 修改this指向的方法
  10. IntelliJ IDEA有些快捷键无法使用的解决方式