Подпись генерируется с использованием вашего приватного ключа, который предоставляется в Личном Кабинете. Публичный ключ и NONCE передаются в заголовках.

Формула подписи

Сообщение для подписи формируется одинаково для всех типов запросов:

path+body+nonce
pathПуть к эндпоинту, например: /api/v1/balance
bodyJSON строка тела запроса с ключами в алфавитном порядке (пустая строка для GET)
nonceУникальное числовое значение — всегда в конце
⚠️
Важно: JSON ключи в теле запроса должны идти в алфавитном порядке! Значение nonce ВСЕГДА добавляется в конец сообщения для подписи.
GET запрос
/api/v1/balance1721585422
POST запрос
/api/v1/pay-in{"amount":"1000","bankId":1,"callbackURL":"https://test.com/callback","currencyId":1,"externalID":"test123","method":"CARD"}1721585422

Генерация подписи

const crypto = require('crypto');

function sortObjectKeys(obj) {
  if (obj === null || typeof obj !== 'object' || Array.isArray(obj)) {
    return obj;
  }
  const sortedObj = {};
  const keys = Object.keys(obj).sort();
  for (const key of keys) {
    sortedObj[key] = sortObjectKeys(obj[key]);
  }
  return sortedObj;
}

function generateSignature(path, body, nonce, privateKey) {
  let bodyString = '';
  if (body && typeof body === 'object') {
    const sortedBodyObj = sortObjectKeys(body);
    bodyString = JSON.stringify(sortedBodyObj);
  }
  // Формируем строку: path + body + nonce
  const stringToSign = path + bodyString + nonce;
  
  const signature = crypto.createHmac('sha512', privateKey)
    .update(stringToSign)
    .digest('hex');
    
  return { stringToSign, signature, body: bodyString };
}

// Пример для GET запроса
const result = generateSignature(
  '/api/v1/balance',
  null,
  generateNonce(),
  'your_private_key_here'
);

// Пример для POST запроса
const postResult = generateSignature(
  '/api/v1/pay-in',
  {
    amount: "1000",
    bankId: 1,
    callbackURL: "https://test.com/callback",
    currencyId: 1,
    externalID: "test123",
    method: "CARD"
  },
  generateNonce(),
  'your_private_key_here'
);
<?php
function sortObjectKeys($obj) {
    if ($obj === null || !is_array($obj)) {
        return $obj;
    }
    ksort($obj);
    foreach ($obj as $key => $value) {
        $obj[$key] = sortObjectKeys($value);
    }
    return $obj;
}

function generateSignature($path, $body, $nonce, $privateKey) {
    $bodyString = '';
    if ($body && is_array($body)) {
        $sortedBodyObj = sortObjectKeys($body);
        $bodyString = json_encode($sortedBodyObj, JSON_UNESCAPED_SLASHES);
    }
    // path + body + nonce
    $stringToSign = $path . $bodyString . $nonce;
    $signature = hash_hmac('sha512', $stringToSign, $privateKey);
    
    return [
        'stringToSign' => $stringToSign,
        'signature'    => $signature,
        'body'         => $bodyString
    ];
}

$result = generateSignature(
    '/api/v1/balance',
    null,
    generateNonce(),
    'your_private_key_here'
);
?>
import hmac
import hashlib
import json

def sort_object_keys(obj):
    if obj is None or not isinstance(obj, dict):
        return obj
    return {key: sort_object_keys(obj[key]) for key in sorted(obj.keys())}

def generate_signature(path, body, nonce, private_key):
    body_string = ''
    if body and isinstance(body, dict):
        sorted_body = sort_object_keys(body)
        body_string = json.dumps(sorted_body, separators=(',', ':'))
    
    # path + body + nonce
    string_to_sign = path + body_string + str(nonce)
    
    signature = hmac.new(
        private_key.encode('utf-8'),
        string_to_sign.encode('utf-8'),
        hashlib.sha512
    ).hexdigest()
    
    return {
        'stringToSign': string_to_sign,
        'signature': signature,
        'body': body_string
    }
using System;
using System.Security.Cryptography;
using System.Text;

// Канонизация JSON (сортировка ключей)
public static class JsonCanonicalizer
{
    public static string Canonicalize(object body)
    {
        if (body == null) return string.Empty;
        var token = JToken.FromObject(body);
        return JsonConvert.SerializeObject(SortToken(token));
    }

