Java微信公众平台开发之扫码支付模式一
官方文档
准备工作:已通过微信认证的公众号,必须通过ICP备案域名(否则会报支付失败)
借鉴了很多大神的文章,在此先谢过了
大体过程:先扫码(还没有确定实际要支付的金额),这个码是商品的二维码,再生成订单,适用于自动贩卖机之类固定金额的。
模式一支付的流程如下图,稍微有点复杂
业务流程说明:
(1)商户后台系统根据微信支付规定格式生成二维码(规则见下文),展示给用户扫码。
(2)用户打开微信“扫一扫”扫描二维码,微信客户端将扫码内容发送到微信支付系统。
(3)微信支付系统收到客户端请求,发起对商户后台系统支付回调URL的调用。调用请求将带productid和用户的openid等参数,并要求商户系统返回交数据包
(4)商户后台系统收到微信支付系统的回调请求,根据productid生成商户系统的订单。
(5)商户系统调用微信支付【统一下单API】请求下单,获取交易会话标识(prepay_id)。
(6)微信支付系统根据商户系统的请求生成预支付交易,并返回交易会话标识(prepay_id)。
(7)商户后台系统得到交易会话标识prepay_id(2小时内有效)。
(8)商户后台系统将prepay_id返回给微信支付系统。
(9)微信支付系统根据交易会话标识,发起用户端授权支付流程。
(10)用户在微信客户端输入密码,确认支付后,微信客户端提交支付授权。
(11)微信支付系统验证后扣款,完成支付交易。
(12)微信支付系统完成支付交易后给微信客户端返回交易结果,并将交易结果通过短信、微信消息提示用户。微信客户端展示支付交易结果页面。
(13)微信支付系统通过发送异步消息通知商户后台系统支付结果。商户后台系统需回复接收情况,通知微信后台系统不再发送该单的支付通知。
(14)未收到支付通知的情况,商户后台系统调用【查询订单API】。
(15)商户确认订单已支付后给用户发货。
一、设置回调地址
商户后台系统根据微信支付规则链接生成二维码,链接中带固定参数productid(可定义为产品标识或订单号)。用户扫码后,微信支付系统将productid和用户唯一标识(openid)回调商户后台系统(需要设置支付回调URL),商户后台系统根据productid生成支付交易,最后微信支付系统发起用户支付流程
商户支付回调URL设置指引:进入公众平台-->微信支付-->开发配置-->扫码支付-->修改,如下图所示。
这个支付回调的URL设置的作用是接收用户扫码后微信支付系统发送的数据,根据接收的数据生成支付订单,调用【统一下单API】提交支付交易。
二、生成微信支付二维码
二维码长链接示例:weixin:/

