Integração Web (QR Code)

Objetivo

Implementar verificação de credencial em uma aplicação Web React usando QR Code (fluxo cross-device).

Fluxo Web

sequenceDiagram
    participant U as Usuário (Desktop)
    participant SDK as App React + SDK
    participant VS as INJI Verify Service
    participant W as Carteira Digital (Celular)

    U->>SDK: Clica "Gerar QR Code"
    SDK->>VS: POST /v1/verify/vp-request
    VS-->>SDK: transactionId + requestId + authorizationDetails
    SDK->>U: Renderiza QR Code
    U->>W: Escaneia QR Code
    W->>VS: POST /v1/verify/vp-submission (credencial)
    VS->>VS: Valida credencial
    Note over SDK,VS: Long-polling até receber resultado
    VS-->>SDK: Resultado
    SDK->>U: Callback executado

Cenário

O usuário está no desktop. O site exibe um QR Code. O usuário abre a Carteira Digital no celular, escaneia o QR Code e autoriza o compartilhamento da credencial.

Passo 1 — Instalar o SDK

npm install @injistack/react-inji-verify-sdk@0.17.0
Importante

É necessário utilizar a versão 0.17.0 do SDK.

Passo 2 — Componente de verificação

// src/components/VerificaIdade.tsx
import { useState } from 'react';
import { OpenID4VPVerification } from '@injistack/react-inji-verify-sdk';

const VERIFY_SERVICE_URL = '/v1/verify';
const CLIENT_ID_DID = 'did:web:localhost:v1:verify';

const presentationDefinition = {
  id: 'eca-age-verification',
  purpose: 'Verificação de idade conforme o Estatuto da Criança e do Adolescente',
  format: { ldp_vc: { proof_type: ['Ed25519Signature2020'] } },
  input_descriptors: [
    {
      id: 'eca credential',
      format: { ldp_vc: { proof_type: ['Ed25519Signature2020'] } },
      constraints: {
        fields: [
          {
            path: ['$.type'],
            filter: { type: 'object', pattern: 'ECACredential' },
          },
        ],
      },
    },
  ],
};

export function VerificaIdade() {
  const [iniciado, setIniciado] = useState(false);

  return (
    <div>
      <h2>Verificação de Idade</h2>
      {!iniciado ? (
        <button onClick={() => setIniciado(true)}>Gerar QR Code</button>
      ) : (
        <OpenID4VPVerification
          verifyServiceUrl={VERIFY_SERVICE_URL}
          clientId={CLIENT_ID_DID}
          protocol="openid4vp://"
          isSameDeviceFlowEnabled={false}
          presentationDefinition={presentationDefinition}
          onVPProcessed={(results) => {
            const ok = results.some((r: { vcStatus: string }) => r.vcStatus === 'SUCCESS');
            console.log('Verificado:', ok);
          }}
          onError={(error) => console.error('Erro na verificação:', error)}
          onQrCodeExpired={() => setIniciado(false)}
        />
      )}
    </div>
  );
}

Passo 3 — Usar o componente

// src/pages/Checkout.tsx
import { VerificaIdade } from '../components/VerificaIdade';

export function Checkout() {
  return (
    <div>
      <h1>Verificação de Credencial</h1>
      <VerificaIdade />
    </div>
  );
}

Versão para produção (com validação no backend)

Para maior segurança, capture o transactionId e valide no backend:

import { OpenID4VPVerification } from '@injistack/react-inji-verify-sdk';

export function VerificaIdadeProd() {
  return (
    <OpenID4VPVerification
      verifyServiceUrl="<VERIFY_BASE_URL>/v1/verify"
      clientId="did:web:<SEU_DOMINIO>:v1:verify"
      protocol="openid4vp://"
      isSameDeviceFlowEnabled={false}
      presentationDefinition={presentationDefinition}
      onVPProcessed={async (results) => {
        const ok = results.some((r: { vcStatus: string }) => r.vcStatus === 'SUCCESS');
        if (ok) { /* liberar acesso */ }
      }}
      onError={(error) => console.error(error)}
      onQrCodeExpired={() => console.log('QR expirou')}
    />
  );
}
Importante

Para maior segurança, seu backend pode buscar o resultado diretamente em GET <VERIFY_BASE_URL>/v1/verify/vp-result/{transactionId} e decidir se libera o acesso, evitando que o frontend seja adulterado.

Customização do QR Code

<OpenID4VPVerification
  // ...
  qrCodeStyles={{
    size: 300,
    bgColor: '#ffffff',
    fgColor: '#000000',
  }}
  triggerElement={<button className="btn-verificar">Verificar Idade</button>}
/>

Props do OpenID4VPVerification

Prop Tipo Obrigatório Descrição
verifyServiceUrl string Sim URL do INJI Verify Service
clientId string Sim DID do verificador
protocol string Sim Protocolo ("openid4vp://")
isSameDeviceFlowEnabled boolean Não Habilita fluxo no mesmo dispositivo. Padrão: true — defina false para fluxo cross-device sem web wallet
presentationDefinition object Sim* Presentation Definition inline
presentationDefinitionId string Sim* ID de definição pré-cadastrada
onVPProcessed (results) => void Sim Callback com resultado completo
onError (error) => void Sim Callback de erro
onQrCodeExpired () => void Sim Callback de QR expirado
qrCodeStyles object Não Estilos do QR Code

* Use presentationDefinition ou presentationDefinitionId, não ambos.

Próximo passo

Para integração mobile, veja Integração Mobile (Deep Link).