Skip to content

Fluxo de Pagamento de Boleto

Pagamento de boletos de terceiros (contas de consumo, tributos, boletos bancários) com débito na conta ativa. Ciclo: consultar a linha digitável → pagar agora ou agendarcomprovante / cancelar.

  • Backend: fastgivr-apiApp\Http\Controllers\Payments\BoletoPaymentController, App\Services\Payments\BoletoPaymentService, App\Services\PinValidator
  • Rotas: routes/payments.php (prefixo /payments, name payments.), carregado por bootstrap/app.php
  • Middleware: auth:api,sanctum · SetActiveAccountMiddleware · InjectAccountIntoRequest
  • Persistência: boleto_withdraws + transactions (ledger)
  • Adaptador bancário: Account::getBank()App\Models\Banks\Teste (sandbox, mock determinístico). getBoletoDetails / payBoleto / pagarBoleto / consultarComprovante / cancelarAgendamento / consultarBoletosDDA — todos mockados.

Substitui os antigos endpoints /quickpay/boleto/{payments,payment-barcode,info-barcode}.

Envelope padrão { code, success, data|<chave>, message? }. Erros de regra de negócio: HTTP 422 com message. Não encontrado: 404.


1. Mapa de endpoints

MétodoRotaAçãoAprovação
POST/payments/boletos/inspectconsulta a linha digitável → registra PENDENTE
GET/payments/boletoshistórico (?status= ?search= ?per_page=)
GET/payments/boletos/ddaboletos DDA disponíveis para a conta
GET/payments/boletos/{id}detalhe
POST/payments/boletos/{id}/paypaga agoraPIN
POST/payments/boletos/{id}/scheduleagenda para uma data futuraPIN
POST/payments/boletos/{id}/cancelcancela um pagamento pendente/agendado
GET/payments/boletos/{id}/receiptcomprovante junto ao banco
POST/payments/boletos/{id}/request-approval"autorizar" — envia para aprovação de outro usuárioPIN
GET/payments/boletos/pending-approvalfila de pagamentos aguardando aprovação
PUT/payments/boletos/{id}/approve"efetivar" — aprova e paga agoraPIN
DELETE/payments/boletos/{id}/rejectrejeita um pagamento pendente de aprovaçãoPIN
PUT/payments/boletos/approve-batchaprova vários pendentes de uma vez (list_id)PIN
DELETE/payments/boletos/reject-batchrejeita vários pendentes de uma vez (list_id)PIN
GET/payments/boletos/recipientsfavorecidos frequentes (cedente/beneficiário)
GET/payments/boletos/recipients/{id}detalhe de um favorecido
DELETE/payments/boletos/recipients/{id}remove um favorecido

2. Visão geral

Máquina de estados (App\Enums\StatusBoletoPayment)

valorsignificado
pendingconsultado, aguardando ação
processingagendado
completedpago / liquidado
canceledcancelado

3. POST /payments/boletos/inspect

InspectBoletoRequestprepareForValidation remove tudo que não for dígito; regra barcode: required · string · digits_between:44,48.

BoletoPaymentService::inspect:

  1. Bloqueia se já houver BoletoWithdraw COMPLETED para o mesmo (account, barcode)422.
  2. Account::getBank()->getBoletoDetails($barcode)App\DTO\BoletoReturnDTO (guardado por method_exists; banco sem o método → LogicException → 422).
  3. updateOrCreate do BoletoWithdraw por (account_id, barcode): status = pending, value = dto.valorAtual, assignor = dto.nomeBeneficiario, due_date (a partir do timestamp dto.dataVencimento), json = dto.toArray().

Resposta 200BoletoWithdrawResource:

jsonc
{
  "id": 42,
  "status": "pending",
  "value": 249.90,
  "barcode": "3419...",
  "assignor": "Telefônica Brasil S.A.",
  "due_date": "2026-09-10T00:00:00.000000Z",
  "boleto": { /* BoletoReturnDTO.toArray(): valor, dataVencimento, nomeBeneficiario, nomeBanco, ... */ },
  "metadata": null
}

4. POST /payments/boletos/{id}/pay

