Разработчикам/Интеграция
Каталог и заказы
Здесь по шагам описан весь процесс покупки: проверить баланс, найти товар, узнать цену, оформить заказ и получить коды.
См. также: Аутентификация · Общие правила · Вебхуки
#Аккаунт и баланс
#Аккаунт
GET/api/v1/account показывает данные вашей компании и то, включён ли доступ к API.
{
"merchant_id": "M00000001",
"name": "Acme Shop",
"legal_name": "Acme Shop Ltd",
"tier": "standard",
"billing_email": "[email protected]",
"status": "active",
"kyb_status": "approved",
"api_access_enabled": true,
"default_currency": "USD"
}default_currency— валюта вашего кошелька. Все цены, которые вы платите, указаны в ней.api_access_enabledстановитсяtrue, когда CardV одобрит вашу компанию.
#Баланс
GET/api/v1/balance показывает, сколько денег вы можете потратить.
{
"currency": "USD",
"balance": "1520.4000",
"reserved_amount": "0.0000",
"available_balance": "1520.4000",
"low_balance_threshold": "200.0000",
"low_balance_notified_at": null,
"is_active": true
}available_balance— сумма, которую можно потратить прямо сейчас. Этоbalanceминусreserved_amount.- Заказ на сумму больше
available_balanceотклоняется, деньги не списываются. low_balance_threshold— порог, при котором приходит письмо о низком балансе. Он задаётся в кабинете.- Пополнить кошелёк можно в кабинете.
#Товары (SKU)
SKU — это конкретный товар, который можно купить, например «Steam Wallet 10 USD».
Его ID выглядит так: S000456. Цену запрашивают и заказ оформляют именно по SKU.
GET/api/v1/skus возвращает список товаров, которые вам доступны. Пример:
GET /api/v1/skus?search=steam®ion=US&limit=50&offset=0Фильтры (все необязательные):
| Фильтр | Пример | Что ищет |
|---|---|---|
search | steam | ID товара, название или бренд |
brand | Steam | Название бренда (регистр не важен) |
region | US | Код или название страны |
vertical | gift_card | Товарное направление |
product_type | pin_code | Способ выдачи |
Постраничная выдача: передайте limit (по умолчанию 100, максимум 500) и offset.
Увеличивайте offset и запрашивайте снова, пока offset не дойдёт до count.
{
"count": 7,
"limit": 1,
"results": [
{
"sku_id": "S000456",
"product_id": "P000123",
"name": "Steam Wallet 10 USD",
"product_name": "Steam Wallet US",
"brand": "Steam",
"region": "US",
"vertical": "gift_card",
"product_type": "pin_code",
"denomination_type": "fixed",
"denomination_value": "10.0000",
"face_currency": "USD",
"merchant_price": "9.2500",
"settlement_currency": "USD",
"availability": "available",
"min_quantity": 1,
"max_quantity": 100,
"required_input_schema": [],
"...": "more fields"
}
],
"filter_options": {"brands": [], "regions": [], "verticals": []}
}GET/api/v1/skus/{sku_id} возвращает один товар с теми же полями.
Самые полезные поля:
| Поле | Что значит |
|---|---|
sku_id | ID, по которому запрашивают цену и оформляют заказ. |
merchant_price | Ваша цена за единицу в валюте settlement_currency. |
availability | available или unavailable. Заказывайте только available. |
denomination_type | fixed или range. См. фиксированный номинал и диапазон. |
face_currency | Валюта, указанная на карте. Может отличаться от валюты кошелька. |
min_quantity, max_quantity | Сколько единиц может быть в одной строке заказа. |
product_type | pin_code (вы получаете код) или direct_charge (мы пополняем аккаунт). |
required_input_schema | Данные, которые нужно передать для прямого пополнения. |
brand_logo_url, image_url | Картинки на серверах CardV или "". |
description, redemption_instructions, terms | Тексты, которые можно показать покупателям. |
Советы:
- Вы видите только активные товары, открытые для вашего аккаунта. Для остальных придёт 404.
- Обновляйте список товаров каждые 5–15 минут. Прямо перед заказом всегда запрашивайте цену.
filter_optionsперечисляет бренды, регионы и направления, по которым можно фильтровать.
#Фиксированный номинал и диапазон
У большинства товаров фиксированный номинал, например 10 USD. У некоторых номинал задаётся диапазоном: покупатель сам выбирает сумму, например от 5 до 500 USD.
| Тип | При запросе цены | При заказе |
|---|---|---|
fixed | Передайте quantity | Не передавайте amount |
range | Передайте quantity и amount | Передайте amount |
Для товара с диапазоном amount должен быть между min_face_value и max_face_value.
Сумма указывается в face_currency.
#Прямое пополнение
Некоторые товары пополняют аккаунт вашего покупателя напрямую, например игровой аккаунт. Для этого CardV нужны данные аккаунта. Какие именно — указано в товаре:
"required_input_schema": [
{"key": "player_id", "label": "Player ID", "required": true},
{"key": "server", "label": "Server", "required": false}
]Передайте значения в поле inputs строки заказа, используя каждый key:
"inputs": {"player_id": "123456789", "server": "EU"}- Поле обязательно, если у него не указано
"required": false. - Если обязательного значения нет, заказ отклоняется с ошибкой
items. - Это персональные данные вашего покупателя. Защищайте их (см. Безопасность).
#Запрос цены
Запрос цены показывает текущую цену за нужное количество.
GET /api/v1/skus/S000456/quote?quantity=2
GET /api/v1/skus/S000789/quote?quantity=1&amount=25.00{
"sku_id": "S000456",
"settlement_currency": "USD",
"merchant_price": "9.2500",
"quantity": 2,
"total_price": "18.5000",
"min_quantity": 1,
"max_quantity": 100,
"availability": "available"
}- Запрос цены не закрепляет цену. Цены могут измениться в любой момент.
- Чтобы подстраховаться, при заказе передайте
merchant_priceв полеexpected_unit_price. Если цена изменилась, CardV отклонит заказ и ничего не спишет. - Количество или сумма вне допустимого диапазона вернёт HTTP 400 с ошибкой
quantityилиamount.
#Оформление заказа
POST/api/v1/orders покупает один или несколько товаров и оплачивает их из кошелька.
Этот запрос нужно подписать.
{
"external_order_id": "SHOP-20260929-10001",
"items": [
{"sku_id": "S000456", "quantity": 2, "expected_unit_price": "9.2500"},
{"sku_id": "S000789", "amount": "25.00", "expected_unit_price": "23.7500"},
{
"sku_id": "S000900",
"expected_unit_price": "4.9000",
"inputs": {"player_id": "123456789"}
}
]
}| Поле | Обязательно | Что значит |
|---|---|---|
external_order_id | Да | Ваш номер заказа, от 1 до 120 символов. Должен быть уникальным. |
items | Да | Одна или несколько строк заказа. |
items[].sku_id | Да | Товар, который вы покупаете. |
items[].quantity | Нет | Сколько штук. По умолчанию 1. |
items[].amount | Для товаров с диапазоном | Номинал, который вы покупаете. |
items[].expected_unit_price | Желательно | merchant_price из запроса цены. Передавайте всегда. |
items[].inputs | Для прямого пополнения | Данные аккаунта для прямого пополнения. |
Приняв заказ, CardV сразу списывает с кошелька всю сумму. Затем CardV в фоновом режиме начинает выдачу кодов.
Ответ приходит с HTTP 201:
{
"idempotent_replay": false,
"order": {
"order_id": "O-00001234",
"external_order_id": "SHOP-20260929-10001",
"status": "accepted",
"total_amount": "46.2000",
"...": "more fields"
}
}Сохраните order.order_id. Кодов в этом ответе нет никогда — их нужно получить позже (см. Получение заказа).
#Отклонённые заказы
Отклонённый заказ возвращает HTTP 400, деньги не списываются. Причину подскажет ключ ошибки:
| Ключ | Причина | Что делать |
|---|---|---|
items | Изменилась цена, товар недоступен, неверная сумма или не хватает данных | Запросите цену заново, исправьте и отправьте снова |
balance | В кошельке не хватает денег | Пополните кошелёк в кабинете |
risk | Превышен лимит на размер заказа или дневной лимит | Свяжитесь с CardV |
external_order_id | Ваш номер заказа уже занят другим заказом | См. Безопасный повтор |
Пример ошибки из-за изменения цены:
{
"items": "SKU S000456 price changed from 9.2500 to 9.4100 USD; refresh quote and confirm again."
}Тариф вашего аккаунта определяет лимиты на один заказ, на сумму покупок за день и на число заказов за день. Дневные лимиты сбрасываются в 00:00 UTC. Узнать свои лимиты можно у CardV.
#Безопасный повтор
Ваш номер заказа (external_order_id) защищает от двойной покупки.
Если вы отправите тот же заказ с тем же номером ещё раз, CardV не спишет деньги повторно,
а вернёт уже существующий заказ.
| Что вы отправляете | Что получаете |
|---|---|
| Новый номер заказа | HTTP 201. Новый заказ. Деньги списываются с кошелька. |
| Тот же номер и тот же заказ | HTTP 200 и "idempotent_replay": true. Существующий заказ. Без списания. |
| Тот же номер, но другой заказ | HTTP 400 с ошибкой external_order_id. Ничего не происходит. |
«Тот же заказ» — это те же строки в том же порядке, с теми же товарами, количеством, суммой и данными inputs.
Если вы передаёте expected_unit_price, он должен совпадать с ценой из первого заказа.
Повторный заказ распознаётся ещё до проверки баланса и цены. Поэтому всегда возвращается первый заказ, даже если с тех пор цена изменилась.
#Как повторять без риска
Если вы не получили понятного ответа, просто отправьте тот же заказ ещё раз.
POST /orders с номером заказа R
├─ 201 или 200 → сохраните order_id. Готово.
├─ 400 items / balance / risk → заказ не создан.
│ Устраните причину и отправьте снова. R можно оставить.
├─ 400 external_order_id → R уже занят другим заказом. Остановитесь и разберитесь.
├─ 403 ошибка подписи → подпишите заново и отправьте то же тело.
├─ 429 → дождитесь Retry-After, подпишите заново, отправьте то же тело.
└─ тайм-аут, 5xx или обрыв соединения
→ отправьте то же тело с тем же R.
Придёт 201 (первая попытка не дошла) или 200 (дошла).Правила:
- Никогда не придумывайте новый номер заказа из-за того, что ответ потерялся. Если первый запрос всё-таки дошёл, с новым номером вы купите всё дважды.
- Для каждой повторной отправки нужны новые время, nonce и подпись. Тело остаётся прежним.
#Получение заказа
GET/api/v1/orders/{order_id} возвращает заказ, его статус и коды.
{
"order_id": "O-00001234",
"external_order_id": "SHOP-20260929-10001",
"status": "succeeded",
"currency": "USD",
"total_amount": "18.5000",
"created_at": "2026-09-29T08:15:30.123456Z",
"updated_at": "2026-09-29T08:15:41.004211Z",
"items": [
{
"sku_id": "S000456",
"product_name": "Steam Wallet US",
"quantity": 2,
"unit_price": "9.2500",
"total_price": "18.5000",
"delivery_count": 2,
"deliveries": [{"...": "see Codes below"}]
}
],
"...": "more fields"
}total_amount— сумма, списанная при приёме заказа.items[].unit_price— цена, закреплённая за этим заказом.- В
items[].deliveriesлежат коды целиком. Обращайтесь с ответом как с секретом. invoice_urlиdelivery_file_url— пути к счёту и к кодам в формате CSV. Оба работают только в кабинете. С API-ключом они вернут HTTP 403.- Ещё в ответе есть
id(старый номер, не используйте его),events(история только для показа) и ход выдачи по каждой строке. Всё это можно не учитывать. - Для неизвестного номера заказа придёт HTTP 404.
#Статус заказа и коды
#Статус
accepted ──► processing ──► succeeded
│
├──► partially_succeeded (часть строк выдана, часть нет)
│
└──► failed ──► refunded (деньги вернулись в кошелёк)| Статус | Завершён? | Что делать |
|---|---|---|
accepted | Нет | Ждать. Деньги списаны, выдача ещё не началась. |
processing | Нет | Ждать. Не оформляйте заказ повторно. |
succeeded | Да | Получите коды и передайте их покупателю. |
partially_succeeded | Да | Выдайте то, что пришло. За остальное деньги вернутся позже. |
failed | Ещё нет | Дождитесь refunded. Статус failed — это ещё не возврат денег. |
refunded | Да | Деньги вернулись в кошелёк. |
Если вы не используете вебхуки, опрашивайте так: через 5 секунд, затем через 10 с, 30 с, 60 с, а дальше каждые 5 минут. Не выходите за лимит запросов. Большинство заказов завершается за секунды. Некоторым нужна ручная проверка, и она может занять несколько часов.
#Коды
Каждая выданная единица — отдельный объект в items[].deliveries:
{
"status": "stored",
"delivery_type": "card_pin",
"display_fields": [
{"key": "card_number", "label": "Card number", "value": "X1234", "kind": "secret"},
{"key": "pin_code", "label": "PIN", "value": "9876", "kind": "secret"}
],
"redeem_url": "",
"expiry_date": "2027-09-29",
"instructions": "Redeem at ...",
"is_masked": false
}- Покажите покупателю
display_fields: у каждого естьlabel(название) иvalue(значение). Также покажитеredeem_url,expiry_dateиinstructions, если они не пустые. kindравенsecretдля кодов и PIN иreference— для справочных данных, например серийных номеров.delivery_typeпоказывает, что именно вы получили:code,card_pin,link,code_linkилиqr. Могут появиться новые типы, поэтому всегда показывайте данные изdisplay_fields.- Для выдачи типа
linkсамredeem_urlи есть код. Держите его в секрете. - Никогда не передавайте покупателю единицу со
statusравнымvoided. - У товаров с прямым пополнением выдачи обычно нет.
succeededзначит, что аккаунт пополнен. - Копии данных, например
card_numberиpin_code, приходят также отдельными полями. Они могут быть пустыми. - Кодов в вебхуках никогда нет. Получив вебхук, запросите заказ.
Вопросы по интеграции? Напишите на [email protected], указав ваш Merchant ID и ID заказа или запроса.