Платежи

M2M-платежи: загрузка платежей из 1С-файла, отправка в банк, статус, поиск. Шесть endpoint'ов, общая идемпотентность по external_id+payload_hash.

:::info Требования к токену Все endpoint'ы ниже требуют:

  • purpose=integration;
  • scope_send_payments=true + per-account allow_send_payments=true на целевом РС.

MCP-токен на любой из endpoint'ов ниже получает 403 forbidden_purpose в auth-middleware до handler'а. :::

Жизненный цикл платежа

Платёж проходит две стадии: parse (загрузка файла, создание batch + items) и submit (отправка items в банк). Это разделение позволяет клиенту увидеть результат разбора и решить, что делать с невалидными строками, прежде чем тратить лимиты на банк.

POST /imports         → batch created, items parsed/duplicate/validation_error/unsupported
GET  /imports/{id}    → текущее состояние batch + items
POST /imports/{id}/submit         → submit всех ready items в банк
POST /imports/{id}/items/{i}/submit → retry одного item
GET  /payments/{id}   → детализированный статус платежа
GET  /payments?external_id=…      → поиск по Номеру 1С

Upload — POST /payments/imports

POST /api/v1/integration/payments/imports

Загружает 1С-файл (1CClientBankExchange v1.02 / v1.03, до 10 MB). Парсит платежи в batch + items, не отправляя в банк.

Запрос — multipart/form-data

ПолеТипОбяз.Описание
bank_account_iduuidРС-плательщик. Должен присутствовать в ответе GET /integration/bank-accounts и иметь permissions.send_payments=true.
filefileФайл формата 1CClientBankExchange. Кодировка cp1251 (как 1С его и пишет). Размер до 10 MB. Версия определяется автоматически по ВерсияФормата в шапке.
curl --silent --show-error \
     -H "Authorization: Bearer bcs_live_<TOKEN>" \
     -F "bank_account_id=<bank-account-uuid>" \
     -F "file=@payments.txt" \
     "https://bsc.example.com/api/v1/integration/payments/imports"
{
  "batch_id": "<batch-uuid>",
  "items_total": 3,
  "items": [
    { "item_id": "<item-uuid>", "external_id": "100", "status": "parsed" },
    { "item_id": "<item-uuid>", "external_id": "101", "status": "duplicate", "payment_order_id": "<existing>" },
    { "item_id": "<item-uuid>", "external_id": "102", "status": "validation_error",
      "error": { "code": "missing_field", "message": "Сумма обязательна" } }
  ]
}

Статусы items на этапе parse

СтатусОписание
parsedГотов к submit. Все поля корректны, нет конфликта по external_id.
duplicateItem с тем же external_id + тем же payload_hash уже существует у этого токена. Возвращается existing payment_order_id; submit игнорируется как no-op replay.
validation_errorItem не прошёл схему: пустой external_id, отрицательная сумма, плохой ИНН и т.п. Submit пропускает.
unsupportedItem корректный, но содержит тип документа, не поддерживаемый банк-адаптером (например, валютный платёж на rub-only адаптере).

:::warning account_mismatch — 422 Если в 1С-файле в РасчСчет плательщика указан номер, не совпадающий с bank_account_id запроса, backend возвращает 422 account_mismatch, batch не создаётся. Это защита от случайной отправки платежей не с того счёта. Сценарии 1С см. Интеграция с 1С. :::

Get batch — GET /payments/imports/{batch_id}

GET /api/v1/integration/payments/imports/{batch_id}

Текущее состояние batch + items. Batch другого токена → 404 (не светим существование).

curl --silent --show-error \
     -H "Authorization: Bearer bcs_live_<TOKEN>" \
     "https://bsc.example.com/api/v1/integration/payments/imports/<batch_id>"

Используется для polling после submit (см. ниже) и для UI, показывающего прогресс отправки.

Submit batch — POST /payments/imports/{batch_id}/submit

POST /api/v1/integration/payments/imports/{batch_id}/submit

Отправляет все ready items batch'а в банк. Идемпотентен: повторный submit ничего не дублирует.

curl --silent --show-error -X POST \
     -H "Authorization: Bearer bcs_live_<TOKEN>" \
     "https://bsc.example.com/api/v1/integration/payments/imports/<batch_id>/submit"
{
  "batch_id": "<batch-uuid>",
  "results": [
    { "item_id": "<id>", "external_id": "100", "status": "accepted",  "payment_order_id": "<new>" },
    { "item_id": "<id>", "external_id": "101", "status": "duplicate", "payment_order_id": "<existing>" },
    { "item_id": "<id>", "external_id": "102", "status": "skipped",   "reason": "validation_error" },
    { "item_id": "<id>", "external_id": "103", "status": "rejected",
      "error": { "code": "conflict", "message": "payload_hash отличается" } }
  ]
}

Статусы items на этапе submit

