توثيق المطوّرين — API بوابة دفع دفتر كاش (Payment Gateway API)
اربط موقعك أو تطبيقك ببوابة الدفع: طلب واحد بيعمل فاتورة ويرجّعلك رابط صفحة دفع جاهزة، والعميل بيحوّل من فودافون كاش أو أورنج كاش أو اتصالات كاش أو وي باي أو إنستاباي، وسيرفرك بيوصله Webhook موقّع أول ما الدفع يتأكد.
Daftar Cash is an Egyptian payment gateway API: create a payment with one request, send the customer to a hosted checkout page, and receive a signed webhook when a Vodafone Cash, Orange Cash, Etisalat Cash, WE Pay or InstaPay transfer is confirmed. Flat monthly subscription, no per-transaction commission. Code examples in cURL, JavaScript, PHP and Python; a WooCommerce plugin is available.
نظرة عامة
البوابة مش بتمسك فلوس عملائك — العميل بيحوّل مباشرة على محفظتك، وهاتف المحفظة (عليه تطبيق دفتر كاش بلس) بيقرأ رسالة التحويل، والنظام بيطابقها مع الفاتورة. دورة الدفع كلها:
POST /api/v1/payments/create/ ويستلم checkout_url.completed وموقّع بـ HMAC، والعميل يرجع لـ redirect_url.JSON وترميز UTF-8، والمبالغ بالجنيه المصري (EGP).البداية السريعة
- اشترك في باقة (أو ابدأ التجربة المجانية) من خطط البوابة.
- من لوحة البوابة: ضيف محفظة استقبال، وسجّل دخول بحسابك على تطبيق دفتر كاش بلس على هاتف المحفظة.
- من تبويب المطوّرين: انسخ
Merchant API KeyوSignature Secret Key، وحط رابط الـ Webhook بتاع سيرفرك. - اعمل أول فاتورة:
curl -X POST https://daftarcash.com/api/v1/payments/create/ \
-H "Content-Type: application/json" \
-H "X-Merchant-Key: vc_live_xxxxxxxxxxxxxxxx" \
-d '{"amount": 450, "merchant_order_id": "1284", "redirect_url": "https://my-store.com/thanks"}'
حوّل العميل على data.checkout_url اللي في الرد، واستنى الـ Webhook.
المصادقة والمفاتيح
عندك التلات مفاتيح دول في لوحة البوابة ← المطوّرين:
| المفتاح | بيستخدم في | مكانه |
|---|---|---|
Merchant API Keyيبدأ بـ vc_live_ | مصادقة طلبات الـ API (إنشاء فاتورة، إعادة إرسال Webhook). | سيرفرك بس. |
Test API Keyيبدأ بـ vc_test_ | نفس طلبات الـ API بالظبط، بس في بيئة التجربة من غير فلوس حقيقية. | سيرفرك (بيئة التطوير). |
Signature Secret Key | التحقق من توقيع الـ Webhooks الواردة لسيرفرك. | سيرفرك بس — ما بيتبعتش في أي طلب. |
ابعت المفتاح العام في أي هيدر من الاتنين:
X-Merchant-Key: vc_live_xxxxxxxxxxxxxxxx
# أو
Authorization: Bearer vc_live_xxxxxxxxxxxxxxxx
بيئة التجربة (Sandbox)
جرّب الربط كله من أوله لآخره من غير ما تحوّل فلوس حقيقية: ابعت نفس الطلبات بمفتاح vc_test_ بدل vc_live_.
- الفاتورة بتتعمل عادي وبترجع
"test_mode": true، ومش بتتحسب من حد حجم المعاملات في باقتك. - صفحة الدفع (
checkout_url) مابتعرضش رقم محفظتك، وبتعرض بداله زرارين: محاكاة دفع ناجح ومحاكاة دفع فاشل. - الدفع الناجح بيبعت Webhook عادي، متوقّع بنفس المفتاح السري، وفيه
"test_mode": true، وبيحوّل العميل علىredirect_url. - الدفع الفاشل بيقفل الفاتورة
failedمن غير Webhook، زي الدفع الحقيقي بالظبط. - فواتير التجربة بتظهر في لوحتك بعلامة "تجربة"، ومش بتدخل في الإحصائيات.
- مفتاح التجربة شغّال حتى لو اشتراك البوابة خلص، عشان تقدر تكمّل تطوير.
test_mode: لو true ما تعلّمش الطلب مدفوع في متجرك الحقيقي. قبل الإطلاق بدّل المفتاح لـ vc_live_.إنشاء فاتورة دفع
الحقول
| الحقل | النوع | الوصف |
|---|---|---|
amountإجباري | number | المبلغ بالجنيه، أكبر من صفر (مثال 450 أو 199.50). العميل لازم يحوّل المبلغ ده أو أكتر. |
order_totalاختياري | number | إجمالي الطلب لو amount ده عربون والباقي هيتدفع كاش عند الاستلام. لازم يكون أكبر من amount. |
merchant_order_idاختياري | string | رقم الطلب عندك (حتى 100 حرف). بيرجعلك في الـ Webhook وبيظهر للعميل في صفحة الدفع. |
redirect_urlاختياري | string (URL) | العميل بيتحوّل عليه تلقائيًا بعد نجاح الدفع (صفحة "شكرًا لطلبك"). |
cancel_urlاختياري | string (URL) | العميل بيروحه لو قفل صفحة دفع منتهية أو مرفوضة. لو ما بعتّوش، بيرجع لجذر موقعك (دومين redirect_url). |
customer_phoneاختياري | string | رقم موبايل العميل المصري. للعرض والإيصالات فقط — المطابقة بتتم برقم العملية اللي العميل بيكتبه في صفحة الدفع. |
customer_emailاختياري | string | إيميل العميل — لو بعتّه، بيوصله إيصال دفع تلقائي بعد اكتمال الفاتورة. |
مثال
curl -X POST https://daftarcash.com/api/v1/payments/create/ \
-H "Content-Type: application/json" \
-H "X-Merchant-Key: $DAFTAR_API_KEY" \
-d '{
"amount": 450,
"merchant_order_id": "1284",
"redirect_url": "https://my-store.com/orders/1284/thanks",
"cancel_url": "https://my-store.com/cart"
}'
const res = await fetch("https://daftarcash.com/api/v1/payments/create/", {
method: "POST",
headers: {
"Content-Type": "application/json",
"X-Merchant-Key": process.env.DAFTAR_API_KEY,
},
body: JSON.stringify({
amount: 450,
merchant_order_id: "1284",
redirect_url: "https://my-store.com/orders/1284/thanks",
cancel_url: "https://my-store.com/cart",
}),
});
const body = await res.json();
if (!body.success) throw new Error(body.message);
// Express: send the customer to the hosted checkout page
response.redirect(body.data.checkout_url);
<?php
$ch = curl_init("https://daftarcash.com/api/v1/payments/create/");
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
"Content-Type: application/json",
"X-Merchant-Key: " . getenv("DAFTAR_API_KEY"),
],
CURLOPT_POSTFIELDS => json_encode([
"amount" => 450,
"merchant_order_id" => "1284",
"redirect_url" => "https://my-store.com/orders/1284/thanks",
"cancel_url" => "https://my-store.com/cart",
]),
]);
$body = json_decode(curl_exec($ch), true);
curl_close($ch);
if (empty($body["success"])) {
throw new Exception($body["message"] ?? "Payment gateway error");
}
header("Location: " . $body["data"]["checkout_url"]);
exit;
import os
import requests
res = requests.post(
"https://daftarcash.com/api/v1/payments/create/",
headers={"X-Merchant-Key": os.environ["DAFTAR_API_KEY"]},
json={
"amount": 450,
"merchant_order_id": "1284",
"redirect_url": "https://my-store.com/orders/1284/thanks",
"cancel_url": "https://my-store.com/cart",
},
timeout=15,
)
body = res.json()
if not body.get("success"):
raise RuntimeError(body.get("message"))
checkout_url = body["data"]["checkout_url"] # redirect the customer here
الرد 201 Created
{
"success": true,
"message": "Payment invoice created successfully.",
"data": {
"payment_id": "3f6c1a2e-8b1d-4e0a-9c55-2d7f0b9e4a11",
"amount": 450.0,
"status": "pending",
"checkout_url": "https://daftarcash.com/checkout/hosted/3f6c1a2e-8b1d-4e0a-9c55-2d7f0b9e4a11/",
"expires_at": "2026-09-27T14:05:12.481Z",
"test_mode": false,
"payment_type": "full",
"order_total": 450.0,
"remaining_on_delivery": 0.0
}
}
احفظ payment_id مع الطلب عندك — ده المعرّف اللي بيرجع في الـ Webhook وفي استعلام الحالة. رابط الدفع صالح 24 ساعة (expires_at).
/payments/create/ بنفس merchant_order_id تاني (retry بسبب مشكلة شبكة مثلًا) ولسه الفاتورة الأولى pending وسارية، هترجعلك نفس payment_id وcheckout_url (كود 200 مش 201) بدل ما تتعمل فاتورة جديدة — بشرط يكون نفس amount وorder_total. لو المبلغ اتغيّر، بتتعمل فاتورة جديدة بالمبلغ الجديد (201) والقديمة بتتقفل ويوصلك لها payment.failed.عربون الدفع عند الاستلام
بدل ما تشحن طلبات الدفع عند الاستلام وتتفاجئ إن العميل رفضها، خلّيه يدفع عربون صغير بالمحفظة يأكّد بيه جدّيته، والباقي يدفعه كاش للمندوب.
- ابعت
amount= العربون، وorder_total= إجمالي الطلب. - صفحة الدفع بتوضّح للعميل: الإجمالي، والعربون اللي هيحوّله دلوقتي، والباقي عند الاستلام.
- التأكيد التلقائي وحد الباقة بيتحسبوا على العربون بس — ده اللي بيعدّي على محفظتك.
- الـ Webhook بيوصلك بـ
"status": "completed"و"payment_type": "deposit"، وremaining_on_delivery= المبلغ اللي المندوب يحصّله. ما تعلّمش الطلب مدفوع بالكامل — علّمه "مؤكَّد، الباقي عند الاستلام". - من غير كود: فعّل "عربون الدفع عند الاستلام" من إعدادات لوحة البوابة، وبعدين في رابط الدفع السريع اكتب العربون في "المبلغ" والإجمالي في "إجمالي الطلب".
- الـ API مش مربوط بالزرار ده —
order_totalبيشتغل دايمًا لو بعته. - بلجن WooCommerce بيعمل ده لوحده: فعّل "عربون الدفع عند الاستلام" من إعداداته وحدد نسبة أو مبلغ ثابت.
{
"amount": 150,
"order_total": 1200,
"merchant_order_id": "1290"
}
صفحة الدفع المستضافة
الـ checkout_url صفحة جاهزة باسم متجرك، شغّالة على الموبايل، ومش محتاج تبني أي واجهة:
- العميل بيختار المحفظة اللي هيحوّل منها — والصفحة بتعرض محافظ الاستقبال النشطة عندك (بالتبادل بينهم).
- بيحوّل المبلغ ويكتب رقم العملية من رسالة تأكيد التحويل.
- الصفحة بتستنى التأكيد التلقائي لمدة بتحددها من لوحة البوابة (دقيقتين، 3 دقائق، 5 دقائق، 10 دقائق، 15 دقيقة، 30 دقيقة، ساعة).
- لو اتأكد: رسالة نجاح وتحويل تلقائي لـ
redirect_url. - لو المدة خلصت: العميل يصحّح رقم العملية أو يرفع لقطة شاشة للتحويل، وإنت تأكد أو ترفض من تبويب المراجعة في لوحتك — والتأكيد اليدوي بيبعت نفس الـ Webhook.
redirect_url عشان تأكد الطلب — العميل ممكن يقفل الصفحة قبل التحويل. الـ Webhook (أو استعلام الحالة) هو مصدر الحقيقة الوحيد.تقدر تبعت العميل للرابط من صفحة الطلب، أو تبعته في رسالة واتساب/SMS — الرابط شغّال من أي جهاز.
متابعة حالة الفاتورة
مفيد كاحتياطي لو الـ Webhook ما وصلش، أو لو عايز تعرض حالة الطلب في صفحتك.
{
"success": true,
"data": {
"payment_id": "3f6c1a2e-8b1d-4e0a-9c55-2d7f0b9e4a11",
"status": "completed",
"awaiting_merchant_review": false,
"amount": 450.0,
"merchant_order_id": "1284",
"merchant_name": "noor_store",
"redirect_url": "https://my-store.com/orders/1284/thanks",
"created_at": "2026-09-26T14:05:12.481Z",
"expires_at": "2026-09-27T14:05:12.481Z",
"test_mode": false,
"payment_type": "full",
"order_total": 450.0,
"remaining_on_delivery": 0.0
}
}
الحالات
| status | المعنى |
|---|---|
| pending | مستنية الدفع. لو awaiting_merchant_review = true، العميل رفع لقطة شاشة ومستنية قرارك (ومش بتنتهي لوحدها). |
| completed | الدفع اتأكد (تلقائي أو يدوي). جهّز الطلب. |
| expired | الرابط خلص (24 ساعة) من غير دفع. |
| failed | إنت رفضت لقطة الشاشة من لوحة المراجعة، أو ألغيتها عبر API الإلغاء. |
| refunded | التاجر علّمها مستردة يدويًا (رجّع الفلوس لعميله بنفسه) عبر API الاسترداد أو لوحة البوابة. |
إلغاء فاتورة
لما العميل يلغي الطلب من متجرك قبل ما يدفع. بيشتغل بس على فاتورة لسه pending، وبيقفلها failed ويبعتلك Webhook بـ event: "payment.failed" — نفس الحدث اللي بيوصلك لما ترفض لقطة شاشة، فأي كود عندك بيتعامل معاه أصلًا هيلغي الطلب صح من غير أي تعديل.
curl -X POST https://daftarcash.com/api/v1/payments/3f6c1a2e-8b1d-4e0a-9c55-2d7f0b9e4a11/cancel/ \ -H "X-Merchant-Key: $DAFTAR_API_KEY"
لو الفاتورة خلاص completed أو expired أو failed، هترجع 400 ("Cannot cancel an invoice in '…' state").
استرداد فاتورة
Webhook يحدّث حالة الطلب عندك.بيشتغل بس على فاتورة completed، وبيقفلها refunded ويبعتلك Webhook بـ event: "payment.refunded". نفس الميزة متاحة يدويًا من لوحة البوابة ← سجلّ طلبات الدفع (زرار "رد المبلغ" جنب أي فاتورة مكتملة).
curl -X POST https://daftarcash.com/api/v1/payments/3f6c1a2e-8b1d-4e0a-9c55-2d7f0b9e4a11/refund/ \ -H "X-Merchant-Key: $DAFTAR_API_KEY"
لو الفاتورة مش completed (لسه pending، أو أصلًا expired/failed/refunded)، هترجع 400 ("Only completed invoices can be refunded").
فواتير دورية
لاشتراكات عملائك (نادي، دروس، خدمة شهرية…): بتعرّف الاشتراك مرة واحدة، وإحنا بنولّد فاتورة جديدة تلقائيًا كل دورة ونبعت لعميلك رابط الدفع على إيميله، ونذكّره مرة لو ما دفعش خلال 24 ساعة. مفيش خصم تلقائي — العميل بيحوّل بنفسه كل دورة، والدفع بيتأكد زي أي فاتورة، وبيوصلك payment.completed فيه recurring_plan_id وmerchant_order_id بصيغة REC<plan_id>-<رقم الدورة> عشان تمدّ خدمة عميلك.
| الحقل | النوع | الوصف |
|---|---|---|
amount | number | مبلغ كل دورة بالجنيه. |
interval | string | weekly أو monthly. الشهري بيحافظ على يوم الشهر (31 يناير ← 28 فبراير ← 31 مارس). |
customer_email | string | مطلوب — بيوصله رابط كل فاتورة. |
customer_name / customer_phone / label / redirect_url | string | اختياري. |
start_at | ISO 8601 | اختياري — موعد أول فاتورة (الافتراضي: دلوقتي). |
curl -X POST https://daftarcash.com/api/v1/recurring/create/ \
-H "X-Merchant-Key: $DAFTAR_API_KEY" -H "Content-Type: application/json" \
-d '{"amount": 300, "interval": "monthly", "customer_email": "ahmed@example.com", "label": "اشتراك الجيم"}'
الرد 201 فيه plan_id. بعدها: GET /api/v1/recurring/<plan_id>/ لحالة الاشتراك وآخر 5 فواتير، وPOST /api/v1/recurring/<plan_id>/pause|resume|cancel/. الاستئناف بعد إيقاف مؤقت بيتخطى الدورات اللي فاتت.
الـ Webhooks
البوابة بتبعت POST لرابط الـ Webhook اللي حاطه في لوحة البوابة ← المطوّرين عند أي تغيير نهائي في حالة الفاتورة: دُفعت (payment.completed)، رفض التاجر إثباتها (payment.failed)، خلصت مهلتها من غير دفع (payment.expired)، أو اتعلّمت مستردة يدويًا (payment.refunded) — مش لما تُدفع بس. فرّق بينهم بحقل event (استخدم ده مش status وحده في كود التكامل عندك).
| الهيدر | القيمة |
|---|---|
Content-Type | application/json |
X-VodaCash-Signature | HMAC-SHA256 (hex) لجسم الطلب الخام، بمفتاح Signature Secret Key. |
X-VodaCash-Delivery-Id | رقم ثابت لكل إشعار (بيتكرر في إعادات المحاولة لنفس الإشعار) — استخدمه مع event وpayment_id لمنع المعالجة المزدوجة. |
X-VodaCash-Attempt | رقم المحاولة (1 للأولى). |
{
"event": "payment.completed",
"payment_id": "3f6c1a2e-8b1d-4e0a-9c55-2d7f0b9e4a11",
"merchant_order_id": "1284",
"amount": 450.0,
"status": "completed",
"verified_transaction_id": "023639006701",
"test_mode": false,
"recurring_plan_id": null,
"payment_type": "full",
"order_total": 450.0,
"remaining_on_delivery": 0.0,
"timestamp": "2026-09-26T14:09:47.120Z"
}
event: واحد منpayment.completed،payment.failed،payment.expired،payment.refunded— نفس قيمةstatusبالظبط بس بصيغة اسم حدث ثابتة، مفيدة لو حبيت تعملswitchبدل مقارنة نص.verified_transaction_id: رقم العملية الحقيقي من رسالة التحويل — مفيد للمراجعة المحاسبية (فاضي فيpayment.failed/payment.expired).payment_typeوorder_totalوremaining_on_delivery: لوpayment_typeقيمته"deposit"يبقى العميل دفع العربون بس، والباقي هيتدفع عند الاستلام.test_mode:trueلو الفاتورة اتعملت بمفتاح التجربة — ما تعتبرهاش دفعة حقيقية.recurring_plan_id: رقم الاشتراك الدوري اللي ولّد الفاتورة، أوnullلأي فاتورة عادية.- رد بأي كود
2xxخلال 10 ثواني. لو ما ردّيتش أو رجّعت خطأ، البوابة بتعيد المحاولة: 3 محاولات متتالية سريعة، وبعدها إعادة تلقائية بعد 5 دقايق، 30 دقيقة، ساعتين، 6 ساعات، 24 ساعة (8 محاولات إجمالًا). بعد كده تقدر تعيد الإرسال يدويًا من هنا. كل إعادة بتحمل نفسeventوstatusبتوع الإشعار الأصلي، بـtimestampجديد. - رابط الـ Webhook لازم يكون
https://على عنوان عام — الروابط اللي بتشاور على عنوان داخلي أو محلي (localhost،127.0.0.1،192.168.x.x…) أو فيها اسم مستخدم وكلمة مرور بتتقفل. للتجربة المحلية استخدم نفق مثل ngrok. البوابة مابتتبعش redirects (3xx بتتحسب فشل) فاحط الرابط النهائي مباشرة. - الحماية من إعادة الإرسال المتأخر: ارفض أي إشعار
timestampبتاعه أقدم من 5 دقايق (إعادات المحاولة بتتبعت بـtimestampجديد فمش هتتأثر). - كل محاولة بتتسجل في سجل إرسال الـ Webhooks في لوحتك (كود الرد + الرد نفسه).
- نفس الإشعار ممكن يوصلك أكتر من مرة (إعادة محاولة أو إرسال يدوي) — خلي المعالجة idempotent: لو الطلب متعلّم مدفوع أو ملغي خلاص بنفس الحالة، تجاهل.
التحقق من التوقيع
احسب HMAC-SHA256 على جسم الطلب الخام كما وصل (مش بعد ما تعمل له parse وتعيد تحويله لـ JSON — أي اختلاف في المسافات بيغيّر التوقيع)، وقارنه بالهيدر بمقارنة آمنة ضد التوقيت.
const crypto = require("crypto");
const express = require("express");
const app = express();
// keep the raw body: the signature is computed over the exact bytes
app.post("/webhooks/daftar-cash", express.raw({ type: "application/json" }), async (req, res) => {
const expected = crypto
.createHmac("sha256", process.env.DAFTAR_WEBHOOK_SECRET)
.update(req.body) // Buffer
.digest("hex");
const received = req.get("X-VodaCash-Signature") || "";
const ok = received.length === expected.length &&
crypto.timingSafeEqual(Buffer.from(received), Buffer.from(expected));
if (!ok) return res.status(401).send("invalid signature");
const event = JSON.parse(req.body);
if (event.test_mode) return res.sendStatus(200); // Sandbox: never touch a real order
if (event.event === "payment.completed") {
await markOrderPaid(event.merchant_order_id, event.payment_id, event.verified_transaction_id);
} else if (event.event === "payment.failed" || event.event === "payment.expired") {
await cancelOrder(event.merchant_order_id);
}
res.sendStatus(200);
});
<?php
$rawBody = file_get_contents("php://input");
$received = $_SERVER["HTTP_X_VODACASH_SIGNATURE"] ?? "";
$expected = hash_hmac("sha256", $rawBody, getenv("DAFTAR_WEBHOOK_SECRET"));
if (!hash_equals($expected, $received)) {
http_response_code(401);
exit("invalid signature");
}
$event = json_decode($rawBody, true);
if (empty($event["test_mode"])) { // skip Sandbox events
if (($event["event"] ?? "") === "payment.completed") {
mark_order_paid($event["merchant_order_id"], $event["payment_id"], $event["verified_transaction_id"]);
} elseif (in_array($event["event"] ?? "", array("payment.failed", "payment.expired"), true)) {
cancel_order($event["merchant_order_id"]);
}
}
http_response_code(200);
import hashlib
import hmac
import json
import os
from django.http import HttpResponse, HttpResponseForbidden
from django.views.decorators.csrf import csrf_exempt
from django.views.decorators.http import require_POST
@csrf_exempt
@require_POST
def daftar_cash_webhook(request):
expected = hmac.new(os.environ["DAFTAR_WEBHOOK_SECRET"].encode(), request.body, hashlib.sha256).hexdigest()
received = request.headers.get("X-VodaCash-Signature", "")
if not hmac.compare_digest(expected, received):
return HttpResponseForbidden("invalid signature")
event = json.loads(request.body)
if not event.get("test_mode"): # skip Sandbox events
if event.get("event") == "payment.completed":
mark_order_paid(event["merchant_order_id"], event["payment_id"], event["verified_transaction_id"])
elif event.get("event") in ("payment.failed", "payment.expired"):
cancel_order(event["merchant_order_id"])
return HttpResponse(status=200)
amount في الإشعار بيساوي مبلغ الطلب عندك، وإن payment_id هو نفس اللي حفظته وقت إنشاء الفاتورة.إعادة إرسال Webhook
بيعيد إرسال إشعار فاتورة completed أو failed أو expired لرابط الـ Webhook الحالي (محاولة واحدة، ويرجعلك نتيجتها). نفس اللي بيعمله زرار "إعادة إرسال" في سجل الـ Webhooks.
curl -X POST https://daftarcash.com/api/v1/payments/webhook-retry/ \
-H "Content-Type: application/json" \
-H "X-Merchant-Key: $DAFTAR_API_KEY" \
-d '{"payment_id": "3f6c1a2e-8b1d-4e0a-9c55-2d7f0b9e4a11"}'
# => {"success": true, "message": "Webhook delivered successfully",
# "details": {"status_code": 200, "response": "OK"}}
الأخطاء والحدود
أي رد فيه "success": false معاه message بيوضّح السبب:
| الكود | السبب | تعمل إيه |
|---|---|---|
400 | JSON غير صالح، أو amount ناقص/صفر/مش رقم. | صلّح الطلب. |
401 | مفتاح الـ API ناقص أو غلط. | راجع الهيدر، أو المفتاح اتجدّد. |
403 | اشتراك البوابة على حسابك منتهي أو مش مفعّل. | جدّد الباقة من لوحة البوابة. |
404 | الفاتورة مش موجودة (أو مش بتاعة حسابك). | راجع payment_id. |
429 + "error_code": "quota_exceeded" | الفاتورة هتتخطى حد حجم المعاملات الشهري لباقتك (المبالغ المدفوعة فعلاً + هامش سماح 10%). | رقّي الباقة — الحد بيتصفّر مع كل دورة (30 يوم). |
429 | تخطيت حد عدد الطلبات. | استنى دقيقة وحاول تاني (استخدم backoff). |
حدود عدد الطلبات
| المسار | الحد |
|---|---|
POST /api/v1/payments/create/ | 30 طلب في الدقيقة لكل حساب. |
POST /api/v1/payments/webhook-retry/ | 10 طلبات في الدقيقة لكل حساب. |
WooCommerce
صفحة الإضافة: المتطلبات وخطوات التركيب والأسئلة الشائعة ←
لو متجرك على WooCommerce مش محتاج تكتب أي كود — البلجن بيعمل الفاتورة ويحوّل العميل ويستقبل الـ Webhook ويحدّث حالة الطلب لوحده.
- حمّل البلجن وارفعه من الإضافات ← أضف جديد ← رفع إضافة وفعّله.
- من WooCommerce ← الإعدادات ← المدفوعات ← دفتر كاش: فعّل البوابة وحط
Merchant API KeyوSignature Secret Key. - البلجن بيعرضلك رابط الـ Webhook (
https://your-store.com/wc-api/vodacash_webhook/) — الصقه في لوحة البوابة ← المطوّرين.
أسئلة شائعة
إيه صيغة الطلبات وعنوان إنشاء فاتورة الدفع؟
بتبعت POST على /api/v1/payments/create/ بصيغة JSON وترميز UTF-8، والمبالغ بالجنيه المصري (EGP)، وبيرجعلك checkout_url تحوّل عليه العميل لصفحة الدفع المستضافة.
إزاي بتتم المصادقة على طلبات الـ API؟
بتبعت Merchant API Key (بيبدأ بـ vc_live_) في هيدر X-Merchant-Key أو Authorization: Bearer، من سيرفرك بس. ما تحطّش المفاتيح في كود المتصفح أو تطبيق الموبايل.
أقدر أجرّب الربط من غير فلوس حقيقية؟
أيوه. بمفتاح التجربة (بيبدأ بـ vc_test_) الفواتير بتتعمل بوضع test_mode، وبيظهر في صفحة الدفع زرارين لمحاكاة دفع ناجح أو فاشل، وبيوصلك Webhook عادي فيه علامة التجربة.
إزاي أتأكد إن الـ Webhook جاي من البوابة فعلًا؟
كل Webhook موقّع بتوقيع HMAC باستخدام Signature Secret Key. بتتحقق من الهيدر X-VodaCash-Signature على الجسم الخام قبل ما تعلّم الطلب مدفوع، والخطوات بالأكواد في قسم التحقق من التوقيع.
إيه حدود عدد الطلبات على الـ API؟
إنشاء الفاتورة 30 طلب في الدقيقة لكل حساب، وإعادة إرسال Webhook 10 طلبات في الدقيقة. لو اتخطيت الحد بيرجع الرد 429 وتستنى دقيقة وتحاول تاني.
لو متجري WooCommerce محتاج أكتب كود؟
لأ. بلجن WooCommerce بيعمل الفاتورة ويحوّل العميل ويستقبل الـ Webhook ويحدّث حالة الطلب لوحده.
اقرأ أيضًا: إضافة WooCommerce · أسعار البوابة · البوابة بدون سجل تجاري
قبل الإطلاق
- المفاتيح في متغيرات بيئة على السيرفر — مش في الكود ولا في الواجهة.
- الـ Webhook بيتحقق من
X-VodaCash-Signatureعلى الجسم الخام، وبيقارن المبلغ وpayment_id. - معالجة الـ Webhook idempotent، وبترد
200بسرعة (أي شغل تقيل يتعمل بعد الرد). - فيه محفظة استقبال نشطة، وهاتفها عليه تطبيق دفتر كاش بلس شغّال في الخلفية.
- جرّبت الربط في بيئة التجربة (دفع ناجح وفاشل)، وبعدين بدّلت لمفتاح
vc_live_. - جرّبت دفعة حقيقية صغيرة من أوّلها لآخرها، وشفت الإشعار في سجل الـ Webhooks.
- حددت مدة انتظار التأكيد المناسبة لعملائك، وبتتابع تبويب المراجعة.