API Documentation
API документация — ваш быстрый вход в интеграцию с нашим платёжным шлюзом. Ниже: правила формирования ссылок на платёжную страницу, подписи запросов, формат вебхуков и готовые примеры кода для Node.js и Python.
Редирект пользователя на платёжное окно
Сформируйте подпись и соберите URL
- Создайте проект и получите ключи.
- Посчитайте подпись (SHA-256 → Base64).
- Сделайте редирект на https://payment.twizzygate.tech/
Базовый URL и параметры
Базовый URL:
Copied!
| Параметр | Тип | Обязат. | Пример | Описание |
|---|---|---|---|---|
| orderNum | string | Да | 456 | ID заказа на стороне мерчанта |
| amount | number | Да | 100.00 | Сумма (2 знака или целое) |
| currency | string | Да | RUB | Валюта |
| client | string | Да | pub_123 | Публичный ключ клиента |
| sign | string | Да | 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¤cy=RUB&orderNum=456&my_secretsign:
KhizMnxNLB/XuSSoLhLZJz/+oq4nwLr3UVTeOYNOWlo=Советы: используйте стабильный формат суммы (например 100.00), не включайте пустые поля в подпись, храните секрет только на бэкенде.
Примеры
Готовый URL
Copied!
https://payment.twizzygate.tech/?orderNum=456&amount=100.00¤cy=RUB&client=pub_123&sign=KhizMnxNLB%2FXuSSoLhLZJz%2F%2Boq4nwLr3UVTeOYNOWlo%3DcURL
Copied!
curl -L
"https://payment.twizzygate.tech/?orderNum=456&amount=100.00¤cy=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.
- Используйте поля orderNum и status из тела запроса.
- Отсортируйте ключи по алфавиту.
- Соберите строку key=value через & и добавьте в конец ваш секретный ключ (без имени).
- Посчитайте SHA-256 и закодируйте результат в Base64.
- Сравните полученное значение с 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 сравнение при проверке подписи.