近期公司项目需要使用到微信卡券模块,主要做的是在小程序打通微信卡券,实现小程序领取卡券的功能效果。

简单说下涉及的东西:

Springboot—使用springboot做后端接口,非常便捷 并且根本是基于SSM

微信公众号—需要认证,并且开通卡券功能。

微信小程序--- 作为项目前端,接受后台接口返回的参数,并调用wx.addcard接口领取卡券。

开发准备:在公众号平台上开通卡券模块,并创建一张卡券,而且要让卡券进入投放的状态,记录下其card_id。

后台函数代码编写参考了网上其它人的程序:

创建一个OpenApi类,这里我把它加一个注解变成控制器

     private static String grantType = "client_credential";
     public static String appId = "wxc9e5635bb78789db";            //微信公众号appid
     public static String secret = "1ee5c4ba6aca792196dbcfc73eabeed8";            //微信公众号密钥
     public static AccessToken token = null;            //微信公众号的accessToken对象,由于请求次数有限制,这里使用全局静态变量保存起来
     public static ApiTicket ticket = null;//使用全局静态变量存储ApiTicket对象,当然如果使用缓存框架保存当然更好,这边只是做一个简单示例
     //用于下面返回随机字符串的函数
     private final static String string = "0123456789";
     final private static char[] chars = string.toCharArray();
 

这里注意 appid跟secret必须是公众号的,不然会有错误。

     /**
      * @param api_ticket:
      * @param cardId:需要领取的卡券的cardId
      * @return
      * @Description: 生成卡券需要的签名并返回参数
      */
     public static Map<String, String> sign(String api_ticket, String cardId) {
         Map<String, String> ret = new HashMap<String, String>();
         String nonce_str = create_nonce_str();
         String timestamp = create_timestamp();
         String signature = "";

         String param[] = new String[4];

         param[0] = nonce_str;
         param[1] = timestamp;
         param[2] = api_ticket;
         param[3] = cardId;

         Arrays.sort(param);//对参数的value值进行字符串的字典序排序

         StringBuilder sb = new StringBuilder();
         for (String b : param) {
             sb.append(b);
         }
         System.out.println(sb);
         //对上面拼接的字符串进行sha1加密,得到signature
         try {
             MessageDigest crypt = MessageDigest.getInstance("SHA-1");
             crypt.reset();
             crypt.update(sb.toString().getBytes("UTF-8"));
             signature = bytesToHexString(crypt.digest());
         } catch (NoSuchAlgorithmException e) {
             e.printStackTrace();
         } catch (UnsupportedEncodingException e) {
             e.printStackTrace();
         }

         //返回领取卡券需要的参数,其中nonceStr和timestamp必须和签名中的保持一致
         ret.put("card_id", cardId);
         ret.put("api_ticket", api_ticket);
         ret.put("nonceStr", nonce_str);
         ret.put("timestamp", timestamp);
         ret.put("signature", signature);

         return ret;
     }

该函数是签名用的函数,注意这里有随机字符串参与签名。

    /**
     * 返回时间戳(秒)
     * @return
     */
    private static String create_timestamp() {
        return String.valueOf(new Date().getTime() / 1000);
    }

    /**
     * 返回随机字符串
     * @return
     */
    private static String create_nonce_str() {
        String nonce = new String();
        for (int i = 0; i < 10; i++) {
            int rannum = (int) (Math.random() * 1000) % (chars.length);
            nonce += chars[rannum];
        }
        return nonce;
    }

