Криптоплатежи для собственного бэкенда
Не привязанный к стеку разбор подключения Qvard к вашему серверу: модель заказа, создание платежа, webhook, сверка и чек-лист запуска, с примерами на Python.

Если ваш магазин, сервис или платформа работают на собственном бэкенде, плагин вам не нужен: Qvard требует один запрос, создающий платёж, и одну точку, принимающую результат. Материал разбирает интеграцию так, чтобы она подходила любому языку и фреймворку, с примерами на Python, и охватывает то, что делает её надёжной: модель заказа, идемпотентность, сверку и короткий чек-лист запуска.
Что нужно
- Аккаунт Qvard с проектом, API-ключом и callback URL вашего webhook, сохранённым в проекте.
- Публичная HTTPS-точка на вашем сервере для webhook.
- Таблица заказов (или её аналог), в которой платёж сопоставляется с заказом по вашему идентификатору.
SDK и sandbox пока нет: интеграция это обычный HTTP, а проверка идёт на небольших реальных суммах. Справочник по форматам запросов в материале API для криптоплатежей; здесь показано, как всё складывается вместе.
Минимальная модель заказа
От ваших данных зависит, насколько чистой получится интеграция. Минимальный набор полей заказа:
id: номер вашего заказа или счёта. Его вы отправляете в Qvard, и он же возвращается в webhook.amount_usd: сумма в долларах США, в которых создаётся платёж.state: напримерawaiting_payment,paid,cancelled. Помечайте оплаченным только из webhook.paid_atи, при желании,idплатежа Qvard из ответа для поддержки.
Не складывайте всю платёжную логику в класс заказа. Небольшой модуль, создающий платёж, и точка, применяющая webhook, сохраняют код тестируемым.
Создание платежа
Когда клиент выбирает оплату, создайте платёж с бэкенда одним POST на https://api.qvard.com/api/v1/payment. Передайте ключ в заголовке Authorization: Bearer и JSON с номером заказа в id (строкой), суммой в price в долларах США и, при желании, paymentSystemId, чтобы страница оплаты открылась с выбранным активом и сетью.
Ответ 201 с redirectUrl, адресом страницы оплаты. Перенаправьте клиента туда или вставьте ссылку в письмо. Правила, которые стоит заложить в клиент:
- Таймаут и повтор. Используйте таймаут и повторяйте при таймауте или
5xx. Отправка того жеidдля того же способа возвращает уже созданный платёж, поэтому повтор не создаст второй. - Не повторяйте
400(«Error validation» с сообщением по каждому полю) и401(неверный ключ), ничего не изменив. - Суммы в долларах. Если магазин считает в другой валюте, переведите сумму до запроса.
import os
import requests
QVARD_URL = "https://api.qvard.com/api/v1/payment"
def create_payment(order_id: str, amount_usd: float) -> str:
"""Create a Qvard payment for an order and return the hosted payment page URL."""
response = requests.post(
QVARD_URL,
headers={"Authorization": "Bearer " + os.environ["QVARD_API_KEY"]},
json={"id": order_id, "price": amount_usd}, # id as a string, price in US dollars
timeout=15,
)
if response.status_code == 201:
return response.json()["redirectUrl"]
# 400 = "Error validation" with a per-field message, 401 = wrong API key
raise RuntimeError(f"Qvard {response.status_code}: {response.text}")Приём webhook
Когда платёж завершён, Qvard отправляет POST на ваш callback URL с id и status подписанным заголовком X-Qvard-Signature. Ваша точка должна:
- Проверить подпись: HMAC-SHA256 вашим секретным ключом по метке времени и сырому телу, сравнить за постоянное время; если не совпадает, ответить
400. - Действовать только по статусу
completed. - Обновлять заказ условно, «оплачен, если ещё ожидал оплаты», чтобы повторы ничего не меняли.
- Ставить медленную работу в очередь и отвечать ровно HTTP 200.
Qvard повторяет любой другой ответ, таймаут или редирект, пять попыток примерно за шесть минут, поэтому повторные доставки это норма. Подробности повторов в материале повторы webhook в платёжных системах, а правила безопасности в как защитить webhook криптоплатежей.
import os
from fastapi import FastAPI, Request, Response
from qvard import Qvard
app = FastAPI()
qvard = Qvard(os.environ["QVARD_API_KEY"])
@app.post("/webhooks/qvard")
async def qvard_webhook(request: Request):
try:
body = qvard.webhooks.construct_event(await request.body(), request.headers.get("X-Qvard-Signature"), os.environ["QVARD_API_KEY"])
except ValueError:
return Response(status_code=401)
if body.get("status") == "completed":
# Only changes an order that is not paid yet, so repeated deliveries are harmless.
changed = await db.execute(
"UPDATE orders SET state = 'paid', paid_at = now() WHERE id = :id AND state = 'awaiting_payment'",
{"id": body.get("id")},
)
if changed:
await queue.enqueue("fulfil_order", body["id"])
return Response(status_code=200)Частичные оплаты, сбои и вид для клиента
Решите, что каждый статус платежа значит для заказа:
wait: ожидает оплаты.partial: пришло меньше полной суммы; webhook о завершении не отправляется. Оставьте заказ открытым, скажите клиенту, сколько не хватает, либо отмените и верните деньги.completed: пришла вся сумма; пометьте заказ оплаченным.rejected: предложите другой способ оплаты.
Возврата по ссылке после оплаты пока нет, поэтому клиент возвращается сам: дайте ему страницу заказа с реальным статусом, которая обновляется, пока идёт ожидание. Возврат это перевод, который вы отправляете сами, потому что криптоплатёж отменить нельзя.
Сверка и чек-лист запуска
Добавьте ежедневную проверку, сравнивающую заказы, которые считаете оплаченными или нет, с платежами и балансом в кабинете Qvard; платёж, завершённый в кабинете при неоплаченном заказе, это пропущенное уведомление, которое можно отправить повторно из кабинета.
Перед запуском:
- API-ключ лежит в переменной окружения или хранилище секретов, а не в репозитории;
- callback URL сохранён, публичен, работает по HTTPS и доступен снаружи вашей сети;
- точка отвечает ровно 200, быстро и безопасна при двойном запуске;
- вы оплатили небольшую настоящую сумму и увидели, что заказ стал оплаченным;
- вы один раз заставили точку вернуть ошибку и увидели повтор от Qvard без двойного выполнения;
- вы вывели небольшую сумму на свой кошелёк. Вывод стоит только сетевую комиссию; вывода на банковский счёт в фиате нет.
Коммерческие условия на странице тарифов, а что увидит клиент, показывает демо-счёт.
FAQ
Есть ли SDK для моего языка?
Нет. Интеграция это обычный HTTP: один POST для создания платежа и один webhook для результата. Примеры в материале показывают схему на Python, и она переносится на любой язык.
Как сопоставить платёж с заказом?
Передайте номер вашего заказа как id при создании платежа. Тот же id вернётся в webhook.
Есть ли sandbox для проверки?
Пока нет. Берите небольшие реальные суммы, а страницу оплаты смотрите на демо-счёте.
Что если сервер лежал, когда платёж завершился?
Qvard повторяет пять раз примерно за шесть минут. После этого отправьте уведомление по платежу ещё раз из кабинета; идемпотентный обработчик делает это безопасным.
