TheVe cho nhà phát triển
Bán vé của hàng trăm điểm đến ngay trên hệ thống của bạn
Một API REST gọn cho OTA, website du lịch và ứng dụng đối tác: xem chỗ trống, đặt vé bằng hạn mức công nợ, nhận vé QR ký số dùng được cả khi cổng mất mạng.
curl https://theve.vietsoftware.vn/api/v1/partner/products \
-H "X-API-Key: tvk_demo_mayxanh_travelhub_2026"- Xác thực bằng một header
- QR ký Ed25519, soát ngoại tuyến
- Webhook ký HMAC, tự thử lại
Bắt đầu nhanh
- 1
Nhận API key
Doanh nghiệp tạo key trong Quản trị → Tích hợp và gắn với một đại lý. Key chỉ hiển thị một lần – lưu ở máy chủ của bạn, không đưa vào ứng dụng di động hay trình duyệt.
- 2
Gửi key trong mọi yêu cầu
Header
X-API-Key: tvk_…, dữ liệu JSON UTF-8, tên trườngsnake_case, tiền là số nguyên VND đã gồm VAT, thời gian theo RFC3339 múi giờ +07:00. - 3
Đặt vé với client_ref
Mỗi đơn gửi một
client_refduy nhất do bạn sinh. Gửi lại cùng giá trị sẽ nhận về đúng đơn cũ – an toàn khi mạng chập chờn.
Base URL
https://theve.vietsoftware.vn/api/v1
Key dùng thử (Mây Xanh × TravelHub)
tvk_demo_mayxanh_travelhub_2026
Thử ngay
Gọi thật tới máy chủ demo bằng key dùng thử. Kết quả là danh sách sản phẩm đang mở bán cho kênh API.
GET /api/v1/partner/products
Danh sách endpoint
Tất cả đường dẫn tính từ https://theve.vietsoftware.vn/api/v1.
| Phương thức | Đường dẫn | Mô tả |
|---|---|---|
| GET | /partner/productsDanh sách sản phẩm mở bán cho kênh API, kèm bảng giá theo đối tượng | Danh sách sản phẩm mở bán cho kênh API, kèm bảng giá theo đối tượng |
| GET | /partner/products/{id}/availability?date=YYYY-MM-DDChỗ còn lại trong ngày và các suất/chuyến còn mở | Chỗ còn lại trong ngày và các suất/chuyến còn mở |
| POST | /partner/ordersTạo đơn và phát hành vé ngay, trừ vào hạn mức công nợ của đại lý gắn với key | Tạo đơn và phát hành vé ngay, trừ vào hạn mức công nợ của đại lý gắn với key |
| GET | /partner/orders/{code}Chi tiết đơn và danh sách vé (mã, QR, trạng thái) | Chi tiết đơn và danh sách vé (mã, QR, trạng thái) |
| POST | /partner/orders/{code}/cancelHuỷ và hoàn toàn bộ đơn theo chính sách hoàn của từng sản phẩm | Huỷ và hoàn toàn bộ đơn theo chính sách hoàn của từng sản phẩm |
| GET | /partner/tickets/{code}Trạng thái một vé: còn hiệu lực, đã dùng, số lượt còn lại | Trạng thái một vé: còn hiệu lực, đã dùng, số lượt còn lại |
Ví dụ gọi API
curl https://theve.vietsoftware.vn/api/v1/partner/products \
-H "X-API-Key: tvk_demo_mayxanh_travelhub_2026"Sản phẩm bán theo suất cần thêm session_id (lấy từ availability); sản phẩm có ghế cần seat_ids và số lượng bằng số ghế.
{
"data": {
"code": "DH260920-7KQ2MX",
"status": "COMPLETED",
"payment_method": "AGENT_CREDIT",
"total": 675000,
"tickets": [
{
"code": "8H3K-9QZP-4MWT",
"qr": "TV1.eyJrIjoiVC1DNVFN….Sgj6eg5rwb6M34…",
"product_name": "Vé vào cổng Mây Xanh",
"audience_label": "Người lớn",
"valid_from": "2026-09-20T00:00:00+07:00",
"valid_to": "2026-09-20T23:59:59+07:00",
"remaining_entries": 1,
"status": "ACTIVE"
}
]
}
}Mã QR ký số
Mỗi vé mang một QR tự chứng minh tính hợp lệ, nên máy soát vé vẫn kiểm tra được khi mất mạng.
TV1.<payload_base64url>.<chữ_ký_base64url>
Chữ ký Ed25519 được tính trên chuỗi ASCII của phần payload (base64url không padding). Khoá riêng chỉ nằm trên máy chủ; thiết bị chỉ giữ khoá công khai từ GET /public/keys.
k | Mã khoá ký (kid) – tra khoá công khai trong /public/keys |
t | ID doanh nghiệp phát hành vé |
c | Mã vé 12 ký tự (Crockford Base32) |
p | ID sản phẩm |
s | ID suất (rỗng nếu không bán theo suất) |
f / u | Hiệu lực từ / đến (Unix giây) |
n | Số lượt tối đa (0 = không giới hạn) |
import { createPublicKey, verify } from "node:crypto";
// qr = "TV1.<payload_b64url>.<signature_b64url>"
export function verifyTicketQr(qr, keys /* from GET /public/keys */) {
const [prefix, payloadB64, sigB64] = qr.split(".");
if (prefix !== "TV1") return null;
const payload = JSON.parse(Buffer.from(payloadB64, "base64url").toString("utf8"));
const key = keys.find((k) => k.kid === payload.k);
if (!key) return null;
const spki = Buffer.concat([
Buffer.from("302a300506032b6570032100", "hex"), // Ed25519 SPKI header
Buffer.from(key.public_key, "base64"),
]);
const ok = verify(null, Buffer.from(payloadB64, "ascii"),
createPublicKey({ key: spki, format: "der", type: "spki" }),
Buffer.from(sigB64, "base64url"));
const now = Math.floor(Date.now() / 1000);
return ok && payload.f <= now && now <= payload.u ? payload : null;
}Thẻ ảo dùng dạng TVC1.<slug doanh nghiệp>.<số thẻ> – không ký, luôn tra cứu trực tuyến.
Webhook
Đăng ký URL trong Quản trị → Tích hợp để nhận sự kiện theo thời gian thực.
order.paid | Đơn đã thanh toán và vé được phát hành |
order.refunded | Đơn được hoàn một phần hoặc toàn bộ |
order.cancelled | Đơn chờ bị huỷ hoặc hết hạn giữ chỗ |
ticket.used | Vé được quét hợp lệ tại cổng |
card.topup | Thẻ trả trước được nạp tiền |
card.blocked | Thẻ bị khoá hoặc báo mất |
Quy tắc gửi
Header X-TheVe-Signature: sha256=<hex> là HMAC-SHA256 của nguyên văn body với secret của webhook; X-TheVe-Event là tên sự kiện.
Trả mã 2xx để xác nhận. Nếu lỗi, TheVe gửi lại tối đa 6 lần sau 1, 5, 15, 60, 180 và 720 phút – hãy xử lý trùng theo trường id.
POST /theve/webhook
X-TheVe-Event: ticket.used
X-TheVe-Signature: sha256=5d41402abc4b2a76b9719d911017c592…
Content-Type: application/json
{
"id": "0b0f5c7e-6a52-4f7e-9f63-2c0b8c5c1a90",
"event": "ticket.used",
"created_at": "2026-09-20T08:31:12+07:00",
"tenant_id": "a7664fac-50ed-4013-88a7-bb93dbe2e4ca",
"data": { "ticket_code": "8H3K-9QZP-4MWT", "gate": "Cổng chính số 1", "direction": "IN" }
}import crypto from "node:crypto";
import express from "express";
const app = express();
app.post("/theve/webhook", express.raw({ type: "application/json" }), (req, res) => {
const expected = "sha256=" + crypto
.createHmac("sha256", process.env.THEVE_WEBHOOK_SECRET)
.update(req.body) // raw bytes, không parse trước khi ký
.digest("hex");
const got = req.get("X-TheVe-Signature") ?? "";
if (got.length !== expected.length ||
!crypto.timingSafeEqual(Buffer.from(got), Buffer.from(expected))) {
return res.status(401).end();
}
const event = JSON.parse(req.body.toString("utf8"));
// event.id là duy nhất – bỏ qua nếu đã xử lý (TheVe có thể gửi lại)
console.log(event.event, event.data);
res.status(200).end();
});Mã lỗi
Lỗi luôn có dạng { "error": { "code", "message", "details" } }; message là tiếng Việt, hiển thị được cho người dùng.
| HTTP | code | Ý nghĩa |
|---|---|---|
| 400 | VALIDATION_ERROR | Dữ liệu không hợp lệ – xem message và details.field |
| 401 | UNAUTHORIZED | Thiếu hoặc sai X-API-Key, hoặc key đã bị thu hồi |
| 403 | FORBIDDEN | Key không đủ quyền (ví dụ key chưa gắn đại lý mà gọi tạo đơn) |
| 404 | NOT_FOUND | Không tìm thấy sản phẩm, đơn hoặc vé |
| 409 | SOLD_OUT | Hết chỗ trong ngày/suất hoặc vượt hạn mức bán |
| 409 | SEAT_UNAVAILABLE | Ghế đã có người giữ |
| 422 | CREDIT_LIMIT_EXCEEDED | Vượt hạn mức công nợ còn lại của đại lý |
| 429 | RATE_LIMITED | Gọi quá nhanh – thử lại sau vài giây |
| 500 | INTERNAL | Lỗi hệ thống – thử lại với cùng client_ref |
Cần hỗ trợ tích hợp? Liên hệ doanh nghiệp bạn hợp tác hoặc xem giải pháp cho doanh nghiệp.