PayBoletoRequest: pin required·size:6, description nullable·max:255. {id} restrito a [0-9]+.

  • O PIN é validado antes de abrir a transação de banco de dados (PinValidator->validate — ver Fluxo de Pix §4 para os códigos 403/409/422/429 e o bloqueio por tentativas).
  • BoletoPaymentService::pay roda dentro de DB::transaction:
    • lockForUpdate() no BoletoWithdraw;
    • assertPayable — bloqueia se processing/completed (422 "já foi pago ou está em processamento") ou canceled;
    • Account::getBank()->payBoleto($barcode, $dados + description) (guardado);
    • status = completed, paid_at = now(), txid (primeiro de txid / idPagamento / idPagamentoTributosBarra / idTransacao / autenticacao), metadata = dados + resposta do banco;
    • lançamento no ledgerrecordTransaction:
campovalor
type_idslug boleto-payment (fallback pagamento-boleto)
value-BoletoWithdraw.value
balanceaccount.balance() - value
descriptionBoletoWithdraw.description ou "Pagamento boleto {barcode}"

Após o lançamento, pay() dispara BoletoPaidNotification in-app aos usuários da conta (via AccountNotificationService, preferência bill_payment; uma falha de notificação não desfaz o pagamento).

Resposta 201BoletoWithdrawResource (agora status: "completed", com paid_at e txid).

O comprovante fica em GET /payments/boletos/{id}/receipt (dados do banco) e, para o extrato, em GET /consolidation/transactions/{id}/pdf.


5. POST /payments/boletos/{id}/schedule

ScheduleBoletoRequest: pin required·size:6, scheduled_at required·date·after:today, description nullable·max:255.

BoletoPaymentService::schedule (dentro de DB::transaction):

  • exige status = pending (senão 422 "Só é possível agendar um boleto pendente.");
  • Account::getBank()->pagarBoleto($barcode, $dados + ['dataPagamento' => 'Y-m-d']);
  • status = processing, scheduled_at, txid do retorno.
  • Não debita o ledger — o débito ocorre na liquidação (webhook/job do PSP).

6. POST /payments/boletos/{id}/cancel

BoletoPaymentService::cancelsem PIN:

  • só permite pending ou processing (senão 422 "Este pagamento não pode ser cancelado.");
  • se processing e houver id de agendamento (txid ou metadata.idPagamento*), chama Account::getBank()->cancelarAgendamento($id);
  • status = canceled.

7. Aprovação em duas etapas (maker-checker)

A collection do parceiro bancário real ("API de Contas — Integração", pasta "Pagamentos") modela um fluxo de dupla checagem para pagamento de boleto: "autorizar" (prepara, sem executar) → fila de pendentes → "efetivar"/aprovar (ou rejeitar), inclusive em lote. BoletoPaymentService (métodos requestApproval/pendingApproval/approvePending/rejectPending/ approvePendingBatch/rejectPendingBatch) e o novo status StatusBoletoPayment::AWAITING_APPROVAL.

  • POST /payments/boletos/{id}/request-approvalPinOnlyBoletoRequest (pin required·size:6). status = pending → awaiting_approval; notifica in-app (bill_payment) via BoletoApprovalRequestedNotification.
  • GET /payments/boletos/pending-approval — lista paginada (status = awaiting_approval, mais recentes primeiro).
  • PUT /payments/boletos/{id}/approve — mesmo PinOnlyBoletoRequest (PIN de quem aprova, pode ser outro usuário da conta). Reaproveita pay() por baixo: efetiva o pagamento no banco de verdade, mesmo ledger e notificação de "boleto pago" do fluxo de pagamento direto.
  • DELETE /payments/boletos/{id}/rejectawaiting_approval → canceled, sem debitar.
  • PUT /payments/boletos/approve-batch / DELETE /payments/boletos/reject-batchBatchPendingBoletoRequest (list_idrequired·array, pin). Processa item a item, best-effort: um id que já não está mais pendente não derruba o lote — devolve { approved: [...], failed: { id: motivo } } (ou rejected/failed).

⚠️ Sem checagem de alçada por papel. Qualquer usuário com acesso à conta e o PIN correto pode tanto "autorizar" quanto "aprovar" — não há hoje nenhuma rota da API que imponha permissão/papel (spatie/laravel-permission está seedado, mas nenhum middleware/Gate o consulta). Ainda não wired na UI (fastgivr-internet-bank) — só backend.

