BlendFi

Ciclo de vida da transação

Da cotação até a liquidação — todo estado que uma transação BlendFi pode estar, as transições legais entre eles e como reagir a cada um.

Fluxo principal

Ciclo de vida da transação

Uma transação BlendFi movimenta dinheiro em três passos: seu usuário final paga BRL via Pix, a BlendFi compra o equivalente em USDT numa corretora regulada, e o USDT é liquidado on-chain na carteira que você indicou. Este guia mostra cada estado pelo qual a transação passa, quando o dinheiro vira irreversível e o que fazer em cada parada.

O caminho feliz

cotação ──► transação criada ──► pending_payment

                                       ▼  (Pix recebido)
                                 payment_received

                                       ▼  (corretora preencheu)
                                  buying_crypto

                                       ▼  (envio on-chain)
                                  sending_crypto

                                       ▼  (minerado)
                                   completed

Cada seta é um evento determinístico. Não tem polling do seu lado — webhooks anunciam cada transição. No sandbox, você dirige esses eventos sozinho com os test helpers.

Três conceitos

Cotações duram pouco

Uma cotação fixa uma taxa BRL→USDT por uns 30 segundos. Você converte numa transação com `POST /v1/transactions`. Uma vez consumida, fim.

Transações são irreversíveis depois de `buying_crypto`

Até `payment_received`, a gente consegue estornar. Depois que compramos cripto, liquidamos — estornos viram operacionais, não automáticos.

Falhas são explícitas

Toda falha terminal tem um `code` dizendo o motivo. Não existe estado ambíguo de `desconhecido`.

Catálogo de estados

EstadoSignificadoEstado do dinheiroPróximos estados legais
pending_paymentTransação criada; aguardando Pix do usuário finalSem movimentopayment_received, cancelled, expired, failed
payment_receivedPix chegou na nossa conta de liquidaçãoBRL conosco, USDT ainda nãobuying_crypto, failed
buying_cryptoEstamos executando a ordem BRL→USDT na corretoraBRL comprometido, USDT em trânsitosending_crypto, failed, refunded
sending_cryptoUSDT comprado; transferência on-chain submetida para a carteira de destinoUSDT em trânsitocompleted, failed
completedTransferência on-chain confirmadaUSDT entregue(terminal)
expiredO usuário final não pagou dentro do TTL do PixSem movimento(terminal)
cancelledVocê cancelou antes do pagamento chegarSem movimento(terminal)
refundedA gente estornou BRL para o usuário final (após progresso parcial)BRL devolvido(terminal)
failedAlgo quebrou em um dos provedores externosVeja failure_reason(terminal)

Limites de segurança do dinheiro

Onde fica o ponto sem volta

Até e incluindo payment_received, a BlendFi pode estornar o usuário final automaticamente. Quando a transação entra em buying_crypto, o BRL já foi comprometido na corretora — estornos ainda são possíveis (estado refunded) mas são operacionais, não automáticos, e exigem que seu suporte coordene com a gente. Depois de sending_crypto o USDT está on-chain; "estorno" significa devolver USDT para uma carteira remetente, o que é uma transação separada que seu usuário final tem que executar.

Andando pelo caminho feliz

1. Pegue uma cotação

Cotações fixam a taxa por uma janela curta. TTL padrão é 30 segundos.

curl -X POST $BLENDFI_BASE/v1/quotes \
  -H "Authorization: Bearer $BLENDFI_KEY" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "content-type: application/json" \
  -d '{
    "user_id": "01J...",
    "source_amount": "100.00",
    "source_currency": "BRL",
    "target_currency": "USDT",
    "destination_wallet_address": "0xabc...",
    "destination_wallet_network": "polygon"
  }'

Resposta:

{
  "id": "01JQ...",
  "rate": "5.42",
  "source_amount": "100.00",
  "target_amount": "18.4502",
  "expires_at": "2026-04-29T13:00:30Z"
}

Se você esperar demais, a cotação retorna 409 quote_expired na hora de consumir. Pega uma nova.

2. Crie a transação

Converta a cotação numa transação. Depois dessa chamada, a transação está em pending_payment e estamos esperando o Pix do usuário final.

curl -X POST $BLENDFI_BASE/v1/transactions \
  -H "Authorization: Bearer $BLENDFI_KEY" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "content-type: application/json" \
  -d '{"quote_id": "01JQ..."}'

Resposta:

{
  "id": "01JT...",
  "status": "pending_payment",
  "pix_qr_code": "00020126...",
  "pix_copy_paste": "...",
  "expires_at": "2026-04-29T13:30:00Z"
}

Mostre o QR Code Pix ou o copia-e-cola para seu usuário final. Ele tem até expires_at (geralmente 30 minutos) para pagar.

3. Usuário final paga via Pix

Quando o Pix chega, a BlendFi muda a transação para payment_received. Você fica sabendo de duas formas:

  • Webhook (recomendado): a gente dispara um POST para sua URL de webhook registrada com o novo estado.
  • Polling (fallback): GET /v1/transactions/{id} mostra o estado atual.
{
  "id": "01JT...",
  "status": "payment_received",
  "paid_at": "2026-04-29T13:04:12Z",
  ...
}

