Skip to content

Fluxo de Pix

Estrutura completa de Pix da conta ativa: Pix In (cobranças recebidas), Pix Out (envio por chave, copia e cola e dados bancários), consulta de chave no DICT, PIN de aprovação e cadastro de contas bancárias favoritas dos clientes.

  • Backend: fastgivr-apiApp\Http\Controllers\Pix\*, App\Services\Pix\*, App\Services\PinValidator
  • Rotas: routes/pix.php (prefixo /pix, name pix.), carregado por bootstrap/app.php
  • Middleware: auth:api,sanctum · SetActiveAccountMiddleware · InjectAccountIntoRequest
  • Persistência: pixes, pix_withdraws, pix_recipients, transactions (ledger)
  • Adaptador bancário: Account::getBank() — ambiente sandbox com um único gateway, App\Models\Banks\Teste (mocks determinísticos). Ver config/banks.php.

Todas as respostas seguem o envelope { code, success, data|<chave>, message? }. Erros de validação: HTTP 422 com { success:false, message, data: { campo: [erros] } }.


1. Mapa de endpoints

MétodoRotaAçãoAprovação
GET/pix/chargeslista cobranças Pix (paginado; ?txid= / ?id= p/ item único)
POST/pix/chargescria cobrança dinâmica (QR + copia e cola)
GET/pix/charges/staticPix estático (copia e cola) da própria conta
POST/pix/charges/{txid}/simulate-paymentliquida a cobrança (só gateway Teste) — atalho autenticado do webhook
GET/pix/charges/{txid}detalhe de uma cobrança
GET/pix/keysminhas chaves Pix registradas no DICT
POST/pix/keysregistra uma chave da conta (cpf|cnpj|email|phone|random)
DELETE/pix/keys/{id}remove uma chave da conta do DICT
POST/pix/keys/lookupconsulta a chave de um terceiro no DICT
POST/pix/pin/verifyvalida o PIN da conta (conta tentativa)
GET/pix/pin/statusestado do PIN (cadastrado? bloqueado?)
GET/pix/transfershistórico de envios (paginado)
POST/pix/transfers/key/previewresolve a chave e cria saque AGUARDANDO
POST/pix/transfers/keyconfirma e envia por chavePIN
POST/pix/transfers/copy-paste/previewdecodifica o BR Code e resolve o beneficiário
POST/pix/transfers/copy-pasteconfirma e paga o BR CodePIN
POST/pix/transfers/manualenvia por dados bancários (banco/agência/conta)PIN
GET/pix/transfers/{id}detalhe de um envio + transação associada
GET/pix/transfers/{id}/receiptcomprovante do envio (JSON; ?format=pdf → PDF)
GET/pix/recipientscontas favoritas / destinatários (?favorite=1 ?client_id= ?search=)
POST/pix/recipientscadastra uma conta bancária favorita de um cliente
GET/pix/recipients/frequenttop 10 por número de usos
GET/pix/recipients/{id}detalhe
PUT|PATCH/pix/recipients/{id}edita
DELETE/pix/recipients/{id}remove (soft delete)

O cadastro/alteração do PIN continua em /access/account/pin.


2. Pix In — cobranças recebidas

Controller Pix\PixInController. Substitui o antigo QuickPay\PixAccount.

2.1 POST /pix/charges — criar cobrança dinâmica

CreatePixChargeRequest:

CampoRegra
amountrequired · numeric · min:{accounts.min_pix_in ?? 1}
webhooknullable · url — URL do lojista notificada quando o Pix for pago
name, email, document, phonenullable — quando os três primeiros vêm juntos, cria/atualiza um Client

Efeitos: cria Pix (status = 0 PENDING), Charge (status pending, payment_method = PIX) e, opcionalmente, o Client. Retorna txid, value, qrcode (payload EMV) e image (QR Code SVG).

2.2 GET /pix/charges/static — Pix estático da conta

Gera uma vez e persiste em accounts.qrcode o payload EMV estático (App\Services\PixEstatico) com a key_pix da conta. Retorna { payload, image }.

