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.
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_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxEndpoint
Base URL: https://walaora.com/api/v1
| GET | /programsLiệt kê các chương trình khách hàng thân thiết. |
| GET | /customers?phone=0551234567&tag=vipTì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}/earnChươ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}/sellGó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}/useGó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}/redeemDù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-offersLiệ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}/couponsTạ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
customer.enrolledcard.earnedreward.unlockedreward.redeemedcard.adjustedcustomer.deletedfeedback.receivedpackage.soldpackage.usedperk.usedcoupon.redeemed
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 ?? ""));
}