# 客户端app跳转微信小程序

# 接入示例

使用活动测试工具快速测试

# 安卓版调用代码示例

String appId = "平台的AppId"; // 填接入平台的移动应用(App)的 AppId,非小程序的 AppID
IWXAPI api = WXAPIFactory.createWXAPI(context, appId);
WXLaunchMiniProgram.Req req = new WXLaunchMiniProgram.Req();
req.userName = "gh_49af166706ae"; // 填腾讯游戏来联小程序原始id: gh_49af166706ae
req.path = "/pages/gameAccountBind/index?t=1591236939&gameIdList=lol,cjm&livePlatId=egame&sig=6e766f5017a737b82d99ae09b7b922da&faceUrl=http%3A%2F%2Fthirdqq.qlogo.cn%2Fg%3Fb%3Doidb%26k%3DgeNGjUqkHHLJdibhAaKycMQ%26s%3D100%26t%3D1483405761&nickName=%E6%96%97%E9%B1%BC%E6%98%B5%E7%A7%B0&redirectUrl=dydeeplink%3A%2F%2Fplatformapi%2FstartApp%3Ftype%3D37&code=25QY9rzaUK9vO0QYUHbNnNTkp0B4tRrmp%2BLog1LhBSYe6psYyShjUnUwzOa7t%2FwoP8RpzTYZICVTXwvNHgh7cg%3D%3D"; // 拉起小程序页面的带参路径,由basePath + queryString拼出
req.miniprogramType = WXLaunchMiniProgram.Req.MINIPTOGRAM_TYPE_RELEASE;// 可选打开 0-正式版,1-开发版,2-体验版
api.sendReq(req);
1
2
3
4
5
6
7

# 调用小程序相关参数

# 移动应用 appId

待接入的 app 在微信开放平台 (opens new window)注册时生成的 appid

以 腾讯游戏来联 APP 示例: image-appid-1 image-appid-2

# 小程序原始 ID

gh_49af166706ae (注:app 拉起时使用)

# 小程序 appId

wx707e2eb408780a5b (注:小程序拉起时使用)

# 基础路径

basePath: /pages/gameAccountBind/index

# 调用的 queryString 参数

字段 字段说明 是否需要加入签名(sig) 参数是否必填
gameIdList 游戏 id 列表,参考已接入业务 示例:cf,lol,yxzj
livePlatId 平台 id
code 包含 uid 的加密串,参考 平台侧构建code
t 时间戳
sig 签名,参考《小程序绑定:生成拉起签名》
actId 指活动 ID,在平台综合绑定中心入口不需要传该参数。但活动场景下为必传
faceUrl 用户所在平台头像地址
nickName 用户所在平台昵称
livePlatName 对应小程序中返回 app 的按钮名称,不传的话显示默认名称(需要通知 腾讯游戏来联 侧添加相关映射)
type 跳转小程序的具体功能场景,如qqCoinDeliver,表示进入到Q币发放页面。详细类型参照《type类型》
nextFlow 为发放真实Q币的流程id。若需引导用户到小程序内完成QB兑换,则此参数为必传参数, nextFlow参数中的值来自上一步发货接口的返回(注意:只有type为qqCoinDeliver时,此参数才有效。)
gameAuthScene 授权场景(对应不同的授权字段,活动场景为act_${actId},其他场景可咨询对接人员)
hideQQLogin 微信小程序内是否隐藏QQ登录模式; 不传默认为false, 传入true表示后续隐藏QQ登录模式
fromId 来源Id, 后续会在后台回调中带上该信息,长度限制为32,超过则丢弃

*注意1:不应传入多余的参数,最终生成的路径与参数的总长度不可超过1024,超过会被小程序底层逻辑截断 *注意2:所有参数均需 encode(编码)一次,编码规范参见encodeURIComponent (opens new window)。go语言中应使用url.PathEscape方法**,其他语言类似。注意生成的参数其中不包含;,/?:@&=+$#等参数

# type类型

  1. qqCoinDeliver: 跳转到Q币发放页面
  2. newGamePreorder: 跳转到新游预约页面
  3. loginLauncherWithBind: 跳转云游戏登录页面

# 小程序绑定:生成拉起签名

sig签名计算流程如下:

第一步:对基于特定参数:livePlatId、gameIdList、t、code和gameAuthScene(无授权场景时则该参数不参与计算签名)参数键名称部分使用ASCII排序。排序后,将键名对应的键值按顺序加入队列,假设队列名为array1。此时,队列中的值为排序后键值对应的实际值。

第二步:排序后对键值做URLENCODE,针对第一步队列中的值使用**+**链接。操作方式为: str1= implode("+", array1)。(js中implode替换为join)

编码补充: 为了了保持编码⼀一致性,腾讯后台侧使⽤用了了RFC 3986编码 规范,故在遇到空格字符时,该规范也会将单个空格变 成%20。而老一些的http encode使⽤用 RFC 1866规范,导致空格字符被编码为+号。

第三步:str1添加从腾讯游戏来联分配的key(每个平台有独立的key,如果遗忘可以联系腾讯游戏来联同学)。str1= str1+"+"+{key}。(注意是+号字符串)

第四步:计算签名。echo strtolower(md5(str1)),并转换成小写。

python实现签名示例代码如下:

#!/usr/bin/python
# -*- coding: UTF-8 -*-

import base64
import hashlib
import json
import time
import urllib
from urllib.parse import urlencode

import requests
from Crypto.Cipher import AES
from requests.utils import requote_uri


def _show_output(msg, title=''):
    print("*****{}*****".format(title), end="\n")
    print(msg, end="\n")


