# Convert API

> Wymieniaj kryptowaluty bezpośrednio z salda sklepu — pobierz aktualną wycenę i zrealizuj po cenie rynkowej.

Convert API pozwala wymieniać waluty przechowywane w saldzie Twojego sklepu po aktualnej cenie rynkowej — ten sam mechanizm, który obsługuje zakładkę **Swap** w panelu sklepu, teraz dostępny z poziomu Twojego backendu.

> **WARNING:** Punkty końcowe Convert są podpisywane Twoim **zwykłym kluczem API** — tym samym, który jest używany do żądań [Payment API](/docs/payments), **nie** kluczem Payout API. Wykonanie konwersji natychmiast obciąża i uznaje saldo Twojego sklepu, więc traktuj ten klucz z taką samą ostrożnością jak każde dane uwierzytelniające przenoszące pieniądze.

## Pobieranie wyceny konwersji

Zwraca orientacyjną wycenę konwersji po aktualnej cenie rynkowej — efektywny kurs oraz wynikowe kwoty. Nic nie jest obciążane ani rezerwowane; wywołuj tyle razy, ile potrzebujesz przed wykonaniem.

`POST /v1/convert/price`

### Parametry żądania

| Pole | Typ | Wymagane | Opis | Wartość |
|------|-----|----------|------|---------|
| `from_currency` | string | tak | Waluta źródłowa | `BTC`, `ETH`, `USDT`, `USDC`, `TRX`, `BNB`, `GRAM`, `SOL`, `POL`, `LTC`, `DASH`, `DOGE`, `ZEC`, `XRP`, `XMR` |
| `to_currency` | string | tak | Waluta docelowa. Musi różnić się od `from_currency` | `USDT`, `USDC`, `BTC`, `ETH`, `TRX`, `BNB`, `GRAM`, `SOL`, `POL`, `LTC`, `DASH`, `DOGE`, `ZEC`, `XRP`, `XMR` |
| `amount` | decimal | tak | Kwota do konwersji, większa niż `0` |  |
| `amount_type` | string | tak | Do której strony odnosi się `amount` | `from`, `to` |

> **INFO:** `amount_type=from` wydaje dokładnie `amount` w `from_currency`. `amount_type=to` otrzymuje dokładnie `amount` w `to_currency`.

**🟢 200 OK** · `application/json`

```json
{
  "state": 0,
  "result": {
    "success": true,
    "from_currency": "BTC",
    "to_currency": "USDT",
    "amount_type": "from",
    "from_amount": "0.01000000",
    "to_amount": "947.86690000",
    "effective_rate": "94786.69000000",
    "from_amount_usd": "947.87",
    "to_amount_usd": "947.87"
  }
}
```

#### Pola odpowiedzi

| Pole | Typ | Opis |
|------|-----|------|
| `success` | boolean | Czy wycena została poprawnie obliczona |
| `from_currency` | string | Waluta źródłowa |
| `to_currency` | string | Waluta docelowa |
| `amount_type` | string | Powtarza `amount_type` z żądania |
| `from_amount` | string | Kwota, która zostałaby obciążona w `from_currency` |
| `to_amount` | string | Kwota, która zostałaby zaksięgowana w `to_currency` |
| `effective_rate` | string | Kurs zastosowany do tej wyceny — 1 jednostka `from_currency` w `to_currency` (już z uwzględnieniem wyceny platformy) |
| `from_amount_usd` | string \| null | Równowartość `from_amount` w USD |
| `to_amount_usd` | string \| null | Równowartość `to_amount` w USD |

- Wycena ma charakter **wyłącznie orientacyjny** — cena rynkowa może się zmienić między pobraniem wyceny a wywołaniem realizacji.
- Ten wywołanie nie obciąża ani nie rezerwuje żadnego salda.

> Use your project UUID and the endpoint-appropriate API key from the merchant dashboard.

#### Interactive request: `POST /v1/convert/price`
  - `from_currency` (enum, required): BTC,ETH,USDT,USDC,TRX,BNB,GRAM,SOL,POL,LTC,DASH,DOGE,ZEC,XRP,XMR
  - `to_currency` (enum, required): USDT,USDC,BTC,ETH,TRX,BNB,GRAM,SOL,POL,LTC,DASH,DOGE,ZEC,XRP,XMR
  - `amount` (decimal, required)
  - `amount_type` (enum, required): from,to

## Realizacja konwersji

Realizuje konwersję po aktualnej cenie rynkowej i aktualizuje saldo Twojego sklepu. Nie ma osobnego kroku „potwierdzenia wyceny” — wywołaj ten punkt końcowy bezpośrednio z kwotą, którą chcesz przekonwertować.

`POST /v1/convert`

> **INFO:** **Idempotencja.** Powtórzenie dokładnie tego samego żądania (te same `from_currency`, `to_currency`, `amount`, `amount_type`) w ciągu około minuty od pierwszego wywołania zwraca istniejącą konwersję zamiast tworzyć drugą. Po upływie tego okna identyczne żądanie jest traktowane jako nowa konwersja — nie ponawiaj żądania na ślepo po przekroczeniu czasu oczekiwania bez wcześniejszego sprawdzenia poprzedniego wyniku.

