此项目已开源欢迎Start、PR、发起Issues一起讨论交流共同进步
https://github.com/Javen205/IJPay
http://git.oschina.net/javen205/IJPay

微信极速开发系列文章:http://www.jianshu.com/p/a172a1b69fdd

上一篇文章介绍了微信提供的那些支付方式以及公众号支付http://www.jianshu.com/p/cb2456a2d7a7

这篇文章我们来聊聊微信扫码支付(模式一以及模式二)


先奉上研究微信扫码支付踩过的坑


微信扫码支付文档


扫码支付官方文档


###### 扫码支付分为以下两种方式:

【模式一】:商户后台系统根据微信支付规则链接生成二维码,链接中带固定参数productid(可定义为产品标识或订单号)。用户扫码后,微信支付系统将productid和用户唯一标识(openid)回调商户后台系统(需要设置支付回调URL),商户后台系统根据productid生成支付交易,最后微信支付系统发起用户支付流程。

【模式二】:商户后台系统调用微信支付统一下单API生成预付交易,将接口返回的链接生成二维码,用户扫码后输入密码完成支付交易。注意:该模式的预付单有效期为2小时,过期后无法支付。详细接入步

扫码支付模式一

1、设置支付回调URL

商户支付回调URL设置指引:进入公众平台-->微信支付-->开发配置-->扫码支付-->修改 如下图(来自官方文档)

在开源项目weixin-guide中扫码支付模式一的回调URL为http://域名[/项目名称]/pay/wxpay

2、根据微信支付规则链接生成二维码

2.1 生成二维码规则

二维码中的内容为链接,形式为:

weixin://wxpay/bizpayurl?sign=XXXXX&appid=XXXXX&mch_id=XXXXX&product_id=XXXXXX&time_stamp=XXXXXX&nonce_str=XXXXX

详细的参数说明参考文档 点击这里

商户ID(mch_id)如何获取点击这里

签名安全规则文档 点击这里

在开源项目weixin-guide中 扫码支付模式一 生成二维码规则封装如下:

public String getCodeUrl(){String url="weixin://wxpay/bizpayurl?sign=XXXXX&appid=XXXXX&mch_id=XXXXX&product_id=XXXXX&time_stamp=XXXXX&nonce_str=XXXXX";String product_id="001";String timeStamp=Long.toString(System.currentTimeMillis() / 1000);String nonceStr=Long.toString(System.currentTimeMillis());Map<String, String> packageParams = new HashMap<String, String>();packageParams.put("appid", appid);packageParams.put("mch_id", partner);packageParams.put("product_id",product_id);packageParams.put("time_stamp", timeStamp);packageParams.put("nonce_str", nonceStr);String packageSign = PaymentKit.createSign(packageParams, paternerKey);return StringUtils.replace(url, "XXXXX", packageSign,appid,partner,product_id,timeStamp,nonceStr);}

以上action 在开源项目weixin-guide中 访问地址为http://域名[/项目名称]/pay/getCodeUrl 其中 product_id 根据实际的业务逻辑可以当做参数传入

2.2 生成二维码并在页面上显示

根据2.1生成二维码规则生成了二维码中的内容(链接)来生成二维码。

商户可调用第三方库生成二维码图片

这里使用google 开源图形码工具Zxing

项目中引入相关的jar包 具体配置参考项目中的pom.xml

<!-- 版本号-->
<zxing.version>3.2.1</zxing.version><!-- 开源多维码生成工具 --><dependency><groupId>com.google.zxing</groupId><artifactId>core</artifactId><version>${zxing.version}</version></dependency><dependency><groupId>com.google.zxing</groupId><artifactId>javase</artifactId><version>${zxing.version}</version></dependency>

封装的工具类为com.javen.kit.ZxingKit

