ภาพรวม
Vito API ให้แอปของคุณทำงานกับยอด Vito ของผู้ใช้จากภายใน Discord — อ่านยอด เรียกเก็บจากยอด หรือเครดิตคืน ผู้ใช้ยืนยันการเรียกเก็บแต่ละครั้งด้วย PIN บน vetox.io ก่อนที่ Vito ใด ๆ จะเคลื่อนไหว และแอปของคุณจะได้รับแจ้งผลลัพธ์ 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 คีย์เดิมจะยังทำงานต่อในช่วงผ่อนผัน 24 ชั่วโมงเพื่อให้คุณนำคีย์ใหม่ไปใช้ได้โดยไม่มีดาวน์ไทม์ แล้วจึงหยุด
ขั้นตอนการยืนยันการเรียกเก็บ
การเรียกเก็บทุกครั้งจะผ่านการยืนยันที่ผู้ใช้อนุมัติด้วย PIN — คีย์ของคุณเพียงอย่างเดียวไม่เคยเคลื่อน Vito:
- 1แอปของคุณเรียก POST /deduct ด้วยผู้ใช้ จำนวน guildId ต้นทาง และรายละเอียดรายการ
- 2Vito สร้างการยืนยันที่รอดำเนินการ (ใช้ได้ 10 นาที) และคืนค่า confirmUrl หากให้ guildId มา ผู้ใช้จะได้รับ DM พร้อมรายละเอียดและปุ่มไปยังหน้ายืนยันด้วย
- 3ผู้ใช้เปิด confirmUrl บน vetox.io และอนุมัติการเรียกเก็บด้วย PIN กระเป๋าเงินของตน
- 4Vito หัก Vito บันทึกธุรกรรม และ — หากคุณกำหนดค่าเว็บฮุกไว้ — ส่งเหตุการณ์ confirmation.completed ที่ลงนามไปยัง URL คอลแบ็กของคุณ
- 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
}เว็บฮุกและการตรวจสอบลายเซ็น
เพิ่ม URL คอลแบ็ก https หนึ่งรายการหรือมากกว่าในแท็บการตั้งค่า Vito จะส่ง POST ที่ลงนามไปยัง URL ที่กำหนดค่าแต่ละรายการทุกครั้งที่การยืนยันถึงสถานะสุดท้าย โดยลองใหม่สูงสุด 5 ครั้งด้วย backoff แบบเอ็กซ์โพเนนเชียล
การตรวจสอบลายเซ็น
การส่งแต่ละครั้งมีส่วนหัว 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
}
}รหัสข้อผิดพลาด
ข้อผิดพลาดคืนค่าสถานะที่ไม่ใช่ 2xx พร้อมเนื้อหา 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 และ ID เซิร์ฟเวอร์ (guild) ทดสอบของคุณลงใน config.json
- 2รัน npm install && npm start แล้วเปิด http://localhost:4000
- 3ค้นหาผู้ใช้ด้วย ID Discord เลือกรายการ และเริ่มการเรียกเก็บ — สาธิตจะเรียก /deduct และแสดง confirmUrl
- 4อนุมัติ PIN บน vetox.io สาธิตจะตรวจพบการเสร็จสิ้น (ผ่านเว็บฮุกหากกำหนดค่าไว้ มิฉะนั้นผ่านการสำรวจ) และดำเนินการตัวอย่างให้เสร็จ
แนวปฏิบัติที่ดี ขีดจำกัด และความปลอดภัย
ความปลอดภัย
- เก็บคีย์ API และความลับสำหรับลงนามไว้ฝั่งเซิร์ฟเวอร์เท่านั้น หมุนคีย์ใดก็ตามทันทีหากรั่วไหล
- ตรวจสอบลายเซ็นเว็บฮุกกับเนื้อหาดิบเสมอ และปฏิเสธการส่งที่เก่ากว่า ~5 นาที (การป้องกันการเล่นซ้ำ)
- ดำเนินการของคุณเฉพาะเมื่อ confirmation.completed เท่านั้น — ไม่เคยที่การตอบกลับ /deduct (ณ จุดนั้นการเรียกเก็บยังไม่เป็นที่สิ้นสุด)
- ใช้รายการ IP ที่อนุญาตและขอเฉพาะขอบเขตที่คุณต้องการจริง ๆ
ขีดจำกัด
- การยืนยันหมดอายุหลัง 10 นาที ให้ถือว่าคำขอที่ไม่ยืนยันถูกละทิ้ง
- ขีดจำกัดอัตราเป็นแบบต่อโปรเจกต์และปรับตามระดับสมาชิกของเจ้าของ
- amount ต้องเป็นจำนวนเต็มบวก metadata จำกัดที่ 10 คีย์