Verifying COINSBUY Callback Signatures

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

Comments

0 comments

Article is closed for comments.