Finance OS / API

Authentication

Finance OS API аутентифицирует интеграции секретными API-ключами. Ключ передаётся стандартным заголовком Authorization: Bearer ... на каждом запросе - никакого логина, никаких сессий и рефреш-токенов.

Кому выдаются ключи
API-ключи получает организация, подключённая к Merchant API или к рублёвому платёжному шлюзу. Интеграция всегда идёт сервер-сервер: ключ живёт на вашем бэкенде и наружу не попадает.

API Keys

Схема как у Stripe / Plaid / Twilio: у учётной записи две пары ключей - для Live (production) и Sandbox (test). Каждая пара состоит из публичного и секретного ключа.

Как сгенерировать

  1. Войти в fin-os.io
  2. Сайдбар → НастройкиAPI ключи
  3. Переключить тоггл MAIN / SANDBOX в нужное окружение
  4. Нажать «Сгенерировать Live ключи» (или Sandbox)
  5. В модальном окне скопировать sk_live_… / sk_test_… - он показывается только один раз

Формат ключей

pk_live_xxx ~36 chars optional
Public Key (Live). Зарезервирован под frontend-кейсы (v1.1). Сейчас не используется для аутентификации - можно безопасно отображать в UI или коммитить.
sk_live_xxx ~48 chars optional
Secret Key (Live). Это и есть ваш Bearer-токен. Хранится на сервере как hash - потерянный ключ восстановить нельзя, только перегенерировать.
pk_test_xxx ~36 chars optional
Public Key (Sandbox). Для тестового окружения.
sk_test_xxx ~48 chars optional
Secret Key (Sandbox). Изолированная песочница: реальных денег такие вызовы не двигают.

Использование

Authorization: Bearer sk_live_abc123...

Это всё. Передавайте на каждом запросе к защищённым endpoints.

Разделение окружений

Окружение определяется префиксом ключа и изолировано на уровне данных:

  • sk_live_… видит только боевые сущности (env='live').
  • sk_test_… видит только песочницу (env='sandbox').
  • Обращение ключом одного окружения к объекту другого возвращает 404 - намеренно, а не 403: ответ не должен подтверждать существование чужого объекта.

Lifecycle

  • Срок жизни: ключ живёт до явной перегенерации. По таймеру не истекает.
  • Регенерация: кнопка «Перегенерировать» в кабинете создаёт новую пару и инвалидирует старую немедленно. Все клиенты со старым ключом получат 401, пока не обновят.
  • Отзыв без выпуска нового: отдельной операции нет - перегенерируйте, чтобы убить старый ключ. Если нужно временно «выключить» интеграцию - отзовите доступ на стороне клиента.
  • Last-used трекинг: на странице ключей видно last_used_at и last_used_ip - полезно для аудита.
Утечка ключа
Если sk_live_… попал в git, чат, лог - немедленно жмите «Перегенерировать» в кабинете. Через несколько секунд старый ключ перестанет работать.

Что открывает ключ

Валидный ключ - это ещё не доступ ко всем контурам. Разделы включаются по договору:

  1. Merchant API (/api/v1/customers/*, /api/v1/merchant/*) требует поднятого флага B2B на учётной записи владельца ключа. Без него - 403 B2B_ACCESS_REQUIRED. Порядок подключения - в разделе Merchant API.
  2. AML Screening (/api/v1/aml/*) тарифицируется с мастер-счёта: без остатка первый же вызов вернёт 402. См. Merchant Balance.
  3. Payment Gateway (RUB) подключается отдельным договором и своими эндпоинтами - см. Overview.

Scopes (v1.1)

В v1 у ключа - полный доступ к ресурсам владельца в пределах подключённых контуров. В v1.1 планируется:

  • Scopes ключа: read-only, payments, withdrawal, compliance
  • Public key scope: pk_live_… получит read-only-доступ к публичным эндпоинтам (курсы, статус сети) - безопасно встраивать в frontend

Ошибки

401 Unauthorized optional
Bearer отсутствует, невалиден или ключ перегенерирован.
403 Forbidden optional
Ключ валидный, но контур не подключён или объект принадлежит другой учётной записи.
402 Payment Required optional
Недостаточно средств на мастер-счёте для платного вызова. См. Merchant Balance.
404 Not Found optional
Объект не существует - в том числе когда ключ Live обращается к объекту Sandbox и наоборот.

Best practices

  • Не коммитьте sk_live_… в git - храните в .env, AWS Secrets Manager, HashiCorp Vault.
  • Используйте sk_test_… на dev/staging, sk_live_… только в production.
  • Ключ не должен попадать в браузер или любой другой клиент на стороне пользователя: он даёт полный доступ к вашим ресурсам.
  • Для каждой среды/команды - отдельная учётная запись с собственными ключами. Это упрощает revoke при увольнении.
  • Все запросы - только по HTTPS. Обычный HTTP отклоняется на уровне nginx.

Примеры кода

# Секретный ключ получен в кабинете: Настройки → API ключи
curl -X GET https://fin-os.io/api/v1/merchant/balance \
  -H "Authorization: Bearer sk_live_aBcDeFgH..." \
  -H "Accept: application/json"

# Тот же вызов в песочнице - отличается только префикс ключа
curl -X GET https://fin-os.io/api/v1/merchant/balance \
  -H "Authorization: Bearer sk_test_aBcDeFgH..." \
  -H "Accept: application/json"
// Node.js / Deno - ключ только на сервере, никогда в браузере
const API_KEY = process.env.FINOS_SECRET_KEY; // sk_live_xxx из .env

const res = await fetch('https://fin-os.io/api/v1/merchant/balance', {
  headers: {
    'Authorization': `Bearer ${API_KEY}`,
    'Accept': 'application/json',
  },
});

if (res.status === 401) throw new Error('Ключ невалиден или перегенерирован');
if (res.status === 403) throw new Error('Контур не подключён к учётной записи');

const { data } = await res.json();
// Laravel HTTP
$apiKey = config('services.finos.secret_key'); // sk_live_xxx из .env

$response = Http::withToken($apiKey)
    ->acceptJson()
    ->get('https://fin-os.io/api/v1/merchant/balance');

if ($response->status() === 402) {
    // Мастер-счёт пуст - пополните до следующего платного вызова
    logger()->warning('Finance OS: master account is empty');
}

$balance = $response->json('data');
import os, requests

API_KEY = os.environ['FINOS_SECRET_KEY']  # sk_live_xxx

r = requests.get(
    'https://fin-os.io/api/v1/merchant/balance',
    headers={
        'Authorization': f'Bearer {API_KEY}',
        'Accept': 'application/json',
    },
)

if r.status_code == 401:
    raise RuntimeError('Ключ невалиден или перегенерирован')

balance = r.json()['data']