2.3 GET /pix/charges e GET /pix/charges/{txid}

Lista paginada (per_page, page) das cobranças da conta, cada uma com o QR renderizado. ?txid= ou ?id= devolve item único com as transações vinculadas. {txid} devolve o PixChargeResource (status legível via PixStatus).

2.4 Estados do Pix (App\Enums\PixStatus)

valornomesignificado
0PENDINGaguardando pagamento
1PAIDpago
2PAIDWITHBOLETOpago via Bolepix
3EXPIREDexpirado
-2DELETEDremovido

2.5 Liquidação do Pix recebido — App\Services\Pix\PixInSettlementService

Um único serviço, idempotente por txid, concentra a liquidação:

  1. PixPAID (+ paid_amount);
  2. lança pix-in no ledger (+ fee-pix-in se accounts.tax_pix_in);
  3. marca a Charge vinculada como paid;
  4. dispara PixReceivedNotification aos usuários da conta (respeita a preferência pix_credit, nunca lança);
  5. agenda o webhook do lojista (pix.webhook e/ou accounts.webhook).

Quem chama o serviço (gateway Teste):

GatilhoDescrição
POST /webhook/teste/pix-in{ txid, amount?, endToEndId?, payer_name? } — público (simula o callback do PSP)
POST /pix/charges/{txid}/simulate-paymentautenticado — atalho para a tela "Cobrar"

Chamar duas vezes o mesmo txid não duplica lançamentos (devolve a transação pix-in existente).


3. Chaves Pix (DICT)

3.1 Minhas chaves — GET/POST/DELETE /pix/keys

Chaves que a própria conta registra no DICT para receber Pix. Persistidas em pix_account_keys (App\Models\PixAccountKey), separadas de pix_recipients (chaves de terceiros). Controller Pix\PixKeyController.

O registro/remoção fala com Account::getBank() via createPixKey / deletePixKey, guardados por method_exists — bancos ainda não integrados devolvem 422 "não implementado" (hoje só o gateway Teste cobre 100%).

MétodoRotaAção
GET/pix/keyslista as chaves da conta ({ keys: [...] })
POST/pix/keysregistra uma chave
DELETE/pix/keys/{id}remove a chave (soft delete + baixa no DICT)

StorePixAccountKeyRequest:

CampoRegra
typerequired · in:cpf,cnpj,email,phone,random
keyrequired_unless:type,random · max:100 — formato validado conforme o type; random é gerada pelo PSP

Regras extras (withValidator): máximo 5 chaves ativas/pendentes por conta, sem chave duplicada e no máximo uma chave cpf/cnpj. O registro cria a linha como pending, chama o PSP e conclui como active (confirmed_at) ou error.

3.2 Consulta de terceiro — POST /pix/keys/lookup

PixKeyLookupRequest: key required, type required · in:cpf,cnpj,email,telefone,aleatorio, amount nullable.

O controller valida o formato da chave conforme o type (CPF 11 dígitos, CNPJ 14, e-mail válido, telefone 11 dígitos, aleatória 32–37 caracteres) e consulta via App\Services\StricService::getPixInfo().

MVP: StricService devolve um titular determinístico derivado da própria chave (mesma entrada → mesma resposta), mantendo o Pix Out por chave e as telas de consulta testáveis no gateway Teste. O bloco HTTP da integração real (PrismaPay / STRIC) fica comentado como referência.

StatusQuando
200{ status: "success", data: { ...dados do titular no DICT } }
400formato inválido para o tipo informado
404chave não encontrada no DICT

4. PIN de aprovação

Toda transferência Pix (chave / copia e cola / dados bancários) exige o PIN de 6 dígitos da conta, validado antes da chamada ao banco por App\Services\PinValidator.

