# 沒有義大利稅號的會員

> 識別一個人，可以透過稅號，也可以透過出生日期、出生地和出生國。在國外出生的人要傳送什麼，不一致的稅號如何確認，入場時要顯示什麼。

URL: https://developers.vimine.network/zh-hant/guides/members-without-cf/

加入協會**並不需要義大利稅號**（codice fiscale）。需要的是當事人的身份已被識別；在 Vimine 上，有兩種方式：

- 透過**稅號**，或者
- 透過**出生資訊**：`dob` + `birthplace` + `birth_country`（ISO 3166-1 alpha-2）。

兩者不能都沒有。本指南的其餘部分逐個欄位說明，對於為他人辦理入會的整合——交換生、剛到義大利的志工、住在國外的會員——有哪些變化。

## 識別一個人

`POST /v1/enroll` 和 `POST /v1/signup` 接受相同的欄位：

| 欄位 | |
|---|---|
| `codicefiscale` | 可選。提供時會進行驗證，並與姓名、出生日期和出生地比對 |
| `dob` | 出生日期，`YYYY-MM-DD` |
| `birthplace` | 在義大利出生的人填市鎮（comune）；在國外出生的人填城市，自由文字 |
| `birth_country` | 出生國，兩個字母（`IT`、`FR`、`SN`…） |
| `gender` | 僅用於統計：**不再**與稅號比對 |

```js
// 沒有稅號：出生日期、出生地和出生國
await vimineServer.enroll({
    email: 'marie.dupont@example.org',
    first_name: 'Marie',
    last_name: 'Dupont',
    dob: '1985-03-10',
    birthplace: 'Lyon',
    birth_country: 'FR',
    accept_statuto: { version: statuto.version, sha256: statuto.sha256 }
});
```

在 `/v1/enroll` 上，沒有稅號時，每缺少一項出生資訊，就傳回 `422 validation_failed`，並在 `errors[]` 中有一條記錄：

```json
{ "code": "validation_failed", "errors": [{ "field": "birth_country", "code": "required" }] }
```

`/v1/signup` 只建立帳戶：在這裡出生資訊仍是可選的，等當事人申請會員資格時才需要。

## 在國外出生：傳送 `birth_country`

在義大利出生的人，稅號中包含出生市鎮；在國外出生的人，稅號中**只包含國家**。因此，對後者的比對看的是 `birth_country`，並把 `birthplace`（城市）當作自由文字。傳送 `birth_country`，正是讓持有有效稅號、在國外出生的人通過驗證的關鍵：如果不傳送，“Lyon”會被當作義大利市鎮去查詢，稅號就會顯得不一致。（以前把國家的義大利語名稱當作出生地傳送的表單——`birthplace: 'Francia'`——仍然可以通過；但更推薦使用 `birth_country`。）

公開驗證介面也是如此，現在它還會說明稅號中包含什麼：

```js
const r = await publicApi().validateFiscalCode({
    code: 'DPNMRA85C50Z110U', first_name: 'Marie', last_name: 'Dupont', date: '1985-03-10', place: 'Lyon', country: 'FR'
});
// { valid: true, error: null, mismatches: [], hints: [], birth_date: '1985-03-10', birth_country: 'FR' }
```

`birth_date` 和 `birth_country` 是從稅號本身讀取的：您可以用它們預填表單。如果您傳送了 `gender`，這個端點仍會比對它；入會和個人資料則不會。

## 不一致的稅號與確認

輸錯的稅號（結構或檢查碼有誤）傳回 `invalid_codicefiscale`：必須更正，永遠不能確認。格式正確但與所填資訊**不一致**的稅號，則傳回 `422`，並帶有一條資訊更豐富的記錄：

```json
{
  "code": "validation_failed",
  "errors": [{
    "field": "codicefiscale",
    "code": "codicefiscale_mismatch",
    "mismatches": ["place"],
    "hints": [],
    "confirmable": true
  }]
}
```

