商户平台OpenAPI文档

开发前须知

成为微游商户

确保已经完成了微游商户的账号注册,可以登录商户平台。若尚未完成,请先完成注册流程

创建渠道

请根据域名、业务信息、变现策略与模板样式,提交对应的渠道链接申请。
image.png

image.png

image.png

获取访问密钥

image.png

API请求说明

请求访问频次

单商户每秒钟请求数上限为10。超过该上限的请求将被服务拒绝,并且返回如下内容:

{
    "code": 429,
    "reason": "RATELIMIT",
    "message": "service unavailable due to rate limit exceeded",
    "metadata": {}
}

签名机制

OpenAPI服务会对所有的API接口请求进行基于密钥签名机制的安全校验。以下为签名机制的详细说明。

密钥获取

参照上面“获取访问密钥”步骤说明获取商户的访问密钥。

签名规则

参数拼接

所有API请求参数(包括 header 中的 ts 时间戳)需按照以下规则进行拼接,最终生成待签名字符串(sign_string)。

  • 参数范围

  1. URL查询参数(GET 请求的 ?key=value 部分)

  2. POST/PUT 请求体参数(如 application/json 格式)

  3. HTTP Header 中的 ts 时间戳​​(需纳入签名计算)

  • 拼接格式

参数按 ​​参数名=参数值​​ 的形式拼接,多个参数之间用 ​​&​​ 分隔符连接。

  • 排序规则

所有参数(包括 ts)需按 ​​参数名的 ASCII 码从小到大排序​​(字典序),再按顺序拼接。

签名生成

上述拼接后的字符串,前面追加访问密钥进行SHA256运算,生成的字符串即为签名字符串。

示例:

const crypto = require('crypto');
const https = require('https');
const { URL } = require('url');

// 配置参数
const config = {
  secretKey: 'xxxx', // 访问密钥,请替换为实际参数,建议改为从环境变量获取
  clientId: '1234', // 商户ID,请替换为实际参数,建议改为从环境变量获取
  baseUrl: 'https://openapi.minigame.com', // 接口地址
};

/**
 * 生成签名字符串
 * @param {string} domain - 渠道域名
 * @returns {object} 包含时间戳和签名字符串的对象
 */
function generateSignature(domain) {
  // 获取当前时间戳(精确到毫秒)
  const timestamp = new Date().getTime();

  // 严格按Java实现的拼接规则
  // 注意:根据您的实际业务规则调整拼接顺序和格式
  const signData = `${config.secretKey}domain=${domain}&ts=${timestamp}`;

  // 生成SHA256签名
  const sign = crypto.createHash('sha256').update(signData).digest('hex');

  console.log('=== 签名生成调试信息 ===');
  console.log('使用的密钥:', config.secretKey);
  console.log('签名原始字符串:', signData);
  console.log('生成签名:', sign);
  console.log('当前时间戳:', timestamp);
  console.log('商户ID:', config.clientId);
  console.log('渠道域名:', domain);

  return { timestamp, sign };
}

返回说明

成功返回

请求OpenAPI服务成功返回HTTP.STATUS为200,body为json格式数据内容。

示例:

{
    "links": [
        {
            "app_id": "xingchenqiyuan-ceshi",
            "link": "https://tgm.minigamebm.com/game/xingchenqiyuan-ceshi/play"
        },
        {
            "app_id": "before-the-divine-register",
            "link": "https://tgm.minigamebm.com/game/before-the-divine-register/play"
        }
    ]
}

错误返回

请求OpenAPI服务成功返回HTTP.STATUS为非200,body为json格式数据内容。

  • 参数说明

名称描述类型是否必须示例备注
code整型错误码int429应用如果需要对OpenAPI错误进行处理,不推荐使用本字段。
reason字符串错误码stringRATELIMIT应用如果需要对OpenAPI错误进行处理,推荐使用本字段。
message错误信息stringRATELIMIT应用如果需要对OpenAPI错误进行处理,不推荐使用本字段。

示例:

