BlendFi

Implemente um offramp USDT → Pix

Caminho de ponta a ponta para uma integração offramp. Da cotação ao webhook de conclusão, com tratamento de standby.

Este tutorial cobre uma integração offramp completa, da primeira cotação até o webhook de conclusão. Use como ponto de partida para sua implementação.

Pré-requisitos

  • Chave de API ativa. Veja Sandbox e chaves.
  • Cliente final cadastrado e com KYC aprovado. Veja Fluxo de KYC.
  • Chave Pix do destinatário registrada no cliente final.
  • Endpoint de webhook configurado com a verificação de assinatura usando o header X-Blendfi-Signature. Veja Integração de webhooks.

Passo 1: criar a cotação

Sua organização chama POST /v1/quotes com o cliente final, o tipo pix_offramp, o valor em USDT (source_amount) ou BRL (target_amount) e a chave Pix de destino.

POST /v1/quotes HTTP/1.1
Authorization: Bearer sk_test_…
Idempotency-Key: <uuid>
Content-Type: application/json

{
  "user_id": "usr_01J...",
  "transaction_type": "pix_offramp",
  "source_amount": "100.00",
  "pix_key": "..."
}

A resposta carrega id, exchange_rate, source_amount, target_amount, expires_at. Mostre target_amount ao cliente final como o valor que ele vai receber em BRL. A taxa vale 5 minutos.

📋 Schema completo em Reference (em breve)

A documentação detalhada do payload aparece aqui em conjunto com a disponibilidade da API. Os campos do exemplo acompanham o desenho conceitual em Cotação.

Passo 2: aceitar a cotação e mostrar o endereço de depósito

Confirmação do cliente final: aceite a cotação com POST /v1/quotes/:id/accept. Em uma única chamada atômica, a BlendFi cria a conversão, emite o endereço de depósito, reserva o limite e abre a janela de 15 minutos.

POST /v1/quotes/qt_01J.../accept HTTP/1.1
Authorization: Bearer sk_test_…
Idempotency-Key: <uuid>

A resposta é a conversão completa, com id, status='awaiting_deposit', deposit_address, deposit_address_network, deposit_window_expires_at. Mostre deposit_address com QR ao cliente final, junto de expected_source_amount e do prazo restante.

Recomendações de UX:

  • Renderize um contador regressivo até deposit_window_expires_at. Após o prazo, o depósito segue para standby.
  • Mostre expected_source_amount em destaque. Cliente final que ajusta o valor "no clique" causa standby.
  • Ofereça um botão "cancelar" que chama POST /v1/conversions/:id/cancel enquanto a conversão estiver em awaiting_deposit.

Passo 3: cliente final transfere USDT (fora da API)

O depósito on-chain acontece fora da API. Sua integração não chama nada nesse passo; apenas espera o webhook.

Passo 4: receber o webhook

A BlendFi entrega um webhook conforme o desfecho. Verifique a assinatura no header X-Blendfi-Signature (o payload assinado é {t}.{raw_body}, HMAC-SHA256 com o segredo do endpoint).

Eventos possíveis:

  • conversion.completed: caminho feliz. O Pix foi entregue ao destinatário.
  • conversion.standby: divergência (under, over, ou janela expirada). Payload inclui standby_reason, received_amount, expected_source_amount. Veja Passo 5b.
  • conversion.failed: erro irrecuperável após funded ou liquidated. Payload inclui failure_reason.
  • conversion.abandoned: 7 dias em standby sem ação. Resolução manual com a BlendFi.

Recomendações:

  • Trate o handler como idempotente: a mesma entrega pode chegar mais de uma vez.
  • Use o conversion_id como chave de controle de duplicatas no seu lado.

📋 Schema completo em Reference (em breve)

Os payloads exatos vão na seção de webhooks quando o catálogo de eventos conversion.* for publicado.

Passo 5a: caminho feliz (conversion.completed)

Recebeu conversion.completed. O Pix está entregue. Mostre confirmação ao cliente final. O payload inclui o identificador Pix de ponta a ponta para sua referência interna ou de auditoria.

Fim do fluxo nominal.

Passo 5b: caminho de standby (conversion.standby)

Recebeu conversion.standby. A conversão está parada com received_amount em custódia. Decida com base em standby_reason:

standby_reasonDecisão usual
under_fundedLiquidar pelo valor recebido (cliente final fica com BRL menor que o esperado, comunique a divergência) ou contatar o suporte para resolução caso a caso.
over_fundedLiquidar pelo valor recebido (cliente final ganha BRL extra) ou contatar o suporte.
window_expiredLiquidar (taxa nova será cotada) ou contatar o suporte.

Para liquidar:

POST /v1/conversions/cnv_01J.../liquidate HTTP/1.1
Authorization: Bearer sk_test_…
Idempotency-Key: <uuid>

A BlendFi cota uma nova taxa server-side sobre o received_amount atual e dispara a liquidação. A resposta carrega a conversão com liquidation_quote_id populado e status liquidated. Você ainda vai receber conversion.completed (ou conversion.failed) quando a liquidação Pix terminar.

Para esperar e ver: não fazer nada por até 7 dias. Após o prazo, a conversão vira abandoned e a resolução é manual. Veja Conversão abandonada.

Para cancelar antes do depósito chegar (cenário diferente do standby): POST /v1/conversions/:id/cancel, válido apenas em awaiting_deposit. Depois do depósito chegar, cancel não é mais opção. Veja Cancelamento.

Passo 6: tratamento de erros e idempotência

  • Idempotency-Key em todo POST mutador. A retentativa segura de uma chamada já bem-sucedida retorna a mesma resposta sem efeitos colaterais.
  • Handler de webhook idempotente. Use conversion_id (e o tipo de evento) como chave de controle de duplicatas.
  • Verificação de assinatura. Rejeite qualquer entrega cuja assinatura não confira. Veja Concepts → Webhooks.
  • Tratamento de lock. Erro de lock no aceite indica que o cliente final já tem outra conversão offramp aberta. Leve a UX a tratar a conversão existente, não a criar uma nova. Veja Lock por usuário e tipo.
  • Reconciliação de borda. Em incidente, leia GET /v1/conversions?status=... para reconstruir o estado do que está aberto.

Próximos passos

Nesta página