- /**
- * 扫码支付模式一生成二维码
- *
- * @param request
- * @param response
- * @throws IOException
- */
- @RequestMapping("qrcode")
- public String createPayImage(HttpServletRequest request, HttpServletResponse response) {
- String nonce_str = PayUtil.createNonceStr();
- String product_id = "product_001"; // 推荐根据商品ID生成
- SortedMap<Object, Object> packageParams = new TreeMap<Object, Object>();
- packageParams.put("appid", WechatConfig.APP_ID);
- packageParams.put("mch_id", WechatConfig.MCH_ID);
- packageParams.put("product_id", product_id);
- packageParams.put("time_stamp", PayUtil.createTimeStamp());
- packageParams.put("nonce_str", nonce_str);
- String str = PayUtil.createPayImageUrl(packageParams);
- String sign = SignatureUtil.createSign(packageParams,WechatConfig.API_KEY, SystemConfig.CHARACTER_ENCODING);
- packageParams.put("sign", sign);
- String payurl = "weixin://wxpay/bizpayurl?sign=" + sign + str;
- logger.info("payurl is " + payurl);
- /**** 转成短链接 ****/
- PayShortUrlParams payShortUrlParams = new PayShortUrlParams();
- payShortUrlParams.setAppid(WechatConfig.APP_ID);
- payShortUrlParams.setMch_id(WechatConfig.MCH_ID);
- payShortUrlParams.setLong_url(payurl);
- payShortUrlParams.setNonce_str(nonce_str);
- String urlSign = SignatureUtil.createSign(payShortUrlParams,WechatConfig.API_KEY, SystemConfig.CHARACTER_ENCODING);
- payShortUrlParams.setSign(urlSign);
- String longXml = XmlUtil.toSplitXml(payShortUrlParams);
- String shortResult = HttpReqUtil.HttpsDefaultExecute(HttpReqUtil.POST_METHOD, WechatConfig.PAY_SHORT_URL, null, longXml);
- PayShortUrlResult payShortUrlResult = XmlUtil.getObjectFromXML(shortResult, PayShortUrlResult.class);
- if("SUCCESS".equals(payShortUrlResult.getReturn_code())){
- payurl = payShortUrlResult.getShort_url();
- }else{
- logger.debug("错误信息"+payShortUrlResult.getReturn_msg());
- }
- /**** 生成 二维码图片自己实现****/
- return null;
- }
- /**
- * 生成支付二维码URL
- *
- * @param params
- * @return
- */
- public static String createPayImageUrl(SortedMap<Object, Object> params) {
- StringBuffer buffer = new StringBuffer();
- for (Map.Entry<Object, Object> entry : params.entrySet()) {
- if (entry.getValue() != null) {
- buffer.append("&" + entry.getKey() + "=" + entry.getValue());
- }
- }
- return buffer.toString();
- }
三、回调商户支付URL
接收用户扫码后微信支付系统发送的数据,根据接收的数据生成支付订单,调用统一下单API提交支付交易
统一接口文档https://pay.weixin.qq.com/wiki/doc/api/native.php?chapter=9_1
- package com.phil.wechatpay.controller;
- import java.io.BufferedOutputStream;
- import javax.servlet.http.HttpServletRequest;
- import javax.servlet.http.HttpServletResponse;
- import org.apache.commons.io.IOUtils;
- import org.apache.log4j.Logger;
- import org.springframework.stereotype.Controller;
- import org.springframework.web.bind.annotation.RequestMapping;
- import com.phil.common.config.SystemConfig;
- import com.phil.common.config.WeChatConfig;
- import com.phil.common.util.HttpReqUtil;
- import com.phil.common.util.PayUtil;
- import com.phil.common.util.SignatureUtil;
- import com.phil.common.util.XmlUtil;
- import com.phil.wechatpay.model.rep.PayCallBackParams;
- import com.phil.wechatpay.model.rep.UnifiedOrderParams;
- import com.phil.wechatpay.model.resp.PayCallBackResult;
- import com.phil.wechatpay.model.resp.UnifiedOrderResult;
- /**
- * 扫码模式一回调
- *
- * @author phil
- * @date 2017年6月27日
- *
- */
- @Controller
- @RequestMapping("/wxpay/")
- public class WechatPayCallBackController {
- private static final Logger logger = Logger.getLogger(WechatPayCallBackController.class);
- @RequestMapping("/callback")//payone
- public void callBack(HttpServletRequest request, HttpServletResponse response) throws Exception {
- String resXml = "";// 反馈给微信服务器
- // 微信支付系统发送的数据(<![CDATA[product_001]]>格式)
- String xml = HttpReqUtil.inputStreamToStrFromByte(request.getInputStream());
- // logger.info("微信支付系统发送的数据"+xml);
- /**** 微信支付系统发送的数据其实就是回调地址输入的参数Xml****/
- // 验证签名
- if (SignatureUtil.checkIsSignValidFromWeiXin(xml, WeChatConfig.API_KEY, SystemConfig.CHARACTER_ENCODING)) {
- // 转换成输入参数,
- PayCallBackParams payCallBackParams = XmlUtil.getObjectFromXML(xml, PayCallBackParams.class);
- // appid openid mch_id is_subscribe nonce_str product_id sign
- // 统一下单
- String openid = payCallBackParams.getOpenid();
- String product_id = payCallBackParams.getProduct_id();
- /**** product_id 等 生成自己系统的订单****/
- int total_fee = 1; // 根据product_id算出价格
- String out_trade_no = PayUtil.createOutTradeNo(); //生成订单号
- String body = product_id; // 商品名称设置为product_id
- String attach = "XXX店"; // 附加数据
- String nonce_str = PayUtil.createNonceStr();
- String spbill_create_ip = HttpReqUtil.getRemortIP(request);
- // 组装统一下单的请求参数
- UnifiedOrderParams unifiedOrderParams = new UnifiedOrderParams();
- unifiedOrderParams.setAppid(WeChatConfig.APP_ID);// 必须
- unifiedOrderParams.setMch_id(WeChatConfig.MCH_ID);// 必须
- unifiedOrderParams.setOut_trade_no(out_trade_no);
- unifiedOrderParams.setBody(body);
- unifiedOrderParams.setAttach(attach);
- unifiedOrderParams.setTotal_fee(total_fee);// 必须
- unifiedOrderParams.setNonce_str(nonce_str); // 必须
- unifiedOrderParams.setSpbill_create_ip(spbill_create_ip); // 必须
- unifiedOrderParams.setTrade_type("NATIVE");// 必须
- unifiedOrderParams.setOpenid(openid);
- unifiedOrderParams.setNotify_url(WeChatConfig.NOTIFY_URL); // 异步通知URL
- // 签名
- String sign = SignatureUtil.createSign(unifiedOrderParams, WeChatConfig.API_KEY, SystemConfig.CHARACTER_ENCODING);
- unifiedOrderParams.setSign(sign);
- // 统一下单 请求的Xml
- String unifiedXmL = XmlUtil.toSplitXml(unifiedOrderParams);
- // 统一下单 返回的xml
- String unifiedOrderResultXmL = HttpReqUtil.HttpsDefaultExecute(HttpReqUtil.POST_METHOD,WeChatConfig.UNIFIED_ORDER_URL, null, unifiedXmL);
- // 统一下单返回 验证签名
- if (SignatureUtil.checkIsSignValidFromWeiXin(unifiedOrderResultXmL, WeChatConfig.API_KEY,SystemConfig.CHARACTER_ENCODING)) {
- UnifiedOrderResult unifiedOrderResult = (UnifiedOrderResult) XmlUtil.getObjectFromXML(unifiedOrderResultXmL, UnifiedOrderResult.class);
- if("SUCCESS".equals(unifiedOrderResult.getReturn_code()) && "SUCCESS".equals(unifiedOrderResult.getResult_code())){
- PayCallBackResult payCallBackResult = new PayCallBackResult();
- payCallBackResult.setReturn_code("SUCCESS");
- payCallBackResult.setAppid(WeChatConfig.APP_ID);
- payCallBackResult.setMch_id(WeChatConfig.MCH_ID);
- payCallBackResult.setNonce_str(unifiedOrderResult.getNonce_str());//直接用微信返回的签名即可
- /**** prepay_id 2小时内都有效,根据product_id再次支付方法自己写 ****/
- payCallBackResult.setPrepay_id(unifiedOrderResult.getPrepay_id());
- payCallBackResult.setResult_code("SUCCESS");
- String callsign = SignatureUtil.createSign(payCallBackResult, WeChatConfig.API_KEY, SystemConfig.CHARACTER_ENCODING);
- payCallBackResult.setSign(callsign);
- resXml = XmlUtil.toXml(payCallBackResult).replace("__", "_");
- //将数据包返回给微信支付系统处理
- }
- } else {
- logger.info("签名验证错误");
- resXml = "<xml>" + "<return_code><![CDATA[FAIL]]></return_code>"+ "<return_msg><![CDATA[签名验证错误]]></return_msg>" + "</xml> ";
- }
- } else {
- logger.info("签名验证错误");
- resXml = "<xml>" + "<return_code><![CDATA[FAIL]]></return_code>"+ "<return_msg><![CDATA[签名验证错误]]></return_msg>" + "</xml> ";
- }
- BufferedOutputStream out = new BufferedOutputStream(response.getOutputStream());
- out.write(resXml.getBytes());
- out.flush();
- IOUtils.closeQuietly(out);
- }
- }
第四步、完成支付并通知支付结果
Java微信公众平台开发之扫码支付模式一的更多相关文章
- Java微信公众平台开发之扫码支付模式二
官方文档点击查看 准备工作:已通过微信认证的公众号,域名可以不通过ICP备案借鉴了很多大神的文章,在此先谢过了大体过程:根据固定金额和商品的ID先生成订单,再生成二维码,客户扫一扫付款模式二支付的流程 ...
- Java微信公众平台开发_07_JSSDK图片上传
一.本节要点 1.获取jsapi_ticket //2.获取getJsapiTicket的接口地址,有效期为7200秒 private static final String GET_JSAPITIC ...
- Java微信公众平台开发--番外篇,对GlobalConstants文件的补充
转自:http://www.cuiyongzhi.com/post/63.html 之前发过一个[微信开发]系列性的文章,也引来了不少朋友观看和点评交流,可能我在写文章时有所疏忽,对部分文件给出的不是 ...
- Java微信公众平台开发_02_启用服务器配置
源码将在晚上上传到 github 一.准备阶段 需要准备事项: 1.一个能在公网上访问的项目: 见:[ Java微信公众平台开发_01_本地服务器映射外网 ] 2.一个微信公众平台账号: 去注册: ...
- Java微信公众平台开发【番外篇】(七)--公众平台测试帐号的申请
转自:http://www.cuiyongzhi.com/post/45.html 前面几篇一直都在写一些比较基础接口的使用,在这个过程中一直使用的都是我个人微博认证的一个个人账号,原本准备这篇是写[ ...
- Java微信公众平台开发(十二)--微信用户信息的获取
转自:http://www.cuiyongzhi.com/post/56.html 前面的文章有讲到微信的一系列开发文章,包括token获取.菜单创建等,在这一篇将讲述在微信公众平台开发中如何获取微信 ...
- Java微信公众平台开发(一)--接入微信公众平台
转自:http://www.cuiyongzhi.com/post/38.html (一)接入流程解析 在我们的开发过程中无论如何最好的参考工具当然是我们的官方文档了:http://mp.weixin ...
- Java微信公众平台开发(十)--微信用户信息的获取
前面的文章有讲到微信的一系列开发文章,包括token获取.菜单创建等,在这一篇将讲述在微信公众平台开发中如何获取微信用户的信息,在上一篇我们有说道微信用户和微信公众账号之间的联系可以通过Openid关 ...
- Java微信公众平台开发之获取地理位置
本部分需要用到微信的JS-SDK,微信JS-SDK是微信公众平台面向网页开发者提供的基于微信内的网页开发工具包.通过使用微信JS-SDK,网页开发者可借助微信高效地使用拍照.选图.语音.位置等手机系统 ...
随机推荐
- eclipse在多modules项目结构下避免模块间依赖引用的场景
这个在单一classLoader时,不会有问题.如果多classloader下会有问题. 假设工程有两个模块,module2 依赖module1 当我们执行mvc eclipse:eclipse后,然 ...
- sql将一列数据拼成一个字符串的方法
SELECT STUFF(CONVERT(VARCHAR(500), ( SELECT TOP 10 ',' + BG_Country FROM dbo.BS_Budget FOR XML PATH( ...
- KBEngine简单RPG-Demo源码解析(2)
七:服务端资产库文件夹结构http://kbengine.org/cn/docs/concepts/directorys.html看assets, 注意:demo使用的不是默认的assets资产目录, ...
- 简约的HTML5音乐播放器插件
从我刚开始接触前端的时候就想写一个能播放音乐的小程序,刚开始写的时候虽然可以放,但是确实很慢,很卡,有很多可以优化的地方.最近在前一个版本的基础上重写了一个音乐播放器的插件,速度还可以吧 因为追求简约 ...
- PeopleCode事件和方法只用于online界面不能用于组件接口(component interface)
在使用CI过程中,哪些方法是不能使用的.以下为PeopleBook解释的内容. 一.搜索框代码不执行:SearchInit, SearchSave, and RowSelect events 意味着使 ...
- Java读取property配置文件
读取配置文件已经成了Java程序员工作的一项必备技能. 配置文件的优点: 可维护性好 怎么个可维护性好呢? 它会让程序中变化的地方很灵活的配置,不需要修改代码. Java程序部署到服务器上去之后就变成 ...
- 多元线性回归模型的特征压缩:岭回归和Lasso回归
多元线性回归模型中,如果所有特征一起上,容易造成过拟合使测试数据误差方差过大:因此减少不必要的特征,简化模型是减小方差的一个重要步骤.除了直接对特征筛选,来也可以进行特征压缩,减少某些不重要的特征系数 ...
- POJ 3683 Priest John's Busiest Day / OpenJ_Bailian 3788 Priest John's Busiest Day(2-sat问题)
POJ 3683 Priest John's Busiest Day / OpenJ_Bailian 3788 Priest John's Busiest Day(2-sat问题) Descripti ...
- MySQL 启动参数说明及性能优化建议
[mysqld] port = 3306 serverid = 1 socket = /tmp/mysql.sock skip-name-resolve #禁止MySQL对外部连接进行DNS解析,使用 ...
- css 子div自适应父div高度
<div class="out"> <div class="a"></div> <div class="b& ...