Pular para o conteúdo

Incorporação e verificação das carteirinhas

Ver como Markdown

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:

<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).

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)
<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). 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.

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.

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:

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:

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).

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

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.

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

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.

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). 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:

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.