# 公开 API

> publicApi()——会员卡核验、协会的公开资料、税号、公钥。无需凭据。

URL: https://developers.vimine.network/zh-hans/sdk/public/

```js
import { publicApi } from '@vimine/sdk';
const api = publicApi();                       // 或 vimine.public / vimineServer.public
```

既不需要凭据、也不需要登录的端点。可以在任何页面、扫码器或服务器上使用。`publicApi({ apiUrl?, fetch? })` 接受与其他类相同的网络选项。

| 方法 | 端点 | |
|---|---|---|
| `verifyCard(cardToken)` | `GET /v1/verify/{card_token}` | “这张会员卡现在有效吗？”——只返回状态和有效期截止时间 |
| `association(slug, { locale }?)` | `GET /v1/aps/{slug}` | 公开资料：品牌形象、会员卡类型及其会费、现行章程、协会如何接纳会员、是否收集会员地址（`member_address`） |
| `validateFiscalCode(input)` | `POST /v1/validate/fiscal-code` | 结构和校验码；提供个人信息时，还检查其一致性——并说明税号中包含什么（`birth_date`、`birth_country`）。对于在国外出生的人，请发送 `country` |
| `cardKeys()` | `GET /v1/card-keys` | 用于离线验证令牌的公钥（JWKS，Ed25519） |
| `confirmEnrollment(token, identification?, address?)` | `POST /v1/enroll/confirm` | 使用“确认您的申请”链接中的令牌——当某个系统为已有账户办理入会时会发送该链接；收到 `409 identification_required` 后带上 `identification` 重试，收到 `409 address_required` 后带上 `address` 重试 |
| `events.list(slug, params?)` · `events.get(slug, event, { locale }?)` | `GET /v1/aps/{slug}/events[/{event}]` | 协会的**公开**活动，附每种类型的剩余名额（[指南](https://developers.vimine.network/zh-hans/guides/events/)） |
| `confirmRegistration(token)` | `POST /v1/registrations/confirm` | 使用嘉宾“确认您的报名”链接中的令牌：返回该报名，确认后带有二维码 `token` |
| `viewRegistration(token)` | `POST /v1/registrations/view` | 读取嘉宾专属链接（确认名额的那封邮件中的链接）背后的名额：其状态，以及在已确认期间的二维码 `token`；在活动结束之前一直有效（自 0.3.2 起） |

```js
const { status, valid_until } = await api.verifyCard('vmt_…');
// { status: 'active', valid_until: '2027-03-31T22:59:59.000Z' } | { status: 'inactive', reason: 'expired' }

const { aps, membership_types, statuto } = await api.association('your-slug');

const check = await api.validateFiscalCode({ code: 'RSSMRA80A01H501U', first_name: 'Mario', last_name: 'Rossi', date: '1980-01-01', place: 'Roma', country: 'IT' });
// { valid: true, error: null, mismatches: [], hints: [], birth_date: '1980-01-01', birth_country: 'IT' }
// | { valid: false, error: 'mismatch', mismatches: ['date'], hints: [], birth_date: '1980-01-01', birth_country: 'IT' }

const { keys } = await api.cardKeys();   // [{ kty: 'OKP', crv: 'Ed25519', x, kid, use: 'sig', alg: 'EdDSA' }]

const { status, already, association } = await api.confirmEnrollment(token);   // 来自电子邮件中收到的链接
// { status: 'pending', already: false, association: { name, slug } } — 过期时返回 400 invalid_token，409 statuto_outdated

// 409 identification_required：该账户尚未识别身份；err.errors 列出缺少的内容。询问后重试：
await api.confirmEnrollment(token, { dob: '1985-03-10', birthplace: 'Lyon', birth_country: 'FR' });

// 409 address_required：协会要求每位会员提供地址。询问后重试（如已提供身份识别信息，一并带上）：
await api.confirmEnrollment(token, undefined, { line: 'Via Roma 1', postal_code: '20100', city: 'Milano', province: 'MI' });
```

税号是可选的：`error` 为 `invalid`、`mismatch` 或 `null`；当姓和名互换后税号能对上时，`hints` 中包含 `names_swapped`。`identification` 中的出生信息和地址都放在请求体中传送，绝不放在链接中。参见[没有意大利税号的会员](https://developers.vimine.network/zh-hans/guides/members-without-cf/)。

公钥很少变化：请缓存它们，每天重新加载一次，或在遇到未知的 `kid` 时重新加载。离线核验的其余内容见[嵌入与会员卡核验](https://developers.vimine.network/zh-hans/guides/embed-and-verify/)。

这些端点按 IP 限流（核验和税号接口每分钟 60 次请求）：如需更高的请求量，请使用带有 `verify:read` 的机密客户端。
