Virtual Cards
Виртуальные карты as-a-Service на ваших managed customers: выпуск (issue), список, детали и пополнение (top-up). Каждая карта привязана к конкретному customer'у и номинирована в RUB. Наружу отдаётся только обезличенное представление карты — внутренний платёжный контур не раскрывается.
⚠
Денежные операции дормант
Выпуск и пополнение карт в live по умолчанию выключены и возвращают 503, пока функция не активирована для платформы. В sandbox всё работает на синтетике: POST /cards отдаёт фиктивную карту с искусственным masked_pan, а POST /cards/{card}/topup симулирует зачисление, не обращаясь к апстриму.
ℹ
Окружение по префиксу ключа
Как и во всём B2B API, окружение выводится из префикса ключа: sk_test_ = sandbox, sk_live_ = live. Отдельного заголовка нет. Карта видна только тому ключу, чьё окружение совпадает с env карты; обращение к карте из чужого окружения возвращает 404 (намеренно, чтобы не раскрывать существование).
Объект карты
Все endpoints возвращают карту в одном и том же обезличенном виде:
Card object
id
string
optional
UUID карты. Используется в путях
/cards/{card}.status
string
optional
Статус карты в нижнем регистре, напр.
open.masked_pan
string|null
optional
Маскированный номer, напр.
537643••••9f3a. Для только что выпущенной live-карты может быть null, пока не подтянется снимок при GET /cards/{card}.balance
string
optional
Баланс в RUB строкой с 2 знаками, напр.
0.00.currency
string
optional
Всегда
RUB.created_at
string
optional
ISO 8601 timestamp создания.
markup
object
optional
Ваша наценка (%) по операциям карт:
issue_pct, topup_pct, payout_pct. 0, если наценка не настроена.| Name | Type | Required | Description |
|---|---|---|---|
id
|
string
|
optional |
UUID карты. Используется в путях /cards/{card}.
|
status
|
string
|
optional |
Статус карты в нижнем регистре, напр. open.
|
masked_pan
|
string|null
|
optional |
Маскированный номer, напр. 537643••••9f3a. Для только что выпущенной live-карты может быть null, пока не подтянется снимок при GET /cards/{card}.
|
balance
|
string
|
optional |
Баланс в RUB строкой с 2 знаками, напр. 0.00.
|
currency
|
string
|
optional |
Всегда RUB.
|
created_at
|
string
|
optional | ISO 8601 timestamp создания. |
markup
|
object
|
optional |
Ваша наценка (%) по операциям карт: issue_pct, topup_pct, payout_pct. 0, если наценка не настроена.
|
Выпуск карты
POST
/api/v1/customers/{uuid}/cards
⚷
Bearer Token
Выпустить виртуальную карту для customer'а
Все поля профиля держателя необязательны — карту можно выпустить и без них. В sandbox возвращается детерминированная синтетическая карта; в live операция дормант (см. предупреждение выше).
Body
first_name
string
optional
Имя держателя. До 100 символов.
last_name
string
optional
Фамилия держателя. До 100 символов.
patronymic
string
optional
Отчество. До 100 символов.
birth_date
string
optional
Дата рождения. До 20 символов.
email
string
optional
Email держателя. Валидный email, до 150 символов.
| Name | Type | Required | Description |
|---|---|---|---|
first_name
|
string
|
optional | Имя держателя. До 100 символов. |
last_name
|
string
|
optional | Фамилия держателя. До 100 символов. |
patronymic
|
string
|
optional | Отчество. До 100 символов. |
birth_date
|
string
|
optional | Дата рождения. До 20 символов. |
email
|
string
|
optional | Email держателя. Валидный email, до 150 символов. |
Responses
{
"data": {
"id": "1a7ee75b-7f0e-48af-a075-a56721f1140e",
"status": "open",
"masked_pan": "537643••••9f3a",
"balance": "0.00",
"currency": "RUB",
"created_at": "2026-07-13T12:41:08+00:00",
"markup": {
"issue_pct": 2.5,
"topup_pct": 1.5,
"payout_pct": 1.0
}
}
}
Список карт
GET
/api/v1/customers/{uuid}/cards
⚷
Bearer Token
Все карты customer'а
Возвращает карты данного customer'а в текущем окружении, самые свежие первыми.
Responses
{
"data": [
{
"id": "1a7ee75b-7f0e-48af-a075-a56721f1140e",
"status": "open",
"masked_pan": "537643••••9f3a",
"balance": "1500.00",
"currency": "RUB",
"created_at": "2026-07-13T12:41:08+00:00",
"markup": {
"issue_pct": 2.5,
"topup_pct": 1.5,
"payout_pct": 1.0
}
}
]
}
Детали карты
GET
/api/v1/customers/{uuid}/cards/{card}
⚷
Bearer Token
Одна карта по UUID
В live (если функция включена) подтягивает свежий снимок баланса/статуса; иначе отдаёт последний локальный снимок. При любой ошибке апстрима наружу возвращается локальный снимок — сырой ответ провайдера не раскрывается.
Responses
{
"data": {
"id": "1a7ee75b-7f0e-48af-a075-a56721f1140e",
"status": "open",
"masked_pan": "537643••••9f3a",
"balance": "1500.00",
"currency": "RUB",
"created_at": "2026-07-13T12:41:08+00:00",
"markup": {
"issue_pct": 2.5,
"topup_pct": 1.5,
"payout_pct": 1.0
}
}
}
Пополнение карты
POST
/api/v1/customers/{uuid}/cards/{card}/topup
⚷
Bearer Token
Пополнить карту со счёта площадки (RUB)
Зачисляет RUB на карту. В sandbox зачисление симулируется и баланс сразу увеличивается; в live операция дормант (
503, пока функция не активирована). Ответ приходит с кодом 202 Accepted — карта пополняется асинхронно.Body
amount
string
required
Сумма пополнения в RUB. Строка-число до 2 знаков после точки, напр.
1500.00 или 1500.| Name | Type | Required | Description |
|---|---|---|---|
amount
|
string
|
required |
Сумма пополнения в RUB. Строка-число до 2 знаков после точки, напр. 1500.00 или 1500.
|
Responses
{
"data": {
"status": "accepted",
"amount": "1500.00",
"currency": "RUB",
"card_id": "1a7ee75b-7f0e-48af-a075-a56721f1140e",
"pricing": {
"markup_pct": 1.5
},
"sandbox": true
}
}
pricing.markup_pct — ваша наценка на пополнение (та же, что markup.topup_pct в объекте карты). Флаг sandbox присутствует только в sandbox-ответах; в live его нет.
Коды ошибок
Ошибки приходят в стандартном конверте { "error": { "code", "message", "request_id" } }. Успех — { "data": ... }.
Примеры кода
# Выпустить виртуальную карту для customer'а
curl -X POST https://fin-os.io/api/v1/customers/$UUID/cards \
-H "Authorization: Bearer sk_test_..." \
-H "Content-Type: application/json" \
-d '{"first_name": "Ivan", "last_name": "Petrov", "email": "ivan@example.com"}'
# Список карт customer'а
curl https://fin-os.io/api/v1/customers/$UUID/cards \
-H "Authorization: Bearer sk_test_..."
# Детали одной карты
curl https://fin-os.io/api/v1/customers/$UUID/cards/$CARD_ID \
-H "Authorization: Bearer sk_test_..."
# Пополнить карту со счёта площадки (RUB)
curl -X POST https://fin-os.io/api/v1/customers/$UUID/cards/$CARD_ID/topup \
-H "Authorization: Bearer sk_test_..." \
-H "Content-Type: application/json" \
-d '{"amount": "1500.00"}'
const FINOS_API = 'https://fin-os.io/api/v1';
const SECRET = process.env.FINOS_SECRET_KEY; // sk_test_ или sk_live_
const H = { 'Authorization': `Bearer ${SECRET}`, 'Content-Type': 'application/json' };
// Выпустить виртуальную карту
async function issueCard(uuid, profile = {}) {
const res = await fetch(`${FINOS_API}/customers/${uuid}/cards`, {
method: 'POST',
headers: H,
body: JSON.stringify(profile), // { first_name, last_name, patronymic, birth_date, email }
});
if (res.status === 503) throw new Error('Card issuance not enabled yet');
const { data } = await res.json();
return data; // { id, status, masked_pan, balance, currency, markup, ... }
}
// Список карт
async function listCards(uuid) {
const res = await fetch(`${FINOS_API}/customers/${uuid}/cards`, { headers: H });
return (await res.json()).data;
}
// Пополнить карту (RUB, строкой до 2 знаков)
async function topupCard(uuid, cardId, amount) {
const res = await fetch(
`${FINOS_API}/customers/${uuid}/cards/${cardId}/topup`,
{ method: 'POST', headers: H, body: JSON.stringify({ amount }) }
);
if (res.status === 503) throw new Error('Card top-up not enabled yet');
return (await res.json()).data; // { status: 'accepted', amount, currency, card_id, pricing }
}
use Illuminate\Support\Facades\Http;
class FinosCards
{
private string $base = 'https://fin-os.io/api/v1';
public function __construct(private string $secretKey) {} // sk_test_ или sk_live_
/** Выпустить виртуальную карту. */
public function issue(string $customerUuid, array $profile = []): array
{
return Http::withToken($this->secretKey)
->post("{$this->base}/customers/{$customerUuid}/cards", $profile)
->throw()
->json('data');
}
/** Все карты customer'а. */
public function list(string $customerUuid): array
{
return Http::withToken($this->secretKey)
->get("{$this->base}/customers/{$customerUuid}/cards")
->json('data');
}
/** Пополнить карту (amount — строка RUB до 2 знаков). */
public function topup(string $customerUuid, string $cardId, string $amount): array
{
return Http::withToken($this->secretKey)
->post("{$this->base}/customers/{$customerUuid}/cards/{$cardId}/topup", [
'amount' => $amount,
])
->throw()
->json('data');
}
}
import requests, os
FINOS = 'https://fin-os.io/api/v1'
KEY = os.environ['FINOS_SECRET_KEY'] # sk_test_ или sk_live_
H = {'Authorization': f'Bearer {KEY}'}
def issue_card(uuid, profile=None):
r = requests.post(
f'{FINOS}/customers/{uuid}/cards', headers=H, json=profile or {}
)
if r.status_code == 503:
raise RuntimeError('Card issuance not enabled yet')
r.raise_for_status()
return r.json()['data']
def list_cards(uuid):
r = requests.get(f'{FINOS}/customers/{uuid}/cards', headers=H)
r.raise_for_status()
return r.json()['data']
def topup_card(uuid, card_id, amount):
# amount — строка RUB до 2 знаков, напр. '1500.00'
r = requests.post(
f'{FINOS}/customers/{uuid}/cards/{card_id}/topup',
headers=H, json={'amount': amount},
)
if r.status_code == 503:
raise RuntimeError('Card top-up not enabled yet')
r.raise_for_status()
return r.json()['data']
# usage
card = issue_card(customer_uuid, {'first_name': 'Ivan', 'email': 'ivan@example.com'})
print('Card:', card['id'], card['masked_pan'])
topup_card(customer_uuid, card['id'], '1500.00')