初识小游戏- 开放能力

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 敏感数据获取的时间戳, 开发者可以用于数据时效性校验

你可能感兴趣的:(初识小游戏- 开放能力)