OPT Retention
للمطورين

واجهة API والويب هوك

اربط الكاشير أو متجرك أو نظامك الخاص بـ OPT Retention: تسجيل العملاء والعمليات تلقائيًا، وإشعار فوري عند كل حدث. مفاتيح لكل تكامل، سجل طلبات، وإعادة إرسال عند الفشل.

البدء في 5 دقائق

واجهة API تتيح لنظام الكاشير أو المتجر أو أي برنامج لديك أن يسجّل العملاء والعمليات تلقائيًا في حسابك، وأن يستقبل إشعارًا فوريًا (ويب هوك) عند كل حدث مهم: تسجيل عميل، عملية، مكافأة، تقييم، موعد.

  1. من لوحة التحكم: الإدارة ← API و Webhooks. الميزة ضمن باقة الاحتراف أو كإضافة «ربط API» على الباقات الأخرى.
  2. اضغط + مفتاح API، سمِّه (مثلًا «كاشير الفرع الرئيسي») وحدد النطاقات. السر يظهر مرة واحدة فقط — انسخه فورًا.
  3. جرّب أول طلب:
curl https://retention.aimenu.me/api/v1/customers?limit=5 \
  -H "Authorization: Bearer grs_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
العنوان الأساسي: https://retention.aimenu.me/api/v1 · كل الطلبات والردود JSON بترميز UTF-8 · التواريخ بصيغة ISO 8601 (2026-09-22T14:05:00Z) · المبالغ بالهللات/القروش كأعداد صحيحة (amount_minor) أو كمبلغ عشري (amount).

المصادقة والنطاقات

أرسل المفتاح في ترويسة Authorization: Bearer <المفتاح> (أو X-Api-Key). المفتاح يخص منشأة واحدة ولا يرى غيرها.

النطاقيسمح بـ
customers:readعرض العملاء والبحث فيهم
customers:writeإنشاء عميل
transactions:writeتسجيل عملية شراء (تُحتسب نقاطها فورًا)
customers:* / *كل نطاقات المجموعة / كل شيء

المفتاح يتوقف تلقائيًا إذا انتهى الاشتراك أو انتقلت المنشأة إلى باقة بلا API (ردّ 403).

العملاء

GET /api/v1/customers

قائمة العملاء مع ترقيم بالمؤشر (cursor).

معاملالنوعالوصف
limitعدد 1–100حجم الصفحة (افتراضي 50)
cursorنصقيمة meta.next_cursor من الصفحة السابقة
searchنصبحث في الاسم أو الجوال
lifecycle_stateنصnew · active · at_risk · churned
{
  "ok": true,
  "data": [
    { "id": "cust_8f2…", "name": "سارة العتيبي", "phone": "+9665xxxxxxxx",
      "lifecycle_state": "active", "visit_count": 12, "points": 340,
      "last_visit_at": "2026-09-18T16:20:00Z", "return_due_at": "2026-10-02T16:20:00Z" }
  ],
  "meta": { "limit": 50, "count": 1, "next_cursor": null, "has_more": false }
}

GET /api/v1/customers/{id}

عميل واحد بالشكل نفسه. غير موجود ← 404.

POST /api/v1/customers

حقلمطلوبالقواعد
nameنعمحتى 120 حرفًا
phoneنعمجوال؛ يُطبَّع تلقائيًا بمفتاح دولة المنشأة (05xxxxxxxx يصبح +9665xxxxxxxx). الجوال المكرر ← 422
expected_return_daysلا1–3650 (افتراضي 30) — دورة العودة المتوقعة لهذا العميل
branch_idلامعرّف فرع من فروعك
preferred_localeلاar-SA افتراضيًا
curl -X POST https://retention.aimenu.me/api/v1/customers \
  -H "Authorization: Bearer grs_…" -H "Content-Type: application/json" \
  -d '{"name":"سارة العتيبي","phone":"0551234567"}'
