# 调用指引

# 说明

本指引旨在详细阐述合作平台与游戏侧进行数据通信的流程,包括但不限于游戏数据拉取、平台数据上报、用户发货请求等操作。通过遵循本指引,合作平台能够确保与腾讯游戏来联侧的数据交互准确、高效且安全。

# 网关地址:

https://s1.livelink.qq.com/livelink

# 请求示例

以下是一个调用活动流程的示例,用于获取用户参与数据。其中,立项的活动ID为1201,游戏ID为cf。活动中由腾讯游戏来联运营人员配置的流程ID为69f33ur0。

请求类型:application/json

curl "https://s1.livelink.qq.com/livelink/?apiName=ApiRequest&actId=1201&livePlatId=xxx&gameId=cf&v=2.0&t=1710832329&nonce=urdszl&code=ikGRKGbjKrv86QU3UYlSPQ1rLUlTYFf1v3h2WJUpuNEZi%2B%2BXrkBwkfx0kLnCXXYYjcI%2B%2FumZHGvxlMIU1I51YtAtFO2KRg35KGA%2FG6ovKA3GLJ6tMcaUo8HHECD%2F1ULT&sig=187d8c99c79850b7c20d68799c3988fa" -d '{"flowId":"69f33ur0"}'
1

# 公共参数(GET)

以下是请求中必须携带的公共参数,主要用于确保请求的安全性。

apiName=ApiRequest&actId=1201&livePlatId=xxx&gameId=cf&v=2.0&t=1710832329&nonce=urdszl&code=ikGRKGbjKrv86QU3UYlSPQ1rLUlTYFf1v3h2WJUpuNEZi%2B%2BXrkBwkfx0kLnCXXYYjcI%2B%2FumZHGvxlMIU1I51YtAtFO2KRg35KGA%2FG6ovKA3GLJ6tMcaUo8HHECD%2F1ULT&sig=187d8c99c79850b7c20d68799c3988fa
1
参数 参与签名 必填 类型 说明
apiName string 请求方式。当前支持的请求方式包括:ApiRequest, FlowTaskQuery等。
livePlatId string 平台ID,由腾讯游戏来联统一分配。
actId int 活动ID。具体如何生成和查看活动ID请参考相关文档。
如何生成活动ID
如何查看活动列表 (opens new window)
gameId string 游戏ID(gameID)。见《业务代码
v float 版本号,当前场景默认填写:2.0
t int Unix时间戳,精度:秒
nonce string 8位随机字符串,范围为[a-zA-Z0-9]。,范围[a-zA-Z0-9]
code string 用户登录态。平台侧参考《平台侧接入》。见下文Demo:_get_code 函数
sig string 签名,见《加密与签名》。见下文Demo:get_sig_info

# 私有参数(json)

私有参数用于流程相关调用参数的填充。以下是支持的参数及其说明。

参数 必填 类型 说明
flowId string 流程id,必填项。由腾讯游戏来联在配置接口时生成。请确保填写正确,否则调用将无法成功。
serialCode string 选填项,用于请求防重。建议填写以提高数据一致性。如果为空,则每次都会发起新的请求处理。
anchorId string 选填项,主播id。如果填写,可以与特定主播的流程相关联。

serialCode是一个重要的可选参数,用于防止外部网络请求中的重复处理。由于外网环境的不稳定性,合作平台与腾讯游戏之间的网络请求可能无法每次都正常到达。为了保证数据的一致性,我们添加了serialCode字段。通过此字段,您可以多次重复尝试请求,以解决外网访问可能出现的超时问题。我们强烈建议您在使用接口时填写此字段。

# 请求示例

curl "https://s1.livelink.qq.com/livelink/?
apiName=ApiRequest&livePlatId=egame&actId=1201&gameId=cf&v=2.0&t=&nonce=&code=diVa28UpyL5G%2BVmKNVIcg%3D%3D&sig=e3909d0a2cb7f7d8f2ec03fb4ea96007" -d '{"flowId":"69f33ur0"}'
1
2

# 接口返回

{
  "iRet": 0,
  "apiName": "{接口ID}",  // 当前处理输出的模块ID
  "v":2,
  "jData": {
    //  每个模块ID输出结构固定
  },
  "sMsg": "ok",
  "tid": "174591110042135028"
}
1
2
3
4
5
6
7
8
9
10

# 返回说明

参数 类型 说明
iRet int 接口状态码,0-正常,非0-错误
v int 协议版本号,当前协议版本为2
apiName string 用于输出的模块名称
jData int 具体结构依赖apiName,详见《接口标准返回

# 接口标准返回(持续迭代)