8. Favorecidos de boletos

GET /payments/boletos/recipients[/{id}] · DELETE /payments/boletos/recipients/{id} — cedentes/beneficiários frequentes, tabela boleto_recipients (account_id, document nullable, name, favorite, count, last_used_at). Sem cadastro manual (POST): a identidade só existe depois de um boleto real ser pago — BoletoPaymentService::pay() grava/atualiza automaticamente a partir de documentoBeneficiario/nomeBeneficiario do BoletoReturnDTO; sem documento do cedente, casa por nome para não colidir favorecidos diferentes num mesmo registro.


9. GET /payments/boletos/{id}/receipt

BoletoPaymentService::receipt — resolve o id do pagamento (txid ou metadata.idPagamento*); se não houver → 422 "ainda não possui comprovante". Caso contrário Account::getBank()->consultarComprovante($id) e devolve o array cru do banco.


10. GET /payments/boletos/dda

BoletoPaymentService::ddaAccount::getBank()->consultarBoletosDDA($filtros) (query string, exceto page/per_page). Retorna o array do banco (vazio no Teste).


11. GET /payments/boletos — histórico

?status= (um dos valores de StatusBoletoPayment), ?search= (casa assignor, description e barcode só-dígitos), ?per_page= (default 15). Ordenado por created_at desc; devolve BoletoWithdrawResource::collection paginado com o envelope { data, meta }.


12. Modelo boleto_withdraws (App\Models\BoletoWithdraw)

Migrações: 0001_01_01_000007_* (base) + 2026_09_01_000006_align_boleto_withdraws_for_payments_table.php.

ColunaTipoNota
unique_hashstring?sha256(account_id | barcode | value) (trait HasUniqueHash)
statusstringcast StatusBoletoPayment (pending/processing/awaiting_approval/completed/canceled)
valuedecimal(12,2)valor a debitar
barcodestring?linha digitável / código de barras (só dígitos)
assignorstring?beneficiário (exibição em lista)
descriptionstring?descrição informada no pagamento
txidstring?id do pagamento no PSP (ou autenticação)
jsonjsonsnapshot do BoletoReturnDTO da consulta
metadatajsondados de trabalho + resposta do banco
due_datetimestamp?vencimento
scheduled_attimestamp?data do agendamento
paid_attimestamp?liquidação
deleted_attimestamp?soft delete

Relação account (belongsTo).


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

Sandbox: um único adaptador, com mocks determinísticos. guardBank() lança um erro claro caso um provedor real futuro não implemente algum método.

MétodoUsoBankInterface?
getBoletoDetails(barcode): BoletoReturnDTOinspectnão
payBoleto(barcode, data): arraypaynão
pagarBoleto(barcode, data, idempotencyKey?)schedulesim
consultarComprovante(idPagamento)receiptsim
cancelarAgendamento(idPagamento)cancel (agendado)sim
consultarBoletosDDA(filters)ddasim

BoletoReturnDTO (App\DTO\BoletoReturnDTO) normaliza a consulta: valor/valorAtual, dataVencimento (timestamp), nomeBeneficiario, nomeBanco, linhaDigitavel, nossoNumero, pagavel, vencido, json.


14. Frontend

fastgivr-internet-bank/src/services/payments.tspayments.boletos.{ list, get, inspect, pay, schedule, cancel, receipt, dda }. Tela src/pages/payments/BoletoPaymentPage.tsx (rota /pagamentos/boleto sob AppLayout):

  1. ConsultarboletoPaymentsApi.inspect({ barcode }); guarda o id retornado e exibe beneficiário / vencimento / valor.
  2. Confirmar — diálogo com resumo + input do PIN (6 dígitos); botão só habilita com pin.length === 6.
  3. PagarboletoPaymentsApi.pay(id, { pin, description? }); em sucesso, abre o comprovante local e limpa o formulário.

Erros do backend (422 regra de negócio, 403/409/429 PIN) sobem como rejeição do Axios e são exibidos via toast.

FastGivr API Documentation