中国立直麻将天梯榜 API

面向门店/雀庄的免费开放接口,用于雀士注册、对局录入与赛季排名查询。

概述

Base URL 1https://www.mj-rate.com

Base URL 2https://www.mj-rate.cn

所有接口均需在请求体中携带 rate_idsequenceparamstimestampsign,并通过签名校验。

参数类型必填说明
rate_idstring平台提供一个门店唯一的ID信息点击查看申请
sequencestring携带请求的序号,用于防重放,8位长度字符串(仅限数字或字母组合,不区分大小写)
paramsarray/object携带的包体信息
timestampint秒级时间戳
signstring32位大写字符串签名信息
请求头HEADER中需包含 Content-Type: application/json

认证与签名

平台为每个门店分配 rate_id(8 位非零数字串)与 secret点击查看申请

rate_id(8 位非零数字串)与 secret请向平台申请

secret 仅用于服务端签名计算,严禁下发前端或提交到公开仓库。

签名步骤:

  1. 将请求体除 sign 外的字段按键名升序排序;
  2. key=value&key=value&... 拼接,末尾追加 &secret=你的secret
  3. 对拼接串做 MD5 并转为大写,得到 sign
params 为对象时:需先对 params 递归按 key 升序排序,再 JSON.stringify(不转义 unicode、不转义 /)后参与拼接,否则验签失败。

timestamp 为秒级时间戳,与服务器时间差不得超过 ±300 秒。sequence 用于防重放,同一 rate_id + sequence 在 10 分钟内不可重复使用。

请求与响应

请求体统一结构:

{
  "rate_id": 12345678,
  "sequence": ABCD4321,
  "params": { 具体业务参数信息 },
  "timestamp": 1710000000,
  "sign": "XXXXXXXXXXXXXXXX"
}

响应统一结构:

{
  "code": 200,
  "msg": "success",
  "data": { "status": 1, "msg": "操作成功", ... }
}

code 为 200 表示请求被接受;data.status === 1 表示业务成功,0 表示业务失败。

错误码

code说明
200成功
400参数错误
405请求方法错误(需为 POST)
40001签名错误
40002时间戳过期
40003格式错误(缺参、类型错误等)
40004非授权IP地址(非授权IP请求超过10次,将封禁该IP地址24小时)
403禁止访问(如重复请求)
404资源不存在

雀士注册

POST /api/user/regedit

雀士信息注册。

params 参数

参数类型必填说明
usernamestring雀士昵称(中文昵称请使用URL编码,并删除所有空格,最大20个中文汉字,最小2个字符,不可以数字开头或纯数字)
phonestring手机号,国内 11 位 1 开头
avatarstring雀士头像 base64格式,解码后 ≤200KB
genderint雀士性别 0 女 / 1 男 / 2 未知(默认 2)
wechatstring微信号,后期绑定微信机器人使用
qqintQQ号码,一个QQ号码只能绑定一个雀士
emailstring雀士邮箱,后期可能发送广告信息等
birthdaystring雀士生日,格式为 YYYY-MM-DD,如果雀士已有生日,则不能修改

请求示例

{
  "rate_id": 12345678,
  "sequence": 87654321,
  "params": {
    "username": "Tom%20Cat",
    "phone": "13800138000",
    "avatar": "data:image/png;base64,iVBORw0KGgo...",
    "gender": 1
  },
  "timestamp": 1710000000,
  "sign": "YOUR_SIGN"
}

成功响应

{
  "code": 200,
  "msg": "success",
  "data": { "uuid": "01234567", "msg": "注册成功", "status": 1 }
}

雀士编辑

POST /api/user/edit

门店端修改雀士资料(仅限本门店或在本店有记录的雀士)。

params 参数

