TunnelHunt

Robokassa ResultURL на localhost: подпись, OK и повтор уведомления

5 мин чтения

Проверяем ResultURL Robokassa локально: Flask, подпись MD5 с Password 2 и Shp_, сумма счёта, ответ OK и диагностика через HTTPS-туннель.

Проверка ResultURL Robokassa через локальный обработчик и HTTPS-туннель

ResultURL — серверный адрес для уведомления Robokassa об успешной оплате. При локальной разработке откройте обработчик через HTTPS-туннель, проверьте подпись и верните ответ вида OK9001, где 9001 — номер счёта. Возвращение покупателя на SuccessURL само по себе не подтверждает оплату.

Ниже — тестовый Flask-обработчик. Он проверяет подпись и сумму одного демонстрационного счёта, но не выдаёт товары и не активирует подписки. Для рабочего магазина нужна транзакция в вашей базе данных.

1. Подготовьте обработчик ResultURL

Пример рассчитан на метод POST и алгоритм MD5 в технических настройках тестового магазина. Для другого алгоритма замените функцию хэширования. Используйте тестовый Password 2; пароль №1 сюда не подходит.

Создайте app.py в отдельной папке:

import hashlib
import hmac
import os
from decimal import Decimal, InvalidOperation
from flask import Flask, Response, request

app = Flask(__name__)
PASSWORD_2 = os.environ["ROBO_PASSWORD_2"]
INVOICE = os.environ.get("DEMO_INVOICE", "9001")
AMOUNT = Decimal(os.environ.get("DEMO_AMOUNT", "199.00"))

@app.post("/webhook/robokassa")
def result_url():
    out_sum = request.form.get("OutSum", "")
    inv_id = request.form.get("InvId", "")
    received = request.form.get("SignatureValue", "")
    if not out_sum or not inv_id or not received:
        return "Missing parameters", 400
    if any(len(request.form.getlist(key)) != 1 for key in request.form):
        return "Duplicate parameters", 400

    # Preserve OutSum exactly as received when computing the signature.
    base = f"{out_sum}:{inv_id}:{PASSWORD_2}"
    for key in sorted(key for key in request.form if key.startswith("Shp_")):
        base += f":{key}={request.form[key]}"
    expected = hashlib.md5(base.encode("utf-8")).hexdigest()
    if not hmac.compare_digest(expected, received.lower()):
        return "Invalid signature", 403

    if inv_id != INVOICE:
        return "Unknown invoice", 404
    try:
        amount = Decimal(out_sum)
        if not amount.is_finite() or amount != AMOUNT:
            return "Amount mismatch", 400
    except InvalidOperation:
        return "Invalid amount", 400

    # Demo acknowledgement only. No product is issued by this example.
    print(f"Verified test notification: invoice={inv_id}", flush=True)
    return Response(f"OK{inv_id}", mimetype="text/plain")

if __name__ == "__main__":
    app.run(host="127.0.0.1", port=8000, debug=False)

В Bash/zsh создайте окружение и запустите приложение:

python3 -m venv .venv
source .venv/bin/activate
python -m pip install Flask
export ROBO_PASSWORD_2='тестовый-пароль-2-из-настроек-магазина'
export DEMO_INVOICE='9001'
export DEMO_AMOUNT='199.00'
python app.py

На Windows используйте WSL либо активируйте .venv и задайте переменные средствами PowerShell. Пароли не сохраняйте в коде, Git или опубликованных логах.

2. Проверьте подпись локально без платежа

В другом терминале с тем же ROBO_PASSWORD_2 сформируйте подпись искусственного уведомления:

SIGNATURE=$(python3 -c 'import hashlib, os; print(hashlib.md5(("199.00:9001:" + os.environ["ROBO_PASSWORD_2"]).encode()).hexdigest())')
curl -i http://127.0.0.1:8000/webhook/robokassa --data-urlencode 'OutSum=199.00' --data-urlencode 'InvId=9001' --data-urlencode "SignatureValue=${SIGNATURE}"

Ожидаются статус 200 и тело OK9001. Повтор тоже возвращает OK9001. С произвольной подписью ожидается 403, без обязательного параметра — 400. Эта проверка подтверждает работу кода, но не доставку от Robokassa и не реальную оплату.

3. Откройте localhost через HTTPS

Для лимитов Free добавьте публичный SSH-ключ в кабинет, затем запустите:

ssh -p 2222 -o ExitOnForwardFailure=yes -R 80:127.0.0.1:8000 tunnelhunt.ru

Скопируйте HTTPS-адрес из терминала. Повторите POST из предыдущего шага, заменив локальный адрес на https://YOUR-SUBDOMAIN.tunnelhunt.ru/webhook/robokassa. Он должен вернуть то же OK9001. Браузер отправляет GET, поэтому открытие ссылки не проверяет этот POST-обработчик.