接口ID 接口名称 使用说明
ApiActList 活动列表获取 获取平台在腾讯游戏来联上的立项活动列表
ApiDoc 活动详情获取 获取单个活动id的配置信息,包含流程列表,流程描述,以及其他流程配置属性
ApiGameList 获取游戏列表 获取腾讯游戏来联侧已支持的游戏列表信息
GetBindInfo 用户绑定查询 读取平台用户绑定过的游戏角色信息
ActBind 绑定用户与活动关系 固定用户参与该活动的绑定信息
GetActBind 查询用户在活动中的绑定信息 读取平台用户在禁止换绑活动绑定的游戏角色信息
Output 标准化返回 通用标准化输出
Error 接口异常 通用异常判定
ApiActionActQuas 查询活动流程资格 查询指定活动下某个流程的用户资格
JFCloud 云积分查询 查询用户云积分信息,包含总积分与可用积分
Lottery 道具发放(抽奖/领取) 通用道具发放返回信息
FlowTaskQuery 流程条件判定 快速判定用户是否满足流程信息,仅仅返回bool类型
FlowConditionQuery 输出条件信息 输出用户在流程条件中的数字信息
LotteryRecord 输出用户抽奖领取记录 输出用户角色在腾讯礼包单中的领取记录信息

# Demo:发起请求

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

from typing import Match
import requests
import time
import random
import string
import json
from Crypto.Cipher import AES
import urllib
import hashlib
from urllib.parse import urlencode, quote_plus
import binascii

import base64


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

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 = binascii.b2a_hex(data)  # 输出hex
    encrypt_data = base64.b64encode(data)         # 取消注释,输出Base64格式
    return encrypt_data

## code 加密计算
def _get_code(user_id, seckey):
    # test  login_info = {'userid':'000311'}
    login_info = {'userid': user_id, 'isAnchor': 0}
    json_str = json.dumps(login_info)
    ecb_data =  _encrypt(json_str,seckey)
    _show_output(ecb_data,'code加密完成')
    return str( ecb_data,'utf-8')

# 根据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,'计算md5')
    return  hashlib.new('md5', origin_str).hexdigest()

# sig 计算
def get_sig_info(act_id,user_id, seckey, sigkey):
    sign_info = {}
    sign_info["livePlatId"] = liveplat_id
    sign_info["gameId"] = game_id
    sign_info["v"] = 2.0
    sign_info["t"] = int( time.time() )
    sign_info["nonce"] = ''.join(random.choice(
        string.ascii_lowercase) for _ in range(6))
    sign_info["code"] =   _get_code(user_id, seckey)
    sign_info['actId'] = act_id
    # 按key升序排序
    sorted_string = _sorted_string(sign_info)
    _show_output(sorted_string,'计算签名')

    sig = _get_md5(sorted_string, sigkey)
    # 最终参数
    params = urllib.parse.urlencode(sign_info,quote_via=urllib.parse.quote) + "&sig="+sig
    return params

# 读取接口返回结构
def main_return_hanlder(ret_str):
    json_obj = json.loads(ret_str)
    # 读取apiname确定返回是来自那种模块,更多apiname参考文档《2.0协议版本说明》
    apiname = json_obj['apiName']
    # 如果是报错,则apiname为Error模块,可直接输出Msg错误信息,也可以捕获iRet之后结合《全局返回码》自定义错误信息
    if apiname =="Error":
        print(json_obj['sMsg'])
        return
    # 接口为报错,模块数据存在jData下
    data =  json_obj['jData']
    # 根据不同模块处理不同的返回
    if apiname == "Lottery":
        print("恭喜您获得了:"+data['packageName'])
        # 若是采用默认腾讯游戏来联返回,可直接弹出msg部分
        print(data['message'])
        return
    if apiname == "JFCloud":
        print("用户当前总积分为:"+ data['all']+",剩余积分为:"+data['left'])
        return

# 请求流程测试
if __name__ == '__main__':
    # 平台配置
    liveplat_id = "tencent"
    sigkey = "ertyuiopasdfghjl"
    seckey = "JHyuxq123xhtgwdc"
    # 游戏活动配置
    game_id = "cf"
    # 参与活动的平台用户ID,且该用户UID已完成与游戏角色的绑定,这里是cf活动
    user_id = "1234567"
    # 腾讯游戏来联平台活动立项ID
    act_id = 790
    # 活动流程ID
    flow_id = 'v0wgv6yb'

    request_params = get_sig_info(act_id,user_id, seckey, sigkey)
    headers={'Content-Type': 'application/json'}
    url = 'https://s1.livelink.qq.com/livelink/?apiName=ApiRequest&{}'.format(http_prefix,ip,request_params)


    _show_output(url,'开始http请求')
    rsp = requests.post(url, data=json.dumps({"flowId": flow_id}), headers={
                        'Content-Type': 'application/json'})
    print(rsp.text)
    # 返回值处理
    main_return_hanlder(rsp.text)

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
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
lastUpdate: 4/15/2024, 3:08:43 PM