# VimineServer

> Referencia de la clase VimineServer de @vimine/sdk/server para los clientes confidenciales.

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

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

Para los sistemas **propios** de la asociación: un cliente confidencial que se autentica con `client_credentials`,
consulta el estado de los carnés y gestiona su sandbox. Node ≥ 18. **El secreto nunca debe llegar a un navegador.**

## Opciones

| Opción | Tipo | Por defecto | |
|---|---|---|---|
| `clientId` | `string` | — | obligatorio |
| `clientSecret` | `string` | — | obligatorio; `vms_…` o `vms_test_…` |
| `scopes` | `Scope[]` | todos los registrados | un subconjunto que solicitar |
| `apiUrl` | `string` | `https://api.vimine.network` | |
| `fetch` | `typeof fetch` | `globalThis.fetch` | |

## Métodos

### `token()`

Un token de acceso de cliente (`client_credentials`, HTTP Basic), en caché hasta 30 segundos antes de que caduque
(1 hora); una sola petición aunque haya llamadas concurrentes.

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

Una llamada autenticada con `Authorization: Bearer`; JSON de entrada y de salida; errores como
[`VimineError`](https://developers.vimine.network/es/sdk/errors/). Un `401` (credenciales regeneradas o revocadas) se repite una vez con un token nuevo.

### `memberships.status(id)`

Estado de una inscripción de la asociación, sin datos personales (`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?)`

Crea una cuenta de Vimine para una persona (`identity:signup`): recibe el correo para confirmar su dirección y elegir
una contraseña. Responde siempre `{ status: 'accepted' }` — no se te revela si la dirección ya tenía una cuenta (se le
avisa a su titular, por correo). `opts.idempotencyKey` (se genera si falta).

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

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

Envía una solicitud de inscripción para una persona (`enroll:write`): una dirección nueva recibe una cuenta y una
solicitud *pendiente*; el titular de una cuenta existente recibe en cambio un enlace de confirmación (no se escribe
nada hasta que lo usa). `accept_statuto` (la versión en vigor) es obligatorio; también lo es `privacy_notice` cuando
la asociación publica una política de privacidad para sus socios (`association(slug).privacy_notice`): la persona la
lee en tu sitio y tú pasas la versión mostrada. Consulta
[Integraciones de servidor](https://developers.vimine.network/es/guides/server/#inscribir-socios-desde-tu-sistema).

La persona se identifica con `codicefiscale` **o** con `dob` + `birthplace` + `birth_country` — nunca sin ninguno de
los dos (`422`, una entrada `required` por cada dato de nacimiento que falte). Un código que no coincide con los datos
de nacimiento es una entrada `codicefiscale_mismatch` en `err.errors`, con `mismatches`, `hints` y `confirmable`:
cuando `confirmable` es `true` y la persona lo confirma, repite con `codicefiscale_confirmed: true`. `address`
(`MemberAddress`) cuando la asociación recoge la dirección de sus socios (`aps.member_address`). Consulta
[Socios sin código fiscal](https://developers.vimine.network/es/guides/members-without-cf/).

```js
// con código fiscal
await vimineServer.enroll({ ...person, codicefiscale: 'RSSMRA80A01H501U', accept_statuto: { version: 3 }, privacy_notice: { version: 2 } }, { idempotencyKey: 'order-42' });
// sin él: fecha, lugar y país de nacimiento
await vimineServer.enroll({ ...person, dob: '1985-03-10', birthplace: 'Lyon', birth_country: 'FR', accept_statuto: { version: 3 }, privacy_notice: { version: 2 } });
```

### `checkins.create(token)`

Registra un check-in en la entrada a partir del token leído en el QR (`checkins:write`). Un carné responde `{ kind: 'card', verdict, status, membership_id, valid_until, valid_from, identity_check, checked_in_at }` — `identity_check: 'pending'` es una nota que hay que mostrar a quien atiende la entrada, no forma parte de `verdict` y no restringe nada ([guía](https://developers.vimine.network/es/guides/members-without-cf/#en-la-entrada-la-comprobación-de-identidad)); el QR de una inscripción a un evento responde `{ kind: 'registration', verdict, registration_id, event, first, membership_verdict, … }` (consulta [Eventos e inscripciones](https://developers.vimine.network/es/guides/events/#en-la-entrada)).

### `events`

`events.list(params?)` · `events.get(slug)` — los eventos publicados de la asociación, incluidos los que son solo para socios (cualquier token de cliente).

### `registrations`

| | |
|---|---|
| `registrations.listForEvent(slug, { status?, limit?, cursor? })` | las inscripciones de un evento: id, estado, tipo, condición de socio, check-in — sin datos personales (`registrations:read`, planes con listas por API) |
| `registrations.guest(slug, { email, first_name, last_name, registration_type, note? }, opts?)` | inscribe a un **invitado** sin cuenta en un evento público; siempre `{ status: 'accepted' }`; `Idempotency-Key` generada (consulta [invitados](https://developers.vimine.network/es/guides/events/#invitados-eventos-públicos)) |

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

Las inscripciones de la asociación (`memberships:read`, planes con listas): `{ 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 para clientes de prueba: los socios sintéticos del [sandbox](https://developers.vimine.network/es/guides/sandbox/) y el restablecimiento
(contraseña común nueva, devuelta una sola vez). Un cliente de producción recibe `403`.

### `public`

Los endpoints públicos, sin credenciales: consulta [`publicApi`](https://developers.vimine.network/es/sdk/public/).

## Un middleware de ejemplo

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