DOCUMENTAZIONE PER SVILUPPATORI
Documentazione API
Integra fatture, checkout e notifiche di pagamento.
Risultati di ricerca
Nessun risultato. Prova il nome di un endpoint, un campo o una guida.
Avvio rapido
Crea la tua prima fattura.
- Prepara un negozio
Abilita i suoi metodi di pagamento, configura i provider ed esegui il backup dei wallet del progetto.
- Crea una credenziale API
In Impostazioni → Accesso API della console, scegli lettura/scrittura e assegna il progetto.
- Invia la richiesta
Usa il tuo host API e copia gli ID di progetto e negozio. Invia gli importi decimali come stringhe.
- Apri il checkout
Reindirizza a
links.checkoutdalla risposta. Verifica il pagamento prima di evadere l'ordine.
: "${WHOLLY_TOKEN:?Set WHOLLY_TOKEN to your server-side API token}"
# Keep this key and the exact body for retries; use a new key for each new invoice.
curl --fail-with-body --max-time 30 \
--request POST \
--url "https://api.example.com/v1/projects/YOUR_PROJECT_ID/stores/YOUR_STORE_ID/invoices" \
--header "Authorization: Bearer $WHOLLY_TOKEN" \
--header 'Idempotency-Key: order-1042-attempt-1' \
--header 'Content-Type: application/json' \
--data-raw '{
"amount": "49.90",
"currency": "USD",
"order_id": "order-1042",
"exchange_rate_spread_percent": "0.5"
}'// Node.js 18+ · run on your server, never in browser code.
const token = process.env.WHOLLY_TOKEN;
if (!token) throw new Error("Set WHOLLY_TOKEN");
// Keep this key and the exact body for retries; use a new key for each new invoice.
const body = `{
"amount": "49.90",
"currency": "USD",
"order_id": "order-1042",
"exchange_rate_spread_percent": "0.5"
}`;
const response = await fetch("https://api.example.com/v1/projects/YOUR_PROJECT_ID/stores/YOUR_STORE_ID/invoices", {
method: "POST",
headers: {
"Authorization": `Bearer ${token}`,
"Idempotency-Key": "order-1042-attempt-1",
"Content-Type": "application/json"
},
body,
redirect: "error",
signal: AbortSignal.timeout(30000)
});
if (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);
console.log(await response.json());<?php
// PHP 8+ with the cURL extension; run on your server.
$token = getenv('WHOLLY_TOKEN');
if (!$token) { throw new RuntimeException('Set WHOLLY_TOKEN'); }
// Keep this key and the exact body for retries; use a new key for each new invoice.
$body = <<<'JSON'
{
"amount": "49.90",
"currency": "USD",
"order_id": "order-1042",
"exchange_rate_spread_percent": "0.5"
}
JSON;
$ch = curl_init("https://api.example.com/v1/projects/YOUR_PROJECT_ID/stores/YOUR_STORE_ID/invoices");
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => 'POST',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_FOLLOWLOCATION => false,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTPHEADER => ['Authorization: Bearer ' . $token, "Idempotency-Key: order-1042-attempt-1", "Content-Type: application/json"],
CURLOPT_POSTFIELDS => $body,
]);
$response = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if ($response === false) { throw new RuntimeException(curl_error($ch)); }
curl_close($ch);
if ($status < 200 || $status >= 300) { throw new RuntimeException("HTTP $status: $response"); }
print_r(json_decode($response, true, 512, JSON_THROW_ON_ERROR));# Python 3 · standard library; run on your server.
import json
import os
from urllib.request import Request, build_opener, HTTPRedirectHandler
class NoRedirect(HTTPRedirectHandler):
def redirect_request(self, req, fp, code, msg, headers, newurl):
return None
# Keep this key and the exact body for retries; use a new key for each new invoice.
headers = {
"Idempotency-Key": "order-1042-attempt-1",
"Content-Type": "application/json"
}
headers["Authorization"] = "Bearer " + os.environ["WHOLLY_TOKEN"]
body = """{
"amount": "49.90",
"currency": "USD",
"order_id": "order-1042",
"exchange_rate_spread_percent": "0.5"
}""".encode("utf-8")
request = Request("https://api.example.com/v1/projects/YOUR_PROJECT_ID/stores/YOUR_STORE_ID/invoices",
method="POST", headers=headers, data=body)
# Non-2xx responses raise HTTPError. Do not retry writes with a new key.
with build_opener(NoRedirect()).open(request, timeout=30) as response:
print(json.load(response))Gli esempi usano segnaposto e non inviano richieste da questa pagina. Vedi tutti i campi della fattura e il formato della risposta →
ID di progetto e negozio
Dove trovare YOUR_PROJECT_ID e YOUR_STORE_ID.
Usa gli UUID della console, non i nomi di progetto o negozio né i loro identificativi leggibili.
| Segnaposto | Dove trovarlo | Usato per |
|---|---|---|
| YOUR_PROJECT_ID | Progetto → Impostazioni → ID API → ID API del progetto → Copia. Mostrato anche nella scheda Generale del negozio. | Richieste a livello di progetto e negozio. |
| YOUR_STORE_ID | Progetto → Negozi → seleziona un negozio → Generale → ID API → ID API del negozio → Copia. | Creazione di fatture e richieste dei metodi di pagamento del negozio. |
- Creare una fattura richiede entrambi gli ID, anche per il negozio predefinito. Il negozio deve appartenere a quel progetto e la credenziale API deve avere accesso al progetto.
- Creazione, elenco, dettaglio e checkout delle fatture restituiscono invoice_id: lo stesso UUID inviato in IPN e webhook. Usalo nei percorsi delle fatture, non l'id interno o order_id. Dal merchant 4.0.0, il vecchio campo di risposta public_id è rimosso; aggiorna le integrazioni prima dell'upgrade.
- L'API REST non offre rotte per elencare progetti o negozi. Copia gli ID nella console oppure usa gli strumenti MCP list_projects e list_stores con ambito limitato nel merchant 5.0.0+.
- Negozio → Generale → Domini del negozio seleziona i nomi host attivi di console merchant, pagamenti e API. I link di checkout restituiti e i nuovi link di callback preferiscono quel negozio, poi il negozio predefinito, poi i valori di sistema. I nomi ritirati o non attivati non vengono mai selezionati. Configura l'SDK con il nome host API preferito; cambiare una preferenza non reindirizza gli altri alias attivi.
Autenticazione e ambito
Conserva le credenziali sul server e concedi solo l'accesso necessario.
| Host predefinito | Scopo |
|---|---|
| merchant.example.com | Console merchant e Impostazioni |
| pay.example.com | Checkout cliente |
| api.example.com | Richieste API merchant |
Sostituisci example.com con il tuo dominio. Le installazioni esistenti mantengono i nomi configurati; gestisci gli alias in Impostazioni → Sistema.
Authorization: Bearer YOUR_MERCHANT_API_TOKEN| Impostazione | Come funziona |
|---|---|
| Livello di accesso | Le credenziali in sola lettura possono elencare e recuperare dati. Quelle in lettura/scrittura possono anche creare fatture e aggiornare le politiche degli asset documentate. |
| Progetti | Assegna i progetti a cui la credenziale può accedere. Gli ID di negozio e fattura devono appartenere a un progetto assegnato. |
| Restrizioni IP | Puoi consentire indirizzi pubblici esatti IPv4 o IPv6 di uscita in Impostazioni → Accesso API. |
| Archiviazione delle credenziali | Conserva i token nella configurazione del backend. Non includere mai una credenziale bearer in un browser o in un link di checkout. |
Le rotte pubbliche del checkout usano l'ID pubblico della fattura ed espongono solo dati sicuri per il checkout. Sessioni della console e controlli amministrativi sono separati dalle credenziali API merchant.
Asset e wallet
Scegli i metodi di pagamento separatamente per ogni negozio.
- Leggi gli asset di pagamento del progetto e la loro disponibilità.
- Abilita la blockchain nativa e configura wallet e provider.
- Esplora i token candidati e verifica il contratto o il mint prima di abilitare un token.
- Seleziona, nell'ordine desiderato, i metodi di pagamento. Le nuove fatture usano le selezioni pronte a ricevere pagamenti.
I token condividono il wallet della loro blockchain nativa. I saldi dei wallet restituiscono importi atomici esatti e valori fiat indicativi. Usa i campi di disponibilità restituiti per determinare quali metodi possono ricevere pagamenti.
I token ERC-20 verificati usano le reti EVM supportate; i token SPL verificati usano Solana. I metodi di pagamento nativi sono disponibili nelle 30 reti integrate. Monero usa una connessione a un wallet esterno in sola visualizzazione, associata al progetto.
API di ricezione e copertura di monete native e token
| Canale | Supporto | Prove | Requisiti |
|---|---|---|---|
| Canali di pagamento nativi | supportato | Scansione delle transazioni | BTC, SOL, ETH (Ethereum/Base/Arbitrum/OP), BNB, HYPE, AVAX e POL; output Bitcoin, transazioni e ricevute EVM canoniche e trasferimenti Solana analizzati forniscono le prove per le fatture. |
| Canali token ERC-20 | supportato | Scansione delle transazioni | Ethereum, Base, BNB Chain, HyperEVM, Avalanche, Polygon, Arbitrum e Optimism richiedono verifica on-chain; i log Transfer indicizzati forniscono l'attribuzione dei pagamenti. |
| Canali token SPL | supportato | Scansione delle transazioni | I candidati Solana richiedono verifica della mainnet e del mint; le differenze esatte dei saldi token nelle transazioni analizzate forniscono l'attribuzione dei pagamenti. |
| Altri canali nativi UTXO | supportato | Scansione delle transazioni | BCH/LTC/DOGE usano Esplora; BCH/DOGE accettano anche Bitcore, LTC/DOGE/DASH accettano BlockCypher, Dash accetta Insight e ZEC trasparente accetta zcash-explorer. Tutti accettano anche blocchi node-rpc completi conservati e compatibili con Core. La modalità diretta richiede 1–48 conferme, non il rilevamento mempool. Zcash shielded non è supportato. |
| Canali nativi ad account indicizzati | supportato | Scansione delle transazioni | TRON usa tron-indexer o node-rpc con blocchi solidificati; XRP usa xrpl-jsonrpc; Stellar usa stellar-horizon o registri Stellar node-rpc conservati; Cosmos Hub usa cometbft-jsonrpc; Algorand usa algorand-indexer o algod node-rpc; Hedera richiede hedera-mirror, non un relay EVM. |
| Canali di pagamento nativi a registro | supportato | Scansione delle transazioni | Aptos usa aptos-rest; Sui usa sui-graphql; NEAR usa near-jsonrpc; Kaspa usa kaspa-rest. Polkadot Asset Hub accetta substrate-rest o node-rpc finalizzato con supporto ai metadati; Tezos accetta tezos-tzkt o operazioni complete Octez node-rpc. Solo incassi nativi; per le fatture più vecchie è richiesta la conservazione dell'archivio. |
| Canali nativi Cardano e TON | supportato | Scansione delle transazioni | Cardano richiede cardano-koios e TON richiede toncenter-v3. Tag XRP, ID memo Stellar e commenti delle fatture TON vengono restituiti come destination_tag e devono essere inviati esattamente. |
| Integrità del regolamento | supportato | Verifica indipendente | Per impostazione predefinita, il regolamento finale richiede che due provider indipendenti concordino su transazione/evento esatti, importo, blocco o slot canonico e finalità. Le finestre EVM dirette e condivise verificano anche la completezza della copertura. Un amministratore può scegliere esplicitamente un unico provider fidato per una blockchain; questo rimuove il controllo incrociato indipendente, non i controlli di identità, completezza o finalità. |
| Canale nativo Monero | supportato | RPC wallet in sola visualizzazione associato al progetto | Un wallet-RPC esterno dedicato in sola osservazione, dietro un gateway HTTPS con lista dei metodi consentiti, crea sottoindirizzi dell'account 0. La soglia configurata dei daemon mainnet (predefinita: 2 fonti indipendenti; facoltativa: 1) fornisce le prove di regolamento. Il parametro nativo --restricted-rpc è incompatibile con create_address; backup del wallet e assenza di una chiave di spesa sono attestati esplicitamente dall'operatore, senza inviare materiale crittografico a Wholly Crypto. |
I saldi degli exchange e le scelte sweep wallet o exchange per ogni asset sono disponibili nella console, non nell'API pubblica v1. Vedi la configurazione degli exchange.
Ciclo di vita della fattura
Prove di pagamento, regolamento ed evasione degli ordini.
| Stato | Significato |
|---|---|
| new | In attesa di un pagamento |
| processing | Pagamento rilevato; importo accettato o finalità in attesa |
| settled | Accettato secondo la politica di regolamento della fattura o manualmente |
| expired | Scadenza superata; il monitoraggio dei pagamenti tardivi può continuare |
| invalid | Il pagamento non può essere accettato automaticamente |
| cancelled | Annullato; solo una riconciliazione esplicita può riaprirlo |
amount_status registra none, partial, paid oppure overpaid. timing_status distingue i pagamenti puntuali da quelli tardivi. Le regole del negozio stabiliscono le conferme richieste e la tolleranza accettata per i pagamenti insufficienti.
Usa il valore della fattura invoice_id con la rotta del dettaglio fattura. Un reindirizzamento dal checkout da solo non prova il pagamento. Verifica le eccezioni tramite riconciliazione.
Nuovi tentativi sicuri
La creazione di fatture richiede Idempotency-Key. Dopo un timeout, riprova con la stessa credenziale, la stessa chiave e lo stesso identico corpo della richiesta. Usa una nuova chiave solo per una nuova fattura.
Scansione dei pagamenti EVM
Il rilevamento condiviso dei blocchi nativi e degli ERC-20 raggruppa le fatture recenti separatamente dal recupero storico di quelle più vecchie. Ogni fattura conserva il proprio cursore storico persistente. Le query token usano al massimo 100 blocchi per richiesta e si riducono per i provider con limiti più stretti. Due provider indipendenti verificano ogni finestra per impostazione predefinita. Impostazioni → Connessioni blockchain → Dettagli consente di scegliere per una blockchain una sola fonte fidata, senza verifica incrociata indipendente; restano i controlli sulla transazione canonica, sull'importo e sulle conferme. I dettagli della connessione distinguono ritardi degli scanner, limiti dello storico e attese per quota esaurita dallo stato di base del nodo. La capacità RPC pubblica non è garantita.
IPN e webhook
Ricevi e verifica gli eventi di pagamento.
IPN riceve ogni evento generato per la fattura all'ipn_url effettivo della fattura. I webhook ricevono solo gli eventi selezionati per ogni endpoint attivo del negozio. Entrambi inviano via POST la stessa istantanea JSON; sono indipendenti, quindi abilitarli entrambi può notificare due volte l'applicazione.
Imposta ipn_url quando crei una fattura, oppure eredita il valore predefinito del negozio. IPN usa il segreto di Negozio → IPN ; ogni Negozio → Webhook endpoint ha il proprio segreto. Nessuno dei due è la tua chiave API.
Quando devo evadere un ordine?
Per la gestione basata sugli eventi, usa event_type = invoice.settled insieme a status = settled per avviare il controllo dell'ordine. Verifica la fattura attuale ed evadi ogni ordine una sola volta.
status è lo stato della fattura quando è stato creato l'evento. event_type indica cosa è successo. payment.received può riportare processing o settled; non indica un secondo pagamento e non è un segnale indipendente per evadere l'ordine.
Quali eventi e stati vengono inviati?
| Evento nelle impostazioni/cronologia | Stato nel corpo | Significato |
|---|---|---|
| invoice.created | new | Fattura creata e in attesa di pagamento. Usato anche quando una riapertura controllata riporta una fattura a new. |
| payment.received | Resulting invoice status | È stato registrato un pagamento o l'importo ricevuto è aumentato. Di solito processing o settled; questo evento da solo non prova il regolamento. |
| invoice.processing | processing | Pagamento rilevato, ma l'importo accettato o la finalità richiesta non sono ancora raggiunti. Include i pagamenti parziali. |
| invoice.settled | settled | Politica di regolamento soddisfatta o accettazione manuale. Controlla resolution e il tuo ordine prima dell'evasione. |
| invoice.expired | expired | Scadenza del pagamento superata. Un pagamento tardivo può ancora cambiare lo stato mentre il monitoraggio continua. |
| invoice.invalid | invalid | Non può essere accettato automaticamente, le prove di pagamento sono state perse oppure un commerciante lo ha rifiutato. Verifica la fattura. |
| invoice.cancelled | cancelled | Fattura annullata. Non evadere l'ordine; l'annullamento non rimborsa un pagamento on-chain. |
Perché Ethereum e Solana possono inviare flussi di eventi diversi
Le conferme arrivano dopo (esempio Ethereum)
| Sequenza | event_type | status |
|---|---|---|
| 1 | invoice.created | new |
| 2 | payment.received | processing |
| 2 | invoice.processing | processing |
| 3 | invoice.settled | settled |
Già definitivo al rilevamento (esempio Solana)
| Sequenza | event_type | status |
|---|---|---|
| 1 | invoice.created | new |
| 2 | payment.received | settled |
| 2 | invoice.settled | settled |
Questi esempi mostrano la creazione degli eventi, non un ordine di consegna garantito. Entrambi i flussi possono verificarsi su altre blockchain in base ai tempi di rilevamento e alla politica di regolamento. Non richiedere un evento processing prima di settled.
Evadi una sola volta: esempio di ricevitore e protezione dai duplicati
| Approccio | Come gestirlo |
|---|---|
| Ricevitore basato sugli eventi | Mantieni distinti gli eventi tramite l'event_id firmato, poi seleziona invoice.settled con status = settled. Non scartare questo evento perché payment.received con la stessa sequence è arrivato prima. |
| Coda SDK dello stato ordini | Gli esempi di ricevitore PHP, Python e Node forniti raggruppano project + invoice_id + sequence. Elabora lo stato salvato indipendentemente da event_type, recupera la fattura attuale ed evadi una sola volta se settled. Non aggiungere un filtro solo invoice.settled dopo questo raggruppamento. |
Un nuovo tentativo conserva event_id e il corpo originale. Eventi diversi possono condividere sequence ma avere valori event_id diversi. Deduplica le consegne usando l'event_id firmato per la gestione basata sugli eventi; proteggi separatamente l'evasione tramite installazione/progetto configurati + invoice_id e il tuo ordine. Un nuovo regolamento successivo non deve accreditare l'ordine due volte.
HTTP receiver:
Verify raw-body signature, timestamp and configured project/store scope.
Save to a durable inbox; deduplicate the signed event_id.
Return HTTP 2xx only after persistence succeeds.
Event-based background worker:
Other events go to status/reconciliation handling, not fulfilment.
Continue here only for event_type = invoice.settled and status = settled.
Fetch the current invoice from your configured API origin.
Check settled status, project/store, order, amount, currency and review policy.
In one database transaction:
Lock the order and check the scoped invoice has not been fulfilled.
Credit/complete once and save the fulfilment record.
Queue any external fulfilment with the same business idempotency key.
SDK order-state worker:
Use the same current-invoice checks and fulfil-once transaction.
Do not filter event_type after collapsing events by invoice revision.Pseudocodice, non un ricevitore pronto all'uso.
Tutti gli stati delle fatture e le eccezioni di pagamento
| Campo | Valori | Significato |
|---|---|---|
| status | new, processing, settled, expired, invalid, cancelled | Stato della fattura alla creazione dell'evento; non necessariamente lo stato attuale alla consegna. |
| amount_status | none, partial, paid, overpaid | Importo ricevuto, inclusa la tolleranza accettata. paid non indica la finalità delle conferme. |
| timing_status | on_time, late | Indica se il pagamento ha rispettato la scadenza della fattura. |
| resolution | automatic, manually_settled, manually_invalidated | Indica se il risultato deriva dalle regole normali o da un'accettazione/rifiuto manuale. |
| requires_review | false, true | Indicazione di eccezione, non un altro stato della fattura né un permesso automatico di evasione o rimborso. |
| Situazione | Gestione |
|---|---|
| Pagamento insufficiente / tolleranza | Con le regole automatiche, partial non completa il regolamento. paid può includere un ammanco accettato, ma la finalità resta necessaria. Usa lo stato della fattura, non solo un confronto degli importi. |
| Pagamento in eccesso | overpaid può coesistere con settled e requires_review = true. Applica la tua politica per gli importi in eccesso; non accreditare mai l'ordine due volte e non rimborsare automaticamente un indirizzo non verificato. |
| Pagamento tardivo | expired può cambiare in seguito mentre il monitoraggio continua. timing_status = late segnala una verifica; non riaprire né spedire automaticamente un ordine annullato. |
| Accettazione manuale | invoice.settled può avere resolution = manually_settled senza fondi on-chain idonei. Decidi se la tua integrazione accetta questa eccezione; i campi riepilogativi del pagamento possono essere null. |
| Riorganizzazione / invalidazione | Una revisione più recente può invalidare prove di pagamento precedenti. Recupera di nuovo lo stato attuale e gestisci lo storno tramite riconciliazione. Non ignorarlo solo perché l'ordine è stato saldato in passato. |
| Zero conferme / importo zero | Il regolamento con zero conferme può avvenire al rilevamento e comporta rischio di riorganizzazione. Una fattura a importo zero esplicitamente consentita si salda senza pagamento. Nessuno dei due casi richiede prima un evento payment.received. |
Usa status = settled per evadere, non amount_status = paid né un reindirizzamento dal checkout. Con zero conferme richieste, il regolamento può avvenire al rilevamento; ciò comporta un rischio di riorganizzazione.
Un pagamento insufficiente è amount_status = partial; uno eccessivo è overpaid. paid indica che è arrivato il minimo accettato, inclusa la tolleranza per importi insufficienti della fattura. Sono stati dell'importo, non stati della fattura. late è un timing_status, non un evento separato.
Un flusso tipico è new → processing → settled, ma gli stati intermedi possono essere saltati. Una fattura a importo zero esplicitamente consentita si salda senza pagamento e mantiene amount_status = none. L'accettazione manuale è indicata con manually_settled.
I callback sono istantanee immutabili, non risposte sullo stato in tempo reale. Possono arrivare in ritardo, fuori ordine o più volte. Gli eventi di pagamento e di stato possono condividere la sequenza della fattura e gli stessi campi di stato, ma hanno valori firmati event_id ed event_type diversi. Il conteggio delle conferme non genera un callback garantito per ogni blocco.
Cosa ricevi
{
"invoice_id": "11111111-2222-4333-8444-555555555555",
"status": "settled",
"amount_status": "paid",
"timing_status": "on_time",
"resolution": "automatic",
"sequence": 3,
"amount": "49.9",
"currency": "EUR",
"order_id": "order-1042",
"payload_version": 2,
"event_id": "aaaaaaaa-aaaa-4aaa-8aaa-aaaaaaaaaaaa",
"event_type": "invoice.settled",
"occurred_at": "2026-09-14T12:05:00Z",
"project_id": "11111111-1111-4111-8111-111111111111",
"store_id": "22222222-2222-4222-8222-222222222222",
"description": "Annual plan",
"email": "ada@example.test",
"customer": {
"firstname": "Ada",
"lastname": "Lovelace",
"countryiso2": "GB"
},
"metadata": {
"firstname": "Ada",
"lastname": "Lovelace",
"countryiso2": "GB",
"cart_id": "cart-681"
},
"created_at": "2026-09-14T12:00:00Z",
"updated_at": "2026-09-14T12:05:00Z",
"expires_at": "2026-09-14T12:15:00Z",
"monitoring_expires_at": "2026-09-21T12:15:00Z",
"settled_at": "2026-09-14T12:05:00Z",
"paid_chain": "ethereum",
"paid_asset": "USDC",
"paid_asset_amount": "58.17342",
"paid_asset_amount_received": "58.17342",
"paid_payment_method_id": "33333333-3333-4333-8333-333333333333",
"settlement_exchange_rate": {
"rate": "1.17",
"units": "asset_per_invoice_currency",
"currency": "EUR",
"symbol": "USDC",
"observed_at": "2026-09-14T12:05:00Z",
"as_of": "2026-09-14T12:04:30Z",
"pricing_provider": "kraken",
"asset_provider": "kraken",
"pricing_fetched_at": "2026-09-14T12:04:30Z",
"asset_fetched_at": "2026-09-14T12:04:30Z",
"stale": false,
"is_fixed": false,
"reference_currency": "USD",
"uses_reference_proxy": false
},
"cancelled_at": null,
"exchange_rate_spread_percent": "0.5",
"underpayment_tolerance_percent": "1",
"reason_code": "payment_confirmed",
"requires_review": false,
"links": {
"checkout": "https://pay.example.com/invoice/11111111-2222-4333-8444-555555555555",
"invoice": "https://api.example.com/v1/projects/11111111-1111-4111-8111-111111111111/invoices/11111111-2222-4333-8444-555555555555",
"payments": "https://api.example.com/v1/projects/11111111-1111-4111-8111-111111111111/invoices/11111111-2222-4333-8444-555555555555/payments"
},
"payment_info": {
"active_payment_method_id": "33333333-3333-4333-8333-333333333333",
"method_count": 1,
"methods_truncated": false,
"methods": [
{
"payment_method_id": "33333333-3333-4333-8333-333333333333",
"payment_rail": "onchain",
"chain_slug": "ethereum",
"network": "mainnet",
"caip_network_id": "eip155:1",
"asset_id": "44444444-4444-4444-8444-444444444444",
"asset_key": "eip155:1/erc20:0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48",
"caip_asset_id": "eip155:1/erc20:0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48",
"asset_name": "USD Coin",
"symbol": "USDC",
"asset_kind": "token",
"asset_decimals": 6,
"contract_address": "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48",
"token_standard": "erc20",
"destination_address": "0x1111111111111111111111111111111111111111",
"destination_tag": null,
"status": "paid",
"amounts": {
"expected_amount": "58.17342",
"expected_amount_atomic": "58173420",
"received_amount": "58.17342",
"received_amount_atomic": "58173420",
"confirmed_amount": "58.17342",
"confirmed_amount_atomic": "58173420",
"unconfirmed_amount": "0",
"unconfirmed_amount_atomic": "0",
"minimum_payment_amount": "57.591686",
"minimum_payment_amount_atomic": "57591686",
"remaining_amount": "0",
"remaining_amount_atomic": "0",
"remaining_to_full_amount": "0",
"remaining_to_full_amount_atomic": "0",
"overpaid_amount": "0",
"overpaid_amount_atomic": "0"
},
"acceptance": {
"finality_mode": "confirmations",
"required_confirmations": 2,
"observed_confirmations": 2,
"underpayment_tolerance_percent": "1"
},
"quote": {
"effective_rate": "1.1658",
"reference_rate": "1.16",
"units": "asset_per_invoice_currency",
"currency": "EUR",
"symbol": "USDC",
"exchange_rate_spread_percent": "0.5",
"quote_expires_at": "2026-09-14T12:15:00Z",
"provenance_available": true,
"rounding": "up",
"unrounded_payment_amount": "58.17342",
"rounding_adjustment": "0",
"pricing_provider": "kraken",
"asset_provider": "kraken",
"pricing_fetched_at": "2026-09-14T11:59:30Z",
"asset_fetched_at": "2026-09-14T11:59:30Z"
},
"market_rate_at_event": {
"rate": "1.17",
"units": "asset_per_invoice_currency",
"currency": "EUR",
"symbol": "USDC",
"observed_at": "2026-09-14T12:05:00Z",
"pricing_provider": "kraken",
"asset_provider": "kraken",
"pricing_fetched_at": "2026-09-14T12:04:30Z",
"asset_fetched_at": "2026-09-14T12:04:30Z",
"as_of": "2026-09-14T12:04:30Z",
"stale": false,
"is_fixed": false,
"reference_currency": "USD",
"uses_reference_proxy": false
},
"payment_count": 1,
"payments_truncated": false,
"payments": [
{
"payment_id": "55555555-5555-4555-8555-555555555555",
"payment_method_id": "33333333-3333-4333-8333-333333333333",
"transaction_id": "0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
"payment_hash": null,
"event_index": 0,
"payment_rail": "onchain",
"chain_slug": "ethereum",
"network": "mainnet",
"asset_id": "44444444-4444-4444-8444-444444444444",
"asset_key": "eip155:1/erc20:0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48",
"caip_asset_id": "eip155:1/erc20:0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48",
"symbol": "USDC",
"asset_decimals": 6,
"amount": "58.17342",
"amount_atomic": "58173420",
"status": "final",
"counts_towards_received": true,
"confirmations": 2,
"block_height": 26000000,
"observed_at": "2026-09-14T12:04:30Z",
"chain_time": "2026-09-14T12:04:20Z",
"finalized_at": "2026-09-14T12:05:00Z",
"explorer_name": "Etherscan",
"explorer_url": "https://etherscan.io/tx/0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
}
],
"links": {
"payments": "https://api.example.com/v1/projects/11111111-1111-4111-8111-111111111111/invoices/11111111-2222-4333-8444-555555555555/payments?payment_method_id=33333333-3333-4333-8333-333333333333"
}
}
]
}
}amount è il totale originale della fattura. payment_info descrive i trasferimenti crypto osservati, gli importi ancora mancanti e i tassi bloccati. La versione 2 firma anche il nome dell'evento, l'ID evento e l'ambito progetto/negozio.
Tutti i campi dei callback e i dati aggiuntivi della fattura
| Campo | Tipo | Significato |
|---|---|---|
| invoice_id | UUID | UUID pubblico della fattura, usato dalla rotta autenticata del dettaglio fattura |
| status | string | Stato della fattura nell'istantanea: new, processing, settled, expired, invalid, cancelled |
| amount_status | string | none, partial, paid o overpaid; paid include la tolleranza accettata per importi insufficienti, non la finalità delle conferme |
| timing_status | string | on_time o late |
| resolution | string | automatic, manually_settled o manually_invalidated |
| sequence | integer | Revisione crescente della fattura; eventi diversi possono condividere una revisione. Confronta senza perdere la precisione intera |
| amount | decimal string | Totale originale della fattura, non l'importo crypto ricevuto; conserva la precisione decimale |
| currency | string | Valuta di amount, ad esempio EUR per una fattura in EUR pagata con USDC |
| order_id | string | null | Riferimento dell'ordine del commerciante |
| payload_version | integer | 2 per gli eventi generati dalla 4.1.0+; assente negli eventi precedenti conservati |
| event_id | UUID | Identità firmata dell'evento, invariata nei nuovi tentativi e nei reinvii manuali |
| event_type | string | Uno dei sette eventi a cui iscriversi |
| occurred_at | timestamp | Momento di creazione di questo evento immutabile, non di consegna |
| project_id | UUID | Ambito del progetto merchant; deve corrispondere al ricevitore configurato |
| store_id | UUID | Ambito del negozio merchant; deve corrispondere al ricevitore configurato |
| description | string | null | Descrizione originale della fattura |
| string | null | Email cliente facoltativa al momento della creazione dell'evento | |
| customer | object | Campi facoltativi riconosciuti dei metadati cliente; nessun dato personale dedotto o arricchito |
| metadata | object | Metadati originali del commerciante come erano alla creazione dell'evento |
| created_at | timestamp | Data e ora di creazione della fattura |
| updated_at | timestamp | Data e ora di aggiornamento dello stato della fattura |
| expires_at | timestamp | Scadenza di pagamento della fattura |
| monitoring_expires_at | timestamp | Scadenza del monitoraggio dei pagamenti tardivi |
| settled_at | timestamp | null | Data e ora del regolamento |
| paid_chain | string | null | 4.1.2+: slug della blockchain del metodo di regolamento comprovato, ad esempio ethereum; null senza un regolamento valido salvato |
| paid_asset | string | null | 4.1.2+: ticker della moneta nativa o del token, ad esempio BTC, ETH o USDC; etichetta visiva, non identità univoca dell'asset |
| paid_asset_amount | decimal string | null | 5.0.1+: intero importo bloccato richiesto in unità paid_asset, prima di sottrarre la tolleranza; salvato al regolamento |
| paid_asset_amount_received | decimal string | null | 5.0.1+: importo valido totale ricevuto per il metodo vincente al regolamento, inclusi ammanchi/eccessi accettati; congelato, non un saldo in tempo reale |
| paid_payment_method_id | UUID | null | 4.1.2+: ID dell'intento che ha regolato il pagamento; corrisponde a payment_info.methods[].payment_method_id e alla sua rete/contratto esatti |
| settlement_exchange_rate | object | null | 4.1.2+: istantanea di mercato prima dello spread salvata al regolamento, con unità, valuta, timestamp delle fonti e indicatori di qualità espliciti; mai ricalcolata alla consegna |
| cancelled_at | timestamp | null | Data e ora dell'annullamento |
| exchange_rate_spread_percent | decimal string | Spread bloccato, non il valore predefinito attuale del negozio |
| underpayment_tolerance_percent | decimal string | Tolleranza bloccata della fattura; ogni metodo riporta anche la sua tolleranza effettiva |
| reason_code | string | null | Motivo della transizione di stato leggibile dalla macchina |
| requires_review | boolean | Indicazione di eccezione di pagamento; non autorizza l'evasione o il rimborso automatici |
| links | object | URL di checkout, fattura autenticata e pagamenti alla creazione dell'evento. Si applicano le preferenze dei domini in Negozio → Generale, poi il negozio predefinito, poi il dominio primario globale; vengono usati solo domini attivi con ruolo corrispondente. I nuovi tentativi mantengono i link firmati originali; null se non esiste un record host attivo. |
| payment_info | object | Metodi effettivamente osservati, importi esatti, quotazione bloccata, istantanea di mercato indicativa e osservazioni di pagamento limitate; vedi i gruppi di campi sotto |
Riepilogo del regolamento: settlement_exchange_rate
| Campo | Tipo | Significato |
|---|---|---|
| rate / units / currency / symbol | strings | Unità dell'asset prima dello spread per una unità della valuta della fattura. Stringa decimale, non importo di pagamento né operazione eseguita. |
| observed_at / as_of | timestamps | Momento di acquisizione al regolamento / timestamp precedente della fonte. Non trattare i dati in cache come una quotazione in tempo reale. |
| pricing_provider / asset_provider / pricing_fetched_at / asset_fetched_at | strings / timestamps | Fonti dei prezzi fiat e degli asset e relativi tempi di acquisizione, salvati al regolamento. |
| stale / is_fixed / uses_reference_proxy / reference_currency | booleans / string | Stessi indicatori di qualità di market_rate_at_event. I prezzi fissi di progetto sono etichettati; la valuta di riferimento è USD. |
| Missing snapshot or price | null | Nessun tasso storico dedotto. Prima del regolamento tutti i campi riepilogativi sono null; i soli prezzi mancanti lasciano disponibili gli identificativi paid_* comprovati. |
Metodi di pagamento: payment_info
| Campo | Tipo | Significato |
|---|---|---|
| active_payment_method_id | UUID | null | Metodo osservato vincente o selezionato. Null prima del rilevamento o dopo l'invalidazione; nessun metodo predefinito viene dedotto. |
| method_count / methods_truncated | integer / boolean | Totale dei metodi osservati e indicazione di incompletezza dell'elenco incorporato. |
| methods[] | object[] | Al massimo otto metodi osservati, con quello attivo per primo. Nessun totale tra asset diversi. |
| payment_method_id / payment_rail | UUID / string | Identità dell'intento della fattura e trasporto onchain o lightning. |
| chain_slug / network / caip_network_id | string | Identità della rete. Associa sempre l'identità del token alla sua rete. |
| asset_id / asset_key / caip_asset_id | UUID / string / nullable string | Identità verificata nel registro; i simboli da soli non sono univoci. |
| asset_name / symbol / asset_kind | string | Nome visualizzato dell'asset, ticker e tipo nativo o token. |
| contract_address / token_standard | string | null | Contratto o mint del token e standard; null per gli asset nativi. |
| asset_decimals | integer | Precisione atomica; Lightning BTC usa 11. |
| destination_address / destination_tag | string | null | Indirizzo pubblico di ricezione e memo/tag richiesto. L'indirizzo è null per Lightning; mai una chiave privata. |
| status | string | Stato del metodo: pending, partial, paid, overpaid, expired o invalid. Paid da solo non indica che la fattura sia saldata. |
| payment_count / payments_truncated / payments[] | integer / boolean / object[] | Totale delle osservazioni e le ultime cinque o meno. Ogni osservazione è descritta sotto. |
| links.payments | HTTPS URL | null | Cronologia autenticata e paginata di questo metodo sull'origine API configurata. |
Importi esatti: methods[].amounts
| Campo | Tipo | Significato |
|---|---|---|
| expected_amount | decimal string | Quotazione completa bloccata, dopo spread e arrotondamento per eccesso. |
| received_amount / confirmed_amount | decimal strings | Fondi validi rilevati / fondi che soddisfano la politica di conferma o finalità di questo metodo. |
| unconfirmed_amount | decimal string | max(received - confirmed, 0). Non è un importo aggiuntivo da inviare. |
| minimum_payment_amount | decimal string | Soglia accettata dopo la tolleranza. Può essere inferiore alla quotazione completa. |
| remaining_amount | decimal string | max(minimo accettato - ricevuto, 0). Fondi aggiuntivi necessari per raggiungere la soglia accettata, non avanzamento delle conferme. |
| remaining_to_full_amount | decimal string | max(quotazione completa - ricevuto, 0), ignorando la tolleranza. |
| overpaid_amount | decimal string | max(ricevuto - quotazione completa, 0). Non autorizza un rimborso automatico. |
| Every amount's *_atomic companion | integer string | Rappresentazione esatta nell'unità minima. Usa librerie decimali o intere; mai float o JavaScript Number per il denaro. |
Politica di conferma: methods[].acceptance
| Campo | Tipo | Significato |
|---|---|---|
| finality_mode / required_confirmations | string / integer | Conferme bloccate o politica finalized. Zero conferme è esplicitamente consentito dalla politica del commerciante, non è finalità universale della rete. |
| observed_confirmations | integer | null | Minimo tra le osservazioni valide, non solo il trasferimento più recente. Null per Lightning o in assenza di osservazioni valide. |
| underpayment_tolerance_percent | decimal string | Tolleranza effettiva del metodo. Lightning usa zero anche quando la fattura ha una tolleranza on-chain diversa da zero. |
Tassi: methods[].quote e market_rate_at_event
| Campo | Tipo | Significato |
|---|---|---|
| quote.effective_rate / units / currency / symbol | strings | Tasso asset_per_invoice_currency bloccato che include lo spread; valuta e simbolo indicano esplicitamente la direzione. |
| quote.exchange_rate_spread_percent / quote_expires_at | decimal string / timestamp | Spread bloccato e scadenza della quotazione. Mai sostituiti con le impostazioni attuali del negozio. |
| quote.reference_rate / unrounded_payment_amount / rounding_adjustment | decimal string | null | Riferimento prima dello spread, importo prima dell'arrotondamento e adeguamento per eccesso in unità dell'asset. |
| quote.pricing_provider / asset_provider / pricing_fetched_at / asset_fetched_at | string or timestamp | null | Fonti e tempi originali dei prezzi di valuta e asset. Nessuna chiave API o credenziale dei provider. |
| quote.provenance_available / rounding | boolean / string | False per le vecchie fatture senza un'istantanea salvata della fonte; l'arrotondamento è per eccesso. |
| market_rate_at_event | object | null | Istantanea di mercato indicativa in cache alla creazione dell'evento. I dati mancanti restano null; non modifica mai gli importi della fattura e non attende richieste di rete. |
| market_rate_at_event.rate / units / currency / symbol | strings | Tasso di mercato prima dello spread, con la stessa direzione esplicita di quote. |
| market_rate_at_event.observed_at / as_of / pricing_fetched_at / asset_fetched_at | timestamps | Momento dell'istantanea dell'evento / il più vecchio dei due tempi delle fonti / tempo di ogni fonte. |
| market_rate_at_event.pricing_provider / asset_provider | strings | Fonti in cache di valuta e asset, inclusi i prezzi configurati dei token personalizzati. |
| market_rate_at_event.stale / is_fixed / uses_reference_proxy / reference_currency | booleans / string | Indica se la cache è obsoleta, il prezzo del token è fisso o il riferimento USD usa una stablecoin come proxy. La valuta di riferimento è USD. Stale è un'indicazione, mai una quotazione aggiornata. |
Record dei trasferimenti: methods[].payments[] e GET …/payments
| Campo | Tipo | Significato |
|---|---|---|
| payment_id / payment_method_id | UUID | Identità dell'osservazione / identità dell'intento padre. Usa payment_id per deduplicare la cronologia. |
| transaction_id / payment_hash / event_index | string | null / integer | Hash on-chain e indice di trasferimento/log/output, oppure hash Lightning. Lightning non ha una transazione né un link explorer. |
| payment_rail / chain_slug / network / asset_id / asset_key / caip_asset_id / symbol / asset_decimals | strings / UUID / integer | Gli stessi identificativi di asset e rete del metodo che lo contiene. |
| amount / amount_atomic | decimal / integer strings | Valore esatto di questo trasferimento, mai una conversione fiat. |
| status / counts_towards_received | string / boolean | detected, confirming e final contano; reorged, replaced e invalid no. Conserva la cronologia invalidata per la riconciliazione. |
| confirmations / block_height | integer | null | Dati del blocco dell'osservazione; conferme null per Lightning. |
| observed_at / chain_time / finalized_at | timestamp | null | Prima osservazione locale, ora attendibile della blockchain se disponibile e ora di finalità secondo la politica, se raggiunta. |
| explorer_name / explorer_url | string | null | Riferimento convalidato a un explorer pubblico, dove supportato. |
Il merchant 5.13.3 esclude i trasferimenti interni verificati di finanziamento del gas dai totali dei pagamenti cliente, da payment_info, dall'API dei pagamenti delle fatture, dai limiti di rimborso e dagli eventi payment.received. I loro record blockchain/tesoreria restano disponibili per la contabilità dei wallet. I trasferimenti ordinari e i veri pagamenti in eccesso continuano a contare. I corpi dei callback già firmati non vengono mai riscritti. Se un regolamento storico dipendeva da finanziamenti interni invece che da fondi del cliente, la riconciliazione emette invoice.invalid con reason_code internal_gas_funding_excluded; verificalo invece di evadere di nuovo l'ordine.
Il merchant 4.1.0 aggiunge payload_version 2 senza spostare o modificare i nove campi originali. Gli eventi già in coda mantengono il corpo originale e potrebbero non avere payload_version. event_id, event_type e gli ID di progetto e negozio sono ora nel corpo firmato; gli header di trasporto relativi a evento e consegna restano non firmati.
payment_info descrive i pagamenti osservati, non tutte le opzioni offerte dal checkout. Prima del rilevamento, active_payment_method_id è null e methods è vuoto. Le osservazioni riorganizzate o non valide possono restare in methods anche quando il metodo attivo diventa null. Non sommare mai importi di asset o reti diversi.
Tutti gli importi, gli interi atomici, i tassi e le percentuali sono stringhe. received_amount include fondi validi in attesa di conferma; confirmed_amount soddisfa la politica di finalità del metodo. remaining_amount è max(minimum_payment_amount meno received_amount, 0); remaining_to_full_amount è max(expected_amount meno received_amount, 0). Esempio: 100 USDC attesi, 99 ricevuti e tolleranza dell'1% danno remaining_amount 0 e remaining_to_full_amount 1. La finalità resta necessaria.
quote è il calcolo bloccato della fattura: unità dell'asset per una unità della valuta della fattura. Lo spread si applica prima dell'arrotondamento per eccesso. Usa expected_amount_atomic per confrontare esattamente il pagamento; un tasso visualizzato da solo potrebbe non riprodurre l'arrotondamento per eccesso. Le vecchie fatture senza provenienza delle fonti salvata espongono campi fonte/riferimento/arrotondamento null e provenance_available false, mai dati di oggi presentati come quotazione storica.
market_rate_at_event è un dato indicativo in cache prima dello spread, congelato alla creazione dell'evento. Ha tempi delle fonti e indicatori di obsolescenza e proxy di riferimento; è null se non esiste una coppia in cache utilizzabile. Nessuna richiesta di tasso in tempo reale blocca una notifica e questa osservazione di mercato non modifica mai l'importo dovuto. I token personalizzati a prezzo fisso sono marcati is_fixed; i token DEX usano la fonte specifica del progetto, non un token con lo stesso simbolo.
I campi principali paid_chain, paid_asset, paid_payment_method_id e settlement_exchange_rate (4.1.2+) identificano il metodo vincente comprovato dopo il regolamento, non un'opzione selezionata al checkout né una somma di metodi diversi. Prima del regolamento, dopo l'invalidazione, per vecchi regolamenti senza istantanea o per accettazioni manuali senza fondi validi definitivi secondo la politica, i campi riepilogativi sono null. I simboli sono etichette visive: segui l'ID del metodo per l'identità esatta di rete, asset e contratto.
Il merchant 5.0.1 aggiunge paid_asset_amount e paid_asset_amount_received come stringhe decimali esatte in unità paid_asset; payload_version resta 2. paid_asset_amount è la quotazione completa bloccata, inclusi spread e arrotondamento per eccesso, mai la soglia di tolleranza o un saldo residuo. paid_asset_amount_received è il totale degli incassi validi del metodo vincente al regolamento, inclusi fondi in attesa di conferma ed eventuali ammanchi o eccessi accettati. Esempio: 100 USDC quotati, 99 ricevuti e accettati con tolleranza danno 100 e 99, non 99 e 99. Entrambi restano congelati nell'istantanea del regolamento; usa payment_info.methods[].amounts per gli incassi a ogni evento o l'API dei pagamenti per i record attuali. Sono null senza un'istantanea valida e per le istantanee precedenti alla 5.0.1; i corpi dei vecchi eventi in coda non cambiano. Non convertire mai stringhe decimali esatte in virgola mobile per la contabilità.
settlement_exchange_rate è l'osservazione di mercato in cache prima dello spread acquisita al regolamento, non la quotazione bloccata della fattura né uno scambio eseguito. La sua struttura corrisponde a market_rate_at_event; 1.17 asset_per_invoice_currency con EUR/USDC significa 1 EUR = 1.17 USDC. Timestamp delle fonti e indicatori stale/fixed/proxy ne descrivono la qualità. Una coppia mancante lascia il tasso null, ma un metodo comprovato conserva i campi paid_*. Non modifica mai l'importo dovuto e non attende chiamate in tempo reale a un provider. Pagamenti successivi con lo stesso metodo, nuovi tentativi e reinvii non possono sostituire l'istantanea salvata, compreso un tasso null salvato. Un vero nuovo regolamento o un cambio di metodo acquisisce una nuova istantanea; observed_at identifica quell'acquisizione, mentre settled_at può mantenere il momento del primo regolamento. I corpi degli eventi precedenti restano invariati.
Sono inclusi al massimo otto metodi osservati e le cinque osservazioni di pagamento più recenti per metodo, con conteggi e indicatori di troncamento. Il limite del payload può ridurre ulteriormente gli array. Un'osservazione di pagamento è un trasferimento, log o output UTXO, non necessariamente un hash di transazione univoco. Usa GET /v1/projects/YOUR_PROJECT_ID/invoices/{invoice_id}/payments con payment_method_id, limit e offset per la cronologia attuale completa. Il dettaglio fattura conserva ogni metodo quotato e i suoi quote_details. I link API richiedono l'host e le credenziali configurati: non inoltrare mai un token bearer a un URL arbitrario fornito da un callback.
Lightning usa payment_hash invece di transaction_id; indirizzo di ricezione, explorer e conferme osservate sono null. L'importo BTC esatto usa 11 decimali (millisatoshi) e la tolleranza effettiva è zero. Non sono inclusi preimage di pagamento BOLT11, chiavi wallet, segreti di firma o credenziali dei provider. I campi cliente e metadati appartengono solo alle risposte merchant e ai callback firmati, mai al checkout pubblico; non inserire credenziali nei metadati.
Ricevi in sicurezza
- Verifica l'esatto corpo grezzo con il segreto corrispondente prima del parsing. Negozio → IPN fornisce il segreto IPN, anche per le consegne a ipn_url personalizzati. Ogni endpoint Negozio → Webhook ha il proprio segreto. Nessuno dei due è il token API; ruotarne uno non ruota gli altri.
- Controlla il timestamp firmato (predefinito SDK: cinque minuti in entrambe le direzioni) e confronta gli ID firmati di progetto e negozio con la configurazione del ricevitore, quando presenti. Accoda in modo durevole prima di restituire HTTP 2xx. Per l'elaborazione per evento, event_id v2 è firmato; gli ID negli header da soli non proteggono dai replay perché quegli header non sono firmati. Per le code dello stato ordini, deduplica invoice_id e sequence e confronta i campi originali dello stato fattura, non l'intero corpo v2: tipi e ID evento diversi possono condividere una revisione.
- In un worker, recupera la fattura attuale dall'origine API configurata, non da un link callback arbitrario. Verifica corrispondenza con ordine salvato, progetto/negozio, importo e valuta, richiedi lo stato attuale settled e applica la tua politica per accettazioni manuali ed eccezioni. Blocca l'ordine ed evadilo una sola volta in una transazione del database, indipendentemente dalla deduplicazione degli eventi.
- Non applicare mai una sequence più vecchia su una più recente. Eventi diversi possono condividere una revisione; non combinare la deduplicazione a livello di revisione con un filtro solo invoice.settled. Riapertura e riconciliazione possono cambiare lo stato; è sequence, non un ordine fisso degli stati, a ordinare gli aggiornamenti. Registra gli storni per la verifica invece di evadere di nuovo.
Esempi di ricevitore: PHP · Python · Node.js / TypeScript.
Verifica delle firme e regole di consegna
import { createHmac, timingSafeEqual } from "node:crypto";
export function verifyWhollySignature(rawBody, header, signingSecret, toleranceSeconds = 300) {
const match = /^t=(\d+),v1=([0-9a-f]{64})$/.exec(header || "");
if (!match) return false;
const timestamp = Number(match[1]);
if (!Number.isSafeInteger(timestamp)) return false;
const now = Math.floor(Date.now() / 1000);
if (Math.abs(now - timestamp) > toleranceSeconds) return false;
// rawBody must be the exact request Buffer, before JSON parsing.
const expected = createHmac("sha256", signingSecret)
.update(String(timestamp))
.update(".")
.update(rawBody)
.digest();
const presented = Buffer.from(match[2], "hex");
return timingSafeEqual(expected, presented);
}| Regola di consegna | Dettagli |
|---|---|
| Header | Wholly-Signature, Wholly-Event-Id e Wholly-Delivery-Id; Content-Type è application/json. |
| Firma | HMAC-SHA256 su <unix timestamp>.<exact raw body>; formato dell'header t=<timestamp>,v1=<64 lowercase hex>. |
| Successo | Qualsiasi risposta HTTP 2xx. I reindirizzamenti non vengono seguiti; le risposte non 2xx sono errori. |
| Timeout | Timeout di connessione di 5 secondi e timeout totale della richiesta di 10 secondi. |
| Pianificazione dei nuovi tentativi | Fino a 8 tentativi per errori riprovabili: subito, poi ritardi di 10s, 1m, 5m, 15m, 1h, 6h e 24h dopo la fine del tentativo precedente. IPN riprova automaticamente; il ritentativo automatico dei webhook può essere disattivato per endpoint. |
| Sicurezza della destinazione | Solo HTTPS pubblico. Il DNS viene rivalidato e fissato per la consegna; destinazioni locali, private o riservate vengono rifiutate. |
| Conservazione degli eventi | Payload degli eventi di notifica e consegne sono conservati per 90 giorni; i dettagli conservati vengono eliminati in lotti limitati. |
| Deduplicazione | Salva in modo persistente invoice_id e sequence firmati, limitandoli al progetto configurato. Wholly-Event-Id identifica un evento; Wholly-Delivery-Id identifica un record di consegna (i nuovi tentativi lo riusano; un reinvio manuale ne crea un altro). Nessuno dei due header ID è firmato. |
| Nomi degli eventi | La versione 2 firma event_id ed event_type nel corpo. I vecchi eventi in coda non hanno nessuno dei due. Tipi di evento diversi possono condividere una sequence di fattura; riconcilia lo stato per revisione oppure deduplica i singoli eventi tramite event_id firmato. |
| Rotazione dei segreti | La rotazione non ha sovrapposizione né header di versione e cambia immediatamente le firme delle consegne in coda, ritentate e manuali. |
| Consegne sospese | Crediti di elaborazione insufficienti sospendono IPN e webhook, inclusi i nuovi tentativi. I pagamenti in entrata continuano; le notifiche in coda riprendono dopo la ricarica entro il periodo di conservazione del payload. |
Assistenti IA · MCP
Collega un assistente alla tua installazione merchant.
Il merchant 5.0.0 include un server MCP facoltativo sul dominio API configurato. Funziona all'interno della tua installazione, non tramite un relay Wholly Crypto condiviso.
- Apri Impostazioni → Accesso API. Crea una credenziale dedicata, assegna solo i progetti necessari all'assistente e inizia in sola lettura. In un account ospitato da un operatore, l'operatore abilita prima il servizio MCP dell'installazione; tu gestisci solo le tue credenziali e autorizzazioni.
- In Connessioni IA · MCP, abilita MCP, seleziona la credenziale e salva l'accesso MCP. Le credenziali esistenti non hanno accesso MCP finché non viene abilitato esplicitamente.
- Copia l'URL del server MCP nelle impostazioni del server HTTP remoto del client. Con OAuth, accedi alla console merchant, controlla nome del client e indirizzo di ritorno, scegli una credenziale e approva. Le protezioni Basic Auth e TOTP esistenti restano valide.
- La creazione di fatture richiede inoltre una credenziale in lettura/scrittura, Lettura + creazione fatture nella sua politica MCP, l'ambito OAuth mcp:invoice:create e un'approvazione esplicita. Una connessione OAuth non ottiene mai i progetti aggiunti alla credenziale dopo l'approvazione.
{
"mcpServers": {
"whollycrypto": {
"url": "https://api.example.com/mcp"
}
}
}Guida alla configurazione MCP →
| Strumento | Accesso | Scopo |
|---|---|---|
| list_projects | Leggi gli | Progetti attivi assegnati alla connessione; paginazione limit/offset. |
| list_stores | Leggi gli | Negozi, ID e stato di attivazione in project_id; paginazione limit/offset. |
| list_payment_methods | Leggi gli | Metodi configurati per blockchain, token e Lightning per project_id + store_id. |
| get_wallet_balances | Leggi gli | Indirizzi di ricezione e saldi in cache, con campi di aggiornamento/disponibilità; mai segreti dei wallet. |
| list_invoices | Leggi gli | Fatture del progetto, filtrate per negozio, stato o ricerca; paginazione limit/offset. |
| get_invoice | Leggi gli | Dettagli completi della fattura e link di checkout tramite project_id + invoice_id. |
| get_delivery_history | Leggi gli | Stati IPN/webhook del negozio, tentativi e risultati HTTP. Filtri invoice_id/kind facoltativi; nessun segreto né corpo dei callback. |
| convert_amount | Leggi gli | Conversione indicativa in cache tramite from, to e un importo come stringa decimale; non è una quotazione di fattura. |
| create_invoice | Scrittura esplicita | project_id, store_id, idempotency_key e invoice (il corpo esistente per creare fatture). invoice.payment_methods filtra i metodi attivi del negozio; la 5.4.0+ ignora le scelte inattive/non accettate e usa i valori predefiniti del negozio se nessuna corrisponde. La sola blockchain seleziona tutti gli asset attivi accettati. asset_tickers limitati alla blockchain sono supportati dalla 5.3.0. Restituisce la normale risposta della fattura. |
Protocollo, OAuth e sicurezza
Usa Streamable HTTP su HTTPS. Negozia una versione del protocollo dichiarata e includi MCP-Protocol-Version nei POST successivi. Invia Content-Type: application/json e Accept: application/json, text/event-stream. Le risposte sono JSON finiti; le riconnessioni non richiedono un ID di sessione MCP.
OAuth usa token di accesso di breve durata (15 minuti), codici S256 PKCE monouso (5 minuti) e token di aggiornamento a rotazione (durata della connessione: 30 giorni). Riutilizzare un token di aggiornamento già usato revoca quella connessione. Ricollegati dopo scadenza, rotazione delle credenziali, modifiche alla politica o cambio del dominio API canonico.
La discovery OAuth è pubblica solo quando MCP è abilitato. Il parametro resource deve essere uguale all'URL canonico restituito dalla discovery, incluso /mcp. La registrazione dinamica è supportata; documenti remoti di metadati del client-ID e segreti client no.
Per i client che supportano header Authorization personalizzati, è possibile usare invece un token API merchant abilitato a MCP come Bearer. Mantiene i propri permessi REST separati; preferisci OAuth per una connessione limitata a MCP. Non incollare mai credenziali in chat, URL, argomenti degli strumenti o controllo di versione.
MCP condivide la quota REST al minuto della credenziale e le restrizioni esatte degli IP di origine, oltre alle restrizioni IP dell'host API. OAuth non aggira una lista consentita. Per client IA remoti, consenti gli IP di uscita documentati oppure lascia deliberatamente disattivata questa restrizione. Non applicare verifiche di sicurezza web interattive o cache alle rotte MCP/OAuth.
Errori HTTP: 401 richiede autenticazione, 403 nega origine/IP/permesso, 404 indica MCP disabilitato o host errato, 405 richiede POST, 413 indica il limite del corpo di 32 KiB, 429 include Retry-After. Gli errori JSON-RPC usano error.code; gli errori a livello di strumento usano result.isError=true anche con HTTP 200. I risultati riusciti includono content e structuredContent.
Gli elenchi hanno 25 righe per impostazione predefinita, massimo 100; offset è limitato a 1000000. Le risposte degli strumenti sono limitate a 2 MiB. Autorizzazioni scadute, richieste di autorizzazione e contatori dei limiti vengono rimossi automaticamente; nelle impostazioni sono mostrate al massimo 100 connessioni OAuth attive.
I progetti e negozi disattivati non possono essere gestiti tramite MCP. La connessione può elencare lo stato di attivazione di un negozio, ma leggerne i metodi di pagamento, la cronologia delle consegne o creare fatture richiede un negozio attivo. I normali utenti di progetto della console non possono amministrare MCP.
Usa un nuovo idempotency_key per una nuova fattura; dopo un timeout riprova con la stessa credenziale, chiave e identico oggetto invoice. Importi decimali, spread, tolleranza, conferme e aspetto del checkout seguono il contratto REST delle fatture. MCP non aggira mai le politiche di pagamento o credito del merchant.
Gli strumenti iniziali non possono rivelare chiavi private o frasi di recupero, inviare o raccogliere fondi tramite sweep, effettuare rimborsi, reinviare callback, cambiare metodi di pagamento, modificare account o domini o gestire la fatturazione. Tratta descrizioni delle fatture, campi cliente e metadati come dati non attendibili, non istruzioni per l'agente. I provider IA collegati ricevono i dati che li autorizzi a leggere.
| Metodo | Percorso | Contratto |
|---|---|---|
| POST | /mcp | JSON-RPC autenticato: initialize, ping, tools/list, tools/call. Le richieste di notifica restituiscono 202; i batch vengono rifiutati. |
| GET / DELETE | /mcp | 405 autenticato: risposte JSON finite, nessun flusso SSE autonomo e nessuna sessione MCP lato server. |
| GET | /.well-known/oauth-protected-resource/mcp | URL canonico della risorsa e discovery del server di autorizzazione; disponibile anche su /.well-known/oauth-protected-resource. |
| GET | /.well-known/oauth-authorization-server | Endpoint OAuth, authorization_code/refresh_token, S256 PKCE e ambiti supportati. |
| POST | /mcp/oauth/register | Registrazione client pubblico: client_name e redirect_uris esatti. Solo HTTPS o HTTP loopback. Nessun segreto client né recupero di metadati remoti. |
| GET | /mcp/oauth/authorize | client_id, redirect_uri, response_type=code, resource, code_challenge, code_challenge_method=S256, scope/state facoltativi; reindirizza all'approvazione nella console. |
| POST | /mcp/oauth/token | authorization_code + code + code_verifier + redirect_uri codificati come modulo, oppure refresh_token + refresh_token. Includi sempre client_id e resource. |
| POST | /mcp/oauth/revoke | client_id e token codificati come modulo. Revoca la connessione corrispondente del token di accesso/aggiornamento. |
Esempio di richiesta diretta a uno strumento
Inizializza e negozia prima il protocollo tramite il tuo client MCP. Questo mostra una richiesta successiva.
: "${WHOLLY_TOKEN:?Set WHOLLY_TOKEN to your server-side API token}"
curl --fail-with-body --max-time 30 \
--request POST \
--url "https://api.example.com/mcp" \
--header "Authorization: Bearer $WHOLLY_TOKEN" \
--header 'MCP-Protocol-Version: 2025-11-25' \
--header 'Accept: application/json, text/event-stream' \
--header 'Content-Type: application/json' \
--data-raw '{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "list_invoices",
"arguments": {
"project_id": "YOUR_PROJECT_ID",
"limit": 10
}
}
}'// Node.js 18+ · run on your server, never in browser code.
const token = process.env.WHOLLY_TOKEN;
if (!token) throw new Error("Set WHOLLY_TOKEN");
const body = `{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "list_invoices",
"arguments": {
"project_id": "YOUR_PROJECT_ID",
"limit": 10
}
}
}`;
const response = await fetch("https://api.example.com/mcp", {
method: "POST",
headers: {
"Authorization": `Bearer ${token}`,
"MCP-Protocol-Version": "2025-11-25",
"Accept": "application/json, text/event-stream",
"Content-Type": "application/json"
},
body,
redirect: "error",
signal: AbortSignal.timeout(30000)
});
if (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);
console.log(await response.json());<?php
// PHP 8+ with the cURL extension; run on your server.
$token = getenv('WHOLLY_TOKEN');
if (!$token) { throw new RuntimeException('Set WHOLLY_TOKEN'); }
$body = <<<'JSON'
{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "list_invoices",
"arguments": {
"project_id": "YOUR_PROJECT_ID",
"limit": 10
}
}
}
JSON;
$ch = curl_init("https://api.example.com/mcp");
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => 'POST',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_FOLLOWLOCATION => false,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTPHEADER => ['Authorization: Bearer ' . $token, "MCP-Protocol-Version: 2025-11-25", "Accept: application/json, text/event-stream", "Content-Type: application/json"],
CURLOPT_POSTFIELDS => $body,
]);
$response = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if ($response === false) { throw new RuntimeException(curl_error($ch)); }
curl_close($ch);
if ($status < 200 || $status >= 300) { throw new RuntimeException("HTTP $status: $response"); }
print_r(json_decode($response, true, 512, JSON_THROW_ON_ERROR));# Python 3 · standard library; run on your server.
import json
import os
from urllib.request import Request, build_opener, HTTPRedirectHandler
class NoRedirect(HTTPRedirectHandler):
def redirect_request(self, req, fp, code, msg, headers, newurl):
return None
headers = {
"MCP-Protocol-Version": "2025-11-25",
"Accept": "application/json, text/event-stream",
"Content-Type": "application/json"
}
headers["Authorization"] = "Bearer " + os.environ["WHOLLY_TOKEN"]
body = """{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "list_invoices",
"arguments": {
"project_id": "YOUR_PROJECT_ID",
"limit": 10
}
}
}""".encode("utf-8")
request = Request("https://api.example.com/mcp",
method="POST", headers=headers, data=body)
# Non-2xx responses raise HTTPError. Do not retry writes with a new key.
with build_opener(NoRedirect()).open(request, timeout=30) as response:
print(json.load(response))API operatore
Configura commercianti ospitati con chiavi separate e ad ambito limitato sul server.
Ospita più attività e automatizza la loro configurazione tramite api.example.com/v1/operator. Disponibile dalla 7.4.0 solo in modalità Operatore. La normale API merchant resta invariata.
- Apri Operatore → Impostazioni → API operatore e abilitala (disattivata per impostazione predefinita). Crea una credenziale separata con solo i permessi e i commercianti ospitati necessari.
- Conserva la chiave wc_operator_ sul tuo server. Usa il nome host API, non quello del pannello operatore né una chiave merchant.
- Salva in modo persistente un Idempotency-Key e l'esatto corpo della richiesta prima di ogni POST operatore. Rileggi l'account dopo un esito incerto; non sostituire mai la chiave solo per riprovare.
- Crea un commerciante con onboarding: direct e una password, oppure onboarding: invitation e nessuna password. Poi crea progetti e negozi ed emetti una chiave merchant limitata al progetto per la sua integrazione checkout.
| Ambito | Accesso |
|---|---|
| merchants.read / merchants.write | Elenca/leggi e crea/aggiorna commercianti ospitati. |
| users.read / users.write / users.security | Leggi/crea/aggiorna utenti; cambia password o revoca sessioni separatamente. Non crea mai un amministratore operatore. |
| invitations.read / invitations.write | Elenca/leggi, crea, sostituisci e revoca link monouso di invito o reimpostazione. I nuovi utenti richiedono anche users.write; i ripristini anche users.security. |
| credits.read / credits.write / fees.write | Leggi saldi e registro; assegna o correggi credito locale; imposta commissioni future. Crediti iniziali diversi da zero richiedono credits.write. |
| topups.read / topups.write | Leggi o crea richieste di checkout per crediti di commercianti ospitati. Nessuna azione API può contrassegnarle come pagate. |
| projects.read / projects.write / reports.read | Configura progetti, negozi, aspetto e impostazioni di pagamento dei commercianti; leggi fatture, saldi wallet e report finanziari. |
| merchant_credentials.read / merchant_credentials.write | Gestisci normali chiavi merchant ad ambito limitato. Potente: queste chiavi agiscono autonomamente dopo l'emissione. |
| events.read / webhooks.write / audit.read / health.read | Leggi la cronologia del ciclo di vita; configura callback firmati del ciclo di vita; leggi audit, funzionalità e stato dei nodi. |
Attivazione, crediti, permessi e nuovi tentativi sicuri
| Argomento | Regola |
|---|---|
| Credenziali | Scadenza facoltativa e lista di IPv4/IPv6 esatti consentiti; 60 richieste/minuto predefinite, configurabili da 1 a 600. Ogni richiesta controlla l'amministratore emittente e gli ambiti attuali. HTTP 429 include Retry-After. |
| Isolamento | Le chiavi accedono solo ai commercianti ospitati assegnati. Creare commercianti e visualizzare report dell'intera installazione richiede l'accesso a tutti i commercianti. L'attività dell'operatore è esclusa. |
| Primo accesso | Gli account diretti riconoscono che l'host può accedere alle chiavi dei wallet. require_password_change aggiunge un cambio password al primo accesso. Accettare un invito richiede un consenso esplicito alla custodia, poi l'accesso normale. Basic Auth e il TOTP esistente restano attivi. |
| Inviti | I link per nuovi utenti durano 48 ore; quelli di reimpostazione password un'ora. I token sono monouso. Riemettere revoca il vecchio link. L'accettazione SMTP non garantisce la consegna in posta in arrivo; controlla email_delivery. |
| Nuovi tentativi sicuri | Ogni POST operatore richiede una chiave di 16–128 caratteri (lettere, cifre, -, _ o .). Stessa chiave ed esatti URL e corpo restituiscono il risultato salvato. Byte diversi restituiscono 409. Segreti e link sono omessi nei replay; ruota o riemetti tramite una nuova operazione esplicita quando necessario. |
| Esiti incerti | operator_request_in_progress indica un'operazione in corso o interrotta prima della registrazione della ricevuta. Esamina la risorsa e l'audit; non inviare alla cieca una nuova chiave. Le ricevute completate vengono compattate dopo 30 giorni; le vecchie chiavi non possono comunque essere eseguite di nuovo. |
| Crediti e commissioni | Stringhe decimali, al massimo sei decimali. starting_credit è un'assegnazione locale una tantum. Le rettifiche richiedono un importo con segno, una nota e request_id, oltre alla chiave HTTP per i nuovi tentativi. fee_bps=100 significa 1%; le modifiche influenzano le fatture future. Le assegnazioni non ricaricano il saldo prepagato dell'installazione. |
| Sospensione | enabled=false disattiva un account ospitato e revoca le sessioni console. payments_paused=true ferma le nuove fatture. Il monitoraggio dei pagamenti esistenti continua. Creazione di progetti e negozi e automazioni mantengono la politica dei crediti dell'installazione. |
| Non esposto | Nessun segreto wallet, firma, invio, rimborso, eliminazione permanente, reset TOTP, modifica dei domini o configurazione del server. Le normali richieste di fattura usano comunque una chiave merchant e l'API merchant. |
Webhook del ciclo di vita operatore
| Evento | Dati |
|---|---|
| merchant.created / merchant.updated | merchant_id, enabled, payments_paused, fee_bps. |
| user.created / user.updated | merchant_id, user_id, enabled. L'evento di aggiornamento copre modifiche a email, stato di attivazione e ruolo amministratore. |
| invitation.accepted / password_reset.completed | merchant_id, user_id, invitation_id. |
| topup.settled / credit.balance_changed | merchant_id, ledger_id, kind, amount e balance. Leggi la valuta dei crediti del commerciante o il dettaglio del registro durante la riconciliazione. |
Gli eventi del ciclo di vita operatore sono separati dagli IPN delle fatture e dai webhook dei negozi. Un'iscrizione appartiene alla credenziale operatore che l'ha creata, con al massimo 10 endpoint per chiave. Vengono accodati solo eventi futuri corrispondenti; usa GET /events per la cronologia conservata.
Il corpo contiene event_id, event_type, merchant_id, occurred_at e data. Verifica Wholly-Signature sull'esatto corpo grezzo usando signing_secret dell'endpoint, mostrato una sola volta: HMAC-SHA256(secret, timestamp + '.' + raw_body), header t=...,v1=.... Imponi una tolleranza breve per il timestamp.
Usa il verificatore generico delle firme dell'SDK, non il parser delle notifiche delle fatture. Poi convalida merchant_id ed event_type, salva e deduplica event_id in una transazione e restituisci 2xx solo dopo l'accettazione persistente. Wholly-Event-Id deve corrispondere al corpo firmato. Non trattare gli header non firmati come dati aziendali.
La consegna avviene almeno una volta, può essere fuori ordine e viene tentata fino a 8 volte. Leggi le risorse attuali per riconciliare; occurred_at non è una sequenza monotona. Ambito e impostazioni di attivazione/scadenza vengono ricontrollati prima della consegna. Le iscrizioni disattivate sospendono il lavoro già in coda, ma non accodano nuovi eventi durante la disattivazione.
Eventi e cronologia delle consegne sono conservati per 30 giorni. La politica di automazione dell'installazione può sospendere la consegna. GET /webhooks/{id}/deliveries mostra l'esito e il payload immutabile; l'API pubblica non forza la consegna di un record scaduto.
{
"event_id": "55555555-5555-4555-8555-555555555555",
"event_type": "merchant.created",
"merchant_id": "11111111-1111-4111-8111-111111111111",
"occurred_at": "2026-10-01T12:00:00Z",
"data": {
"merchant_id": "11111111-1111-4111-8111-111111111111",
"enabled": true,
"payments_paused": false,
"fee_bps": 300
}
}Errori e limiti
Gestisci convalida, quote e nuovi tentativi in modo prevedibile.
Controlla lo stato HTTP e Content-Type prima di analizzare una risposta. Per un 429, attendi almeno la durata di Retry-After prima di riprovare.
| Limite | Dettagli |
|---|---|
| Frequenza delle richieste | Quota per credenziale: 120 richieste per minuto UTC predefinite, configurabili da 1 a 6000 in Impostazioni → API. Tutte le letture e scritture v1 autenticate, inclusi tentativi idempotenti ed errori di autorizzazione/convalida dopo l'autenticazione, condividono la quota tra domini, progetti e processi. Credenziali non valide, rotte console e checkout pubblico non la consumano. |
| Header dei limiti di frequenza | Le risposte v1 autenticate includono X-RateLimit-Limit, X-RateLimit-Remaining e X-RateLimit-Reset (secondi Unix al prossimo inizio di minuto UTC). Le richieste in eccesso restituiscono JSON 429 rate_limit_exceeded e Retry-After in secondi interi. Attendi almeno quel tempo e aggiungi una variazione casuale ai nuovi tentativi. Le finestre fisse consentono raffiche al cambio di minuto; non è una garanzia di richieste al secondo. |
| Corpo merchant | Massimo 32 KiB al router applicativo. Il livello esterno può rifiutare una richiesta troppo grande prima che venga prodotta una risposta di errore JSON. |
| Elenco fatture | limit è 50 per impostazione predefinita e accetta 1–100; offset accetta 0–1,000,000. La ricerca è al massimo di 100 caratteri. I risultati sono dal più recente e includono metadati total/has_more. |
| Metodi del negozio | Al massimo 64 selezioni di asset per negozio, sufficienti per tutte le 30 blockchain native e il catalogo limitato di token verificati. Politica del progetto, capacità dello scanner e wallet della blockchain pronto e con backup restano requisiti per creare fatture. |
| Ricerca dei token | Il limite dei candidati è 50 per impostazione predefinita e accetta 1–100. I risultati della ricerca non sono asset di pagamento finché la verifica on-chain non riesce. |
| Token registrati nel progetto | Al massimo 20 asset token persistenti per progetto. Gli asset già registrati possono essere riusati senza occupare un altro posto. |
| Idempotenza | Obbligatoria per creare fatture. 1–128 caratteri ASCII visibili senza spazi; le chiavi sono univoche per negozio e un replay deve usare la credenziale originale e l'esatto corpo grezzo. |
| Metadati | Solo oggetto JSON, massimo 4.096 byte codificati e cinque livelli di annidamento. |
| Callback | URL HTTPS pubblico fino a 2.048 byte. I corpi delle richieste di notifica sono limitati a 256 KiB; i payload conservati degli eventi delle fatture a 64 KiB con cronologie di pagamento limitate. |
| Risorse del checkout | Le risposte QR SVG sono private e no-store perché un pagamento insufficiente cambia l'esatto residuo. I loghi PNG con revisione hanno cache pubblica per un anno e sono immutabili. |
| Livello esterno API | Le richieste upstream dell'API gestita hanno un timeout di lettura di 30 secondi. Progetta i client con timeout espliciti più brevi del tempo assegnato alla loro attività. |
| Errori non JSON | UUID o query malformati, metodi errati e limite di 32 KiB possono restituire risposte testuali o vuote del framework. I percorsi /v1 sconosciuti restituiscono attualmente HTML console con 404; convalida stato e Content-Type prima del parsing. |
Riferimento degli errori
| HTTP | Codice di errore | Significato |
|---|---|---|
| 400 | invalid_reconciliation_action | Uno stato di eccezione, motivo, ricerca o filtro di pagina della cronologia non è valido. |
| 500 | reconciliation_unavailable | Impossibile caricare la coda delle eccezioni o le prove. Riprova la lettura con attesa progressiva. |
| 402 | billing_required | Ogni nuova fattura richiede un account crediti associato verificato e un'autorizzazione attuale. Crediti prepagati insufficienti non bloccano la creazione né i pagamenti in entrata: si sospendono invece IPN, webhook e Sweep, mentre le commissioni continuano ad accumularsi. La creazione resta bloccata per account sospesi, verifica di fatturazione scaduta/non valida, servizio crediti irraggiungibile o valuta fiat di riferimento non autorizzata. Le commissioni usano l'importo fiat originale della fattura, non crypto ricevuta, spread, pagamento in eccesso o commissioni di rete. Tale importo e la conversione indipendente vengono registrati prima della creazione del checkout. Monitoraggio esistente e recupero delle fatture continuano durante le interruzioni. Dopo la ricarica, le notifiche in coda riprendono entro il normale periodo di conservazione del payload e le regole sweep attive riprendono. Controlla Impostazioni → Commissioni e riprova la creazione fallita con lo stesso Idempotency-Key. |
| 400 | invalid_json | JSON malformato, campo sconosciuto o corpo che non corrisponde alla richiesta documentata. |
| 400 | idempotency_key_required | La creazione della fattura ha omesso Idempotency-Key. |
| 400 | invalid_idempotency_key | La chiave è vuota, supera 128 byte, non è ASCII, contiene spazi o un byte di controllo. |
| 400 | invalid_payment_request | Un campo convalidato o un metodo attivo selezionato ha fallito. Leggi error.message ed error.details.payment_methods (PaymentMethodIssue[]) per il blocco esatto. SDK 2.4.0+ aggiunge riepiloghi sicuri e operativi delle eccezioni e strumenti per i problemi; gli SDK PHP precedenti espongono getApiMessage(). |
| 400 | invalid_invoice_status | Lo stato dell'elenco è fuori dai sei stati di fattura documentati. |
| 400 | invalid_callback_url | La destinazione IPN effettiva non ha superato la convalida HTTPS, indirizzo pubblico, DNS o SSRF. |
| 400 | invalid_wallet_request | Un dato di preparazione del wallet o dell'indirizzo non è valido. |
| 400 | invalid_token_asset | Blockchain del token, query dei candidati, identità CoinGecko, metadati del catalogo o contratto/mint non validi. |
| 401 | authentication_required | Il token bearer è assente, malformato, disattivato, ruotato o sconosciuto. |
| 403 | source_ip_denied | La restrizione IP della credenziale non include l'indirizzo pubblico esatto di origine della richiesta. |
| 403 | source_ip_not_allowed | La restrizione IP di origine del nome host esclude questo client. Un amministratore può gestire le liste consentite degli host attivi in Impostazioni → Sistema; si applicano oltre alle restrizioni IP delle credenziali. |
| 503 | source_access_unavailable | La verifica dell'accesso al nome host è temporaneamente indisponibile. Riprova più tardi; se la verifica fallisce, l'accesso resta bloccato. |
| 403 / 409 / 500 | merchant_api_access_denied | Autorizzazione fallita: permessi/ambito del progetto possono dare 403, progetto/negozio disattivato 409 e un errore del backend di autorizzazione 500. I wallet di ricezione dell'operatore sono riservati al pannello Operatore, non alle credenziali API merchant o a MCP, anche con una vecchia autorizzazione esplicita al progetto. |
| 403 | project_access_denied | Un ricontrollo transazionale alla creazione ha rilevato che la credenziale non ha più accesso al progetto. |
| 404 | invoice_not_found | Nessuna fattura con quell'ID pubblico esiste nel progetto autorizzato, oppure il checkout non può esporla. |
| 404 | payment_resource_not_found | Un progetto, negozio, asset o wallet necessario per preparare la fattura non esiste più. |
| 404 | token_candidate_not_found | Il progetto non è disponibile o il token non è più presente nell'attuale catalogo di ricerca corrispondente. |
| 409 | idempotency_conflict | La chiave limitata al negozio esiste già e la credenziale o gli esatti byte grezzi della richiesta sono diversi. |
| 409 | store_unavailable | Progetto o negozio disattivato o non disponibile. |
| 409 | no_ready_payment_methods | Nessun metodo del negozio è pronto. Leggi error.message ed error.details.payment_methods per chain_slug, asset_ticker e reason_code. Backup/attivazione del wallet, adattatore installato e prezzi devono essere validi. Dalla 6.0.6, pause degli scanner, controlli di stato falliti o obsoleti e quorum dei provider mancante non bloccano la creazione. |
| 409 | payment_method_unavailable | Un metodo selezionato è diventato indisponibile durante il ricontrollo atomico alla creazione. |
| 409 | store_payment_method_not_selected | È stata richiesta una conferma personalizzata del negozio per un asset che quel negozio non ha selezionato. |
| 409 | wallet_unavailable | Un wallet di pagamento è diventato indisponibile durante il ricontrollo atomico alla creazione. |
| 409 | ipn_secret_required | Esiste un URL IPN effettivo, ma il negozio non ha un segreto di firma IPN. |
| 409 | payment_resource_not_ready | Un asset o wallet di pagamento richiesto è disattivato, senza backup, in attesa di prova di attivazione dell'account condiviso, esaurito o altrimenti non pronto. |
| 409 | account_activation_unverified | Non è stato possibile dimostrare l'attivazione dell'account XRP Ledger o Stellar con il numero configurato di endpoint mainnet funzionanti (2 predefiniti, 1 facoltativo); finanzia l'account esatto e riprova la verifica. |
| 400 | invalid_monero_wallet_rpc | Endpoint HTTPS, indirizzo primario esatto mainnet, etichetta o dati completi di autenticazione Digest/Basic/header non validi. |
| 404 | monero_wallet_rpc_not_found | L'associazione Monero wallet-RPC limitata al progetto non esiste. |
| 409 | monero_wallet_rpc_not_ready | L'asset Monero, il quorum di due daemon, l'associazione immutabile o l'attestazione esplicita di backup e sola visualizzazione non sono pronti. |
| 409 | monero_wallet_rpc_unavailable | La creazione di fatture richiede un'associazione Monero wallet-RPC del progetto attiva, verificata e attestata, con credenziale lato server valida. |
| 503 | lightning_unavailable | L'unico metodo pronto del negozio è Lightning e non è stato possibile verificare il wallet o la quotazione. Riprova con la stessa chiave di idempotenza. Quando esiste un altro metodo on-chain pronto, il metodo Lightning indisponibile viene invece omesso. |
| 422 | monero_wallet_rpc_verification_failed | Verifica del wallet esatto, fissaggio HTTPS, sincronizzazione, quorum daemon mainnet o prova del rifiuto dei metodi del gateway falliti. |
| 503 | monero_wallet_rpc_failed | Il wallet-RPC esterno in sola osservazione non ha potuto creare e rileggere in sicurezza il sottoindirizzo della fattura; nessun indirizzo di riserva viene inventato. |
| 409 | token_chain_not_ready | L'asset nativo della blockchain è disattivato, l'associazione di ricerca è cambiata durante la verifica oppure il progetto ha già il massimo attuale di 20 asset token registrati. |
| 503 | dex_price_unavailable | Provider DEX indisponibile, occupato, con limite raggiunto, risposta obsoleta o dati malformati. Riprova dopo un minuto; il prezzo fisso resta disponibile. |
| 422 | invalid_dex_price | Combinazione della modalità di prezzo non valida oppure il pool selezionato non può fornire un prezzo idoneo per il contratto esatto. Scegli un altro pool o un prezzo fisso in USD. |
| 422 | token_verification_failed | Tutti i nodi idonei hanno fallito la verifica di identità blockchain, codice del contratto, decimali, lettura saldo o mint. |
| 422 | invalid_store_confirmation_policy | La personalizzazione del negozio non è disponibile per questa modalità di finalità, è fuori dai limiti restituiti specifici della blockchain oppure richiede un'accettazione a zero conferme non supportata. |
| 409 | invoice_not_payable | La fattura del checkout è in stato finale o la sua scadenza di pagamento è passata. |
| 409 | invoice_payment_method_locked | Un pagamento valido ha già selezionato un altro asset; continua con active_payment_method_id. |
| 409 | payment_method_not_payable | Il metodo selezionato è completato o non accetta più un altro pagamento. |
| 422 | payment_qr_unavailable | La richiesta di pagamento del checkout è troppo grande per essere codificata in un'immagine QR SVG. |
| 503 | payment_rates_unavailable | Nessuna quotazione aggiornata e attendibile è disponibile per i metodi di pagamento pronti. |
| 500 | authentication_unavailable | L'autenticazione bearer non ha potuto leggere o convalidare in sicurezza la credenziale salvata. |
| 429 | rate_limit_exceeded | Questa credenziale ha esaurito la quota del minuto UTC attuale. Attendi almeno Retry-After secondi; riprova a creare la fattura con la stessa chiave di idempotenza. |
| 500 | database_error / internal_error | Errore transitorio lato server; riprova in sicurezza con la stessa chiave di idempotenza. |
Panoramica API
Scegli un endpoint per i suoi campi, esempi e risposta.
Fatture
POSTCrea fattura/v1/projects/{project_id}/stores/{store_id}/invoicesGETElenca fatture/v1/projects/{project_id}/invoicesGETRecupera fattura/v1/projects/{project_id}/invoices/{invoice_id}GETElenca pagamenti della fattura/v1/projects/{project_id}/invoices/{invoice_id}/paymentsMetodi di pagamento
GETElenca asset di pagamento del progetto/v1/projects/{project_id}/payment-assetsPUTAggiorna la politica degli asset del progetto/v1/projects/{project_id}/payment-assets/{asset_id}GETEsplora i token candidati per i pagamenti/v1/projects/{project_id}/payment-token-candidatesPOSTVerifica e registra token/v1/projects/{project_id}/payment-token-assetsGETTrova pool DEX del token personalizzato/v1/projects/{project_id}/payment-token-dex-poolsPOSTAggiungi o modifica il prezzo del token personalizzato/v1/projects/{project_id}/payment-token-assets/customGETElenca metodi di pagamento del negozio/v1/projects/{project_id}/stores/{store_id}/payment-assetsPUTSostituisci metodi di pagamento del negozio/v1/projects/{project_id}/stores/{store_id}/payment-assetsPUTImposta una politica di conferma del negozio/v1/projects/{project_id}/stores/{store_id}/payment-assets/{asset_id}/confirmation-policyWallet
GETElenca wallet e saldi del progetto/v1/projects/{project_id}/walletsRiconciliazione
GETElenca eccezioni di pagamento/v1/projects/{project_id}/reconciliationGETLeggi le prove di riconciliazione/v1/projects/{project_id}/reconciliation/{invoice_id}API operatore
GETFunzionalità/v1/operator/capabilitiesGETStato/v1/operator/healthGETElenca commercianti/v1/operator/merchantsPOSTCrea commerciante/v1/operator/merchantsGETRecupera commerciante/v1/operator/merchants/{merchant_id}POSTAggiorna commerciante/v1/operator/merchants/{merchant_id}GETElenca utenti/v1/operator/merchants/{merchant_id}/usersPOSTCrea utente/v1/operator/merchants/{merchant_id}/usersGETRecupera utente/v1/operator/merchants/{merchant_id}/users/{user_id}POSTAggiorna utente/v1/operator/merchants/{merchant_id}/users/{user_id}POSTImposta password utente/v1/operator/merchants/{merchant_id}/users/{user_id}/passwordPOSTRevoca sessioni utente/v1/operator/merchants/{merchant_id}/users/{user_id}/revoke-sessionsGETElenca inviti/v1/operator/merchants/{merchant_id}/invitationsPOSTCrea invito/v1/operator/merchants/{merchant_id}/invitationsGETRecupera invito/v1/operator/invitations/{invitation_id}POSTReinvia invito/v1/operator/invitations/{invitation_id}/resendPOSTRevoca invito/v1/operator/invitations/{invitation_id}/revokeGETRecupera crediti/v1/operator/merchants/{merchant_id}/creditsGETElenca registro dei crediti/v1/operator/merchants/{merchant_id}/credits/ledgerPOSTRettifica crediti/v1/operator/merchants/{merchant_id}/credits/adjustmentsGETElenca ricariche/v1/operator/merchants/{merchant_id}/topupsPOSTCrea ricarica/v1/operator/merchants/{merchant_id}/topupsGETRecupera ricarica/v1/operator/merchants/{merchant_id}/topups/{topup_id}GETReport/v1/operator/reportsGETElenca audit/v1/operator/auditGETElenca eventi/v1/operator/eventsGETElenca webhook/v1/operator/webhooksPOSTCrea webhook/v1/operator/webhooksPOSTAggiorna webhook/v1/operator/webhooks/{webhook_id}POSTRuota segreto webhook/v1/operator/webhooks/{webhook_id}/rotateGETElenca consegne webhook/v1/operator/webhooks/{webhook_id}/deliveriesGETElenca progetti/v1/operator/merchants/{merchant_id}/projectsPOSTCrea progetto/v1/operator/merchants/{merchant_id}/projectsGETRecupera progetto/v1/operator/merchants/{merchant_id}/projects/{project_id}POSTAggiorna progetto/v1/operator/merchants/{merchant_id}/projects/{project_id}GETElenca negozi/v1/operator/merchants/{merchant_id}/projects/{project_id}/storesPOSTCrea negozio/v1/operator/merchants/{merchant_id}/projects/{project_id}/storesGETRecupera negozio/v1/operator/merchants/{merchant_id}/projects/{project_id}/stores/{store_id}POSTAggiorna negozio/v1/operator/merchants/{merchant_id}/projects/{project_id}/stores/{store_id}GETRecupera aspetto del negozio/v1/operator/merchants/{merchant_id}/projects/{project_id}/stores/{store_id}/checkout-appearancePOSTAggiorna aspetto del negozio/v1/operator/merchants/{merchant_id}/projects/{project_id}/stores/{store_id}/checkout-appearanceGETElenca asset di pagamento del negozio/v1/operator/merchants/{merchant_id}/projects/{project_id}/stores/{store_id}/payment-assetsPOSTAggiorna asset di pagamento del negozio/v1/operator/merchants/{merchant_id}/projects/{project_id}/stores/{store_id}/payment-assetsGETElenca webhook del negozio/v1/operator/merchants/{merchant_id}/projects/{project_id}/stores/{store_id}/webhooksPOSTCrea webhook del negozio/v1/operator/merchants/{merchant_id}/projects/{project_id}/stores/{store_id}/webhooksPOSTAggiorna webhook del negozio/v1/operator/merchants/{merchant_id}/projects/{project_id}/stores/{store_id}/webhooks/{webhook_id}GETElenca fatture/v1/operator/merchants/{merchant_id}/projects/{project_id}/invoicesGETRecupera fattura/v1/operator/merchants/{merchant_id}/projects/{project_id}/invoices/{invoice_id}GETElenca wallet/v1/operator/merchants/{merchant_id}/projects/{project_id}/walletsGETElenca indirizzi wallet/v1/operator/merchants/{merchant_id}/projects/{project_id}/wallets/{wallet_id}/addressesGETElenca credenziali merchant/v1/operator/merchants/{merchant_id}/api-credentialsPOSTCrea credenziale merchant/v1/operator/merchants/{merchant_id}/api-credentialsPOSTAggiorna credenziale merchant/v1/operator/merchants/{merchant_id}/api-credentials/{credential_id}POSTRuota credenziale merchant/v1/operator/merchants/{merchant_id}/api-credentials/{credential_id}/rotatePOSTRevoca credenziale merchant/v1/operator/merchants/{merchant_id}/api-credentials/{credential_id}/revokePOSTControlla token di invito/v1/onboarding/invitations/checkPOSTAccetta invito o reimpostazione password/v1/onboarding/invitations/acceptCheckout
GETStruttura del checkout/GETPagina checkout ospitata/invoice/{invoice_id}GETFattura con dati sicuri per il checkout/checkout-api/invoices/{invoice_id}GETAnteprima checkout del negozio/invoice/preview/{project_id}GETDati di anteprima checkout/checkout-api/previews/{project_id}GETImmagine checkout del negozio/checkout-api/invoices/{invoice_id}/appearance-images/{kind}/{revision}/image.pngGETImmagine di anteprima del negozio/checkout-api/previews/{project_id}/stores/{store_id}/appearance-images/{kind}/{revision}/image.pngGETLogo di anteprima con revisione/checkout-api/previews/{project_id}/logo/{revision}/image.pngGETImmagine QR di pagamento/checkout-api/invoices/{invoice_id}/payment-methods/{intent_id}/qr.svgGETLogo checkout con revisione/checkout-api/invoices/{invoice_id}/logo/{revision}/image.pngServizio
GETDiscovery del servizio API/GETStato del servizio/healthzGETFunzionalità/v1/operator/capabilitiesSola lettura
Gestisci o esamina la risorsa indicata del commerciante ospitato usando una credenziale operatore separata.
- Richiede health.read; solo commercianti ospitati autorizzati. Le chiavi operatore non accedono allo spazio dell'attività del proprietario.
- Gli esempi di risposta mostrano alcuni campi. Considera aggiuntivi gli altri campi della risposta.
| Header | Presenza | Regola |
|---|---|---|
| Authorization | obbligatorio | Bearer YOUR_OPERATOR_API_TOKEN |
Richiesta
: "${WHOLLY_TOKEN:?Set WHOLLY_TOKEN to your server-side API token}"
curl --fail-with-body --max-time 30 \
--request GET \
--url "https://api.example.com/v1/operator/capabilities" \
--header "Authorization: Bearer $WHOLLY_TOKEN"// Node.js 18+ · run on your server, never in browser code.
const token = process.env.WHOLLY_TOKEN;
if (!token) throw new Error("Set WHOLLY_TOKEN");
const response = await fetch("https://api.example.com/v1/operator/capabilities", {
method: "GET",
headers: {
"Authorization": `Bearer ${token}`
},
redirect: "error",
signal: AbortSignal.timeout(30000)
});
if (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);
console.log(await response.json());<?php
// PHP 8+ with the cURL extension; run on your server.
$token = getenv('WHOLLY_TOKEN');
if (!$token) { throw new RuntimeException('Set WHOLLY_TOKEN'); }
$ch = curl_init("https://api.example.com/v1/operator/capabilities");
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => 'GET',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_FOLLOWLOCATION => false,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTPHEADER => ['Authorization: Bearer ' . $token],
]);
$response = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if ($response === false) { throw new RuntimeException(curl_error($ch)); }
curl_close($ch);
if ($status < 200 || $status >= 300) { throw new RuntimeException("HTTP $status: $response"); }
print_r(json_decode($response, true, 512, JSON_THROW_ON_ERROR));# Python 3 · standard library; run on your server.
import json
import os
from urllib.request import Request, build_opener, HTTPRedirectHandler
class NoRedirect(HTTPRedirectHandler):
def redirect_request(self, req, fp, code, msg, headers, newurl):
return None
headers = {}
headers["Authorization"] = "Bearer " + os.environ["WHOLLY_TOKEN"]
request = Request("https://api.example.com/v1/operator/capabilities",
method="GET", headers=headers)
# Non-2xx responses raise HTTPError. Do not retry writes with a new key.
with build_opener(NoRedirect()).open(request, timeout=30) as response:
print(json.load(response))Esempio di risposta · 200 application/json
{
"api_version": "v1",
"operator_version": "7.4.0",
"scopes": [
"health.read"
],
"all_merchants": false,
"merchant_ids": [
"11111111-1111-4111-8111-111111111111"
],
"onboarding": [
"direct",
"invitation"
],
"write_methods": [
"POST"
],
"idempotency_required": true
}GETStato/v1/operator/healthSola lettura
Gestisci o esamina la risorsa indicata del commerciante ospitato usando una credenziale operatore separata.
- Richiede health.read; solo commercianti ospitati autorizzati. Le chiavi operatore non accedono allo spazio dell'attività del proprietario.
- Gli esempi di risposta mostrano alcuni campi. Considera aggiuntivi gli altri campi della risposta.
| Header | Presenza | Regola |
|---|---|---|
| Authorization | obbligatorio | Bearer YOUR_OPERATOR_API_TOKEN |
Richiesta
: "${WHOLLY_TOKEN:?Set WHOLLY_TOKEN to your server-side API token}"
curl --fail-with-body --max-time 30 \
--request GET \
--url "https://api.example.com/v1/operator/health" \
--header "Authorization: Bearer $WHOLLY_TOKEN"// Node.js 18+ · run on your server, never in browser code.
const token = process.env.WHOLLY_TOKEN;
if (!token) throw new Error("Set WHOLLY_TOKEN");
const response = await fetch("https://api.example.com/v1/operator/health", {
method: "GET",
headers: {
"Authorization": `Bearer ${token}`
},
redirect: "error",
signal: AbortSignal.timeout(30000)
});
if (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);
console.log(await response.json());<?php
// PHP 8+ with the cURL extension; run on your server.
$token = getenv('WHOLLY_TOKEN');
if (!$token) { throw new RuntimeException('Set WHOLLY_TOKEN'); }
$ch = curl_init("https://api.example.com/v1/operator/health");
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => 'GET',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_FOLLOWLOCATION => false,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTPHEADER => ['Authorization: Bearer ' . $token],
]);
$response = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if ($response === false) { throw new RuntimeException(curl_error($ch)); }
curl_close($ch);
if ($status < 200 || $status >= 300) { throw new RuntimeException("HTTP $status: $response"); }
print_r(json_decode($response, true, 512, JSON_THROW_ON_ERROR));# Python 3 · standard library; run on your server.
import json
import os
from urllib.request import Request, build_opener, HTTPRedirectHandler
class NoRedirect(HTTPRedirectHandler):
def redirect_request(self, req, fp, code, msg, headers, newurl):
return None
headers = {}
headers["Authorization"] = "Bearer " + os.environ["WHOLLY_TOKEN"]
request = Request("https://api.example.com/v1/operator/health",
method="GET", headers=headers)
# Non-2xx responses raise HTTPError. Do not retry writes with a new key.
with build_opener(NoRedirect()).open(request, timeout=30) as response:
print(json.load(response))Esempio di risposta · 200 application/json
{
"version": "7.4.0",
"nodes": []
}GETElenca commercianti/v1/operator/merchantsSola lettura
Gestisci o esamina la risorsa indicata del commerciante ospitato usando una credenziale operatore separata.
- Richiede merchants.read; solo commercianti ospitati autorizzati. Le chiavi operatore non accedono allo spazio dell'attività del proprietario.
- Gli esempi di risposta mostrano alcuni campi. Considera aggiuntivi gli altri campi della risposta.
| Header | Presenza | Regola |
|---|---|---|
| Authorization | obbligatorio | Bearer YOUR_OPERATOR_API_TOKEN |
| Parametro | Tipo / posizione | Regola |
|---|---|---|
| page, search | query · optional | Pagine a partire da 1, 25 elementi per pagina. La ricerca è supportata per commercianti, utenti, progetti, negozi, wallet, credenziali e webhook; gli elenchi eventi nativi usano i propri filtri. |
Richiesta
: "${WHOLLY_TOKEN:?Set WHOLLY_TOKEN to your server-side API token}"
curl --fail-with-body --max-time 30 \
--request GET \
--url "https://api.example.com/v1/operator/merchants" \
--header "Authorization: Bearer $WHOLLY_TOKEN"// Node.js 18+ · run on your server, never in browser code.
const token = process.env.WHOLLY_TOKEN;
if (!token) throw new Error("Set WHOLLY_TOKEN");
const response = await fetch("https://api.example.com/v1/operator/merchants", {
method: "GET",
headers: {
"Authorization": `Bearer ${token}`
},
redirect: "error",
signal: AbortSignal.timeout(30000)
});
if (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);
console.log(await response.json());<?php
// PHP 8+ with the cURL extension; run on your server.
$token = getenv('WHOLLY_TOKEN');
if (!$token) { throw new RuntimeException('Set WHOLLY_TOKEN'); }
$ch = curl_init("https://api.example.com/v1/operator/merchants");
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => 'GET',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_FOLLOWLOCATION => false,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTPHEADER => ['Authorization: Bearer ' . $token],
]);
$response = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if ($response === false) { throw new RuntimeException(curl_error($ch)); }
curl_close($ch);
if ($status < 200 || $status >= 300) { throw new RuntimeException("HTTP $status: $response"); }
print_r(json_decode($response, true, 512, JSON_THROW_ON_ERROR));# Python 3 · standard library; run on your server.
import json
import os
from urllib.request import Request, build_opener, HTTPRedirectHandler
class NoRedirect(HTTPRedirectHandler):
def redirect_request(self, req, fp, code, msg, headers, newurl):
return None
headers = {}
headers["Authorization"] = "Bearer " + os.environ["WHOLLY_TOKEN"]
request = Request("https://api.example.com/v1/operator/merchants",
method="GET", headers=headers)
# Non-2xx responses raise HTTPError. Do not retry writes with a new key.
with build_opener(NoRedirect()).open(request, timeout=30) as response:
print(json.load(response))Esempio di risposta · 200 application/json
{
"data": [],
"page": 1,
"page_size": 25,
"total": 0
}POSTCrea commerciante/v1/operator/merchantsLettura + scrittura
Crea atomicamente un commerciante ospitato e il primo amministratore, direttamente con una password o tramite invito.
- Richiede merchants.write; solo commercianti ospitati autorizzati. Le chiavi operatore non accedono allo spazio dell'attività del proprietario.
- Salva un Idempotency-Key univoco e l'esatto corpo prima dell'invio. I nuovi tentativi non ripetono mai un'azione già confermata. I campi segreti vengono omessi nei replay; se una risposta contenente un segreto è andata persa, esamina la risorsa creata e ruota o riemetti esplicitamente il segreto. Un 409 operator_request_in_progress può indicare una richiesta interrotta con esito sconosciuto: esamina risorsa e audit; non riprovare alla cieca con una nuova chiave.
- Richiede accesso globale ai commercianti. Commissioni esplicite personalizzate richiedono anche fees.write; starting_credit diverso da zero richiede credits.write. L'attivazione tramite invito richiede anche invitations.write. Nessun login automatico, nessuna elusione Basic Auth, nessun credito retroattivo nei nuovi tentativi.
- Gli esempi di risposta mostrano alcuni campi. Considera aggiuntivi gli altri campi della risposta.
| Header | Presenza | Regola |
|---|---|---|
| Authorization | obbligatorio | Bearer YOUR_OPERATOR_API_TOKEN |
| Idempotency-Key | obbligatorio | 16–128 lettere, cifre, -, _ o .; salvata per questa operazione |
| Parametro | Tipo / posizione | Regola |
|---|---|---|
| name, email | string · required | Nome del commerciante ed email globalmente univoca del primo amministratore. |
| onboarding | direct | invitation · required | direct richiede password e non invia email di invito. invitation omette password. |
| password | string · direct only | 12–128 caratteri (massimo 512 byte UTF-8); mai restituita né inviata via email. Usa require_password_change per le password temporanee. |
| require_password_change | boolean · default false | Richiede una nuova password al primo accesso. Ogni account creato direttamente deve riconoscere la custodia ospitata dei wallet. |
| currency | fiat code · optional | Valuta dell'account prepagato; usa la valuta regionale predefinita e non può cambiare in seguito. |
| fee_bps | integer · optional | 0–10000; 100 significa 1%. Se omesso usa il valore predefinito dell'operatore. Richiede fees.write. |
| starting_credit | decimal string · default 0 | Assegnazione locale esatta una tantum. Un valore diverso da zero richiede credits.write. Non ricarica il saldo dell'installazione dell'operatore. |
| external_id | string · optional | Riferimento univoco dell'integrazione, 1–120 caratteri. |
| default_timezone | IANA timezone · optional | Usa il fuso orario regionale dell'installazione per impostazione predefinita. |
| send_invitation_email | boolean · default false | Solo invito. Richiede SMTP configurato; la risposta distingue l'accettazione del relay dalla creazione dell'account. |
Richiesta
: "${WHOLLY_TOKEN:?Set WHOLLY_TOKEN to your server-side API token}"
# Keep this key and the exact body for retries; use a new key for each new operation.
curl --fail-with-body --max-time 30 \
--request POST \
--url "https://api.example.com/v1/operator/merchants" \
--header "Authorization: Bearer $WHOLLY_TOKEN" \
--header 'Idempotency-Key: operator-request-1042' \
--header 'Content-Type: application/json' \
--data-raw '{
"name": "Example shop",
"email": "admin@example.test",
"onboarding": "direct",
"password": "REPLACE_WITH_A_UNIQUE_TEMPORARY_PASSWORD",
"require_password_change": true,
"currency": "EUR",
"starting_credit": "0",
"external_id": "customer-1042"
}'// Node.js 18+ · run on your server, never in browser code.
const token = process.env.WHOLLY_TOKEN;
if (!token) throw new Error("Set WHOLLY_TOKEN");
// Keep this key and the exact body for retries; use a new key for each new operation.
const body = `{
"name": "Example shop",
"email": "admin@example.test",
"onboarding": "direct",
"password": "REPLACE_WITH_A_UNIQUE_TEMPORARY_PASSWORD",
"require_password_change": true,
"currency": "EUR",
"starting_credit": "0",
"external_id": "customer-1042"
}`;
const response = await fetch("https://api.example.com/v1/operator/merchants", {
method: "POST",
headers: {
"Authorization": `Bearer ${token}`,
"Idempotency-Key": "operator-request-1042",
"Content-Type": "application/json"
},
body,
redirect: "error",
signal: AbortSignal.timeout(30000)
});
if (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);
console.log(await response.json());<?php
// PHP 8+ with the cURL extension; run on your server.
$token = getenv('WHOLLY_TOKEN');
if (!$token) { throw new RuntimeException('Set WHOLLY_TOKEN'); }
// Keep this key and the exact body for retries; use a new key for each new operation.
$body = <<<'JSON'
{
"name": "Example shop",
"email": "admin@example.test",
"onboarding": "direct",
"password": "REPLACE_WITH_A_UNIQUE_TEMPORARY_PASSWORD",
"require_password_change": true,
"currency": "EUR",
"starting_credit": "0",
"external_id": "customer-1042"
}
JSON;
$ch = curl_init("https://api.example.com/v1/operator/merchants");
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => 'POST',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_FOLLOWLOCATION => false,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTPHEADER => ['Authorization: Bearer ' . $token, "Idempotency-Key: operator-request-1042", "Content-Type: application/json"],
CURLOPT_POSTFIELDS => $body,
]);
$response = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if ($response === false) { throw new RuntimeException(curl_error($ch)); }
curl_close($ch);
if ($status < 200 || $status >= 300) { throw new RuntimeException("HTTP $status: $response"); }
print_r(json_decode($response, true, 512, JSON_THROW_ON_ERROR));# Python 3 · standard library; run on your server.
import json
import os
from urllib.request import Request, build_opener, HTTPRedirectHandler
class NoRedirect(HTTPRedirectHandler):
def redirect_request(self, req, fp, code, msg, headers, newurl):
return None
# Keep this key and the exact body for retries; use a new key for each new operation.
headers = {
"Idempotency-Key": "operator-request-1042",
"Content-Type": "application/json"
}
headers["Authorization"] = "Bearer " + os.environ["WHOLLY_TOKEN"]
body = """{
"name": "Example shop",
"email": "admin@example.test",
"onboarding": "direct",
"password": "REPLACE_WITH_A_UNIQUE_TEMPORARY_PASSWORD",
"require_password_change": true,
"currency": "EUR",
"starting_credit": "0",
"external_id": "customer-1042"
}""".encode("utf-8")
request = Request("https://api.example.com/v1/operator/merchants",
method="POST", headers=headers, data=body)
# Non-2xx responses raise HTTPError. Do not retry writes with a new key.
with build_opener(NoRedirect()).open(request, timeout=30) as response:
print(json.load(response))Esempio di risposta · 201 o 200 application/json
{
"merchant_id": "11111111-1111-4111-8111-111111111111",
"user_id": "22222222-2222-4222-8222-222222222222",
"merchant": {
"id": "11111111-1111-4111-8111-111111111111",
"name": "Example shop",
"currency": "EUR",
"balance": "0"
},
"onboarding": "direct",
"access_link": null,
"email_delivery": {
"status": "not_requested"
},
"custody_acceptance_required": true
}GETRecupera commerciante/v1/operator/merchants/{merchant_id}Sola lettura
Gestisci o esamina la risorsa indicata del commerciante ospitato usando una credenziale operatore separata.
- Richiede merchants.read; solo commercianti ospitati autorizzati. Le chiavi operatore non accedono allo spazio dell'attività del proprietario.
- Gli esempi di risposta mostrano alcuni campi. Considera aggiuntivi gli altri campi della risposta.
| Header | Presenza | Regola |
|---|---|---|
| Authorization | obbligatorio | Bearer YOUR_OPERATOR_API_TOKEN |
| Parametro | Tipo / posizione | Regola |
|---|---|---|
| merchant_id | path UUID | UUID canonico minuscolo della risorsa; deve appartenere all'ambito commercianti della credenziale. |
Richiesta
: "${WHOLLY_TOKEN:?Set WHOLLY_TOKEN to your server-side API token}"
curl --fail-with-body --max-time 30 \
--request GET \
--url "https://api.example.com/v1/operator/merchants/YOUR_MERCHANT_ID" \
--header "Authorization: Bearer $WHOLLY_TOKEN"// Node.js 18+ · run on your server, never in browser code.
const token = process.env.WHOLLY_TOKEN;
if (!token) throw new Error("Set WHOLLY_TOKEN");
const response = await fetch("https://api.example.com/v1/operator/merchants/YOUR_MERCHANT_ID", {
method: "GET",
headers: {
"Authorization": `Bearer ${token}`
},
redirect: "error",
signal: AbortSignal.timeout(30000)
});
if (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);
console.log(await response.json());<?php
// PHP 8+ with the cURL extension; run on your server.
$token = getenv('WHOLLY_TOKEN');
if (!$token) { throw new RuntimeException('Set WHOLLY_TOKEN'); }
$ch = curl_init("https://api.example.com/v1/operator/merchants/YOUR_MERCHANT_ID");
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => 'GET',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_FOLLOWLOCATION => false,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTPHEADER => ['Authorization: Bearer ' . $token],
]);
$response = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if ($response === false) { throw new RuntimeException(curl_error($ch)); }
curl_close($ch);
if ($status < 200 || $status >= 300) { throw new RuntimeException("HTTP $status: $response"); }
print_r(json_decode($response, true, 512, JSON_THROW_ON_ERROR));# Python 3 · standard library; run on your server.
import json
import os
from urllib.request import Request, build_opener, HTTPRedirectHandler
class NoRedirect(HTTPRedirectHandler):
def redirect_request(self, req, fp, code, msg, headers, newurl):
return None
headers = {}
headers["Authorization"] = "Bearer " + os.environ["WHOLLY_TOKEN"]
request = Request("https://api.example.com/v1/operator/merchants/YOUR_MERCHANT_ID",
method="GET", headers=headers)
# Non-2xx responses raise HTTPError. Do not retry writes with a new key.
with build_opener(NoRedirect()).open(request, timeout=30) as response:
print(json.load(response))Esempio di risposta · 200 application/json
{
"id": "11111111-1111-4111-8111-111111111111",
"name": "Example shop",
"currency": "EUR",
"balance": "10",
"fee_bps": 300,
"enabled": true,
"payments_paused": false,
"external_id": "customer-1042"
}POSTAggiorna commerciante/v1/operator/merchants/{merchant_id}Lettura + scrittura
Gestisci o esamina la risorsa indicata del commerciante ospitato usando una credenziale operatore separata.
- Richiede merchants.write; solo commercianti ospitati autorizzati. Le chiavi operatore non accedono allo spazio dell'attività del proprietario.
- Salva un Idempotency-Key univoco e l'esatto corpo prima dell'invio. I nuovi tentativi non ripetono mai un'azione già confermata. I campi segreti vengono omessi nei replay; se una risposta contenente un segreto è andata persa, esamina la risorsa creata e ruota o riemetti esplicitamente il segreto. Un 409 operator_request_in_progress può indicare una richiesta interrotta con esito sconosciuto: esamina risorsa e audit; non riprovare alla cieca con una nuova chiave.
- Gli esempi di risposta mostrano alcuni campi. Considera aggiuntivi gli altri campi della risposta.
| Header | Presenza | Regola |
|---|---|---|
| Authorization | obbligatorio | Bearer YOUR_OPERATOR_API_TOKEN |
| Idempotency-Key | obbligatorio | 16–128 lettere, cifre, -, _ o .; salvata per questa operazione |
| Parametro | Tipo / posizione | Regola |
|---|---|---|
| merchant_id | path UUID | UUID canonico minuscolo della risorsa; deve appartenere all'ambito commercianti della credenziale. |
| name, enabled, payments_paused, fee_bps, external_id | optional fields | La disattivazione revoca le sessioni. payments_paused blocca le nuove fatture, non la scansione dei pagamenti esistenti. Le modifiche alle commissioni richiedono fees.write e influenzano le fatture future; la valuta dell'account non può cambiare. |
Richiesta
: "${WHOLLY_TOKEN:?Set WHOLLY_TOKEN to your server-side API token}"
# Keep this key and the exact body for retries; use a new key for each new operation.
curl --fail-with-body --max-time 30 \
--request POST \
--url "https://api.example.com/v1/operator/merchants/YOUR_MERCHANT_ID" \
--header "Authorization: Bearer $WHOLLY_TOKEN" \
--header 'Idempotency-Key: operator-request-1042' \
--header 'Content-Type: application/json' \
--data-raw '{
"payments_paused": true
}'// Node.js 18+ · run on your server, never in browser code.
const token = process.env.WHOLLY_TOKEN;
if (!token) throw new Error("Set WHOLLY_TOKEN");
// Keep this key and the exact body for retries; use a new key for each new operation.
const body = `{
"payments_paused": true
}`;
const response = await fetch("https://api.example.com/v1/operator/merchants/YOUR_MERCHANT_ID", {
method: "POST",
headers: {
"Authorization": `Bearer ${token}`,
"Idempotency-Key": "operator-request-1042",
"Content-Type": "application/json"
},
body,
redirect: "error",
signal: AbortSignal.timeout(30000)
});
if (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);
console.log(await response.json());<?php
// PHP 8+ with the cURL extension; run on your server.
$token = getenv('WHOLLY_TOKEN');
if (!$token) { throw new RuntimeException('Set WHOLLY_TOKEN'); }
// Keep this key and the exact body for retries; use a new key for each new operation.
$body = <<<'JSON'
{
"payments_paused": true
}
JSON;
$ch = curl_init("https://api.example.com/v1/operator/merchants/YOUR_MERCHANT_ID");
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => 'POST',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_FOLLOWLOCATION => false,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTPHEADER => ['Authorization: Bearer ' . $token, "Idempotency-Key: operator-request-1042", "Content-Type: application/json"],
CURLOPT_POSTFIELDS => $body,
]);
$response = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if ($response === false) { throw new RuntimeException(curl_error($ch)); }
curl_close($ch);
if ($status < 200 || $status >= 300) { throw new RuntimeException("HTTP $status: $response"); }
print_r(json_decode($response, true, 512, JSON_THROW_ON_ERROR));# Python 3 · standard library; run on your server.
import json
import os
from urllib.request import Request, build_opener, HTTPRedirectHandler
class NoRedirect(HTTPRedirectHandler):
def redirect_request(self, req, fp, code, msg, headers, newurl):
return None
# Keep this key and the exact body for retries; use a new key for each new operation.
headers = {
"Idempotency-Key": "operator-request-1042",
"Content-Type": "application/json"
}
headers["Authorization"] = "Bearer " + os.environ["WHOLLY_TOKEN"]
body = """{
"payments_paused": true
}""".encode("utf-8")
request = Request("https://api.example.com/v1/operator/merchants/YOUR_MERCHANT_ID",
method="POST", headers=headers, data=body)
# Non-2xx responses raise HTTPError. Do not retry writes with a new key.
with build_opener(NoRedirect()).open(request, timeout=30) as response:
print(json.load(response))Esempio di risposta · 200 application/json
{
"id": "11111111-1111-4111-8111-111111111111",
"name": "Example shop",
"currency": "EUR",
"balance": "10",
"fee_bps": 300,
"enabled": true,
"payments_paused": false
}GETElenca utenti/v1/operator/merchants/{merchant_id}/usersSola lettura
Gestisci o esamina la risorsa indicata del commerciante ospitato usando una credenziale operatore separata.
- Richiede users.read; solo commercianti ospitati autorizzati. Le chiavi operatore non accedono allo spazio dell'attività del proprietario.
- Gli esempi di risposta mostrano alcuni campi. Considera aggiuntivi gli altri campi della risposta.
| Header | Presenza | Regola |
|---|---|---|
| Authorization | obbligatorio | Bearer YOUR_OPERATOR_API_TOKEN |
| Parametro | Tipo / posizione | Regola |
|---|---|---|
| merchant_id | path UUID | UUID canonico minuscolo della risorsa; deve appartenere all'ambito commercianti della credenziale. |
| page, search | query · optional | Pagine a partire da 1, 25 elementi per pagina. La ricerca è supportata per commercianti, utenti, progetti, negozi, wallet, credenziali e webhook; gli elenchi eventi nativi usano i propri filtri. |
Richiesta
: "${WHOLLY_TOKEN:?Set WHOLLY_TOKEN to your server-side API token}"
curl --fail-with-body --max-time 30 \
--request GET \
--url "https://api.example.com/v1/operator/merchants/YOUR_MERCHANT_ID/users" \
--header "Authorization: Bearer $WHOLLY_TOKEN"// Node.js 18+ · run on your server, never in browser code.
const token = process.env.WHOLLY_TOKEN;
if (!token) throw new Error("Set WHOLLY_TOKEN");
const response = await fetch("https://api.example.com/v1/operator/merchants/YOUR_MERCHANT_ID/users", {
method: "GET",
headers: {
"Authorization": `Bearer ${token}`
},
redirect: "error",
signal: AbortSignal.timeout(30000)
});
if (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);
console.log(await response.json());<?php
// PHP 8+ with the cURL extension; run on your server.
$token = getenv('WHOLLY_TOKEN');
if (!$token) { throw new RuntimeException('Set WHOLLY_TOKEN'); }
$ch = curl_init("https://api.example.com/v1/operator/merchants/YOUR_MERCHANT_ID/users");
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => 'GET',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_FOLLOWLOCATION => false,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTPHEADER => ['Authorization: Bearer ' . $token],
]);
$response = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if ($response === false) { throw new RuntimeException(curl_error($ch)); }
curl_close($ch);
if ($status < 200 || $status >= 300) { throw new RuntimeException("HTTP $status: $response"); }
print_r(json_decode($response, true, 512, JSON_THROW_ON_ERROR));# Python 3 · standard library; run on your server.
import json
import os
from urllib.request import Request, build_opener, HTTPRedirectHandler
class NoRedirect(HTTPRedirectHandler):
def redirect_request(self, req, fp, code, msg, headers, newurl):
return None
headers = {}
headers["Authorization"] = "Bearer " + os.environ["WHOLLY_TOKEN"]
request = Request("https://api.example.com/v1/operator/merchants/YOUR_MERCHANT_ID/users",
method="GET", headers=headers)
# Non-2xx responses raise HTTPError. Do not retry writes with a new key.
with build_opener(NoRedirect()).open(request, timeout=30) as response:
print(json.load(response))Esempio di risposta · 200 application/json
{
"data": [],
"page": 1,
"page_size": 25,
"total": 0
}POSTCrea utente/v1/operator/merchants/{merchant_id}/usersLettura + scrittura
Aggiungi un amministratore del commerciante o un utente limitato a progetti selezionati.
- Richiede users.write; solo commercianti ospitati autorizzati. Le chiavi operatore non accedono allo spazio dell'attività del proprietario.
- Salva un Idempotency-Key univoco e l'esatto corpo prima dell'invio. I nuovi tentativi non ripetono mai un'azione già confermata. I campi segreti vengono omessi nei replay; se una risposta contenente un segreto è andata persa, esamina la risorsa creata e ruota o riemetti esplicitamente il segreto. Un 409 operator_request_in_progress può indicare una richiesta interrotta con esito sconosciuto: esamina risorsa e audit; non riprovare alla cieca con una nuova chiave.
- Gli esempi di risposta mostrano alcuni campi. Considera aggiuntivi gli altri campi della risposta.
| Header | Presenza | Regola |
|---|---|---|
| Authorization | obbligatorio | Bearer YOUR_OPERATOR_API_TOKEN |
| Idempotency-Key | obbligatorio | 16–128 lettere, cifre, -, _ o .; salvata per questa operazione |
| Parametro | Tipo / posizione | Regola |
|---|---|---|
| merchant_id | path UUID | UUID canonico minuscolo della risorsa; deve appartenere all'ambito commercianti della credenziale. |
| email, display_name | strings · required | L'email è univoca nell'installazione. |
| onboarding, password, require_password_change, send_invitation_email | same as merchant creation | La creazione di inviti richiede anche invitations.write. |
| access_level | admin | projects · default admin | admin è solo l'amministratore di questo commerciante, mai quello dell'installazione o operatore. |
| project_ids | UUID[] | Solo progetti del commerciante. Selezioni obbligatorie per l'accesso limitato ai progetti; mai tra clienti diversi. |
| default_timezone | IANA timezone · optional | Valore regionale predefinito se omesso. |
Richiesta
: "${WHOLLY_TOKEN:?Set WHOLLY_TOKEN to your server-side API token}"
# Keep this key and the exact body for retries; use a new key for each new operation.
curl --fail-with-body --max-time 30 \
--request POST \
--url "https://api.example.com/v1/operator/merchants/YOUR_MERCHANT_ID/users" \
--header "Authorization: Bearer $WHOLLY_TOKEN" \
--header 'Idempotency-Key: operator-request-1042' \
--header 'Content-Type: application/json' \
--data-raw '{
"email": "staff@example.test",
"display_name": "Store team",
"onboarding": "invitation",
"access_level": "projects",
"project_ids": [
"11111111-1111-4111-8111-111111111111"
],
"send_invitation_email": false
}'// Node.js 18+ · run on your server, never in browser code.
const token = process.env.WHOLLY_TOKEN;
if (!token) throw new Error("Set WHOLLY_TOKEN");
// Keep this key and the exact body for retries; use a new key for each new operation.
const body = `{
"email": "staff@example.test",
"display_name": "Store team",
"onboarding": "invitation",
"access_level": "projects",
"project_ids": [
"11111111-1111-4111-8111-111111111111"
],
"send_invitation_email": false
}`;
const response = await fetch("https://api.example.com/v1/operator/merchants/YOUR_MERCHANT_ID/users", {
method: "POST",
headers: {
"Authorization": `Bearer ${token}`,
"Idempotency-Key": "operator-request-1042",
"Content-Type": "application/json"
},
body,
redirect: "error",
signal: AbortSignal.timeout(30000)
});
if (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);
console.log(await response.json());<?php
// PHP 8+ with the cURL extension; run on your server.
$token = getenv('WHOLLY_TOKEN');
if (!$token) { throw new RuntimeException('Set WHOLLY_TOKEN'); }
// Keep this key and the exact body for retries; use a new key for each new operation.
$body = <<<'JSON'
{
"email": "staff@example.test",
"display_name": "Store team",
"onboarding": "invitation",
"access_level": "projects",
"project_ids": [
"11111111-1111-4111-8111-111111111111"
],
"send_invitation_email": false
}
JSON;
$ch = curl_init("https://api.example.com/v1/operator/merchants/YOUR_MERCHANT_ID/users");
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => 'POST',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_FOLLOWLOCATION => false,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTPHEADER => ['Authorization: Bearer ' . $token, "Idempotency-Key: operator-request-1042", "Content-Type: application/json"],
CURLOPT_POSTFIELDS => $body,
]);
$response = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if ($response === false) { throw new RuntimeException(curl_error($ch)); }
curl_close($ch);
if ($status < 200 || $status >= 300) { throw new RuntimeException("HTTP $status: $response"); }
print_r(json_decode($response, true, 512, JSON_THROW_ON_ERROR));# Python 3 · standard library; run on your server.
import json
import os
from urllib.request import Request, build_opener, HTTPRedirectHandler
class NoRedirect(HTTPRedirectHandler):
def redirect_request(self, req, fp, code, msg, headers, newurl):
return None
# Keep this key and the exact body for retries; use a new key for each new operation.
headers = {
"Idempotency-Key": "operator-request-1042",
"Content-Type": "application/json"
}
headers["Authorization"] = "Bearer " + os.environ["WHOLLY_TOKEN"]
body = """{
"email": "staff@example.test",
"display_name": "Store team",
"onboarding": "invitation",
"access_level": "projects",
"project_ids": [
"11111111-1111-4111-8111-111111111111"
],
"send_invitation_email": false
}""".encode("utf-8")
request = Request("https://api.example.com/v1/operator/merchants/YOUR_MERCHANT_ID/users",
method="POST", headers=headers, data=body)
# Non-2xx responses raise HTTPError. Do not retry writes with a new key.
with build_opener(NoRedirect()).open(request, timeout=30) as response:
print(json.load(response))Esempio di risposta · 201 o 200 application/json
{
"user": {
"id": "22222222-2222-4222-8222-222222222222",
"merchant_id": "11111111-1111-4111-8111-111111111111",
"email": "admin@example.test",
"display_name": "Shop administrator",
"access_level": "admin",
"project_ids": [],
"enabled": true,
"password_setup_required": false,
"custody_acceptance_required": true,
"password_change_required": true
},
"user_id": "22222222-2222-4222-8222-222222222222",
"access_link": null,
"email_delivery": {
"status": "not_requested"
},
"merchant_id": "11111111-1111-4111-8111-111111111111"
}GETRecupera utente/v1/operator/merchants/{merchant_id}/users/{user_id}Sola lettura
Gestisci o esamina la risorsa indicata del commerciante ospitato usando una credenziale operatore separata.
- Richiede users.read; solo commercianti ospitati autorizzati. Le chiavi operatore non accedono allo spazio dell'attività del proprietario.
- Gli esempi di risposta mostrano alcuni campi. Considera aggiuntivi gli altri campi della risposta.
| Header | Presenza | Regola |
|---|---|---|
| Authorization | obbligatorio | Bearer YOUR_OPERATOR_API_TOKEN |
| Parametro | Tipo / posizione | Regola |
|---|---|---|
| merchant_id | path UUID | UUID canonico minuscolo della risorsa; deve appartenere all'ambito commercianti della credenziale. |
| user_id | path UUID | UUID canonico minuscolo della risorsa; deve appartenere all'ambito commercianti della credenziale. |
Richiesta
: "${WHOLLY_TOKEN:?Set WHOLLY_TOKEN to your server-side API token}"
curl --fail-with-body --max-time 30 \
--request GET \
--url "https://api.example.com/v1/operator/merchants/YOUR_MERCHANT_ID/users/YOUR_USER_ID" \
--header "Authorization: Bearer $WHOLLY_TOKEN"// Node.js 18+ · run on your server, never in browser code.
const token = process.env.WHOLLY_TOKEN;
if (!token) throw new Error("Set WHOLLY_TOKEN");
const response = await fetch("https://api.example.com/v1/operator/merchants/YOUR_MERCHANT_ID/users/YOUR_USER_ID", {
method: "GET",
headers: {
"Authorization": `Bearer ${token}`
},
redirect: "error",
signal: AbortSignal.timeout(30000)
});
if (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);
console.log(await response.json());<?php
// PHP 8+ with the cURL extension; run on your server.
$token = getenv('WHOLLY_TOKEN');
if (!$token) { throw new RuntimeException('Set WHOLLY_TOKEN'); }
$ch = curl_init("https://api.example.com/v1/operator/merchants/YOUR_MERCHANT_ID/users/YOUR_USER_ID");
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => 'GET',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_FOLLOWLOCATION => false,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTPHEADER => ['Authorization: Bearer ' . $token],
]);
$response = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if ($response === false) { throw new RuntimeException(curl_error($ch)); }
curl_close($ch);
if ($status < 200 || $status >= 300) { throw new RuntimeException("HTTP $status: $response"); }
print_r(json_decode($response, true, 512, JSON_THROW_ON_ERROR));# Python 3 · standard library; run on your server.
import json
import os
from urllib.request import Request, build_opener, HTTPRedirectHandler
class NoRedirect(HTTPRedirectHandler):
def redirect_request(self, req, fp, code, msg, headers, newurl):
return None
headers = {}
headers["Authorization"] = "Bearer " + os.environ["WHOLLY_TOKEN"]
request = Request("https://api.example.com/v1/operator/merchants/YOUR_MERCHANT_ID/users/YOUR_USER_ID",
method="GET", headers=headers)
# Non-2xx responses raise HTTPError. Do not retry writes with a new key.
with build_opener(NoRedirect()).open(request, timeout=30) as response:
print(json.load(response))Esempio di risposta · 200 application/json
{
"id": "22222222-2222-4222-8222-222222222222",
"merchant_id": "11111111-1111-4111-8111-111111111111",
"email": "admin@example.test",
"display_name": "Shop administrator",
"access_level": "admin",
"project_ids": [],
"enabled": true,
"password_setup_required": false,
"custody_acceptance_required": true,
"password_change_required": true
}POSTAggiorna utente/v1/operator/merchants/{merchant_id}/users/{user_id}Lettura + scrittura
Gestisci o esamina la risorsa indicata del commerciante ospitato usando una credenziale operatore separata.
- Richiede users.write; solo commercianti ospitati autorizzati. Le chiavi operatore non accedono allo spazio dell'attività del proprietario.
- Salva un Idempotency-Key univoco e l'esatto corpo prima dell'invio. I nuovi tentativi non ripetono mai un'azione già confermata. I campi segreti vengono omessi nei replay; se una risposta contenente un segreto è andata persa, esamina la risorsa creata e ruota o riemetti esplicitamente il segreto. Un 409 operator_request_in_progress può indicare una richiesta interrotta con esito sconosciuto: esamina risorsa e audit; non riprovare alla cieca con una nuova chiave.
- Gli esempi di risposta mostrano alcuni campi. Considera aggiuntivi gli altri campi della risposta.
| Header | Presenza | Regola |
|---|---|---|
| Authorization | obbligatorio | Bearer YOUR_OPERATOR_API_TOKEN |
| Idempotency-Key | obbligatorio | 16–128 lettere, cifre, -, _ o .; salvata per questa operazione |
| Parametro | Tipo / posizione | Regola |
|---|---|---|
| merchant_id | path UUID | UUID canonico minuscolo della risorsa; deve appartenere all'ambito commercianti della credenziale. |
| user_id | path UUID | UUID canonico minuscolo della risorsa; deve appartenere all'ambito commercianti della credenziale. |
| email, display_name, enabled, access_level, project_ids, default_timezone | optional fields | Aggiorna i campi forniti; resta la protezione dell'ultimo amministratore. Le password hanno un'operazione users.security separata. |
Richiesta
: "${WHOLLY_TOKEN:?Set WHOLLY_TOKEN to your server-side API token}"
# Keep this key and the exact body for retries; use a new key for each new operation.
curl --fail-with-body --max-time 30 \
--request POST \
--url "https://api.example.com/v1/operator/merchants/YOUR_MERCHANT_ID/users/YOUR_USER_ID" \
--header "Authorization: Bearer $WHOLLY_TOKEN" \
--header 'Idempotency-Key: operator-request-1042' \
--header 'Content-Type: application/json' \
--data-raw '{
"display_name": "Store manager"
}'// Node.js 18+ · run on your server, never in browser code.
const token = process.env.WHOLLY_TOKEN;
if (!token) throw new Error("Set WHOLLY_TOKEN");
// Keep this key and the exact body for retries; use a new key for each new operation.
const body = `{
"display_name": "Store manager"
}`;
const response = await fetch("https://api.example.com/v1/operator/merchants/YOUR_MERCHANT_ID/users/YOUR_USER_ID", {
method: "POST",
headers: {
"Authorization": `Bearer ${token}`,
"Idempotency-Key": "operator-request-1042",
"Content-Type": "application/json"
},
body,
redirect: "error",
signal: AbortSignal.timeout(30000)
});
if (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);
console.log(await response.json());<?php
// PHP 8+ with the cURL extension; run on your server.
$token = getenv('WHOLLY_TOKEN');
if (!$token) { throw new RuntimeException('Set WHOLLY_TOKEN'); }
// Keep this key and the exact body for retries; use a new key for each new operation.
$body = <<<'JSON'
{
"display_name": "Store manager"
}
JSON;
$ch = curl_init("https://api.example.com/v1/operator/merchants/YOUR_MERCHANT_ID/users/YOUR_USER_ID");
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => 'POST',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_FOLLOWLOCATION => false,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTPHEADER => ['Authorization: Bearer ' . $token, "Idempotency-Key: operator-request-1042", "Content-Type: application/json"],
CURLOPT_POSTFIELDS => $body,
]);
$response = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if ($response === false) { throw new RuntimeException(curl_error($ch)); }
curl_close($ch);
if ($status < 200 || $status >= 300) { throw new RuntimeException("HTTP $status: $response"); }
print_r(json_decode($response, true, 512, JSON_THROW_ON_ERROR));# Python 3 · standard library; run on your server.
import json
import os
from urllib.request import Request, build_opener, HTTPRedirectHandler
class NoRedirect(HTTPRedirectHandler):
def redirect_request(self, req, fp, code, msg, headers, newurl):
return None
# Keep this key and the exact body for retries; use a new key for each new operation.
headers = {
"Idempotency-Key": "operator-request-1042",
"Content-Type": "application/json"
}
headers["Authorization"] = "Bearer " + os.environ["WHOLLY_TOKEN"]
body = """{
"display_name": "Store manager"
}""".encode("utf-8")
request = Request("https://api.example.com/v1/operator/merchants/YOUR_MERCHANT_ID/users/YOUR_USER_ID",
method="POST", headers=headers, data=body)
# Non-2xx responses raise HTTPError. Do not retry writes with a new key.
with build_opener(NoRedirect()).open(request, timeout=30) as response:
print(json.load(response))Esempio di risposta · 200 application/json
{
"id": "22222222-2222-4222-8222-222222222222",
"merchant_id": "11111111-1111-4111-8111-111111111111",
"email": "admin@example.test",
"display_name": "Shop administrator",
"access_level": "admin",
"project_ids": [],
"enabled": true,
"password_setup_required": false,
"custody_acceptance_required": true,
"password_change_required": true
}POSTImposta password utente/v1/operator/merchants/{merchant_id}/users/{user_id}/passwordLettura + scrittura
Imposta la password di un account ospitato e revoca le sessioni. Il TOTP esistente viene conservato.
- Richiede users.security; solo commercianti ospitati autorizzati. Le chiavi operatore non accedono allo spazio dell'attività del proprietario.
- Salva un Idempotency-Key univoco e l'esatto corpo prima dell'invio. I nuovi tentativi non ripetono mai un'azione già confermata. I campi segreti vengono omessi nei replay; se una risposta contenente un segreto è andata persa, esamina la risorsa creata e ruota o riemetti esplicitamente il segreto. Un 409 operator_request_in_progress può indicare una richiesta interrotta con esito sconosciuto: esamina risorsa e audit; non riprovare alla cieca con una nuova chiave.
- Gli esempi di risposta mostrano alcuni campi. Considera aggiuntivi gli altri campi della risposta.
| Header | Presenza | Regola |
|---|---|---|
| Authorization | obbligatorio | Bearer YOUR_OPERATOR_API_TOKEN |
| Idempotency-Key | obbligatorio | 16–128 lettere, cifre, -, _ o .; salvata per questa operazione |
| Parametro | Tipo / posizione | Regola |
|---|---|---|
| merchant_id | path UUID | UUID canonico minuscolo della risorsa; deve appartenere all'ambito commercianti della credenziale. |
| user_id | path UUID | UUID canonico minuscolo della risorsa; deve appartenere all'ambito commercianti della credenziale. |
| password | string · required | Cambia la password e revoca le sessioni, mantenendo TOTP. Richiede users.security. |
| require_password_change | boolean · default true | L'utente deve impostare la propria password al prossimo accesso riuscito. |
Richiesta
: "${WHOLLY_TOKEN:?Set WHOLLY_TOKEN to your server-side API token}"
# Keep this key and the exact body for retries; use a new key for each new operation.
curl --fail-with-body --max-time 30 \
--request POST \
--url "https://api.example.com/v1/operator/merchants/YOUR_MERCHANT_ID/users/YOUR_USER_ID/password" \
--header "Authorization: Bearer $WHOLLY_TOKEN" \
--header 'Idempotency-Key: operator-request-1042' \
--header 'Content-Type: application/json' \
--data-raw '{
"password": "REPLACE_WITH_A_NEW_UNIQUE_PASSWORD",
"require_password_change": true
}'// Node.js 18+ · run on your server, never in browser code.
const token = process.env.WHOLLY_TOKEN;
if (!token) throw new Error("Set WHOLLY_TOKEN");
// Keep this key and the exact body for retries; use a new key for each new operation.
const body = `{
"password": "REPLACE_WITH_A_NEW_UNIQUE_PASSWORD",
"require_password_change": true
}`;
const response = await fetch("https://api.example.com/v1/operator/merchants/YOUR_MERCHANT_ID/users/YOUR_USER_ID/password", {
method: "POST",
headers: {
"Authorization": `Bearer ${token}`,
"Idempotency-Key": "operator-request-1042",
"Content-Type": "application/json"
},
body,
redirect: "error",
signal: AbortSignal.timeout(30000)
});
if (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);
console.log(await response.json());<?php
// PHP 8+ with the cURL extension; run on your server.
$token = getenv('WHOLLY_TOKEN');
if (!$token) { throw new RuntimeException('Set WHOLLY_TOKEN'); }
// Keep this key and the exact body for retries; use a new key for each new operation.
$body = <<<'JSON'
{
"password": "REPLACE_WITH_A_NEW_UNIQUE_PASSWORD",
"require_password_change": true
}
JSON;
$ch = curl_init("https://api.example.com/v1/operator/merchants/YOUR_MERCHANT_ID/users/YOUR_USER_ID/password");
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => 'POST',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_FOLLOWLOCATION => false,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTPHEADER => ['Authorization: Bearer ' . $token, "Idempotency-Key: operator-request-1042", "Content-Type: application/json"],
CURLOPT_POSTFIELDS => $body,
]);
$response = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if ($response === false) { throw new RuntimeException(curl_error($ch)); }
curl_close($ch);
if ($status < 200 || $status >= 300) { throw new RuntimeException("HTTP $status: $response"); }
print_r(json_decode($response, true, 512, JSON_THROW_ON_ERROR));# Python 3 · standard library; run on your server.
import json
import os
from urllib.request import Request, build_opener, HTTPRedirectHandler
class NoRedirect(HTTPRedirectHandler):
def redirect_request(self, req, fp, code, msg, headers, newurl):
return None
# Keep this key and the exact body for retries; use a new key for each new operation.
headers = {
"Idempotency-Key": "operator-request-1042",
"Content-Type": "application/json"
}
headers["Authorization"] = "Bearer " + os.environ["WHOLLY_TOKEN"]
body = """{
"password": "REPLACE_WITH_A_NEW_UNIQUE_PASSWORD",
"require_password_change": true
}""".encode("utf-8")
request = Request("https://api.example.com/v1/operator/merchants/YOUR_MERCHANT_ID/users/YOUR_USER_ID/password",
method="POST", headers=headers, data=body)
# Non-2xx responses raise HTTPError. Do not retry writes with a new key.
with build_opener(NoRedirect()).open(request, timeout=30) as response:
print(json.load(response))Esempio di risposta · 200 application/json
{
"user_id": "11111111-1111-4111-8111-111111111111",
"sessions_revoked": true,
"totp_preserved": true
}POSTRevoca sessioni utente/v1/operator/merchants/{merchant_id}/users/{user_id}/revoke-sessionsLettura + scrittura
Gestisci o esamina la risorsa indicata del commerciante ospitato usando una credenziale operatore separata.
- Richiede users.security; solo commercianti ospitati autorizzati. Le chiavi operatore non accedono allo spazio dell'attività del proprietario.
- Salva un Idempotency-Key univoco e l'esatto corpo prima dell'invio. I nuovi tentativi non ripetono mai un'azione già confermata. I campi segreti vengono omessi nei replay; se una risposta contenente un segreto è andata persa, esamina la risorsa creata e ruota o riemetti esplicitamente il segreto. Un 409 operator_request_in_progress può indicare una richiesta interrotta con esito sconosciuto: esamina risorsa e audit; non riprovare alla cieca con una nuova chiave.
- Gli esempi di risposta mostrano alcuni campi. Considera aggiuntivi gli altri campi della risposta.
| Header | Presenza | Regola |
|---|---|---|
| Authorization | obbligatorio | Bearer YOUR_OPERATOR_API_TOKEN |
| Idempotency-Key | obbligatorio | 16–128 lettere, cifre, -, _ o .; salvata per questa operazione |
| Parametro | Tipo / posizione | Regola |
|---|---|---|
| merchant_id | path UUID | UUID canonico minuscolo della risorsa; deve appartenere all'ambito commercianti della credenziale. |
| user_id | path UUID | UUID canonico minuscolo della risorsa; deve appartenere all'ambito commercianti della credenziale. |
Richiesta
: "${WHOLLY_TOKEN:?Set WHOLLY_TOKEN to your server-side API token}"
# Keep this key and the exact body for retries; use a new key for each new operation.
curl --fail-with-body --max-time 30 \
--request POST \
--url "https://api.example.com/v1/operator/merchants/YOUR_MERCHANT_ID/users/YOUR_USER_ID/revoke-sessions" \
--header "Authorization: Bearer $WHOLLY_TOKEN" \
--header 'Idempotency-Key: operator-request-1042' \
--header 'Content-Type: application/json' \
--data-raw '{}'// Node.js 18+ · run on your server, never in browser code.
const token = process.env.WHOLLY_TOKEN;
if (!token) throw new Error("Set WHOLLY_TOKEN");
// Keep this key and the exact body for retries; use a new key for each new operation.
const body = `{}`;
const response = await fetch("https://api.example.com/v1/operator/merchants/YOUR_MERCHANT_ID/users/YOUR_USER_ID/revoke-sessions", {
method: "POST",
headers: {
"Authorization": `Bearer ${token}`,
"Idempotency-Key": "operator-request-1042",
"Content-Type": "application/json"
},
body,
redirect: "error",
signal: AbortSignal.timeout(30000)
});
if (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);
console.log(await response.json());<?php
// PHP 8+ with the cURL extension; run on your server.
$token = getenv('WHOLLY_TOKEN');
if (!$token) { throw new RuntimeException('Set WHOLLY_TOKEN'); }
// Keep this key and the exact body for retries; use a new key for each new operation.
$body = <<<'JSON'
{}
JSON;
$ch = curl_init("https://api.example.com/v1/operator/merchants/YOUR_MERCHANT_ID/users/YOUR_USER_ID/revoke-sessions");
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => 'POST',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_FOLLOWLOCATION => false,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTPHEADER => ['Authorization: Bearer ' . $token, "Idempotency-Key: operator-request-1042", "Content-Type: application/json"],
CURLOPT_POSTFIELDS => $body,
]);
$response = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if ($response === false) { throw new RuntimeException(curl_error($ch)); }
curl_close($ch);
if ($status < 200 || $status >= 300) { throw new RuntimeException("HTTP $status: $response"); }
print_r(json_decode($response, true, 512, JSON_THROW_ON_ERROR));# Python 3 · standard library; run on your server.
import json
import os
from urllib.request import Request, build_opener, HTTPRedirectHandler
class NoRedirect(HTTPRedirectHandler):
def redirect_request(self, req, fp, code, msg, headers, newurl):
return None
# Keep this key and the exact body for retries; use a new key for each new operation.
headers = {
"Idempotency-Key": "operator-request-1042",
"Content-Type": "application/json"
}
headers["Authorization"] = "Bearer " + os.environ["WHOLLY_TOKEN"]
body = """{}""".encode("utf-8")
request = Request("https://api.example.com/v1/operator/merchants/YOUR_MERCHANT_ID/users/YOUR_USER_ID/revoke-sessions",
method="POST", headers=headers, data=body)
# Non-2xx responses raise HTTPError. Do not retry writes with a new key.
with build_opener(NoRedirect()).open(request, timeout=30) as response:
print(json.load(response))Esempio di risposta · 200 application/json
{
"user_id": "11111111-1111-4111-8111-111111111111",
"sessions_revoked": true,
"totp_preserved": true
}GETElenca inviti/v1/operator/merchants/{merchant_id}/invitationsSola lettura
Gestisci o esamina la risorsa indicata del commerciante ospitato usando una credenziale operatore separata.
- Richiede invitations.read; solo commercianti ospitati autorizzati. Le chiavi operatore non accedono allo spazio dell'attività del proprietario.
- Gli esempi di risposta mostrano alcuni campi. Considera aggiuntivi gli altri campi della risposta.
| Header | Presenza | Regola |
|---|---|---|
| Authorization | obbligatorio | Bearer YOUR_OPERATOR_API_TOKEN |
| Parametro | Tipo / posizione | Regola |
|---|---|---|
| merchant_id | path UUID | UUID canonico minuscolo della risorsa; deve appartenere all'ambito commercianti della credenziale. |
| page, search | query · optional | Pagine a partire da 1, 25 elementi per pagina. La ricerca è supportata per commercianti, utenti, progetti, negozi, wallet, credenziali e webhook; gli elenchi eventi nativi usano i propri filtri. |
Richiesta
: "${WHOLLY_TOKEN:?Set WHOLLY_TOKEN to your server-side API token}"
curl --fail-with-body --max-time 30 \
--request GET \
--url "https://api.example.com/v1/operator/merchants/YOUR_MERCHANT_ID/invitations" \
--header "Authorization: Bearer $WHOLLY_TOKEN"// Node.js 18+ · run on your server, never in browser code.
const token = process.env.WHOLLY_TOKEN;
if (!token) throw new Error("Set WHOLLY_TOKEN");
const response = await fetch("https://api.example.com/v1/operator/merchants/YOUR_MERCHANT_ID/invitations", {
method: "GET",
headers: {
"Authorization": `Bearer ${token}`
},
redirect: "error",
signal: AbortSignal.timeout(30000)
});
if (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);
console.log(await response.json());<?php
// PHP 8+ with the cURL extension; run on your server.
$token = getenv('WHOLLY_TOKEN');
if (!$token) { throw new RuntimeException('Set WHOLLY_TOKEN'); }
$ch = curl_init("https://api.example.com/v1/operator/merchants/YOUR_MERCHANT_ID/invitations");
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => 'GET',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_FOLLOWLOCATION => false,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTPHEADER => ['Authorization: Bearer ' . $token],
]);
$response = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if ($response === false) { throw new RuntimeException(curl_error($ch)); }
curl_close($ch);
if ($status < 200 || $status >= 300) { throw new RuntimeException("HTTP $status: $response"); }
print_r(json_decode($response, true, 512, JSON_THROW_ON_ERROR));# Python 3 · standard library; run on your server.
import json
import os
from urllib.request import Request, build_opener, HTTPRedirectHandler
class NoRedirect(HTTPRedirectHandler):
def redirect_request(self, req, fp, code, msg, headers, newurl):
return None
headers = {}
headers["Authorization"] = "Bearer " + os.environ["WHOLLY_TOKEN"]
request = Request("https://api.example.com/v1/operator/merchants/YOUR_MERCHANT_ID/invitations",
method="GET", headers=headers)
# Non-2xx responses raise HTTPError. Do not retry writes with a new key.
with build_opener(NoRedirect()).open(request, timeout=30) as response:
print(json.load(response))Esempio di risposta · 200 application/json
{
"data": [],
"page": 1,
"page_size": 25,
"total": 0
}POSTCrea invito/v1/operator/merchants/{merchant_id}/invitationsLettura + scrittura
Crea o sostituisci un link monouso di invito o reimpostazione password.
- Richiede invitations.write; solo commercianti ospitati autorizzati. Le chiavi operatore non accedono allo spazio dell'attività del proprietario.
- Salva un Idempotency-Key univoco e l'esatto corpo prima dell'invio. I nuovi tentativi non ripetono mai un'azione già confermata. I campi segreti vengono omessi nei replay; se una risposta contenente un segreto è andata persa, esamina la risorsa creata e ruota o riemetti esplicitamente il segreto. Un 409 operator_request_in_progress può indicare una richiesta interrotta con esito sconosciuto: esamina risorsa e audit; non riprovare alla cieca con una nuova chiave.
- Gli esempi di risposta mostrano alcuni campi. Considera aggiuntivi gli altri campi della risposta.
| Header | Presenza | Regola |
|---|---|---|
| Authorization | obbligatorio | Bearer YOUR_OPERATOR_API_TOKEN |
| Idempotency-Key | obbligatorio | 16–128 lettere, cifre, -, _ o .; salvata per questa operazione |
| Parametro | Tipo / posizione | Regola |
|---|---|---|
| merchant_id | path UUID | UUID canonico minuscolo della risorsa; deve appartenere all'ambito commercianti della credenziale. |
| user_id, send_email | UUID, boolean | Emette o sostituisce un link monouso per un account esistente. Gli utenti già attivati ricevono un link di reimpostazione di un'ora e richiedono users.security. |
| new user fields | alternative to user_id | Usa email, display_name, access_level e project_ids per creare un utente invitato; richiede users.write. |
Richiesta
: "${WHOLLY_TOKEN:?Set WHOLLY_TOKEN to your server-side API token}"
# Keep this key and the exact body for retries; use a new key for each new operation.
curl --fail-with-body --max-time 30 \
--request POST \
--url "https://api.example.com/v1/operator/merchants/YOUR_MERCHANT_ID/invitations" \
--header "Authorization: Bearer $WHOLLY_TOKEN" \
--header 'Idempotency-Key: operator-request-1042' \
--header 'Content-Type: application/json' \
--data-raw '{
"user_id": "11111111-1111-4111-8111-111111111111",
"send_email": false
}'// Node.js 18+ · run on your server, never in browser code.
const token = process.env.WHOLLY_TOKEN;
if (!token) throw new Error("Set WHOLLY_TOKEN");
// Keep this key and the exact body for retries; use a new key for each new operation.
const body = `{
"user_id": "11111111-1111-4111-8111-111111111111",
"send_email": false
}`;
const response = await fetch("https://api.example.com/v1/operator/merchants/YOUR_MERCHANT_ID/invitations", {
method: "POST",
headers: {
"Authorization": `Bearer ${token}`,
"Idempotency-Key": "operator-request-1042",
"Content-Type": "application/json"
},
body,
redirect: "error",
signal: AbortSignal.timeout(30000)
});
if (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);
console.log(await response.json());<?php
// PHP 8+ with the cURL extension; run on your server.
$token = getenv('WHOLLY_TOKEN');
if (!$token) { throw new RuntimeException('Set WHOLLY_TOKEN'); }
// Keep this key and the exact body for retries; use a new key for each new operation.
$body = <<<'JSON'
{
"user_id": "11111111-1111-4111-8111-111111111111",
"send_email": false
}
JSON;
$ch = curl_init("https://api.example.com/v1/operator/merchants/YOUR_MERCHANT_ID/invitations");
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => 'POST',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_FOLLOWLOCATION => false,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTPHEADER => ['Authorization: Bearer ' . $token, "Idempotency-Key: operator-request-1042", "Content-Type: application/json"],
CURLOPT_POSTFIELDS => $body,
]);
$response = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if ($response === false) { throw new RuntimeException(curl_error($ch)); }
curl_close($ch);
if ($status < 200 || $status >= 300) { throw new RuntimeException("HTTP $status: $response"); }
print_r(json_decode($response, true, 512, JSON_THROW_ON_ERROR));# Python 3 · standard library; run on your server.
import json
import os
from urllib.request import Request, build_opener, HTTPRedirectHandler
class NoRedirect(HTTPRedirectHandler):
def redirect_request(self, req, fp, code, msg, headers, newurl):
return None
# Keep this key and the exact body for retries; use a new key for each new operation.
headers = {
"Idempotency-Key": "operator-request-1042",
"Content-Type": "application/json"
}
headers["Authorization"] = "Bearer " + os.environ["WHOLLY_TOKEN"]
body = """{
"user_id": "11111111-1111-4111-8111-111111111111",
"send_email": false
}""".encode("utf-8")
request = Request("https://api.example.com/v1/operator/merchants/YOUR_MERCHANT_ID/invitations",
method="POST", headers=headers, data=body)
# Non-2xx responses raise HTTPError. Do not retry writes with a new key.
with build_opener(NoRedirect()).open(request, timeout=30) as response:
print(json.load(response))Esempio di risposta · 201 o 200 application/json
{
"user_id": "22222222-2222-4222-8222-222222222222",
"email": "admin@example.test",
"kind": "invitation",
"expires_at": "2026-10-03T12:00:00Z",
"url": "https://merchant.example.com/account-access.html#token=YOUR_PRIVATE_INVITATION_TOKEN"
}GETRecupera invito/v1/operator/invitations/{invitation_id}Sola lettura
Gestisci o esamina la risorsa indicata del commerciante ospitato usando una credenziale operatore separata.
- Richiede invitations.read; solo commercianti ospitati autorizzati. Le chiavi operatore non accedono allo spazio dell'attività del proprietario.
- Gli esempi di risposta mostrano alcuni campi. Considera aggiuntivi gli altri campi della risposta.
| Header | Presenza | Regola |
|---|---|---|
| Authorization | obbligatorio | Bearer YOUR_OPERATOR_API_TOKEN |
| Parametro | Tipo / posizione | Regola |
|---|---|---|
| invitation_id | path UUID | UUID canonico minuscolo della risorsa; deve appartenere all'ambito commercianti della credenziale. |
Richiesta
: "${WHOLLY_TOKEN:?Set WHOLLY_TOKEN to your server-side API token}"
curl --fail-with-body --max-time 30 \
--request GET \
--url "https://api.example.com/v1/operator/invitations/YOUR_INVITATION_ID" \
--header "Authorization: Bearer $WHOLLY_TOKEN"// Node.js 18+ · run on your server, never in browser code.
const token = process.env.WHOLLY_TOKEN;
if (!token) throw new Error("Set WHOLLY_TOKEN");
const response = await fetch("https://api.example.com/v1/operator/invitations/YOUR_INVITATION_ID", {
method: "GET",
headers: {
"Authorization": `Bearer ${token}`
},
redirect: "error",
signal: AbortSignal.timeout(30000)
});
if (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);
console.log(await response.json());<?php
// PHP 8+ with the cURL extension; run on your server.
$token = getenv('WHOLLY_TOKEN');
if (!$token) { throw new RuntimeException('Set WHOLLY_TOKEN'); }
$ch = curl_init("https://api.example.com/v1/operator/invitations/YOUR_INVITATION_ID");
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => 'GET',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_FOLLOWLOCATION => false,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTPHEADER => ['Authorization: Bearer ' . $token],
]);
$response = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if ($response === false) { throw new RuntimeException(curl_error($ch)); }
curl_close($ch);
if ($status < 200 || $status >= 300) { throw new RuntimeException("HTTP $status: $response"); }
print_r(json_decode($response, true, 512, JSON_THROW_ON_ERROR));# Python 3 · standard library; run on your server.
import json
import os
from urllib.request import Request, build_opener, HTTPRedirectHandler
class NoRedirect(HTTPRedirectHandler):
def redirect_request(self, req, fp, code, msg, headers, newurl):
return None
headers = {}
headers["Authorization"] = "Bearer " + os.environ["WHOLLY_TOKEN"]
request = Request("https://api.example.com/v1/operator/invitations/YOUR_INVITATION_ID",
method="GET", headers=headers)
# Non-2xx responses raise HTTPError. Do not retry writes with a new key.
with build_opener(NoRedirect()).open(request, timeout=30) as response:
print(json.load(response))Esempio di risposta · 200 application/json
{
"id": "44444444-4444-4444-8444-444444444444",
"user_id": "22222222-2222-4222-8222-222222222222",
"merchant_id": "11111111-1111-4111-8111-111111111111",
"kind": "invitation",
"status": "pending",
"created_at": "2026-10-01T12:00:00Z",
"expires_at": "2026-10-03T12:00:00Z"
}POSTReinvia invito/v1/operator/invitations/{invitation_id}/resendLettura + scrittura
Gestisci o esamina la risorsa indicata del commerciante ospitato usando una credenziale operatore separata.
- Richiede invitations.write; solo commercianti ospitati autorizzati. Le chiavi operatore non accedono allo spazio dell'attività del proprietario.
- Salva un Idempotency-Key univoco e l'esatto corpo prima dell'invio. I nuovi tentativi non ripetono mai un'azione già confermata. I campi segreti vengono omessi nei replay; se una risposta contenente un segreto è andata persa, esamina la risorsa creata e ruota o riemetti esplicitamente il segreto. Un 409 operator_request_in_progress può indicare una richiesta interrotta con esito sconosciuto: esamina risorsa e audit; non riprovare alla cieca con una nuova chiave.
- Gli esempi di risposta mostrano alcuni campi. Considera aggiuntivi gli altri campi della risposta.
| Header | Presenza | Regola |
|---|---|---|
| Authorization | obbligatorio | Bearer YOUR_OPERATOR_API_TOKEN |
| Idempotency-Key | obbligatorio | 16–128 lettere, cifre, -, _ o .; salvata per questa operazione |
| Parametro | Tipo / posizione | Regola |
|---|---|---|
| invitation_id | path UUID | UUID canonico minuscolo della risorsa; deve appartenere all'ambito commercianti della credenziale. |
| send_email | boolean · default false | Sostituisce il token precedente, non aggiunge mai credito. Restituisce una sola volta un nuovo link generato. Un account già attivato richiede users.security. |
Richiesta
: "${WHOLLY_TOKEN:?Set WHOLLY_TOKEN to your server-side API token}"
# Keep this key and the exact body for retries; use a new key for each new operation.
curl --fail-with-body --max-time 30 \
--request POST \
--url "https://api.example.com/v1/operator/invitations/YOUR_INVITATION_ID/resend" \
--header "Authorization: Bearer $WHOLLY_TOKEN" \
--header 'Idempotency-Key: operator-request-1042' \
--header 'Content-Type: application/json' \
--data-raw '{
"send_email": false
}'// Node.js 18+ · run on your server, never in browser code.
const token = process.env.WHOLLY_TOKEN;
if (!token) throw new Error("Set WHOLLY_TOKEN");
// Keep this key and the exact body for retries; use a new key for each new operation.
const body = `{
"send_email": false
}`;
const response = await fetch("https://api.example.com/v1/operator/invitations/YOUR_INVITATION_ID/resend", {
method: "POST",
headers: {
"Authorization": `Bearer ${token}`,
"Idempotency-Key": "operator-request-1042",
"Content-Type": "application/json"
},
body,
redirect: "error",
signal: AbortSignal.timeout(30000)
});
if (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);
console.log(await response.json());<?php
// PHP 8+ with the cURL extension; run on your server.
$token = getenv('WHOLLY_TOKEN');
if (!$token) { throw new RuntimeException('Set WHOLLY_TOKEN'); }
// Keep this key and the exact body for retries; use a new key for each new operation.
$body = <<<'JSON'
{
"send_email": false
}
JSON;
$ch = curl_init("https://api.example.com/v1/operator/invitations/YOUR_INVITATION_ID/resend");
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => 'POST',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_FOLLOWLOCATION => false,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTPHEADER => ['Authorization: Bearer ' . $token, "Idempotency-Key: operator-request-1042", "Content-Type: application/json"],
CURLOPT_POSTFIELDS => $body,
]);
$response = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if ($response === false) { throw new RuntimeException(curl_error($ch)); }
curl_close($ch);
if ($status < 200 || $status >= 300) { throw new RuntimeException("HTTP $status: $response"); }
print_r(json_decode($response, true, 512, JSON_THROW_ON_ERROR));# Python 3 · standard library; run on your server.
import json
import os
from urllib.request import Request, build_opener, HTTPRedirectHandler
class NoRedirect(HTTPRedirectHandler):
def redirect_request(self, req, fp, code, msg, headers, newurl):
return None
# Keep this key and the exact body for retries; use a new key for each new operation.
headers = {
"Idempotency-Key": "operator-request-1042",
"Content-Type": "application/json"
}
headers["Authorization"] = "Bearer " + os.environ["WHOLLY_TOKEN"]
body = """{
"send_email": false
}""".encode("utf-8")
request = Request("https://api.example.com/v1/operator/invitations/YOUR_INVITATION_ID/resend",
method="POST", headers=headers, data=body)
# Non-2xx responses raise HTTPError. Do not retry writes with a new key.
with build_opener(NoRedirect()).open(request, timeout=30) as response:
print(json.load(response))Esempio di risposta · 200 application/json
{
"user_id": "22222222-2222-4222-8222-222222222222",
"email": "admin@example.test",
"kind": "invitation",
"expires_at": "2026-10-03T12:00:00Z",
"url": "https://merchant.example.com/account-access.html#token=YOUR_PRIVATE_INVITATION_TOKEN"
}POSTRevoca invito/v1/operator/invitations/{invitation_id}/revokeLettura + scrittura
Gestisci o esamina la risorsa indicata del commerciante ospitato usando una credenziale operatore separata.
- Richiede invitations.write; solo commercianti ospitati autorizzati. Le chiavi operatore non accedono allo spazio dell'attività del proprietario.
- Salva un Idempotency-Key univoco e l'esatto corpo prima dell'invio. I nuovi tentativi non ripetono mai un'azione già confermata. I campi segreti vengono omessi nei replay; se una risposta contenente un segreto è andata persa, esamina la risorsa creata e ruota o riemetti esplicitamente il segreto. Un 409 operator_request_in_progress può indicare una richiesta interrotta con esito sconosciuto: esamina risorsa e audit; non riprovare alla cieca con una nuova chiave.
- Gli esempi di risposta mostrano alcuni campi. Considera aggiuntivi gli altri campi della risposta.
| Header | Presenza | Regola |
|---|---|---|
| Authorization | obbligatorio | Bearer YOUR_OPERATOR_API_TOKEN |
| Idempotency-Key | obbligatorio | 16–128 lettere, cifre, -, _ o .; salvata per questa operazione |
| Parametro | Tipo / posizione | Regola |
|---|---|---|
| invitation_id | path UUID | UUID canonico minuscolo della risorsa; deve appartenere all'ambito commercianti della credenziale. |
Richiesta
: "${WHOLLY_TOKEN:?Set WHOLLY_TOKEN to your server-side API token}"
# Keep this key and the exact body for retries; use a new key for each new operation.
curl --fail-with-body --max-time 30 \
--request POST \
--url "https://api.example.com/v1/operator/invitations/YOUR_INVITATION_ID/revoke" \
--header "Authorization: Bearer $WHOLLY_TOKEN" \
--header 'Idempotency-Key: operator-request-1042' \
--header 'Content-Type: application/json' \
--data-raw '{}'// Node.js 18+ · run on your server, never in browser code.
const token = process.env.WHOLLY_TOKEN;
if (!token) throw new Error("Set WHOLLY_TOKEN");
// Keep this key and the exact body for retries; use a new key for each new operation.
const body = `{}`;
const response = await fetch("https://api.example.com/v1/operator/invitations/YOUR_INVITATION_ID/revoke", {
method: "POST",
headers: {
"Authorization": `Bearer ${token}`,
"Idempotency-Key": "operator-request-1042",
"Content-Type": "application/json"
},
body,
redirect: "error",
signal: AbortSignal.timeout(30000)
});
if (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);
console.log(await response.json());<?php
// PHP 8+ with the cURL extension; run on your server.
$token = getenv('WHOLLY_TOKEN');
if (!$token) { throw new RuntimeException('Set WHOLLY_TOKEN'); }
// Keep this key and the exact body for retries; use a new key for each new operation.
$body = <<<'JSON'
{}
JSON;
$ch = curl_init("https://api.example.com/v1/operator/invitations/YOUR_INVITATION_ID/revoke");
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => 'POST',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_FOLLOWLOCATION => false,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTPHEADER => ['Authorization: Bearer ' . $token, "Idempotency-Key: operator-request-1042", "Content-Type: application/json"],
CURLOPT_POSTFIELDS => $body,
]);
$response = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if ($response === false) { throw new RuntimeException(curl_error($ch)); }
curl_close($ch);
if ($status < 200 || $status >= 300) { throw new RuntimeException("HTTP $status: $response"); }
print_r(json_decode($response, true, 512, JSON_THROW_ON_ERROR));# Python 3 · standard library; run on your server.
import json
import os
from urllib.request import Request, build_opener, HTTPRedirectHandler
class NoRedirect(HTTPRedirectHandler):
def redirect_request(self, req, fp, code, msg, headers, newurl):
return None
# Keep this key and the exact body for retries; use a new key for each new operation.
headers = {
"Idempotency-Key": "operator-request-1042",
"Content-Type": "application/json"
}
headers["Authorization"] = "Bearer " + os.environ["WHOLLY_TOKEN"]
body = """{}""".encode("utf-8")
request = Request("https://api.example.com/v1/operator/invitations/YOUR_INVITATION_ID/revoke",
method="POST", headers=headers, data=body)
# Non-2xx responses raise HTTPError. Do not retry writes with a new key.
with build_opener(NoRedirect()).open(request, timeout=30) as response:
print(json.load(response))Esempio di risposta · 200 application/json
{
"revoked": true,
"invitation_id": "44444444-4444-4444-8444-444444444444"
}GETRecupera crediti/v1/operator/merchants/{merchant_id}/creditsSola lettura
Gestisci o esamina la risorsa indicata del commerciante ospitato usando una credenziale operatore separata.
- Richiede credits.read; solo commercianti ospitati autorizzati. Le chiavi operatore non accedono allo spazio dell'attività del proprietario.
- Gli esempi di risposta mostrano alcuni campi. Considera aggiuntivi gli altri campi della risposta.
| Header | Presenza | Regola |
|---|---|---|
| Authorization | obbligatorio | Bearer YOUR_OPERATOR_API_TOKEN |
| Parametro | Tipo / posizione | Regola |
|---|---|---|
| merchant_id | path UUID | UUID canonico minuscolo della risorsa; deve appartenere all'ambito commercianti della credenziale. |
Richiesta
: "${WHOLLY_TOKEN:?Set WHOLLY_TOKEN to your server-side API token}"
curl --fail-with-body --max-time 30 \
--request GET \
--url "https://api.example.com/v1/operator/merchants/YOUR_MERCHANT_ID/credits" \
--header "Authorization: Bearer $WHOLLY_TOKEN"// Node.js 18+ · run on your server, never in browser code.
const token = process.env.WHOLLY_TOKEN;
if (!token) throw new Error("Set WHOLLY_TOKEN");
const response = await fetch("https://api.example.com/v1/operator/merchants/YOUR_MERCHANT_ID/credits", {
method: "GET",
headers: {
"Authorization": `Bearer ${token}`
},
redirect: "error",
signal: AbortSignal.timeout(30000)
});
if (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);
console.log(await response.json());<?php
// PHP 8+ with the cURL extension; run on your server.
$token = getenv('WHOLLY_TOKEN');
if (!$token) { throw new RuntimeException('Set WHOLLY_TOKEN'); }
$ch = curl_init("https://api.example.com/v1/operator/merchants/YOUR_MERCHANT_ID/credits");
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => 'GET',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_FOLLOWLOCATION => false,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTPHEADER => ['Authorization: Bearer ' . $token],
]);
$response = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if ($response === false) { throw new RuntimeException(curl_error($ch)); }
curl_close($ch);
if ($status < 200 || $status >= 300) { throw new RuntimeException("HTTP $status: $response"); }
print_r(json_decode($response, true, 512, JSON_THROW_ON_ERROR));# Python 3 · standard library; run on your server.
import json
import os
from urllib.request import Request, build_opener, HTTPRedirectHandler
class NoRedirect(HTTPRedirectHandler):
def redirect_request(self, req, fp, code, msg, headers, newurl):
return None
headers = {}
headers["Authorization"] = "Bearer " + os.environ["WHOLLY_TOKEN"]
request = Request("https://api.example.com/v1/operator/merchants/YOUR_MERCHANT_ID/credits",
method="GET", headers=headers)
# Non-2xx responses raise HTTPError. Do not retry writes with a new key.
with build_opener(NoRedirect()).open(request, timeout=30) as response:
print(json.load(response))Esempio di risposta · 200 application/json
{
"id": "11111111-1111-4111-8111-111111111111",
"name": "Example shop",
"currency": "EUR",
"balance": "10",
"fee_bps": 300,
"enabled": true,
"payments_paused": false,
"external_id": "customer-1042"
}GETElenca registro dei crediti/v1/operator/merchants/{merchant_id}/credits/ledgerSola lettura
Gestisci o esamina la risorsa indicata del commerciante ospitato usando una credenziale operatore separata.
- Richiede credits.read; solo commercianti ospitati autorizzati. Le chiavi operatore non accedono allo spazio dell'attività del proprietario.
- Gli esempi di risposta mostrano alcuni campi. Considera aggiuntivi gli altri campi della risposta.
| Header | Presenza | Regola |
|---|---|---|
| Authorization | obbligatorio | Bearer YOUR_OPERATOR_API_TOKEN |
| Parametro | Tipo / posizione | Regola |
|---|---|---|
| merchant_id | path UUID | UUID canonico minuscolo della risorsa; deve appartenere all'ambito commercianti della credenziale. |
| page, q | query · optional | Pagine a partire da 1, 25 elementi per pagina. La ricerca è supportata per commercianti, utenti, progetti, negozi, wallet, credenziali e webhook; gli elenchi eventi nativi usano i propri filtri. |
Richiesta
: "${WHOLLY_TOKEN:?Set WHOLLY_TOKEN to your server-side API token}"
curl --fail-with-body --max-time 30 \
--request GET \
--url "https://api.example.com/v1/operator/merchants/YOUR_MERCHANT_ID/credits/ledger" \
--header "Authorization: Bearer $WHOLLY_TOKEN"// Node.js 18+ · run on your server, never in browser code.
const token = process.env.WHOLLY_TOKEN;
if (!token) throw new Error("Set WHOLLY_TOKEN");
const response = await fetch("https://api.example.com/v1/operator/merchants/YOUR_MERCHANT_ID/credits/ledger", {
method: "GET",
headers: {
"Authorization": `Bearer ${token}`
},
redirect: "error",
signal: AbortSignal.timeout(30000)
});
if (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);
console.log(await response.json());<?php
// PHP 8+ with the cURL extension; run on your server.
$token = getenv('WHOLLY_TOKEN');
if (!$token) { throw new RuntimeException('Set WHOLLY_TOKEN'); }
$ch = curl_init("https://api.example.com/v1/operator/merchants/YOUR_MERCHANT_ID/credits/ledger");
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => 'GET',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_FOLLOWLOCATION => false,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTPHEADER => ['Authorization: Bearer ' . $token],
]);
$response = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if ($response === false) { throw new RuntimeException(curl_error($ch)); }
curl_close($ch);
if ($status < 200 || $status >= 300) { throw new RuntimeException("HTTP $status: $response"); }
print_r(json_decode($response, true, 512, JSON_THROW_ON_ERROR));# Python 3 · standard library; run on your server.
import json
import os
from urllib.request import Request, build_opener, HTTPRedirectHandler
class NoRedirect(HTTPRedirectHandler):
def redirect_request(self, req, fp, code, msg, headers, newurl):
return None
headers = {}
headers["Authorization"] = "Bearer " + os.environ["WHOLLY_TOKEN"]
request = Request("https://api.example.com/v1/operator/merchants/YOUR_MERCHANT_ID/credits/ledger",
method="GET", headers=headers)
# Non-2xx responses raise HTTPError. Do not retry writes with a new key.
with build_opener(NoRedirect()).open(request, timeout=30) as response:
print(json.load(response))Esempio di risposta · 200 application/json
{
"data": [],
"page": 1,
"page_size": 25,
"total": 0
}POSTRettifica crediti/v1/operator/merchants/{merchant_id}/credits/adjustmentsLettura + scrittura
Aggiungi un'assegnazione o correzione motivata al registro prepagato di questo commerciante.
- Richiede credits.write; solo commercianti ospitati autorizzati. Le chiavi operatore non accedono allo spazio dell'attività del proprietario.
- Salva un Idempotency-Key univoco e l'esatto corpo prima dell'invio. I nuovi tentativi non ripetono mai un'azione già confermata. I campi segreti vengono omessi nei replay; se una risposta contenente un segreto è andata persa, esamina la risorsa creata e ruota o riemetti esplicitamente il segreto. Un 409 operator_request_in_progress può indicare una richiesta interrotta con esito sconosciuto: esamina risorsa e audit; non riprovare alla cieca con una nuova chiave.
- Gli esempi di risposta mostrano alcuni campi. Considera aggiuntivi gli altri campi della risposta.
| Header | Presenza | Regola |
|---|---|---|
| Authorization | obbligatorio | Bearer YOUR_OPERATOR_API_TOKEN |
| Idempotency-Key | obbligatorio | 16–128 lettere, cifre, -, _ o .; salvata per questa operazione |
| Parametro | Tipo / posizione | Regola |
|---|---|---|
| merchant_id | path UUID | UUID canonico minuscolo della risorsa; deve appartenere all'ambito commercianti della credenziale. |
| amount | signed decimal string · required | Assegnazione positiva o correzione negativa, fino a sei decimali nella valuta dei crediti del commerciante. Non è un trasferimento on-chain. |
| note | string · required | Motivo conservato nel registro che consente solo aggiunte. |
| request_id | UUID · required | Salva insieme all'importo e al motivo, oltre all'Idempotency-Key HTTP. |
Richiesta
: "${WHOLLY_TOKEN:?Set WHOLLY_TOKEN to your server-side API token}"
# Keep this key and the exact body for retries; use a new key for each new operation.
curl --fail-with-body --max-time 30 \
--request POST \
--url "https://api.example.com/v1/operator/merchants/YOUR_MERCHANT_ID/credits/adjustments" \
--header "Authorization: Bearer $WHOLLY_TOKEN" \
--header 'Idempotency-Key: operator-request-1042' \
--header 'Content-Type: application/json' \
--data-raw '{
"amount": "10.00",
"note": "Promotional credit",
"request_id": "11111111-1111-4111-8111-111111111111"
}'// Node.js 18+ · run on your server, never in browser code.
const token = process.env.WHOLLY_TOKEN;
if (!token) throw new Error("Set WHOLLY_TOKEN");
// Keep this key and the exact body for retries; use a new key for each new operation.
const body = `{
"amount": "10.00",
"note": "Promotional credit",
"request_id": "11111111-1111-4111-8111-111111111111"
}`;
const response = await fetch("https://api.example.com/v1/operator/merchants/YOUR_MERCHANT_ID/credits/adjustments", {
method: "POST",
headers: {
"Authorization": `Bearer ${token}`,
"Idempotency-Key": "operator-request-1042",
"Content-Type": "application/json"
},
body,
redirect: "error",
signal: AbortSignal.timeout(30000)
});
if (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);
console.log(await response.json());<?php
// PHP 8+ with the cURL extension; run on your server.
$token = getenv('WHOLLY_TOKEN');
if (!$token) { throw new RuntimeException('Set WHOLLY_TOKEN'); }
// Keep this key and the exact body for retries; use a new key for each new operation.
$body = <<<'JSON'
{
"amount": "10.00",
"note": "Promotional credit",
"request_id": "11111111-1111-4111-8111-111111111111"
}
JSON;
$ch = curl_init("https://api.example.com/v1/operator/merchants/YOUR_MERCHANT_ID/credits/adjustments");
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => 'POST',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_FOLLOWLOCATION => false,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTPHEADER => ['Authorization: Bearer ' . $token, "Idempotency-Key: operator-request-1042", "Content-Type: application/json"],
CURLOPT_POSTFIELDS => $body,
]);
$response = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if ($response === false) { throw new RuntimeException(curl_error($ch)); }
curl_close($ch);
if ($status < 200 || $status >= 300) { throw new RuntimeException("HTTP $status: $response"); }
print_r(json_decode($response, true, 512, JSON_THROW_ON_ERROR));# Python 3 · standard library; run on your server.
import json
import os
from urllib.request import Request, build_opener, HTTPRedirectHandler
class NoRedirect(HTTPRedirectHandler):
def redirect_request(self, req, fp, code, msg, headers, newurl):
return None
# Keep this key and the exact body for retries; use a new key for each new operation.
headers = {
"Idempotency-Key": "operator-request-1042",
"Content-Type": "application/json"
}
headers["Authorization"] = "Bearer " + os.environ["WHOLLY_TOKEN"]
body = """{
"amount": "10.00",
"note": "Promotional credit",
"request_id": "11111111-1111-4111-8111-111111111111"
}""".encode("utf-8")
request = Request("https://api.example.com/v1/operator/merchants/YOUR_MERCHANT_ID/credits/adjustments",
method="POST", headers=headers, data=body)
# Non-2xx responses raise HTTPError. Do not retry writes with a new key.
with build_opener(NoRedirect()).open(request, timeout=30) as response:
print(json.load(response))Esempio di risposta · 200 application/json
{
"balance": "25"
}GETElenca ricariche/v1/operator/merchants/{merchant_id}/topupsSola lettura
Gestisci o esamina la risorsa indicata del commerciante ospitato usando una credenziale operatore separata.
- Richiede topups.read; solo commercianti ospitati autorizzati. Le chiavi operatore non accedono allo spazio dell'attività del proprietario.
- Gli esempi di risposta mostrano alcuni campi. Considera aggiuntivi gli altri campi della risposta.
| Header | Presenza | Regola |
|---|---|---|
| Authorization | obbligatorio | Bearer YOUR_OPERATOR_API_TOKEN |
| Parametro | Tipo / posizione | Regola |
|---|---|---|
| merchant_id | path UUID | UUID canonico minuscolo della risorsa; deve appartenere all'ambito commercianti della credenziale. |
| page, search | query · optional | Pagine a partire da 1, 25 elementi per pagina. La ricerca è supportata per commercianti, utenti, progetti, negozi, wallet, credenziali e webhook; gli elenchi eventi nativi usano i propri filtri. |
Richiesta
: "${WHOLLY_TOKEN:?Set WHOLLY_TOKEN to your server-side API token}"
curl --fail-with-body --max-time 30 \
--request GET \
--url "https://api.example.com/v1/operator/merchants/YOUR_MERCHANT_ID/topups" \
--header "Authorization: Bearer $WHOLLY_TOKEN"// Node.js 18+ · run on your server, never in browser code.
const token = process.env.WHOLLY_TOKEN;
if (!token) throw new Error("Set WHOLLY_TOKEN");
const response = await fetch("https://api.example.com/v1/operator/merchants/YOUR_MERCHANT_ID/topups", {
method: "GET",
headers: {
"Authorization": `Bearer ${token}`
},
redirect: "error",
signal: AbortSignal.timeout(30000)
});
if (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);
console.log(await response.json());<?php
// PHP 8+ with the cURL extension; run on your server.
$token = getenv('WHOLLY_TOKEN');
if (!$token) { throw new RuntimeException('Set WHOLLY_TOKEN'); }
$ch = curl_init("https://api.example.com/v1/operator/merchants/YOUR_MERCHANT_ID/topups");
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => 'GET',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_FOLLOWLOCATION => false,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTPHEADER => ['Authorization: Bearer ' . $token],
]);
$response = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if ($response === false) { throw new RuntimeException(curl_error($ch)); }
curl_close($ch);
if ($status < 200 || $status >= 300) { throw new RuntimeException("HTTP $status: $response"); }
print_r(json_decode($response, true, 512, JSON_THROW_ON_ERROR));# Python 3 · standard library; run on your server.
import json
import os
from urllib.request import Request, build_opener, HTTPRedirectHandler
class NoRedirect(HTTPRedirectHandler):
def redirect_request(self, req, fp, code, msg, headers, newurl):
return None
headers = {}
headers["Authorization"] = "Bearer " + os.environ["WHOLLY_TOKEN"]
request = Request("https://api.example.com/v1/operator/merchants/YOUR_MERCHANT_ID/topups",
method="GET", headers=headers)
# Non-2xx responses raise HTTPError. Do not retry writes with a new key.
with build_opener(NoRedirect()).open(request, timeout=30) as response:
print(json.load(response))Esempio di risposta · 200 application/json
{
"data": [],
"page": 1,
"page_size": 25,
"total": 0
}POSTCrea ricarica/v1/operator/merchants/{merchant_id}/topupsLettura + scrittura
Crea un checkout per credito prepagato; non segnarlo mai manualmente come pagato.
- Richiede topups.write; solo commercianti ospitati autorizzati. Le chiavi operatore non accedono allo spazio dell'attività del proprietario.
- Salva un Idempotency-Key univoco e l'esatto corpo prima dell'invio. I nuovi tentativi non ripetono mai un'azione già confermata. I campi segreti vengono omessi nei replay; se una risposta contenente un segreto è andata persa, esamina la risorsa creata e ruota o riemetti esplicitamente il segreto. Un 409 operator_request_in_progress può indicare una richiesta interrotta con esito sconosciuto: esamina risorsa e audit; non riprovare alla cieca con una nuova chiave.
- Gli esempi di risposta mostrano alcuni campi. Considera aggiuntivi gli altri campi della risposta.
| Header | Presenza | Regola |
|---|---|---|
| Authorization | obbligatorio | Bearer YOUR_OPERATOR_API_TOKEN |
| Idempotency-Key | obbligatorio | 16–128 lettere, cifre, -, _ o .; salvata per questa operazione |
| Parametro | Tipo / posizione | Regola |
|---|---|---|
| merchant_id | path UUID | UUID canonico minuscolo della risorsa; deve appartenere all'ambito commercianti della credenziale. |
| amount | decimal string · required | Almeno un'unità della valuta dei crediti del commerciante. Richiede un negozio di ricezione operatore pronto. |
| request_id | UUID · required | Conserva tra i nuovi tentativi. Restituisce la fattura esistente se già creata. Il credito viene applicato solo dopo il regolamento osservato. |
Richiesta
: "${WHOLLY_TOKEN:?Set WHOLLY_TOKEN to your server-side API token}"
# Keep this key and the exact body for retries; use a new key for each new operation.
curl --fail-with-body --max-time 30 \
--request POST \
--url "https://api.example.com/v1/operator/merchants/YOUR_MERCHANT_ID/topups" \
--header "Authorization: Bearer $WHOLLY_TOKEN" \
--header 'Idempotency-Key: operator-request-1042' \
--header 'Content-Type: application/json' \
--data-raw '{
"amount": "25.00",
"request_id": "11111111-1111-4111-8111-111111111111"
}'// Node.js 18+ · run on your server, never in browser code.
const token = process.env.WHOLLY_TOKEN;
if (!token) throw new Error("Set WHOLLY_TOKEN");
// Keep this key and the exact body for retries; use a new key for each new operation.
const body = `{
"amount": "25.00",
"request_id": "11111111-1111-4111-8111-111111111111"
}`;
const response = await fetch("https://api.example.com/v1/operator/merchants/YOUR_MERCHANT_ID/topups", {
method: "POST",
headers: {
"Authorization": `Bearer ${token}`,
"Idempotency-Key": "operator-request-1042",
"Content-Type": "application/json"
},
body,
redirect: "error",
signal: AbortSignal.timeout(30000)
});
if (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);
console.log(await response.json());<?php
// PHP 8+ with the cURL extension; run on your server.
$token = getenv('WHOLLY_TOKEN');
if (!$token) { throw new RuntimeException('Set WHOLLY_TOKEN'); }
// Keep this key and the exact body for retries; use a new key for each new operation.
$body = <<<'JSON'
{
"amount": "25.00",
"request_id": "11111111-1111-4111-8111-111111111111"
}
JSON;
$ch = curl_init("https://api.example.com/v1/operator/merchants/YOUR_MERCHANT_ID/topups");
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => 'POST',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_FOLLOWLOCATION => false,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTPHEADER => ['Authorization: Bearer ' . $token, "Idempotency-Key: operator-request-1042", "Content-Type: application/json"],
CURLOPT_POSTFIELDS => $body,
]);
$response = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if ($response === false) { throw new RuntimeException(curl_error($ch)); }
curl_close($ch);
if ($status < 200 || $status >= 300) { throw new RuntimeException("HTTP $status: $response"); }
print_r(json_decode($response, true, 512, JSON_THROW_ON_ERROR));# Python 3 · standard library; run on your server.
import json
import os
from urllib.request import Request, build_opener, HTTPRedirectHandler
class NoRedirect(HTTPRedirectHandler):
def redirect_request(self, req, fp, code, msg, headers, newurl):
return None
# Keep this key and the exact body for retries; use a new key for each new operation.
headers = {
"Idempotency-Key": "operator-request-1042",
"Content-Type": "application/json"
}
headers["Authorization"] = "Bearer " + os.environ["WHOLLY_TOKEN"]
body = """{
"amount": "25.00",
"request_id": "11111111-1111-4111-8111-111111111111"
}""".encode("utf-8")
request = Request("https://api.example.com/v1/operator/merchants/YOUR_MERCHANT_ID/topups",
method="POST", headers=headers, data=body)
# Non-2xx responses raise HTTPError. Do not retry writes with a new key.
with build_opener(NoRedirect()).open(request, timeout=30) as response:
print(json.load(response))Esempio di risposta · 201 o 200 application/json
{
"id": "44444444-4444-4444-8444-444444444444",
"invoice_id": "33333333-3333-4333-8333-333333333333",
"checkout_url": "https://pay.example.com/invoice/33333333-3333-4333-8333-333333333333"
}GETRecupera ricarica/v1/operator/merchants/{merchant_id}/topups/{topup_id}Sola lettura
Gestisci o esamina la risorsa indicata del commerciante ospitato usando una credenziale operatore separata.
- Richiede topups.read; solo commercianti ospitati autorizzati. Le chiavi operatore non accedono allo spazio dell'attività del proprietario.
- Gli esempi di risposta mostrano alcuni campi. Considera aggiuntivi gli altri campi della risposta.
| Header | Presenza | Regola |
|---|---|---|
| Authorization | obbligatorio | Bearer YOUR_OPERATOR_API_TOKEN |
| Parametro | Tipo / posizione | Regola |
|---|---|---|
| merchant_id | path UUID | UUID canonico minuscolo della risorsa; deve appartenere all'ambito commercianti della credenziale. |
| topup_id | path UUID | UUID canonico minuscolo della risorsa; deve appartenere all'ambito commercianti della credenziale. |
Richiesta
: "${WHOLLY_TOKEN:?Set WHOLLY_TOKEN to your server-side API token}"
curl --fail-with-body --max-time 30 \
--request GET \
--url "https://api.example.com/v1/operator/merchants/YOUR_MERCHANT_ID/topups/YOUR_TOPUP_ID" \
--header "Authorization: Bearer $WHOLLY_TOKEN"// Node.js 18+ · run on your server, never in browser code.
const token = process.env.WHOLLY_TOKEN;
if (!token) throw new Error("Set WHOLLY_TOKEN");
const response = await fetch("https://api.example.com/v1/operator/merchants/YOUR_MERCHANT_ID/topups/YOUR_TOPUP_ID", {
method: "GET",
headers: {
"Authorization": `Bearer ${token}`
},
redirect: "error",
signal: AbortSignal.timeout(30000)
});
if (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);
console.log(await response.json());<?php
// PHP 8+ with the cURL extension; run on your server.
$token = getenv('WHOLLY_TOKEN');
if (!$token) { throw new RuntimeException('Set WHOLLY_TOKEN'); }
$ch = curl_init("https://api.example.com/v1/operator/merchants/YOUR_MERCHANT_ID/topups/YOUR_TOPUP_ID");
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => 'GET',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_FOLLOWLOCATION => false,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTPHEADER => ['Authorization: Bearer ' . $token],
]);
$response = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if ($response === false) { throw new RuntimeException(curl_error($ch)); }
curl_close($ch);
if ($status < 200 || $status >= 300) { throw new RuntimeException("HTTP $status: $response"); }
print_r(json_decode($response, true, 512, JSON_THROW_ON_ERROR));# Python 3 · standard library; run on your server.
import json
import os
from urllib.request import Request, build_opener, HTTPRedirectHandler
class NoRedirect(HTTPRedirectHandler):
def redirect_request(self, req, fp, code, msg, headers, newurl):
return None
headers = {}
headers["Authorization"] = "Bearer " + os.environ["WHOLLY_TOKEN"]
request = Request("https://api.example.com/v1/operator/merchants/YOUR_MERCHANT_ID/topups/YOUR_TOPUP_ID",
method="GET", headers=headers)
# Non-2xx responses raise HTTPError. Do not retry writes with a new key.
with build_opener(NoRedirect()).open(request, timeout=30) as response:
print(json.load(response))Esempio di risposta · 200 application/json
{
"id": "44444444-4444-4444-8444-444444444444",
"merchant_id": "11111111-1111-4111-8111-111111111111",
"invoice_id": "33333333-3333-4333-8333-333333333333",
"amount": "25",
"currency": "EUR",
"status": "pending",
"invoice_status": "new",
"checkout_url": "https://pay.example.com/invoice/33333333-3333-4333-8333-333333333333"
}GETReport/v1/operator/reportsSola lettura
Leggi la panoramica finanziaria dell'operatore. Richiede accesso a tutti i commercianti ospitati.
- Richiede reports.read; solo commercianti ospitati autorizzati. Le chiavi operatore non accedono allo spazio dell'attività del proprietario.
- Gli esempi di risposta mostrano alcuni campi. Considera aggiuntivi gli altri campi della risposta.
| Header | Presenza | Regola |
|---|---|---|
| Authorization | obbligatorio | Bearer YOUR_OPERATOR_API_TOKEN |
| Parametro | Tipo / posizione | Regola |
|---|---|---|
| period, start, end, currency, timezone, merchant_id | query · optional | Filtri finanziari. period predefinito last30; usa custom con start/end in formato YYYY-MM-DD. Solo credenziali per tutti i commercianti. |
Richiesta
: "${WHOLLY_TOKEN:?Set WHOLLY_TOKEN to your server-side API token}"
curl --fail-with-body --max-time 30 \
--request GET \
--url "https://api.example.com/v1/operator/reports" \
--header "Authorization: Bearer $WHOLLY_TOKEN"// Node.js 18+ · run on your server, never in browser code.
const token = process.env.WHOLLY_TOKEN;
if (!token) throw new Error("Set WHOLLY_TOKEN");
const response = await fetch("https://api.example.com/v1/operator/reports", {
method: "GET",
headers: {
"Authorization": `Bearer ${token}`
},
redirect: "error",
signal: AbortSignal.timeout(30000)
});
if (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);
console.log(await response.json());<?php
// PHP 8+ with the cURL extension; run on your server.
$token = getenv('WHOLLY_TOKEN');
if (!$token) { throw new RuntimeException('Set WHOLLY_TOKEN'); }
$ch = curl_init("https://api.example.com/v1/operator/reports");
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => 'GET',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_FOLLOWLOCATION => false,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTPHEADER => ['Authorization: Bearer ' . $token],
]);
$response = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if ($response === false) { throw new RuntimeException(curl_error($ch)); }
curl_close($ch);
if ($status < 200 || $status >= 300) { throw new RuntimeException("HTTP $status: $response"); }
print_r(json_decode($response, true, 512, JSON_THROW_ON_ERROR));# Python 3 · standard library; run on your server.
import json
import os
from urllib.request import Request, build_opener, HTTPRedirectHandler
class NoRedirect(HTTPRedirectHandler):
def redirect_request(self, req, fp, code, msg, headers, newurl):
return None
headers = {}
headers["Authorization"] = "Bearer " + os.environ["WHOLLY_TOKEN"]
request = Request("https://api.example.com/v1/operator/reports",
method="GET", headers=headers)
# Non-2xx responses raise HTTPError. Do not retry writes with a new key.
with build_opener(NoRedirect()).open(request, timeout=30) as response:
print(json.load(response))Esempio di risposta · 200 application/json
{
"summary": {
"fees": "10",
"costs": "3",
"margin": "7",
"credits": "25",
"pending": 0,
"missing_rates": 0
},
"merchants": [],
"filters": {
"period": "last30",
"currency": "EUR",
"timezone": "UTC"
},
"basis": "first_settlement_latest_net_fees"
}GETElenca audit/v1/operator/auditSola lettura
Gestisci o esamina la risorsa indicata del commerciante ospitato usando una credenziale operatore separata.
- Richiede audit.read; solo commercianti ospitati autorizzati. Le chiavi operatore non accedono allo spazio dell'attività del proprietario.
- Gli esempi di risposta mostrano alcuni campi. Considera aggiuntivi gli altri campi della risposta.
| Header | Presenza | Regola |
|---|---|---|
| Authorization | obbligatorio | Bearer YOUR_OPERATOR_API_TOKEN |
| Parametro | Tipo / posizione | Regola |
|---|---|---|
| merchant_id, event_type / search | query · optional | Filtra commerciante autorizzato, tipo evento esatto (events) o testo dell'azione (audit). Eventi conservati: 30 giorni. |
| page, search | query · optional | Pagine a partire da 1, 25 elementi per pagina. La ricerca è supportata per commercianti, utenti, progetti, negozi, wallet, credenziali e webhook; gli elenchi eventi nativi usano i propri filtri. |
Richiesta
: "${WHOLLY_TOKEN:?Set WHOLLY_TOKEN to your server-side API token}"
curl --fail-with-body --max-time 30 \
--request GET \
--url "https://api.example.com/v1/operator/audit" \
--header "Authorization: Bearer $WHOLLY_TOKEN"// Node.js 18+ · run on your server, never in browser code.
const token = process.env.WHOLLY_TOKEN;
if (!token) throw new Error("Set WHOLLY_TOKEN");
const response = await fetch("https://api.example.com/v1/operator/audit", {
method: "GET",
headers: {
"Authorization": `Bearer ${token}`
},
redirect: "error",
signal: AbortSignal.timeout(30000)
});
if (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);
console.log(await response.json());<?php
// PHP 8+ with the cURL extension; run on your server.
$token = getenv('WHOLLY_TOKEN');
if (!$token) { throw new RuntimeException('Set WHOLLY_TOKEN'); }
$ch = curl_init("https://api.example.com/v1/operator/audit");
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => 'GET',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_FOLLOWLOCATION => false,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTPHEADER => ['Authorization: Bearer ' . $token],
]);
$response = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if ($response === false) { throw new RuntimeException(curl_error($ch)); }
curl_close($ch);
if ($status < 200 || $status >= 300) { throw new RuntimeException("HTTP $status: $response"); }
print_r(json_decode($response, true, 512, JSON_THROW_ON_ERROR));# Python 3 · standard library; run on your server.
import json
import os
from urllib.request import Request, build_opener, HTTPRedirectHandler
class NoRedirect(HTTPRedirectHandler):
def redirect_request(self, req, fp, code, msg, headers, newurl):
return None
headers = {}
headers["Authorization"] = "Bearer " + os.environ["WHOLLY_TOKEN"]
request = Request("https://api.example.com/v1/operator/audit",
method="GET", headers=headers)
# Non-2xx responses raise HTTPError. Do not retry writes with a new key.
with build_opener(NoRedirect()).open(request, timeout=30) as response:
print(json.load(response))Esempio di risposta · 200 application/json
{
"data": [],
"page": 1,
"page_size": 25,
"total": 0
}GETElenca eventi/v1/operator/eventsSola lettura
Gestisci o esamina la risorsa indicata del commerciante ospitato usando una credenziale operatore separata.
- Richiede events.read; solo commercianti ospitati autorizzati. Le chiavi operatore non accedono allo spazio dell'attività del proprietario.
- Gli esempi di risposta mostrano alcuni campi. Considera aggiuntivi gli altri campi della risposta.
| Header | Presenza | Regola |
|---|---|---|
| Authorization | obbligatorio | Bearer YOUR_OPERATOR_API_TOKEN |
| Parametro | Tipo / posizione | Regola |
|---|---|---|
| merchant_id, event_type / search | query · optional | Filtra commerciante autorizzato, tipo evento esatto (events) o testo dell'azione (audit). Eventi conservati: 30 giorni. |
| page, search | query · optional | Pagine a partire da 1, 25 elementi per pagina. La ricerca è supportata per commercianti, utenti, progetti, negozi, wallet, credenziali e webhook; gli elenchi eventi nativi usano i propri filtri. |
Richiesta
: "${WHOLLY_TOKEN:?Set WHOLLY_TOKEN to your server-side API token}"
curl --fail-with-body --max-time 30 \
--request GET \
--url "https://api.example.com/v1/operator/events" \
--header "Authorization: Bearer $WHOLLY_TOKEN"// Node.js 18+ · run on your server, never in browser code.
const token = process.env.WHOLLY_TOKEN;
if (!token) throw new Error("Set WHOLLY_TOKEN");
const response = await fetch("https://api.example.com/v1/operator/events", {
method: "GET",
headers: {
"Authorization": `Bearer ${token}`
},
redirect: "error",
signal: AbortSignal.timeout(30000)
});
if (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);
console.log(await response.json());<?php
// PHP 8+ with the cURL extension; run on your server.
$token = getenv('WHOLLY_TOKEN');
if (!$token) { throw new RuntimeException('Set WHOLLY_TOKEN'); }
$ch = curl_init("https://api.example.com/v1/operator/events");
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => 'GET',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_FOLLOWLOCATION => false,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTPHEADER => ['Authorization: Bearer ' . $token],
]);
$response = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if ($response === false) { throw new RuntimeException(curl_error($ch)); }
curl_close($ch);
if ($status < 200 || $status >= 300) { throw new RuntimeException("HTTP $status: $response"); }
print_r(json_decode($response, true, 512, JSON_THROW_ON_ERROR));# Python 3 · standard library; run on your server.
import json
import os
from urllib.request import Request, build_opener, HTTPRedirectHandler
class NoRedirect(HTTPRedirectHandler):
def redirect_request(self, req, fp, code, msg, headers, newurl):
return None
headers = {}
headers["Authorization"] = "Bearer " + os.environ["WHOLLY_TOKEN"]
request = Request("https://api.example.com/v1/operator/events",
method="GET", headers=headers)
# Non-2xx responses raise HTTPError. Do not retry writes with a new key.
with build_opener(NoRedirect()).open(request, timeout=30) as response:
print(json.load(response))Esempio di risposta · 200 application/json
{
"data": [],
"page": 1,
"page_size": 25,
"total": 0
}GETElenca webhook/v1/operator/webhooksSola lettura
Gestisci o esamina la risorsa indicata del commerciante ospitato usando una credenziale operatore separata.
- Richiede events.read; solo commercianti ospitati autorizzati. Le chiavi operatore non accedono allo spazio dell'attività del proprietario.
- Gli esempi di risposta mostrano alcuni campi. Considera aggiuntivi gli altri campi della risposta.
| Header | Presenza | Regola |
|---|---|---|
| Authorization | obbligatorio | Bearer YOUR_OPERATOR_API_TOKEN |
| Parametro | Tipo / posizione | Regola |
|---|---|---|
| page, search | query · optional | Pagine a partire da 1, 25 elementi per pagina. La ricerca è supportata per commercianti, utenti, progetti, negozi, wallet, credenziali e webhook; gli elenchi eventi nativi usano i propri filtri. |
Richiesta
: "${WHOLLY_TOKEN:?Set WHOLLY_TOKEN to your server-side API token}"
curl --fail-with-body --max-time 30 \
--request GET \
--url "https://api.example.com/v1/operator/webhooks" \
--header "Authorization: Bearer $WHOLLY_TOKEN"// Node.js 18+ · run on your server, never in browser code.
const token = process.env.WHOLLY_TOKEN;
if (!token) throw new Error("Set WHOLLY_TOKEN");
const response = await fetch("https://api.example.com/v1/operator/webhooks", {
method: "GET",
headers: {
"Authorization": `Bearer ${token}`
},
redirect: "error",
signal: AbortSignal.timeout(30000)
});
if (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);
console.log(await response.json());<?php
// PHP 8+ with the cURL extension; run on your server.
$token = getenv('WHOLLY_TOKEN');
if (!$token) { throw new RuntimeException('Set WHOLLY_TOKEN'); }
$ch = curl_init("https://api.example.com/v1/operator/webhooks");
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => 'GET',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_FOLLOWLOCATION => false,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTPHEADER => ['Authorization: Bearer ' . $token],
]);
$response = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if ($response === false) { throw new RuntimeException(curl_error($ch)); }
curl_close($ch);
if ($status < 200 || $status >= 300) { throw new RuntimeException("HTTP $status: $response"); }
print_r(json_decode($response, true, 512, JSON_THROW_ON_ERROR));# Python 3 · standard library; run on your server.
import json
import os
from urllib.request import Request, build_opener, HTTPRedirectHandler
class NoRedirect(HTTPRedirectHandler):
def redirect_request(self, req, fp, code, msg, headers, newurl):
return None
headers = {}
headers["Authorization"] = "Bearer " + os.environ["WHOLLY_TOKEN"]
request = Request("https://api.example.com/v1/operator/webhooks",
method="GET", headers=headers)
# Non-2xx responses raise HTTPError. Do not retry writes with a new key.
with build_opener(NoRedirect()).open(request, timeout=30) as response:
print(json.load(response))Esempio di risposta · 200 application/json
{
"data": [],
"page": 1,
"page_size": 25,
"total": 0
}POSTCrea webhook/v1/operator/webhooksLettura + scrittura
Iscriviti agli eventi futuri del ciclo di vita operatore. Non è un webhook di pagamento del negozio.
- Richiede webhooks.write + events.read; solo commercianti ospitati autorizzati. Le chiavi operatore non accedono allo spazio dell'attività del proprietario.
- Salva un Idempotency-Key univoco e l'esatto corpo prima dell'invio. I nuovi tentativi non ripetono mai un'azione già confermata. I campi segreti vengono omessi nei replay; se una risposta contenente un segreto è andata persa, esamina la risorsa creata e ruota o riemetti esplicitamente il segreto. Un 409 operator_request_in_progress può indicare una richiesta interrotta con esito sconosciuto: esamina risorsa e audit; non riprovare alla cieca con una nuova chiave.
- Gli esempi di risposta mostrano alcuni campi. Considera aggiuntivi gli altri campi della risposta.
| Header | Presenza | Regola |
|---|---|---|
| Authorization | obbligatorio | Bearer YOUR_OPERATOR_API_TOKEN |
| Idempotency-Key | obbligatorio | 16–128 lettere, cifre, -, _ o .; salvata per questa operazione |
| Parametro | Tipo / posizione | Regola |
|---|---|---|
| url | public HTTPS URL · required | Nessuna credenziale, IP privato o reindirizzamento. DNS e IP vengono ricontrollati alla consegna. |
| events | string[] · required | Scegli gli eventi del ciclo di vita nella guida Operatore, non i callback delle fatture. |
| merchant_ids | UUID[] · optional | Vuoto significa tutti i commercianti consentiti da questa credenziale. Le restrizioni dell'ambito attuale vengono ricontrollate. |
| enabled | boolean · default true | Gli endpoint sospesi conservano le consegne in coda; la riattivazione riprende il lavoro conservato ancora valido. |
Richiesta
: "${WHOLLY_TOKEN:?Set WHOLLY_TOKEN to your server-side API token}"
# Keep this key and the exact body for retries; use a new key for each new operation.
curl --fail-with-body --max-time 30 \
--request POST \
--url "https://api.example.com/v1/operator/webhooks" \
--header "Authorization: Bearer $WHOLLY_TOKEN" \
--header 'Idempotency-Key: operator-request-1042' \
--header 'Content-Type: application/json' \
--data-raw '{
"url": "https://shop.example.test/operator-events",
"events": [
"merchant.created",
"topup.settled"
],
"merchant_ids": [],
"enabled": true
}'// Node.js 18+ · run on your server, never in browser code.
const token = process.env.WHOLLY_TOKEN;
if (!token) throw new Error("Set WHOLLY_TOKEN");
// Keep this key and the exact body for retries; use a new key for each new operation.
const body = `{
"url": "https://shop.example.test/operator-events",
"events": [
"merchant.created",
"topup.settled"
],
"merchant_ids": [],
"enabled": true
}`;
const response = await fetch("https://api.example.com/v1/operator/webhooks", {
method: "POST",
headers: {
"Authorization": `Bearer ${token}`,
"Idempotency-Key": "operator-request-1042",
"Content-Type": "application/json"
},
body,
redirect: "error",
signal: AbortSignal.timeout(30000)
});
if (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);
console.log(await response.json());<?php
// PHP 8+ with the cURL extension; run on your server.
$token = getenv('WHOLLY_TOKEN');
if (!$token) { throw new RuntimeException('Set WHOLLY_TOKEN'); }
// Keep this key and the exact body for retries; use a new key for each new operation.
$body = <<<'JSON'
{
"url": "https://shop.example.test/operator-events",
"events": [
"merchant.created",
"topup.settled"
],
"merchant_ids": [],
"enabled": true
}
JSON;
$ch = curl_init("https://api.example.com/v1/operator/webhooks");
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => 'POST',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_FOLLOWLOCATION => false,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTPHEADER => ['Authorization: Bearer ' . $token, "Idempotency-Key: operator-request-1042", "Content-Type: application/json"],
CURLOPT_POSTFIELDS => $body,
]);
$response = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if ($response === false) { throw new RuntimeException(curl_error($ch)); }
curl_close($ch);
if ($status < 200 || $status >= 300) { throw new RuntimeException("HTTP $status: $response"); }
print_r(json_decode($response, true, 512, JSON_THROW_ON_ERROR));# Python 3 · standard library; run on your server.
import json
import os
from urllib.request import Request, build_opener, HTTPRedirectHandler
class NoRedirect(HTTPRedirectHandler):
def redirect_request(self, req, fp, code, msg, headers, newurl):
return None
# Keep this key and the exact body for retries; use a new key for each new operation.
headers = {
"Idempotency-Key": "operator-request-1042",
"Content-Type": "application/json"
}
headers["Authorization"] = "Bearer " + os.environ["WHOLLY_TOKEN"]
body = """{
"url": "https://shop.example.test/operator-events",
"events": [
"merchant.created",
"topup.settled"
],
"merchant_ids": [],
"enabled": true
}""".encode("utf-8")
request = Request("https://api.example.com/v1/operator/webhooks",
method="POST", headers=headers, data=body)
# Non-2xx responses raise HTTPError. Do not retry writes with a new key.
with build_opener(NoRedirect()).open(request, timeout=30) as response:
print(json.load(response))Esempio di risposta · 201 o 200 application/json
{
"id": "11111111-1111-4111-8111-111111111111",
"signing_secret": "wco_whsec_EXAMPLE_ONLY_SAVE_ONCE"
}POSTAggiorna webhook/v1/operator/webhooks/{webhook_id}Lettura + scrittura
Gestisci o esamina la risorsa indicata del commerciante ospitato usando una credenziale operatore separata.
- Richiede webhooks.write + events.read; solo commercianti ospitati autorizzati. Le chiavi operatore non accedono allo spazio dell'attività del proprietario.
- Salva un Idempotency-Key univoco e l'esatto corpo prima dell'invio. I nuovi tentativi non ripetono mai un'azione già confermata. I campi segreti vengono omessi nei replay; se una risposta contenente un segreto è andata persa, esamina la risorsa creata e ruota o riemetti esplicitamente il segreto. Un 409 operator_request_in_progress può indicare una richiesta interrotta con esito sconosciuto: esamina risorsa e audit; non riprovare alla cieca con una nuova chiave.
- Gli esempi di risposta mostrano alcuni campi. Considera aggiuntivi gli altri campi della risposta.
| Header | Presenza | Regola |
|---|---|---|
| Authorization | obbligatorio | Bearer YOUR_OPERATOR_API_TOKEN |
| Idempotency-Key | obbligatorio | 16–128 lettere, cifre, -, _ o .; salvata per questa operazione |
| Parametro | Tipo / posizione | Regola |
|---|---|---|
| webhook_id | path UUID | UUID canonico minuscolo della risorsa; deve appartenere all'ambito commercianti della credenziale. |
| url | public HTTPS URL · required | Nessuna credenziale, IP privato o reindirizzamento. DNS e IP vengono ricontrollati alla consegna. |
| events | string[] · required | Scegli gli eventi del ciclo di vita nella guida Operatore, non i callback delle fatture. |
| merchant_ids | UUID[] · optional | Vuoto significa tutti i commercianti consentiti da questa credenziale. Le restrizioni dell'ambito attuale vengono ricontrollate. |
| enabled | boolean · default true | Gli endpoint sospesi conservano le consegne in coda; la riattivazione riprende il lavoro conservato ancora valido. |
Richiesta
: "${WHOLLY_TOKEN:?Set WHOLLY_TOKEN to your server-side API token}"
# Keep this key and the exact body for retries; use a new key for each new operation.
curl --fail-with-body --max-time 30 \
--request POST \
--url "https://api.example.com/v1/operator/webhooks/YOUR_WEBHOOK_ID" \
--header "Authorization: Bearer $WHOLLY_TOKEN" \
--header 'Idempotency-Key: operator-request-1042' \
--header 'Content-Type: application/json' \
--data-raw '{
"url": "https://shop.example.test/operator-events",
"events": [
"topup.settled"
],
"merchant_ids": [],
"enabled": false
}'// Node.js 18+ · run on your server, never in browser code.
const token = process.env.WHOLLY_TOKEN;
if (!token) throw new Error("Set WHOLLY_TOKEN");
// Keep this key and the exact body for retries; use a new key for each new operation.
const body = `{
"url": "https://shop.example.test/operator-events",
"events": [
"topup.settled"
],
"merchant_ids": [],
"enabled": false
}`;
const response = await fetch("https://api.example.com/v1/operator/webhooks/YOUR_WEBHOOK_ID", {
method: "POST",
headers: {
"Authorization": `Bearer ${token}`,
"Idempotency-Key": "operator-request-1042",
"Content-Type": "application/json"
},
body,
redirect: "error",
signal: AbortSignal.timeout(30000)
});
if (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);
console.log(await response.json());<?php
// PHP 8+ with the cURL extension; run on your server.
$token = getenv('WHOLLY_TOKEN');
if (!$token) { throw new RuntimeException('Set WHOLLY_TOKEN'); }
// Keep this key and the exact body for retries; use a new key for each new operation.
$body = <<<'JSON'
{
"url": "https://shop.example.test/operator-events",
"events": [
"topup.settled"
],
"merchant_ids": [],
"enabled": false
}
JSON;
$ch = curl_init("https://api.example.com/v1/operator/webhooks/YOUR_WEBHOOK_ID");
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => 'POST',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_FOLLOWLOCATION => false,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTPHEADER => ['Authorization: Bearer ' . $token, "Idempotency-Key: operator-request-1042", "Content-Type: application/json"],
CURLOPT_POSTFIELDS => $body,
]);
$response = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if ($response === false) { throw new RuntimeException(curl_error($ch)); }
curl_close($ch);
if ($status < 200 || $status >= 300) { throw new RuntimeException("HTTP $status: $response"); }
print_r(json_decode($response, true, 512, JSON_THROW_ON_ERROR));# Python 3 · standard library; run on your server.
import json
import os
from urllib.request import Request, build_opener, HTTPRedirectHandler
class NoRedirect(HTTPRedirectHandler):
def redirect_request(self, req, fp, code, msg, headers, newurl):
return None
# Keep this key and the exact body for retries; use a new key for each new operation.
headers = {
"Idempotency-Key": "operator-request-1042",
"Content-Type": "application/json"
}
headers["Authorization"] = "Bearer " + os.environ["WHOLLY_TOKEN"]
body = """{
"url": "https://shop.example.test/operator-events",
"events": [
"topup.settled"
],
"merchant_ids": [],
"enabled": false
}""".encode("utf-8")
request = Request("https://api.example.com/v1/operator/webhooks/YOUR_WEBHOOK_ID",
method="POST", headers=headers, data=body)
# Non-2xx responses raise HTTPError. Do not retry writes with a new key.
with build_opener(NoRedirect()).open(request, timeout=30) as response:
print(json.load(response))Esempio di risposta · 200 application/json
{
"id": "44444444-4444-4444-8444-444444444444",
"signing_secret": null
}POSTRuota segreto webhook/v1/operator/webhooks/{webhook_id}/rotateLettura + scrittura
Gestisci o esamina la risorsa indicata del commerciante ospitato usando una credenziale operatore separata.
- Richiede webhooks.write + events.read; solo commercianti ospitati autorizzati. Le chiavi operatore non accedono allo spazio dell'attività del proprietario.
- Salva un Idempotency-Key univoco e l'esatto corpo prima dell'invio. I nuovi tentativi non ripetono mai un'azione già confermata. I campi segreti vengono omessi nei replay; se una risposta contenente un segreto è andata persa, esamina la risorsa creata e ruota o riemetti esplicitamente il segreto. Un 409 operator_request_in_progress può indicare una richiesta interrotta con esito sconosciuto: esamina risorsa e audit; non riprovare alla cieca con una nuova chiave.
- Gli esempi di risposta mostrano alcuni campi. Considera aggiuntivi gli altri campi della risposta.
| Header | Presenza | Regola |
|---|---|---|
| Authorization | obbligatorio | Bearer YOUR_OPERATOR_API_TOKEN |
| Idempotency-Key | obbligatorio | 16–128 lettere, cifre, -, _ o .; salvata per questa operazione |
| Parametro | Tipo / posizione | Regola |
|---|---|---|
| webhook_id | path UUID | UUID canonico minuscolo della risorsa; deve appartenere all'ambito commercianti della credenziale. |
Richiesta
: "${WHOLLY_TOKEN:?Set WHOLLY_TOKEN to your server-side API token}"
# Keep this key and the exact body for retries; use a new key for each new operation.
curl --fail-with-body --max-time 30 \
--request POST \
--url "https://api.example.com/v1/operator/webhooks/YOUR_WEBHOOK_ID/rotate" \
--header "Authorization: Bearer $WHOLLY_TOKEN" \
--header 'Idempotency-Key: operator-request-1042' \
--header 'Content-Type: application/json' \
--data-raw '{}'// Node.js 18+ · run on your server, never in browser code.
const token = process.env.WHOLLY_TOKEN;
if (!token) throw new Error("Set WHOLLY_TOKEN");
// Keep this key and the exact body for retries; use a new key for each new operation.
const body = `{}`;
const response = await fetch("https://api.example.com/v1/operator/webhooks/YOUR_WEBHOOK_ID/rotate", {
method: "POST",
headers: {
"Authorization": `Bearer ${token}`,
"Idempotency-Key": "operator-request-1042",
"Content-Type": "application/json"
},
body,
redirect: "error",
signal: AbortSignal.timeout(30000)
});
if (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);
console.log(await response.json());<?php
// PHP 8+ with the cURL extension; run on your server.
$token = getenv('WHOLLY_TOKEN');
if (!$token) { throw new RuntimeException('Set WHOLLY_TOKEN'); }
// Keep this key and the exact body for retries; use a new key for each new operation.
$body = <<<'JSON'
{}
JSON;
$ch = curl_init("https://api.example.com/v1/operator/webhooks/YOUR_WEBHOOK_ID/rotate");
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => 'POST',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_FOLLOWLOCATION => false,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTPHEADER => ['Authorization: Bearer ' . $token, "Idempotency-Key: operator-request-1042", "Content-Type: application/json"],
CURLOPT_POSTFIELDS => $body,
]);
$response = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if ($response === false) { throw new RuntimeException(curl_error($ch)); }
curl_close($ch);
if ($status < 200 || $status >= 300) { throw new RuntimeException("HTTP $status: $response"); }
print_r(json_decode($response, true, 512, JSON_THROW_ON_ERROR));# Python 3 · standard library; run on your server.
import json
import os
from urllib.request import Request, build_opener, HTTPRedirectHandler
class NoRedirect(HTTPRedirectHandler):
def redirect_request(self, req, fp, code, msg, headers, newurl):
return None
# Keep this key and the exact body for retries; use a new key for each new operation.
headers = {
"Idempotency-Key": "operator-request-1042",
"Content-Type": "application/json"
}
headers["Authorization"] = "Bearer " + os.environ["WHOLLY_TOKEN"]
body = """{}""".encode("utf-8")
request = Request("https://api.example.com/v1/operator/webhooks/YOUR_WEBHOOK_ID/rotate",
method="POST", headers=headers, data=body)
# Non-2xx responses raise HTTPError. Do not retry writes with a new key.
with build_opener(NoRedirect()).open(request, timeout=30) as response:
print(json.load(response))Esempio di risposta · 200 application/json
{
"id": "11111111-1111-4111-8111-111111111111",
"signing_secret": "wco_whsec_EXAMPLE_ONLY_SAVE_ONCE"
}GETElenca consegne webhook/v1/operator/webhooks/{webhook_id}/deliveriesSola lettura
Gestisci o esamina la risorsa indicata del commerciante ospitato usando una credenziale operatore separata.
- Richiede events.read; solo commercianti ospitati autorizzati. Le chiavi operatore non accedono allo spazio dell'attività del proprietario.
- Gli esempi di risposta mostrano alcuni campi. Considera aggiuntivi gli altri campi della risposta.
| Header | Presenza | Regola |
|---|---|---|
| Authorization | obbligatorio | Bearer YOUR_OPERATOR_API_TOKEN |
| Parametro | Tipo / posizione | Regola |
|---|---|---|
| webhook_id | path UUID | UUID canonico minuscolo della risorsa; deve appartenere all'ambito commercianti della credenziale. |
| page | query · optional | Pagine a partire da 1, 25 elementi per pagina. La ricerca è supportata per commercianti, utenti, progetti, negozi, wallet, credenziali e webhook; gli elenchi eventi nativi usano i propri filtri. |
Richiesta
: "${WHOLLY_TOKEN:?Set WHOLLY_TOKEN to your server-side API token}"
curl --fail-with-body --max-time 30 \
--request GET \
--url "https://api.example.com/v1/operator/webhooks/YOUR_WEBHOOK_ID/deliveries" \
--header "Authorization: Bearer $WHOLLY_TOKEN"// Node.js 18+ · run on your server, never in browser code.
const token = process.env.WHOLLY_TOKEN;
if (!token) throw new Error("Set WHOLLY_TOKEN");
const response = await fetch("https://api.example.com/v1/operator/webhooks/YOUR_WEBHOOK_ID/deliveries", {
method: "GET",
headers: {
"Authorization": `Bearer ${token}`
},
redirect: "error",
signal: AbortSignal.timeout(30000)
});
if (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);
console.log(await response.json());<?php
// PHP 8+ with the cURL extension; run on your server.
$token = getenv('WHOLLY_TOKEN');
if (!$token) { throw new RuntimeException('Set WHOLLY_TOKEN'); }
$ch = curl_init("https://api.example.com/v1/operator/webhooks/YOUR_WEBHOOK_ID/deliveries");
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => 'GET',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_FOLLOWLOCATION => false,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTPHEADER => ['Authorization: Bearer ' . $token],
]);
$response = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if ($response === false) { throw new RuntimeException(curl_error($ch)); }
curl_close($ch);
if ($status < 200 || $status >= 300) { throw new RuntimeException("HTTP $status: $response"); }
print_r(json_decode($response, true, 512, JSON_THROW_ON_ERROR));# Python 3 · standard library; run on your server.
import json
import os
from urllib.request import Request, build_opener, HTTPRedirectHandler
class NoRedirect(HTTPRedirectHandler):
def redirect_request(self, req, fp, code, msg, headers, newurl):
return None
headers = {}
headers["Authorization"] = "Bearer " + os.environ["WHOLLY_TOKEN"]
request = Request("https://api.example.com/v1/operator/webhooks/YOUR_WEBHOOK_ID/deliveries",
method="GET", headers=headers)
# Non-2xx responses raise HTTPError. Do not retry writes with a new key.
with build_opener(NoRedirect()).open(request, timeout=30) as response:
print(json.load(response))Esempio di risposta · 200 application/json
{
"data": [],
"page": 1,
"page_size": 25,
"total": 0
}GETElenca progetti/v1/operator/merchants/{merchant_id}/projectsSola lettura
Gestisci o esamina la risorsa indicata del commerciante ospitato usando una credenziale operatore separata.
- Richiede projects.read; solo commercianti ospitati autorizzati. Le chiavi operatore non accedono allo spazio dell'attività del proprietario.
- Gli esempi di risposta mostrano alcuni campi. Considera aggiuntivi gli altri campi della risposta.
| Header | Presenza | Regola |
|---|---|---|
| Authorization | obbligatorio | Bearer YOUR_OPERATOR_API_TOKEN |
| Parametro | Tipo / posizione | Regola |
|---|---|---|
| merchant_id | path UUID | UUID canonico minuscolo della risorsa; deve appartenere all'ambito commercianti della credenziale. |
| page, search | query · optional | Pagine a partire da 1, 25 elementi per pagina. La ricerca è supportata per commercianti, utenti, progetti, negozi, wallet, credenziali e webhook; gli elenchi eventi nativi usano i propri filtri. |
Richiesta
: "${WHOLLY_TOKEN:?Set WHOLLY_TOKEN to your server-side API token}"
curl --fail-with-body --max-time 30 \
--request GET \
--url "https://api.example.com/v1/operator/merchants/YOUR_MERCHANT_ID/projects" \
--header "Authorization: Bearer $WHOLLY_TOKEN"// Node.js 18+ · run on your server, never in browser code.
const token = process.env.WHOLLY_TOKEN;
if (!token) throw new Error("Set WHOLLY_TOKEN");
const response = await fetch("https://api.example.com/v1/operator/merchants/YOUR_MERCHANT_ID/projects", {
method: "GET",
headers: {
"Authorization": `Bearer ${token}`
},
redirect: "error",
signal: AbortSignal.timeout(30000)
});
if (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);
console.log(await response.json());<?php
// PHP 8+ with the cURL extension; run on your server.
$token = getenv('WHOLLY_TOKEN');
if (!$token) { throw new RuntimeException('Set WHOLLY_TOKEN'); }
$ch = curl_init("https://api.example.com/v1/operator/merchants/YOUR_MERCHANT_ID/projects");
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => 'GET',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_FOLLOWLOCATION => false,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTPHEADER => ['Authorization: Bearer ' . $token],
]);
$response = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if ($response === false) { throw new RuntimeException(curl_error($ch)); }
curl_close($ch);
if ($status < 200 || $status >= 300) { throw new RuntimeException("HTTP $status: $response"); }
print_r(json_decode($response, true, 512, JSON_THROW_ON_ERROR));# Python 3 · standard library; run on your server.
import json
import os
from urllib.request import Request, build_opener, HTTPRedirectHandler
class NoRedirect(HTTPRedirectHandler):
def redirect_request(self, req, fp, code, msg, headers, newurl):
return None
headers = {}
headers["Authorization"] = "Bearer " + os.environ["WHOLLY_TOKEN"]
request = Request("https://api.example.com/v1/operator/merchants/YOUR_MERCHANT_ID/projects",
method="GET", headers=headers)
# Non-2xx responses raise HTTPError. Do not retry writes with a new key.
with build_opener(NoRedirect()).open(request, timeout=30) as response:
print(json.load(response))Esempio di risposta · 200 application/json
{
"data": [],
"page": 1,
"page_size": 25,
"total": 0
}POSTCrea progetto/v1/operator/merchants/{merchant_id}/projectsLettura + scrittura
Gestisci o esamina la risorsa indicata del commerciante ospitato usando una credenziale operatore separata.
- Richiede projects.write; solo commercianti ospitati autorizzati. Le chiavi operatore non accedono allo spazio dell'attività del proprietario.
- Salva un Idempotency-Key univoco e l'esatto corpo prima dell'invio. I nuovi tentativi non ripetono mai un'azione già confermata. I campi segreti vengono omessi nei replay; se una risposta contenente un segreto è andata persa, esamina la risorsa creata e ruota o riemetti esplicitamente il segreto. Un 409 operator_request_in_progress può indicare una richiesta interrotta con esito sconosciuto: esamina risorsa e audit; non riprovare alla cieca con una nuova chiave.
- Gli esempi di risposta mostrano alcuni campi. Considera aggiuntivi gli altri campi della risposta.
| Header | Presenza | Regola |
|---|---|---|
| Authorization | obbligatorio | Bearer YOUR_OPERATOR_API_TOKEN |
| Idempotency-Key | obbligatorio | 16–128 lettere, cifre, -, _ o .; salvata per questa operazione |
| Parametro | Tipo / posizione | Regola |
|---|---|---|
| merchant_id | path UUID | UUID canonico minuscolo della risorsa; deve appartenere all'ambito commercianti della credenziale. |
| name, slug | strings · required | Nome e identificativo stabile univoco del progetto. Crea wallet locali usando l'inizializzazione esistente del progetto, non trasferisce mai fondi. |
| enabled, reporting_timezone, checkout_title, checkout_description, checkout_theme, checkout_accent_color | optional | enabled è true per impostazione predefinita; si consiglia di creare in pausa e configurare prima un negozio. |
Richiesta
: "${WHOLLY_TOKEN:?Set WHOLLY_TOKEN to your server-side API token}"
# Keep this key and the exact body for retries; use a new key for each new operation.
curl --fail-with-body --max-time 30 \
--request POST \
--url "https://api.example.com/v1/operator/merchants/YOUR_MERCHANT_ID/projects" \
--header "Authorization: Bearer $WHOLLY_TOKEN" \
--header 'Idempotency-Key: operator-request-1042' \
--header 'Content-Type: application/json' \
--data-raw '{
"name": "Online shop",
"slug": "online-shop",
"enabled": false
}'// Node.js 18+ · run on your server, never in browser code.
const token = process.env.WHOLLY_TOKEN;
if (!token) throw new Error("Set WHOLLY_TOKEN");
// Keep this key and the exact body for retries; use a new key for each new operation.
const body = `{
"name": "Online shop",
"slug": "online-shop",
"enabled": false
}`;
const response = await fetch("https://api.example.com/v1/operator/merchants/YOUR_MERCHANT_ID/projects", {
method: "POST",
headers: {
"Authorization": `Bearer ${token}`,
"Idempotency-Key": "operator-request-1042",
"Content-Type": "application/json"
},
body,
redirect: "error",
signal: AbortSignal.timeout(30000)
});
if (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);
console.log(await response.json());<?php
// PHP 8+ with the cURL extension; run on your server.
$token = getenv('WHOLLY_TOKEN');
if (!$token) { throw new RuntimeException('Set WHOLLY_TOKEN'); }
// Keep this key and the exact body for retries; use a new key for each new operation.
$body = <<<'JSON'
{
"name": "Online shop",
"slug": "online-shop",
"enabled": false
}
JSON;
$ch = curl_init("https://api.example.com/v1/operator/merchants/YOUR_MERCHANT_ID/projects");
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => 'POST',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_FOLLOWLOCATION => false,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTPHEADER => ['Authorization: Bearer ' . $token, "Idempotency-Key: operator-request-1042", "Content-Type: application/json"],
CURLOPT_POSTFIELDS => $body,
]);
$response = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if ($response === false) { throw new RuntimeException(curl_error($ch)); }
curl_close($ch);
if ($status < 200 || $status >= 300) { throw new RuntimeException("HTTP $status: $response"); }
print_r(json_decode($response, true, 512, JSON_THROW_ON_ERROR));# Python 3 · standard library; run on your server.
import json
import os
from urllib.request import Request, build_opener, HTTPRedirectHandler
class NoRedirect(HTTPRedirectHandler):
def redirect_request(self, req, fp, code, msg, headers, newurl):
return None
# Keep this key and the exact body for retries; use a new key for each new operation.
headers = {
"Idempotency-Key": "operator-request-1042",
"Content-Type": "application/json"
}
headers["Authorization"] = "Bearer " + os.environ["WHOLLY_TOKEN"]
body = """{
"name": "Online shop",
"slug": "online-shop",
"enabled": false
}""".encode("utf-8")
request = Request("https://api.example.com/v1/operator/merchants/YOUR_MERCHANT_ID/projects",
method="POST", headers=headers, data=body)
# Non-2xx responses raise HTTPError. Do not retry writes with a new key.
with build_opener(NoRedirect()).open(request, timeout=30) as response:
print(json.load(response))Esempio di risposta · 201 o 200 application/json
{
"id": "33333333-3333-4333-8333-333333333333",
"slug": "example-shop",
"name": "Example shop",
"enabled": false,
"reporting_currency": "EUR",
"reporting_timezone": "Europe/Berlin",
"stores": []
}GETRecupera progetto/v1/operator/merchants/{merchant_id}/projects/{project_id}Sola lettura
Gestisci o esamina la risorsa indicata del commerciante ospitato usando una credenziale operatore separata.
- Richiede projects.read; solo commercianti ospitati autorizzati. Le chiavi operatore non accedono allo spazio dell'attività del proprietario.
- Gli esempi di risposta mostrano alcuni campi. Considera aggiuntivi gli altri campi della risposta.
| Header | Presenza | Regola |
|---|---|---|
| Authorization | obbligatorio | Bearer YOUR_OPERATOR_API_TOKEN |
| Parametro | Tipo / posizione | Regola |
|---|---|---|
| merchant_id | path UUID | UUID canonico minuscolo della risorsa; deve appartenere all'ambito commercianti della credenziale. |
| project_id | path UUID | UUID canonico minuscolo della risorsa; deve appartenere all'ambito commercianti della credenziale. |
Richiesta
: "${WHOLLY_TOKEN:?Set WHOLLY_TOKEN to your server-side API token}"
curl --fail-with-body --max-time 30 \
--request GET \
--url "https://api.example.com/v1/operator/merchants/YOUR_MERCHANT_ID/projects/YOUR_PROJECT_ID" \
--header "Authorization: Bearer $WHOLLY_TOKEN"// Node.js 18+ · run on your server, never in browser code.
const token = process.env.WHOLLY_TOKEN;
if (!token) throw new Error("Set WHOLLY_TOKEN");
const response = await fetch("https://api.example.com/v1/operator/merchants/YOUR_MERCHANT_ID/projects/YOUR_PROJECT_ID", {
method: "GET",
headers: {
"Authorization": `Bearer ${token}`
},
redirect: "error",
signal: AbortSignal.timeout(30000)
});
if (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);
console.log(await response.json());<?php
// PHP 8+ with the cURL extension; run on your server.
$token = getenv('WHOLLY_TOKEN');
if (!$token) { throw new RuntimeException('Set WHOLLY_TOKEN'); }
$ch = curl_init("https://api.example.com/v1/operator/merchants/YOUR_MERCHANT_ID/projects/YOUR_PROJECT_ID");
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => 'GET',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_FOLLOWLOCATION => false,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTPHEADER => ['Authorization: Bearer ' . $token],
]);
$response = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if ($response === false) { throw new RuntimeException(curl_error($ch)); }
curl_close($ch);
if ($status < 200 || $status >= 300) { throw new RuntimeException("HTTP $status: $response"); }
print_r(json_decode($response, true, 512, JSON_THROW_ON_ERROR));# Python 3 · standard library; run on your server.
import json
import os
from urllib.request import Request, build_opener, HTTPRedirectHandler
class NoRedirect(HTTPRedirectHandler):
def redirect_request(self, req, fp, code, msg, headers, newurl):
return None
headers = {}
headers["Authorization"] = "Bearer " + os.environ["WHOLLY_TOKEN"]
request = Request("https://api.example.com/v1/operator/merchants/YOUR_MERCHANT_ID/projects/YOUR_PROJECT_ID",
method="GET", headers=headers)
# Non-2xx responses raise HTTPError. Do not retry writes with a new key.
with build_opener(NoRedirect()).open(request, timeout=30) as response:
print(json.load(response))Esempio di risposta · 200 application/json
{
"id": "33333333-3333-4333-8333-333333333333",
"slug": "example-shop",
"name": "Example shop",
"enabled": false,
"reporting_currency": "EUR",
"reporting_timezone": "Europe/Berlin",
"stores": []
}POSTAggiorna progetto/v1/operator/merchants/{merchant_id}/projects/{project_id}Lettura + scrittura
Gestisci o esamina la risorsa indicata del commerciante ospitato usando una credenziale operatore separata.
- Richiede projects.write; solo commercianti ospitati autorizzati. Le chiavi operatore non accedono allo spazio dell'attività del proprietario.
- Salva un Idempotency-Key univoco e l'esatto corpo prima dell'invio. I nuovi tentativi non ripetono mai un'azione già confermata. I campi segreti vengono omessi nei replay; se una risposta contenente un segreto è andata persa, esamina la risorsa creata e ruota o riemetti esplicitamente il segreto. Un 409 operator_request_in_progress può indicare una richiesta interrotta con esito sconosciuto: esamina risorsa e audit; non riprovare alla cieca con una nuova chiave.
- Gli esempi di risposta mostrano alcuni campi. Considera aggiuntivi gli altri campi della risposta.
| Header | Presenza | Regola |
|---|---|---|
| Authorization | obbligatorio | Bearer YOUR_OPERATOR_API_TOKEN |
| Idempotency-Key | obbligatorio | 16–128 lettere, cifre, -, _ o .; salvata per questa operazione |
| Parametro | Tipo / posizione | Regola |
|---|---|---|
| merchant_id | path UUID | UUID canonico minuscolo della risorsa; deve appartenere all'ambito commercianti della credenziale. |
| project_id | path UUID | UUID canonico minuscolo della risorsa; deve appartenere all'ambito commercianti della credenziale. |
| name, enabled, reporting_currency, reporting_timezone, checkout_title, checkout_description, checkout_theme, checkout_accent_color | optional | Aggiornamento parziale. Identificativo e commerciante proprietario non possono cambiare. |
Richiesta
: "${WHOLLY_TOKEN:?Set WHOLLY_TOKEN to your server-side API token}"
# Keep this key and the exact body for retries; use a new key for each new operation.
curl --fail-with-body --max-time 30 \
--request POST \
--url "https://api.example.com/v1/operator/merchants/YOUR_MERCHANT_ID/projects/YOUR_PROJECT_ID" \
--header "Authorization: Bearer $WHOLLY_TOKEN" \
--header 'Idempotency-Key: operator-request-1042' \
--header 'Content-Type: application/json' \
--data-raw '{
"enabled": true
}'// Node.js 18+ · run on your server, never in browser code.
const token = process.env.WHOLLY_TOKEN;
if (!token) throw new Error("Set WHOLLY_TOKEN");
// Keep this key and the exact body for retries; use a new key for each new operation.
const body = `{
"enabled": true
}`;
const response = await fetch("https://api.example.com/v1/operator/merchants/YOUR_MERCHANT_ID/projects/YOUR_PROJECT_ID", {
method: "POST",
headers: {
"Authorization": `Bearer ${token}`,
"Idempotency-Key": "operator-request-1042",
"Content-Type": "application/json"
},
body,
redirect: "error",
signal: AbortSignal.timeout(30000)
});
if (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);
console.log(await response.json());<?php
// PHP 8+ with the cURL extension; run on your server.
$token = getenv('WHOLLY_TOKEN');
if (!$token) { throw new RuntimeException('Set WHOLLY_TOKEN'); }
// Keep this key and the exact body for retries; use a new key for each new operation.
$body = <<<'JSON'
{
"enabled": true
}
JSON;
$ch = curl_init("https://api.example.com/v1/operator/merchants/YOUR_MERCHANT_ID/projects/YOUR_PROJECT_ID");
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => 'POST',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_FOLLOWLOCATION => false,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTPHEADER => ['Authorization: Bearer ' . $token, "Idempotency-Key: operator-request-1042", "Content-Type: application/json"],
CURLOPT_POSTFIELDS => $body,
]);
$response = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if ($response === false) { throw new RuntimeException(curl_error($ch)); }
curl_close($ch);
if ($status < 200 || $status >= 300) { throw new RuntimeException("HTTP $status: $response"); }
print_r(json_decode($response, true, 512, JSON_THROW_ON_ERROR));# Python 3 · standard library; run on your server.
import json
import os
from urllib.request import Request, build_opener, HTTPRedirectHandler
class NoRedirect(HTTPRedirectHandler):
def redirect_request(self, req, fp, code, msg, headers, newurl):
return None
# Keep this key and the exact body for retries; use a new key for each new operation.
headers = {
"Idempotency-Key": "operator-request-1042",
"Content-Type": "application/json"
}
headers["Authorization"] = "Bearer " + os.environ["WHOLLY_TOKEN"]
body = """{
"enabled": true
}""".encode("utf-8")
request = Request("https://api.example.com/v1/operator/merchants/YOUR_MERCHANT_ID/projects/YOUR_PROJECT_ID",
method="POST", headers=headers, data=body)
# Non-2xx responses raise HTTPError. Do not retry writes with a new key.
with build_opener(NoRedirect()).open(request, timeout=30) as response:
print(json.load(response))Esempio di risposta · 200 application/json
{
"id": "33333333-3333-4333-8333-333333333333",
"slug": "example-shop",
"name": "Example shop",
"enabled": false,
"reporting_currency": "EUR",
"reporting_timezone": "Europe/Berlin",
"stores": []
}GETElenca negozi/v1/operator/merchants/{merchant_id}/projects/{project_id}/storesSola lettura
Gestisci o esamina la risorsa indicata del commerciante ospitato usando una credenziale operatore separata.
- Richiede projects.read; solo commercianti ospitati autorizzati. Le chiavi operatore non accedono allo spazio dell'attività del proprietario.
- Gli esempi di risposta mostrano alcuni campi. Considera aggiuntivi gli altri campi della risposta.
| Header | Presenza | Regola |
|---|---|---|
| Authorization | obbligatorio | Bearer YOUR_OPERATOR_API_TOKEN |
| Parametro | Tipo / posizione | Regola |
|---|---|---|
| merchant_id | path UUID | UUID canonico minuscolo della risorsa; deve appartenere all'ambito commercianti della credenziale. |
| project_id | path UUID | UUID canonico minuscolo della risorsa; deve appartenere all'ambito commercianti della credenziale. |
| page, search | query · optional | Pagine a partire da 1, 25 elementi per pagina. La ricerca è supportata per commercianti, utenti, progetti, negozi, wallet, credenziali e webhook; gli elenchi eventi nativi usano i propri filtri. |
Richiesta
: "${WHOLLY_TOKEN:?Set WHOLLY_TOKEN to your server-side API token}"
curl --fail-with-body --max-time 30 \
--request GET \
--url "https://api.example.com/v1/operator/merchants/YOUR_MERCHANT_ID/projects/YOUR_PROJECT_ID/stores" \
--header "Authorization: Bearer $WHOLLY_TOKEN"// Node.js 18+ · run on your server, never in browser code.
const token = process.env.WHOLLY_TOKEN;
if (!token) throw new Error("Set WHOLLY_TOKEN");
const response = await fetch("https://api.example.com/v1/operator/merchants/YOUR_MERCHANT_ID/projects/YOUR_PROJECT_ID/stores", {
method: "GET",
headers: {
"Authorization": `Bearer ${token}`
},
redirect: "error",
signal: AbortSignal.timeout(30000)
});
if (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);
console.log(await response.json());<?php
// PHP 8+ with the cURL extension; run on your server.
$token = getenv('WHOLLY_TOKEN');
if (!$token) { throw new RuntimeException('Set WHOLLY_TOKEN'); }
$ch = curl_init("https://api.example.com/v1/operator/merchants/YOUR_MERCHANT_ID/projects/YOUR_PROJECT_ID/stores");
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => 'GET',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_FOLLOWLOCATION => false,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTPHEADER => ['Authorization: Bearer ' . $token],
]);
$response = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if ($response === false) { throw new RuntimeException(curl_error($ch)); }
curl_close($ch);
if ($status < 200 || $status >= 300) { throw new RuntimeException("HTTP $status: $response"); }
print_r(json_decode($response, true, 512, JSON_THROW_ON_ERROR));# Python 3 · standard library; run on your server.
import json
import os
from urllib.request import Request, build_opener, HTTPRedirectHandler
class NoRedirect(HTTPRedirectHandler):
def redirect_request(self, req, fp, code, msg, headers, newurl):
return None
headers = {}
headers["Authorization"] = "Bearer " + os.environ["WHOLLY_TOKEN"]
request = Request("https://api.example.com/v1/operator/merchants/YOUR_MERCHANT_ID/projects/YOUR_PROJECT_ID/stores",
method="GET", headers=headers)
# Non-2xx responses raise HTTPError. Do not retry writes with a new key.
with build_opener(NoRedirect()).open(request, timeout=30) as response:
print(json.load(response))Esempio di risposta · 200 application/json
{
"data": [],
"page": 1,
"page_size": 25,
"total": 0
}POSTCrea negozio/v1/operator/merchants/{merchant_id}/projects/{project_id}/storesLettura + scrittura
Gestisci o esamina la risorsa indicata del commerciante ospitato usando una credenziale operatore separata.
- Richiede projects.write; solo commercianti ospitati autorizzati. Le chiavi operatore non accedono allo spazio dell'attività del proprietario.
- Salva un Idempotency-Key univoco e l'esatto corpo prima dell'invio. I nuovi tentativi non ripetono mai un'azione già confermata. I campi segreti vengono omessi nei replay; se una risposta contenente un segreto è andata persa, esamina la risorsa creata e ruota o riemetti esplicitamente il segreto. Un 409 operator_request_in_progress può indicare una richiesta interrotta con esito sconosciuto: esamina risorsa e audit; non riprovare alla cieca con una nuova chiave.
- Gli esempi di risposta mostrano alcuni campi. Considera aggiuntivi gli altri campi della risposta.
| Header | Presenza | Regola |
|---|---|---|
| Authorization | obbligatorio | Bearer YOUR_OPERATOR_API_TOKEN |
| Idempotency-Key | obbligatorio | 16–128 lettere, cifre, -, _ o .; salvata per questa operazione |
| Parametro | Tipo / posizione | Regola |
|---|---|---|
| merchant_id | path UUID | UUID canonico minuscolo della risorsa; deve appartenere all'ambito commercianti della credenziale. |
| project_id | path UUID | UUID canonico minuscolo della risorsa; deve appartenere all'ambito commercianti della credenziale. |
| name, slug | strings · required | Nome del negozio e identificativo stabile. |
| default_currency, invoice_expiry_minutes, exchange_rate_spread_percent, underpayment_tolerance_percent | optional | Usa stringhe decimali per le percentuali. I nuovi negozi ereditano l'aspetto del negozio predefinito del progetto. |
| enabled, is_default, allow_zero_amount_invoices, allow_underpayments, allow_overpayments, allowed_chain_slugs | optional | Configura gli asset accettati con payment-assets; le fatture a importo zero sono disattivate per impostazione predefinita. |
| ipn_enabled, default_ipn_url, default_redirect_url, default_cancel_url, redirect_automatically | optional | IPN e URL di ritorno seguono la convalida URL esistente. Nessun HTML/JavaScript arbitrario. |
| checkout_language, embed_enabled, allowed_embed_origins, domains | optional | Usa una lingua supportata e domini attivi per i rispettivi ruoli; configura esplicitamente le origini di incorporamento. |
Richiesta
: "${WHOLLY_TOKEN:?Set WHOLLY_TOKEN to your server-side API token}"
# Keep this key and the exact body for retries; use a new key for each new operation.
curl --fail-with-body --max-time 30 \
--request POST \
--url "https://api.example.com/v1/operator/merchants/YOUR_MERCHANT_ID/projects/YOUR_PROJECT_ID/stores" \
--header "Authorization: Bearer $WHOLLY_TOKEN" \
--header 'Idempotency-Key: operator-request-1042' \
--header 'Content-Type: application/json' \
--data-raw '{
"name": "Web checkout",
"slug": "web-checkout",
"default_currency": "EUR",
"enabled": false
}'// Node.js 18+ · run on your server, never in browser code.
const token = process.env.WHOLLY_TOKEN;
if (!token) throw new Error("Set WHOLLY_TOKEN");
// Keep this key and the exact body for retries; use a new key for each new operation.
const body = `{
"name": "Web checkout",
"slug": "web-checkout",
"default_currency": "EUR",
"enabled": false
}`;
const response = await fetch("https://api.example.com/v1/operator/merchants/YOUR_MERCHANT_ID/projects/YOUR_PROJECT_ID/stores", {
method: "POST",
headers: {
"Authorization": `Bearer ${token}`,
"Idempotency-Key": "operator-request-1042",
"Content-Type": "application/json"
},
body,
redirect: "error",
signal: AbortSignal.timeout(30000)
});
if (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);
console.log(await response.json());<?php
// PHP 8+ with the cURL extension; run on your server.
$token = getenv('WHOLLY_TOKEN');
if (!$token) { throw new RuntimeException('Set WHOLLY_TOKEN'); }
// Keep this key and the exact body for retries; use a new key for each new operation.
$body = <<<'JSON'
{
"name": "Web checkout",
"slug": "web-checkout",
"default_currency": "EUR",
"enabled": false
}
JSON;
$ch = curl_init("https://api.example.com/v1/operator/merchants/YOUR_MERCHANT_ID/projects/YOUR_PROJECT_ID/stores");
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => 'POST',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_FOLLOWLOCATION => false,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTPHEADER => ['Authorization: Bearer ' . $token, "Idempotency-Key: operator-request-1042", "Content-Type: application/json"],
CURLOPT_POSTFIELDS => $body,
]);
$response = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if ($response === false) { throw new RuntimeException(curl_error($ch)); }
curl_close($ch);
if ($status < 200 || $status >= 300) { throw new RuntimeException("HTTP $status: $response"); }
print_r(json_decode($response, true, 512, JSON_THROW_ON_ERROR));# Python 3 · standard library; run on your server.
import json
import os
from urllib.request import Request, build_opener, HTTPRedirectHandler
class NoRedirect(HTTPRedirectHandler):
def redirect_request(self, req, fp, code, msg, headers, newurl):
return None
# Keep this key and the exact body for retries; use a new key for each new operation.
headers = {
"Idempotency-Key": "operator-request-1042",
"Content-Type": "application/json"
}
headers["Authorization"] = "Bearer " + os.environ["WHOLLY_TOKEN"]
body = """{
"name": "Web checkout",
"slug": "web-checkout",
"default_currency": "EUR",
"enabled": false
}""".encode("utf-8")
request = Request("https://api.example.com/v1/operator/merchants/YOUR_MERCHANT_ID/projects/YOUR_PROJECT_ID/stores",
method="POST", headers=headers, data=body)
# Non-2xx responses raise HTTPError. Do not retry writes with a new key.
with build_opener(NoRedirect()).open(request, timeout=30) as response:
print(json.load(response))Esempio di risposta · 201 o 200 application/json
{
"id": "44444444-4444-4444-8444-444444444444",
"project_id": "33333333-3333-4333-8333-333333333333",
"slug": "online",
"name": "Online shop",
"enabled": false,
"default_currency": "EUR",
"invoice_expiry_minutes": 60
}GETRecupera negozio/v1/operator/merchants/{merchant_id}/projects/{project_id}/stores/{store_id}Sola lettura
Gestisci o esamina la risorsa indicata del commerciante ospitato usando una credenziale operatore separata.
- Richiede projects.read; solo commercianti ospitati autorizzati. Le chiavi operatore non accedono allo spazio dell'attività del proprietario.
- Gli esempi di risposta mostrano alcuni campi. Considera aggiuntivi gli altri campi della risposta.
| Header | Presenza | Regola |
|---|---|---|
| Authorization | obbligatorio | Bearer YOUR_OPERATOR_API_TOKEN |
| Parametro | Tipo / posizione | Regola |
|---|---|---|
| merchant_id | path UUID | UUID canonico minuscolo della risorsa; deve appartenere all'ambito commercianti della credenziale. |
| project_id | path UUID | UUID canonico minuscolo della risorsa; deve appartenere all'ambito commercianti della credenziale. |
| store_id | path UUID | UUID canonico minuscolo della risorsa; deve appartenere all'ambito commercianti della credenziale. |
Richiesta
: "${WHOLLY_TOKEN:?Set WHOLLY_TOKEN to your server-side API token}"
curl --fail-with-body --max-time 30 \
--request GET \
--url "https://api.example.com/v1/operator/merchants/YOUR_MERCHANT_ID/projects/YOUR_PROJECT_ID/stores/YOUR_STORE_ID" \
--header "Authorization: Bearer $WHOLLY_TOKEN"// Node.js 18+ · run on your server, never in browser code.
const token = process.env.WHOLLY_TOKEN;
if (!token) throw new Error("Set WHOLLY_TOKEN");
const response = await fetch("https://api.example.com/v1/operator/merchants/YOUR_MERCHANT_ID/projects/YOUR_PROJECT_ID/stores/YOUR_STORE_ID", {
method: "GET",
headers: {
"Authorization": `Bearer ${token}`
},
redirect: "error",
signal: AbortSignal.timeout(30000)
});
if (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);
console.log(await response.json());<?php
// PHP 8+ with the cURL extension; run on your server.
$token = getenv('WHOLLY_TOKEN');
if (!$token) { throw new RuntimeException('Set WHOLLY_TOKEN'); }
$ch = curl_init("https://api.example.com/v1/operator/merchants/YOUR_MERCHANT_ID/projects/YOUR_PROJECT_ID/stores/YOUR_STORE_ID");
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => 'GET',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_FOLLOWLOCATION => false,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTPHEADER => ['Authorization: Bearer ' . $token],
]);
$response = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if ($response === false) { throw new RuntimeException(curl_error($ch)); }
curl_close($ch);
if ($status < 200 || $status >= 300) { throw new RuntimeException("HTTP $status: $response"); }
print_r(json_decode($response, true, 512, JSON_THROW_ON_ERROR));# Python 3 · standard library; run on your server.
import json
import os
from urllib.request import Request, build_opener, HTTPRedirectHandler
class NoRedirect(HTTPRedirectHandler):
def redirect_request(self, req, fp, code, msg, headers, newurl):
return None
headers = {}
headers["Authorization"] = "Bearer " + os.environ["WHOLLY_TOKEN"]
request = Request("https://api.example.com/v1/operator/merchants/YOUR_MERCHANT_ID/projects/YOUR_PROJECT_ID/stores/YOUR_STORE_ID",
method="GET", headers=headers)
# Non-2xx responses raise HTTPError. Do not retry writes with a new key.
with build_opener(NoRedirect()).open(request, timeout=30) as response:
print(json.load(response))Esempio di risposta · 200 application/json
{
"id": "44444444-4444-4444-8444-444444444444",
"project_id": "33333333-3333-4333-8333-333333333333",
"slug": "online",
"name": "Online shop",
"enabled": false,
"default_currency": "EUR",
"invoice_expiry_minutes": 60
}POSTAggiorna negozio/v1/operator/merchants/{merchant_id}/projects/{project_id}/stores/{store_id}Lettura + scrittura
Gestisci o esamina la risorsa indicata del commerciante ospitato usando una credenziale operatore separata.
- Richiede projects.write; solo commercianti ospitati autorizzati. Le chiavi operatore non accedono allo spazio dell'attività del proprietario.
- Salva un Idempotency-Key univoco e l'esatto corpo prima dell'invio. I nuovi tentativi non ripetono mai un'azione già confermata. I campi segreti vengono omessi nei replay; se una risposta contenente un segreto è andata persa, esamina la risorsa creata e ruota o riemetti esplicitamente il segreto. Un 409 operator_request_in_progress può indicare una richiesta interrotta con esito sconosciuto: esamina risorsa e audit; non riprovare alla cieca con una nuova chiave.
- Gli esempi di risposta mostrano alcuni campi. Considera aggiuntivi gli altri campi della risposta.
| Header | Presenza | Regola |
|---|---|---|
| Authorization | obbligatorio | Bearer YOUR_OPERATOR_API_TOKEN |
| Idempotency-Key | obbligatorio | 16–128 lettere, cifre, -, _ o .; salvata per questa operazione |
| Parametro | Tipo / posizione | Regola |
|---|---|---|
| merchant_id | path UUID | UUID canonico minuscolo della risorsa; deve appartenere all'ambito commercianti della credenziale. |
| project_id | path UUID | UUID canonico minuscolo della risorsa; deve appartenere all'ambito commercianti della credenziale. |
| store_id | path UUID | UUID canonico minuscolo della risorsa; deve appartenere all'ambito commercianti della credenziale. |
| store fields | optional | Stesse impostazioni modificabili della creazione negozio, eccetto slug. Cambiano solo i campi forniti. |
Richiesta
: "${WHOLLY_TOKEN:?Set WHOLLY_TOKEN to your server-side API token}"
# Keep this key and the exact body for retries; use a new key for each new operation.
curl --fail-with-body --max-time 30 \
--request POST \
--url "https://api.example.com/v1/operator/merchants/YOUR_MERCHANT_ID/projects/YOUR_PROJECT_ID/stores/YOUR_STORE_ID" \
--header "Authorization: Bearer $WHOLLY_TOKEN" \
--header 'Idempotency-Key: operator-request-1042' \
--header 'Content-Type: application/json' \
--data-raw '{
"enabled": true
}'// Node.js 18+ · run on your server, never in browser code.
const token = process.env.WHOLLY_TOKEN;
if (!token) throw new Error("Set WHOLLY_TOKEN");
// Keep this key and the exact body for retries; use a new key for each new operation.
const body = `{
"enabled": true
}`;
const response = await fetch("https://api.example.com/v1/operator/merchants/YOUR_MERCHANT_ID/projects/YOUR_PROJECT_ID/stores/YOUR_STORE_ID", {
method: "POST",
headers: {
"Authorization": `Bearer ${token}`,
"Idempotency-Key": "operator-request-1042",
"Content-Type": "application/json"
},
body,
redirect: "error",
signal: AbortSignal.timeout(30000)
});
if (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);
console.log(await response.json());<?php
// PHP 8+ with the cURL extension; run on your server.
$token = getenv('WHOLLY_TOKEN');
if (!$token) { throw new RuntimeException('Set WHOLLY_TOKEN'); }
// Keep this key and the exact body for retries; use a new key for each new operation.
$body = <<<'JSON'
{
"enabled": true
}
JSON;
$ch = curl_init("https://api.example.com/v1/operator/merchants/YOUR_MERCHANT_ID/projects/YOUR_PROJECT_ID/stores/YOUR_STORE_ID");
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => 'POST',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_FOLLOWLOCATION => false,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTPHEADER => ['Authorization: Bearer ' . $token, "Idempotency-Key: operator-request-1042", "Content-Type: application/json"],
CURLOPT_POSTFIELDS => $body,
]);
$response = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if ($response === false) { throw new RuntimeException(curl_error($ch)); }
curl_close($ch);
if ($status < 200 || $status >= 300) { throw new RuntimeException("HTTP $status: $response"); }
print_r(json_decode($response, true, 512, JSON_THROW_ON_ERROR));# Python 3 · standard library; run on your server.
import json
import os
from urllib.request import Request, build_opener, HTTPRedirectHandler
class NoRedirect(HTTPRedirectHandler):
def redirect_request(self, req, fp, code, msg, headers, newurl):
return None
# Keep this key and the exact body for retries; use a new key for each new operation.
headers = {
"Idempotency-Key": "operator-request-1042",
"Content-Type": "application/json"
}
headers["Authorization"] = "Bearer " + os.environ["WHOLLY_TOKEN"]
body = """{
"enabled": true
}""".encode("utf-8")
request = Request("https://api.example.com/v1/operator/merchants/YOUR_MERCHANT_ID/projects/YOUR_PROJECT_ID/stores/YOUR_STORE_ID",
method="POST", headers=headers, data=body)
# Non-2xx responses raise HTTPError. Do not retry writes with a new key.
with build_opener(NoRedirect()).open(request, timeout=30) as response:
print(json.load(response))Esempio di risposta · 200 application/json
{
"id": "44444444-4444-4444-8444-444444444444",
"project_id": "33333333-3333-4333-8333-333333333333",
"slug": "online",
"name": "Online shop",
"enabled": false,
"default_currency": "EUR",
"invoice_expiry_minutes": 60
}GETRecupera aspetto del negozio/v1/operator/merchants/{merchant_id}/projects/{project_id}/stores/{store_id}/checkout-appearanceSola lettura
Gestisci o esamina la risorsa indicata del commerciante ospitato usando una credenziale operatore separata.
- Richiede projects.read; solo commercianti ospitati autorizzati. Le chiavi operatore non accedono allo spazio dell'attività del proprietario.
- Gli esempi di risposta mostrano alcuni campi. Considera aggiuntivi gli altri campi della risposta.
| Header | Presenza | Regola |
|---|---|---|
| Authorization | obbligatorio | Bearer YOUR_OPERATOR_API_TOKEN |
| Parametro | Tipo / posizione | Regola |
|---|---|---|
| merchant_id | path UUID | UUID canonico minuscolo della risorsa; deve appartenere all'ambito commercianti della credenziale. |
| project_id | path UUID | UUID canonico minuscolo della risorsa; deve appartenere all'ambito commercianti della credenziale. |
| store_id | path UUID | UUID canonico minuscolo della risorsa; deve appartenere all'ambito commercianti della credenziale. |
Richiesta
: "${WHOLLY_TOKEN:?Set WHOLLY_TOKEN to your server-side API token}"
curl --fail-with-body --max-time 30 \
--request GET \
--url "https://api.example.com/v1/operator/merchants/YOUR_MERCHANT_ID/projects/YOUR_PROJECT_ID/stores/YOUR_STORE_ID/checkout-appearance" \
--header "Authorization: Bearer $WHOLLY_TOKEN"// Node.js 18+ · run on your server, never in browser code.
const token = process.env.WHOLLY_TOKEN;
if (!token) throw new Error("Set WHOLLY_TOKEN");
const response = await fetch("https://api.example.com/v1/operator/merchants/YOUR_MERCHANT_ID/projects/YOUR_PROJECT_ID/stores/YOUR_STORE_ID/checkout-appearance", {
method: "GET",
headers: {
"Authorization": `Bearer ${token}`
},
redirect: "error",
signal: AbortSignal.timeout(30000)
});
if (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);
console.log(await response.json());<?php
// PHP 8+ with the cURL extension; run on your server.
$token = getenv('WHOLLY_TOKEN');
if (!$token) { throw new RuntimeException('Set WHOLLY_TOKEN'); }
$ch = curl_init("https://api.example.com/v1/operator/merchants/YOUR_MERCHANT_ID/projects/YOUR_PROJECT_ID/stores/YOUR_STORE_ID/checkout-appearance");
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => 'GET',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_FOLLOWLOCATION => false,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTPHEADER => ['Authorization: Bearer ' . $token],
]);
$response = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if ($response === false) { throw new RuntimeException(curl_error($ch)); }
curl_close($ch);
if ($status < 200 || $status >= 300) { throw new RuntimeException("HTTP $status: $response"); }
print_r(json_decode($response, true, 512, JSON_THROW_ON_ERROR));# Python 3 · standard library; run on your server.
import json
import os
from urllib.request import Request, build_opener, HTTPRedirectHandler
class NoRedirect(HTTPRedirectHandler):
def redirect_request(self, req, fp, code, msg, headers, newurl):
return None
headers = {}
headers["Authorization"] = "Bearer " + os.environ["WHOLLY_TOKEN"]
request = Request("https://api.example.com/v1/operator/merchants/YOUR_MERCHANT_ID/projects/YOUR_PROJECT_ID/stores/YOUR_STORE_ID/checkout-appearance",
method="GET", headers=headers)
# Non-2xx responses raise HTTPError. Do not retry writes with a new key.
with build_opener(NoRedirect()).open(request, timeout=30) as response:
print(json.load(response))Esempio di risposta · 200 application/json
{
"revision": 1,
"settings": {
"inherit_default_store": true
},
"effective": {
"title": "Pay securely"
}
}POSTAggiorna aspetto del negozio/v1/operator/merchants/{merchant_id}/projects/{project_id}/stores/{store_id}/checkout-appearanceLettura + scrittura
Salva un design del negozio convalidato e protetto da revisione.
- Richiede projects.write; solo commercianti ospitati autorizzati. Le chiavi operatore non accedono allo spazio dell'attività del proprietario.
- Salva un Idempotency-Key univoco e l'esatto corpo prima dell'invio. I nuovi tentativi non ripetono mai un'azione già confermata. I campi segreti vengono omessi nei replay; se una risposta contenente un segreto è andata persa, esamina la risorsa creata e ruota o riemetti esplicitamente il segreto. Un 409 operator_request_in_progress può indicare una richiesta interrotta con esito sconosciuto: esamina risorsa e audit; non riprovare alla cieca con una nuova chiave.
- Gli esempi di risposta mostrano alcuni campi. Considera aggiuntivi gli altri campi della risposta.
| Header | Presenza | Regola |
|---|---|---|
| Authorization | obbligatorio | Bearer YOUR_OPERATOR_API_TOKEN |
| Idempotency-Key | obbligatorio | 16–128 lettere, cifre, -, _ o .; salvata per questa operazione |
| Parametro | Tipo / posizione | Regola |
|---|---|---|
| merchant_id | path UUID | UUID canonico minuscolo della risorsa; deve appartenere all'ambito commercianti della credenziale. |
| project_id | path UUID | UUID canonico minuscolo della risorsa; deve appartenere all'ambito commercianti della credenziale. |
| store_id | path UUID | UUID canonico minuscolo della risorsa; deve appartenere all'ambito commercianti della credenziale. |
| revision | integer · required | Leggi prima la revisione attuale con GET. Una revisione obsoleta fallisce senza sovrascrivere il lavoro di un altro editor. |
| settings | appearance object · required | Aspetto del checkout convalidato, inclusi inherit_default_store, marchio, intro/outro, dimensioni caratteri e visibilità. Nessun HTML/JavaScript arbitrario. Il caricamento dei byte delle immagini è disponibile solo nella console. |
Richiesta
: "${WHOLLY_TOKEN:?Set WHOLLY_TOKEN to your server-side API token}"
# Keep this key and the exact body for retries; use a new key for each new operation.
curl --fail-with-body --max-time 30 \
--request POST \
--url "https://api.example.com/v1/operator/merchants/YOUR_MERCHANT_ID/projects/YOUR_PROJECT_ID/stores/YOUR_STORE_ID/checkout-appearance" \
--header "Authorization: Bearer $WHOLLY_TOKEN" \
--header 'Idempotency-Key: operator-request-1042' \
--header 'Content-Type: application/json' \
--data-raw '{
"revision": 1,
"settings": {
"inherit_default_store": false,
"title": "Pay securely",
"theme": "light"
}
}'// Node.js 18+ · run on your server, never in browser code.
const token = process.env.WHOLLY_TOKEN;
if (!token) throw new Error("Set WHOLLY_TOKEN");
// Keep this key and the exact body for retries; use a new key for each new operation.
const body = `{
"revision": 1,
"settings": {
"inherit_default_store": false,
"title": "Pay securely",
"theme": "light"
}
}`;
const response = await fetch("https://api.example.com/v1/operator/merchants/YOUR_MERCHANT_ID/projects/YOUR_PROJECT_ID/stores/YOUR_STORE_ID/checkout-appearance", {
method: "POST",
headers: {
"Authorization": `Bearer ${token}`,
"Idempotency-Key": "operator-request-1042",
"Content-Type": "application/json"
},
body,
redirect: "error",
signal: AbortSignal.timeout(30000)
});
if (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);
console.log(await response.json());<?php
// PHP 8+ with the cURL extension; run on your server.
$token = getenv('WHOLLY_TOKEN');
if (!$token) { throw new RuntimeException('Set WHOLLY_TOKEN'); }
// Keep this key and the exact body for retries; use a new key for each new operation.
$body = <<<'JSON'
{
"revision": 1,
"settings": {
"inherit_default_store": false,
"title": "Pay securely",
"theme": "light"
}
}
JSON;
$ch = curl_init("https://api.example.com/v1/operator/merchants/YOUR_MERCHANT_ID/projects/YOUR_PROJECT_ID/stores/YOUR_STORE_ID/checkout-appearance");
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => 'POST',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_FOLLOWLOCATION => false,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTPHEADER => ['Authorization: Bearer ' . $token, "Idempotency-Key: operator-request-1042", "Content-Type: application/json"],
CURLOPT_POSTFIELDS => $body,
]);
$response = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if ($response === false) { throw new RuntimeException(curl_error($ch)); }
curl_close($ch);
if ($status < 200 || $status >= 300) { throw new RuntimeException("HTTP $status: $response"); }
print_r(json_decode($response, true, 512, JSON_THROW_ON_ERROR));# Python 3 · standard library; run on your server.
import json
import os
from urllib.request import Request, build_opener, HTTPRedirectHandler
class NoRedirect(HTTPRedirectHandler):
def redirect_request(self, req, fp, code, msg, headers, newurl):
return None
# Keep this key and the exact body for retries; use a new key for each new operation.
headers = {
"Idempotency-Key": "operator-request-1042",
"Content-Type": "application/json"
}
headers["Authorization"] = "Bearer " + os.environ["WHOLLY_TOKEN"]
body = """{
"revision": 1,
"settings": {
"inherit_default_store": false,
"title": "Pay securely",
"theme": "light"
}
}""".encode("utf-8")
request = Request("https://api.example.com/v1/operator/merchants/YOUR_MERCHANT_ID/projects/YOUR_PROJECT_ID/stores/YOUR_STORE_ID/checkout-appearance",
method="POST", headers=headers, data=body)
# Non-2xx responses raise HTTPError. Do not retry writes with a new key.
with build_opener(NoRedirect()).open(request, timeout=30) as response:
print(json.load(response))Esempio di risposta · 200 application/json
{
"settings": {
"inherit_default_store": false,
"title": "Pay securely",
"theme": "light"
},
"revision": 2
}GETElenca asset di pagamento del negozio/v1/operator/merchants/{merchant_id}/projects/{project_id}/stores/{store_id}/payment-assetsSola lettura
Gestisci o esamina la risorsa indicata del commerciante ospitato usando una credenziale operatore separata.
- Richiede projects.read; solo commercianti ospitati autorizzati. Le chiavi operatore non accedono allo spazio dell'attività del proprietario.
- Gli esempi di risposta mostrano alcuni campi. Considera aggiuntivi gli altri campi della risposta.
| Header | Presenza | Regola |
|---|---|---|
| Authorization | obbligatorio | Bearer YOUR_OPERATOR_API_TOKEN |
| Parametro | Tipo / posizione | Regola |
|---|---|---|
| merchant_id | path UUID | UUID canonico minuscolo della risorsa; deve appartenere all'ambito commercianti della credenziale. |
| project_id | path UUID | UUID canonico minuscolo della risorsa; deve appartenere all'ambito commercianti della credenziale. |
| store_id | path UUID | UUID canonico minuscolo della risorsa; deve appartenere all'ambito commercianti della credenziale. |
Richiesta
: "${WHOLLY_TOKEN:?Set WHOLLY_TOKEN to your server-side API token}"
curl --fail-with-body --max-time 30 \
--request GET \
--url "https://api.example.com/v1/operator/merchants/YOUR_MERCHANT_ID/projects/YOUR_PROJECT_ID/stores/YOUR_STORE_ID/payment-assets" \
--header "Authorization: Bearer $WHOLLY_TOKEN"// Node.js 18+ · run on your server, never in browser code.
const token = process.env.WHOLLY_TOKEN;
if (!token) throw new Error("Set WHOLLY_TOKEN");
const response = await fetch("https://api.example.com/v1/operator/merchants/YOUR_MERCHANT_ID/projects/YOUR_PROJECT_ID/stores/YOUR_STORE_ID/payment-assets", {
method: "GET",
headers: {
"Authorization": `Bearer ${token}`
},
redirect: "error",
signal: AbortSignal.timeout(30000)
});
if (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);
console.log(await response.json());<?php
// PHP 8+ with the cURL extension; run on your server.
$token = getenv('WHOLLY_TOKEN');
if (!$token) { throw new RuntimeException('Set WHOLLY_TOKEN'); }
$ch = curl_init("https://api.example.com/v1/operator/merchants/YOUR_MERCHANT_ID/projects/YOUR_PROJECT_ID/stores/YOUR_STORE_ID/payment-assets");
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => 'GET',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_FOLLOWLOCATION => false,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTPHEADER => ['Authorization: Bearer ' . $token],
]);
$response = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if ($response === false) { throw new RuntimeException(curl_error($ch)); }
curl_close($ch);
if ($status < 200 || $status >= 300) { throw new RuntimeException("HTTP $status: $response"); }
print_r(json_decode($response, true, 512, JSON_THROW_ON_ERROR));# Python 3 · standard library; run on your server.
import json
import os
from urllib.request import Request, build_opener, HTTPRedirectHandler
class NoRedirect(HTTPRedirectHandler):
def redirect_request(self, req, fp, code, msg, headers, newurl):
return None
headers = {}
headers["Authorization"] = "Bearer " + os.environ["WHOLLY_TOKEN"]
request = Request("https://api.example.com/v1/operator/merchants/YOUR_MERCHANT_ID/projects/YOUR_PROJECT_ID/stores/YOUR_STORE_ID/payment-assets",
method="GET", headers=headers)
# Non-2xx responses raise HTTPError. Do not retry writes with a new key.
with build_opener(NoRedirect()).open(request, timeout=30) as response:
print(json.load(response))Esempio di risposta · 200 application/json
{
"data": []
}POSTAggiorna asset di pagamento del negozio/v1/operator/merchants/{merchant_id}/projects/{project_id}/stores/{store_id}/payment-assetsLettura + scrittura
Gestisci o esamina la risorsa indicata del commerciante ospitato usando una credenziale operatore separata.
- Richiede projects.write; solo commercianti ospitati autorizzati. Le chiavi operatore non accedono allo spazio dell'attività del proprietario.
- Salva un Idempotency-Key univoco e l'esatto corpo prima dell'invio. I nuovi tentativi non ripetono mai un'azione già confermata. I campi segreti vengono omessi nei replay; se una risposta contenente un segreto è andata persa, esamina la risorsa creata e ruota o riemetti esplicitamente il segreto. Un 409 operator_request_in_progress può indicare una richiesta interrotta con esito sconosciuto: esamina risorsa e audit; non riprovare alla cieca con una nuova chiave.
- Gli esempi di risposta mostrano alcuni campi. Considera aggiuntivi gli altri campi della risposta.
| Header | Presenza | Regola |
|---|---|---|
| Authorization | obbligatorio | Bearer YOUR_OPERATOR_API_TOKEN |
| Idempotency-Key | obbligatorio | 16–128 lettere, cifre, -, _ o .; salvata per questa operazione |
| Parametro | Tipo / posizione | Regola |
|---|---|---|
| merchant_id | path UUID | UUID canonico minuscolo della risorsa; deve appartenere all'ambito commercianti della credenziale. |
| project_id | path UUID | UUID canonico minuscolo della risorsa; deve appartenere all'ambito commercianti della credenziale. |
| store_id | path UUID | UUID canonico minuscolo della risorsa; deve appartenere all'ambito commercianti della credenziale. |
| assets | array · required | Sostituzione completa dei metodi on-chain: UUID asset_id e display_order. [] rimuove gli asset on-chain accettati. Solo asset di progetto verificati; non configura Lightning. |
Richiesta
: "${WHOLLY_TOKEN:?Set WHOLLY_TOKEN to your server-side API token}"
# Keep this key and the exact body for retries; use a new key for each new operation.
curl --fail-with-body --max-time 30 \
--request POST \
--url "https://api.example.com/v1/operator/merchants/YOUR_MERCHANT_ID/projects/YOUR_PROJECT_ID/stores/YOUR_STORE_ID/payment-assets" \
--header "Authorization: Bearer $WHOLLY_TOKEN" \
--header 'Idempotency-Key: operator-request-1042' \
--header 'Content-Type: application/json' \
--data-raw '{
"assets": [
{
"asset_id": "11111111-1111-4111-8111-111111111111",
"display_order": 0
}
]
}'// Node.js 18+ · run on your server, never in browser code.
const token = process.env.WHOLLY_TOKEN;
if (!token) throw new Error("Set WHOLLY_TOKEN");
// Keep this key and the exact body for retries; use a new key for each new operation.
const body = `{
"assets": [
{
"asset_id": "11111111-1111-4111-8111-111111111111",
"display_order": 0
}
]
}`;
const response = await fetch("https://api.example.com/v1/operator/merchants/YOUR_MERCHANT_ID/projects/YOUR_PROJECT_ID/stores/YOUR_STORE_ID/payment-assets", {
method: "POST",
headers: {
"Authorization": `Bearer ${token}`,
"Idempotency-Key": "operator-request-1042",
"Content-Type": "application/json"
},
body,
redirect: "error",
signal: AbortSignal.timeout(30000)
});
if (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);
console.log(await response.json());<?php
// PHP 8+ with the cURL extension; run on your server.
$token = getenv('WHOLLY_TOKEN');
if (!$token) { throw new RuntimeException('Set WHOLLY_TOKEN'); }
// Keep this key and the exact body for retries; use a new key for each new operation.
$body = <<<'JSON'
{
"assets": [
{
"asset_id": "11111111-1111-4111-8111-111111111111",
"display_order": 0
}
]
}
JSON;
$ch = curl_init("https://api.example.com/v1/operator/merchants/YOUR_MERCHANT_ID/projects/YOUR_PROJECT_ID/stores/YOUR_STORE_ID/payment-assets");
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => 'POST',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_FOLLOWLOCATION => false,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTPHEADER => ['Authorization: Bearer ' . $token, "Idempotency-Key: operator-request-1042", "Content-Type: application/json"],
CURLOPT_POSTFIELDS => $body,
]);
$response = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if ($response === false) { throw new RuntimeException(curl_error($ch)); }
curl_close($ch);
if ($status < 200 || $status >= 300) { throw new RuntimeException("HTTP $status: $response"); }
print_r(json_decode($response, true, 512, JSON_THROW_ON_ERROR));# Python 3 · standard library; run on your server.
import json
import os
from urllib.request import Request, build_opener, HTTPRedirectHandler
class NoRedirect(HTTPRedirectHandler):
def redirect_request(self, req, fp, code, msg, headers, newurl):
return None
# Keep this key and the exact body for retries; use a new key for each new operation.
headers = {
"Idempotency-Key": "operator-request-1042",
"Content-Type": "application/json"
}
headers["Authorization"] = "Bearer " + os.environ["WHOLLY_TOKEN"]
body = """{
"assets": [
{
"asset_id": "11111111-1111-4111-8111-111111111111",
"display_order": 0
}
]
}""".encode("utf-8")
request = Request("https://api.example.com/v1/operator/merchants/YOUR_MERCHANT_ID/projects/YOUR_PROJECT_ID/stores/YOUR_STORE_ID/payment-assets",
method="POST", headers=headers, data=body)
# Non-2xx responses raise HTTPError. Do not retry writes with a new key.
with build_opener(NoRedirect()).open(request, timeout=30) as response:
print(json.load(response))Esempio di risposta · 200 application/json
{
"data": []
}GETElenca webhook del negozio/v1/operator/merchants/{merchant_id}/projects/{project_id}/stores/{store_id}/webhooksSola lettura
Gestisci o esamina la risorsa indicata del commerciante ospitato usando una credenziale operatore separata.
- Richiede projects.read; solo commercianti ospitati autorizzati. Le chiavi operatore non accedono allo spazio dell'attività del proprietario.
- Gli esempi di risposta mostrano alcuni campi. Considera aggiuntivi gli altri campi della risposta.
| Header | Presenza | Regola |
|---|---|---|
| Authorization | obbligatorio | Bearer YOUR_OPERATOR_API_TOKEN |
| Parametro | Tipo / posizione | Regola |
|---|---|---|
| merchant_id | path UUID | UUID canonico minuscolo della risorsa; deve appartenere all'ambito commercianti della credenziale. |
| project_id | path UUID | UUID canonico minuscolo della risorsa; deve appartenere all'ambito commercianti della credenziale. |
| store_id | path UUID | UUID canonico minuscolo della risorsa; deve appartenere all'ambito commercianti della credenziale. |
| page, search | query · optional | Pagine a partire da 1, 25 elementi per pagina. La ricerca è supportata per commercianti, utenti, progetti, negozi, wallet, credenziali e webhook; gli elenchi eventi nativi usano i propri filtri. |
Richiesta
: "${WHOLLY_TOKEN:?Set WHOLLY_TOKEN to your server-side API token}"
curl --fail-with-body --max-time 30 \
--request GET \
--url "https://api.example.com/v1/operator/merchants/YOUR_MERCHANT_ID/projects/YOUR_PROJECT_ID/stores/YOUR_STORE_ID/webhooks" \
--header "Authorization: Bearer $WHOLLY_TOKEN"// Node.js 18+ · run on your server, never in browser code.
const token = process.env.WHOLLY_TOKEN;
if (!token) throw new Error("Set WHOLLY_TOKEN");
const response = await fetch("https://api.example.com/v1/operator/merchants/YOUR_MERCHANT_ID/projects/YOUR_PROJECT_ID/stores/YOUR_STORE_ID/webhooks", {
method: "GET",
headers: {
"Authorization": `Bearer ${token}`
},
redirect: "error",
signal: AbortSignal.timeout(30000)
});
if (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);
console.log(await response.json());<?php
// PHP 8+ with the cURL extension; run on your server.
$token = getenv('WHOLLY_TOKEN');
if (!$token) { throw new RuntimeException('Set WHOLLY_TOKEN'); }
$ch = curl_init("https://api.example.com/v1/operator/merchants/YOUR_MERCHANT_ID/projects/YOUR_PROJECT_ID/stores/YOUR_STORE_ID/webhooks");
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => 'GET',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_FOLLOWLOCATION => false,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTPHEADER => ['Authorization: Bearer ' . $token],
]);
$response = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if ($response === false) { throw new RuntimeException(curl_error($ch)); }
curl_close($ch);
if ($status < 200 || $status >= 300) { throw new RuntimeException("HTTP $status: $response"); }
print_r(json_decode($response, true, 512, JSON_THROW_ON_ERROR));# Python 3 · standard library; run on your server.
import json
import os
from urllib.request import Request, build_opener, HTTPRedirectHandler
class NoRedirect(HTTPRedirectHandler):
def redirect_request(self, req, fp, code, msg, headers, newurl):
return None
headers = {}
headers["Authorization"] = "Bearer " + os.environ["WHOLLY_TOKEN"]
request = Request("https://api.example.com/v1/operator/merchants/YOUR_MERCHANT_ID/projects/YOUR_PROJECT_ID/stores/YOUR_STORE_ID/webhooks",
method="GET", headers=headers)
# Non-2xx responses raise HTTPError. Do not retry writes with a new key.
with build_opener(NoRedirect()).open(request, timeout=30) as response:
print(json.load(response))Esempio di risposta · 200 application/json
{
"data": [],
"page": 1,
"page_size": 25,
"total": 0
}POSTCrea webhook del negozio/v1/operator/merchants/{merchant_id}/projects/{project_id}/stores/{store_id}/webhooksLettura + scrittura
Gestisci o esamina la risorsa indicata del commerciante ospitato usando una credenziale operatore separata.
- Richiede projects.write; solo commercianti ospitati autorizzati. Le chiavi operatore non accedono allo spazio dell'attività del proprietario.
- Salva un Idempotency-Key univoco e l'esatto corpo prima dell'invio. I nuovi tentativi non ripetono mai un'azione già confermata. I campi segreti vengono omessi nei replay; se una risposta contenente un segreto è andata persa, esamina la risorsa creata e ruota o riemetti esplicitamente il segreto. Un 409 operator_request_in_progress può indicare una richiesta interrotta con esito sconosciuto: esamina risorsa e audit; non riprovare alla cieca con una nuova chiave.
- Gli esempi di risposta mostrano alcuni campi. Considera aggiuntivi gli altri campi della risposta.
| Header | Presenza | Regola |
|---|---|---|
| Authorization | obbligatorio | Bearer YOUR_OPERATOR_API_TOKEN |
| Idempotency-Key | obbligatorio | 16–128 lettere, cifre, -, _ o .; salvata per questa operazione |
| Parametro | Tipo / posizione | Regola |
|---|---|---|
| merchant_id | path UUID | UUID canonico minuscolo della risorsa; deve appartenere all'ambito commercianti della credenziale. |
| project_id | path UUID | UUID canonico minuscolo della risorsa; deve appartenere all'ambito commercianti della credenziale. |
| store_id | path UUID | UUID canonico minuscolo della risorsa; deve appartenere all'ambito commercianti della credenziale. |
| name, url, event_types | strings / array · required | Ricevitore HTTPS pubblico e nomi eventi fattura dalla documentazione IPN e webhook. |
| enabled, automatic_redelivery | booleans · default true | La creazione restituisce il segreto di firma una sola volta. Sono callback di pagamento del negozio, non eventi del ciclo di vita operatore. |
Richiesta
: "${WHOLLY_TOKEN:?Set WHOLLY_TOKEN to your server-side API token}"
# Keep this key and the exact body for retries; use a new key for each new operation.
curl --fail-with-body --max-time 30 \
--request POST \
--url "https://api.example.com/v1/operator/merchants/YOUR_MERCHANT_ID/projects/YOUR_PROJECT_ID/stores/YOUR_STORE_ID/webhooks" \
--header "Authorization: Bearer $WHOLLY_TOKEN" \
--header 'Idempotency-Key: operator-request-1042' \
--header 'Content-Type: application/json' \
--data-raw '{
"name": "Orders",
"url": "https://shop.example.test/payments",
"event_types": [
"invoice.settled"
],
"enabled": true,
"automatic_redelivery": true
}'// Node.js 18+ · run on your server, never in browser code.
const token = process.env.WHOLLY_TOKEN;
if (!token) throw new Error("Set WHOLLY_TOKEN");
// Keep this key and the exact body for retries; use a new key for each new operation.
const body = `{
"name": "Orders",
"url": "https://shop.example.test/payments",
"event_types": [
"invoice.settled"
],
"enabled": true,
"automatic_redelivery": true
}`;
const response = await fetch("https://api.example.com/v1/operator/merchants/YOUR_MERCHANT_ID/projects/YOUR_PROJECT_ID/stores/YOUR_STORE_ID/webhooks", {
method: "POST",
headers: {
"Authorization": `Bearer ${token}`,
"Idempotency-Key": "operator-request-1042",
"Content-Type": "application/json"
},
body,
redirect: "error",
signal: AbortSignal.timeout(30000)
});
if (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);
console.log(await response.json());<?php
// PHP 8+ with the cURL extension; run on your server.
$token = getenv('WHOLLY_TOKEN');
if (!$token) { throw new RuntimeException('Set WHOLLY_TOKEN'); }
// Keep this key and the exact body for retries; use a new key for each new operation.
$body = <<<'JSON'
{
"name": "Orders",
"url": "https://shop.example.test/payments",
"event_types": [
"invoice.settled"
],
"enabled": true,
"automatic_redelivery": true
}
JSON;
$ch = curl_init("https://api.example.com/v1/operator/merchants/YOUR_MERCHANT_ID/projects/YOUR_PROJECT_ID/stores/YOUR_STORE_ID/webhooks");
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => 'POST',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_FOLLOWLOCATION => false,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTPHEADER => ['Authorization: Bearer ' . $token, "Idempotency-Key: operator-request-1042", "Content-Type: application/json"],
CURLOPT_POSTFIELDS => $body,
]);
$response = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if ($response === false) { throw new RuntimeException(curl_error($ch)); }
curl_close($ch);
if ($status < 200 || $status >= 300) { throw new RuntimeException("HTTP $status: $response"); }
print_r(json_decode($response, true, 512, JSON_THROW_ON_ERROR));# Python 3 · standard library; run on your server.
import json
import os
from urllib.request import Request, build_opener, HTTPRedirectHandler
class NoRedirect(HTTPRedirectHandler):
def redirect_request(self, req, fp, code, msg, headers, newurl):
return None
# Keep this key and the exact body for retries; use a new key for each new operation.
headers = {
"Idempotency-Key": "operator-request-1042",
"Content-Type": "application/json"
}
headers["Authorization"] = "Bearer " + os.environ["WHOLLY_TOKEN"]
body = """{
"name": "Orders",
"url": "https://shop.example.test/payments",
"event_types": [
"invoice.settled"
],
"enabled": true,
"automatic_redelivery": true
}""".encode("utf-8")
request = Request("https://api.example.com/v1/operator/merchants/YOUR_MERCHANT_ID/projects/YOUR_PROJECT_ID/stores/YOUR_STORE_ID/webhooks",
method="POST", headers=headers, data=body)
# Non-2xx responses raise HTTPError. Do not retry writes with a new key.
with build_opener(NoRedirect()).open(request, timeout=30) as response:
print(json.load(response))Esempio di risposta · 201 o 200 application/json
{
"signing_secret": "EXAMPLE_ONLY_SAVE_ONCE",
"endpoint": {
"id": "44444444-4444-4444-8444-444444444444",
"name": "Orders",
"enabled": true
},
"secret_visible_once": true
}POSTAggiorna webhook del negozio/v1/operator/merchants/{merchant_id}/projects/{project_id}/stores/{store_id}/webhooks/{webhook_id}Lettura + scrittura
Gestisci o esamina la risorsa indicata del commerciante ospitato usando una credenziale operatore separata.
- Richiede projects.write; solo commercianti ospitati autorizzati. Le chiavi operatore non accedono allo spazio dell'attività del proprietario.
- Salva un Idempotency-Key univoco e l'esatto corpo prima dell'invio. I nuovi tentativi non ripetono mai un'azione già confermata. I campi segreti vengono omessi nei replay; se una risposta contenente un segreto è andata persa, esamina la risorsa creata e ruota o riemetti esplicitamente il segreto. Un 409 operator_request_in_progress può indicare una richiesta interrotta con esito sconosciuto: esamina risorsa e audit; non riprovare alla cieca con una nuova chiave.
- Gli esempi di risposta mostrano alcuni campi. Considera aggiuntivi gli altri campi della risposta.
| Header | Presenza | Regola |
|---|---|---|
| Authorization | obbligatorio | Bearer YOUR_OPERATOR_API_TOKEN |
| Idempotency-Key | obbligatorio | 16–128 lettere, cifre, -, _ o .; salvata per questa operazione |
| Parametro | Tipo / posizione | Regola |
|---|---|---|
| merchant_id | path UUID | UUID canonico minuscolo della risorsa; deve appartenere all'ambito commercianti della credenziale. |
| project_id | path UUID | UUID canonico minuscolo della risorsa; deve appartenere all'ambito commercianti della credenziale. |
| store_id | path UUID | UUID canonico minuscolo della risorsa; deve appartenere all'ambito commercianti della credenziale. |
| webhook_id | path UUID | UUID canonico minuscolo della risorsa; deve appartenere all'ambito commercianti della credenziale. |
| name, url, event_types | strings / array · required | Ricevitore HTTPS pubblico e nomi eventi fattura dalla documentazione IPN e webhook. |
| enabled, automatic_redelivery | booleans · default true | La creazione restituisce il segreto di firma una sola volta. Sono callback di pagamento del negozio, non eventi del ciclo di vita operatore. |
Richiesta
: "${WHOLLY_TOKEN:?Set WHOLLY_TOKEN to your server-side API token}"
# Keep this key and the exact body for retries; use a new key for each new operation.
curl --fail-with-body --max-time 30 \
--request POST \
--url "https://api.example.com/v1/operator/merchants/YOUR_MERCHANT_ID/projects/YOUR_PROJECT_ID/stores/YOUR_STORE_ID/webhooks/YOUR_WEBHOOK_ID" \
--header "Authorization: Bearer $WHOLLY_TOKEN" \
--header 'Idempotency-Key: operator-request-1042' \
--header 'Content-Type: application/json' \
--data-raw '{
"name": "Orders",
"url": "https://shop.example.test/payments",
"event_types": [
"invoice.settled"
],
"enabled": false,
"automatic_redelivery": true
}'// Node.js 18+ · run on your server, never in browser code.
const token = process.env.WHOLLY_TOKEN;
if (!token) throw new Error("Set WHOLLY_TOKEN");
// Keep this key and the exact body for retries; use a new key for each new operation.
const body = `{
"name": "Orders",
"url": "https://shop.example.test/payments",
"event_types": [
"invoice.settled"
],
"enabled": false,
"automatic_redelivery": true
}`;
const response = await fetch("https://api.example.com/v1/operator/merchants/YOUR_MERCHANT_ID/projects/YOUR_PROJECT_ID/stores/YOUR_STORE_ID/webhooks/YOUR_WEBHOOK_ID", {
method: "POST",
headers: {
"Authorization": `Bearer ${token}`,
"Idempotency-Key": "operator-request-1042",
"Content-Type": "application/json"
},
body,
redirect: "error",
signal: AbortSignal.timeout(30000)
});
if (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);
console.log(await response.json());<?php
// PHP 8+ with the cURL extension; run on your server.
$token = getenv('WHOLLY_TOKEN');
if (!$token) { throw new RuntimeException('Set WHOLLY_TOKEN'); }
// Keep this key and the exact body for retries; use a new key for each new operation.
$body = <<<'JSON'
{
"name": "Orders",
"url": "https://shop.example.test/payments",
"event_types": [
"invoice.settled"
],
"enabled": false,
"automatic_redelivery": true
}
JSON;
$ch = curl_init("https://api.example.com/v1/operator/merchants/YOUR_MERCHANT_ID/projects/YOUR_PROJECT_ID/stores/YOUR_STORE_ID/webhooks/YOUR_WEBHOOK_ID");
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => 'POST',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_FOLLOWLOCATION => false,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTPHEADER => ['Authorization: Bearer ' . $token, "Idempotency-Key: operator-request-1042", "Content-Type: application/json"],
CURLOPT_POSTFIELDS => $body,
]);
$response = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if ($response === false) { throw new RuntimeException(curl_error($ch)); }
curl_close($ch);
if ($status < 200 || $status >= 300) { throw new RuntimeException("HTTP $status: $response"); }
print_r(json_decode($response, true, 512, JSON_THROW_ON_ERROR));# Python 3 · standard library; run on your server.
import json
import os
from urllib.request import Request, build_opener, HTTPRedirectHandler
class NoRedirect(HTTPRedirectHandler):
def redirect_request(self, req, fp, code, msg, headers, newurl):
return None
# Keep this key and the exact body for retries; use a new key for each new operation.
headers = {
"Idempotency-Key": "operator-request-1042",
"Content-Type": "application/json"
}
headers["Authorization"] = "Bearer " + os.environ["WHOLLY_TOKEN"]
body = """{
"name": "Orders",
"url": "https://shop.example.test/payments",
"event_types": [
"invoice.settled"
],
"enabled": false,
"automatic_redelivery": true
}""".encode("utf-8")
request = Request("https://api.example.com/v1/operator/merchants/YOUR_MERCHANT_ID/projects/YOUR_PROJECT_ID/stores/YOUR_STORE_ID/webhooks/YOUR_WEBHOOK_ID",
method="POST", headers=headers, data=body)
# Non-2xx responses raise HTTPError. Do not retry writes with a new key.
with build_opener(NoRedirect()).open(request, timeout=30) as response:
print(json.load(response))Esempio di risposta · 200 application/json
{
"id": "44444444-4444-4444-8444-444444444444",
"name": "Orders",
"enabled": true
}GETElenca fatture/v1/operator/merchants/{merchant_id}/projects/{project_id}/invoicesSola lettura
Gestisci o esamina la risorsa indicata del commerciante ospitato usando una credenziale operatore separata.
- Richiede reports.read; solo commercianti ospitati autorizzati. Le chiavi operatore non accedono allo spazio dell'attività del proprietario.
- Gli esempi di risposta mostrano alcuni campi. Considera aggiuntivi gli altri campi della risposta.
| Header | Presenza | Regola |
|---|---|---|
| Authorization | obbligatorio | Bearer YOUR_OPERATOR_API_TOKEN |
| Parametro | Tipo / posizione | Regola |
|---|---|---|
| merchant_id | path UUID | UUID canonico minuscolo della risorsa; deve appartenere all'ambito commercianti della credenziale. |
| project_id | path UUID | UUID canonico minuscolo della risorsa; deve appartenere all'ambito commercianti della credenziale. |
| limit, offset, search, status, store_id | query · optional | Paginazione e filtri delle fatture, come nell'elenco fatture del progetto. |
Richiesta
: "${WHOLLY_TOKEN:?Set WHOLLY_TOKEN to your server-side API token}"
curl --fail-with-body --max-time 30 \
--request GET \
--url "https://api.example.com/v1/operator/merchants/YOUR_MERCHANT_ID/projects/YOUR_PROJECT_ID/invoices" \
--header "Authorization: Bearer $WHOLLY_TOKEN"// Node.js 18+ · run on your server, never in browser code.
const token = process.env.WHOLLY_TOKEN;
if (!token) throw new Error("Set WHOLLY_TOKEN");
const response = await fetch("https://api.example.com/v1/operator/merchants/YOUR_MERCHANT_ID/projects/YOUR_PROJECT_ID/invoices", {
method: "GET",
headers: {
"Authorization": `Bearer ${token}`
},
redirect: "error",
signal: AbortSignal.timeout(30000)
});
if (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);
console.log(await response.json());<?php
// PHP 8+ with the cURL extension; run on your server.
$token = getenv('WHOLLY_TOKEN');
if (!$token) { throw new RuntimeException('Set WHOLLY_TOKEN'); }
$ch = curl_init("https://api.example.com/v1/operator/merchants/YOUR_MERCHANT_ID/projects/YOUR_PROJECT_ID/invoices");
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => 'GET',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_FOLLOWLOCATION => false,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTPHEADER => ['Authorization: Bearer ' . $token],
]);
$response = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if ($response === false) { throw new RuntimeException(curl_error($ch)); }
curl_close($ch);
if ($status < 200 || $status >= 300) { throw new RuntimeException("HTTP $status: $response"); }
print_r(json_decode($response, true, 512, JSON_THROW_ON_ERROR));# Python 3 · standard library; run on your server.
import json
import os
from urllib.request import Request, build_opener, HTTPRedirectHandler
class NoRedirect(HTTPRedirectHandler):
def redirect_request(self, req, fp, code, msg, headers, newurl):
return None
headers = {}
headers["Authorization"] = "Bearer " + os.environ["WHOLLY_TOKEN"]
request = Request("https://api.example.com/v1/operator/merchants/YOUR_MERCHANT_ID/projects/YOUR_PROJECT_ID/invoices",
method="GET", headers=headers)
# Non-2xx responses raise HTTPError. Do not retry writes with a new key.
with build_opener(NoRedirect()).open(request, timeout=30) as response:
print(json.load(response))Esempio di risposta · 200 application/json
{
"data": [],
"pagination": {
"limit": 25,
"offset": 0,
"total": 0,
"has_more": false
}
}GETRecupera fattura/v1/operator/merchants/{merchant_id}/projects/{project_id}/invoices/{invoice_id}Sola lettura
Gestisci o esamina la risorsa indicata del commerciante ospitato usando una credenziale operatore separata.
- Richiede reports.read; solo commercianti ospitati autorizzati. Le chiavi operatore non accedono allo spazio dell'attività del proprietario.
- Gli esempi di risposta mostrano alcuni campi. Considera aggiuntivi gli altri campi della risposta.
- invoice_id è l'ID pubblico della fattura restituito alla creazione e nei callback, non l'id interno.
| Header | Presenza | Regola |
|---|---|---|
| Authorization | obbligatorio | Bearer YOUR_OPERATOR_API_TOKEN |
| Parametro | Tipo / posizione | Regola |
|---|---|---|
| merchant_id | path UUID | UUID canonico minuscolo della risorsa; deve appartenere all'ambito commercianti della credenziale. |
| project_id | path UUID | UUID canonico minuscolo della risorsa; deve appartenere all'ambito commercianti della credenziale. |
| invoice_id | path UUID | UUID canonico minuscolo della risorsa; deve appartenere all'ambito commercianti della credenziale. |
Richiesta
: "${WHOLLY_TOKEN:?Set WHOLLY_TOKEN to your server-side API token}"
curl --fail-with-body --max-time 30 \
--request GET \
--url "https://api.example.com/v1/operator/merchants/YOUR_MERCHANT_ID/projects/YOUR_PROJECT_ID/invoices/YOUR_INVOICE_ID" \
--header "Authorization: Bearer $WHOLLY_TOKEN"// Node.js 18+ · run on your server, never in browser code.
const token = process.env.WHOLLY_TOKEN;
if (!token) throw new Error("Set WHOLLY_TOKEN");
const response = await fetch("https://api.example.com/v1/operator/merchants/YOUR_MERCHANT_ID/projects/YOUR_PROJECT_ID/invoices/YOUR_INVOICE_ID", {
method: "GET",
headers: {
"Authorization": `Bearer ${token}`
},
redirect: "error",
signal: AbortSignal.timeout(30000)
});
if (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);
console.log(await response.json());<?php
// PHP 8+ with the cURL extension; run on your server.
$token = getenv('WHOLLY_TOKEN');
if (!$token) { throw new RuntimeException('Set WHOLLY_TOKEN'); }
$ch = curl_init("https://api.example.com/v1/operator/merchants/YOUR_MERCHANT_ID/projects/YOUR_PROJECT_ID/invoices/YOUR_INVOICE_ID");
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => 'GET',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_FOLLOWLOCATION => false,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTPHEADER => ['Authorization: Bearer ' . $token],
]);
$response = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if ($response === false) { throw new RuntimeException(curl_error($ch)); }
curl_close($ch);
if ($status < 200 || $status >= 300) { throw new RuntimeException("HTTP $status: $response"); }
print_r(json_decode($response, true, 512, JSON_THROW_ON_ERROR));# Python 3 · standard library; run on your server.
import json
import os
from urllib.request import Request, build_opener, HTTPRedirectHandler
class NoRedirect(HTTPRedirectHandler):
def redirect_request(self, req, fp, code, msg, headers, newurl):
return None
headers = {}
headers["Authorization"] = "Bearer " + os.environ["WHOLLY_TOKEN"]
request = Request("https://api.example.com/v1/operator/merchants/YOUR_MERCHANT_ID/projects/YOUR_PROJECT_ID/invoices/YOUR_INVOICE_ID",
method="GET", headers=headers)
# Non-2xx responses raise HTTPError. Do not retry writes with a new key.
with build_opener(NoRedirect()).open(request, timeout=30) as response:
print(json.load(response))Esempio di risposta · 200 application/json
{
"invoice_id": "44444444-4444-4444-8444-444444444444",
"project_id": "33333333-3333-4333-8333-333333333333",
"amount": "25",
"currency": "EUR",
"status": "new",
"metadata": {},
"payment_intents": []
}GETElenca wallet/v1/operator/merchants/{merchant_id}/projects/{project_id}/walletsSola lettura
Leggi i saldi pubblici dei wallet in cache, mai chiavi private o frasi di recupero.
- Richiede reports.read; solo commercianti ospitati autorizzati. Le chiavi operatore non accedono allo spazio dell'attività del proprietario.
- I saldi sono osservazioni in cache con campi di aggiornamento, non una garanzia di saldo spendibile. Invio e esportazione delle chiavi non sono disponibili tramite questa API.
- Gli esempi di risposta mostrano alcuni campi. Considera aggiuntivi gli altri campi della risposta.
| Header | Presenza | Regola |
|---|---|---|
| Authorization | obbligatorio | Bearer YOUR_OPERATOR_API_TOKEN |
| Parametro | Tipo / posizione | Regola |
|---|---|---|
| merchant_id | path UUID | UUID canonico minuscolo della risorsa; deve appartenere all'ambito commercianti della credenziale. |
| project_id | path UUID | UUID canonico minuscolo della risorsa; deve appartenere all'ambito commercianti della credenziale. |
| page, search | query · optional | Pagine a partire da 1, 25 elementi per pagina. La ricerca è supportata per commercianti, utenti, progetti, negozi, wallet, credenziali e webhook; gli elenchi eventi nativi usano i propri filtri. |
Richiesta
: "${WHOLLY_TOKEN:?Set WHOLLY_TOKEN to your server-side API token}"
curl --fail-with-body --max-time 30 \
--request GET \
--url "https://api.example.com/v1/operator/merchants/YOUR_MERCHANT_ID/projects/YOUR_PROJECT_ID/wallets" \
--header "Authorization: Bearer $WHOLLY_TOKEN"// Node.js 18+ · run on your server, never in browser code.
const token = process.env.WHOLLY_TOKEN;
if (!token) throw new Error("Set WHOLLY_TOKEN");
const response = await fetch("https://api.example.com/v1/operator/merchants/YOUR_MERCHANT_ID/projects/YOUR_PROJECT_ID/wallets", {
method: "GET",
headers: {
"Authorization": `Bearer ${token}`
},
redirect: "error",
signal: AbortSignal.timeout(30000)
});
if (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);
console.log(await response.json());<?php
// PHP 8+ with the cURL extension; run on your server.
$token = getenv('WHOLLY_TOKEN');
if (!$token) { throw new RuntimeException('Set WHOLLY_TOKEN'); }
$ch = curl_init("https://api.example.com/v1/operator/merchants/YOUR_MERCHANT_ID/projects/YOUR_PROJECT_ID/wallets");
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => 'GET',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_FOLLOWLOCATION => false,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTPHEADER => ['Authorization: Bearer ' . $token],
]);
$response = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if ($response === false) { throw new RuntimeException(curl_error($ch)); }
curl_close($ch);
if ($status < 200 || $status >= 300) { throw new RuntimeException("HTTP $status: $response"); }
print_r(json_decode($response, true, 512, JSON_THROW_ON_ERROR));# Python 3 · standard library; run on your server.
import json
import os
from urllib.request import Request, build_opener, HTTPRedirectHandler
class NoRedirect(HTTPRedirectHandler):
def redirect_request(self, req, fp, code, msg, headers, newurl):
return None
headers = {}
headers["Authorization"] = "Bearer " + os.environ["WHOLLY_TOKEN"]
request = Request("https://api.example.com/v1/operator/merchants/YOUR_MERCHANT_ID/projects/YOUR_PROJECT_ID/wallets",
method="GET", headers=headers)
# Non-2xx responses raise HTTPError. Do not retry writes with a new key.
with build_opener(NoRedirect()).open(request, timeout=30) as response:
print(json.load(response))Esempio di risposta · 200 application/json
{
"data": [],
"page": 1,
"page_size": 25,
"total": 0
}GETElenca indirizzi wallet/v1/operator/merchants/{merchant_id}/projects/{project_id}/wallets/{wallet_id}/addressesSola lettura
Gestisci o esamina la risorsa indicata del commerciante ospitato usando una credenziale operatore separata.
- Richiede reports.read; solo commercianti ospitati autorizzati. Le chiavi operatore non accedono allo spazio dell'attività del proprietario.
- Gli esempi di risposta mostrano alcuni campi. Considera aggiuntivi gli altri campi della risposta.
| Header | Presenza | Regola |
|---|---|---|
| Authorization | obbligatorio | Bearer YOUR_OPERATOR_API_TOKEN |
| Parametro | Tipo / posizione | Regola |
|---|---|---|
| merchant_id | path UUID | UUID canonico minuscolo della risorsa; deve appartenere all'ambito commercianti della credenziale. |
| project_id | path UUID | UUID canonico minuscolo della risorsa; deve appartenere all'ambito commercianti della credenziale. |
| wallet_id | path UUID | UUID canonico minuscolo della risorsa; deve appartenere all'ambito commercianti della credenziale. |
| limit, before, search, has_balance, hide_small_balances | query · optional | Limite 1–50, predefinito 25. Passa next_cursor come before per la pagina successiva. Ometti before per la pagina 1. has_balance=false e hide_small_balances=false includono saldi vuoti/piccoli. |
Richiesta
: "${WHOLLY_TOKEN:?Set WHOLLY_TOKEN to your server-side API token}"
curl --fail-with-body --max-time 30 \
--request GET \
--url "https://api.example.com/v1/operator/merchants/YOUR_MERCHANT_ID/projects/YOUR_PROJECT_ID/wallets/YOUR_WALLET_ID/addresses" \
--header "Authorization: Bearer $WHOLLY_TOKEN"// Node.js 18+ · run on your server, never in browser code.
const token = process.env.WHOLLY_TOKEN;
if (!token) throw new Error("Set WHOLLY_TOKEN");
const response = await fetch("https://api.example.com/v1/operator/merchants/YOUR_MERCHANT_ID/projects/YOUR_PROJECT_ID/wallets/YOUR_WALLET_ID/addresses", {
method: "GET",
headers: {
"Authorization": `Bearer ${token}`
},
redirect: "error",
signal: AbortSignal.timeout(30000)
});
if (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);
console.log(await response.json());<?php
// PHP 8+ with the cURL extension; run on your server.
$token = getenv('WHOLLY_TOKEN');
if (!$token) { throw new RuntimeException('Set WHOLLY_TOKEN'); }
$ch = curl_init("https://api.example.com/v1/operator/merchants/YOUR_MERCHANT_ID/projects/YOUR_PROJECT_ID/wallets/YOUR_WALLET_ID/addresses");
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => 'GET',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_FOLLOWLOCATION => false,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTPHEADER => ['Authorization: Bearer ' . $token],
]);
$response = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if ($response === false) { throw new RuntimeException(curl_error($ch)); }
curl_close($ch);
if ($status < 200 || $status >= 300) { throw new RuntimeException("HTTP $status: $response"); }
print_r(json_decode($response, true, 512, JSON_THROW_ON_ERROR));# Python 3 · standard library; run on your server.
import json
import os
from urllib.request import Request, build_opener, HTTPRedirectHandler
class NoRedirect(HTTPRedirectHandler):
def redirect_request(self, req, fp, code, msg, headers, newurl):
return None
headers = {}
headers["Authorization"] = "Bearer " + os.environ["WHOLLY_TOKEN"]
request = Request("https://api.example.com/v1/operator/merchants/YOUR_MERCHANT_ID/projects/YOUR_PROJECT_ID/wallets/YOUR_WALLET_ID/addresses",
method="GET", headers=headers)
# Non-2xx responses raise HTTPError. Do not retry writes with a new key.
with build_opener(NoRedirect()).open(request, timeout=30) as response:
print(json.load(response))Esempio di risposta · 200 application/json
{
"wallet": {
"id": "22222222-2222-4222-8222-222222222222",
"chain_slug": "ethereum"
},
"items": [],
"total": 0,
"total_pages": 1,
"next_cursor": null,
"reporting_currency": "EUR",
"has_balance": true,
"hide_small_balances": true,
"small_balance_threshold": {
"amount": "0.20",
"currency": "EUR"
}
}GETElenca credenziali merchant/v1/operator/merchants/{merchant_id}/api-credentialsSola lettura
Gestisci o esamina la risorsa indicata del commerciante ospitato usando una credenziale operatore separata.
- Richiede merchant_credentials.read; solo commercianti ospitati autorizzati. Le chiavi operatore non accedono allo spazio dell'attività del proprietario.
- Gli esempi di risposta mostrano alcuni campi. Considera aggiuntivi gli altri campi della risposta.
| Header | Presenza | Regola |
|---|---|---|
| Authorization | obbligatorio | Bearer YOUR_OPERATOR_API_TOKEN |
| Parametro | Tipo / posizione | Regola |
|---|---|---|
| merchant_id | path UUID | UUID canonico minuscolo della risorsa; deve appartenere all'ambito commercianti della credenziale. |
| page, search | query · optional | Pagine a partire da 1, 25 elementi per pagina. La ricerca è supportata per commercianti, utenti, progetti, negozi, wallet, credenziali e webhook; gli elenchi eventi nativi usano i propri filtri. |
Richiesta
: "${WHOLLY_TOKEN:?Set WHOLLY_TOKEN to your server-side API token}"
curl --fail-with-body --max-time 30 \
--request GET \
--url "https://api.example.com/v1/operator/merchants/YOUR_MERCHANT_ID/api-credentials" \
--header "Authorization: Bearer $WHOLLY_TOKEN"// Node.js 18+ · run on your server, never in browser code.
const token = process.env.WHOLLY_TOKEN;
if (!token) throw new Error("Set WHOLLY_TOKEN");
const response = await fetch("https://api.example.com/v1/operator/merchants/YOUR_MERCHANT_ID/api-credentials", {
method: "GET",
headers: {
"Authorization": `Bearer ${token}`
},
redirect: "error",
signal: AbortSignal.timeout(30000)
});
if (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);
console.log(await response.json());<?php
// PHP 8+ with the cURL extension; run on your server.
$token = getenv('WHOLLY_TOKEN');
if (!$token) { throw new RuntimeException('Set WHOLLY_TOKEN'); }
$ch = curl_init("https://api.example.com/v1/operator/merchants/YOUR_MERCHANT_ID/api-credentials");
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => 'GET',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_FOLLOWLOCATION => false,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTPHEADER => ['Authorization: Bearer ' . $token],
]);
$response = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if ($response === false) { throw new RuntimeException(curl_error($ch)); }
curl_close($ch);
if ($status < 200 || $status >= 300) { throw new RuntimeException("HTTP $status: $response"); }
print_r(json_decode($response, true, 512, JSON_THROW_ON_ERROR));# Python 3 · standard library; run on your server.
import json
import os
from urllib.request import Request, build_opener, HTTPRedirectHandler
class NoRedirect(HTTPRedirectHandler):
def redirect_request(self, req, fp, code, msg, headers, newurl):
return None
headers = {}
headers["Authorization"] = "Bearer " + os.environ["WHOLLY_TOKEN"]
request = Request("https://api.example.com/v1/operator/merchants/YOUR_MERCHANT_ID/api-credentials",
method="GET", headers=headers)
# Non-2xx responses raise HTTPError. Do not retry writes with a new key.
with build_opener(NoRedirect()).open(request, timeout=30) as response:
print(json.load(response))Esempio di risposta · 200 application/json
{
"data": [],
"page": 1,
"page_size": 25,
"total": 0
}POSTCrea credenziale merchant/v1/operator/merchants/{merchant_id}/api-credentialsLettura + scrittura
Gestisci o esamina la risorsa indicata del commerciante ospitato usando una credenziale operatore separata.
- Richiede merchant_credentials.write; solo commercianti ospitati autorizzati. Le chiavi operatore non accedono allo spazio dell'attività del proprietario.
- Salva un Idempotency-Key univoco e l'esatto corpo prima dell'invio. I nuovi tentativi non ripetono mai un'azione già confermata. I campi segreti vengono omessi nei replay; se una risposta contenente un segreto è andata persa, esamina la risorsa creata e ruota o riemetti esplicitamente il segreto. Un 409 operator_request_in_progress può indicare una richiesta interrotta con esito sconosciuto: esamina risorsa e audit; non riprovare alla cieca con una nuova chiave.
- Gli esempi di risposta mostrano alcuni campi. Considera aggiuntivi gli altri campi della risposta.
| Header | Presenza | Regola |
|---|---|---|
| Authorization | obbligatorio | Bearer YOUR_OPERATOR_API_TOKEN |
| Idempotency-Key | obbligatorio | 16–128 lettere, cifre, -, _ o .; salvata per questa operazione |
| Parametro | Tipo / posizione | Regola |
|---|---|---|
| merchant_id | path UUID | UUID canonico minuscolo della risorsa; deve appartenere all'ambito commercianti della credenziale. |
| name | string · required | Etichetta per una nuova chiave merchant ordinaria, non una chiave operatore. |
| access_level | read_only | read_write · default read_only | Lettura/scrittura abilita il contratto API merchant esistente. |
| project_ids | UUID[] | Solo progetti del commerciante selezionato; un elenco vuoto segue la politica esistente per tutti i progetti del commerciante. |
| enabled, ip_restriction_enabled, allowed_ips, requests_per_minute | optional | Controlli esistenti delle chiavi merchant. Segreto restituito una sola volta; richiede merchant_credentials.write. |
Richiesta
: "${WHOLLY_TOKEN:?Set WHOLLY_TOKEN to your server-side API token}"
# Keep this key and the exact body for retries; use a new key for each new operation.
curl --fail-with-body --max-time 30 \
--request POST \
--url "https://api.example.com/v1/operator/merchants/YOUR_MERCHANT_ID/api-credentials" \
--header "Authorization: Bearer $WHOLLY_TOKEN" \
--header 'Idempotency-Key: operator-request-1042' \
--header 'Content-Type: application/json' \
--data-raw '{
"name": "Store integration",
"access_level": "read_only",
"enabled": true,
"ip_restriction_enabled": false,
"allowed_ips": [],
"project_ids": [
"11111111-1111-4111-8111-111111111111"
]
}'// Node.js 18+ · run on your server, never in browser code.
const token = process.env.WHOLLY_TOKEN;
if (!token) throw new Error("Set WHOLLY_TOKEN");
// Keep this key and the exact body for retries; use a new key for each new operation.
const body = `{
"name": "Store integration",
"access_level": "read_only",
"enabled": true,
"ip_restriction_enabled": false,
"allowed_ips": [],
"project_ids": [
"11111111-1111-4111-8111-111111111111"
]
}`;
const response = await fetch("https://api.example.com/v1/operator/merchants/YOUR_MERCHANT_ID/api-credentials", {
method: "POST",
headers: {
"Authorization": `Bearer ${token}`,
"Idempotency-Key": "operator-request-1042",
"Content-Type": "application/json"
},
body,
redirect: "error",
signal: AbortSignal.timeout(30000)
});
if (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);
console.log(await response.json());<?php
// PHP 8+ with the cURL extension; run on your server.
$token = getenv('WHOLLY_TOKEN');
if (!$token) { throw new RuntimeException('Set WHOLLY_TOKEN'); }
// Keep this key and the exact body for retries; use a new key for each new operation.
$body = <<<'JSON'
{
"name": "Store integration",
"access_level": "read_only",
"enabled": true,
"ip_restriction_enabled": false,
"allowed_ips": [],
"project_ids": [
"11111111-1111-4111-8111-111111111111"
]
}
JSON;
$ch = curl_init("https://api.example.com/v1/operator/merchants/YOUR_MERCHANT_ID/api-credentials");
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => 'POST',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_FOLLOWLOCATION => false,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTPHEADER => ['Authorization: Bearer ' . $token, "Idempotency-Key: operator-request-1042", "Content-Type: application/json"],
CURLOPT_POSTFIELDS => $body,
]);
$response = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if ($response === false) { throw new RuntimeException(curl_error($ch)); }
curl_close($ch);
if ($status < 200 || $status >= 300) { throw new RuntimeException("HTTP $status: $response"); }
print_r(json_decode($response, true, 512, JSON_THROW_ON_ERROR));# Python 3 · standard library; run on your server.
import json
import os
from urllib.request import Request, build_opener, HTTPRedirectHandler
class NoRedirect(HTTPRedirectHandler):
def redirect_request(self, req, fp, code, msg, headers, newurl):
return None
# Keep this key and the exact body for retries; use a new key for each new operation.
headers = {
"Idempotency-Key": "operator-request-1042",
"Content-Type": "application/json"
}
headers["Authorization"] = "Bearer " + os.environ["WHOLLY_TOKEN"]
body = """{
"name": "Store integration",
"access_level": "read_only",
"enabled": true,
"ip_restriction_enabled": false,
"allowed_ips": [],
"project_ids": [
"11111111-1111-4111-8111-111111111111"
]
}""".encode("utf-8")
request = Request("https://api.example.com/v1/operator/merchants/YOUR_MERCHANT_ID/api-credentials",
method="POST", headers=headers, data=body)
# Non-2xx responses raise HTTPError. Do not retry writes with a new key.
with build_opener(NoRedirect()).open(request, timeout=30) as response:
print(json.load(response))Esempio di risposta · 201 o 200 application/json
{
"credential": {
"id": "22222222-2222-4222-8222-222222222222",
"name": "Checkout",
"access_level": "read_only",
"project_ids": [
"33333333-3333-4333-8333-333333333333"
],
"enabled": true
},
"token": "wc_live_EXAMPLE_ONLY_SAVE_ONCE"
}POSTAggiorna credenziale merchant/v1/operator/merchants/{merchant_id}/api-credentials/{credential_id}Lettura + scrittura
Gestisci o esamina la risorsa indicata del commerciante ospitato usando una credenziale operatore separata.
- Richiede merchant_credentials.write; solo commercianti ospitati autorizzati. Le chiavi operatore non accedono allo spazio dell'attività del proprietario.
- Salva un Idempotency-Key univoco e l'esatto corpo prima dell'invio. I nuovi tentativi non ripetono mai un'azione già confermata. I campi segreti vengono omessi nei replay; se una risposta contenente un segreto è andata persa, esamina la risorsa creata e ruota o riemetti esplicitamente il segreto. Un 409 operator_request_in_progress può indicare una richiesta interrotta con esito sconosciuto: esamina risorsa e audit; non riprovare alla cieca con una nuova chiave.
- Gli esempi di risposta mostrano alcuni campi. Considera aggiuntivi gli altri campi della risposta.
| Header | Presenza | Regola |
|---|---|---|
| Authorization | obbligatorio | Bearer YOUR_OPERATOR_API_TOKEN |
| Idempotency-Key | obbligatorio | 16–128 lettere, cifre, -, _ o .; salvata per questa operazione |
| Parametro | Tipo / posizione | Regola |
|---|---|---|
| merchant_id | path UUID | UUID canonico minuscolo della risorsa; deve appartenere all'ambito commercianti della credenziale. |
| credential_id | path UUID | UUID canonico minuscolo della risorsa; deve appartenere all'ambito commercianti della credenziale. |
| name, access_level, enabled, ip_restriction_enabled, allowed_ips | required fields | Invia la configurazione attuale completa della credenziale con le modifiche. project_ids predefinito []; requests_per_minute usa la quota API merchant predefinita. |
Richiesta
: "${WHOLLY_TOKEN:?Set WHOLLY_TOKEN to your server-side API token}"
# Keep this key and the exact body for retries; use a new key for each new operation.
curl --fail-with-body --max-time 30 \
--request POST \
--url "https://api.example.com/v1/operator/merchants/YOUR_MERCHANT_ID/api-credentials/YOUR_CREDENTIAL_ID" \
--header "Authorization: Bearer $WHOLLY_TOKEN" \
--header 'Idempotency-Key: operator-request-1042' \
--header 'Content-Type: application/json' \
--data-raw '{
"name": "Checkout",
"access_level": "read_only",
"enabled": true,
"ip_restriction_enabled": false,
"allowed_ips": [],
"project_ids": [
"33333333-3333-4333-8333-333333333333"
]
}'// Node.js 18+ · run on your server, never in browser code.
const token = process.env.WHOLLY_TOKEN;
if (!token) throw new Error("Set WHOLLY_TOKEN");
// Keep this key and the exact body for retries; use a new key for each new operation.
const body = `{
"name": "Checkout",
"access_level": "read_only",
"enabled": true,
"ip_restriction_enabled": false,
"allowed_ips": [],
"project_ids": [
"33333333-3333-4333-8333-333333333333"
]
}`;
const response = await fetch("https://api.example.com/v1/operator/merchants/YOUR_MERCHANT_ID/api-credentials/YOUR_CREDENTIAL_ID", {
method: "POST",
headers: {
"Authorization": `Bearer ${token}`,
"Idempotency-Key": "operator-request-1042",
"Content-Type": "application/json"
},
body,
redirect: "error",
signal: AbortSignal.timeout(30000)
});
if (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);
console.log(await response.json());<?php
// PHP 8+ with the cURL extension; run on your server.
$token = getenv('WHOLLY_TOKEN');
if (!$token) { throw new RuntimeException('Set WHOLLY_TOKEN'); }
// Keep this key and the exact body for retries; use a new key for each new operation.
$body = <<<'JSON'
{
"name": "Checkout",
"access_level": "read_only",
"enabled": true,
"ip_restriction_enabled": false,
"allowed_ips": [],
"project_ids": [
"33333333-3333-4333-8333-333333333333"
]
}
JSON;
$ch = curl_init("https://api.example.com/v1/operator/merchants/YOUR_MERCHANT_ID/api-credentials/YOUR_CREDENTIAL_ID");
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => 'POST',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_FOLLOWLOCATION => false,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTPHEADER => ['Authorization: Bearer ' . $token, "Idempotency-Key: operator-request-1042", "Content-Type: application/json"],
CURLOPT_POSTFIELDS => $body,
]);
$response = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if ($response === false) { throw new RuntimeException(curl_error($ch)); }
curl_close($ch);
if ($status < 200 || $status >= 300) { throw new RuntimeException("HTTP $status: $response"); }
print_r(json_decode($response, true, 512, JSON_THROW_ON_ERROR));# Python 3 · standard library; run on your server.
import json
import os
from urllib.request import Request, build_opener, HTTPRedirectHandler
class NoRedirect(HTTPRedirectHandler):
def redirect_request(self, req, fp, code, msg, headers, newurl):
return None
# Keep this key and the exact body for retries; use a new key for each new operation.
headers = {
"Idempotency-Key": "operator-request-1042",
"Content-Type": "application/json"
}
headers["Authorization"] = "Bearer " + os.environ["WHOLLY_TOKEN"]
body = """{
"name": "Checkout",
"access_level": "read_only",
"enabled": true,
"ip_restriction_enabled": false,
"allowed_ips": [],
"project_ids": [
"33333333-3333-4333-8333-333333333333"
]
}""".encode("utf-8")
request = Request("https://api.example.com/v1/operator/merchants/YOUR_MERCHANT_ID/api-credentials/YOUR_CREDENTIAL_ID",
method="POST", headers=headers, data=body)
# Non-2xx responses raise HTTPError. Do not retry writes with a new key.
with build_opener(NoRedirect()).open(request, timeout=30) as response:
print(json.load(response))Esempio di risposta · 200 application/json
{
"id": "22222222-2222-4222-8222-222222222222",
"name": "Checkout",
"access_level": "read_only",
"project_ids": [
"33333333-3333-4333-8333-333333333333"
],
"enabled": true
}POSTRuota credenziale merchant/v1/operator/merchants/{merchant_id}/api-credentials/{credential_id}/rotateLettura + scrittura
Gestisci o esamina la risorsa indicata del commerciante ospitato usando una credenziale operatore separata.
- Richiede merchant_credentials.write; solo commercianti ospitati autorizzati. Le chiavi operatore non accedono allo spazio dell'attività del proprietario.
- Salva un Idempotency-Key univoco e l'esatto corpo prima dell'invio. I nuovi tentativi non ripetono mai un'azione già confermata. I campi segreti vengono omessi nei replay; se una risposta contenente un segreto è andata persa, esamina la risorsa creata e ruota o riemetti esplicitamente il segreto. Un 409 operator_request_in_progress può indicare una richiesta interrotta con esito sconosciuto: esamina risorsa e audit; non riprovare alla cieca con una nuova chiave.
- Gli esempi di risposta mostrano alcuni campi. Considera aggiuntivi gli altri campi della risposta.
| Header | Presenza | Regola |
|---|---|---|
| Authorization | obbligatorio | Bearer YOUR_OPERATOR_API_TOKEN |
| Idempotency-Key | obbligatorio | 16–128 lettere, cifre, -, _ o .; salvata per questa operazione |
| Parametro | Tipo / posizione | Regola |
|---|---|---|
| merchant_id | path UUID | UUID canonico minuscolo della risorsa; deve appartenere all'ambito commercianti della credenziale. |
| credential_id | path UUID | UUID canonico minuscolo della risorsa; deve appartenere all'ambito commercianti della credenziale. |
Richiesta
: "${WHOLLY_TOKEN:?Set WHOLLY_TOKEN to your server-side API token}"
# Keep this key and the exact body for retries; use a new key for each new operation.
curl --fail-with-body --max-time 30 \
--request POST \
--url "https://api.example.com/v1/operator/merchants/YOUR_MERCHANT_ID/api-credentials/YOUR_CREDENTIAL_ID/rotate" \
--header "Authorization: Bearer $WHOLLY_TOKEN" \
--header 'Idempotency-Key: operator-request-1042' \
--header 'Content-Type: application/json' \
--data-raw '{}'// Node.js 18+ · run on your server, never in browser code.
const token = process.env.WHOLLY_TOKEN;
if (!token) throw new Error("Set WHOLLY_TOKEN");
// Keep this key and the exact body for retries; use a new key for each new operation.
const body = `{}`;
const response = await fetch("https://api.example.com/v1/operator/merchants/YOUR_MERCHANT_ID/api-credentials/YOUR_CREDENTIAL_ID/rotate", {
method: "POST",
headers: {
"Authorization": `Bearer ${token}`,
"Idempotency-Key": "operator-request-1042",
"Content-Type": "application/json"
},
body,
redirect: "error",
signal: AbortSignal.timeout(30000)
});
if (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);
console.log(await response.json());<?php
// PHP 8+ with the cURL extension; run on your server.
$token = getenv('WHOLLY_TOKEN');
if (!$token) { throw new RuntimeException('Set WHOLLY_TOKEN'); }
// Keep this key and the exact body for retries; use a new key for each new operation.
$body = <<<'JSON'
{}
JSON;
$ch = curl_init("https://api.example.com/v1/operator/merchants/YOUR_MERCHANT_ID/api-credentials/YOUR_CREDENTIAL_ID/rotate");
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => 'POST',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_FOLLOWLOCATION => false,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTPHEADER => ['Authorization: Bearer ' . $token, "Idempotency-Key: operator-request-1042", "Content-Type: application/json"],
CURLOPT_POSTFIELDS => $body,
]);
$response = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if ($response === false) { throw new RuntimeException(curl_error($ch)); }
curl_close($ch);
if ($status < 200 || $status >= 300) { throw new RuntimeException("HTTP $status: $response"); }
print_r(json_decode($response, true, 512, JSON_THROW_ON_ERROR));# Python 3 · standard library; run on your server.
import json
import os
from urllib.request import Request, build_opener, HTTPRedirectHandler
class NoRedirect(HTTPRedirectHandler):
def redirect_request(self, req, fp, code, msg, headers, newurl):
return None
# Keep this key and the exact body for retries; use a new key for each new operation.
headers = {
"Idempotency-Key": "operator-request-1042",
"Content-Type": "application/json"
}
headers["Authorization"] = "Bearer " + os.environ["WHOLLY_TOKEN"]
body = """{}""".encode("utf-8")
request = Request("https://api.example.com/v1/operator/merchants/YOUR_MERCHANT_ID/api-credentials/YOUR_CREDENTIAL_ID/rotate",
method="POST", headers=headers, data=body)
# Non-2xx responses raise HTTPError. Do not retry writes with a new key.
with build_opener(NoRedirect()).open(request, timeout=30) as response:
print(json.load(response))Esempio di risposta · 200 application/json
{
"credential": {
"id": "22222222-2222-4222-8222-222222222222",
"name": "Checkout",
"access_level": "read_only",
"project_ids": [
"33333333-3333-4333-8333-333333333333"
],
"enabled": true
},
"token": "wc_live_EXAMPLE_ONLY_SAVE_ONCE"
}POSTRevoca credenziale merchant/v1/operator/merchants/{merchant_id}/api-credentials/{credential_id}/revokeLettura + scrittura
Gestisci o esamina la risorsa indicata del commerciante ospitato usando una credenziale operatore separata.
- Richiede merchant_credentials.write; solo commercianti ospitati autorizzati. Le chiavi operatore non accedono allo spazio dell'attività del proprietario.
- Salva un Idempotency-Key univoco e l'esatto corpo prima dell'invio. I nuovi tentativi non ripetono mai un'azione già confermata. I campi segreti vengono omessi nei replay; se una risposta contenente un segreto è andata persa, esamina la risorsa creata e ruota o riemetti esplicitamente il segreto. Un 409 operator_request_in_progress può indicare una richiesta interrotta con esito sconosciuto: esamina risorsa e audit; non riprovare alla cieca con una nuova chiave.
- Gli esempi di risposta mostrano alcuni campi. Considera aggiuntivi gli altri campi della risposta.
| Header | Presenza | Regola |
|---|---|---|
| Authorization | obbligatorio | Bearer YOUR_OPERATOR_API_TOKEN |
| Idempotency-Key | obbligatorio | 16–128 lettere, cifre, -, _ o .; salvata per questa operazione |
| Parametro | Tipo / posizione | Regola |
|---|---|---|
| merchant_id | path UUID | UUID canonico minuscolo della risorsa; deve appartenere all'ambito commercianti della credenziale. |
| credential_id | path UUID | UUID canonico minuscolo della risorsa; deve appartenere all'ambito commercianti della credenziale. |
Richiesta
: "${WHOLLY_TOKEN:?Set WHOLLY_TOKEN to your server-side API token}"
# Keep this key and the exact body for retries; use a new key for each new operation.
curl --fail-with-body --max-time 30 \
--request POST \
--url "https://api.example.com/v1/operator/merchants/YOUR_MERCHANT_ID/api-credentials/YOUR_CREDENTIAL_ID/revoke" \
--header "Authorization: Bearer $WHOLLY_TOKEN" \
--header 'Idempotency-Key: operator-request-1042' \
--header 'Content-Type: application/json' \
--data-raw '{}'// Node.js 18+ · run on your server, never in browser code.
const token = process.env.WHOLLY_TOKEN;
if (!token) throw new Error("Set WHOLLY_TOKEN");
// Keep this key and the exact body for retries; use a new key for each new operation.
const body = `{}`;
const response = await fetch("https://api.example.com/v1/operator/merchants/YOUR_MERCHANT_ID/api-credentials/YOUR_CREDENTIAL_ID/revoke", {
method: "POST",
headers: {
"Authorization": `Bearer ${token}`,
"Idempotency-Key": "operator-request-1042",
"Content-Type": "application/json"
},
body,
redirect: "error",
signal: AbortSignal.timeout(30000)
});
if (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);
console.log(await response.json());<?php
// PHP 8+ with the cURL extension; run on your server.
$token = getenv('WHOLLY_TOKEN');
if (!$token) { throw new RuntimeException('Set WHOLLY_TOKEN'); }
// Keep this key and the exact body for retries; use a new key for each new operation.
$body = <<<'JSON'
{}
JSON;
$ch = curl_init("https://api.example.com/v1/operator/merchants/YOUR_MERCHANT_ID/api-credentials/YOUR_CREDENTIAL_ID/revoke");
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => 'POST',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_FOLLOWLOCATION => false,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTPHEADER => ['Authorization: Bearer ' . $token, "Idempotency-Key: operator-request-1042", "Content-Type: application/json"],
CURLOPT_POSTFIELDS => $body,
]);
$response = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if ($response === false) { throw new RuntimeException(curl_error($ch)); }
curl_close($ch);
if ($status < 200 || $status >= 300) { throw new RuntimeException("HTTP $status: $response"); }
print_r(json_decode($response, true, 512, JSON_THROW_ON_ERROR));# Python 3 · standard library; run on your server.
import json
import os
from urllib.request import Request, build_opener, HTTPRedirectHandler
class NoRedirect(HTTPRedirectHandler):
def redirect_request(self, req, fp, code, msg, headers, newurl):
return None
# Keep this key and the exact body for retries; use a new key for each new operation.
headers = {
"Idempotency-Key": "operator-request-1042",
"Content-Type": "application/json"
}
headers["Authorization"] = "Bearer " + os.environ["WHOLLY_TOKEN"]
body = """{}""".encode("utf-8")
request = Request("https://api.example.com/v1/operator/merchants/YOUR_MERCHANT_ID/api-credentials/YOUR_CREDENTIAL_ID/revoke",
method="POST", headers=headers, data=body)
# Non-2xx responses raise HTTPError. Do not retry writes with a new key.
with build_opener(NoRedirect()).open(request, timeout=30) as response:
print(json.load(response))Esempio di risposta · 200 application/json
{
"revoked": true
}POSTControlla token di invito/v1/onboarding/invitations/checkPubblico
Attivazione solo tramite token. Non accetta una chiave operatore e non effettua l'accesso automatico. Il login console richiede comunque Basic Auth del sito e il TOTP esistente.
- Invito di 48 ore; link di reimpostazione password di un'ora. Token con hash monouso. Riemettere revoca il link precedente. L'accettazione conserva TOTP e revoca le vecchie sessioni.
- Nessun nuovo tentativo automatico. Se l'accettazione scade per timeout, controlla lo stato del link e prova ad accedere; non presumere un fallimento. Frequenza limitata per IP di origine osservato. Il destinatario deve fornire personalmente il consenso alla custodia.
- Gli esempi di risposta mostrano alcuni campi. Considera aggiuntivi gli altri campi della risposta.
| Parametro | Tipo / posizione | Regola |
|---|---|---|
| token | string · required | Segreto dal frammento dell'URL di invito. Non registrarlo mai nei log. |
Richiesta
curl --fail-with-body --max-time 30 \
--request POST \
--url "https://api.example.com/v1/onboarding/invitations/check" \
--header 'Content-Type: application/json' \
--data-raw '{
"token": "YOUR_PRIVATE_INVITATION_TOKEN"
}'// Node.js 18+ · run on your server, never in browser code.
const body = `{
"token": "YOUR_PRIVATE_INVITATION_TOKEN"
}`;
const response = await fetch("https://api.example.com/v1/onboarding/invitations/check", {
method: "POST",
headers: {
"Content-Type": "application/json"
},
body,
redirect: "error",
signal: AbortSignal.timeout(30000)
});
if (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);
console.log(await response.json());<?php
// PHP 8+ with the cURL extension; run on your server.
$body = <<<'JSON'
{
"token": "YOUR_PRIVATE_INVITATION_TOKEN"
}
JSON;
$ch = curl_init("https://api.example.com/v1/onboarding/invitations/check");
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => 'POST',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_FOLLOWLOCATION => false,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTPHEADER => ["Content-Type: application/json"],
CURLOPT_POSTFIELDS => $body,
]);
$response = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if ($response === false) { throw new RuntimeException(curl_error($ch)); }
curl_close($ch);
if ($status < 200 || $status >= 300) { throw new RuntimeException("HTTP $status: $response"); }
print_r(json_decode($response, true, 512, JSON_THROW_ON_ERROR));# Python 3 · standard library; run on your server.
import json
from urllib.request import Request, build_opener, HTTPRedirectHandler
class NoRedirect(HTTPRedirectHandler):
def redirect_request(self, req, fp, code, msg, headers, newurl):
return None
headers = {
"Content-Type": "application/json"
}
body = """{
"token": "YOUR_PRIVATE_INVITATION_TOKEN"
}""".encode("utf-8")
request = Request("https://api.example.com/v1/onboarding/invitations/check",
method="POST", headers=headers, data=body)
# Non-2xx responses raise HTTPError. Do not retry writes with a new key.
with build_opener(NoRedirect()).open(request, timeout=30) as response:
print(json.load(response))Esempio di risposta · 200 application/json
{
"kind": "invitation",
"email": "admin@example.test",
"merchant_name": "Example shop"
}POSTAccetta invito o reimpostazione password/v1/onboarding/invitations/acceptPubblico
Attivazione solo tramite token. Non accetta una chiave operatore e non effettua l'accesso automatico. Il login console richiede comunque Basic Auth del sito e il TOTP esistente.
- Invito di 48 ore; link di reimpostazione password di un'ora. Token con hash monouso. Riemettere revoca il link precedente. L'accettazione conserva TOTP e revoca le vecchie sessioni.
- Nessun nuovo tentativo automatico. Se l'accettazione scade per timeout, controlla lo stato del link e prova ad accedere; non presumere un fallimento. Frequenza limitata per IP di origine osservato. Il destinatario deve fornire personalmente il consenso alla custodia.
- Gli esempi di risposta mostrano alcuni campi. Considera aggiuntivi gli altri campi della risposta.
| Parametro | Tipo / posizione | Regola |
|---|---|---|
| token | string · required | Segreto dal frammento dell'URL di invito. Non registrarlo mai nei log. |
| password | string · required | Nuova password, 12–128 caratteri (massimo 512 byte UTF-8). |
| custody_acknowledged | boolean | Deve essere true quando si accetta un nuovo invito per wallet ospitati. |
Richiesta
curl --fail-with-body --max-time 30 \
--request POST \
--url "https://api.example.com/v1/onboarding/invitations/accept" \
--header 'Content-Type: application/json' \
--data-raw '{
"token": "YOUR_PRIVATE_INVITATION_TOKEN",
"password": "REPLACE_WITH_YOUR_OWN_UNIQUE_PASSWORD",
"custody_acknowledged": true
}'// Node.js 18+ · run on your server, never in browser code.
const body = `{
"token": "YOUR_PRIVATE_INVITATION_TOKEN",
"password": "REPLACE_WITH_YOUR_OWN_UNIQUE_PASSWORD",
"custody_acknowledged": true
}`;
const response = await fetch("https://api.example.com/v1/onboarding/invitations/accept", {
method: "POST",
headers: {
"Content-Type": "application/json"
},
body,
redirect: "error",
signal: AbortSignal.timeout(30000)
});
if (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);
console.log(await response.json());<?php
// PHP 8+ with the cURL extension; run on your server.
$body = <<<'JSON'
{
"token": "YOUR_PRIVATE_INVITATION_TOKEN",
"password": "REPLACE_WITH_YOUR_OWN_UNIQUE_PASSWORD",
"custody_acknowledged": true
}
JSON;
$ch = curl_init("https://api.example.com/v1/onboarding/invitations/accept");
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => 'POST',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_FOLLOWLOCATION => false,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTPHEADER => ["Content-Type: application/json"],
CURLOPT_POSTFIELDS => $body,
]);
$response = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if ($response === false) { throw new RuntimeException(curl_error($ch)); }
curl_close($ch);
if ($status < 200 || $status >= 300) { throw new RuntimeException("HTTP $status: $response"); }
print_r(json_decode($response, true, 512, JSON_THROW_ON_ERROR));# Python 3 · standard library; run on your server.
import json
from urllib.request import Request, build_opener, HTTPRedirectHandler
class NoRedirect(HTTPRedirectHandler):
def redirect_request(self, req, fp, code, msg, headers, newurl):
return None
headers = {
"Content-Type": "application/json"
}
body = """{
"token": "YOUR_PRIVATE_INVITATION_TOKEN",
"password": "REPLACE_WITH_YOUR_OWN_UNIQUE_PASSWORD",
"custody_acknowledged": true
}""".encode("utf-8")
request = Request("https://api.example.com/v1/onboarding/invitations/accept",
method="POST", headers=headers, data=body)
# Non-2xx responses raise HTTPError. Do not retry writes with a new key.
with build_opener(NoRedirect()).open(request, timeout=30) as response:
print(json.load(response))Esempio di risposta · 200 application/json
{
"password_set": true
}GETElenca eccezioni di pagamento/v1/projects/{project_id}/reconciliationSola lettura
Un'unica coda di verifica paginata per pagamenti insufficienti, eccessivi, tardivi, riorganizzati o ambigui, consegne fallite e metodi disattivati/scaduti. I casi riconosciuti da un operatore si riaprono quando arrivano nuove prove.
- Sola lettura, limitata al progetto e coperta dalla quota della credenziale. Decisioni finanziarie e rimborsi restano disponibili solo nella console.
- Le righe contengono id (UUID interno), invoice_id (UUID pubblico, uguale ai callback), informazioni del negozio, importo/valuta fiat originali, invoice_status, stato del caso, motivi, revisione e updated_at. Usa invoice_id nell'endpoint merchant di dettaglio.
- Il rilevamento automatico segue la finestra originale di monitoraggio della fattura; Ripeti scansione estende l'osservazione di un'ora senza abilitare il checkout. I metodi dopo il regolamento o annullati continuano a essere monitorati entro quella finestra.
| Header | Presenza | Regola |
|---|---|---|
| Authorization | obbligatorio | Bearer YOUR_MERCHANT_API_TOKEN |
| Parametro | Tipo / posizione | Regola |
|---|---|---|
| project_id | path UUID | Progetto assegnato a questa credenziale. |
| status | query string | open (predefinito), resolved o all. |
| reason | query string | underpaid, overpaid, late, reorged, ambiguous, delivery_failed, disabled_method o expired_method. |
| search | query string | Fino a 100 caratteri: ID fattura, ordine, cliente o negozio. |
| store_id | query UUID | Filtro facoltativo per negozio. |
| page | query integer | 1–40001. 25 casi fissi per pagina. |
Risposta della coda delle eccezioni
| Campo | Tipo | Presenza | Descrizione |
|---|---|---|---|
| data | ExceptionRow[] | sempre | Casi aggiornati più di recente per primi. Usa invoice_id, non l'id interno, negli URL merchant di dettaglio. |
| pagination | object | sempre | page (1–40001), per_page (25), total delle righe corrispondenti, has_more. |
| counts | object | sempre | Totali open e resolved dell'intero progetto, indipendenti dai filtri attuali. |
ExceptionRow
| Campo | Tipo | Presenza | Descrizione |
|---|---|---|---|
| id / invoice_id | UUID | sempre | ID del record interno / UUID della fattura visibile al cliente. invoice_id corrisponde ai payload dei callback. |
| store_id / store_name | UUID / string | sempre | Negozio proprietario. |
| order_id / email | string | null | sempre | Riferimento ordine privato del commerciante ed email cliente. |
| amount / currency | decimal string / string | sempre | Importo e valuta fiat originali della fattura. |
| invoice_status | invoice status | sempre | Stato attuale del ciclo di vita del pagamento. |
| status / reasons | open|resolved / string[] | sempre | Stato del caso e tipi di eccezione elencati nel filtro reason. |
| revision / updated_at | integer / timestamp | sempre | Revisione attuale della verifica e ora di aggiornamento. |
Richiesta
: "${WHOLLY_TOKEN:?Set WHOLLY_TOKEN to your server-side API token}"
curl --fail-with-body --max-time 30 \
--request GET \
--url "https://api.example.com/v1/projects/YOUR_PROJECT_ID/reconciliation?status=open&page=1" \
--header "Authorization: Bearer $WHOLLY_TOKEN"// Node.js 18+ · run on your server, never in browser code.
const token = process.env.WHOLLY_TOKEN;
if (!token) throw new Error("Set WHOLLY_TOKEN");
const response = await fetch("https://api.example.com/v1/projects/YOUR_PROJECT_ID/reconciliation?status=open&page=1", {
method: "GET",
headers: {
"Authorization": `Bearer ${token}`
},
redirect: "error",
signal: AbortSignal.timeout(30000)
});
if (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);
console.log(await response.json());<?php
// PHP 8+ with the cURL extension; run on your server.
$token = getenv('WHOLLY_TOKEN');
if (!$token) { throw new RuntimeException('Set WHOLLY_TOKEN'); }
$ch = curl_init("https://api.example.com/v1/projects/YOUR_PROJECT_ID/reconciliation?status=open&page=1");
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => 'GET',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_FOLLOWLOCATION => false,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTPHEADER => ['Authorization: Bearer ' . $token],
]);
$response = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if ($response === false) { throw new RuntimeException(curl_error($ch)); }
curl_close($ch);
if ($status < 200 || $status >= 300) { throw new RuntimeException("HTTP $status: $response"); }
print_r(json_decode($response, true, 512, JSON_THROW_ON_ERROR));# Python 3 · standard library; run on your server.
import json
import os
from urllib.request import Request, build_opener, HTTPRedirectHandler
class NoRedirect(HTTPRedirectHandler):
def redirect_request(self, req, fp, code, msg, headers, newurl):
return None
headers = {}
headers["Authorization"] = "Bearer " + os.environ["WHOLLY_TOKEN"]
request = Request("https://api.example.com/v1/projects/YOUR_PROJECT_ID/reconciliation?status=open&page=1",
method="GET", headers=headers)
# Non-2xx responses raise HTTPError. Do not retry writes with a new key.
with build_opener(NoRedirect()).open(request, timeout=30) as response:
print(json.load(response))Esempio di risposta · 200 application/json
{"data":[],"pagination":{"page":1,"per_page":25,"total":0,"has_more":false},"counts":{"open":0,"resolved":0}}GETLeggi le prove di riconciliazione/v1/projects/{project_id}/reconciliation/{invoice_id}Sola lettura
Restituisce fattura, caso, totali esatti per metodo e importi rimborsabili, transazioni osservate, cronologia consegne, decisioni del commerciante e trasferimenti di rimborso collegati. Non espone mai chiavi di firma o segreti dei callback.
- case è null quando la fattura non ha generato un'eccezione. Vengono restituite le 100 osservazioni e 50 consegne più recenti; la cronologia delle decisioni è paginata.
- refundable_atomic richiede almeno una conferma di rete, esclude le prenotazioni di rimborso esistenti e non promette fondi spendibili nel wallet. Una quotazione attuale verifica anche disponibilità del wallet, saldi di origine e commissioni.
- Un rimborso trasmesso significa inviato a un endpoint blockchain, non una ricezione del cliente confermata indipendentemente. Le commissioni sono aggiuntive e quelle di elaborazione fiat non vengono automaticamente riaccreditate emettendo un rimborso.
- Menu progetto della console → Richiede attenzione offre annullamento, accettazione, rifiuto, riapertura, verifica, note, Ripeti scansione, ritentativo di consegna e rimborsi sulle blockchain supportate. Le decisioni usano sessioni protette da CSRF, request_id univoco, revisione attuale del caso, nota obbligatoria e conferma esplicita; i token bearer non possono invocare queste modifiche.
| Header | Presenza | Regola |
|---|---|---|
| Authorization | obbligatorio | Bearer YOUR_MERCHANT_API_TOKEN |
| Parametro | Tipo / posizione | Regola |
|---|---|---|
| project_id | path UUID | Progetto assegnato. |
| invoice_id | path UUID | UUID pubblico della fattura, non id interno. |
| page | query integer | Pagina della cronologia decisioni, da 1; 25 decisioni per pagina. |
Risposta di riconciliazione
| Campo | Tipo | Presenza | Descrizione |
|---|---|---|---|
| invoice | InvoiceDetail | sempre | Fattura merchant completa: campi riepilogativi, metadati privati e payment_intents. Non racchiusa in data. |
| case | object | null | sempre | Caso attuale con stato, motivi, revisione e timestamp; null senza eccezioni. Le prove interne sono escluse. |
| methods | object[] | sempre | id, wallet_id, asset_id, symbol, chain, decimals, expected_atomic, received_atomic, confirmed_atomic, refundable_atomic, address, tag, monitor_error, last_checked_at, monitoring_expires_at e spending_supported. Gli importi atomici sono stringhe. |
| history | object[] | sempre | Le 25 decisioni più recenti di questa pagina: id, action, note, actor, result, created_at. |
| history_pagination | object | sempre | page, per_page (25), total. Solo la cronologia delle decisioni è paginata tramite page. |
| refunds | object[] | sempre | I 100 rimborsi più recenti: id, payment_intent_id, amount_atomic, destination, status, request, treasury_intent_id, transfer_status, created_at e transactions (id/status). L'invio dei rimborsi è disponibile solo nella console. |
| observations | object[] | sempre | I 100 più recenti: payment_intent_id, transaction_id, event_index, amount, status, confirmations, observed_at, symbol, chain e disabled_at_detection. explorer_name/explorer_url sono inclusi dove supportati. |
| deliveries | object[] | sempre | Le 50 più recenti: id, kind, status, attempts, response_status, error, next_attempt_at, event_type e created_at. Nessun segreto dei callback. |
Riepilogo fattura
| Campo | Tipo | Presenza | Descrizione |
|---|---|---|---|
| id | UUID | sempre | UUID interno della fattura. Non usarlo nei percorsi merchant di dettaglio o checkout. |
| invoice_id | UUID | sempre | UUID pubblico della fattura usato nei percorsi merchant di dettaglio e checkout. |
| project_id | UUID | sempre | Progetto proprietario. |
| store_id | UUID | sempre | Negozio proprietario. |
| source | manual | api | sempre | Come è stata creata la fattura. |
| order_id | string | null | sempre | Riferimento dell'ordine del commerciante. |
| string | null | sempre | Email cliente solo per il commerciante. Mai restituita dal checkout pubblico. | |
| customer_name | string | null | sempre | Nome visualizzato derivato dai metadati privati firstname, lastname e company. |
| customer_address | string | null | sempre | Indirizzo del commerciante su una riga derivato dai metadati privati company, street, street2, zip, city, country, countryiso2 e vatid. |
| description | string | null | sempre | Descrizione visibile al cliente. |
| amount | decimal string | sempre | Importo canonico della fattura. |
| currency | string | sempre | Codice normalizzato della valuta/asset della fattura. |
| exchange_rate_spread_percent | decimal string | sempre | Spread della quotazione bloccato: il valore specificato alla creazione o il predefinito del negozio se omesso. Applicato prima dell'arrotondamento per eccesso; non cambia mai su questa fattura. |
| underpayment_tolerance_percent | decimal string | sempre | Percentuale immutabile di ammanco accettato salvata alla creazione della fattura. |
| status | invoice status | sempre | new, processing, settled, expired, invalid o cancelled. |
| amount_status | amount status | sempre | none, partial, paid o overpaid. Una fattura a importo zero esplicitamente consentita si salda con none e nessun metodo di pagamento. |
| timing_status | timing status | sempre | on_time o late. |
| resolution | resolution | sempre | automatic, manually_settled o manually_invalidated. |
| sequence | integer | sempre | Sequenza monotona dello stato della fattura, a partire da 1. |
| winning_payment_intent_id | UUID | null | sempre | Metodo di pagamento che ha risolto la fattura, quando selezionato. |
| expires_at | RFC 3339 timestamp | sempre | Scadenza della quotazione/pagamento. |
| monitoring_expires_at | RFC 3339 timestamp | sempre | Ultimo termine configurato di monitoraggio tardivo tra i metodi di pagamento. |
| settled_at | timestamp | null | sempre | Momento del regolamento quando saldata. |
| cancelled_at | timestamp | null | sempre | Momento dell'annullamento quando annullata. |
| archived_at | timestamp | null | sempre | Momento dell'archiviazione quando archiviata. |
| created_at | RFC 3339 timestamp | sempre | Momento della creazione. |
| updated_at | RFC 3339 timestamp | sempre | Momento dell'ultimo aggiornamento di stato. |
Aggiunte al dettaglio fattura
| Campo | Tipo | Presenza | Descrizione |
|---|---|---|---|
| ipn_url | string | null | sempre | Destinazione IPN effettiva della singola fattura. Solo risposta merchant; omessa dal checkout pubblico. |
| redirect_url | string | null | sempre | URL di successo effettivo usato dopo il regolamento. |
| cancel_url | string | null | sempre | URL di ritorno effettivo usato quando il checkout termina senza pagamento riuscito. |
| redirect_automatically | boolean | sempre | Indica se il checkout deve reindirizzare automaticamente dopo il successo. |
| checkout_language | string | sempre | Tag di lingua effettivo del checkout. |
| metadata | object | sempre | Metadati del commerciante. Mai restituiti dal checkout pubblico. |
| payment_intents | PaymentIntent[] | sempre | Metodi di pagamento quotati e stato del monitoraggio. |
Richiesta
: "${WHOLLY_TOKEN:?Set WHOLLY_TOKEN to your server-side API token}"
curl --fail-with-body --max-time 30 \
--request GET \
--url "https://api.example.com/v1/projects/YOUR_PROJECT_ID/reconciliation/YOUR_PUBLIC_INVOICE_ID" \
--header "Authorization: Bearer $WHOLLY_TOKEN"// Node.js 18+ · run on your server, never in browser code.
const token = process.env.WHOLLY_TOKEN;
if (!token) throw new Error("Set WHOLLY_TOKEN");
const response = await fetch("https://api.example.com/v1/projects/YOUR_PROJECT_ID/reconciliation/YOUR_PUBLIC_INVOICE_ID", {
method: "GET",
headers: {
"Authorization": `Bearer ${token}`
},
redirect: "error",
signal: AbortSignal.timeout(30000)
});
if (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);
console.log(await response.json());<?php
// PHP 8+ with the cURL extension; run on your server.
$token = getenv('WHOLLY_TOKEN');
if (!$token) { throw new RuntimeException('Set WHOLLY_TOKEN'); }
$ch = curl_init("https://api.example.com/v1/projects/YOUR_PROJECT_ID/reconciliation/YOUR_PUBLIC_INVOICE_ID");
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => 'GET',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_FOLLOWLOCATION => false,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTPHEADER => ['Authorization: Bearer ' . $token],
]);
$response = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if ($response === false) { throw new RuntimeException(curl_error($ch)); }
curl_close($ch);
if ($status < 200 || $status >= 300) { throw new RuntimeException("HTTP $status: $response"); }
print_r(json_decode($response, true, 512, JSON_THROW_ON_ERROR));# Python 3 · standard library; run on your server.
import json
import os
from urllib.request import Request, build_opener, HTTPRedirectHandler
class NoRedirect(HTTPRedirectHandler):
def redirect_request(self, req, fp, code, msg, headers, newurl):
return None
headers = {}
headers["Authorization"] = "Bearer " + os.environ["WHOLLY_TOKEN"]
request = Request("https://api.example.com/v1/projects/YOUR_PROJECT_ID/reconciliation/YOUR_PUBLIC_INVOICE_ID",
method="GET", headers=headers)
# Non-2xx responses raise HTTPError. Do not retry writes with a new key.
with build_opener(NoRedirect()).open(request, timeout=30) as response:
print(json.load(response))Esempio di risposta · 200 application/json
{"invoice":{"invoice_id":"YOUR_PUBLIC_INVOICE_ID","status":"processing"},"case":{"status":"open","reasons":["underpaid"],"revision":1},"methods":[],"history":[],"history_pagination":{"page":1,"per_page":25,"total":0},"refunds":[],"observations":[],"deliveries":[]}GETDiscovery del servizio API/Pubblico
Risposta del livello esterno dell'host API gestito che conferma il ruolo di API pubblica v1. È prodotta dal proxy gestito, non dal router Axum del merchant.
- Non serve un token bearer.
- Solo il nome host API gestito garantisce questa risposta esatta alla radice.
Richiesta
curl --fail-with-body --max-time 30 \
--request GET \
--url "https://api.example.com/"// Node.js 18+ · run on your server, never in browser code.
const response = await fetch("https://api.example.com/", {
method: "GET",
headers: {},
redirect: "error",
signal: AbortSignal.timeout(30000)
});
if (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);
console.log(await response.json());<?php
// PHP 8+ with the cURL extension; run on your server.
$ch = curl_init("https://api.example.com/");
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => 'GET',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_FOLLOWLOCATION => false,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTPHEADER => [],
]);
$response = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if ($response === false) { throw new RuntimeException(curl_error($ch)); }
curl_close($ch);
if ($status < 200 || $status >= 300) { throw new RuntimeException("HTTP $status: $response"); }
print_r(json_decode($response, true, 512, JSON_THROW_ON_ERROR));# Python 3 · standard library; run on your server.
import json
from urllib.request import Request, build_opener, HTTPRedirectHandler
class NoRedirect(HTTPRedirectHandler):
def redirect_request(self, req, fp, code, msg, headers, newurl):
return None
headers = {}
request = Request("https://api.example.com/",
method="GET", headers=headers)
# Non-2xx responses raise HTTPError. Do not retry writes with a new key.
with build_opener(NoRedirect()).open(request, timeout=30) as response:
print(json.load(response))Esempio di risposta · 200 application/json
{
"service": "Wholly Crypto API",
"status": "ready",
"version": "v1"
}GETStato del servizio/healthzPubblico
Controlla la raggiungibilità dell'app e un ping del database di due secondi. Usalo per il monitoraggio, non come sostituto dello stato della fattura.
- Non serve un token bearer.
- Il valore version è la versione del pacchetto in esecuzione, non quella del percorso API.
Richiesta
curl --fail-with-body --max-time 30 \
--request GET \
--url "https://api.example.com/healthz"// Node.js 18+ · run on your server, never in browser code.
const response = await fetch("https://api.example.com/healthz", {
method: "GET",
headers: {},
redirect: "error",
signal: AbortSignal.timeout(30000)
});
if (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);
console.log(await response.json());<?php
// PHP 8+ with the cURL extension; run on your server.
$ch = curl_init("https://api.example.com/healthz");
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => 'GET',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_FOLLOWLOCATION => false,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTPHEADER => [],
]);
$response = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if ($response === false) { throw new RuntimeException(curl_error($ch)); }
curl_close($ch);
if ($status < 200 || $status >= 300) { throw new RuntimeException("HTTP $status: $response"); }
print_r(json_decode($response, true, 512, JSON_THROW_ON_ERROR));# Python 3 · standard library; run on your server.
import json
from urllib.request import Request, build_opener, HTTPRedirectHandler
class NoRedirect(HTTPRedirectHandler):
def redirect_request(self, req, fp, code, msg, headers, newurl):
return None
headers = {}
request = Request("https://api.example.com/healthz",
method="GET", headers=headers)
# Non-2xx responses raise HTTPError. Do not retry writes with a new key.
with build_opener(NoRedirect()).open(request, timeout=30) as response:
print(json.load(response))Esempio di risposta · 200 funzionante; 503 database non disponibile
{
"status": "ok",
"database": "ok",
"version": "0.1.0"
}GETElenca asset di pagamento del progetto/v1/projects/{project_id}/payment-assetsSola lettura
Elenca asset nativi e token verificati con politica del progetto, disponibilità del wallet della blockchain e capacità di scansione e lettura saldi installate. scanner_ready indica la presenza dell'adattatore compilato, non il risultato del quorum attuale degli endpoint. Dalla 6.0.6, la creazione conserva i metodi configurati durante le interruzioni dello scanner. La verifica di ricezione richiede comunque la soglia configurata di provider funzionanti con ruolo esatto (2 predefiniti, 1 facoltativo).
- Un token può essere elencato globalmente ma restare non selezionabile quando scanner_ready o payment_supported è false.
- La matrice delle capacità dell'operatore richiede anche il ruolo endpoint esatto dello scanner; un endpoint funzionante che serve un'API incompatibile non viene contato.
- I token condividono il wallet di progetto della loro blockchain nativa; non creano un'altra frase seed.
- I riepiloghi wallet incorporati riguardano solo la disponibilità e mantengono i saldi vuoti; usa GET /v1/projects/{project_id}/wallets per i saldi arricchiti.
| Header | Presenza | Regola |
|---|---|---|
| Authorization | obbligatorio | Bearer YOUR_MERCHANT_API_TOKEN |
| Accept | consigliato | application/json |
| Parametro | Tipo / posizione | Regola |
|---|---|---|
| project_id | path UUID | Progetto attivo assegnato alla credenziale. |
PaymentAsset
| Campo | Tipo | Presenza | Descrizione |
|---|---|---|---|
| id | UUID | sempre | Identificativo persistente dell'asset di pagamento usato dalle rotte delle politiche di progetto e negozio. |
| asset_key | string | sempre | Identità canonica dell'asset nativo o contratto in stile CAIP. |
| chain_slug / network | string | sempre | Identificativo blockchain Wholly Crypto e rete configurata. |
| caip_network_id / caip_asset_id | string / string|null | sempre | Identità canoniche di rete e asset. |
| asset_kind | native | token | sempre | Indica se il regolamento usa la valuta della blockchain o un contratto/mint verificato. |
| payment_rail | string | sempre | Canale runtime: utxo, evm-native, solana-native, account-native, privacy-native o token-transfer. |
| symbol / name / decimals | string / string / integer | sempre | Identità visualizzata e precisione esatta in unità atomiche. |
| contract_address | string | null | sempre | Contratto ERC-20 o mint SPL canonico per i token; null per gli asset nativi. |
| coingecko_id | string | null | sempre | Identità per ricerca e prezzi. Null per contratti personalizzati; non dedurre mai un prezzo di mercato dal ticker. I soli metadati CoinGecko non rendono mai un token selezionabile. |
| custom_token | boolean | sempre | Contratto personalizzato verificato on-chain, con prezzo fisso in USD o pool DEX selezionato a livello di progetto. |
| icon_path | path | null | sempre | Icona del token in cache locale, se disponibile. |
| token_standard | erc20 | spl-token | null | sempre | Standard token verificato del runtime; null per gli asset nativi. |
| metadata_verified_at | timestamp | null | sempre | Momento della verifica dei metadati on-chain per i token promossi. |
| payment_supported / scanner_ready / balance_ready | boolean | sempre | Requisiti del registro verificati in compilazione. scanner_ready indica che il runtime dello scanner dei pagamenti è installato; la conferma richiede il numero configurato di provider funzionanti con ruolo esatto (2 predefiniti, 1 facoltativo); l'indisponibilità temporanea dello scanner non blocca la creazione di fatture dalla 6.0.6. balance_ready è true solo per adattatori di saldo implementati. |
| default_finality_mode | confirmations | finalized | sempre | Modello di finalità predefinito ereditato da una nuova politica di progetto. |
| default_required_confirmations / default_monitoring_minutes | integer | sempre | Politica predefinita di conferma e monitoraggio. |
ProjectPaymentAsset
| Campo | Tipo | Presenza | Descrizione |
|---|---|---|---|
| asset | PaymentAsset | sempre | Asset nativo o token verificato persistente. |
| policy | ProjectAssetPolicy | null | sempre | Politica di attivazione/finalità del progetto, o null se non configurata. Include custom_price_mode (fixed/dex), custom_price_usd (stringa decimale fissa o null), custom_dex_pair (pool selezionato o null) e custom_dex (dex_id, quote_symbol, price_usd attuale o null, liquidity_usd, fetched_at, last_error). I prezzi personalizzati sono condivisi tra i negozi del progetto. |
| wallet | WalletSummary | null | sempre | Wallet di progetto non custodial della blockchain. I token condividono il wallet nativo della loro blockchain. |
| wallet_readiness | readiness enum | sempre | unsupported, project_disabled, project_asset_disabled, store_disabled, store_asset_disabled, wallet_missing, wallet_pending, wallet_disabled, wallet_error, backup_required, account_activation_required, external_wallet_rpc_required o ready. |
| receive_readiness | ReceiveReadiness | null | 5.5.0+ | Valutazione condivisa della configurazione di ricezione del progetto. Include controlli del wallet e dei provider scanner indipendenti, separati dall'aggiornamento dei saldi e dal gas per l'invio. Null se non esiste una politica di progetto. Valuta e tassi vengono controllati alla creazione della fattura. |
WalletSummary
| Campo | Tipo | Presenza | Descrizione |
|---|---|---|---|
| id / project_id / native_asset_id | UUID | sempre | Identificativi del wallet, del progetto proprietario e dell'asset nativo della blockchain. |
| chain_slug / network | string | sempre | Blockchain e rete del wallet. |
| asset_symbol / asset_name | string | sempre | Identità visualizzata dell'asset nativo della blockchain. |
| status | pending | active | disabled | error | sempre | Stato operativo del wallet. |
| label | string | sempre | Etichetta dell'operatore. |
| public_key / primary_address | string | null | sempre | Identità pubblica del wallet; nessuna frase seed o chiave privata viene esposta. |
| derivation_scheme / address_format | string | null | sempre | Politica e formato degli indirizzi. |
| backup_confirmed_at | timestamp | null | sempre | Non null dopo che l'operatore conferma il backup di recupero. |
| activation_required / activation_verified_at | boolean / timestamp|null | sempre | Gli account condivisi XRP e Stellar restano indisponibili finché l'operatore non finanzia l'indirizzo mostrato e i provider scanner configurati non verificano quell'account esatto. La prova persistente non scade; lo stato attuale degli scanner viene controllato separatamente per verificare i pagamenti, non per creare fatture. |
| receive_readiness | ReceiveReadiness | null | 5.5.0+ | Incluso negli elenchi dei wallet: configurazione di ricezione del progetto e prerequisiti degli scanner blockchain. Separato da saldi, gas dei token e disponibilità all'invio. Altre risposte dei wallet possono lasciarlo null. |
| monero_wallet_rpc | MoneroWalletRpcBinding | null | sempre | Stato dell'associazione wallet-RPC esterna in sola visualizzazione per Monero, ripulito dai dati sensibili. Include endpoint, modalità di autenticazione, indirizzo primario dell'account 0, indicatori/altezze delle prove tecniche e timestamp delle attestazioni dell'operatore; credenziali, chiavi e file dei wallet non vengono mai serializzati. |
| last_secret_revealed_at / secret_reveal_count | timestamp|null / integer | sempre | Metadati di audit della divulgazione dei segreti lato console. |
| next_receive_index | integer | sempre | Indice del prossimo indirizzo figlio riservato. |
| last_scanned_height / last_scanned_at / last_error | integer|null / timestamp|null / string|null | sempre | Stato dello scanner wallet. |
| balances | WalletAssetBalance[] | sempre | Saldi in cache per tutti i 30 canali nativi delle blockchain, più asset ERC-20 e SPL verificati. Per Monero serve un wallet-RPC esterno in sola visualizzazione configurato. |
| total_value_usd | decimal string | null | sempre | Somma indicativa dei saldi con un prezzo USD attuale. |
| balance_status | pending | refreshing | fresh | stale | error | unknown | sempre | Aggiornamento aggregato della cache; unknown è un valore di riserva prudente e nessuno di questi stati prova il regolamento della fattura. |
| balance_checked_at | timestamp | null | sempre | Il più vecchio controllo di saldo riuscito pertinente rappresentato dall'aggregato. |
| recent_payments | WalletRecentPayment[] | sempre | Fino alle tre osservazioni valide più recenti detected, confirming o final attribuite a questo esatto wallet. |
| created_at / updated_at | RFC 3339 timestamp | sempre | Momento di creazione e ultimo aggiornamento del wallet. |
ReceiveReadiness
| Campo | Tipo | Presenza | Descrizione |
|---|---|---|---|
| ready | boolean | sempre | I controlli della configurazione di ricezione passano. Non descrive disponibilità alla spesa, gas, aggiornamento saldi o una quotazione futura garantita. |
| invoice_creatable | boolean | 6.0.6+ | La configurazione consente un metodo di fattura nonostante avvisi temporanei dello scanner. Il prezzo della valuta viene controllato alla creazione. Non è una verifica del pagamento: ready può essere false mentre invoice_creatable è true. Wallet mancanti, politica disattivata e adattatori non supportati continuano a bloccare in sicurezza. |
| checked_at | timestamp | sempre | Momento della valutazione. Un elenco non esegue richieste di rete né assegna indirizzi. |
| issues | PaymentMethodIssue[] | sempre | Vuoto quando pronto; altrimenti un avviso di ricezione o un blocco di configurazione. Controlla invoice_creatable per distinguere avvisi temporanei dello scanner da errori di configurazione delle fatture. |
PaymentMethodIssue
| Campo | Tipo | Presenza | Descrizione |
|---|---|---|---|
| chain_slug / asset_id / asset_ticker | string / UUID / string | se noto | Identifica blockchain e asset interessati. Lightning può omettere asset_id. |
| reason_code | string | sempre | scanner_provider_quorum, scanner_not_checked, scanner_unavailable, wallet_missing, wallet_disabled, wallet_backup_required, wallet_key_unavailable, wallet_activation_required, monero_binding_unavailable, rate_unavailable, custom_rate_unavailable, lightning_unavailable, project_disabled, store_disabled, chain_disabled, asset_disabled o asset_not_accepted. |
| message / action | string | se disponibile | Spiegazione per il commerciante e identificativo dell'azione: chain_connections, wallets, rates, payment_methods, project_settings o store_settings. Nessuna credenziale o URL privato dei provider. |
| required_endpoint_role | string | null | on-chain | Ruolo API scanner preferito (campo precedente). Usa accepted_endpoint_roles per l'elenco completo di compatibilità. Lo stato di base del nodo non prova il supporto alla cronologia dei pagamenti. |
| accepted_endpoint_roles | string[] | null | on-chain | Dialetti API compatibili, non prova della cronologia o capacità dell'endpoint. node-rpc diretto supporta BTC/BCH/LTC/DOGE/DASH e ZEC trasparente (blocchi decodificati completi, 1–48 conferme), TRX nativo solidificato, ALGO nativo via algod, XTZ via Octez, DOT Asset Hub finalizzato via metadati SCALE e XLM nativo via Stellar RPC con ID memo fattura. Una cronologia potata o incompleta non è idonea. Questi adattatori diretti non aggiungono canali token. Le API indicizzate restano alternative; vedi la tabella dei canali sotto. Fonti miste dirette/indicizzate verificano in modo indipendente finestre limitate; restano predefiniti due provider indipendenti, non alias dello stesso operatore. Altezza di base del nodo, informazioni blockchain ORDnet e un relay EVM per un canale non EVM non sono prove di ricezione. Monero richiede comunque un wallet-RPC in sola visualizzazione associato al progetto. |
| healthy_endpoints | integer | on-chain | Endpoint funzionanti corrispondenti, non il numero di provider indipendenti. |
| usable_independent_providers / required_independent_providers | integer | on-chain | Slot di verifica utilizzabili, limitati a due. required_independent_providers è l'impostazione della blockchain: 2 predefiniti, o 1 dopo scelta esplicita dell'amministratore. In modalità a due provider servono chiavi provider E host diversi. Fonti disattivate, obsolete (oltre dieci minuti) o in attesa non occupano uno slot. Lightning usa regole di connessione proprie. |
| last_checked_at | timestamp | null | on-chain | Ultimo controllo di stato dell'endpoint corrispondente, separato dal momento della valutazione. |
Richiesta
: "${WHOLLY_TOKEN:?Set WHOLLY_TOKEN to your server-side API token}"
curl --fail-with-body --max-time 30 \
--request GET \
--url "https://api.example.com/v1/projects/YOUR_PROJECT_ID/payment-assets" \
--header "Authorization: Bearer $WHOLLY_TOKEN" \
--header 'Accept: application/json'// Node.js 18+ · run on your server, never in browser code.
const token = process.env.WHOLLY_TOKEN;
if (!token) throw new Error("Set WHOLLY_TOKEN");
const response = await fetch("https://api.example.com/v1/projects/YOUR_PROJECT_ID/payment-assets", {
method: "GET",
headers: {
"Authorization": `Bearer ${token}`,
"Accept": "application/json"
},
redirect: "error",
signal: AbortSignal.timeout(30000)
});
if (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);
console.log(await response.json());<?php
// PHP 8+ with the cURL extension; run on your server.
$token = getenv('WHOLLY_TOKEN');
if (!$token) { throw new RuntimeException('Set WHOLLY_TOKEN'); }
$ch = curl_init("https://api.example.com/v1/projects/YOUR_PROJECT_ID/payment-assets");
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => 'GET',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_FOLLOWLOCATION => false,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTPHEADER => ['Authorization: Bearer ' . $token, "Accept: application/json"],
]);
$response = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if ($response === false) { throw new RuntimeException(curl_error($ch)); }
curl_close($ch);
if ($status < 200 || $status >= 300) { throw new RuntimeException("HTTP $status: $response"); }
print_r(json_decode($response, true, 512, JSON_THROW_ON_ERROR));# Python 3 · standard library; run on your server.
import json
import os
from urllib.request import Request, build_opener, HTTPRedirectHandler
class NoRedirect(HTTPRedirectHandler):
def redirect_request(self, req, fp, code, msg, headers, newurl):
return None
headers = {
"Accept": "application/json"
}
headers["Authorization"] = "Bearer " + os.environ["WHOLLY_TOKEN"]
request = Request("https://api.example.com/v1/projects/YOUR_PROJECT_ID/payment-assets",
method="GET", headers=headers)
# Non-2xx responses raise HTTPError. Do not retry writes with a new key.
with build_opener(NoRedirect()).open(request, timeout=30) as response:
print(json.load(response))Esempio di risposta · 200 application/json
{
"data": [
{
"asset": {
"id": "10000000-0000-4000-8000-000000000003",
"asset_key": "eip155:1/slip44:60",
"chain_slug": "ethereum",
"network": "mainnet",
"caip_network_id": "eip155:1",
"caip_asset_id": "eip155:1/slip44:60",
"asset_kind": "native",
"payment_rail": "evm-native",
"symbol": "ETH",
"name": "Ethereum",
"decimals": 18,
"contract_address": null,
"coingecko_id": "ethereum",
"icon_path": "/assets/coingecko/ethereum.png",
"token_standard": null,
"metadata_verified_at": null,
"payment_supported": true,
"scanner_ready": true,
"balance_ready": true,
"default_finality_mode": "confirmations",
"default_required_confirmations": 12,
"default_monitoring_minutes": 60
},
"policy": {
"enabled": true,
"finality_mode": "confirmations",
"required_confirmations": 12,
"monitoring_minutes": 60,
"late_monitoring_days": 30
},
"wallet": null,
"wallet_readiness": "wallet_missing",
"receive_readiness": { "ready": false, "invoice_creatable": false, "checked_at": "2026-09-16T09:00:00Z", "issues": [{ "chain_slug": "ethereum", "asset_id": "10000000-0000-4000-8000-000000000003", "asset_ticker": "ETH", "reason_code": "wallet_missing", "message": "ethereum / ETH: Create a project wallet for this chain.", "action": "wallets" }] }
}
]
}PUTAggiorna la politica degli asset del progetto/v1/projects/{project_id}/payment-assets/{asset_id}Lettura + scrittura
Crea o sostituisce la politica del progetto per un asset persistente e restituisce l'elenco aggiornato degli asset di progetto. Disattivare una blockchain nativa rende il suo asset nativo e i suoi token indisponibili per nuove fatture, ma conserva politiche token, wallet e selezioni dei negozi per consentirne la riattivazione.
- Il corpo sostituisce l'intera politica e rifiuta campi sconosciuti.
- L'abilitazione nel progetto non seleziona da sola l'asset per alcun negozio.
| Header | Presenza | Regola |
|---|---|---|
| Authorization | obbligatorio | Bearer YOUR_MERCHANT_API_TOKEN |
| Content-Type | obbligatorio | application/json |
| Accept | consigliato | application/json |
| Parametro | Tipo / posizione | Regola |
|---|---|---|
| project_id | path UUID | Progetto attivo assegnato alla credenziale. |
| asset_id | path UUID | ID dell'asset restituito dall'elenco asset del progetto o dalla registrazione del token. |
Aggiornamento politica asset del progetto
| Campo | Tipo | Presenza | Descrizione |
|---|---|---|---|
| enabled | boolean | obbligatorio | Abilita o disabilita l'asset per il progetto. La blockchain nativa deve essere abilitata prima di qualsiasi token. |
| finality_mode | confirmations | finalized | obbligatorio | Politica di finalità supportata dal canale dell'asset. finalized richiede required_confirmations=1. |
| required_confirmations | integer | obbligatorio | I canali Bitcoin ed EVM accettano zero; gli altri canali a conferme richiedono almeno una conferma, quelli solo finalized ne richiedono esattamente una e i canali EVM sono limitati a 0–48 affinché ogni trasferimento resti nella finestra di rilettura delle transazioni. |
| monitoring_minutes | integer | obbligatorio | Finestra di polling di 1–10.080 minuti mentre una fattura è attiva. |
| late_monitoring_days | integer | obbligatorio | 0–3.650 giorni di monitoraggio dopo la scadenza della fattura. |
PaymentAsset
| Campo | Tipo | Presenza | Descrizione |
|---|---|---|---|
| id | UUID | sempre | Identificativo persistente dell'asset di pagamento usato dalle rotte delle politiche di progetto e negozio. |
| asset_key | string | sempre | Identità canonica dell'asset nativo o contratto in stile CAIP. |
| chain_slug / network | string | sempre | Identificativo blockchain Wholly Crypto e rete configurata. |
| caip_network_id / caip_asset_id | string / string|null | sempre | Identità canoniche di rete e asset. |
| asset_kind | native | token | sempre | Indica se il regolamento usa la valuta della blockchain o un contratto/mint verificato. |
| payment_rail | string | sempre | Canale runtime: utxo, evm-native, solana-native, account-native, privacy-native o token-transfer. |
| symbol / name / decimals | string / string / integer | sempre | Identità visualizzata e precisione esatta in unità atomiche. |
| contract_address | string | null | sempre | Contratto ERC-20 o mint SPL canonico per i token; null per gli asset nativi. |
| coingecko_id | string | null | sempre | Identità per ricerca e prezzi. Null per contratti personalizzati; non dedurre mai un prezzo di mercato dal ticker. I soli metadati CoinGecko non rendono mai un token selezionabile. |
| custom_token | boolean | sempre | Contratto personalizzato verificato on-chain, con prezzo fisso in USD o pool DEX selezionato a livello di progetto. |
| icon_path | path | null | sempre | Icona del token in cache locale, se disponibile. |
| token_standard | erc20 | spl-token | null | sempre | Standard token verificato del runtime; null per gli asset nativi. |
| metadata_verified_at | timestamp | null | sempre | Momento della verifica dei metadati on-chain per i token promossi. |
| payment_supported / scanner_ready / balance_ready | boolean | sempre | Requisiti del registro verificati in compilazione. scanner_ready indica che il runtime dello scanner dei pagamenti è installato; la conferma richiede il numero configurato di provider funzionanti con ruolo esatto (2 predefiniti, 1 facoltativo); l'indisponibilità temporanea dello scanner non blocca la creazione di fatture dalla 6.0.6. balance_ready è true solo per adattatori di saldo implementati. |
| default_finality_mode | confirmations | finalized | sempre | Modello di finalità predefinito ereditato da una nuova politica di progetto. |
| default_required_confirmations / default_monitoring_minutes | integer | sempre | Politica predefinita di conferma e monitoraggio. |
ProjectPaymentAsset
| Campo | Tipo | Presenza | Descrizione |
|---|---|---|---|
| asset | PaymentAsset | sempre | Asset nativo o token verificato persistente. |
| policy | ProjectAssetPolicy | null | sempre | Politica di attivazione/finalità del progetto, o null se non configurata. Include custom_price_mode (fixed/dex), custom_price_usd (stringa decimale fissa o null), custom_dex_pair (pool selezionato o null) e custom_dex (dex_id, quote_symbol, price_usd attuale o null, liquidity_usd, fetched_at, last_error). I prezzi personalizzati sono condivisi tra i negozi del progetto. |
| wallet | WalletSummary | null | sempre | Wallet di progetto non custodial della blockchain. I token condividono il wallet nativo della loro blockchain. |
| wallet_readiness | readiness enum | sempre | unsupported, project_disabled, project_asset_disabled, store_disabled, store_asset_disabled, wallet_missing, wallet_pending, wallet_disabled, wallet_error, backup_required, account_activation_required, external_wallet_rpc_required o ready. |
| receive_readiness | ReceiveReadiness | null | 5.5.0+ | Valutazione condivisa della configurazione di ricezione del progetto. Include controlli del wallet e dei provider scanner indipendenti, separati dall'aggiornamento dei saldi e dal gas per l'invio. Null se non esiste una politica di progetto. Valuta e tassi vengono controllati alla creazione della fattura. |
ReceiveReadiness
| Campo | Tipo | Presenza | Descrizione |
|---|---|---|---|
| ready | boolean | sempre | I controlli della configurazione di ricezione passano. Non descrive disponibilità alla spesa, gas, aggiornamento saldi o una quotazione futura garantita. |
| invoice_creatable | boolean | 6.0.6+ | La configurazione consente un metodo di fattura nonostante avvisi temporanei dello scanner. Il prezzo della valuta viene controllato alla creazione. Non è una verifica del pagamento: ready può essere false mentre invoice_creatable è true. Wallet mancanti, politica disattivata e adattatori non supportati continuano a bloccare in sicurezza. |
| checked_at | timestamp | sempre | Momento della valutazione. Un elenco non esegue richieste di rete né assegna indirizzi. |
| issues | PaymentMethodIssue[] | sempre | Vuoto quando pronto; altrimenti un avviso di ricezione o un blocco di configurazione. Controlla invoice_creatable per distinguere avvisi temporanei dello scanner da errori di configurazione delle fatture. |
PaymentMethodIssue
| Campo | Tipo | Presenza | Descrizione |
|---|---|---|---|
| chain_slug / asset_id / asset_ticker | string / UUID / string | se noto | Identifica blockchain e asset interessati. Lightning può omettere asset_id. |
| reason_code | string | sempre | scanner_provider_quorum, scanner_not_checked, scanner_unavailable, wallet_missing, wallet_disabled, wallet_backup_required, wallet_key_unavailable, wallet_activation_required, monero_binding_unavailable, rate_unavailable, custom_rate_unavailable, lightning_unavailable, project_disabled, store_disabled, chain_disabled, asset_disabled o asset_not_accepted. |
| message / action | string | se disponibile | Spiegazione per il commerciante e identificativo dell'azione: chain_connections, wallets, rates, payment_methods, project_settings o store_settings. Nessuna credenziale o URL privato dei provider. |
| required_endpoint_role | string | null | on-chain | Ruolo API scanner preferito (campo precedente). Usa accepted_endpoint_roles per l'elenco completo di compatibilità. Lo stato di base del nodo non prova il supporto alla cronologia dei pagamenti. |
| accepted_endpoint_roles | string[] | null | on-chain | Dialetti API compatibili, non prova della cronologia o capacità dell'endpoint. node-rpc diretto supporta BTC/BCH/LTC/DOGE/DASH e ZEC trasparente (blocchi decodificati completi, 1–48 conferme), TRX nativo solidificato, ALGO nativo via algod, XTZ via Octez, DOT Asset Hub finalizzato via metadati SCALE e XLM nativo via Stellar RPC con ID memo fattura. Una cronologia potata o incompleta non è idonea. Questi adattatori diretti non aggiungono canali token. Le API indicizzate restano alternative; vedi la tabella dei canali sotto. Fonti miste dirette/indicizzate verificano in modo indipendente finestre limitate; restano predefiniti due provider indipendenti, non alias dello stesso operatore. Altezza di base del nodo, informazioni blockchain ORDnet e un relay EVM per un canale non EVM non sono prove di ricezione. Monero richiede comunque un wallet-RPC in sola visualizzazione associato al progetto. |
| healthy_endpoints | integer | on-chain | Endpoint funzionanti corrispondenti, non il numero di provider indipendenti. |
| usable_independent_providers / required_independent_providers | integer | on-chain | Slot di verifica utilizzabili, limitati a due. required_independent_providers è l'impostazione della blockchain: 2 predefiniti, o 1 dopo scelta esplicita dell'amministratore. In modalità a due provider servono chiavi provider E host diversi. Fonti disattivate, obsolete (oltre dieci minuti) o in attesa non occupano uno slot. Lightning usa regole di connessione proprie. |
| last_checked_at | timestamp | null | on-chain | Ultimo controllo di stato dell'endpoint corrispondente, separato dal momento della valutazione. |
Richiesta
: "${WHOLLY_TOKEN:?Set WHOLLY_TOKEN to your server-side API token}"
curl --fail-with-body --max-time 30 \
--request PUT \
--url "https://api.example.com/v1/projects/YOUR_PROJECT_ID/payment-assets/YOUR_ASSET_ID" \
--header "Authorization: Bearer $WHOLLY_TOKEN" \
--header 'Content-Type: application/json' \
--data-raw '{
"enabled": true,
"finality_mode": "confirmations",
"required_confirmations": 2,
"monitoring_minutes": 60,
"late_monitoring_days": 30
}'// Node.js 18+ · run on your server, never in browser code.
const token = process.env.WHOLLY_TOKEN;
if (!token) throw new Error("Set WHOLLY_TOKEN");
const body = `{
"enabled": true,
"finality_mode": "confirmations",
"required_confirmations": 2,
"monitoring_minutes": 60,
"late_monitoring_days": 30
}`;
const response = await fetch("https://api.example.com/v1/projects/YOUR_PROJECT_ID/payment-assets/YOUR_ASSET_ID", {
method: "PUT",
headers: {
"Authorization": `Bearer ${token}`,
"Content-Type": "application/json"
},
body,
redirect: "error",
signal: AbortSignal.timeout(30000)
});
if (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);
console.log(await response.json());<?php
// PHP 8+ with the cURL extension; run on your server.
$token = getenv('WHOLLY_TOKEN');
if (!$token) { throw new RuntimeException('Set WHOLLY_TOKEN'); }
$body = <<<'JSON'
{
"enabled": true,
"finality_mode": "confirmations",
"required_confirmations": 2,
"monitoring_minutes": 60,
"late_monitoring_days": 30
}
JSON;
$ch = curl_init("https://api.example.com/v1/projects/YOUR_PROJECT_ID/payment-assets/YOUR_ASSET_ID");
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => 'PUT',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_FOLLOWLOCATION => false,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTPHEADER => ['Authorization: Bearer ' . $token, "Content-Type: application/json"],
CURLOPT_POSTFIELDS => $body,
]);
$response = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if ($response === false) { throw new RuntimeException(curl_error($ch)); }
curl_close($ch);
if ($status < 200 || $status >= 300) { throw new RuntimeException("HTTP $status: $response"); }
print_r(json_decode($response, true, 512, JSON_THROW_ON_ERROR));# Python 3 · standard library; run on your server.
import json
import os
from urllib.request import Request, build_opener, HTTPRedirectHandler
class NoRedirect(HTTPRedirectHandler):
def redirect_request(self, req, fp, code, msg, headers, newurl):
return None
headers = {
"Content-Type": "application/json"
}
headers["Authorization"] = "Bearer " + os.environ["WHOLLY_TOKEN"]
body = """{
"enabled": true,
"finality_mode": "confirmations",
"required_confirmations": 2,
"monitoring_minutes": 60,
"late_monitoring_days": 30
}""".encode("utf-8")
request = Request("https://api.example.com/v1/projects/YOUR_PROJECT_ID/payment-assets/YOUR_ASSET_ID",
method="PUT", headers=headers, data=body)
# Non-2xx responses raise HTTPError. Do not retry writes with a new key.
with build_opener(NoRedirect()).open(request, timeout=30) as response:
print(json.load(response))Esempio di risposta · 200 application/json
{
"data": [
{ "asset": { "id": "ASSET_UUID", "symbol": "USDC", "asset_kind": "token", "scanner_ready": true }, "policy": { "enabled": true, "finality_mode": "confirmations", "required_confirmations": 2, "monitoring_minutes": 60, "late_monitoring_days": 30 }, "wallet_readiness": "ready" }
]
}GETEsplora i token candidati per i pagamenti/v1/projects/{project_id}/payment-token-candidatesSola lettura
Cerca nelle associazioni dei contratti CoinGecko in cache locale solo sulle blockchain con scanner fatture token e adattatore saldi implementati. I risultati sono candidati di ricerca, non asset di pagamento attendibili.
- Adattatori token supportati: ERC-20 su Ethereum, Base, BNB Chain, HyperEVM, Avalanche, Polygon, Arbitrum e Optimism; SPL su Solana.
- Le blockchain del catalogo non supportate vengono rifiutate invece di apparire selezionabili.
- Posizione, icona e prezzo CoinGecko sono dati indicativi di ricerca.
| Header | Presenza | Regola |
|---|---|---|
| Authorization | obbligatorio | Bearer YOUR_MERCHANT_API_TOKEN |
| Accept | consigliato | application/json |
| Parametro | Tipo / posizione | Regola |
|---|---|---|
| project_id | path UUID | Progetto attivo assegnato alla credenziale. |
| chain_slug | query string | Slug obbligatorio di una blockchain EVM supportata o solana. |
| q | query string | Sottostringa facoltativa di nome, simbolo, ID CoinGecko, contratto o mint; massimo 80 caratteri. |
| limit | query integer | Facoltativo 1–100; predefinito 50. |
TokenCandidate
| Campo | Tipo | Presenza | Descrizione |
|---|---|---|---|
| coingecko_id | string | sempre | Identità di ricerca CoinGecko usata dalla richiesta di registrazione. |
| chain_slug | string | sempre | Blockchain Wholly Crypto corrispondente. |
| symbol / name | string | sempre | Identità visualizzata nel catalogo. |
| contract_address | string | sempre | Contratto o mint corrispondente; viene verificato on-chain prima della registrazione. |
| market_cap_rank | integer | null | sempre | Posizione nella ricerca, non indicatore di affidabilità o disponibilità per i pagamenti. |
| icon_path | path | sempre | Percorso dell'icona CoinGecko in cache locale. |
| current_price_usd | decimal string | null | sempre | Prezzo USD indicativo in cache. |
| token_standard | erc20 | spl-token | sempre | Standard token supportato dall'adattatore della blockchain selezionata. |
| scanner_ready | boolean | sempre | True solo per candidati su un canale token implementato in questa build. |
| registered_asset_id | UUID | null | sempre | Asset persistente esistente se già promosso. |
| project_enabled | boolean | sempre | Indica se l'asset registrato è abilitato per questo progetto. |
Richiesta
: "${WHOLLY_TOKEN:?Set WHOLLY_TOKEN to your server-side API token}"
curl --fail-with-body --max-time 30 \
--request GET \
--url "https://api.example.com/v1/projects/YOUR_PROJECT_ID/payment-token-candidates?chain_slug=ethereum&q=USDC&limit=50" \
--header "Authorization: Bearer $WHOLLY_TOKEN"// Node.js 18+ · run on your server, never in browser code.
const token = process.env.WHOLLY_TOKEN;
if (!token) throw new Error("Set WHOLLY_TOKEN");
const response = await fetch("https://api.example.com/v1/projects/YOUR_PROJECT_ID/payment-token-candidates?chain_slug=ethereum&q=USDC&limit=50", {
method: "GET",
headers: {
"Authorization": `Bearer ${token}`
},
redirect: "error",
signal: AbortSignal.timeout(30000)
});
if (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);
console.log(await response.json());<?php
// PHP 8+ with the cURL extension; run on your server.
$token = getenv('WHOLLY_TOKEN');
if (!$token) { throw new RuntimeException('Set WHOLLY_TOKEN'); }
$ch = curl_init("https://api.example.com/v1/projects/YOUR_PROJECT_ID/payment-token-candidates?chain_slug=ethereum&q=USDC&limit=50");
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => 'GET',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_FOLLOWLOCATION => false,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTPHEADER => ['Authorization: Bearer ' . $token],
]);
$response = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if ($response === false) { throw new RuntimeException(curl_error($ch)); }
curl_close($ch);
if ($status < 200 || $status >= 300) { throw new RuntimeException("HTTP $status: $response"); }
print_r(json_decode($response, true, 512, JSON_THROW_ON_ERROR));# Python 3 · standard library; run on your server.
import json
import os
from urllib.request import Request, build_opener, HTTPRedirectHandler
class NoRedirect(HTTPRedirectHandler):
def redirect_request(self, req, fp, code, msg, headers, newurl):
return None
headers = {}
headers["Authorization"] = "Bearer " + os.environ["WHOLLY_TOKEN"]
request = Request("https://api.example.com/v1/projects/YOUR_PROJECT_ID/payment-token-candidates?chain_slug=ethereum&q=USDC&limit=50",
method="GET", headers=headers)
# Non-2xx responses raise HTTPError. Do not retry writes with a new key.
with build_opener(NoRedirect()).open(request, timeout=30) as response:
print(json.load(response))Esempio di risposta · 200 application/json
{
"data": [
{
"coingecko_id": "usd-coin",
"chain_slug": "ethereum",
"symbol": "USDC",
"name": "USDC",
"contract_address": "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48",
"market_cap_rank": 7,
"icon_path": "/assets/coingecko/usd-coin.png",
"current_price_usd": "1.0001",
"token_standard": "erc20",
"scanner_ready": true,
"registered_asset_id": null,
"project_enabled": false
}
]
}POSTVerifica e registra token/v1/projects/{project_id}/payment-token-assetsLettura + scrittura
Promuove un candidato attuale nel registro persistente dei pagamenti solo dopo che i nodi configurati verificano identità della blockchain, del contratto/mint, decimali e una query di saldo utilizzabile. La registrazione non si fida mai dei soli metadati CoinGecko e ogni progetto è limitato a 20 asset token registrati.
- Abilita l'asset nativo della blockchain nel progetto prima di registrarne i token.
- Un progetto può registrare al massimo 20 asset token; un nuovo candidato oltre il limite restituisce token_chain_not_ready (409). Riutilizzare un asset già registrato non occupa un altro posto.
- La verifica dei nodi può richiedere più tempo di una lettura del catalogo; usa un timeout esplicito del client.
- Dopo la registrazione, seleziona l'asset per ogni negozio che deve offrirlo.
| Header | Presenza | Regola |
|---|---|---|
| Authorization | obbligatorio | Bearer YOUR_MERCHANT_API_TOKEN |
| Content-Type | obbligatorio | application/json |
| Accept | consigliato | application/json |
| Parametro | Tipo / posizione | Regola |
|---|---|---|
| project_id | path UUID | Progetto attivo assegnato alla credenziale. |
Corpo della registrazione token
| Campo | Tipo | Presenza | Descrizione |
|---|---|---|---|
| chain_slug | string | obbligatorio | ethereum, base, bnb-chain, hyperliquid, avalanche, polygon, arbitrum, optimism o solana. |
| coingecko_id | string | obbligatorio | Identità esatta del candidato restituita dalla ricerca token. Conserva underscore o trattini iniziali, come _ o -6. Non ricavare questo ID dal nome o ticker del token. |
| enabled | boolean | facoltativo | Stato della politica del progetto dopo la verifica; predefinito true. |
RegisteredTokenAsset
| Campo | Tipo | Presenza | Descrizione |
|---|---|---|---|
| asset_id | UUID | sempre | Identificativo persistente dell'asset di pagamento. |
| chain_slug / coingecko_id | string | sempre | Blockchain verificata e identità di ricerca/prezzi conservata. |
| contract_address | string | sempre | Contratto o mint canonico verificato. |
| token_standard | erc20 | spl-token | sempre | Standard token runtime verificato. |
| symbol / name / decimals | string / string / integer | sempre | Identità visualizzata promossa e precisione esatta. |
| enabled | boolean | sempre | Stato iniziale della politica del progetto. |
| metadata_verified_at | RFC 3339 timestamp | sempre | Momento della verifica on-chain. |
Richiesta
: "${WHOLLY_TOKEN:?Set WHOLLY_TOKEN to your server-side API token}"
curl --fail-with-body --max-time 30 \
--request POST \
--url "https://api.example.com/v1/projects/YOUR_PROJECT_ID/payment-token-assets" \
--header "Authorization: Bearer $WHOLLY_TOKEN" \
--header 'Content-Type: application/json' \
--data-raw '{
"chain_slug": "ethereum",
"coingecko_id": "usd-coin",
"enabled": true
}'// Node.js 18+ · run on your server, never in browser code.
const token = process.env.WHOLLY_TOKEN;
if (!token) throw new Error("Set WHOLLY_TOKEN");
const body = `{
"chain_slug": "ethereum",
"coingecko_id": "usd-coin",
"enabled": true
}`;
const response = await fetch("https://api.example.com/v1/projects/YOUR_PROJECT_ID/payment-token-assets", {
method: "POST",
headers: {
"Authorization": `Bearer ${token}`,
"Content-Type": "application/json"
},
body,
redirect: "error",
signal: AbortSignal.timeout(30000)
});
if (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);
console.log(await response.json());<?php
// PHP 8+ with the cURL extension; run on your server.
$token = getenv('WHOLLY_TOKEN');
if (!$token) { throw new RuntimeException('Set WHOLLY_TOKEN'); }
$body = <<<'JSON'
{
"chain_slug": "ethereum",
"coingecko_id": "usd-coin",
"enabled": true
}
JSON;
$ch = curl_init("https://api.example.com/v1/projects/YOUR_PROJECT_ID/payment-token-assets");
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => 'POST',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_FOLLOWLOCATION => false,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTPHEADER => ['Authorization: Bearer ' . $token, "Content-Type: application/json"],
CURLOPT_POSTFIELDS => $body,
]);
$response = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if ($response === false) { throw new RuntimeException(curl_error($ch)); }
curl_close($ch);
if ($status < 200 || $status >= 300) { throw new RuntimeException("HTTP $status: $response"); }
print_r(json_decode($response, true, 512, JSON_THROW_ON_ERROR));# Python 3 · standard library; run on your server.
import json
import os
from urllib.request import Request, build_opener, HTTPRedirectHandler
class NoRedirect(HTTPRedirectHandler):
def redirect_request(self, req, fp, code, msg, headers, newurl):
return None
headers = {
"Content-Type": "application/json"
}
headers["Authorization"] = "Bearer " + os.environ["WHOLLY_TOKEN"]
body = """{
"chain_slug": "ethereum",
"coingecko_id": "usd-coin",
"enabled": true
}""".encode("utf-8")
request = Request("https://api.example.com/v1/projects/YOUR_PROJECT_ID/payment-token-assets",
method="POST", headers=headers, data=body)
# Non-2xx responses raise HTTPError. Do not retry writes with a new key.
with build_opener(NoRedirect()).open(request, timeout=30) as response:
print(json.load(response))Esempio di risposta · 201 application/json
{
"data": {
"asset_id": "44444444-4444-4444-8444-444444444444",
"chain_slug": "ethereum",
"coingecko_id": "usd-coin",
"contract_address": "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48",
"token_standard": "erc20",
"symbol": "USDC",
"name": "USDC",
"decimals": 6,
"enabled": true,
"metadata_verified_at": "2026-08-31T18:00:00Z"
}
}GETTrova pool DEX del token personalizzato/v1/projects/{project_id}/payment-token-dex-poolsSola lettura
Trova fino a 12 pool idonei tramite blockchain e contratto esatto del token base su DEX Screener, ordinati per liquidità. Non registra né abilita un token.
- Un array data vuoto indica che non è stato trovato alcun pool idoneo. Vengono restituiti solo pool in cui il contratto esatto richiesto è il token base; i prezzi USD del token di quotazione non vengono mai presunti.
- La presenza su un DEX non è un audit di sicurezza. Liquidità minima e attività recente riducono le quotazioni inutilizzabili ma non impediscono la manipolazione del mercato.
- Uniswap, PancakeSwap e altri DEX indicizzati sono supportati dove lo scanner esistente della blockchain supporta i token. L'accesso API resta limitato al progetto e soggetto a limiti di frequenza. Anche le chiamate ai provider sono serializzate e limitate.
| Header | Presenza | Regola |
|---|---|---|
| Authorization | obbligatorio | Bearer YOUR_MERCHANT_API_TOKEN |
| Parametro | Tipo / posizione | Regola |
|---|---|---|
| project_id | path UUID | Progetto assegnato. |
| chain_slug | query string | Blockchain EVM token supportata o solana. |
| contract_address | query string | Contratto ERC-20 esatto o mint SPL classico. |
CustomDexPool
| Campo | Tipo | Presenza | Descrizione |
|---|---|---|---|
| pair_address / dex_id / quote_symbol | string | sempre | Identificativo esatto del pool, ID exchange (es. uniswap/pancakeswap) e ticker abbinato solo per visualizzazione. |
| price_usd / liquidity_usd | decimal string | sempre | Prezzo USD del token base richiesto e liquidità totale del pool. Sono richiesti almeno $10,000 di liquidità e uno scambio nell'ultima ora. |
| fetched_at | RFC 3339 timestamp | sempre | Quando il server ha recuperato l'osservazione del provider, non il timestamp di uno scambio on-chain. |
| url | HTTPS URL | sempre | Link DEX Screener convalidato a questo pool. |
Richiesta
: "${WHOLLY_TOKEN:?Set WHOLLY_TOKEN to your server-side API token}"
curl --fail-with-body --max-time 30 \
--request GET \
--url "https://api.example.com/v1/projects/YOUR_PROJECT_ID/payment-token-dex-pools?chain_slug=ethereum&contract_address=YOUR_TOKEN_CONTRACT" \
--header "Authorization: Bearer $WHOLLY_TOKEN"// Node.js 18+ · run on your server, never in browser code.
const token = process.env.WHOLLY_TOKEN;
if (!token) throw new Error("Set WHOLLY_TOKEN");
const response = await fetch("https://api.example.com/v1/projects/YOUR_PROJECT_ID/payment-token-dex-pools?chain_slug=ethereum&contract_address=YOUR_TOKEN_CONTRACT", {
method: "GET",
headers: {
"Authorization": `Bearer ${token}`
},
redirect: "error",
signal: AbortSignal.timeout(30000)
});
if (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);
console.log(await response.json());<?php
// PHP 8+ with the cURL extension; run on your server.
$token = getenv('WHOLLY_TOKEN');
if (!$token) { throw new RuntimeException('Set WHOLLY_TOKEN'); }
$ch = curl_init("https://api.example.com/v1/projects/YOUR_PROJECT_ID/payment-token-dex-pools?chain_slug=ethereum&contract_address=YOUR_TOKEN_CONTRACT");
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => 'GET',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_FOLLOWLOCATION => false,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTPHEADER => ['Authorization: Bearer ' . $token],
]);
$response = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if ($response === false) { throw new RuntimeException(curl_error($ch)); }
curl_close($ch);
if ($status < 200 || $status >= 300) { throw new RuntimeException("HTTP $status: $response"); }
print_r(json_decode($response, true, 512, JSON_THROW_ON_ERROR));# Python 3 · standard library; run on your server.
import json
import os
from urllib.request import Request, build_opener, HTTPRedirectHandler
class NoRedirect(HTTPRedirectHandler):
def redirect_request(self, req, fp, code, msg, headers, newurl):
return None
headers = {}
headers["Authorization"] = "Bearer " + os.environ["WHOLLY_TOKEN"]
request = Request("https://api.example.com/v1/projects/YOUR_PROJECT_ID/payment-token-dex-pools?chain_slug=ethereum&contract_address=YOUR_TOKEN_CONTRACT",
method="GET", headers=headers)
# Non-2xx responses raise HTTPError. Do not retry writes with a new key.
with build_opener(NoRedirect()).open(request, timeout=30) as response:
print(json.load(response))Esempio di risposta · 200 application/json
{"data":[{"pair_address":"0x2222222222222222222222222222222222222222","dex_id":"uniswap","quote_symbol":"WETH","price_usd":"0.25","liquidity_usd":"250000.00","fetched_at":"2026-09-09T12:00:00Z","url":"https://dexscreener.com/ethereum/0x2222222222222222222222222222222222222222"}]}POSTAggiungi o modifica il prezzo del token personalizzato/v1/projects/{project_id}/payment-token-assets/customLettura + scrittura
Verifica un contratto personalizzato usando i nodi blockchain configurati e lo registra senza richiedere la presenza su CoinGecko. Il prezzo fisso in USD o il pool DEX automatico selezionato appartiene a questo progetto, non al ticker o ad altri progetti. Ripetere la stessa identità aggiorna il prezzo del progetto senza modificare una politica di attivazione/disattivazione esistente.
- Dopo la registrazione, seleziona asset_id nell'endpoint payment-assets del negozio; la sola registrazione non abilita mai un metodo del negozio.
- Token personalizzati e di catalogo condividono il limite di 20 token per progetto. Lo stesso contratto su blockchain diverse è un asset di pagamento diverso.
- I contratti già nel catalogo restituiscono 409: usa la registrazione del catalogo per mantenere i tassi di mercato automatici. Un ticker personalizzato non prende mai il prezzo da un token omonimo.
- I prezzi fissi sono stime dell'operatore. I prezzi DEX automatici sono osservazioni spot dal pool selezionato via DEX Screener, non un oracolo resistente alla manipolazione. Spread del negozio e arrotondamento per eccesso restano applicati, con tassi fiat aggiornati. Le quotazioni già emesse non cambiano.
- Per la modalità DEX, trova prima un pool, poi invia price_mode: dex e dex_pair_address, omettendo price_usd. Un'attività condivisa in background aggiorna i pool selezionati ogni minuto. Controlli falliti o prezzi più vecchi di cinque minuti rimuovono questo token dalle nuove quotazioni; nessun ripiego silenzioso su prezzo fisso o ticker.
- Sono accettati solo token ERC-20 standard e SPL classici. Token-2022/estensioni e blockchain solo native vengono rifiutati. La verifica tecnica non è un audit di sicurezza dell'emittente o del contratto; token con commissioni sui trasferimenti, rebasing o liste di blocco possono comportarsi in modo incompatibile.
- Usa un timeout client di almeno 60 secondi. La verifica è limitata e può provare nodi di riserva. Dati non validi restituiscono 400; controlli blockchain/contratto falliti 422; conflitti di identità o limiti 409.
| Header | Presenza | Regola |
|---|---|---|
| Authorization | obbligatorio | Bearer YOUR_MERCHANT_API_TOKEN |
| Content-Type | obbligatorio | application/json |
| Parametro | Tipo / posizione | Regola |
|---|---|---|
| project_id | path UUID | Progetto assegnato a questa credenziale con permessi di scrittura. |
Registrazione token personalizzato
| Campo | Tipo | Presenza | Descrizione |
|---|---|---|---|
| chain_slug | string | obbligatorio | ethereum, base, bnb-chain, hyperliquid, avalanche, polygon, arbitrum, optimism o solana. Fisso per questo contratto. |
| contract_address | string | obbligatorio | Contratto ERC-20 (0x più 40 caratteri esadecimali) o mint SPL classico. I nodi verificano identità di rete e decimali esatti; decimali e URL RPC forniti dal chiamante vengono rifiutati. |
| name / symbol | string / string | obbligatorio | Nome visualizzato (1–80 caratteri) e ticker (1–16 lettere/cifre/punti/underscore/trattini, primo carattere alfanumerico). Le identità esistenti non possono essere rinominate da questo endpoint. |
| price_mode | fixed | dex | facoltativo | Predefinito fixed per retrocompatibilità. DEX usa un pool specifico trovato per blockchain e contratto esatti. |
| price_usd | decimal string | modalità fixed | Valore USD fisso di UN token, positivo, massimo 30 decimali, massimo 1000000000000000000000000. Niente esponenti o float. Ometti in modalità dex. |
| dex_pair_address | string | modalità dex | Indirizzo pool da payment-token-dex-pools. Obbligatorio in modalità dex; ometti in modalità fixed. Il server ricontrolla identità del pool, prezzo, liquidità e attività a ogni salvataggio. |
Richiesta
: "${WHOLLY_TOKEN:?Set WHOLLY_TOKEN to your server-side API token}"
curl --fail-with-body --max-time 30 \
--request POST \
--url "https://api.example.com/v1/projects/YOUR_PROJECT_ID/payment-token-assets/custom" \
--header "Authorization: Bearer $WHOLLY_TOKEN" \
--header 'Content-Type: application/json' \
--data-raw '{
"chain_slug": "ethereum",
"contract_address": "YOUR_VERIFIED_TOKEN_CONTRACT",
"name": "Example token",
"symbol": "EXAMPLE",
"price_usd": "0.25"
}'// Node.js 18+ · run on your server, never in browser code.
const token = process.env.WHOLLY_TOKEN;
if (!token) throw new Error("Set WHOLLY_TOKEN");
const body = `{
"chain_slug": "ethereum",
"contract_address": "YOUR_VERIFIED_TOKEN_CONTRACT",
"name": "Example token",
"symbol": "EXAMPLE",
"price_usd": "0.25"
}`;
const response = await fetch("https://api.example.com/v1/projects/YOUR_PROJECT_ID/payment-token-assets/custom", {
method: "POST",
headers: {
"Authorization": `Bearer ${token}`,
"Content-Type": "application/json"
},
body,
redirect: "error",
signal: AbortSignal.timeout(30000)
});
if (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);
console.log(await response.json());<?php
// PHP 8+ with the cURL extension; run on your server.
$token = getenv('WHOLLY_TOKEN');
if (!$token) { throw new RuntimeException('Set WHOLLY_TOKEN'); }
$body = <<<'JSON'
{
"chain_slug": "ethereum",
"contract_address": "YOUR_VERIFIED_TOKEN_CONTRACT",
"name": "Example token",
"symbol": "EXAMPLE",
"price_usd": "0.25"
}
JSON;
$ch = curl_init("https://api.example.com/v1/projects/YOUR_PROJECT_ID/payment-token-assets/custom");
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => 'POST',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_FOLLOWLOCATION => false,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTPHEADER => ['Authorization: Bearer ' . $token, "Content-Type: application/json"],
CURLOPT_POSTFIELDS => $body,
]);
$response = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if ($response === false) { throw new RuntimeException(curl_error($ch)); }
curl_close($ch);
if ($status < 200 || $status >= 300) { throw new RuntimeException("HTTP $status: $response"); }
print_r(json_decode($response, true, 512, JSON_THROW_ON_ERROR));# Python 3 · standard library; run on your server.
import json
import os
from urllib.request import Request, build_opener, HTTPRedirectHandler
class NoRedirect(HTTPRedirectHandler):
def redirect_request(self, req, fp, code, msg, headers, newurl):
return None
headers = {
"Content-Type": "application/json"
}
headers["Authorization"] = "Bearer " + os.environ["WHOLLY_TOKEN"]
body = """{
"chain_slug": "ethereum",
"contract_address": "YOUR_VERIFIED_TOKEN_CONTRACT",
"name": "Example token",
"symbol": "EXAMPLE",
"price_usd": "0.25"
}""".encode("utf-8")
request = Request("https://api.example.com/v1/projects/YOUR_PROJECT_ID/payment-token-assets/custom",
method="POST", headers=headers, data=body)
# Non-2xx responses raise HTTPError. Do not retry writes with a new key.
with build_opener(NoRedirect()).open(request, timeout=30) as response:
print(json.load(response))Esempio di risposta · 200 application/json
{"data":{"asset_id":"44444444-4444-4444-8444-444444444444"}}GETElenca metodi di pagamento del negozio/v1/projects/{project_id}/stores/{store_id}/payment-assetsSola lettura
Elenca gli asset on-chain in data e la disponibilità Lightning separata in lightning. I metodi on-chain richiedono wallet blockchain pronti. Lightning usa la connessione esterna di ricezione verificata selezionata nel negozio, indipendentemente dal wallet Bitcoin on-chain.
- selected è la configurazione on-chain; wallet_readiness è il suo requisito attuale di idoneità.
- Il membro lightning della risposta contiene payment_rail: lightning, symbol: BTC, asset_decimals: 11, enabled e ready. Non contiene mai credenziali del nodo. Configura questo metodo nella console del negozio; aggiornare l'array assets non modifica Lightning.
- confirmation_policy si applica solo ai metodi on-chain. Lightning si salda senza conferme di blocco e richiede l'intero importo BOLT11, senza tolleranza per pagamenti parziali.
- I metodi nativi e token di una blockchain usano la stessa destinazione di fattura per il wallet di quella blockchain.
- I riepiloghi wallet incorporati riguardano solo la disponibilità e mantengono i saldi vuoti; usa la rotta dedicata ai wallet di progetto per i valori attuali.
| Header | Presenza | Regola |
|---|---|---|
| Authorization | obbligatorio | Bearer YOUR_MERCHANT_API_TOKEN |
| Accept | consigliato | application/json |
| Parametro | Tipo / posizione | Regola |
|---|---|---|
| project_id | path UUID | Progetto assegnato alla credenziale; può essere in pausa. |
| store_id | path UUID | Negozio appartenente a project_id; può essere in pausa. |
PaymentAsset
| Campo | Tipo | Presenza | Descrizione |
|---|---|---|---|
| id | UUID | sempre | Identificativo persistente dell'asset di pagamento usato dalle rotte delle politiche di progetto e negozio. |
| asset_key | string | sempre | Identità canonica dell'asset nativo o contratto in stile CAIP. |
| chain_slug / network | string | sempre | Identificativo blockchain Wholly Crypto e rete configurata. |
| caip_network_id / caip_asset_id | string / string|null | sempre | Identità canoniche di rete e asset. |
| asset_kind | native | token | sempre | Indica se il regolamento usa la valuta della blockchain o un contratto/mint verificato. |
| payment_rail | string | sempre | Canale runtime: utxo, evm-native, solana-native, account-native, privacy-native o token-transfer. |
| symbol / name / decimals | string / string / integer | sempre | Identità visualizzata e precisione esatta in unità atomiche. |
| contract_address | string | null | sempre | Contratto ERC-20 o mint SPL canonico per i token; null per gli asset nativi. |
| coingecko_id | string | null | sempre | Identità per ricerca e prezzi. Null per contratti personalizzati; non dedurre mai un prezzo di mercato dal ticker. I soli metadati CoinGecko non rendono mai un token selezionabile. |
| custom_token | boolean | sempre | Contratto personalizzato verificato on-chain, con prezzo fisso in USD o pool DEX selezionato a livello di progetto. |
| icon_path | path | null | sempre | Icona del token in cache locale, se disponibile. |
| token_standard | erc20 | spl-token | null | sempre | Standard token verificato del runtime; null per gli asset nativi. |
| metadata_verified_at | timestamp | null | sempre | Momento della verifica dei metadati on-chain per i token promossi. |
| payment_supported / scanner_ready / balance_ready | boolean | sempre | Requisiti del registro verificati in compilazione. scanner_ready indica che il runtime dello scanner dei pagamenti è installato; la conferma richiede il numero configurato di provider funzionanti con ruolo esatto (2 predefiniti, 1 facoltativo); l'indisponibilità temporanea dello scanner non blocca la creazione di fatture dalla 6.0.6. balance_ready è true solo per adattatori di saldo implementati. |
| default_finality_mode | confirmations | finalized | sempre | Modello di finalità predefinito ereditato da una nuova politica di progetto. |
| default_required_confirmations / default_monitoring_minutes | integer | sempre | Politica predefinita di conferma e monitoraggio. |
StorePaymentAsset
| Campo | Tipo | Presenza | Descrizione |
|---|---|---|---|
| asset | PaymentAsset | sempre | Asset nativo o token verificato visibile al progetto. |
| project_policy | ProjectAssetPolicy | null | sempre | Politica del progetto padre. |
| selected | boolean | sempre | Indica se questo metodo fa parte della configurazione desiderata salvata del negozio. Viene offerto quando politica del progetto, wallet, adattatore installato e prezzi sono validi. Le interruzioni temporanee degli scanner non lo rimuovono dalle nuove fatture. |
| display_order | integer | null | sempre | Ordine nel checkout del negozio quando selezionato. |
| confirmation_policy | StoreConfirmationPolicy | null | sempre | Politica effettiva del negozio per un asset configurato nel progetto. Null se non esiste una politica di progetto. |
| wallet | WalletSummary | null | sempre | Wallet blockchain condiviso da asset nativi e token. |
| wallet_readiness | readiness enum | sempre | Solo stato di wallet e politica; usa receive_readiness per i prerequisiti degli scanner. |
| receive_readiness | ReceiveReadiness | null | 5.5.0+ | Configurazione di ricezione condivisa più accettazione del negozio. Usa osservazioni in cache; non è una prenotazione né una garanzia. La creazione ricontrolla requisiti e tasso effettivo della fattura. |
StoreConfirmationPolicy
| Campo | Tipo | Presenza | Descrizione |
|---|---|---|---|
| finality_mode | confirmations | finalized | sempre | Indica se il regolamento usa un numero di blocchi configurabile o la finalità di rete. |
| project_required_confirmations | integer | sempre | Valore attuale predefinito del progetto usato dalle fatture future quando non è impostata una personalizzazione del negozio. |
| override_required_confirmations | integer | null | sempre | Numero specifico del negozio, oppure null per ereditare il predefinito del progetto. |
| effective_required_confirmations | integer | sempre | Numero che verrà salvato nelle nuove fatture per questo negozio e asset. |
| editable | boolean | sempre | False per reti finalized la cui politica di finalità non può essere modificata. |
| minimum_required_confirmations | integer | sempre | Limite inferiore incluso, specifico della blockchain; 0 è esposto solo sui canali che supportano l'accettazione al rilevamento. |
| maximum_required_confirmations | integer | sempre | Limite superiore incluso, specifico della blockchain. |
WalletSummary
| Campo | Tipo | Presenza | Descrizione |
|---|---|---|---|
| id / project_id / native_asset_id | UUID | sempre | Identificativi del wallet, del progetto proprietario e dell'asset nativo della blockchain. |
| chain_slug / network | string | sempre | Blockchain e rete del wallet. |
| asset_symbol / asset_name | string | sempre | Identità visualizzata dell'asset nativo della blockchain. |
| status | pending | active | disabled | error | sempre | Stato operativo del wallet. |
| label | string | sempre | Etichetta dell'operatore. |
| public_key / primary_address | string | null | sempre | Identità pubblica del wallet; nessuna frase seed o chiave privata viene esposta. |
| derivation_scheme / address_format | string | null | sempre | Politica e formato degli indirizzi. |
| backup_confirmed_at | timestamp | null | sempre | Non null dopo che l'operatore conferma il backup di recupero. |
| activation_required / activation_verified_at | boolean / timestamp|null | sempre | Gli account condivisi XRP e Stellar restano indisponibili finché l'operatore non finanzia l'indirizzo mostrato e i provider scanner configurati non verificano quell'account esatto. La prova persistente non scade; lo stato attuale degli scanner viene controllato separatamente per verificare i pagamenti, non per creare fatture. |
| receive_readiness | ReceiveReadiness | null | 5.5.0+ | Incluso negli elenchi dei wallet: configurazione di ricezione del progetto e prerequisiti degli scanner blockchain. Separato da saldi, gas dei token e disponibilità all'invio. Altre risposte dei wallet possono lasciarlo null. |
| monero_wallet_rpc | MoneroWalletRpcBinding | null | sempre | Stato dell'associazione wallet-RPC esterna in sola visualizzazione per Monero, ripulito dai dati sensibili. Include endpoint, modalità di autenticazione, indirizzo primario dell'account 0, indicatori/altezze delle prove tecniche e timestamp delle attestazioni dell'operatore; credenziali, chiavi e file dei wallet non vengono mai serializzati. |
| last_secret_revealed_at / secret_reveal_count | timestamp|null / integer | sempre | Metadati di audit della divulgazione dei segreti lato console. |
| next_receive_index | integer | sempre | Indice del prossimo indirizzo figlio riservato. |
| last_scanned_height / last_scanned_at / last_error | integer|null / timestamp|null / string|null | sempre | Stato dello scanner wallet. |
| balances | WalletAssetBalance[] | sempre | Saldi in cache per tutti i 30 canali nativi delle blockchain, più asset ERC-20 e SPL verificati. Per Monero serve un wallet-RPC esterno in sola visualizzazione configurato. |
| total_value_usd | decimal string | null | sempre | Somma indicativa dei saldi con un prezzo USD attuale. |
| balance_status | pending | refreshing | fresh | stale | error | unknown | sempre | Aggiornamento aggregato della cache; unknown è un valore di riserva prudente e nessuno di questi stati prova il regolamento della fattura. |
| balance_checked_at | timestamp | null | sempre | Il più vecchio controllo di saldo riuscito pertinente rappresentato dall'aggregato. |
| recent_payments | WalletRecentPayment[] | sempre | Fino alle tre osservazioni valide più recenti detected, confirming o final attribuite a questo esatto wallet. |
| created_at / updated_at | RFC 3339 timestamp | sempre | Momento di creazione e ultimo aggiornamento del wallet. |
ReceiveReadiness
| Campo | Tipo | Presenza | Descrizione |
|---|---|---|---|
| ready | boolean | sempre | I controlli della configurazione di ricezione passano. Non descrive disponibilità alla spesa, gas, aggiornamento saldi o una quotazione futura garantita. |
| invoice_creatable | boolean | 6.0.6+ | La configurazione consente un metodo di fattura nonostante avvisi temporanei dello scanner. Il prezzo della valuta viene controllato alla creazione. Non è una verifica del pagamento: ready può essere false mentre invoice_creatable è true. Wallet mancanti, politica disattivata e adattatori non supportati continuano a bloccare in sicurezza. |
| checked_at | timestamp | sempre | Momento della valutazione. Un elenco non esegue richieste di rete né assegna indirizzi. |
| issues | PaymentMethodIssue[] | sempre | Vuoto quando pronto; altrimenti un avviso di ricezione o un blocco di configurazione. Controlla invoice_creatable per distinguere avvisi temporanei dello scanner da errori di configurazione delle fatture. |
PaymentMethodIssue
| Campo | Tipo | Presenza | Descrizione |
|---|---|---|---|
| chain_slug / asset_id / asset_ticker | string / UUID / string | se noto | Identifica blockchain e asset interessati. Lightning può omettere asset_id. |
| reason_code | string | sempre | scanner_provider_quorum, scanner_not_checked, scanner_unavailable, wallet_missing, wallet_disabled, wallet_backup_required, wallet_key_unavailable, wallet_activation_required, monero_binding_unavailable, rate_unavailable, custom_rate_unavailable, lightning_unavailable, project_disabled, store_disabled, chain_disabled, asset_disabled o asset_not_accepted. |
| message / action | string | se disponibile | Spiegazione per il commerciante e identificativo dell'azione: chain_connections, wallets, rates, payment_methods, project_settings o store_settings. Nessuna credenziale o URL privato dei provider. |
| required_endpoint_role | string | null | on-chain | Ruolo API scanner preferito (campo precedente). Usa accepted_endpoint_roles per l'elenco completo di compatibilità. Lo stato di base del nodo non prova il supporto alla cronologia dei pagamenti. |
| accepted_endpoint_roles | string[] | null | on-chain | Dialetti API compatibili, non prova della cronologia o capacità dell'endpoint. node-rpc diretto supporta BTC/BCH/LTC/DOGE/DASH e ZEC trasparente (blocchi decodificati completi, 1–48 conferme), TRX nativo solidificato, ALGO nativo via algod, XTZ via Octez, DOT Asset Hub finalizzato via metadati SCALE e XLM nativo via Stellar RPC con ID memo fattura. Una cronologia potata o incompleta non è idonea. Questi adattatori diretti non aggiungono canali token. Le API indicizzate restano alternative; vedi la tabella dei canali sotto. Fonti miste dirette/indicizzate verificano in modo indipendente finestre limitate; restano predefiniti due provider indipendenti, non alias dello stesso operatore. Altezza di base del nodo, informazioni blockchain ORDnet e un relay EVM per un canale non EVM non sono prove di ricezione. Monero richiede comunque un wallet-RPC in sola visualizzazione associato al progetto. |
| healthy_endpoints | integer | on-chain | Endpoint funzionanti corrispondenti, non il numero di provider indipendenti. |
| usable_independent_providers / required_independent_providers | integer | on-chain | Slot di verifica utilizzabili, limitati a due. required_independent_providers è l'impostazione della blockchain: 2 predefiniti, o 1 dopo scelta esplicita dell'amministratore. In modalità a due provider servono chiavi provider E host diversi. Fonti disattivate, obsolete (oltre dieci minuti) o in attesa non occupano uno slot. Lightning usa regole di connessione proprie. |
| last_checked_at | timestamp | null | on-chain | Ultimo controllo di stato dell'endpoint corrispondente, separato dal momento della valutazione. |
Richiesta
: "${WHOLLY_TOKEN:?Set WHOLLY_TOKEN to your server-side API token}"
curl --fail-with-body --max-time 30 \
--request GET \
--url "https://api.example.com/v1/projects/YOUR_PROJECT_ID/stores/YOUR_STORE_ID/payment-assets" \
--header "Authorization: Bearer $WHOLLY_TOKEN"// Node.js 18+ · run on your server, never in browser code.
const token = process.env.WHOLLY_TOKEN;
if (!token) throw new Error("Set WHOLLY_TOKEN");
const response = await fetch("https://api.example.com/v1/projects/YOUR_PROJECT_ID/stores/YOUR_STORE_ID/payment-assets", {
method: "GET",
headers: {
"Authorization": `Bearer ${token}`
},
redirect: "error",
signal: AbortSignal.timeout(30000)
});
if (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);
console.log(await response.json());<?php
// PHP 8+ with the cURL extension; run on your server.
$token = getenv('WHOLLY_TOKEN');
if (!$token) { throw new RuntimeException('Set WHOLLY_TOKEN'); }
$ch = curl_init("https://api.example.com/v1/projects/YOUR_PROJECT_ID/stores/YOUR_STORE_ID/payment-assets");
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => 'GET',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_FOLLOWLOCATION => false,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTPHEADER => ['Authorization: Bearer ' . $token],
]);
$response = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if ($response === false) { throw new RuntimeException(curl_error($ch)); }
curl_close($ch);
if ($status < 200 || $status >= 300) { throw new RuntimeException("HTTP $status: $response"); }
print_r(json_decode($response, true, 512, JSON_THROW_ON_ERROR));# Python 3 · standard library; run on your server.
import json
import os
from urllib.request import Request, build_opener, HTTPRedirectHandler
class NoRedirect(HTTPRedirectHandler):
def redirect_request(self, req, fp, code, msg, headers, newurl):
return None
headers = {}
headers["Authorization"] = "Bearer " + os.environ["WHOLLY_TOKEN"]
request = Request("https://api.example.com/v1/projects/YOUR_PROJECT_ID/stores/YOUR_STORE_ID/payment-assets",
method="GET", headers=headers)
# Non-2xx responses raise HTTPError. Do not retry writes with a new key.
with build_opener(NoRedirect()).open(request, timeout=30) as response:
print(json.load(response))Esempio di risposta · 200 application/json
{
"data": [
{ "asset": { "id": "ASSET_UUID", "chain_slug": "ethereum", "symbol": "USDC", "asset_kind": "token", "token_standard": "erc20", "scanner_ready": true }, "project_policy": { "enabled": true, "required_confirmations": 12 }, "selected": true, "display_order": 0, "confirmation_policy": { "finality_mode": "confirmations", "project_required_confirmations": 12, "override_required_confirmations": 3, "effective_required_confirmations": 3, "editable": true, "minimum_required_confirmations": 0, "maximum_required_confirmations": 48 }, "wallet": { "id": "WALLET_UUID", "status": "active" }, "wallet_readiness": "ready" }
],
"lightning": { "payment_rail": "lightning", "symbol": "BTC", "asset_decimals": 11, "enabled": true, "ready": true }
}PUTSostituisci metodi di pagamento del negozio/v1/projects/{project_id}/stores/{store_id}/payment-assetsLettura + scrittura
Sostituisce atomicamente l'intero sottoinsieme ordinato degli asset del negozio e restituisce l'elenco aggiornato. Gli asset omessi vengono deselezionati.
- L'array accetta al massimo 64 asset e ordini di visualizzazione univoci.
- Le selezioni rappresentano la configurazione desiderata salvata e possono essere preparate prima del backup del wallet o mentre una blockchain è in pausa. La creazione di fatture offre comunque solo metodi con politica del progetto, politica nativa padre, wallet e controlli runtime pronti.
- Invia un array assets vuoto per non configurare alcun metodo di pagamento.
| Header | Presenza | Regola |
|---|---|---|
| Authorization | obbligatorio | Bearer YOUR_MERCHANT_API_TOKEN |
| Content-Type | obbligatorio | application/json |
| Accept | consigliato | application/json |
| Parametro | Tipo / posizione | Regola |
|---|---|---|
| project_id | path UUID | Progetto assegnato alla credenziale; può essere in pausa. |
| store_id | path UUID | Negozio appartenente a project_id; può essere in pausa. |
Corpo di selezione degli asset di pagamento del negozio
| Campo | Tipo | Presenza | Descrizione |
|---|---|---|---|
| assets | StoreAssetSelection[] | obbligatorio | Elenco sostitutivo completo, massimo 64 elementi. Ognuno contiene un asset_id univoco e un display_order univoco da 0 a 10.000. |
PaymentAsset
| Campo | Tipo | Presenza | Descrizione |
|---|---|---|---|
| id | UUID | sempre | Identificativo persistente dell'asset di pagamento usato dalle rotte delle politiche di progetto e negozio. |
| asset_key | string | sempre | Identità canonica dell'asset nativo o contratto in stile CAIP. |
| chain_slug / network | string | sempre | Identificativo blockchain Wholly Crypto e rete configurata. |
| caip_network_id / caip_asset_id | string / string|null | sempre | Identità canoniche di rete e asset. |
| asset_kind | native | token | sempre | Indica se il regolamento usa la valuta della blockchain o un contratto/mint verificato. |
| payment_rail | string | sempre | Canale runtime: utxo, evm-native, solana-native, account-native, privacy-native o token-transfer. |
| symbol / name / decimals | string / string / integer | sempre | Identità visualizzata e precisione esatta in unità atomiche. |
| contract_address | string | null | sempre | Contratto ERC-20 o mint SPL canonico per i token; null per gli asset nativi. |
| coingecko_id | string | null | sempre | Identità per ricerca e prezzi. Null per contratti personalizzati; non dedurre mai un prezzo di mercato dal ticker. I soli metadati CoinGecko non rendono mai un token selezionabile. |
| custom_token | boolean | sempre | Contratto personalizzato verificato on-chain, con prezzo fisso in USD o pool DEX selezionato a livello di progetto. |
| icon_path | path | null | sempre | Icona del token in cache locale, se disponibile. |
| token_standard | erc20 | spl-token | null | sempre | Standard token verificato del runtime; null per gli asset nativi. |
| metadata_verified_at | timestamp | null | sempre | Momento della verifica dei metadati on-chain per i token promossi. |
| payment_supported / scanner_ready / balance_ready | boolean | sempre | Requisiti del registro verificati in compilazione. scanner_ready indica che il runtime dello scanner dei pagamenti è installato; la conferma richiede il numero configurato di provider funzionanti con ruolo esatto (2 predefiniti, 1 facoltativo); l'indisponibilità temporanea dello scanner non blocca la creazione di fatture dalla 6.0.6. balance_ready è true solo per adattatori di saldo implementati. |
| default_finality_mode | confirmations | finalized | sempre | Modello di finalità predefinito ereditato da una nuova politica di progetto. |
| default_required_confirmations / default_monitoring_minutes | integer | sempre | Politica predefinita di conferma e monitoraggio. |
StorePaymentAsset
| Campo | Tipo | Presenza | Descrizione |
|---|---|---|---|
| asset | PaymentAsset | sempre | Asset nativo o token verificato visibile al progetto. |
| project_policy | ProjectAssetPolicy | null | sempre | Politica del progetto padre. |
| selected | boolean | sempre | Indica se questo metodo fa parte della configurazione desiderata salvata del negozio. Viene offerto quando politica del progetto, wallet, adattatore installato e prezzi sono validi. Le interruzioni temporanee degli scanner non lo rimuovono dalle nuove fatture. |
| display_order | integer | null | sempre | Ordine nel checkout del negozio quando selezionato. |
| confirmation_policy | StoreConfirmationPolicy | null | sempre | Politica effettiva del negozio per un asset configurato nel progetto. Null se non esiste una politica di progetto. |
| wallet | WalletSummary | null | sempre | Wallet blockchain condiviso da asset nativi e token. |
| wallet_readiness | readiness enum | sempre | Solo stato di wallet e politica; usa receive_readiness per i prerequisiti degli scanner. |
| receive_readiness | ReceiveReadiness | null | 5.5.0+ | Configurazione di ricezione condivisa più accettazione del negozio. Usa osservazioni in cache; non è una prenotazione né una garanzia. La creazione ricontrolla requisiti e tasso effettivo della fattura. |
StoreConfirmationPolicy
| Campo | Tipo | Presenza | Descrizione |
|---|---|---|---|
| finality_mode | confirmations | finalized | sempre | Indica se il regolamento usa un numero di blocchi configurabile o la finalità di rete. |
| project_required_confirmations | integer | sempre | Valore attuale predefinito del progetto usato dalle fatture future quando non è impostata una personalizzazione del negozio. |
| override_required_confirmations | integer | null | sempre | Numero specifico del negozio, oppure null per ereditare il predefinito del progetto. |
| effective_required_confirmations | integer | sempre | Numero che verrà salvato nelle nuove fatture per questo negozio e asset. |
| editable | boolean | sempre | False per reti finalized la cui politica di finalità non può essere modificata. |
| minimum_required_confirmations | integer | sempre | Limite inferiore incluso, specifico della blockchain; 0 è esposto solo sui canali che supportano l'accettazione al rilevamento. |
| maximum_required_confirmations | integer | sempre | Limite superiore incluso, specifico della blockchain. |
ReceiveReadiness
| Campo | Tipo | Presenza | Descrizione |
|---|---|---|---|
| ready | boolean | sempre | I controlli della configurazione di ricezione passano. Non descrive disponibilità alla spesa, gas, aggiornamento saldi o una quotazione futura garantita. |
| invoice_creatable | boolean | 6.0.6+ | La configurazione consente un metodo di fattura nonostante avvisi temporanei dello scanner. Il prezzo della valuta viene controllato alla creazione. Non è una verifica del pagamento: ready può essere false mentre invoice_creatable è true. Wallet mancanti, politica disattivata e adattatori non supportati continuano a bloccare in sicurezza. |
| checked_at | timestamp | sempre | Momento della valutazione. Un elenco non esegue richieste di rete né assegna indirizzi. |
| issues | PaymentMethodIssue[] | sempre | Vuoto quando pronto; altrimenti un avviso di ricezione o un blocco di configurazione. Controlla invoice_creatable per distinguere avvisi temporanei dello scanner da errori di configurazione delle fatture. |
PaymentMethodIssue
| Campo | Tipo | Presenza | Descrizione |
|---|---|---|---|
| chain_slug / asset_id / asset_ticker | string / UUID / string | se noto | Identifica blockchain e asset interessati. Lightning può omettere asset_id. |
| reason_code | string | sempre | scanner_provider_quorum, scanner_not_checked, scanner_unavailable, wallet_missing, wallet_disabled, wallet_backup_required, wallet_key_unavailable, wallet_activation_required, monero_binding_unavailable, rate_unavailable, custom_rate_unavailable, lightning_unavailable, project_disabled, store_disabled, chain_disabled, asset_disabled o asset_not_accepted. |
| message / action | string | se disponibile | Spiegazione per il commerciante e identificativo dell'azione: chain_connections, wallets, rates, payment_methods, project_settings o store_settings. Nessuna credenziale o URL privato dei provider. |
| required_endpoint_role | string | null | on-chain | Ruolo API scanner preferito (campo precedente). Usa accepted_endpoint_roles per l'elenco completo di compatibilità. Lo stato di base del nodo non prova il supporto alla cronologia dei pagamenti. |
| accepted_endpoint_roles | string[] | null | on-chain | Dialetti API compatibili, non prova della cronologia o capacità dell'endpoint. node-rpc diretto supporta BTC/BCH/LTC/DOGE/DASH e ZEC trasparente (blocchi decodificati completi, 1–48 conferme), TRX nativo solidificato, ALGO nativo via algod, XTZ via Octez, DOT Asset Hub finalizzato via metadati SCALE e XLM nativo via Stellar RPC con ID memo fattura. Una cronologia potata o incompleta non è idonea. Questi adattatori diretti non aggiungono canali token. Le API indicizzate restano alternative; vedi la tabella dei canali sotto. Fonti miste dirette/indicizzate verificano in modo indipendente finestre limitate; restano predefiniti due provider indipendenti, non alias dello stesso operatore. Altezza di base del nodo, informazioni blockchain ORDnet e un relay EVM per un canale non EVM non sono prove di ricezione. Monero richiede comunque un wallet-RPC in sola visualizzazione associato al progetto. |
| healthy_endpoints | integer | on-chain | Endpoint funzionanti corrispondenti, non il numero di provider indipendenti. |
| usable_independent_providers / required_independent_providers | integer | on-chain | Slot di verifica utilizzabili, limitati a due. required_independent_providers è l'impostazione della blockchain: 2 predefiniti, o 1 dopo scelta esplicita dell'amministratore. In modalità a due provider servono chiavi provider E host diversi. Fonti disattivate, obsolete (oltre dieci minuti) o in attesa non occupano uno slot. Lightning usa regole di connessione proprie. |
| last_checked_at | timestamp | null | on-chain | Ultimo controllo di stato dell'endpoint corrispondente, separato dal momento della valutazione. |
Richiesta
: "${WHOLLY_TOKEN:?Set WHOLLY_TOKEN to your server-side API token}"
curl --fail-with-body --max-time 30 \
--request PUT \
--url "https://api.example.com/v1/projects/YOUR_PROJECT_ID/stores/YOUR_STORE_ID/payment-assets" \
--header "Authorization: Bearer $WHOLLY_TOKEN" \
--header 'Content-Type: application/json' \
--data-raw '{
"assets": [
{
"asset_id": "YOUR_ASSET_ID",
"display_order": 0
}
]
}'// Node.js 18+ · run on your server, never in browser code.
const token = process.env.WHOLLY_TOKEN;
if (!token) throw new Error("Set WHOLLY_TOKEN");
const body = `{
"assets": [
{
"asset_id": "YOUR_ASSET_ID",
"display_order": 0
}
]
}`;
const response = await fetch("https://api.example.com/v1/projects/YOUR_PROJECT_ID/stores/YOUR_STORE_ID/payment-assets", {
method: "PUT",
headers: {
"Authorization": `Bearer ${token}`,
"Content-Type": "application/json"
},
body,
redirect: "error",
signal: AbortSignal.timeout(30000)
});
if (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);
console.log(await response.json());<?php
// PHP 8+ with the cURL extension; run on your server.
$token = getenv('WHOLLY_TOKEN');
if (!$token) { throw new RuntimeException('Set WHOLLY_TOKEN'); }
$body = <<<'JSON'
{
"assets": [
{
"asset_id": "YOUR_ASSET_ID",
"display_order": 0
}
]
}
JSON;
$ch = curl_init("https://api.example.com/v1/projects/YOUR_PROJECT_ID/stores/YOUR_STORE_ID/payment-assets");
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => 'PUT',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_FOLLOWLOCATION => false,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTPHEADER => ['Authorization: Bearer ' . $token, "Content-Type: application/json"],
CURLOPT_POSTFIELDS => $body,
]);
$response = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if ($response === false) { throw new RuntimeException(curl_error($ch)); }
curl_close($ch);
if ($status < 200 || $status >= 300) { throw new RuntimeException("HTTP $status: $response"); }
print_r(json_decode($response, true, 512, JSON_THROW_ON_ERROR));# Python 3 · standard library; run on your server.
import json
import os
from urllib.request import Request, build_opener, HTTPRedirectHandler
class NoRedirect(HTTPRedirectHandler):
def redirect_request(self, req, fp, code, msg, headers, newurl):
return None
headers = {
"Content-Type": "application/json"
}
headers["Authorization"] = "Bearer " + os.environ["WHOLLY_TOKEN"]
body = """{
"assets": [
{
"asset_id": "YOUR_ASSET_ID",
"display_order": 0
}
]
}""".encode("utf-8")
request = Request("https://api.example.com/v1/projects/YOUR_PROJECT_ID/stores/YOUR_STORE_ID/payment-assets",
method="PUT", headers=headers, data=body)
# Non-2xx responses raise HTTPError. Do not retry writes with a new key.
with build_opener(NoRedirect()).open(request, timeout=30) as response:
print(json.load(response))Esempio di risposta · 200 application/json
{
"data": [
{ "asset": { "id": "44444444-4444-4444-8444-444444444444", "symbol": "USDC" }, "selected": true, "display_order": 0, "confirmation_policy": { "finality_mode": "confirmations", "project_required_confirmations": 12, "override_required_confirmations": null, "effective_required_confirmations": 12, "editable": true, "minimum_required_confirmations": 0, "maximum_required_confirmations": 48 }, "wallet_readiness": "ready" }
]
}PUTImposta una politica di conferma del negozio/v1/projects/{project_id}/stores/{store_id}/payment-assets/{asset_id}/confirmation-policyLettura + scrittura
Imposta o rimuove una personalizzazione delle conferme del negozio e restituisce l'elenco aggiornato dei metodi di pagamento. L'asset deve già essere selezionato per il negozio. La configurazione resta disponibile mentre progetto, negozio, blockchain o wallet sono in pausa.
- Usa {"strategy":"inherit"} per rimuovere la personalizzazione del negozio e seguire il predefinito attuale del progetto per le fatture future.
- Le reti finalized restituiscono editable false e usano la finalità di rete; non accettano una personalizzazione del numero di blocchi.
- Un valore di 0 significa accettare al rilevamento senza conferme di rete né protezione dalle riorganizzazioni. È accettato solo quando minimum_required_confirmations è 0.
- Le modifiche alla politica influenzano solo le nuove fatture. Quelle esistenti conservano l'istantanea della politica di conferma di progetto e negozio acquisita alla creazione.
- Gli aggiornamenti riguardano un asset alla volta; serializza le modifiche simultanee dello stesso asset del negozio e usa la risposta aggiornata come stato attuale.
| Header | Presenza | Regola |
|---|---|---|
| Authorization | obbligatorio | Bearer YOUR_MERCHANT_API_TOKEN |
| Content-Type | obbligatorio | application/json |
| Accept | consigliato | application/json |
| Parametro | Tipo / posizione | Regola |
|---|---|---|
| project_id | path UUID | Progetto assegnato alla credenziale; può essere in pausa. |
| store_id | path UUID | Negozio appartenente a project_id; può essere in pausa. |
| asset_id | path UUID | Asset di pagamento attualmente selezionato nel negozio da aggiornare. |
Corpo della politica di conferma del negozio
| Campo | Tipo | Presenza | Descrizione |
|---|---|---|---|
| strategy | inherit | custom | obbligatorio | Strategia con tag. inherit rimuove la personalizzazione del negozio; custom richiede required_confirmations. |
| required_confirmations | integer | solo custom | Numero intero entro il minimo/massimo restituiti per questo asset. I campi sconosciuti o aggiuntivi vengono rifiutati. |
PaymentAsset
| Campo | Tipo | Presenza | Descrizione |
|---|---|---|---|
| id | UUID | sempre | Identificativo persistente dell'asset di pagamento usato dalle rotte delle politiche di progetto e negozio. |
| asset_key | string | sempre | Identità canonica dell'asset nativo o contratto in stile CAIP. |
| chain_slug / network | string | sempre | Identificativo blockchain Wholly Crypto e rete configurata. |
| caip_network_id / caip_asset_id | string / string|null | sempre | Identità canoniche di rete e asset. |
| asset_kind | native | token | sempre | Indica se il regolamento usa la valuta della blockchain o un contratto/mint verificato. |
| payment_rail | string | sempre | Canale runtime: utxo, evm-native, solana-native, account-native, privacy-native o token-transfer. |
| symbol / name / decimals | string / string / integer | sempre | Identità visualizzata e precisione esatta in unità atomiche. |
| contract_address | string | null | sempre | Contratto ERC-20 o mint SPL canonico per i token; null per gli asset nativi. |
| coingecko_id | string | null | sempre | Identità per ricerca e prezzi. Null per contratti personalizzati; non dedurre mai un prezzo di mercato dal ticker. I soli metadati CoinGecko non rendono mai un token selezionabile. |
| custom_token | boolean | sempre | Contratto personalizzato verificato on-chain, con prezzo fisso in USD o pool DEX selezionato a livello di progetto. |
| icon_path | path | null | sempre | Icona del token in cache locale, se disponibile. |
| token_standard | erc20 | spl-token | null | sempre | Standard token verificato del runtime; null per gli asset nativi. |
| metadata_verified_at | timestamp | null | sempre | Momento della verifica dei metadati on-chain per i token promossi. |
| payment_supported / scanner_ready / balance_ready | boolean | sempre | Requisiti del registro verificati in compilazione. scanner_ready indica che il runtime dello scanner dei pagamenti è installato; la conferma richiede il numero configurato di provider funzionanti con ruolo esatto (2 predefiniti, 1 facoltativo); l'indisponibilità temporanea dello scanner non blocca la creazione di fatture dalla 6.0.6. balance_ready è true solo per adattatori di saldo implementati. |
| default_finality_mode | confirmations | finalized | sempre | Modello di finalità predefinito ereditato da una nuova politica di progetto. |
| default_required_confirmations / default_monitoring_minutes | integer | sempre | Politica predefinita di conferma e monitoraggio. |
StorePaymentAsset
| Campo | Tipo | Presenza | Descrizione |
|---|---|---|---|
| asset | PaymentAsset | sempre | Asset nativo o token verificato visibile al progetto. |
| project_policy | ProjectAssetPolicy | null | sempre | Politica del progetto padre. |
| selected | boolean | sempre | Indica se questo metodo fa parte della configurazione desiderata salvata del negozio. Viene offerto quando politica del progetto, wallet, adattatore installato e prezzi sono validi. Le interruzioni temporanee degli scanner non lo rimuovono dalle nuove fatture. |
| display_order | integer | null | sempre | Ordine nel checkout del negozio quando selezionato. |
| confirmation_policy | StoreConfirmationPolicy | null | sempre | Politica effettiva del negozio per un asset configurato nel progetto. Null se non esiste una politica di progetto. |
| wallet | WalletSummary | null | sempre | Wallet blockchain condiviso da asset nativi e token. |
| wallet_readiness | readiness enum | sempre | Solo stato di wallet e politica; usa receive_readiness per i prerequisiti degli scanner. |
| receive_readiness | ReceiveReadiness | null | 5.5.0+ | Configurazione di ricezione condivisa più accettazione del negozio. Usa osservazioni in cache; non è una prenotazione né una garanzia. La creazione ricontrolla requisiti e tasso effettivo della fattura. |
StoreConfirmationPolicy
| Campo | Tipo | Presenza | Descrizione |
|---|---|---|---|
| finality_mode | confirmations | finalized | sempre | Indica se il regolamento usa un numero di blocchi configurabile o la finalità di rete. |
| project_required_confirmations | integer | sempre | Valore attuale predefinito del progetto usato dalle fatture future quando non è impostata una personalizzazione del negozio. |
| override_required_confirmations | integer | null | sempre | Numero specifico del negozio, oppure null per ereditare il predefinito del progetto. |
| effective_required_confirmations | integer | sempre | Numero che verrà salvato nelle nuove fatture per questo negozio e asset. |
| editable | boolean | sempre | False per reti finalized la cui politica di finalità non può essere modificata. |
| minimum_required_confirmations | integer | sempre | Limite inferiore incluso, specifico della blockchain; 0 è esposto solo sui canali che supportano l'accettazione al rilevamento. |
| maximum_required_confirmations | integer | sempre | Limite superiore incluso, specifico della blockchain. |
ReceiveReadiness
| Campo | Tipo | Presenza | Descrizione |
|---|---|---|---|
| ready | boolean | sempre | I controlli della configurazione di ricezione passano. Non descrive disponibilità alla spesa, gas, aggiornamento saldi o una quotazione futura garantita. |
| invoice_creatable | boolean | 6.0.6+ | La configurazione consente un metodo di fattura nonostante avvisi temporanei dello scanner. Il prezzo della valuta viene controllato alla creazione. Non è una verifica del pagamento: ready può essere false mentre invoice_creatable è true. Wallet mancanti, politica disattivata e adattatori non supportati continuano a bloccare in sicurezza. |
| checked_at | timestamp | sempre | Momento della valutazione. Un elenco non esegue richieste di rete né assegna indirizzi. |
| issues | PaymentMethodIssue[] | sempre | Vuoto quando pronto; altrimenti un avviso di ricezione o un blocco di configurazione. Controlla invoice_creatable per distinguere avvisi temporanei dello scanner da errori di configurazione delle fatture. |
PaymentMethodIssue
| Campo | Tipo | Presenza | Descrizione |
|---|---|---|---|
| chain_slug / asset_id / asset_ticker | string / UUID / string | se noto | Identifica blockchain e asset interessati. Lightning può omettere asset_id. |
| reason_code | string | sempre | scanner_provider_quorum, scanner_not_checked, scanner_unavailable, wallet_missing, wallet_disabled, wallet_backup_required, wallet_key_unavailable, wallet_activation_required, monero_binding_unavailable, rate_unavailable, custom_rate_unavailable, lightning_unavailable, project_disabled, store_disabled, chain_disabled, asset_disabled o asset_not_accepted. |
| message / action | string | se disponibile | Spiegazione per il commerciante e identificativo dell'azione: chain_connections, wallets, rates, payment_methods, project_settings o store_settings. Nessuna credenziale o URL privato dei provider. |
| required_endpoint_role | string | null | on-chain | Ruolo API scanner preferito (campo precedente). Usa accepted_endpoint_roles per l'elenco completo di compatibilità. Lo stato di base del nodo non prova il supporto alla cronologia dei pagamenti. |
| accepted_endpoint_roles | string[] | null | on-chain | Dialetti API compatibili, non prova della cronologia o capacità dell'endpoint. node-rpc diretto supporta BTC/BCH/LTC/DOGE/DASH e ZEC trasparente (blocchi decodificati completi, 1–48 conferme), TRX nativo solidificato, ALGO nativo via algod, XTZ via Octez, DOT Asset Hub finalizzato via metadati SCALE e XLM nativo via Stellar RPC con ID memo fattura. Una cronologia potata o incompleta non è idonea. Questi adattatori diretti non aggiungono canali token. Le API indicizzate restano alternative; vedi la tabella dei canali sotto. Fonti miste dirette/indicizzate verificano in modo indipendente finestre limitate; restano predefiniti due provider indipendenti, non alias dello stesso operatore. Altezza di base del nodo, informazioni blockchain ORDnet e un relay EVM per un canale non EVM non sono prove di ricezione. Monero richiede comunque un wallet-RPC in sola visualizzazione associato al progetto. |
| healthy_endpoints | integer | on-chain | Endpoint funzionanti corrispondenti, non il numero di provider indipendenti. |
| usable_independent_providers / required_independent_providers | integer | on-chain | Slot di verifica utilizzabili, limitati a due. required_independent_providers è l'impostazione della blockchain: 2 predefiniti, o 1 dopo scelta esplicita dell'amministratore. In modalità a due provider servono chiavi provider E host diversi. Fonti disattivate, obsolete (oltre dieci minuti) o in attesa non occupano uno slot. Lightning usa regole di connessione proprie. |
| last_checked_at | timestamp | null | on-chain | Ultimo controllo di stato dell'endpoint corrispondente, separato dal momento della valutazione. |
Richiesta
: "${WHOLLY_TOKEN:?Set WHOLLY_TOKEN to your server-side API token}"
curl --fail-with-body --max-time 30 \
--request PUT \
--url "https://api.example.com/v1/projects/YOUR_PROJECT_ID/stores/YOUR_STORE_ID/payment-assets/YOUR_ASSET_ID/confirmation-policy" \
--header "Authorization: Bearer $WHOLLY_TOKEN" \
--header 'Content-Type: application/json' \
--data-raw '{
"strategy": "custom",
"required_confirmations": 0
}'// Node.js 18+ · run on your server, never in browser code.
const token = process.env.WHOLLY_TOKEN;
if (!token) throw new Error("Set WHOLLY_TOKEN");
const body = `{
"strategy": "custom",
"required_confirmations": 0
}`;
const response = await fetch("https://api.example.com/v1/projects/YOUR_PROJECT_ID/stores/YOUR_STORE_ID/payment-assets/YOUR_ASSET_ID/confirmation-policy", {
method: "PUT",
headers: {
"Authorization": `Bearer ${token}`,
"Content-Type": "application/json"
},
body,
redirect: "error",
signal: AbortSignal.timeout(30000)
});
if (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);
console.log(await response.json());<?php
// PHP 8+ with the cURL extension; run on your server.
$token = getenv('WHOLLY_TOKEN');
if (!$token) { throw new RuntimeException('Set WHOLLY_TOKEN'); }
$body = <<<'JSON'
{
"strategy": "custom",
"required_confirmations": 0
}
JSON;
$ch = curl_init("https://api.example.com/v1/projects/YOUR_PROJECT_ID/stores/YOUR_STORE_ID/payment-assets/YOUR_ASSET_ID/confirmation-policy");
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => 'PUT',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_FOLLOWLOCATION => false,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTPHEADER => ['Authorization: Bearer ' . $token, "Content-Type: application/json"],
CURLOPT_POSTFIELDS => $body,
]);
$response = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if ($response === false) { throw new RuntimeException(curl_error($ch)); }
curl_close($ch);
if ($status < 200 || $status >= 300) { throw new RuntimeException("HTTP $status: $response"); }
print_r(json_decode($response, true, 512, JSON_THROW_ON_ERROR));# Python 3 · standard library; run on your server.
import json
import os
from urllib.request import Request, build_opener, HTTPRedirectHandler
class NoRedirect(HTTPRedirectHandler):
def redirect_request(self, req, fp, code, msg, headers, newurl):
return None
headers = {
"Content-Type": "application/json"
}
headers["Authorization"] = "Bearer " + os.environ["WHOLLY_TOKEN"]
body = """{
"strategy": "custom",
"required_confirmations": 0
}""".encode("utf-8")
request = Request("https://api.example.com/v1/projects/YOUR_PROJECT_ID/stores/YOUR_STORE_ID/payment-assets/YOUR_ASSET_ID/confirmation-policy",
method="PUT", headers=headers, data=body)
# Non-2xx responses raise HTTPError. Do not retry writes with a new key.
with build_opener(NoRedirect()).open(request, timeout=30) as response:
print(json.load(response))Esempio di risposta · 200 application/json
{
"data": [
{
"asset": { "id": "YOUR_ASSET_ID", "chain_slug": "bitcoin", "symbol": "BTC" },
"selected": true,
"display_order": 0,
"confirmation_policy": {
"finality_mode": "confirmations",
"project_required_confirmations": 2,
"override_required_confirmations": 0,
"effective_required_confirmations": 0,
"editable": true,
"minimum_required_confirmations": 0,
"maximum_required_confirmations": 10000
},
"wallet_readiness": "ready"
}
]
}GETElenca wallet e saldi del progetto/v1/projects/{project_id}/walletsSola lettura
Restituisce metadati pubblici dei wallet e ogni asset registrato con lettura saldo sulla blockchain e rete esatte del wallet. Copre tutti i 30 canali nativi; monitora anche asset ERC-20 e SPL verificati. Gli asset compaiono subito, anche prima della prima scansione o quando non sono accettati per i pagamenti. project_enabled indica l'accettazione dei pagamenti; tracking_active indica separatamente l'idoneità all'aggiornamento in sola lettura. Monero richiede il wallet-RPC esterno in sola visualizzazione associato al progetto. Scansiona saldi nella console dà priorità a letture limitate con avanzamento/errori per asset; solo cicli completi aggiornano i totali correnti. Il regolamento delle fatture continua a dipendere dal monitoraggio delle singole transazioni e dalla politica di conferma, non da questi saldi in cache.
- Questa rotta bearer non restituisce mai frasi di recupero, chiavi private, segreti cifrati o metodi di spesa.
- Un asset appena registrato sulla stessa blockchain viene restituito con saldi null e stato pending prima della prima scansione completa; non viene mai segnalato con uno zero inventato.
- Disattivare un progetto, un wallet per l'accettazione dei pagamenti, un canale nativo o un asset non ferma il monitoraggio dei saldi in sola lettura: wallet attivi e disattivati con indirizzo primario continuano ad aggiornare ogni asset registrato e supportato sulla stessa blockchain. Wallet pending ed error non vengono scansionati.
- project_enabled indica solo la politica di accettazione degli asset del progetto e può essere false mentre tracking_active resta true.
- balance e balance_atomic sono stringhe esatte; price_usd, value_usd e total_value_usd sono indicativi e possono essere null. Un saldo aggiornato non garantisce un prezzo di mercato aggiornato.
- La valutazione preferisce prezzi CoinGecko vecchi al massimo due ore. Monete native e USDC/USDT canonici verificati possono usare come riserva quotazioni USD Kraken/Binance abilitate vecchie al massimo cinque minuti, prima il provider principale. Nessuna parità col dollaro presunta né prezzi di token personalizzati basati sul solo ticker; i prezzi fissi/DEX del progetto restano separati. Le quotazioni delle fatture non cambiano.
- Pending non ha un'istantanea completa. Refreshing conserva l'ultimo importo completo e checked_at; non significa che un trasferimento blockchain sia in attesa. Anche importi stale/error possono conservare valori precedenti. Non interpretare mai una cache indisponibile come zero o pagamento mancante. Gli aggiornamenti ordinari EVM/Solana riusano gli indirizzi vuoti verificati di recente fino a 30 minuti tra gli audit, mentre indirizzi con fondi, nuovi e modificati vengono ricontrollati. Scansiona saldi esplicito nella console richiede una scansione completa.
- recent_payments è limitato a tre osservazioni per wallet ed esclude la cronologia invalidata.
| Header | Presenza | Regola |
|---|---|---|
| Authorization | obbligatorio | Bearer YOUR_MERCHANT_API_TOKEN |
| Accept | consigliato | application/json |
| Parametro | Tipo / posizione | Regola |
|---|---|---|
| project_id | path UUID | Progetto attivo assegnato alla credenziale. |
WalletSummary
| Campo | Tipo | Presenza | Descrizione |
|---|---|---|---|
| id / project_id / native_asset_id | UUID | sempre | Identificativi del wallet, del progetto proprietario e dell'asset nativo della blockchain. |
| chain_slug / network | string | sempre | Blockchain e rete del wallet. |
| asset_symbol / asset_name | string | sempre | Identità visualizzata dell'asset nativo della blockchain. |
| status | pending | active | disabled | error | sempre | Stato operativo del wallet. |
| label | string | sempre | Etichetta dell'operatore. |
| public_key / primary_address | string | null | sempre | Identità pubblica del wallet; nessuna frase seed o chiave privata viene esposta. |
| derivation_scheme / address_format | string | null | sempre | Politica e formato degli indirizzi. |
| backup_confirmed_at | timestamp | null | sempre | Non null dopo che l'operatore conferma il backup di recupero. |
| activation_required / activation_verified_at | boolean / timestamp|null | sempre | Gli account condivisi XRP e Stellar restano indisponibili finché l'operatore non finanzia l'indirizzo mostrato e i provider scanner configurati non verificano quell'account esatto. La prova persistente non scade; lo stato attuale degli scanner viene controllato separatamente per verificare i pagamenti, non per creare fatture. |
| receive_readiness | ReceiveReadiness | null | 5.5.0+ | Incluso negli elenchi dei wallet: configurazione di ricezione del progetto e prerequisiti degli scanner blockchain. Separato da saldi, gas dei token e disponibilità all'invio. Altre risposte dei wallet possono lasciarlo null. |
| monero_wallet_rpc | MoneroWalletRpcBinding | null | sempre | Stato dell'associazione wallet-RPC esterna in sola visualizzazione per Monero, ripulito dai dati sensibili. Include endpoint, modalità di autenticazione, indirizzo primario dell'account 0, indicatori/altezze delle prove tecniche e timestamp delle attestazioni dell'operatore; credenziali, chiavi e file dei wallet non vengono mai serializzati. |
| last_secret_revealed_at / secret_reveal_count | timestamp|null / integer | sempre | Metadati di audit della divulgazione dei segreti lato console. |
| next_receive_index | integer | sempre | Indice del prossimo indirizzo figlio riservato. |
| last_scanned_height / last_scanned_at / last_error | integer|null / timestamp|null / string|null | sempre | Stato dello scanner wallet. |
| balances | WalletAssetBalance[] | sempre | Saldi in cache per tutti i 30 canali nativi delle blockchain, più asset ERC-20 e SPL verificati. Per Monero serve un wallet-RPC esterno in sola visualizzazione configurato. |
| total_value_usd | decimal string | null | sempre | Somma indicativa dei saldi con un prezzo USD attuale. |
| balance_status | pending | refreshing | fresh | stale | error | unknown | sempre | Aggiornamento aggregato della cache; unknown è un valore di riserva prudente e nessuno di questi stati prova il regolamento della fattura. |
| balance_checked_at | timestamp | null | sempre | Il più vecchio controllo di saldo riuscito pertinente rappresentato dall'aggregato. |
| recent_payments | WalletRecentPayment[] | sempre | Fino alle tre osservazioni valide più recenti detected, confirming o final attribuite a questo esatto wallet. |
| created_at / updated_at | RFC 3339 timestamp | sempre | Momento di creazione e ultimo aggiornamento del wallet. |
WalletAssetBalance
| Campo | Tipo | Presenza | Descrizione |
|---|---|---|---|
| wallet_id / asset_id | UUID | sempre | Identità del wallet e dell'asset persistente. |
| project_enabled | boolean | sempre | Indica se l'asset è attualmente abilitato dalla politica degli asset del progetto. |
| active_store_count | integer | sempre | Numero di negozi attivi che selezionano attualmente questo asset. È una vista dell'accettazione; il monitoraggio dei saldi in sola lettura resta indipendente. |
| active_store_ids | UUID[] | sempre | Negozi attivi in questo progetto che accettano attualmente l'asset. Consente un filtro locale esatto per negozio senza un'altra richiesta API. |
| tracking_active | boolean | sempre | Indica se questo wallet leggibile e l'asset registrato sulla stessa blockchain sono idonei agli aggiornamenti dei saldi in background. Gli interruttori di accettazione di progetto e metodi di pagamento non sospendono il monitoraggio in sola lettura. |
| asset_kind | native | token | sempre | Valuta nativa o asset con contratto/mint verificato. |
| contract_address | string | null | sempre | Contratto o mint del token; null per la valuta nativa. |
| symbol / name / decimals | string / string / integer | sempre | Identità visualizzata e precisione atomica. |
| coingecko_id | string | null | sempre | Identità per i prezzi quando associata. |
| balance / balance_atomic | decimal string|null / integer string|null | sempre | Saldo esatto visualizzato e atomico sull'indirizzo primario del wallet e sugli indirizzi fattura emessi. Null finché non è disponibile un valore completo. |
| price_usd | decimal string | null | sempre | Prezzo unitario USD indicativo in cache usato per la valutazione. |
| value_usd | decimal string | null | sempre | Valutazione fiat indicativa quando esiste un tasso attuale. |
| status | pending | refreshing | fresh | stale | error | sempre | Stato della scansione in cache. refreshing può conservare un saldo completo: usa checked_at per la sua età. Pending significa nessuna istantanea completa. Nessuno di questi stati prova un trasferimento in attesa o una fattura saldata. |
| checked_at | timestamp | null | sempre | Momento rappresentato da una scansione completa del saldo. |
| last_error | string | null | sempre | Diagnostica sicura per l'operatore. |
WalletRecentPayment
| Campo | Tipo | Presenza | Descrizione |
|---|---|---|---|
| invoice_public_id | UUID | sempre | Identità della fattura visibile al cliente associata all'osservazione. |
| chain_slug / symbol | string | sempre | Blockchain e simbolo visualizzato della moneta nativa o del token verificato. |
| transaction_id / event_index | string / integer | sempre | Identità canonica della transazione e dell'evento di trasferimento. |
| amount | decimal string | sempre | Importo esatto dell'asset osservato senza conversione in virgola mobile. |
| status | detected | confirming | final | sempre | Stato attuale valido dell'osservazione. Sono escluse osservazioni riorganizzate, sostituite e non valide. |
| confirmations | integer | sempre | Ultimo numero di conferme osservato. |
| observed_at | RFC 3339 timestamp | sempre | Momento in cui Wholly Crypto ha osservato per la prima volta il pagamento. |
ReceiveReadiness
| Campo | Tipo | Presenza | Descrizione |
|---|---|---|---|
| ready | boolean | sempre | I controlli della configurazione di ricezione passano. Non descrive disponibilità alla spesa, gas, aggiornamento saldi o una quotazione futura garantita. |
| invoice_creatable | boolean | 6.0.6+ | La configurazione consente un metodo di fattura nonostante avvisi temporanei dello scanner. Il prezzo della valuta viene controllato alla creazione. Non è una verifica del pagamento: ready può essere false mentre invoice_creatable è true. Wallet mancanti, politica disattivata e adattatori non supportati continuano a bloccare in sicurezza. |
| checked_at | timestamp | sempre | Momento della valutazione. Un elenco non esegue richieste di rete né assegna indirizzi. |
| issues | PaymentMethodIssue[] | sempre | Vuoto quando pronto; altrimenti un avviso di ricezione o un blocco di configurazione. Controlla invoice_creatable per distinguere avvisi temporanei dello scanner da errori di configurazione delle fatture. |
PaymentMethodIssue
| Campo | Tipo | Presenza | Descrizione |
|---|---|---|---|
| chain_slug / asset_id / asset_ticker | string / UUID / string | se noto | Identifica blockchain e asset interessati. Lightning può omettere asset_id. |
| reason_code | string | sempre | scanner_provider_quorum, scanner_not_checked, scanner_unavailable, wallet_missing, wallet_disabled, wallet_backup_required, wallet_key_unavailable, wallet_activation_required, monero_binding_unavailable, rate_unavailable, custom_rate_unavailable, lightning_unavailable, project_disabled, store_disabled, chain_disabled, asset_disabled o asset_not_accepted. |
| message / action | string | se disponibile | Spiegazione per il commerciante e identificativo dell'azione: chain_connections, wallets, rates, payment_methods, project_settings o store_settings. Nessuna credenziale o URL privato dei provider. |
| required_endpoint_role | string | null | on-chain | Ruolo API scanner preferito (campo precedente). Usa accepted_endpoint_roles per l'elenco completo di compatibilità. Lo stato di base del nodo non prova il supporto alla cronologia dei pagamenti. |
| accepted_endpoint_roles | string[] | null | on-chain | Dialetti API compatibili, non prova della cronologia o capacità dell'endpoint. node-rpc diretto supporta BTC/BCH/LTC/DOGE/DASH e ZEC trasparente (blocchi decodificati completi, 1–48 conferme), TRX nativo solidificato, ALGO nativo via algod, XTZ via Octez, DOT Asset Hub finalizzato via metadati SCALE e XLM nativo via Stellar RPC con ID memo fattura. Una cronologia potata o incompleta non è idonea. Questi adattatori diretti non aggiungono canali token. Le API indicizzate restano alternative; vedi la tabella dei canali sotto. Fonti miste dirette/indicizzate verificano in modo indipendente finestre limitate; restano predefiniti due provider indipendenti, non alias dello stesso operatore. Altezza di base del nodo, informazioni blockchain ORDnet e un relay EVM per un canale non EVM non sono prove di ricezione. Monero richiede comunque un wallet-RPC in sola visualizzazione associato al progetto. |
| healthy_endpoints | integer | on-chain | Endpoint funzionanti corrispondenti, non il numero di provider indipendenti. |
| usable_independent_providers / required_independent_providers | integer | on-chain | Slot di verifica utilizzabili, limitati a due. required_independent_providers è l'impostazione della blockchain: 2 predefiniti, o 1 dopo scelta esplicita dell'amministratore. In modalità a due provider servono chiavi provider E host diversi. Fonti disattivate, obsolete (oltre dieci minuti) o in attesa non occupano uno slot. Lightning usa regole di connessione proprie. |
| last_checked_at | timestamp | null | on-chain | Ultimo controllo di stato dell'endpoint corrispondente, separato dal momento della valutazione. |
Richiesta
: "${WHOLLY_TOKEN:?Set WHOLLY_TOKEN to your server-side API token}"
curl --fail-with-body --max-time 30 \
--request GET \
--url "https://api.example.com/v1/projects/YOUR_PROJECT_ID/wallets" \
--header "Authorization: Bearer $WHOLLY_TOKEN"// Node.js 18+ · run on your server, never in browser code.
const token = process.env.WHOLLY_TOKEN;
if (!token) throw new Error("Set WHOLLY_TOKEN");
const response = await fetch("https://api.example.com/v1/projects/YOUR_PROJECT_ID/wallets", {
method: "GET",
headers: {
"Authorization": `Bearer ${token}`
},
redirect: "error",
signal: AbortSignal.timeout(30000)
});
if (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);
console.log(await response.json());<?php
// PHP 8+ with the cURL extension; run on your server.
$token = getenv('WHOLLY_TOKEN');
if (!$token) { throw new RuntimeException('Set WHOLLY_TOKEN'); }
$ch = curl_init("https://api.example.com/v1/projects/YOUR_PROJECT_ID/wallets");
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => 'GET',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_FOLLOWLOCATION => false,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTPHEADER => ['Authorization: Bearer ' . $token],
]);
$response = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if ($response === false) { throw new RuntimeException(curl_error($ch)); }
curl_close($ch);
if ($status < 200 || $status >= 300) { throw new RuntimeException("HTTP $status: $response"); }
print_r(json_decode($response, true, 512, JSON_THROW_ON_ERROR));# Python 3 · standard library; run on your server.
import json
import os
from urllib.request import Request, build_opener, HTTPRedirectHandler
class NoRedirect(HTTPRedirectHandler):
def redirect_request(self, req, fp, code, msg, headers, newurl):
return None
headers = {}
headers["Authorization"] = "Bearer " + os.environ["WHOLLY_TOKEN"]
request = Request("https://api.example.com/v1/projects/YOUR_PROJECT_ID/wallets",
method="GET", headers=headers)
# Non-2xx responses raise HTTPError. Do not retry writes with a new key.
with build_opener(NoRedirect()).open(request, timeout=30) as response:
print(json.load(response))Esempio di risposta · 200 application/json
{
"data": [
{
"id": "55555555-5555-4555-8555-555555555555",
"project_id": "11111111-1111-4111-8111-111111111111",
"native_asset_id": "10000000-0000-4000-8000-000000000003",
"chain_slug": "ethereum",
"network": "mainnet",
"asset_symbol": "ETH",
"asset_name": "Ethereum",
"status": "active",
"label": "Primary Ethereum wallet",
"public_key": "0x…",
"primary_address": "0x…",
"derivation_scheme": "bip44",
"address_format": "eip55",
"backup_confirmed_at": "2026-08-31T17:00:00Z",
"last_secret_revealed_at": null,
"secret_reveal_count": 0,
"next_receive_index": 43,
"last_scanned_height": 23123456,
"last_scanned_at": "2026-08-31T18:05:00Z",
"last_error": null,
"balances": [
{ "wallet_id": "55555555-5555-4555-8555-555555555555", "asset_id": "10000000-0000-4000-8000-000000000003", "project_enabled": true, "tracking_active": true, "asset_kind": "native", "contract_address": null, "symbol": "ETH", "name": "Ethereum", "decimals": 18, "coingecko_id": "ethereum", "balance": "0.125", "balance_atomic": "125000000000000000", "price_usd": "4500", "value_usd": "562.50", "status": "fresh", "checked_at": "2026-08-31T18:05:00Z", "last_error": null },
{ "wallet_id": "55555555-5555-4555-8555-555555555555", "asset_id": "10000000-0000-4000-8000-000000000099", "project_enabled": false, "tracking_active": true, "asset_kind": "token", "contract_address": "0xA0b86991c6218b36c1d19d4a2e9eb0cE3606eB48", "symbol": "USDC", "name": "USDC", "decimals": 6, "coingecko_id": "usd-coin", "balance": null, "balance_atomic": null, "price_usd": "1", "value_usd": null, "status": "pending", "checked_at": null, "last_error": null }
],
"total_value_usd": "562.50",
"balance_status": "fresh",
"balance_checked_at": "2026-08-31T18:05:00Z",
"recent_payments": [
{ "invoice_public_id": "0a6a98db-d93d-48ee-8c3c-fd45f90c4a50", "chain_slug": "ethereum", "symbol": "USDC", "transaction_id": "0x…", "event_index": 0, "amount": "25", "status": "final", "confirmations": 12, "observed_at": "2026-08-31T18:04:00Z" }
],
"created_at": "2026-08-31T16:00:00Z",
"updated_at": "2026-08-31T18:05:00Z"
}
]
}POSTCrea fattura/v1/projects/{project_id}/stores/{store_id}/invoicesLettura + scrittura
Crea atomicamente una fattura con destinazioni wallet, quotazioni esatte aggiornate, cronologia audit e voci nella coda di notifiche in uscita. Ripetere gli stessi byte grezzi del corpo con la stessa credenziale e Idempotency-Key restituisce la fattura originale.
- payment_methods filtra i metodi attivi del negozio solo per questa fattura. Omesso/null mantiene tutti i metodi del negozio; [] non è valido. Trovi l'indicazione chain_slug e i ticker visualizzati in Progetto → Negozi → Metodi di pagamento. L'elenco API payment-assets fornisce chain_slug, asset.symbol e asset.id. Usa {chain_slug: ethereum, asset_tickers: [USDC, USDT]} per i token Ethereum accettati; BTC e PEPE funzionano allo stesso modo sulle blockchain selezionate. I ticker non distinguono maiuscole/minuscole, sono limitati alla blockchain e si risolvono solo all'interno del negozio. Due contratti accettati con lo stesso ticker restituiscono 400 invece di sceglierne uno, anche se uno non è pronto; in quel caso usa asset_ids. Asset nativi, token di catalogo e personalizzati seguono le stesse regole. Ogni blockchain/canale può apparire una volta; massimo 64 metodi finali. Merchant 5.4.0+: scelte sconosciute, disattivate, su blockchain errata o non accettate vengono ignorate. Se l'intera selezione non ha corrispondenze attive accettate, si applicano i valori predefiniti del negozio; altrimenti vengono usate solo le scelte corrispondenti. Una voce con la sola blockchain include ogni asset on-chain attivo accettato. I metodi attivi selezionati richiedono wallet validi, adattatori scanner installati e tassi attendibili. Dalla 6.0.6, scanner indisponibili, pause e controlli di stato in attesa/obsoleti non bloccano la creazione di fatture né rimuovono i metodi on-chain configurati. Il rilevamento riprova automaticamente; il regolamento richiede ancora quorum dei provider e conferme. Monitora receive_readiness e mantieni disponibili i provider: una fattura può restare non verificata finché gli scanner non si riprendono. L'assegnazione dei sottoindirizzi Monero e la generazione BOLT11 Lightning richiedono comunque il servizio esterno wallet/nodo. Gli errori restituiscono error.message più error.details.payment_methods con chain_slug, asset_ticker, reason_code e, per la diagnostica scanner, required_endpoint_role, healthy_endpoints e required_independent_providers. TRON accetta cronologia indicizzata o API supportate di blocchi nativi solidificati diretti; il solo stato di base non prova la compatibilità scanner. Gli errori di prezzo identificano asset/valuta. Nulla abilita un asset non accettato o modifica la politica del negozio. Nelle versioni merchant precedenti alla 5.4.0, le scelte esplicite sconosciute/inattive falliscono invece. I metodi delle fatture esistenti non si ampliano mai quando cambiano le impostazioni del negozio. Lightning va selezionato separatamente. I replay mantengono i metodi originali e cambiare selezioni con lo stesso Idempotency-Key restituisce 409.
- checkout_appearance supporta tutte le impostazioni di presentazione elencate sopra. I campi omessi vengono ereditati, gli array sostituiscono e i campi messaggio annidati si uniscono; un oggetto messaggio vuoto cancella quell'ambito. Design risolto e immagini vengono salvati per questa fattura senza modificare il negozio. Leggi appearance dal JSON del checkout pubblico per controllare il risultato. L'intera richiesta è limitata a 32 KiB e le impostazioni risolte a 20 KiB.
- Cambiare checkout_appearance con lo stesso Idempotency-Key restituisce 409; riprova con byte grezzi identici. L'aspetto non cambia importi, tassi, asset accettati, conferme richieste, stato reale o permessi di incorporamento. Niente HTML, CSS, script o recupero di immagini remote.
- exchange_rate_spread_percent sostituisce il predefinito del negozio per questa fattura: ometti o invia null per ereditare, oppure invia "0" per disattivarlo. Le quotazioni delle fatture esistenti non cambiano mai.
- Lo spread si applica prima dell'arrotondamento per eccesso. Le commissioni restano basate sull'importo fiat originale della fattura, escluso lo spread.
- Invia sempre expected_amount o expected_amount_atomic restituito. L'arrotondamento è per eccesso, limitato dalla precisione dell'asset, dallo 0.1% dell'importo e da un'unità minore fiat.
- I nuovi tentativi devono mantenere la stessa credenziale, Idempotency-Key e gli esatti byte del corpo. Cambiare lo spread con la stessa chiave restituisce 409 idempotency_conflict.
- Un replay esatto viene controllato prima di una nuova quotazione, DNS del callback o preparazione degli indirizzi. Ambito della credenziale e autorizzazione progetto/negozio vengono comunque controllati a ogni richiesta.
- Un ipn_url effettivo richiede il segreto di firma IPN del negozio. I campi sconosciuti del corpo vengono rifiutati.
| Header | Presenza | Regola |
|---|---|---|
| Authorization | obbligatorio | Bearer YOUR_MERCHANT_API_TOKEN |
| Idempotency-Key | obbligatorio | 1–128 caratteri ASCII visibili univoci, senza spazi. |
| Content-Type | consigliato | application/json. Il gestore attuale del corpo grezzo analizza il JSON senza imporre il tipo di contenuto. |
| Accept | consigliato | application/json |
| Parametro | Tipo / posizione | Regola |
|---|---|---|
| project_id | path UUID | Copia ID API del progetto da Progetto → Impostazioni → ID API. Deve essere assegnato alla credenziale; non è accettato un identificativo leggibile del progetto. |
| store_id | path UUID | Copia ID API del negozio da Progetto → Negozi → seleziona un negozio → Generale → ID API. Obbligatorio anche per il negozio predefinito; deve essere attivo e appartenere a project_id. |
Corpo della creazione fattura
| Campo | Tipo | Presenza | Descrizione |
|---|---|---|---|
| amount | string | obbligatorio | Stringa decimale semplice senza segno; niente segno o esponente, fino a 48 cifre intere e 30 decimali. Deve essere positiva per impostazione predefinita. Un negozio può consentire fatture a importo zero in Negozi → Fattura; i totali zero si saldano senza ricevere fondi, assegnare indirizzi o commissioni di elaborazione. |
| currency | string | null | facoltativo | Valuta fiat supportata di tre lettere, normalizzata in maiuscolo. Omessa o null eredita la valuta delle fatture del negozio. La creazione richiede anche un tasso di conversione di fatturazione disponibile in modo indipendente. |
| payment_methods | InvoicePaymentSelection[] | null | facoltativo | Seleziona i metodi attivi del negozio per questa fattura. Merchant 5.4.0+: ignora scelte sconosciute/inattive/non accettate; se nessuna corrisponde, usa i predefiniti del negozio. Omesso/null usa anch'esso i predefiniti; [] non è valido. Non abilita mai un metodo né modifica le impostazioni del negozio. Vedi lo schema di selezione sotto. |
| order_id | string | null | facoltativo | Riferimento ordine del commerciante, 1–128 caratteri dopo la rimozione degli spazi esterni; caratteri di controllo rifiutati. |
| string | null | facoltativo | Email cliente solo per il commerciante, normalizzata in un indirizzo ASCII utilizzabile di massimo 254 caratteri. Omessa o null non salva alcuna email. | |
| description | string | null | facoltativo | Descrizione visibile al cliente, 1–500 caratteri; ritorni a capo e tabulazioni consentiti. |
| expires_in_seconds | integer | null | facoltativo | Durata della quotazione della fattura da 300 a 86.400 secondi; omessa o null eredita la politica del negozio. |
| exchange_rate_spread_percent | decimal string | null | facoltativo | Maggiorazione della quotazione da 0 a 100, massimo due decimali. Omessa o null eredita il predefinito del negozio; "0" la disattiva per questa fattura. Applicata prima dell'arrotondamento per eccesso, poi bloccata. Non modifica l'importo fiat della fattura né la base della commissione di elaborazione. |
| underpayment_tolerance_percent | decimal string | null | facoltativo | Ammanco accettato da 0 a 99.99, massimo due decimali. Omesso o null eredita il predefinito del negozio. |
| ipn_url | string | null | facoltativo | Callback HTTPS pubblico, massimo 2.048 byte, senza credenziali né frammento. Sostituisce il predefinito del negozio; null/omesso lo eredita. |
| redirect_url | string | null | facoltativo | URL HTTPS di successo usato dopo il regolamento, massimo 2.048 byte e senza credenziali incorporate. Omesso o null eredita il predefinito del negozio e non può cancellarlo. |
| cancel_url | string | null | facoltativo | URL HTTPS di ritorno usato quando il checkout termina senza pagamento riuscito. Omesso o null eredita il predefinito del negozio e non può cancellarlo. |
| redirect_automatically | boolean | null | facoltativo | Omesso o null eredita la politica del negozio. true richiede un redirect_url effettivo. |
| language | string | null | facoltativo | Tag BCP 47 inglese o tedesco, come en, de o de-DE; omesso o null eredita la politica del negozio. |
| checkout_appearance | CheckoutAppearanceOverride | null | facoltativo | Impostazioni parziali di presentazione per questa fattura. Omesso/null segue il design attuale del negozio. Un oggetto, incluso {}, congela design risolto e immagini alla creazione. Vedi lo schema di personalizzazione sotto; nessuna impostazione finanziaria, HTML, CSS, JavaScript o URL di immagini remote. |
| metadata | object | null | facoltativo | Oggetto JSON solo per il commerciante; omesso o null diventa {}, massimo 4.096 byte codificati e cinque livelli annidati. firstname, lastname, street, street2, zip, city, country, countryiso2, company e vatid vengono convalidati, normalizzati e proiettati nei campi riepilogativi del cliente. |
InvoicePaymentSelection · scegli blockchain e asset del negozio
| Campo | Tipo | Presenza | Descrizione |
|---|---|---|---|
| chain_slug | string | obbligatorio | Copia chain_slug in Progetto → Negozi → Metodi di pagamento, oppure leggilo da GET /v1/projects/{project_id}/stores/{store_id}/payment-assets, ad esempio ethereum, base o bitcoin. Una coppia blockchain/canale può comparire una sola volta. |
| asset_ids | UUID[] | null | facoltativo | UUID asset.id on-chain, non indirizzi dei contratti né ID dei metodi di pagamento della fattura. Usa questo O asset_tickers. Ometti entrambi i selettori per tutti gli asset attivi accettati su questa blockchain. [] e ID duplicati/nulli non sono validi. Dalla 5.4.0+, ignora ID non attivi/accettati su questa blockchain in questo negozio; una selezione interamente senza corrispondenze usa i predefiniti del negozio. |
| asset_tickers | string[] | null | facoltativo | Merchant 5.3.0+. Simboli come BTC, USDC o PEPE, limitati a chain_slug e a questo negozio. 1–64 ticker univoci; spazi esterni rimossi, senza distinzione maiuscole/minuscole, 1–40 lettere/cifre/punti/underscore/trattini ASCII. Usa questo O asset_ids. Dalla 5.4.0+, ignora ticker sconosciuti/inattivi/non accettati. Simboli accettati ambigui falliscono comunque: usa asset_ids. I metodi attivi selezionati richiedono wallet e prezzi validi; interruzioni temporanee degli scanner on-chain non bloccano la creazione dalla 6.0.6. Lightning accetta facoltativamente solo BTC. |
| payment_rail | onchain | lightning | facoltativo | Predefinito onchain. Per scegliere Bitcoin Lightning, usa {chain_slug: bitcoin, payment_rail: lightning} senza asset_ids; asset_tickers può facoltativamente essere [BTC]. Bitcoin on-chain non include Lightning. La connessione Lightning del negozio deve essere già attiva e pronta. |
CheckoutAppearanceOverride · tutti i campi facoltativi
| Campo | Tipo | Presenza | Descrizione |
|---|---|---|---|
| inherit_default_store | boolean | facoltativo | true seleziona come base il design del negozio predefinito del progetto; altrimenti usa quello effettivo del negozio destinazione. Le personalizzazioni vengono poi applicate e salvate indipendentemente; il flag risolto della fattura è false. |
| title | string | facoltativo | Titolo del checkout, massimo 120 caratteri. Vuoto usa il titolo standard. |
| intro / outro | string | facoltativo | Testo semplice, massimo 2.000 caratteri ciascuno. Intro appare in alto, Outro in basso in ogni stato. I ritorni a capo sono conservati; gli URL sicuri nel testo diventano link. Una stringa vuota cancella. Il vecchio customer_message è accettato come alias di intro; non inviarli entrambi. |
| intro_font_size / outro_font_size | integer | facoltativo | Pixel: 12, 14, 16, 18, 20 o 24. Predefinito 16 salvo diversa ereditarietà. |
| theme | system | light | dim | dark | facoltativo | Segui il dispositivo del cliente o usa un tema fisso. |
| accent_color / background_color / card_color / button_color | string | facoltativo | #RRGGBB. Sfondo, scheda e pulsante possono essere vuoti per colori automatici. Il contrasto del testo è automatico. |
| logo_size / logo_alignment | string | facoltativo | small, medium o large; left o center. |
| images | object | facoltativo | Chiavi logo_light, logo_dark, favicon. Una chiave omessa conserva l'immagine di base; null la rimuove. Un oggetto {store_id: UUID, kind?: logo_light|logo_dark|favicon} riusa l'immagine caricata effettiva di quel negozio nello STESSO progetto. kind usa per default la chiave destinazione. Carica prima in Negozio → Checkout; copia ID API del negozio da Generale → ID API. Immagini mancanti o ID di altri progetti restituiscono 400. Nessun URL esterno o dato immagine accettato. |
| show_order_id / show_description / details_expanded | boolean | facoltativo | Mostra i dettagli dell'ID ordine e una descrizione in testo semplice sotto il titolo. details_expanded apre subito i dettagli dell'ID ordine. Solo visualizzazione, non oscuramento dei dati. |
| show_project_name / show_store_name | boolean | facoltativo | Merchant 5.6.0+: mostra o nascondi ogni nome nell'intestazione del checkout cliente. Entrambi predefiniti true. Disponibile anche in Negozio → Checkout; ereditato e salvato nell'istantanea della fattura come le altre impostazioni di aspetto. Solo visualizzazione, non oscuramento dei dati. |
| featured_chains | string[] | facoltativo | Slug blockchain ordinati, massimo 60 valori univoci (lettere minuscole, cifre, trattini; fino a 64 caratteri). [] cancella. Riordina solo i metodi disponibili della fattura. |
| featured_asset_ids / default_asset_id | UUID[] / UUID|null | facoltativo | Fino a 100 ID asset univoci ordinati; [] cancella. L'asset predefinito può essere null. Gli ID provengono da payment-assets, non dagli intenti di pagamento. Non abilitano mai metodi; pagamenti ricevuti e preferenze valide del cliente hanno priorità. |
| messages | object | facoltativo | Oggetti en/de con stringhe semplici waiting, confirming, paid, underpaid, expired (500 caratteri ciascuna). Cambiano solo lingue/stati forniti; {} cancella tutti i messaggi, {en:{}} cancella l'inglese e una stringa vuota di stato cancella quello stato. L'inglese è la lingua di riserva. Non sostituisce lo stato reale. |
| support_email | string | facoltativo | Email ASCII, massimo 254 caratteri. Vuoto cancella. |
| support_url / terms_url / privacy_url | string | facoltativo | URL HTTPS fino a 2.048 caratteri, senza credenziali. Vuoto cancella. I link si aprono in una nuova finestra. |
| return_button_text | string | facoltativo | Etichetta fino a 60 caratteri. Usa redirect_url/cancel_url/redirect_automatically/language principali per il comportamento della fattura. |
Riepilogo fattura
| Campo | Tipo | Presenza | Descrizione |
|---|---|---|---|
| id | UUID | sempre | UUID interno della fattura. Non usarlo nei percorsi merchant di dettaglio o checkout. |
| invoice_id | UUID | sempre | UUID pubblico della fattura usato nei percorsi merchant di dettaglio e checkout. |
| project_id | UUID | sempre | Progetto proprietario. |
| store_id | UUID | sempre | Negozio proprietario. |
| source | manual | api | sempre | Come è stata creata la fattura. |
| order_id | string | null | sempre | Riferimento dell'ordine del commerciante. |
| string | null | sempre | Email cliente solo per il commerciante. Mai restituita dal checkout pubblico. | |
| customer_name | string | null | sempre | Nome visualizzato derivato dai metadati privati firstname, lastname e company. |
| customer_address | string | null | sempre | Indirizzo del commerciante su una riga derivato dai metadati privati company, street, street2, zip, city, country, countryiso2 e vatid. |
| description | string | null | sempre | Descrizione visibile al cliente. |
| amount | decimal string | sempre | Importo canonico della fattura. |
| currency | string | sempre | Codice normalizzato della valuta/asset della fattura. |
| exchange_rate_spread_percent | decimal string | sempre | Spread della quotazione bloccato: il valore specificato alla creazione o il predefinito del negozio se omesso. Applicato prima dell'arrotondamento per eccesso; non cambia mai su questa fattura. |
| underpayment_tolerance_percent | decimal string | sempre | Percentuale immutabile di ammanco accettato salvata alla creazione della fattura. |
| status | invoice status | sempre | new, processing, settled, expired, invalid o cancelled. |
| amount_status | amount status | sempre | none, partial, paid o overpaid. Una fattura a importo zero esplicitamente consentita si salda con none e nessun metodo di pagamento. |
| timing_status | timing status | sempre | on_time o late. |
| resolution | resolution | sempre | automatic, manually_settled o manually_invalidated. |
| sequence | integer | sempre | Sequenza monotona dello stato della fattura, a partire da 1. |
| winning_payment_intent_id | UUID | null | sempre | Metodo di pagamento che ha risolto la fattura, quando selezionato. |
| expires_at | RFC 3339 timestamp | sempre | Scadenza della quotazione/pagamento. |
| monitoring_expires_at | RFC 3339 timestamp | sempre | Ultimo termine configurato di monitoraggio tardivo tra i metodi di pagamento. |
| settled_at | timestamp | null | sempre | Momento del regolamento quando saldata. |
| cancelled_at | timestamp | null | sempre | Momento dell'annullamento quando annullata. |
| archived_at | timestamp | null | sempre | Momento dell'archiviazione quando archiviata. |
| created_at | RFC 3339 timestamp | sempre | Momento della creazione. |
| updated_at | RFC 3339 timestamp | sempre | Momento dell'ultimo aggiornamento di stato. |
Aggiunte al dettaglio fattura
| Campo | Tipo | Presenza | Descrizione |
|---|---|---|---|
| ipn_url | string | null | sempre | Destinazione IPN effettiva della singola fattura. Solo risposta merchant; omessa dal checkout pubblico. |
| redirect_url | string | null | sempre | URL di successo effettivo usato dopo il regolamento. |
| cancel_url | string | null | sempre | URL di ritorno effettivo usato quando il checkout termina senza pagamento riuscito. |
| redirect_automatically | boolean | sempre | Indica se il checkout deve reindirizzare automaticamente dopo il successo. |
| checkout_language | string | sempre | Tag di lingua effettivo del checkout. |
| metadata | object | sempre | Metadati del commerciante. Mai restituiti dal checkout pubblico. |
| payment_intents | PaymentIntent[] | sempre | Metodi di pagamento quotati e stato del monitoraggio. |
PaymentIntent
| Campo | Tipo | Presenza | Descrizione |
|---|---|---|---|
| id | UUID | sempre | Identificativo dell'intento di pagamento; usato anche come intent_id del QR checkout. |
| payment_rail | onchain | lightning | sempre | Trasporto della fattura. Bitcoin on-chain e Lightning possono condividere asset_id; usa l'id dell'intento più questo campo, non solo il simbolo. È diverso da payment_rail dello scanner nel catalogo asset. |
| bolt11 | string | null | sempre | Richiesta di pagamento Lightning, altrimenti null. Paga questa richiesta con un wallet Lightning; non inviare mai fondi on-chain al suo hash di pagamento. |
| asset_id | UUID | sempre | Identificativo dell'asset di pagamento configurato. |
| asset_key | string | sempre | Chiave canonica dell'asset in stile CAIP. |
| chain_slug | string | sempre | Identificativo blockchain Wholly Crypto. |
| network | string | sempre | Rete configurata, attualmente mainnet per gli asset di pagamento supportati. |
| caip_network_id | string | sempre | Identificativo di rete canonico CAIP-2. |
| caip_asset_id | string | null | sempre | Identificativo canonico CAIP-19 se registrato. |
| symbol | string | sempre | Simbolo dell'asset. |
| asset_decimals | integer | sempre | Precisione in unità atomiche. Lightning BTC usa 11 (millisatoshi), non gli 8 di Bitcoin on-chain. Le quotazioni sono in satoshi interi; gli incassi mantengono la precisione al millisatoshi. |
| status | intent status | sempre | pending, partial, paid, overpaid, expired o invalid. |
| finality_mode | confirmations | finalized | sempre | Politica di finalità. |
| required_confirmations | integer | sempre | Conferme richieste, se applicabili. |
| quote_rate | decimal string | sempre | Unità dell'asset per una unità della valuta della fattura, incluso lo spread bloccato. Ad esempio 1.02 USDC per USD. Non il tasso inverso. |
| quote_details | object | null | sempre | Provenienza della quotazione bloccata: reference_rate prima dello spread, unrounded_payment_amount, rounding_adjustment, pricing_provider, asset_provider, pricing_fetched_at e asset_fetched_at. Null nelle vecchie fatture; nessun valore storico viene inventato. |
| expected_amount | decimal string | sempre | Importo esatto bloccato dell'asset da pagare dopo spread e arrotondamento per eccesso. Dalla 4.1.1, le stablecoin fiat riconosciute e verificate (come USDC, USDT, DAI, USDS, EURC) si arrotondano per eccesso a massimo due decimali; 1.321 diventa 1.33, mai 1.32. È l'importo atteso anche con tolleranza zero. Gli altri asset mantengono la precisione adattiva. Le fatture esistenti non vengono mai riquotate. |
| expected_amount_atomic | integer string | sempre | Importo esatto nell'unità minima dell'asset. |
| minimum_payment_amount | decimal string | sempre | Importo minimo accettato come pagato dopo la tolleranza della fattura. |
| minimum_payment_amount_atomic | integer string | sempre | Soglia esatta accettata nell'unità minima dell'asset. |
| received_amount | decimal string | sempre | Importo osservato. |
| received_amount_atomic | integer string | sempre | Importo atomico osservato. |
| confirmed_amount | decimal string | sempre | Importo confermato/finale. |
| confirmed_amount_atomic | integer string | sempre | Importo atomico confermato/finale. |
| destination_address | string | sempre | Indirizzo di ricezione on-chain o hash di pagamento di 64 caratteri per Lightning. Usa bolt11 per pagare con Lightning: il suo hash non è un indirizzo Bitcoin. |
| destination_tag | string | null | sempre | Riferimento pubblico di pagamento obbligatorio quando il circuito ne usa uno: destination tag XRP, memo ID Stellar o commento della fattura TON. Null per i circuiti con indirizzi univoci. |
| derivation_index | integer | sempre | Indice figlio riservato del wallet; solo nel dettaglio del commerciante. |
| quote_expires_at | RFC 3339 timestamp | sempre | Scadenza del preventivo. |
| monitoring_expires_at | RFC 3339 timestamp | sempre | Termine del monitoraggio tardivo per questo metodo. |
| next_check_at | timestamp | null | sempre | Prossimo controllo programmato della blockchain. |
| last_checked_at | timestamp | null | sempre | Ultimo controllo della blockchain. |
| last_chain_height | integer | null | sempre | Ultima altezza affidabile osservata dal monitor. |
| last_anchor_hash | string | null | sempre | Ultimo hash di ancoraggio/blocco del monitor. |
| last_monitor_error | string | null | sempre | Diagnostica di monitoraggio sicura per gli operatori. |
| first_payment_at | timestamp | null | sempre | Ora della prima osservazione del pagamento. |
| fully_paid_at | timestamp | null | sempre | Ora in cui è stato raggiunto per la prima volta l'importo minimo accettato. |
| finalized_at | timestamp | null | sempre | Ora in cui il pagamento ha soddisfatto la politica di finalità. |
PaymentMethodIssue
| Campo | Tipo | Presenza | Descrizione |
|---|---|---|---|
| chain_slug / asset_id / asset_ticker | string / UUID / string | se noto | Identifica blockchain e asset interessati. Lightning può omettere asset_id. |
| reason_code | string | sempre | scanner_provider_quorum, scanner_not_checked, scanner_unavailable, wallet_missing, wallet_disabled, wallet_backup_required, wallet_key_unavailable, wallet_activation_required, monero_binding_unavailable, rate_unavailable, custom_rate_unavailable, lightning_unavailable, project_disabled, store_disabled, chain_disabled, asset_disabled o asset_not_accepted. |
| message / action | string | se disponibile | Spiegazione per il commerciante e identificativo dell'azione: chain_connections, wallets, rates, payment_methods, project_settings o store_settings. Nessuna credenziale o URL privato dei provider. |
| required_endpoint_role | string | null | on-chain | Ruolo API scanner preferito (campo precedente). Usa accepted_endpoint_roles per l'elenco completo di compatibilità. Lo stato di base del nodo non prova il supporto alla cronologia dei pagamenti. |
| accepted_endpoint_roles | string[] | null | on-chain | Dialetti API compatibili, non prova della cronologia o capacità dell'endpoint. node-rpc diretto supporta BTC/BCH/LTC/DOGE/DASH e ZEC trasparente (blocchi decodificati completi, 1–48 conferme), TRX nativo solidificato, ALGO nativo via algod, XTZ via Octez, DOT Asset Hub finalizzato via metadati SCALE e XLM nativo via Stellar RPC con ID memo fattura. Una cronologia potata o incompleta non è idonea. Questi adattatori diretti non aggiungono canali token. Le API indicizzate restano alternative; vedi la tabella dei canali sotto. Fonti miste dirette/indicizzate verificano in modo indipendente finestre limitate; restano predefiniti due provider indipendenti, non alias dello stesso operatore. Altezza di base del nodo, informazioni blockchain ORDnet e un relay EVM per un canale non EVM non sono prove di ricezione. Monero richiede comunque un wallet-RPC in sola visualizzazione associato al progetto. |
| healthy_endpoints | integer | on-chain | Endpoint funzionanti corrispondenti, non il numero di provider indipendenti. |
| usable_independent_providers / required_independent_providers | integer | on-chain | Slot di verifica utilizzabili, limitati a due. required_independent_providers è l'impostazione della blockchain: 2 predefiniti, o 1 dopo scelta esplicita dell'amministratore. In modalità a due provider servono chiavi provider E host diversi. Fonti disattivate, obsolete (oltre dieci minuti) o in attesa non occupano uno slot. Lightning usa regole di connessione proprie. |
| last_checked_at | timestamp | null | on-chain | Ultimo controllo di stato dell'endpoint corrispondente, separato dal momento della valutazione. |
Richiesta
: "${WHOLLY_TOKEN:?Set WHOLLY_TOKEN to your server-side API token}"
# Keep this key and the exact body for retries; use a new key for each new invoice.
curl --fail-with-body --max-time 30 \
--request POST \
--url "https://api.example.com/v1/projects/YOUR_PROJECT_ID/stores/YOUR_STORE_ID/invoices" \
--header "Authorization: Bearer $WHOLLY_TOKEN" \
--header 'Idempotency-Key: order-1042-attempt-1' \
--header 'Content-Type: application/json' \
--data-raw '{
"payment_methods": [
{
"chain_slug": "bitcoin",
"asset_tickers": [
"BTC"
]
}
],
"amount": "49.90",
"currency": "USD",
"order_id": "order-1042",
"email": "ada@example.com",
"description": "Annual plan",
"underpayment_tolerance_percent": "1",
"ipn_url": "https://merchant.example/wholly/ipn",
"redirect_url": "https://merchant.example/orders/1042",
"cancel_url": "https://merchant.example/cart",
"redirect_automatically": true,
"language": "en",
"metadata": {
"cart_id": "cart-681",
"firstname": "Ada",
"lastname": "Lovelace",
"street": "12 Example Street",
"street2": "Suite 2",
"zip": "10115",
"city": "Berlin",
"country": "Germany",
"countryiso2": "DE",
"company": "Example GmbH",
"vatid": "DE123456789"
},
"exchange_rate_spread_percent": "0.5",
"checkout_appearance": {
"title": "Complete your order",
"intro": "Thanks for choosing our annual plan.",
"outro": "Questions? https://merchant.example/help",
"intro_font_size": 18,
"outro_font_size": 14,
"theme": "light",
"accent_color": "#1768CE",
"messages": {
"en": {
"paid": "Your order is ready."
}
}
}
}'// Node.js 18+ · run on your server, never in browser code.
const token = process.env.WHOLLY_TOKEN;
if (!token) throw new Error("Set WHOLLY_TOKEN");
// Keep this key and the exact body for retries; use a new key for each new invoice.
const body = `{
"payment_methods": [
{
"chain_slug": "bitcoin",
"asset_tickers": [
"BTC"
]
}
],
"amount": "49.90",
"currency": "USD",
"order_id": "order-1042",
"email": "ada@example.com",
"description": "Annual plan",
"underpayment_tolerance_percent": "1",
"ipn_url": "https://merchant.example/wholly/ipn",
"redirect_url": "https://merchant.example/orders/1042",
"cancel_url": "https://merchant.example/cart",
"redirect_automatically": true,
"language": "en",
"metadata": {
"cart_id": "cart-681",
"firstname": "Ada",
"lastname": "Lovelace",
"street": "12 Example Street",
"street2": "Suite 2",
"zip": "10115",
"city": "Berlin",
"country": "Germany",
"countryiso2": "DE",
"company": "Example GmbH",
"vatid": "DE123456789"
},
"exchange_rate_spread_percent": "0.5",
"checkout_appearance": {
"title": "Complete your order",
"intro": "Thanks for choosing our annual plan.",
"outro": "Questions? https://merchant.example/help",
"intro_font_size": 18,
"outro_font_size": 14,
"theme": "light",
"accent_color": "#1768CE",
"messages": {
"en": {
"paid": "Your order is ready."
}
}
}
}`;
const response = await fetch("https://api.example.com/v1/projects/YOUR_PROJECT_ID/stores/YOUR_STORE_ID/invoices", {
method: "POST",
headers: {
"Authorization": `Bearer ${token}`,
"Idempotency-Key": "order-1042-attempt-1",
"Content-Type": "application/json"
},
body,
redirect: "error",
signal: AbortSignal.timeout(30000)
});
if (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);
console.log(await response.json());<?php
// PHP 8+ with the cURL extension; run on your server.
$token = getenv('WHOLLY_TOKEN');
if (!$token) { throw new RuntimeException('Set WHOLLY_TOKEN'); }
// Keep this key and the exact body for retries; use a new key for each new invoice.
$body = <<<'JSON'
{
"payment_methods": [
{
"chain_slug": "bitcoin",
"asset_tickers": [
"BTC"
]
}
],
"amount": "49.90",
"currency": "USD",
"order_id": "order-1042",
"email": "ada@example.com",
"description": "Annual plan",
"underpayment_tolerance_percent": "1",
"ipn_url": "https://merchant.example/wholly/ipn",
"redirect_url": "https://merchant.example/orders/1042",
"cancel_url": "https://merchant.example/cart",
"redirect_automatically": true,
"language": "en",
"metadata": {
"cart_id": "cart-681",
"firstname": "Ada",
"lastname": "Lovelace",
"street": "12 Example Street",
"street2": "Suite 2",
"zip": "10115",
"city": "Berlin",
"country": "Germany",
"countryiso2": "DE",
"company": "Example GmbH",
"vatid": "DE123456789"
},
"exchange_rate_spread_percent": "0.5",
"checkout_appearance": {
"title": "Complete your order",
"intro": "Thanks for choosing our annual plan.",
"outro": "Questions? https://merchant.example/help",
"intro_font_size": 18,
"outro_font_size": 14,
"theme": "light",
"accent_color": "#1768CE",
"messages": {
"en": {
"paid": "Your order is ready."
}
}
}
}
JSON;
$ch = curl_init("https://api.example.com/v1/projects/YOUR_PROJECT_ID/stores/YOUR_STORE_ID/invoices");
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => 'POST',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_FOLLOWLOCATION => false,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTPHEADER => ['Authorization: Bearer ' . $token, "Idempotency-Key: order-1042-attempt-1", "Content-Type: application/json"],
CURLOPT_POSTFIELDS => $body,
]);
$response = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if ($response === false) { throw new RuntimeException(curl_error($ch)); }
curl_close($ch);
if ($status < 200 || $status >= 300) { throw new RuntimeException("HTTP $status: $response"); }
print_r(json_decode($response, true, 512, JSON_THROW_ON_ERROR));# Python 3 · standard library; run on your server.
import json
import os
from urllib.request import Request, build_opener, HTTPRedirectHandler
class NoRedirect(HTTPRedirectHandler):
def redirect_request(self, req, fp, code, msg, headers, newurl):
return None
# Keep this key and the exact body for retries; use a new key for each new invoice.
headers = {
"Idempotency-Key": "order-1042-attempt-1",
"Content-Type": "application/json"
}
headers["Authorization"] = "Bearer " + os.environ["WHOLLY_TOKEN"]
body = """{
"payment_methods": [
{
"chain_slug": "bitcoin",
"asset_tickers": [
"BTC"
]
}
],
"amount": "49.90",
"currency": "USD",
"order_id": "order-1042",
"email": "ada@example.com",
"description": "Annual plan",
"underpayment_tolerance_percent": "1",
"ipn_url": "https://merchant.example/wholly/ipn",
"redirect_url": "https://merchant.example/orders/1042",
"cancel_url": "https://merchant.example/cart",
"redirect_automatically": true,
"language": "en",
"metadata": {
"cart_id": "cart-681",
"firstname": "Ada",
"lastname": "Lovelace",
"street": "12 Example Street",
"street2": "Suite 2",
"zip": "10115",
"city": "Berlin",
"country": "Germany",
"countryiso2": "DE",
"company": "Example GmbH",
"vatid": "DE123456789"
},
"exchange_rate_spread_percent": "0.5",
"checkout_appearance": {
"title": "Complete your order",
"intro": "Thanks for choosing our annual plan.",
"outro": "Questions? https://merchant.example/help",
"intro_font_size": 18,
"outro_font_size": 14,
"theme": "light",
"accent_color": "#1768CE",
"messages": {
"en": {
"paid": "Your order is ready."
}
}
}
}""".encode("utf-8")
request = Request("https://api.example.com/v1/projects/YOUR_PROJECT_ID/stores/YOUR_STORE_ID/invoices",
method="POST", headers=headers, data=body)
# Non-2xx responses raise HTTPError. Do not retry writes with a new key.
with build_opener(NoRedirect()).open(request, timeout=30) as response:
print(json.load(response))Risposta di esempio · 201 nuova fattura; 200 ripetizione idempotente esatta
{
"data": {
"id": "2f798f9f-01f2-42f0-9d10-5581d6116b4c",
"invoice_id": "0a6a98db-d93d-48ee-8c3c-fd45f90c4a50",
"project_id": "11111111-1111-4111-8111-111111111111",
"store_id": "22222222-2222-4222-8222-222222222222",
"source": "api",
"order_id": "order-1042",
"email": "ada@example.com",
"customer_name": "Ada Lovelace · Example GmbH",
"customer_address": "Example GmbH · 12 Example Street · Suite 2 · 10115 Berlin · Germany (DE) · VAT DE123456789",
"description": "Annual plan",
"amount": "49.9",
"currency": "USD",
"exchange_rate_spread_percent": "0.5",
"underpayment_tolerance_percent": "1",
"status": "new",
"amount_status": "none",
"timing_status": "on_time",
"resolution": "automatic",
"sequence": 1,
"winning_payment_intent_id": null,
"expires_at": "2026-08-31T18:15:00Z",
"monitoring_expires_at": "2026-09-07T18:15:00Z",
"settled_at": null,
"cancelled_at": null,
"archived_at": null,
"created_at": "2026-08-31T18:00:00Z",
"updated_at": "2026-08-31T18:00:00Z",
"ipn_url": "https://merchant.example/wholly/ipn",
"redirect_url": "https://merchant.example/orders/1042",
"cancel_url": "https://merchant.example/cart",
"redirect_automatically": true,
"checkout_language": "en",
"metadata": { "cart_id": "cart-681", "firstname": "Ada", "lastname": "Lovelace", "street": "12 Example Street", "street2": "Suite 2", "zip": "10115", "city": "Berlin", "country": "Germany", "countryiso2": "DE", "company": "Example GmbH", "vatid": "DE123456789" },
"payment_intents": [
{
"id": "33333333-3333-4333-8333-333333333333",
"asset_id": "10000000-0000-4000-8000-000000000001",
"asset_key": "bip122:000000000019d6689c085ae165831e93/slip44:0",
"chain_slug": "bitcoin",
"network": "mainnet",
"caip_network_id": "bip122:000000000019d6689c085ae165831e93",
"caip_asset_id": "bip122:000000000019d6689c085ae165831e93/slip44:0",
"symbol": "BTC",
"asset_decimals": 8,
"status": "pending",
"finality_mode": "confirmations",
"required_confirmations": 1,
"quote_rate": "0.000009218",
"quote_details": null,
"expected_amount": "0.00046",
"expected_amount_atomic": "46000",
"minimum_payment_amount": "0.0004554",
"minimum_payment_amount_atomic": "45540",
"received_amount": "0",
"received_amount_atomic": "0",
"confirmed_amount": "0",
"confirmed_amount_atomic": "0",
"destination_address": "bc1q…example",
"destination_tag": null,
"derivation_index": 42,
"quote_expires_at": "2026-08-31T18:15:00Z",
"monitoring_expires_at": "2026-09-07T18:15:00Z",
"next_check_at": "2026-08-31T18:00:00Z",
"last_checked_at": null,
"last_chain_height": null,
"last_anchor_hash": null,
"last_monitor_error": null,
"first_payment_at": null,
"fully_paid_at": null,
"finalized_at": null
}
]
},
"links": {
"checkout": "https://pay.example.com/invoice/0a6a98db-d93d-48ee-8c3c-fd45f90c4a50"
}
}GETElenca fatture/v1/projects/{project_id}/invoicesSola lettura
Restituisce una pagina compatta di riepiloghi delle fatture nell'ambito autorizzato, dalla più recente, inclusi email e campi cliente riservati al commerciante e ricavati dai metadati riconosciuti. Ricerca e filtri per stato e negozio vengono valutati lato server; la risposta include total e has_more per una paginazione prevedibile.
- Ordinati per created_at decrescente, poi per id interno decrescente.
- Gli elementi dell'elenco sono oggetti InvoiceSummary; email, customer_name e customer_address sono riservati al commerciante. Richiedi il dettaglio per i metadati grezzi e gli intenti di pagamento.
- Per la pagina successiva imposta offset su pagination.offset + pagination.limit solo quando has_more è true.
- Conteggio e pagina vengono letti da un'unica istantanea del database a lettura ripetibile; le scritture concorrenti appaiono in una richiesta successiva.
| Header | Presenza | Regola |
|---|---|---|
| Authorization | obbligatorio | Bearer YOUR_MERCHANT_API_TOKEN |
| Accept | consigliato | application/json |
| Parametro | Tipo / posizione | Regola |
|---|---|---|
| project_id | path UUID | Progetto attivo assegnato alla credenziale. |
| store_id | query UUID | Filtro esatto facoltativo per negozio. |
| status | query enum | Facoltativo: new, processing, settled, expired, invalid o cancelled. |
| search | query string | Prefisso facoltativo di ID fattura, ID ordine o email senza distinzione tra maiuscole e minuscole; UUID fattura esatto; oppure sottostringa nella descrizione e nei campi cliente riconosciuti. Anche tutte le chiavi dei metadati e i valori testuali, numerici e booleani (inclusi oggetti/array annidati) supportano una ricerca indicizzata per prefisso delle parole: ogni parola cercata deve corrispondere e la punteggiatura è trattata come separatore. Spazi iniziali e finali rimossi, massimo 100 caratteri, nessun carattere di controllo. Le corrispondenze nei metadati non aggiungono metadati grezzi alle risposte dell'elenco; usa il dettaglio della fattura per leggerli. |
| limit | query integer | Facoltativo 1–100; predefinito 50. |
| offset | query integer | Facoltativo, 0–1.000.000; valore predefinito 0. |
Riepilogo fattura
| Campo | Tipo | Presenza | Descrizione |
|---|---|---|---|
| id | UUID | sempre | UUID interno della fattura. Non usarlo nei percorsi merchant di dettaglio o checkout. |
| invoice_id | UUID | sempre | UUID pubblico della fattura usato nei percorsi merchant di dettaglio e checkout. |
| project_id | UUID | sempre | Progetto proprietario. |
| store_id | UUID | sempre | Negozio proprietario. |
| source | manual | api | sempre | Come è stata creata la fattura. |
| order_id | string | null | sempre | Riferimento dell'ordine del commerciante. |
| string | null | sempre | Email cliente solo per il commerciante. Mai restituita dal checkout pubblico. | |
| customer_name | string | null | sempre | Nome visualizzato derivato dai metadati privati firstname, lastname e company. |
| customer_address | string | null | sempre | Indirizzo del commerciante su una riga derivato dai metadati privati company, street, street2, zip, city, country, countryiso2 e vatid. |
| description | string | null | sempre | Descrizione visibile al cliente. |
| amount | decimal string | sempre | Importo canonico della fattura. |
| currency | string | sempre | Codice normalizzato della valuta/asset della fattura. |
| exchange_rate_spread_percent | decimal string | sempre | Spread della quotazione bloccato: il valore specificato alla creazione o il predefinito del negozio se omesso. Applicato prima dell'arrotondamento per eccesso; non cambia mai su questa fattura. |
| underpayment_tolerance_percent | decimal string | sempre | Percentuale immutabile di ammanco accettato salvata alla creazione della fattura. |
| status | invoice status | sempre | new, processing, settled, expired, invalid o cancelled. |
| amount_status | amount status | sempre | none, partial, paid o overpaid. Una fattura a importo zero esplicitamente consentita si salda con none e nessun metodo di pagamento. |
| timing_status | timing status | sempre | on_time o late. |
| resolution | resolution | sempre | automatic, manually_settled o manually_invalidated. |
| sequence | integer | sempre | Sequenza monotona dello stato della fattura, a partire da 1. |
| winning_payment_intent_id | UUID | null | sempre | Metodo di pagamento che ha risolto la fattura, quando selezionato. |
| expires_at | RFC 3339 timestamp | sempre | Scadenza della quotazione/pagamento. |
| monitoring_expires_at | RFC 3339 timestamp | sempre | Ultimo termine configurato di monitoraggio tardivo tra i metodi di pagamento. |
| settled_at | timestamp | null | sempre | Momento del regolamento quando saldata. |
| cancelled_at | timestamp | null | sempre | Momento dell'annullamento quando annullata. |
| archived_at | timestamp | null | sempre | Momento dell'archiviazione quando archiviata. |
| created_at | RFC 3339 timestamp | sempre | Momento della creazione. |
| updated_at | RFC 3339 timestamp | sempre | Momento dell'ultimo aggiornamento di stato. |
Paginazione delle fatture
| Campo | Tipo | Presenza | Descrizione |
|---|---|---|---|
| limit | integer | sempre | Dimensione effettiva della pagina, 1–100. |
| offset | integer | sempre | Offset effettivo delle righe a partire da zero, 0–1.000.000. |
| total | integer | sempre | Numero totale di righe corrispondenti ai filtri per progetto, negozio, stato e ricerca nell'istantanea della pagina. |
| has_more | boolean | sempre | True quando offset più il numero di righe restituite è inferiore a total. |
Richiesta
: "${WHOLLY_TOKEN:?Set WHOLLY_TOKEN to your server-side API token}"
curl --fail-with-body --max-time 30 \
--request GET \
--url "https://api.example.com/v1/projects/YOUR_PROJECT_ID/invoices?search=order-1042&status=processing&limit=50&offset=0" \
--header "Authorization: Bearer $WHOLLY_TOKEN" \
--header 'Accept: application/json'// Node.js 18+ · run on your server, never in browser code.
const token = process.env.WHOLLY_TOKEN;
if (!token) throw new Error("Set WHOLLY_TOKEN");
const response = await fetch("https://api.example.com/v1/projects/YOUR_PROJECT_ID/invoices?search=order-1042&status=processing&limit=50&offset=0", {
method: "GET",
headers: {
"Authorization": `Bearer ${token}`,
"Accept": "application/json"
},
redirect: "error",
signal: AbortSignal.timeout(30000)
});
if (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);
console.log(await response.json());<?php
// PHP 8+ with the cURL extension; run on your server.
$token = getenv('WHOLLY_TOKEN');
if (!$token) { throw new RuntimeException('Set WHOLLY_TOKEN'); }
$ch = curl_init("https://api.example.com/v1/projects/YOUR_PROJECT_ID/invoices?search=order-1042&status=processing&limit=50&offset=0");
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => 'GET',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_FOLLOWLOCATION => false,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTPHEADER => ['Authorization: Bearer ' . $token, "Accept: application/json"],
]);
$response = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if ($response === false) { throw new RuntimeException(curl_error($ch)); }
curl_close($ch);
if ($status < 200 || $status >= 300) { throw new RuntimeException("HTTP $status: $response"); }
print_r(json_decode($response, true, 512, JSON_THROW_ON_ERROR));# Python 3 · standard library; run on your server.
import json
import os
from urllib.request import Request, build_opener, HTTPRedirectHandler
class NoRedirect(HTTPRedirectHandler):
def redirect_request(self, req, fp, code, msg, headers, newurl):
return None
headers = {
"Accept": "application/json"
}
headers["Authorization"] = "Bearer " + os.environ["WHOLLY_TOKEN"]
request = Request("https://api.example.com/v1/projects/YOUR_PROJECT_ID/invoices?search=order-1042&status=processing&limit=50&offset=0",
method="GET", headers=headers)
# Non-2xx responses raise HTTPError. Do not retry writes with a new key.
with build_opener(NoRedirect()).open(request, timeout=30) as response:
print(json.load(response))Esempio di risposta · 200 application/json
{
"data": [
{
"id": "2f798f9f-01f2-42f0-9d10-5581d6116b4c",
"invoice_id": "0a6a98db-d93d-48ee-8c3c-fd45f90c4a50",
"project_id": "11111111-1111-4111-8111-111111111111",
"store_id": "22222222-2222-4222-8222-222222222222",
"source": "api",
"order_id": "order-1042",
"email": "ada@example.com",
"customer_name": "Ada Lovelace · Example GmbH",
"customer_address": "Example GmbH · 12 Example Street · Suite 2 · 10115 Berlin · Germany (DE) · VAT DE123456789",
"description": "Annual plan",
"amount": "49.9",
"currency": "USD",
"exchange_rate_spread_percent": "0.5",
"underpayment_tolerance_percent": "1",
"status": "processing",
"amount_status": "paid",
"timing_status": "on_time",
"resolution": "automatic",
"sequence": 3,
"winning_payment_intent_id": "33333333-3333-4333-8333-333333333333",
"expires_at": "2026-08-31T18:15:00Z",
"monitoring_expires_at": "2026-09-07T18:15:00Z",
"settled_at": null,
"cancelled_at": null,
"archived_at": null,
"created_at": "2026-08-31T18:00:00Z",
"updated_at": "2026-08-31T18:04:10Z"
}
],
"pagination": {
"limit": 50,
"offset": 0,
"total": 143,
"has_more": true
}
}GETRecupera fattura/v1/projects/{project_id}/invoices/{invoice_id}Sola lettura
Restituisce il dettaglio completo della fattura per il commerciante e l'URL di checkout attualmente attivo. Usa questa rotta per il polling e la riconciliazione.
- Una ricerca limitata all'ambito autorizzato restituisce intenzionalmente invoice_not_found quando l'ID pubblico non appartiene al progetto autorizzato.
- links.checkout usa Negozio → Generale → Domini del negozio: prima l'hostname pay attivo di questo negozio, poi la scelta del suo negozio predefinito, infine quello principale del sistema. Gli host ritirati/in bozza o associati al servizio sbagliato vengono ignorati. Vale anche per le risposte di creazione e MCP; i link vengono risolti al momento della risposta, incluse le ripetizioni idempotenti. I link dei callback firmati vengono fissati alla creazione dell'evento, non riscritti nei tentativi successivi. Queste preferenze generano solo link: non reindirizzano il traffico e non modificano le restrizioni IP.
| Header | Presenza | Regola |
|---|---|---|
| Authorization | obbligatorio | Bearer YOUR_MERCHANT_API_TOKEN |
| Accept | consigliato | application/json |
| Parametro | Tipo / posizione | Regola |
|---|---|---|
| project_id | path UUID | Progetto attivo assegnato alla credenziale. |
| invoice_id | path UUID | L'invoice_id restituito durante la creazione/l'elenco, non l'id interno. |
Riepilogo fattura
| Campo | Tipo | Presenza | Descrizione |
|---|---|---|---|
| id | UUID | sempre | UUID interno della fattura. Non usarlo nei percorsi merchant di dettaglio o checkout. |
| invoice_id | UUID | sempre | UUID pubblico della fattura usato nei percorsi merchant di dettaglio e checkout. |
| project_id | UUID | sempre | Progetto proprietario. |
| store_id | UUID | sempre | Negozio proprietario. |
| source | manual | api | sempre | Come è stata creata la fattura. |
| order_id | string | null | sempre | Riferimento dell'ordine del commerciante. |
| string | null | sempre | Email cliente solo per il commerciante. Mai restituita dal checkout pubblico. | |
| customer_name | string | null | sempre | Nome visualizzato derivato dai metadati privati firstname, lastname e company. |
| customer_address | string | null | sempre | Indirizzo del commerciante su una riga derivato dai metadati privati company, street, street2, zip, city, country, countryiso2 e vatid. |
| description | string | null | sempre | Descrizione visibile al cliente. |
| amount | decimal string | sempre | Importo canonico della fattura. |
| currency | string | sempre | Codice normalizzato della valuta/asset della fattura. |
| exchange_rate_spread_percent | decimal string | sempre | Spread della quotazione bloccato: il valore specificato alla creazione o il predefinito del negozio se omesso. Applicato prima dell'arrotondamento per eccesso; non cambia mai su questa fattura. |
| underpayment_tolerance_percent | decimal string | sempre | Percentuale immutabile di ammanco accettato salvata alla creazione della fattura. |
| status | invoice status | sempre | new, processing, settled, expired, invalid o cancelled. |
| amount_status | amount status | sempre | none, partial, paid o overpaid. Una fattura a importo zero esplicitamente consentita si salda con none e nessun metodo di pagamento. |
| timing_status | timing status | sempre | on_time o late. |
| resolution | resolution | sempre | automatic, manually_settled o manually_invalidated. |
| sequence | integer | sempre | Sequenza monotona dello stato della fattura, a partire da 1. |
| winning_payment_intent_id | UUID | null | sempre | Metodo di pagamento che ha risolto la fattura, quando selezionato. |
| expires_at | RFC 3339 timestamp | sempre | Scadenza della quotazione/pagamento. |
| monitoring_expires_at | RFC 3339 timestamp | sempre | Ultimo termine configurato di monitoraggio tardivo tra i metodi di pagamento. |
| settled_at | timestamp | null | sempre | Momento del regolamento quando saldata. |
| cancelled_at | timestamp | null | sempre | Momento dell'annullamento quando annullata. |
| archived_at | timestamp | null | sempre | Momento dell'archiviazione quando archiviata. |
| created_at | RFC 3339 timestamp | sempre | Momento della creazione. |
| updated_at | RFC 3339 timestamp | sempre | Momento dell'ultimo aggiornamento di stato. |
Aggiunte al dettaglio fattura
| Campo | Tipo | Presenza | Descrizione |
|---|---|---|---|
| ipn_url | string | null | sempre | Destinazione IPN effettiva della singola fattura. Solo risposta merchant; omessa dal checkout pubblico. |
| redirect_url | string | null | sempre | URL di successo effettivo usato dopo il regolamento. |
| cancel_url | string | null | sempre | URL di ritorno effettivo usato quando il checkout termina senza pagamento riuscito. |
| redirect_automatically | boolean | sempre | Indica se il checkout deve reindirizzare automaticamente dopo il successo. |
| checkout_language | string | sempre | Tag di lingua effettivo del checkout. |
| metadata | object | sempre | Metadati del commerciante. Mai restituiti dal checkout pubblico. |
| payment_intents | PaymentIntent[] | sempre | Metodi di pagamento quotati e stato del monitoraggio. |
PaymentIntent
| Campo | Tipo | Presenza | Descrizione |
|---|---|---|---|
| id | UUID | sempre | Identificativo dell'intento di pagamento; usato anche come intent_id del QR checkout. |
| payment_rail | onchain | lightning | sempre | Trasporto della fattura. Bitcoin on-chain e Lightning possono condividere asset_id; usa l'id dell'intento più questo campo, non solo il simbolo. È diverso da payment_rail dello scanner nel catalogo asset. |
| bolt11 | string | null | sempre | Richiesta di pagamento Lightning, altrimenti null. Paga questa richiesta con un wallet Lightning; non inviare mai fondi on-chain al suo hash di pagamento. |
| asset_id | UUID | sempre | Identificativo dell'asset di pagamento configurato. |
| asset_key | string | sempre | Chiave canonica dell'asset in stile CAIP. |
| chain_slug | string | sempre | Identificativo blockchain Wholly Crypto. |
| network | string | sempre | Rete configurata, attualmente mainnet per gli asset di pagamento supportati. |
| caip_network_id | string | sempre | Identificativo di rete canonico CAIP-2. |
| caip_asset_id | string | null | sempre | Identificativo canonico CAIP-19 se registrato. |
| symbol | string | sempre | Simbolo dell'asset. |
| asset_decimals | integer | sempre | Precisione in unità atomiche. Lightning BTC usa 11 (millisatoshi), non gli 8 di Bitcoin on-chain. Le quotazioni sono in satoshi interi; gli incassi mantengono la precisione al millisatoshi. |
| status | intent status | sempre | pending, partial, paid, overpaid, expired o invalid. |
| finality_mode | confirmations | finalized | sempre | Politica di finalità. |
| required_confirmations | integer | sempre | Conferme richieste, se applicabili. |
| quote_rate | decimal string | sempre | Unità dell'asset per una unità della valuta della fattura, incluso lo spread bloccato. Ad esempio 1.02 USDC per USD. Non il tasso inverso. |
| quote_details | object | null | sempre | Provenienza della quotazione bloccata: reference_rate prima dello spread, unrounded_payment_amount, rounding_adjustment, pricing_provider, asset_provider, pricing_fetched_at e asset_fetched_at. Null nelle vecchie fatture; nessun valore storico viene inventato. |
| expected_amount | decimal string | sempre | Importo esatto bloccato dell'asset da pagare dopo spread e arrotondamento per eccesso. Dalla 4.1.1, le stablecoin fiat riconosciute e verificate (come USDC, USDT, DAI, USDS, EURC) si arrotondano per eccesso a massimo due decimali; 1.321 diventa 1.33, mai 1.32. È l'importo atteso anche con tolleranza zero. Gli altri asset mantengono la precisione adattiva. Le fatture esistenti non vengono mai riquotate. |
| expected_amount_atomic | integer string | sempre | Importo esatto nell'unità minima dell'asset. |
| minimum_payment_amount | decimal string | sempre | Importo minimo accettato come pagato dopo la tolleranza della fattura. |
| minimum_payment_amount_atomic | integer string | sempre | Soglia esatta accettata nell'unità minima dell'asset. |
| received_amount | decimal string | sempre | Importo osservato. |
| received_amount_atomic | integer string | sempre | Importo atomico osservato. |
| confirmed_amount | decimal string | sempre | Importo confermato/finale. |
| confirmed_amount_atomic | integer string | sempre | Importo atomico confermato/finale. |
| destination_address | string | sempre | Indirizzo di ricezione on-chain o hash di pagamento di 64 caratteri per Lightning. Usa bolt11 per pagare con Lightning: il suo hash non è un indirizzo Bitcoin. |
| destination_tag | string | null | sempre | Riferimento pubblico di pagamento obbligatorio quando il circuito ne usa uno: destination tag XRP, memo ID Stellar o commento della fattura TON. Null per i circuiti con indirizzi univoci. |
| derivation_index | integer | sempre | Indice figlio riservato del wallet; solo nel dettaglio del commerciante. |
| quote_expires_at | RFC 3339 timestamp | sempre | Scadenza del preventivo. |
| monitoring_expires_at | RFC 3339 timestamp | sempre | Termine del monitoraggio tardivo per questo metodo. |
| next_check_at | timestamp | null | sempre | Prossimo controllo programmato della blockchain. |
| last_checked_at | timestamp | null | sempre | Ultimo controllo della blockchain. |
| last_chain_height | integer | null | sempre | Ultima altezza affidabile osservata dal monitor. |
| last_anchor_hash | string | null | sempre | Ultimo hash di ancoraggio/blocco del monitor. |
| last_monitor_error | string | null | sempre | Diagnostica di monitoraggio sicura per gli operatori. |
| first_payment_at | timestamp | null | sempre | Ora della prima osservazione del pagamento. |
| fully_paid_at | timestamp | null | sempre | Ora in cui è stato raggiunto per la prima volta l'importo minimo accettato. |
| finalized_at | timestamp | null | sempre | Ora in cui il pagamento ha soddisfatto la politica di finalità. |
Richiesta
: "${WHOLLY_TOKEN:?Set WHOLLY_TOKEN to your server-side API token}"
curl --fail-with-body --max-time 30 \
--request GET \
--url "https://api.example.com/v1/projects/YOUR_PROJECT_ID/invoices/YOUR_PUBLIC_INVOICE_ID" \
--header "Authorization: Bearer $WHOLLY_TOKEN" \
--header 'Accept: application/json'// Node.js 18+ · run on your server, never in browser code.
const token = process.env.WHOLLY_TOKEN;
if (!token) throw new Error("Set WHOLLY_TOKEN");
const response = await fetch("https://api.example.com/v1/projects/YOUR_PROJECT_ID/invoices/YOUR_PUBLIC_INVOICE_ID", {
method: "GET",
headers: {
"Authorization": `Bearer ${token}`,
"Accept": "application/json"
},
redirect: "error",
signal: AbortSignal.timeout(30000)
});
if (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);
console.log(await response.json());<?php
// PHP 8+ with the cURL extension; run on your server.
$token = getenv('WHOLLY_TOKEN');
if (!$token) { throw new RuntimeException('Set WHOLLY_TOKEN'); }
$ch = curl_init("https://api.example.com/v1/projects/YOUR_PROJECT_ID/invoices/YOUR_PUBLIC_INVOICE_ID");
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => 'GET',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_FOLLOWLOCATION => false,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTPHEADER => ['Authorization: Bearer ' . $token, "Accept: application/json"],
]);
$response = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if ($response === false) { throw new RuntimeException(curl_error($ch)); }
curl_close($ch);
if ($status < 200 || $status >= 300) { throw new RuntimeException("HTTP $status: $response"); }
print_r(json_decode($response, true, 512, JSON_THROW_ON_ERROR));# Python 3 · standard library; run on your server.
import json
import os
from urllib.request import Request, build_opener, HTTPRedirectHandler
class NoRedirect(HTTPRedirectHandler):
def redirect_request(self, req, fp, code, msg, headers, newurl):
return None
headers = {
"Accept": "application/json"
}
headers["Authorization"] = "Bearer " + os.environ["WHOLLY_TOKEN"]
request = Request("https://api.example.com/v1/projects/YOUR_PROJECT_ID/invoices/YOUR_PUBLIC_INVOICE_ID",
method="GET", headers=headers)
# Non-2xx responses raise HTTPError. Do not retry writes with a new key.
with build_opener(NoRedirect()).open(request, timeout=30) as response:
print(json.load(response))Esempio di risposta · 200 application/json
{
"data": {
"id": "2f798f9f-01f2-42f0-9d10-5581d6116b4c",
"invoice_id": "0a6a98db-d93d-48ee-8c3c-fd45f90c4a50",
"project_id": "11111111-1111-4111-8111-111111111111",
"store_id": "22222222-2222-4222-8222-222222222222",
"source": "api",
"order_id": "order-1042",
"email": "ada@example.com",
"customer_name": "Ada Lovelace · Example GmbH",
"customer_address": "Example GmbH · 12 Example Street · Suite 2 · 10115 Berlin · Germany (DE) · VAT DE123456789",
"description": "Annual plan",
"amount": "49.9",
"currency": "USD",
"exchange_rate_spread_percent": "0.5",
"underpayment_tolerance_percent": "1",
"status": "settled",
"amount_status": "paid",
"timing_status": "on_time",
"resolution": "automatic",
"sequence": 4,
"winning_payment_intent_id": "33333333-3333-4333-8333-333333333333",
"expires_at": "2026-08-31T18:15:00Z",
"monitoring_expires_at": "2026-09-07T18:15:00Z",
"settled_at": "2026-08-31T18:05:00Z",
"cancelled_at": null,
"archived_at": null,
"created_at": "2026-08-31T18:00:00Z",
"updated_at": "2026-08-31T18:05:00Z",
"ipn_url": "https://merchant.example/wholly/ipn",
"redirect_url": "https://merchant.example/orders/1042",
"cancel_url": "https://merchant.example/cart",
"redirect_automatically": true,
"checkout_language": "en",
"metadata": { "cart_id": "cart-681", "firstname": "Ada", "lastname": "Lovelace", "street": "12 Example Street", "street2": "Suite 2", "zip": "10115", "city": "Berlin", "country": "Germany", "countryiso2": "DE", "company": "Example GmbH", "vatid": "DE123456789" },
"payment_intents": [
{
"id": "33333333-3333-4333-8333-333333333333",
"asset_id": "10000000-0000-4000-8000-000000000001",
"asset_key": "bip122:000000000019d6689c085ae165831e93/slip44:0",
"chain_slug": "bitcoin",
"network": "mainnet",
"caip_network_id": "bip122:000000000019d6689c085ae165831e93",
"caip_asset_id": "bip122:000000000019d6689c085ae165831e93/slip44:0",
"symbol": "BTC",
"asset_decimals": 8,
"status": "paid",
"finality_mode": "confirmations",
"required_confirmations": 1,
"quote_rate": "0.000009218",
"quote_details": null,
"expected_amount": "0.00046",
"expected_amount_atomic": "46000",
"minimum_payment_amount": "0.0004554",
"minimum_payment_amount_atomic": "45540",
"received_amount": "0.0004554",
"received_amount_atomic": "45540",
"confirmed_amount": "0.0004554",
"confirmed_amount_atomic": "45540",
"destination_address": "bc1q…example",
"destination_tag": null,
"derivation_index": 42,
"quote_expires_at": "2026-08-31T18:15:00Z",
"monitoring_expires_at": "2026-09-07T18:15:00Z",
"next_check_at": null,
"last_checked_at": "2026-08-31T18:05:00Z",
"last_chain_height": 912345,
"last_anchor_hash": "000000000000000000example",
"last_monitor_error": null,
"first_payment_at": "2026-08-31T18:03:00Z",
"fully_paid_at": "2026-08-31T18:03:00Z",
"finalized_at": "2026-08-31T18:05:00Z"
}
]
},
"links": {
"checkout": "https://pay.example.com/invoice/0a6a98db-d93d-48ee-8c3c-fd45f90c4a50"
}
}GETElenca pagamenti della fattura/v1/projects/{project_id}/invoices/{invoice_id}/paymentsSola lettura
Cronologia completa e attuale dei trasferimenti, incluse le osservazioni invalidate. Usala quando un callback segnala payments_truncated. È lo stato attuale, non la ricostruzione di un evento precedente.
- Un'osservazione è un log di token, un output UTXO o un altro trasferimento del circuito, non necessariamente un hash di transazione univoco. Deduplica per payment_id; transaction_id insieme a event_index identifica il trasferimento sulla blockchain.
- status è detected, confirming, final, reorged, replaced o invalid. Solo le osservazioni con counts_towards_received contribuiscono agli importi ricevuti. Non sommare mai importi di asset diversi.
- I record Lightning usano payment_hash con transaction_id, conferme e link agli explorer null; la precisione BTC è 11 (millisatoshi). Non vengono esposti preimage, BOLT11 o segreti del wallet.
- Ordinati per observed_at decrescente, poi per payment_id decrescente. Conteggio e pagina usano un'unica istantanea a lettura ripetibile; le pagine successive possono cambiare all'arrivo dei pagamenti. Deduplica per payment_id durante la paginazione di una fattura attiva.
- Si applicano l'ambito di progetto in sola lettura, le restrizioni IP e i limiti di frequenza per credenziale esistenti. Non seguire mai un link fornito da un callback con il tuo token, a meno che la sua origine corrisponda all'host API configurato.
| Header | Presenza | Regola |
|---|---|---|
| Authorization | obbligatorio | Bearer YOUR_MERCHANT_API_TOKEN |
| Accept | consigliato | application/json |
| Parametro | Tipo / posizione | Regola |
|---|---|---|
| project_id | path UUID | Progetto assegnato a questa credenziale. |
| invoice_id | path UUID | invoice_id pubblico restituito alla creazione. |
| payment_method_id | optional query UUID | Limita a un solo metodo di pagamento della fattura. |
| limit | query integer | 1–100; valore predefinito 25. |
| offset | query integer | 0–1.000.000; valore predefinito 0. |
Richiesta
: "${WHOLLY_TOKEN:?Set WHOLLY_TOKEN to your server-side API token}"
curl --fail-with-body --max-time 30 \
--request GET \
--url "https://api.example.com/v1/projects/YOUR_PROJECT_ID/invoices/YOUR_PUBLIC_INVOICE_ID/payments?limit=25&offset=0" \
--header "Authorization: Bearer $WHOLLY_TOKEN" \
--header 'Accept: application/json'// Node.js 18+ · run on your server, never in browser code.
const token = process.env.WHOLLY_TOKEN;
if (!token) throw new Error("Set WHOLLY_TOKEN");
const response = await fetch("https://api.example.com/v1/projects/YOUR_PROJECT_ID/invoices/YOUR_PUBLIC_INVOICE_ID/payments?limit=25&offset=0", {
method: "GET",
headers: {
"Authorization": `Bearer ${token}`,
"Accept": "application/json"
},
redirect: "error",
signal: AbortSignal.timeout(30000)
});
if (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);
console.log(await response.json());<?php
// PHP 8+ with the cURL extension; run on your server.
$token = getenv('WHOLLY_TOKEN');
if (!$token) { throw new RuntimeException('Set WHOLLY_TOKEN'); }
$ch = curl_init("https://api.example.com/v1/projects/YOUR_PROJECT_ID/invoices/YOUR_PUBLIC_INVOICE_ID/payments?limit=25&offset=0");
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => 'GET',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_FOLLOWLOCATION => false,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTPHEADER => ['Authorization: Bearer ' . $token, "Accept: application/json"],
]);
$response = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if ($response === false) { throw new RuntimeException(curl_error($ch)); }
curl_close($ch);
if ($status < 200 || $status >= 300) { throw new RuntimeException("HTTP $status: $response"); }
print_r(json_decode($response, true, 512, JSON_THROW_ON_ERROR));# Python 3 · standard library; run on your server.
import json
import os
from urllib.request import Request, build_opener, HTTPRedirectHandler
class NoRedirect(HTTPRedirectHandler):
def redirect_request(self, req, fp, code, msg, headers, newurl):
return None
headers = {
"Accept": "application/json"
}
headers["Authorization"] = "Bearer " + os.environ["WHOLLY_TOKEN"]
request = Request("https://api.example.com/v1/projects/YOUR_PROJECT_ID/invoices/YOUR_PUBLIC_INVOICE_ID/payments?limit=25&offset=0",
method="GET", headers=headers)
# Non-2xx responses raise HTTPError. Do not retry writes with a new key.
with build_opener(NoRedirect()).open(request, timeout=30) as response:
print(json.load(response))Esempio di risposta · 200 application/json
{
"invoice_id": "11111111-2222-4333-8444-555555555555",
"data": [{
"payment_id": "44444444-4444-4444-8444-444444444444",
"payment_method_id": "33333333-3333-4333-8333-333333333333",
"transaction_id": "0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
"payment_hash": null,
"event_index": 12,
"payment_rail": "onchain",
"chain_slug": "ethereum",
"network": "mainnet",
"asset_id": "55555555-5555-4555-8555-555555555555",
"asset_key": "eip155:1/erc20:0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48",
"caip_asset_id": "eip155:1/erc20:0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48",
"symbol": "USDC",
"asset_decimals": 6,
"amount": "58.17342",
"amount_atomic": "58173420",
"status": "final",
"counts_towards_received": true,
"confirmations": 2,
"block_height": 25975377,
"observed_at": "2026-09-14T12:03:00Z",
"chain_time": "2026-09-14T12:02:48Z",
"finalized_at": "2026-09-14T12:04:00Z",
"explorer_name": "Etherscan",
"explorer_url": "https://etherscan.io/tx/0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
}],
"pagination": {"limit": 25, "offset": 0, "total": 1, "has_more": false}
}GETStruttura del checkout/Pubblico
Radice dell'host di checkout gestito che serve l'applicazione di checkout senza selezionare una fattura. Le integrazioni rivolte ai clienti dovrebbero normalmente usare links.checkout.
- Non serve un token bearer.
- Il punto di accesso del checkout gestito consente GET/HEAD e rifiuta gli altri metodi.
Richiesta
curl --fail-with-body --max-time 30 \
--request GET \
--url "https://pay.example.com/" \
--output 'checkout.html'// Node.js 18+ · run on your server, never in browser code.
import { writeFile } from "node:fs/promises";
const response = await fetch("https://pay.example.com/", {
method: "GET",
headers: {},
redirect: "error",
signal: AbortSignal.timeout(30000)
});
if (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);
await writeFile("checkout.html", Buffer.from(await response.arrayBuffer()));<?php
// PHP 8+ with the cURL extension; run on your server.
$ch = curl_init("https://pay.example.com/");
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => 'GET',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_FOLLOWLOCATION => false,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTPHEADER => [],
]);
$response = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if ($response === false) { throw new RuntimeException(curl_error($ch)); }
curl_close($ch);
if ($status < 200 || $status >= 300) { throw new RuntimeException("HTTP $status: $response"); }
file_put_contents("checkout.html", $response);# Python 3 · standard library; run on your server.
import json
from pathlib import Path
from urllib.request import Request, build_opener, HTTPRedirectHandler
class NoRedirect(HTTPRedirectHandler):
def redirect_request(self, req, fp, code, msg, headers, newurl):
return None
headers = {}
request = Request("https://pay.example.com/",
method="GET", headers=headers)
# Non-2xx responses raise HTTPError. Do not retry writes with a new key.
with build_opener(NoRedirect()).open(request, timeout=30) as response:
Path("checkout.html").write_bytes(response.read())Risposta di esempio · 200 text/html
<!doctype html>
<!-- Hosted Wholly Crypto checkout shell -->GETPagina checkout ospitata/invoice/{invoice_id}Pubblico
Checkout HTML rivolto ai clienti. La pagina recupera JSON sicuro per il checkout dallo stesso host. L'incorporamento è negato, salvo che il negozio lo abiliti e autorizzi esplicitamente l'origine HTTPS della pagina contenitrice.
- Nessun token bearer è accettato o necessario.
- La struttura HTML restituisce 200 anche se la fattura è assente; la sua richiesta JSON di checkout riceve poi invoice_not_found.
- La risposta è no-store, noindex e ha una CSP frame-ancestors specifica per la fattura.
- Un progetto/negozio disabilitato o una fattura sconosciuta non espone dati di checkout.
| Parametro | Tipo / posizione | Regola |
|---|---|---|
| invoice_id | path UUID | UUID pubblico della fattura restituito dall'API commerciante. |
Richiesta
curl --fail-with-body --max-time 30 \
--request GET \
--url "https://pay.example.com/invoice/YOUR_PUBLIC_INVOICE_ID" \
--output 'checkout.html'// Node.js 18+ · run on your server, never in browser code.
import { writeFile } from "node:fs/promises";
const response = await fetch("https://pay.example.com/invoice/YOUR_PUBLIC_INVOICE_ID", {
method: "GET",
headers: {},
redirect: "error",
signal: AbortSignal.timeout(30000)
});
if (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);
await writeFile("checkout.html", Buffer.from(await response.arrayBuffer()));<?php
// PHP 8+ with the cURL extension; run on your server.
$ch = curl_init("https://pay.example.com/invoice/YOUR_PUBLIC_INVOICE_ID");
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => 'GET',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_FOLLOWLOCATION => false,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTPHEADER => [],
]);
$response = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if ($response === false) { throw new RuntimeException(curl_error($ch)); }
curl_close($ch);
if ($status < 200 || $status >= 300) { throw new RuntimeException("HTTP $status: $response"); }
file_put_contents("checkout.html", $response);# Python 3 · standard library; run on your server.
import json
from pathlib import Path
from urllib.request import Request, build_opener, HTTPRedirectHandler
class NoRedirect(HTTPRedirectHandler):
def redirect_request(self, req, fp, code, msg, headers, newurl):
return None
headers = {}
request = Request("https://pay.example.com/invoice/YOUR_PUBLIC_INVOICE_ID",
method="GET", headers=headers)
# Non-2xx responses raise HTTPError. Do not retry writes with a new key.
with build_opener(NoRedirect()).open(request, timeout=30) as response:
Path("checkout.html").write_bytes(response.read())Risposta di esempio · 200 text/html
<!doctype html>
<!-- Hosted Wholly Crypto checkout application -->GETFattura con dati sicuri per il checkout/checkout-api/invoices/{invoice_id}Pubblico
Restituisce solo i campi necessari per mostrare il checkout. Omette intenzionalmente ID interni, email del cliente e campi indirizzo derivati, URL IPN, metadati del commerciante, ID dei wallet, percorsi di derivazione e diagnostica del monitor.
- Non serve un token bearer.
- Cache-Control è no-store e l'indicizzazione nei motori di ricerca è disabilitata.
- Tratta invoice_id come un dato che conferisce accesso al cliente; evita di pubblicarlo inutilmente.
- asset_icon_url è un asset locale della stessa origine; il checkout del cliente non deve mai contattare CoinGecko per mostrarlo.
- Quando destination_tag non è null, mostralo e copialo accanto all'indirizzo: è un destination tag XRP, memo ID Stellar o commento della fattura TON obbligatorio e deve essere inviato esattamente com'è.
- Per i token verificati, asset_kind è token, contract_address identifica l'esatto contratto ERC-20 o mint SPL, token_standard identifica il circuito e payment_uri include questa identità del token.
| Parametro | Tipo / posizione | Regola |
|---|---|---|
| invoice_id | path UUID | UUID pubblico della fattura. |
Fattura pubblica di checkout
| Campo | Tipo | Presenza | Descrizione |
|---|---|---|---|
| invoice_id | UUID | sempre | UUID pubblico della fattura. |
| order_id | string | null | sempre | Riferimento dell'ordine del commerciante. |
| description | string | null | sempre | Descrizione visibile al cliente. |
| amount | decimal string | sempre | Importo della fattura. |
| currency | string | sempre | Valuta della fattura. |
| exchange_rate_spread_percent | decimal string | sempre | Spread effettivo del preventivo fissato alla creazione, inclusa un'eventuale impostazione specifica della fattura. |
| underpayment_tolerance_percent | decimal string | sempre | Percentuale di ammanco accettata per questa fattura. |
| status | invoice status | sempre | Stato attuale della fattura. |
| amount_status | amount status | sempre | none, partial, paid o overpaid. Una fattura a importo zero esplicitamente consentita si salda con none e nessun metodo di pagamento. |
| timing_status | timing status | sempre | on_time o late. |
| sequence | integer | sempre | Sequenza dello stato attuale. |
| active_payment_method_id | UUID | null | sempre | Il metodo di pagamento elencato che ha ricevuto fondi. Il checkout rimane su questo metodo per evitare che un pagamento insufficiente prosegua con un asset incompatibile. |
| payment_method_locked | boolean | sempre | True dopo che un pagamento valido seleziona active_payment_method_id. |
| server_time | RFC 3339 timestamp | sempre | Ora del server rilevata per questa risposta; usala con expires_at per evitare gli scarti dell'orologio del dispositivo del cliente. |
| expires_at | RFC 3339 timestamp | sempre | Termine della fattura. |
| expires_in_seconds | integer | sempre | Secondi interi rimanenti a server_time, arrotondati per eccesso e limitati a un minimo di zero. |
| payment_open | boolean | sempre | True solo quando una fattura new o processing non è ancora scaduta e ha almeno un metodo pagabile con un importo residuo. |
| redirect_url | string | null | sempre | Destinazione di ritorno del cliente dopo il regolamento riuscito. |
| cancel_url | string | null | sempre | Destinazione di ritorno del cliente quando esce senza regolamento riuscito. |
| redirect_automatically | boolean | sempre | Politica di reindirizzamento automatico. |
| checkout_language | string | sempre | Lingua del checkout. |
| project | object | sempre | name, checkout_title, checkout_description, theme, accent_color e logo_url. |
| store | object | sempre | Nome pubblico del negozio. |
| appearance | CheckoutAppearance | sempre | Presentazione effettiva: impostazione specifica della fattura fissata se fornita, altrimenti il design attuale del negozio. Non modifica mai i campi finanziari o gli avvisi di sicurezza. |
| payment_methods | CheckoutPaymentMethod[] | sempre | Metodi di pagamento sicuri per il checkout. |
CheckoutAppearance
| Campo | Tipo | Presenza | Descrizione |
|---|---|---|---|
| inherit_default_store | boolean | sempre | True quando l'aspetto proviene dal negozio predefinito del progetto. False per i negozi indipendenti e le impostazioni specifiche fissate nelle fatture. |
| invoice_override | boolean | sempre | True quando checkout_appearance è stato fornito alla creazione della fattura. Se omesso/null, rimane false. |
| title / intro / outro | string | sempre | Intestazione del commerciante, messaggio superiore e messaggio inferiore in testo semplice. intro sostituisce customer_message; il vecchio testo memorizzato viene conservato. Non interpretarli mai come markup. |
| intro_font_size / outro_font_size | integer | sempre | Dimensioni dei caratteri in pixel: 12, 14, 16, 18, 20 o 24. |
| customer_message | string | sempre | Alias di compatibilità deprecato di intro. Usa intro per le nuove integrazioni. |
| theme | system | light | dim | dark | sempre | Preferenza del dispositivo del cliente o tema fisso. |
| accent_color / background_color / card_color / button_color | string | sempre | Colori rigorosamente #RRGGBB. I colori facoltativi sono vuoti per i valori automatici; il contrasto del primo piano viene calcolato. |
| logo_size / logo_alignment | string | sempre | small, medium o large; left o center. Le immagini vengono contenute, non ritagliate. |
| images | object | sempre | URL facoltativi logo_light, logo_dark e favicon: immagini PNG normalizzate, della stessa origine e limitate all'ambito autorizzato. |
| show_order_id / show_description / details_expanded | boolean | sempre | Visibilità dell'ID ordine, descrizione sotto il titolo ed espansione iniziale dell'ID ordine. L'importo resta visibile; sono controlli di visualizzazione, non di oscuramento dei dati. |
| show_project_name / show_store_name | boolean | sempre | Merchant 5.6.0+: visibilità del nome nell'intestazione. Entrambi hanno true come valore predefinito. L'identità del progetto/negozio resta disponibile nel JSON. |
| featured_chains / featured_asset_ids | array | sempre | Preferenze ordinate, applicate solo ai metodi già presenti nella fattura. I metodi mancanti o disabilitati vengono ignorati. |
| default_asset_id | UUID | null | sempre | Metodo iniziale suggerito. Hanno priorità una preferenza valida memorizzata del cliente o un metodo che sta già ricevendo fondi. |
| messages | object | sempre | Testo semplice en/de con chiavi waiting, confirming, paid, underpaid ed expired. Inglese come ripiego. Supplementare: non sostituisce mai lo stato effettivo. |
| support_email / support_url / terms_url / privacy_url | string | sempre | Contatto e link HTTPS facoltativi, senza credenziali negli URL. I link esterni si aprono in una nuova finestra. |
| return_button_text | string | sempre | Solo etichetta facoltativa. Le destinazioni di successo/annullamento e la politica di reindirizzamento appartengono comunque alla fattura. |
CheckoutPaymentMethod
| Campo | Tipo | Presenza | Descrizione |
|---|---|---|---|
| payment_rail | onchain | lightning | sempre | Lightning rimane un metodo Bitcoin, separato da BTC on-chain. Identifica la scelta tramite id dell'intento e circuito, non solo asset_id. |
| bolt11 | string | null | sempre | Richiesta Lightning firmata; null per i metodi on-chain. Non pagare mai dopo che payable diventa false. |
| payment_hash | string | null | sempre | Hash di pagamento Lightning per la riconciliazione, non un indirizzo di ricezione. Null per i metodi on-chain. |
| id | UUID | sempre | Identificatore dell'intento di pagamento. |
| asset_id | UUID | sempre | UUID dell'asset usato dalle preferenze di aspetto; distinto dall'id dell'intento di pagamento di questa fattura. |
| asset_key | string | sempre | Chiave canonica dell'asset. |
| chain_slug / chain_name | string | sempre | Nomi della blockchain per la macchina e per la visualizzazione. |
| network | string | sempre | Rete di pagamento. |
| caip_network_id | string | sempre | Identità canonica della rete usata per distinguere senza ambiguità la blockchain selezionata. |
| caip_asset_id | string | null | sempre | Identità canonica esatta dell'asset, incluso un contratto o mint di token verificato se pertinente. |
| asset_name / symbol | string | sempre | Valori di visualizzazione dell'asset di pagamento. |
| asset_icon_url | string | null | sempre | Icona dell'asset della stessa origine, memorizzata localmente, oppure null se non esiste un'associazione CoinGecko verificata. |
| asset_kind | native | token | sempre | Distingue la valuta nativa dal pagamento con contratto/mint. |
| contract_address | string | null | sempre | Contratto ERC-20 o mint SPL canonico per i token; null per la valuta nativa. |
| token_standard | erc20 | spl-token | null | sempre | Ambiente di esecuzione verificato del token, oppure null per la valuta nativa. |
| asset_decimals | integer | sempre | Precisione dell'unità atomica: 11 per i millisatoshi BTC Lightning, 8 per i satoshi BTC on-chain. |
| status | intent status | sempre | Stato attuale del metodo di pagamento. |
| payable | boolean | sempre | True solo quando questo preciso metodo può attualmente accettare un pagamento; false per i metodi inattivi dopo che un altro asset ha ricevuto fondi. |
| finality_mode / required_confirmations | string / integer | sempre | Politica di finalità. |
| expected_amount / expected_amount_atomic | decimal / integer string | sempre | Preventivo completo fissato, in unità di visualizzazione e unità on-chain effettive. Le stablecoin fiat riconosciute usano al massimo due decimali nel preventivo, sempre arrotondati per eccesso dopo lo spread; gli altri asset usano una precisione adattiva. I decimali effettivi del token, i fondi ricevuti e i residui dei pagamenti parziali restano esatti. Usa gli importi restituiti senza modificarli. |
| minimum_payment_amount / minimum_payment_amount_atomic | decimal / integer string | sempre | Soglia di regolamento accettata dopo l'applicazione della tolleranza sui pagamenti insufficienti. |
| received_amount / received_amount_atomic | decimal / integer string | sempre | Importo osservato. |
| remaining_amount | decimal string | sempre | Importo di visualizzazione esatto ancora necessario per raggiungere la soglia accettata, limitato a un minimo di zero. |
| remaining_amount_atomic | integer string | sempre | Ammanco rispetto alla soglia accettata in unità atomiche. Non è l'importo di pagamento richiesto: la tolleranza influisce solo sull'accettazione. |
| confirmed_amount / confirmed_amount_atomic | decimal / integer string | sempre | Importo confermato/finale. |
| destination_address / destination_tag | string / string|null | sempre | Destinazione on-chain e riferimento facoltativo. Per Lightning è l'hash di pagamento senza tag; paga invece tramite bolt11/payment_uri. |
| quote_expires_at | RFC 3339 timestamp | sempre | Scadenza del preventivo. |
| payment_uri | string | null | sempre | Richiesta adatta alla blockchain: ERC-681, Solana Pay, URI nativo o lightning:<bolt11>. Le richieste con importo usano l'intero importo previsto meno i fondi ricevuti, mai la soglia di tolleranza. Null quando payable è false, anche dopo l'accettazione di un ammanco tollerato. Il QR Lightning codifica l'intera richiesta Lightning, non l'hash di pagamento. |
| qr_url | path | null | sempre | Percorso QR SVG della stessa origine con revisione basata sulla sequenza e sul resto esatto, oppure null quando payable è false. L'SVG è no-store. |
| address_explorer_name / address_explorer_url | string|null | sempre | Explorer mainnet di ripiego convalidato dove supportato. |
| transaction_count | integer | sempre | Numero totale di transazioni pubbliche, valide e distinte osservate per questo metodo. |
| transactions_truncated | boolean | sempre | True quando transaction_count supera l'elenco delle transazioni recenti restituito. |
| transactions | CheckoutTransaction[] | sempre | Fino alle 10 transazioni pubbliche e valide più recenti. I totali ricevuti esatti restano indipendenti da questo limite di visualizzazione. |
CheckoutTransaction
| Campo | Tipo | Presenza | Descrizione |
|---|---|---|---|
| transaction_id | string | sempre | Identificatore della transazione osservata. |
| status | detected | confirming | final | sempre | Stato pubblico dell'osservazione. |
| confirmations | integer | sempre | Numero di conferme osservato. |
| block_height | integer | null | sempre | Altezza del blocco/ledger osservata. |
| explorer_name | string | se restituito | Nome fisso convalidato dell'explorer. |
| explorer_url | string | se restituito | URL mainnet fisso convalidato dell'explorer. |
Richiesta
curl --fail-with-body --max-time 30 \
--request GET \
--url "https://pay.example.com/checkout-api/invoices/YOUR_PUBLIC_INVOICE_ID" \
--header 'Accept: application/json'// Node.js 18+ · run on your server, never in browser code.
const response = await fetch("https://pay.example.com/checkout-api/invoices/YOUR_PUBLIC_INVOICE_ID", {
method: "GET",
headers: {
"Accept": "application/json"
},
redirect: "error",
signal: AbortSignal.timeout(30000)
});
if (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);
console.log(await response.json());<?php
// PHP 8+ with the cURL extension; run on your server.
$ch = curl_init("https://pay.example.com/checkout-api/invoices/YOUR_PUBLIC_INVOICE_ID");
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => 'GET',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_FOLLOWLOCATION => false,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTPHEADER => ["Accept: application/json"],
]);
$response = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if ($response === false) { throw new RuntimeException(curl_error($ch)); }
curl_close($ch);
if ($status < 200 || $status >= 300) { throw new RuntimeException("HTTP $status: $response"); }
print_r(json_decode($response, true, 512, JSON_THROW_ON_ERROR));# Python 3 · standard library; run on your server.
import json
from urllib.request import Request, build_opener, HTTPRedirectHandler
class NoRedirect(HTTPRedirectHandler):
def redirect_request(self, req, fp, code, msg, headers, newurl):
return None
headers = {
"Accept": "application/json"
}
request = Request("https://pay.example.com/checkout-api/invoices/YOUR_PUBLIC_INVOICE_ID",
method="GET", headers=headers)
# Non-2xx responses raise HTTPError. Do not retry writes with a new key.
with build_opener(NoRedirect()).open(request, timeout=30) as response:
print(json.load(response))Esempio di risposta · 200 application/json
{
"data": {
"invoice_id": "0a6a98db-d93d-48ee-8c3c-fd45f90c4a50",
"order_id": "order-1042",
"description": "Annual plan",
"amount": "49.9",
"currency": "USD",
"exchange_rate_spread_percent": "0.5",
"underpayment_tolerance_percent": "1",
"status": "processing",
"amount_status": "partial",
"timing_status": "on_time",
"sequence": 3,
"active_payment_method_id": "33333333-3333-4333-8333-333333333333",
"payment_method_locked": true,
"server_time": "2026-08-31T18:10:00Z",
"expires_at": "2026-08-31T18:15:00Z",
"expires_in_seconds": 300,
"payment_open": true,
"redirect_url": "https://merchant.example/orders/1042",
"cancel_url": "https://merchant.example/cart",
"redirect_automatically": true,
"checkout_language": "en",
"project": {
"name": "Example project",
"checkout_title": "Complete your payment",
"checkout_description": "Send the exact amount shown.",
"theme": "system",
"accent_color": "#42e39b",
"logo_url": "/checkout-api/invoices/…/logo/…/image.png"
},
"store": { "name": "Online shop" },
"payment_methods": [
{
"id": "33333333-3333-4333-8333-333333333333",
"asset_key": "bip122:000000000019d6689c085ae165831e93/slip44:0",
"chain_slug": "bitcoin",
"chain_name": "Bitcoin",
"network": "mainnet",
"caip_network_id": "bip122:000000000019d6689c085ae165831e93",
"caip_asset_id": "bip122:000000000019d6689c085ae165831e93/slip44:0",
"asset_name": "Bitcoin",
"symbol": "BTC",
"asset_icon_url": "/assets/coingecko/bitcoin.png",
"asset_kind": "native",
"contract_address": null,
"token_standard": null,
"asset_decimals": 8,
"status": "partial",
"payable": true,
"finality_mode": "confirmations",
"required_confirmations": 1,
"expected_amount": "0.00046",
"expected_amount_atomic": "46000",
"minimum_payment_amount": "0.0004554",
"minimum_payment_amount_atomic": "45540",
"received_amount": "0.0002",
"received_amount_atomic": "20000",
"remaining_amount": "0.0002554",
"remaining_amount_atomic": "25540",
"confirmed_amount": "0",
"confirmed_amount_atomic": "0",
"destination_address": "bc1q…example",
"destination_tag": null,
"quote_expires_at": "2026-08-31T18:15:00Z",
"payment_uri": "bitcoin:bc1q…example?amount=0.00026",
"qr_url": "/checkout-api/invoices/…/payment-methods/…/qr.svg?sequence=3&amount_atomic=26000",
"address_explorer_name": "mempool.space",
"address_explorer_url": "https://mempool.space/address/…",
"transaction_count": 0,
"transactions_truncated": false,
"transactions": []
}
]
}
}GETAnteprima checkout del negozio/invoice/preview/{project_id}Pubblico
Mostra l'aspetto salvato del negozio con un importo illustrativo e metadati reali degli asset accettati. Passa tra gli esempi waiting, confirming, paid, underpaid ed expired senza creare pagamenti.
- L'anteprima riguarda solo l'identità visiva e non deve mai essere inviata a un cliente come richiesta di pagamento.
- Nessun indirizzo di ricezione, QR pagabile, azione del wallet, reindirizzamento o polling dei pagamenti. Gli esempi non cambiano lo stato effettivo della fattura.
- La risposta è no-store, noindex e non può essere incorporata.
| Parametro | Tipo / posizione | Regola |
|---|---|---|
| project_id | path UUID | UUID del progetto copiato nel link di anteprima dalla console autenticata. |
| store_id | query UUID, optional | Negozio appartenente a questo progetto. Ometti per usare il suo primo negozio/predefinito. |
| state | query string, optional | waiting, confirming, paid, underpaid o expired. Esempio solo nel browser. |
Richiesta
curl --fail-with-body --max-time 30 \
--request GET \
--url "https://pay.example.com/invoice/preview/YOUR_PROJECT_ID?store_id=YOUR_STORE_ID&state=confirming" \
--output 'checkout-preview.html'// Node.js 18+ · run on your server, never in browser code.
import { writeFile } from "node:fs/promises";
const response = await fetch("https://pay.example.com/invoice/preview/YOUR_PROJECT_ID?store_id=YOUR_STORE_ID&state=confirming", {
method: "GET",
headers: {},
redirect: "error",
signal: AbortSignal.timeout(30000)
});
if (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);
await writeFile("checkout-preview.html", Buffer.from(await response.arrayBuffer()));<?php
// PHP 8+ with the cURL extension; run on your server.
$ch = curl_init("https://pay.example.com/invoice/preview/YOUR_PROJECT_ID?store_id=YOUR_STORE_ID&state=confirming");
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => 'GET',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_FOLLOWLOCATION => false,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTPHEADER => [],
]);
$response = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if ($response === false) { throw new RuntimeException(curl_error($ch)); }
curl_close($ch);
if ($status < 200 || $status >= 300) { throw new RuntimeException("HTTP $status: $response"); }
file_put_contents("checkout-preview.html", $response);# Python 3 · standard library; run on your server.
import json
from pathlib import Path
from urllib.request import Request, build_opener, HTTPRedirectHandler
class NoRedirect(HTTPRedirectHandler):
def redirect_request(self, req, fp, code, msg, headers, newurl):
return None
headers = {}
request = Request("https://pay.example.com/invoice/preview/YOUR_PROJECT_ID?store_id=YOUR_STORE_ID&state=confirming",
method="GET", headers=headers)
# Non-2xx responses raise HTTPError. Do not retry writes with a new key.
with build_opener(NoRedirect()).open(request, timeout=30) as response:
Path("checkout-preview.html").write_bytes(response.read())Risposta di esempio · 200 text/html
<!doctype html>
<!-- Hosted branding preview; no invoice is created -->GETDati di anteprima checkout/checkout-api/previews/{project_id}Pubblico
Restituisce l'aspetto effettivo del negozio e metadati sicuri degli asset accettati. payment_methods rimane vuoto; preview_methods non contiene indirizzi di pagamento, preventivi o dati privati del wallet.
- Nessun token bearer è accettato o necessario.
- Non viene restituita alcuna fattura, destinazione, wallet, transazione, IPN, webhook o metadato del commerciante.
- Usa la console autenticata per ottenere il corretto link di anteprima sul dominio pay.
| Parametro | Tipo / posizione | Regola |
|---|---|---|
| project_id | path UUID | UUID del progetto dal link di anteprima della console. |
| store_id | query UUID, optional | Deve appartenere a questo progetto; ID non corrispondenti restituiscono 404. I campi di query sconosciuti vengono rifiutati. |
CheckoutAppearance
| Campo | Tipo | Presenza | Descrizione |
|---|---|---|---|
| inherit_default_store | boolean | sempre | True quando l'aspetto proviene dal negozio predefinito del progetto. False per i negozi indipendenti e le impostazioni specifiche fissate nelle fatture. |
| invoice_override | boolean | sempre | True quando checkout_appearance è stato fornito alla creazione della fattura. Se omesso/null, rimane false. |
| title / intro / outro | string | sempre | Intestazione del commerciante, messaggio superiore e messaggio inferiore in testo semplice. intro sostituisce customer_message; il vecchio testo memorizzato viene conservato. Non interpretarli mai come markup. |
| intro_font_size / outro_font_size | integer | sempre | Dimensioni dei caratteri in pixel: 12, 14, 16, 18, 20 o 24. |
| customer_message | string | sempre | Alias di compatibilità deprecato di intro. Usa intro per le nuove integrazioni. |
| theme | system | light | dim | dark | sempre | Preferenza del dispositivo del cliente o tema fisso. |
| accent_color / background_color / card_color / button_color | string | sempre | Colori rigorosamente #RRGGBB. I colori facoltativi sono vuoti per i valori automatici; il contrasto del primo piano viene calcolato. |
| logo_size / logo_alignment | string | sempre | small, medium o large; left o center. Le immagini vengono contenute, non ritagliate. |
| images | object | sempre | URL facoltativi logo_light, logo_dark e favicon: immagini PNG normalizzate, della stessa origine e limitate all'ambito autorizzato. |
| show_order_id / show_description / details_expanded | boolean | sempre | Visibilità dell'ID ordine, descrizione sotto il titolo ed espansione iniziale dell'ID ordine. L'importo resta visibile; sono controlli di visualizzazione, non di oscuramento dei dati. |
| show_project_name / show_store_name | boolean | sempre | Merchant 5.6.0+: visibilità del nome nell'intestazione. Entrambi hanno true come valore predefinito. L'identità del progetto/negozio resta disponibile nel JSON. |
| featured_chains / featured_asset_ids | array | sempre | Preferenze ordinate, applicate solo ai metodi già presenti nella fattura. I metodi mancanti o disabilitati vengono ignorati. |
| default_asset_id | UUID | null | sempre | Metodo iniziale suggerito. Hanno priorità una preferenza valida memorizzata del cliente o un metodo che sta già ricevendo fondi. |
| messages | object | sempre | Testo semplice en/de con chiavi waiting, confirming, paid, underpaid ed expired. Inglese come ripiego. Supplementare: non sostituisce mai lo stato effettivo. |
| support_email / support_url / terms_url / privacy_url | string | sempre | Contatto e link HTTPS facoltativi, senza credenziali negli URL. I link esterni si aprono in una nuova finestra. |
| return_button_text | string | sempre | Solo etichetta facoltativa. Le destinazioni di successo/annullamento e la politica di reindirizzamento appartengono comunque alla fattura. |
Richiesta
curl --fail-with-body --max-time 30 \
--request GET \
--url "https://pay.example.com/checkout-api/previews/YOUR_PROJECT_ID?store_id=YOUR_STORE_ID" \
--header 'Accept: application/json'// Node.js 18+ · run on your server, never in browser code.
const response = await fetch("https://pay.example.com/checkout-api/previews/YOUR_PROJECT_ID?store_id=YOUR_STORE_ID", {
method: "GET",
headers: {
"Accept": "application/json"
},
redirect: "error",
signal: AbortSignal.timeout(30000)
});
if (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);
console.log(await response.json());<?php
// PHP 8+ with the cURL extension; run on your server.
$ch = curl_init("https://pay.example.com/checkout-api/previews/YOUR_PROJECT_ID?store_id=YOUR_STORE_ID");
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => 'GET',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_FOLLOWLOCATION => false,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTPHEADER => ["Accept: application/json"],
]);
$response = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if ($response === false) { throw new RuntimeException(curl_error($ch)); }
curl_close($ch);
if ($status < 200 || $status >= 300) { throw new RuntimeException("HTTP $status: $response"); }
print_r(json_decode($response, true, 512, JSON_THROW_ON_ERROR));# Python 3 · standard library; run on your server.
import json
from urllib.request import Request, build_opener, HTTPRedirectHandler
class NoRedirect(HTTPRedirectHandler):
def redirect_request(self, req, fp, code, msg, headers, newurl):
return None
headers = {
"Accept": "application/json"
}
request = Request("https://pay.example.com/checkout-api/previews/YOUR_PROJECT_ID?store_id=YOUR_STORE_ID",
method="GET", headers=headers)
# Non-2xx responses raise HTTPError. Do not retry writes with a new key.
with build_opener(NoRedirect()).open(request, timeout=30) as response:
print(json.load(response))Esempio di risposta · 200 application/json
{
"data": {
"preview": true,
"invoice_id": "YOUR_PROJECT_ID",
"amount": "100.00",
"currency": "USD",
"project": {
"name": "Example project",
"checkout_title": "Complete your payment",
"checkout_description": "Choose a network and send the exact amount shown.",
"theme": "system",
"accent_color": "#42e39b",
"logo_url": "/checkout-api/previews/…/logo/…/image.png"
},
"appearance": {"inherit_default_store": true, "theme": "system", "accent_color": "#42E39B", "images": {}},
"preview_methods": [],
"payment_methods": []
}
}GETImmagine checkout del negozio/checkout-api/invoices/{invoice_id}/appearance-images/{kind}/{revision}/image.pngPubblico
Restituisce un logo o una favicon normalizzati del negozio appartenenti a questa fattura. Usa gli URL appearance.images dei dati di checkout.
- Usa appearance.images dal JSON di checkout. Le immagini fissate nella fattura continuano a funzionare dopo che il negozio di origine sostituisce o rimuove un caricamento. Le revisioni rimosse esplicitamente, associate alla fattura o al tipo sbagliati e sconosciute restituiscono 404; un'istantanea non ripiega mai sull'immagine attuale del negozio.
- Senza un'impostazione specifica della fattura, viene usata l'immagine effettiva attuale del negozio e le revisioni sostituite/rimosse restituiscono 404. Solo PNG, nosniff e cache privata.
- Il caricamento delle immagini del negozio accetta PNG, JPEG o WebP entro i limiti previsti nella console autenticata; mai SVG, HTML o URL di immagini remote.
| Parametro | Tipo / posizione | Regola |
|---|---|---|
| invoice_id | path UUID | UUID pubblico della fattura. |
| kind | path enum | logo_light, logo_dark o favicon. |
| revision | path UUID | Revisione attuale dell'immagine. |
Richiesta
curl --fail-with-body --max-time 30 \
--request GET \
--url "https://pay.example.com/checkout-api/invoices/YOUR_PUBLIC_INVOICE_ID/appearance-images/logo_light/YOUR_IMAGE_REVISION/image.png" \
--output 'store-logo.png'// Node.js 18+ · run on your server, never in browser code.
import { writeFile } from "node:fs/promises";
const response = await fetch("https://pay.example.com/checkout-api/invoices/YOUR_PUBLIC_INVOICE_ID/appearance-images/logo_light/YOUR_IMAGE_REVISION/image.png", {
method: "GET",
headers: {},
redirect: "error",
signal: AbortSignal.timeout(30000)
});
if (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);
await writeFile("store-logo.png", Buffer.from(await response.arrayBuffer()));<?php
// PHP 8+ with the cURL extension; run on your server.
$ch = curl_init("https://pay.example.com/checkout-api/invoices/YOUR_PUBLIC_INVOICE_ID/appearance-images/logo_light/YOUR_IMAGE_REVISION/image.png");
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => 'GET',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_FOLLOWLOCATION => false,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTPHEADER => [],
]);
$response = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if ($response === false) { throw new RuntimeException(curl_error($ch)); }
curl_close($ch);
if ($status < 200 || $status >= 300) { throw new RuntimeException("HTTP $status: $response"); }
file_put_contents("store-logo.png", $response);# Python 3 · standard library; run on your server.
import json
from pathlib import Path
from urllib.request import Request, build_opener, HTTPRedirectHandler
class NoRedirect(HTTPRedirectHandler):
def redirect_request(self, req, fp, code, msg, headers, newurl):
return None
headers = {}
request = Request("https://pay.example.com/checkout-api/invoices/YOUR_PUBLIC_INVOICE_ID/appearance-images/logo_light/YOUR_IMAGE_REVISION/image.png",
method="GET", headers=headers)
# Non-2xx responses raise HTTPError. Do not retry writes with a new key.
with build_opener(NoRedirect()).open(request, timeout=30) as response:
Path("store-logo.png").write_bytes(response.read())Risposta di esempio · 200 image/png
(binary PNG response)GETImmagine di anteprima del negozio/checkout-api/previews/{project_id}/stores/{store_id}/appearance-images/{kind}/{revision}/image.pngPubblico
Restituisce un'immagine di anteprima normalizzata solo per progetto, negozio, tipo e revisione attuale corrispondenti.
- Usa appearance.images dai dati di anteprima. Gli ID sconosciuti o non corrispondenti restituiscono 404. Non viene esposta alcuna informazione su wallet o pagamenti.
| Parametro | Tipo / posizione | Regola |
|---|---|---|
| project_id | path UUID | UUID del progetto. |
| store_id | path UUID | Negozio appartenente al progetto. |
| kind | path enum | logo_light, logo_dark o favicon. |
| revision | path UUID | Revisione attuale dell'immagine. |
Richiesta
curl --fail-with-body --max-time 30 \
--request GET \
--url "https://pay.example.com/checkout-api/previews/YOUR_PROJECT_ID/stores/YOUR_STORE_ID/appearance-images/logo_light/YOUR_IMAGE_REVISION/image.png" \
--output 'store-preview-logo.png'// Node.js 18+ · run on your server, never in browser code.
import { writeFile } from "node:fs/promises";
const response = await fetch("https://pay.example.com/checkout-api/previews/YOUR_PROJECT_ID/stores/YOUR_STORE_ID/appearance-images/logo_light/YOUR_IMAGE_REVISION/image.png", {
method: "GET",
headers: {},
redirect: "error",
signal: AbortSignal.timeout(30000)
});
if (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);
await writeFile("store-preview-logo.png", Buffer.from(await response.arrayBuffer()));<?php
// PHP 8+ with the cURL extension; run on your server.
$ch = curl_init("https://pay.example.com/checkout-api/previews/YOUR_PROJECT_ID/stores/YOUR_STORE_ID/appearance-images/logo_light/YOUR_IMAGE_REVISION/image.png");
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => 'GET',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_FOLLOWLOCATION => false,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTPHEADER => [],
]);
$response = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if ($response === false) { throw new RuntimeException(curl_error($ch)); }
curl_close($ch);
if ($status < 200 || $status >= 300) { throw new RuntimeException("HTTP $status: $response"); }
file_put_contents("store-preview-logo.png", $response);# Python 3 · standard library; run on your server.
import json
from pathlib import Path
from urllib.request import Request, build_opener, HTTPRedirectHandler
class NoRedirect(HTTPRedirectHandler):
def redirect_request(self, req, fp, code, msg, headers, newurl):
return None
headers = {}
request = Request("https://pay.example.com/checkout-api/previews/YOUR_PROJECT_ID/stores/YOUR_STORE_ID/appearance-images/logo_light/YOUR_IMAGE_REVISION/image.png",
method="GET", headers=headers)
# Non-2xx responses raise HTTPError. Do not retry writes with a new key.
with build_opener(NoRedirect()).open(request, timeout=30) as response:
Path("store-preview-logo.png").write_bytes(response.read())Risposta di esempio · 200 image/png
(binary PNG response)GETLogo di anteprima con revisione/checkout-api/previews/{project_id}/logo/{revision}/image.pngPubblico
Restituisce il logo normalizzato del progetto solo quando il progetto e la revisione del logo sicura per la cache corrispondono. Usa project.logo_url dai dati di anteprima invece di costruire questo URL.
- I progetti sconosciuti e le revisioni obsolete del logo restituiscono invoice_not_found senza rivelare quale componente fosse assente.
- L'immagine con revisione restituita con successo è immutabile e può essere memorizzata nella cache.
| Parametro | Tipo / posizione | Regola |
|---|---|---|
| project_id | path UUID | UUID del progetto. |
| revision | path UUID | Revisione attuale del logo di checkout restituita in project.logo_url. |
Richiesta
curl --fail-with-body --max-time 30 \
--request GET \
--url "https://pay.example.com/checkout-api/previews/YOUR_PROJECT_ID/logo/YOUR_LOGO_REVISION/image.png" \
--output 'checkout-preview-logo.png'// Node.js 18+ · run on your server, never in browser code.
import { writeFile } from "node:fs/promises";
const response = await fetch("https://pay.example.com/checkout-api/previews/YOUR_PROJECT_ID/logo/YOUR_LOGO_REVISION/image.png", {
method: "GET",
headers: {},
redirect: "error",
signal: AbortSignal.timeout(30000)
});
if (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);
await writeFile("checkout-preview-logo.png", Buffer.from(await response.arrayBuffer()));<?php
// PHP 8+ with the cURL extension; run on your server.
$ch = curl_init("https://pay.example.com/checkout-api/previews/YOUR_PROJECT_ID/logo/YOUR_LOGO_REVISION/image.png");
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => 'GET',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_FOLLOWLOCATION => false,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTPHEADER => [],
]);
$response = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if ($response === false) { throw new RuntimeException(curl_error($ch)); }
curl_close($ch);
if ($status < 200 || $status >= 300) { throw new RuntimeException("HTTP $status: $response"); }
file_put_contents("checkout-preview-logo.png", $response);# Python 3 · standard library; run on your server.
import json
from pathlib import Path
from urllib.request import Request, build_opener, HTTPRedirectHandler
class NoRedirect(HTTPRedirectHandler):
def redirect_request(self, req, fp, code, msg, headers, newurl):
return None
headers = {}
request = Request("https://pay.example.com/checkout-api/previews/YOUR_PROJECT_ID/logo/YOUR_LOGO_REVISION/image.png",
method="GET", headers=headers)
# Non-2xx responses raise HTTPError. Do not retry writes with a new key.
with build_opener(NoRedirect()).open(request, timeout=30) as response:
Path("checkout-preview-logo.png").write_bytes(response.read())Risposta di esempio · 200 image/png
(binary PNG response)GETImmagine QR di pagamento/checkout-api/invoices/{invoice_id}/payment-methods/{intent_id}/qr.svgPubblico
Genera un QR SVG di 512×512 per l'esatto payload di pagamento specifico della blockchain di un metodo di pagamento della fattura.
- Non serve un token bearer.
- Usa qr_url con revisione basata sulla sequenza e sul resto restituito dal JSON di checkout; l'SVG è privato e no-store.
- Dopo un pagamento parziale richiede l'esatto importo residuo e rimane bloccato su quell'asset.
- Restituisce 409 dopo la scadenza, il completamento o quando è attivo un altro metodo; restituisce payment_qr_unavailable (422) se la richiesta è troppo grande da codificare.
| Parametro | Tipo / posizione | Regola |
|---|---|---|
| invoice_id | path UUID | UUID pubblico della fattura. |
| intent_id | path UUID | id del metodo di pagamento dal JSON di checkout. |
Richiesta
curl --fail-with-body --max-time 30 \
--request GET \
--url "https://pay.example.com/checkout-api/invoices/YOUR_PUBLIC_INVOICE_ID/payment-methods/YOUR_INTENT_ID/qr.svg" \
--output 'payment-qr.svg'// Node.js 18+ · run on your server, never in browser code.
import { writeFile } from "node:fs/promises";
const response = await fetch("https://pay.example.com/checkout-api/invoices/YOUR_PUBLIC_INVOICE_ID/payment-methods/YOUR_INTENT_ID/qr.svg", {
method: "GET",
headers: {},
redirect: "error",
signal: AbortSignal.timeout(30000)
});
if (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);
await writeFile("payment-qr.svg", Buffer.from(await response.arrayBuffer()));<?php
// PHP 8+ with the cURL extension; run on your server.
$ch = curl_init("https://pay.example.com/checkout-api/invoices/YOUR_PUBLIC_INVOICE_ID/payment-methods/YOUR_INTENT_ID/qr.svg");
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => 'GET',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_FOLLOWLOCATION => false,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTPHEADER => [],
]);
$response = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if ($response === false) { throw new RuntimeException(curl_error($ch)); }
curl_close($ch);
if ($status < 200 || $status >= 300) { throw new RuntimeException("HTTP $status: $response"); }
file_put_contents("payment-qr.svg", $response);# Python 3 · standard library; run on your server.
import json
from pathlib import Path
from urllib.request import Request, build_opener, HTTPRedirectHandler
class NoRedirect(HTTPRedirectHandler):
def redirect_request(self, req, fp, code, msg, headers, newurl):
return None
headers = {}
request = Request("https://pay.example.com/checkout-api/invoices/YOUR_PUBLIC_INVOICE_ID/payment-methods/YOUR_INTENT_ID/qr.svg",
method="GET", headers=headers)
# Non-2xx responses raise HTTPError. Do not retry writes with a new key.
with build_opener(NoRedirect()).open(request, timeout=30) as response:
Path("payment-qr.svg").write_bytes(response.read())Risposta di esempio · 200 image/svg+xml
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512">…</svg>GETLogo checkout con revisione/checkout-api/invoices/{invoice_id}/logo/{revision}/image.pngPubblico
Restituisce il logo di checkout normalizzato del progetto solo quando la fattura e la revisione attuale del logo corrispondono. Preferisci project.logo_url restituito dal JSON di checkout invece di costruire questa rotta.
- Non serve un token bearer.
- La durata della cache pubblica è di un anno con immutable, perché la revisione identifica lo stato in base al contenuto.
- Le revisioni sconosciute/non corrispondenti restituiscono invoice_not_found.
| Parametro | Tipo / posizione | Regola |
|---|---|---|
| invoice_id | path UUID | UUID pubblico della fattura. |
| revision | path UUID | Revisione attuale del logo di checkout inclusa in project.logo_url. |
Richiesta
curl --fail-with-body --max-time 30 \
--request GET \
--url "https://pay.example.com/checkout-api/invoices/YOUR_PUBLIC_INVOICE_ID/logo/YOUR_LOGO_REVISION/image.png" \
--output 'checkout-logo.png'// Node.js 18+ · run on your server, never in browser code.
import { writeFile } from "node:fs/promises";
const response = await fetch("https://pay.example.com/checkout-api/invoices/YOUR_PUBLIC_INVOICE_ID/logo/YOUR_LOGO_REVISION/image.png", {
method: "GET",
headers: {},
redirect: "error",
signal: AbortSignal.timeout(30000)
});
if (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);
await writeFile("checkout-logo.png", Buffer.from(await response.arrayBuffer()));<?php
// PHP 8+ with the cURL extension; run on your server.
$ch = curl_init("https://pay.example.com/checkout-api/invoices/YOUR_PUBLIC_INVOICE_ID/logo/YOUR_LOGO_REVISION/image.png");
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => 'GET',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_FOLLOWLOCATION => false,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTPHEADER => [],
]);
$response = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if ($response === false) { throw new RuntimeException(curl_error($ch)); }
curl_close($ch);
if ($status < 200 || $status >= 300) { throw new RuntimeException("HTTP $status: $response"); }
file_put_contents("checkout-logo.png", $response);# Python 3 · standard library; run on your server.
import json
from pathlib import Path
from urllib.request import Request, build_opener, HTTPRedirectHandler
class NoRedirect(HTTPRedirectHandler):
def redirect_request(self, req, fp, code, msg, headers, newurl):
return None
headers = {}
request = Request("https://pay.example.com/checkout-api/invoices/YOUR_PUBLIC_INVOICE_ID/logo/YOUR_LOGO_REVISION/image.png",
method="GET", headers=headers)
# Non-2xx responses raise HTTPError. Do not retry writes with a new key.
with build_opener(NoRedirect()).open(request, timeout=30) as response:
Path("checkout-logo.png").write_bytes(response.read())Risposta di esempio · 200 image/png
(binary PNG response)Riferimento per Wholly Crypto 7.5.5. Per la versione installata, apri Impostazioni → Accesso API → Documentazione nella tua console. Vedi le versioni.