微信扫码支付(模式一)遇到的那些坑

在这个二维码风起云涌的时代,在线支付已经成为潮流,没事扫一扫,打赏一下我也不介意。

timg.jpg

酝酿

谈坑之前先聊一聊模式一的大体流程,模式一的适用场景一般为自助售卖机或者固定价格的商品的线下交易居多。

当然我能想象到的线上交易,比如,对于固定价格的商品进行支付,由商户交易回调中设置短信或者邮件激活码之类的做验证。

感觉模式一更像是一个无状态的支付,二维码中的信息可用的只有product_id而已,对于用户-订单来说并没有任何关联。

如果小伙伴们有类似使用场景,还望告知。

生成二维码

首先根据商品ID以及其他信息,由商户后台生成二维码。

参数列表

123.png

部分代码

/**
 * 二维码生成器(扫码支付模式一)
 * 创建者  小柒2012
 * 创建时间    2017年8月2日
 */
public class qrCodeUtil {
    public static void main(String[] args) {
        SortedMap<Object, Object> packageParams = new TreeMap<Object, Object>();
        //封装通用参数
        ConfigUtil.commonParams(packageParams);
        packageParams.put("product_id", "20170731");//真实商品ID
        packageParams.put("time_stamp", PayCommonUtil.getCurrTime());
        //生成签名
        String sign = PayCommonUtil.createSign("UTF-8", packageParams, ConfigUtil.API_KEY);
        //组装二维码信息(注意全角和半角:的区别 狗日的腾讯)
        StringBuffer qrCode = new StringBuffer();
        qrCode.append("weixin://wxpay/bizpayurl?");
        qrCode.append("appid="+ConfigUtil.APP_ID);
        qrCode.append("&mch_id="+ConfigUtil.MCH_ID);
        qrCode.append("&nonce_str="+packageParams.get("nonce_str"));
        qrCode.append("&product_id=20170731");
        qrCode.append("&time_stamp="+packageParams.get("time_stamp"));
        qrCode.append("&sign="+sign);
        //生成二维码
        ZxingUtils.getQRCodeImge(qrCode.toString(), 256, "D:\\weixn.png");
    }
}

回调设置

配置回调地址

公众平台微信支付公众号支付授权目录、扫码支付回调URL配置入口已于8月1日迁移至商户平台(pay.weixin.qq.com)。迁移后,原有配置数据不会受影响,你可在商户平台查看和配置。带来的不便敬请谅解。

模式一支付.png

回调方法

简单说一下,回调方法中具体的逻辑,如下:

  1. 读取xml参数
  2. 解析xml成map
  3. 校验签名是否正确
  4. 统一下单
  5. 验证下单是否成功
  6. 通知微信下单成功
  7. 最终用户授权支付
  8. 当然后面还有一系列的交易逻辑(非此方法)
/**
     * 模式一支付回调URL(生成二维码见 qrCodeUtil)
     * 商户支付回调URL设置指引:进入公众平台-->微信支付-->开发配置-->扫码支付-->修改
     * @Author  科帮网
     * @param request
     * @param response
     * @throws Exception  void
     * @Date    2017年8月3日
     * 更新日志
     * 2017年8月3日  科帮网 首次创建
     *
     */
    @SuppressWarnings({ "unchecked", "rawtypes"})
    @RequestMapping(value = "bizpayurl")
    public void bizpayurl(HttpServletRequest request, HttpServletResponse response) throws Exception {
        logger.info("模式一支付回调URL");
        //读取参数
        InputStream inputStream = request.getInputStream();
        StringBuffer sb = new StringBuffer();
        String s;
        BufferedReader in = new BufferedReader(new InputStreamReader(inputStream, "UTF-8"));
        while ((s = in.readLine()) != null) {
            sb.append(s);
        }
        in.close();
        inputStream.close();
        
        //解析xml成map
        Map<String, String> map = XMLUtil.doXMLParse(sb.toString());
        //过滤空 设置 TreeMap
        SortedMap<Object, Object> packageParams = new TreeMap<Object, Object>();
        Iterator it = map.keySet().iterator();
        while (it.hasNext()) {
            String parameter = (String) it.next();
            String parameterValue = map.get(parameter);

            String v = "";
            if (null != parameterValue) {
                v = parameterValue.trim();
            }
            packageParams.put(parameter, v);
        }
        //判断签名是否正确
        if (PayCommonUtil.isTenpaySign("UTF-8", packageParams, ConfigUtil.API_KEY)) {
            //统一下单
            SortedMap<Object, Object> params = new TreeMap<Object, Object>();
            ConfigUtil.commonParams(params);
            //随即生成一个 入库 走业务逻辑
            String out_trade_no=Long.toString(System.currentTimeMillis());
            params.put("body", "模式一扫码支付");// 商品描述
            params.put("out_trade_no", out_trade_no);// 商户订单号
            params.put("total_fee", "100");// 总金额
            params.put("spbill_create_ip", "192.168.1.66");// 发起人IP地址
            params.put("notify_url", notify_url);// 回调地址
            params.put("trade_type", "NATIVE");// 交易类型
            
            String paramsSign = PayCommonUtil.createSign("UTF-8", params, ConfigUtil.API_KEY);
            params.put("sign", paramsSign);// 签名
            String requestXML = PayCommonUtil.getRequestXml(params);

            String resXml = HttpUtil.postData(ConfigUtil.UNIFIED_ORDER_URL, requestXML);
            Map<String, String>  payResult = XMLUtil.doXMLParse(resXml);
            String returnCode = (String) payResult.get("return_code");
            if("SUCCESS".equals(returnCode)){
                String resultCode = (String) payResult.get("result_code");
                if("SUCCESS".equals(resultCode)){
                    logger.info("(订单号:{}生成微信支付码成功)",out_trade_no);
                    
                    String prepay_id = payResult.get("prepay_id");
                    SortedMap<Object, Object> prepayParams = new TreeMap<Object, Object>();
                    ConfigUtil.commonParams(params);
                    prepayParams.put("prepay_id", prepay_id);
                    prepayParams.put("return_code", "SUCCESS");
                    prepayParams.put("result_code", "SUCCESS");
                    String prepaySign =  PayCommonUtil.createSign("UTF-8", prepayParams, ConfigUtil.API_KEY);
                    prepayParams.put("sign", prepaySign);
                    String prepayXml = PayCommonUtil.getRequestXml(prepayParams);
                    
                    //通知微信 预下单成功
                    BufferedOutputStream out = new BufferedOutputStream(response.getOutputStream());
                    out.write(prepayXml.getBytes());
                    out.flush();
                    out.close();
                }else{
                    String errCodeDes = (String) map.get("err_code_des");
                    logger.info("(订单号:{}生成微信支付码(系统)失败[{}])",out_trade_no,errCodeDes);
                }
            }else{
                String returnMsg = (String) map.get("return_msg");
                logger.info("(订单号:{} 生成微信支付码(通信)失败[{}])",out_trade_no,returnMsg);
            }
        }else{
            logger.info("签名错误");
        }
    }

