# Incorporação e verificação das carteirinhas

> O formulário de adesão no seu site, o perfil público da associação e a verificação das carteirinhas on-line e off-line.

URL: https://developers.vimine.network/pt/guides/embed-and-verify/

## O formulário de adesão no seu site

Cada associação que ativou a adesão pública tem uma página `https://app.vimine.network/enroll/<slug>`
que pode ser colocada num iframe no site da associação:

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

A associação precisa incluir o domínio do site entre os seus **domínios de incorporação**, nas configurações. A página
de login e de consentimento do *Entrar com Vimine*, por outro lado, **não pode** ser colocada num iframe: ela é
sempre uma página inteira em vimine.network.

`?locale=en` abre o formulário em inglês (o elemento `<vimine-enroll>` o passa sozinho, a partir do idioma da página).

### A aparência do formulário

O formulário pode assumir as cores do seu site. Uma página num iframe não enxerga o CSS de quem a hospeda, então o
estilo viaja no endereço:

| Parâmetro | Valores | |
|---|---|---|
| `scheme` | `light`, `dark` | o esquema da página; sem ele, o do fundo ou o do visitante |
| `accent` | uma cor hexadecimal (`1e3a5f`) | botões, links, campos em foco e o nome da associação |
| `background` | uma cor hexadecimal, ou `transparent` | o fundo atrás dos cartões; `transparent` deixa a sua página aparecer |
| `font` | `inter`, `roboto`, `open-sans`, `montserrat`, `nunito`, `lora`, `merriweather`, `roboto-slab`, `system` | a fonte da página inteira, campos incluídos, entre as que o próprio Vimine serve (`system`: a do dispositivo) |

```html
<iframe src="https://app.vimine.network/enroll/your-slug?scheme=light&accent=1e3a5f&background=transparent"
        style="width:100%;min-height:900px;border:0"></iframe>
```