获取token的方式跟微信小程序获取token的方式一样

    /**
     * 获取token
     * @return
     * @throws WeixinException
     * @throws JsonParseException
     * @throws JsonMappingException
     * @throws IOException
     * @throws org.weixin4j.WeixinException
     */
    public static AccessToken getToken() throws WeixinException, JsonParseException, JsonMappingException, IOException, org.weixin4j.WeixinException {
        if (token == null || token.getExpires_in() < System.currentTimeMillis()) {
            //拼接参数
            String param = "?grant_type=" + grantType + "&appid=" + appId + "&secret=" + secret;
            //创建请求对象
            HttpsClient http = new HttpsClient();
            //调用获取access_token接口
            Response res = http.get("https://api.weixin.qq.com/cgi-bin/token" + param);
            System.out.println(res.asString());
            ObjectMapper mapper = new ObjectMapper();
            token = mapper.readValue(res.asString(), AccessToken.class);
        }
        return token;
    }
    /**
     * Convert byte[] to hex string
     * @param src byte[] data
     * @return hex string
     */
    public static String bytesToHexString(byte[] src) {
        StringBuilder stringBuilder = new StringBuilder("");
        if (src == null || src.length <= 0) {
            return null;
        }
        for (int i = 0; i < src.length; i++) {
            int v = src[i] & 0xFF;
            String hv = Integer.toHexString(v);
            if (hv.length() < 2) {
                stringBuilder.append(0);
            }
            stringBuilder.append(hv);
        }
        return stringBuilder.toString();
    }
}
 
    /**
     * @param cardId:需要领取的卡券的cardId
     * @return
     * @throws WeixinException
     * @throws JsonParseException
     * @throws JsonMappingException
     * @throws IOException
     * @Description: 获取领取卡券获取签名等参数
     */
    @RequestMapping("getCardSign")
    @ResponseBody
    public Map<String, String> getCardSign(String cardId) throws WeixinException, JsonParseException, JsonMappingException, IOException, org.weixin4j.WeixinException {
        Map<String, String> ret = new HashMap<String, String>();
        //先要获取api_ticket,由于请求api_ticket的接口访问有次数限制,所以最好将获得到的api_ticket保存到缓存中,这边做法比较简单,直接使用的静态变量
        if (ticket == null || ticket.getExpires_in() < System.currentTimeMillis()) {
            //创建请求对象
            HttpsClient http = new HttpsClient();

            ObjectMapper mapper = new ObjectMapper();

            AccessToken token = OpenApi.getToken();//这里获取的token就是最上方代码保存的微信公众号全局静态变量token

            //通过access_token调用获取api_ticket接口
            Response res = http.get("https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token=" + token.getAccess_token() + "&type=wx_card");
            System.out.println(res.asString());
            ticket = mapper.readValue(res.asString(), ApiTicket.class);
        }

        ret = sign(ticket.getTicket(), cardId);//生成领取卡券需要的签名,并返回相关的参数

        for (Map.Entry entry : ret.entrySet()) {
            System.out.println(entry.getKey() + ", " + entry.getValue());
        }
        return ret;
    }

@ResponseBody 注解的用处是让这个接口返回的是json数据。也可以在控制器定义的时候 将@Controller 直接写为@RestController。那这里就可以不用加

@ResponseBody注解。

接下来,小程序前端发起网络请求访问这个接口。返回签名所需要的数据

小程序调用wx.addCard函数

wx.addCard({

cardList: [{

cardId: cardId,

cardExt: '{"nonce_str":"' + res.data.nonceStr + '","timestamp":"' + res.data.timestamp + '","signature":"' + res.data.signature + '"}'

}],

success: function (res) {

console.log("卡券添加结果",res.cardList) // 卡券添加结果

}

})

很多人在这里会出现签名错误。我也是纠结了一天 才挑出问题,这里列一下有可能出现签名错误的原因。

  • 后端签名的时候,appid跟secret没有用公众号的,而是用小程序的。一般后端出现问题的几率不高,这里可以用微信提供的接口自行验证签名是否

有问题。 签名校验地址: https://mp.weixin.qq.com/debug/cgi-bin/sandbox?t=cardsign

  • 我的问题主要是出现在前端。cardExtc参数传值有误,或者拼接出错,都会出现签名错误的提示。这里需要注意的是,首先 官方文档中,cardExt有

openid跟code参数,但是实际上没有这两个值的话是不用填在cardExt里面的,比如我是通过公众平台直接创建的卡券,所以没有code和openid这两个参数,那么我上面的传值就干脆不写。

  • 其实,我看了网上其它人的参数,有些人有这个nonce_str参数,一开始我是没传这个进入,结果一直显示签名错误,弄了我半天也不知道找不出原因。

后来我才知道,你在后台参与签名用的参数,在前端同样的得再cardExt中传过去,否则就会签名错误!这点希望注意下,确实坑。。但是也只能怪我自己

不够细心。

另外就是cardExt这个参数是要拼接成字符串json形式传值的,请不要直接传一个cardExt对象过去,或者直接构建一个cardExt对象,然后使用

JSON.stringify()函数转化一下,

排除掉签名错误的问题就大功告成了。。

