Przegląd
Vito API pozwala Twojej aplikacji pracować z saldem Vito użytkownika z poziomu Discorda — odczytywać je, obciążać lub uznawać z powrotem. Użytkownik potwierdza każde obciążenie swoim PIN-em na vetox.io, zanim jakiekolwiek Vito zostanie przeniesione, a Twoja aplikacja jest powiadamiana o wyniku. Vito to wewnętrzna wirtualna waluta ekosystemu Vetox; używaj API, aby budować doświadczenia oparte na Vito — odblokowywanie treści, funkcji, korzyści lub nagród. Nie odbywa się żadna sprzedaż ani zakup za prawdziwe pieniądze.
To API REST uwierzytelniane tajnym kluczem. Wszystkie punkty końcowe zwracają JSON i są wersjonowane pod /v1.
https://api.vetox.io/public/vito/v1Salda i rozliczenie
Twój klucz API jest powiązany z Twoim kontem Vetox. Każde obciążenie jest rozliczane na Twoją korzyść: gdy Twoja aplikacja odejmuje Vito użytkownikowi, te Vito są księgowane na Twoim własnym saldzie (po potrąceniu opłaty platformy) i nigdy nie są niszczone. Gdy Twoja aplikacja dodaje Vito użytkownikowi, jest to finansowane z Twojego salda.
Vito zawsze przepływa między saldami — nigdy do ani z prawdziwych pieniędzy. Użytkownik jest obciążany pełną kwotą, o którą prosisz; Ty otrzymujesz kwotę po opłacie platformy; uznania i nagrody są finansowane z Twojego salda przez /add.
Wymagania
Dostęp do Vito API jest przyznawany dla każdego projektu. Zanim będziesz mógł je wywołać, potrzebujesz:
- Aktywnego członkostwa użytkownika (poziom Silver lub wyższy). API zamraża się automatycznie, gdy członkostwo właściciela wygaśnie, i przywraca się po ponownej subskrypcji.
- Zatwierdzonego wniosku deweloperskiego. Złóż go na tej stronie; zespół Vetox sprawdza go ręcznie.
- Zaakceptowane warunki dla deweloperów API — potwierdzane przy wysyłaniu wniosku deweloperskiego.
- Zakresów, których potrzebuje Twój projekt. Są przyznawane przez zespół Vetox na podstawie wniosku.
Dostępne zakresy
balance:readdeduct:createcredit:createtransfer:createtransactions:readUwierzytelnianie
Uwierzytelniaj każde żądanie swoim tajnym kluczem API w nagłówku Authorization jako token Bearer. Klucze mają prefiks vito_live_.
Authorization: Bearer vito_live_xxxxxxxxxxxxxxxxDwie opcjonalne warstwy dodatkowo chronią Twój projekt:
- Lista dozwolonych IP — ogranicz żądania do konkretnych adresów IP serwera (skonfiguruj w zakładce Ustawienia).
- Limity szybkości — pułapy żądań na projekt, które skalują się z Twoim poziomem członkostwa.
Klucze API — przechowywanie, rotacja i bezpieczeństwo
Twój klucz jest generowany po zatwierdzeniu wniosku. Jest pokazywany tylko raz — ujawnij go w zakładce Klucze API i natychmiast zapisz.
Vetox nigdy nie przechowuje klucza w czytelnej formie (tylko solony hash). Trzymaj go jako sekret po stronie serwera — zmienną środowiskową lub menedżera sekretów — nigdy w kodzie klienta, repozytorium git ani przeglądarce.
Rotuj go w zakładce Klucze API. Poprzedni klucz działa jeszcze przez 24-godzinny okres przejściowy, abyś mógł wdrożyć nowy bez przestoju, a potem przestaje.
Przepływ potwierdzania obciążenia
Każde obciążenie przechodzi przez potwierdzenie, które użytkownik zatwierdza PIN-em — sam klucz nigdy nie przenosi Vito:
- 1Twoja aplikacja wywołuje POST /deduct z użytkownikiem, kwotą, źródłowym guildId i szczegółami przedmiotu.
- 2Vito tworzy oczekujące potwierdzenie (ważne 10 minut) i zwraca confirmUrl. Jeśli podano guildId, użytkownik otrzymuje też DM ze szczegółami i przyciskiem do strony potwierdzenia.
- 3Użytkownik otwiera confirmUrl na vetox.io i zatwierdza obciążenie PIN-em swojego portfela.
- 4Vito potrąca Vito, zapisuje transakcję i — jeśli masz skonfigurowany webhook — wysyła podpisane zdarzenie confirmation.completed na Twój URL wywołania zwrotnego.
- 5Twoja aplikacja weryfikuje podpis i wykonuje swoją akcję (np. odblokowuje treść).
Parametry żądania (/deduct)
/v1/deductPunkt końcowy potrącenia przyjmuje następujące pola treści:
discordIdamountguildIdreasonmerchantRefproductproduct.imageUrlmetadata* pole wymagane.
POST https://api.vetox.io/public/vito/v1/deduct
Authorization: Bearer vito_live_xxxxxxxxxxxxxxxx
Content-Type: application/json
Idempotency-Key: req_9a1f2b # optional, dedupes retries for 24h
{
"discordId": "123456789012345678",
"amount": 500,
"guildId": "1470389021162209280",
"reason": "Unlock premium theme",
"merchantRef": "ref_1042",
"product": {
"type": "feature",
"name": "Premium theme",
"description": "Unlocks the premium theme pack",
"imageUrl": "https://cdn.example.com/theme.png"
},
"metadata": { "ref": "1042" }
}{
"success": true,
"data": {
"confirmationId": "f3c9...e1",
"confirmUrl": "https://vetox.io/confirm/vito/f3c9...e1",
"amount": 500,
"expiresAt": 1735689600000,
"status": "pending"
},
"requestId": "req_abc123",
"timestamp": 1735689300000
}Dodawanie Vito — uznania (/add)
/v1/addPOST /add zasila użytkownika w Vito z Twojego własnego salda — na nagrody lub uznania. Przyjmuje te same pola co /deduct z wyjątkiem guildId i product. W przeciwieństwie do obciążenia jest natychmiastowe i nie wymaga PIN-u użytkownika — Twój klucz API jest Twoim upoważnieniem — więc odpowiedź już odzwierciedla ukończony transfer. Użytkownik otrzymuje pełną kwotę; nie obowiązuje żadna opłata platformy.
POST https://api.vetox.io/public/vito/v1/add
Authorization: Bearer vito_live_xxxxxxxxxxxxxxxx
Content-Type: application/json
Idempotency-Key: credit_7f3a # optional, dedupes retries for 24h
{
"discordId": "123456789012345678",
"amount": 250,
"reason": "Event reward",
"merchantRef": "reward_1042",
"metadata": { "ref": "1042" }
}{
"success": true,
"data": {
"transactionId": "txn_91b2",
"toDiscordId": "123456789012345678",
"amount": 250,
"ownerBalance": 48250,
"recipientBalance": 1250,
"status": "completed"
},
"requestId": "req_def456",
"timestamp": 1735689300000
}Webhooki i weryfikacja podpisu
Dodaj jeden lub więcej adresów URL wywołań zwrotnych https w zakładce Ustawienia. Vito dostarcza podpisany POST na każdy skonfigurowany URL, gdy potwierdzenie osiągnie stan końcowy, ponawiając próbę do 5 razy z wykładniczym wycofaniem.
Weryfikacja podpisu
Każde dostarczenie zawiera nagłówek X-Vito-Signature w formie ts=<unix>;h1=<hex>. Przelicz ponownie HMAC na "<ts>:<rawBody>" swoim sekretem podpisu i porównaj w czasie stałym. Zawsze weryfikuj na surowej treści żądania, przed parsowaniem JSON.
signature = HMAC_SHA256(signingSecret, "<ts>:<rawBody>")
header = X-Vito-Signature: ts=<unix-seconds>;h1=<hex-signature>import crypto from 'crypto';
import express from 'express';
const app = express();
// IMPORTANT: verify against the RAW body, before JSON parsing.
app.post('/webhooks/vito', express.raw({ type: '*/*' }), (req, res) => {
const sig = req.header('X-Vito-Signature') || ''; // "ts=<unix>;h1=<hmac>[;h1=<hmac>]"
const pairs = sig.split(';').map(p => p.split('='));
const ts = pairs.find(([k]) => k === 'ts')?.[1];
const sigs = pairs.filter(([k]) => k === 'h1').map(([, v]) => v);
const rawBody = req.body.toString('utf8');
const expected = crypto
.createHmac('sha256', process.env.VITO_SIGNING_SECRET) // whsec_...
.update(`${ts}:${rawBody}`)
.digest('hex');
const expectedBuf = Buffer.from(expected, 'hex');
// During a 24h secret rotation the header carries TWO h1= values — accept ANY.
// Length-guard FIRST: crypto.timingSafeEqual throws on a length mismatch.
const ok = sigs.some(h => {
const got = Buffer.from(h, 'hex');
return (
got.length === expectedBuf.length &&
crypto.timingSafeEqual(got, expectedBuf)
);
});
// Reject anything older than ~5 minutes to stop replays.
const fresh = ts && Math.abs(Date.now() / 1000 - Number(ts)) < 300;
if (!ok || !fresh) return res.status(401).end();
const event = JSON.parse(rawBody);
if (event.eventType === 'confirmation.completed') {
grantPremiumTheme(event.data.merchantRef, event.data.fromDiscordId);
}
res.status(200).end(); // ack fast; run your action async if slow
});Zdarzenia webhooków i ładunki
Vito wysyła jeden z czterech typów zdarzeń. Wszystkie mają ten sam kształt ładunku; data.status odzwierciedla wynik.
confirmation.completedconfirmation.failedconfirmation.expiredconfirmation.cancelledcredit.completedPOST https://your-app.com/webhooks/vito
X-Vito-Signature: ts=1735689600;h1=4f8b2c...e9
X-Vito-Event-Id: evt_3a2b1c
X-Vito-Event-Type: confirmation.completed
Content-Type: application/json
{
"eventId": "evt_3a2b1c",
"eventType": "confirmation.completed",
"timestamp": 1735689600000,
"data": {
"confirmationId": "f3c9...e1",
"type": "deduct",
"fromDiscordId": "123456789012345678",
"toDiscordId": null,
"amount": 500,
"reason": "Unlock premium theme",
"merchantRef": "ref_1042",
"product": { "type": "feature", "name": "Premium theme" },
"status": "completed",
"transactionId": "txn_77a2",
"metadata": { "ref": "1042" },
"failureReason": null
}
}Kody błędów
Błędy zwracają status inny niż 2xx z treścią JSON zawierającą error.code na najwyższym poziomie. Częste przypadki:
VITO_VALIDATION_ERRORinvalid keyOWNER_MEMBERSHIP_INACTIVEscope / IPnot foundinsufficient balanceINSUFFICIENT_OWNER_FUNDSrate limited{
"success": false,
"error": { "type": "Bad Request", "message": "guildId must be a valid Discord server ID", "code": "VITO_VALIDATION_ERROR" },
"requestId": "req_abc123",
"timestamp": 1735689300000
}Przykłady integracji
Utwórz żądanie obciążenia:
/v1/deductcurl -X POST https://api.vetox.io/public/vito/v1/deduct \
-H "Authorization: Bearer $VITO_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"discordId": "123456789012345678",
"amount": 500,
"guildId": "1470389021162209280",
"merchantRef": "ref_1042",
"product": { "type": "feature", "name": "Premium theme" }
}'Sprawdź saldo użytkownika:
/v1/balance/:discordIdcurl https://api.vetox.io/public/vito/v1/balance/123456789012345678 \
-H "Authorization: Bearer $VITO_API_KEY"Testowanie z integracją demonstracyjną
Udostępniono samodzielną integrację demonstracyjną do przećwiczenia pełnego przepływu od początku do końca bez pisania kodu. Przechodzi przez żądanie → potwierdzenie → PIN → webhook → ukończenie.
- 1Otwórz folder integracji demonstracyjnej i dodaj swój klucz Vito API, token bota Discord oraz identyfikator testowego serwera (guild) do config.json.
- 2Uruchom npm install && npm start, a następnie otwórz http://localhost:4000.
- 3Wyszukaj użytkownika po ID Discord, wybierz przedmiot i rozpocznij obciążenie — demo wywołuje /deduct i pokazuje confirmUrl.
- 4Zatwierdź PIN na vetox.io; demo wykrywa ukończenie (przez webhook, jeśli skonfigurowany, w przeciwnym razie przez odpytywanie) i wykonuje przykładową akcję.
Najlepsze praktyki, limity i bezpieczeństwo
Bezpieczeństwo
- Trzymaj klucz API i sekret podpisu tylko po stronie serwera; w razie wycieku natychmiast rotuj którykolwiek z nich.
- Zawsze weryfikuj podpis webhooka na surowej treści i odrzucaj dostarczenia starsze niż ~5 minut (ochrona przed powtórzeniem).
- Wykonuj swoją akcję tylko przy confirmation.completed — nigdy przy odpowiedzi /deduct (w tym momencie obciążenie nie jest ostateczne).
- Używaj listy dozwolonych IP i żądaj tylko tych zakresów, których naprawdę potrzebujesz.
Limity
- Potwierdzenia wygasają po 10 minutach; traktuj niepotwierdzone żądania jako porzucone.
- Limity szybkości są na projekt i skalują się z poziomem członkostwa właściciela.
- amount musi być dodatnią liczbą całkowitą; metadata jest ograniczone do 10 kluczy.