ادفع بالكنز — دليل تكامل الشريك
سلّم هذا الملف لأي تطبيق/مشروع مملوك للكنز يجب أن يقبل دفع المحفظة.
تحتاج من المشرف العام (تُعرض مرة واحدة عند الإنشاء/التدوير):
| السر | أين يُستخدم |
|---|---|
مفتاح API (ewk_…) | الخادم → الكنز عند إنشاء/استعلام/إلغاء الـ intents |
سر توقيع الـ Webhook (ews_…) | التحقق من webhooks payment.succeeded |
لا تضع مفتاح الـ API في المتصفح أو تطبيق الجوال. فقط في خادمك.
المضيفات
| البيئة | قاعدة الـ API | واجهة تأكيد المشتري |
|---|---|---|
| الإنتاج | https://api.elknz.com | https://elknz.com/{locale}/pay/confirm |
| محلي | https://api.elknz.com أو http://127.0.0.1:1233 | https://elknz.com/... (Caddy) أو Next للمنصة :3000 |
روابط التأكيد تُبنى من WALLET_PUBLIC_ORIGIN / MODE_A_CONFIRM_ORIGIN / MARKETPLACE_PUBLIC_ORIGIN (الافتراضي `https://elknz.com`). تطبيق منفصل على wallet.elknz.com اختياري وليس مطلوباً للوضع A.
كل استدعاءات الشريك أدناه تحت:
{API_BASE}/api/wallet/v1/...رأس المصادقة في كل استدعاء شريك:
X-Elknz-Wallet-Key: ewk_YOUR_API_KEYالتدفق من البداية للنهاية
Your app (server) Elknz API Buyer browser Your webhook
| | | |
|-- POST payment-intents -->| | |
|<-- intent_id + confirm_url | | |
|-- redirect buyer --------|----------------------->| confirm_url |
| |<----- JWT confirm -----| |
| |-- debit buyer / credit merchant wallet ------->|
| |---------------- POST payment.succeeded ------>|
|<-- buyer return_url ----|<----- redirect --------| |- أنشئ Payment Intent (من الخادم).
- أعد توجيه المشتري إلى
confirm_urlمن الاستجابة. - يسجّل المشتري الدخول في المحفظة (إن لزم) ويؤكّد.
- يعود المشتري إلى
return_urlمع معاملات الاستعلام. - اعتمد على الـ webhook (أو استعلام GET للـ intent) قبل التسليم — وليس على رابط العودة وحده.
1) إنشاء Payment Intent
POST /api/wallet/v1/payment-intents
X-Elknz-Wallet-Key: ewk_…
Content-Type: application/json
{
"amount": 12.50,
"currency": "USD",
"merchant_reference": "order_1001",
"return_url": "https://your-app.example/pay/return",
"webhook_url": "https://your-app.example/webhooks/elknz"
}| الحقل | مطلوب | ملاحظات |
|---|---|---|
amount | نعم | موجب؛ محدود بسقف الكنز (MODE_A_MAX_AMOUNT، الافتراضي 500) |
currency | لا | الافتراضي USD |
merchant_reference | نعم | فريد لكل شريك — معرّف طلبك/فاتورتك (إنشاء متماثل) |
return_url | نعم | http(s)://… — يصل المشتري هنا بعد التأكيد |
webhook_url | لا | يتجاوز رابط webhook الافتراضي للشريك لهذا الـ intent |
billing_mode | لا | one_time (افتراضي) أو recurring |
interval | إن كان اشتراكاً | month أو year |
إنشاء اشتراك متكرر
{
"amount": 9.99,
"currency": "USD",
"merchant_reference": "plan_pro_user_42",
"return_url": "https://your-app.example/pay/return",
"webhook_url": "https://your-app.example/webhooks/elknz",
"billing_mode": "recurring",
"interval": "month"
}الاستجابة تتضمن subscription_id (wsub_…) وbilling_mode / interval. أول خصم ما زال يستخدم confirm_url كما في الدفع لمرة واحدة.
بعد التأكيد، يجدّد الكنز تلقائياً عند حلول period_end (خصم المحفظة → رصيد التاجر). فشل التجديد ينهي الاشتراك.
إلغاء الشريك:
POST /api/wallet/v1/subscriptions/{subscription_id}/cancel
X-Elknz-Wallet-Key: ewk_…استعلام:
GET /api/wallet/v1/subscriptions/{subscription_id}
X-Elknz-Wallet-Key: ewk_…مثال استجابة
{
"intent_id": "wpi_…",
"amount": 12.5,
"currency": "USD",
"merchant_reference": "order_1001",
"status": "requires_confirmation",
"return_url": "https://your-app.example/pay/return",
"billing_mode": "one_time",
"interval": null,
"subscription_id": null,
"expires_at": "2026-09-13T02:30:00",
"confirmed_at": null,
"created_at": "2026-09-13T02:00:00",
"buyer_user_id": null,
"partner_name": "My App",
"confirm_url": "https://elknz.com/ar/pay/confirm?intent=wpi_…"
}curl
curl -sS -X POST "$API_BASE/api/wallet/v1/payment-intents" \
-H "X-Elknz-Wallet-Key: $ELKNZ_WALLET_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"amount": 12.50,
"currency": "USD",
"merchant_reference": "order_1001",
"return_url": "https://your-app.example/pay/return",
"webhook_url": "https://your-app.example/webhooks/elknz"
}'ثم أعد التوجيه:
HTTP 302 Location: {confirm_url}(أو window.location = confirm_url من واجهتك بعد أن يعيد خادمك الرابط)
تنتهي الـ intents (~30 دقيقة). الإنشاء بنفس merchant_reference يعيد الـ intent القائم إن كان ما زال مفتوحاً.
2) رابط العودة (المتصفح)
بعد النجاح، يعيد الكنز التوجيه إلى:
{return_url}?intent_id=wpi_…&status=succeededاعتبر هذا للتجربة فقط. أكّد الدفع عبر الـ webhook أو GET (أدناه).
3) استعلام الحالة (اختياري)
GET /api/wallet/v1/payment-intents/{intent_id}
X-Elknz-Wallet-Key: ewk_…الحالات: requires_confirmation | succeeded | canceled | expired
4) الإلغاء (اختياري)
POST /api/wallet/v1/payment-intents/{intent_id}/cancel
X-Elknz-Wallet-Key: ewk_…فقط إن لم يكن بالفعل succeeded.
5) Webhook payment.succeeded
يرسل الكنز JSON إلى webhook_url (أو الافتراضي للشريك).
الرؤوس
| الرأس | المعنى |
|---|---|
Content-Type | application/json |
X-Elknz-Signature | sha256=<hex> — HMAC-SHA256 لـ جسم الطلب الخام بسر توقيع الـ webhook |
X-Elknz-Event | payment.succeeded |
X-Elknz-Timestamp | ثوانٍ Unix |
User-Agent | ELKNZ-WalletModeA/1 |
الجسم
{
"event": "payment.succeeded",
"event_id": "payment.succeeded:wpi_…:2026-09-13T02:01:10.883694",
"status": "paid",
"payment_id": "wpi_…",
"amount": 12.5,
"currency": "USD",
"merchant_reference": "order_1001",
"billing_mode": "one_time",
"interval": null,
"subscription_id": null,
"partner_slug": "my-app",
"elknz_user_id": 42,
"elknz_username": "buyer1",
"paid_at": "2026-09-13T02:01:10.883694"
}أول خصم للاشتراك يتضمن billing_mode: "recurring" وinterval وsubscription_id وperiod_start / period_end.
أحداث دورة حياة الاشتراك
X-Elknz-Event | متى |
|---|---|
subscription.renewed | نجح التجديد التلقائي |
subscription.canceled | ألغى الشريك |
subscription.expired | فشل التجديد (رصيد غير كافٍ وغيرها) |
نفس رؤوس HMAC؛ الجسم يستخدم subscription_id (بدون payment_id). أزل التكرار عبر event_id.
- أجب بـ 2xx بسرعة وإلا يعيد الكنز المحاولة (backoff).
- أزل التكرار عبر
event_id(و/أوpayment_id/merchant_reference/subscription_id). - روابط webhook في الإنتاج يجب أن تكون HTTPS (الاختبارات المحلية تسمح بـ
http://127.0.0.1/localhost/host.docker.internal).
التحقق من التوقيع (Python)
import hashlib
import hmac
def verify_elknz_signature(*, secret: str, raw_body: bytes, header: str) -> bool:
expected = "sha256=" + hmac.new(
secret.encode("utf-8"), raw_body, hashlib.sha256
).hexdigest()
return hmac.compare_digest(expected, (header or "").strip())التحقق من التوقيع (Node.js)
const crypto = require("crypto");
function verifyElknzSignature(secret, rawBodyBuffer, header) {
const expected =
"sha256=" +
crypto.createHmac("sha256", secret).update(rawBodyBuffer).digest("hex");
const provided = (header || "").trim();
return (
expected.length === provided.length &&
crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(provided))
);
}مهم: هاش البايتات الخام للجسم (قبل JSON.parse). لا تُعِد تسلسل JSON ثم تتحقق.
إعادة الإرسال (عمليات)
POST /api/wallet/v1/payment-intents/{intent_id}/resend-webhook
X-Elknz-Wallet-Key: ewk_…فقط لـ intents بحالة succeeded.
حركة المال (ماذا يفعل الكنز)
عند التأكيد:
- يخصم من محفظة المشتري القابلة للاستخدام.
- يضيف رصيداً لمستخدم التاجر في السوق (
owner_user_idفي المشرف العام) — رصيد قابل للسحب. - يرسل الـ webhook.
تطبيقك لا يحرّك المال؛ علّم الطلب مدفوعاً عندما يتحقق الـ webhook.
قائمة لمشروع كنز جديد
- المشرف العام → شركاء المحفظة → أنشئ شريكاً → اربط مستخدم التاجر → انسخ مفتاح API وسر الـ webhook.
- عيّن في المشروع الآخر:
ELKNZ_WALLET_API_KEY،ELKNZ_WALLET_WEBHOOK_SECRET،ELKNZ_API_BASE. - نفّذ إنشاء الـ intent + إعادة التوجيه إلى
confirm_url. - نفّذ webhook عبر HTTPS مع التحقق من التوقيع وتسليم متماثل.
- اختيارياً استعلم GET عن الـ intent في صفحة العودة كاحتياطي للتجربة.
راجع أيضاً: الوصول (الوضع B) عندما يدفع المشترون من كتالوج الوصول على الكنز بدلاً من موقعك.