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

简单说下涉及的东西:

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. 浅析人脸检测之Haar分类器方法:Haar特征、积分图、 AdaBoost 、级联

    浅析人脸检测之Haar分类器方法 一.Haar分类器的前世今生 人脸检测属于计算机视觉的范畴,早期人们的主要研究方向是人脸识别,即根据人脸来识别人物的身份,后来在复杂背景下的人脸检测需求越来越大,人脸 ...

  2. [转]ORA-28001: the password has expired解决方法

    本文转自:http://blog.csdn.net/btt2013/article/details/54862420 参考文献:http://www.zhetao.com/content259 后台报 ...

  3. C 语言 static、extern与指针函数介绍

    1.exit(0)正常退出程序 exit(1)程序异常时退出程序 2.static(静态变量)修饰局部变量 在局部变量使用static修饰,会延长局部变量的存在期.但我们需要注意一下几点: 虽然sta ...

  4. SSH框架搭建步骤总结以及Hibernate二级缓存,查询缓存

    二级缓存.查询缓存 一级缓存: 默认启动,生命周期是和session同步的,session独享 二级缓存: 需要加载配置信息,生命周期是和应用服务器同步,session共享 1:在hibernate. ...

  5. Intellij IDEA run coverage之覆盖率测试

    Intellij IDEA run coverage之覆盖率测试 idea 的coverage + 我们自己写的测试用例.最后看一下,我们要测的代码有没有测试到,这是一个不错的提高代码质量的方法. i ...

  6. mysql行转列,列转行

    行转列,列转行是我们在开发过程中经常碰到的问题.行转列一般通过CASE WHEN 语句来实现,也可以通过 SQL SERVER 2005 新增的运算符PIVOT来实现.用传统的方法,比较好理解.层次清 ...

  7. 【学习笔记】--- 老男孩学Python,day12 函数名的应用,闭包,迭代器

    1, 函数名的应用,第一类对象 函数名可以像变量一样进行使用 1.赋值 def func(): print("你吃了么?") # print(func) # a = func # ...

  8. monggo查询语法

    db.getCollection('vvt_user_reward').find({"description":"双节活动"})

  9. iTem2 保持连接,解决ssh的"Write failed: Broken pipe"问题

    方法一: profiles -> sessions -> When idel, send ASCII code 问题场景 服务器环境:阿里云 Linux CentOS 主机 客户端:Mac ...

  10. gulp实用配置(1)——demo

    在React和Vue推进下,现在很多人都在使用webpack作为自动化构建工具,但其实在很多时候我们并不是一定需要用到它,gulp这样的轻量级构建工具就足够了. 最近一段时间不是太忙,所以就写了三份配 ...