背景
大家好, 今天给大家分享的是打码指南, 由猫眼线下扫码1分购谈起.
小程序在发布之后是没有入口的, 之后确定是由线下扫二维码进入, 今天分享的是我们自己尝试在下线投放二维码, 进行促销活动, 中间经历了一些波折. 这里给大家介绍一下中间经历的事情.
先做下自我介绍, 在2015年加入美团之前, 一个是去哪儿, 一个是CSDN, 再之前在西安, 主要是外包项目, 企业信息管理系统. 之前在做Java, 当时没有前端工程师的岗位, 但日常工作做前端比较多, 后面大家会看到有时有前端, 有时没有前端的介绍, 这是因为我个人准全栈的经历导致. 如果有志向做PM的话, 也建议关注下, 因为一个好的PM, 你应该知道这个东西是怎么工作起来, 都有哪些人参与, 每个人负责什么东西, 这个也是很有帮助的.
回到主题, 我们今天大概介绍这4个点
- 小程序码和二维码的约束
- 产出物料后修改需求
- 优惠券和用户触达通知
- 线上监控发现的问题
第1点和业务无关, 纯技术的描述. 因为后面在介绍业务相关的场景的时候, 为了说的更流畅, 所以需要先介绍下小程序码和二维码的约束.
小程序码和二维码的约束
类似菊花那种, 官方名字叫小程序码, 我们在日常沟通当中不会在意这个细节, 反正都能扫, 都叫二维码.
方案A: getWXACode
这是微信文档有这个API, 用于生成这个二维码, 参数包括
path: 'pages/movie/index?arg=foo'
限制:
- path参数限制128字节长度
- 调用总数量限制: 10w个, 如果超过微信会拒绝后续的接口请求, 直接出错, 提示超量
方案B: getWXACodeUnlimt
仍旧是菊花码, 参数包括
path: 'pages/movie/index'
scene: 'arg=foo'
限制:
- path 不能加query参数
- scene 长度限制32个字符
在业务场景中不是很通用, 只能固定落地页, 但是我们扫码的目的还是需要有一些线下的特点能够传过来, 如果使用scene参数传递额外参数的话, 这个限制比上一个更苛刻, 但是它不限数量.
这个是在多数大规模投放二维码的方案, 小规模的, 比如我们的今天要讲的这个案例, 我们投放了大概2000多个影院, 其实这2个方案都可以.
方案C: createWXAQRCode
小程序官方叫小程序二维码, 看起来和普通二维码差不多, 由于二维码本身有一定容错能力, 所以LOGO放着是没问题, 参数
path: 'pages/movie/index?arg=foo'
限制:
- path 128字节长度
- 调用总数量限制: 10w个, 和方案A公用配额
这个二维码可以用普通扫码程序扫描, 扫码之后可以发现地址是 mp.weixin.qq.com/a/xxxxxxxxxxx, 这是一个有意思的地方, 我们明明给传的是上面的参数, 但是生成的是下面这个地址, 感觉是做了转换.
前面介绍几种形式, 限制和支持的参数, 存储的信息, 本质上这些信息和URL差不多. 我们用二维码连接线上线下, 二维码本身存储的信息是路径加参数.
传统web开发我们需要关注URL上的输入信息, 比如query参数, 路径, 特别是单页应用的路由框架特别关注URL上的信息.
可以理解URL也是一种输入, 类似触控. 所以二维码也需要前端的同学关注, 因为我们要去处理二维码背后的参数, 和我们的期望是否能匹配.
接下来看下实际案例, 和遇到的问题.
领取优惠券后, 期望能点击后退
我们在线下投放的业务, 通过易拉宝的形式, 扫上面的二维码, 进入活动落地页, 登录支付1分钱, 领取两张优惠券, 分别用于支付电影和小吃如爆米花时抵扣. 投放位置是电影院, 我们的主要合作方.
需求第1阶段
5块的优惠券不是凭空出来的, 是得有人承担的, 一般是平台和商家互相分摊的. 平台期望活动越多越好, 但是影院是有限度, 因为影院的规模, 活动成本有要求. 因此我们必须要识别出来是那家电影院, 涉及到财务结算, 不能错.
我们的方案:
- 活动页每家电影院带上影院参数, 每家影院生成不一样的二维码
- 我们还需要打印出来
问题来了, 我们有2000家影院, 每家比如需要2份物料, 那么我们需要打印4000份, 有2000种不一样的二维码.
物料一般是总部生产, 因为多数公司比较在意自己的品牌, 不会允许地下城市自己随便生产涉及品牌形象的物料. 总部制作好之后还需要邮寄到各个城市, 把它放到具体的影院中.
正确的打印和邮寄, 人工不出错才见鬼了. 快递还可以直接看单子, 二维码人不能直接识别, 打印错了也难以发现.
所以直接按照这个方案走是很容易出错的. 1%的出错比例影响也比较大, 出错的影院会直接找上门, 那就会是很大的麻烦.
为了避免出错, 调整之后的方案, 我们印刷一个模板, 不包含二维码, 因为物料只有二维码部分是变的, 别的都不变, 这样就可以批量的印刷, 批量的邮寄的问题. 如果邮寄出错了, 我们可以把电子版素材发过去, 在当地印刷出来也来得及, 也允许电影院自行印刷更合适的介质.
二维码通过影院后台推送, 影院自行粘贴, 这也是大家看一些大型活动二维码和背后的物料是分离的原因.
看起来没啥问题, 实际落地效果基本符合预期. 但接着就进入到一些更具体的业务场景
需求第2阶段
进入活动落地页, 完成简单任务或直接领取优惠券, 领取优惠券之后期望支持后退, 如果我们什么额外的都不做, 左上角什么都没有, 没有后退, 只能点右上角关闭, 再进来又是活动页.
用户就会疑问, 我领了优惠券到哪里去花呢?
所以需求肯定会要求支持回退, 跳转到能消费优惠券的地方. 之前在掘金小程序开发者大会当中有讲师也提到过, 左上角的后退按钮对用户体验和数据的提升很重要的, 前端就需要考虑怎么实现这个需求.
最笨的办法: 先到首页, 首页再做一次重定向到活动页, 一般我们期望后退到首页.
另一个方案是自定义导航, 相当于页面是全屏的, 只是在右上角浮出2个小程序自身的按钮, 关闭和菜单按钮. 但这个方案有一些限制, 我们最后没有用这个方案.
使用第一个方案的话, 二维码变了, 虽然没超过128字节, 但已经印刷的二维码就不能用了, 这就麻烦了, 还得重新生成二维码, 重新印刷, 并粘贴上去.
如果真的是这样走一步看一步, 走到这肯定出问题, 肯定会吐槽.
更多需求
实际不仅仅如此
- 期望能后退到别的页面
- 一家影院在不同入口放置的二维码效果如何?
- 期望知道影院入口的效果
- 参数越来越多, 长度超限怎么办?
类似的需求越多, 参数就会越多. 如果是方案B的二维码, 参数限制只有32位, 如果是方案A, 总长度128能稍好一些, 但反复生成二维码很有可能超过10w个数量限制.
最终会发现产出物料后改需求是一个常态, 我们如何才能支持这个常态.
短网址服务
这时就回到了我们提到的那个有意思的方案C, 我们提交的参数是一个地址, 但它生成的二维码实际存储的是另外一个地址, 很像短网址服务有没有. 我们是不是也可以做一个这样的服务, 客户端的方案如下
- 入口:
path: 'pages/jump?id=3a5fc8'
, id参数表示一个短网址的id, 这个用于生成二维码 pages/jump
页面- 请求后端
wx.request({ url: 'xxxx', data: '3a5fc8' })
- 期望获取到
{ path: 'pages/movie/index?go=pages/onecent/index?cinema=15280&utm_source=foo' }
- 期望获取到
wx.redirectTo({ url: path })
- 请求后端
这个 path
就没什么长度限制, 这样就可以完美的控制后退导航行为, 以及增加类似 utm_source
的埋点参数, 用于跟踪放在影院的不同入口扫码意愿的差异.
做这些需要前端 后端 PM整体协商合作.
接下来看看服务端我们期望它有什么功能
- 创建短网址 API: 因为我们会批量创建
- 批量查询和修改 API: 因为是批量创建的, 我们又有产出物料后改需求的常态
- 短网址的额外信息
- 类别: 同一活动属于同一类别
- 附加信息: Map结构, 比如包含 影院ID, 后退的页面地址, 埋点信息等, 为了通用性
最终我们生成的二维码包含的 path 信息是: pages/jump?id=3a5fc8
, 我们只需要到短网址服务后台修改就可以控制它的行为.
更多
这个方案需求是解决了, 但由于多了个中间页, 可能界面是白的, 最多加个loading态, 改善下体验, 但时间上可能会有点问题, 比原来要慢些.
一般的解决办法, 首次进入缓存, 第2次进入直接使用缓存数据, 但仍旧发一个请求出去, 响应了再更新缓存, 这样无论缓存过期还是别的状况, 请求都不会阻塞.
更进一步, 我们可以生成一个http的地址, 其它的程序也能扫码成功, 不限于微信.
http://m.maoyan.com/jump?id=3a5fc8
比如我们期望使用一个二维码, 任何终端扫码都能进入活动. 我们期望微信扫码打开小程序, 这样体验更好, 其它程序扫码打开h5页面.
微信扫普通二维码能打开小程序, 这个能力来自小程序管理后台, 叫"扫普通链接二维码打开小程序", 只需要配置一个URL模式, 模式限制数量10个.
有些公司可能小程序公众号参与者权限互相有隔离, 所以实施这个会有一些沟通工作.
小结
- 需求是多变的: 基础需求, 后退, 埋点等诉求, 这是一种常态
- 物料产出后难以修改: 随着需求变更而重新生产物料是不现实的
- 短网址服务: 最终的方案
我们现在已经能让用户扫码, 进入活动页, 领取优惠券, 后退回到首页, 完成购票流程.
但不是所有用户都会走购票流程, 比如用户现在想去吃饭, 就会关闭小程序走了, 这种情况我们 就只能等用户来购票.
显然我们不能等用户来购票, 这样在体验上还是差点, 用户领取了优惠券, 但是没有消费是我们不愿意看到的.
优惠券和用户触达通知
我们期望用户领了优惠券之后能通知用户, 方案有两种
- 微信支付优惠券
- 自建优惠券
微信支付优惠券
微信支付 预充值代金券
- 入口: 消息列表 "微信支付" 服务号
- 时机: 领取时, 即将到期时
- 满减 商品限制 预算限制
- 防刷 对账 消耗记录
需要先充值, 会占用一些现金, 有做PM的同学需要注意这点
前端需要注意商品限制, 有这种限制的时候, 需要前端把商品标记传给微信支付API
自建优惠券
有些公司可能不太愿意提前垫付资金, 这样对现金流有要求, 那么就可能需要自建优惠券.
如果要做到类似微信支付优惠券的体验, 我们需要做
- 入口: 消息列表 "服务通知"
- 时机: 自由
- 满减 限制商品 预算限制
- 防刷 对账 消耗记录
限制
- 要使用服务通知, 所有消息都需要凭证, 凭证通过表单 form 标签 和支付能收集到, 也就是用户必须有点击行为, 7天有效期
- 前端需要刻意在小程序上尽量收集这些凭证, 因为需求是多变的, 可能一开始说发2次就行, 后续又说需要发4次
- 客诉, 用户觉得骚扰, 会给微信投诉, 微信会直接删除消息模板, 消息就发不出去, 投诉再多就可能触犯了小程序的规定, 有封号可能
- 一定要告知给PM, 特别是业务流程需要这个通知, 一定不要做违规的试探行为
- 防刷 对账对于后端来说工作量还是很大的
小结
自建优惠劵需要做的工作很多, 初创公司建议直接使用微信支付优惠券, 特别是合规 防刷 对账
自建优惠券, 前端需要注意多收集凭证, 通过 form + button
线上监控发现的问题
至此, 我们的活动上线, 用户领了优惠券后, 既能现场买票, 也可以过一段时间收到通知, 得知还有优惠券可以去买票.
上线后还需要观察服务本身有什么意外, 由于我们对于多数的异步调用都做了监控, 所以...
无法完成登录
wx.getUserInfo() 的返回中包含 signature: 签名参数, 正常流程是返回了用户昵称 头像信息后需要进行数字签名并比对是否一致, 不一致就表示有问题, 一般是认为获取用户信息失败.
但实际中发现Android 5.x系统下的微信客户端, 如果用户昵称中如果包含 emoji 字符, 那么 signature 肯定不一致. 此时用户昵称中的 emoji 字符实际ASCII值为 0xFEFE 这样的字符, 一般看起来是乱码.
解决方案: 除了等android 5.x基本消失, 就是如何符合上述条件, 可以忽略这个数字签名比对, 昵称只能获取到个大概, 基本够看.
任何微信接口调用可能失败
我们调用了很多微信的接口, 比如 wx.login 相关, 生成二维码. 有时会超时, 小程序端上的表现是卡顿, 隔了很久提示网络错误, 偶发, 很难复现, 大概0.5%.
对于百万以上日活用户的app来说, 这个问题还是挺严重的, 有些用户如果点击后发现没响应, 就停在这了, 用户会认为你的小程序有问题, 直接关闭退出了.
调研的结论是网络问题, 微信这边给的方案是尝试用 api2.weixin.qq.com, 也就是我们需要实现一个容灾 重试策略.
我们最终实现了一个延迟重试的策略, 上线后调用微信接口的可用性从 99.5% 上升到 99.99%+.
cat监控系统
我们用过的一些异常收集系统, 或者在系统出问题的时候, 收集系统也挂掉了, 或者不够实时, 或者没有合适的分类, 只见树不见林, 无法知道整个问题的情况, 不容易抓住重点.
我们发现上述问题主要依赖一个叫cat的系统
基于java, 吞吐量 负载能力超强, 分类聚合满足需求
总结
- 我们介绍了小程序二维码的约束
- 需求变化导致我们需要支持产出物料后改需求, 我们通过短网址服务来支持这个业务
- 我们期望促销活动能产生用户消费, 需要通知用户使用优惠券, 介绍了两种方案的利弊, 成熟的公司可能两种方案都要支持
- 上线的异步调用 远程调用监控, 发现的2个问题及其解决方案.
回顾下标题, 打码指南: 由猫眼线下扫码1分购谈起, 靠谱的线下扫码活动需要技术团队提供那些支撑.
我们讨论了需要开发什么样的系统, 关注什么流程, 那些约束条件需要周知给各方. 因为前端所有的工作都是在一个宿主环境下, 都有很多的限制, 我们需要在各种限制的条件下完成各种需求.
Q&A
- 为什么没有使用自定义导航栏的方案
自定义导航栏, navigationStyle: custom 1.9.5 开始支持, 但需要 整个小程序 所有页面都自定义导航栏才行, 这对于我们的场景不太适用, 只能作为长远打算
- 能讲下延迟重试策略么?
根据我们的监控统计, 请求微信的接口95%的都会在650ms内响应, 大于这个时间最终请求超时的概率就很大了, 所以最终策略是
一旦请求在650ms内未完成, 立即发出后备请求, 直到任意一个获得响应, 并且是正确的响应内容
这个策略我们开发成了一个superagent插件, 后续会考虑开源出来
- 这么看做线下扫码活动成本 门槛还是有点高, 有什么别的方法能减少这方面成本?
我们最初还考虑过另一种方案, 可以通过定位, 让用户选择附近商家, 会在体验 运营自由度上有折扣, 但可以规避各种二维码物料的问题.
不过最后还是没有用这个方案, 如果可接受体验上的折扣, 还是可以用这种方案的.
其次就是把上文提到的二维码相关功能做成服务开放出来, 我也有关注提供这方面支持的互联网产品, 目前看未有发现.
上面的文字版的总结,也推荐大家如有时间的话观看原版视频分享,现在已经上传到了 Bilibili,链接为:www.bilibili.com/video/av348… ,欢迎大家观看学习和收藏~