> **WARNING:** Ten punkt końcowy jest ograniczony do **10 żądań na minutę** na wywołującego — bardziej restrykcyjnie niż ogólny limit API — ponieważ każde wywołanie porusza realnym saldem.

### Parametry żądania

| Pole | Typ | Wymagane | Opis | Wartość |
|------|-----|----------|------|---------|
| `from_currency` | string | tak | Waluta źródłowa | `BTC`, `ETH`, `USDT`, `USDC`, `TRX`, `BNB`, `GRAM`, `SOL`, `POL`, `LTC`, `DASH`, `DOGE`, `ZEC`, `XRP`, `XMR` |
| `to_currency` | string | tak | Waluta docelowa. Musi różnić się od `from_currency` | `USDT`, `USDC`, `BTC`, `ETH`, `TRX`, `BNB`, `GRAM`, `SOL`, `POL`, `LTC`, `DASH`, `DOGE`, `ZEC`, `XRP`, `XMR` |
| `amount` | decimal | tak | Kwota do konwersji, większa niż `0` |  |
| `amount_type` | string | tak | Do której strony odnosi się `amount` | `from`, `to` |

**🟢 200 OK** · `application/json`

```json
{
  "state": 0,
  "result": {
    "id": 12345,
    "type": "manual",
    "status": "completed",
    "from_currency": "BTC",
    "to_currency": "USDT",
    "from_amount": "0.01000000",
    "requested_from_amount": "0.01000000",
    "refund_amount": null,
    "to_amount": "947.86690000",
    "exchange_rate": "94786.69000000",
    "fee_amount": "0.00000000",
    "from_amount_usd": "947.87",
    "to_amount_usd": "947.87",
    "processed_at": "2026-01-20T15:30:24Z",
    "created_at": "2026-01-20T15:30:22Z"
  }
}
```

#### Pola odpowiedzi

| Pole | Typ | Opis |
|------|-----|------|
| `id` | int | ID zlecenia konwersji nadane przez system |
| `type` | string | Zawsze `manual` dla tego API |
| `status` | string | Bieżący status (patrz «Statusy konwersji» poniżej) |
| `from_currency` | string | Waluta źródłowa |
| `to_currency` | string | Waluta docelowa |
| `from_amount` | string | Kwota obciążona w `from_currency` |
| `requested_from_amount` | string \| null | Twoja pierwotnie żądana kwota źródłowa, gdy `amount_type = from`. `null`, gdy `amount_type = to` |
| `refund_amount` | string \| null | Część wcześniej obciążonej kwoty zwrócona Ci po częściowej realizacji. `null`, jeśli zlecenie zrealizowano w całości |
| `to_amount` | string | Kwota zaksięgowana w `to_currency` |
| `exchange_rate` | string | Kurs faktycznie zastosowany do tej konwersji — 1 jednostka `from_currency` w `to_currency` (już z uwzględnieniem wyceny platformy) |
| `fee_amount` | string | Opłata platformy naliczona za tę konwersję, wyrażona w `from_currency` lub `to_currency` w zależności od kierunku transakcji. Już uwzględniona w `exchange_rate` — pokazana dla przejrzystości |
| `from_amount_usd` | string \| null | Równowartość `from_amount` w USD |
| `to_amount_usd` | string \| null | Równowartość `to_amount` w USD |
| `processed_at` | string (ISO 8601) \| null | Moment zakończenia realizacji konwersji. `null`, dopóki jest w trakcie przetwarzania |
| `created_at` | string (ISO 8601) | Moment utworzenia zlecenia konwersji |

#### Statusy konwersji

| Status | Opis |
|--------|------|
| `pending` | Utworzone, jeszcze nie wysłane na rynek |
| `processing` | Saldo zablokowane, zlecenie umieszczone na rynku |
| `completed` | W pełni zrealizowane — `to_amount` zostało zaksięgowane na Twoim saldzie |
| `failed` | Nie udało się zrealizować — wcześniej obciążona kwota została automatycznie zwrócona |
| `partially_completed` | Tylko dla par walut bez bezpośredniego rynku (routing przez walutę pośrednią): pierwszy etap zakończył się powodzeniem, ale drugi nie. Zamiast `to_currency` otrzymujesz walutę pośrednią — przekonwertuj ją ponownie, aby osiągnąć pierwotny cel |

#### Interactive request: `POST /v1/convert`
  - `from_currency` (enum, required): BTC,ETH,USDT,USDC,TRX,BNB,GRAM,SOL,POL,LTC,DASH,DOGE,ZEC,XRP,XMR
  - `to_currency` (enum, required): USDT,USDC,BTC,ETH,TRX,BNB,GRAM,SOL,POL,LTC,DASH,DOGE,ZEC,XRP,XMR
  - `amount` (decimal, required)
  - `amount_type` (enum, required): from,to

## Błędy

W przypadku niepowodzenia odpowiedź ma `state: 1` oraz `error_code` — wspólne dla `/v1/convert/price` i `/v1/convert`:

