Bỏ qua đến nội dung
walaoraWalaora

Tài liệu tham khảo API Walaora

Kết nối hệ thống POS, cửa hàng thương mại điện tử hoặc hệ thống nội bộ. JSON qua HTTPS, mỗi tích hợp một khóa API.

Chỉ gửi dữ liệu của khách hàng khi họ biết, và chỉ đặt marketingConsent thành true khi khách hàng đã đồng ý rõ ràng.

Xác thực

Tạo khóa trong Bảng điều khiển → API & webhooks (gói Doanh nghiệp hoặc Tập đoàn). Gửi khóa trong mọi yêu cầu. Khóa chỉ được hiển thị một lần; hãy thu hồi nếu bị lộ. Giới hạn: 120 yêu cầu mỗi phút cho mỗi khóa.

Authorization: Bearer wl_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

Endpoint

Base URL: https://walaora.com/api/v1

GET/programs

Liệt kê các chương trình khách hàng thân thiết.

GET/customers?phone=0551234567&tag=vip

Tìm khách hàng (50 mục mỗi trang, ?page=2…). ?tag=vip liệt kê khách có nhãn đó; mỗi khách kèm theo các nhãn của mình.

POST/customers

Đăng ký khách hàng vào một chương trình. Phần thân: { programId, name, phone, email?, marketingConsent?, locale? }. Trả về 201 khi được tạo, 200 nếu đã đăng ký từ trước.

GET/cards/{serial}

Thẻ theo mã trên thẻ / mã QR của khách hàng.

POST/cards/{serial}/earn

Chương trình tích dấu: { stamps?: 1–10 }. Chương trình tích điểm: { amount } theo đơn vị tiền tệ của không gian làm việc. Tùy chọn { branchId }. Thẻ nhiều loại tem: gửi setId của một bộ trên thẻ.

POST/cards/{serial}/redeem

Đổi một phần thưởng hiện có, hoặc gửi {"rewardId"} để đổi một phần thưởng trong danh mục được định giá bằng dấu/điểm.

POST/cards/{serial}/sell

Gói trả trước: bán một gói, số lượt được cộng vào thẻ (thanh toán tại quầy của bạn). Webhook: package.sold.

POST/cards/{serial}/use

Gói trả trước: trừ một lượt (từ chối khi hết hoặc hết hạn). Thẻ nhân viên: ghi nhận quyền lợi trong giới hạn mỗi ngày. Webhooks: package.used, perk.used.

GET/coupons/{code}

Kiểm tra phiếu giảm giá mà không sử dụng: ưu đãi của phiếu, phiếu có dùng được không và nếu không thì vì sao (đã dùng, hết hạn hoặc tạm dừng).

POST/coupons/{code}/redeem

Dùng phiếu giảm giá một lần. Tùy chọn { amount } (số tiền hóa đơn, theo đơn vị tiền tệ của không gian làm việc) và { branchId }. Trả về 409 khi phiếu đã được dùng hoặc ưu đãi đã kết thúc. Webhook: coupon.redeemed.

GET/coupon-offers

Liệt kê các ưu đãi phiếu giảm giá cùng giá trị, ngày kết thúc và hiện có thể tạo mã hay không (usable).

POST/coupon-offers/{id}/coupons

Tạo mã của một ưu đãi. { customerId } cấp một mã cho khách hàng đó (hiện trên trang thẻ của họ); hoặc { count: 1–500 } mã không gắn khách hàng để in hoặc chia sẻ. 201 kèm các mã; 409 nếu ưu đãi đang tạm dừng hoặc đã kết thúc.

curl -X POST https://walaora.com/api/v1/cards/ABCDE23456/earn \
  -H "Authorization: Bearer wl_live_…" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: receipt-10045" \
  -d '{"stamps": 1}'

Thử lại & tính lũy đẳng

Với các yêu cầu POST, hãy gửi header Idempotency-Key (một chuỗi duy nhất bất kỳ, ví dụ số biên lai của bạn). Thử lại với cùng khóa trong vòng 24 giờ sẽ trả về phản hồi ban đầu thay vì đóng dấu hai lần.

Lỗi

Lỗi trả về phần thân JSON { error: { code, message } } kèm mã trạng thái HTTP: 401 khóa không hợp lệ · 403 plan_required · 404 không tìm thấy · 409 cooldown / no_reward / program_inactive · 422 lỗi xác thực dữ liệu · 429 vượt giới hạn tần suất.

Webhooks

Thêm một endpoint HTTPS trong bảng điều khiển và chọn sự kiện. Mỗi lần gửi là một yêu cầu POST với JSON { id, type, createdAt, data } và các header sau: Walaora-Event, Walaora-Delivery và Walaora-Signature. Hãy phản hồi bằng mã 2xx trong vòng 10 giây. Các lần gửi thất bại sẽ được thử lại sau 1 phút, 5 phút, 30 phút, 2 giờ và 12 giờ.

Sự kiện

Xác minh chữ ký (Node.js)

import crypto from "node:crypto";

// rawBody: the exact request body string; header: req.headers["walaora-signature"]
export function verifyWalaora(rawBody, header, secret) {
  const parts = Object.fromEntries(header.split(",").map((p) => p.split("=")));
  const t = Number(parts.t);
  if (!t || Math.abs(Date.now() / 1000 - t) > 300) return false; // replay window
  const expected = crypto.createHmac("sha256", secret).update(`${t}.${rawBody}`).digest("hex");
  return crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(parts.v1 ?? ""));
}