2018-05-22

微信公众号开发

前面做过 HG 项目的微信端,里面用到微信扫码、支付、图片选取、拍照、分享的功能。用到 weixin-js-sdk 和 WeixinJSBridge。

前端开发以Vue代码为例,后端以NodeJS为例。后端开发还需详细阅读开发者文档

1、开发准备

1.1 新建测试号

用于开发和测试环境,产品经理需准备

步骤:

登录【公众号平台】 → 侧边栏开发选择【开发者工具】→ 进入【公众平台测试帐号】→ 扫码确认 → 成功

注意:测试号的配置都在此页面

如图:

1.2 配置公众号

保存 appID 、appSecret 和 测试号二维码

注意:及时保存,以免需要的时候找不到

修改授权回调页面地址

注意:用户确认授权后重定向的地址

修改接口配置信息、JS接口安全域名

注意:调用 weixin-js-sdk 功能或调用 WeixinJSBridge 页面所在域名

修改JS安全域名:

​公众号主页侧边栏设置选择【公众号设置】→ 选择上面第二个tab【功能设置】第三项

如果有支付功能需要配置授权目录

注意:这个需要在商户平台修改,不是在公众平台

举例:

​现在需要在 http://www.behuntergatherer.com/pay/payment 页面进行支付,就需要设置一个授权目录为 http://www.behuntergatherer.com/pay/ 。在支付页面路由的前一个目录。

建议:

​统一一个页面进行支付。比如 http://www.behuntergatherer.com/pay/payment 是支付页面,我在 http://www.behuntergatherer.com/vip/order 页面需要支付,则生成好订单后跳转 http://www.behuntergatherer.com/pay/payment 页面进行支付。

下载安装【Web开发者工具】

注意:开发同学准备,下载地址

目的:

页面开发兼容性、布局和浏览器表现不一致

微信接口调试、调用接口有日志

注意事项:

支付功能不能在本地测试,不能使用测试公众号测试

开发工具可能存在未知 bug,请保持更新

##2、简单config封装

2.1 开发文档

示例 用手机端微信打开体验

微信JS接口 微信接口详细文档

微信JS接口调试工具 可以在线上测试接口返回的数据结构

2.2 配置封装

这些接口需要配置以后才能调用

API可以配需要的几个,亦可以全部

一次配置只对当前页面有效,跳转页面需要重新配置

2.2.1 JS-SDK接口配置

注意:需要调用微信JSAPI接口的页面,不能使用h5 pushState进行跳转,会造成无效的签名问题。请使用location.href。

// utils.js//微信配置接口通用方法constALL_API_LIST = ['scanQRCode','...']exportfunctionconfigWxApi(jsApiList = ALL_API_LIST){returnnewPromise((resolve, reject) =>{//使用当前的href获取签名,后端返回配置接口所需要的参数get('/mdm2/api/getSignature.do', {url:encodeURIComponent(window.location.href),}).then(data=>{wx.config({debug: process.env.NODE_ENV !=='production',appId: data.appId,timestamp:Number(data.timestamp),//秒数,不是毫秒nonceStr: data.nonceStr,signature: data.signature,jsApiList,})wx.error(function(res){Toast('调用微信jsapi返回的状态:'+ res.errMsg)reject()})wx.ready(resolve)})})}

2.2.2 WeixinJSBridge

// utils.jsexportfunctioncheckWxBridge(){returnnewPromise((resolve, reject) =>{if(typeofWeixinJSBridge =="undefined") {if(document.addEventListener) {document.addEventListener('WeixinJSBridgeReady', resolve,false)}elseif(document.attachEvent){document.attachEvent('WeixinJSBridgeReady', resolve)document.attachEvent('onWeixinJSBridgeReady', resolve)}}else{resolve()}})}

2.3 配置函数使用

2.3.1 JS-SDK使用

注意事项:

所有调用 JS-SDK 的接口都必须 queryWxApi

都必须处理错误情况,增加用户体验

