نمای کلی
Vito API به برنامه شما اجازه میدهد از داخل Discord با موجودی Vito کاربر کار کند — خواندن آن، دریافت هزینه از آن یا اعتباردهی به آن. کاربر هر دریافت هزینه را پیش از جابهجایی هر Vito با PIN خود در vetox.io تأیید میکند و نتیجه به برنامه شما اطلاع داده میشود. Vito ارز مجازی دروناکوسیستم Vetox است؛ از API برای ساخت تجربههای مبتنی بر Vito استفاده کنید — باز کردن محتوا، ویژگیها، مزایا یا پاداشها. هیچ خرید یا فروشی با پول واقعی انجام نمیشود.
این یک REST API است که با یک کلید مخفی احراز هویت میشود. همه نقاط پایانی JSON برمیگردانند و تحت /v1 نسخهبندی شدهاند.
https://api.vetox.io/public/vito/v1موجودیها و تسویه
کلید API شما به حساب Vetox شما گره خورده است. هر برداشت به نفع شما تسویه میشود: وقتی برنامه شما Vito را از یک کاربر کسر میکند، آن Vito به موجودی خودتان افزوده میشود (پس از کسر کارمزد پلتفرم) و هرگز از بین نمیرود. وقتی برنامه شما به کاربر Vito اضافه میکند، از موجودی شما تأمین میشود.
Vito همیشه میان موجودیها جابهجا میشود — هرگز از یا به پول واقعی. از کاربر مبلغ کاملی را که درخواست میکنید دریافت میشود؛ شما مبلغ را پس از کارمزد پلتفرم دریافت میکنید؛ اعتبارها و پاداشها از موجودی شما از طریق /add تأمین میشوند.
پیشنیازها
دسترسی به Vito API برای هر پروژه اعطا میشود. پیش از فراخوانی آن نیاز دارید به:
- یک عضویت کاربری فعال (سطح Silver یا بالاتر). اگر عضویت مالک منقضی شود، API بهطور خودکار فریز میشود و با اشتراک مجدد بازمیگردد.
- یک درخواست توسعهدهنده تأییدشده. یکی را از این صفحه ارسال کنید؛ تیم Vetox آن را بهصورت دستی بررسی میکند.
- پذیرش شرایط توسعهدهندگان API — هنگام ارسال درخواست توسعهدهنده تأیید میشود.
- دامنههایی که پروژه شما نیاز دارد. توسط تیم Vetox بر اساس درخواست شما اعطا میشود.
دامنههای موجود
balance:readdeduct:createcredit:createtransfer:createtransactions:readاحراز هویت
هر درخواست را با کلید API مخفی خود در هدر Authorization بهعنوان توکن Bearer احراز هویت کنید. کلیدها پیشوند vito_live_ دارند.
Authorization: Bearer vito_live_xxxxxxxxxxxxxxxxدو لایه اختیاری پروژه شما را بیشتر محافظت میکنند:
- فهرست مجاز IP — درخواستها را به IPهای خاص سرور محدود کنید (آن را در زبانه تنظیمات پیکربندی کنید).
- محدودیتهای نرخ — سقف درخواست برای هر پروژه که با سطح عضویت شما مقیاس میگیرد.
کلیدهای API — ذخیرهسازی، چرخش و امنیت
کلید شما هنگام تأیید درخواست ایجاد میشود. تنها یک بار نمایش داده میشود — آن را از زبانه کلیدهای API نمایش دهید و فوراً ذخیره کنید.
Vetox هرگز کلید شما را بهصورت قابلخواندن ذخیره نمیکند (فقط یک هش نمکدار). آن را بهعنوان یک راز سمت سرور — یک متغیر محیطی یا یک مدیر رازها — نگه دارید؛ هرگز در کد سمت کلاینت، مخزن git یا مرورگر.
آن را از زبانه کلیدهای API بچرخانید. کلید قبلی برای استقرار کلید جدید بدون قطعی، در یک دوره مهلت ۲۴ ساعته همچنان کار میکند و سپس متوقف میشود.
جریان تأیید دریافت هزینه
هر دریافت هزینه از یک تأیید که کاربر با PIN خود تصویب میکند عبور میکند — کلید شما بهتنهایی هرگز Vito جابهجا نمیکند:
- 1برنامه شما POST /deduct را با کاربر، مبلغ، guildId مبدأ و جزئیات آیتم فراخوانی میکند.
- 2Vito یک تأیید در انتظار (معتبر برای ۱۰ دقیقه) میسازد و یک confirmUrl برمیگرداند. اگر guildId داده شود، یک پیام مستقیم با جزئیات و دکمهای به صفحه تأیید نیز برای کاربر ارسال میشود.
- 3کاربر confirmUrl را در vetox.io باز میکند و هزینه را با PIN کیفپول خود تصویب میکند.
- 4Vito مقدار Vito را کسر میکند، تراکنش را ثبت میکند و — اگر وبهوکی پیکربندی کرده باشید — یک رویداد امضاشده confirmation.completed به آدرس کالبک شما ارسال میکند.
- 5برنامه شما امضا را بررسی میکند و اقدام خود را کامل میکند (مثلاً محتوا را باز میکند).
پارامترهای درخواست (/deduct)
/v1/deductنقطه پایانی کسر فیلدهای بدنه زیر را میپذیرد:
discordIdamountguildIdreasonmerchantRefproductproduct.imageUrlmetadata* فیلد الزامی.
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
}افزودن Vito — اعتبارها (/add)
/v1/addPOST /add مبلغ Vito را از موجودی خودتان به یک کاربر اعتبار میدهد — برای پاداشها یا اعتبارها. همان فیلدهای /deduct را میگیرد بهجز guildId و product. برخلاف یک برداشت، فوری است و به PIN کاربر نیاز ندارد — کلید API شما اختیار شماست — بنابراین پاسخ از پیش انتقال کاملشده را بازتاب میدهد. کاربر مبلغ کامل را دریافت میکند؛ هیچ کارمزد پلتفرمی اعمال نمیشود.
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
}وبهوکها و تأیید امضا
یک یا چند آدرس کالبک https را در زبانه تنظیمات اضافه کنید. هرگاه یک تأیید به وضعیت نهایی برسد، Vito یک POST امضاشده به هر آدرس پیکربندیشده تحویل میدهد و تا ۵ بار با عقبنشینی نمایی دوباره تلاش میکند.
تأیید امضا
هر تحویل یک هدر X-Vito-Signature به شکل ts=<unix>;h1=<hex> دارد. HMAC را روی "<ts>:<rawBody>" با راز امضای خود دوباره محاسبه کنید و در زمان ثابت مقایسه کنید. همیشه پیش از تجزیه 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
});رویدادهای وبهوک و محمولهها
Vito یکی از چهار نوع رویداد را ارسال میکند. همه شکل محموله یکسانی دارند؛ data.status نتیجه را منعکس میکند.
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
}
}کدهای خطا
خطاها یک وضعیت غیر ۲xx با یک بدنه JSON که یک error.code سطح بالا دارد برمیگردانند. موارد رایج:
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
}نمونههای یکپارچهسازی
یک درخواست دریافت هزینه بسازید:
/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" }
}'موجودی یک کاربر را بررسی کنید:
/v1/balance/:discordIdcurl https://api.vetox.io/public/vito/v1/balance/123456789012345678 \
-H "Authorization: Bearer $VITO_API_KEY"آزمایش با یکپارچهسازی نمایشی
یک یکپارچهسازی نمایشی مستقل ارائه شده تا کل جریان را بدون نوشتن هیچ کدی بهصورت سرتاسری تمرین کنید. درخواست ← تأیید ← PIN ← وبهوک ← تکمیل را طی میکند.
- 1پوشه یکپارچهسازی نمایشی را باز کنید و کلید Vito API، یک توکن ربات Discord و شناسه سرور (guild) آزمایشی خود را به config.json اضافه کنید.
- 2npm install && npm start را اجرا کنید، سپس http://localhost:4000 را باز کنید.
- 3یک کاربر را با شناسه Discord جستوجو کنید، یک آیتم انتخاب کنید و یک دریافت هزینه را آغاز کنید — نمایش /deduct را فراخوانی میکند و confirmUrl را نشان میدهد.
- 4PIN را در vetox.io تأیید کنید؛ نمایش تکمیل را تشخیص میدهد (در صورت پیکربندی از طریق وبهوک، در غیر این صورت با نظرسنجی) و اقدام نمونه را کامل میکند.
بهترین شیوهها، محدودیتها و امنیت
امنیت
- کلید API و راز امضای خود را فقط در سمت سرور نگه دارید؛ در صورت لو رفتن هر کدام، فوراً آن را بچرخانید.
- همیشه امضای وبهوک را در برابر بدنه خام تأیید کنید و تحویلهای قدیمیتر از ~۵ دقیقه را رد کنید (محافظت در برابر بازپخش).
- اقدام خود را فقط در confirmation.completed کامل کنید — هرگز در پاسخ /deduct (در آن لحظه دریافت هزینه هنوز نهایی نشده است).
- از فهرست مجاز IP استفاده کنید و فقط دامنههایی را که واقعاً نیاز دارید درخواست کنید.
محدودیتها
- تأییدها پس از ۱۰ دقیقه منقضی میشوند؛ درخواستهای تأییدنشده را رهاشده در نظر بگیرید.
- محدودیتهای نرخ برای هر پروژه است و با سطح عضویت مالک مقیاس میگیرد.
- amount باید یک عدد صحیح مثبت باشد؛ metadata به ۱۰ کلید محدود است.