К содержимому

РазработчикамГайд

Как принимать криптовалюту на Python

Приём Bitcoin и USDT в приложении на Python: подпись запросов через hmac, создание платежа через requests, страница оплаты и проверка вебхука во Flask.

Приложение на Python принимает криптовалюту через CryptoLike в три шага: подписать каждый запрос к API стандартными модулями hmac и hashlib, создать платёж по заказу и проверить подписанный вебхук, прежде чем отметить заказ оплаченным. В примерах — requests и Flask; та же логика подходит для Django, FastAPI и других фреймворков.

Магазину на WooCommerce, OpenCart, PrestaShop или Magento 2 код не нужен — смотрите интеграции. Это руководство — для собственного бэкенда.

Что понадобится

Аккаунт CryptoLike, Python 3.10 или новее и публичный адрес https:// для вебхуков. Секреты держите в окружении: в примерах это CRYPTOLIKE_API_KEY_ID, CRYPTOLIKE_API_SECRET и CRYPTOLIKE_WEBHOOK_SECRET, а также CRYPTOLIKE_API_URL для тестового адреса API (без него — рабочий). Начинайте с тестовых ключей и тестовых сетей.

1. Создайте тестовый API-ключ

В кабинете откройте Разработчикам → API-ключи → Создать ключ с правами create_invoice и read. Ключ ck_test_ работает с тестовыми сетями, где монеты ничего не стоят. Тестовые ключи выдаёт тестовая среда: поддержка CryptoLike даст доступ к её кабинету, где вы создадите такой же ключ, и адрес её API (CRYPTOLIKE_API_URL в примерах). Рабочий кабинет выпускает только боевые ключи, а в основных сетях тестовый ключ получает 401 auth.test_key. Секрет показывается один раз — сразу положите его в окружение или менеджер секретов.

2. Подписывайте запросы через hmac

Подпись — HMAC-SHA256 от четырёх строк: времени Unix в секундах, метода, пути с query и SHA-256 тела, с ключом — API-секретом:

python
import hashlib, hmac, json, os, time
import requests

def cryptolike(method: str, path: str, payload: dict | None = None, idempotency_key: str | None = None) -> dict:
    body = b"" if payload is None else json.dumps(payload, separators=(",", ":")).encode()
    ts = str(int(time.time()))
    canonical = "\n".join([ts, method.upper(), path, hashlib.sha256(body).hexdigest()])
    headers = {
        "X-API-Key": os.environ["CRYPTOLIKE_API_KEY_ID"],
        "X-Timestamp": ts,
        "X-Signature": hmac.new(os.environ["CRYPTOLIKE_API_SECRET"].encode(), canonical.encode(), hashlib.sha256).hexdigest(),
        "Content-Type": "application/json",
    }
    if idempotency_key:
        headers["Idempotency-Key"] = idempotency_key
    r = requests.request(method, os.environ.get("CRYPTOLIKE_API_URL", "https://api.cryptolike.net") + path, data=body or None, headers=headers, timeout=30)
    data = r.json()
    if r.status_code >= 400:
        raise RuntimeError(f"{data.get('error', {}).get('code', r.status_code)}: {data.get('error', {}).get('message', '')}")
    return data

Отправляйте те же байты, что хешировали: data=body, а не json=payload, который сериализует тело заново. Сверьте функцию с эталонным вектором из раздела аутентификация API.

3. Создайте платёж по заказу

Создавайте платёж на сервере в момент оформления заказа. Укажите цену в USD или EUR и дайте покупателю выбрать монету или передайте coin_id из GET /v1/currencies. Без coin_id страница покажет монеты, доступные для инвойсов, с ориентировочной суммой ≈ у каждой; добавьте allowed_coin_ids (id с invoice_enabled: true из подписанного GET /v1/currencies), чтобы предложить только часть из них. Номер заказа как ключ идемпотентности вернёт тот же платёж при повторе:

python
invoice = cryptolike("POST", "/v1/invoices", {
    "amount": "49.90",
    "currency": "USD",
    "order_id": order.id,
    "idempotency_by_order_id": True,
    "return_url": f"https://example.com/orders/{order.id}",
}, idempotency_key=f"order-{order.id}")

order.invoice_id = invoice["id"]

Храните суммы строками или Decimal — никогда не float.

4. Отправьте покупателя на страницу оплаты

