Consenti alle app approvate di addebitare i tuoi Vito con conferma via PIN e gestisci le richieste di pagamento dalla tua pagina Acquisti.
Richiede un abbonamento Silver o superiore — sia per candidarsi sia per ogni chiamata autenticata. Se l’abbonamento del titolare della chiave scade, il progetto viene congelato finché non si riabbona.
Un’API REST che permette alla tua applicazione di operare sul saldo Vito di un utente da dentro Discord: leggerlo, addebitarlo, accreditarlo o spostarlo tra utenti. Tutti gli endpoint restituiscono JSON e sono versionati sotto /v1.
I Vito non si convertono mai in denaro reale, né viceversa. Si spostano soltanto tra saldi Vetox.
La chiave e il segreto di firma vengono mostrati esattamente una volta. Dopo l’approvazione hai una finestra di 7 giorni per rivelarli dalla scheda delle chiavi API. Vetox conserva solo un hash e non può mostrarli di nuovo: se perdi la finestra devi effettuare una rotazione.
Tienila solo lato server — chiunque la possieda può addebitare i tuoi utenti
Ruota dalla scheda delle chiavi API. La chiave precedente resta valida per un periodo di tolleranza di 24 ore, così puoi rilasciare senza interruzioni
Il segreto di firma dei webhook si ruota separatamente, con una sovrapposizione di 24 ore propria
La tua chiave da sola non può spostare i Vito di un utente. Ogni addebito richiede l’approvazione dell’utente con il PIN del suo portafoglio, su vetox.io — mai dentro la tua app né dentro Discord.
1
La tua app chiama POST /v1/deduct
Con l’utente, l’importo, il guildId di origine e i dettagli dell’articolo.
2
Vito restituisce una confirmUrl
Una conferma in sospeso, valida 10 minuti. L’utente riceve anche un MP.
3
L'utente approva con il suo PIN
Su vetox.io.
4
Vito liquida e notifica
Il saldo viene addebitato, la transazione registrata e viene inviato un webhook firmato, se ne hai configurato uno.
5
La tua app verifica e completa
Controlla la firma, poi sblocca il contenuto o consegna l’articolo.
Completa la tua azione solo su confirmation.completed — mai sulla risposta di /deduct. A quel punto l’addebito non è ancora definitivo.
POST /v1/add accredita Vito a un utente dal tuo saldo — per premi o rimborsi. Stessi campi di /deduct tranne guildId e product.
A differenza di un addebito, un accredito non ha una fase di conferma: viene liquidato subito. Richiede lo scope credit:create e saldo sufficiente, altrimenti la chiamata restituisce 402 VITO_INSUFFICIENT_OWNER_FUNDS.
Ogni addebito ti viene liquidato al netto della commissione della piattaforma, secondo lo stesso schema dei trasferimenti Vito nell’app e in base al tuo livello di abbonamento:
Il tuo abbonamento
Commissione
Normal, Silver, Gold
7 %
Platinum
6 %
Diamond
5 %
Gli importi di 5 Vito o meno sono esenti da commissioni, e gli accrediti tramite /v1/add lo sono sempre.
Aggiungi uno o più URL di callback https nella scheda delle impostazioni. Vito invia un POST firmato ogni volta che una conferma raggiunge uno stato finale.
I webhook partono solo se il progetto ha sia un URL di callback sia un segreto di firma. Rivela il segreto (whsec_…) una sola volta dalla scheda delle chiavi API.
Accetta la consegna se corrisponde una qualsiasi delle h1. Un verificatore che legge solo la prima rifiuterà ogni webhook finché non avrà rilasciato il nuovo segreto, vanificando lo scopo della sovrapposizione di 24 ore.
Ricalcola l’HMAC su <ts>:<rawBody> con il tuo segreto di firma e confronta a tempo costante.
const crypto = require('crypto');function verify(rawBody, header, secret) { const parts = header.split(';'); const ts = parts.find(p => p.startsWith('ts='))?.slice(3); const sigs = parts.filter(p => p.startsWith('h1=')).map(p => p.slice(3)); if (!ts || sigs.length === 0) return false; // Reject anything older than ~5 minutes — replay protection. if (Math.abs(Date.now() / 1000 - Number(ts)) > 300) return false; const expected = Buffer.from( crypto.createHmac('sha256', secret).update(`${ts}:${rawBody}`).digest('hex'), ); // Any matching signature is valid — a rotation emits several. return sigs.some(sig => { const actual = Buffer.from(sig); // timingSafeEqual throws when the lengths differ, so check first. return ( actual.length === expected.length && crypto.timingSafeEqual(actual, expected) ); });}
Verifica sul corpo grezzo, prima di qualunque parsing JSON o middleware che lo riscriva.
Esiste anche un limite per IP pari alla metà della tua quota al minuto, con un minimo di 30.
Sotto carico gli endpoint di scrittura falliscono in modo chiuso — un addebito viene rifiutato anziché rischiare una doppia spesa. Le letture falliscono in modo aperto. Tratta una scrittura rifiutata come «non avvenuta» e riprova.
Invia un header Idempotency-Key per deduplicare i tentativi in sicurezza.