参数类型必填说明
phonestring雀士手机号
usernamestring雀士昵称,最大20个中文汉字,不可以数字开头,删除所有空格,中文昵称请使用URL编码
avatarstring雀士头像 base64格式,解码后 ≤200KB
genderint雀士性别,0 女 / 1 男 / 2 未知(默认 2),如果雀士已有性别,则不能修改
wechatstring微信号,后期绑定微信机器人使用
qqintQQ号码,一个QQ号码只能绑定一个雀士
emailstring雀士邮箱,后期可能发送广告信息等
birthdaystring雀士生日,格式为 YYYY-MM-DD,如果雀士已有生日,则不能修改

成功响应

{
  "code": 200,
  "msg": "success",
  "data": { "msg": "修改成功", "status": 1 }
}

查询雀士分数与排名

POST /api/user/getUserRateInfo

根据雀士手机号查询雀士的全局数据、各赛季数据,以及各赛季下的门店数据(区分四麻/三麻),并返回赛季全局排名与门店排名。

params 参数

参数类型必填说明
phonestring雀士手机号

请求示例

{
  "rate_id": 12345678,
  "sequence": 87654324,
  "params": {
    "phone": "13800138000"
  },
  "timestamp": 1710000000,
  "sign": "YOUR_SIGN"
}

成功响应

{
  "code": 200,
  "msg": "success",
  "data": {
    "status": 1,
    "msg": "处理成功",
    "userInfo": {
      "uuid": "01234567",
      "username": "Tom%20Cat",
      "avatarUrl": "https://xxx/xxx.png"
    },
    "recordData": {
      "global": {
        "four": {
          "total": 0,
          "one_total": 0, "two_total": 0, "three_total": 0, "four_total": 0, "fly_total": 0,
          "one_score": 0, "two_score": 0, "three_score": 0, "four_score": 0, "fly_score": 0
        },
        "three": {
          "total": 0,
          "one_total": 0, "two_total": 0, "three_total": 0, "four_total": 0, "fly_total": 0,
          "one_score": 0, "two_score": 0, "three_score": 0, "four_score": 0, "fly_score": 0
        }
      },
      "seasons": [
        {
          "seasonId": 1,
          "seasonName": "S1",
          "four": {
            "total": 0,
            "one_total": 0, "two_total": 0, "three_total": 0, "four_total": 0, "fly_total": 0,
            "one_score": 0, "two_score": 0, "three_score": 0, "four_score": 0, "fly_score": 0,
            "seasonRank": {
              "id": 1,
              "seasonTotal": 0,
              "bestStartIndex": 0,
              "bestWin": 0,
              "bestSum": 0,
              "bestValid": 1,
              "bestGroup": [],
              "rank": 0
            }
          },
          "three": {
            "total": 0,
            "one_total": 0, "two_total": 0, "three_total": 0, "four_total": 0, "fly_total": 0,
            "one_score": 0, "two_score": 0, "three_score": 0, "four_score": 0, "fly_score": 0,
            "seasonRank": {
              "id": 2,
              "seasonTotal": 0,
              "bestStartIndex": 0,
              "bestWin": 0,
              "bestSum": 0,
              "bestValid": 1,
              "bestGroup": [],
              "rank": 0
            }
          },
          "stores": [
            {
              "storeId": 100,
              "storeName": "某某雀庄",
              "four": {
                "total": 0,
                "one_total": 0, "two_total": 0, "three_total": 0, "four_total": 0, "fly_total": 0,
                "one_score": 0, "two_score": 0, "three_score": 0, "four_score": 0, "fly_score": 0,
                "seasonRank": {
                  "id": 3,
                  "seasonTotal": 0,
                  "bestStartIndex": 0,
                  "bestWin": 0,
                  "bestSum": 0,
                  "bestValid": 1,
                  "bestGroup": [],
                  "rank": 0
                }
              },
              "three": {
                "total": 0,
                "one_total": 0, "two_total": 0, "three_total": 0, "four_total": 0, "fly_total": 0,
                "one_score": 0, "two_score": 0, "three_score": 0, "four_score": 0, "fly_score": 0,
                "seasonRank": {
                  "id": 4,
                  "seasonTotal": 0,
                  "bestStartIndex": 0,
                  "bestWin": 0,
                  "bestSum": 0,
                  "bestValid": 1,
                  "bestGroup": [],
                  "rank": 0
                }
              }
            }
          ]
        }
      ]
    }
  }
}