SituaçãoHTTPdata.reason
PIN não enviado422pin_required
PIN não cadastrado na conta409pin_not_configured
PIN incorreto403pin_invalid + attempts_remaining
Bloqueado por tentativas429pin_locked + retry_after_seconds
  • Limite: MAX_ATTEMPTS = 5 tentativas erradas → bloqueio de DECAY_SECONDS = 900s (15 min), contabilizado por conta no RateLimiter (cache).
  • PinException é Responsable — o Laravel renderiza o corpo diretamente, sem passar pelo handleException.
  • POST /pix/pin/verify { pin }200 { valid: true } ou o erro acima (também conta tentativa).
  • GET /pix/pin/status{ configured, locked, retry_after_seconds, attempts_remaining }.

5. Pix Out — envio por chave

Fluxo em 2 passos: preview (resolve o beneficiário e cria o saque AGUARDANDO) → send (PIN + envio).

5.1 POST /pix/transfers/key/preview

PixWithdrawRequest: keypix required·max:150, value nullable·numeric·min:0, message nullable·max:255. withValidator bloqueia saldo insuficiente (balance - tax_pix_out) e duplicidade (mesmo documento+valor AGUARDANDO na última 1 h).

Se já houver um saque AGUARDANDO idêntico na última hora, devolve o existente (não cria outro).

5.2 POST /pix/transfers/key

PixWithdrawalConfirmarRequest: internal_id required·exists:pix_withdraws,id, keypix, value required·min:0.01, pin required·size:6, message nullable·max:140. withValidator reconfirma titularidade, estado AGUARDANDO, ausência de duplicata CONCLUIDO e saldo.

Lançamentos no ledger (App\Services\Pix\PixService::recordOutTransactions):

type_id (slug)valor
pix-out-valor
fee-pix-out (fallback taxa)-accounts.tax_pix_out (só se houver)

Ao final, recordOutTransactions dispara PixSentNotification in-app (preferência pix_debit). O mesmo vale para copia e cola e dados bancários.

5.3 GET /pix/transfers/{id}/receipt — comprovante

Devolve o comprovante normalizado (payer, receiver, value, txid, end_to_end_id, transaction) de um saque CONCLUIDO422 caso contrário. ?format=pdf renderiza o PDF (view pdfs.transaction); ?download=0 abre inline. pdf_url no corpo aponta para essa mesma rota.


6. Pix Out — copia e cola (BR Code)

Decodifica o payload EMV®MPM ("Pix Copia e Cola") localmente e paga.

  • Decoder: App\Services\Pix\BrCodeDecoder — TLV EMV, valida o CRC16-CCITT (polinômio 0x1021, init 0xFFFF) sobre o payload até 6304 inclusive.
  • DTO: App\DTO\Pix\BrCodePayloadDTOtype = static | dynamic (dinâmico = traz URL na tag 26/25 ou POI method 12).
TagConteúdo
00Payload Format Indicator
01Point of Initiation Method (11 reutilizável, 12 uso único)
26/00 26/01 26/02 26/25GUI (br.gov.bcb.pix) · chave · descrição · URL da cobrança
52 53 54 58 59 60MCC · moeda (986) · valor · país (BR) · nome · cidade
62/05TXID
63CRC16

PixCopyPasteRequest: payload required·min:20·max:2000, amountnullable·numeric·min:0.01 (obrigatório quando o BR Code estático não traz valor — checado no controller após o decode), message nullable·max:140, pin nullable·size:6 (exigido no envio).

  • Estático: valor vem do payload (tag 54) ou do campo amount; chave da tag 26/01.
  • Dinâmico: Account::getBank()->getPixCharge(url) busca a cobrança no PSP — mock determinístico no gateway Teste.

O preview não persiste nada. O send cria o PixWithdraw (com metadata.br_code), lança no ledger e finaliza como CONCLUIDO.


7. Pix Out — dados bancários

POST /pix/transfers/manual

PixBankTransferRequest: bank_code, agency, account, beneficiary_name, beneficiary_document, amount required·min:0.01, message nullable, pin required·size:6.

Resolve o ISPB por BankInstitution::findByIspbOrCode($bank_code), chama makePixTransferBank(PixOutBankRequestDTO), grava o PixWithdraw, lança no ledger (pix-out + fee-pix-out) e cadastra o destinatário.


