واجهة REST و OAuth
صادِق عبر OAuth 2.0 واقرأ أو اكتب بيانات متجر التاجر عبر HTTPS.
نظرة عامة
واجهة المطورين بتخلّي تطبيق طرف-ثالث يقرأ ويكتب بيانات متجر التاجر عبر HTTPS. الصلاحية بيمنحها التاجر نفسه مش إنت: هو يثبّت تطبيقك، يراجع الأذونات اللي بتطلبها، ويقدر يلغيها في أي وقت.
- الرابط الأساسي:
https://{platform-domain}/api/v1 - المصادقة: OAuth 2.0 بمنح authorization-code، PKCE إلزامي
- الصيغة: JSON دخولًا وخروجًا (الواجهة دايمًا بترد JSON)
- حد المعدل: 120 طلب/دقيقة لكل توكن
1. سجّل تطبيقك
من كونسول المطور روح تطبيقاتي (/developer/apps) واعمل تطبيق. بتدخل:
| الحقل | ملاحظات |
|---|---|
| الاسم والوصف والموقع | بتظهر للتاجر في شاشة الموافقة |
| Redirect URIs | واحد في كل سطر. لازم https:// (http:// مسموح على localhost). بتتطابق حرفيًا وقت التفويض |
| الصلاحيات (Scopes) | الأذونات اللي تطبيقك ممكن يطلبها |
هتستلم client_id وclient_secret. السر بيظهر مرة واحدة ومش قابل للاسترجاع أبدًا — بيتخزن الهاش بتاعه بس. ضاع منك؟ استخدم تدوير السر (القديم يموت فورًا).
2. الصلاحيات
اطلب أقل حاجة تحتاجها — التاجر بيشوف كل صلاحية في شاشة الموافقة.
| الصلاحية | تمنح |
|---|---|
products.read |
عرض المنتجات والمتغيرات والمخزون |
products.write |
إنشاء وتعديل وحذف المنتجات |
categories.read |
عرض الفئات |
categories.write |
إنشاء وتعديل وحذف الفئات |
orders.read |
عرض الطلبات وعناصرها |
orders.write |
تحديث حالة الطلب والتنفيذ |
customers.read |
عرض العملاء وبيانات التواصل |
webhooks.manage |
إدارة اشتراكات الـwebhook |
3. وجّه التاجر لشاشة الموافقة
جهّز زوج PKCE الأول:
code_verifier = نص عشوائي 43–128 حرف (سري، على الخادم)
code_challenge = BASE64URL( SHA256(code_verifier) )
وبعدين وجّه التاجر لـ:
GET https://{platform-domain}/oauth/authorize
?response_type=code
&client_id=dapi_xxxxxxxx
&redirect_uri=https://yourapp.com/oauth/callback
&scope=products.read%20orders.read
&state=RANDOM_ANTI_CSRF_VALUE
&code_challenge=BASE64URL_SHA256_OF_VERIFIER
&code_challenge_method=S256
التاجر يسجّل دخول ويوافق. بنرجّعه لـredirect_uri بتاعك:
https://yourapp.com/oauth/callback?code=ONE_TIME_CODE&state=RANDOM_ANTI_CSRF_VALUE
تحقق دايمًا إن state مطابق للي بعتّه. لو التاجر رفض، هتستلم ?error=access_denied بدل الكود.
لو
client_idأوredirect_uriغير صالح بنعرض صفحة خطأ وما بنوجّهش — إحنا أبدًا ما بنبعت كود لوجهة غير مسجّلة.
4. بدّل الكود بتوكنات
curl -X POST https://{platform-domain}/oauth/token \
-d grant_type=authorization_code \
-d client_id=dapi_xxxxxxxx \
-d client_secret=dapi_sec_xxxxxxxx \
-d code=ONE_TIME_CODE \
-d redirect_uri=https://yourapp.com/oauth/callback \
-d code_verifier=YOUR_ORIGINAL_VERIFIER
{
"access_token": "dapi_at_…",
"refresh_token": "dapi_rt_…",
"token_type": "Bearer",
"expires_in": 3600,
"scope": "products.read orders.read"
}
الكود يُستخدم مرة واحدة وينتهي خلال 10 دقائق. إعادة استخدامه، أو code_verifier غلط، أو redirect_uri غير مطابق — كلها ترجّع invalid_grant.
بيانات اعتماد العميل ممكن كمان تتبعت عبر HTTP Basic بدل حقول الفورم.
التجديد
توكن الوصول بيعيش ساعة، وتوكن التجديد 30 يوم. التجديد بيدوّر الزوج — التوكنات القديمة تتبطّل فورًا:
curl -X POST https://{platform-domain}/oauth/token \
-d grant_type=refresh_token \
-d client_id=dapi_xxxxxxxx \
-d client_secret=dapi_sec_xxxxxxxx \
-d refresh_token=dapi_rt_…
الإبطال
curl -X POST https://{platform-domain}/oauth/revoke \
-d client_id=… -d client_secret=… -d token=dapi_at_…
بيرجّع دايمًا 200 (حسب RFC 7009)، حتى لتوكن غير معروف.
5. نادِ الواجهة
ابعت توكن الوصول كـBearer:
curl https://{platform-domain}/api/v1/products \
-H "Authorization: Bearer dapi_at_…"
اعرف التوكن بتاع أنهي متجر:
GET /api/v1/me
{
"data": {
"application": { "client_id": "dapi_…", "name": "تطبيقك" },
"store": { "user_id": 42, "name": "متجر أكمي", "email": "owner@acme.com" },
"scopes": ["products.read"],
"expires_at": "2026-07-18T12:58:04+00:00"
}
}
6. النقاط (Endpoints)
كل نقطة مقصورة على التاجر اللي منح التوكن — مستحيل تشوف أو تلمس بيانات متجر تاني.
المنتجات — products.read / products.write
| الميثود | المسار | ملاحظات |
|---|---|---|
GET |
/api/v1/products |
فلاتر: search, category_id, is_active, per_page |
GET |
/api/v1/products/{id} |
|
POST |
/api/v1/products |
name وprice مطلوبين |
PUT |
/api/v1/products/{id} |
|
DELETE |
/api/v1/products/{id} |
curl -X POST https://{platform-domain}/api/v1/products \
-H "Authorization: Bearer dapi_at_…" \
-H "Content-Type: application/json" \
-d '{"name":"Blue Widget","price":99.50,"stock_quantity":7,"type":"physical"}'
type بيقبل physical (الافتراضي) أو digital. وcategory_id لازم تكون تابعة لنفس المتجر.
الفئات — categories.read / categories.write
| الميثود | المسار |
|---|---|
GET |
/api/v1/categories (فلاتر: parent_id, is_active) |
GET |
/api/v1/categories/{id} |
POST |
/api/v1/categories |
PUT |
/api/v1/categories/{id} |
DELETE |
/api/v1/categories/{id} |
الطلبات — orders.read / orders.write
| الميثود | المسار | ملاحظات |
|---|---|---|
GET |
/api/v1/orders |
فلاتر: status, payment_status, created_after |
GET |
/api/v1/orders/{id} |
بتشمل عناصر الطلب |
PATCH |
/api/v1/orders/{id} |
status, payment_status, notes فقط |
status: pending, processing, completed, canceled.
payment_status: pending, paid, failed.
حقول الفلوس مش قابلة للكتابة. الإجماليات بتحسبها خطوط تسعير المنصة نفسها.
العملاء — customers.read (قراءة فقط)
| الميثود | المسار |
|---|---|
GET |
/api/v1/customers (فلتر: search) |
GET |
/api/v1/customers/{id} |
العملاء بيانات شخصية: المورد ده للقراءة فقط وما بيكشفش كلمات مرور ولا أرصدة محافظ.
الـWebhooks — webhooks.manage
| الميثود | المسار |
|---|---|
GET |
/api/v1/webhooks |
POST |
/api/v1/webhooks |
DELETE |
/api/v1/webhooks/{id} |
7. الترقيم (Pagination)
نقاط القوائم بترجّع data وmeta. استخدم per_page (أقصى 100، الافتراضي 25) وpage.
{
"data": [ … ],
"meta": { "current_page": 1, "per_page": 25, "total": 134, "last_page": 6 }
}
8. الـWebhooks
اشترك في حدث وإحنا بنبعتهولك على نقطتك ساعة ما يحصل.
curl -X POST https://{platform-domain}/api/v1/webhooks \
-H "Authorization: Bearer dapi_at_…" \
-H "Content-Type: application/json" \
-d '{"event":"order.created","target_url":"https://yourapp.com/hooks/orders"}'
الرد بيحتوي على signing_secret — بيظهر مرة واحدة، خزّنه.
الأحداث: order.created, order.updated, product.created, product.updated, customer.created.
الـtarget_url لازم تكون https://.
شكل التسليم
POST /hooks/orders
X-Dropsaas-Event: order.created
X-Dropsaas-Event-Id: evt_abc123…
X-Dropsaas-Signature: t=1752835200,v1=9f86d0818…
Content-Type: application/json
{ "id": "evt_abc123…", "event": "order.created",
"created_at": "2026-07-18T12:00:00+00:00", "data": { … } }
التحقق من التوقيع
أعد حساب الـHMAC على "{timestamp}.{raw_request_body}":
[$t, $v1] = // فكّ "t=…,v1=…" من X-Dropsaas-Signature
$expected = hash_hmac('sha256', $t.'.'.$rawBody, $signingSecret);
if (! hash_equals($expected, $v1)) {
abort(400); // ارفض — مش مننا
}
// ارفض أي حاجة أقدم من ~5 دقائق لمنع إعادة التشغيل.
if (abs(time() - (int) $t) > 300) {
abort(400);
}
الطابع الزمني جوه النص الموقّع، فمهاجم ما يقدرش يعيد إرسال جسم ملتقَط بطابع زمني جديد.
إعادة المحاولة
رجّع 2xx للإقرار. أي رد غير 2xx أو انتهاء مهلة بيتعاد بتراجع (10ث، 1د، 5د، 15د، 1س — 5 محاولات). بعد 10 إخفاقات متتالية الاشتراك بيتعطّل تلقائيًا. استخدم X-Dropsaas-Event-Id لمنع التكرار: نفس معرّف الحدث ممكن يوصل أكتر من مرة.
9. الأخطاء
| الحالة | error |
المعنى |
|---|---|---|
401 |
invalid_token |
توكن ناقص أو تالف أو منتهي أو مُبطَل |
401 |
invalid_client |
فشلت مصادقة العميل على /oauth/token |
400 |
invalid_grant |
كود غلط/مستخدم، أو PKCE أو redirect_uri غلط، أو refresh ميت |
400 |
unsupported_grant_type |
فقط authorization_code وrefresh_token |
403 |
insufficient_scope |
التوكن ما يحملش الصلاحية المطلوبة (الرد بيسمّيها) |
404 |
not_found |
مفيش سجل بالمعرّف ده في المتجر ده |
422 |
— | فشل التحقق؛ شوف errors |
429 |
— | تجاوزت حد المعدل |
{ "error": "insufficient_scope",
"error_description": "This token does not carry the required scope.",
"required_scope": "orders.read" }
10. ملاحظات أمنية
- ما تشحنش الـ
client_secretفي تطبيق موبايل أو حزمة واجهة أمامية. PKCE بيحمي تبديل الكود، لكن السر للعملاء السرّيين (على الخادم). - خزّن التوكنات مشفّرة؛ عاملها زي كلمات المرور.
- تحقق من
stateفي كل callback لمنع CSRF. - تحقق من توقيع الـwebhook في كل تسليم — ما تثقش في الجسم لوحده أبدًا.
- التاجر يقدر يلغي وصولك فورًا من التطبيقات المتصلة؛ تعامل مع
401 invalid_tokenبإعادة تشغيل تدفّق التفويض.