Langsung ke konten
walaoraWalaora

Referensi API Walaora

Hubungkan sistem kasir (POS), toko online, atau sistem internal. JSON melalui HTTPS, satu kunci API per integrasi.

Kirim data pelanggan hanya dengan sepengetahuan mereka, dan setel marketingConsent ke true hanya jika pelanggan secara tegas menyetujuinya.

Autentikasi

Buat kunci di Dasbor → API & webhook (paket Bisnis atau Enterprise). Kirimkan kunci tersebut di setiap permintaan. Kunci hanya ditampilkan sekali; cabut kunci jika bocor. Batas: 120 permintaan per menit per kunci.

Authorization: Bearer wl_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

Endpoint

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

GET/programs

Daftar program loyalitas.

GET/customers?phone=0551234567&tag=vip

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

POST/customers

Daftarkan pelanggan ke sebuah program. Body: { programId, name, phone, email?, marketingConsent?, locale? }. Mengembalikan 201 jika dibuat, 200 jika sudah terdaftar.

GET/cards/{serial}

Kartu berdasarkan kode pada kartu / QR pelanggan.

POST/cards/{serial}/earn

Program stempel: { stamps?: 1–10 }. Program poin: { amount } dalam mata uang ruang kerja. Opsional { branchId }. Kartu multi-stempel: kirim setId, salah satu set kartu.

POST/cards/{serial}/redeem

Tukarkan satu hadiah yang tersedia, atau kirim {"rewardId"} untuk menukarkan hadiah katalog yang dihargai dalam stempel/poin.

POST/cards/{serial}/sell

Paket prabayar: menjual satu paket, pemakaiannya ditambahkan ke kartu (pembayaran di kasir Anda). Webhook: package.sold.

POST/cards/{serial}/use

Paket prabayar: mengurangi satu pemakaian (ditolak jika habis atau kedaluwarsa). Kartu karyawan: mencatat keuntungan dalam batas hariannya. Webhook: package.used, perk.used.

GET/coupons/{code}

Memeriksa kupon tanpa memakainya: penawarannya, apakah bisa dipakai dan, jika tidak, alasannya (sudah ditukarkan, kedaluwarsa, atau dijeda).

POST/coupons/{code}/redeem

Memakai kupon satu kali. { amount } (nilai penjualan, dalam mata uang ruang kerja) dan { branchId } opsional. 409 jika sudah dipakai atau penawaran sudah berakhir. Webhook: coupon.redeemed.

GET/coupon-offers

Daftar penawaran kupon beserta nilainya, tanggal berakhir, dan apakah kode bisa dibuat sekarang (usable).

POST/coupon-offers/{id}/coupons

Buat kode dari sebuah penawaran. { customerId } memberi satu kode kepada pelanggan itu (tampil di halaman kartunya); atau { count: 1–500 } kode tanpa pelanggan untuk dicetak atau dibagikan. 201 beserta kodenya; 409 jika penawaran dijeda atau sudah berakhir.

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}'

Percobaan ulang & idempotensi

Untuk permintaan POST, kirim header Idempotency-Key (string unik apa pun, misalnya nomor struk Anda). Mencoba ulang dengan kunci yang sama dalam 24 jam akan mengembalikan respons asli, bukan menambahkan stempel dua kali.

Galat

Galat mengembalikan body JSON { error: { code, message } } dengan status HTTP: 401 kunci salah · 403 plan_required · 404 tidak ditemukan · 409 cooldown / no_reward / program_inactive · 422 validasi · 429 batas permintaan terlampaui.

Webhooks

Tambahkan endpoint HTTPS di dasbor dan pilih event. Setiap pengiriman berupa POST dengan JSON { id, type, createdAt, data } dan header berikut: Walaora-Event, Walaora-Delivery, dan Walaora-Signature. Balas dengan 2xx dalam 10 detik. Pengiriman yang gagal dicoba ulang setelah 1 menit, 5 menit, 30 menit, 2 jam, dan 12 jam.

Event

Memverifikasi tanda tangan (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 ?? ""));
}