Langkau ke kandungan
walaoraWalaora

Rujukan API Walaora

Sambungkan sistem POS, kedai e-dagang atau sistem dalaman. JSON melalui HTTPS, satu kunci API bagi setiap integrasi.

Hanya hantar data pelanggan dengan pengetahuan mereka, dan tetapkan marketingConsent kepada true hanya apabila pelanggan telah bersetuju secara jelas.

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_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

Titik akhir

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

GET/programs

Senaraikan program kesetiaan.

GET/customers?phone=0551234567&tag=vip

Cari pelanggan (50 setiap halaman, ?page=2…). ?tag=vip menyenaraikan pelanggan dengan tag itu; setiap pelanggan disertakan tag-nya.

POST/customers

Daftarkan 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}/earn

Program 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}/redeem

Tebus satu ganjaran yang tersedia, atau hantar {"rewardId"} untuk menebus ganjaran katalog yang berharga dalam cop/mata.

POST/cards/{serial}/sell

Pakej prabayar: menjual satu pakej, penggunaannya ditambah ke kad (bayaran di kaunter anda). Webhook: package.sold.

POST/cards/{serial}/use

Pakej 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}/redeem

Guna 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-offers

Senarai tawaran kupon dengan nilai, tarikh tamat dan sama ada kod boleh dicipta sekarang (usable).

POST/coupon-offers/{id}/coupons

Cipta 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

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 ?? ""));
}