# VimineServer

> Riferimento della classe VimineServer di @vimine/sdk/server per i client riservati.

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

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

Per i sistemi **dell'associazione**: un client riservato che si autentica con `client_credentials`, legge lo
stato delle tessere e guida la sua sandbox. Node ≥ 18. **Il secret non deve mai raggiungere un browser.**

## Opzioni

| Opzione | Tipo | Default | |
|---|---|---|---|
| `clientId` | `string` | — | obbligatorio |
| `clientSecret` | `string` | — | obbligatorio; `vms_…` o `vms_test_…` |
| `scopes` | `Scope[]` | tutti quelli registrati | un sottoinsieme da chiedere |
| `apiUrl` | `string` | `https://api.vimine.network` | |
| `fetch` | `typeof fetch` | `globalThis.fetch` | |

## Metodi

### `token()`

Un access token del client (`client_credentials`, HTTP Basic), messo in cache fino a 30 secondi prima della
scadenza (1 ora); una sola richiesta anche con chiamate concorrenti.

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

Una chiamata autenticata con `Authorization: Bearer`; JSON in entrata e in uscita; errori come
[`VimineError`](https://developers.vimine.network/sdk/errors/). Una risposta `401` (credenziali ruotate o revocate) viene ritentata una volta
con un token nuovo.

### `memberships.status(id)`

Stato di una tessera dell'associazione, senza dati personali (`memberships:read`):

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

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

Crea un account Vimine per una persona (`identity:signup`): riceverà la mail per confermare l'indirizzo e
scegliere la password. Risponde sempre `{ status: 'accepted' }` — se l'indirizzo aveva già un account non ti
viene detto (lo sa il titolare, per posta). `opts.idempotencyKey` (generata se assente).

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

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

Invia una richiesta di tesseramento per una persona (`enroll:write`): un indirizzo nuovo ottiene account e richiesta
*in attesa*; il titolare di un account esistente riceve invece un link di conferma (non viene scritto nulla finché
non lo usa). `accept_statuto` (versione in vigore) è obbligatorio; `privacy_notice` lo è quando
l'associazione pubblica un'informativa ai soci (`association(slug).privacy_notice`): la persona la legge sul tuo sito e
tu passi la versione mostrata. Vedi [Integrazioni server](https://developers.vimine.network/guides/server/#registrare-soci-dal-tuo-sistema).

La persona si identifica con `codicefiscale` **oppure** con `dob` + `birthplace` + `birth_country` — mai nessuno dei
due (`422`, voce `required` su ogni dato mancante). Un codice che non concorda con i dati è una voce
`codicefiscale_mismatch` in `err.errors`, con `mismatches`, `hints` e `confirmable`: se `confirmable` è `true` e la
persona conferma, ripeti con `codicefiscale_confirmed: true`. `address` (`MemberAddress`) quando l'associazione
raccoglie l'indirizzo dei soci (`aps.member_address`). Vedi [Soci senza codice fiscale](https://developers.vimine.network/guides/members-without-cf/).

```js
// con un codice fiscale
await vimineServer.enroll({ ...persona, codicefiscale: 'RSSMRA80A01H501U', accept_statuto: { version: 3 }, privacy_notice: { version: 2 } }, { idempotencyKey: 'ordine-42' });
// senza: data, luogo e Stato di nascita
await vimineServer.enroll({ ...persona, dob: '1985-03-10', birthplace: 'Lyon', birth_country: 'FR', accept_statuto: { version: 3 }, privacy_notice: { version: 2 } });
```

### `checkins.create(token)`

Registra un accesso dal token letto sul QR (`checkins:write`). Una tessera risponde `{ kind: 'card', verdict, status, membership_id, valid_until, valid_from, identity_check, checked_in_at }` — `identity_check: 'pending'` è una nota da mostrare a chi è all'ingresso, non fa parte di `verdict` e non limita nulla ([guida](https://developers.vimine.network/guides/members-without-cf/#allingresso-il-controllo-didentità)); il QR di un'iscrizione a un evento risponde `{ kind: 'registration', verdict, registration_id, event, first, membership_verdict, … }` (vedi [Eventi e iscrizioni](https://developers.vimine.network/guides/events/#allingresso)).

### `events`

`events.list(params?)` · `events.get(slug)` — gli eventi pubblicati dell'associazione, compresi quelli riservati ai soci (qualsiasi token del client).

### `registrations`

| | |
|---|---|
| `registrations.listForEvent(slug, { status?, limit?, cursor? })` | le iscrizioni di un evento: id, stato, tipo, tessera, ingresso — senza dati personali (`registrations:read`, piani con gli elenchi via API) |
| `registrations.guest(slug, { email, first_name, last_name, registration_type, note? }, opts?)` | iscrive un **ospite** senza account a un evento pubblico; sempre `{ status: 'accepted' }`; `Idempotency-Key` generata (vedi [ospiti](https://developers.vimine.network/guides/events/#ospiti-eventi-pubblici)) |

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

Elenco delle tessere dell'associazione (`memberships:read`, piani con gli elenchi): `{ items: [{ id, status, verdict, membership_type, issued_at, valid_until, updated_at }], next_cursor }`; `params`: `status`, `updated_since`, `limit`, `cursor`.

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

Solo per i client di test: i soci sintetici della [sandbox](https://developers.vimine.network/guides/sandbox/) e il reset (nuova password
condivisa, restituita una volta). Un client di produzione riceve `403`.

### `public`

Gli endpoint pubblici, senza credenziali: vedi [`publicApi`](https://developers.vimine.network/sdk/public/).

## Un middleware di esempio

```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('/tessere/:id/stato', 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;
    }
});
```