新增对局记录

POST /api/record/add

新增一局天梯榜记录对局。name1~name4 为手机号,四个手机号均须已注册。

params 参数

参数类型必填说明
typeint对局类型,0为四人麻将,1为三人麻将
name1string第一位雀士手机号
score1float第一位雀士分数
name2string第二位雀士手机号
score2float第二位雀士分数
name3string第三位雀士手机号
score3float第三位雀士分数
name4string第四位雀士手机号,三人麻将时为0
score4float第四位雀士分数,三人麻将时为0
record_timestring对局时间 Y-m-d H:i:s,不传则当前时间
总分 1000 时,服务端会取整后 ×100 转为万点制。

您无需负责对对局分数的顺位排序,平台会根据分数一一匹配自行排序处理。

同分可用小数区分顺位(如 35000.2、35000.1),请注意使用降序,即0.2顺位在0.1前面。

请求示例

{
  "rate_id": 12345678,
  "sequence": 87654323,
  "params": {
    "type": 0,
    "name1": "13800138000", "score1": 35000,
    "name2": "13900139000", "score2": 25000,
    "name3": "13700137000", "score3": 22000,
    "name4": "13600136000", "score4": 18000,
    "record_time": "2026-03-11 20:00:00"
  },
  "timestamp": 1710000000,
  "sign": "YOUR_SIGN"
}

成功响应

{
  "code": 200,
  "msg": "success",
  "data": {
            "msg": "添加成功",
            "status": 1,
            "record_id": 64位字符串,请自行保存记录,后续可用此 ID 修改和删除记录
            "errCode": 200, // 错误码,见下表
           }
}

修改对局记录

POST /api/record/update

修改已有对局,仅限本门店记录。

params 参数

参数类型必填说明
record_idstring对局记录 ID,64位字符串
name1string第一位雀士手机号
score1float第一位雀士分数
name2string第二位雀士手机号
score2float第二位雀士分数
name3string第三位雀士手机号
score3float第三位雀士分数
name4string第四位雀士手机号,三人麻将时为0
score4float第四位雀士分数,三人麻将时为0

成功响应

{
  "code": 200,
  "msg": "success",
  "data": { "msg": "对局记录修改成功", "status": 1, "errCode": 200}
}

删除对局记录

POST /api/record/delete

删除对局,仅限本门店记录。params 只需 record_id

成功响应

{
  "code": 200,
  "msg": "success",
  "data": { "msg": "对局记录删除成功", "status": 1 }
}

查询对局记录

GET /api/record/getRecordList

查询门店的对局记录信息,默认查询门店全部对局记录。支持筛选功能,包括对局类型、雀士手机号、对局时间等。

params 参数

参数类型必填说明
typeint对局类型,0为四人麻将,1为三人麻将
phonestring雀士手机号
start_timeint/string查询的开始时间,支持时间戳和 Y-m-d H:i:s等多种格式
end_timeint/string查询的结束时间,支持时间戳和 Y-m-d H:i:s等多种格式
pageint查询页码,默认为1
page_sizeint每页数量,默认100,最大1000
如果设置时间筛选,结束时间必须大于开始时间,且单次时间跨度周期不可以超过3个月。

请求示例

{
  "rate_id": 12345678,
  "sequence": 87654323,
  "params": {
    "type": 0,
    "phone": "13800138000",
    "start_time": 1710000000,
    "end_time": "2026-03-21 20:00:00",
    "page": 1,
    "page_size": 100
  },
  "timestamp": 1710000000,
  "sign": "YOUR_SIGN"
}

成功响应

