Ringkasan
Vito API memungkinkan aplikasi Anda bekerja dengan saldo Vito pengguna dari dalam Discord — membacanya, menagihnya, atau mengkreditkannya kembali. Pengguna mengonfirmasi setiap tagihan dengan PIN di vetox.io sebelum ada Vito yang berpindah, dan aplikasi Anda diberi tahu hasilnya. Vito adalah mata uang virtual internal ekosistem Vetox; gunakan API untuk membangun pengalaman berbasis Vito — membuka konten, fitur, keuntungan, atau hadiah. Tidak ada pembelian atau penjualan dengan uang sungguhan.
Ini adalah REST API yang diautentikasi dengan kunci rahasia. Semua endpoint mengembalikan JSON dan diberi versi di bawah /v1.
https://api.vetox.io/public/vito/v1Saldo & penyelesaian
Kunci API Anda terikat ke akun Vetox Anda. Setiap penagihan diselesaikan untuk Anda: saat aplikasi Anda memotong Vito dari pengguna, Vito itu dikreditkan ke saldo Anda sendiri (dikurangi biaya platform), tidak pernah dimusnahkan. Saat aplikasi Anda menambahkan Vito ke pengguna, itu didanai dari saldo Anda.
Vito selalu berpindah antar saldo — tidak pernah dari atau ke uang sungguhan. Pengguna ditagih jumlah penuh yang Anda minta; Anda menerima jumlah setelah biaya platform; kredit dan hadiah didanai dari saldo Anda melalui /add.
Persyaratan
Akses ke Vito API diberikan per proyek. Sebelum dapat memanggilnya, Anda memerlukan:
- Keanggotaan pengguna aktif (tingkat Silver atau lebih tinggi). API membeku otomatis jika keanggotaan pemilik berakhir, dan pulih saat berlangganan lagi.
- Pengajuan pengembang yang disetujui. Kirim satu dari halaman ini; tim Vetox meninjaunya secara manual.
- Persyaratan Pengembang API yang disetujui — dikonfirmasi saat Anda mengirim aplikasi pengembang Anda.
- Cakupan yang dibutuhkan proyek Anda. Diberikan oleh tim Vetox berdasarkan pengajuan Anda.
Cakupan yang tersedia
balance:readdeduct:createcredit:createtransfer:createtransactions:readAutentikasi
Autentikasi setiap permintaan dengan kunci API rahasia Anda di header Authorization sebagai token Bearer. Kunci memiliki prefiks vito_live_.
Authorization: Bearer vito_live_xxxxxxxxxxxxxxxxDua lapisan opsional melindungi proyek Anda lebih lanjut:
- Daftar izin IP — batasi permintaan ke IP server tertentu (konfigurasikan di tab Pengaturan).
- Batas laju — plafon permintaan per proyek yang naik sesuai tingkat keanggotaan Anda.
Kunci API — penyimpanan, rotasi, dan keamanan
Kunci Anda dibuat saat pengajuan disetujui. Hanya ditampilkan sekali — tampilkan dari tab Kunci API dan simpan segera.
Vetox tidak pernah menyimpan kunci Anda dalam bentuk yang dapat dibaca (hanya hash bergaram). Simpan sebagai rahasia sisi server — variabel lingkungan atau pengelola rahasia — jangan pernah dalam kode klien, repositori git, atau peramban.
Rotasi dari tab Kunci API. Kunci sebelumnya tetap berfungsi selama masa tenggang 24 jam agar Anda dapat menerapkan yang baru tanpa henti, lalu berhenti.
Alur konfirmasi tagihan
Setiap tagihan melalui konfirmasi yang disetujui pengguna dengan PIN — kunci Anda saja tidak pernah memindahkan Vito:
- 1Aplikasi Anda memanggil POST /deduct dengan pengguna, jumlah, guildId asal, dan detail item.
- 2Vito membuat konfirmasi tertunda (berlaku 10 menit) dan mengembalikan confirmUrl. Jika guildId diberikan, pengguna juga menerima DM berisi detail dengan tombol ke halaman konfirmasi.
- 3Pengguna membuka confirmUrl di vetox.io dan menyetujui tagihan dengan PIN dompetnya.
- 4Vito memotong Vito, mencatat transaksi, dan — jika Anda mengonfigurasi webhook — mengirim peristiwa confirmation.completed yang ditandatangani ke URL callback Anda.
- 5Aplikasi Anda memverifikasi tanda tangan dan menyelesaikan tindakannya (misalnya, membuka konten).
Parameter permintaan (/deduct)
/v1/deductEndpoint pemotongan menerima bidang body berikut:
discordIdamountguildIdreasonmerchantRefproductproduct.imageUrlmetadata* bidang wajib.
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
}Menambahkan Vito — kredit (/add)
/v1/addPOST /add mengkreditkan Vito ke pengguna dari saldo Anda sendiri — untuk hadiah atau kredit. Menerima field yang sama dengan /deduct kecuali guildId dan product. Tidak seperti penagihan, ini instan dan tidak memerlukan PIN pengguna — kunci API Anda adalah otoritas Anda — sehingga respons sudah mencerminkan transfer yang selesai. Pengguna menerima jumlah penuh; tidak ada biaya platform yang berlaku.
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
}Webhook dan verifikasi tanda tangan
Tambahkan satu atau beberapa URL callback https di tab Pengaturan. Vito mengirim POST yang ditandatangani ke setiap URL yang dikonfigurasi setiap kali konfirmasi mencapai status akhir, mencoba ulang hingga 5 kali dengan backoff eksponensial.
Memverifikasi tanda tangan
Setiap pengiriman membawa header X-Vito-Signature berbentuk ts=<unix>;h1=<hex>. Hitung ulang HMAC atas "<ts>:<rawBody>" dengan rahasia penandatanganan Anda dan bandingkan dalam waktu konstan. Selalu verifikasi terhadap body permintaan mentah, sebelum mem-parsing 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
});Peristiwa webhook dan payload
Vito mengirim salah satu dari empat jenis peristiwa. Semuanya berbagi bentuk payload yang sama; data.status mencerminkan hasilnya.
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
}
}Kode kesalahan
Kesalahan mengembalikan status non-2xx dengan body JSON yang membawa error.code tingkat atas. Kasus umum:
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
}Contoh integrasi
Buat permintaan tagihan:
/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" }
}'Periksa saldo pengguna:
/v1/balance/:discordIdcurl https://api.vetox.io/public/vito/v1/balance/123456789012345678 \
-H "Authorization: Bearer $VITO_API_KEY"Menguji dengan integrasi demo
Integrasi demo mandiri disediakan untuk menjalankan seluruh alur dari ujung ke ujung tanpa menulis kode. Ia menelusuri permintaan → konfirmasi → PIN → webhook → penyelesaian.
- 1Buka folder integrasi demo dan tambahkan kunci Vito API, token bot Discord, dan ID server (guild) uji Anda ke config.json.
- 2Jalankan npm install && npm start, lalu buka http://localhost:4000.
- 3Cari pengguna berdasarkan ID Discord, pilih item, dan mulai tagihan — demo memanggil /deduct dan menampilkan confirmUrl.
- 4Setujui PIN di vetox.io; demo mendeteksi penyelesaian (melalui webhook jika dikonfigurasi, jika tidak melalui polling) dan menyelesaikan tindakan contoh.
Praktik terbaik, batas, dan keamanan
Keamanan
- Simpan kunci API dan rahasia penandatanganan Anda hanya di sisi server; rotasi salah satunya segera jika bocor.
- Selalu verifikasi tanda tangan webhook terhadap body mentah dan tolak pengiriman yang lebih tua dari ~5 menit (perlindungan replay).
- Selesaikan tindakan Anda hanya pada confirmation.completed — jangan pernah pada respons /deduct (pada titik itu tagihan belum final).
- Gunakan daftar izin IP dan minta hanya cakupan yang benar-benar Anda butuhkan.
Batas
- Konfirmasi kedaluwarsa setelah 10 menit; perlakukan permintaan yang tidak dikonfirmasi sebagai ditinggalkan.
- Batas laju bersifat per proyek dan naik sesuai tingkat keanggotaan pemilik.
- amount harus bilangan bulat positif; metadata dibatasi 10 kunci.