Payments
Buy (RUB → USDT через СБП), Sell (USDT → RUB через СБП), Crypto Deposit и Withdraw на managed customers. Все операции требуют kyc_status='verified'.
⚠
KYC gate
Любая операция без верифицированного customer'а вернёт 422 KYC_REQUIRED. Сначала пройдите KYC.
Котировки
Курсы обновляются у Finance OS раз в минуту и фиксируются на 60 секунд после первой котировки в сессии.
Buy quote
POST
/api/v1/customers/{uuid}/payments/buy/calculate
⚷
Bearer Token
Сколько USDT за N рублей
Body
amount_rub
numeric
required
100..100000 RUB.
| Name | Type | Required | Description |
|---|---|---|---|
amount_rub
|
numeric
|
required | 100..100000 RUB. |
Sell quote
POST
/api/v1/customers/{uuid}/payments/sell/calculate
⚷
Bearer Token
Сколько RUB за N USDT
Body
amount_usdt
numeric
required
1..1000 USDT.
| Name | Type | Required | Description |
|---|---|---|---|
amount_usdt
|
numeric
|
required | 1..1000 USDT. |
Текущие курсы (без customer)
GET
/api/v1/payments/rates
⚷
Bearer Token
Buy/sell rate без привязки к customer'у
Buy (RUB → USDT)
POST
/api/v1/customers/{uuid}/payments/buy
⚷
Bearer Token
Создать СБП-платёж
Возвращает transaction с QR-кодом для СБП. End-user сканирует QR в своём банковском приложении, переводит RUB, USDT зачисляется на баланс customer'а.
Body
amount_rub
numeric
required
100..100000.
bank_id
string
required
Идентификатор банка-получателя.
| Name | Type | Required | Description |
|---|---|---|---|
amount_rub
|
numeric
|
required | 100..100000. |
bank_id
|
string
|
required | Идентификатор банка-получателя. |
Responses
{
"data": {
"uuid": "1a7ee75b-7f0e-48af-a075-a56721f1140e",
"type": "payment",
"status": "pending",
"status_code": 0,
"amount_from": "1000",
"amount_to": "12.9602550000",
"currency_from": "RUB",
"currency_to": "USDT",
"rate": "0.012960254775",
"qr_url": "https://fin-os.io/api/v1/sandbox/qr/abc123.png",
"expires_at": "2026-05-27T14:38:00+00:00",
"env": "sandbox"
}
}
Buy status
GET
/api/v1/customers/{uuid}/payments/buy/{txUuid}/status
⚷
Bearer Token
Проверить статус buy-транзакции
В sandbox через ~5 секунд автоматически переходит в
completed и USDT кредитится на баланс customer'а. В live статус обновляется через провайдерский webhook.Sell (USDT → RUB)
POST
/api/v1/customers/{uuid}/payments/sell
⚷
Bearer Token
Создать СБП-выплату на банковский счёт
Сумма USDT блокируется (
locked) в момент создания. При success — consume'ится; при failure — возвращается в available.Body
amount_usdt
numeric
required
1..1000.
bank_id
string
required
Банк-получатель.
phone
string
required
Телефон счёта в СБП (E.164).
| Name | Type | Required | Description |
|---|---|---|---|
amount_usdt
|
numeric
|
required | 1..1000. |
bank_id
|
string
|
required | Банк-получатель. |
phone
|
string
|
required | Телефон счёта в СБП (E.164). |
Sell status
GET
/api/v1/customers/{uuid}/payments/sell/{txUuid}/status
⚷
Bearer Token
Проверить статус sell-транзакции
Crypto deposit
POST
/api/v1/customers/{uuid}/payments/deposit/address
⚷
Bearer Token
Получить адрес для крипто-депозита
Возвращает адрес для пополнения customer'а в указанной сети. End-user отправляет криптовалюту по этому адресу — балансы customer'а обновятся при подтверждении транзакции в блокчейне.
Body
currency
enum
optional
USDT, BTC, ETH, LTC, KAS.Default:
USDT| Name | Type | Required | Description |
|---|---|---|---|
currency
|
enum
|
optional |
USDT, BTC, ETH, LTC, KAS.
Default:
USDT |
Responses
{
"data": {
"currency": "USDT",
"network": "TRC20",
"address": "TXYZ1234567890abcdef...",
"min_deposit": "1.000000",
"env": "live",
"warning": "Send only USDT in TRC20 network to this address."
}
}
Crypto withdraw
POST
/api/v1/customers/{uuid}/payments/withdraw
⚷
Bearer Token
Crypto-вывод на внешний адрес
Сумма блокируется (
locked). После транзакции в сети — consume'ится.Body
currency
enum
required
USDT/BTC/ETH/LTC/KAS.amount
numeric
required
Минимум 0.000001.
address
string
required
Внешний адрес.
| Name | Type | Required | Description |
|---|---|---|---|
currency
|
enum
|
required |
USDT/BTC/ETH/LTC/KAS.
|
amount
|
numeric
|
required | Минимум 0.000001. |
address
|
string
|
required | Внешний адрес. |
Коды ошибок
Примеры кода
# Buy: RUB → USDT через СБП
curl -X POST https://fin-os.io/api/v1/customers/$UUID/payments/buy \
-H "Authorization: Bearer sk_test_..." \
-H "Content-Type: application/json" \
-d '{"amount_rub": 1000, "bank_id": "sber"}'
# Sell: USDT → RUB через СБП
curl -X POST https://fin-os.io/api/v1/customers/$UUID/payments/sell \
-H "Authorization: Bearer sk_test_..." \
-d '{"amount_usdt": 10, "bank_id": "sber", "phone": "+79001234567"}'
# Crypto deposit address
curl -X POST https://fin-os.io/api/v1/customers/$UUID/payments/deposit/address \
-H "Authorization: Bearer sk_test_..." \
-d '{"currency": "USDT"}'
# Crypto withdraw
curl -X POST https://fin-os.io/api/v1/customers/$UUID/payments/withdraw \
-H "Authorization: Bearer sk_test_..." \
-d '{"currency": "USDT", "amount": 5, "address": "TXYZabc..."}'
async function buyUsdt(uuid, amountRub, bankId) {
const res = await fetch(
`https://fin-os.io/api/v1/customers/${uuid}/payments/buy`,
{
method: 'POST',
headers: {
'Authorization': `Bearer ${process.env.FINOS_SECRET_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({ amount_rub: amountRub, bank_id: bankId }),
}
);
const { data } = await res.json();
console.log('Show QR to user:', data.qr_url);
return data; // poll status via /payments/buy/{data.uuid}/status
}
async function pollUntilCompleted(uuid, txUuid, timeoutSec = 600) {
const deadline = Date.now() + timeoutSec * 1000;
while (Date.now() < deadline) {
const r = await fetch(
`https://fin-os.io/api/v1/customers/${uuid}/payments/buy/${txUuid}/status`,
{ headers: { 'Authorization': `Bearer ${process.env.FINOS_SECRET_KEY}` } }
);
const { data } = await r.json();
if (['completed', 'failed', 'cancelled'].includes(data.status)) return data;
await new Promise(r => setTimeout(r, 3000));
}
throw new Error('timeout');
}
use Illuminate\Support\Facades\Http;
class FinosPayments
{
private string $base = 'https://fin-os.io/api/v1';
public function __construct(private string $secretKey) {}
public function buy(string $customerUuid, string $amountRub, string $bankId): array
{
return Http::withToken($this->secretKey)
->post("{$this->base}/customers/{$customerUuid}/payments/buy", [
'amount_rub' => $amountRub,
'bank_id' => $bankId,
])
->json('data');
}
public function sell(string $uuid, string $amountUsdt, string $bankId, string $phone): array
{
return Http::withToken($this->secretKey)
->post("{$this->base}/customers/{$uuid}/payments/sell", compact('amountUsdt', 'bankId', 'phone'))
->json('data');
}
public function withdraw(string $uuid, string $currency, string $amount, string $address): array
{
return Http::withToken($this->secretKey)
->post("{$this->base}/customers/{$uuid}/payments/withdraw", compact('currency', 'amount', 'address'))
->json('data');
}
}
import requests, time, os
FINOS = 'https://fin-os.io/api/v1'
KEY = os.environ['FINOS_SECRET_KEY']
H = {'Authorization': f'Bearer {KEY}'}
def buy(uuid, amount_rub, bank_id):
r = requests.post(
f'{FINOS}/customers/{uuid}/payments/buy',
headers=H,
json={'amount_rub': amount_rub, 'bank_id': bank_id},
)
r.raise_for_status()
return r.json()['data']
def poll_buy(uuid, tx_uuid, timeout=600):
deadline = time.time() + timeout
while time.time() < deadline:
r = requests.get(
f'{FINOS}/customers/{uuid}/payments/buy/{tx_uuid}/status', headers=H
)
data = r.json()['data']
if data['status'] in ('completed', 'failed', 'cancelled'):
return data
time.sleep(3)
raise TimeoutError('Buy did not complete in time')
# usage
tx = buy(customer_uuid, 1000, 'sber')
print('Show QR:', tx['qr_url'])
result = poll_buy(customer_uuid, tx['uuid'])
print('Final:', result['status'])