{
  "code": 200,
  "msg": "success",
  "data": {
            "msg": "查询成功",
            "status": 1,
            "total": 888, // 符合查询条件的总记录数据
            "recordList": {
                    {
                      "id": 64位字符串,
                      "storeName": 雀庄名称, "typeName": "天梯四麻" 或 "天梯三麻",
                      "name1": 一位雀士昵称, "score1": 一位雀士分数,
                      "name2": 二位雀士昵称, "score2": 二位雀士分数,
                      "name3": 三位雀士昵称, "score3": 三位雀士分数,
                      "name4": 四位雀士昵称 三麻时为空, "score4": 四位雀士分数 三麻时为空,
                      "recordTime": 对局时间, "createTime": 记录时间
                    },
                    {
                      "id": 64位字符串,
                      "storeName": 雀庄名称, "typeName": "天梯四麻" 或 "天梯三麻",
                      "name1": 一位雀士昵称, "score1": 一位雀士分数,
                      "name2": 二位雀士昵称, "score2": 二位雀士分数,
                      "name3": 三位雀士昵称, "score3": 三位雀士分数,
                      "name4": 四位雀士昵称 三麻时为空, "score4": 四位雀士分数 三麻时为空,
                      "recordTime": 对局时间, "createTime": 记录时间
                    },...
                },
           }
}

记录操作执行错误码

errCode说明
200成功
400name1~name4 或 score1~score4 数据有缺失
402name1~name4 中有重复雀士信息
305该对局信息在最近30天内完全重叠
403对局总分之和不为 1000 或 100000 (整数部分)
501、502、503、504name1~name4 中有未在平台注册雀士
601、602、603、604name1~name4 有雀士分数异常状态
701、702、703、704name1~name4 有雀士处于记录冷却期(5分钟)
801、802、803、804name1~name4 有雀士处于平台封禁期
500+错误时,服务器端将错误直接抛出,所以并不会检查是否会有多个雀士错误的情况。

查询用户赛季排名(该部分功能尚未完善)

GET /api/rank/getUserRank

按手机号查询雀士的赛季数据。

params 参数

参数类型必填说明
phonestring手机号
datestringYYYY-MM-DD,传则查该日所在赛季;不传返回所有赛季

成功响应示例(单赛季)

{
  "code": 200,
  "msg": "success",
  "data": {
    "username": "张三",
    "avatarUrl": "",
    "status": 1,
    "seasonId": 1,
    "seasonName": "S1",
    "seasonTotal": 12,
    "bestStartIndex": 2,
    "bestSum": 45.5,
    "bestGroup": [{ "score": 35000, "rank": 0, "point": 40.0 }],
    "createTime": "2026-03-01 00:00:00",
    "msg": "查询成功",
    "status": 1
  }
}

门店接口

提示:门店注册接口并不对外开放。
POST /api/store/regedit

用于注册雀庄门店基础信息,并返回该门店的 API 凭证(rate_id / secret)。

params 参数

参数类型必填说明
licenseTypeint企业性质(0=个体户,1=企业)
companyNamestring企业名称
companyCodestring统一社会信用代码
idCardNamestring法人姓名
companyPhonestring法人联系方式(数字,最多 11 位)
namestring门店名称(需唯一)
phonestring门店联系方式(数字,最多 11 位)
ipstring门店对接服务器 IP(用于生成/绑定白名单)
logostring门店 LOGO(Base64 图片)

请求示例

{
  "rate_id": "12345678",
  "sequence": "87654321",
  "params": {
    "licenseType": 1,
    "companyName": "示例科技有限公司",
    "companyCode": "91310000XXXXXXXXXX",
    "idCardName": "张三",
    "companyPhone": "13800138000",
    "name": "示例雀庄(上海店)",
    "phone": "02112345678",
    "ip": "1.2.3.4",
    "logo": "data:image/png;base64,...."
  },
  "timestamp": 1700000000,
  "sign": "YOUR_SIGN"
}