# 201 → { "ok": true, "data": { "id": "cust_…", … } }

تسجيل عملية شراء

POST /api/v1/transactions

يسجّل عملية للعميل ويحتسب نقاطه ومستواه ومكافآت الإحالة فورًا، ويشغّل الرسائل التلقائية (شكر بعد الزيارة، طلب التقييم).

ترويسة Idempotency-Key إلزامية (8–128 حرفًا، مثل رقم الفاتورة أو UUID). إعادة إرسال الطلب نفسه بالمفتاح نفسه لا تسجّل العملية مرتين، بل تعيد العملية الأصلية مع "duplicate": true. المفتاح نفسه ببيانات مختلفة ← 409.
حقلمطلوبالقواعد
customer_idنعممعرّف عميل من منشأتك
amount أو amount_minorنعم47.50 أو 4750 (هللات) — أكبر من صفر
currencyلاثلاثة أحرف؛ افتراضيًا عملة المنشأة (SAR)
occurred_atلاISO 8601؛ افتراضيًا الآن
external_transaction_idلارقم الفاتورة في نظامك — يُحفظ ويمنع التكرار أيضًا
branch_idلاافتراضيًا فرع العميل المفضل
curl -X POST https://retention.aimenu.me/api/v1/transactions \
  -H "Authorization: Bearer grs_…" -H "Content-Type: application/json" \
  -H "Idempotency-Key: INV-2026-000123" \
  -d '{"customer_id":"cust_8f2…","amount":180.00,"external_transaction_id":"INV-2026-000123"}'
# 200 → { "ok": true, "data": { "transaction": { "id": "…", "amount_minor": 18000, "currency": "SAR", "status": "completed" }, "duplicate": false } }

نصيحة للكاشير: أرسل العملية بعد إغلاق الفاتورة واستخدم رقم الفاتورة مفتاحًا للتكرار — فتستطيع إعادة المحاولة بأمان عند انقطاع الشبكة.

الأخطاء والحدود

{ "error": { "code": "validation_failed", "message": "تعذر التحقق من الطلب.",
             "request_id": "req_…", "fields": { "phone": ["يجب أن يكون رقم هاتف صالحًا"] } } }
HTTPcodeالمعنى
401invalid_api_keyالمفتاح غير صالح أو محذوف
403insufficient_scope · feature_not_available · subscription_inactiveالنطاق لا يكفي / الباقة بلا API / الاشتراك متوقف
400idempotency_key_requiredترويسة التكرار ناقصة
404not_foundالمسار أو العميل غير موجود
409idempotency_conflictالمفتاح نفسه ببيانات مختلفة
422validation_failedحقول غير صالحة — التفاصيل في fields
429rate_limitedأكثر من 240 طلبًا في الدقيقة للمفتاح — انتظر ثم أعد

أرفق request_id عند التواصل مع الدعم.

الويب هوك (إشعارات صادرة)

من الصفحة نفسها اضغط + Webhook: رابط HTTPS في نظامك + الأحداث التي تريدها (فارغ = كل الأحداث). يظهر السر whsec_… مرة واحدة — احفظه للتحقق من التوقيع.

كل حدث يُرسل بطلب POST بهذا الشكل:

POST https://your-system.example/hooks/retention
Content-Type: application/json
User-Agent: RetentionOS-Webhook/1.0
X-Retention-Event: transaction.completed
X-Retention-Event-Id: evt_…
X-Retention-Signature: sha256=3f1a…