8. Contas bancárias favoritas dos clientes

pix_recipients = uma conta bancária de um cliente + suas chaves Pix. Controller Pix\PixRecipientController.

  • favorite — cadastro explícito via POST /pix/recipients (vs. só "frequente", cujo count cresce sozinho após cada transferência).
  • pix_keysjson com [{ key, type }]: várias chaves da mesma conta. pix_key guarda a chave primária (busca / compatibilidade).
  • alias — apelido ("Fornecedor João — Itaú").

POST /pix/recipients (StorePixRecipientRequest)

CampoRegra
client_idnullable·exists:clients,id — precisa pertencer à conta ativa
namerequired·max:150
documentrequired·cpf_ou_cnpj
aliasnullable·max:120
favoritesometimes·boolean (default true neste endpoint)
bank_name bank_code branch accountnullable
account_typenullable·in:CORRENTE,POUPANCA,PAGAMENTO,SALARIO
pix_keynullable·max:150
pix_keysnullable·array·max:10 — cada item { key (required_with), type? in cpf,cnpj,email,phone,random }

Regra cruzada: pelo menos uma chave OU dados bancários (bank_code + account). O type ausente é deduzido pelo formato (App\Services\Pix\PixService::guessKeyType).

Identidade do favorito = (account_id, document, conta bancária | chave primária). Re-cadastrar a mesma conta funde novas chaves (não duplica) e restaura um registro soft-deleted.

PUT/PATCH edita (parcial; document imutável); DELETE faz soft delete.


9. Modelos

pixes (App\Models\Pix)

txid, qrcode (payload EMV), value, paid_amount, payment_date, status (PixStatus), webhook, name/email/document (pagador), account_id, metadata. Relação charge, transactions (por txid).

pix_withdraws (App\Models\PixWithdraw)

unique_hash, name, document, bank_code, branch, account_number, account_type, key_pix, value, txid, end_to_end_id, status (WithdrawStatusEnum: aguardando / concluido / erro), scheduled_at, metadata (inclui br_code no fluxo copia e cola), message.

pix_recipients (App\Models\PixRecipient)

account_id, client_id, favorite, alias, pix_key, pix_keys (array), name, document, bank_name, bank_code, branch, account, account_type, count, last_used_at. allKeys() normaliza pix_key + pix_keys.

pix_account_keys (App\Models\PixAccountKey)

Chaves da própria conta no DICT. account_id, type (cpf/cnpj/email/phone/random), key, status (pending/active/error), psp_key_id, metadata, requested_at, confirmed_at, soft delete. Único por (account_id, key).


10. Adaptador bancário (Account::getBank()App\Models\Banks\Teste)

Sandbox: um único adaptador, com mocks determinísticos.

MétodoUso
createPixDinamico(value)cobrança dinâmica
getPixTransactionDetails(key)DICT p/ Pix Out por chave
makePixTransfer(PixOutRequestDTO)envio por chave / copia e cola
makePixTransferBank(PixOutBankRequestDTO)envio por dados bancários
getPixCharge(url)copia e cola dinâmico
createPixKey(type, key?) / deletePixKey(key)registrar/remover minha chave

StricService::getPixInfo cobre o DICT do endpoint /pix/keys/lookup (mock determinístico no MVP).


11. Frontend

fastgivr-internet-bank/src/services/pix.tspix.in (create / staticQr / getByTxid / simulatePayment), pix.pin, pix.keys (list / register / remove / lookup), pix.transfers (list / get / receipt / receiptPdfUrl / previews e envios), pix.recipients. Telas em src/pages/pix/* sob o layout AppLayout (rotas /pix, /pix/transferir, /pix/cobrar, /pix/extrato, /pix/chaves).

O interceptor de src/services/api.ts injeta Authorization: Bearer e X-Device-Id; um 401 em request autenticado limpa a sessão e volta para /login?session=expired.

FastGivr API Documentation