API Reference

API Documentation

Complete reference for integrating with the BrudaPay P2P payment gateway. All endpoints use JSON and require Bearer token authentication unless noted otherwise.

🔐 Authentication

All authenticated endpoints require a Bearer token in the Authorization header. Obtain a token via the login endpoint.

Authorization: Bearer <access_token>

User Roles

Admin Merchant Trader

Admin: full system access. Merchant: manage orders via API keys. Trader: accept/confirm payment orders.

🔐 Authentication

POST /api/auth/login Authenticate and receive access token

Request Body

{
  "email": "user@example.com",
  "password": "secure_password"
}

Response — 200 OK

{
  "access_token": "eyJhbGciOiJIUzI1NiIs...",
  "user": {
    "id": "usr_abc123",
    "email": "merchant@example.com",
    "name": "John Doe",
    "role": "merchant"
  }
}

Error — 401 Unauthorized

{
  "error": "Invalid credentials"
}

🏪 Orders (Merchant)

POST /api/merchant/orders Create a new deposit order

Requires Bearer <merchant_token>

Request Body

{
  "amount": 500000,
  "currency": "RUB",
  "method": "CARD",
  "description": "Deposit for account #12345",
  "metadata": {
    "userId": "12345",
    "promo": "WELCOME"
  }
}

Fields: amount (integer, kopecks, required), currency (RUB|USD|USDT, default RUB), method (CARD|SBP, required), description (string, optional), metadata (object, optional, max 4KB).

Response — 201 Created

{
  "order": {
    "id": "ord_xK9m2nPq",
    "amount": 500000,
    "currency": "RUB",
    "method": "CARD",
    "status": "created",
    "description": "Deposit for account #12345",
    "metadata": { "userId": "12345" },
    "createdAt": "2025-01-15T10:30:00Z",
    "expiresAt": "2025-01-15T10:45:00Z"
  }
}
GET /api/merchant/orders List merchant's orders

Requires Bearer <merchant_token>

Query Parameters

status=pending|created|accepted|confirmed|completed|cancelled|expired
sort=createdAt|-createdAt
limit=20
offset=0

Response — 200 OK

{
  "orders": [
    {
      "id": "ord_xK9m2nPq",
      "amount": 500000,
      "currency": "RUB",
      "method": "CARD",
      "status": "completed",
      "traderName": "Trader#42",
      "fee": 12500,
      "createdAt": "2025-01-15T10:30:00Z"
    }
  ],
  "total": 47
}
GET /api/merchant/orders/:id Get order details

Response — 200 OK

{
  "order": {
    "id": "ord_xK9m2nPq",
    "amount": 500000,
    "currency": "RUB",
    "method": "CARD",
    "status": "accepted",
    "description": "Deposit for account #12345",
    "metadata": { "userId": "12345" },
    "traderName": "Jane Smith",
    "fee": 12500,
    "createdAt": "2025-01-15T10:30:00Z",
    "expiresAt": "2025-01-15T10:45:00Z"
  }
}
POST /api/merchant/orders/:id/cancel Cancel a pending/created order

Only works on orders with status created or pending. Returns 400 if order is already accepted or completed.

Response — 200 OK

{
  "message": "Order cancelled",
  "order": {
    "id": "ord_xK9m2nPq",
    "status": "cancelled"
  }
}

Orders (Trader)

GET /api/trader/orders/pending List orders available for acceptance

Requires Bearer <trader_token>

Response — 200 OK

{
  "orders": [
    {
      "id": "ord_xK9m2nPq",
      "amount": 500000,
      "currency": "RUB",
      "method": "CARD",
      "merchantName": "Casino XYZ",
      "description": "Deposit for account #12345",
      "createdAt": "2025-01-15T10:30:00Z",
      "expiresAt": "2025-01-15T10:45:00Z"
    }
  ]
}
POST /api/trader/orders/:id/accept Accept a pending order

Assigns the order to the trader. Payment details are returned in the response. The order moves to accepted status and starts a 15-minute timer.

Response — 200 OK