{ "event": "transaction.completed", "timestamp": "2026-09-22T14:05:00Z",
  "data": { "transaction_id": "…", "customer_id": "cust_…", "amount_minor": 18000, "gross_minor": 18000, "discount_minor": 0, "currency": "SAR" },
  "meta": { "event_id": "evt_…", "correlation_id": null } }
  • ردّ بـ 2xx خلال ثوانٍ ثم عالج الحدث في الخلفية. أي ردّ آخر يُعاد إرساله تلقائيًا: بعد دقيقة، 5 دقائق، 15، ساعة، 6 ساعات (5 محاولات).
  • ردّ 4xx (عدا 408/429) يعني أن نظامك رفض الحدث فلا يُعاد؛ يظهر «فاشلة نهائيًا» في اللوحة مع زر إعادة الإرسال.
  • الأحداث قد تصل أكثر من مرة — اجعل المعالجة آمنة للتكرار باستخدام event_id.
  • الرابط يجب أن يكون HTTPS عامًا (لا localhost ولا عناوين داخلية).

جدول الأحداث

الحدثمتىأهم حقول data
customer.createdتسجيل عميل (QR، واي فاي، كاشير، API)customer_id, display_name
customer.mergedدمج عميلينsource_customer_id, target_customer_id
transaction.completedعملية مسجلة (كاشير/يدوي/API)transaction_id, customer_id, amount_minor, gross_minor, discount_minor, currency
transaction.voidedإلغاء عمليةtransaction_id, customer_id
sale.paid / sale.refundedبيع من كاشير المنصة / استردادnumber, total_minor, customer_id, invoice_id / amount_minor, full
reward.redeemedاستبدال مكافأةcustomer_id, reward_id, transaction_id
loyalty.adjustedتعديل نقاط يدويcustomer_id, delta
tier.changedتغيّر مستوى العميلcustomer_id, to, direction
coupon.issued / coupon.usedإصدار كوبون / استخدامهcustomer_id, coupon_id, code / transaction_id, discount_minor
referral.joined / referral.qualified / referral.rewardedصديق انضم / اشترى / مكافأة المُحيلreferrer_customer_id, referee_customer_id / transaction_id / grant_type
review.ratedتقييم بعد الزيارةcustomer_id, rating, positive
appointment.created / appointment.rescheduled / appointment.canceledالحجوزاتappointment_id, customer_id, starts_at, source / by
game.wonجائزة من عجلة الحظcustomer_id, prize, kind

التحقق من التوقيع

التوقيع = sha256= + HMAC-SHA256 لجسم الطلب الخام بالسر whsec_…. تحقق قبل أي معالجة:

// PHP
$raw = file_get_contents('php://input');
$expected = 'sha256=' . hash_hmac('sha256', $raw, $secret);
if (!hash_equals($expected, $_SERVER['HTTP_X_RETENTION_SIGNATURE'] ?? '')) { http_response_code(401); exit; }
$event = json_decode($raw, true);
http_response_code(200); // ثم عالج $event في الخلفية
// Node.js (Express — استخدم الجسم الخام لا المُحلَّل)
app.post('/hooks/retention', express.raw({ type: '*/*' }), (req, res) => {
  const expected = 'sha256=' + crypto.createHmac('sha256', SECRET).update(req.body).digest('hex');
  const given = req.get('X-Retention-Signature') || '';
  if (expected.length !== given.length || !crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(given))) return res.sendStatus(401);
  const event = JSON.parse(req.body);
  res.sendStatus(200); // ثم عالج event
});

قبل الإطلاق

  • مفتاح API منفصل لكل نظام (كاشير، متجر، ERP) بأقل نطاقات تكفيه؛ يمكن إيقاف أي مفتاح من اللوحة دون أن يتأثر غيره.
  • رقم الفاتورة هو Idempotency-Key وexternal_transaction_id معًا.
  • سجّل العميل أولًا (أو ابحث عنه بالجوال) ثم أرسل العملية بمعرّفه.
  • معالِج الويب هوك يرد 2xx فورًا ويتجاهل الأحداث المكررة بـevent_id.
  • راقب صفحة «API و Webhooks»: آخر استخدام لكل مفتاح، وحالة تسليم كل ويب هوك، وإعادة الإرسال عند الفشل.

الدعم الفني للتكامل: تواصل معنا مع request_id أو event_id.