运营商三要素实名认证:注册实名校验集成方案
idcard-phone-auth-scenario-integration-1389 # 运营商三要素实名认证:注册实名校验集成方案
> **接口**:运营商三要素 - 运营商手机号实名认证 · **apiCode**:1389 · **接入点**:1389-1
> **请求方式**:POST · **返回格式**:JSON · **计费**:按次(ret_code=0 时扣费,具体档位以官方说明为准)
> **适用人群**:产品经理、全栈工程师
> **阅读时间**:10 分钟
> **最后实测核对**:2026-09-07
---
## 核心要点
- 最佳触发时机:用户提交注册表单时,后端在创建账户**之前**调用,而非之后。
- 失败时不要直接拒绝注册——给出明确错误提示,允许用户修正后重试(最多 3 次)。
- 归属地信息(省/市/运营商)可用于前端展示和风控评分,但不作为注册强约束条件。
---
## 场景:用户注册时的实名校验流程
### 典型数据流
```
用户填表(姓名+身份证+手机号)
│
▼
前端表单校验(格式检查)
│
▼
后端接收请求
│
▼
调用运营商三要素接口(apiCode=1389)
│
├── code=0 ──► 认证成功 ──► 创建账户 ──► 返回注册成功
│
├── code=1 ──► 三要素不匹配 ──► 返回"信息有误,请核对后重试"
│
├── code=2 ──► 手机号未实名 ──► 返回"该手机号未实名,请先绑定真实号码"
│
└── code=11/12/13 ──► 参数格式错误 ──► 返回对应格式提示
```
### 数据库设计建议
注册表新增以下字段,用于后续风控和审计:
| 字段名 | 类型 | 说明 |
|--------|------|------|
| `id_card` | VARCHAR(18) | 身份证号(加密存储) |
| `real_name` | VARCHAR(50) | 实名姓名 |
| `phone` | VARCHAR(11) | 手机号 |
| `auth_status` | TINYINT | 0=未认证 1=认证成功 2=认证失败待复核 |
| `auth_code` | TINYINT | 最近一次认证返回的 code 值 |
| `auth_time` | DATETIME | 最近一次认证时间 |
| `belong_prov` | VARCHAR(20) | 归属省(可选,来自 belongArea.prov) |
| `belong_city` | VARCHAR(50) | 归属市(可选) |
| `belong_type` | TINYINT | 运营商类型(可选,来自 belongArea.type) |
> **安全提醒**:身份证号属于敏感个人信息,存储时必须加密(如 AES-256),传输时走 HTTPS,日志中不得明文打印。
---
## 后端集成代码示例(Python Flask)
```python
from flask import Flask, request, jsonify
import requests
import hashlib
from datetime import datetime
app = Flask(__name__)
APPKEY = "YOUR_APPKEY" # 从环境变量读取,不要硬编码
@app.route("/api/register", methods=["POST"])
def register():
data = request.json
# 1. 前端传来的参数
name = data.get("name", "").strip()
id_card = data.get("id_card", "").strip()
phone = data.get("phone", "").strip()
# 2. 基础格式校验(避免无意义的接口调用)
if not name or not id_card or not phone:
return jsonify({"error": "name、id_card、phone 均不能为空"}), 400
if len(id_card) != 18:
return jsonify({"error": "身份证号应为 18 位"}), 400
if not phone.isdigit() or len(phone) != 11:
return jsonify({"error": "手机号应为 11 位数字"}), 400
# 3. 调用运营商三要素接口
url = "https://route.showapi.com/1389-1"
params = {
"appKey": APPKEY,
"idCard": id_card,
"name": name,
"phone": phone,
"needBelongArea": "true"
}
try:
resp = requests.post(url, data=params, timeout=10)
resp.raise_for_status()
result = resp.json()
except requests.exceptions.Timeout:
return jsonify({"error": "实名校验超时,请稍后重试"}), 503
except Exception as e:
app.logger.error(f"实名校验调用异常: {e}")
return jsonify({"error": "实名校验服务异常"}), 503
# 4. 系统层检查
if result.get("showapi_res_code") != 0:
app.logger.error(f"系统层调用失败: {result.get('showapi_res_error')}")
return jsonify({"error": "实名校验服务异常,请稍后重试"}), 503
body = result.get("showapi_res_body", {})
code = body.get("code")
msg = body.get("msg", "")
# 5. 业务层处理
if code == 0:
# 认证成功
belong_area = body.get("belongArea", {})
# 存入数据库(加密身份证号)
encrypted_id = hashlib.sha256(id_card.encode()).hexdigest()
user = create_user(
name=name,
id_card_encrypted=encrypted_id,
phone=phone,
auth_status=1,
auth_code=0,
auth_time=datetime.now(),
belong_prov=belong_area.get("prov"),
belong_city=belong_area.get("city"),
belong_type=belong_area.get("type")
)
return jsonify({
"user_id": user.id,
"message": "注册成功",
"belong_area": belong_area
}), 201
elif code == 1:
return jsonify({"error": "姓名、身份证号或手机号与运营商记录不一致,请核对后重新输入"}), 400
elif code == 2:
return jsonify({"error": "该手机号尚未实名认证,请使用已实名的手机号注册"}), 400
elif code == 11:
return jsonify({"error": "请填写完整的姓名、身份证号和手机号"}), 400
elif code == 12:
return jsonify({"error": "身份证号格式不正确,请检查"}), 400
elif code == 13:
return jsonify({"error": "手机号格式不正确,请检查"}), 400
elif code in (21, 22):
app.logger.warning(f"渠道暂停服务: code={code}")
return jsonify({"error": "实名校验服务暂时不可用,请稍后重试"}), 503
else:
app.logger.error(f"未知认证结果: code={code}, msg={msg}")
return jsonify({"error": "实名校验结果未知,请联系客服"}), 503
def create_user(**kwargs):
"""业务层用户创建逻辑,实际项目中使用 ORM"""
pass
```
---
## 前端交互建议
### 成功场景
注册成功后,可以在用户中心展示归属地信息,增强信任感:
```
欢迎,张三!
您的号码归属地:山西省 临汾市(中国移动)
```
### 失败场景的提示文案
| code | 前端提示文案 |
|------|------------|
| 1 | 您输入的姓名、身份证号或手机号与运营商记录不一致,请核对后重新输入 |
| 2 | 该手机号尚未实名认证,请使用已实名的手机号注册 |
| 11 | 请填写完整的姓名、身份证号和手机号 |
| 12 | 身份证号格式不正确,示例:11010119900307421X |
| 13 | 手机号格式不正确,应为 11 位中国大陆手机号 |
| 21/22 | 实名校验服务暂时不可用,请稍后重试 |
---
## 时序图
```
用户 前端 后端 ShowAPI 接口
│ │ │ │
│ 填写注册信息 │ │ │
│────────────►│ │ │
│ │ 提交表单 │ │
│ │────────────►│ │
│ │ │ 格式校验 │
│ │ │───────────────│
│ │ │ 调用 1389 接口 │
│ │ │──────────────►│
│ │ │ │ 核验三要素
│ │ │◄──────────────│
│ │ │ code=0 认证成功 │
│ │ │ 创建用户账户 │
│ │◄────────────│ 返回 user_id │
│ │ 展示欢迎页 │ │
│◄────────────│ │ │
```
---
## 边界情况处理
### 携号转网用户
携号转网后,运营商数据库中的归属信息可能与新运营商不一致。接口返回的 `belongArea.type` 反映的是**当前运营商**,而非入网时的原始运营商。认证本身不受影响,但归属地展示可能与用户预期不符——这是数据源限制,不是接口 bug。
### 虚拟运营商号码
170/171 等虚拟运营商号段部分未完全接入实名数据库,可能出现 `code=2`(无记录)。遇到此类情况,引导用户联系运营商确认实名状态。
### 批量注册场景
如果业务涉及批量用户注册(如企业开户),建议结合缓存策略(见 → [缓存策略文章](https://www.showapi.com/guides/idcard-phone-auth-cache-cost-1389))避免重复调用。
---
## FAQ
**Q1:认证失败后用户可以立即重试吗?**
可以。code=1(不匹配)时用户修改信息后可以立即重试,无需等待。但建议前端限制 3 次尝试,超过后短暂锁定,防止恶意试探。
**Q2:认证成功后的数据需要保存到数据库吗?**
建议保存。至少记录 auth_status、auth_code、auth_time、belongArea 信息,便于后续风控审计和异常排查。
**Q3:接口返回的 belongArea 数据可以用于地理定位吗?**
`belongArea` 提供的是号码归属地(注册地),不是用户当前位置。不要将其用于实时定位场景。
**Q4:是否需要用户授权才能调用此接口?**
需要。身份证号和手机号属于敏感个人信息,应在注册协议中明确告知用户信息用途,并获取用户授权。
**Q5:code=21/22 期间用户能注册吗?**
建议临时关闭实名校验入口或展示"服务暂时不可用"提示,等渠道恢复后再开通。不要静默失败后继续注册流程。
---
## 下一步阅读
- [完整错误码对照表](https://www.showapi.com/guides/idcard-phone-auth-response-codes-1389) —— 各 code 值的详细排查
- [按次计费下的缓存策略](https://www.showapi.com/guides/idcard-phone-auth-cache-cost-1389) —— 批量场景如何省钱
- [三要素 vs 二要素 vs 四要素方案对比](https://www.showapi.com/guides/idcard-phone-auth-comparison-1389) —— 选型决策参考
---
**- 本系列共 6 篇**:查看[运营商三要素实名认证指南总目录](https://www.showapi.com/guides/idcard-phone-auth-guides-1389)