初识小游戏- 开放能力

1.用户授权

部分接口需要经过用户授权同意才能调用。这些接口按使用范围分成多个 scope ,用户选择对 scope 来进行授权,当授权给一个 scope 之后,其对应的所有接口都可以直接使用。

调用接口发起授权

第一次使用某个 scope 下的接口时,会弹窗询问用户,“XXX申请获得以下权限:(权限描述)”。如果用户点击允许,则可获得此 scope 的接口权限。并且接口调用成功,否则接口调用失败。

wx.login({
  success: function () {
    wx.getUserInfo()
  }
})

提前发起授权

如果需要提前发起授权获得用户同意,则可调用 [wx.authorize()] 来提前发起授权。

wx.authorize({
  scope: 'scope.record'
})

处理用户拒绝授权

用户有可能拒绝小程序发起的授权申请,需要处理这种情况。

wx.login({
  success: function () {
    wx.getUserInfo({
      fail: function (res) {
        // iOS 和 Android 对于拒绝授权的回调 errMsg 没有统一,需要做一下兼容处理
        if (res.errMsg.indexOf('auth deny') > -1 ||     res.errMsg.indexOf('auth denied') > -1 ) {
          // 处理用户拒绝授权的情况
        }
      }
    })
  }
})

wx.authorize({
  scope: 'scope.record',
  fail: function (res) {
    // iOS 和 Android 对于拒绝授权的回调 errMsg 没有统一,需要做一下兼容处理
    if (res.errMsg.indexOf('auth deny') > -1 ||     res.errMsg.indexOf('auth denied') > -1 ) {
      // 处理用户拒绝授权的情况
    }    
  }
})

获取用户授权设置

通过调用 [wx.getSetting()]接口可以获取用户当前的授权处理信息。

wx.getSetting({
  success: function (res) {
    var authSetting = res.authSetting
    if (authSetting['scope.userInfo'] === true) {
      // 用户已授权,可以直接调用相关 API
    } else if (authSetting['scope.userInfo'] === false){
      // 用户已拒绝授权,再调用相关 API 或者 wx.authorize 会失败,需要引导用户到设置页面打开授权开关
    } else {
      // 未询问过用户授权,调用相关 API 或者 wx.authorize 会弹窗询问用户
    }
  }
})

引导用户重新授权

如果用户拒绝过某个 scope 的授权申请,则后续这个 scope 下的相关 API 调用都会直接失败,用 [wx.authorize()]申请此 scope 也会直接失败,而不会弹窗询问用户。这种情况下,需要引导用户主动到设置页面打开相应的 scope 权限。

授权页面的进入路径为:右上角菜单->关于(小程序名字)->右上角菜单->设置

Scope 列表

scope 对应 API 描述
scope.userInfo [wx.getUserInfo()] 用户信息
scope.userLocation [wx.getLocation()] 地理位置
scope.werun [wx.getWeRunData()] 微信运动步数
scope.record [wx.startRecord()]、[RecorderManager.start()] 录音功能
scope.writePhotosAlbum [wx.saveImageToPhotosAlbum()] 保存到相册

2.用户登录态签名

小程序的一部分后台(HTTP)接口要求验证用户登录态。开发者在调用时需提供以session_key为密钥生成的签名。其中session_key是指通过wx.login 获得的登录态。

签名算法

目前支持的签名算法是 hmac_sha256。 对于POST请求,开发者生成签名的算法是:

signature = hmac_sha256( post_data, session_key )

其中post_data为本次POST请求的数据包。特别地,对于GET请求,post_data等于长度为0的字符串。

signature = hmac_sha256( "", session_key )

签名示例

例如开发者需要请求的HTTP(POST)接口,其中请求包为一个json字符串。

curl -d '{"foo":"bar"}' 'https://api.weixin.qq.com/some_api?access_token=xxx&openid=xxx&signature=???&sig_method=hmac_sha256'

开发者需要计算出signature参数。假设用户当前有效的session_key 为 :

'o0q0otL8aEzpcZL/FT9WsQ=='

则开发者生成签名应该是

hmac_sha256('{"foo":"bar"}', 'o0q0otL8aEzpcZL/FT9WsQ==') = 654571f79995b2ce1e149e53c0a33dc39c0a74090db514261454e8dbe432aa0b

开发者服务器发起的HTTP请求

curl -d '{"foo":"bar"}' 'https://api.weixin.qq.com/some_api?access_token=xxx&openid=xxx&signature=654571f79995b2ce1e149e53c0a33dc39c0a74090db514261454e8dbe432aa0b&sig_method=hmac_sha256'

session_key 合法性校验