Перенаправьте на invoice["checkout_url"] (во Flask — redirect(invoice["checkout_url"], code=303)). На странице видны сумма, сеть, адрес с QR-кодом, оставшееся время и статус в реальном времени, а после оплаты покупатель вернётся на return_url. Чтобы он не уходил с сайта, встройте страницу виджетом оплаты.

5. Проверьте вебхук во Flask

Добавьте вебхук-эндпоинт в разделе Разработчикам → Вебхуки с событиями invoice.*. Проверяйте подпись по сырому телу из request.get_data(), отсеивайте повторы по event_id и отвечайте быстро:

python
from flask import Flask, request

app = Flask(__name__)

@app.post("/webhooks/cryptolike")
def cryptolike_webhook():
    raw = request.get_data()
    ts = request.headers.get("X-Timestamp", "")
    got = request.headers.get("X-Signature", "")
    secret = os.environ.get("CRYPTOLIKE_WEBHOOK_SECRET", "")
    want = "sha256=" + hmac.new(secret.encode(), ts.encode() + b"." + raw, hashlib.sha256).hexdigest()
    if not secret or not ts.isdigit() or abs(time.time() - int(ts)) > 300 or not hmac.compare_digest(got, want):
        return "", 401
    event = json.loads(raw)
    if first_time(event["event_id"]) and event["type"] == "invoice.paid":
        mark_order_paid(event["data"]["invoice"]["order_id"])
    return "", 200

Доставка — «хотя бы один раз», и порядок может нарушаться: опирайтесь на статус внутри data. Полная процедура — в руководстве по проверке подписи вебхуков.

6. Проверьте в тестовой сети и запускайте

Создайте платёж тестовым ключом и оплатите его тестовыми монетами в той сети, что указана на странице: сначала придёт invoice.pending, затем invoice.paid. Обработайте также invoice.underpaid, invoice.expired и invoice.reverted, затем перейдите на рабочий ключ и пройдите чек-лист запуска из руководства разработчика.

Частые ошибки

Ошибка Что произошло Что сделать
401 auth.invalid_signature Тело запроса отличается от подписанного Отправляйте хешированные байты через data=, а не json=
401 auth.expired_timestamp Часы расходятся больше чем на 5 минут Синхронизируйте часы сервера через NTP
401 на вашем вебхуке Тело разобрано или декодировано до проверки Проверяйте по request.get_data()
400 validation_error Поле отсутствует или в неверном формате Посмотрите details.field; суммы передавайте строками
409 conflict У заказа уже есть платёж Возьмите details.invoice_id вместо создания нового
422 no_allowed_coins Ни одна монета из allowed_coin_ids не доступна для инвойсов в этой валюте Передайте другие id или уберите поле, чтобы предложить все монеты

Удобнее с SDK?

Официальный Python SDK для Python 3.10+ оборачивает эти вызовы: подпись запросов, повторы при 429 и 5xx, Idempotency-Key на каждом POST, постраничные списки и проверку вебхуков. Код выше — то, что SDK делает внутри, так что перейти на него можно позже, не меняя схему.

Вопросы и ответы

Почему подпись не проходит, если передавать тело через json= в requests?

json=payload сериализует тело заново, со своими пробелами, и отправленные байты отличаются от хешированных. Сериализуйте один раз, хешируйте эти байты и передавайте их же через data=. То же на стороне вебхука: проверяйте подпись по request.get_data(), прежде чем что-то разбирать.

Можно ли использовать Django или FastAPI вместо Flask?

Да. Логика во всех фреймворках одна: прочитать сырое тело, вычислить sha256= и HMAC от метки времени, точки и тела, сравнить через hmac.compare_digest, отклонить метки, которые расходятся с вашими часами больше чем на 300 секунд, и только потом разбирать JSON. В Django сырое тело — request.body, в FastAPI — await request.body().

Как проверить интеграцию, не тратя настоящую криптовалюту?

Используйте ключ ck_test_ и тестовый адрес API от поддержки CryptoLike. Тестовые ключи работают с тестовыми сетями, где тестовые монеты ничего не стоят, а весь сценарий — страница оплаты, подтверждения, подписанные вебхуки — ведёт себя как в рабочем режиме. Держите тестовые и рабочие ключи в разных конфигурациях, чтобы они не смешивались.

Термины на странице

Читать дальше

Начните с тестового платежа

Создайте аккаунт, попросите у поддержки доступ к тестовой среде и посмотрите, как платёж проходит путь от pending до paid в тестовой сети.