{
    "code": 429,
    "reason": "RATELIMIT",
    "message": "service unavailable due to rate limit exceeded",
    "metadata": {}
}
错误码定义
错误码描述备注
RATELIMIT访问频次到达上限。目前的访问频次限制为商户级别,为10次/秒,超过该频次将有可能返回RATELIMIT错误。 请合理规划访问频次。
UNAUTHORIZED未授权。请检查:
请求头x-md-global-cid是否正确指定了商户唯一标识 ;
请求头sign是否正确指定了签名;
SERVER_INTERNAL服务器内部错误。请联系平台管理员进行处理。
MERCHANT_OPEN_API_INVALID商户open-api不可用。请求异常,请联系平台管理员进行处理。
MERCHANT_OPEN_API_CHANNEL_NOT_FOUND商户渠道不存在。请确认请求指定的渠道是否存在。
MERCHANT_OPEN_API_CHANNEL_FORBIDDEN商户渠道禁止访问。请确认是否有权限访问指定的渠道。
MERCHANT_OPEN_API_GAME_NOT_FOUND游戏未找到。请确认是否请求指定的游戏是否存在。

API列表

API概览

名称描述功能
GetChannelGameLinks获取渠道的游戏链接列表调用本接口根据渠道域名获取指定渠道的游戏链接列表
GetChannelGameDetail获取渠道的游戏详情调用本接口根据渠道域名获取指定渠道的游戏详情

根据渠道域名获取渠道的游戏链接列表。

请求格式

请求头

名称描述类型是否必须示例
sign签名stringca25acec11d7b89259f277b557c0841b9ae68391dd3e35bb3cda7f0c9a790e9e
此值仅为示例,实际值以应用实际请求为准
tsUnix时间戳(毫秒级,13位整数)int1749193616816
此值仅为示例,实际值以应用实际请求为准
x-md-global-cid商户唯一标识int205150566760842225
此值仅为示例,实际值以应用实际请求为准

请求参数

名称描述类型是否必须示例
domain渠道域名stringemu3lg.minigame.com
此值仅为示例,实际值以应用实际请求为准

返回参数

名称描述类型是否必须
links渠道游戏链接列表结构数组。
每个元素为如下结构:
{
"app_id":游戏的app_id,
"link":游戏在渠道内链接
}

示例:

{
    "links": [
        {
            "app_id": "xingchenqiyuan-ceshi",
            "link": "https://tgm.minigamebm.com/game/xingchenqiyuan-ceshi/play"
        },
        {
            "app_id": "before-the-divine-register",
            "link": "https://tgm.minigamebm.com/game/before-the-divine-register/play"
        }
    ]
}

示例代码 (开发语言为javascript)

const crypto = require('crypto');
const https = require('https');
const { URL } = require('url');

// 配置参数
const config = {
  secretKey: 'xxxx', // 访问密钥,请替换为实际参数,建议改为从环境变量获取
  clientId: '1234', // 商户ID,请替换为实际参数,建议改为从环境变量获取
  baseUrl: 'https://openapi.minigame.com', // 接口地址
};

/**
 * 生成签名字符串
 * @param {string} domain - 渠道域名
 * @returns {object} 包含时间戳和签名字符串的对象
 */
function generateSignature(domain) {
  // 获取当前时间戳(精确到毫秒)
  const timestamp = new Date().getTime();

  // 严格按Java实现的拼接规则
  // 注意:根据您的实际业务规则调整拼接顺序和格式
  const signData = `${config.secretKey}domain=${domain}&ts=${timestamp}`;

  // 生成SHA256签名
  const sign = crypto.createHash('sha256').update(signData).digest('hex');

  console.log('=== 签名生成调试信息 ===');
  console.log('使用的密钥:', config.secretKey);
  console.log('签名原始字符串:', signData);
  console.log('生成签名:', sign);
  console.log('当前时间戳:', timestamp);
  console.log('商户ID:', config.clientId);
  console.log('渠道域名:', domain);

  return { timestamp, sign };
}

/**
 * 封装后的发起 HTTPS 请求函数
 * @param {string} url - 请求地址
 * @param {object} headers - 请求头(Request Headers)
 * @returns {Promise} 返回参数
 */