我们提供接口供开发者校验服务器所保存的登录态session_key是否合法。 为了保持session_key私密性,我们提供的校验接口本身不直接明文session_key,而是通过校验登录态签名完成。

接口地址
请求方法:GET

https://api.weixin.qq.com/wxa/checksession?access\_token=ACCESS\_TOKEN&signature=SIGNATURE&openid=OPENID&sig\_method=SIG\_METHOD

调用示例

curl -G 'https://api.weixin.qq.com/wxa/checksession?access_token=OsAoOMw4niuuVbfSxxxxxxxxxxxxxxxxxxx&signature=fefce01bfba4670c85b228e6ca2b493c90971e7c442f54fc448662eb7cd72509&openid=oGZUI0egBJY1zhBYw2KhdUfwVJJE&sig_method=hmac_sha256'

参数说明

参数 必填 说明
access_token 用凭证
openid 用户唯一标识符
signature 用户登录态签名
sig_method 用户登录态签名的哈希方法
buffer 托管数据,类型为字符串,长度不超过1000字节

返回结果

//正确时的返回JSON数据包如下:
{"errcode":0,"errmsg":"ok"}
//错误时的返回JSON数据包如下(示例为签名错误):
{"errcode":87009,"errmsg":"invalid signature"}

3.米大师支付签名

以查询余额的接口为例,原始请求信息:
米大师密钥:zNLgAGgqsEWJOg1nFVaO5r7fAlIQxr1u
HTTP请求方式: POST
请求的URI:/cgi-bin/midas/getbalance

sig签名
参与米大师签名请求参数

"openid":"odkx20ENSNa2w5y3g_qOkOvBNM1g",
"appid":"wx1234567",
"offer_id":"12345678",
"ts":1507530737,
"zone_id":"1",
"pf":"iap"

对参与米大师签名的参数按照key=value的格式,并按照参数名ASCII字典序升序排序如下:

stringA="appid=wx1234567&offer_id=12345678&openid=odkx20ENSNa2w5y3g_qOkOvBNM1g&pf=iap&ts=1507530737&zone_id=1"

拼接uri、method和米大师密钥:

stringSignTemp=stringA+"&org_loc=/cgi-bin/midas/getbalance&method=POST&secret=zNLgAGgqsEWJOg1nFVaO5r7fAlIQxr1u"

把米大师密钥作为key,使用HMAC-SHA256得到签名。

sig=hmac_sha256(key,stringSignTemp)
   ="d1f0a41272f9b85618361323e1b19cd8cb0213f21b935aeaa39c160892031e97"

mp_sig签名

1 参与开平签名请求参数

"access_token":"ACCESSTOKEN",
"openid":"odkx20ENSNa2w5y3g_qOkOvBNM1g",
"appid":"wx1234567",
"offer_id":"12345678",
"ts":1507530737,
"zone_id":"1",
"pf":"iap",
"sig":"d1f0a41272f9b85618361323e1b19cd8cb0213f21b935aeaa39c160892031e97"

2 对参与开平签名的参数按照key=value的格式,并按照参数名ASCII字典序升序排序如下:

stringA="access_token=ACCESSTOKEN&appid=wx1234567&offer_id=12345678&openid=odkx20ENSNa2w5y3g_qOkOvBNM1g&pf=iap&sig=d1f0a41272f9b85618361323e1b19cd8cb0213f21b935aeaa39c160892031e97&ts=1507530737&zone_id=1"

3 拼接uri、method和session_key:

stringSignTemp=stringA+"&org_loc=/cgi-bin/midas/getbalance&method=POST&session_key=V7Q38/i2KXaqrQyl2Yx9Hg=="

4 把session_key作为key,使用HMAC-SHA256得到签名。

mp_sig=hmac_sha256(key,stringSignTemp)   ="f7fc0198b1bf795892bed804d145206105eb5835d6ac53fd745834b4a1236c78"

4.关系链数据使用指南

一个微信用户的关系链数据包括两部分:

  • 该用户好友的用户数据
  • 该用户所在的某个群的群成员的用户数据。

获取关系链数据的 API:

  • [wx.getFriendCloudStorage()]获取当前用户也玩该小游戏的好友的用户数据
  • [wx.getGroupCloudStorage()]获取当前用户在某个群中也玩该小游戏的成员的用户数据

这两个 API 的返回结果都是一个对象数组,数组的每一个元素都是一个表示用户数据的对象,其结构如下:

属性 类型 说明
openId string 用户的 openId
avatarUrl string 用户的微信头像 url
nickName string 用户的微信昵称
data Object 用户的游戏数据

