Salta ai contenuti

Embed e verifica tessere

Apri come Markdown

Ogni associazione che ha attivato il tesseramento pubblico ha una pagina https://app.vimine.network/enroll/<slug> che può essere inserita in un iframe nel sito dell’associazione:

<iframe src="https://app.vimine.network/enroll/tuo-slug" style="width:100%;min-height:900px;border:0"></iframe>

L’associazione deve elencare il dominio del sito tra i domini di incorporamento nelle proprie impostazioni. La pagina di accesso e consenso di Accedi con Vimine invece non può essere inserita in un iframe: è sempre una pagina a tutto schermo su vimine.network.

?locale=en apre il modulo in inglese (l’elemento <vimine-enroll> lo passa da solo, dalla lingua della pagina).

Il modulo può prendere i colori del tuo sito. Una pagina in un iframe non vede il CSS di chi la ospita, quindi lo stile viaggia nell’indirizzo:

Parametro Valori
scheme light, dark lo schema della pagina; senza, quello dello sfondo o del visitatore
accent un colore esadecimale (1e3a5f) pulsanti, link, campi attivi e il nome dell’associazione
background un colore esadecimale, o transparent lo sfondo dietro le schede; transparent lascia vedere la tua pagina
font inter, roboto, open-sans, montserrat, nunito, lora, merriweather, roboto-slab, system il carattere di tutta la pagina, campi compresi, tra quelli che Vimine serve da sé (system: quello del dispositivo)
<iframe src="https://app.vimine.network/enroll/tuo-slug?scheme=light&accent=1e3a5f&background=transparent"
style="width:100%;min-height:900px;border:0"></iframe>

Altri valori sono ignorati. Con <vimine-enroll> non serve scrivere l’indirizzo: imposti --vimine-scheme, --vimine-accent, --vimine-background e --vimine-typeface nel tuo CSS (elementi). Con transparent scegli lo schema che si legge sulla tua pagina: le schede del modulo hanno il loro fondo, il testo fuori dalle schede sta sul tuo.

Un’identità vale per tutte le associazioni: chi ha già un account non deve ricompilare il modulo. Aperta direttamente, la pagina …/enroll/<slug> riconosce la sessione e mostra una sola schermata — il tipo di tessera, lo statuto e l’informativa privacy di questa associazione, e soltanto ciò che questa associazione chiede in più (l’indirizzo, se ha dichiarato di raccoglierlo). Statuto e informativa si accettano ogni volta: nulla passa da un’associazione all’altra.

Dentro un iframe la sessione non è leggibile: il browser tiene separata una pagina incorniciata dallo stesso sito aperto direttamente. Per questo il modulo incorniciato offre Hai già un account Vimine? Continua su Vimine, che apre la pagina in una nuova scheda. Se scrivi l’iframe a mano e usi sandbox, includi allow-popups e allow-popups-to-escape-sandbox, altrimenti quella scheda resta chiusa nella sandbox; <vimine-enroll> lo fa già.

GET /v1/aps/{slug} restituisce il profilo pubblico dell’associazione — grafica, tipi di tessera con le quote, statuto in vigore — se vuoi costruire un’esperienza tua:

import { publicApi } from '@vimine/sdk';
const { aps, membership_types, statuto } = await publicApi().association('tuo-slug');

Nessuna credenziale. Da una pagina in cui il socio ha fatto Accedi con Vimine puoi poi raccogliere la richiesta:

const { aps, membership_types, statuto, privacy_notice } = await publicApi().association('tuo-slug');
await vimine.membership.request({
membership_type,
accept_statuto: { version: statuto.version },
// l'informativa privacy dell'associazione, che la tua pagina ha mostrato
privacy_notice: privacy_notice && { version: privacy_notice.version, sha256: privacy_notice.sha256, locale: privacy_notice.locale },
// l'indirizzo, se l'associazione lo raccoglie (aps.member_address: 'optional' | 'required')
address: { line: 'Via Roma 1', postal_code: '20100', city: 'Milano', province: 'MI' }
});

