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: full system access. Merchant: manage orders via API keys. Trader: accept/confirm payment orders.
Authentication
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)
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"
}
}
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
}
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"
}
}
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)
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"
}
]
}
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"
}
}
}
Response — 200 OK
{
"message": "Order declined"
}
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
}
}
Response — 200 OK
{
"orders": [
{
"id": "ord_xK9m2nPq",
"amount": 500000,
"status": "accepted",
"paymentDetails": { "cardNumber": "2200 **** **** 1234" },
"expiresAt": "2025-01-15T10:45:00Z"
}
]
}
Response — 200 OK
{
"orders": [
{
"id": "ord_xK9m2nPq",
"amount": 500000,
"status": "completed",
"fee": 12500,
"completedAt": "2025-01-15T10:42:15Z"
}
]
}
Response — 200 OK
{
"stats": {
"balance": 1250000,
"pendingBalance": 500000,
"todayEarnings": 87500,
"totalEarned": 45250000,
"todayOrders": 12,
"completionRate": 94.5
}
}
Admin
Requires Bearer <admin_token>
Response — 200 OK
{
"stats": {
"completedVolume": 854200000,
"feesEarned": 21355000,
"activeMerchants": 24,
"onlineTraders": 8,
"fraudEvents": 3
}
}
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"
}
]
}
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"
}
}
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"
}
}
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"
}
]
}
Request Body
{
"name": "New Trader",
"email": "trader@example.com",
"password": "secure_password"
}
Response — 201 Created
{
"trader": {
"id": "usr_jkl012",
"name": "New Trader",
"status": "active"
}
}
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
}
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"
}
}
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"
}
]
}
Response — 200 OK
{
"disputes": [
{
"id": "disp_001",
"orderId": "ord_xK9m2nPq",
"merchantName": "Casino XYZ",
"traderName": "Jane Smith",
"amount": 500000,
"reason": "Payment not received",
"status": "open"
}
]
}
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
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).