用户的 游戏数据 指的是用户的段位、战绩等游戏业务特有的数据,通过调用 [wx.setUserCloudStorage()]可以将当前用户的游戏数据托管在微信后台。只有被托管过数据的用户,才会被视为 玩过 该小游戏的用户,才会出现在 [wx.getFriendCloudStorage()] 和 [wx.getGroupCloudStorage()] 返回的对象数组中。

还提供了以下 API:

  • [wx.removeUserCloudStorage()] 删除用户托管数据中指定字段的数据
  • [wx.getUserCloudStorage()]获取当前用户的托管数据

[wx.getUserCloudStorage]、[wx.getFriendCloudStorage()]和 [wx.getGroupCloudStorage()] 只能在 开放数据域 中调用。
[wx.setUserCloudStorage()]和 [wx.removeUserCloudStorage()]可以同时在 主域 和开放数据域中调用。

开放数据域

开放数据域 是一个封闭、独立的 JavaScript 作用域。要让代码运行在开放数据域,需要在 game.json 中添加配置项 openDataContext 指定开放数据域的代码目录。添加该配置项表示小游戏启用了开放数据域,这将会导致一些 [限制]。

{
  "deviceOrientation": "portrait",
  "openDataContext": "src/myOpenDataContext"
}

同时还需要在该目录下创建 index.js 作为开放数据域的入口文件,其代码运行在开放数据域。game.js 是整个游戏的入口文件,其代码运行在 主域
主域和开放数据域中的代码不能相互 require。

主域和开放数据域的通信

开放数据域不能向主域发送消息。

主域可以向开放数据域发送消息。调用 wx.getOpenDataContext()方法可以获取开放数据域实例,调用实例上的 OpenDataContext.postMessage()]方法可以向开放数据域发送消息。

// game.js
let openDataContext = wx.getOpenDataContext()
openDataContext.postMessage({
  text: 'hello',
  year: (new Date()).getFullYear()
})

在开放数据域中通过 wx.onMessage()方法可以监听从主域发来的消息。

// src/myOpenDataContext/index.js
wx.onMessage(data => {
  console.log(data)
  /* {
    text: 'hello',
    year: 2018
  } */
})

展示关系链数据

如果想要展示通过关系链 API 获取到的用户数据,如绘制排行榜等业务场景,需要将排行榜绘制到 sharedCanvas 上,再在主域将 sharedCanvas 渲染上屏。

// src/myOpenDataContext/index.js
let sharedCanvas = wx.getSharedCanvas()

function drawRankList (data) {
  data.forEach((item, index) => {
    // ...
  })
}

wx.getFriendUserGameData({
  success: res => {
    let data = res.data
    drawRankList(data)
  }
})

sharedCanvas 是主域和开放数据域都可以访问的一个离屏画布。在开放数据域调用 wx.getSharedCanvas() 将返回 sharedCanvas。

// src/myOpenDataContext/index.js
let sharedCanvas = wx.getSharedCanvas()
let context = sharedCanvas.getContext('2d')
context.fillStyle = 'red'
context.fillRect(0, 0, 100, 100)

在主域中可以通过开放数据域实例访问 sharedCanvas,通过 drawImage() 方法可以将 sharedCanvas 绘制到上屏画布。

// game.js
let openDataContext = wx.getOpenDataContext()
let sharedCanvas = openDataContext.canvas

let canvas = wx.createCanvas()
let context = canvas.getContext('2d')
context.drawImage(sharedCanvas, 0, 0)

5. 虚拟支付

小游戏为开发者提供游戏内虚拟物品的购买服务。

注:目前小游戏虚拟支付能力只支持在安卓Android系统内使用,暂不开放苹果iOS系统内虚拟支付功能。

在开通虚拟支付功能前,开发者需完成:

  1. 开通小程序微信支付
  2. 申请开通小游戏虚拟支付

wx.requestMidasPayment()是我们提供购买游戏币的API:

wx.requestMidasPayment(Object options)

示例代码

// 游戏币
wx.requestMidasPayment({
    mode: 'game',
    offerId: '',
    buyQuantity: 10,
    zoneId: 1,
    success() {
        // 支付成功
    },
    fail({ errMsg, errCode }) {
        // 支付失败
        console.log(errMsg, errCode)
    }
})

提供用于测试验证与调试的沙箱测试环境,并相应提供以下API:

接口名 功能 说明
[midasGetBalance] 查询余额 查看某个用户的游戏币余额
[midasPay] 扣除游戏币 扣除某个用户的游戏币
[midasCancelPay] 取消支付 在有效期内的订单,可以通过本接口取消该笔扣除游戏币的订单
[midasPresent] 游戏币赠送 向某个用户赠送游戏币