/*** google 开源图形码工具Zxing使用*/
public class ZxingKit {private static Log log = Log.getLog(ZxingKit.class.getSimpleName());/*** Zxing图形码生成工具** @param contents*            内容* @param barcodeFormat*            BarcodeFormat对象* @param format*            图片格式,可选[png,jpg,bmp]* @param width*            宽* @param height*            高* @param margin*            边框间距px* @param saveImgFilePath*            存储图片的完整位置,包含文件名* @return*/public static Boolean encode(String contents, BarcodeFormat barcodeFormat, Integer margin,ErrorCorrectionLevel errorLevel, String format, int width, int height, String saveImgFilePath) {Boolean bool = false;BufferedImage bufImg;Map<EncodeHintType, Object> hints = new HashMap<EncodeHintType, Object>();// 指定纠错等级hints.put(EncodeHintType.ERROR_CORRECTION, errorLevel);hints.put(EncodeHintType.MARGIN, margin);hints.put(EncodeHintType.CHARACTER_SET, "UTF-8");try {// contents = new String(contents.getBytes("UTF-8"), "ISO-8859-1");BitMatrix bitMatrix = new MultiFormatWriter().encode(contents, barcodeFormat, width, height, hints);MatrixToImageConfig config = new MatrixToImageConfig(0xFF000001, 0xFFFFFFFF);bufImg = MatrixToImageWriter.toBufferedImage(bitMatrix, config);bool = writeToFile(bufImg, format, saveImgFilePath);} catch (Exception e) {e.printStackTrace();}return bool;}/*** @param srcImgFilePath*            要解码的图片地址* @return*/@SuppressWarnings("finally")public static Result decode(String srcImgFilePath) {Result result = null;BufferedImage image;try {File srcFile = new File(srcImgFilePath);image = ImageIO.read(srcFile);if (null != image) {LuminanceSource source = new BufferedImageLuminanceSource(image);BinaryBitmap bitmap = new BinaryBitmap(new HybridBinarizer(source));Hashtable<DecodeHintType, String> hints = new Hashtable<DecodeHintType, String>();hints.put(DecodeHintType.CHARACTER_SET, "UTF-8");result = new MultiFormatReader().decode(bitmap, hints);} else {log.debug("Could not decode image.");}} catch (Exception e) {e.printStackTrace();} finally {return result;}}/*** 将BufferedImage对象写入文件** @param bufImg*            BufferedImage对象* @param format*            图片格式,可选[png,jpg,bmp]* @param saveImgFilePath*            存储图片的完整位置,包含文件名* @return*/@SuppressWarnings("finally")public static Boolean writeToFile(BufferedImage bufImg, String format, String saveImgFilePath) {Boolean bool = false;try {bool = ImageIO.write(bufImg, format, new File(saveImgFilePath));} catch (Exception e) {e.printStackTrace();} finally {return bool;}}public static void main(String[] args) {String saveImgFilePath = "D://zxing.png";Boolean encode = encode("我是Javen205", BarcodeFormat.QR_CODE, 3, ErrorCorrectionLevel.H, "png", 200, 200,saveImgFilePath);if (encode) {Result result = decode(saveImgFilePath);String text = result.getText();System.out.println(text);}}
}

OK 上面就是生成支付二维码的部分,接下来就是要将二维码显示在页面上,于是就有了下面的代码:
com.javen.weixin.controller.WeixinPayController.getPayQRCode()

src\\main\\webapp\\view\\payQRCode.jsp

/*** 生成支付二维码(模式一)并在页面上显示*/
public void scanCode1(){//获取扫码支付(模式一)urlString qrCodeUrl=getCodeUrl();System.out.println(qrCodeUrl);//生成二维码保存的路径String name = "payQRCode.png";Boolean encode = ZxingKit.encode(qrCodeUrl, BarcodeFormat.QR_CODE, 3, ErrorCorrectionLevel.H, "png", 200, 200,PathKit.getWebRootPath()+File.separator+"view"+File.separator+name );if (encode) {//在页面上显示setAttr("payQRCode", name);render("payQRCode.jsp");}}

JSP 部分代码如下

<body>
<img alt="" src="<%=path %>/view/${payQRCode}">
</body>

最终生成二维码访问地址为http://域名[/项目名称]/pay/scanCode1

以上就是微信扫码支付(模式一)生成支付二维码的全过程

3、扫码回调商户支付URL

用户扫码后,微信支付系统将productid和用户唯一标识(openid)回调商户后台系统。

此回调的URL为上文设置支付回调的URL。特别要注意的是返回参数是xml输入流

HttpServletRequest request = getRequest();/*** 获取用户扫描二维码后,微信返回的信息*/InputStream inStream = request.getInputStream();ByteArrayOutputStream outSteam = new ByteArrayOutputStream();byte[] buffer = new byte[1024];int len = 0;while ((len = inStream.read(buffer)) != -1) {outSteam.write(buffer, 0, len);}outSteam.close();inStream.close();String result  = new String(outSteam.toByteArray(),"utf-8");System.out.println("callBack_xml>>>"+result);
<xml>
<appid><![CDATA[wx5e9360a3f46f64cd]]></appid>
<openid><![CDATA[o_pncsidC-pRRfCP4zj98h6slREw]]></openid>
<mch_id><![CDATA[商户ID]]></mch_id>
<is_subscribe><![CDATA[Y]]></is_subscribe>
<nonce_str><![CDATA[gT5NJAlv9eXawn1j]]></nonce_str>
<product_id><![CDATA[001]]></product_id>
<sign><![CDATA[D2BD7949269271B3112B442421B43D66]]></sign>
</xml>

4、根据回调参数生成预付订单进行支付

根据回调参数调用统一下单API生成预支付交易的prepay_id

prepay_xml>>>
<xml><return_code><![CDATA[SUCCESS]]></return_code>
<return_msg><![CDATA[OK]]></return_msg>
<appid><![CDATA[微信的appid]]></appid>
<mch_id><![CDATA[商户ID]]></mch_id>
<nonce_str><![CDATA[p46NAoD82eAH2d9j]]></nonce_str>
<sign><![CDATA[4117C601F41533DC84159AF6B892F72D]]></sign>
<result_code><![CDATA[SUCCESS]]></result_code>
<prepay_id><![CDATA[wx201610151315007cb1cbe40b0064755332]]></prepay_id>
<trade_type><![CDATA[NATIVE]]></trade_type>
<code_url><![CDATA[weixin://wxpay/bizpayurl?pr=QCLqJIG]]></code_url>
</xml>

商户后台系统将prepay_id返回给微信支付系统,微信支付系统根据交易会话标识,发起用户端授权支付流程。

/*** 发送信息给微信服务器*/Map<String, String> payResult = PaymentKit.xmlToMap(xmlResult);String return_code = payResult.get("return_code");String result_code = payResult.get("result_code");if (StrKit.notBlank(return_code) && StrKit.notBlank(result_code) && return_code.equalsIgnoreCase("SUCCESS")&&result_code.equalsIgnoreCase("SUCCESS")) {// 以下字段在return_code 和result_code都为SUCCESS的时候有返回String prepay_id = payResult.get("prepay_id");Map<String, String> prepayParams = new HashMap<String, String>();prepayParams.put("return_code", "SUCCESS");prepayParams.put("appId", appid);prepayParams.put("mch_id", mch_id);prepayParams.put("nonceStr", System.currentTimeMillis() + "");prepayParams.put("prepay_id", prepay_id);String prepaySign = null;if (sign.equals(packageSign)) {prepayParams.put("result_code", "SUCCESS");}else {prepayParams.put("result_code", "FAIL");prepayParams.put("err_code_des", "订单失效");   //result_code为FAIL时,添加该键值对,value值是微信告诉客户的信息}prepaySign = PaymentKit.createSign(prepayParams, paternerKey);prepayParams.put("sign", prepaySign);String xml = PaymentKit.toXml(prepayParams);log.error(xml);renderText(xml);}

5、支付结果通用通知


官方文档 点击这里

对后台通知交互时,如果微信收到商户的应答不是成功或超时,微信认为通知失败,微信会通过一定的策略定期重新发起通知,尽可能提高通知的成功率,但微信不保证通知最终能成功。 (通知频率为15/15/30/180/1800/1800/1800/1800/3600,单位:秒)
注意:同样的通知可能会多次发送给商户系统。商户系统必须能够正确处理重复的通知。
推荐的做法是,当收到通知进行处理时,首先检查对应业务数据的状态,判断该通知是否已经处理过,如果没有处理过再进行处理,如果处理过直接返回结果成功。在对业务数据进行状态检查和处理之前,要采用数据锁进行并发控制,以避免函数重入造成的数据混乱。
特别提醒:商户系统对于支付结果通知的内容一定要做签名验证,防止数据泄漏导致出现“假通知”,造成资金损失。
技术人员可登进微信商户后台扫描加入接口报警群。

此通知接收地址为生成预付订单时设置的notify_url 。在开源项目weixin-guide中通知默认的地址为http://域名[/项目名称]/pay/pay_notify

以上是微信扫码支付模式一的全过程。

扫码支付模式二

模式二与模式一相比,流程更为简单,不依赖设置的回调支付URL。商户后台系统先调用微信支付的统一下单接口,微信后台系统返回链接参数code_url,商户后台系统将code_url值生成二维码图片,用户使用微信客户端扫码后发起支付。注意:code_url有效期为2小时,过期后扫码不能再发起支付。

微信支付的统一下单接口具体实现上文也有提及到,如果还不是很清楚可以看 com.javen.weixin.controller.WeixinPayController中的scanCode2 以及官方文档介绍

以下是调用预付订单返回的xml

<xml><return_code><![CDATA[SUCCESS]]></return_code>
<return_msg><![CDATA[OK]]></return_msg>
<appid><![CDATA[wx5e9360a3f46f64cd]]></appid>
<mch_id><![CDATA[1322117501]]></mch_id>
<nonce_str><![CDATA[XdVf2zXLErIHRfJn]]></nonce_str>
<sign><![CDATA[916703CD13C3615B9B629C4A9E4C3337]]></sign>
<result_code><![CDATA[SUCCESS]]></result_code>
<prepay_id><![CDATA[wx2016101514433661797ee3010493199442]]></prepay_id>
<trade_type><![CDATA[NATIVE]]></trade_type>
<code_url><![CDATA[weixin://wxpay/bizpayurl?pr=WWOXnrb]]></code_url>
</xml>

其中code_url 就是生成二维码的链接

String qrCodeUrl = result.get("code_url");String name = "payQRCode1.png";Boolean encode = ZxingKit.encode(qrCodeUrl, BarcodeFormat.QR_CODE, 3, ErrorCorrectionLevel.H, "png", 200, 200,PathKit.getWebRootPath()+File.separator+"view"+File.separator+name );if (encode) {//在页面上显示setAttr("payQRCode", name);render("payQRCode.jsp");}

扫码即可进行支付,code_url有效期为2小时,过期后扫码不能再发起支付

最终生成二维码访问地址为http://域名[/项目名称]/pay/scanCode2

码字完毕,以上就是微信扫码支付(模式一、模式二)的详细介绍。

欢迎留言、转发
微信极速开发系列文章:http://www.jianshu.com/p/a172a1b69fdd

后续更新预告
1、刷卡支付
2、微信红包
3、企业转账

微信公众号开发之扫码支付相关推荐

  1. JS_微信公众号开发调用扫码支付功能

    需要在公众号里面切入扫码功能 前端代码: <!DOCTYPE html> <html> <head lang="en"><meta cha ...

  2. 关于微信公众号开发中扫码关注和关注之后继续扫码的不同点

    2019独角兽企业重金招聘Python工程师标准>>> 开发微信商城,当遇到需要绑定上下级关系的时候,会通过扫码的方式关注公众号,但是如果不想做这个人的下级,后台添加解绑功能,继续扫 ...

  3. 微信公众号调起扫码功能

    偶然接触到了公众号开发.说需要调起微信扫一扫.便查看了公众号开发文档 看完文档后,发现js是相对简单的.唯一的难点是在java后台编写的秘钥生成代码. 也就是说.首先需要获取access_token, ...

  4. java微信公众号开发及源码分享

    java微信公众号开发源码下载 刚做完微信公众号项目,分享一下代码,经验!初写博客,格式写得不好,还请见谅.有不懂的可以留言或加qq 505281494交流下. 一. 第一步进行服务器接口配置,提交信 ...

  5. 微信公众号拉取扫码功能

    点击扫码按钮拉取微信扫码 // 扫码添加设备goAddEquipment() {const wx = window.wxlet _this = this// wx.ready(function() { ...

  6. 以后的blog将转移到微信公众号,请扫码关注谢谢!

  7. 微信公众号开发功能大全

    1.微信公众号开发之现金红包:https://www.jianshu.com/p/c06120bc1f5c 2.微信公众号开发之模板消息:https://www.jianshu.com/p/eb0e9 ...

  8. 微信公众号开发小记——4.两种邀请用户的方式 扫码链接

    描述 假设的我们的服务号有这么一些功能,比如底部有按钮,点击会有一些复杂的功能,这时候可能就需要一个用户系统,有用户系统就经常想要做什么分享邀请新用户之类的,这时候就又有几种方式,1.直接一个连接,让 ...

  9. 实例:用C#.NET手把手教你做微信公众号开发(20)--使用微信支付线上收款:jsapi方式

    在做线上.线下销售时,可以使用微信便捷支付,通过微信公众号收款有很多种收款方式,如下图: 今天我们来讲一下jsapi支付,场景就是在微信内打开某个页面,完成在线支付,同样一个网页,使用微信打开就是js ...

最新文章

  1. php 画布插入图像,javascript – 如何在PHP中将html5画布图像保存到数据库
  2. MySQL的安装与配置--windows下安装
  3. ADO学习(六)服务器和客户端游标
  4. 远程包含shell时,上传shell时所遇到一句话木马不能上传问题解决的办法(这里为单引号与双引号问题)
  5. 判断浏览器是否为手机端 is mobile
  6. 架构师技术文档:Redis+Nginx+Dubbo+Spring+架构师精选视频
  7. 高中计算机算计Access怎么做,Access非常简单实用的计算器
  8. ArrayList 一个面试题
  9. 恢复Cisco路由器口令
  10. 【java】switch的用法介绍
  11. javaEE版eclipse设置默认编码格式为utf-8
  12. 如何区分电梯卡为id卡ic卡_如何分辨IC卡和ID卡
  13. 【转】局域网速度测试 三款软件轻松搞定
  14. 云适配签约中远海运特运 构建移动BPM平台
  15. 【算法基础12】最小生成树的两种解法(普里姆、克鲁斯卡尔)
  16. stc32G库函数(二)——定时器
  17. HashMap在JDK1.7版本头插法实现解析
  18. WPF将控件保存为图片
  19. (附源码)计算机毕业设计SSM大学生互动交流论坛
  20. Linux ar命令说明

热门文章

  1. 计算机软考证书领取时间及地点
  2. VoIP之消息等待提示(MWI)和语音信箱(VoiceMail)
  3. 大数据推动交易平台信息化
  4. Redis的高级特性
  5. 大数据基础知识:什么是集群?
  6. Duilib控件字体设置
  7. mysql数据如何恢复到任意时间节点?mysql数据恢复,mysql数据备份
  8. mysql 创建游标报错,MySQL游标概念与用法详解
  9. lg android tv蓝牙,LG Q52获蓝牙认证 运行Android 10系统
  10. median-of-two-sorted-arrays