# Eventi e iscrizioni

> Pubblica gli eventi dell'associazione sul tuo sito, fai iscrivere i soci (e gli ospiti agli eventi pubblici), registra gli accessi all'ingresso con lo stesso scanner delle tessere.

URL: https://developers.vimine.network/guides/events/

Gli eventi che l'associazione pubblica dalla console sono disponibili sull'API con lo stesso principio delle
tessere: **copia «headless»** (titolo, sommario, descrizione in markdown, immagini, luogo, date, link) e
**disponibilità in tempo reale** per ogni tipo di iscrizione — mai *chi* è iscritto. Il vocabolario è quello
del Terzo settore: si parla di **iscrizione** e, quando c'è un importo, di **contributo**.

:::note[Prima di iniziare]
Il tuo client deve avere i permessi `events:read`, `registrations:read` e `registrations:write` (vedi
[Permessi, errori e limiti](https://developers.vimine.network/guides/scopes-and-errors/)). Per provare senza toccare dati reali usa la
[sandbox](https://developers.vimine.network/guides/sandbox/): l'associazione gemella può pubblicare eventi come quella vera.
:::

## Due visibilità

| `visibility` | Chi la vede | Chi si iscrive |
|---|---|---|
| `members_only` (predefinita) | solo con un token del socio o del client dell'associazione | i soci con tessera valida |
| `public` | chiunque, anche senza accesso: `GET /v1/aps/{slug}/events` | i soci e — se il tipo di iscrizione non richiede la tessera — gli **ospiti** |

Un evento pubblico mostra ai visitatori del tuo sito la locandina e i tipi di iscrizione con i posti
rimanenti; gli eventi riservati compaiono solo dopo l'accesso.

## Mostrare gli eventi

```js title="pagina pubblica, senza accesso"
import { publicApi } from '@vimine/sdk';
const api = publicApi();
const { items } = await api.events.list('il-tuo-slug', { filter: 'upcoming' });
const evento = await api.events.get('il-tuo-slug', 'serata-jazz');
```

```js title="dopo Accedi con Vimine (anche gli eventi riservati ai soci)"
const { items } = await vimine.events.list();           // events:read
const evento = await vimine.events.get('serata-jazz');
```

Ogni evento porta `registration_types[]`: `amount` (0 = gratuita), `currency`, `remaining` (posti rimasti,
`null` = illimitati), `open` e — quando non è aperto — `reason` (`sold_out`, `registration_not_open`,
`registration_closed`). `status` vale `published`, `completed` (la data è passata) o `cancelled`.

Senza scrivere codice: l'elemento [`<vimine-events>`](https://developers.vimine.network/sdk/elements/) rende la lista con i pulsanti di
iscrizione.

## Chi non è ancora socio

Un evento pubblico arriva anche a chi non è socio: è il momento in cui una persona decide di diventarlo. La
pagina di iscrizione dell'associazione su Vimine si apre **con l'evento**, e la persona aderisce e prenota in
un solo passaggio:

```
https://app.vimine.network/enroll/il-tuo-slug?event=serata-jazz
```

```js title="lo stesso indirizzo, dal tuo codice"
import { eventEnrollUrl } from '@vimine/sdk/elements';
const url = eventEnrollUrl('https://app.vimine.network', 'il-tuo-slug', 'serata-jazz', 'it');
```

Che cosa succede al posto dipende da dove arriva l'adesione, e la pagina lo dice alla persona:

| L'adesione | Il posto |
|---|---|
| è attiva subito (tessera gratuita, nessuna approvazione) | **confermato**, se l'iscrizione all'evento è gratuita |
| aspetta l'approvazione o la quota | per un'iscrizione riservata ai soci: **riservato** finché la tessera non è attiva, e a quel punto, se è gratuita, si conferma da sola. Per un'iscrizione aperta a tutti: confermato subito, come per chi non è socio |
| l'iscrizione all'evento prevede un contributo | **riservato** fino al termine che l'associazione ha fissato: si conferma quando registra il contributo |
| l'indirizzo e-mail non è ancora confermato | nulla è riservato: il posto viene chiesto quando la persona conferma l'indirizzo dal link nella mail, e una mail le dice com'è andata |

Chi è già socio usa la stessa pagina: accede e prenota con un pulsante, senza compilare nulla
(`&signin=1` apre subito l'accesso). Vale solo per gli eventi `public` pubblicati e non ancora passati: con
qualunque altro slug la pagina è la normale pagina di iscrizione.

La pagina pubblica dell'evento su Vimine (`/agenda/il-tuo-slug/serata-jazz`) e `<vimine-events>` propongono
già questa strada ai visitatori: *Iscriviti all'associazione e prenota*.

## Far iscrivere un socio

Prima di mostrare il pulsante chiedi all'API se il socio può iscriversi — la risposta spiega perché no:

```js
const { can_register, types } = await vimine.events.eligibility('serata-jazz');
// types[i].reasons: membership_required, membership_not_active, membership_not_yet_valid, sold_out, max_per_user, registration_closed, …
```

Poi iscrivilo (`registrations:write`):

```js
const reg = await vimine.registrations.create('serata-jazz', { registration_type: types[0].uid, note: 'Vengo con mia sorella' });
if (reg.status === 'confirmed') drawQr(reg.token);           // iscrizione gratuita: posto confermato, QR pronto
if (reg.status === 'reserved') show(reg.payment_instructions); // con contributo: posto riservato fino al versamento
```

- Un tipo **gratuito** conferma subito e restituisce `token`: è il contenuto del QR che il socio mostra
  all'ingresso (disegnalo tu, con la libreria che preferisci).
- Un tipo **con contributo** riserva il posto (`reserved_until`) e restituisce le istruzioni di pagamento
  dell'associazione; la segreteria conferma quando registra il contributo — il socio riceve un'e-mail e da
  quel momento `vimine.registrations.get(id)` porta il `token`. Il pagamento con carta arriva con Stripe.
- Se la tessera del socio è ancora in attesa di attivazione, l'iscrizione resta `reserved` e si conferma da
  sola quando la tessera diventa attiva.
- `422 not_eligible` porta in `errors[]` le stesse ragioni dell'idoneità.

Le iscrizioni del socio: `vimine.registrations.list({ filter: 'upcoming' })`; l'annullamento entro la finestra
dell'evento (`registration.cancellation_hours` ore prima dell'inizio): `vimine.registrations.cancel(id)` — oltre,
`409 window_closed`.

## Ospiti (eventi pubblici)

Un visitatore senza account Vimine può iscriversi a un evento **pubblico**, su un tipo di iscrizione che
non richiede la tessera, dal tuo server (client riservato, `registrations:write`):

```js title="server"
await srv.registrations.guest('serata-jazz', { email, first_name, last_name, registration_type });
// → { status: 'accepted' }, sempre
```

Nessun account viene creato. L'ospite riceve un'e-mail con un link di conferma (valido 24 ore) che porta
alla pagina dell'evento su Vimine: con un tipo gratuito il posto viene confermato lì e il QR mostrato
subito; con un contributo il posto resta riservato con le istruzioni di pagamento. Se preferisci gestire tu
la pagina di conferma, consuma il token del link con `api.confirmRegistration(token)`.

Quando il posto è confermato — subito con un tipo gratuito, alla registrazione del contributo negli altri casi —
l'ospite riceve una seconda e-mail con il proprio link: apre la pagina dell'evento con il QR da mostrare
all'ingresso e funziona fino alla fine dell'evento (anche se l'evento viene spostato). Un ospite non ha un'area
personale: il link è il suo posto, ma non il QR stesso (una mail inoltrata non è un posto). Se ospiti tu quella
pagina, leggi il posto con `api.viewRegistration(token)` (SDK dalla 0.3.2).

La risposta è sempre `202 { status: 'accepted' }`: l'API non rivela se un indirizzo è già iscritto. I problemi
di idoneità (`sold_out`, `membership_required` per un tipo riservato ai soci) sono `422`.

:::caution[Fiscalità]
Per il Codice del Terzo settore i contributi dei **non soci** sono proventi commerciali: la console lo
ricorda quando un evento viene reso pubblico, e ogni contributo viene classificato automaticamente dalla
tessera che l'iscrizione aveva al momento del pagamento.
:::

## All'ingresso

Il QR dell'iscrizione è un token firmato come quello della tessera: lo stesso scanner e la stessa chiamata
lo accettano.

```js title="server (tornello, scanner, cassa)"
const r = await srv.checkins.create(tokenLetto);
if (r.kind === 'registration') {
    // r.verdict: valid | already_checked_in | not_confirmed | cancelled | event_cancelled | membership_invalid
    // r.first: true al primo passaggio; r.membership_verdict quando il tipo richiede la tessera
}
```

Anche la verifica pubblica risponde a un token di iscrizione: `GET /v1/verify/{token}` →
`{ status, kind: 'registration', event: { slug, starts_at } }`, senza dati personali.

## Dal tuo gestionale

L'elenco delle iscrizioni di un evento — id, stato, tipo, tessera, ingresso, **senza dati personali** — è
`srv.registrations.listForEvent(slug, { status, limit, cursor })` (`registrations:read`, piani con gli elenchi
via API). I nominativi restano nella console dell'associazione.

## Riferimenti

- [SDK per il browser](https://developers.vimine.network/sdk/browser/#events) · [SDK per il server](https://developers.vimine.network/sdk/server/#events) ·
  [`<vimine-events>`](https://developers.vimine.network/sdk/elements/) · [Tipi](https://developers.vimine.network/sdk/types/)
- API HTTP: le operazioni con tag `events` e `registrations` nel [riferimento](https://developers.vimine.network/api/console/).
