Un'unica API, un unico flusso ospitato
CryptoPayIn valorizza un ordine nella valuta di presentazione selezionata, blocca istantanee verificate fiat/USD e crypto/USD, assegna un indirizzo di deposito dedicato e monitora i propri nodi blockchain in attesa del pagamento. La contabilità interna, le commissioni e i saldi restano in USD. Il tuo backend riceve subito un URL di checkout e, in seguito, gli eventi firmati del ciclo di vita.
Questa è un'API live. Non esiste un prefisso sandbox. Ogni creazione riuscita assegna un indirizzo on-chain reale. Per i test end-to-end utilizza un importo ridotto in una valuta supportata e conserva le chiavi segrete sul tuo server.
Come si compone l'integrazione
Genera una chiave segreta una sola volta in Dashboard -> Sviluppatori.
Invia con POST la valuta dell'ordine, l'importo e l'asset selezionato.
Indirizza il cliente all'URL ospitato restituito.
Verifica l'HMAC e aggiorna il tuo ordine in modo idempotente.
Crea il tuo primo pagamento
Genera una chiave API nella dashboard dell'esercente, salva la chiave segreta in una variabile d'ambiente, quindi crea un pagamento dal tuo backend. L'esempio utilizza ETH, così puoi testarlo senza dover scegliere una rete per il token.
curl --request POST https://cryptopayin.com/v1/payments \
--header "Authorization: Bearer $CPI_SECRET_KEY" \
--header "Content-Type: application/json" \
--header "Idempotency-Key: order_1042" \
--data '{
"amount": 49.99,
"currency": "USD",
"asset": "ETH",
"order_ref": "order_1042",
"redirect_url": "https://shop.example/orders/1042/paid"
}'
const response = await fetch("https://cryptopayin.com/v1/payments", {
method: "POST",
headers: {
"Authorization": `Bearer ${process.env.CPI_SECRET_KEY}`,
"Content-Type": "application/json",
"Idempotency-Key": "order_1042"
},
body: JSON.stringify({
amount: 49.99, currency: "USD", asset: "ETH",
order_ref: "order_1042",
redirect_url: "https://shop.example/orders/1042/paid"
})
});
if (!response.ok) throw new Error(await response.text());
const payment = await response.json();
$payload = json_encode([
'amount' => 49.99, 'currency' => 'USD', 'asset' => 'ETH',
'order_ref' => 'order_1042',
'redirect_url' => 'https://shop.example/orders/1042/paid',
]);
$ch = curl_init('https://cryptopayin.com/v1/payments');
curl_setopt_array($ch, [
CURLOPT_POST => true, CURLOPT_POSTFIELDS => $payload,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
'Authorization: Bearer ' . getenv('CPI_SECRET_KEY'),
'Content-Type: application/json',
'Idempotency-Key: order_1042',
],
]);
$payment = json_decode(curl_exec($ch), true, flags: JSON_THROW_ON_ERROR);
import os, requests
response = requests.post(
"https://cryptopayin.com/v1/payments",
headers={
"Authorization": f"Bearer {os.environ['CPI_SECRET_KEY']}",
"Idempotency-Key": "order_1042",
},
json={
"amount": 49.99, "currency": "USD", "asset": "ETH",
"order_ref": "order_1042",
"redirect_url": "https://shop.example/orders/1042/paid",
}, timeout=15,
)
response.raise_for_status()
payment = response.json()Usa la risposta
Conserva id del pagamento insieme al tuo ordine, poi reindirizza il cliente a checkout_url. Non calcolare tu stesso un importo in crypto o un indirizzo di deposito.
{
"id": "P-9F27C1E4KD",
"object": "payment",
"status": "created",
"amount": 49.99,
"amount_decimal": "49.99",
"amount_minor": 4999,
"currency": "USD",
"currency_minor_units": 2,
"amount_usd": 49.99,
"amount_usd_cents": 4999,
"fx_rate_usd": "1.000000000000",
"fx_source": "fixed:USD",
"fx_observed_at": "2026-07-17T13:00:00+00:00",
"fx_discrepancy_bps": 0,
"asset": "ETH",
"network": "mainnet",
"crypto_amount": "0.01388612",
"crypto_received": "0",
"deposit_address": "0x71b8c3d4700000000000000000000000000084e2",
"exchange_rate": "3600.00000000",
"exchange_rate_currency": "USD",
"exchange_rate_source": "median:cb,cg,cl",
"exchange_rate_source_count": 3,
"exchange_rate_observed_at": "2026-07-17T13:00:00+00:00",
"exchange_rate_discrepancy_bps": 12,
"confirmations": 0,
"confirmations_required": 12,
"order_ref": "order_1042",
"checkout_url": "https://cryptopayin.com/i/P-9F27C1E4KD",
"expires_at": "2026-07-17T13:30:00+00:00",
"created_at": "2026-07-17T13:00:00+00:00",
"completed_at": null
}Autenticazione
Ogni richiesta API utilizza la chiave segreta in un header HTTP Bearer. Le chiavi segrete iniziano con csk_live_. Il valore cpk_live_ associato è un identificatore pubblico per la tua dashboard e non deve mai essere utilizzato come credenziale Bearer.
Authorization: Bearer csk_live_...Ogni endpointLa chiave segreta in chiaro viene restituita solo al momento della creazione. CryptoPayIn memorizza un hash della password più un indice di ricerca SHA-256, mai il segreto in chiaro.
Crea chiavi separate per ogni applicazione o ambiente e revocale in modo indipendente. Un account può avere fino a 50 chiavi attive.
Non inserire mai un valore csk_live_ in JavaScript lato browser, in un binario mobile, in un repository pubblico o in una pagina di checkout.
Un endpoint webhook può valere per l'intero account oppure essere associato a una singola chiave API, mantenendo le integrazioni isolate.
Una chiave segreta mancante, malformata, revocata o sconosciuta restituisce 401 unauthorized. Un account esercente sospeso o chiuso viene respinto allo stesso modo. Una chiave valida utilizzata al di fuori delle autorizzazioni concesse restituisce 403 insufficient_scope.
Idempotenza
Invia un Idempotency-Key univoco a ogni creazione di pagamento. Se la connessione cade dopo l'invio, ripeti lo stesso JSON con la stessa chiave: CryptoPayIn restituisce il pagamento originale invece di assegnare un nuovo indirizzo.
| Caso | Risultato | HTTP |
|---|---|---|
| Primo utilizzo | Crea e restituisce un nuovo pagamento. | 201 |
| Stessa chiave + stesso JSON | Restituisce il pagamento esistente con Idempotent-Replayed: true. | 200 |
| Stessa chiave + JSON diverso | Rifiuta la richiesta come idempotency_conflict. | 409 |
Le chiavi sono associate alla credenziale API e possono contenere da 1 a 128 caratteri tra lettere, cifre, punti, trattini bassi, due punti o trattini. Uno UUID d'ordine persistente è una buona scelta. Lo stesso meccanismo protegge anche i prelievi, in modo che un prelievo ripetuto non possa mai spostare i fondi due volte.
Riferimento API
L'API copre pagamenti, link di pagamento, negozi, prodotti, saldi e prelievi. Le risposte utilizzano JSON UTF-8 su HTTPS; ogni creazione o aggiornamento richiede Content-Type: application/json. Le operazioni di modifica sono soggette alle autorizzazioni della chiave chiamante. Non esiste, intenzionalmente, alcun flusso CORS da browser: le chiamate spettano al tuo backend.
/v1/assetsScopri gli asset disponibili/v1/currenciesScopri le valute fiat di presentazione/v1/paymentsCrea un pagamento/v1/paymentsElenca e filtra i pagamenti/v1/payments/{id}Recupera un pagamentoLa versione 1 può ricevere campi ed endpoint retrocompatibili. Una modifica del contratto con breaking change utilizzerà un nuovo percorso di base, anziché modificare silenziosamente /v1.
Elenca gli asset
/v1/assetsRichiede autenticazione BearerUsa questo endpoint come fonte di verità per le scelte di checkout. Restituisce le voci di catalogo abilitate, i tassi attuali verificati in modo indipendente, gli importi minimi equivalenti in USD e se il nodo e il feed dei prezzi sono pronti. Una voce può restare elencata con available: false mentre un nodo è in fase di sincronizzazione o il suo prezzo non può essere verificato.
curl https://cryptopayin.com/v1/assets \
--header "Authorization: Bearer $CPI_SECRET_KEY"{
"object": "list",
"data": [{
"asset": "USDT",
"network": "TRC20",
"type": "token",
"decimals": 6,
"minimum_amount": 1,
"currency": "USD",
"base_confirmations": 19,
"available": true,
"rate_usd": "1.00000000",
"rate_source": "median:cb,cg,cl",
"rate_source_count": 3,
"rate_discrepancy_bps": 4,
"rate_status": "healthy",
"rate_updated_at": "2026-07-17T13:00:00+00:00"
}]
}Identificatori di catalogo e di rete
| Asset | Valore di rete | Abbreviazione | Conferme di base |
|---|---|---|---|
| BTC | mainnet | BTC | 2 |
| ETH | mainnet | ETH | 6 |
| USDT | TRC20 / ERC20 | USDT.TRC20 | 19 / 6 |
| USDC | ERC20 | USDC.ERC20 | 6 |
| DAI | ERC20 | DAI | 6 |
| SHIB | ERC20 | SHIB | 6 |
| PEPE | ERC20 | PEPE | 6 |
| LTC | mainnet | LTC | 6 |
| TRX | mainnet | TRX | 19 |
| DOGE | mainnet | DOGE | 20 |
| XMR | mainnet | XMR | 10 |
| SOL | mainnet | SOL | 32 |
La disponibilità è dinamica. Non codificare in modo statico la tabella precedente come allow-list live. Per i simboli multi-rete come USDT, invia esplicitamente network oppure utilizza l'abbreviazione ASSET.NETWORK.
Elenca le valute fiat
/v1/currenciesRichiede autenticazione BearerRestituisce le valute di presentazione abilitate, la loro precisione ISO e lo stato attuale della conversione in USD. Proponi solo le righe con available: true. L'USD è intrinseco; ogni altra valuta richiede una quotazione live aggiornata e una verifica di riferimento indipendente. minimum_amount converte in quella valuta la soglia minima configurata tra gli asset abilitati; l'asset scelto può richiedere un importo più alto, quindi leggi sempre anche GET /v1/assets.
curl https://cryptopayin.com/v1/currencies \
--header "Authorization: Bearer $CPI_SECRET_KEY"{
"object": "list",
"accounting_currency": "USD",
"data": [{
"currency": "EUR",
"name": "Euro",
"symbol": "€",
"minor_units": 2,
"available": true,
"minimum_amount": "0.86",
"rate_usd": "1.160000000000",
"rate_source": "coinbase+ecb",
"rate_source_count": 2,
"rate_discrepancy_bps": 18,
"rate_updated_at": "2026-07-17T13:00:00+00:00",
"rate_reference_at": "2026-07-16T00:00:00+00:00"
}]
}La valuta omessa su POST /v1/payments significa comunque USD per compatibilità con le versioni precedenti. Le valute a zero decimali come JPY rifiutano importi frazionari. Considera i minimi e i tassi di catalogo come dati live, mai come costanti fisse.
Crea un pagamento
/v1/payments120 richieste / minuto / chiaveCrea una fattura nella valuta di presentazione richiesta, blocca istantanee fiat/USD e crypto/USD verificate e aggiornate, calcola l'importo esatto in crypto e associa un indirizzo di deposito on-chain dedicato.
Corpo della richiesta
| Campo | Tipo | Requisito | Descrizione |
|---|---|---|---|
| amount | numero o stringa decimale | obbligatorio | Valore in currency, alla precisione ISO di quella valuta. Il suo equivalente in USD bloccato non deve superare $1,000,000.00; si applicano anche gli importi minimi per asset. |
| currency | stringa | facoltativo | Valuta a 3 lettere abilitata da GET /v1/currencies. Il valore predefinito è USD. |
| asset | stringa | obbligatorio | Simbolo come ETH, oppure abbreviazione come USDT.TRC20. |
| network | stringa | condizionale | Obbligatorio quando un simbolo esiste su più reti. Esempio: ERC20. |
| order_ref | stringa | facoltativo | Il tuo identificatore d'ordine, massimo 128 caratteri. Restituito nelle risposte e negli eventi dell'API. |
| customer_email | stringa | facoltativo | Indirizzo email valido, massimo 190 caratteri. Salvato con il record di pagamento dell'esercente. |
| redirect_url | stringa | facoltativo | URL HTTPS, massimo 255 caratteri, proposto dopo un checkout riuscito. |
Campi della risposta
| Campo | Tipo | Descrizione |
|---|---|---|
| id | stringa | Identificatore stabile del pagamento, che inizia con P-. |
| status | stringa | Stato attuale del ciclo di vita. |
| amount / amount_decimal / amount_minor | numero / stringa / intero | Valore di presentazione richiesto, espresso in forma comoda, decimale esatta e in unità minori ISO. |
| currency / currency_minor_units | stringa / intero | Valuta di presentazione bloccata e la sua precisione. |
| amount_usd / amount_usd_cents | numero / intero | Valore contabile interno in USD, immutabile. |
| fx_rate_usd / fx_source / fx_observed_at | stringa decimale / stringa / ISO 8601 | Istantanea bloccata del tasso USD per unità di presentazione e i relativi metadati di audit. |
| asset / network | stringa | Asset on-chain risolto. |
| crypto_amount | stringa decimale | Importo esatto che il cliente deve inviare. Non interpretare mai i decimali delle crypto come numeri in virgola mobile binari. |
| crypto_received | stringa decimale | Totale attualmente osservato all'indirizzo di deposito. |
| deposit_address | stringa | Indirizzo dedicato assegnato a questo pagamento. |
| exchange_rate | stringa decimale | Tasso crypto/USD bloccato, usato per calcolare crypto_amount; questo campo mantiene il suo significato originale della v1. |
| exchange_rate_source / exchange_rate_observed_at | stringa / ISO 8601 | Istantanea di audit immutabile del tasso crypto. |
| confirmations | intero | Conferme di rete attuali. |
| confirmations_required | intero | Soglia per questo pagamento. Fasce USD più alte possono richiedere conferme aggiuntive. |
| checkout_url | URL | Fattura ospitata da mostrare al cliente. |
| expires_at | ISO 8601 | Scadenza per una fattura non pagata. |
| completed_at | ISO 8601 / null | Orario di liquidazione finale a completamento avvenuto. |
Entrambe le conversioni live vengono validate per aggiornamento, numero di fonti e divergenza prima della creazione. Se la verifica fallisce, la creazione restituisce un errore invece di utilizzare un tasso non aggiornato. L'importo in crypto viene arrotondato per eccesso alla precisione utile per l'asset, così l'arrotondamento non lascia mai l'esercente in perdita.
Elenca i pagamenti
/v1/payments240 richieste / minuto / chiaveRestituisce prima i pagamenti più recenti per l'account esercente autenticato. Usa la paginazione a cursore per la riconciliazione e filtri esatti per individuare un ordine senza scorrere l'intera cronologia.
Parametri di query
| Parametro | Predefinito | Descrizione |
|---|---|---|
| limit | 20 | Dimensione della pagina da 1 a 100. |
| starting_after | — | ID del pagamento restituito come next_cursor della pagina precedente. |
| status | — | Stato esatto del ciclo di vita, ad esempio pending, completed o expired. |
| order_ref | — | Riferimento esatto dell'ordine dell'esercente, massimo 128 caratteri. |
curl "https://cryptopayin.com/v1/payments?status=completed&limit=20" \
--header "Authorization: Bearer $CPI_SECRET_KEY"{
"object": "list",
"data": [{
"id": "P-9F27C1E4KD",
"object": "payment",
"status": "completed",
"amount": 49.99,
"currency": "USD",
"asset": "ETH",
"network": "mainnet",
"crypto_amount": "0.01388612",
"crypto_received": "0.01388612",
"order_ref": "order_1042",
"confirmations": 12,
"confirmations_required": 12,
"completed_at": "2026-07-17T13:12:42+00:00"
}],
"has_more": true,
"next_cursor": "P-9F27C1E4KD"
}Quando has_more è true, trasmetti next_cursor invariato come starting_after. I parametri di query sconosciuti o ripetuti in stile array vengono rifiutati anziché ignorati.
Recupera un pagamento
/v1/payments/{id}240 richieste / minuto / chiaveRestituisce lo stesso oggetto pagamento della creazione, con stato aggiornato, importo ricevuto, hash della transazione e conferme. Una chiave può recuperare solo i pagamenti appartenenti al proprio account esercente.
curl https://cryptopayin.com/v1/payments/P-9F27C1E4KD \
--header "Authorization: Bearer $CPI_SECRET_KEY"I webhook dovrebbero guidare i normali aggiornamenti dell'ordine. Usa il recupero per riconciliare dopo un timeout, verificare un evento, generare una pagina di stato lato backend o correggere consegne mancate.
Ciclo di vita del pagamento
Considera sempre lo stato dell'API come autorevole. Non dedurre il completamento da un reindirizzamento del browser o dal fatto che il cliente dichiari di aver pagato.
| Stato | Significato | Azione dell'esercente |
|---|---|---|
| created | Fattura e indirizzo assegnati; nessun finanziamento rilevato ancora. | Mostra il checkout ospitato. |
| pending | In attesa di un pagamento on-chain utilizzabile. | Mantieni l'ordine aperto. |
| underpaid | I fondi arrivati sono inferiori alla tolleranza dell'esercente. | Chiedi al pagatore di inviare il residuo indicato. |
| confirming | Rilevato un valore sufficiente; in attesa delle conferme. | Non evadere ancora l'ordine. |
| completed | Raggiunti il valore richiesto e le conferme. | Evadi l'ordine una sola volta. |
| overpaid | È stato confermato più del previsto. | Evadi l'ordine e verifica l'eccedenza. |
| expired | Nessun pagamento idoneo è stato rilevato prima della scadenza. | Crea un nuovo pagamento. |
| failed | Assegnazione dell'indirizzo o elaborazione non riuscita. | Registra l'errore e crea un nuovo pagamento. |
I trasferimenti on-chain sono irreversibili e CryptoPayIn non dispone di un meccanismo di rimborso — un pagamento confermato è definitivo. Qualsiasi restituzione per cortesia commerciale viene gestita direttamente tra te e il tuo cliente, al di fuori della piattaforma.
Checkout ospitato
Ogni pagamento API include una checkout_url responsive. Mostra l'esercente, l'importo di presentazione richiesto, l'equivalente in USD bloccato quando pertinente, l'importo esatto in crypto, l'indirizzo di deposito, il codice QR, l'avviso di rete, il conto alla rovescia e l'avanzamento delle conferme in tempo reale.
Il cliente vede lo stesso crypto_amount restituito dall'API per la finestra della fattura.
Il pagatore non crea un account CryptoPayIn né condivide credenziali.
La pagina interroga il pagamento in modo sicuro e passa da in attesa a in conferma a pagato.
Un redirect_url HTTPS viene proposto dopo il successo; non costituisce prova di pagamento.
Mantieni l'evasione sul tuo backend. La navigazione del browser può essere abbandonata, ripetuta o falsificata; solo un webhook verificato o una GET autenticata dimostrano lo stato del pagamento.
Autorizzazioni & scope
Ogni chiave API porta con sé un insieme fisso di autorizzazioni scelte al momento della creazione in Dashboard → Sviluppatori. Ogni endpoint verifica gli scope della chiave prima di eseguire qualsiasi operazione; una chiamata al di fuori del permesso concesso a una chiave restituisce 403 insufficient_scope con un header X-Required-Scope che indica l'autorizzazione mancante. Gli scope vengono impostati una sola volta alla creazione e non possono essere ampliati in seguito — genera invece una nuova chiave. Le chiavi create prima dell'esistenza degli scope mantengono esattamente la loro capacità originale: payments:read e payments:write.
| Scope | Concede | Endpoint |
|---|---|---|
payments:read | Elenca e recupera i pagamenti | GET /v1/payments, GET /v1/payments/{id} |
payments:write | Crea pagamenti ospitati | POST /v1/payments |
links:read | Elenca e recupera i link di pagamento | GET /v1/links, GET /v1/links/{id} |
links:write | Crea, modifica, sospendi ed elimina i link di pagamento | POST/PATCH/DELETE /v1/links |
shops:read | Elenca e recupera i negozi e i relativi prodotti | GET /v1/shops, GET .../products |
shops:write | Crea e modifica negozi, prodotti e varianti | POST/PATCH/DELETE /v1/shops e prodotti |
balance:read | Legge i saldi in crypto e le stime in USD | GET /v1/balance |
payouts:read | Elenca e recupera i prelievi | GET /v1/payouts, GET /v1/payouts/{id} |
payouts:write | Richiede prelievi on-chain | POST /v1/payouts |
payouts:write sposta fondi on-chain ed è irreversibile. Concedilo solo a chiavi di cui ti fidi pienamente, mantieni quelle chiavi lato server e preferisci una chiave dedicata per ogni processo automatizzato. GET /v1/account restituisce gli scope della chiave chiamante e i limiti del tuo account.
Link di pagamento
Link ospitati e riutilizzabili che un cliente può pagare un numero qualsiasi di volte. Un link condivide la stessa logica di prezzo, asset, consegna e domande di checkout del costruttore di link della dashboard — l'API si limita a pilotarla. Un account può avere fino a 50 link di pagamento.
/v1/linkslinks:read/v1/linkslinks:write/v1/links/{id}links:read/v1/links/{id}links:write/v1/links/{id}links:writeCorpo della richiesta
| Campo | Tipo | Requisito | Descrizione |
|---|---|---|---|
| title | stringa | obbligatorio | 3–120 caratteri. |
| description | stringa | facoltativo | Fino a 2,000 caratteri, mostrati nel checkout. |
| template | stringa | facoltativo | Tema del checkout: signature (predefinito), midnight, atelier, horizon, compact o ledger. |
| public_label | stringa | facoltativo | Nome pubblico del venditore mostrato agli acquirenti (2–80 caratteri). Mai un ID account. |
| amount_type | stringa | facoltativo | fixed (predefinito) o open (il cliente sceglie entro min/max). |
| currency | stringa | facoltativo | Valuta di presentazione da GET /v1/currencies. Il valore predefinito è la valuta del tuo account. |
| amount | numero o stringa | condizionale | Obbligatorio per fixed. In currency alla sua precisione ISO. |
| min / max | numero o stringa | condizionale | Limiti per i link open. max può essere 0 od omesso per non avere un tetto massimo. |
| accepted_assets | array di stringhe | facoltativo | Codici degli asset, ad esempio ["BTC","USDT.TRC20"]. Ometti per includere ogni asset disponibile. |
| max_uses | intero | facoltativo | Limite massimo di pagamenti completati. 0 indica nessun limite. |
| expires_at | ISO 8601 | facoltativo | Almeno 5 minuti nel futuro, al massimo 12 mesi. UTC. |
| delivery_type | stringa | facoltativo | none, text, url o keys — beni digitali consegnati dopo il pagamento. |
| delivery_text / delivery_url | stringa | condizionale | Contenuto (≤50,000 caratteri) o un URL https per il tipo di consegna corrispondente. |
| delivery_keys | array di stringhe | condizionale | Una chiave per elemento per la consegna keys. Fino a 10,000, ciascuna ≤500 caratteri. |
| checkout_fields | array | facoltativo | Fino a 5 oggetti {label, type, required}; il tipo è text, email, textarea o number. |
| success_message / redirect_url | stringa | facoltativo | Messaggio successivo al pagamento (≤500 caratteri) e un reindirizzamento https. |
| status | stringa | facoltativo | Solo PATCH: active o paused. |
curl -X POST https://cryptopayin.com/v1/links \
-H "Authorization: Bearer $CPI_SECRET_KEY" \
-H "Content-Type: application/json" \
-d '{
"title": "Pro license",
"amount": 49.99,
"currency": "EUR",
"accepted_assets": ["BTC", "ETH", "USDT.TRC20"],
"delivery_type": "keys",
"delivery_keys": ["ABC-1", "ABC-2", "ABC-3"]
}'{
"id": "PL-N5PKTYB7",
"object": "payment_link",
"url": "https://cryptopaylink.co/pay/PL-N5PKTYB7",
"status": "active",
"title": "Pro license",
"amount_type": "fixed",
"currency": "EUR",
"amount": 49.99,
"amount_decimal": "49.99",
"accepted_assets": [{"asset":"BTC","network":"mainnet"},{"asset":"ETH","network":"mainnet"},{"asset":"USDT","network":"TRC20"}],
"uses": {"started": 0, "completed": 0, "in_flight": 0},
"delivery": {"type": "keys", "keys_available": 3, "keys_total": 3},
"expires_at": null,
"created_at": "2026-07-19T19:00:00+00:00"
}PATCH è un aggiornamento parziale: invia solo i campi che modifichi, mentre il resto viene preservato, incluse le chiavi di licenza non ancora vendute. Per i link keys, l'invio di delivery_keys sostituisce il pool di chiavi non vendute; le chiavi già consegnate non vengono mai toccate. L'eliminazione di un link con pagamenti associati è implicitamente impedita, mantenendone intatta la cronologia.
Checkout agenti
Ogni link di pagamento attivo è anche un checkout leggibile dalle macchine: un agente IA o qualsiasi script può scoprirlo, creare una fattura e leggere la consegna senza un browser — e senza alcuna chiave API, perché si tratta di endpoint pubblici per l'acquirente sul dominio del link, non di endpoint per l'esercente. Contratto completo ed esempio pratico: cryptopayin.com/agents.
https://cryptopaylink.co/pay/{link}.jsonpubblico · discoveryRestituisce lo stato, il prezzo, gli asset accettati e il contratto esatto di input per la chiamata di fatturazione (campi obbligatori, schema di spedizione, varianti).
https://cryptopaylink.co/pay/{link}/invoicepubblico · supporta Idempotency-KeyCrea la fattura utilizzando lo stesso nucleo, la stessa istantanea dei prezzi e gli stessi limiti anti-abuso della pagina ospitata, e restituisce l'indirizzo di deposito, l'importo esatto in crypto, un URI del wallet e l'URL della ricevuta. L'agente paga quindi on-chain da qualsiasi wallet che controlla.
https://cryptopaylink.co/pay/{link}/receipt?p={payment}pubblico · polling ogni 5–10 sStato e conferme in tempo reale; una volta completato il pagamento, la risposta include la consegna — il tuo contenuto testuale, l'URL privato o una chiave di licenza riservata a quel pagamento — oltre al tuo messaggio di successo e all'URL di reindirizzamento.
I negozi parlano lo stesso protocollo
Le vetrine espongono lo stesso identico flusso sul proprio dominio: il catalogo con lo stock in tempo reale, poi un'unica chiamata che convalida il carrello, riserva lo stock e restituisce la fattura.
https://shopycrypto.com/s/{shop}.jsonpubblico · catalogohttps://shopycrypto.com/s/{shop}/orderpubblico · carrello → fattura, supporta Idempotency-Keyhttps://shopycrypto.com/s/{shop}/o/{order}/receipt?p={payment}pubblico · polling ogni 5–10 sControlli per il venditore
Il checkout agenti è attivo per impostazione predefinita e costa la stessa commissione fissa dell'1%. Disattivalo per l'intero account in Dashboard → Impostazioni → Generali → IA & checkout agenti: gli endpoint automatizzati su link e negozi risponderanno quindi 403 agents_disabled mentre le tue pagine di checkout per utenti umani continueranno a funzionare. I pagamenti creati dagli agenti non recano alcun contrassegno speciale — sono pagamenti ordinari nella tua dashboard, nei webhook e nelle esportazioni.
Negozi
Una vetrina ospitata che raggruppa i prodotti sotto un'unica pagina brandizzata. Un account può avere fino a 10 negozi. I prodotti si gestiscono tramite gli endpoint di prodotto annidati riportati di seguito.
/v1/shopsshops:read/v1/shopsshops:write/v1/shops/{id}shops:read/v1/shops/{id}shops:write/v1/shops/{id}shops:writeCorpo della richiesta
| Campo | Tipo | Requisito | Descrizione |
|---|---|---|---|
| name | stringa | obbligatorio | 2–80 caratteri. |
| tagline | stringa | facoltativo | Fino a 160 caratteri. |
| theme | stringa | facoltativo | light (predefinito) o dark. |
| accent | stringa | facoltativo | Colore accento esadecimale dalla palette del negozio, restituito come accent_palette su GET /v1/shops. |
| accepted_assets | array di stringhe | facoltativo | Asset predefiniti per i prodotti del negozio, ad es. ["BTC","LTC","XMR"]. Applicati a tutti i prodotti quando modificati. |
| status | stringa | facoltativo | Solo PATCH: active o paused. |
Un negozio disabilitato da CryptoPayIn per motivi di policy non può essere riattivato né eliminato tramite l'API e restituisce admin_disabled (403). L'eliminazione di un negozio rimuove i relativi prodotti; i pagamenti passati restano invariati.
Prodotti & varianti
I prodotti risiedono all'interno di un negozio. Ogni negozio può avere fino a 50 prodotti. Un prodotto può essere digitale (con consegna istantanea) o fisico (con paesi di spedizione), e può esporre fino a 30 combinazioni di varianti costruite da 1–3 gruppi di opzioni.
/v1/shops/{shop}/productsshops:read/v1/shops/{shop}/productsshops:write/v1/shops/{shop}/products/{id}shops:read/v1/shops/{shop}/products/{id}shops:write/v1/shops/{shop}/products/{id}shops:writeCorpo della richiesta
| Campo | Tipo | Requisito | Descrizione |
|---|---|---|---|
| title | stringa | obbligatorio | 3–120 caratteri. |
| description / blurb | stringa | facoltativo | Descrizione completa e una riga per la scheda del negozio di massimo ≤200 caratteri. |
| emoji | stringa | facoltativo | Singola emoji mostrata sulla scheda del prodotto. |
| featured | booleano | facoltativo | Al massimo un prodotto in evidenza per negozio. |
| product_type | stringa | facoltativo | digital (predefinito) o physical. |
| shipping_countries | array di stringhe | condizionale | Solo prodotti fisici: codici ISO come ["FR","BE"], oppure ["*"] per la spedizione mondiale. |
| amount_type / currency / amount / min / max | misto | condizionale | Prezzo di base, con le stesse regole dei link di pagamento. I prodotti fisici devono essere fixed. |
| max_uses | intero | facoltativo | Limite totale di vendite (0 = illimitato). |
| delivery_type + delivery_text/url/keys | misto | facoltativo | Consegna digitale per il prodotto base, con lo stesso formato dei link di pagamento. |
| variant_options | array | facoltativo | 1–3 gruppi {name, values[]}, ciascuno con 2–10 valori. Le combinazioni non devono superare 30. |
| variants | array | condizionale | Un oggetto per combinazione (vedi sotto). Obbligatorio ed esaustivo quando è presente variant_options. |
| status | stringa | facoltativo | Solo PATCH: active o paused. |
Oggetto variante
| Campo | Tipo | Descrizione |
|---|---|---|
| options | array di stringhe | Un valore per gruppo di opzioni, nell'ordine dei gruppi, ad es. ["Pro","Lifetime"]. |
| price | numero o stringa | Prezzo della variante nella valuta del prodotto. |
| stock | intero o null | Unità rimanenti, oppure null per illimitato. |
| delivery_type + delivery_text/url/keys | misto | Override opzionale della consegna digitale per singola variante (inherit per impostazione predefinita). Le chiavi devono essere univoche nell'intero prodotto. |
curl -X POST https://cryptopayin.com/v1/shops/SH-JYRK8TFJ/products \
-H "Authorization: Bearer $CPI_SECRET_KEY" \
-H "Content-Type: application/json" \
-d '{
"title": "Software license",
"amount": 30,
"currency": "USD",
"variant_options": [
{"name": "Edition", "values": ["Standard", "Pro"]},
{"name": "Term", "values": ["1 year", "Lifetime"]}
],
"variants": [
{"options": ["Standard","1 year"], "price": 30, "delivery_type": "keys", "delivery_keys": ["S1Y-1"]},
{"options": ["Standard","Lifetime"], "price": 79, "stock": 10, "delivery_type": "keys", "delivery_keys": ["SLT-1"]},
{"options": ["Pro","1 year"], "price": 59, "delivery_type": "keys", "delivery_keys": ["P1Y-1"]},
{"options": ["Pro","Lifetime"], "price": 149, "stock": 5, "delivery_type": "keys", "delivery_keys": ["PLT-1"]}
]
}'PATCH preserva gli ordini esistenti e le chiavi già consegnate. Per modificare prezzi o stock, invia nuovamente l'array variants corrispondente; le combinazioni che ometti vengono messe in pausa se hanno ordini, altrimenti rimosse. Un prodotto può contenere al massimo 10,000 chiavi di licenza attive tra base e varianti, e ogni chiave deve essere univoca all'interno del prodotto.
Saldo
/v1/balancebalance:readRestituisce i tuoi saldi crypto liquidati per asset, con una stima in USD su base best-effort e la commissione di rete addebitata su un prelievo. La contabilità interna è sempre in USD; i saldi maturano dai pagamenti completati al netto della commissione dell'esercente.
{
"object": "list",
"accounting_currency": "USD",
"minimum_payout_usd": 25,
"data": [{
"asset": "USDT",
"network": "TRC20",
"amount": "99.099",
"usd_estimate": 99.02,
"payout_network_fee": "2.445463"
}]
}usd_estimate è null quando un tasso live verificato non è momentaneamente disponibile; il saldo sottostante resta comunque esatto. Usa questi valori per decidere i prelievi, non per la contabilità definitiva.
Prelievi
Sposta la crypto liquidata verso un wallet esterno. I prelievi sono irreversibili, quindi questo endpoint applica tutte le stesse protezioni della dashboard: una destinazione valida per l'asset, un tasso live verificato, il minimo dell'account, un saldo sufficiente comprensivo della commissione di rete e la conferma a due fattori quando l'account ha la 2FA attiva.
/v1/payoutspayouts:read/v1/payoutspayouts:write · 30 / min / chiave/v1/payouts/{id}payouts:readCorpo della richiesta
| Campo | Tipo | Requisito | Descrizione |
|---|---|---|---|
| asset | stringa | obbligatorio | Simbolo o abbreviazione, ad es. LTC o USDT.TRC20. |
| network | stringa | condizionale | Obbligatorio quando il simbolo esiste su più reti. |
| amount | numero o stringa | obbligatorio | Importo da inviare, esclusa la commissione di rete, alla precisione dell'asset. Il suo controvalore in USD deve rispettare il minimo dell'account. |
| address | stringa | obbligatorio | Indirizzo di destinazione, validato per la chain dell'asset. |
| note | stringa | facoltativo | Riferimento personale, fino a 255 caratteri. |
| totp_code | stringa | condizionale | Codice attuale a 6 cifre o codice di recupero. Obbligatorio quando la 2FA è attiva sull'account. |
curl -X POST https://cryptopayin.com/v1/payouts \
-H "Authorization: Bearer $CPI_SECRET_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: withdraw-2026-07-19-01" \
-d '{
"asset": "USDT.TRC20",
"amount": "50",
"address": "TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t",
"note": "weekly settlement"
}'{
"id": "W-EM64ZCJB",
"object": "payout",
"status": "requested",
"asset": "USDT",
"network": "TRC20",
"amount": "50",
"fee": "2.445463",
"total_debited": "52.445463",
"address": "TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t",
"txid": null,
"confirmations": 0,
"created_at": "2026-07-19T19:10:00+00:00"
}Ciclo di vita dello stato
| Stato | Significato |
|---|---|
requested | Riservato dal tuo saldo; in attesa di revisione da parte dell'operatore o di approvazione automatica. |
approved / processing | Autorizzato e in coda per la trasmissione da parte dell'esecutore. |
sent | Trasmesso on-chain; txid è valorizzato. |
confirmed | Ha raggiunto le conferme richieste. Definitivo. |
failed | Non è stato possibile inviarlo; failure_message spiega il motivo e il saldo viene restituito. |
cancelled | Annullato prima della trasmissione; il saldo riservato viene restituito. |
Invia un Idempotency-Key in modo che un nuovo tentativo di rete non possa mai creare un secondo prelievo: la stessa chiave con lo stesso corpo restituisce il prelievo originale (Idempotent-Replayed: true); la stessa chiave con un corpo diverso restituisce 409 idempotency_conflict. Il codice 2FA è deliberatamente escluso dall'impronta di idempotenza, così un codice che cambia non genera un falso conflitto. La riserva addebita subito il tuo saldo; un prelievo fallito o annullato lo restituisce.
Account
/v1/accountqualsiasi chiave validaRestituisce il profilo del tuo account, gli scope della chiave chiamante, la tua commissione di piattaforma e tutti i limiti live — utile per un'integrazione auto-configurante o un controllo preliminare.
{
"object": "account",
"id": "MC67T3PHQZ",
"fee_bps": 100,
"fee_percent": 1,
"default_currency": "USD",
"two_factor_enabled": false,
"api_key": {"label": "production", "scopes": ["payments:read","payments:write"]},
"limits": {
"shops": {"used": 1, "max": 10},
"payment_links": {"used": 0, "max": 50},
"products_per_shop_max": 50,
"variant_combinations_per_product_max": 30,
"license_keys_per_product_max": 10000,
"active_api_keys_max": 50,
"checkout_fields_per_link_max": 5
},
"minimum_payout_usd": 25
}Webhook
Aggiungi fino a 10 endpoint HTTPS pubblici in Dashboard -> Sviluppatori. Ogni endpoint riceve un proprio segreto di firma whsec_..., mostrato una sola volta. Può ascoltare per l'intero account oppure essere associato a una specifica chiave API attiva; gli endpoint associati a una chiave ricevono solo i pagamenti creati con quella chiave.
Verifica prima di eseguire il parsing
CryptoPayIn firma il corpo grezzo esatto della richiesta utilizzando il segreto dell'endpoint. La versione 1 firma timestamp + "." + raw_body. Rifiuta i timestamp non aggiornati prima di accettare l'evento.
import crypto from "node:crypto";
const timestamp = req.headers["x-cpi-timestamp"];
const signature = req.headers["x-cpi-signature"];
const rawBody = req.rawBody; // Buffer captured before JSON parsing
if (Math.abs(Date.now() / 1000 - Number(timestamp)) > 300) {
throw new Error("stale webhook");
}
const expected = "sha256=" + crypto
.createHmac("sha256", process.env.CPI_WEBHOOK_SECRET)
.update(Buffer.concat([Buffer.from(`${timestamp}.`), rawBody]))
.digest("hex");
const valid = crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(signature));
if (!valid) throw new Error("invalid webhook signature");
$rawBody = file_get_contents('php://input');
$timestamp = $_SERVER['HTTP_X_CPI_TIMESTAMP'] ?? '';
$received = $_SERVER['HTTP_X_CPI_SIGNATURE'] ?? '';
if (!ctype_digit($timestamp) || abs(time() - (int)$timestamp) > 300) {
http_response_code(400); exit('stale webhook');
}
$expected = 'sha256=' . hash_hmac(
'sha256', $timestamp . '.' . $rawBody, getenv('CPI_WEBHOOK_SECRET')
);
if (!hash_equals($expected, $received)) {
http_response_code(401); exit('invalid signature');
}
$event = json_decode($rawBody, true, flags: JSON_THROW_ON_ERROR);
import hashlib, hmac, os, time
raw_body = request.get_data() # bytes, before JSON decoding
timestamp = request.headers.get("X-CPI-Timestamp", "")
received = request.headers.get("X-CPI-Signature", "")
if not timestamp.isdigit() or abs(time.time() - int(timestamp)) > 300:
raise ValueError("stale webhook")
signed = timestamp.encode() + b"." + raw_body
expected = "sha256=" + hmac.new(
os.environ["CPI_WEBHOOK_SECRET"].encode(), signed, hashlib.sha256
).hexdigest()
if not hmac.compare_digest(expected, received):
raise ValueError("invalid signature")Header di consegna
| Header | Esempio | Scopo |
|---|---|---|
| Content-Type | application/json | Corpo JSON UTF-8. |
| X-CPI-Timestamp | 1784293200 | Secondi Unix inclusi nel messaggio firmato. |
| X-CPI-Signature | sha256=... | HMAC-SHA256 esadecimale. |
| X-CPI-Signature-Version | v1 | Versione dello schema di firma. |
| X-CPI-Event-Id | evt_a12b... | ID logico stabile dell'evento; identico tra i vari tentativi. |
| X-CPI-Delivery-Id | 1842 | ID stabile del record di consegna dell'endpoint. |
Payload del pagamento
{
"event_id": "evt_a12b34c56d78e90f12345678",
"event": "payment.completed",
"id": "P-9F27C1E4KD",
"status": "completed",
"order_ref": "order_1042",
"amount": 49.99,
"amount_decimal": "49.99",
"amount_minor": 4999,
"currency_minor_units": 2,
"currency": "USD",
"amount_usd": 49.99,
"amount_usd_cents": 4999,
"fx_rate_usd": "1.000000000000",
"fx_source": "fixed:USD",
"fx_observed_at": "2026-07-17T13:00:00Z",
"fx_discrepancy_bps": 0,
"exchange_rate": "3600.00000000",
"exchange_rate_currency": "USD",
"exchange_rate_source": "median:cb,cg,cl",
"exchange_rate_source_count": 3,
"exchange_rate_observed_at": "2026-07-17T13:00:00Z",
"exchange_rate_discrepancy_bps": 12,
"asset": "ETH",
"network": "mainnet",
"crypto_amount": "0.01388612",
"crypto_received": "0.01388612",
"txid": "0x9d81...75af",
"confirmations": 12,
"confirmations_required": 12,
"deposit_address": "0x71b8c3d4700000000000000000000000000084e2",
"sent_at": "2026-07-17T13:12:42Z"
}Tentativi e sicurezza dell'endpoint
Una consegna ha esito positivo con HTTP 200-299. Esegui le operazioni onerose in modo asincrono e rispondi rapidamente.
Il corpo esatto e l'ID evento vengono conservati; in caso di errore, i tentativi vengono ripetuti dopo circa 1 minuto, 5 minuti, 30 minuti, 2 ore e 6 ore.
Le risposte 3xx non vengono seguite. Registra direttamente l'URL HTTPS finale.
Gli IP privati, loopback, link-local e riservati sono bloccati; ogni risposta DNS viene convalidata e la connessione è soggetta a pinning.
Le consegne avvengono almeno una volta. Rendi il tuo handler idempotente registrando event_id con un vincolo di unicità prima dell'evasione. Recupera l'oggetto tramite l'API quando riconcili un evento imprevisto.
Riferimento eventi
payment.completedValore atteso confermato.payment.overpaidConfermato più del previsto.payment.underpaidRilevato un finanziamento inferiore alla tolleranza.payment.expiredFinestra della fattura non pagata chiusa.payment.failedConfigurazione o elaborazione del pagamento non riuscita.payout.sentPrelievo trasmesso on-chain.payout.confirmedIl prelievo ha raggiunto le conferme.payout.failedIl prelievo non è andato a buon fine.webhook.testTest di connettività manuale.Struttura dell'evento di prelievo
{
"event_id": "evt_b98c76d54e32a10f87654321",
"event": "payout.sent",
"id": "W-8J2K7M4RQP",
"status": "sent",
"asset": "ETH",
"network": "mainnet",
"amount": "0.25",
"fee": "0.00081768",
"address": "0x84f2...9bc1",
"txid": "0xa1c4...07ee",
"confirmations": 0,
"note": "weekly treasury",
"sent_at": "2026-07-17T14:08:31Z"
}Errori e limiti di frequenza
Gli errori utilizzano sempre un unico involucro JSON. Effettua la scelta in base a error.type; il messaggio leggibile può essere migliorato senza un cambio di versione. Cita error.request_id o l'header di risposta X-Request-Id corrispondente quando contatti il supporto.
{
"error": {
"type": "ambiguous_asset",
"message": "specify network (e.g. USDT.ERC20 or USDT.TRC20)",
"request_id": "b942e21f8dca4b06b8672eb9"
}
}| HTTP | Tipi tipici | Significato |
|---|---|---|
| 400 | invalid_request, unknown_parameter | JSON, query o header di idempotenza malformati. |
| 401 | unauthorized | Credenziale/account mancante, non valido o non attivo. |
| 403 | insufficient_scope, admin_disabled | Chiave valida priva dell'autorizzazione richiesta (vedi X-Required-Scope), oppure una risorsa bloccata da un amministratore. |
| 404 | not_found | Endpoint sconosciuto, oppure una risorsa esterna a questo account esercente. |
| 405 | method_not_allowed | Usa il metodo indicato nell'header Allow. |
| 409 | idempotency_conflict | Chiave riutilizzata con un JSON diverso. |
| 413 | request_too_large | Il corpo JSON supera 64 KiB. |
| 415 | unsupported_media_type | Il corpo della POST non è dichiarato come application/json. |
| 422 | invalid, limit, invalid_amount, below_minimum, bad_address, 2fa_required, unsupported_asset | Una richiesta ben formata non ha superato la validazione deterministica oppure ha raggiunto un limite di risorsa. |
| 429 | rate_limited | Attendi Retry-After. |
| 500 | server_error | Errore imprevisto; puoi ritentare in sicurezza con la stessa chiave di idempotenza. |
| 503 | maintenance, temporarily_unavailable, asset_unavailable, no_rate, price_stale, fiat_rate_unavailable, fiat_rate_stale, derive_failed | Errore transitorio della piattaforma, del nodo, del prezzo o dell'assegnazione dell'indirizzo. Non viene sostituita alcuna conversione non aggiornata. |
Limiti attuali
| Ambito | Limite | Finestra |
|---|---|---|
| Tetto di sicurezza Nginx per IP | 10 richieste/secondo, burst 30 | Continuo |
| Tetto per IP non autenticato | 300 richieste | 60 secondi |
| POST /v1/payments, links, shops, products | 120 richieste per chiave API | 60 secondi |
| POST /v1/payouts | 30 richieste per chiave API | 60 secondi |
| Endpoint GET | 240 richieste per chiave API | 60 secondi |
Limiti dell'account
| Risorsa | Limite |
|---|---|
| Negozi per account | 10 |
| Link di pagamento per account | 50 |
| Prodotti per negozio | 50 |
| Combinazioni di varianti per prodotto | 30 |
| Chiavi di licenza per prodotto / link | 10,000 |
| Domande di checkout per link | 5 |
| Chiavi API attive per account | 50 |
Consulta il tuo utilizzo in tempo reale rispetto a questi limiti da GET /v1/account.
Le risposte con esito positivo ma limitate a livello applicativo espongono X-RateLimit-Limit, X-RateLimit-Remaining e X-RateLimit-Reset. Ritenta su 429, 500 e 503 con backoff esponenziale e jitter. Per le richieste POST, riutilizza sempre la Idempotency-Key originale e lo stesso JSON.
Sicurezza dell'integrazione
Caricali a runtime; non registrarne mai il valore completo nei log né includerli nel controllo di versione.
Un client browser o mobile non può conservare in sicurezza un segreto dell'esercente.
Controlla l'aggiornamento del timestamp e utilizza un confronto della firma a tempo costante prima del parsing JSON.
Registra gli eventi/ordini elaborati in modo transazionale, così i tentativi ripetuti non spediscono mai due volte.
Recupera il pagamento quando un evento è inatteso o il tuo stato locale non coincide.
Crea una chiave sostitutiva, distribuiscila, verifica il traffico, quindi elimina la vecchia chiave.
L'accesso all'account è controllato da una chiave esercente a 16 cifre non recuperabile, protetta facoltativamente da TOTP. Conserva sia l'accesso dell'esercente sia i segreti API con la stessa cura riservata alle credenziali del wallet.
Checklist di go-live
Non riutilizzare la copia personale di uno sviluppatore su più servizi.
Usa GET /v1/assets e GET /v1/currencies; mostra solo le voci presenti e available: true.
Salva il segreto di firma una sola volta; verifica timestamp e firma, quindi deduplica l'ID evento stabile.
Esegui un tentativo duplicato e conferma che esista un solo ID pagamento.
Lo stato del tuo ordine deve restare sicuro anche in presenza di tassi fiat non aggiornati, asset non disponibili, conferme in ritardo e ogni percorso non ideale.
Confronta i tuoi ordini con gli stati di pagamento dell'API, i log dei webhook e il registro dell'esercente.
Pronto per integrare?
Crea un account in pochi secondi, genera una chiave e tieni questo riferimento accanto al tuo codice.