6.获取二维码

小游戏的二维码与小程序有着相同的样式和获取方式。通过后台接口可以获取小游戏的二维码,扫描该二维码可以直接进入小游戏。目前微信支持两种二维码;

7. 转发

用户在使用小游戏过程中,可转发消息给其他用户或群聊。

转发菜单

点击右上角按钮,会弹出菜单,菜单中的“转发”选项默认不展示。通过 [wx.showShareMenu()] 和 [wx.hideShareMenu()]可动态显示、隐藏这个选项。

被动转发

用户点击右上角菜单中的“转发”选项后,会触发转发事件,如果小游戏通过 [wx.onShareAppMessage()] 监听了这个事件,可通过返回自定义转发参数来修改转发卡片的内容,否则使用默认内容。

wx.onShareAppMessage(function () {
  // 用户点击了“转发”按钮
  return {
    title: '转发标题'
  }
})

主动转发

游戏内可通过 [wx.shareAppMessage()]接口直接调起转发界面,与被动转发类似,可以自定义转发卡片内容。

wx.shareAppMessage({
  title: '转发标题'
})

使用 Canvas 内容作为转发图片

如果不指定转发图片,默认会显示一个小程序的 logo。如果希望转发的时候显示 Canvas 的内容,可以使用 [Canvas.toTempFilePath()]或 [Canvas.toTempFilePathSync()]来生成一张本地图片,然后把图片路径传给 imageUrl 参数。

转发出来的消息卡片中,图片的最佳显示比例是 5:4

wx.onShareAppMessage(function () {
  return {
    title: '转发标题',
    imageUrl: canvas.toTempFilePathSync({
      destWidth: 500,
      destHeight: 400
    })
  }
})

withShareTicket 模式

通过 [wx.updateShareMenu] 接口可修改转发属性。如果设置 withShareTickettrue ,会有以下效果

  1. 选择联系人的时候只能选择一个目标,不能多选
  2. 消息被转发出去之后,在会话窗口中无法被长按二次转发
  3. 消息转发的目标如果是一个群聊,则
    • 会在转发成功的时候获得一个 shareTicket
    • 每次用户从这个消息卡片进入的时候,也会获得一个 shareTicket,通过调用 [wx.getShareInfo()] 接口传入 shareTicket 可以获取群相关信息

修改这个属性后,同时对主动转发和被动转发生效。

// 设置 withShareTicket: true
wx.updateShareMenu({
  withShareTicket: true
})

8.用户数据的签名验证和加解密

数据签名校验

为了确保开放接口返回用户数据的安全性,微信会对明文数据进行签名。开发者可以根据业务需要对数据包进行签名校验,确保数据的完整性。

  1. 签名校验算法涉及用户的session_key,通过 [wx.login] 登录流程获取用户session_key,并自行维护与应用自身登录态的对应关系。
  2. 通过调用接口(如 wx.getUserInfo]获取数据时,接口会同时返回 rawData、signature,其中 signature = sha1( rawData + session_key )
  3. 开发者将 signature、rawData 发送到开发者服务器进行校验。服务器利用用户对应的 session_key 使用相同的算法计算出签名 signature2 ,比对 signature 与 signature2 即可校验数据的完整性。

加密数据解密算法

接口如果涉及敏感数据(如[wx.getUserInfo]当中的 openId 和unionId ),接口的明文内容将不包含这些敏感数据。开发者如需要获取敏感数据,需要对接口返回的加密数据( encryptedData )进行对称解密。 解密算法如下:

  1. 对称解密使用的算法为 AES-128-CBC,数据采用PKCS#7填充。
  2. 对称解密的目标密文为 Base64_Decode(encryptedData)。
  3. 对称解密秘钥 aeskey = Base64_Decode(session_key), aeskey 是16字节。
  4. 对称解密算法初始向量 为Base64_Decode(iv),其中iv由数据接口返回。

另外,为了应用能校验数据的有效性,我们会在敏感数据加上数据水印( watermark )

watermark参数说明:

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

推荐阅读更多精彩内容

  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,594评论 18 139
  • 1.初识 微信小游戏目前的主要入口有下面几个:群或好友分享识别小游戏二维码微信聊天列表页面下拉后出现最近玩过的小游...
    勇敢的_心_阅读 2,919评论 0 6
  • 转载链接 注:本文转载知乎上的回答 作者:初雪 链接:https://www.zhihu.com/question...
    pengshuangta阅读 28,475评论 9 295
  • 我想有一双魔术手套,手套的每个手指上都有属于自己的音符,想想……大概是8个音符,加上两个配音! 还有还有,戴上个这...
    芢长大阅读 227评论 0 0