Outros valores são ignorados. Com `<vimine-enroll>` não há endereço para escrever: defina `--vimine-scheme`, `--vimine-accent`,
`--vimine-background` e `--vimine-typeface` no seu CSS ([elementos](https://developers.vimine.network/pt/sdk/elements/)). Com `transparent`, escolha o esquema que se lê bem na
sua página: os cartões do formulário têm o seu próprio preenchimento, o texto fora deles fica sobre o seu.

### Quem já tem uma conta Vimine

Uma só identidade serve para todas as associações: quem já tem uma conta não precisa preencher o formulário de novo.
Aberta diretamente, a página `…/enroll/<slug>` reconhece a sessão e mostra **uma só tela** — o tipo de carteirinha, o
statuto e o aviso de privacidade *desta* associação, e só o que esta associação pede a mais (o endereço, quando ela
declarou que o coleta). O statuto e o aviso são aceitos todas as vezes: nada passa de uma associação para outra.

Dentro de um iframe a sessão não pode ser lida: um navegador mantém uma página emoldurada separada do mesmo site
aberto diretamente. Por isso o formulário no iframe oferece *Já tem uma conta Vimine? Continue no Vimine*, que abre a
página numa nova aba. Se você escreve o iframe à mão e usa `sandbox`, inclua `allow-popups` e
`allow-popups-to-escape-sandbox`, senão aquela aba continua dentro do sandbox; o `<vimine-enroll>` já faz isso.

## O perfil público

`GET /v1/aps/{slug}` devolve o perfil público da associação — identidade visual, tipos de carteirinha com as taxas,
statuto em vigor — se você quiser construir uma experiência sua:

```js
import { publicApi } from '@vimine/sdk';

const { aps, membership_types, statuto } = await publicApi().association('your-slug');
```

Sem credenciais. A partir de uma página em que o associado fez *Entrar com Vimine*, você pode então receber o pedido:

```js
const { aps, membership_types, statuto, privacy_notice } = await publicApi().association('your-slug');

await vimine.membership.request({
    membership_type,
    accept_statuto: { version: statuto.version },
    // o aviso de privacidade da associação, que a sua página mostrou
    privacy_notice: privacy_notice && { version: privacy_notice.version, sha256: privacy_notice.sha256, locale: privacy_notice.locale },
    // o endereço, quando a associação o coleta (aps.member_address: 'optional' | 'required')
    address: { line: 'Via Roma 1', postal_code: '20100', city: 'Milano', province: 'MI' }
});
```

O que uma associação pede a quem solicita a adesão não depende da porta por onde o pedido chega:

- **o aviso de privacidade.** Enquanto a associação tiver um em vigor que a pessoa ainda não viu, `privacy_notice` é
  obrigatório: sem ele, `422 validation_failed` no campo `privacy_notice`; com uma versão que não está mais em
  vigor, `422 privacy_notice_outdated`. `vimine.legal.acceptances()` diz o que ainda falta confirmar; o texto está em
  `GET /v1/aps/{slug}/privacy-notice`. A ciência fica guardada como um registro de prova ligado a esta carteirinha.
- **o endereço.** `aps.member_address` diz se a associação o coleta: com `required`, um pedido sem
  `address` responde `422 validation_failed` no campo `address`; com `none`, ele é ignorado.
- **quem é a pessoa.** Quando o perfil ainda não está identificado — nem um código fiscal, nem data, local e país de
  nascimento — o pedido responde `422 identification_required`: complete o perfil com `vimine.me.update(...)` e
  repita (veja [Associados sem código fiscal](https://developers.vimine.network/pt/guides/members-without-cf/#identification_required)).

:::caution[Mudou em 2 de outubro de 2026]
Até a versão `2026-09-27` da API, este pedido não exigia nem o aviso nem o endereço. Uma integração que o
chama sem `privacy_notice` para uma associação com um aviso em vigor agora recebe `422`.
:::

## Verificação das carteirinhas

A carteirinha digital é um **token assinado** (`vmt_…`) que o associado mostra como código QR. Ela pode ser verificada
de duas formas.

### On-line

Sem credenciais, sem dados pessoais na resposta:

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

`not_yet_valid`: a carteirinha é autêntica e está ativa, mas a organização definiu um intervalo entre a admissão e o
momento em que a carteirinha pode ser usada. `valid_from` diz quando ela começa a valer; até lá, na entrada, é uma
carteirinha que não vale. O mesmo instante está no token (`nbf`), então um leitor off-line chega sozinho à mesma
resposta.

```js
import { publicApi } from '@vimine/sdk';

const { status } = await publicApi().verifyCard(tokenReadFromQr);
```

### Off-line

O token é assinado com **Ed25519**; as chaves públicas estão em
`https://api.vimine.network/.well-known/vimine-card-keys.json` (também `GET /v1/card-keys`, ou
`publicApi().cardKeys()`). Um leitor verifica **três coisas** com a chave em cache: a assinatura, a janela de validade (`nbf`/`exp`, no máximo 90
dias, depois o token muda) e que o status no payload seja ativo — **`s === "active"`**. Depois ele sincroniza de novo
on-line quando a rede voltar. Um `kid` desconhecido
quer dizer que a chave mudou: recarregue as chaves.

O token muda a cada mudança de status, a cada renovação e, de qualquer forma, a cada 90 dias: uma captura de tela antiga
não abre nada para sempre.

Uma carteirinha só existe para uma adesão **ativa**: enquanto o pedido está pendente (admissão ou taxa), e quando a
carteirinha está suspensa ou revogada, a API não emite nenhum token — `card` é `null` em `GET /v1/me/membership`, e
`GET /v1/me/membership/card`, a imagem, o e-mail e o link do Google Wallet respondem `409 not_active`. Verificar `s`
continua obrigatório: isso recusa os tokens emitidos antes desta regra e qualquer token que não declare um status
ativo. Respeite também o `nbf`: uma organização pode decidir que uma carteirinha se torna utilizável algumas horas
depois da admissão, e esse instante viaja ali.

:::caution
A verificação off-line diz que o token era **autêntico e estava dentro da sua janela** quando foi emitido, não que a
carteirinha está ativa neste exato instante: uma suspensão que aconteceu depois só aparece on-line. Sincronize de novo
assim que puder.
:::

## Código fiscal

O código fiscal é **opcional**: quem não tem um se associa com a data, o local e o país de nascimento (veja
[Associados sem código fiscal](https://developers.vimine.network/pt/guides/members-without-cf/)). Quando existe, `POST /v1/validate/fiscal-code`
(ou `publicApi().validateFiscalCode(...)`) verifica a estrutura e o caractere de controle de um codice fiscale italiano
e, se você também passar os dados pessoais, a coerência deles com o código — e diz o que o próprio código traz:

```js
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' }
//   ou
// { valid: false, error: 'mismatch', mismatches: ['date'], hints: [], birth_date: '1980-01-01', birth_country: 'IT' }
```

`error` é `invalid` (estrutura ou caractere de controle), `mismatch` (um código bem formado que não confere com os dados
enviados) ou `null`; `hints` traz `names_swapped` quando o código confere trocando sobrenome e nome. Para uma
pessoa nascida no exterior, envie `country` (ISO 3166-1 alpha-2): o código dela só traz o país, e `place` — a cidade —
não é comparado.
