Интеграции

Криптоплатежи для собственного бэкенда

Не привязанный к стеку разбор подключения 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 (неверный ключ), ничего не изменив.
  • Суммы в долларах. Если магазин считает в другой валюте, переведите сумму до запроса.
Pythonqvard_client.py
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. Ваша точка должна:

  1. Проверить подпись: HMAC-SHA256 вашим секретным ключом по метке времени и сырому телу, сравнить за постоянное время; если не совпадает, ответить 400.
  2. Действовать только по статусу completed.
  3. Обновлять заказ условно, «оплачен, если ещё ожидал оплаты», чтобы повторы ничего не меняли.
  4. Ставить медленную работу в очередь и отвечать ровно HTTP 200.

Qvard повторяет любой другой ответ, таймаут или редирект, пять попыток примерно за шесть минут, поэтому повторные доставки это норма. Подробности повторов в материале повторы webhook в платёжных системах, а правила безопасности в как защитить webhook криптоплатежей.

Pythonwebhook.py
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 повторяет пять раз примерно за шесть минут. После этого отправьте уведомление по платежу ещё раз из кабинета; идемпотентный обработчик делает это безопасным.

Похожие статьи

Похожие статьи