成功响应

{
  "code": 200,
  "msg": "success",
  "data": {
    "rate_id": "23456789",
    "secret": "YOUR_RATE_SECRET",
    "msg": "注册成功",
    "status": 1
  }
}
POST /api/store/edit

用于编辑门店信息(门店必须存在且为启用状态)。可按需提交要修改的字段。

params 参数

参数类型必填说明
namestring门店名称(需唯一)
phonestring门店联系方式
logostring门店 LOGO(Base64 图片)
cityCodestring城市代码
addressstring详细地址
longitudestring经度
latitudestring纬度

请求示例

{
  "rate_id": "23456789",
  "sequence": "11223344",
  "params": {
    "name": "示例雀庄(上海店)- 新名称",
    "phone": "02100001111",
    "address": "上海市浦东新区XX路XX号",
    "longitude": "121.500000",
    "latitude": "31.200000"
  },
  "timestamp": 1700000000,
  "sign": "YOUR_SIGN"
}

成功响应

{
  "code": 200,
  "msg": "success",
  "data": {
    "msg": "编辑成功",
    "status": 1
  }
}

签名伪代码

客户端需与服务端保持一致的 params 序列化与字段排序。

input = { rate_id, sequence, params(object), timestamp }
secret = 门店 secret

params_str = JSON.stringify(sortKeysRecursively(params))

data = {
  "params": params_str,
  "rate_id": rate_id,
  "sequence": sequence,
  "timestamp": timestamp
}
sort keys of data asc
signStr = join(key=value, "&") + "&secret=" + secret
sign = upper(MD5(signStr))

PHP 调用示例(cURL)

安全提醒:secret 仅用于服务端签名计算,严禁下发前端或提交到公开仓库。
<?php

class MjRateApiClient
{
    const RATE_ID = 'YOUR_RATE_ID';
    const RATE_SECRET = 'YOUR_RATE_SECRET';
    const RATE_URL = 'https://www.mj-rate.com/api/';

    public static function send(string $path, array $bizParams): array
    {
        $body = [
            'rate_id' => self::RATE_ID,
            'sequence' => (string)random_int(10000000, 99999999),
            'params' => $bizParams,
            'timestamp' => time(),
        ];
        $body['sign'] = self::buildSign($body);

        $ch = curl_init();
        curl_setopt($ch, CURLOPT_URL, self::RATE_URL . $path);
        curl_setopt($ch, CURLOPT_TIMEOUT, 30);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
        curl_setopt($ch, CURLOPT_POST, 1);
        curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($body, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES));
        curl_setopt($ch, CURLOPT_HTTPHEADER, ['Content-Type: application/json; charset=utf-8']);
        $res = curl_exec($ch);
        curl_close($ch);

        $json = json_decode((string)$res, true);
        return is_array($json) ? $json : ['code' => 0, 'message' => '解析失败', 'data' => $res];
    }

    private static function buildSign(array $body): string
    {
        $sign = strtoupper((string)($body['sign'] ?? ''));
        unset($body['sign']);

        $data = [];
        foreach ($body as $k => $v) {
            $data[$k] = is_array($v)
                ? json_encode(self::sortKeysRecursive($v), JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES)
                : (string)$v;
        }
        ksort($data, SORT_STRING);

        $parts = [];
        foreach ($data as $k => $v) {
            $parts[] = $k . '=' . $v;
        }
        $parts[] = 'secret=' . self::RATE_SECRET;
        return strtoupper(md5(implode('&', $parts)));
    }

    private static function sortKeysRecursive(array $arr): array
    {
        ksort($arr, SORT_STRING);
        foreach ($arr as $k => $v) {
            if (is_array($v)) $arr[$k] = self::sortKeysRecursive($v);
        }
        return $arr;
    }
}

