PDV — Pagamento via QR impresso + PIN
Rota regional (wallet-api, /v1/events/{eventId}/...), pra participante
sem o app: o caixa já emitiu a credencial (ver Caixa → credencial QR
impressa com PIN); o PDV lê o QR,
informa o valor e pede o PIN pro participante digitar.
| Endpoint | O que faz |
|---|---|
POST /v1/events/{eventId}/pdv/payments | Debita a wallet do participante pelo valor informado, credita a conta EVENT_REVENUE do evento, tudo na mesma transação. |
- curl
- JavaScript
- Python
curl -X POST http://127.0.0.1:8787/v1/events/{eventId}/pdv/payments \
-H "Authorization: Bearer <accessToken do PDV>" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: <uuid gerado pelo cliente>" \
-d '{
"qrPayload": "wallet://credential?v=1&event=<eventId>&credential=<token do QR>",
"pin": "482913",
"amountMinor": 2500
}'
const response = await fetch(`http://127.0.0.1:8787/v1/events/${eventId}/pdv/payments`, {
method: "POST",
headers: {
Authorization: `Bearer ${pdvAccessToken}`,
"Content-Type": "application/json",
"Idempotency-Key": crypto.randomUUID(),
},
body: JSON.stringify({ qrPayload, pin, amountMinor: 2500 }),
});
const { walletId, availableAmountMinor } = await response.json();
import requests
import uuid
response = requests.post(
f"http://127.0.0.1:8787/v1/events/{event_id}/pdv/payments",
headers={
"Authorization": f"Bearer {pdv_access_token}",
"Idempotency-Key": str(uuid.uuid4()),
},
json={"qrPayload": qr_payload, "pin": pin, "amountMinor": 2500},
)
result = response.json()
{
"walletId": "018f26d7-...",
"eventId": "018f26d7-...",
"availableAmountMinor": 16750,
"replayed": false
}
O que o endpoint verifica, nessa ordem:
- O QR é deste evento? O
qrPayloadcarrega o próprioeventIddentro dele — se não bater com o evento da URL, a resposta é404 CREDENTIAL_NOT_FOUND, igual a um QR desconhecido (nunca revela que o token existe, só que é de outro evento). - A credencial está ativa, não expirou e não está bloqueada? Senão,
409 CREDENTIAL_NOT_ACTIVE/409 CREDENTIAL_EXPIRED/403 CREDENTIAL_LOCKED. - O PIN confere? Se não,
403 INVALID_PIN— e essa tentativa fica registrada na credencial. 5 tentativas erradas seguidas bloqueiam a credencial por 15 minutos (403 CREDENTIAL_LOCKED); o participante precisa esperar ou o caixa reemitir uma credencial nova. - Tem saldo suficiente? Senão,
409 INSUFFICIENT_BALANCE.
A wallet fica travada durante toda a verificação + débito — se dois PDVs tentarem cobrar a mesma wallet ao mesmo tempo (ou uma recarga e uma cobrança coincidirem), uma espera a outra terminar; nunca rodam em paralelo.
Exige o header Idempotency-Key, com o mesmo comportamento do recarga do
caixa: repetir a mesma chave devolve o mesmo resultado sem
cobrar duas vezes (replayed: true na resposta, pra deixar claro que não
foi cobrado de novo), e uma segunda chamada com a mesma chave enquanto a
primeira ainda está em andamento recebe 409 IDEMPOTENCY_KEY_IN_PROGRESS.
Reusar a mesma chave com um valor ou QR diferente vira 409 IDEMPOTENCY_KEY_BODY_MISMATCH em vez de cobrar (ou devolver) o valor errado
— gere uma chave nova a cada cobrança.