You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

81 lines
3.1 KiB

// Package idverify 身份证二要素(姓名 + 身份证号)实名核验。
//
// 与其它 sys 子系统不同,本包**不是启动时初始化的单例**:核验服务配在后台
// 「第三方服务配置」里(类别 comm.SvcCatIdVerify),按应用作用域存在 svc_config,
// 调用时才解析出凭据。所以这里只提供无状态的工厂 + 接口,由业务侧每次带配置进来。
//
// ⚠️ 这类服务的凭据是**云账号主 AK/SK**,comm.IsServerOnlySvc 会保证它们
// 永不随 user_getthirdsvcs / user_getappconfig 下发给客户端。
package idverify
import (
"context"
"errors"
"fmt"
"strings"
)
// 服务商标识(与 console 内置模板 ThirdSvcTemplate.Provider 一致)。
const (
ProviderAliyun = "aliyun"
ProviderTencent = "tencent"
ProviderChuanglan = "chuanglan"
)
var (
// ErrUnsupportedProvider 配了本包不认识的服务商。
ErrUnsupportedProvider = errors.New("idverify: 不支持的服务商")
// ErrMissingCredential 服务商凭据字段缺失。
ErrMissingCredential = errors.New("idverify: 凭据字段缺失")
)
// Result 一次核验的结果。
//
// 注意区分两种"失败":
// - Matched=false 且 err=nil:服务商**明确判定不一致**(正常业务结果,已计费)。
// - err!=nil:调用本身失败(网络/鉴权/额度耗尽/参数被拒),**结论未知**,不能当作"不一致"。
//
// 把后者当成"不一致"会让用户在服务商欠费时看到"您填的信息有误",是最难排查的一类线上问题。
type Result struct {
Matched bool // 姓名与身份证号是否一致
BizCode string // 服务商返回的原始结果码(阿里 ResultObject.BizCode / 腾讯 Result / 创蓝 data.result),便于对账排查
Message string // 服务商返回的可读说明
}
// Verifier 一个已装配好凭据的核验客户端。
type Verifier interface {
// Verify 执行二要素核验。realname/idcardno 为明文,调用方负责不落库。
Verify(ctx context.Context, realname, idcardno string) (Result, error)
// Provider 返回服务商标识,用于落库记录来源。
Provider() string
}
// New 按服务商与字段表装配一个核验客户端。
// fields 来自 comm.ResolveSvcPlainFields 的解密结果(键名与 console 内置模板一致)。
func New(provider string, fields map[string]string) (Verifier, error) {
switch strings.TrimSpace(strings.ToLower(provider)) {
case ProviderAliyun:
return newAliyun(fields)
case ProviderTencent:
return newTencent(fields)
case ProviderChuanglan:
return newChuanglan(fields)
default:
return nil, fmt.Errorf("%w: %s", ErrUnsupportedProvider, provider)
}
}
// field 取字段并去空白;缺失返回空串。
func field(fields map[string]string, key string) string {
return strings.TrimSpace(fields[key])
}
// requireFields 校验必填凭据字段齐全,返回第一个缺失的字段名。
func requireFields(fields map[string]string, keys ...string) error {
for _, k := range keys {
if field(fields, k) == "" {
return fmt.Errorf("%w: %s", ErrMissingCredential, k)
}
}
return nil
}