Saltar para o conteúdo principal

Introdução

Geralmente, quando você faz uma solicitação a um endpoint de API, espera obter uma resposta quase imediata. No entanto, algumas solicitações podem levar muito tempo para processar, o que pode levar a erros de timeout. Para evitar um erro de timeout, uma resposta pendente é retornada. Como seus registros precisam ser atualizados com o estado final da solicitação, você precisa:
  1. Fazer uma solicitação de atualização (popularmente conhecida como polling) ou,
  2. Ouvir eventos usando uma URL de webhook.
Dica Útil
Recomendamos que você use webhook para fornecer valor aos seus clientes em vez de usar callbacks ou polling. Com callbacks, não temos controle sobre o que acontece no lado do cliente. Nem você. Callbacks podem falhar se a conexão de rede no dispositivo de um cliente falhar ou estiver fraca ou se o dispositivo desligar após uma transação.

Polling vs Webhooks

Polling requer fazer uma solicitação GET em intervalos regulares para obter o status final de uma solicitação. Por exemplo, quando você emite um endereço para um cliente para depósitos, você continua fazendo uma solicitação de transações vinculadas ao endereço até encontrar uma. Com webhooks, o servidor de recursos, Blockradar neste caso, envia atualizações para o seu servidor quando o status da sua solicitação muda. A mudança no status de uma solicitação é conhecida como um evento. Você normalmente ouvirá esses eventos em um endpoint POST chamado URL de webhook. A tabela abaixo destaca algumas diferenças entre polling e webhooks:
PollingWebhooks
Modo de atualizaçãoManualAutomático
Limitação de taxaSimNão
Impactado por escalaSimNão

Criar uma URL de webhook

Uma URL de webhook é simplesmente um endpoint POST para o qual um servidor de recursos envia atualizações. A URL precisa analisar uma solicitação JSON e retornar um 200 OK:
// Using Express
app.post("/my/webhook/url", function(req, res) {
    // Retrieve the request's body
    const event = req.body;
    // Do something with event
    res.send(200);
});
Quando sua URL de webhook recebe um evento, ela precisa analisar e reconhecer o evento. Reconhecer um evento significa retornar um 200 OK no cabeçalho HTTP. Sem um 200 OK no cabeçalho de resposta, continuaremos enviando eventos pelas próximas 2 horas e 35 minutos:
  • Tentaremos enviar webhooks 5 vezes com um atraso crescente entre cada tentativa, começando com 5 minutos e aumentando exponencialmente até 80 minutos. A duração total dessas 5 tentativas abrangerá aproximadamente 2 horas e 35 minutos.
Evite tarefas de longa duração
Se você tiver tarefas de longa duração em sua função de webhook, você deve reconhecer o evento antes de executar as tarefas de longa duração. Tarefas de longa duração levarão a um timeout de solicitação e uma resposta de erro automática do seu servidor. Sem uma resposta 200 OK, tentamos novamente conforme descrito no parágrafo acima.

Testando Webhooks Localmente

Durante o desenvolvimento, você pode configurar sua URL de webhook no ambiente de carteira principal TEST para um endereço local, como http://localhost ou 127.0.0.1. Para expor seu servidor local à internet para fins de teste, considere usar uma ferramenta como ngrok ou webhook.site. Essas ferramentas permitem que você veja como é a carga útil do webhook e simule eventos de webhook localmente antes de implantar seu aplicativo em um ambiente ativo. Usando essas ferramentas, você pode garantir que sua integração de webhook esteja funcionando corretamente e lidar com quaisquer problemas em um ambiente local e controlado antes de passar para produção.

Reenviar Webhook de Transação

No caso em que por algum motivo você não recebe o webhook de transação e o tempo de backoff expirou, fornecemos uma API que você pode usar para reenviar um webhook de transação
Use com cautela!
Como isso pode levar ao processamento de transações já concluídas, certifique-se de ter validação adequada em vigor ao usar este endpoint.
curl --request POST \
  --url https://api.blockradar.co/v1/wallets/{walletId}/transactions/webhooks/resend \
  --header 'Content-Type: application/json' \
  --header 'x-api-key: <api-key>' \
  --data '{
  "id": "TRANSACTION_ID"
}'

Verificar origem do evento

