API Documentation

API документация — ваш быстрый вход в интеграцию с нашим платёжным шлюзом. Ниже: правила формирования ссылок на платёжную страницу, подписи запросов, формат вебхуков и готовые примеры кода для Node.js и Python.

Редирект пользователя на платёжное окно

Сформируйте подпись и соберите URL

  • Создайте проект и получите ключи.
  • Посчитайте подпись (SHA-256 → Base64).
  • Сделайте редирект на https://payment.twizzygate.tech/

Базовый URL и параметры

Базовый URL:
Copied!
https://payment.twizzygate.tech/
ПараметрТипОбязат.ПримерОписание
orderNumstringДа456ID заказа на стороне мерчанта
amountnumberДа100.00Сумма (2 знака или целое)
currencystringДаRUBВалюта
clientstringДаpub_123Публичный ключ клиента
signstringДаBase64Подпись (см. ниже)
Важно! Для подписи исключите client и sign. Отсортируйте ключи, склейте key=value через & и добавьте в конец secret без имени.

Подпись запроса

  • Исключите client и sign из набора данных.
  • Отсортируйте ключи по алфавиту (ASCII).
  • Соберите строку: k1=v1&k2=v2&...&secret.
  • Посчитайте SHA-256 от строки и закодируйте в Base64.
Copied!
// ⚠️ Считайте подпись на сервере. Секрет не уходит на фронт.
import crypto from "node:crypto";

type Params = Record<string, string | number | undefined>;

function makeSign(params: Params, secret: string): string {
  const entries = Object.entries(params)
    .filter(([k, v]) => k !== "client" && k !== "sign" && v !== undefined)
    .sort(([a], [b]) => a.localeCompare(b));

  const base = entries.map(([k, v]) => `${k}=${v}`).join("&") + `&${secret}`;
  const digest = crypto.createHash("sha256").update(base, "utf8").digest();
  return Buffer.from(digest).toString("base64");
}

const params = { orderNum: "456", amount: "100.00", currency: "RUB", client: "pub_123" };
const secret = "my_secret";
const sign = makeSign(params, secret);
console.log(sign);
Тест-векторы
params: {amount: "100.00", currency: "RUB", orderNum: "456"}, secret: my_secret
base: amount=100.00&currency=RUB&orderNum=456&my_secret
sign: KhizMnxNLB/XuSSoLhLZJz/+oq4nwLr3UVTeOYNOWlo=
Советы:  используйте стабильный формат суммы (например 100.00), не включайте пустые поля в подпись, храните секрет только на бэкенде.

Примеры

Готовый URL
Copied!
https://payment.twizzygate.tech/?orderNum=456&amount=100.00&currency=RUB&client=pub_123&sign=KhizMnxNLB%2FXuSSoLhLZJz%2F%2Boq4nwLr3UVTeOYNOWlo%3D
cURL
Copied!
curl -L
  "https://payment.twizzygate.tech/?orderNum=456&amount=100.00&currency=RUB&client=pub_123&sign=KhizMnxNLB%2FXuSSoLhLZJz%2F%2Boq4nwLr3UVTeOYNOWlo%3D"

Ошибки

При некорректной подписи или параметрах пользователь увидит страницу с ошибкой.

Коллбек с результатом операции (Webhook)

После обработки платежа мы отправим POST-запрос на ваш бэкенд с результатом операции. URL вы настраиваете в параметрах проекта.

Метод
POST
URL

Указывается вами (например: https://merchant.com/payment/callback).

Заголовки
НазваниеОписание
X-Client-KeyПубличный ключ клиента.
X-Signature Подпись запроса (по тем же правилам подписи, что и редирект).
Тело запроса (application/json)
{
  "orderNum": "456",
  "status": "success",
  "details": {}
}
  • orderNum — ID заказа на вашей стороне.
  • status — "success", "fail" или "cancel".
  • details — объект с дополнительной информацией (может быть пустым).

Подпись коллбека (X-Signature)

Каждый коллбек подписывается. На вашей стороне необходимо пересчитать подпись и сравнить с заголовком X-Signature.

  1. Используйте поля orderNum и status из тела запроса.
  2. Отсортируйте ключи по алфавиту.
  3. Соберите строку key=value через & и добавьте в конец ваш секретный ключ (без имени).
  4. Посчитайте SHA-256 и закодируйте результат в Base64.
  5. Сравните полученное значение с X-Signature.
Пример строки (orderNum=456, status=success, секрет=sec_abc):
orderNum=456&status=success&sec_abc
Copied!
import crypto from "node:crypto";

function makeCallbackSignature(body: { orderNum?: string; status?: string }, secret: string): string {
  const params = {
    orderNum: body.orderNum,
    status: body.status,
  };

  const entries = Object.entries(params)
    .filter(([, v]) => v !== undefined && v !== null)
    .sort(([a], [b]) => a.localeCompare(b));

  const base = entries.map(([k, v]) => `${k}=${v}`).join("&") + `&${secret}`;
  const digest = crypto.createHash("sha256").update(base, "utf8").digest();
  return Buffer.from(digest).toString("base64");
}

function isValidCallback(body: any, headers: Record<string, string | string[] | undefined>, secret: string): boolean {
  const expected = makeCallbackSignature(body, secret);
  const receivedHeader = headers["x-signature"] ?? headers["X-Signature"];
  const received = Array.isArray(receivedHeader) ? receivedHeader[0] : (receivedHeader || "");

  const expBuf = Buffer.from(expected);
  const recBuf = Buffer.from(received);

  if (expBuf.length !== recBuf.length) {
    return false;
  }

  return crypto.timingSafeEqual(expBuf, recBuf);
}
Важно:
  • Секретный ключ храните только на сервере.
  • Проверяйте X-Client-Key и соответствие вашему проекту.
  • Используйте timing-safe сравнение при проверке подписи.