**🔴 422 / 400** · `application/json`

```json
{
  "state": 1,
  "error_code": "amount_too_small",
  "errors": {
    "amount": "Amount is too small for this conversion. Please increase the amount and try again."
  }
}
```

| `error_code` | Status HTTP | Opis |
|--------------|-------------|------|
| `validation_failed` | 422 | Nieprawidłowe lub brakujące parametry albo odrzucenie z powodu reguły biznesowej (np. niewystarczające saldo) — szczegóły w polu `errors` |
| `amount_too_small` | 422 | `amount` jest poniżej minimalnej wielkości transakcji dla tej pary walut |
| `convert_unavailable` | 400 | Nie udało się w tej chwili zrealizować konwersji (dane rynkowe niedostępne lub brak trasy między dwiema walutami) — spróbuj ponownie wkrótce |
| `internal_error` | 400 | Nieoczekiwany wewnętrzny błąd serwera podczas przetwarzania żądania |

## Automatyczna konwersja wpływających płatności

Auto-konwersja jest ustawieniem projektu dla wpływających faktur i kredytów w portfelach statycznych. Konfiguruje się ją w panelu sprzedawcy, a nie przez dodawanie pól do `/v1/payment`. Każda reguła wybiera jedną lub więcej walut źródłowych i walutę docelową.

Po zakończeniu konwersji informacje o płatności i webhooks sprzedawcy mogą obejmować:

```json
{
  "payment_amount": "0.14800000",
  "merchant_amount": "0.146520000000000000",
  "payer_currency": "XMR",
  "convert": {
    "to_currency": "USDT",
    "commission": "0.09000000",
    "rate": "323.21000000",
    "amount": "47.262015740000000000"
  }
}
```

Zakresy kwot są celowo oddzielne:

- `payment_amount` — co zostało wykryte na łańcuchu w walucie płatności źródłowej;
- `merchant_amount` — netto kwota źródłowa przypisana sprzedawcy przed konwersją;
- `convert.amount` — kwota zaksięgowana w `convert.to_currency`;
- `convert.rate` i `convert.commission` — wynik wykonanej konwersji, a nie cena, którą powinieneś przeliczać lokalnie.

> **WARNING:** Brak `convert` ma znaczenie: konwersja mogła nie zostać ukończona, może nie być skonfigurowana dla tego źródła lub mogła powrócić do kredytu w walucie źródłowej. Nigdy nie wymyślaj kwoty docelowej na podstawie `/exchange-rates` ani ceny rynkowej.

### Nieudana automatyczna konwersja i przejście do

Konwersja następuje po otrzymaniu płatności w blockchain. Dostępność na rynku, minimalne wielkości zamówień, ograniczenia dokładności, limity czasowe wymiany i niewystarczająca wykonalna płynność mogą opóźnić lub uniemożliwić konwersję.

- Wpłaty poniżej minimalnej wartości globalnej/projektowej omijają proces konwersji i są księgowane w walucie źródłowej.
- Przejściowe awarie mogą być ponawiane asynchronicznie.
- Duże lub niehandlowalne wpłaty mogą zostać przepięte na kredyt w walucie źródłowej po wyczerpaniu polityki ponawiania.
- Płatność może być zatem ważna nawet wtedy, gdy żądana konwersja na walutę docelową nie nastąpiła.

Twoja integracja powinna najpierw zapisać zweryfikowaną płatność, a następnie uzgodnić faktycznie uznaną walutę z informacji o płatności, opcjonalnego bloku `convert` oraz sald handlowca. Nie blokuj potwierdzenia webhooka płatności podczas oczekiwania na własne systemy analityczne lub powiadomień.

### Testy akceptacji automatycznej konwersji

Przetestuj co najmniej: pomyślna bezpośrednia konwersja, konwersja pośrednia/wieloetapowa, pył poniżej minimum, tymczasowa ponowna próba, powrót do waluty źródłowej, niedopłata, nadpłata, zduplikowany webhook, brak `convert` oraz uzgodnienie po niejednoznacznym przekroczeniu limitu czasu.

## Ręczne przypadki krawędziowe konwersji

- `/v1/convert/price` to poglądowa wskazówka; ruchy na rynku mogą zmienić wynik wykonania.
- `amount_type: from` naprawia żądanie po stronie źródłowej, podczas gdy `amount_type: to` żąda kwoty po stronie docelowej. Nie zamieniaj znaczenia przy prezentowaniu interfejsu potwierdzenia.
- Para bez bezpośredniego rynku może być skierowana przez walutę pośrednią. Jeśli tylko jedna część zostanie zakończona, `partially_completed` raportuje kredyt pośredni.
- Jeśli wywołanie execute przekroczy limit czasu, przeprowadź uzgadnianie przed ponowną próbą. Zlecenie rynkowe może zostać zrealizowane nawet wtedy, gdy jego odpowiedź HTTP zostanie utracona.
- Traktuj `failed` jako stan do uzgodnienia, a nie jako pozwolenie na zastosowanie lokalnej wpisu kompensacyjnego; platforma zarządza księgowością debetów/zwrotów.