Pular para o conteúdo principal

Pagar via QR ou NFC

O PDV declara um valor e gera um payload — QR na tela ou transmitido por NFC, tanto faz, o app trata os dois do mesmo jeito. O app do participante lê esse payload, confirma o pagamento no próprio app (login já é o fator de autenticação — sem PIN, diferente do fluxo do QR impresso do caixa, que não tem login nenhum por trás).

PDV declara o valor
→ PDV gera o payload (QR/NFC) com um token opaco
→ app do participante lê o payload
→ app resolve o token no servidor (mostra valor + PDV antes de qualquer coisa)
→ participante confirma
→ app aprova o pagamento (debita a wallet)

O payload não contém o valor — só event e um token opaco de uso único:

wallet://payment?v=1&event=<eventId>&intent=<token>

O app extrai eventId e token desse payload; o valor de verdade só vem do passo de resolver, abaixo — nunca confie em nada que não seja a resposta do servidor pra decidir quanto vai ser cobrado.

1. Resolver (antes de confirmar)​

EndpointO que faz
GET /v1/events/{eventId}/wallet/payment-intents/{token}Consulta pura — nunca cobra nada. Devolve o valor e qual PDV declarou, pra montar a tela de confirmação.
curl "http://127.0.0.1:8787/v1/events/{eventId}/wallet/payment-intents/{token}" \
-H "Authorization: Bearer <accessToken do participante>"
Resposta
{
"intentId": "018f26d7-8a7b-7e7d-8c96-4f0b8d0f2b13",
"amountMinor": 2500,
"status": "pending",
"operatorUsername": "PDV-00184723",
"operatorDescription": "Entrada Principal",
"expiresAt": "2026-09-09T12:05:00.000Z"
}

404 PAYMENT_INTENT_NOT_FOUND se o token não existe (ou já expirou faz tempo e foi limpo) nesse evento.

2. Aprovar (o toque de "Confirmar" do participante)​

EndpointO que faz
POST /v1/events/{eventId}/wallet/payment-intents/{token}/approveDebita a wallet do participante autenticado pelo valor do intent, credita EVENT_REVENUE do evento.

Exige o header Idempotency-Key — mesmo mecanismo já documentado na recarga do caixa: repetir a mesma chave devolve o mesmo resultado sem cobrar de novo (replayed: true na resposta, indicando que nada foi debitado agora).

curl -X POST "http://127.0.0.1:8787/v1/events/{eventId}/wallet/payment-intents/{token}/approve" \
-H "Authorization: Bearer <accessToken do participante>" \
-H "Idempotency-Key: <uuid gerado pelo cliente>"
Resposta
{
"walletId": "018f26d7-...",
"eventId": "018f26d7-...",
"availableAmountMinor": 16750,
"replayed": false
}

O que pode dar errado, e por quê:

CódigoSituação
404 PAYMENT_INTENT_NOT_FOUNDToken inválido ou de outro evento.
404 WALLET_NOT_FOUNDVocê ainda não ativou a wallet nesse evento.
409 PAYMENT_INTENT_ALREADY_RESOLVEDAlguém já aprovou, recusou, ou o intent já expirou. Cada intent só pode ser resolvido uma vez.
409 PAYMENT_INTENT_EXPIREDPassou do tempo (curto, poucos minutos) sem ninguém aprovar. Peça um novo QR ao PDV.
409 INSUFFICIENT_BALANCESua wallet não tem saldo suficiente.
409 IDEMPOTENCY_KEY_IN_PROGRESSJá existe uma aprovação em andamento com essa mesma chave; tente de novo em seguida.

Diferente do QR impresso do caixa, aqui não existe PIN — o próprio login do app já é o fator que prova quem está pagando. Por isso a confirmação explícita no passo 2 é obrigatória: o app nunca aprova sozinho, sem o participante ver o valor e tocar em confirmar.