เอกสาร Vito API

อัปเดตล่าสุด: 26 มิถุนายน 2026
01

ภาพรวม

Vito API ให้แอปของคุณทำงานกับยอด Vito ของผู้ใช้จากภายใน Discord — อ่านยอด เรียกเก็บจากยอด หรือเครดิตคืน ผู้ใช้ยืนยันการเรียกเก็บแต่ละครั้งด้วย PIN บน vetox.io ก่อนที่ Vito ใด ๆ จะเคลื่อนไหว และแอปของคุณจะได้รับแจ้งผลลัพธ์ Vito คือสกุลเงินเสมือนภายในระบบนิเวศ Vetox ใช้ API เพื่อสร้างประสบการณ์ที่อิงกับ Vito — ปลดล็อกเนื้อหา ฟีเจอร์ สิทธิประโยชน์ หรือรางวัล ไม่มีการซื้อหรือขายด้วยเงินจริง

เป็น REST API ที่ตรวจสอบสิทธิ์ด้วยคีย์ลับ ทุกเอนด์พอยต์คืนค่า JSON และมีเวอร์ชันภายใต้ /v1

URL ฐานhttps://api.vetox.io/public/vito/v1
02

ยอดคงเหลือและการชำระบัญชี

คีย์ API ของคุณผูกกับบัญชี Vetox ของคุณ ทุกการเรียกเก็บจะชำระเข้าบัญชีคุณ: เมื่อแอปของคุณหัก Vito จากผู้ใช้ Vito นั้นจะถูกเครดิตเข้ายอดคงเหลือของคุณเอง (หักค่าธรรมเนียมแพลตฟอร์มแล้ว) ไม่เคยถูกทำลาย เมื่อแอปของคุณเพิ่ม Vito ให้ผู้ใช้ จะถูกหักจากยอดคงเหลือของคุณ

Vito เคลื่อนไหวระหว่างยอดคงเหลือเสมอ — ไม่เคยจากหรือไปยังเงินจริง ผู้ใช้ถูกเรียกเก็บเต็มจำนวนที่คุณร้องขอ คุณได้รับยอดหลังหักค่าธรรมเนียมแพลตฟอร์ม เครดิตและรางวัลได้รับการสนับสนุนจากยอดคงเหลือของคุณผ่าน /add

ค่าธรรมเนียมแพลตฟอร์ม — ตารางเดียวกับการโอน Vito ในแอป ตามระดับสมาชิกของคุณ: 7% (Silver / Gold), 6% (Platinum), 5% (Diamond) การเรียกเก็บ 5 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 คีย์เดิมจะยังทำงานต่อในช่วงผ่อนผัน 24 ชั่วโมงเพื่อให้คุณนำคีย์ใหม่ไปใช้ได้โดยไม่มีดาวน์ไทม์ แล้วจึงหยุด

ปฏิบัติต่อคีย์เหมือนรหัสผ่าน — ใครก็ตามที่มีคีย์สามารถเรียกเก็บเงินจากผู้ใช้ของคุณได้ หากรั่วไหลให้หมุนทันทีและตรวจสอบธุรกรรมล่าสุดของคุณ
06

ขั้นตอนการยืนยันการเรียกเก็บ

การเรียกเก็บทุกครั้งจะผ่านการยืนยันที่ผู้ใช้อนุมัติด้วย PIN — คีย์ของคุณเพียงอย่างเดียวไม่เคยเคลื่อน Vito:

  1. 1แอปของคุณเรียก POST /deduct ด้วยผู้ใช้ จำนวน guildId ต้นทาง และรายละเอียดรายการ
  2. 2Vito สร้างการยืนยันที่รอดำเนินการ (ใช้ได้ 10 นาที) และคืนค่า confirmUrl หากให้ guildId มา ผู้ใช้จะได้รับ DM พร้อมรายละเอียดและปุ่มไปยังหน้ายืนยันด้วย
  3. 3ผู้ใช้เปิด confirmUrl บน vetox.io และอนุมัติการเรียกเก็บด้วย PIN กระเป๋าเงินของตน
  4. 4Vito หัก Vito บันทึกธุรกรรม และ — หากคุณกำหนดค่าเว็บฮุกไว้ — ส่งเหตุการณ์ confirmation.completed ที่ลงนามไปยัง URL คอลแบ็กของคุณ
  5. 5แอปของคุณตรวจสอบลายเซ็นและดำเนินการของตน (เช่น ปลดล็อกเนื้อหา)
PIN ถูกป้อนเฉพาะบน vetox.io เท่านั้น — ไม่เคยอยู่ในแอปของคุณหรือใน Discord หากผู้ใช้ไม่ยืนยัน คำขอจะหมดอายุหลัง 10 นาทีและคุณจะได้รับเหตุการณ์ confirmation.expired
07

พารามิเตอร์คำขอ (/deduct)

POST/v1/deduct

เอนด์พอยต์การหักรับฟิลด์เนื้อหาต่อไปนี้:

discordId
ID ผู้ใช้ Discord ของผู้ใช้ — snowflake 17–20 หลัก จำเป็น
amount
จำนวน Vito ที่จะเรียกเก็บ — จำนวนเต็มบวก จำเป็น
guildId
เซิร์ฟเวอร์ Discord ที่การเรียกเก็บเกิดขึ้น (snowflake) จำเป็น — ทุกการเรียกเก็บต้องมาจากเซิร์ฟเวอร์ที่ระบุ ซึ่งยังเปิดใช้งาน DM ถึงผู้ใช้ด้วย
reason
คำอธิบายสั้นที่อ่านได้ (≤ 256 อักขระ) แสดงต่อผู้ใช้และทำซ้ำในเว็บฮุก
merchantRef
สตริงอ้างอิงของคุณเอง (≤ 128 อักขระ) ใช้เพื่อจับคู่เว็บฮุกกับบันทึกของคุณ
product
ตัวอธิบายรายการ — { type, name, description?, imageUrl? } แสดงต่อผู้ใช้บนหน้ายืนยันและ DM และทำซ้ำในทุกเว็บฮุก
product.imageUrl
รูปภาพสินค้าตัวเลือก — ต้องเป็น URL https://
metadata
คู่คีย์/ค่าสตริงสูงสุด 10 คู่ ส่งผ่านตามเดิมในเว็บฮุก

* ฟิลด์ที่จำเป็น

ตัวอย่างคำขอ
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

เว็บฮุกและการตรวจสอบลายเซ็น

เพิ่ม URL คอลแบ็ก https หนึ่งรายการหรือมากกว่าในแท็บการตั้งค่า Vito จะส่ง POST ที่ลงนามไปยัง URL ที่กำหนดค่าแต่ละรายการทุกครั้งที่การยืนยันถึงสถานะสุดท้าย โดยลองใหม่สูงสุด 5 ครั้งด้วย backoff แบบเอ็กซ์โพเนนเชียล

เว็บฮุกจะทำงานก็ต่อเมื่อโปรเจกต์ของคุณมีทั้ง URL คอลแบ็กที่กำหนดค่าและความลับสำหรับลงนาม แสดงความลับสำหรับลงนาม (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
ผู้ใช้ไม่ยืนยันภายใน 10 นาที ไม่มี 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

รหัสข้อผิดพลาด

ข้อผิดพลาดคืนค่าสถานะที่ไม่ใช่ 2xx พร้อมเนื้อหา 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 และ ID เซิร์ฟเวอร์ (guild) ทดสอบของคุณลงใน config.json
  2. 2รัน npm install && npm start แล้วเปิด http://localhost:4000
  3. 3ค้นหาผู้ใช้ด้วย ID Discord เลือกรายการ และเริ่มการเรียกเก็บ — สาธิตจะเรียก /deduct และแสดง confirmUrl
  4. 4อนุมัติ PIN บน vetox.io สาธิตจะตรวจพบการเสร็จสิ้น (ผ่านเว็บฮุกหากกำหนดค่าไว้ มิฉะนั้นผ่านการสำรวจ) และดำเนินการตัวอย่างให้เสร็จ
การรับเว็บฮุกต้องมีคอลแบ็ก https สาธารณะและความลับสำหรับลงนาม หากไม่มี สาธิตจะกลับไปสำรวจธุรกรรมของคุณ จึงทำงานได้โดยไม่ต้องตั้งค่า
14

แนวปฏิบัติที่ดี ขีดจำกัด และความปลอดภัย

ความปลอดภัย

  • เก็บคีย์ API และความลับสำหรับลงนามไว้ฝั่งเซิร์ฟเวอร์เท่านั้น หมุนคีย์ใดก็ตามทันทีหากรั่วไหล
  • ตรวจสอบลายเซ็นเว็บฮุกกับเนื้อหาดิบเสมอ และปฏิเสธการส่งที่เก่ากว่า ~5 นาที (การป้องกันการเล่นซ้ำ)
  • ดำเนินการของคุณเฉพาะเมื่อ confirmation.completed เท่านั้น — ไม่เคยที่การตอบกลับ /deduct (ณ จุดนั้นการเรียกเก็บยังไม่เป็นที่สิ้นสุด)
  • ใช้รายการ IP ที่อนุญาตและขอเฉพาะขอบเขตที่คุณต้องการจริง ๆ

ขีดจำกัด

  • การยืนยันหมดอายุหลัง 10 นาที ให้ถือว่าคำขอที่ไม่ยืนยันถูกละทิ้ง
  • ขีดจำกัดอัตราเป็นแบบต่อโปรเจกต์และปรับตามระดับสมาชิกของเจ้าของ
  • amount ต้องเป็นจำนวนเต็มบวก metadata จำกัดที่ 10 คีย์
Idempotency-Key ขจัดการลองใหม่ที่ซ้ำเว็บฮุกลองใหม่ 5× ด้วย backoffตอบ 2xx เร็ว ดำเนินการแบบอะซิงโครนัส