Если снаружи приходит 502, сначала проверьте Flask локально и порт в SSH. Полная диагностика — в инструкции доступа к localhost.

4. Настройте тестовое уведомление Robokassa

В технических настройках тестового магазина укажите полный ResultURL с путём /webhook/robokassa, метод POST, MD5 и соответствующий Password 2. Тестовые и рабочие пароли могут различаться. При создании тестового счёта используйте InvId=9001 и сумму 199.00 либо измените DEMO_INVOICE и DEMO_AMOUNT под ваш счёт и перезапустите Flask.

Пройдите тестовый платёж по инструкции Robokassa. Проверьте три результата: POST дошёл до локального обработчика, подпись и сумма приняты, сервер вернул OK с номером этого счёта. Формула подписи и ответ описаны в официальной документации ResultURL.

Почему ResultURL не подтверждается

Симптом Проверка
405 Method Not Allowed В магазине выбран GET, а обработчик принимает POST
403 Invalid signature Password 2, тестовый/рабочий режим, алгоритм хэша и полный набор Shp_
Подпись не совпадает при одинаковой сумме Для подписи используйте исходную строку OutSum, без округления и float
400 Amount mismatch Подписанная сумма должна совпадать с ожидаемой суммой счёта
404 Unknown invoice Проверьте InvId и счёт, который ожидает обработчик
После переподключения нет уведомлений Изменился случайный домен; обновите ResultURL или закрепите адрес
Покупатель видит успех, но заказ не выдан Проверьте серверный ResultURL и транзакцию выдачи

Повтор уведомления и рабочий обработчик

Replay в кабинете позволяет повторить сохранённый тестовый запрос после изменения кода. В рабочей базе найдите счёт, сверьте сумму, заблокируйте запись, измените статус и выдайте продукт в одной транзакции. Для уже обработанного счёта верните подтверждение без повторной выдачи. Отвечайте OK{InvId} после успешного сохранения результата.

Flask-пример выше ничего не сохраняет: он подходит для проверки доставки и подписи, но не является системой учёта оплат.

Постоянный ResultURL для повторных тестов

На Pro за 199 ₽/месяц можно зарезервировать поддомен; квота — 50 ГБ в месяц. После активации в разделе «Туннели» выберите свободное имя, привяжите SSH-ключ и переподключитесь с ним. Проверьте адрес из терминала, затем сохраните его как ResultURL.

Это сохраняет адрес между сеансами разработки. Для доступности нужны Flask, SSH, включённый компьютер и сеть. Восстановление подключения — в инструкции autossh; другие провайдеры — в обзоре платёжных вебхуков.

Отладьте ResultURL на своём компьютере

Запустите тестовый обработчик и убедитесь, что он возвращает OK с номером счёта. Для повторных тестов закрепите поддомен, чтобы не менять ResultURL в настройках магазина.

Free: 1 ГБ трафика в месяц, 1 туннель, сессия до 4 часов. Pro: 199 ₽/месяц, 50 ГБ, до 5 туннелей и резервирование поддомена. Компьютер, приложение и SSH-соединение должны работать; постоянный адрес сохраняет имя, а не доступность выключенного сервера.

Готовы запустить свой первый туннель?

Начните использовать TunnelHunt бесплатно уже сегодня и откройте доступ к локальному серверу всего одной командой.

Зарегистрироваться бесплатно →

По теме

Локальная проверка платёжного вебхука через HTTPS-туннель

Платёжные вебхуки на localhost: ЮKassa, Т-Банк, Robokassa и CloudPayments

7 сентября 2025 г.

Как проверить доставку платёжного вебхука на локальный сервер: HTTPS-туннель, выбор инструкции провайдера, подпись, повтор и диагностика HTTP-ответов.

Читать далее →
Как повторить вебхук: отладка HTTP-запросов через Replay

Как повторить вебхук: отладка HTTP-запросов через Replay

20 июня 2026 г.

Получите тестовый вебхук, найдите ошибку и повторите запрос после исправления. Рабочий пример на Python, диагностика подписей и ограничения Replay.

Читать далее →
Как открыть localhost в интернете: публичный HTTPS через SSH

Как открыть localhost в интернете: публичный HTTPS через SSH

11 сентября 2026 г.

Пошагово открываем локальный сайт из интернета без настройки роутера: SSH-туннель, проверка ссылки, Docker, Vite и диагностика ошибок.

Читать далее →
autossh: постоянный SSH-туннель, переподключение и systemd

autossh: постоянный SSH-туннель, переподключение и systemd

16 июня 2026 г.

Настройка autossh с ServerAliveInterval и systemd: восстановление SSH-туннеля, проверка подключения и диагностика Broken pipe без обещаний вечного uptime.

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