# AES ecb encrypt
def _encrypt(data, password):
    if isinstance(password, str):
        password = password.encode('utf8')

    bs = AES.block_size
    pad = lambda s: s + (bs - len(s) % bs) * chr(bs - len(s) % bs)
    cipher = AES.new(password, AES.MODE_ECB)
    data = cipher.encrypt(pad(data).encode('utf8'))
    encrypt_data = base64.b64encode(data)  # 取消注释,输出Base64格式
    return encrypt_data

# code计算,code用于加密用户UID信息
def _get_code(user_id, seckey):
    _show_output("", "code计算")
    login_info = {'userid': user_id, 'isAnchor': 0}
    json_str = json.dumps(login_info)
    _show_output(json_str, "code计算前先处理json部分")
    ecb_data = _encrypt(json_str, seckey)
    encrypt_str = str(ecb_data, 'utf-8')
    _show_output(encrypt_str, 'code加密完成')
    _show_output("https://oktools.net/aes", "ECB验证地址")
    return encrypt_str


# 根据key升序排序后生成query string
def _sorted_string(target_dict):
    sorted_vk = []
    for i in sorted(target_dict.keys()):
        k = i
        v = urllib.parse.quote( str(target_dict[k]),safe="" )
        sorted_vk.append(v)
    sorted_string = '+'.join(sorted_vk)
    return sorted_string


def _get_md5(stored_string, sigkey):
    origin_str = (stored_string + '+' + sigkey).encode(encoding='utf-8')
    _show_output(origin_str, 'sig计算,需要在签名后携带中台分配的sigkey,如下')
    return hashlib.new('md5', origin_str).hexdigest()

def get_sig_info(liveplat_id, gameIdList):
    # 签名所需参数配置
    _show_output("", "开始计算sig")
    sign_info = {
        "livePlatId": liveplat_id,
        "gameIdList": gameIdList,
        "t": int(time.time()),
        "code": _get_code("1", seckey)  # urlencode
    }
    # 按升序排序
    sorted_string = _sorted_string(sign_info)
    _show_output(sorted_string, '计算签名前先完成参数排序')

    # 计算签名
    sig = _get_md5(sorted_string, sigkey)
    _show_output(sig, "md5结果")
    # 最终参数
    params = urllib.parse.urlencode(sign_info, quote_via=urllib.parse.quote) + "&sig=" + sig
    _show_output(params, "最终结果")
    return params

if __name__ == '__main__':
    # sig签名计算
    liveplat_id = "test"
    sigkey = "YzJ6MqD1CT1z9aDr"
    seckey = "udeval1ErioGc1F9"
    gameIdList = "yxzj"
    actID = '904'
    get_sig_info(liveplat_id, gameIdList)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90

输出:

*****code计算前先处理json部分*****
{"userid": "1", "isAnchor": 0}
*****code加密完成*****
QwCF9dWRJG7EHkKEmnaCKn7eDhUgI1y/ByKrY+ctc6E=
*****ECB验证地址*****
https://oktools.net/aes
*****计算签名前先完成参数排序*****
QwCF9dWRJG7EHkKEmnaCKn7eDhUgI1y%2FByKrY%2Bctc6E%3D+yxzj+test+1702974479
*****sig计算,需要在签名后携带中台分配的sigkey,如下*****
b'QwCF9dWRJG7EHkKEmnaCKn7eDhUgI1y%2FByKrY%2Bctc6E%3D+yxzj+test+1702974479+YzJ6MqD1CT1z9aDr'
*****md5结果*****
18cc71c744c375d641f71671bbe42bdd
*****最终结果*****
livePlatId=test&gameIdList=yxzj&t=1702974479&code=QwCF9dWRJG7EHkKEmnaCKn7eDhUgI1y%2FByKrY%2Bctc6E%3D&sig=18cc71c744c375d641f71671bbe42bdd
1
2
3
4
5
6
7
8
9
10
11
12
13
14

其他语言sig计算可以参考接入SDK

# 返回 APP

用户完成角色绑定后,点击返回 APP 按钮,即可返回 APP,接入详情请参考返回说明 (opens new window)

返回时会将传递到小程序的 queryString 透传到 app

# 回调说明

WXEntryActivity 中

public void onResp(BaseResp resp) {
  if (resp.getType() == ConstantsAPI.COMMAND_LAUNCH_WX_MINIPROGRAM) {
    WXLaunchMiniProgram.Resp launchMiniProResp = (WXLaunchMiniProgram.Resp) resp;
      String extraData =launchMiniProResp.extMsg; // 对应传递过来的queryString参数
  }
}
1
2
3
4
5
6

# 绑定测试

绑定测试工具地址: https://livelink.qq.com/activities/#/testTool?activeName=bindCenter (opens new window)

隐私授权工具地址: https://livelink.qq.com/activities/#/testTool?activeName=gameDataAuth (opens new window)

# Q&A

# 安卓机器无法从小程序正常回跳到 app

解决方案:WXEntryActivity中添加taskAffinity属性,参见: https://www.jianshu.com/p/53cfb1b5995c (opens new window)

# 打开小程序报 bad_param 参数错误

解决方案: 检查 appID 为应用(app)的 appId, 检查 userName 为小程序(miniProgram)的原始 Id

# 提示应用安全检验不通过

自检流程: 1.不要同时调用SendReq和自检函数调用,自检函数会覆盖sendReq中的一些上下文数据,导致无法校验通过。 2. 上一次SendReq拉起微信完成操作前,不要再次调用sendReq。 后一次会覆盖前一次的上下文数据,导致检验不通过。 3. 确保拉起微信和微信跳回的App是同一个

# 应用第一次不能从小程序跳回app,第二次可以跳回

解决方案: 可以通过这个接口去注册一次来解决

public interface IWXAPI {
    boolean registerApp(String var1); //应用App
}
1
2
3
lastUpdate: 11/12/2024, 4:00:16 PM