Como sua URL de webhook está disponível publicamente, você precisa verificar se os eventos se originam do Blockradar e não de um agente mal-intencionado. Existem duas maneiras de garantir que os eventos para sua URL de webhook sejam do Blockradar:
  1. Validação de assinatura (recomendado)

Validação de assinatura

Eventos enviados do Blockradar carregam o cabeçalho x-blockradar-signature. O valor deste cabeçalho é uma assinatura HMAC SHA512 da carga útil do evento assinada usando sua chave secreta. A verificação da assinatura do cabeçalho deve ser feita antes de processar o evento:
const crypto = require('crypto');
const apiKey = process.env.WALLET_API_KEY;
// Using Express
app.post("/my/webhook/url", function(req, res) {
    //validate event
    const hash = crypto.createHmac('sha512', apiKey).update(JSON.stringify(req.body)).digest('hex');
    if (hash == req.headers['x-blockradar-signature']) {
    // Retrieve the request's body
    const event = req.body;
    // Do something with event  
    }
    res.send(200);
});

Checklist de Entrada em Produção

Agora que você criou com sucesso sua URL de webhook, aqui estão algumas maneiras de garantir que você tenha uma experiência agradável:
  • Adicione a URL do webhook em suas Carteiras Principais no painel Blockradar
  • Certifique-se de que sua URL de webhook esteja disponível publicamente (URLs localhost não podem receber eventos)
  • Se estiver usando .htaccess, lembre-se de adicionar a barra / final à URL
  • Teste seu webhook para garantir que você está recebendo o corpo JSON e retornando uma resposta HTTP 200 OK
  • Se sua função de webhook tiver tarefas de longa duração, você deve primeiro reconhecer o recebimento do webhook retornando um 200 OK antes de prosseguir com as tarefas de longa duração
  • Se não recebermos uma resposta HTTP 200 OK de seus webhooks, sinalizamos como uma tentativa falhada
  • Tentaremos enviar webhooks 5 vezes com um atraso crescente entre cada tentativa, começando com 5 minutos e aumentando exponencialmente até 80 minutos. A duração total dessas 5 tentativas abrangerá aproximadamente 2 horas e 35 minutos.

Tipos de eventos

Aqui estão os eventos que atualmente levantamos. Adicionaremos mais a esta lista à medida que nos conectarmos a mais ações no futuro.

Exemplos de Eventos