// 示例:注册雀士
$resp = MjRateApiClient::send('user/regedit', [
    'username' => rawurlencode('Tom Cat'),
    'phone' => '13800138000',
    'gender' => 1,
    // 'avatar' => 'data:image/png;base64,...' // ≤200KB(解码后)
]);
var_dump($resp);

Java 调用示例(OkHttp)

安全提醒:secret 仅用于服务端签名计算,严禁下发前端或提交到公开仓库。
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import okhttp3.*;

import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.util.*;

public class MjRateApiClient {
    static final String RATE_ID = "YOUR_RATE_ID";
    static final String RATE_SECRET = "YOUR_RATE_SECRET";
    static final String RATE_URL = "https://www.mj-rate.com/api/";

    static final ObjectMapper MAPPER = new ObjectMapper();
    static final OkHttpClient HTTP = new OkHttpClient.Builder()
            .callTimeout(java.time.Duration.ofSeconds(30))
            .build();

    public static Map<String, Object> send(String path, Map<String, Object> bizParams) throws Exception {
        Map<String, Object> body = new LinkedHashMap<>();
        body.put("rate_id", RATE_ID);
        body.put("sequence", String.valueOf(10000000 + new Random().nextInt(90000000)));
        body.put("params", bizParams);
        body.put("timestamp", System.currentTimeMillis() / 1000);
        body.put("sign", buildSign(body));

        String json = MAPPER.writeValueAsString(body);
        Request req = new Request.Builder()
                .url(RATE_URL + path)
                .post(RequestBody.create(json, MediaType.parse("application/json; charset=utf-8")))
                .build();
        try (Response resp = HTTP.newCall(req).execute()) {
            String res = resp.body() != null ? resp.body().string() : "";
            return MAPPER.readValue(res, new TypeReference<Map<String, Object>>() {});
        }
    }

    private static String buildSign(Map<String, Object> body) throws Exception {
        Map<String, String> data = new TreeMap<>();
        for (Map.Entry<String, Object> e : body.entrySet()) {
            if ("sign".equals(e.getKey())) continue;
            Object v = e.getValue();
            if (v instanceof Map || v instanceof List) {
                data.put(e.getKey(), MAPPER.writeValueAsString(sortKeysRecursive(v)));
            } else {
                data.put(e.getKey(), String.valueOf(v));
            }
        }
        StringBuilder sb = new StringBuilder();
        for (Map.Entry<String, String> e : data.entrySet()) {
            if (sb.length() > 0) sb.append('&');
            sb.append(e.getKey()).append('=').append(e.getValue());
        }
        sb.append("&secret=").append(RATE_SECRET);
        return md5Upper(sb.toString());
    }

    private static Object sortKeysRecursive(Object obj) {
        if (obj instanceof Map) {
            Map<String, Object> m = new TreeMap<>();
            ((Map<?, ?>) obj).forEach((k, v) -> m.put(String.valueOf(k), sortKeysRecursive(v)));
            return m;
        }
        if (obj instanceof List) {
            List<Object> out = new ArrayList<>();
            for (Object v : (List<?>) obj) out.add(sortKeysRecursive(v));
            return out;
        }
        return obj;
    }

    private static String md5Upper(String s) throws Exception {
        MessageDigest md = MessageDigest.getInstance("MD5");
        byte[] dig = md.digest(s.getBytes(StandardCharsets.UTF_8));
        StringBuilder hex = new StringBuilder();
        for (byte b : dig) hex.append(String.format("%02x", b));
        return hex.toString().toUpperCase(Locale.ROOT);
    }

    public static void main(String[] args) throws Exception {
        Map<String, Object> params = new LinkedHashMap<>();
        params.put("username", java.net.URLEncoder.encode("Tom Cat", "UTF-8"));
        params.put("phone", "13800138000");
        params.put("gender", 1);
        Map<String, Object> resp = send("user/regedit", params);
        System.out.println(resp);
    }
}

Node.js 调用示例(fetch)

安全提醒:secret 仅用于服务端签名计算,严禁下发前端或提交到公开仓库。
import crypto from 'node:crypto';

