Документация TonPay API
Полное руководство по интеграции
TonPay - это платежная система для приема платежей в TON и Jetton токенах. Данная документация поможет вам интегрировать TonPay в ваше приложение.
- Версия API: 1.0
- Базовый URL:
https://pay.whaile.ru:3000
- Withdraw API:
https://pay.whaile.ru:2998 - Формат данных: JSON
Все суммы должны иметь не более 2 знаков после точки. Все валюты отображаются в верхнем регистре (TON, JETTON). Таймаут платежа составляет 20 минут.
Введение
TonPay предоставляет простой и безопасный способ приема платежей в TON и Jetton токенах.
Основные возможности
- Прием платежей в TON и Jetton токенах
- Автоматическое отслеживание транзакций
- Webhook уведомления о статусе платежей
- Управление несколькими платежными кассами
- Вывод средств с касс
- Детальная статистика и отчеты
Как это работает
- Создайте платежную кассу через API или веб-интерфейс
- Создайте платеж через API, указав сумму и адрес кошелька получателя
- Пользователь отправляет средства на указанный адрес
- Система автоматически отслеживает транзакцию в блокчейне
- При подтверждении транзакции отправляется webhook уведомление
Аутентификация
Для работы с API требуется API токен, который вы можете получить после регистрации и входа в систему.
Получение API токена
API токен доступен в вашем личном кабинете после авторизации. Токен используется для:
- Создания и управления кассами
- Вывода средств
- Доступа к информации о кассах
Использование токена
API токен передается в запросах следующим образом:
В теле запроса (POST/PUT):
{
"user_id": 1,
"api_token": "ваш_api_токен",
"name": "Название кассы"
}
В query параметрах (GET):
https://pay.whaile.ru:3000/cashier/1?user_id=1&api_token=ваш_api_токен
ВажноНикогда не публикуйте ваш API токен в открытом доступе. Храните его в безопасном месте.
Базовый URL
API доступно по следующим адресам:
| Сервис | URL | Описание |
|---|---|---|
| Payment API | https://pay.whaile.ru:3000 |
Создание платежей, управление кассами |
| Withdraw API | https://pay.whaile.ru:2998 |
Вывод средств с касс |
Валюта и суммы
Поддерживаемые валюты
TON - The Open Network (нативная валюта)
JETTON - Jetton токены (требуется указание адреса контракта)
- TON - The Open Network (нативная валюта)
- JETTON - Jetton токены (требуется указание адреса контракта)
Формат сумм
Все денежные суммы должны соответствовать следующим требованиям:
- Максимум 2 знака после точки (например:
0.01,10.50,100.00) - Минимальная сумма платежа:
0.01 - Минимальная сумма вывода:
0.01 - Валюта всегда отображается в верхнем регистре (TON, JETTON)
При создании кассы можно установить минимальную и максимальную сумму платежа. По умолчанию минимальная сумма: 0.01.
Payment API
API для создания и управления платежами
POST /create_payment
Создание нового платежа
Описание
Создает новый платеж в системе. После создания платежа пользователь должен отправить указанную сумму на указанный адрес кошелька. Система автоматически отслеживает транзакцию в блокчейне и отправляет webhook уведомление при подтверждении.
URL
https://pay.whaile.ru:3000/create_payment
Метод
POST
Аутентификация
Не требуется
Параметры запроса
| Параметр | Тип | Обязательный | Описание |
|---|---|---|---|
cashier_id |
integer | Да | ID платежной кассы |
amount |
float | Да | Сумма платежа (максимум 2 знака после точки, минимум 0.01) |
wallet |
string | Да | Адрес кошелька получателя (в любом формате: UQ... или 0:...) |
currency |
string | Нет | Валюта: ton или jetton. Если не указана, берется из настроек кассы |
payload |
string | Нет | Дополнительные данные, которые будут отправлены в webhook |
transaction_uuid |
string | Нет | UUID существующей транзакции (для восстановления платежа) |
return_url |
string | Нет | URL для перенаправления пользователя после успешной оплаты (опционально) |
Валидация
- Сумма должна быть не меньше минимальной суммы кассы (
min_amount) - Сумма не должна превышать максимальную сумму кассы (
max_amount), если она установлена - Сумма должна иметь не более 2 знаков после точки
- Касса должна быть активна (
status = 'active') - У кассы должен быть установлен
webhook_url
Пример запроса
import requests
response = requests.post(
'https://pay.whaile.ru:3000/create_payment',
json={
'cashier_id': 1,
'amount': 0.01,
'wallet': '...',
'currency': 'ton',
'payload': 'order_id=12345',
'return_url': 'https://example.com/success' # Опционально
}
)
data = response.json()
print(data)
Формат ответа (успех)
{
"status": "ok",
"payment_id": 123,
"transaction_uuid": "550e8400-e29b-41d4-a716-446655440000",
"currency": "ton",
"amount": 0.01,
"wallet_to_send": "...",
"return_url": "https://example.com/success",
"message": "Send the exact amount to the specified address. Once confirmed, the application will be automatically updated.",
"time_recorded": 1704067200000
}
Примечания
- Если указан
return_url, пользователь будет автоматически перенаправлен на этот URL после успешной оплаты return_urlможет быть передан как в запросе API, так и в URL страницы оплаты (?return_url=...)
Поля ответа
| Поле | Тип | Описание |
|---|---|---|
status |
string | Статус ответа (всегда "ok" при успехе) |
payment_id |
integer | Уникальный ID платежа |
transaction_uuid |
string | UUID транзакции (можно использовать для восстановления платежа) |
currency |
string | Валюта платежа (ton или jetton) |
amount |
float | Сумма платежа |
wallet_to_send |
string | Адрес кошелька, на который нужно отправить средства |
time_recorded |
integer | Unix timestamp (в миллисекундах) времени создания платежа |
Обработка ошибок
| HTTP код | Описание |
|---|---|
| 400 | Неверные параметры запроса (сумма меньше минимума, превышает максимум, неверный формат суммы и т.д.) |
| 404 | Касса не найдена |
| 500 | Внутренняя ошибка сервера |
Пример ответа с ошибкой
{
"detail": "Amount is less than minimum: 0.01"
}
Примечания
- Платеж имеет таймаут 20 минут. После истечения времени платеж перестает отслеживаться
- Если передан
transaction_uuidсуществующей транзакции с теми же параметрами, платеж будет восстановлен - После создания платежа система автоматически начинает отслеживать транзакцию в блокчейне
- При подтверждении транзакции отправляется webhook уведомление на
webhook_urlкассы
GET /payment_by_uuid/{transaction_uuid}
Получение платежа по UUID транзакции
Описание
Позволяет получить информацию о платеже по его UUID. Полезно для восстановления платежа или проверки его статуса.
URL
https://pay.whaile.ru:3000/payment_by_uuid/{transaction_uuid}
Метод
GET
Параметры пути
| Параметр | Тип | Описание |
|---|---|---|
transaction_uuid |
string | UUID транзакции |
Пример запроса
import requests
uuid = "550e8400-e29b-41d4-a716-446655440000"
response = requests.get(
f'https://pay.whaile.ru:3000/payment_by_uuid/{uuid}'
)
data = response.json()
print(data)
Формат ответа
{
"status": "ok",
"payment_id": 123,
"transaction_uuid": "550e8400-e29b-41d4-a716-446655440000",
"currency": "ton",
"amount": 0.01,
"wallet": "...",
"wallet_to_send": "...",
"payment_status": "nohash",
"cashier_id": 1,
"time_recorded": 1704067200000
}
Возможные статусы платежа
nohash- Ожидание оплаты (транзакция еще не найдена)pending- Транзакция найдена, ожидание подтвержденияsuccess- Платеж успешно выполненerror- Ошибка при обработке платежа
Обработка ошибок
| HTTP код | Описание |
|---|---|
| 404 | Платеж не найден |
| 500 | Внутренняя ошибка сервера |
GET /payment_status/{currency}/{payment_id}
Получение статуса платежа
Описание
Возвращает текущий статус платежа по его ID и валюте.
URL
https://pay.whaile.ru:3000/payment_status/{currency}/{payment_id}
Метод
GET
Параметры пути
| Параметр | Тип | Описание |
|---|---|---|
currency |
string | Валюта: ton или jetton |
payment_id |
integer | ID платежа |
Пример запроса
import requests
response = requests.get(
'https://pay.whaile.ru:3000/payment_status/ton/123'
)
data = response.json()
print(data)
Формат ответа
{
"status": "ok",
"payment_id": 123,
"user_id": 1,
"wallet": "...",
"amount": 0.01,
"payment_status": "success",
"time_recorded": 1704067200000,
"return_url": "https://example.com/success"
}
Поле return_url присутствует только если было указано при создании платежа.
Cashier API
API для управления платежными кассами
POST /create_cashier
Создание новой платежной кассы
Описание
Создает новую платежную кассу. Касса используется для приема платежей. У каждой кассы есть свой баланс, настройки минимальной/максимальной суммы и webhook URL для уведомлений.
URL
https://pay.whaile.ru:3000/create_cashier
Метод
POST
Аутентификация
Требуется. Передайте user_id и api_token в теле запроса.
Параметры запроса
| Параметр | Тип | Обязательный | Описание |
|---|---|---|---|
user_id |
integer | Да | ID пользователя |
api_token |
string | Да | API токен пользователя |
name |
string | Да | Название кассы (1-255 символов) |
description |
string | Нет | Описание кассы |
category |
string | Нет | Категория кассы |
currency |
string | Нет | Валюта по умолчанию: ton или jetton (по умолчанию: ton) |
min_amount |
float | Нет | Минимальная сумма платежа (по умолчанию: 0.01, минимум: 0.01, максимум 2 знака после точки) |
max_amount |
float | Нет | Максимальная сумма платежа (необязательно, максимум 2 знака после точки) |
webhook_url |
string | Нет | URL для отправки webhook уведомлений о платежах |
jetton_address |
string | Условно | Адрес контракта Jetton (обязательно, если currency = "jetton") |
Валидация
- Если
currency = "jetton", тоjetton_addressобязателен min_amountдолжен быть не менее 0.01max_amountдолжен быть большеmin_amount, если указан- Все суммы должны иметь не более 2 знаков после точки
Пример запроса
import requests
response = requests.post(
'https://pay.whaile.ru:3000/create_cashier',
json={
'user_id': 1,
'api_token': 'ваш_api_токен',
'name': 'Мой интернет-магазин',
'description': 'Касса для приема платежей',
'category': 'Электронная коммерция',
'currency': 'ton',
'min_amount': 0.01,
'max_amount': 1000.00,
'webhook_url': 'https://example.com/webhook'
}
)
data = response.json()
print(data)
Формат ответа
{
"status": "ok",
"cashier_id": 1,
"cashier": {
"id": 1,
"user_id": 1,
"name": "Мой интернет-магазин",
"description": "Касса для приема платежей",
"category": "Электронная коммерция",
"currency": "TON",
"status": "active",
"min_amount": 0.01,
"max_amount": 1000.00,
"balance": 0.00,
"webhook_url": "https://example.com/webhook",
"created_at": "2024-01-01 12:00:00"
}
}
Обработка ошибок
| HTTP код | Описание |
|---|---|
| 400 | Неверные параметры (неверный формат суммы, отсутствует jetton_address для jetton и т.д.) |
| 401 | Неверный API токен |
| 500 | Внутренняя ошибка сервера |
GET /cashiers/{user_id}
Получение списка касс пользователя
Описание
Возвращает список всех касс, принадлежащих указанному пользователю.
URL
https://pay.whaile.ru:3000/cashiers/{user_id}
Метод
GET
Аутентификация
Не требуется
Параметры пути
| Параметр | Тип | Описание |
|---|---|---|
user_id |
integer | ID пользователя |
Пример запроса
import requests
response = requests.get(
'https://pay.whaile.ru:3000/cashiers/1'
)
data = response.json()
print(data)
Формат ответа
{
"status": "ok",
"cashiers": [
{
"id": 1,
"user_id": 1,
"name": "Мой интернет-магазин",
"currency": "TON",
"status": "active",
"balance": 10.50,
"min_amount": 0.01,
"max_amount": 1000.00,
"created_at": "2024-01-01 12:00:00"
}
]
}
GET /cashier/{cashier_id}
Получение информации о кассе
Описание
Возвращает детальную информацию о конкретной кассе. Если указаны user_id и api_token, проверяется принадлежность кассы пользователю.
URL
https://pay.whaile.ru:3000/cashier/{cashier_id}?user_id={user_id}&api_token={api_token}
Метод
GET
Аутентификация
Опционально. Если указаны user_id и api_token, проверяется доступ.
Параметры пути
| Параметр | Тип | Описание |
|---|---|---|
cashier_id |
integer | ID кассы |
Query параметры
| Параметр | Тип | Обязательный | Описание |
|---|---|---|---|
user_id |
integer | Нет | ID пользователя (для проверки доступа) |
api_token |
string | Нет | API токен (для проверки доступа) |
Пример запроса
import requests
response = requests.get(
'https://pay.whaile.ru:3000/cashier/1',
params={
'user_id': 1,
'api_token': 'ваш_api_токен'
}
)
data = response.json()
print(data)
Формат ответа
{
"status": "ok",
"cashier": {
"id": 1,
"user_id": 1,
"name": "Мой интернет-магазин",
"description": "Касса для приема платежей",
"category": "Электронная коммерция",
"currency": "TON",
"status": "active",
"min_amount": 0.01,
"max_amount": 1000.00,
"balance": 10.50,
"webhook_url": "https://example.com/webhook",
"jetton_address": null,
"created_at": "2024-01-01 12:00:00"
}
}
Обработка ошибок
| HTTP код | Описание |
|---|---|
| 401 | Неверный API токен |
| 403 | Доступ запрещен (касса принадлежит другому пользователю) |
| 404 | Касса не найдена |
POST /cashier/{cashier_id}/status
Изменение статуса кассы
Описание
Активирует или деактивирует кассу. Неактивные кассы не могут принимать платежи.
URL
https://pay.whaile.ru:3000/cashier/{cashier_id}/status
Метод
POST
Аутентификация
Требуется. Передайте user_id и api_token в теле запроса.
Параметры пути
| Параметр | Тип | Описание |
|---|---|---|
cashier_id |
integer | ID кассы |
Параметры запроса
| Параметр | Тип | Обязательный | Описание |
|---|---|---|---|
user_id |
integer | Да | ID пользователя |
api_token |
string | Да | API токен пользователя |
status |
string | Да | Новый статус: active или inactive |
Пример запроса
import requests
response = requests.post(
'https://pay.whaile.ru:3000/cashier/1/status',
json={
'user_id': 1,
'api_token': 'ваш_api_токен',
'status': 'active'
}
)
data = response.json()
print(data)
Формат ответа
{
"status": "ok",
"message": "Cashier status updated successfully"
}
PUT /cashier/{cashier_id}
Обновление настроек кассы
Описание
Обновляет настройки кассы. Валюта и адрес Jetton не могут быть изменены после создания.
URL
https://pay.whaile.ru:3000/cashier/{cashier_id}
Метод
PUT
Аутентификация
Требуется. Передайте user_id и api_token в теле запроса.
Параметры запроса
| Параметр | Тип | Обязательный | Описание |
|---|---|---|---|
user_id |
integer | Да | ID пользователя |
api_token |
string | Да | API токен пользователя |
name |
string | Нет | Новое название кассы |
description |
string | Нет | Новое описание |
category |
string | Нет | Новая категория |
min_amount |
float | Нет | Новая минимальная сумма (максимум 2 знака после точки, минимум 0.01) |
max_amount |
float | Нет | Новая максимальная сумма (максимум 2 знака после точки, должна быть больше min_amount) |
webhook_url |
string | Нет | Новый webhook URL |
Пример запроса
import requests
response = requests.put(
'https://pay.whaile.ru:3000/cashier/1',
json={
'user_id': 1,
'api_token': 'ваш_api_токен',
'name': 'Обновленное название',
'min_amount': 0.10,
'max_amount': 5000.00
}
)
data = response.json()
print(data)
Формат ответа
{
"status": "ok",
"message": "Cashier updated successfully"
}
Withdraw API
API для вывода средств с касс
POST /withdraw
Вывод средств с кассы
Описание
Выполняет вывод средств с указанной кассы на указанный адрес кошелька. Поддерживает вывод TON и Jetton токенов.
URL
https://pay.whaile.ru:2998/withdraw
Метод
POST
Аутентификация
Требуется. API токен проверяется автоматически на основе кассы.
Параметры запроса
| Параметр | Тип | Обязательный | Описание |
|---|---|---|---|
cashier_id |
integer | Да | ID кассы, с которой выполняется вывод |
amount |
float | Да | Сумма для вывода (максимум 2 знака после точки, минимум 0.01) |
wallet |
string | Да | Адрес кошелька получателя (в любом формате) |
api_token |
string | Да | API токен пользователя (владельца кассы) |
Валидация
- Баланс кассы должен быть достаточным для вывода запрошенной суммы
- Сумма должна быть не менее 0.01
- Сумма должна иметь не более 2 знаков после точки
- Касса должна существовать и принадлежать пользователю
- При выводе TON: комиссия блокчейна вычитается из суммы перевода (пользователь получит сумму минус комиссия)
- При выводе JETTON: требуется наличие активной TON кассы с достаточным балансом для оплаты комиссии блокчейна
Пример запроса
import requests
response = requests.post(
'https://pay.whaile.ru:2998/withdraw',
json={
'cashier_id': 1,
'amount': 1.00,
'wallet': '...',
'api_token': 'ваш_api_токен'
}
)
data = response.json()
print(data)
Формат ответа (успех)
{
"status": "ok",
"message": "Withdrawal successful. Requested: 1.00 TON, blockchain fee: ~0.007 TON, you will receive: ~0.993 TON",
"tx_hash": "0x1234...",
"request_id": "abc123...",
"amount": 1.00,
"currency": "TON",
"blockchain_fee": 0.007,
"requested_amount": 1.00,
"actual_amount": 0.993
}
Статусы вывода
pending- Вывод принят в обработку, транзакция ожидает подтверждения в блокчейнеsuccess- Вывод успешно выполнен, транзакция подтверждена в блокчейнеfailed- Вывод не выполнен, транзакция отклонена или не найдена в блокчейне
Обработка ошибок
| HTTP код | Описание |
|---|---|
| 400 | Недостаточно средств, неверная сумма, неверные параметры |
| 401 | Неверный API токен |
| 404 | Касса не найдена |
| 500 | Ошибка при выполнении перевода |
Примечания
- Вывод выполняется с баланса конкретной кассы, а не с общего баланса пользователя
- Валюта вывода определяется автоматически на основе валюты кассы
- Для Jetton касс требуется, чтобы у кассы был установлен
jetton_address - Транзакция сохраняется в базе данных со статусом
pendingи может быть отслежена - Комиссия блокчейна:
- При выводе TON: комиссия вычитается из суммы перевода (пользователь получает запрошенную сумму минус комиссия)
- При выводе JETTON: комиссия списывается с TON баланса пользователя (требуется активная TON касса)
- Комиссия рассчитывается динамически через API блокчейна (обычно 0.005-0.007 TON для TON, 0.05-0.1+ TON для JETTON)
- С кассы списывается ровно запрошенная сумма, комиссию платит пользователь
- После создания запроса на вывод транзакция получает статус
pending, который обновляется наsuccessилиfailedпосле проверки в блокчейне
Frontend Integration
Интеграция TonPay в ваше приложение
Создание ссылок на оплату
Самый простой способ приема платежей - создание ссылки на оплату
Быстрый старт
Самый простой способ принять платеж - создать ссылку и отправить её пользователю. Пользователь перейдет по ссылке, увидит QR-код и адрес для оплаты, отправит средства, и вы получите webhook уведомление.
Формат ссылки
https://pay.whaile.ru/payment.php?cashier_id={id}&amount={сумма}&wallet={адрес}¤cy={валюта}&payload={данные}&return_url={url}
Обязательные параметры
| Параметр | Описание | Пример |
|---|---|---|
cashier_id |
ID вашей платежной кассы | 1 |
amount |
Сумма платежа (максимум 2 знака после точки) | 10.50 |
wallet |
Адрес кошелька получателя (ваш кошелек) | ... |
Опциональные параметры
| Параметр | Описание | Пример |
|---|---|---|
currency |
Валюта: ton или jetton (если не указана, берется из кассы) |
ton |
payload |
Дополнительные данные (будут отправлены в webhook) | order_id=12345 |
return_url |
URL для перенаправления после успешной оплаты | https://example.com/success |
Примеры ссылок
Простая ссылка для оплаты TON
https://pay.whaile.ru/payment.php?cashier_id=1&amount=10.50&wallet=...
Ссылка с дополнительными данными
https://pay.whaile.ru/payment.php?cashier_id=1&amount=10.50&wallet=...&payload=order_id=12345&user_id=789
Ссылка для оплаты Jetton
https://pay.whaile.ru/payment.php?cashier_id=2&amount=100.00&wallet=...¤cy=jetton
Ссылка с return_url для перенаправления после оплаты
https://pay.whaile.ru/payment.php?cashier_id=1&amount=10.50&wallet=...&return_url=https://example.com/success
Создание ссылки в коде
PHP
<?php
$cashier_id = 1;
$amount = 10.50;
$wallet = "...";
$order_id = 12345;
// Создание ссылки
$payment_url = "https://pay.whaile.ru/payment.php?" . http_build_query([
'cashier_id' => $cashier_id,
'amount' => $amount,
'wallet' => $wallet,
'payload' => "order_id={$order_id}",
'return_url' => 'https://example.com/success' // Опционально
]);
echo "<a href='{$payment_url}'>Оплатить {$amount} TON</a>";
?>
Python
from urllib.parse import urlencode
cashier_id = 1
amount = 10.50
wallet = "..."
order_id = 12345
# Создание ссылки
params = {
'cashier_id': cashier_id,
'amount': amount,
'wallet': wallet,
'payload': f'order_id={order_id}',
'return_url': 'https://example.com/success' # Опционально
}
payment_url = f"https://pay.whaile.ru/payment.php?{urlencode(params)}"
print(f"Оплатить {amount} TON")
JavaScript
const cashierId = 1;
const amount = 10.50;
const wallet = "...";
const orderId = 12345;
// Создание ссылки
const params = new URLSearchParams({
cashier_id: cashierId,
amount: amount,
wallet: wallet,
payload: `order_id=${orderId}`,
return_url: 'https://example.com/success' // Опционально
});
const paymentUrl = `https://pay.whaile.ru/payment.php?${params.toString()}`;
console.log(`Оплатить ${amount} TON`);
Что происходит после перехода по ссылке
- Пользователь переходит по ссылке
- Система создает платеж (или восстанавливает существующий по UUID)
- Происходит автоматический редирект на URL только с
transaction_uuid - Пользователь видит страницу оплаты с QR-кодом и адресом кошелька
- Пользователь отправляет средства на указанный адрес
- Система автоматически отслеживает транзакцию
- При подтверждении отправляется webhook уведомление на ваш сервер
Редирект на чистый URL
После создания платежа происходит автоматический редирект на URL только с transaction_uuid:
https://pay.whaile.ru/payment.php?transaction_uuid=550e8400-e29b-41d4-a716-446655440000
Это позволяет:
- Сохранить ссылку для повторного использования
- Поделиться ссылкой с другими пользователями
- Восстановить платеж позже, используя тот же UUID
Восстановление платежа
Если пользователь перейдет по ссылке с теми же параметрами (cashier_id, amount, wallet), система восстановит существующий платеж по UUID:
# Первый раз - создается новый платеж
https://pay.whaile.ru/payment.php?cashier_id=1&amount=10.50&wallet=UQC...
# Редирект на UUID
https://pay.whaile.ru/payment.php?transaction_uuid=550e8400-...
# Повторный переход с теми же параметрами - восстановление платежа
https://pay.whaile.ru/payment.php?cashier_id=1&amount=10.50&wallet=UQC...
Готовые примеры для копирования
HTML кнопка
<!-- Простая кнопка оплаты -->
<a href="https://pay.whaile.ru/payment.php?cashier_id=1&amount=10.50&wallet=..."
class="btn btn-primary">
Оплатить 10.50 TON
</a>
HTML форма
<form action="https://pay.whaile.ru/payment.php" method="GET">
<input type="hidden" name="cashier_id" value="1">
<input type="hidden" name="amount" value="10.50">
<input type="hidden" name="wallet" value="...">
<input type="hidden" name="payload" value="order_id=12345">
<button type="submit" class="btn btn-primary">Оплатить 10.50 TON</button>
</form>
СоветИспользуйте функцию http_build_query() в PHP или URLSearchParams в JavaScript для безопасного создания URL с параметрами.
Webhook уведомления
Автоматические уведомления о статусе платежей
Описание
При изменении статуса платежа система отправляет POST запрос на webhook_url, указанный в настройках кассы.
Настройка webhook
Укажите webhook_url при создании или обновлении кассы:
# При создании кассы
{
"webhook_url": "https://example.com/webhook"
}
# Или при обновлении
PUT /cashier/1
{
"webhook_url": "https://example.com/webhook"
}
Формат webhook запроса
Система отправляет POST запрос с JSON телом:
{
"payment_id": 123,
"status": "success",
"currency": "ton",
"payload": "order_id=12345"
}
Параметры webhook
| Параметр | Тип | Описание |
|---|---|---|
payment_id |
integer | ID платежа |
status |
string | Статус платежа: nohash, pending, success, error |
currency |
string | Валюта: ton или jetton |
payload |
string | Дополнительные данные, переданные при создании платежа (если были указаны) |
Безопасность webhook (HMAC подпись)
Для защиты от подделки webhook запросов система поддерживает HMAC подпись. Если при создании кассы был указан webhook_secret, все webhook запросы будут содержать заголовок X-Webhook-Signature с подписью.
Формат подписи
Заголовок имеет формат: X-Webhook-Signature: sha256={signature}
Алгоритм проверки подписи
- Получите JSON тело запроса
- Отсортируйте ключи JSON объекта в алфавитном порядке
- Преобразуйте JSON в строку без пробелов:
{"payment_id":123,"status":"success","currency":"ton"} - Вычислите HMAC-SHA256 используя ваш
webhook_secretкак ключ - Сравните полученную подпись с заголовком
X-Webhook-Signature
Пример проверки подписи (Python)
import hmac
import hashlib
import json
def verify_webhook_signature(request_body, signature_header, webhook_secret):
# Парсим JSON
payload = json.loads(request_body)
# Сортируем ключи и создаем строку без пробелов
sorted_payload = json.dumps(payload, sort_keys=True, separators=(',', ':'))
# Вычисляем HMAC-SHA256
expected_signature = hmac.new(
webhook_secret.encode('utf-8'),
sorted_payload.encode('utf-8'),
hashlib.sha256
).hexdigest()
# Извлекаем подпись из заголовка (формат: sha256=...)
received_signature = signature_header.replace('sha256=', '')
# Сравниваем подписи (используем constant-time сравнение)
return hmac.compare_digest(expected_signature, received_signature)
# Пример использования
webhook_secret = "ваш_webhook_secret"
signature_header = request.headers.get('X-Webhook-Signature', '')
request_body = request.get_data(as_text=True)
if verify_webhook_signature(request_body, signature_header, webhook_secret):
# Webhook подлинный
process_webhook(json.loads(request_body))
else:
# Webhook подделан
return {'error': 'Invalid signature'}, 401
Пример проверки подписи (PHP)
<?php
function verifyWebhookSignature($requestBody, $signatureHeader, $webhookSecret) {
// Парсим JSON
$payload = json_decode($requestBody, true);
// Сортируем ключи и создаем строку без пробелов
ksort($payload);
$sortedPayload = json_encode($payload, JSON_UNESCAPED_SLASHES);
// Вычисляем HMAC-SHA256
$expectedSignature = hash_hmac('sha256', $sortedPayload, $webhookSecret);
// Извлекаем подпись из заголовка (формат: sha256=...)
$receivedSignature = str_replace('sha256=', '', $signatureHeader);
// Сравниваем подписи (используем constant-time сравнение)
return hash_equals($expectedSignature, $receivedSignature);
}
// Пример использования
$webhookSecret = "ваш_webhook_secret";
$signatureHeader = $_SERVER['HTTP_X_WEBHOOK_SIGNATURE'] ?? '';
$requestBody = file_get_contents('php://input');
if (verifyWebhookSignature($requestBody, $signatureHeader, $webhookSecret)) {
// Webhook подлинный
$data = json_decode($requestBody, true);
processWebhook($data);
} else {
// Webhook подделан
http_response_code(401);
echo json_encode(['error' => 'Invalid signature']);
exit();
}
?>
Пример проверки подписи (JavaScript/Node.js)
const crypto = require('crypto');
function verifyWebhookSignature(requestBody, signatureHeader, webhookSecret) {
// Парсим JSON
const payload = JSON.parse(requestBody);
// Сортируем ключи и создаем строку без пробелов
const sortedPayload = JSON.stringify(payload, Object.keys(payload).sort());
// Вычисляем HMAC-SHA256
const expectedSignature = crypto
.createHmac('sha256', webhookSecret)
.update(sortedPayload)
.digest('hex');
// Извлекаем подпись из заголовка (формат: sha256=...)
const receivedSignature = signatureHeader.replace('sha256=', '');
// Сравниваем подписи (используем constant-time сравнение)
return crypto.timingSafeEqual(
Buffer.from(expectedSignature),
Buffer.from(receivedSignature)
);
}
// Пример использования (Express.js)
app.post('/webhook', (req, res) => {
const webhookSecret = "ваш_webhook_secret";
const signatureHeader = req.headers['x-webhook-signature'] || '';
const requestBody = JSON.stringify(req.body);
if (verifyWebhookSignature(requestBody, signatureHeader, webhookSecret)) {
// Webhook подлинный
processWebhook(req.body);
res.json({ status: 'ok' });
} else {
// Webhook подделан
res.status(401).json({ error: 'Invalid signature' });
}
});
ВажноВсегда проверяйте подпись webhook перед обработкой данных. Никогда не обрабатывайте webhook без проверки подписи, даже если запрос приходит с правильного IP адреса.
Возможные статусы
nohash- Платеж создан, ожидание оплатыpending- Транзакция найдена в блокчейне, ожидание подтвержденияsuccess- Платеж успешно выполненerror- Ошибка при обработке платежа
Пример обработки webhook (PHP)
<?php
// webhook.php
require_once('verify_signature.php'); // Функция проверки подписи
$webhook_secret = "ваш_webhook_secret"; // Получите из настроек кассы
$signature_header = $_SERVER['HTTP_X_WEBHOOK_SIGNATURE'] ?? '';
$request_body = file_get_contents('php://input');
// Проверяем подпись
if (!verifyWebhookSignature($request_body, $signature_header, $webhook_secret)) {
http_response_code(401);
echo json_encode(['error' => 'Invalid signature']);
exit();
}
$data = json_decode($request_body, true);
$payment_id = $data['payment_id'];
$status = $data['status'];
$currency = $data['currency'];
$payload = $data['payload'] ?? null;
if ($status === 'success') {
// Платеж успешно выполнен
// Обновить статус заказа, начислить товар и т.д.
echo "Payment {$payment_id} completed successfully";
} else {
// Обработка других статусов
echo "Payment {$payment_id} status: {$status}";
}
?>
Пример обработки webhook (Python)
from flask import Flask, request
import hmac
import hashlib
import json
app = Flask(__name__)
def verify_webhook_signature(request_body, signature_header, webhook_secret):
payload = json.loads(request_body)
sorted_payload = json.dumps(payload, sort_keys=True, separators=(',', ':'))
expected_signature = hmac.new(
webhook_secret.encode('utf-8'),
sorted_payload.encode('utf-8'),
hashlib.sha256
).hexdigest()
received_signature = signature_header.replace('sha256=', '')
return hmac.compare_digest(expected_signature, received_signature)
@app.route('/webhook', methods=['POST'])
def webhook():
webhook_secret = "ваш_webhook_secret" # Получите из настроек кассы
signature_header = request.headers.get('X-Webhook-Signature', '')
request_body = request.get_data(as_text=True)
# Проверяем подпись
if not verify_webhook_signature(request_body, signature_header, webhook_secret):
return {'error': 'Invalid signature'}, 401
data = request.json
payment_id = data['payment_id']
status = data['status']
currency = data['currency']
payload = data.get('payload')
if status == 'success':
# Платеж успешно выполнен
print(f"Payment {payment_id} completed successfully")
# Обновить статус заказа, начислить товар и т.д.
return {'status': 'ok'}, 200
Пример обработки webhook (JavaScript/Node.js)
const express = require('express');
const crypto = require('crypto');
const app = express();
app.use(express.json());
function verifyWebhookSignature(requestBody, signatureHeader, webhookSecret) {
const payload = JSON.parse(requestBody);
const sortedPayload = JSON.stringify(payload, Object.keys(payload).sort());
const expectedSignature = crypto
.createHmac('sha256', webhookSecret)
.update(sortedPayload)
.digest('hex');
const receivedSignature = signatureHeader.replace('sha256=', '');
return crypto.timingSafeEqual(
Buffer.from(expectedSignature),
Buffer.from(receivedSignature)
);
}
app.post('/webhook', (req, res) => {
const webhookSecret = "ваш_webhook_secret"; // Получите из настроек кассы
const signatureHeader = req.headers['x-webhook-signature'] || '';
const requestBody = JSON.stringify(req.body);
// Проверяем подпись
if (!verifyWebhookSignature(requestBody, signatureHeader, webhookSecret)) {
return res.status(401).json({ error: 'Invalid signature' });
}
const { payment_id, status, currency, payload } = req.body;
if (status === 'success') {
// Платеж успешно выполнен
console.log(`Payment ${payment_id} completed successfully`);
// Обновить статус заказа, начислить товар и т.д.
}
res.json({ status: 'ok' });
});
app.listen(3000);
ВажноВаш webhook endpoint должен отвечать статусом 200 в течение 10 секунд. Если ответ не получен, система может повторить запрос.
БезопасностьЕсли вы используете webhook_secret, всегда проверяйте подпись перед обработкой данных. Не обрабатывайте webhook без проверки подписи.
Проверка статуса платежа
Проверка статуса платежа на странице оплаты
Описание
На странице оплаты автоматически выполняется проверка статуса платежа каждые 30 секунд. Также можно проверить статус вручную через API.
Автоматическая проверка
На странице payment.php статус проверяется автоматически. Платеж имеет таймаут 20 минут.
Ручная проверка через API
import requests
# Проверка статуса по ID и валюте
response = requests.get(
'https://pay.whaile.ru:3000/payment_status/ton/123'
)
data = response.json()
print(f"Status: {data['payment_status']}")
Проверка по UUID
import requests
# Получение платежа по UUID
response = requests.get(
'https://pay.whaile.ru:3000/payment_by_uuid/550e8400-e29b-41d4-a716-446655440000'
)
data = response.json()
print(f"Status: {data['payment_status']}")
Примеры интеграции
Готовые примеры кода для различных языков программирования
Пример на Python
Полная интеграция
import requests
import time
API_BASE = "https://pay.whaile.ru:3000"
WITHDRAW_API = "https://pay.whaile.ru:2998"
# Ваши данные
USER_ID = 1
API_TOKEN = "ваш_api_токен"
CASHIER_ID = 1
# 1. Создание платежа
def create_payment(amount, wallet, currency="ton"):
response = requests.post(
f"{API_BASE}/create_payment",
json={
"cashier_id": CASHIER_ID,
"amount": amount,
"wallet": wallet,
"currency": currency,
"payload": f"order_id=12345"
}
)
return response.json()
# 2. Проверка статуса платежа
def check_payment_status(payment_id, currency="ton"):
response = requests.get(
f"{API_BASE}/payment_status/{currency}/{payment_id}"
)
return response.json()
# 3. Получение списка касс
def get_cashiers():
response = requests.get(
f"{API_BASE}/cashiers/{USER_ID}"
)
return response.json()
# 4. Вывод средств
def withdraw(cashier_id, amount, wallet):
response = requests.post(
f"{WITHDRAW_API}/withdraw",
json={
"cashier_id": cashier_id,
"amount": amount,
"wallet": wallet,
"api_token": API_TOKEN
}
)
return response.json()
# Пример использования
if __name__ == "__main__":
# Создание платежа
payment = create_payment(
amount=0.01,
wallet="..."
)
print(f"Payment created: {payment}")
# Проверка статуса
payment_id = payment['payment_id']
status = check_payment_status(payment_id)
print(f"Payment status: {status['payment_status']}")
Пример на PHP
Полная интеграция
<?php
$api_base = "https://pay.whaile.ru:3000";
$withdraw_api = "https://pay.whaile.ru:2998";
$user_id = 1;
$api_token = "ваш_api_токен";
$cashier_id = 1;
// Функция для создания платежа
function createPayment($amount, $wallet, $currency = "ton") {
global $api_base, $cashier_id;
$ch = curl_init();
curl_setopt_array($ch, [
CURLOPT_URL => "$api_base/create_payment",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => json_encode([
"cashier_id" => $cashier_id,
"amount" => $amount,
"wallet" => $wallet,
"currency" => $currency,
"payload" => "order_id=12345"
]),
CURLOPT_HTTPHEADER => [
"Content-Type: application/json"
],
CURLOPT_SSL_VERIFYPEER => false
]);
$response = curl_exec($ch);
curl_close($ch);
return json_decode($response, true);
}
// Функция для проверки статуса
function checkPaymentStatus($payment_id, $currency = "ton") {
global $api_base;
$ch = curl_init();
curl_setopt_array($ch, [
CURLOPT_URL => "$api_base/payment_status/$currency/$payment_id",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_SSL_VERIFYPEER => false
]);
$response = curl_exec($ch);
curl_close($ch);
return json_decode($response, true);
}
// Пример использования
$payment = createPayment(
0.01,
"..."
);
echo "Payment created: " . json_encode($payment) . "\n";
$status = checkPaymentStatus($payment['payment_id']);
echo "Payment status: " . $status['payment_status'] . "\n";
?>
Пример на JavaScript
Полная интеграция
const API_BASE = "https://pay.whaile.ru:3000";
const WITHDRAW_API = "https://pay.whaile.ru:2998";
const USER_ID = 1;
const API_TOKEN = "ваш_api_токен";
const CASHIER_ID = 1;
// Создание платежа
async function createPayment(amount, wallet, currency = "ton") {
const response = await fetch(`${API_BASE}/create_payment`, {
method: "POST",
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify({
cashier_id: CASHIER_ID,
amount: amount,
wallet: wallet,
currency: currency,
payload: "order_id=12345"
})
});
return await response.json();
}
// Проверка статуса платежа
async function checkPaymentStatus(paymentId, currency = "ton") {
const response = await fetch(
`${API_BASE}/payment_status/${currency}/${paymentId}`
);
return await response.json();
}
// Получение списка касс
async function getCashiers() {
const response = await fetch(`${API_BASE}/cashiers/${USER_ID}`);
return await response.json();
}
// Вывод средств
async function withdraw(cashierId, amount, wallet) {
const response = await fetch(`${WITHDRAW_API}/withdraw`, {
method: "POST",
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify({
cashier_id: cashierId,
amount: amount,
wallet: wallet,
api_token: API_TOKEN
})
});
return await response.json();
}
// Пример использования
(async () => {
const payment = await createPayment(
0.01,
"..."
);
console.log("Payment created:", payment);
const status = await checkPaymentStatus(payment.payment_id);
console.log("Payment status:", status.payment_status);
})();
Обработка ошибок
HTTP коды статуса и формат ошибок
HTTP коды статуса
| Код | Описание |
|---|---|
| 200 | Успешный запрос |
| 400 | Неверные параметры запроса (неверный формат суммы, недостаточно средств и т.д.) |
| 401 | Не авторизован (неверный API токен) |
| 403 | Доступ запрещен (касса принадлежит другому пользователю) |
| 404 | Ресурс не найден (касса, платеж и т.д.) |
| 500 | Внутренняя ошибка сервера |
Формат ответа с ошибкой
{
"detail": "Описание ошибки"
}
Примеры ошибок
{"detail": "Amount is less than minimum: 0.01"}- Сумма меньше минимальной{"detail": "Insufficient balance. Available: 0.50, Requested: 1.00"}- Недостаточно средств{"detail": "Invalid API token"}- Неверный API токен{"detail": "Cashier not found"}- Касса не найдена{"detail": "Amount must have no more than 2 decimal places"}- Неверный формат суммы