- `mismatches`——哪些地方不一致：`lastName`、`firstName`、`date`、`place`；
- `hints`——當姓和名互換後稅號能對上時，為 `names_swapped`：通常提示對方互換即可；
- `confirmable`——當事人能否**確認**這就是其健保卡（tessera sanitaria）上的稅號。這種情況確實會發生：已撤銷的市鎮、音譯、以另一種順序登記的姓名。

當 `confirmable` 為 `true` 時，請詢問當事人，並帶上 `codicefiscale_confirmed: true` 重複請求。當姓、名**和**出生日期全都不一致時，就再也沒有什麼能把這個稅號和這個人聯繫起來：`confirmable` 為 `false`，唯一的辦法是更正。

```js
import { VimineError } from '@vimine/sdk/server';

try {
    await vimineServer.enroll(input);
} catch (err) {
    const entry = err instanceof VimineError && err.errors?.find((e) => e.code === 'codicefiscale_mismatch');
    if (!entry) throw err;
    if (entry.confirmable && (await askThePerson(entry.mismatches, entry.hints))) {
        await vimineServer.enroll({ ...input, codicefiscale_confirmed: true });
    }
}
```

不要自作主張傳送 `codicefiscale_confirmed`：這是當事人的聲明，並會作為聲明被記錄。

## 之後再補充稅號

入會時沒有稅號的人，在取得稅號後可以在自己的個人資料中補充：

```js
const me = await vimine.me.update({ codicefiscale: 'DPNMRA85C50Z110U' });
me.codicefiscale_check;   // 'matched'
```

`PATCH /v1/me` 接受 `birth_country` 和唯寫欄位 `codicefiscale_confirmed`；`codicefiscale` 也可以重新設為 `null`。`GET /v1/me` 說明當事人是如何被識別的：

| `codicefiscale_check` | |
|---|---|
| `matched` | 稅號與當事人的出生資訊一致 |
| `mismatch_confirmed` | 稅號不一致，但已由當事人確認 |
| `absent` | 沒有稅號：透過出生日期、出生地和出生國識別 |
| `null` | 尚未識別：不能申請會員資格 |

`matched` 的意思是**一致**，而不是已經過稅務登記機關核實：稅號始終是當事人申報的資訊。

## `identification_required`

帳戶可以存在但尚未識別身份——例如只為登入而註冊的人。當這樣的人申請會員資格時，API 會明確指出，並列出缺少什麼：

- **`POST /v1/me/membership`** → `422 identification_required`，`errors[]` 為缺少的出生資訊（`{ field: 'birthplace', code: 'required' }`…）。請引導當事人透過 `PATCH /v1/me`（權限 `profile:write`）完善個人資料，然後重複請求。
- **`POST /v1/enroll/confirm`**——已有帳戶的所有者收到的“確認您的申請”連結 → `409 identification_required`，`errors[]` 相同。請在頁面上詢問出生資訊，並帶上 `identification` 再次提交權杖：

```js
await publicApi().confirmEnrollment(token, { dob: '1985-03-10', birthplace: 'Lyon', birth_country: 'FR' });
// POST /v1/enroll/confirm  { token, identification: { dob, birthplace, birth_country, codicefiscale?, codicefiscale_confirmed? } }
```

出生資訊放在**請求本文中傳送，絕不放在連結中**：連結裡從來沒有任何個人資訊。`identification` 只補全個人資料中的空欄位，不會覆蓋已有內容；如果驗證不通過，回應就是上文那樣的 `422 validation_failed`。