4. BlendFi compra USDT e liquida on-chain

Isso é automático. A transação passa por buying_crypto (ordem na corretora) → sending_crypto (envio on-chain) → completed (minerado). Cada transição vira um webhook.

O payload terminal completed inclui o hash da transação on-chain, que você pode mostrar para seu usuário final:

{
  "id": "01JT...",
  "status": "completed",
  "usdt_tx_hash": "0xabc123...",
  "completed_at": "2026-04-29T13:08:05Z"
}

Modos de falha

Toda falha terminal vem com um failure_reason. As quatro que você realmente vai encontrar:

failure_reasonEm qual estadoO que aconteceuSua ação
payment_timeoutpending_paymentexpiredUsuário final não pagou antes da expiração do PixAvise o usuário a tentar de novo com cotação nova
exchange_rejectedbuying_cryptofailedA corretora não conseguiu preencher a ordem BRL→USDT na taxa fixadaEstorno iniciado automaticamente; entramos em contato
chain_revertsending_cryptofailedA transação on-chain foi revertida (gas, RPC, problema de rede)Estorno operacional — abra um ticket com o request_id
kyc_blockedpending_paymentfailedO status de KYC do usuário mudou no meio do fluxo (revogado, expirado)Trate na sua UI; o usuário tem que verificar de novo

Se você ver um estado failed sem failure_reason, trate como internal_error — abra um ticket com o ID da transação.

Dirigindo a máquina de estados no sandbox

O sandbox não aceita pagamentos Pix de verdade nem fala com uma corretora real. Em vez disso, ele oferece endpoints test_helpers que mapeiam 1:1 para transições reais de estado, então sua integração vê a mesma sequência de eventos que veria em produção.

TXID=01JT...
H='-H "Authorization: Bearer $BLENDFI_KEY"'
I='-H "Idempotency-Key: $(uuidgen)"'

# Caminho feliz
curl -X POST $BLENDFI_BASE/v1/test_helpers/transactions/$TXID/pay $H $I
curl -X POST $BLENDFI_BASE/v1/test_helpers/transactions/$TXID/fill-hedge $H $I
curl -X POST $BLENDFI_BASE/v1/test_helpers/transactions/$TXID/send-crypto $H $I
curl -X POST $BLENDFI_BASE/v1/test_helpers/transactions/$TXID/confirm-crypto $H $I

Variantes de falha que você pode simular:

EndpointEfeito
POST /v1/test_helpers/transactions/{id}/expirePula a espera do Pix → expired
POST /v1/test_helpers/transactions/{id}/fail-hedgeSimula exchange_rejectedfailed
POST /v1/test_helpers/transactions/{id}/fail-cryptoSimula chain_revertfailed

Use isso para validar suas retentativas, exibição de erros e tratamento de estorno antes de tocar em produção.

Webhooks vs polling

Para confiabilidade nível parceiro, registre uma URL de webhook. A gente faz POST de cada transição de estado nela com o corpo completo da transação e um cabeçalho assinado que você pode verificar.

Se um webhook estiver indisponível, a BlendFi tenta de novo com backoff exponencial (1m → 1h → 6h → 24h). Você também pode chamar GET /v1/transactions/{id} a qualquer momento para ler o estado canônico — não tem penalidade de rate-limit para polling de "já terminou?" dentro de limites razoáveis.

Cadência de polling

Polling de uma vez por segundo por transação é tranquilo. Polling a cada 100ms em milhares de transações não é — você vai bater em rate_limit_exceeded. Se você quiser polling mais apertado, é sinal de que precisa de webhook.

O que ler em seguida

FAQ

Quanto tempo as cotações duram? TTL padrão é 30 segundos. O expires_at exato vem na resposta da cotação — não hardcode 30s, leia o campo.

E se o usuário final pagar atrasado, depois da expiração do Pix? A transação já está expired. O banco do Pix devolve o pagamento atrasado. A gente não cria uma transação nova automaticamente.

Posso cancelar uma transação pending_payment antes do usuário pagar? Pode. POST /v1/transactions/{id}/cancel (com chave de idempotência) move ela para cancelled. Depois que o Pix chega, cancelar exige fluxo de estorno.

E se a rede on-chain estiver congestionada e a transferência demorar horas? O estado fica em sending_crypto. A gente retenta com gas mais alto se a transação original ficar travada. Não damos timeout em failed por congestionamento — só por revert explícito ou erro de RPC.

Preenchimentos parciais são possíveis na corretora? Não. A gente só aceita preenchimentos all-or-nothing. Um preenchimento parcial é tratado como rejeição e a transação vai para failed com failure_reason: exchange_rejected.

Por que existe um payment_received separado e um buying_crypto, em vez de ir direto de um para o outro? Os dois eventos têm latências diferentes. Pix geralmente liquida em menos de 10 segundos; a ordem na corretora pode levar até um minuto em dias movimentados. Expor separados deixa sua UI mostrar "recebemos seu pagamento, agora convertendo…" — parceiros nos dizem que os usuários finais preferem isso a um spinner ambíguo.

Nesta página