# VimineServer

> @vimine/sdk/server 中供機密用戶端使用的 VimineServer 類別的參考。

URL: https://developers.vimine.network/zh-hant/sdk/server/

```js
import { VimineServer } from '@vimine/sdk/server';
const vimineServer = new VimineServer({ clientId: 'vmc_…', clientSecret: process.env.VIMINE_SECRET });
```

用於協會**自己的**系統：一個透過 `client_credentials` 驗證的機密用戶端，讀取會員卡狀態並操作其沙箱。需要 Node ≥ 18。**secret 絕不能出現在瀏覽器中。**

## 選項

| 選項 | 型別 | 預設值 | |
|---|---|---|---|
| `clientId` | `string` | — | 必填 |
| `clientSecret` | `string` | — | 必填；`vms_…` 或 `vms_test_…` |
| `scopes` | `Scope[]` | 所有已登記的權限 | 要申請的子集 |
| `apiUrl` | `string` | `https://api.vimine.network` | |
| `fetch` | `typeof fetch` | `globalThis.fetch` | |

## 方法

### `token()`

用戶端存取權杖（`client_credentials`，HTTP Basic），快取至到期（1 小時）前 30 秒；即使被並行呼叫，也只發出一次請求。

### `request(path, init?)`

一次帶 `Authorization: Bearer`、經過驗證的呼叫；輸入輸出均為 JSON；錯誤以 [`VimineError`](https://developers.vimine.network/zh-hant/sdk/errors/) 拋出。收到 `401`（憑證已輪換或已撤銷）時，用新權杖重試一次。

### `memberships.status(id)`

協會某個會員資格的狀態，不含個人資料（`memberships:read`）：

```js
const s = await vimineServer.memberships.status('membership-uuid');
// { id, status: 'active', verdict: 'valid', valid_until: '2027-03-31T22:59:59.000Z', membership_type: { cod: 'ORD' } }
```

### `signup(input, opts?)`

為某人建立 Vimine 帳戶（`identity:signup`）：當事人會收到郵件，用於確認電子郵件地址並設定密碼。始終傳回 `{ status: 'accepted' }`——不會告訴您該地址是否已有帳戶（信箱的所有者會透過郵件得知）。`opts.idempotencyKey`（未提供時自動產生）。

```js
await vimineServer.signup({ email: 'mario.rossi@example.org', first_name: 'Mario', last_name: 'Rossi', phone: '+39 …' });
```

### `enroll(input, opts?)`

為某人提交入會申請（`enroll:write`）：新地址會獲得一個帳戶和一份*待處理*的申請；已有帳戶的所有者則會收到一個確認連結（在其使用連結之前不寫入任何內容）。`accept_statuto`（現行版本）為必填；當協會發布了給會員的隱私權聲明時（`association(slug).privacy_notice`），`privacy_notice` 也為必填：當事人在您的網站上閱讀後，您傳入所展示的版本。參見[伺服器整合](https://developers.vimine.network/zh-hant/guides/server/#從您的系統為會員辦理入會)。

識別當事人，要麼透過 `codicefiscale`，**要麼**透過 `dob` + `birthplace` + `birth_country`——兩者不能都沒有（`422`，每缺少一項出生資訊就有一條 `required`）。稅號與出生資訊不一致時，`err.errors` 中會有一條 `codicefiscale_mismatch` 記錄，帶有 `mismatches`、`hints` 和 `confirmable`：當 `confirmable` 為 `true` 且當事人確認時，請帶上 `codicefiscale_confirmed: true` 重複呼叫。如果協會登記會員的地址（`aps.member_address`），請傳入 `address`（`MemberAddress`）。參見[沒有義大利稅號的會員](https://developers.vimine.network/zh-hant/guides/members-without-cf/)。

```js
// 有稅號
await vimineServer.enroll({ ...person, codicefiscale: 'RSSMRA80A01H501U', accept_statuto: { version: 3 }, privacy_notice: { version: 2 } }, { idempotencyKey: 'order-42' });
// 沒有稅號：出生日期、出生地和出生國
await vimineServer.enroll({ ...person, dob: '1985-03-10', birthplace: 'Lyon', birth_country: 'FR', accept_statuto: { version: 3 }, privacy_notice: { version: 2 } });
```

### `checkins.create(token)`

用從 QR Code 中讀取的權杖記錄一次入場登記（`checkins:write`）。會員卡傳回 `{ kind: 'card', verdict, status, membership_id, valid_until, valid_from, identity_check, checked_in_at }`——`identity_check: 'pending'` 是需要顯示給入口值班人員的提示，它不屬於 `verdict`，也不限制任何操作（[指南](https://developers.vimine.network/zh-hant/guides/members-without-cf/#入場時身份核對)）；活動報名的 QR Code 傳回 `{ kind: 'registration', verdict, registration_id, event, first, membership_verdict, … }`（參見[活動與報名](https://developers.vimine.network/zh-hant/guides/events/#入場時)）。

### `events`

`events.list(params?)` · `events.get(slug)`——協會已發布的活動，包括僅限會員的活動（任何用戶端權杖均可）。

### `registrations`

| | |
|---|---|
| `registrations.listForEvent(slug, { status?, limit?, cursor? })` | 某個活動的報名：id、狀態、類型、會員資格、入場登記——不含個人資料（`registrations:read`，適用於支援 API 清單的方案） |
| `registrations.guest(slug, { email, first_name, last_name, registration_type, note? }, opts?)` | 為沒有帳戶的**嘉賓**報名公開活動；始終傳回 `{ status: 'accepted' }`；自動產生 `Idempotency-Key`（參見[嘉賓](https://developers.vimine.network/zh-hant/guides/events/#嘉賓公開活動)） |

### `memberships.list(params?)`

協會的會員資格清單（`memberships:read`，適用於支援清單的方案）：`{ items: [{ id, status, verdict, membership_type, issued_at, valid_until, updated_at }], next_cursor }`；`params`：`status`、`updated_since`、`limit`、`cursor`。

### `sandbox.members()` · `sandbox.reset()`

僅限測試用戶端：[沙箱](https://developers.vimine.network/zh-hant/guides/sandbox/)中的虛構會員，以及重設（新的共用密碼，只傳回一次）。生產用戶端會得到 `403`。

### `public`

無需憑證的公開端點：參見 [`publicApi`](https://developers.vimine.network/zh-hant/sdk/public/)。

## 中介軟體範例

```js title="express.js"
import express from 'express';
import { VimineServer, VimineError } from '@vimine/sdk/server';

const vimineServer = new VimineServer({ clientId: process.env.VIMINE_CLIENT_ID, clientSecret: process.env.VIMINE_SECRET });
const app = express();

app.get('/cards/:id/status', async (req, res) => {
    try {
        const { status, verdict, valid_until } = await vimineServer.memberships.status(req.params.id);
        res.json({ status, verdict, valid_until });
    } catch (err) {
        if (err instanceof VimineError && err.code === 'not_found') return res.status(404).end();
        throw err;
    }
});
```
