初识小游戏- 开放能力

作者: 勇敢的_心_ | 来源:发表于2018-04-09 15:22 被阅读116次

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

    相关文章

      网友评论

        本文标题:初识小游戏- 开放能力

        本文链接:https://www.haomeiwen.com/subject/qtlihftx.html