# VimineServer

> Referenz der Klasse VimineServer aus @vimine/sdk/server für vertrauliche Clients.

URL: https://developers.vimine.network/de/sdk/server/

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

Für die **eigenen** Systeme des Vereins: ein vertraulicher Client, der sich mit `client_credentials` authentifiziert,
Ausweisstatus liest und seine Sandbox steuert. Node ≥ 18. **Das Secret darf nie in einen Browser gelangen.**

## Optionen

| Option | Typ | Standard | |
|---|---|---|---|
| `clientId` | `string` | — | Pflicht |
| `clientSecret` | `string` | — | Pflicht; `vms_…` oder `vms_test_…` |
| `scopes` | `Scope[]` | alle registrierten | eine anzufordernde Teilmenge |
| `apiUrl` | `string` | `https://api.vimine.network` | |
| `fetch` | `typeof fetch` | `globalThis.fetch` | |

## Methoden

### `token()`

Ein Client-Access-Token (`client_credentials`, HTTP Basic), zwischengespeichert bis 30 Sekunden vor seinem Ablauf (1 Stunde);
eine einzige Anfrage, auch bei gleichzeitigen Aufrufen.

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

Ein authentifizierter Aufruf mit `Authorization: Bearer`; JSON hin und zurück; Fehler als
[`VimineError`](https://developers.vimine.network/de/sdk/errors/). Ein `401` (rotierte oder widerrufene Zugangsdaten) wird einmal mit einem frischen Token wiederholt.

### `memberships.status(id)`

Status einer Mitgliedschaft des Vereins, ohne personenbezogene Daten (`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?)`

Legt ein Vimine-Konto für eine Person an (`identity:signup`): Sie erhält die E-Mail, um die Adresse zu bestätigen und ein
Passwort zu wählen. Antwortet immer `{ status: 'accepted' }` — ob die Adresse schon ein Konto hatte, wird Ihnen nicht verraten
(ihr Inhaber erfährt es, per E-Mail). `opts.idempotencyKey` (wird erzeugt, wenn er fehlt).

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

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

Stellt einen Beitrittsantrag für eine Person (`enroll:write`): Eine neue Adresse erhält ein Konto und einen *ausstehenden* Antrag;
der Inhaber eines bestehenden Kontos erhält stattdessen einen Bestätigungslink (nichts wird geschrieben, bis er ihn verwendet).
`accept_statuto` (die geltende Version) ist Pflicht; ebenso `privacy_notice`, wenn der Verein
Datenschutzhinweise für seine Mitglieder veröffentlicht (`association(slug).privacy_notice`): Die Person liest sie auf Ihrer Website, und Sie übergeben
die gezeigte Version. Siehe [Server-Integrationen](https://developers.vimine.network/de/guides/server/#beitritte-aus-ihrem-system).

Die Person wird über `codicefiscale` **oder** über `dob` + `birthplace` + `birth_country` identifiziert — nie über keins von beidem (`422`,
ein Eintrag `required` für jede fehlende Geburtsangabe). Eine Steuernummer, die nicht zu den Geburtsdaten passt, ist ein
Eintrag `codicefiscale_mismatch` in `err.errors`, mit `mismatches`, `hints` und `confirmable`: Ist `confirmable` gleich
`true` und bestätigt die Person, wiederholen Sie mit `codicefiscale_confirmed: true`. `address` (`MemberAddress`), wenn der
Verein die Adresse seiner Mitglieder erfasst (`aps.member_address`). Siehe
[Mitglieder ohne Steuernummer](https://developers.vimine.network/de/guides/members-without-cf/).

```js
// mit einer Steuernummer
await vimineServer.enroll({ ...person, codicefiscale: 'RSSMRA80A01H501U', accept_statuto: { version: 3 }, privacy_notice: { version: 2 } }, { idempotencyKey: 'order-42' });
// ohne: Geburtsdatum, Geburtsort und Geburtsland
await vimineServer.enroll({ ...person, dob: '1985-03-10', birthplace: 'Lyon', birth_country: 'FR', accept_statuto: { version: 3 }, privacy_notice: { version: 2 } });
```

### `checkins.create(token)`

Erfasst einen Check-in am Einlass aus dem vom QR-Code gelesenen Token (`checkins:write`). Ein Ausweis antwortet `{ kind: 'card', verdict, status, membership_id, valid_until, valid_from, identity_check, checked_in_at }` — `identity_check: 'pending'` ist ein Hinweis für die Person am Einlass, nicht Teil von `verdict`, und schränkt nichts ein ([Anleitung](https://developers.vimine.network/de/guides/members-without-cf/#am-einlass-die-identitätsprüfung)); der QR-Code einer Veranstaltungsanmeldung antwortet `{ kind: 'registration', verdict, registration_id, event, first, membership_verdict, … }` (siehe [Veranstaltungen und Anmeldungen](https://developers.vimine.network/de/guides/events/#am-einlass)).

### `events`

`events.list(params?)` · `events.get(slug)` — die veröffentlichten Veranstaltungen des Vereins, einschließlich derer nur für Mitglieder (jedes Client-Token).

### `registrations`

| | |
|---|---|
| `registrations.listForEvent(slug, { status?, limit?, cursor? })` | die Anmeldungen einer Veranstaltung: ID, Status, Art, Mitgliedschaft, Check-in — keine personenbezogenen Daten (`registrations:read`, Tarife mit API-Listen) |
| `registrations.guest(slug, { email, first_name, last_name, registration_type, note? }, opts?)` | meldet einen **Gast** ohne Konto zu einer öffentlichen Veranstaltung an; immer `{ status: 'accepted' }`; `Idempotency-Key` wird erzeugt (siehe [Gäste](https://developers.vimine.network/de/guides/events/#gäste-öffentliche-veranstaltungen)) |

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

Die Mitgliedschaften des Vereins (`memberships:read`, Tarife mit Listen): `{ items: [{ id, status, verdict, membership_type, issued_at, valid_until, updated_at }], next_cursor }`; `params`: `status`, `updated_since`, `limit`, `cursor`.

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

Nur für Test-Clients: die synthetischen Mitglieder der [Sandbox](https://developers.vimine.network/de/guides/sandbox/) und das Zurücksetzen (neues gemeinsames
Passwort, einmal zurückgegeben). Ein Production-Client erhält `403`.

### `public`

Die öffentlichen Endpunkte ohne Zugangsdaten: siehe [`publicApi`](https://developers.vimine.network/de/sdk/public/).

## Ein Beispiel-Middleware

```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;
    }
});
```
