# Convert API

> Converti tra criptovalute direttamente dal saldo del tuo negozio — ottieni una quotazione in tempo reale ed esegui al prezzo di mercato.

La Convert API ti permette di scambiare le valute detenute nel saldo del tuo negozio al prezzo di mercato attuale — lo stesso motore che alimenta la scheda **Swap** nella dashboard del negozio, ora richiamabile dal tuo backend.

> **WARNING:** Gli endpoint Convert vengono firmati con la tua **chiave API abituale** — la stessa usata per le richieste della [Payment API](/docs/payments), **non** la chiave Payout API. Eseguire una conversione addebita e accredita immediatamente il saldo del tuo negozio, quindi tratta questa chiave con la stessa cautela di qualsiasi credenziale che muove denaro.

## Ottenere una quotazione di conversione

Restituisce una quotazione indicativa per una conversione al prezzo di mercato attuale — il tasso effettivo e gli importi risultanti. Nulla viene addebitato o riservato; chiamala tutte le volte che ti serve prima di eseguire.

`POST /v1/convert/price`

### Parametri della richiesta

| Campo | Tipo | Obbligatorio | Descrizione | Valore |
|-------|------|--------------|-------------|--------|
| `from_currency` | string | sì | Valuta di origine | `BTC`, `ETH`, `USDT`, `USDC`, `TRX`, `BNB`, `GRAM`, `SOL`, `POL`, `LTC`, `DASH`, `DOGE`, `ZEC`, `XRP`, `XMR` |
| `to_currency` | string | sì | Valuta di destinazione. Deve essere diversa da `from_currency` | `USDT`, `USDC`, `BTC`, `ETH`, `TRX`, `BNB`, `GRAM`, `SOL`, `POL`, `LTC`, `DASH`, `DOGE`, `ZEC`, `XRP`, `XMR` |
| `amount` | decimal | sì | Importo da convertire, maggiore di `0` |  |
| `amount_type` | string | sì | A quale lato si riferisce `amount` | `from`, `to` |

> **INFO:** `amount_type=from` spende esattamente `amount` di `from_currency`. `amount_type=to` riceve esattamente `amount` di `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"
  }
}
```

#### Campi della risposta

| Campo | Tipo | Descrizione |
|-------|------|-------------|
| `success` | boolean | Se la quotazione è stata calcolata con successo |
| `from_currency` | string | Valuta di origine |
| `to_currency` | string | Valuta di destinazione |
| `amount_type` | string | Riporta l'`amount_type` della richiesta |
| `from_amount` | string | Importo che verrebbe addebitato in `from_currency` |
| `to_amount` | string | Importo che verrebbe accreditato in `to_currency` |
| `effective_rate` | string | Tasso applicato a questa quotazione — 1 unità di `from_currency` in `to_currency` (include già il prezzo della piattaforma) |
| `from_amount_usd` | string \| null | Equivalente in USD di `from_amount` |
| `to_amount_usd` | string \| null | Equivalente in USD di `to_amount` |

- La quotazione è **solo indicativa** — il prezzo di mercato può cambiare tra la quotazione e la chiamata di esecuzione.
- Questa chiamata non addebita né riserva alcun saldo.

> 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

## Eseguire una conversione

Esegue una conversione al prezzo di mercato attuale e aggiorna il saldo del tuo negozio. Non esiste un passaggio separato per "confermare una quotazione" — chiama direttamente questo endpoint con l'importo che vuoi convertire.

`POST /v1/convert`

> **INFO:** **Idempotenza.** Ripetere esattamente la stessa richiesta (stessi `from_currency`, `to_currency`, `amount`, `amount_type`) entro circa un minuto dalla prima chiamata restituisce la conversione esistente invece di crearne una seconda. Superata questa finestra, una richiesta identica viene trattata come una nuova conversione — non ritentare alla cieca dopo un timeout senza prima verificare il risultato precedente.

