مستندات Vito API

آخرین به‌روزرسانی: ۶ تیر ۱۴۰۵ (۲۶ ژوئن ۲۰۲۶)
01

نمای کلی

Vito API به برنامه شما اجازه می‌دهد از داخل Discord با موجودی Vito کاربر کار کند — خواندن آن، دریافت هزینه از آن یا اعتباردهی به آن. کاربر هر دریافت هزینه را پیش از جابه‌جایی هر Vito با PIN خود در vetox.io تأیید می‌کند و نتیجه به برنامه شما اطلاع داده می‌شود. Vito ارز مجازی درون‌اکوسیستم Vetox است؛ از API برای ساخت تجربه‌های مبتنی بر Vito استفاده کنید — باز کردن محتوا، ویژگی‌ها، مزایا یا پاداش‌ها. هیچ خرید یا فروشی با پول واقعی انجام نمی‌شود.

این یک REST API است که با یک کلید مخفی احراز هویت می‌شود. همه نقاط پایانی JSON برمی‌گردانند و تحت /v1 نسخه‌بندی شده‌اند.

آدرس پایهhttps://api.vetox.io/public/vito/v1
02

موجودی‌ها و تسویه

کلید API شما به حساب Vetox شما گره خورده است. هر برداشت به نفع شما تسویه می‌شود: وقتی برنامه شما Vito را از یک کاربر کسر می‌کند، آن Vito به موجودی خودتان افزوده می‌شود (پس از کسر کارمزد پلتفرم) و هرگز از بین نمی‌رود. وقتی برنامه شما به کاربر Vito اضافه می‌کند، از موجودی شما تأمین می‌شود.

Vito همیشه میان موجودی‌ها جابه‌جا می‌شود — هرگز از یا به پول واقعی. از کاربر مبلغ کاملی را که درخواست می‌کنید دریافت می‌شود؛ شما مبلغ را پس از کارمزد پلتفرم دریافت می‌کنید؛ اعتبارها و پاداش‌ها از موجودی شما از طریق /add تأمین می‌شوند.

کارمزد پلتفرم — همان جدول انتقال‌های درون‌برنامه‌ای Vito، بر اساس سطح عضویت شما: ۷٪ (Silver / Gold)، ۶٪ (Platinum)، ۵٪ (Diamond). برداشت‌های ۵ Vito یا کمتر بدون کارمزد هستند. اعتبارها از طریق /add هرگز مشمول کارمزد نمی‌شوند.
03

پیش‌نیازها

دسترسی به Vito API برای هر پروژه اعطا می‌شود. پیش از فراخوانی آن نیاز دارید به:

  • یک عضویت کاربری فعال (سطح Silver یا بالاتر). اگر عضویت مالک منقضی شود، API به‌طور خودکار فریز می‌شود و با اشتراک مجدد بازمی‌گردد.
  • یک درخواست توسعه‌دهنده تأییدشده. یکی را از این صفحه ارسال کنید؛ تیم Vetox آن را به‌صورت دستی بررسی می‌کند.
  • پذیرش شرایط توسعه‌دهندگان API — هنگام ارسال درخواست توسعه‌دهنده تأیید می‌شود.
  • دامنه‌هایی که پروژه شما نیاز دارد. توسط تیم Vetox بر اساس درخواست شما اعطا می‌شود.

دامنه‌های موجود

balance:read
خواندن موجودی Vito یک کاربر.
deduct:create
ایجاد یک کسر — دریافت هزینه از موجودی Vito یک کاربر.
credit:create
افزودن Vito به یک کاربر — تأمین پاداش‌ها یا اعتبارها از موجودی شما.
transfer:create
ایجاد یک انتقال بین دو کاربر.
transactions:read
فهرست و خواندن تراکنش‌های پروژه شما.
04

احراز هویت

هر درخواست را با کلید API مخفی خود در هدر Authorization به‌عنوان توکن Bearer احراز هویت کنید. کلیدها پیشوند vito_live_ دارند.

http
Authorization: Bearer vito_live_xxxxxxxxxxxxxxxx

دو لایه اختیاری پروژه شما را بیشتر محافظت می‌کنند:

  • فهرست مجاز IP — درخواست‌ها را به IPهای خاص سرور محدود کنید (آن را در زبانه تنظیمات پیکربندی کنید).
  • محدودیت‌های نرخ — سقف درخواست برای هر پروژه که با سطح عضویت شما مقیاس می‌گیرد.