function httpsRequest(url, headers = {}) {
  return new Promise((resolve, reject) => {
    const urlObj = new URL(url);

    const options = {
      hostname: urlObj.hostname,
      port: urlObj.port || 443,
      path: urlObj.pathname + urlObj.search,
      method: 'GET',
      headers: headers,
    };

    const req = https.request(options, (res) => {
      let data = '';

      res.on('data', (chunk) => {
        data += chunk;
      });

      res.on('end', () => {
        try {
          const parsedData = JSON.parse(data);
          resolve({
            status: res.statusCode,
            data: parsedData,
          });
        } catch (error) {
          resolve({
            status: res.statusCode,
            data: data,
          });
        }
      });
    });

    req.on('error', (error) => {
      reject(error);
    });

    req.end();
  });
}

/**
 * 获取游戏列表
 * @param {string} domain  - 渠道域名
 */
async function getGames(domain) {
  try {
    console.log(`\n=== Requesting ${domain} game list ===`);

    const { timestamp, sign } = generateSignature(domain);

    const url = `${config.baseUrl}/openapi/v2/channel/${domain}/game/links`;
    const response = await httpsRequest(url, {
      sign: sign,
      ts: timestamp.toString(),
      'x-md-global-cid': config.clientId,
    });

    console.log(url);

    console.log('Response status:', response.status);
    console.log('Response data:', JSON.stringify(response.data, null, 2));
  } catch (error) {
    console.error('Request failed:', error.message);
  }
}

async function main() {
  console.log('Starting API requests...\n');

  await getGames('minigame.com'); // 请替换为实际渠道域名
}

main().catch(console.error);

GetChannelGameDetail

根据渠道域名和游戏id(app_id)获取渠道的游戏详情。

请求格式

请求头

名称描述类型是否必须示例
sign签名stringca25acec11d7b89259f277b557c0841b9ae68391dd3e35bb3cda7f0c9a790e9e
此值仅为示例,实际值以应用实际请求为准
tsUnix时间戳(毫秒级,13位整数)int1749193616816
此值仅为示例,实际值以应用实际请求为准
x-md-global-cid商户唯一标识int205150566760842225
此值仅为示例,实际值以应用实际请求为准

请求参数

名称描述类型是否必须示例
domain渠道域名stringemu3lg.minigame.com
此值仅为示例,实际值以应用实际请求为准
id游戏id(app_id)stringim-a-game
此值仅为示例,实际值以应用实际请求为准

返回参数

名称描述类型是否必须
id游戏id(app_id)string
detail游戏信息结构:
name,游戏名称
type,游戏类型,枚举定义参见“游戏类型定义
how_to_play,游戏玩法
description,游戏简介
icon,游戏图标,结构定义参见“素材结构定义
big_icon,游戏大图标,结构定义参见“素材结构定义
banner,游戏Banner宣传图,结构定义参见“素材结构定义
flash,游戏闪屏图,结构定义参见“素材结构定义
link,游戏渠道内链接
结构

游戏类型定义

枚举值描述
1超休
2冒险
3音乐
4模拟
5纸牌
6棋类
7体育
8竞速
9益智
10策略
11动作
12战斗
13跑酷
14射击
15装扮
16塔防
17合成
18突破
19化妆
20博彩
21教育
22街机
23休闲竞技
24经营

素材结构定义

字段描述
uri素材uri
size素材尺寸
width,宽度,单位:像素
height,高度,单位:像素

示例:

{
    "id": "xingchenqiyuan-ceshi",
    "detail": {
        "name": "poxiaoxulie-658song7tian.danshi",
        "type": 3,
        "how_to_play": "",
        "description": "",
        "icon": {
            "uri": "https://asset.minigamecloud.com/minicloud-open/developer/uuppii_icon.png",
            "size": {
                "width": 256,
                "height": 256
            }
        },
        "big_icon": {
            "uri": "https://asset.minigamecloud.com/minicloud-open/developer/popstone2_big_icon.png",
            "size": {
                "width": 512,
                "height": 512
            }
        },
        "banner": {
            "uri": "https://asset.minigamecloud.com/minicloud-open/developer/game/cp/108872806854295552/04ab6803-3dfb-45fe-8f36-d87390edfded.png",
            "size": {
                "width": 580,
                "height": 390
            }
        },
        "flash": {
            "uri": "https://asset.minigamecloud.com/minicloud-open/developer/game/cp/108872806854295552/cd0d7487-3a6d-4af0-99b2-7fd237c28a50.png",
            "size": {
                "width": 720,
                "height": 1280
            }
        },
        "link": "https://tgm.minigamebm.com/game/xingchenqiyuan-ceshi/play"
    }
}

