TheVe

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
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. 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. 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ường snake_case, tiền là số nguyên VND đã gồm VAT, thời gian theo RFC3339 múi giờ +07:00.

  3. 3

    Đặt vé với client_ref

    Mỗi đơn gửi một client_ref duy 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ẫnMô tả
GET/partner/products

Danh 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-DD

Chỗ 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/orders

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

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}/cancel

Huỷ 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ế.

Phản hồi tạo đơn (rút gọn)
{
  "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.

kMã khoá ký (kid) – tra khoá công khai trong /public/keys
tID doanh nghiệp phát hành vé
cMã vé 12 ký tự (Crockford Base32)
pID sản phẩm
sID suất (rỗng nếu không bán theo suất)
f / uHiệu lực từ / đến (Unix giây)
nSố lượt tối đa (0 = không giới hạn)
Kiểm tra QR (Node.js)
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.usedVé được quét hợp lệ tại cổng
card.topupThẻ trả trước được nạp tiền
card.blockedThẻ 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.

Ví dụ yêu cầu
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" }
}
Kiểm tra chữ ký
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.

HTTPcodeÝ nghĩa
400VALIDATION_ERRORDữ liệu không hợp lệ – xem message và details.field
401UNAUTHORIZEDThiếu hoặc sai X-API-Key, hoặc key đã bị thu hồi
403FORBIDDENKey không đủ quyền (ví dụ key chưa gắn đại lý mà gọi tạo đơn)
404NOT_FOUNDKhông tìm thấy sản phẩm, đơn hoặc vé
409SOLD_OUTHết chỗ trong ngày/suất hoặc vượt hạn mức bán
409SEAT_UNAVAILABLEGhế đã có người giữ
422CREDIT_LIMIT_EXCEEDEDVượt hạn mức công nợ còn lại của đại lý
429RATE_LIMITEDGọi quá nhanh – thử lại sau vài giây
500INTERNALLỗ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.