Обзор #
На BIN провайдера W карту можно выпустить с буфером: это небольшой резерв,
который PayCA держит рядом с картой на ваше имя. Собственные списания провайдера
по карте — комиссии за отказы, комиссии за трансграничные операции — оплачиваются
из буфера и не уменьшают баланс карты, который видит ваш пользователь.
Когда буфер включён для вашего клиента, карты на этих BIN выпускаются через
/v2/cards, и все карты, выпущенные так, обслуживаются тоже через /v2/cards.
Вызовы /v1/cards, которые двигают деньги или меняют состояние карты, отвечают
400 с card_buffer_requires_v2 в details: выпуск на таком BIN, а также
пополнение, вывод, заморозка, разморозка, закрытие и история по буферной карте.
Буфер для клиента включает ваш менеджер PayCA; включён ли он, покажет запрос
capability ниже.
Эти вызовы остаются в v1 и работают как раньше:
| Вызов | Назначение |
|---|---|
POST /v1/users, POST /v1/users/{id}/cardholder |
Создать пользователя и держателя, на которого выпускается карта. |
GET /v1/cards/{id}/sensitive |
Номер карты, CVC и срок действия. |
POST /v1/webhooks |
Регистрация адресов вебхуков. |
GET /v2/accounts |
Ваши счета, балансы и BIN. |
Все суммы в /v2/cards — строки в USD, не больше двух знаков после точки.
Передавайте "50.00" или "50", но не число. В ответах нули в конце
отбрасываются ("18", "2.8"), поэтому разбирайте их как десятичные числа, а не по формату.
Перед началом #
- Ключи. Передавайте
x-client-idиx-client-secretв каждом запросе.
Если ключи верные,GET /v2/accountsотвечает200. - Счёт. Возьмите из
GET /v2/accountsidсчёта, к которому подключён BIN.
В примерах это<account-id>: деньги на карту берутся с него и возвращаются
на него. - Держатель. Каждая карта выпускается на одобренного держателя
(pass_audit). Как его создать, зависит от BIN — см.
Provider W Cardholder Integration. На
B2B-BIN это один вызов с именем, фамилией и email пользователя. - Вебхуки. Подключите через
POST /v1/webhooksтакие типы:
| Тип | Что приходит |
|---|---|
card_buffer |
Каждое изменение статуса ваших операций /v2/cards. Так вы узнаёте, что карта выпущена. |
card |
События по картам, в том числе коды 3DS. Без него пользователи не смогут подтверждать онлайн-платежи. |
cardholder |
Изменения статуса держателей. |
Как делится платёж #
У каждого BIN своя политика буфера. Берите цифры из API, не зашивайте их в код:
они есть в каждой котировке и в GET /v2/cards/{id}.
| Поле | Значение |
|---|---|
target_amount |
Размер полного буфера. |
freeze_threshold_amount |
20% от полного буфера. Когда буфер падает до этой суммы или ниже, либо какое-то списание осталось неоплаченным, карта замораживается автоматически. |
resume_threshold_amount |
50% от полного буфера. Замороженная карта размораживается сама, когда буфер снова не меньше этой суммы и неоплаченных списаний нет. |
Выпуск. funding_amount — всё, что спишется с вашего счёта. Сумма делится так:
funding_amount = operation_fee (комиссия за выпуск) + buffer_refill (полный буфер) + card_credit
При буфере $14 и комиссии за выпуск $3 сумма "50.00" даёт на карте $33.00.
Минимальная сумма приходит в minimum_funding_amount (в этом примере $18.00:
минимум карты $1, буфер и комиссия).
Пополнение. В буфер идёт не больше 50% пополнения: сначала на погашение
списаний, которые буфер не покрыл (uncovered_repayment), потом на доливку буфера
до полного (buffer_refill). Остальное — card_credit. В полный буфер ничего
не идёт.
Когда буфера не хватает. Списание больше буфера обнуляет его, остаток
записывается в uncovered_fee_amount, карта замораживается. Следующее пополнение
гасит этот остаток из своей доли 50%.
Комиссии вашего тарифа (за выпуск, ежемесячная, за разморозку, за вывод)
списываются как раньше. Комиссия за выпуск показана в котировке как
operation_fee.
Выпуск карты #
Шаг 1 — Проверить доступность #
curl -s -G "$PAYCA_BASE_URL/v2/cards/issue/capability" \
--data-urlencode "source_account_id=<account-id>" \
--data-urlencode "bin=49372410" \
-H "x-client-id: $PAYCA_CLIENT_ID" \
-H "x-client-secret: $PAYCA_CLIENT_SECRET"
{
"model": "separate_buffer_v1",
"required": true,
"eligible": true,
"minimum_funding_amount": "18"
}
required: true значит, что для вашего клиента этот BIN выпускается через
/v2/cards. При eligible: false приходит reason_code:
reason_code |
Значение |
|---|---|
not_enabled |
Буфер не включён для вашего клиента или для этого BIN. |
bin_not_attached |
BIN не подключён к этому счёту. |
bin_disabled |
BIN выключен на этом счёте. |
client_not_ready, program_not_ready, parent_not_ready, delivery_fee_policy_unresolved |
Не завершена настройка на нашей стороне. Напишите в поддержку. |
Этот запрос необязателен: котировка проверяет то же самое.
Шаг 2 — Получить котировку #
curl -s -X POST "$PAYCA_BASE_URL/v2/cards/issue/quote" \
-H 'Content-Type: application/json' \
-H "x-client-id: $PAYCA_CLIENT_ID" \
-H "x-client-secret: $PAYCA_CLIENT_SECRET" \
-d '{
"source_account_id": "<account-id>",
"bin": "49372410",
"cardholder_id": "<cardholder-id>",
"funding_amount": "50.00",
"external_user_id": "usr-001"
}'
| Поле | Обязательное | Назначение |
|---|---|---|
source_account_id |
Да | Счёт, с которого берутся деньги. |
bin |
Да | BIN, на котором выпускается карта. |
funding_amount |
Да | Вся сумма, вместе с комиссией и буфером. |
cardholder_id |
Передавайте всегда | Одобренный держатель, на которого выпускается карта. Без него карта не привязана ни к одному держателю, и лимит карт на держателя к ней не применяется. |
external_user_id |
Нет | Ваш идентификатор для карты. |
Ответ (сокращённо):
{
"quote_id": "8b2d6f0e-6c1a-4b7e-9d43-2f1c0a9e5b11",
"kind": "issue",
"bin": "49372410",
"currency": "USD",
"expires_at": "2026-10-29T10:01:00Z",
"funding_amount": "50",
"operation_fee": "3",
"buffer_refill": "14",
"card_credit": "33",
"total_debit": "50",
"buffer_after": "14",
"freeze_threshold_amount": "2.8",
"resume_threshold_amount": "7",
"minimum_funding_amount": "18"
}
Пока ничего не списано. Держатель проверяется на этом шаге: неодобренный,
чужой, зарегистрированный на другой BIN или уже с максимумом карт держатель
отклоняется здесь.
Котировка действует одну минуту (expires_at). Подтвердите её сразу или
запросите новую.
Шаг 3 — Подтвердить котировку #
curl -s -X POST "$PAYCA_BASE_URL/v2/cards" \
-H 'Content-Type: application/json' \
-H "x-client-id: $PAYCA_CLIENT_ID" \
-H "x-client-secret: $PAYCA_CLIENT_SECRET" \
-d '{
"quote_id": "8b2d6f0e-6c1a-4b7e-9d43-2f1c0a9e5b11",
"idempotency_key": "issue-usr-001-0001"
}'
Ответ — 202 с операцией. total_debit теперь удержан на вашем счёте,
карта выпускается асинхронно.
{
"operation_id": "3f9a1c52-0d7e-4f6b-a1e8-6c2b9d4e7a30",
"kind": "issue",
"status": "funds_held",
"source_account_id": "<account-id>",
"funding_amount": "50",
"quoted_card_credit": "33",
"quoted_operation_fee": "3",
"maximum_source_debit": "50",
"idempotency_key": "issue-usr-001-0001"
}
idempotency_key (1–200 символов) делает запрос безопасным для повтора: тот же
ключ с той же котировкой возвращает ту же операцию. Ключ, уже использованный для
другого запроса, отклоняется с idempotency_conflict.
Шаг 4 — Дождаться результата #
Следите за операцией по вебхуку card_buffer или запрашивайте её:
curl -s "$PAYCA_BASE_URL/v2/cards/operations/<operation-id>" \
-H "x-client-id: $PAYCA_CLIENT_ID" \
-H "x-client-secret: $PAYCA_CLIENT_SECRET"
status |
Значение |
|---|---|
funds_held |
Принято, деньги удержаны на вашем счёте. |
provider_submission_prepared, provider_pending |
Отправлено эмитенту, ждём ответа. |
needs_reconciliation |
Ответ эмитента сверяется на нашей стороне. Ждите, не повторяйте запрос с новым ключом. |
succeeded |
Готово. У выпуска заполнен card_id, поля actual_* показывают, что проведено. |
rejected |
Отказ, причина в error_code. При отказе в выпуске удержанные деньги возвращаются на счёт. |
compensated |
Операция отменена, деньги возвращены. |
succeeded, rejected и compensated — конечные статусы.
Шаг 5 — Получить карту #
curl -s "$PAYCA_BASE_URL/v2/cards/<card-id>" \
-H "x-client-id: $PAYCA_CLIENT_ID" \
-H "x-client-secret: $PAYCA_CLIENT_SECRET"
В ответе карта (card) и её буфер (buffer) рядом: buffer.balance,
target_amount, uncovered_fee_amount, оба порога и флаги, что карта принимает
сейчас: can_topup, can_auto_resume, can_manual_unfreeze. model равен
separate_buffer_v1 у буферной карты и legacy_on_card у карты, выпущенной до
включения буфера.
Номер карты, CVC и срок действия — в GET /v1/cards/{card-id}/sensitive.
Пополнение карты #
Те же два шага, что при выпуске: котировка, затем подтверждение.
curl -s -X POST "$PAYCA_BASE_URL/v2/cards/<card-id>/topup/quote" \
-H 'Content-Type: application/json' \
-H "x-client-id: $PAYCA_CLIENT_ID" \
-H "x-client-secret: $PAYCA_CLIENT_SECRET" \
-d '{ "source_account_id": "<account-id>", "funding_amount": "100.00" }'
curl -s -X POST "$PAYCA_BASE_URL/v2/cards/<card-id>/topup" \
-H 'Content-Type: application/json' \
-H "x-client-id: $PAYCA_CLIENT_ID" \
-H "x-client-secret: $PAYCA_CLIENT_SECRET" \
-d '{ "quote_id": "<quote-id>", "idempotency_key": "topup-0001" }'
Котировка показывает разбивку: uncovered_repayment, buffer_refill,
card_credit, буфер до и после (buffer_before, buffer_after).
- Заморожена из-за буфера. Карту, замороженную из-за нехватки буфера, можно
пополнить. Пополнение должно быть достаточным, чтобы из своей доли 50% погасить
неоплаченное и вернуть буфер доresume_threshold_amount; иначе котировка
назовёт минимум. Карта разморозится сама, когда пополнение пройдёт. - Заморожена вами. Карту с причиной
manualилиinsufficient_funds
пополнить нельзя, сначала разморозьте её. - Занята. По карте идёт одна операция за раз. Вторая отклоняется с
operation_in_progress, пока первая не завершится. - Устаревшая котировка. Если буфер изменился между котировкой и
подтверждением, подтверждение отклоняется сquote_stale; запросите новую.
Вывод с карты #
Переводит деньги с карты обратно на счёт, с которого она пополнялась. Буфер не
затрагивается.
curl -s -X POST "$PAYCA_BASE_URL/v2/cards/<card-id>/withdraw" \
-H 'Content-Type: application/json' \
-H "x-client-id: $PAYCA_CLIENT_ID" \
-H "x-client-secret: $PAYCA_CLIENT_SECRET" \
-d '{ "source_account_id": "<account-id>", "amount": "20.00", "idempotency_key": "withdraw-0001" }'
source_account_id должен быть счётом, с которого выпущена карта. Комиссия за
вывод по вашему тарифу, если она есть, вычитается из суммы
(quoted_operation_fee); сумма, которая её не покрывает, отклоняется.
Заморозка и разморозка #
curl -s -X POST "$PAYCA_BASE_URL/v2/cards/<card-id>/freeze" \
-H 'Content-Type: application/json' \
-H "x-client-id: $PAYCA_CLIENT_ID" \
-H "x-client-secret: $PAYCA_CLIENT_SECRET" \
-d '{ "idempotency_key": "freeze-0001", "reason": "manual" }'
POST /v2/cards/<card-id>/unfreeze принимает то же тело. У карты может быть
несколько причин заморозки сразу, каждая снимается отдельно:
reason |
Кто ставит | Как снять |
|---|---|---|
manual (по умолчанию) |
Вы | unfreeze с manual. Бесплатно. |
insufficient_funds |
Вы, после отказа по недостатку средств | unfreeze с insufficient_funds. Списывается комиссия за разморозку по вашему тарифу (buffer.unfreeze_fee), когда эмитент подтвердит разморозку. |
| заморозка буфера | PayCA, автоматически | Пополнение, которое восстанавливает буфер. Через unfreeze не снимается. |
Разморозка, после которой карта осталась бы замороженной по другой причине,
отклоняется: сначала снимите ту причину. Заранее это видно по
can_manual_unfreeze в GET /v2/cards/{id}.
Закрытие карты #
curl -s -X DELETE "$PAYCA_BASE_URL/v2/cards/<card-id>" \
-H 'Content-Type: application/json' \
-H "x-client-id: $PAYCA_CLIENT_ID" \
-H "x-client-secret: $PAYCA_CLIENT_SECRET" \
-d '{ "idempotency_key": "close-0001" }'
Закрытие подтверждает эмитент, это может занять около 30 дней. До этого
операция остаётся в процессе (provider_pending).
- Баланс карты возвращается на ваш счёт, когда эмитент подтвердит закрытие.
- Буфер. Списания могут прийти и после закрытия карты, поэтому буфер держится
60 дней после запроса на закрытие и оплачивает их. Остаток затем
возвращается на ваш счёт, а вебхукcard_bufferпо операции закрытия приходит
ещё раз с событиемclose_hold_released.
История #
| Вызов | Что возвращает |
|---|---|
GET /v2/cards/{id}/transactions |
Записи по карте, её буферу и комиссиям. |
GET /v2/accounts/{id}/transactions |
Историю счёта вместе с операциями буфера. |
Оба принимают limit (1–100, по умолчанию 50) и cursor; чтобы получить
следующую страницу, передайте next_cursor предыдущей. Когда на счёте появились
операции буфера, GET /v1/accounts/{id}/transactions для него отвечает
card_buffer_history_requires_v2.
Вебхуки #
Тело вебхука card_buffer — это сама операция, тот же объект, что в
GET /v2/cards/operations/{operation_id}. Он отправляется при каждом изменении
статуса, поэтому по одной операции приходит несколько вебхуков. Убирайте дубли по
паре operation_id + status и проверяйте заголовок x-signature, как для любого
вебхука PayCA.
Перед обработкой смотрите kind: кроме ваших операций (issue, topup,
withdraw, freeze, unfreeze, close) могут приходить операции, которые вы
не запускали, например комиссия, оплаченная из буфера.
Ошибки #
Ошибка приходит как {"code": "...", "details": "..."}.
| HTTP | code |
Значение | Что делать |
|---|---|---|---|
| 409 | quote_expired |
Котировке больше минуты. | Запросите новую. |
| 409 | quote_stale |
Котировка больше не соответствует карте или счёту. | Запросите новую. |
| 409 | idempotency_conflict |
Ключ уже использован для другого запроса. | Для нового запроса нужен новый ключ. |
| 409 | operation_in_progress |
По карте уже идёт операция или карта закрывается. | Дождитесь её завершения. |
| 409 | buffer_state_changed |
Состояние заморозки изменилось до выполнения запроса. | Запросите карту заново. |
| 403 | feature_not_enabled |
Для вашего клиента не включены буфер, пополнения или политика буфера на BIN. | Обратитесь к менеджеру. |
| 400 | card_buffer_ineligible |
Запрос нельзя выполнить в таком виде: сумма ниже минимума, карта заморожена или закрывается, счёт не владеет картой. В details причина, а для слишком маленькой суммы — минимум. |
Исправьте запрос по details. |
| 400 | ErrFailedPrecondition с card_buffer_requires_v2 |
Вызов v1 для BIN или карты с буфером. | Используйте вызов /v2/cards. |
| 400 | ErrFailedPrecondition с card_buffer_history_requires_v2 |
GET /v1/accounts/{id}/transactions по счёту с операциями буфера. |
Используйте GET /v2/accounts/{id}/transactions. |
| 400 | buffer_model_requires_v2 |
Вызов пополнения v2 для карты без буфера. | Для этой карты используйте вызов v1. |
| 400 | ErrInvalidArg с card_buffer_ineligible |
BIN не поддерживает буфер. | Выберите BIN с буфером. |
| 400 | ErrFailedPrecondition с kyc required: ... |
Держателя нет, он не одобрен или зарегистрирован не на этот BIN. | См. Provider W Cardholder Integration. |
| 400 | ErrFailedPrecondition с cardholder card limit reached: ... |
У держателя максимум активных карт. | Закройте одну из его карт или возьмите другого держателя. |
| 400 | ErrInsufficientFunds |
На счёте не хватает денег на котировку. | Пополните счёт. |
| 400 | ErrInvalidArg |
Неверное тело: сумма не положительная строка с двумя знаками, или переданы обе суммы, или ни одной. | Исправьте запрос. |
Справочник вызовов #
| Метод | Вызов | Описание |
|---|---|---|
GET |
/v2/cards/issue/capability |
Выпускается ли BIN с буфером для этого счёта, и минимальная сумма. |
POST |
/v2/cards/issue/quote |
Котировка выпуска. |
POST |
/v2/cards |
Подтверждение котировки выпуска. |
GET |
/v2/cards/{id} |
Карта и буфер. |
POST |
/v2/cards/{id}/topup/quote |
Котировка пополнения. |
POST |
/v2/cards/{id}/topup |
Подтверждение котировки пополнения. |
POST |
/v2/cards/{id}/withdraw |
Вывод с карты на её счёт. |
POST |
/v2/cards/{id}/freeze |
Заморозка с причиной. |
POST |
/v2/cards/{id}/unfreeze |
Снятие причины заморозки. |
DELETE |
/v2/cards/{id} |
Закрытие карты. |
GET |
/v2/cards/operations/{operation_id} |
Операция. |
GET |
/v2/cards/{id}/transactions |
История карты. |
GET |
/v2/accounts/{id}/transactions |
История счёта. |