//采用Promise封装有两种使用方法,统一选择一个// app.jsimport{ configWxApi }from'utils'// Promise写法mounted () {  configWxApi()    .then(res=>{//配置完成wx.chooseImage()    })    .catch(err=>{//配置失败})}//同步函数写法asyncmounted () {try{constconfigResult =awaitqueryWxApi(['scanQRCode'])//成功处理}catch(err) {//错误处理}}

2.3.2 WeixinJSBridge

注意事项:

所有调用JS-SDK的的接口都必须 checkWxBridge

都必须处理错误情况,增加用户体验

// someComponent.jsimport{ checkWxBridge }from'utils'checkWxBridge().then(()=>{ WeixinJSBridge.call('hideOptionMenu')  WeixinJSBridge.invoke('getBrandWCPayRequest')})

3、后端开发示例

后端一两个常见例子说明

3.1 Access_token

​这是微信开发中最基础的东西,详情见官方文档 详细文档 => 任意门

这是什么东西?

access_token是公众号的全局唯一接口调用凭据,公众号调用各接口时都需使用access_token

有什么属性?

access_token的存储至少要保留512个字符空间

access_token的有效期目前为2个小时(7200秒)

重复获取将导致上次获取的access_token失效

怎么获取?

调用接口时,请登录“微信公众平台-开发-基本配置”提前将服务器IP地址添加到IP白名单中,点击查看设置方法,否则将无法调用成功。

https请求方式: GET

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

参数说明

参数是否必须说明

grant_type是获取access_token填写client_credential

appid是第三方用户唯一凭证

secret是第三方用户唯一凭证密钥,即appsecret

返回说明

正常情况下,微信会返回下述JSON数据包给公众号:

{"access_token":"ACCESS_TOKEN","expires_in":7200}

开发建议:统一获取和刷新Access_token,不应该各自去刷新,否则容易造成冲突,导致access_token覆盖而影响业务。

// accessToken.jsmodule.exports = {getToken:function(callback){//判断token是否过期,如果过期就重取,未过期就返回vartokenPath = path.join(__dirname,'./token.json')vartoken =JSON.parse(fs.readFileSync(tokenPath))varcurrTime = utils.getTimeStamp().secondvarisExpired = currTime > token.timestampif(isExpired) fetchToken(callback)elsecallback(token.value)},updateToken:function(callback){fetchToken(callback)},}functionfetchToken(callback){varconfigPath = path.join(__dirname,'./config.json')varconfig =JSON.parse(fs.readFileSync(configPath))varparam = {grant_type:'client_credential',appid: config.appId,secret: config.secret,}varparamStr = utils.obj2Params(param)varurl =`https://api.weixin.qq.com/cgi-bin/token?${paramStr}`request(url,function(err, res, body){//记录获取token时间,两小时后过期重取try{varresReult =JSON.parse(body);varnewToken = {value: resReult.access_token,timestamp: utils.getTimeStamp().second +7200,            }varfilePath = path.join(__dirname,'token.json')            fs.writeFileSync(filePath,JSON.stringify(newToken))            callback(newToken.value)        }catch(e) {console.log('获取token出错,', e)          callback()        }})}// somejs.jsimport{ getToken }from'accessToken'getToken(function(token){//其他业务})

3.2 获取用户openid

公众号开发中常见的需求 详细文档 => 任意门

openid是啥?

是加密后的微信号,每个用户对每个公众号的OpenID是唯一的。对于不同公众号,同一用户的openid不同。

openid能干啥?

公众号可通过本接口来根据OpenID获取用户基本信息,包括昵称头像性别所在城市语言关注时间

提示:

​用户头像链接http://wx.qlogo.cn/mmopen/g3MonUZtNHkdmzicIlibx6iaFqAc56vxLSUfpb6n5WKSYVY0ChQKkiaJSgQ1dZuTOgvLLrhJbERQQ4eMsv84eavHiaiceqxibJxCfHe/0 修改链接最后一个数字也是就是0,能获取不同尺寸的头像,(有0、46、64、96、132数值可选,0代表640*640正方形头像)。