Quello che un’associazione chiede a chi aderisce non dipende dalla porta da cui arriva la richiesta:

  • l’informativa privacy. Finché l’associazione ne ha una in vigore che la persona non ha ancora visto, privacy_notice è obbligatorio: senza, 422 validation_failed sul campo privacy_notice; con una versione non più in vigore, 422 privacy_notice_outdated. vimine.legal.acceptances() dice cosa è ancora da confermare; il testo è GET /v1/aps/{slug}/privacy-notice. La presa visione resta come riga di prova legata a questa tessera.
  • l’indirizzo. aps.member_address dice se l’associazione lo raccoglie: con required, senza address la richiesta risponde 422 validation_failed sul campo address; con none viene ignorato.
  • chi è la persona. Se il profilo non è ancora identificato — né un codice fiscale, né data, luogo e Stato di nascita — la richiesta risponde 422 identification_required: completa il profilo con vimine.me.update(...) e ripeti (vedi Soci senza codice fiscale).

La tessera digitale è un token firmato (vmt_…) che il socio mostra come QR. È verificabile in due modi.

Nessuna credenziale, nessun dato personale nella risposta:

GET https://api.vimine.network/v1/verify/vmt_…
→ { "status": "active", "valid_until": "2027-03-31T22:59:59.000Z" }
oppure
→ { "status": "inactive", "reason": "expired" }
oppure
→ { "status": "inactive", "reason": "not_yet_valid", "valid_from": "2026-09-19T19:00:00.000Z" }

not_yet_valid: la tessera è autentica e l’adesione è attiva, ma l’ente ha stabilito un intervallo tra l’ammissione e l’utilizzabilità della tessera. valid_from dice da quando vale; fino ad allora, alla porta, è una tessera che non conta. Lo stesso istante è nel token (nbf), quindi uno scanner offline arriva alla stessa risposta da solo.

import { publicApi } from '@vimine/sdk';
const { status } = await publicApi().verifyCard(tokenLettoDalQr);

Il token è firmato con Ed25519; le chiavi pubbliche stanno su https://api.vimine.network/.well-known/vimine-card-keys.json (anche GET /v1/card-keys, o publicApi().cardKeys()). Uno scanner verifica tre cose, con la chiave in cache: la firma, la finestra di validità (nbf/exp, al massimo 90 giorni, poi il token ruota) e che lo stato nel payload sia attivo — s === "active". Poi si riallinea online quando torna la rete. Un kid sconosciuto significa che la chiave è ruotata: ricarica le chiavi.

Il token ruota a ogni cambio di stato, a ogni rinnovo e comunque ogni 90 giorni: uno screenshot vecchio non apre nulla per sempre.

Una tessera esiste solo per un’adesione attiva: finché la domanda è in attesa (di ammissione o della quota), e quando l’adesione è sospesa o revocata, l’API non rilascia alcun token — card è null in GET /v1/me/membership, e GET /v1/me/membership/card, l’immagine, l’e-mail e il link a Google Wallet rispondono 409 not_active. Il controllo di s resta obbligatorio: rifiuta i token emessi prima di questa regola e qualunque token che non dichiari uno stato attivo. Rispetta anche nbf: un ente può stabilire che la tessera diventi utilizzabile qualche ora dopo l’ammissione, e quell’istante viaggia lì.

Il codice fiscale è facoltativo: chi non ne ha uno si iscrive con data, luogo e Stato di nascita (vedi Soci senza codice fiscale). Quando c’è, POST /v1/validate/fiscal-code (o publicApi().validateFiscalCode(...)) controlla struttura e carattere di controllo e, se passi anche i dati anagrafici, la loro coerenza con il codice — e dice che cosa il codice stesso contiene:

const r = await publicApi().validateFiscalCode({ code: 'RSSMRA80A01H501U', first_name: 'Mario', last_name: 'Rossi', date: '1980-01-01', place: 'Roma', country: 'IT' });
// { valid: true, error: null, mismatches: [], hints: [], birth_date: '1980-01-01', birth_country: 'IT' }
// oppure
// { valid: false, error: 'mismatch', mismatches: ['date'], hints: [], birth_date: '1980-01-01', birth_country: 'IT' }

error vale invalid (struttura o carattere di controllo), mismatch (un codice ben formato che non concorda con i dati inviati) o null; hints contiene names_swapped quando il codice torna scambiando nome e cognome. Per chi è nato all’estero invia country (ISO 3166-1 alpha-2): il suo codice contiene solo lo Stato, e place — la città — non viene confrontato.