    private static JToken SortToken(JToken token)
    {
        if (token is JObject obj)
        {
            var sortedObj = new JObject();
            foreach (var prop in obj.Properties()
                .OrderBy(p => p.Name, StringComparer.Ordinal))
                sortedObj[prop.Name] = SortToken(prop.Value);
            return sortedObj;
        }
        if (token is JArray arr)
        {
            var newArr = new JArray();
            foreach (var item in arr) newArr.Add(SortToken(item));
            return newArr;
        }
        return token;
    }
}

// Генерация подписи
public static class PlatixSignature
{
    public static string ComputeSignature(
        string path, string bodyString, 
        string nonce, string privateKey)
    {
        string stringToSign = path + bodyString + nonce;
        using var hmac = new HMACSHA512(
            Encoding.UTF8.GetBytes(privateKey)
        );
        var hash = hmac.ComputeHash(
            Encoding.UTF8.GetBytes(stringToSign)
        );
        return BitConverter.ToString(hash)
            .Replace("-", "").ToLowerInvariant();
    }
}

Генерация NONCE

NONCE — уникальное числовое значение для каждого запроса. Система запоминает последний NONCE для каждого мерчанта и отклоняет запросы с меньшим или равным значением.

💡
Структура NONCE: TTTTTTTTTTTTT (13 цифр времени) + CCC (3 цифры счётчика) + RR (2 случайных цифры) = 18 цифр
Время (13 цифр)Date.now() — текущее время в миллисекундах
Счётчик (3 цифры)Инкрементируется при каждом вызове, сбрасывается на 999
Случайность (2 цифры)Случайное число 0-99 для дополнительной энтропии
let counter = 0;

function generateNonce() {
  const timePart    = Date.now();                              // 13 цифр
  const counterPart = (counter++ % 1000).toString().padStart(3, "0"); // 3
  const randomPart  = Math.floor(Math.random() * 100)
                        .toString().padStart(2, "0");          // 2

  return parseInt(`${timePart}${counterPart}${randomPart}`);
}

const nonce = generateNonce();
// Example: 172325680000000112
<?php
function generateNonce() {
    static $counter = 0;
    $timePart    = (string) round(microtime(true) * 1000); // 13 цифр
    $counterPart = str_pad(($counter++ % 1000), 3, "0", STR_PAD_LEFT);
    $randomPart  = str_pad(mt_rand(0, 99), 2, "0", STR_PAD_LEFT);
    return (int) ($timePart . $counterPart . $randomPart);
}
$nonce = generateNonce();
?>
import time
import random

def generate_nonce():
    counter = 0
    def inner():
        nonlocal counter
        time_part    = str(int(time.time() * 1000))  # 13 цифр
        counter_part = str(counter % 1000).zfill(3)  # 3 цифры
        random_part  = str(random.randint(0, 99)).zfill(2)  # 2 цифры
        counter += 1
        return int(time_part + counter_part + random_part)
    return inner()
using System;
using System.Security.Cryptography;
using System.Threading;

public static class NonceGenerator
{
    private static int _counter = 0;

    public static string GenerateNonce()
    {
        string timePart    = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds().ToString();
        string counterPart = (Interlocked.Increment(ref _counter) % 1000)
                                .ToString().PadLeft(3, '0');
        string randomPart  = RandomNumberGenerator.GetInt32(0, 100)
                                .ToString().PadLeft(2, '0');
        return $"{timePart}{counterPart}{randomPart}";
    }

    public static long GenerateNonceAsLong() => long.Parse(GenerateNonce());
}
Преимущества подхода: поддержка до 1,000 RPS без коллизий, 1,000,000 уникальных значений/мс, совместимость с MySQL, PostgreSQL, Redis.

Обязательные заголовки

Content-Typeapplication/json
Public-KeyВаш публичный ключ из Личного Кабинета
nonceУникальное числовое значение (должно быть больше предыдущего)
SignatureHMAC-SHA512 подпись, сгенерированная с частным ключом
http
Content-Type: application/json
nonce: 1717025133
Public-Key: your_public_key_here
Signature: 2816894fc8ebe05d47e96eca553ee3ca59863ae8d41a25a42d92b71df5e0e95b4490cfc8ff180e7575c5dbbc643ab3842ca05ae8bbb9f08e57c58cab748f8677