Verifying B2BINPAY Callback Signatures

Have more questions? Submit a request

When your system receives callbacks from the system, it's essential to verify that the request truly originated from B2BINPAY and hasn’t been altered. This is done through HMAC-SHA256 signature verification.


✅ What You Need

  • API login (your API key)
  • API password (your API secret)
  • The following fields from the callback payload:
    • transfer.status
    • transfer.amount
    • deposit.tracking_id
    • meta.time
    • meta.sign

🧮 How Verification Works

  1. Concatenate the following into one string:
    status + amount + tracking_id + time
  2. Create a secret key using:
    sha256(login + password)
  3. Generate a signature using HMAC-SHA256:

    HMAC_SHA256(message, key)
    
  4. Compare the generated signature with meta.sign from the payload.

💻 PHP Example

<?php

# Your API login and password
$login = 'Your API key';
$password = 'Your API secret';

# Parse callback data
$callback_payload = json_decode (
  '{
    "data": {
      "type": "deposit",
      "id": "11203",
      "attributes": {
        "address": "0xcb959a408cbfbe64116a2dadc20188c290226fae",
        "created_at": "2022-07-15T16:51:52.702456Z",
        "tracking_id": "",
        "target_paid": "0.300000000000000000",
        "destination": {
          "address_type": null,
          "address": "0xcb959a408cbfbe64116a2dadc20188c290226fae"
        }
      },
      "relationships": {
        "currency": {
          "data": {
            "type": "currency",
            "id": "1002"
          }
        },
        "wallet": {
          "data": {
            "type": "wallet",
            "id": "318"
          }
        },
        "transfer": {
          "data": {
            "type": "transfer",
            "id": "17618"
          }
        }
      }
    },
    "included": [
      {
        "type": "currency",
        "id": "1002",
        "attributes": {
          "iso": 1002,
          "name": "Ethereum",
          "alpha": "ETH",
          "alias": null,
          "exp": 18,
          "confirmation_blocks": 3,
          "minimal_transfer_amount": "0.000000000000000000",
          "block_delay": 30
        }
      },
      {
        "type": "transfer",
        "id": "17618",
        "attributes": {
          "op_id": 11203,
          "op_type": 1,
          "amount": "0.300000000000000000",
          "commission": "0.001200000000000000",
          "fee": "0.000000000000000000",
          "txid": "0xa09cb1de38b9b21712ff18d08d6a625cc80ec41c9e64586095d4c46449a9eb51",
          "status": 2,
          "user_message": null,
          "created_at": "2022-07-15T16:53:04.098536Z",
          "updated_at": "2022-07-15T16:54:39.903843Z",
          "confirmations": 8,
          "risk": 0,
          "risk_status": 4,
          "amount_cleared": "0.298800000000000000"
        },
        "relationships": {
          "currency": {
            "data": {
              "type": "currency",
              "id": "1002"
            }
          }
        }
      }
    ],
    "meta": {
      "time": "2022-07-15T16:54:39.966327+00:00",
      "sign": "1377e7a9eb3d62f7708285cd148711c62f50e53d8046e4d412a18ae9a575da85"
    }
  }',
  true
);

$callback_sign = $callback_payload['meta']['sign'];
$callback_time = $callback_payload['meta']['time'];

# Get transfer and deposit info
$included_transfer = array_filter(
  $callback_payload['included'],
  function ($item) {
    return $item['type'] === 'transfer';
  }
);
$included_transfer = array_pop($included_transfer)['attributes'];
$deposit = $callback_payload['data']['attributes'];

$status = $included_transfer['status'];
$amount = $included_transfer['amount'];
$tracking_id = $deposit['tracking_id'];

# Create hash
$message = $status . $amount . $tracking_id . $callback_time;
$hash_secret = hash('sha256', $login . $password, true);
$hash_hmac_result = hash_hmac('sha256', $message, $hash_secret);

# Verify
if ($hash_hmac_result === $callback_sign) {
  echo 'Verified';
} else {
  echo 'Invalid sign';
}
echo PHP_EOL;
?>

⚙️ JavaScript (Node.js) Example

const crypto = require('crypto');

// Your API credentials
const login = 'Your API key';
const password = 'Your API secret';

// Example callback payload (parse from request)
const callbackPayload = require('./callback.json'); // or JSON.parse(body)

const transfer = callbackPayload.included.find(item => item.type === 'transfer').attributes;
const deposit = callbackPayload.data.attributes;

const status = transfer.status.toString();
const amount = transfer.amount;
const trackingId = deposit.tracking_id;
const callbackTime = callbackPayload.meta.time;
const callbackSign = callbackPayload.meta.sign;

const message = status + amount + trackingId + callbackTime;
const key = crypto.createHash('sha256').update(login + password).digest();
const hmac = crypto.createHmac('sha256', key).update(message).digest('hex');

if (hmac === callbackSign) {
  console.log('✅ Verified');
} else {
  console.log('❌ Invalid sign');
}

🐍 Python Example

import hashlib
import hmac
import json

login = 'Your API key'
password = 'Your API secret'

with open('callback.json') as f:
    callback_payload = json.load(f)

transfer = next(item for item in callback_payload['included'] if item['type'] == 'transfer')['attributes']
deposit = callback_payload['data']['attributes']

status = str(transfer['status'])
amount = transfer['amount']
tracking_id = deposit['tracking_id']
callback_time = callback_payload['meta']['time']
callback_sign = callback_payload['meta']['sign']

message = f"{status}{amount}{tracking_id}{callback_time}"
key = hashlib.sha256((login + password).encode()).digest()
generated_sign = hmac.new(key, message.encode(), hashlib.sha256).hexdigest()

if generated_sign == callback_sign:
    print("✅ Verified")
else:
    print("❌ Invalid sign")

🛡 Best Practices

  • Always validate callback signatures before trusting the data.
  • Store your login and password securely; never expose them in frontend code.
  • Use HTTPS to receive callbacks securely.

Articles in this section

Was this article helpful?
0 out of 0 found this helpful
Share

Comments

0 comments

Article is closed for comments.