入會審核不變：`pending_reasons` 對所有人都取決於協會的入會模式（參見[入會審核與生效](https://developers.vimine.network/zh-hant/guides/admission/)）。

## 入場時：身份核對

與出生資訊一致的稅號可以據此核對；僅有出生資訊，或由當事人確認的稅號，則無法核對。對於這些會員，協會會在當事人第一次到場時檢視**一次**證件。整合掃描器的開發者會在 `POST /v1/checkins`（權限 `checkins:write`）針對會員卡的回應中看到它：

```js
const r = await vimineServer.checkins.create('vmt_…');
// { kind: 'card', verdict: 'valid', …, identity_check: 'pending' }
if (r.identity_check === 'pending') show('首次入場：請查驗證件');
```

| `identity_check` | |
|---|---|
| `pending` | 該會員沒有可核實的稅號，協會中也還沒有人檢視過其證件：請把它**顯示給**入口值班人員 |
| `verified` | 協會中已有人檢視過證件 |
| `flagged` | 有人檢視過證件，但與資訊不符：由協會秘書處接手處理 |
| `null` | 無需核對 |

這只是一條**提示**，不屬於 `verdict`，也**不限制任何操作**：帶有 `identity_check: 'pending'` 的 `valid` 會員卡照樣可以入場。核對結果在協會的管理後臺中記錄，而不是透過 API。公開核驗介面 `GET /v1/verify/{card_token}` 從不攜帶它：從外部核驗會員卡的人只能看到狀態和到期日。

## 地址

有些協會會在會員登記冊中儲存會員的地址，有些則不會。這由協會決定，並且對**所有會員都適用，或對所有會員都不適用**；公開資料中會聲明：

```
GET https://api.vimine.network/v1/aps/{slug}
→ { "aps": { …, "member_address": "optional" } }
```

| `member_address` | |
|---|---|
| `none` | 不要詢問：即使傳送了 `address` 也不會儲存 |
| `optional` | 詢問，但不強制 |
| `required` | 沒有 `address` 時，入會傳回 `422`，並帶有 `{ field: 'address', code: 'required' }` |

```js
await vimineServer.enroll({
    …,
    address: { kind: 'residence', line: 'Via Roma 1', postal_code: '20121', city: 'Milano', province: 'MI', country: 'IT' }
});
```

`address` 中的 `line` 和 `city` 為必填；`kind` 為 `residence`（預設，戶籍地）或 `domicile`（住所地，與戶籍地不同時填寫）；省略 `country` 時為 `IT`；`province` 僅適用於義大利地址。`/v1/signup` 沒有 `address`：地址屬於會員資格，而不屬於帳戶。

## 協會不能強制要求稅號

不存在“稅號必填”這樣的設定。協會不能要求每位申請人都提供稅號：沒有稅號的人，在任何地方都透過出生資訊來識別。如果您的表單把稅號設為必填，那是您的表單的選擇——它會把協會本可以接納的人擋在門外。

## 變更內容（2026-09-19 版）

全部為新增：**始終傳送稅號的表單無需任何改動即可繼續使用**。現有整合可能會注意到：

- `POST /v1/enroll` 中的 `codicefiscale` 不再是必填項；沒有稅號時，需要 `dob`、`birthplace` 和 `birth_country`（每缺少一項傳回 `422` `required`）；
- 新的可選欄位 `birth_country`、`codicefiscale_confirmed`（enroll、signup、`PATCH /v1/me`）和 `address`（enroll）；
- `gender` 不再與稅號比對：不會再因為性別單獨傳回 `422`；
- 不一致的稅號仍傳回 `codicefiscale_mismatch`，現在在 `mismatches` 之外還帶有 `hints` 和 `confirmable`；格式錯誤的稅號仍是 `invalid_codicefiscale`；
- `GET`/`PATCH /v1/me` 現在有宣告的 schema（`Me`），包含 `birth_country` 和 `codicefiscale_check`；
- 新錯誤 `identification_required`：在 `POST /v1/me/membership` 上為 `422`，在 `POST /v1/enroll/confirm` 上為 `409`（後者在請求本文中接受 `identification`）；
- `POST /v1/validate/fiscal-code` 接受 `country`，並且還傳回 `hints`、`birth_date`、`birth_country`；`error` 為 `invalid`、`mismatch` 或 `null`；
- `POST /v1/checkins` 在針對會員卡的回應中增加了 `identity_check`；`GET /v1/verify/{card_token}` 保持不變；
- 公開資料增加了 `aps.member_address`。