> **WARNING:** Questo endpoint è limitato a **10 richieste al minuto** per chiamante — più restrittivo del limite generale dell'API — perché ogni chiamata movimenta saldo reale.

### Parametri della richiesta

| Campo | Tipo | Obbligatorio | Descrizione | Valore |
|-------|------|--------------|-------------|--------|
| `from_currency` | string | sì | Valuta di origine | `BTC`, `ETH`, `USDT`, `USDC`, `TRX`, `BNB`, `GRAM`, `SOL`, `POL`, `LTC`, `DASH`, `DOGE`, `ZEC`, `XRP`, `XMR` |
| `to_currency` | string | sì | Valuta di destinazione. Deve essere diversa da `from_currency` | `USDT`, `USDC`, `BTC`, `ETH`, `TRX`, `BNB`, `GRAM`, `SOL`, `POL`, `LTC`, `DASH`, `DOGE`, `ZEC`, `XRP`, `XMR` |
| `amount` | decimal | sì | Importo da convertire, maggiore di `0` |  |
| `amount_type` | string | sì | A quale lato si riferisce `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"
  }
}
```

#### Campi della risposta

| Campo | Tipo | Descrizione |
|-------|------|-------------|
| `id` | int | ID dell'ordine di conversione assegnato dal sistema |
| `type` | string | Sempre `manual` per questa API |
| `status` | string | Stato attuale (vedi «Stati di conversione» qui sotto) |
| `from_currency` | string | Valuta di origine |
| `to_currency` | string | Valuta di destinazione |
| `from_amount` | string | Importo addebitato in `from_currency` |
| `requested_from_amount` | string \| null | Il tuo importo di origine originariamente richiesto quando `amount_type = from`. `null` quando `amount_type = to` |
| `refund_amount` | string \| null | Parte dell'importo pre-addebitato rimborsata dopo un'esecuzione parziale. `null` se l'ordine è stato eseguito completamente |
| `to_amount` | string | Importo accreditato in `to_currency` |
| `exchange_rate` | string | Tasso effettivamente applicato a questa conversione — 1 unità di `from_currency` in `to_currency` (include già il prezzo della piattaforma) |
| `fee_amount` | string | Commissione della piattaforma applicata a questa conversione, denominata in `from_currency` o `to_currency` a seconda della direzione dell'operazione. Già riflessa in `exchange_rate` — mostrata per trasparenza |
| `from_amount_usd` | string \| null | Equivalente in USD di `from_amount` |
| `to_amount_usd` | string \| null | Equivalente in USD di `to_amount` |
| `processed_at` | string (ISO 8601) \| null | Momento in cui la conversione ha terminato l'esecuzione. `null` mentre è ancora in elaborazione |
| `created_at` | string (ISO 8601) | Momento in cui è stato creato l'ordine di conversione |

#### Stati di conversione

| Stato | Descrizione |
|-------|-------------|
| `pending` | Creato, non ancora inviato al mercato |
| `processing` | Saldo bloccato e ordine collocato sul mercato |
| `completed` | Eseguito completamente — `to_amount` è stato accreditato sul tuo saldo |
| `failed` | Impossibile eseguire — qualsiasi importo pre-addebitato è stato rimborsato automaticamente |
| `partially_completed` | Solo per coppie di valute senza mercato diretto (instradate tramite una valuta intermedia): il primo passaggio è stato completato ma il secondo è fallito. Ti viene accreditata la valuta intermedia invece di `to_currency` — converti di nuovo da lì per raggiungere l'obiettivo originale |

#### 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

## Errori