{
  "order": {
    "id": "ord_xK9m2nPq",
    "status": "accepted",
    "paymentDetails": {
      "cardNumber": "2200 **** **** 1234",
      "holderName": "IVAN IVANOV",
      "bank": "Sberbank",
      "expiresAt": "2025-01-15T10:45:00Z"
    }
  }
}
POST /api/trader/orders/:id/decline Decline a pending order

Response — 200 OK

{
  "message": "Order declined"
}
POST /api/trader/orders/:id/confirm Confirm payment received

Confirms that the payment was received. Order moves to confirmed then completed status. The merchant is notified via webhook.

Request Body (optional)

{
  "payerName": "Ivan Petrov",
  "note": "Confirmed via phone call"
}

Response — 200 OK

{
  "order": {
    "id": "ord_xK9m2nPq",
    "status": "completed",
    "fee": 12500
  }
}
GET /api/trader/orders/active List currently active orders

Response — 200 OK

{
  "orders": [
    {
      "id": "ord_xK9m2nPq",
      "amount": 500000,
      "status": "accepted",
      "paymentDetails": { "cardNumber": "2200 **** **** 1234" },
      "expiresAt": "2025-01-15T10:45:00Z"
    }
  ]
}
GET /api/trader/orders/history Completed order history

Response — 200 OK

{
  "orders": [
    {
      "id": "ord_xK9m2nPq",
      "amount": 500000,
      "status": "completed",
      "fee": 12500,
      "completedAt": "2025-01-15T10:42:15Z"
    }
  ]
}
GET /api/trader/dashboard/stats Trader balance and daily statistics

Response — 200 OK

{
  "stats": {
    "balance": 1250000,
    "pendingBalance": 500000,
    "todayEarnings": 87500,
    "totalEarned": 45250000,
    "todayOrders": 12,
    "completionRate": 94.5
  }
}

🛡️ Admin

GET /api/admin/dashboard/stats System-wide statistics

Requires Bearer <admin_token>

Response — 200 OK

{
  "stats": {
    "completedVolume": 854200000,
    "feesEarned": 21355000,
    "activeMerchants": 24,
    "onlineTraders": 8,
    "fraudEvents": 3
  }
}
GET /api/admin/merchants List all merchants

Response — 200 OK

{
  "merchants": [
    {
      "id": "usr_abc123",
      "name": "Casino XYZ",
      "email": "admin@casinoxyz.com",
      "status": "active",
      "balance": 15200000,
      "apiKey": "mk_live_xK9m2nPqR5tW...",
      "createdAt": "2024-12-01T00:00:00Z"
    }
  ]
}
POST /api/admin/merchants Create a new merchant

Request Body

{
  "name": "New Casino",
  "email": "admin@newcasino.com",
  "password": "secure_password"
}

Response — 201 Created

{
  "merchant": {
    "id": "usr_def456",
    "name": "New Casino",
    "email": "admin@newcasino.com",
    "apiKey": "mk_live_aB3cD4eF...",
    "status": "active"
  }
}
GET /api/admin/merchants/:id Get merchant details

Response — 200 OK

{
  "merchant": {
    "id": "usr_abc123",
    "name": "Casino XYZ",
    "email": "admin@casinoxyz.com",
    "status": "active",
    "balance": 15200000,
    "apiKey": "mk_live_xK9m2nPqR5tW...",
    "webhookUrl": "https://casinoxyz.com/webhook",
    "createdAt": "2024-12-01T00:00:00Z"
  }
}
GET /api/admin/traders List all traders

Response — 200 OK

{
  "traders": [
    {
      "id": "usr_ghi789",
      "name": "Jane Smith",
      "email": "jane@example.com",
      "status": "active",
      "isOnline": true,
      "balance": 3200000,
      "createdAt": "2024-12-15T00:00:00Z"
    }
  ]
}
POST /api/admin/traders Create a new trader

Request Body

{
  "name": "New Trader",
  "email": "trader@example.com",
  "password": "secure_password"
}

Response — 201 Created

{
  "trader": {
    "id": "usr_jkl012",
    "name": "New Trader",
    "status": "active"
  }
}
GET /api/admin/orders List all orders with filters

