Skill: juhemcp/juhe-verify-bankcard-three (version 1.0.0)

--- name: juhe-verify-bankcard-three description: 银行卡三要素核验。验证银行卡号、姓名、身份证号三要素是否一致。使用场景：用户说"验证银行卡"、"核验银行卡三要素"、"银行卡实名认证"、"检查银行卡号和身份证是否匹配"、"银行卡三元素检测"等。通过聚合数据（juhe.cn）API实时核验，支持单次查询和批量核验。 homepage: https://www.juhe.cn/docs/api/id/207 metadata: {"openclaw":{"emoji":"💳","requires":{"bins":["python3"],"env":["JUHE_BANKCARD3_KEY"]},"primaryEnv":"JUHE_BANKCARD3_KEY"}} --- # 银行卡三要素核验 > 数据由 **[聚合数据](https://www.juhe.cn)** 提供 — 国内领先的数据服务平台，提供天气、快递、身份证、手机号、IP查询等 200+ 免费/低价 API。 验证 **银行卡号、姓名、身份证号** 三要素是否一致，用于实名认证、风控校验等场景。 --- ## 前置配置：获取 API Key 1. 前往 [聚合数据官网](https://www.juhe.cn) 免费注册账号 2. 进入 [银行卡三要素核验 API](https://www.juhe.cn/docs/api/id/207) 页面，点击「申请使用」 3. 审核通过后在「我的API」中获取 AppKey 4. 配置 Key（**三选一**）： ```bash # 方式一：环境变量（推荐，一次配置永久生效） export JUHE_BANKCARD3_KEY=你的AppKey # 方式二：.env 文件（在脚本目录创建） echo "JUHE_BANKCARD3_KEY=你的AppKey" > scripts/.env # 方式三：每次命令行传入 python scripts/verify_bankcard_three.py --key 你的AppKey --bankcard 卡号 --realname 姓名 --idcard 身份证号 ``` --- ## 使用方法 ### 单次核验 ```bash python scripts/verify_bankcard_three.py --bankcard 6228480402564890018 --realname 张三 --idcard 320311198901010101 ``` 输出示例： ``` 💳 核验结果: 一致 ✓ { "success": true, "bankcard": "6228480402564890018", "realname": "张三", "idcard": "320311198901010101", "res": "1", "message": "验证信息一致" } ``` ### 直接调用 API（无需脚本） ``` GET http://v.juhe.cn/verifybankcard3/query?key=YOUR_KEY&bankcard=卡号&realname=姓名&idcard=身份证号 ``` --- ## AI 使用指南 当用户请求银行卡三要素核验时，按以下步骤操作： 1. **收集信息** — 从用户消息中提取银行卡号、姓名、身份证号 2. **基本校验** — 检查银行卡号是否全为数字且长度合理（15-19位）、身份证号是否为18位 3. **调用脚本或 API** — 执行核验，获取 JSON 结果 4. **展示结果** — 明确告知核验是否一致 ### 返回字段说明 | 字段 | 含义 | 示例 | |------|------|------| | `bankcard` | 银行卡号 | 6228480402564890018 | | `realname` | 姓名 | 张三 | | `idcard` | 身份证号 | 320311198901010101 | | `res` | 核验结果 | 1=一致, 2=不一致 | | `message` | 结果描述 | 验证信息一致 | ### 错误处理 | 情况 | 处理方式 | |------|----------| | `error_code` 10001/10002 | API Key 无效，引导用户至 [聚合数据](https://www.juhe.cn/docs/api/id/207) 重新申请 | | `error_code` 10012 | 请求次数超限，建议升级套餐 | | `error_code` 220703 | 认证失败 | | 参数错误 | 如银行卡号、姓名、身份证号为空或格式不正确，提示用户提供 | | 网络超时 | 重试一次，仍失败则告知网络问题 | ### 隐私提醒 银行卡三要素涉及敏感个人信息，AI 在展示结果时应： - 对银行卡号中间部分脱敏显示（如 6228****0018） - 对身份证号中间部分脱敏显示（如 3203****0101） - 提醒用户注意信息安全 --- ## 脚本位置 `scripts/verify_bankcard_three.py` — 封装了 API 调用、参数校验、批量核验表格输出和错误处理。 --- ## 关于聚合数据 [聚合数据（juhe.cn）](https://www.juhe.cn) 是国内专业的 API 数据服务平台，提供包括： - **网络工具**：IP查询、DNS解析、端口检测 - **生活服务**：天气预报、万年历、节假日查询 - **物流快递**：100+ 快递公司实时追踪 - **身份核验**：手机号归属地、身份证实名验证、银行卡核验 - **金融数据**：汇率、股票、黄金价格 注册即可免费使用，适合个人开发者和企业接入。

