Files
frontend/src/features/b2b/api/b2bApi.ts
2026-07-24 18:28:45 +03:00

148 lines
3.5 KiB
TypeScript

import { getCsrfToken } from '@shared/api/csrf'
import { refreshAccessToken } from '@shared/api/tokenStore'
const B2B_API_URL = 'https://app.b2b.elcsa.ru'
async function doB2bRequest<T>(
path: string,
options: RequestInit,
allowRetry: boolean,
): Promise<T> {
const csrf = await getCsrfToken()
const res = await fetch(`${B2B_API_URL}${path}`, {
...options,
credentials: 'include',
headers: {
'X-CSRF-Token': csrf,
...options.headers,
},
})
if (res.status === 401 && allowRetry) {
try {
await refreshAccessToken()
return doB2bRequest<T>(path, options, false)
} catch {
throw new Error('Unauthorized')
}
}
const data = await res.json()
if (!res.ok) throw data
return data as T
}
export interface CreatePurchaseRequestBody {
rub_amount: number | string
comment?: string | null
target_wallet_chain?: string | null
target_wallet_address?: string | null
}
export interface B2bPurchaseRequest {
id: string
organization_id: string
status: string
usdt_amount: string
rub_amount: string | null
exchange_rate: string | null
service_fee_percent: string | null
comment: string | null
admin_comment: string | null
target_wallet_chain: string | null
target_wallet_address: string | null
tx_hash: string | null
created_at: string | null
updated_at: string | null
completed_at: string | null
}
export interface B2bPurchaseRequestListResponse {
items: B2bPurchaseRequest[]
total: number
}
export function createPurchaseRequest(
body: CreatePurchaseRequestBody,
): Promise<B2bPurchaseRequest> {
return doB2bRequest('/purchase-requests', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(body),
}, true)
}
export function getMyPurchaseRequests(
limit = 50,
offset = 0,
): Promise<B2bPurchaseRequestListResponse> {
return doB2bRequest(`/purchase-requests?limit=${limit}&offset=${offset}`, {}, true)
}
// --- Organizations (legal-entity wallets) ---
// Note: organizations endpoints live under /v1, unlike /purchase-requests.
export interface OrgWallet {
id: string
chain: string
address: string
derivation_path: string
created_at: string | null
}
export interface OrgAmountRaw {
raw: string
formatted: string
decimals: number
// null when pricing is unavailable (e.g. an upstream provider error).
usd_price: string | null
usd_value: string | null
}
export interface OrgWalletBalance {
id: string
chain: string
address: string
derivation_path: string
native_symbol: string
native: OrgAmountRaw
tokens: Record<string, OrgAmountRaw>
// null when the wallet's balance could not be fetched (see `error`).
total_usd: string | null
error: string | null
}
export interface OrgBalancesResponse {
items: OrgWalletBalance[]
total_usd: string | null
has_errors: boolean
}
export interface OrgMnemonicResponse {
mnemonic: string
}
export interface OrgSecretKey {
chain: string
address: string
derivation_path: string
private_key: string
}
export function getOrganizationWallets(): Promise<OrgWallet[]> {
return doB2bRequest('/v1/organizations/wallets', {}, true)
}
export function getOrganizationBalances(): Promise<OrgBalancesResponse> {
return doB2bRequest('/v1/organizations/wallets/balances', {}, true)
}
export function getOrganizationMnemonic(): Promise<OrgMnemonicResponse> {
return doB2bRequest('/v1/organizations/wallets/mnemonic', {}, true)
}
export function getOrganizationSecretKeys(): Promise<OrgSecretKey[]> {
return doB2bRequest('/v1/organizations/wallets/secret-keys', {}, true)
}