微信小程序卡券开发

前言

这篇是一边做预研一边写的,因为是预研,所有的代码都是在小程序中进行的,并没有交给后台。

前期准备

  • 小程序内领取卡券
    1.开发者须有一个有卡券权限的公众号(服务号)和认证后的小程序账号;
    2.开发者须申请一个开放平台账号,并将小程序和公众号绑定在同一个开放平台账号下,关于开放平台的介绍请参照:微信开放平台
    3.阅读卡券接口说明创建卡券接口,创建卡券并获得card_id(不知道为什么微信这里只提供了接口生成卡券,明明服务号也是必备的也可以在界面上生成卡券,却没有告诉,当然自己后台调用接口生成卡片可以有更多的自定义,服务号上我个人认为是提供了几种默认的样式和配置);

具体调用

由于我其实不是很清楚后续的步骤,那么我就看到小程序提供了两个接口,添加和展示卡券的,那么我就从这俩卡券接口入手调用。

  • 接口一 :wx.addCard(Object object)

具体说明文档查看微信官方

wx.addCard({
      cardList: [
        {
          cardId: 'dfdsfsdfsdfsdfsdfdsfdfd',//假的
          cardExt: '{"code": "", "openid": "", "timestamp": "", "signature":""}'
        }
      ],
      success(res) {
        console.log(res.cardList) // 卡券添加结果
      }
    })

上面代码我先调用了一下看看什么效果

可以看到,签名错误,这是因为signature参数我们没有传,那么这个参数是怎么来的呢,不得不吐槽一下微信官方,以下是官方的一句signature参数说明:

签名,商户将接口列表中的参数按照指定方式进行签名,签名方式使用 SHA1,具体签名方案参见:卡券签名

我按照链接点过去凌乱了,麻烦你请告诉我你的说明在哪儿???

获取添加卡片需要的签名signature

一通搜索之后,找到如下:


我们获取签名的流程如下:

1.使用创建卡券公众号的appid和secret获取access_token(注意是公众号的appid和secret,而不是小程序的),因为access_token每天调用次数有限每次时效2小时,请务必使用某种方法去全局保存access_token
2.使用获取到的access_token去换取ticket(这个ticket是用来参与生成签名的也是有调用次数限制和时效2小时,务必全局保存)
3.按照字典序排序以下参数 必填参数:ticket(步骤2中换取的) timestamp(当前时间戳)nonce_str(不超过32位的随机字符串) card_id(要投放的卡券的卡券id) 选填参数:code(自定义code模式下填写) openid(指定领取者的情况下填写)
4.将3.中字典序排序完成之后的参数进行字符串拼接然后使用sha1加密,即为前端需要用到的signature

签名涉及的相关变量如下

api_ticket (调用卡券相关接口的临时票据) 【必须】
timestamp (时间戳,单位为:秒)【必须】
card_id (卡卷创建后获得的卡卷ID)【必须】
code (卡券code码,相当于用户领取的那张卡卷的卡卷号)【非必须,根据业务需要】
openid (指定领取用户的opeind)【非必须,根据业务需要】
nonce_str (不超过32位随机字符串,随你随便写)【非必须,最好有】

我是在微信公众平台创建卡券的,然后code和openid并不需要用到,实际参与签名的是api_ticket、timestamp、card_id、nonce_str这4个,card_id在公众平台创建卡卷的时候就能看到,timestamp和nonce_str这两个时间戳和随机数生成就不用说了很简单,这里说一下api_ticket 。

我们首先需要获取到access_token,然后再用access_token换取api_ticket

https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=APPID&secret=APPSECRET

请求代码如下:

let url = 'https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=xxxxxxxx' + '&secret=' + 'xxxxxxxxxxxx';
    console.log('url',url);
    wx.request({
      url: url,
      data: {},
      method: 'GET',
      success(res) {
        console.log('信息',res)
      }
    });

点击上方蓝色标题查看官方文档,我们获取access_token的appid和secret都是服务号中的appid和secret,打印结果是:


后来又是搜,获取access_token40125错误,说服务号中其实是有两个,一个是在【基本配置】中appid和secret,另外还有一个是在【开发者工具】——【公众平台测试账号】中,我在开发过程中一开始用了基本配置中的那套,修改成后者之后验证成功获取access_token。
但是,我这次最大的坑就在这儿了,虽然这样是可以请求成功的,但是当后面签名啥的一系列都是正确的,却依然显示的签名错误,其实40125就是单纯的告诉你secrete错误,找了相关同事重新确认secrete修改才可以了

说明:
access_token是公众号的全局唯一接口调用凭据,公众号调用各接口时都需使用access_token。开发者需要进行妥善保存。access_token的存储至少要保留512个字符空间。access_token的有效期目前为2个小时,需定时刷新,重复获取将导致上次获取的access_token失效。