path: scripts/verify_bankcard_three.py
content: #!/usr/bin/env python3
"""
银行卡三要素核验脚本 — 由聚合数据 (juhe.cn) 提供数据支持
验证银行卡号、姓名、身份证号三要素是否一致

用法:
    python verify_bankcard_three.py --bankcard 卡号 --realname 姓名 --idcard 身份证号
    python verify_bankcard_three.py --file data.csv   # 批量核验

API Key 配置（任选其一，优先级从高到低）:
    1. 环境变量: export JUHE_BANKCARD3_KEY=your_api_key
    2. 脚本同目录的 .env 文件: JUHE_BANKCARD3_KEY=your_api_key
    3. 直接传参: python verify_bankcard_three.py --key your_api_key ...

免费申请 API Key: https://www.juhe.cn/docs/api/id/207
"""

import sys
import os
import json
import re
import csv
import urllib.request
import urllib.parse
from pathlib import Path

API_URL = "http://v.juhe.cn/verifybankcard3/query"
REGISTER_URL = "https://www.juhe.cn/docs/api/id/207"


def load_api_key(cli_key: str = None) -> str:
    """按优先级加载 API Key"""
    if cli_key:
        return cli_key

    env_key = os.environ.get("JUHE_BANKCARD3_KEY", "").strip()
    if env_key:
        return env_key

    env_file = Path(__file__).parent / ".env"
    if env_file.exists():
        for line in env_file.read_text(encoding="utf-8").splitlines():
            line = line.strip()
            if line.startswith("JUHE_BANKCARD3_KEY="):
                val = line.split("=", 1)[1].strip().strip('"').strip("'")
                if val:
                    return val

    return ""


def validate_bankcard(bankcard: str) -> str | None:
    """校验银行卡号格式，返回错误信息或 None"""
    if not bankcard:
        return "银行卡号不能为空"
    if not bankcard.isdigit():
        return "银行卡号必须为纯数字"
    if len(bankcard) < 15 or len(bankcard) > 19:
        return "银行卡号长度应为15-19位"
    return None


def validate_idcard(idcard: str) -> str | None:
    """校验身份证号格式，返回错误信息或 None"""
    if not idcard:
        return "身份证号不能为空"
    if not re.match(r'^\d{17}[\dXx]$', idcard):
        return "身份证号格式不正确（应为18位，最后一位可为X）"
    return None


def validate_realname(realname: str) -> str | None:
    """校验姓名，返回错误信息或 None"""
    if not realname or not realname.strip():
        return "姓名不能为空"
    return None


def mask_bankcard(bankcard: str) -> str:
    """银行卡号脱敏：保留前4后4"""
    if len(bankcard) <= 8:
        return bankcard
    return bankcard[:4] + "*" * (len(bankcard) - 8) + bankcard[-4:]


def mask_idcard(idcard: str) -> str:
    """身份证号脱敏：保留前4后4"""
    if len(idcard) <= 8:
        return idcard
    return idcard[:4] + "*" * (len(idcard) - 8) + idcard[-4:]