示例代码 (开发语言为javascript)

const crypto = require('crypto');
const https = require('https');
const { URL } = require('url');

// 配置参数
const config = {
  secretKey: 'xxxx', // 访问密钥,请替换为实际参数,建议改为从环境变量获取
  clientId: '1234', // 商户ID,请替换为实际参数,建议改为从环境变量获取
  baseUrl: 'https://openapi.minigame.com', // 接口地址
};

/**
 * 生成签名字符串
 * @param {string} domain - 渠道域名
 * @param {string} gameId - 游戏ID(app_id)
 * @returns {object} 包含时间戳和签名字符串的对象
 */
function generateSignature(domain, gameId) {
  // 获取当前时间戳(精确到毫秒)
  const timestamp = new Date().getTime();

  // 严格按Java实现的拼接规则
  // 注意:根据您的实际业务规则调整拼接顺序和格式
  const signData = `${config.secretKey}domain=${domain}&id=${gameId}&ts=${timestamp}`;

  // 生成SHA256签名
  const sign = crypto.createHash('sha256').update(signData).digest('hex');

  console.log('=== 签名生成调试信息 ===');
  console.log('使用的密钥:', config.secretKey);
  console.log('签名原始字符串:', signData);
  console.log('生成签名:', sign);
  console.log('当前时间戳:', timestamp);
  console.log('商户ID:', config.clientId);
  console.log('渠道域名:', domain);
  console.log('游戏ID:', gameId);

  return { timestamp, sign };
}

/**
 * 封装后的发起 HTTPS 请求函数
 * @param {string} url - 请求地址
 * @param {object} headers - 请求头(Request Headers)
 * @returns {Promise} 返回参数
 */
function httpsRequest(url, headers = {}) {
  return new Promise((resolve, reject) => {
    const urlObj = new URL(url);

    const options = {
      hostname: urlObj.hostname,
      port: urlObj.port || 443,
      path: urlObj.pathname + urlObj.search,
      method: 'GET',
      headers: headers,
    };

    const req = https.request(options, (res) => {
      let data = '';

      res.on('data', (chunk) => {
        data += chunk;
      });

      res.on('end', () => {
        try {
          const parsedData = JSON.parse(data);
          resolve({
            status: res.statusCode,
            data: parsedData,
          });
        } catch (error) {
          resolve({
            status: res.statusCode,
            data: data,
          });
        }
      });
    });

    req.on('error', (error) => {
      reject(error);
    });

    req.end();
  });
}

/**
 * 获取游戏详情
 * @param {string} domain - 渠道域名
 * @param {string} gameId - 游戏ID(app_id)
 */
async function getGameInfo(domain, gameId) {
  try {
    console.log(`\n=== Requesting ${domain} game ${gameId} details ===`);

    const { timestamp, sign } = generateSignature(domain, gameId);

    const response = await httpsRequest(
      `${config.baseUrl}/openapi/v2/channel/${domain}/game/${gameId}/detail`,
      {
        sign: sign,
        ts: timestamp.toString(),
        'x-md-global-cid': config.clientId,
      },
    );

    console.log('Response status:', response.status);
    console.log('Response data:', JSON.stringify(response.data, null, 2));
  } catch (error) {
    console.error('Request failed:', error.message);
  }
}

async function main() {
  console.log('Starting API requests...\n');

  await getGameInfo(
    'minigame.com', // 请替换为实际渠道域名
    'crazy-ball', // 请替换为实际游戏ID(app_id)
  );
}

main().catch(console.error);

------------------------------------------------------ END ---------------------------------------------------------------------------

商务对接
商务对接
公众号
公众号