天音实名认证 API 文档
天音实名认证平台提供标准 RESTful API,支持二要素身份校验和人脸核身两大核心能力。本文档将帮助您快速完成 API 对接。
接口概述
基础信息
| 项目 | 说明 |
| 接口地址 | https://您的域名/?route=api/{action} |
| 请求方式 | POST |
| 数据格式 | 请求:application/x-www-form-urlencoded,响应:application/json |
| 字符编码 | UTF-8 |
| 签名算法 | MD5 |
| 时间窗口 | 请求 timestamp 与服务器时间差不超过 ±300 秒 |
通用请求参数
所有接口(除特殊说明外)均需携带以下公共参数:
| 参数名 | 类型 | 必填 | 说明 |
appid | string | 是 | 应用 AppID,在控制台创建应用后获取 |
timestamp | string | 是 | Unix 时间戳(秒级),与服务器时间差不超过 300 秒 |
nonce | string | 是 | 随机字符串,建议 16-32 位,防重放攻击 |
sign | string | 是 | 请求签名,算法见下方签名认证章节 |
通用响应格式
{
"code": 0, // 0 表示成功,非 0 为错误码
"msg": "success", // 结果描述
"data": { ... } // 业务数据,失败时可能为 null
}
签名认证
所有 API 请求必须携带签名参数,用于验证请求合法性。签名算法如下:
签名生成规则
签名公式:sign = md5(appid + timestamp + nonce + appsecret)
将 appid、timestamp、nonce、appsecret 四个字符串按顺序直接拼接,然后取 MD5 哈希值(32 位小写)。
签名步骤
- 获取当前 Unix 时间戳(秒级)作为
timestamp
- 生成一个随机字符串作为
nonce(建议 16-32 位字母数字)
- 将
appid + timestamp + nonce + appsecret 拼接成一个字符串
- 对拼接后的字符串计算 MD5,得到 32 位小写哈希值即为
sign
示例
// 假设参数如下:
appid = "a1b2c3d4e5f6g7h8"
timestamp = "1700000000"
nonce = "abcdef1234567890"
appsecret = "s3cr3tk3y1234567890abcdef12345678"
// 拼接字符串:
raw = "a1b2c3d4e5f6g7h81700000000abcdef1234567890s3cr3tk3y1234567890abcdef12345678"
// 计算 MD5:
sign = md5(raw) = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
注意:AppSecret 仅用于服务端签名计算,严禁在客户端(浏览器、APP)中暴露。所有 API 调用必须从您的服务端发起。
错误码
| 错误码 | 含义 | 说明 |
| 0 | 成功 | 请求处理成功 |
| 1001 | 签名校验失败 | sign 参数错误或 timestamp 超出有效窗口 |
| 1002 | IP 受限 | 请求 IP 不在应用白名单中 |
| 1003 | 请求频率限制 | 请求过于频繁,请稍后重试 |
| 1004 | 应用无效 | AppID 不存在、应用已停用或账号被封禁 |
| 2001 | 余额不足 | 账户余额不足且无可用流量包 |
| 2002 | 参数错误 | 缺少必要参数或参数格式不正确 |
| 3001 | 上游接口异常 | 认证服务商接口调用失败 |
| 3002 | 认证失败 | 身份信息不匹配或人脸核身未通过 |
| 3003 | 等待中 | 用户尚未完成人脸核身操作 |
二要素身份校验
通过姓名和身份证号实时比对权威数据库,验证身份信息是否一致。
POST
/?route=api/check2factor
请求参数
| 参数名 | 类型 | 必填 | 说明 |
appid | string | 是 | 应用 AppID |
timestamp | string | 是 | Unix 时间戳(秒) |
nonce | string | 是 | 随机字符串 |
sign | string | 是 | 请求签名 |
real_name | string | 是 | 真实姓名 |
id_card | string | 是 | 身份证号码(18位) |
out_order_no | string | 是 | 外部订单号,由调用方生成,用于对账 |
成功响应
{
"code": 0,
"msg": "认证成功",
"data": {
"score": 100
}
}
失败响应
{
"code": 3002,
"msg": "认证失败",
"data": null
}
计费说明:调用成功(code=0)时扣费,调用失败不扣费。优先消耗流量包额度,流量包用完后从余额扣除。
人脸核身 - 创建会话
创建一个人脸核身会话,获取人脸采集页面 URL。将该 URL 提供给终端用户在浏览器中打开,完成活体检测和人脸比对。
POST
/?route=api/face_init
请求参数
| 参数名 | 类型 | 必填 | 说明 |
appid | string | 是 | 应用 AppID |
timestamp | string | 是 | Unix 时间戳(秒) |
nonce | string | 是 | 随机字符串 |
sign | string | 是 | 请求签名 |
real_name | string | 是 | 真实姓名 |
id_card | string | 是 | 身份证号码(18位) |
out_order_no | string | 是 | 外部订单号 |
成功响应
{
"code": 0,
"msg": "人脸核身会话创建成功",
"data": {
"verify_url": "https://您的域名/?route=api/face_capture&token=xxx",
"verify_id": "xxxxxxxxxxxxxxxx",
"log_id": 123
}
}
对接流程
- 服务端调用本接口,获取
verify_url、verify_id 和 log_id
- 将
verify_url 返回给前端,引导用户在浏览器中打开(支持 H5 / 微信内置浏览器)
- 用户完成人脸采集后,服务端轮询「查询结果」接口获取核身结果
人脸核身会话有效期为 5 分钟,超时未完成将自动退款。请在创建会话后及时引导用户完成操作。
人脸核身 - 查询结果
查询人脸核身会话的处理结果。建议在创建会话后,以 2-3 秒间隔轮询本接口。
POST
/?route=api/face_result
请求参数
| 参数名 | 类型 | 必填 | 说明 |
appid | string | 是 | 应用 AppID |
timestamp | string | 是 | Unix 时间戳(秒) |
nonce | string | 是 | 随机字符串 |
sign | string | 是 | 请求签名 |
verify_id | string | 是 | 人脸核身会话 ID(face_init 返回的 verify_id) |
log_id | int | 是 | 认证记录 ID(face_init 返回的 log_id) |
响应状态
| code | 含义 | 说明 |
0 | 核身通过 | 人脸比对成功,data 中包含 score |
3002 | 核身未通过 | 人脸比对失败 |
3003 | 等待中 | 用户尚未完成人脸采集,请继续轮询 |
成功响应
{
"code": 0,
"msg": "人脸核身通过",
"data": {
"score": 80
}
}
查询认证模式
查询当前应用配置的认证模式,用于前端动态展示对应的认证流程。
POST
/?route=api/verify_mode
请求参数
仅需公共参数(appid, timestamp, nonce, sign),无额外业务参数。
成功响应
{
"code": 0,
"msg": "ok",
"data": {
"mode": "2factor" // 可选值: 2factor | face | both
}
}
| mode 值 | 说明 |
2factor | 仅二要素校验 |
face | 仅人脸核身 |
both | 二要素 + 人脸核身 |
签名示例代码
PHP
<?php
function callApi(string $url, string $appId, string $appSecret, array $params): array
{
$timestamp = (string)time();
$nonce = bin2hex(random_bytes(16));
$sign = md5($appId . $timestamp . $nonce . $appSecret);
$postData = array_merge($params, [
'appid' => $appId,
'timestamp' => $timestamp,
'nonce' => $nonce,
'sign' => $sign,
]);
$ch = curl_init($url);
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => http_build_query($postData),
CURLOPT_RETURNTRANSFER => true,
CURLOPT_TIMEOUT => 10,
CURLOPT_SSL_VERIFYPEER => true,
]);
$response = curl_exec($ch);
curl_close($ch);
return json_decode($response, true) ?: [];
}
// 调用二要素校验
$result = callApi(
'https://您的域名/?route=api/check2factor',
'您的AppID',
'您的AppSecret',
[
'real_name' => '张三',
'id_card' => '110101199001011234',
'out_order_no' => 'ORDER_' . time(),
]
);
print_r($result);
Java
import java.net.http.*;
import java.security.MessageDigest;
import java.util.UUID;
public class TianyinClient {
private final String appId;
private final String appSecret;
private final String baseUrl;
public TianyinClient(String baseUrl, String appId, String appSecret) {
this.baseUrl = baseUrl;
this.appId = appId;
this.appSecret = appSecret;
}
public String check2Factor(String realName, String idCard, String outOrderNo) throws Exception {
String timestamp = String.valueOf(System.currentTimeMillis() / 1000);
String nonce = UUID.randomUUID().toString().replace("-", "");
String sign = md5(appId + timestamp + nonce + appSecret);
String body = "appid=" + appId
+ "×tamp=" + timestamp
+ "&nonce=" + nonce
+ "&sign=" + sign
+ "&real_name=" + URLEncoder.encode(realName, "UTF-8")
+ "&id_card=" + idCard
+ "&out_order_no=" + outOrderNo;
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create(baseUrl + "/?route=api/check2factor"))
.header("Content-Type", "application/x-www-form-urlencoded")
.POST(HttpRequest.BodyPublishers.ofString(body))
.build();
HttpResponse<String> response = HttpClient.newHttpClient()
.send(request, HttpResponse.BodyHandlers.ofString());
return response.body();
}
private String md5(String input) throws Exception {
MessageDigest md = MessageDigest.getInstance("MD5");
byte[] digest = md.digest(input.getBytes("UTF-8"));
StringBuilder sb = new StringBuilder();
for (byte b : digest) sb.append(String.format("%02x", b));
return sb.toString();
}
}
Python
import hashlib, time, secrets, requests
class TianyinClient:
def __init__(self, base_url, app_id, app_secret):
self.base_url = base_url
self.app_id = app_id
self.app_secret = app_secret
def _sign(self, timestamp, nonce):
raw = self.app_id + timestamp + nonce + self.app_secret
return hashlib.md5(raw.encode()).hexdigest()
def check_2factor(self, real_name, id_card, out_order_no):
timestamp = str(int(time.time()))
nonce = secrets.token_hex(16)
sign = self._sign(timestamp, nonce)
resp = requests.post(
f"{self.base_url}/?route=api/check2factor",
data={
"appid": self.app_id,
"timestamp": timestamp,
"nonce": nonce,
"sign": sign,
"real_name": real_name,
"id_card": id_card,
"out_order_no": out_order_no,
},
timeout=10
)
return resp.json()
# 使用示例
client = TianyinClient("https://您的域名", "您的AppID", "您的AppSecret")
result = client.check_2factor("张三", "110101199001011234", f"ORDER_{int(time.time())}")
print(result)
更新日志
| 日期 | 版本 | 更新内容 |
| 2026-02-23 | v1.0 | 发布二要素身份校验、人脸核身、查询认证模式接口 |