Как проверить оплату через API без webhook
Серверная проверка инвойса через API: статусы оплаты, ограниченный polling, ошибки, однократное исполнение заказа и восстановление после перезапуска.
Обновлено:
Проверить оплату без webhook можно с сервера своего приложения: периодически читать инвойс через API Cryptanio, сверять его с заказом и выполнять заказ при актуальном статусе paid. Такой опрос называют polling. Он требует очереди проверок, обработки ошибок и защиты от повторной выдачи: один успешный HTTP-ответ ещё не подтверждает оплату.
Этот вариант подходит, если приложение может выполнять фоновые задачи, но принимать входящие уведомления пока неудобно. Изменения обнаруживаются при очередном чтении; мгновенного уведомления polling не даёт.
Что понадобится
Сохраните в своей базе связь заказа с инвойсом: его id, проект, свой reference, согласованную цену и режим работы. Идентификаторы храните строками. Проверяется инвойс конкретного заказа, а не найденная транзакция с похожей суммой.
Подготовьте серверный API-ключ с правом чтения invoices, доступом к услуге payments и нужному проекту. Для реального заказа нужен соответствующий рабочий режим. Ключ нельзя помещать в браузер, ссылку покупателя или публичный журнал. Для отдельного процесса сверки достаточно доступа на чтение.
Шаг 1. Получите текущее состояние
Пример выполняет чтение одного инвойса. На сервере задайте CRYPTANIO_API_KEY и CRYPTANIO_RESOURCE_ID; второе значение — сохранённый id инвойса, не номер заказа и не TXID.
#!/usr/bin/env bash
set -euo pipefail
: "${CRYPTANIO_RESOURCE_ID:?Set CRYPTANIO_RESOURCE_ID}"
: "${CRYPTANIO_API_KEY:?Set CRYPTANIO_API_KEY}"
urlencode() {
local LC_ALL=C text="$1" char index
for ((index=0; index<${#text}; index++)); do
char="${text:index:1}"
case "$char" in [a-zA-Z0-9.~_-]) printf '%s' "$char" ;; *) printf '%%%02X' "'$char" ;; esac
done
}
response_file="$(mktemp)"
trap 'rm -f "$response_file"' EXIT
transfer_status=0
http_status="$(curl \
--silent --show-error --max-time 30 --request GET \
--header "Authorization: Bearer $CRYPTANIO_API_KEY" \
--header 'Accept: application/json' \
"https://api.cryptanio.com/v1/invoices/$(urlencode "$CRYPTANIO_RESOURCE_ID")" \
--output "$response_file" --write-out '%{http_code}')" || transfer_status=$?
if [[ "$http_status" == 2?? ]]; then
cat "$response_file"
exit "$transfer_status"
fi
printf 'HTTP %s\n' "$http_status" >&2
cat "$response_file" >&2
exit 1
После HTTP 200 разберите JSON и сверяйте id, project_id, reference и sandbox. Для рабочего заказа ожидается sandbox=false. Если сохраняли цену в invoice_currency и invoice_amount, проверьте и их. Чужой инвойс или запись другого режима не должны менять заказ.
Ссылка checkout_url из GET предназначена для просмотра. Не заменяйте ею сохранённую интерактивную ссылку из ответа создания инвойса: чтение статуса не создаёт новый платёж и не выдаёт новый интерактивный checkout.
Шаг 2. Разделите ожидание и успешную оплату
Принимайте решение по status инвойса:
pending— оплата ожидается;seen— перевод обнаружен, но оплата ещё не завершена;partial— подтверждена часть суммы, продолжается ожидание по правилам платежа;underpaid,overpaid,late— требуется разбор; учитывайтеneeds_decisionи решение продавца;paid— инвойс учтён как оплаченный; после сверки связи с заказом можно готовить исполнение;expired,canceled— прекратите активное ожидание покупателя, но сохраните запись для последующей сверки.
Истечение срока не исключает позднего поступления. Ручное решение и откат транзакции также могут изменить дальнейшую обработку. Не объявляйте состояние окончательным навсегда и не удаляйте связь заказа с инвойсом после закрытия страницы.
Подтверждённая транзакция и оплаченный инвойс — разные вещи. Подтверждения не доказывают, что пришли нужные актив, сумма и реквизиты именно этого заказа. Подробности различий разобраны в инструкции о неоплаченном заказе.
Шаг 3. Учтите сумму и попытку оплаты
При смене способа оплаты у инвойса могут появляться разные попытки. Для оплаченного счёта учитывайте settled_attempt_id и settled_attempt. При разборе истории проверяйте attempt_history.has_more и курсор продолжения; для переводов выбранной попытки используйте payment_history. Первая страница не обязательно содержит всю историю.
amount — точная криптосумма для отправки. amount_raw и paid — строки с целыми значениями в минимальных единицах актива; масштаб задаёт decimals. Не сравнивайте их как дробные числа с плавающей точкой или как обычные строки по алфавиту. При этом собственная проверка суммы не должна подменять статус инвойса и правила проекта: переплата может быть принята либо отправлена на рассмотрение. Суммирование недоплат без проверки условий также недопустимо.
Шаг 4. Ограничьте polling и повторы
Поставьте проверки в серверную очередь с ограниченной параллельностью. Несколько вкладок покупателя не должны создавать независимые циклы опроса. Учитывайте общий поток аккаунта: разные API-ключи и режимы разделяют квоту. Актуальные ограничения приведены в справочнике API.
Назначайте задержки под свой процесс, оставляя запас для других запросов. При сетевой ошибке или HTTP 500/503 используйте ограниченные повторы с увеличением задержки и случайным разбросом. Для HTTP 429 сократите общий поток; если получен Retry-After, выдержите указанную паузу. Не заменяйте отсутствующий заголовок немедленным повтором. У каждого запроса должен быть тайм-аут, у серии повторов — предел, после которого задача остаётся для последующего разбора.
HTTP 401/403 требуют проверки ключа и разрешений. При 404 проверьте ID, проект и режим: ограничения доступа могут скрывать инвойс. Эти ошибки не означают, что клиент ничего не заплатил.
Шаг 5. Сохраните исполнение и восстановление
При paid согласованно сохраните состояние заказа и задание на исполнение. Используйте уникальный ключ заказа и защиту от параллельной обработки. Повторное чтение paid не должно повторно выдавать доступ или товар; сбой после выдачи не должен создавать второе исполнение.
Храните последнюю успешную проверку и следующую задачу в устойчивом хранилище. После перезапуска поднимайте незавершённые проверки и задания, а не начинайте отсчёт только при открытии страницы. Активное ожидание ограничьте сроками вашего процесса; отдельная фоновая сверка должна учитывать поздние платежи, решения и откаты, включая ранее оплаченные заказы. Перед ещё не выполненной выдачей повторно проверьте право на исполнение; для уже выданного доступа предусмотрите пересмотр.
На подготовленных ответах проверьте повтор paid, тайм-аут, перезапуск и переход после отката. Результат считается корректным, когда заказ исполняется один раз, а неопределённый или спорный платёж остаётся на проверке. Позже polling можно дополнить webhook, сохранив ту же защиту исполнения.