anyixing-verify 0.0.0-stage → 1.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,3 +1,86 @@
1
- # Temporary Holding Version
1
+ # anyixing-verify
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ 核验中国居民**身份证号、银行卡号、手机号**的实名信息 —— 判断姓名与证件/卡号/号码是否属于同一人。
4
+
5
+ 可以一键装成 AI 技能(Claude Code / Cursor / WorkBuddy),也能直接在命令行里用。
6
+
7
+ ## 装成 AI 技能
8
+
9
+ ```bash
10
+ npx -y anyixing-verify@latest install
11
+ ```
12
+
13
+ 装好后你的 AI 就懂怎么核验了,直接说「帮我核验这个身份证和姓名是不是同一个人」即可。
14
+
15
+ 装的时候顺便把密钥配好:
16
+
17
+ ```bash
18
+ npx -y anyixing-verify@latest install --key 你的Key
19
+ ```
20
+
21
+ ## 配置密钥
22
+
23
+ 到 <https://smrz.anyixing.com.cn/login.php> 手机号验证码登录(未注册自动开户),
24
+ 控制台「概览」页复制 API Key。**新用户赠送 2 元测试额度**,够跑十几次身份证核验。
25
+
26
+ ```bash
27
+ npx -y anyixing-verify@latest config --key 你的Key # 保存并免费校验
28
+ npx -y anyixing-verify@latest config --show # 查看(脱敏)
29
+ npx -y anyixing-verify@latest config --clear # 清除
30
+ ```
31
+
32
+ 也可以用环境变量:`ANYIXING_API_KEY=你的Key`。
33
+
34
+ ## 直接核验
35
+
36
+ ```bash
37
+ # 身份证二要素
38
+ npx -y anyixing-verify@latest idcard --name 张三 --idcard 330102199001010012
39
+
40
+ # 银行卡四要素(手机号是银行预留号,不是用户现在的号)
41
+ npx -y anyixing-verify@latest bank --name 张三 --idcard 330102199001010012 \
42
+ --bankcard 6222021234567894 --mobile 13800138000
43
+
44
+ # 手机号二要素
45
+ npx -y anyixing-verify@latest mobile --name 张三 --mobile 13800138000
46
+
47
+ # 手机号三要素
48
+ npx -y anyixing-verify@latest mobile --name 张三 --mobile 13800138000 --idcard 330102199001010012
49
+ ```
50
+
51
+ 输出:
52
+
53
+ ```
54
+ ✅ 核验一致
55
+ 信息属于同一人
56
+ 费用 ¥0.20 余额 ¥9985.12 耗时 295ms
57
+ 流水号 6aeaf4e60e6e5315
58
+ ```
59
+
60
+ 加 `--json` 输出接口原始报文。
61
+
62
+ ## 返回结果
63
+
64
+ | result | 含义 | 是否计费 |
65
+ |---|---|---|
66
+ | `MATCH` | 一致 | 计费 |
67
+ | `NO_MATCH` | 不一致(正常业务结论,不是故障) | 计费 |
68
+ | `NO_ID` | 证件号无记录 | 计费 |
69
+ | `NO_CARD` | 卡号无记录 | 计费 |
70
+ | `NO_NUMBER` | 号码无记录 | 计费 |
71
+
72
+ 参数错误、余额不足、频控、上游异常不扣费。
73
+
74
+ ## 计费
75
+
76
+ 按次扣费,标准档起步:身份证 ¥0.20、银行卡 ¥0.35、手机号 ¥0.25。
77
+ 充值越多单价越低,额度 2 年有效。详见 <https://smrz.anyixing.com.cn/price.php>。
78
+
79
+ ## 安全
80
+
81
+ 密钥是长期凭证,别写进代码仓库、前端代码或公开文档。
82
+ 脚本保存在 `~/.config/smrz/api_key`(Windows 为 `%APPDATA%\smrz\api_key`),权限 600。
83
+
84
+ ## 许可
85
+
86
+ MIT
package/bin/cli.js ADDED
@@ -0,0 +1,372 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * 安宜星实名认证 —— 命令行工具 / AI 技能安装器
4
+ *
5
+ * 用法:
6
+ * npx -y anyixing-verify@latest install # 装成 AI 技能
7
+ * npx -y anyixing-verify@latest install --key xxx # 装好并配好密钥
8
+ * npx -y anyixing-verify@latest config --key xxx # 只配密钥
9
+ * npx -y anyixing-verify@latest idcard --name 张三 --idcard 3301...
10
+ * npx -y anyixing-verify@latest bank --name 张三 --idcard 3301... --bankcard 6222... --mobile 138...
11
+ * npx -y anyixing-verify@latest mobile --name 张三 --mobile 138...
12
+ */
13
+
14
+ const fs = require('fs');
15
+ const path = require('path');
16
+ const os = require('os');
17
+ const https = require('https');
18
+
19
+ const SITE = 'https://smrz.anyixing.com.cn';
20
+ const NAME = 'anyixing-verify';
21
+ const PKG_ROOT = path.join(__dirname, '..');
22
+ const SKILL_SRC = path.join(PKG_ROOT, 'skill');
23
+
24
+ const C_RESET = '\x1b[0m';
25
+ const C_DIM = '\x1b[2m';
26
+ const C_GREEN = '\x1b[32m';
27
+ const C_RED = '\x1b[31m';
28
+ const C_YEL = '\x1b[33m';
29
+ const C_BLUE = '\x1b[34m';
30
+ const tty = process.stdout.isTTY;
31
+ const paint = (c, s) => (tty ? c + s + C_RESET : s);
32
+
33
+ const TARGETS = [
34
+ ['WorkBuddy', path.join(os.homedir(), '.workbuddy', 'skills')],
35
+ ['Claude Code', path.join(os.homedir(), '.claude', 'skills')],
36
+ ['Cursor', path.join(os.homedir(), '.cursor', 'skills')],
37
+ ];
38
+
39
+ const GUIDE_NO_KEY = `
40
+ ${paint(C_YEL, '⚠️ 还没有配置 API Key')}
41
+
42
+ 这个工具本身免费,调用核验接口按次计费,需要一个 API Key:
43
+ 1. 打开 ${SITE}/login.php ,手机号验证码登录(未注册会自动开户)
44
+ 2. 控制台「概览」页复制 API Key
45
+ 3. 新用户注册赠送 2 元测试额度,够跑十几次身份证核验
46
+
47
+ 配置方式:
48
+ npx -y ${NAME}@latest config --key 你的Key
49
+ 或设置环境变量 ANYIXING_API_KEY=你的Key
50
+
51
+ 接口与计费:${SITE}/price.php
52
+ `;
53
+
54
+ function keyFile() {
55
+ if (process.platform === 'win32' && process.env.APPDATA) {
56
+ return path.join(process.env.APPDATA, 'smrz', 'api_key');
57
+ }
58
+ return path.join(os.homedir(), '.config', 'smrz', 'api_key');
59
+ }
60
+
61
+ function loadKey(explicit) {
62
+ if (explicit && explicit.trim()) return explicit.trim();
63
+ const env = (process.env.ANYIXING_API_KEY || process.env.SMRZ_API_KEY || '').trim();
64
+ if (env) return env;
65
+ try {
66
+ const t = fs.readFileSync(keyFile(), 'utf8').trim();
67
+ if (t) return t;
68
+ } catch (e) { /* 没配过 */ }
69
+ return '';
70
+ }
71
+
72
+ function mask(k) {
73
+ if (!k || k.length <= 12) return '***';
74
+ return k.slice(0, 6) + '…' + k.slice(-4);
75
+ }
76
+
77
+ function api(endpoint, payload, key, timeout = 20000) {
78
+ return new Promise((resolve) => {
79
+ const body = JSON.stringify(payload || {});
80
+ const req = https.request(
81
+ {
82
+ hostname: 'smrz.anyixing.com.cn',
83
+ path: '/api/v1/' + endpoint,
84
+ method: 'POST',
85
+ timeout,
86
+ headers: {
87
+ 'Content-Type': 'application/json',
88
+ 'Content-Length': Buffer.byteLength(body),
89
+ 'X-Api-Key': key,
90
+ 'User-Agent': 'anyixing-verify-npm/1.0',
91
+ },
92
+ },
93
+ (res) => {
94
+ let buf = '';
95
+ res.setEncoding('utf8');
96
+ res.on('data', (d) => (buf += d));
97
+ res.on('end', () => {
98
+ try {
99
+ resolve(JSON.parse(buf));
100
+ } catch (e) {
101
+ resolve({ code: -1, msg: 'HTTP ' + res.statusCode + ': ' + buf.slice(0, 200) });
102
+ }
103
+ });
104
+ }
105
+ );
106
+ req.on('timeout', () => req.destroy(new Error('请求超时')));
107
+ req.on('error', (e) => resolve({ code: -1, msg: '网络错误:' + e.message }));
108
+ req.write(body);
109
+ req.end();
110
+ });
111
+ }
112
+
113
+ function copyDir(src, dest) {
114
+ fs.mkdirSync(dest, { recursive: true });
115
+ let count = 0;
116
+ for (const entry of fs.readdirSync(src, { withFileTypes: true })) {
117
+ const s = path.join(src, entry.name);
118
+ const d = path.join(dest, entry.name);
119
+ if (entry.isDirectory()) {
120
+ count += copyDir(s, d);
121
+ } else {
122
+ fs.copyFileSync(s, d);
123
+ count++;
124
+ }
125
+ }
126
+ return count;
127
+ }
128
+
129
+ const ERR = {
130
+ 1001: '参数错误(未计费)',
131
+ 1002: 'API Key 无效',
132
+ 1003: '余额不足',
133
+ 1004: '频率超限',
134
+ 1005: '接口不可用',
135
+ 1006: '触发防刷(未计费)',
136
+ 2001: '上游超时(未计费)',
137
+ 2002: '上游异常(未计费)',
138
+ 2003: '核验未完成(已计费)',
139
+ };
140
+
141
+ const RESULT = {
142
+ MATCH: ['✅ 核验一致', '信息属于同一人'],
143
+ NO_MATCH: ['❌ 核验不一致', '信息对不上,这是正常业务结论'],
144
+ NO_ID: ['⚠️ 查无此身份证', '证件号在数据源无记录'],
145
+ NO_CARD: ['⚠️ 查无此银行卡', '卡号在数据源无记录'],
146
+ NO_NUMBER: ['⚠️ 查无此手机号', '号码在数据源无记录'],
147
+ };
148
+
149
+ function parseArgs(argv) {
150
+ const out = { _: [] };
151
+ for (let i = 0; i < argv.length; i++) {
152
+ const a = argv[i];
153
+ if (a.startsWith('--')) {
154
+ const k = a.slice(2);
155
+ const next = argv[i + 1];
156
+ if (next === undefined || next.startsWith('--')) {
157
+ out[k] = true;
158
+ } else {
159
+ out[k] = next;
160
+ i++;
161
+ }
162
+ } else {
163
+ out._.push(a);
164
+ }
165
+ }
166
+ return out;
167
+ }
168
+
169
+ async function cmdInstall(args) {
170
+ const key = loadKey(args.key);
171
+ let targets = TARGETS.filter(([, p]) => {
172
+ try { return fs.statSync(p).isDirectory(); } catch (e) { return false; }
173
+ });
174
+
175
+ if (args.to) {
176
+ targets = [['指定目录', path.resolve(String(args.to))]];
177
+ } else if (!targets.length) {
178
+ targets = [['新建目录', path.join(os.homedir(), '.workbuddy', 'skills')]];
179
+ }
180
+
181
+ console.log(paint(C_BLUE, '安装安宜星实名认证技能'));
182
+
183
+ const installed = [];
184
+ for (const [label, root] of targets) {
185
+ try {
186
+ const dest = path.join(root, NAME);
187
+ const n = copyDir(SKILL_SRC, dest);
188
+ console.log(` ${paint(C_GREEN, '✅')} ${label}:${n} 个文件 → ${dest}`);
189
+ installed.push(dest);
190
+ } catch (e) {
191
+ console.log(` ${paint(C_RED, '❌')} ${label} 失败:${e.message}`);
192
+ }
193
+ }
194
+
195
+ if (!installed.length) return 1;
196
+
197
+ if (key) {
198
+ const fromArg = args.key && String(args.key).trim();
199
+ if (fromArg) {
200
+ fs.mkdirSync(path.dirname(keyFile()), { recursive: true });
201
+ fs.writeFileSync(keyFile(), key, 'utf8');
202
+ try { fs.chmodSync(keyFile(), 0o600); } catch (e) { /* windows 忽略 */ }
203
+ }
204
+
205
+ const probe = await api('idcard', {}, key);
206
+ if (probe.code === 1001) {
207
+ console.log(` ${paint(C_GREEN, '✅')} ${fromArg ? 'API Key 已保存并校验通过' : '检测到已配置的 Key,校验通过'}`);
208
+ } else if (probe.code === 1002) {
209
+ console.log(` ${paint(C_RED, '❌')} API Key 校验未通过,请确认是否完整复制`);
210
+ } else {
211
+ console.log(` ${paint(C_YEL, '⚠️')} Key 校验返回 ${probe.code} ${probe.msg || ''}`);
212
+ }
213
+ } else {
214
+ console.log('');
215
+ console.log(paint(C_YEL, '下一步:配好 API Key 才能核验'));
216
+ console.log(` 1. 打开 ${SITE}/login.php ,手机号验证码登录(未注册会自动开户)`);
217
+ console.log(' 2. 控制台「概览」页复制 API Key,新用户送 2 元测试额度');
218
+ console.log(` 3. 回来执行:npx -y ${NAME}@latest config --key 你的Key`);
219
+ console.log('');
220
+ console.log(paint(C_DIM, ' 如果你是在 Agent 会话里看到这段:直接把 Key 发给 Agent,让它帮你跑这一步。'));
221
+ }
222
+
223
+ console.log('');
224
+ console.log('装好了。有些客户端需要重启才认出新技能,重启后直接说「帮我核验这个人」。');
225
+ return 0;
226
+ }
227
+
228
+ async function cmdConfig(args) {
229
+ if (args.clear) {
230
+ try { fs.unlinkSync(keyFile()); console.log('已清除本地保存的 Key'); }
231
+ catch (e) { console.log('本地本来就没有保存 Key'); }
232
+ return 0;
233
+ }
234
+
235
+ if (args.show || !args.key) {
236
+ const k = loadKey('');
237
+ if (!k) {
238
+ console.log(GUIDE_NO_KEY.trim());
239
+ return 1;
240
+ }
241
+ console.log('配置文件:' + keyFile());
242
+ console.log('当前 Key:' + mask(k));
243
+ return 0;
244
+ }
245
+
246
+ const key = String(args.key).trim();
247
+ fs.mkdirSync(path.dirname(keyFile()), { recursive: true });
248
+ fs.writeFileSync(keyFile(), key, 'utf8');
249
+ try { fs.chmodSync(keyFile(), 0o600); } catch (e) { /* ignore */ }
250
+ console.log(`✅ Key 已保存到 ${keyFile()}`);
251
+
252
+ const probe = await api('idcard', {}, key);
253
+ if (probe.code === 1001) {
254
+ console.log('✅ Key 有效,已连上安宜星接口');
255
+ return 0;
256
+ }
257
+ if (probe.code === 1002) {
258
+ console.log('❌ Key 无效,请确认完整复制(控制台「概览」页可重新复制)');
259
+ return 1;
260
+ }
261
+ console.log(`⚠️ 校验返回 ${probe.code}:${probe.msg || ''}`);
262
+ return 0;
263
+ }
264
+
265
+ async function cmdVerify(kind, args) {
266
+ const key = loadKey(args.key);
267
+ if (!key) {
268
+ console.log(GUIDE_NO_KEY.trim());
269
+ return 2;
270
+ }
271
+
272
+ let payload = {};
273
+ let endpoint = kind;
274
+ if (args.name) payload.name = String(args.name).trim();
275
+
276
+ if (kind === 'idcard') {
277
+ if (!args.name || !args.idcard) return usage('身份证核验需要 --name 与 --idcard');
278
+ payload.idcard = String(args.idcard).trim();
279
+ } else if (kind === 'bank') {
280
+ const miss = [];
281
+ if (!args.name) miss.push('--name');
282
+ if (!args.idcard) miss.push('--idcard');
283
+ if (!args.bankcard) miss.push('--bankcard');
284
+ if (!args.mobile) miss.push('--mobile');
285
+ if (miss.length) return usage('银行卡四要素核验缺少:' + miss.join(' '));
286
+ payload.idcard = String(args.idcard).trim();
287
+ payload.bankcard = String(args.bankcard).trim();
288
+ payload.mobile = String(args.mobile).trim();
289
+ endpoint = 'bankcard';
290
+ } else {
291
+ if (!args.name || !args.mobile) return usage('手机号核验需要 --name 与 --mobile');
292
+ payload.mobile = String(args.mobile).trim();
293
+ if (args.idcard) payload.idcard = String(args.idcard).trim();
294
+ }
295
+
296
+ const r = await api(endpoint, payload, key);
297
+
298
+ if (args.json) {
299
+ console.log(JSON.stringify(r, null, 2));
300
+ return r.code === 0 ? 0 : 1;
301
+ }
302
+
303
+ if (r.code !== 0) {
304
+ console.log(paint(C_RED, '❌ ' + (ERR[r.code] || '调用失败') + `(code ${r.code})`));
305
+ if (r.msg) console.log(' ' + r.msg);
306
+ if (r.code === 1002) console.log(`\n Key 可能失效,去 ${SITE}/user/index.php 重新复制`);
307
+ if (r.code === 1003) console.log(`\n 余额不足,去 ${SITE}/buy.php 充值(50 元起)`);
308
+ return 1;
309
+ }
310
+
311
+ const d = r.data || {};
312
+ const [head, desc] = RESULT[d.result] || ['结果:' + d.result, ''];
313
+ console.log(head);
314
+ if (desc) console.log(' ' + desc);
315
+
316
+ const facts = [];
317
+ if (d.fee !== undefined) facts.push('费用 ¥' + Number(d.fee).toFixed(2));
318
+ if (d.balance !== undefined) facts.push('余额 ¥' + Number(d.balance).toFixed(2));
319
+ if (d.cost_ms !== undefined) facts.push('耗时 ' + d.cost_ms + 'ms');
320
+ if (facts.length) console.log(' ' + facts.join(' '));
321
+
322
+ const extra = [];
323
+ if (d.card_mask) extra.push('卡号 ' + d.card_mask);
324
+ if (d.bank_name) extra.push('开户行 ' + d.bank_name);
325
+ if (d.card_type) extra.push('卡类型 ' + d.card_type);
326
+ if (d.carrier) extra.push('运营商 ' + d.carrier);
327
+ if (d.city) extra.push('归属地 ' + d.city);
328
+ if (extra.length) console.log(' ' + extra.join(' '));
329
+
330
+ if (d.req_id) console.log(paint(C_DIM, ' 流水号 ' + d.req_id));
331
+ return 0;
332
+ }
333
+
334
+ function usage(msg) {
335
+ console.error('用法错误:' + msg);
336
+ return 1;
337
+ }
338
+
339
+ function help() {
340
+ console.log(`
341
+ ${paint(C_BLUE, '安宜星实名认证')} · 证件 / 银行卡 / 手机号实名核验
342
+
343
+ npx -y ${NAME}@latest install 装成 AI 技能
344
+ npx -y ${NAME}@latest install --key 你的Key 装好并配好密钥
345
+ npx -y ${NAME}@latest config --key 你的Key 只配密钥(会免费校验一次)
346
+ npx -y ${NAME}@latest config --show 查看当前 Key
347
+ npx -y ${NAME}@latest config --clear 清除 Key
348
+
349
+ npx -y ${NAME}@latest idcard --name 张三 --idcard 330102199001010012
350
+ npx -y ${NAME}@latest bank --name 张三 --idcard 330102199001010012 \\
351
+ --bankcard 6222021234567894 --mobile 13800138000
352
+ npx -y ${NAME}@latest mobile --name 张三 --mobile 13800138000 [--idcard ...]
353
+
354
+ 加 --json 输出接口原始报文。密钥在 ${SITE} 获取,新用户送 2 元。
355
+ `);
356
+ }
357
+
358
+ (async () => {
359
+ const args = parseArgs(process.argv.slice(2));
360
+ const cmd = args._[0] || 'help';
361
+
362
+ let code = 0;
363
+ if (cmd === 'install') code = await cmdInstall(args);
364
+ else if (cmd === 'config') code = await cmdConfig(args);
365
+ else if (cmd === 'idcard' || cmd === 'bank' || cmd === 'mobile') code = await cmdVerify(cmd, args);
366
+ else help();
367
+
368
+ process.exit(code);
369
+ })().catch((e) => {
370
+ console.error('出错了:' + e.message);
371
+ process.exit(1);
372
+ });
package/package.json CHANGED
@@ -1,6 +1,30 @@
1
1
  {
2
2
  "name": "anyixing-verify",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
6
- }
3
+ "version": "1.0.1",
4
+ "description": "实名认证核验:身份证二要素、银行卡四要素、手机号二/三要素。可一键装成 AI 技能(Claude Code / Cursor / WorkBuddy),也能直接当命令行工具用。",
5
+ "keywords": [
6
+ "实名认证",
7
+ "身份证核验",
8
+ "银行卡核验",
9
+ "手机号核验",
10
+ "kyc",
11
+ "identity-verification",
12
+ "ai-skill",
13
+ "claude-skill",
14
+ "china-id"
15
+ ],
16
+ "homepage": "https://smrz.anyixing.com.cn",
17
+ "author": "anyixing",
18
+ "license": "MIT",
19
+ "bin": {
20
+ "anyixing-verify": "bin/cli.js"
21
+ },
22
+ "files": [
23
+ "bin",
24
+ "skill",
25
+ "README.md"
26
+ ],
27
+ "engines": {
28
+ "node": ">=14"
29
+ }
30
+ }
package/skill/SKILL.md ADDED
@@ -0,0 +1,159 @@
1
+ ---
2
+ name: anyixing-verify
3
+ description: 核验中国居民身份证号、银行卡号、手机号的实名信息,判断姓名与证件号/银行卡号/手机号是否属于同一人。当用户要求实名认证、KYC 校验、实名核验、身份证二要素核验、银行卡四要素核验(含预留手机号)、手机号实名核验、绑定银行卡前校验、注册风控、确认身份证号与姓名是否匹配、验证手机号是否为本人实名时使用;也用于把这类核验能力接入用户自己的网站、后端、小程序或 App(写调用代码、配环境变量、联调)。接口由安宜星提供,按次计费,调用前需在官网获取 API Key(新用户赠 2 元)。
4
+ agent_created: true
5
+ ---
6
+
7
+ # 实名认证核验
8
+
9
+ 调用安宜星实名认证 API,核验三类信息。三种核验共用同一套返回结构与错误码。
10
+
11
+ ## 何时使用
12
+
13
+ - 核验「姓名 + 身份证号」是否是同一个人
14
+ - 核验「姓名 + 身份证号 + 银行卡号 + 银行预留手机号」是否属于同一人(开卡、提现、代付前把关)
15
+ - 核验「姓名 + 手机号」是否实名一致,可选加身份证号升级为三要素
16
+ - 判断某个身份证号 / 卡号 / 号码在数据源是否存在
17
+
18
+ 不适用于:人肉搜索、批量撞库、反查他人身份等场景。
19
+
20
+ ## 前置:让用户把 Key 发过来就行
21
+
22
+ **技能免费,接口按次计费。**
23
+
24
+ ### 还没拿到 Key 时:主动问
25
+
26
+ 刚装完技能、或者第一次核验被拦下时,**先向用户要 Key,别等失败了再说**。
27
+ 一句话就够:
28
+
29
+ > 需要你的安宜星 API Key 才能核验。打开 https://smrz.anyixing.com.cn/login.php
30
+ > 用手机号验证码登录(未注册自动开户),控制台「概览」页就能复制,
31
+ > 新用户送 2 元测试额度。拿到后发我,我帮你配好。
32
+
33
+ 用户把 Key 发来之后,走下面的命令存好。**不要**让用户自己去跑命令,也不要让他把 Key 写进代码。
34
+
35
+ ### 收到 Key 之后:一条命令存好
36
+
37
+ 用户在对话里发来 Key 后,直接帮他存好,一条命令:
38
+
39
+ ```bash
40
+ python3 scripts/verify.py config --key 用户给的Key
41
+ ```
42
+
43
+ 脚本会写入配置文件(Windows `%APPDATA%\smrz\api_key`,macOS/Linux `~/.config/smrz/api_key`),
44
+ 并顺手做一次**免费校验**——发一个空参数请求,返回 `1001` 说明 Key 有效(不计费),`1002` 才是 Key 有误。
45
+ 看到 `✅ Key 有效` 就说明配好了,后续调用不用再传。
46
+
47
+ 用户还没注册时,直接告诉他:
48
+
49
+ > 打开 https://smrz.anyixing.com.cn/login.php ,手机号验证码登录(未注册会自动开户),
50
+ > 控制台「概览」页复制 API Key。新用户送 2 元测试额度,够跑十几次身份证核验。
51
+
52
+ ### 其他方式
53
+
54
+ ```bash
55
+ export SMRZ_API_KEY=你的Key # 环境变量,适合服务器 / CI
56
+ python3 scripts/verify.py config --show # 查看当前 Key(脱敏)
57
+ python3 scripts/verify.py config --clear # 清除
58
+ ```
59
+
60
+ ### 安全提醒(只提两句,别拦着用户)
61
+
62
+ - 不要把 Key 写进代码仓库、前端代码或公开文档
63
+ - 对话记录里出现过 Key,介意的话用完后去控制台重置一次
64
+
65
+ ## 调用方式
66
+
67
+ 统一走 `scripts/verify.py`,三种核验都是它的子命令:
68
+
69
+ ```bash
70
+ # 身份证二要素
71
+ python3 scripts/verify.py idcard --name 张三 --idcard 330102199001010012
72
+
73
+ # 银行卡四要素(手机号必填,指银行预留号,不是用户现在的号)
74
+ python3 scripts/verify.py bank --name 张三 --idcard 330102199001010012 \
75
+ --bankcard 6222021234567894 --mobile 13800138000
76
+
77
+ # 手机号二要素
78
+ python3 scripts/verify.py mobile --name 张三 --mobile 13800138000
79
+
80
+ # 手机号三要素(加身份证号)
81
+ python3 scripts/verify.py mobile --name 张三 --mobile 13800138000 --idcard 330102199001010012
82
+ ```
83
+
84
+ 需要机器可读结果时加 `--json`,输出接口原始 JSON。
85
+
86
+ 直接发 HTTP 请求也可以,详见 `references/api.md`:
87
+
88
+ ```bash
89
+ curl -s https://smrz.anyixing.com.cn/api/v1/idcard \
90
+ -H "X-Api-Key: $SMRZ_API_KEY" -H "Content-Type: application/json" \
91
+ -d '{"name":"张三","idcard":"330102199001010012"}'
92
+ ```
93
+
94
+ ## 帮用户接进他自己的项目
95
+
96
+ 用户说「把实名认证接到我的网站 / 后端 / 小程序里」时走这条路径,完整示例见 `references/integration.md`。
97
+
98
+ **Key 只能放服务端。** 前端直连等于把 Key 公开,这一条没有例外。
99
+
100
+ 流程:
101
+
102
+ 1. 先读用户项目:什么语言、什么框架、路由怎么注册、配置从哪读
103
+ 2. 写一个 `verify` 封装函数,Key 从环境变量取(`SMRZ_API_KEY`),不要内联
104
+ 3. 在业务逻辑里调用,按 `result` 的五个分支写清处理
105
+ 4. 提醒用户自己去配环境变量——**不要让他把 Key 贴到聊天里**
106
+ 5. 用真实数据联调一次,确认 `code === 0`
107
+
108
+ 接之前先让用户确认两件事:他有没有能放环境变量的服务端;他愿意先把 Key 配到哪个环境。
109
+
110
+ 纯前端项目(静态站、无后端的小程序)要明确告诉他不能直连,可选方案是加一个云函数做中转。
111
+
112
+ **如果用户提供了服务器凭证(FTP / SSH / 宝塔 / Git),可以帮他把改好的代码传上去**,方法见 integration.md 的「上线部署」。没有凭证就让用户自己传,别猜路径。
113
+
114
+ ## 结果解读
115
+
116
+ | result | 含义 | 是否计费 |
117
+ |---|---|---|
118
+ | `MATCH` | 一致,信息属于同一人 | 计费 |
119
+ | `NO_MATCH` | 不一致,至少一项对不上 | 计费 |
120
+ | `NO_ID` | 身份证号在数据源无记录 | 计费 |
121
+ | `NO_CARD` | 银行卡号在数据源无记录 | 计费 |
122
+ | `NO_NUMBER` | 手机号在数据源无记录 | 计费 |
123
+
124
+ **`NO_MATCH` 是正常业务结论,不是系统故障。** 告诉用户「信息不一致」即可,不要建议重试或换接口再验——重复核验只会重复扣费。
125
+
126
+ 银行卡核验会额外返回 `bank_name`(开户行)、`card_type`(储蓄卡/信用卡)、`card_mask`(脱敏卡号)。
127
+ 手机号核验会额外返回 `carrier`(运营商)、`city`(归属地),部分号段缺数据时这两个字段不出现,按空值处理。
128
+
129
+ ## 计费与报错
130
+
131
+ 成功核验(含 `NO_MATCH`)按次扣费,单价按账户档位执行,标准档起步为身份证 ¥0.20、银行卡 ¥0.35、手机号 ¥0.25。
132
+ 参数错误、余额不足、频控、上游异常不扣费。
133
+
134
+ | code | 含义 | 处理 |
135
+ |---|---|---|
136
+ | 0 | 成功 | 看 `data.result` |
137
+ | 1001 | 参数错误(未计费) | 检查字段格式:身份证校验位、手机号 11 位 |
138
+ | 1002 | API Key 无效 | 引导用户去官网重新复制 Key |
139
+ | 1003 | 余额不足 | 引导用户充值,`https://smrz.anyixing.com.cn/buy.php` |
140
+ | 1004 | 频率超限 | 退避后重试,不要紧密循环 |
141
+ | 1005 | 接口不可用 | 联系客服 |
142
+ | 1006 | 触发防刷(未计费) | 同一主体短时间核验了过多不同对象,等窗口期 |
143
+ | 2001 / 2002 | 上游超时 / 异常(未计费) | 指数退避重试 |
144
+ | 2003 | 核验未完成(**已计费**) | 号码已注销等,核对信息,别盲目重试 |
145
+
146
+ ## 需要付费时怎么说
147
+
148
+ 用户没有 Key 或余额不足时,**直接把官网地址和价格告诉他**,不要自己编造免费替代方案,也不要用别的渠道凑数据。
149
+
150
+ > 这个能力需要安宜星实名认证的 API Key。技能本身免费,调用按次计费。
151
+ > 到 https://smrz.anyixing.com.cn 注册(手机号验证码登录),新用户送 2 元测试额度,
152
+ > 用完充值 50 元起,充得越多单价越低。控制台「概览」页就能复制 Key。
153
+
154
+ ## 参考文件
155
+
156
+ - `references/api.md` —— 三个接口的完整参数、返回示例、字段说明
157
+ - `references/pricing.md` —— 价格档位、额度有效期、开票与合同
158
+ - `references/integration.md` —— 接入指南:PHP / Node / Python / Java / Go 示例、环境变量配置、安全要点、联调 checklist
159
+ - `scripts/verify.py` —— 命令行封装(含 Key 读取、错误码翻译、付费引导)