Query Parameters

status=pending|created|accepted|confirmed|completed|cancelled|declined|expired
q=search_term
limit=20
offset=0
sort=-createdAt

Response — 200 OK

{
  "orders": [
    {
      "id": "ord_xK9m2nPq",
      "amount": 500000,
      "currency": "RUB",
      "method": "CARD",
      "status": "completed",
      "merchantName": "Casino XYZ",
      "traderName": "Jane Smith",
      "createdAt": "2025-01-15T10:30:00Z"
    }
  ],
  "total": 256
}
GET /api/admin/orders/:id Get order full details

Response — 200 OK

{
  "order": {
    "id": "ord_xK9m2nPq",
    "amount": 500000,
    "currency": "RUB",
    "method": "CARD",
    "status": "completed",
    "description": "Deposit for account #12345",
    "metadata": { "userId": "12345" },
    "merchantName": "Casino XYZ",
    "traderName": "Jane Smith",
    "fee": 12500,
    "createdAt": "2025-01-15T10:30:00Z",
    "expiresAt": "2025-01-15T10:45:00Z"
  }
}
GET /api/admin/fraud List fraud events

Response — 200 OK

{
  "events": [
    {
      "id": "fraud_001",
      "type": "velocity_check",
      "userName": "suspicious_user",
      "orderId": "ord_abc123",
      "score": 85,
      "description": "More than 5 orders in 1 minute",
      "createdAt": "2025-01-15T11:00:00Z"
    }
  ]
}
GET /api/admin/disputes List disputes

Response — 200 OK

{
  "disputes": [
    {
      "id": "disp_001",
      "orderId": "ord_xK9m2nPq",
      "merchantName": "Casino XYZ",
      "traderName": "Jane Smith",
      "amount": 500000,
      "reason": "Payment not received",
      "status": "open"
    }
  ]
}
PUT /api/admin/disputes/:id/resolve Resolve a dispute

Request Body

{
  "action": "refund_merchant",
  "note": "Trader did not confirm payment in time"
}

Actions: refund_merchant | pay_trader | split

Response — 200 OK

{
  "dispute": {
    "id": "disp_001",
    "status": "resolved"
  }
}

🔔 Webhooks

Webhook Overview

BrudaPay sends HTTP POST requests to your configured webhook URL when order statuses change. All webhook payloads are signed with HMAC-SHA256 for verification.

Webhook Events

order.created order.accepted order.confirmed order.completed order.cancelled order.expired order.declined

Webhook Payload

{
  "event": "order.completed",
  "timestamp": "2025-01-15T10:42:15Z",
  "order": {
    "id": "ord_xK9m2nPq",
    "amount": 500000,
    "currency": "RUB",
    "status": "completed",
    "fee": 12500,
    "metadata": { "userId": "12345" },
    "completedAt": "2025-01-15T10:42:15Z"
  }
}

HMAC-SHA256 Signature Verification

Each webhook request includes an X-BrudaPay-Signature header containing the HMAC-SHA256 hash of the raw request body using your webhook secret.

Node.js

const crypto = require('crypto');

function verifyWebhook(body, signature, secret) {
  const expected = crypto
    .createHmac('sha256', secret)
    .update(body)
    .digest('hex');
  return crypto.timingSafeEqual(
    Buffer.from(signature),
    Buffer.from(expected)
  );
}

const valid = verifyWebhook(
  req.rawBody,
  req.headers['x-brudapay-signature'],
  WEBHOOK_SECRET
);

Python

import hmac, hashlib

def verify_webhook(body, signature, secret):
    expected = hmac.new(
        secret.encode(),
        body.encode(),
        hashlib.sha256
    ).hexdigest()
    return hmac.compare_digest(
        signature, expected
    )

valid = verify_webhook(
    request.body,
    request.headers['X-BrudaPay-Signature'],
    WEBHOOK_SECRET
)

Important: Always use constant-time comparison (timingSafeEqual / compare_digest) to prevent timing attacks. Return HTTP 200 within 5 seconds or the webhook will be retried (up to 5 retries with exponential backoff).