رفتن به محتوا
walaoraWalaora

مرجع API در Walaora

یک صندوق فروش (POS)، فروشگاه اینترنتی یا سامانهٔ داخلی را متصل کنید. JSON روی HTTPS، با یک کلید API برای هر یکپارچه‌سازی.

داده‌های مشتریان را فقط با آگاهی خودشان ارسال کنید و marketingConsent را تنها زمانی true قرار دهید که مشتری صراحتاً موافقت کرده باشد.

احراز هویت

در داشبورد → API و Webhooks یک کلید بسازید (طرح کسب‌وکار یا سازمانی). آن را در هر درخواست ارسال کنید. کلیدها فقط یک بار نمایش داده می‌شوند؛ اگر فاش شدند، آن‌ها را باطل کنید. محدودیت: 120 درخواست در دقیقه برای هر کلید.

Authorization: Bearer wl_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

نقاط پایانی (Endpoints)

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

GET/programs

فهرست برنامه‌های وفاداری.

GET/customers?phone=0551234567&tag=vip

یافتن مشتریان (50 مورد در هر صفحه، ?page=2…). ‎?tag=vip مشتریان دارای آن برچسب را فهرست می‌کند؛ هر مشتری برچسب‌هایش را دارد.

POST/customers

ثبت‌نام یک مشتری در یک برنامه. بدنه: { programId, name, phone, email?, marketingConsent?, locale? }. در صورت ایجاد 201 و اگر از قبل ثبت‌نام شده باشد 200 برمی‌گرداند.

GET/cards/{serial}

کارت بر اساس کد روی کارت / QR مشتری.

POST/cards/{serial}/earn

برنامه‌های مهری: { stamps?: 1–10 }. برنامه‌های امتیازی: { amount } به واحد پول فضای کاری. اختیاری: { branchId }. کارت‌های چندمهری: setId یکی از مجموعه‌های کارت را بفرستید.

POST/cards/{serial}/redeem

دریافت یک پاداش موجود، یا ارسال {"rewardId"} برای دریافت پاداشی از کاتالوگ که با مهر/امتیاز قیمت‌گذاری شده است.

POST/cards/{serial}/sell

بسته‌های پیش‌پرداخت: یک بسته می‌فروشد و دفعات آن به کارت اضافه می‌شود (پرداخت در صندوق شما). Webhook: package.sold.

POST/cards/{serial}/use

بسته‌های پیش‌پرداخت: یک بار استفاده کم می‌کند (اگر تمام یا منقضی شده باشد رد می‌شود). کارت کارکنان: امتیاز را در حد روزانه ثبت می‌کند. Webhooks: package.used، perk.used.

GET/coupons/{code}

بررسی یک کوپن بدون استفاده از آن: پیشنهاد مربوط، اینکه قابل استفاده است یا نه و اگر نیست، چرا (استفاده‌شده، منقضی یا متوقف).

POST/coupons/{code}/redeem

استفادهٔ یک‌باره از کوپن. اختیاری: { amount } (مبلغ فروش به واحد پول فضای کاری) و { branchId }. اگر قبلاً استفاده شده یا پیشنهاد پایان یافته باشد 409 برمی‌گرداند. Webhook: coupon.redeemed.

GET/coupon-offers

فهرست پیشنهادهای کوپن با ارزش، تاریخ پایان و اینکه آیا الان می‌توان کد ساخت (usable).

POST/coupon-offers/{id}/coupons

ساخت کدهای یک پیشنهاد. { customerId } یک کد به آن مشتری می‌دهد (در صفحهٔ کارت او نمایش داده می‌شود)؛ یا { count: 1–500 } کد بدون مشتری برای چاپ یا اشتراک‌گذاری. 201 همراه کدها؛ 409 اگر پیشنهاد متوقف شده یا به پایان رسیده باشد.

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

تلاش مجدد و Idempotency

در درخواست‌های POST، سرآیند Idempotency-Key را ارسال کنید (هر رشتهٔ یکتا، مثلاً شمارهٔ رسید شما). تلاش مجدد با همان کلید در 24 ساعت، به‌جای دو بار مهر زدن، همان پاسخ اولیه را برمی‌گرداند.

خطاها

خطاها یک بدنهٔ JSON به شکل { error: { code, message } } همراه با یک وضعیت HTTP برمی‌گردانند: 401 کلید نادرست · 403 plan_required · 404 یافت نشد · 409 cooldown / no_reward / program_inactive · 422 خطای اعتبارسنجی · 429 محدودیت تعداد درخواست.

Webhooks

در داشبورد یک نقطهٔ پایانی HTTPS اضافه کنید و رویدادها را انتخاب کنید. هر ارسال یک درخواست POST با JSON به شکل { id, type, createdAt, data } و این سرآیندهاست: Walaora-Event، Walaora-Delivery و Walaora-Signature. ظرف 10 ثانیه با کد 2xx پاسخ دهید. ارسال‌های ناموفق پس از 1 دقیقه، 5 دقیقه، 30 دقیقه، 2 ساعت و 12 ساعت دوباره تلاش می‌شوند.

رویدادها

تأیید امضاها (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 ?? ""));
}