const RATE_ID = 'YOUR_RATE_ID';
const RATE_SECRET = 'YOUR_RATE_SECRET';
const RATE_URL = 'https://www.mj-rate.com/api/';

function sortKeysRecursive(obj) {
  if (Array.isArray(obj)) return obj.map(sortKeysRecursive);
  if (obj && typeof obj === 'object') {
    const out = {};
    Object.keys(obj).sort().forEach((k) => { out[k] = sortKeysRecursive(obj[k]); });
    return out;
  }
  return obj;
}

function buildSign(body) {
  const copy = { ...body };
  delete copy.sign;
  const data = {};
  Object.keys(copy).forEach((k) => {
    const v = copy[k];
    data[k] = (v && typeof v === 'object') ? JSON.stringify(sortKeysRecursive(v)) : String(v);
  });
  const keys = Object.keys(data).sort();
  const parts = keys.map((k) => `${k}=${data[k]}`);
  parts.push(`secret=${RATE_SECRET}`);
  return crypto.createHash('md5').update(parts.join('&'), 'utf8').digest('hex').toUpperCase();
}

async function send(path, bizParams) {
  const body = {
    rate_id: RATE_ID,
    sequence: String(Math.floor(10000000 + Math.random() * 90000000)),
    params: bizParams,
    timestamp: Math.floor(Date.now() / 1000),
  };
  body.sign = buildSign(body);

  const res = await fetch(RATE_URL + path, {
    method: 'POST',
    headers: { 'Content-Type': 'application/json; charset=utf-8' },
    body: JSON.stringify(body),
  });
  return await res.json();
}

// 示例:注册雀士
const resp = await send('user/regedit', {
  username: encodeURIComponent('Tom Cat'),
  phone: '13800138000',
  gender: 1,
  // avatar: 'data:image/png;base64,...'
});
console.log(resp);

Python 调用示例(requests)

安全提醒:secret 仅用于服务端签名计算,严禁下发前端或提交到公开仓库。
import hashlib
import json
import random
import time
import urllib.parse

import requests

RATE_ID = "YOUR_RATE_ID"
RATE_SECRET = "YOUR_RATE_SECRET"
RATE_URL = "https://www.mj-rate.com/api/"

def sort_keys_recursive(obj):
    if isinstance(obj, list):
        return [sort_keys_recursive(v) for v in obj]
    if isinstance(obj, dict):
        return {k: sort_keys_recursive(obj[k]) for k in sorted(obj.keys())}
    return obj

def build_sign(body: dict) -> str:
    data = {}
    for k, v in body.items():
        if k == "sign":
            continue
        if isinstance(v, (dict, list)):
            data[k] = json.dumps(sort_keys_recursive(v), ensure_ascii=False, separators=(",", ":"))
        else:
            data[k] = str(v)
    parts = [f"{k}={data[k]}" for k in sorted(data.keys())]
    parts.append(f"secret={RATE_SECRET}")
    raw = "&".join(parts)
    return hashlib.md5(raw.encode("utf-8")).hexdigest().upper()

def send(path: str, biz_params: dict) -> dict:
    body = {
        "rate_id": RATE_ID,
        "sequence": str(random.randint(10000000, 99999999)),
        "params": biz_params,
        "timestamp": int(time.time()),
    }
    body["sign"] = build_sign(body)
    resp = requests.post(RATE_URL + path, json=body, timeout=30)
    return resp.json()

# 示例:注册雀士
resp = send("user/regedit", {
    "username": urllib.parse.quote("Tom Cat"),
    "phone": "13800138000",
    "gender": 1,
    # "avatar": "data:image/png;base64,..."
})
print(resp)

技术支持与联系方式

如需对接支持、接口开通、问题排查,请通过以下方式联系我们:

邮箱

提交对接信息与错误截图

电话

适合紧急问题与现场对接

微信

点击微信号并添加

QQQ

点击直接发起会话