# VimineServer

> Référence de la classe VimineServer de @vimine/sdk/server pour les clients confidentiels.

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

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

Pour les systèmes **propres** à l’association : un client confidentiel qui s’authentifie avec `client_credentials`,
lit l’état des cartes et pilote sa sandbox. Node ≥ 18. **Le secret ne doit jamais atteindre un navigateur.**

## Options

| Option | Type | Par défaut | |
|---|---|---|---|
| `clientId` | `string` | — | obligatoire |
| `clientSecret` | `string` | — | obligatoire ; `vms_…` ou `vms_test_…` |
| `scopes` | `Scope[]` | tous ceux enregistrés | un sous-ensemble à demander |
| `apiUrl` | `string` | `https://api.vimine.network` | |
| `fetch` | `typeof fetch` | `globalThis.fetch` | |

## Méthodes

### `token()`

Un jeton d’accès client (`client_credentials`, HTTP Basic), mis en cache jusqu’à 30 secondes avant son expiration
(1 heure) ; une seule requête même en cas d’appels concurrents.

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

Un appel authentifié avec `Authorization: Bearer` ; JSON en entrée et en sortie ; erreurs sous forme de
[`VimineError`](https://developers.vimine.network/fr/sdk/errors/). Un `401` (identifiants régénérés ou révoqués) est réessayé une fois avec un nouveau
jeton.

### `memberships.status(id)`

État d’une adhésion de l’association, sans données personnelles (`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?)`

Crée un compte Vimine pour une personne (`identity:signup`) : elle reçoit l’e-mail pour confirmer son adresse et
choisir un mot de passe. Répond toujours `{ status: 'accepted' }` — que l’adresse ait déjà eu un compte ne vous est pas
révélé (son titulaire en est informé, par e-mail). `opts.idempotencyKey` (générée si elle est absente).

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

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

Envoie une demande d’adhésion pour une personne (`enroll:write`) : une nouvelle adresse obtient un compte et une
demande *en attente* ; le titulaire d’un compte existant reçoit plutôt un lien de confirmation (rien n’est écrit tant
qu’il ne l’utilise pas). `accept_statuto` (la version en vigueur) est obligatoire ; `privacy_notice` aussi quand
l’association publie une notice d’information à ses membres (`association(slug).privacy_notice`) : la personne la lit
sur votre site et vous passez la version affichée. Voir [Intégrations serveur](https://developers.vimine.network/fr/guides/server/#faire-adhérer-des-membres-depuis-votre-système).

La personne est identifiée par `codicefiscale` **ou** par `dob` + `birthplace` + `birth_country` — jamais ni l’un ni
l’autre (`422`, une entrée `required` pour chaque donnée manquante). Un code en désaccord avec les données de naissance
est une entrée `codicefiscale_mismatch` dans `err.errors`, avec `mismatches`, `hints` et `confirmable` : quand
`confirmable` vaut `true` et que la personne confirme, répétez avec `codicefiscale_confirmed: true`. `address`
(`MemberAddress`) quand l’association recueille l’adresse de ses membres (`aps.member_address`). Voir
[Membres sans code fiscal](https://developers.vimine.network/fr/guides/members-without-cf/).

```js
// avec un code fiscal
await vimineServer.enroll({ ...person, codicefiscale: 'RSSMRA80A01H501U', accept_statuto: { version: 3 }, privacy_notice: { version: 2 } }, { idempotencyKey: 'order-42' });
// sans : date, lieu et pays de naissance
await vimineServer.enroll({ ...person, dob: '1985-03-10', birthplace: 'Lyon', birth_country: 'FR', accept_statuto: { version: 3 }, privacy_notice: { version: 2 } });
```

### `checkins.create(token)`

Enregistre un passage à l’entrée à partir du jeton lu sur le QR (`checkins:write`). Une carte répond `{ kind: 'card', verdict, status, membership_id, valid_until, valid_from, identity_check, checked_in_at }` — `identity_check: 'pending'` est une indication à afficher à la personne qui tient l’entrée, elle ne fait pas partie de `verdict` et ne restreint rien ([guide](https://developers.vimine.network/fr/guides/members-without-cf/#à-lentrée-le-contrôle-didentité)) ; le QR d’une inscription à un événement répond `{ kind: 'registration', verdict, registration_id, event, first, membership_verdict, … }` (voir [Événements et inscriptions](https://developers.vimine.network/fr/guides/events/#à-lentrée)).

### `events`

`events.list(params?)` · `events.get(slug)` — les événements publiés de l’association, y compris ceux réservés aux membres (n’importe quel jeton client).

### `registrations`

| | |
|---|---|
| `registrations.listForEvent(slug, { status?, limit?, cursor? })` | les inscriptions d’un événement : identifiant, état, type, adhésion, entrée — sans données personnelles (`registrations:read`, formules avec listes via l’API) |
| `registrations.guest(slug, { email, first_name, last_name, registration_type, note? }, opts?)` | inscrit un **invité** sans compte à un événement public ; toujours `{ status: 'accepted' }` ; `Idempotency-Key` générée (voir [invités](https://developers.vimine.network/fr/guides/events/#invités-événements-publics)) |

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

Les adhésions de l’association (`memberships:read`, formules avec listes) : `{ items: [{ id, status, verdict, membership_type, issued_at, valid_until, updated_at }], next_cursor }` ; `params` : `status`, `updated_since`, `limit`, `cursor`.

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

Clients de test seulement : les membres synthétiques de la [sandbox](https://developers.vimine.network/fr/guides/sandbox/) et la réinitialisation
(nouveau mot de passe commun, renvoyé une fois). Un client de production reçoit `403`.

### `public`

Les endpoints publics, sans identifiants : voir [`publicApi`](https://developers.vimine.network/fr/sdk/public/).

## Un exemple de 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;
    }
});
```