获取卡券的api_ticket

https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token=' + access_token +'&type=wx_card

通过上个步骤获取到的access_token,获取卡券签名要用到的ticket,注意type一定是wx_card,代码如下:

getTicket(access_token){
    let that = this;
    let url = 'https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token=' + access_token +'&type=wx_card'
    wx.request({
      url: url,
      data: {},
      method: 'GET',
      success(res) {
        console.log('ticket', res); 
      }
    });
  },

通过以上流程,我们获取到了签名所需要的api_ticket ,服务号创建的卡券的card_id,另外一个必须的timestamp也很容易获取,获取签名前还需要对变量进行字典排序,最后是一个对这些变量拼接成的字符串进行sha1加密的算法

主流程代码如下:

let arr = [timestamp, api_ticket, cardid];
let sortarr = sort(arr);//排序
console.log('arr',arr);
let signStr = '';
for(let i=0;i<sortarr.length;i++){//拼接
    signStr +=sortarr[i];
}
let signature = sha1(signStr);//加密
console.log('签名',signature);

主流程中涉及的方法:

//获取timestamp时间戳
function timestampStr(){
  let timestamp = Date.parse(new Date());
  timestamp = timestamp / 1000 + ''; 
  return timestamp;
}
//对拼接的字符串字典排序
function sort(arr){
  return arr.sort();
}
// 字符串加密成 hex 字符串(sha 1加密)
function sha1(s) {
  var data = new Uint8Array(encodeUTF8(s))
  var i, j, t;
  var l = ((data.length + 8) >>> 6 << 4) + 16, s = new Uint8Array(l << 2);
  s.set(new Uint8Array(data.buffer)), s = new Uint32Array(s.buffer);
  for (t = new DataView(s.buffer), i = 0; i < l; i++)s[i] = t.getUint32(i << 2);
  s[data.length >> 2] |= 0x80 << (24 - (data.length & 3) * 8);
  s[l - 1] = data.length << 3;
  var w = [], f = [
    function () { return m[1] & m[2] | ~m[1] & m[3]; },
    function () { return m[1] ^ m[2] ^ m[3]; },
    function () { return m[1] & m[2] | m[1] & m[3] | m[2] & m[3]; },
    function () { return m[1] ^ m[2] ^ m[3]; }
  ], rol = function (n, c) { return n << c | n >>> (32 - c); },
    k = [1518500249, 1859775393, -1894007588, -899497514],
    m = [1732584193, -271733879, null, null, -1009589776];
  m[2] = ~m[0], m[3] = ~m[1];
  for (i = 0; i < s.length; i += 16) {
    var o = m.slice(0);
    for (j = 0; j < 80; j++)
      w[j] = j < 16 ? s[i + j] : rol(w[j - 3] ^ w[j - 8] ^ w[j - 14] ^ w[j - 16], 1),
        t = rol(m[0], 5) + f[j / 20 | 0]() + m[4] + w[j] + k[j / 20 | 0] | 0,
        m[1] = rol(m[1], 30), m.pop(), m.unshift(t);
    for (j = 0; j < 5; j++)m[j] = m[j] + o[j] | 0;
  };
  t = new DataView(new Uint32Array(m).buffer);
  for (var i = 0; i < 5; i++)m[i] = t.getUint32(i << 2);

  var hex = Array.prototype.map.call(new Uint8Array(new Uint32Array(m).buffer), function (e) {
    return (e < 16 ? "0" : "") + e.toString(16);
  }).join("");
  return hex;
}

最后我们继续调用wx.addCard(Object object):

wx.addCard({
      cardList: [
        {
          cardId: 'xxxxxxx',
          cardExt: '{"timestamp": "' + timestamp + '", "signature":"' + signature + '"}'
        }
      ],
      success(res) {
        console.log('添加结果',res.cardList) // 卡券添加结果
      },
      fail(res){
        console.log('添加失败',res);
      },
      complete(res){
        console.log('添加完成', res);
      }
    })

说明:
cardExt是一个字符串的json,里面的内容一定是参与签名的变量(不能多不能少,用了哪几个签名就填哪几个,没有用到的也不要填了)和获取到的签名signature

卡券签名错误可以在以下链接中分析排查:
卡券签名错误排查方法(官方提供)
微信卡券签名校验工具地址

参考文章:
https://www.2cto.com/kf/201801/712897.html

这里有一个后台创建卡券,前端去加载的文章:
https://blog.csdn.net/ioth5/article/details/73821768

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,233评论 6 495
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,357评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 159,831评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,313评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,417评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,470评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,482评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,265评论 0 269
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,708评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,997评论 2 328
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,176评论 1 342
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,827评论 4 337
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,503评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,150评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,391评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,034评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,063评论 2 352