Parallel

Вебхуки

Адрес вебхука магазина, подпись, повторы и порядок доставки

Вебхуки API v1 описаны в разделе API v1.

Вебхук приходит на каждую смену статуса заявки. Тело — заявка целиком, точно как в ответе GET /api/v2/invoices/{invoice_id}. На создание заявки вебхука нет: заявку вы получили в ответе.

Настроить

Адрес вебхука один на магазин.

PUT/api/v2/webhook
curl -X PUT https://api.parallel-pay.com/api/v2/webhook \
  -H "Authorization: Bearer $PARALLEL_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://example.com/webhooks/parallel"
  }'

Ответ — адрес и ключ подписи:

{
  "url": "https://example.com/webhooks/parallel",
  "secret": "8f14e45fceea167a5a36dedd4bea2543"
}

Сохраните secret: им вы проверяете подпись. Ключ создаётся при первой настройке.

Тело запроса:

Поле

Тип

Ответ:

Поле

Тип

Посмотреть настройку

GET/api/v2/webhook

Ответ — тот же объект url и secret. До первого PUT:

HTTP Status: 404 Not Found

{
  "error": {
    "code": "WEBHOOK_NOT_SET",
    "message": "Вебхук ещё не настроен"
  }
}

Перевыпустить ключ

POST/api/v2/webhook/secret
curl -X POST https://api.parallel-pay.com/api/v2/webhook/secret \
  -H "Authorization: Bearer $PARALLEL_TOKEN"

Ответ — тот же объект с новым secret. Если вебхук ещё не настроен — 404 WEBHOOK_NOT_SET.

Пока вебхук не настроен, вебхуки не отправляются. Состояние заявки всегда можно узнать запросом GET /api/v2/invoices/{invoice_id}.

Запрос к вам

POST https://example.com/webhooks/parallel
Content-Type: application/json
X-Webhook-Signature: 3f4c1f0e8a3b4c2b9a1d5e6f7a8b9c0d1e2f3a4b5c6d7e8f9a0b1c2d3e4f5a6b
X-Webhook-Delivery-Id: 7c9e6679-7425-40de-944b-e07fc1f90ae7
ЗаголовокЧто в нём
X-Webhook-SignatureHMAC-SHA256 сырого тела запроса ключом secret, в hex
X-Webhook-Delivery-IdНомер доставки. У повтора того же вебхука — тот же

Тело — заявка. Пример — заявка оплачена:

{
  "internal_id": "order-1001",
  "invoice_id": "cm3k8x7y80001z8j4k5m6n7o8",
  "direction": "IN",
  "created_at": "2026-11-09T12:00:00+03:00",
  "updated_at": "2026-11-09T12:07:00+03:00",
  "amount": "1000",
  "currency": "RUB",
  "deal_rate": "95.5",
  "requisites": {
    "bank": "sberbank",
    "bank_name": "Сбербанк",
    "full_name": "Ivan I.",
    "method": "SBP",
    "phone_number": "+79001234567"
  },
  "attachments": [
    {
      "attachment_id": "cm3k9b2c40002z8j4q1r2s3t4",
      "invoice_id": "cm3k8x7y80001z8j4k5m6n7o8",
      "type": "PDF",
      "file_name": "receipt.pdf",
      "file_url": "https://files.parallel-pay.com/attachments/cm3k9b2c40002z8j4q1r2s3t4/receipt.pdf",
      "received_at": "2026-11-09T12:05:00+03:00",
      "status": "PASSED",
      "checked_at": "2026-11-09T12:07:00+03:00",
      "transfer": {
        "amount": "1000",
        "at": "2026-11-09T12:04:00+03:00",
        "bank_status": "Успешно",
        "operation_id": "C4213110902474567",
        "from": {
          "bank": "tbank",
          "name": "Пётр П."
        },
        "to": {
          "method": "SBP",
          "bank": "sberbank",
          "name": "Иван И.",
          "phone_number": "+79001234567",
          "card_number": null
        }
      }
    }
  ],
  "receipt_delivery": "UPLOAD",
  "status": "PAID",
  "paid_amount": "1000",
  "paid_at": "2026-11-09T12:07:00+03:00"
}

Доставка

  • Вебхук доставлен, если вы ответили любым кодом 2xx.
  • Иначе повторяем через 1 минуту, 5 минут, 30 минут, 2 часа, 6 часов и 12 часов. Повтор приходит с тем же X-Webhook-Delivery-Id: если вы его уже обработали, просто ответьте 2xx.
  • После последнего повтора вебхук больше не отправляем. Состояние заявки всегда можно узнать запросом GET /api/v2/invoices/{invoice_id}.
  • По одной заявке вебхуки приходят строго по порядку. Если сравниваете состояния сами — по updated_at.

Проверка подписи

Считайте HMAC-SHA256 от сырого тела запроса — байтов, как они пришли, до разбора JSON. Ключ — secret из настройки вебхука, результат — в hex. Сравнивайте с X-Webhook-Signature за постоянное время.

import crypto from "node:crypto";
import express from "express";

const app = express();
const secret = process.env.PARALLEL_WEBHOOK_SECRET;

// express.raw: подпись считается от сырого тела, а не от разобранного JSON
app.post("/webhooks/parallel", express.raw({ type: "*/*" }), (req, res) => {
  const expected = crypto.createHmac("sha256", secret).update(req.body).digest("hex");
  const signature = req.get("X-Webhook-Signature") ?? "";

  if (
    signature.length !== expected.length ||
    !crypto.timingSafeEqual(Buffer.from(signature), Buffer.from(expected))
  ) {
    return res.status(401).send("invalid signature");
  }

  const invoice = JSON.parse(req.body.toString("utf8"));
  handleInvoice(invoice, req.get("X-Webhook-Delivery-Id"));
  res.sendStatus(200);
});

app.listen(3000);

Проверьте свой код на этом примере. Тело — ровно эта строка, без пробелов и переноса в конце:

{"invoice_id":"cm3k8x7y80001z8j4k5m6n7o8","status":"PAID"}

С ключом test_secret подпись — a43227d9daf9857e9af9788b204c1881cd6eb5186c31667bd491936ed17d7d6e.

Что делать по статусу

statusЧто делать
REVIEWЖдать: мы проверяем файлы до review_until
PROOF_REQUIREDДо proof_until загрузить видео оплаты или взять ФО
PAIDЗачислить клиенту paid_amount, не amount. После спора PAID приходит ещё раз с новым paid_amount
DISPUTEЗачтённые USDT заморожены, ждать решения спора
REVERSEDЗачисление отменено: отменить его и у себя
EXPIRED, CANCELEDЗакрыть заказ без оплаты

На этой странице