Spring+微信小程序 卡券打通的更多相关文章

  1. 微信小程序-卡券开发(前端)

    刚完成一个微信小程序卡券开发的项目.下面记录开发前,自己困惑的几个问题. 因为我只负责了前端.所以下面主要是前端的工作. 项目概述:按照设计图开发好首页上的优惠券列表,点击某个优惠券,输入手机号,点击 ...

  2. 天河微信小程序入门《三》:打通任督二脉,前后台互通

    原文链接:http://www.wxapp-union.com/forum.php?mod=viewthread&tid=505&extra=page%3D1 天河君在申请到https ...

  3. 微信小程序开发——苹果手机领取卡券出现参数错误(安卓正常)

    异常描述: 微信小程序领取卡券,调用 wx.addCard 接口,安卓手机正常调起领取卡券界面,苹果手机.微信开发者工具中均出现“参数错误”,如图: 异常解析: 安卓手机能正常调起领取界面,那就说明领 ...

  4. Spring Boot后端+Vue前端+微信小程序,完整的开源解决方案!

    项目简介 一个小商场系统,包括: 后端:Spring Boot 管理员前端:Vue 用户前端:微信小程序 功能介绍 1.小商城 首页 专题列表.专题详情 分类列表.分类详情 品牌列表.品牌详情 新品首 ...

  5. 微信小程序领取卡券

    微信小程序领取卡券 标签(空格分隔): php 开发前需要准备的工作 1 小程序和公众号要有绑定 2 小程序和该公众号要绑定到同一个开发平台下 [https://open.weixin.qq.com/ ...

  6. 微信小程序添加卡券到微信卡包,使用wx.addCard()方法传参及整体流程

    一.准备: 1.经微信认证过的微信公众号. 2.经微信认证过的微信小程序号. 先来看看微信小程序官方的文档,https://developers.weixin.qq.com/miniprogram/d ...

  7. vue+uni-app商城实战 | 第一篇:【有来小店】微信小程序快速开发接入Spring Cloud OAuth2认证中心完成授权登录

    一. 前言 本篇通过实战来讲述如何使用uni-app快速进行商城微信小程序的开发以及小程序如何接入后台Spring Cloud微服务. 有来商城 youlai-mall 项目是一套全栈商城系统,技术栈 ...

  8. Spring Security 整合 微信小程序登录的思路探讨

    1. 前言 原本打算把Spring Security中OAuth 2.0的机制讲完后,用小程序登录来实战一下,发现小程序登录流程和Spring Security中OAuth 2.0登录的流程有点不一样 ...

  9. Spring Boot+微信小程序_保存微信登录者的个人信息

    1. 前言 微信小程序开发平台,提供有一类 API,可以让开发者获取到微信登录用户的个人数据.这类 API 统称为开放接口. Tip:微信小程序开发平台,会把微信登录用户的个人信息分为明文数据和敏感数 ...

随机推荐

  1. Java 和 JSP 实现网站访问量统计 (刷新过滤)

    java 和 JSP 实现的统计网站访问量,不需要数据库,将数据存储在 指定位置的 txt 文件中,代码块分为两部分 首先, java 部分: import java.io.File; import ...

  2. 写给创业者:如何打造让用户离不开的App

    百度权重查询 词库网 网站监控 服务器监控 SEO监控 Swift编程语言教程 还记得那个学会编程,开发了一个拼车应用程序——Trees For Cars,并因此发了一笔小财的流浪汉吗?今天的新闻称, ...

  3. [日常] PHP设置 include_path 配置选项

    动态设置php.ini中的include_path 配置选项: 两种方式set_include_path($new_include_path)ini_set('include_path',$new_i ...

  4. java性能监控工具jstat

    jstat Monitors Java Virtual Machine (JVM) statistics. This command is experimental and unsupported. ...

  5. STS的安装教程-鹏鹏

    STS全称Spring Tools Suite. 简介:Spring Tools Suite (STS)其实就是一个被包装过的Eclipse,主要用于快速的开发Spring项目,我们不用再去编辑繁琐的 ...

  6. Unix 高手的10个好习惯

    引言 当您经常使用某个系统时,往往会陷入某种固定的使用模式.有时,您没有养成以尽可能最好的方式做事的习惯.有时,您的不良习惯甚至会导致出现混乱.纠正此类缺点的最佳方法之一,就是有意识地采用抵制这些坏习 ...

  7. HDU3533(KB2-D)

    Escape Time Limit: 20000/10000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)Total Su ...

  8. css对ie的兼容性问题处理(一):

    1.在制作sidebar时对li里面的元素进行浮动,li在ie6/7下会出现4px的间间隙: 解决方法:在li下加上vertical-align属性,值可为top.bottom.middle: 扩展: ...

  9. Keras vs. PyTorch

    We strongly recommend that you pick either Keras or PyTorch. These are powerful tools that are enjoy ...

  10. python的学习笔记之——time模块常用内置函数

    1.Python time time()方法 Python time time() 返回当前时间的时间戳(1970纪元后经过的浮点秒数). time()方法语法: time.time() 举例: #! ...