部署项目,启动,扫码,如下:

123.png

挖坑

其实如果你做过扫码支付模式二,在处理模式一的一些流程上还是比较顺利的。

无论是签名认证,统一下单还是二维码生成基础组间已经都具备了。

唯一dog ri的腾讯,官方文档给的回调URL中冒号是中文全角,导致扫描二维码直接显示回调URL。

码云

微信支付代码

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏java、Spring、技术分享

记一次unable to create new native thread错误处理过程

unable to create new native thread,看到这里,首先想到的是让运维搞一份线上的线程堆栈(可能通过jstack命令搞定的)。...

8071
来自专栏Spring相关

第3章—高级装配—配置profile bean

我们正常开发的过程中经常遇到的问题是,开发环境是一套环境,qa测试是一套环境,线上部署又是一套环境。这样从开发到测试再到部署,会对程序中的配置修改多次,尤其是从...

1002
来自专栏Python专栏

无所不能的Python,这次把手机APP也攻占了

2594
来自专栏xingoo, 一个梦想做发明家的程序员

如何在Java应用中提交Spark任务?

最近看到有几个Github友关注了Streaming的监控工程——Teddy,所以思来想去还是优化下代码,不能让别人看笑话啊。于是就想改一下之前觉得最丑陋的一...

6376
来自专栏JAVA高级架构

2017 年你不能错过的 Java 类库

各位读者好, 这篇文章是在我看过 Andres Almiray 的一篇介绍文后,整理出来的。 因为内容非常好,我便将它整理成参考列表分享给大家, 同时附上各个库...

2888
来自专栏黑泽君的专栏

day32_Hibernate学习笔记_04

  缓存(Cache):是计算机领域非常通用的概念。它介于应用程序和永久性数据存储源(如硬盘上的文件或者数据库)之间,其作用是降低应用程序直接读写硬盘(永久性数...

972
来自专栏个人分享

Spark Netty与Jetty (源码阅读十一)

  spark呢,对Netty API又做了一层封装,那么Netty是什么呢~是个鬼。它基于NIO的服务端客户端框架,具体不再说了,下面开始。

1674
来自专栏猿天地

Spring Boot Async异步执行任务

异步调用就是不用等待结果的返回就执行后面的逻辑,同步调用则需要等带结果再执行后面的逻辑。

1392
来自专栏代码拾遗

Spring Boot 2.0 教程 - 配置详解

Spring Boot 可以通过properties文件,YAML文件,环境变量和命令行参数进行配置。属性值可以通过,@Value注解,Environment...

1103
来自专栏程序员互动联盟

【专业技术】Python爬虫:抓取手机APP的传输数据

1、抓取APP数据包 方法详细可以参考这篇博文:http://my.oschina.net/jhao104/blog/605963 得到超级课程表登录...

3635

扫码关注云+社区

领取腾讯云代金券