05

کلیدهای API — ذخیره‌سازی، چرخش و امنیت

کلید شما هنگام تأیید درخواست ایجاد می‌شود. تنها یک بار نمایش داده می‌شود — آن را از زبانه کلیدهای API نمایش دهید و فوراً ذخیره کنید.

Vetox هرگز کلید شما را به‌صورت قابل‌خواندن ذخیره نمی‌کند (فقط یک هش نمک‌دار). آن را به‌عنوان یک راز سمت سرور — یک متغیر محیطی یا یک مدیر رازها — نگه دارید؛ هرگز در کد سمت کلاینت، مخزن git یا مرورگر.

آن را از زبانه کلیدهای API بچرخانید. کلید قبلی برای استقرار کلید جدید بدون قطعی، در یک دوره مهلت ۲۴ ساعته همچنان کار می‌کند و سپس متوقف می‌شود.

با کلید مانند یک رمز عبور رفتار کنید — هر کسی آن را داشته باشد می‌تواند از کاربران شما هزینه بگیرد. اگر لو رفت، فوراً آن را بچرخانید و تراکنش‌های اخیر خود را بررسی کنید.
06

جریان تأیید دریافت هزینه

هر دریافت هزینه از یک تأیید که کاربر با PIN خود تصویب می‌کند عبور می‌کند — کلید شما به‌تنهایی هرگز Vito جابه‌جا نمی‌کند:

  1. 1برنامه شما POST /deduct را با کاربر، مبلغ، guildId مبدأ و جزئیات آیتم فراخوانی می‌کند.
  2. 2Vito یک تأیید در انتظار (معتبر برای ۱۰ دقیقه) می‌سازد و یک confirmUrl برمی‌گرداند. اگر guildId داده شود، یک پیام مستقیم با جزئیات و دکمه‌ای به صفحه تأیید نیز برای کاربر ارسال می‌شود.
  3. 3کاربر confirmUrl را در vetox.io باز می‌کند و هزینه را با PIN کیف‌پول خود تصویب می‌کند.
  4. 4Vito مقدار Vito را کسر می‌کند، تراکنش را ثبت می‌کند و — اگر وب‌هوکی پیکربندی کرده باشید — یک رویداد امضاشده confirmation.completed به آدرس کال‌بک شما ارسال می‌کند.
  5. 5برنامه شما امضا را بررسی می‌کند و اقدام خود را کامل می‌کند (مثلاً محتوا را باز می‌کند).
PINها فقط در vetox.io وارد می‌شوند — هرگز درون برنامه شما یا در Discord. اگر کاربر هرگز تأیید نکند، درخواست پس از ۱۰ دقیقه منقضی می‌شود و شما یک رویداد confirmation.expired دریافت می‌کنید.
07

پارامترهای درخواست (/deduct)

POST/v1/deduct

نقطه پایانی کسر فیلدهای بدنه زیر را می‌پذیرد:

discordId
شناسه کاربری Discord کاربر — یک snowflake ۱۷ تا ۲۰ رقمی. الزامی.
amount
مقدار Vito برای دریافت — یک عدد صحیح مثبت. الزامی.
guildId
سروری در Discord که دریافت هزینه از آن نشأت می‌گیرد (snowflake). الزامی — هر دریافت هزینه باید از یک سرور مشخص باشد که پیام مستقیم به کاربر را نیز فعال می‌کند.
reason
یک توضیح کوتاه و خوانا (≤ ۲۵۶ کاراکتر) که به کاربر نمایش داده و در وب‌هوک تکرار می‌شود.
merchantRef
رشته مرجع خودتان (≤ ۱۲۸ کاراکتر). از آن برای تطبیق وب‌هوک با سوابق خود استفاده کنید.
product
توصیف‌گر آیتم — { type, name, description?, imageUrl? }. در صفحه تأیید و پیام مستقیم به کاربر نمایش داده و در هر وب‌هوک تکرار می‌شود.
product.imageUrl
تصویر اختیاری محصول — باید یک آدرس //:https باشد.
metadata
تا ۱۰ جفت کلید/مقدار رشته‌ای که عیناً در وب‌هوک منتقل می‌شوند.

