Webhooks Set Up

Introduction

Webhooks allow you to receive real-time notifications about the statuses of your payments and payouts, such as successful completion, failure, or cancellation. These notifications are sent whenever there are changes in the status of your transactions.

How to Use

There are two ways to configure webhooks: you can set up webhooks globally or specify them individually for each payment or payout.

  1. Global Webhook

A global webhook URL applies to all your payments and payouts. Any changes in their statuses will trigger a notification sent to this designated URL.

  • Go to Setup. Merchant Account → Settings → API Settings → Webhook → Enter your desired webhook URL → Change → Confirm.

  1. Individual Webhook

You can also specify a webhook URL for each individual payment or payout you create. This provides more granular control over notifications. If a webhook URL is not specified during payment/payout creation, the global webhook URL (if set) will be used.

Here's an example of how you can include the webhookUrl in the request body using curl for an individual payment:

curl --request POST \
  --url https://api.bitnbox.io/v1/payment \
  --header 'Content-Type: application/json' \
  --header 'x-api-key: YOUR-API-KEY' \
  --data '{
  "amount": 1,
  "method": "direct",
  "platformFeeByUser": false,
  "currency": "<string>",
  "network": "<string>",
  "customerId": "<string>",
  "customerIp": "<string>",
  "orderId": "<string>",
  "orderDescription": "<string>",
  "additionalData": "<string>",
  "webhookUrl": "https://example.com/webhook"
}'

Response

  • HTTP status code is 200 (Required)

For each transaction, Bitnbox sends multiple webhooks in a predefined order corresponding to the transaction status changes. The next webhook is sent only after the previous one has been successfully delivered. A delivery is considered successful if your server responds with HTTP 200 status code.

If the response is a 4xx or 5xx code, Bitnbox retries the delivery. Up to 12 retries are performed, with each retry delayed exponentially: 2^retryCount minutes (for example, 2, 4, 8, 16, etc.). If all retry attempts fail, the webhook is marked as Canceled, and no further webhooks for this transaction are sent.

How to Check Signature

To ensure the integrity and authenticity of the data received through webhooks, it's crucial to verify the x-signature in the headers against the request body using your secure API key.

Here's a breakdown of the verification process:

  1. Retrieve the x-signature from the request headers.

  2. Compute a signature using your API key and the request body. All fields from body in stringified JSON format.

  3. Compare the computed signature with the x-signature from the headers.

Example

const crypto = require('crypto');

// Function to verify signature
function verifySignature(apiKey, requestBody, signatureHeader) {
    // Compute the signature using the API key and the request body
    const computedSignature = crypto.createHmac('sha256', apiKey)
                                    .update(requestBody)
                                    .digest('hex');

    // Compare the computed signature with the signature from the headers
    return computedSignature === signatureHeader;
}

// Example data
const apiKey = 'YOUR-API-KEY';
const body = {
          data: { ... },
          meta: {... }                            
      }; // Example body
const stringifiedBody = JSON.stringify(body);
const signatureHeader = 'a2b4f9c285e38d73eeb9d3c2b478d5e1'; // Example signature from headers

// Verify the signature
const isSignatureValid = verifySignature(apiKey, stringifiedBody, signatureHeader);

// Output result
console.log('Is signature valid?', isSignatureValid);

This process ensures that the data received is secure and has not been tampered with.

Example

Example with real data (BinancePay)

const crypto = require('crypto');

// Function to verify signature
function verifySignature(apiKey, requestBody, signatureHeader) {
    // Compute the signature using the API key and the request body
    const computedSignature = crypto.createHmac('sha256', apiKey)
                                    .update(requestBody)
                                    .digest('hex');

    // Compare the computed signature with the signature from the headers
    return computedSignature === signatureHeader;
}

// Example data
const apiKey = '67f2c8b4-68e1-4019-ae07-83437681ee5e';
const body = {
    "data": {
      "method": "binancepay",
      "status": "waiting",
      "network": "binancepay",
      "orderId": "1234",
      "currency": "usdt",
      "products": [
        {
          "id": "11",
          "name": "Product name",
          "type": "02",
          "description": "Product description"
        }
      ],
      "createdAt": "2025-07-24T09:24:25.348Z",
      "expiredAt": "2025-07-24T12:24:25.346Z",
      "payAmount": "10",
      "paymentId": "a7d950b9-38d1-4e2a-9992-fa0d98fd0d6d",
      "updatedAt": "2025-07-24T09:24:25.348Z",
      "customerId": "id",
      "customerIp": "ip",
      "paidAmount": null,
      "webhookUrl": "https://slimy-nurse-29.webhook.cool",
      "additionalData": "data",
      "merchantAmount": null,
      "payCheckoutUrl": "https://pay.binance.com/en/checkout/811bf3c80f634f65b98e911f632e18b6",
      "processingFeeByUser": false
    },
    "meta": {
      "testMode": false,
      "event": "payment",
      "webhookId": "4d9f67c6-73c5-40fb-8f85-6fba37affcd3",
      "merchantId": "ce7d802e-a14e-4d6b-8b39-9b57caef7756"
    }
  };


const requestBody = JSON.stringify(body);
const signatureHeader = 'f8d2adf5a749ad3b3d2a87b93eb0301898c21917d40709c1074e96e2df6c89f4'; // Example signature from headers

// Verify the signature
const isSignatureValid = verifySignature(apiKey, requestBody, signatureHeader);

// Output result
console.log('Is signature valid?', isSignatureValid);

Last updated