Abaixo estão exemplos de cargas úteis de webhook para alguns dos eventos mais comuns:
{
    "event": "deposit.success",
    "data": {
        "id": "6d2f9646-cae4-48a5-8bfe-1f9379868d4f",
        "reference": "LSk5RLfSrR",
        "senderAddress": "0x2455eC6700092991Ce0782365A89d5Cd89c8Fa22",
        "recipientAddress": "0xe1037B45b48390285e5067424053fa35c478296b",
        "amount": "10.0",
        "amountPaid": "10.0",
        "fee": null,
        "currency": "USD",
        "blockNumber": 6928760,
        "blockHash": "0x5f2e0ed782752b9559e7a3d89c0fb9f6706e4866e74ba7a434cf933bb3f02a2b",
        "hash": "0x94c733496df59c15e5a489f20374096bba31166a8e149ceea4d410e3e5821357",
        "confirmations": 6,
        "confirmed": true,
        "gasPrice": "1201381238",
        "gasUsed": "62159",
        "gasFee": "0.000074676656372842",
        "status": "SUCCESS",
        "type": "DEPOSIT",
        "note": null,
        "amlScreening": {
            "provider": "ofac",
            "status": "success",
            "message": "Address is not sanctioned"
        },
        "assetSwept": null,
        "assetSweptAt": null,
        "assetSweptGasFee": null,
        "assetSweptHash": null,
        "assetSweptSenderAddress": null,
        "assetSweptRecipientAddress": null,
        "assetSweptAmount": null,
        "reason": null,
        "network": "testnet",
        "chainId": 11155111,
        "metadata": {
            "user_id": 1
        },
        "createdAt": "2024-10-23T11:19:58.451Z",
        "updatedAt": "2024-10-23T11:19:58.451Z",
        "asset": {
            "id": "fe04a28c-c615-4e41-8eda-f84c862864f5",
            "name": "USDC Coin",
            "symbol": "USDC",
            "decimals": 6,
            "address": "0x1c7D4B196Cb0C7B01d743Fbc6116a902379C7238",
            "standard": "ERC20",
            "isActive": true,
            "logoUrl": "https://res.cloudinary.com/blockradar/image/upload/v1716800083/crypto-assets/usd-coin-usdc-logo_fs9mhv.png",
            "network": "testnet",
            "createdAt": "2024-05-14T11:53:33.682Z",
            "updatedAt": "2024-06-14T22:32:12.589Z"
        },
        "address": {
            "id": "0a69c48a-6c6f-422c-bd6a-70de3306a3ac",
            "address": "0xe1037B45b48390285e5067424053fa35c478296b",
            "name": "Customer 1",
            "isActive": true,
            "type": "INTERNAL",
            "derivationPath": "m/44'/60'/0'/0/87",
            "metadata": {
                "user_id": 1
            },
            "configurations": {
                "aml": {
                    "status": "success",
                    "message": "Address is not sanctioned",
                    "provider": "ofac"
                },
                "showPrivateKey": false,
                "disableAutoSweep": false,
                "enableGaslessWithdraw": false
            },
            "network": "testnet",
            "createdAt": "2024-10-23T11:13:40.446Z",
            "updatedAt": "2024-10-23T11:13:40.446Z"
        },
        "blockchain": {
            "id": "85ffc132-3972-4c9e-99a5-5cf0ccb688bf",
            "name": "ethereum",
            "symbol": "eth",
            "slug": "ethereum",
            "derivationPath": "m/44'/60'/0'/0",
            "isEvmCompatible": true,
            "logoUrl": "https://res.cloudinary.com/blockradar/image/upload/v1716800081/crypto-assets/ethereum-eth-logo_idraq2.png",
            "isActive": true,
            "tokenStandard": "ERC20",
            "createdAt": "2024-05-14T11:53:33.095Z",
            "updatedAt": "2024-06-14T22:32:11.983Z"
        },
        "wallet": {
            "id": "d236a191-c1d4-423c-a439-54ce6542ca41",
            "name": "Ethereum Master Wallet",
            "description": "This is ethereum testnet master wallet",
            "address": "0x947514e4B803e312C312da0F1B41fEDdbe15ae7a",
            "derivationPath": "m/44'/60'/0'/0/0",
            "isActive": true,
            "status": "ACTIVE",
            "network": "testnet",
            "createdAt": "2024-08-22T09:48:56.322Z",
            "updatedAt": "2024-10-23T10:52:34.332Z",
            "business": {
                "id": "4b96c271-35eb-45e8-b558-6a53f95df601",
                "name": "Test One Inc",
                "sector": "Fintech",
                "status": "ACTIVE",
                "createdAt": "2024-08-22T09:28:37.522Z",
                "updatedAt": "2024-08-22T09:28:37.522Z"
            }
        },
        "beneficiary": null
    }
}

Complete Event List