In caso di errore, la risposta ha `state: 1` e un `error_code` — condiviso da `/v1/convert/price` e `/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` | Stato HTTP | Descrizione |
|--------------|------------|-------------|
| `validation_failed` | 422 | Parametri non validi o mancanti, oppure rifiuto per regola di business (es. saldo insufficiente) — vedi il campo `errors` per i dettagli |
| `amount_too_small` | 422 | `amount` è inferiore alla dimensione minima negoziabile per questa coppia di valute |
| `convert_unavailable` | 400 | La conversione non ha potuto essere eseguita in questo momento (dati di mercato non disponibili o nessuna rotta tra le due valute) — riprova a breve |
| `internal_error` | 400 | Errore interno del server imprevisto durante l'elaborazione della richiesta |

## Conversione automatica dei pagamenti in arrivo

La conversione automatica è un'impostazione del progetto per le fatture in arrivo e i crediti del portafoglio statico. Viene configurata nel cruscotto del commerciante, non aggiungendo campi a `/v1/payment`. Ogni regola seleziona una o più valute di origine e una valuta di destinazione.

Al termine della conversione, le informazioni sul pagamento e i webhook del commerciante possono includere:

```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"
  }
}
```

I domini dell'importo sono intenzionalmente separati:

- `payment_amount` — quanto è stato rilevato on-chain nella valuta di pagamento di origine;
- `merchant_amount` — l'importo netto di origine attribuibile al commerciante prima della conversione;
- `convert.amount` — l'importo accreditato in `convert.to_currency`;
- `convert.rate` e `convert.commission` — il risultato della conversione eseguita, non un prezzo che dovresti ricalcolare localmente.

> **WARNING:** L'assenza di `convert` ha significato: la conversione potrebbe non essere completata, potrebbe non essere configurata per quella fonte, o potrebbe essere ricaduta sul credito in valuta di origine. Non inventare mai un importo target da `/exchange-rates` o da un prezzo di mercato pubblico.

### Errore di conversione automatica e fallback

La conversione avviene a valle della ricezione del pagamento in blockchain. La disponibilità di mercato, le quantità minime d'ordine, i limiti di precisione, i timeout degli exchange e la liquidità eseguibile insufficiente possono ritardare o impedire la conversione.

- I depositi al di sotto del minimo globale o del progetto bypassano il flusso di conversione e accreditano la valuta di origine.
- I guasti transitori possono essere riprovati in modo asincrono.
- Depositi grandi o non commerciabili possono ritornare a un credito nella valuta di origine dopo che la politica di ritentativo è esaurita.
- Un pagamento può quindi essere valido anche quando la conversione nella valuta desiderata non è avvenuta.

La tua integrazione dovrebbe prima memorizzare il pagamento verificato, quindi riconciliare la valuta effettivamente accreditata dalle informazioni sul pagamento, dal blocco opzionale `convert` e dai saldi del commerciante. Non bloccare il riconoscimento del webhook del pagamento mentre si attendono i tuoi sistemi di analisi o notifiche.

### Test di accettazione della conversione automatica

Testare almeno: conversione diretta riuscita, conversione tramite ponte/multi-hop, polvere sotto il minimo, tentativo transitorio, ritorno alla valuta di origine, sotto pagamento, sovrapagamento, webhook duplicato, `convert` mancante e riconciliazione dopo un timeout ambiguo.

## Casi limite di conversione manuale

- `/v1/convert/price` è un'anteprima indicativa; il movimento del mercato può cambiare il risultato dell'esecuzione.
- `amount_type: from` fissa la richiesta lato sorgente, mentre `amount_type: to` richiede un importo lato destinazione. Non invertire il significato durante la presentazione dell'interfaccia di conferma.
- Una coppia senza un mercato diretto può essere instradata attraverso una valuta intermedia. Se viene completata solo una fase, `partially_completed` segnala il credito intermedio.
- Se una chiamata execute scade, riconciliare prima di riprovare. Un ordine di mercato può essere eseguito anche quando la sua risposta HTTP va persa.
- Trattare `failed` come uno stato da riconciliare, non come permesso di applicare una registrazione contabile compensativa locale; la piattaforma possiede la contabilità di addebito/rimborso.