Cho phép các ứng dụng đã phê duyệt tính phí Vito của bạn với xác nhận PIN và quản lý các yêu cầu thanh toán từ trang Purchases của bạn.
Yêu cầu Hội viên Silver trở lên — cả khi đăng ký lẫn cho mọi lệnh gọi đã xác thực. Nếu Hội viên của chủ khóa hết hạn, dự án bị đóng băng cho đến khi họ đăng ký lại.
Một REST API cho phép ứng dụng của bạn làm việc với số dư Vito của người dùng ngay từ trong Discord — đọc, trừ, cộng, hoặc chuyển giữa những người dùng. Mọi endpoint đều trả về JSON và được đánh phiên bản dưới /v1.
Vito không bao giờ chuyển sang hay đến từ tiền thật. Nó chỉ di chuyển giữa các số dư Vetox.
Khóa và khóa bí mật ký của bạn chỉ hiển thị đúng một lần. Sau khi được duyệt, bạn có cửa sổ 7 ngày để hiện chúng ở tab khóa API. Vetox chỉ lưu một bản băm và không thể hiện lại — bỏ lỡ cửa sổ này thì bạn phải xoay vòng khóa.
Chỉ giữ nó ở phía máy chủ — ai cầm được khóa đều có thể trừ tiền người dùng của bạn
Xoay vòng từ tab khóa API. Khóa cũ vẫn hoạt động thêm 24 giờ như thời gian ân hạn, để bạn triển khai mà không gián đoạn
Khóa bí mật ký webhook được xoay vòng riêng, với khoảng chồng lấn 24 giờ của chính nó
Chỉ riêng khóa của bạn không thể làm Vito của người dùng dịch chuyển. Mọi lần trừ tiền đều cần người dùng phê duyệt bằng mã PIN ví của họ, trên vetox.io — không bao giờ trong ứng dụng của bạn và không bao giờ trong Discord.
1
Ứng dụng của bạn gọi POST /v1/deduct
Kèm người dùng, số tiền, guildId khởi phát và chi tiết mặt hàng.
2
Vito trả về một confirmUrl
Một xác nhận đang chờ, có hiệu lực 10 phút. Người dùng cũng nhận được tin nhắn riêng.
3
Người dùng phê duyệt bằng mã PIN
Trên vetox.io.
4
Vito quyết toán và thông báo
Số dư bị trừ, giao dịch được ghi lại, và một webhook có chữ ký được gửi đi nếu bạn đã cấu hình.
5
Ứng dụng của bạn xác minh và hoàn tất
Kiểm tra chữ ký, rồi mở khóa nội dung hoặc giao mặt hàng.
Chỉ hoàn tất hành động của bạn khi có confirmation.completed — đừng bao giờ dựa vào phản hồi của /deduct. Ở thời điểm đó khoản trừ chưa phải là cuối cùng.
POST /v1/add cộng Vito cho một người dùng từ số dư của chính bạn — để thưởng hoặc hoàn tiền. Cùng các trường như /deduct, trừ guildId và product.
Khác với khoản trừ, khoản cộng không có bước xác nhận — nó được quyết toán ngay. Cần scope credit:create và đủ số dư, nếu không lệnh gọi trả về 402 VITO_INSUFFICIENT_OWNER_FUNDS.
Hai header khác đi kèm mỗi lần gửi — hãy dùng X-Vito-Event-Id làm khóa chống trùng lặp, vì lần thử lại sẽ gửi đúng id đó:
Header
Chứa
X-Vito-Event-Id
Id cố định cho sự kiện này — giống nhau qua mọi lần thử lại
X-Vito-Event-Type
Ví dụ confirmation.completed
Trong lúc xoay vòng khóa bí mật ký, header mang nhiều hơn một chữ ký, mới nhất trước:
X-Vito-Signature: ts=<unix>;h1=<mới>;h1=<cũ>
Hãy chấp nhận nếu bất kỳh1 nào khớp. Bộ xác minh chỉ đọc cái đầu tiên sẽ từ chối mọi webhook cho tới khi triển khai xong khóa mới — đúng thứ mà khoảng chồng lấn 24 giờ sinh ra để tránh.
Tính lại HMAC trên <ts>:<rawBody> bằng khóa bí mật ký của bạn và so sánh trong thời gian hằng số.
const crypto = require('crypto');function verify(rawBody, header, secret) { const parts = header.split(';'); const ts = parts.find(p => p.startsWith('ts='))?.slice(3); const sigs = parts.filter(p => p.startsWith('h1=')).map(p => p.slice(3)); if (!ts || sigs.length === 0) return false; // Reject anything older than ~5 minutes — replay protection. if (Math.abs(Date.now() / 1000 - Number(ts)) > 300) return false; const expected = Buffer.from( crypto.createHmac('sha256', secret).update(`${ts}:${rawBody}`).digest('hex'), ); // Any matching signature is valid — a rotation emits several. return sigs.some(sig => { const actual = Buffer.from(sig); // timingSafeEqual throws when the lengths differ, so check first. return ( actual.length === expected.length && crypto.timingSafeEqual(actual, expected) ); });}
Hãy xác minh trên body thô, trước khi việc phân tích JSON hay middleware viết lại nó.
Còn có giới hạn theo từng IP bằng một nửa hạn mức mỗi phút của bạn, với mức sàn là 30.
Khi tải cao, các endpoint ghi thất bại theo hướng đóng — khoản trừ bị từ chối thay vì mạo hiểm chi tiêu trùng. Các endpoint đọc thất bại theo hướng mở. Hãy coi một thao tác ghi bị từ chối là “chưa xảy ra” và thử lại.
Gửi header Idempotency-Key để chống trùng lặp khi thử lại một cách an toàn.