EventDescription
deposit.successTriggered when a deposit transaction is successfully received and confirmed on the blockchain.
deposit.processingTriggered when a deposit transaction is being processed.
deposit.failedTriggered when a deposit transaction fails to be processed or confirmed.
deposit.cancelledTriggered when a deposit transaction is cancelled before completion.
deposit.swept.successTriggered when funds from a deposit are successfully swept (transferred) to the master wallet.
deposit.swept.failedTriggered when the auto-sweep process fails to transfer funds to the master wallet.
withdraw.successTriggered when a withdrawal transaction is successfully executed and confirmed on the blockchain.
withdraw.failedTriggered when a withdrawal transaction fails to execute. This can occur due to insufficient funds, network congestion, or other blockchain-related issues.
withdraw.cancelledTriggered when a withdrawal transaction is cancelled before completion.
gateway-deposit.successTriggered when a USDC deposit to the Gateway Wallet contract is successfully finalized and credited to the unified Gateway balance.
gateway-deposit.failedTriggered when a USDC deposit to the Gateway Wallet contract fails or is rejected.
gateway-deposit.cancelledTriggered when a gateway deposit transaction is cancelled before completion.
gateway-withdraw.successTriggered when USDC is successfully minted and withdrawn on the destination chain through the Gateway Minter contract.
gateway-withdraw.failedTriggered when a withdrawal (mint) transaction via Gateway fails to execute, which may occur because of insufficient unified balance, invalid signature, or blockchain issues.
gateway-withdraw.cancelledTriggered when a gateway withdrawal transaction is cancelled before completion.
signed.successTriggered when a signed transaction is successfully executed, however, transaction is not broadcast on the blockchain.
signed.failedTriggered when a signed transaction fails to execute. This can occur due to network congestion, or other blockchain-related issues.
signed.cancelledTriggered when a signed transaction is cancelled before completion.
swap.successTriggered when a swap transaction is successfully executed and the assets are exchanged.
swap.failedTriggered when a swap transaction fails to execute. This can occur due to insufficient funds, network congestion, or swap contract errors.
swap.cancelledTriggered when a swap transaction is cancelled before completion.
custom-smart-contract.successTriggered when a custom smart contract transaction is successfully executed. This indicates that the contract interaction completed successfully on the blockchain.
custom-smart-contract.failedTriggered when a custom smart contract transaction fails to execute. This can occur due to various reasons such as insufficient funds, network congestion, or smart contract errors.
custom-smart-contract.cancelledTriggered when a custom smart contract transaction is cancelled before completion.
staking.successTriggered when a staking transaction is successfully executed and the assets are staked.
staking.failedTriggered when a staking transaction fails to execute. This can occur due to insufficient funds, network congestion, or staking contract errors.
staking.cancelledTriggered when a staking transaction is cancelled before completion.
unstaking.successTriggered when an unstaking transaction is successfully executed and the assets are unstaked.
unstaking.failedTriggered when an unstaking transaction fails to execute. This can occur due to insufficient staked balance, network congestion, or staking contract errors.
unstaking.cancelledTriggered when an unstaking transaction is cancelled before completion.
unstaking.withdraw.successTriggered when unstaked assets are successfully withdrawn to the user’s address.
unstaking.withdraw.failedTriggered when the withdrawal of unstaked assets fails to execute. This can occur due to insufficient balance, network congestion, or other blockchain-related issues.
unstaking.withdraw.cancelledTriggered when an unstaking withdrawal transaction is cancelled before completion.
restaking.successTriggered when a restaking transaction is successfully executed and the assets are restaked.
restaking.failedTriggered when a restaking transaction fails to execute. This can occur due to insufficient funds, network congestion, or staking contract errors.
restaking.cancelledTriggered when a restaking transaction is cancelled before completion.
auto-settlement.successTriggered when an auto-settlement transaction is successfully executed and the settlement is completed.
auto-settlement.failedTriggered when an auto-settlement transaction fails to execute. This can occur due to insufficient funds, network congestion, or settlement contract errors.
auto-settlement.cancelledTriggered when an auto-settlement transaction is cancelled before completion.
salvage.successTriggered when a salvage operation is successfully executed and the assets are recovered.
salvage.failedTriggered when a salvage operation fails to execute. This can occur due to insufficient funds, network congestion, or salvage contract errors.
salvage.cancelledTriggered when a salvage operation is cancelled before completion.

Transaction Types and Statuses

Transaction Types

The following transaction types are used in webhook payloads:
TypeDescription
DEPOSITA deposit transaction where funds are received into a wallet address
WITHDRAWA withdrawal transaction where funds are sent from a wallet to an external address
SIGNEDA signed transaction where transaction is only signed and not broadcast to network
GATEWAY_DEPOSITA deposit transaction where funds are received into a gateway wallet address
GATEWAY_WITHDRAWA withdrawal transaction where funds are sent from a gateway wallet to an external address
SALVAGEA salvage operation to recover stuck or lost funds
AUTO_SETTLEMENTAn automatic settlement transaction
AUTO_FUNDINGAn auto-funding transaction that mints and moves stablecoins
STAKINGA staking transaction where assets are staked
UNSTAKINGAn unstaking transaction where staked assets are unstaked
RESTAKINGA restaking transaction where assets are restaked
UNSTAKING_WITHDRAWA withdrawal of unstaked assets
CUSTOM_SMART_CONTRACTA custom smart contract interaction
SWAPA swap transaction where one asset is exchanged for another

Transaction Statuses

The following statuses indicate the current state of a transaction:
StatusDescription
PENDINGTransaction is being processed but not yet confirmed
PROCESSINGTransaction is currently being processed
SUCCESSTransaction has been successfully executed and confirmed
FAILEDTransaction failed to execute or was rejected
INCOMPLETETransaction is incomplete and may require additional steps
CANCELLEDTransaction was cancelled before completion
These transaction types and statuses are used in the type and status fields of webhook payloads to help you understand what kind of transaction occurred and its current state.
Happy hacking! ❤️