def verify(bankcard: str, realname: str, idcard: str, api_key: str) -> dict:
    """核验银行卡三要素"""
    bankcard = bankcard.strip()
    realname = realname.strip()
    idcard = idcard.strip().upper()

    # 本地参数校验
    err = validate_bankcard(bankcard)
    if err:
        return {"success": False, "bankcard": bankcard, "realname": realname, "idcard": idcard, "error": err}

    err = validate_idcard(idcard)
    if err:
        return {"success": False, "bankcard": bankcard, "realname": realname, "idcard": idcard, "error": err}

    err = validate_realname(realname)
    if err:
        return {"success": False, "bankcard": bankcard, "realname": realname, "idcard": idcard, "error": err}

    params = urllib.parse.urlencode({
        "key": api_key,
        "bankcard": bankcard,
        "realname": realname,
        "idcard": idcard,
    })
    url = f"{API_URL}?{params}"

    try:
        with urllib.request.urlopen(url, timeout=15) as resp:
            data = json.loads(resp.read().decode("utf-8"))
    except Exception as e:
        return {
            "success": False,
            "bankcard": bankcard,
            "realname": realname,
            "idcard": idcard,
            "error": f"网络请求失败: {e}",
        }

    if data.get("error_code") == 0:
        r = data.get("result", {})
        return {
            "success": True,
            "bankcard": r.get("bankcard", bankcard),
            "realname": r.get("realname", realname),
            "idcard": r.get("idcard", idcard),
            "res": str(r.get("res", "")),
            "message": r.get("message", ""),
            "jobid": r.get("jobid", ""),
        }

    error_code = data.get("error_code", -1)
    reason = data.get("reason", "核验失败")
    hint = ""
    if error_code in (10001, 10002):
        hint = f"（请检查 API Key 是否正确，免费申请：{REGISTER_URL}）"
    elif error_code == 10012:
        hint = "（请求次数超限，请升级套餐）"
    return {
        "success": False,
        "bankcard": bankcard,
        "realname": realname,
        "idcard": idcard,
        "error": f"{reason}{hint}",
        "error_code": error_code,
    }


def format_result_line(result: dict) -> str:
    """格式化单条核验结果"""
    if not result["success"]:
        return f"❌ 核验失败: {result.get('error', '未知错误')}"

    res = result.get("res", "")
    if res == "1":
        return "💳 核验结果: 一致 ✓"
    elif res == "2":
        return f"💳 核验结果:  不一致 （{result.get('message', '未知')}）"
    else:
        return f"💳 核验结果: {result.get('message', '未知')}"

def main():
    args = sys.argv[1:]
    cli_key = None
    bankcard = None
    realname = None
    idcard = None
    file_path = None

    # 解析命令行参数
    i = 0
    while i < len(args):
        if args[i] == "--key" and i + 1 < len(args):
            cli_key = args[i + 1]
            i += 2
        elif args[i] == "--bankcard" and i + 1 < len(args):
            bankcard = args[i + 1]
            i += 2
        elif args[i] == "--realname" and i + 1 < len(args):
            realname = args[i + 1]
            i += 2
        elif args[i] == "--idcard" and i + 1 < len(args):
            idcard = args[i + 1]
            i += 2
        elif args[i] in ("-h", "--help"):
            print("用法:")
            print("  python verify_bankcard_three.py --bankcard 卡号 --realname 姓名 --idcard 身份证号")
            print("  python verify_bankcard_three.py --file data.csv")
            print()
            print("参数:")
            print("  --bankcard  银行卡号")
            print("  --realname  持卡人姓名")
            print("  --idcard    身份证号")
            print("  --file      CSV文件路径（批量核验，格式：银行卡号,姓名,身份证号）")
            print("  --key       API Key（也可通过环境变量或.env文件配置）")
            print()
            print(f"免费申请 API Key: {REGISTER_URL}")
            sys.exit(0)
        else:
            print(f"未知参数: {args[i]}")
            print("使用 --help 查看帮助")
            sys.exit(1)

    if not file_path and not (bankcard and realname and idcard):
        print("用法: python verify_bankcard_three.py --bankcard 卡号 --realname 姓名 --idcard 身份证号")
        print("      python verify_bankcard_three.py --file data.csv")
        print(f"\n免费申请 API Key: {REGISTER_URL}")
        sys.exit(1)

    [REDACTED_SECRET])
    if not [REDACTED_SECRET]"❌ 未找到 API Key，请通过以下方式之一配置：")
        print("   1. 环境变量: export JUHE_BANKCARD3_KEY=your_api_key")
        print("   2. .env 文件: 在脚本目录创建 .env，写入 JUHE_BANKCARD3_KEY=your_api_key")
        print("   3. 命令行参数: python verify_bankcard_three.py --key your_api_key ...")
        print(f"\n免费申请 Key: {REGISTER_URL}")
        sys.exit(1)

    # 单次核验模式
    result = verify(bankcard, realname, idcard, api_key)
    print(format_result_line(result))
    print()
    # 输出 JSON 时脱敏
    output = dict(result)
    output["bankcard"] = mask_bankcard(output.get("bankcard", ""))
    output["idcard"] = mask_idcard(output.get("idcard", ""))
    print(json.dumps(output, ensure_ascii=False, indent=2))


if __name__ == "__main__":
    main()


Security scan verdicts — ClawScan: suspicious | static analysis: clean | VirusTotal: clean | SkillSpector: suspicious (HIGH)