Corsair Abstract 插件接入指南:邮箱、IBAN 与 VAT 校验 API 的统一封装与本地同步
【免费下载链接】corsairConnect your users to their apps项目地址: https://gitcode.com/GitHub_Trending/corsa/corsair
Corsair 的@corsair-dev/abstract插件把 Abstract API 的 4 个只读校验操作(邮箱信誉、邮箱验证、IBAN 验证、VAT 税率分类)封装为类型安全的abstract.api.*调用,并把每次查询结果自动同步到本地数据库,可通过abstract.db.*.search()离线检索。本文以 packages/abstract/README.md 为核心,结合 packages/abstract 的源码实现与 docs/plugins/abstract 的官方文档,讲解安装、配置、四个端点的调用方式、API Key 鉴权机制、错误处理策略以及本地数据同步的使用方法。
插件概览
Abstract 是一家提供开发者校验 API 的服务商,其产品按业务域拆分为多个独立的子域名(每个域名拥有独立的 API Key)。Corsair 插件把以下 4 个操作统一收编进tenant.abstract.api命名空间,全部为只读操作(风险级别read):
| 操作 | Operation ID | 风险 | 说明 |
|---|---|---|---|
email.reputation | abstract.api.email.reputation | read | 评估邮箱的可投递性与质量:格式、一次性/免费/角色邮箱检测、MX 与 SMTP 校验 |
email.validate | abstract.api.email.validate | read | 验证邮箱地址是否真实、格式正确且可投递 |
iban.validate | abstract.api.iban.validate | read | 验证 IBAN 号的格式与国家代码 |
vat.getCategories | abstract.api.vat.getCategories | read | 获取某国的 VAT 税率分类(标准、减免、特殊) |
每个端点都对应一份 Zod 输入/输出 Schema(定义于 packages/abstract/endpoints/types.ts),调用时拥有完整的 TypeScript 类型提示,同时运行时还会做二次校验,确保来自 Abstract 的响应结构与声明一致(见 packages/abstract/endpoints/email-reputation.ts 的EmailReputationResponseSchema.parse(rawResponse))。
安装
在已有 Corsair 项目的根目录安装插件:
pnpm add @corsair-dev/abstract使用 npm / yarn / bun 同样可以安装(详见 docs/plugins/abstract/overview.mdx):
npm install corsair @corsair-dev/abstract # 或 yarn add corsair @corsair-dev/abstract # 或 bun add corsair @corsair-dev/abstract从 packages/abstract/package.json 可以看到该插件的声明依赖(peerDependencies):
corsair >= 0.1.0:插件运行在 Corsair 核心之上;zod ^4.1.13:输入/输出 Schema 与数据库 Schema 均由 zod 定义。
注意包名中的@corsair-dev/abstract是独立包,需与核心包corsair一起安装。
快速接入:把插件挂到 Corsair 实例上
参照 docs/plugins/abstract/overview.mdx 的 Setup 章节,最小化配置如下:
// corsair.ts import Database from 'better-sqlite3'; import { createCorsair } from 'corsair'; import { abstract } from '@corsair-dev/abstract'; export const corsair = createCorsair({ plugins: [abstract()], database: new Database('corsair.db'), kek: process.env.CORSAIR_KEK!, // 用于加密租户凭证的 Key Encryption Key hub: { projectApiKey: process.env.CORSAIR_API_KEY!, signingSecret: process.env.CORSAIR_SIGNING_SECRET!, }, });几个关键点:
database:Corsair 需要本地数据库来持久化凭证与同步数据;kek:租户的 API Key 以加密形式存储,解密需要 KEK(参见 docs/concepts/api-key.mdx);- 多租户:Corsair 默认开启多租户,通过
corsair.withTenant(id)拿到租户作用域实例后再调用 API,租户之间数据与凭证互相隔离(参见 docs/concepts/multi-tenancy.mdx); - 插件工厂
abstract()定义于 packages/abstract/index.ts,authType默认值为api_key。
让租户连接 Abstract 账号
Abstract 使用 API Key 鉴权,无需 OAuth。调用manage.connect.createLink生成一个连接链接,把租户引导到该页面,由 Hub 托管连接页并把结果回传给应用(参见 docs/management/connect.mdx):
const { connectUrl } = await corsair.manage.connect.createLink({ plugin: 'abstract', tenantId: 'acme', }); // 将用户的浏览器重定向到 connectUrl当租户首次发起请求时,如果尚未配置凭证,Corsair 会提示其输入 API Key。
四个端点详解
调用方式统一为corsair.withTenant('acme').abstract.api.<group>.<operation>({ ... }),以下是各端点的输入输出与完整类型。
email.reputation —— 邮箱信誉与可投递性评估
对应 Operation IDabstract.api.email.reputation,风险read。返回 Abstract Email Reputation API(emailreputation.abstractapi.com/v1)的完整评估结果,包括投递状态、质量评分、发件人/域名信息、风险等级与泄露记录。
const tenant = corsair.withTenant('acme'); const result = await tenant.abstract.api.email.reputation({ email: 'support@abstractapi.com', });输入参数:
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
email | string | 是 | 要检查信誉的邮箱地址 |
输出结构(字段细节以 packages/abstract/endpoints/types.ts 为准):
{ email_address: string, suggested_correction?: string | null, email_deliverability: { status: string, // 可投递性结论 status_detail: string, is_format_valid: boolean, is_smtp_valid: boolean, is_mx_valid: boolean, mx_records?: string[] | null }, email_quality: { score: number, // 0.0–1.0 质量评分 is_free_email: boolean, is_username_suspicious: boolean, is_disposable: boolean, // 一次性邮箱 is_catchall: boolean, is_subaddress: boolean, is_role?: boolean, // 角色邮箱(如 info@、support@) is_dmarc_enforced?: boolean, is_spf_strict?: boolean, minimum_age?: number | null }, email_sender: { first_name?, last_name?, email_provider_name?, organization_name?, organization_type? }, email_domain: { domain?, domain_age?, is_live_site?, registrar?, registrar_url?, date_registered?, date_last_renewed?, date_expires?, is_risky_tld? }, email_risk: { address_risk_status?: string | null, domain_risk_status?: string | null }, email_breaches?: { total_breaches?: number | null, date_first_breached?: string | null, date_last_breached?: string | null, breached_domains?: { domain: string, breach_date?: string | null }[] } }对于无效邮箱地址,Abstract 返回的email_domain、email_risk、email_breaches中相关字段为null,Schema 中这些字段声明为nullable,且在测试中专门覆盖了该场景(见 packages/abstract/api.test.ts 中get returns correct type for an invalid address用例)。
email.validate —— 邮箱真实性验证
对应 Operation IDabstract.api.email.validate,风险read。用于在收集到邮箱后、正式发信前确认地址是否真实、格式正确且可投递。
const result = await tenant.abstract.api.email.validate({ email: 'someone@gmail.com', });输入参数:email: string(必填)。
输出(一个扁平化的验证结果):
| 字段 | 类型 | 说明 |
|---|---|---|
email | string | 被验证的邮箱 |
autocorrect | string | Abstract 检测到疑似拼写错误时的修正建议,无则空字符串 |
deliverability | string | 投递结论,如deliverable/undeliverable/risky/unknown |
quality_score | number | 0.0–1.0 置信度评分 |
is_valid_format | boolean | 格式是否合法 |
is_free_email | boolean | 是否免费邮箱(如 gmail.com) |
is_disposable_email | boolean | 是否一次性邮箱 |
is_role_email | boolean | 是否角色邮箱 |
is_catchall_email | boolean | 是否 catch-all 域名 |
is_mx_found | boolean | 是否找到 MX 记录 |
is_smtp_valid | boolean | SMTP 是否验证通过 |
实现细节:Abstract 的独立 Email Validation 产品在插件所针对的账号/套餐上不可用,因此该端点实际调用的是 Email Reputation API,再通过mapEmailReputationToValidation(见 packages/abstract/endpoints/email-validation.ts)把信誉响应的可投递性/质量字段映射为扁平的验证结果。这意味着email.validate与email.reputation使用同一个 API Key。
iban.validate —— IBAN 格式校验
对应 Operation IDabstract.api.iban.validate,风险read。验证 IBAN 的格式与国家代码,用于收款场景中收集 IBAN 后即时校验。
const result = await tenant.abstract.api.iban.validate({ iban: 'DE89370400440532013000', });输入参数:iban: string(必填,可带空格,底层实现会先去除空格再做脱敏处理)。
输出:
| 字段 | 类型 | 说明 |
|---|---|---|
iban | string | 规范化后的 IBAN |
is_valid | boolean | 格式与国家代码是否有效 |
该端点调用ibanvalidation.abstractapi.com/v1(见 packages/abstract/client.ts 中ABSTRACT_API_HOSTS)。
vat.getCategories —— VAT 税率分类查询
对应 Operation IDabstract.api.vat.getCategories,风险read。返回指定国家的标准/减免/特殊 VAT 税率分类,用于需要按国家计算适用税率的产品与服务定价场景。
const result = await tenant.abstract.api.vat.getCategories({ countryCode: 'DE', // ISO 3166-1 alpha-2 大写国家代码 });输入参数:countryCode: string(必填,正则校验^[A-Z]{2}$,必须是两位大写 ISO 3166-1 alpha-2 代码)。
输出为对象数组:
{ country_code: string, // 如 "DE" rate: string, // 十进制字符串,如 "0.070" 表示 7% category: string, // 分类:standard / reduced / special 等 description: string }[]该端点调用vat.abstractapi.com/v1/categories(见 packages/abstract/endpoints/vat.ts)。与另外三个端点不同,它的响应是一个数组,落库时按country_code:category作为实体主键逐条写入(见 packages/abstract/endpoints/vat.ts)。
鉴权机制:API Key 与按产品拆分的多 Key 策略
Abstract 插件仅支持api_key一种鉴权方式(AbstractPluginOptions.authType类型被限定为PickAuth<'api_key'>,见 packages/abstract/index.ts)。
关键背景:Abstract 的 API Key 是在控制台按产品分别签发的——一个能解锁 Email Reputation 的 Key 不一定能解锁 VAT 或 IBAN Validation。因此插件支持三级 Key 解析,优先级从高到低:
- 专用 Key(推荐):通过插件选项
emailReputationApiKey/vatApiKey/ibanApiKey显式配置,或通过密钥管理器存储的账户级字段(ctx.keys.get_email_reputation_api_key()、ctx.keys.get_vat_api_key()、ctx.keys.get_iban_api_key())读取; - 共享兜底 Key:插件选项
key,或密钥管理器中的基础api_key字段; - 均未配置时抛出
AuthMissingError('abstract', 'api_key')。
以email.validate为例,其 Key 解析顺序见 packages/abstract/endpoints/email-validation.ts:
const apiKey = ctx.options.emailReputationApiKey ?? (await tryGetStoredKey(() => ctx.keys?.get_email_reputation_api_key())) ?? ctx.key;对应的插件配置写法:
abstract({ key: 'SHARED_FALLBACK_KEY', // 可选:共享兜底 Key emailReputationApiKey: 'EMAIL_KEY', // 可选:每个产品各自的 Key vatApiKey: 'VAT_KEY', ibanApiKey: 'IBAN_KEY', })账户级字段(email_reputation_api_key、vat_api_key、iban_api_key)由abstractAuthConfig声明(见 packages/abstract/index.ts),Corsair 会在租户首次使用时提示填写。
tryGetStoredKey:处理"账户没有 DEK"的边界情况
tryGetStoredKey(见 packages/abstract/client.ts)是这套解析机制里的关键兜底:当账户完全没有配置 DEK(Data Encryption Key,即只通过插件选项配置专用 Key、从未使用过密钥管理器的账户)时,ctx.keys.get_*()会抛错而非返回null。该函数只匹配 Corsair 密钥管理器中"未找到 DEK"这条特定错误消息(/no dek found/i),将其视为"未配置存储 Key"返回undefined;而真正的解密失败、数据库故障等运维问题会原样向上抛出,避免把真实故障静默掩盖成"没配 Key",进而错误地回退到共享 Key 造成凭据串用。
另外,只有当同时配置了数据库与 KEK 时,Corsair 核心才会在上下文中挂载ctx.keys(相关逻辑位于 packages/corsair/core/client/index.ts),因此插件代码中对ctx.keys使用了可选链(?.)访问,保证无数据库/无 KEK 的纯选项式配置也能正常工作。
传输方式
Abstract 不接受 Bearer Token 或鉴权头,只接受以查询参数形式传递的api_key,且当前 4 个端点全部为 GET 请求。makeAbstractRequest(见 packages/abstract/client.ts)统一拼接:
GET https://emailreputation.abstractapi.com/v1?api_key=xxx&email=... GET https://vat.abstractapi.com/v1/categories?api_key=xxx&country_code=DE GET https://ibanvalidation.abstractapi.com/v1?api_key=xxx&iban=...事件日志中的数据脱敏
出于隐私考虑,插件在写入事件日志前会对敏感数据做脱敏(见 packages/abstract/client.ts):
redactEmail:保留邮箱首字符与域名,如s***@abstractapi.com;redactIban:保留国家代码与前 4 位…实际是保留前 2 位与国家代码、后 4 位,中间用*掩码。
由于 IBAN 直接标识具体银行账户,脱敏比邮箱更彻底。
错误处理与重试策略
插件内置了一套按 HTTP 状态码区分的错误处理器(见 packages/abstract/error-handlers.ts),可通过abstract({ errorHandlers: {...} })与默认处理器合并覆盖:
| 处理器 | 匹配条件 | 策略 |
|---|---|---|
RATE_LIMIT_ERROR | HTTP 429 或消息含429/rate limit | 最多重试 3 次,指数退避,尊重Retry-After响应头 |
AUTH_ERROR | HTTP 401 或消息含invalid api key/unauthorized/401 | 不重试;提示检查 Key 是否针对当前产品签发 |
QUOTA_ERROR | HTTP 422 且错误体含quota/exceeded/limit reached | 不重试;提示套餐配额耗尽(注意它在VALIDATION_ERROR之前匹配) |
VALIDATION_ERROR | HTTP 422(通用)或消息含422/unprocessable | 不重试;提示参数缺失或格式错误 |
SERVER_ERROR | HTTP 5xx | 最多重试 2 次,指数退避 |
DEFAULT | 兜底 | 不重试 |
有两个值得注意的实现细节:
- 422 的双重含义:在 Email Reputation 产品上,Abstract 在配额耗尽时返回的是 422(而非 429),因此
QUOTA_ERROR被排在VALIDATION_ERROR之前匹配,并专门从错误体的error.message/error.code中提取文本关键词判断(getErrorBodyText)。没有配额措辞的 422 才会落入通用的VALIDATION_ERROR。 - 错误体提取:由于 Corsair 请求层在构造错误消息时取的是
body?.message || body?.error || ...,而 Abstract 把信息嵌套在error对象下,error.message可能变成整个对象而非字符串。因此getErrorBodyText直接从AbstractAPIError.body中提取error.message与error.code拼接成可搜索文本。
所有 API 错误都会被包装成AbstractAPIError(见 packages/abstract/client.ts),它透传ApiError的status、statusText、body、retryAfter以及rateLimit*系列字段,供上层错误处理器检查,无需instanceof ApiError判断。
本地数据同步:4 个可检索实体
每次成功的 API 调用,插件都会把结果 upsert 进本地数据库(数据表 Schema 定义于 packages/abstract/schema/database.ts,由 packages/abstract/schema/index.ts 汇总为AbstractSchema)。写入失败只打印警告,不会让 API 调用本身失败(各端点实现中均有try { ... } catch { console.warn(...) })。
同步出的 4 个实体(见 docs/plugins/abstract/database.mdx):
| 实体路径 | 对应端点 | 主要字段 |
|---|---|---|
abstract.db.emailReputations | email.reputation | emailAddress、deliverabilityStatus、qualityScore、isFreeEmail、isDisposable、isCatchall、addressRiskStatus、domainRiskStatus |
abstract.db.emailValidations | email.validate | email、autocorrect、deliverability、qualityScore、isValidFormat、isFreeEmail、isDisposableEmail、isRoleEmail、isCatchallEmail、isMxFound、isSmtpValid |
abstract.db.ibanValidations | iban.validate | iban、isValid |
abstract.db.vatCategories | vat.getCategories | countryCode、category、description、rate |
检索示例
所有实体均支持search()与list():
// 找出所有投递状态为 "deliverable" 的邮箱记录 const rows = await corsair.abstract.db.emailReputations.search({ data: { deliverabilityStatus: { equals: 'deliverable' } }, limit: 100, offset: 0, }); // 列出最近 24 小时内的 IBAN 验证记录 const recent = await corsair.abstract.db.ibanValidations.search({ data: { checkedAt: { after: new Date(Date.now() - 24 * 60 * 60 * 1000) } }, });各实体可用的搜索操作符:字符串字段支持equals/contains/startsWith/endsWith/in,数字字段支持equals/gt/gte/lt/lte/in,布尔字段仅equals,日期字段支持equals/before/after/between。每个实体都可按entity_id(即邮箱地址、IBAN 或country_code:category组合)直接查询,天然去重——同一实体重复校验只会覆盖更新。分页通过limit/offset控制。更完整的操作符说明参见 docs/concepts/database.mdx。
Webhooks:无(纯拉取型 API)
Abstract 插件不提供任何 Webhook。README 明确标注 "No webhooks",源码中abstractWebhooksNested为空对象、pluginWebhookMatcher为undefined、webhookHooks为undefined(见 packages/abstract/index.ts)。Abstract 是纯拉取(pull-based)API——数据变化需要主动调用端点获取,Corsair 不会向你的应用推送事件。如果你的业务流程需要"邮箱被标记为风险时立即告警",需要通过定时任务调用email.reputation并检索本地同步结果来实现。
插件源码结构一览
如果希望深入阅读实现,可关注以下文件:
- packages/abstract/index.ts — 插件工厂
abstract()、插件选项类型、端点树(email.validate/email.reputation/vat.getCategories/iban.validate)、鉴权配置与风险元数据; - packages/abstract/client.ts — Abstract 各产品域名常量、
makeAbstractRequest请求封装、AbstractAPIError、脱敏函数与tryGetStoredKey; - packages/abstract/endpoints/types.ts — 四个端点的 Zod 输入/输出 Schema 与 TypeScript 类型;
- packages/abstract/endpoints/ — 每个端点的具体实现(含落库与事件日志);
- packages/abstract/schema/database.ts — 本地同步实体的存储 Schema;
- packages/abstract/error-handlers.ts — 默认错误处理器;
- packages/abstract/api.test.ts — 针对真实 Abstract API 的集成类型测试(需环境变量
ABSTRACT_EMAIL_REPUTATION_API_KEY/ABSTRACT_VAT_API_KEY/ABSTRACT_IBAN_VALIDATION_API_KEY,也支持共享的ABSTRACT_API_KEY兜底)。
测试用例验证了几个关键行为:免费邮箱(gmail.com)的is_free_email为true;无效邮箱的email_domain.domain、email_risk.address_risk_status为null;重度泄露地址(test@test.com)的total_breaches大于 0;VAT 分类对DE返回非空数组。这些测试也印证了上文提到的 Schema 可空字段设计与按产品拆分 Key 的约定(packages/abstract/api.test.ts)。
许可证与参考
- 插件以Apache-2.0许可证发布(见 packages/abstract/package.json);
- 更多用法与类型参考见 docs/plugins/abstract/api.mdx(完整 API 参考)与 docs/plugins/abstract/database.mdx(数据库检索参考);
- 若需把插件操作暴露为 MCP 工具给 Agent 使用,可参考 docs/mcp-adapters/mcp-adapters.mdx。
总体而言,@corsair-dev/abstract是一个典型的"薄封装 + 厚本地化"的 Corsair 插件:对外提供 4 个类型安全的只读校验操作,对内通过三级 Key 解析、精细的错误分类与重试策略、以及自动的本地数据同步,让业务方可以专注于"用校验结果做决策",而把凭证管理、API 调用细节与数据沉淀全部交给 Corsair。
【免费下载链接】corsairConnect your users to their apps项目地址: https://gitcode.com/GitHub_Trending/corsa/corsair
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考