{"subscribe": 1, //用户是否订阅该公众号0:未关注1:已关注"openid": "o6_bmjrPTlm6_2sgVt7hMZOPfL2M",    "nickname": "Band",//用户的昵称"sex": 1,    //性别,1:男性,2:女性,0:未知"language": "zh_CN",    //用户的语言,简体中文为zh_CN  "city": "广州",    "province": "广东",    "country": "中国",    "headimgurl": "LINK_URL",//用户头像图片链接"subscribe_time": 1382694957,//关注公众号时间"unionid": " o6_bmasdasdsad6_2sgVt7hMZOPfL", //  "remark": "",//公众号运营者对粉丝的备注"groupid": 0,//用户所在的分组ID(兼容旧的用户分组接口)"tagid_list":[128,2]//用户被打上的标签ID列表}

怎么拿到openid?

两种获取的方法(相同接口所带参数不同而已):

静默授权 snsapi_base,体验较好,获取到的数据较少

用户授权 snsapi_userinfo,体验较差,用户可能拒绝,获取到的数据多

​注意:对于已关注公众号的用户,如果用户从公众号的会话或者自定义菜单进入本公众号的网页授权页,即使是scope为snsapi_userinfo,也是静默授权,用户无感知。

3.3 获取签名

签名在微信开发过程中扮演了一个非常重要的角色,很容易出错 详细文档 => 任意门

签名是啥?

公众号用于调用微信接口的临时票据

怎么获取?

官方文档说明 详细文档 => 任意门

3.4 支付功能实现

​官方详细文档 => 任意门

4、前端开发示例

4.1 关注快捷入口

​ 微信是反对引导关注的,但是,为了能留住访客。we have to。使用的链接是公众号详情页面下【历史消息】页面。可以点进页面,然后右上角【点点点】点击然后复制链接获取。

4.2 接口使用【支付】(WeixinJSBridge)

​官方详细文档 => 任意门

4.2.1 功能开发

注意事项

时间戳为秒数,部分系统取到的值为毫秒级,需要转换成秒(10位数字)

package 内容是"prepay_id=" 是预订单id(统一下单任意门

paySign 是 params 所有参数根据微信签名规则生成

import{ checkWxBridge }from'utils'someFunction () {get('/wx/fetchSign').then(data=>{// data需要的数据// appId, timeStamp, nonceStr, package, paySignconstparams =Object.assign(data, {signType:'MD5'})        checkWxBridge().then(()=>{          WeixinJSBridge.invoke('getBrandWCPayRequest', params, res => {// res数据结构{err_msg: 'get_brand_wcpay_request:ok'}// err_msg可能的值ok、fail、cancel})        })    })}

4.2.2 测试号测试支付

测试号是没有支付功能,需要用正式的公众号,而且需要有支付权限的公众号(个人公众号不可支付)。在测试环境使用正式环境的openid去实现支付。

varopenIdMap = {'测试公众号openId':'正式公众号openId','正式公众号openId':'测试公众号openId',}//测试环境1.【前端】购买请求2.【后端】用户购买(测试公众号openId)3.【后端】统一下单用(openIdMap[测试公众号openId])4.【前端】拿到prepay_id,WeixinJSBridge.invoke('getBrandWCPayRequest')支付5.【前端】用户支付请求=>微信6.【后端】收到支付成功或回调(可异步或同步),通知前端7.【后端】保存相应数据到(openIdMap[正式公众号openId])

4.3 头像剪切功能(JS-SDK)

CRMWeb 项目使用的 VueJS,使用的是npm相关包 vue-croppa

4.3.1 通过Input选择

vue-croppa 直接选择图片

输出裁剪后的图片

4.3.2 通过weixin-js-sdk选择

页面签名

import{ queryWxApi }from'utils'mounted () {queryWxApi().then(res=>this.wxConfSucc =true)}

选择本地图片或拍照,获取到 localId

wx.chooseImage({count:1,//可选择张数sizeType: ['original','compressed'],sourceType: ['album',//从相册选'camera',//拍照],success:(res) =>{letlocalId = res.localIds[0]    },})

通过 localId 上传到微信服务器拿到 serverId(只能保存3天)

wx.uploadImage({localId: localId,//本地IDisShowProgressTips:1,//是否显示上传进度success:res=>{constserverId = res.serverId    },})

服务端通过 serverId 下载图片到自己服务器,并返回图片链接给前端

croppa(    v-model="croppa",    :width="coroppaStyle.width":height="coroppaStyle.height":disable-click-to-choose="false":show-remove-button="false":initial-position="position")    img(    :src="showImgUrl"slot="initial")

5、常见错误

调用 config 接的时候传入参数 debug: true 可以开启 debug 模式,页面会 alert 出错误信息。以下为常见错误及解决方法:

5.1 invalid url domain

当前页面所在域名与使用的 corpid 没有绑定(可在该企业号的应用可信域名中配置域名)。

5.2 invalid signature

签名错误,建议按如下顺序检查:

确认签名算法正确,可用 http://mp.weixin.qq.com/debug/cgi-bin/sandbox?t=jsapisign 页面工具进行校验。

确认 config 中 nonceStr, timestamp 与用以签名中的对应 noncestr, timestamp一致。

确认 url 是页面完整的 url (请在当前页面 alert(location.href.split('#')[0]) 确认),包括 http(s):// 部分,以及'?'后面的 GET 参数部分,但不包括 '#'hash 后面的部分。

确认 config 中的 appid 与用来获取 jsapi_ticket 的 corpid 一致。

确保一定缓存 access_token 和 jsapi_ticket。

可能获取 access_token 次数超过一天限制的数量。

使用 pushState 来实现 web app 的页面会导致签名失败,改用 location.href。

确保你获取用来签名的 url 是动态获取的,动态页面可参见实例代码中 php 的实现方式。如果是 html 的静态页面在前端通过 ajax 将 url 传到后台签名,前端需要用js获取当前页面除去'#'hash部分的链接(可用 location.href.split('#')[0] 获取,而且需要encodeURIComponent),因为页面一旦分享,微信客户端会在你的链接末尾加入其它参数,如果不是动态获取当前链接,将导致分享后的页面签名失败。

5.3 the permission value is offline verifying

这个错误是因为 config 没有正确执行,或者是调用的 JSAPI 没有传入 config 的 jsApiList 参数中。建议按如下顺序检查:

确认 config 正确通过。

如果是在页面加载好时就调用了 JSAPI,则必须写在 wx.ready 的回调中。

确认 config 的 jsApiList 参数包含了这个 JSAPI。

5.4 permission denied

​ 该企业号没有权限使用这个 JSAPI(部分接口需要认证之后才能使用)

5.5 function not exist

​ 当前客户端版本不支持该接口,请升级到新版体验。

5.6 6.0.1版本config:ok,6.0.2版本之后不ok

​ 因为6.0.2版本之前没有做权限验证,所以 config 都是ok,但这并不意味着你 config 中的签名是 ok 的,请在6.0.2检验是否生成正确的签名以保证 config 在高版本中也 ok。

5.7 在iOS和Android都无法分享

​请确认企业号已经认证,只有认证的企业号才具有分享相关接口权限,如果确实已经认证,则要检查监听接口是否在wx.ready回调函数中触发

5.8 服务上线之后无法获取jsapi_ticket

​因为 access_token 和 jsapi_ticket 必须要在自己的服务器缓存,否则上线后会触发频率限制。请确保一定对token 和 ticket 做缓存以减少服务器请求,不仅可以避免触发频率限制,还加快你们自己的服务速度。目前为了方便测试提供了1w的获取量,超过阀值后,服务将不再可用,请确保在服务上线前一定全局缓存 access_toke和 jsapi_ticket,两者有效期均为7200秒(以返回结果中的 expires_in 为准),否则一旦上线触发频率限制,服务将不再可用

5.9 uploadImage传多图

​ 目前只支持一次上传一张,多张图片需等前一张图片上传之后再调用该接口。

5.10 对本地选择的图片进行预览

​ chooseImage 接口本身就支持预览,不需要额外支持。

5.11 出现config:fail错误

​ 这是由于传入的 config 参数不全导致,请确保传入正确 appId、timestamp、nonceStr、signature 和需要使用的 jsApiList。

5.12 使用pushState来实现web app的页面会导致签名失败

​ 此问题已在Android6.2中修复。在IOS中此问题依然存在。

5.13 Android uploadImage在chooseImage的回调中有时候会不执行

​ Android6.2 会解决此问题,若需支持低版本可以把调用 uploadImage 放在 setTimeout 中延迟 100ms 解决。

5.14 getLocation坐标在openLocation有偏差

​ 因为 getLocation 返回的是gps坐标,openLocation 打开的腾讯地图为火星坐标,需要第三方自己做转换,6.2 版本开始已经支持直接获取火星坐标。

5.15 未关注测试号

​ 通过 snsapi_base 方式获取未关注公众号的用户 openid。测试号是不可以的。

6、附录

6.1 微信JSAPI列表

['onMenuShareTimeline','onMenuShareAppMessage','onMenuShareQQ','onMenuShareWeibo','onMenuShareQZone','startRecord','stopRecord','onVoiceRecordEnd','playVoice','pauseVoice','stopVoice','onVoicePlayEnd','uploadVoice','downloadVoice','chooseImage','previewImage','uploadImage','downloadImage','translateVoice','getNetworkType','openLocation','getLocation','hideOptionMenu','showOptionMenu','hideMenuItems','showMenuItems','hideAllNonBaseMenuItem','showAllNonBaseMenuItem','closeWindow','scanQRCode',]

6.2 WeixinJSBridge 接口列表

[//类型一'hideOptionMenu',//隐藏右上角按钮'showOptionMenu',//显示右上角按钮'hideToolbar',//隐藏底部工具栏'showToolbar',//显示底部工具栏//类型二'getNetworkType',//获取网络状态'getBrandWCPayRequest',//调用支付]//类型一WeixinJSBridge.call('hideToolbar')//类型二WeixinJSBridge.invoke('getBrandWCPayRequest')

6.3 所有菜单项列表

[//基本类'menuItem:exposeArticle'//举报'menuItem:setFont'//调整字体'menuItem:dayMode'//日间模式'menuItem:nightMode'//夜间模式'menuItem:refresh'//刷新'menuItem:profile'//查看公众号(已添加)'menuItem:addContact'//查看公众号(未添加)//传播类'menuItem:share:appMessage'//发送给朋友'menuItem:share:timeline'//分享到朋友圈'menuItem:share:qq'//分享到QQ'menuItem:share:weiboApp'//分享到Weibo'menuItem:favorite'//收藏'menuItem:share:facebook'//分享到FB//保护类'menuItem:jsDebug'//调试'menuItem:editTag'//编辑标签'menuItem:delete'//删除'menuItem:copyUrl'//复制链接'menuItem:originPage'//原网页'menuItem:readMode'//阅读模式'menuItem:openWithQQBrowser'//在QQ浏览器中打开'menuItem:openWithSafari'//在Safari中打开'menuItem:share:email'//邮件'menuItem:share:brand'//一些特殊公众号]

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

推荐阅读更多精彩内容

  • 2、Android组件化项目实施步骤 1)第一步:配置可自动将组件在Application和Library属性之间...
    自然之秋阅读 336评论 0 0
  • 女,46岁。无法入睡2年多,白天,黑夜都无法入睡。吃安眠药已经没法帮助睡眠了。找过好多专家,名中医治疗。效果...
    医难为阅读 652评论 0 4
  • 文/白素心 今天是我加入无戒第三期学习训练营的第21天,这已经是我在无戒写作训练营的第2个21天了,2个21天,收...
    素心成长阅读 551评论 12 12
  • 感谢桔乡
    刘登平阅读 189评论 0 0