СтатусОписание
acceptedПлатёж принят банком, создан payment_order_id.
duplicateПлатёж с этим external_id уже существует у токена; банк не вызывается; возвращён existing payment_order_id.
skippedItem не был в статусе parsed на момент submit (например, validation_error). В поле reason — текущий DB-статус.
rejectedSubmit прошёл до банк-вызова и был отвергнут: conflict (тот же external_id + другой hash), bank_error (банк вернул ошибку), и т.п.

Submit single item — POST /payments/imports/{batch_id}/items/{item_id}/submit

POST /api/v1/integration/payments/imports/{batch_id}/items/{item_id}/submit

Retry одного item — после исправления validation_error или после bank_error. Возвращает 409 на conflict.

curl --silent --show-error -X POST \
     -H "Authorization: Bearer bcs_live_<TOKEN>" \
     "https://bsc.example.com/api/v1/integration/payments/imports/<batch_id>/items/<item_id>/submit"

В отличие от batch-submit, single-item submit принимает items в статусах parsed, failed, rejected — то есть всё, что подлежит ретраю. Conflict (тот же external_id, другой hash) → HTTP 409 (а не 200 с rejected внутри). Это упрощает обработку retry'ев на стороне клиента.

Идемпотентность — критичный контракт

:::info Правило Тройка (api_token_id, external_id, payload_hash) — уникальный ключ платежа.

  • Тот же external_id + тот же payload_hashduplicate, банк не вызывается, возвращается existing payment_order_id.
  • Тот же external_id + другой payload_hash409 conflict. Чтобы переотправить — выпустите 1С-документ с новым Номер. :::

external_id — это trim()-нутое поле Номер из секции СекцияДокумент 1С-файла. Должен быть уникальным в рамках одного API-токена.

payload_hash = sha256 от канонической сериализации полей: bank_account_id, external_id, amount, currency, doc_date, doc_type, payee (account / bik / inn / kpp / name), purpose. Если изменили хоть одно из этих полей — это другой платёж, и BCS требует новый external_id.

Batch-replay (повторная загрузка того же файла)

Повторный submit того же batch'а — безопасный no-op. Items, которые уже ушли в банк (статусы created / submitting / submitted / completed), возвращаются как skipped с reason = текущий DB-статус. Банк повторно не вызывается, новый payment_order не создаётся.

Get payment — GET /payments/{payment_order_id}

GET /api/v1/integration/payments/{payment_order_id}

Детализированный статус платежа. Платёж другого токена → 404.

curl --silent --show-error \
     -H "Authorization: Bearer bcs_live_<TOKEN>" \
     "https://bsc.example.com/api/v1/integration/payments/<payment_order_id>"
{
  "payment_order_id": "<uuid>",
  "external_id": "100",
  "status": "created",
  "amount": "1234.56",
  "currency": "RUB",
  "doc_date": "2026-05-18",
  "bank_account_id": "<bank-account-uuid>",
  "payee": {
    "account_number_masked": "4070…0111",
    "bik": "044525225",
    "inn": "5566778899",
    "kpp": "556601001",
    "name": "ООО Контрагент"
  },
  "purpose": "Оплата по счёту № 17 от 2026-05-15",
  "created_at": "2026-05-18T10:00:00Z",
  "updated_at": "2026-05-18T10:00:05Z"
}

Статусы payment_order

СтатусОписание
createdПринят BCS, ждёт отправки в банк.
submittingОтправляется в банк прямо сейчас.
submittedБанк принял; ждёт подтверждения статуса (зависит от банк-адаптера).
completedБанк подтвердил исполнение.
failedSubmit не удался (банк-ошибка или transport). Можно повторить через single-item submit.
rejectedБанк отверг платёж (валидация на стороне банка). См. подробности в error поле.

Search — GET /payments?external_id=…

GET /api/v1/integration/payments?external_id=<value>

Поиск платежа по Номеру 1С. Изолирован по api_token_id — видны только платежи текущего токена.

curl --silent --show-error \
     -H "Authorization: Bearer bcs_live_<TOKEN>" \
     "https://bsc.example.com/api/v1/integration/payments?external_id=100"

Возвращает массив payment_orders (обычно 0 или 1 элемент, но technically возможны несколько, если на разных batch'ах использовали один и тот же external_id; конфликт ловится на submit, не на upload).

Ошибки

HTTPcodeКогда возникает
400validation_errorНевалидный формат файла, отсутствующие поля, плохой UUID.
401invalid_tokenТокен невалиден / истёк / отозван.
403forbidden_purposeИспользуется MCP-токен на write-endpoint'е.
403forbidden_scopeУ токена нет scope_send_payments=true.
403forbidden_accountУ токена нет allow_send_payments=true на целевом РС.
403forbidden_companyРС принадлежит другой компании (cross-tenant).
404not_foundBatch / payment не существует или принадлежит другому токену.
409conflictТот же external_id, другой payload_hash. Single-item submit, не batch.
413payload_too_largeФайл больше 10 MB.
422account_mismatchРС-плательщик в файле не совпадает с bank_account_id запроса.
429rate_limitedПревышен per-token лимит.
500bank_errorВнутренняя ошибка банк-адаптера. Подробности в response_body.