* فیلد الزامی.

نمونه درخواست
http
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" }
}
نمونه پاسخ
json
{
  "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
}
08

افزودن Vito — اعتبارها (/add)

POST/v1/add

POST /add مبلغ Vito را از موجودی خودتان به یک کاربر اعتبار می‌دهد — برای پاداش‌ها یا اعتبارها. همان فیلدهای /deduct را می‌گیرد به‌جز guildId و product. برخلاف یک برداشت، فوری است و به PIN کاربر نیاز ندارد — کلید API شما اختیار شماست — بنابراین پاسخ از پیش انتقال کامل‌شده را بازتاب می‌دهد. کاربر مبلغ کامل را دریافت می‌کند؛ هیچ کارمزد پلتفرمی اعمال نمی‌شود.

به دامنه credit:create و Vito کافی در موجودی خودتان نیاز دارد — در غیر این صورت فراخوان 402 INSUFFICIENT_OWNER_FUNDS را برمی‌گرداند.
نمونه درخواست
http
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" }
}
نمونه پاسخ
json
{
  "success": true,
  "data": {
    "transactionId": "txn_91b2",
    "toDiscordId": "123456789012345678",
    "amount": 250,
    "ownerBalance": 48250,
    "recipientBalance": 1250,
    "status": "completed"
  },
  "requestId": "req_def456",
  "timestamp": 1735689300000
}
09

وب‌هوک‌ها و تأیید امضا

یک یا چند آدرس کال‌بک https را در زبانه تنظیمات اضافه کنید. هرگاه یک تأیید به وضعیت نهایی برسد، Vito یک POST امضاشده به هر آدرس پیکربندی‌شده تحویل می‌دهد و تا ۵ بار با عقب‌نشینی نمایی دوباره تلاش می‌کند.

وب‌هوک‌ها تنها زمانی فعال می‌شوند که پروژه شما هم یک آدرس کال‌بک پیکربندی‌شده و هم یک راز امضا داشته باشد. راز امضای خود (whsec_…) را یک بار از زبانه کلیدهای API نمایش دهید.

تأیید امضا

هر تحویل یک هدر X-Vito-Signature به شکل ts=<unix>;h1=<hex> دارد. HMAC را روی "<ts>:<rawBody>" با راز امضای خود دوباره محاسبه کنید و در زمان ثابت مقایسه کنید. همیشه پیش از تجزیه JSON، در برابر بدنه خام درخواست تأیید کنید.

http
signature = HMAC_SHA256(signingSecret, "<ts>:<rawBody>")
header    = X-Vito-Signature: ts=<unix-seconds>;h1=<hex-signature>
تأییدکننده مرجع (Node / Express):
javascript
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
});
10

رویدادهای وب‌هوک و محموله‌ها

Vito یکی از چهار نوع رویداد را ارسال می‌کند. همه شکل محموله یکسانی دارند؛ data.status نتیجه را منعکس می‌کند.

confirmation.completed
کاربر تأیید کرد و Vito کسر شد. اقدام خود را کامل کنید. transactionId دارد.
confirmation.failed
دریافت هزینه نتوانست کامل شود (مثلاً موجودی ناکافی یا یک خطای داخلی). failureReason دارد.
confirmation.expired
کاربر ظرف ۱۰ دقیقه تأیید نکرد. هیچ Vito‌ای جابه‌جا نشد.
confirmation.cancelled
کاربر درخواست را لغو کرد. هیچ Vito‌ای جابه‌جا نشد.
credit.completed
یک اعتبار /add تأمین‌شده توسط مالک تسویه شد. شامل گیرنده و transactionId است و بی‌درنگ ارسال می‌شود — هیچ مرحله تأییدی وجود ندارد.
نمونه محموله (confirmation.completed)
http
POST 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
  }
}
11

کدهای خطا

خطاها یک وضعیت غیر ۲xx با یک بدنه JSON که یک error.code سطح بالا دارد برمی‌گردانند. موارد رایج:

400VITO_VALIDATION_ERROR
یک فیلد درخواست گم‌شده یا بدشکل است (مثلاً یک guildId نامعتبر).
401invalid key
کلید API گم‌شده، بدشکل یا ناشناخته.
403OWNER_MEMBERSHIP_INACTIVE
عضویت مالک پروژه منقضی شده است — API تا زمان اشتراک مجدد فریز است.
403scope / IP
کلید دامنه لازم را ندارد، یا IP درخواست در فهرست مجاز نیست.
404not found
کاربر، تراکنش یا منبع وجود ندارد.
402insufficient balance
کاربر Vito کافی برای دریافت هزینه ندارد.
402INSUFFICIENT_OWNER_FUNDS
موجودی خودتان (مالک) برای تأمین یک اعتبار /add بسیار کم است.
429rate limited
شما از محدودیت نرخ پروژه فراتر رفتید — کمی صبر کنید و دوباره تلاش کنید.
شکل پاسخ خطا
json
{
  "success": false,
  "error": { "type": "Bad Request", "message": "guildId must be a valid Discord server ID", "code": "VITO_VALIDATION_ERROR" },
  "requestId": "req_abc123",
  "timestamp": 1735689300000
}
12

نمونه‌های یکپارچه‌سازی

یک درخواست دریافت هزینه بسازید:

POST/v1/deduct
bash
curl -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" }
  }'

موجودی یک کاربر را بررسی کنید:

GET/v1/balance/:discordId
bash
curl https://api.vetox.io/public/vito/v1/balance/123456789012345678 \
  -H "Authorization: Bearer $VITO_API_KEY"
13

آزمایش با یکپارچه‌سازی نمایشی

یک یکپارچه‌سازی نمایشی مستقل ارائه شده تا کل جریان را بدون نوشتن هیچ کدی به‌صورت سرتاسری تمرین کنید. درخواست ← تأیید ← PIN ← وب‌هوک ← تکمیل را طی می‌کند.

  1. 1پوشه یکپارچه‌سازی نمایشی را باز کنید و کلید Vito API، یک توکن ربات Discord و شناسه سرور (guild) آزمایشی خود را به config.json اضافه کنید.
  2. 2npm install && npm start را اجرا کنید، سپس http://localhost:4000 را باز کنید.
  3. 3یک کاربر را با شناسه Discord جست‌وجو کنید، یک آیتم انتخاب کنید و یک دریافت هزینه را آغاز کنید — نمایش /deduct را فراخوانی می‌کند و confirmUrl را نشان می‌دهد.
  4. 4PIN را در vetox.io تأیید کنید؛ نمایش تکمیل را تشخیص می‌دهد (در صورت پیکربندی از طریق وب‌هوک، در غیر این صورت با نظرسنجی) و اقدام نمونه را کامل می‌کند.
دریافت وب‌هوک نیازمند یک کال‌بک عمومی https و یک راز امضا است؛ بدون آن‌ها نمایش به نظرسنجی تراکنش‌های شما بازمی‌گردد، بنابراین بدون پیکربندی کار می‌کند.
14

بهترین شیوه‌ها، محدودیت‌ها و امنیت

امنیت

  • کلید API و راز امضای خود را فقط در سمت سرور نگه دارید؛ در صورت لو رفتن هر کدام، فوراً آن را بچرخانید.
  • همیشه امضای وب‌هوک را در برابر بدنه خام تأیید کنید و تحویل‌های قدیمی‌تر از ~۵ دقیقه را رد کنید (محافظت در برابر بازپخش).
  • اقدام خود را فقط در confirmation.completed کامل کنید — هرگز در پاسخ /deduct (در آن لحظه دریافت هزینه هنوز نهایی نشده است).
  • از فهرست مجاز IP استفاده کنید و فقط دامنه‌هایی را که واقعاً نیاز دارید درخواست کنید.

محدودیت‌ها

  • تأییدها پس از ۱۰ دقیقه منقضی می‌شوند؛ درخواست‌های تأییدنشده را رهاشده در نظر بگیرید.
  • محدودیت‌های نرخ برای هر پروژه است و با سطح عضویت مالک مقیاس می‌گیرد.
  • amount باید یک عدد صحیح مثبت باشد؛ metadata به ۱۰ کلید محدود است.
Idempotency-Key تلاش‌های مجدد را حذف تکراری می‌کندوب‌هوک‌ها ۵ بار با عقب‌نشینی دوباره تلاش می‌کنندسریع ۲xx را تأیید کنید، به‌صورت ناهمگام انجام دهید