Rujukan API Walaora
Sambungkan sistem POS, kedai e-dagang atau sistem dalaman. JSON melalui HTTPS, satu kunci API bagi setiap integrasi.
Pengesahan
Cipta kunci di Papan pemuka → API & webhooks (pelan Perniagaan atau Perusahaan). Hantar kunci tersebut dengan setiap permintaan. Kunci hanya dipaparkan sekali; batalkan kunci jika terdedah. Had: 120 permintaan seminit bagi setiap kunci.
Authorization: Bearer wl_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxTitik akhir
Base URL: https://walaora.com/api/v1
| GET | /programsSenaraikan program kesetiaan. |
| GET | /customers?phone=0551234567&tag=vipCari pelanggan (50 setiap halaman, ?page=2…). ?tag=vip menyenaraikan pelanggan dengan tag itu; setiap pelanggan disertakan tag-nya. |
| POST | /customersDaftarkan pelanggan dalam program. Badan: { programId, name, phone, email?, marketingConsent?, locale? }. Memulangkan 201 apabila dicipta, 200 jika sudah berdaftar. |
| GET | /cards/{serial}Kad mengikut kod pada kad / QR pelanggan. |
| POST | /cards/{serial}/earnProgram cop: { stamps?: 1–10 }. Program mata: { amount } dalam mata wang ruang kerja. Pilihan { branchId }. Kad pelbagai setem: hantar setId, salah satu set kad. |
| POST | /cards/{serial}/redeemTebus satu ganjaran yang tersedia, atau hantar {"rewardId"} untuk menebus ganjaran katalog yang berharga dalam cop/mata. |
| POST | /cards/{serial}/sellPakej prabayar: menjual satu pakej, penggunaannya ditambah ke kad (bayaran di kaunter anda). Webhook: package.sold. |
| POST | /cards/{serial}/usePakej prabayar: menolak satu penggunaan (ditolak jika habis atau tamat). Kad pekerja: merekod keistimewaan dalam had harian. Webhook: package.used, perk.used. |
| GET | /coupons/{code}Semak kupon tanpa menggunakannya: tawarannya, sama ada ia boleh digunakan dan, jika tidak, sebabnya (sudah ditebus, tamat tempoh atau dijeda). |
| POST | /coupons/{code}/redeemGuna kupon sekali. Pilihan { amount } (jualan, dalam mata wang ruang kerja) dan { branchId }. 409 jika sudah digunakan atau tawaran telah tamat. Webhook: coupon.redeemed. |
| GET | /coupon-offersSenarai tawaran kupon dengan nilai, tarikh tamat dan sama ada kod boleh dicipta sekarang (usable). |
| POST | /coupon-offers/{id}/couponsCipta kod bagi sesuatu tawaran. { customerId } memberi satu kod kepada pelanggan itu (dipaparkan di halaman kadnya); atau { count: 1–500 } kod tanpa pelanggan untuk dicetak atau dikongsi. 201 bersama kod; 409 jika tawaran dijeda atau telah tamat. |
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}'Cubaan semula & idempotensi
Untuk permintaan POST, hantar pengepala Idempotency-Key (sebarang rentetan unik, contohnya nombor resit anda). Cubaan semula dengan kunci yang sama dalam tempoh 24 jam akan memulangkan respons asal dan bukannya menambah cop dua kali.
Ralat
Ralat memulangkan badan JSON { error: { code, message } } bersama status HTTP: 401 kunci tidak sah · 403 plan_required · 404 tidak ditemui · 409 cooldown / no_reward / program_inactive · 422 pengesahan · 429 had kadar dicapai.
Webhooks
Tambah titik akhir HTTPS di papan pemuka dan pilih acara. Setiap penghantaran ialah POST dengan JSON { id, type, createdAt, data } dan pengepala berikut: Walaora-Event, Walaora-Delivery dan Walaora-Signature. Balas dengan 2xx dalam masa 10 saat. Penghantaran yang gagal akan dicuba semula selepas 1 min, 5 min, 30 min, 2 jam dan 12 jam.
Acara
customer.enrolledcard.earnedreward.unlockedreward.redeemedcard.adjustedcustomer.deletedfeedback.receivedpackage.soldpackage.usedperk.usedcoupon.redeemed
Mengesahkan tandatangan (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 ?? ""));
}