01بيشتغل إزاي
- 1
منصتك تبعت كل طلب جديد على
POST /orders. - 2
يودوو يبعت رسالة التأكيد على واتساب من رقم واتساب الأعمال بتاعك، بزرارين «تأكيد» و«إلغاء»، ومعاها كل الأتوميشن اللي مشغّلها للمتجر (المتابعة، تحديث الشحن، طلب التقييم…).
- 3
العميل يدوس تأكيد أو إلغاء. يودوو يرد عليه على واتساب ويبعت
order.confirmedأوorder.cancelledلرابط الـ webhook بتاعك. - 4
منصتك تحرّك الطلب (تشحنه أو تلغيه). ولما يتشحن، اعمل PATCH للطلب برقم التتبع ورسالة تحديث الشحن توصل للعميل.
الرسايل والأزرار والمواعيد هي أتوميشن المتجر في تطبيق يودوو، زي متجر شوبيفاي أو سلة بالظبط: الـ API بس بيدخّل الطلبات وبيطلّع الردود.
02البداية
- 1
ادخل تطبيق يودوو (app.yudoo.io) واربط رقم واتساب الأعمال بتاعك.
- 2
الإعدادات ← منصات المتجر ← منصة أخرى (API): اكتب اسم المتجر ورابطه ودوس ربط.
- 3
انسخ مفتاح الـ API وسر التوقيع. المفتاح بيظهر المرة دي بس؛ خلّي الاتنين على السيرفر بتاعك، وأبداً مش في المتصفح أو تطبيق.
- 4
شغّل الأتوميشن اللي عايزه (تأكيد الدفع عند الاستلام الأول) من صفحة الأتوميشن.
- 5
ضيف رابط الـ webhook في كارت المتجر، ودوس «ابعت حدث تجريبي».
GET/api/v1/me
اتأكد إن المفتاح شغال: بيرد بالمتجر اللي المفتاح تبعه.
curl https://app.yudoo.io/api/v1/me -H "Authorization: Bearer wp_live_your_api_key"
{
"store": { "name": "My Store", "url": "https://mystore.com", "connected_at": "2026-11-04T09:00:00.000Z" },
"webhook": { "url": "https://mystore.com/yudoo/webhook", "events": ["order.confirmed", "order.cancelled"] }
} 03التوثيق
كل طلب لازم يكون فيه مفتاح الـ API بتاع المتجر في هيدر Authorization. وكمان X-API-Key: wp_live_… مقبول.
Authorization: Bearer wp_live_your_api_key - مفتاح واحد لكل متجر. تقدر تعمل مفتاح جديد من كارت المتجر في أي وقت، والقديم بيبطل يشتغل فوراً.
- الطلبات على HTTPS بس، والردود JSON.
- أقصى حاجة 120 طلب في الدقيقة لكل متجر (بعدها 429
rate_limited).
ماتحطش المفتاح أبداً في صفحات المتجر أو تطبيق موبايل أو JavaScript في المتصفح: أي حد معاه المفتاح يقدر يبعت طلبات باسم متجرك.
04إنشاء طلب
POST/api/v1/orders
ابعت كل طلب أول ما يتعمل. يودوو بيحفظه ويشغّل أتوميشن الطلبات بتاعة المتجر: رسالة التأكيد المناسبة لطريقة الدفع الأول. لو بعت نفس الـ id تاني الطلب بيتحدّث بدل ما يتعمل طلب تاني، فإعادة الإرسال دايماً آمنة.
curl -X POST https://app.yudoo.io/api/v1/orders \
-H "Authorization: Bearer wp_live_your_api_key" \
-H "Content-Type: application/json" \
-d '{
"id": "1042",
"number": "1042",
"status": "pending",
"customer": { "name": "Mohamed Ali", "phone": "201001234567", "email": "[email protected]" },
"currency": "EGP",
"total": 540,
"shipping_total": 40,
"payment_method": "cod",
"shipping_address": { "address1": "12 Tahrir St", "city": "Cairo", "province": "Cairo", "country": "EG" },
"items": [
{ "name": "Cotton T-shirt", "variant": "White / L", "quantity": 1, "price": 250, "sku": "TS-WH-L" },
{ "name": "Sport socks", "quantity": 2, "price": 125 }
],
"note": "Please deliver after 5 pm"
}'const res = await fetch("https://app.yudoo.io/api/v1/orders", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.WHATPRO_API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
id: order.id, // your platform's order id
number: order.number,
customer: { name: order.customerName, phone: order.phone }, // 201001234567
currency: "EGP",
total: order.total,
payment_method: "cod",
items: order.lines.map((l) => ({ name: l.title, quantity: l.qty, price: l.price })),
}),
});
const { order: saved, error } = await res.json();$ch = curl_init("https://app.yudoo.io/api/v1/orders");
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
"Authorization: Bearer " . getenv("WHATPRO_API_KEY"),
"Content-Type: application/json",
],
CURLOPT_POSTFIELDS => json_encode([
"id" => (string) $order->id,
"number" => $order->number,
"customer" => ["name" => $order->name, "phone" => $order->phone],
"currency" => "EGP",
"total" => $order->total,
"payment_method" => "cod",
"items" => array_map(fn($l) => ["name" => $l->title, "quantity" => $l->qty, "price" => $l->price], $order->lines),
]),
]);
$response = json_decode(curl_exec($ch), true);import os, requests
res = requests.post(
"https://app.yudoo.io/api/v1/orders",
headers={"Authorization": f"Bearer {os.environ['WHATPRO_API_KEY']}"},
json={
"id": str(order.id),
"number": order.number,
"customer": {"name": order.name, "phone": order.phone},
"currency": "EGP",
"total": order.total,
"payment_method": "cod",
"items": [{"name": l.title, "quantity": l.qty, "price": l.price} for l in order.lines],
},
timeout=10,
)
saved = res.json()["order"] | الحقل | النوع | الوصف |
|---|---|---|
id | string | مطلوب. رقم الطلب عندك في منصتك. لو بعت نفس الـ id تاني، الطلب ده بيتحدّث. |
customer.phone | string | مطلوب. بالصيغة الدولية بكود الدولة، أرقام بس (201001234567). رسايل واتساب بتروح للرقم ده. |
customer.name | string | اسم العميل ({{customer_first_name}} في الرسايل). |
customer.email | string | اختياري. |
number | string | الرقم اللي العميل عارفه ("1042" ← #1042). لو مابعتوش بيبقى #id. |
status | string | pending (الافتراضي)، processing، shipped، delivered، cancelled… شوف حالات الطلب. |
payment_method | string | cod، bank_transfer، instapay، vodafone_cash، card… بيحدد رسالة التأكيد اللي الطلب ياخدها. شوف طرق الدفع. |
currency | string | كود ISO من 3 حروف: EGP، SAR، AED، USD… |
total | number | إجمالي الطلب شامل الشحن. |
shipping_total | number | مصاريف الشحن. |
items[] | array | لحد 200 سطر: name (مطلوب)، quantity، price، variant، sku، product_id، image_url (https). |
shipping_address | object | address1، address2، city، province، postal_code، country (كود من حرفين أو الاسم). |
note | string | ملاحظة العميل على الطلب. |
tracking | object | number، url، carrier — لو الطلب اتشحن خلاص. |
placed_at | string | تاريخ ISO 8601. لو مابعتوش بيبقى وقت وصول الطلب ليودوو. |
الرد بيبقى 201 Created ومعاه الطلب (200 لو الـ id اتبعت قبل كده):
{
"order": {
"id": "1042",
"number": "#1042",
"status": "pending",
"confirmation": { "status": "pending", "at": null, "cancel_reason": null },
"customer": { "name": "Mohamed Ali", "phone": "201001234567", "email": "[email protected]" },
"currency": "EGP",
"total": 540,
"payment_method": "cod",
"items": [
{ "name": "Cotton T-shirt", "variant": "White / L", "quantity": 1, "price": 250, "sku": "TS-WH-L", "product_id": null },
{ "name": "Sport socks", "variant": null, "quantity": 2, "price": 125, "sku": null, "product_id": null }
],
"tracking": null,
"created_at": "2026-11-04T10:00:00.000Z",
"updated_at": "2026-11-04T10:00:00.000Z"
}
} 05قراءة طلب
GET/api/v1/orders/{id}
اقرا طلب بالـ id بتاعك، ورد العميل في confirmation.
curl https://app.yudoo.io/api/v1/orders/1042 \
-H "Authorization: Bearer wp_live_your_api_key" | confirmation.status | المعنى |
|---|---|
pending | لسه مفيش رد. |
confirmed | العميل (أو انت من يودوو) أكد الطلب. |
cancelled | العميل (أو انت) لغى الطلب؛ وcancel_reason فيه السبب اللي اختاره لو قاله. |
06تحديث طلب
PATCH/api/v1/orders/{id}
ابعت اللي اتغيّر بس: الحالة، التتبع، المنتجات أو الإجمالي. الحالة الجديدة بتشغّل الأتوميشن المناسب (تحديث الشحن، جاهز للاستلام، إلغاء الطلب…)، ورقم التتبع بيدّي رسالة تحديث الشحن اللينك بتاعها.
curl -X PATCH https://app.yudoo.io/api/v1/orders/1042 \
-H "Authorization: Bearer wp_live_your_api_key" \
-H "Content-Type: application/json" \
-d '{
"status": "shipped",
"tracking": { "number": "7845120963", "url": "https://track.example.com/7845120963", "carrier": "Bosta" }
}' 07حالات الطلب وطرق الدفع
ابعت الكلمات اللي منصتك بتستخدمها: يودوو بيفهمها بالإنجليزي والعربي. دي اللي بيفهمها:
| الحالة | أمثلة بيفهمها يودوو |
|---|---|
جديد | pending، new، placed، awaiting، unpaid، جديد |
قيد التجهيز | processing، confirmed، preparing، packed، on hold، قيد التجهيز |
جاهز للاستلام | ready for pickup، ready to collect، جاهز للاستلام |
تم الشحن | shipped، out for delivery، in transit، تم الشحن |
تم التوصيل | delivered، completed، fulfilled، تم التوصيل |
فشل التوصيل | failed delivery، undelivered، فشل التوصيل |
ملغي | cancelled، canceled، rejected، refunded، ملغي |
مرتجع | returned، rto، مرتجع |
| payment_method | رسالة التأكيد اللي بتتبعت |
|---|---|
cod، cash on delivery | تأكيد الدفع عند الاستلام (تأكيد / إلغاء) |
bank_transfer، instapay، deposit | تأكيد التحويل البنكي (إرسال إثبات الدفع) |
vodafone_cash، wallet، manual، pay later | تأكيد الدفع اليدوي |
card، paid، أي حاجة تانية | تأكيد الطلب المدفوع |
08الـ Webhooks
ضيف رابط الـ webhook بتاعك في كارت المتجر في يودوو (https بس). يودوو بيبعت عليه حدث JSON كل ما طلب ياخد رد:
| الحدث | إمتى |
|---|---|
order.confirmed | العميل داس تأكيد، أو انت أكدت الطلب من يودوو (data.by = customer / merchant). |
order.cancelled | العميل داس إلغاء، أو انت لغيته من يودوو. السبب لو اتقال في data.order.confirmation.cancel_reason. |
webhook.test | دوست «ابعت حدث تجريبي»، أو استخدمت POST /webhooks/test. |
POST https://mystore.com/yudoo/webhook
Content-Type: application/json
Yudoo-Event: order.cancelled
Yudoo-Delivery: evt_k3j9x2m1q8w7e6r5t4y3u2i1
Yudoo-Signature: t=1793782800,v1=5f2b8c…e91a
{
"id": "evt_k3j9x2m1q8w7e6r5t4y3u2i1",
"type": "order.cancelled",
"created_at": "2026-11-04T10:20:00.000Z",
"data": {
"by": "customer",
"order": {
"id": "1042",
"number": "#1042",
"status": "pending",
"confirmation": { "status": "cancelled", "at": "2026-11-04T10:19:58.000Z", "cancel_reason": "Changed my mind" },
"customer": { "name": "Mohamed Ali", "phone": "201001234567", "email": "[email protected]" },
"…": "the same order object as GET /orders/{id}"
}
}
} - رد بأي 2xx خلال 10 ثواني، واعمل الشغل التقيل بعد ما ترد.
- لو الإرسال فشل بنعيده بعد دقيقة، 5 دقايق، 30 دقيقة، ساعتين، 6 ساعات، 12 ساعة و24 ساعة، وبعدها بيتعلّم فاشل. كارت المتجر بيعرض آخر الإرساليات.
- نفس الحدث ممكن يوصل مرتين (إعادة بعد timeout): لو الـ id اتعامل معاه قبل كده تجاهله.
- اتأكد من التوقيع دايماً قبل ما تثق في أي حدث.
POST/api/v1/webhooks/test
curl -X POST https://app.yudoo.io/api/v1/webhooks/test -H "Authorization: Bearer wp_live_your_api_key"
{ "delivery": { "id": "evt_…", "delivered": true, "error": null } } 09التحقق من التوقيع
كل webhook معاه هيدر Yudoo-Signature: الـ t هو وقت الإرسال (ثواني Unix) والـ v1 هو HMAC-SHA256 لـ "t.raw_body" بسر التوقيع بتاعك، بالـ hex.
Yudoo-Signature: t=1793782800,v1=5f2b8c0d…e91a - 1
اقرا الـ body الخام قبل ما تحوّله لـ JSON.
- 2
احسب HMAC-SHA256(
signing_secret, t + "." +raw_body) بالـ hex. - 3
قارنه بالـ v1 بمقارنة ثابتة الوقت، وارفض أي t أقدم من 5 دقايق.
import crypto from "node:crypto";
import express from "express";
const app = express();
// Read the RAW body: the signature is over the exact bytes Yudoo sent.
app.post("/yudoo/webhook", express.raw({ type: "application/json" }), (req, res) => {
const header = req.get("Yudoo-Signature") ?? "";
const { t, v1 } = Object.fromEntries(header.split(",").map((p) => p.split("=")));
const expected = crypto
.createHmac("sha256", process.env.WHATPRO_SIGNING_SECRET)
.update(`${t}.${req.body}`)
.digest("hex");
const fresh = Math.abs(Date.now() / 1000 - Number(t)) < 300;
if (!fresh || !v1 || !crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(v1))) {
return res.status(400).send("bad signature");
}
const event = JSON.parse(req.body);
// event.type: "order.confirmed" | "order.cancelled" | "webhook.test"
res.sendStatus(200);
});$raw = file_get_contents("php://input");
$header = $_SERVER["HTTP_WHATPRO_SIGNATURE"] ?? "";
parse_str(str_replace(",", "&", $header), $sig); // ["t" => …, "v1" => …]
$expected = hash_hmac("sha256", $sig["t"] . "." . $raw, getenv("WHATPRO_SIGNING_SECRET"));
$fresh = abs(time() - (int) $sig["t"]) < 300;
if (!$fresh || !hash_equals($expected, $sig["v1"] ?? "")) {
http_response_code(400);
exit("bad signature");
}
$event = json_decode($raw, true);
http_response_code(200);import hmac, hashlib, os, time
from flask import Flask, request, abort
app = Flask(__name__)
@app.post("/yudoo/webhook")
def yudoo_webhook():
raw = request.get_data() # the raw bytes, before any JSON parsing
parts = dict(p.split("=", 1) for p in request.headers.get("Yudoo-Signature", "").split(","))
expected = hmac.new(
os.environ["WHATPRO_SIGNING_SECRET"].encode(),
f"{parts.get('t')}.".encode() + raw,
hashlib.sha256,
).hexdigest()
fresh = abs(time.time() - int(parts.get("t", 0))) < 300
if not fresh or not hmac.compare_digest(expected, parts.get("v1", "")):
abort(400)
event = request.get_json()
return "", 200 10الأخطاء
الطلب اللي بيفشل بيرد بـ HTTP status وكائن خطأ واحد:
HTTP/1.1 422 Unprocessable Entity
{
"error": {
"code": "missing_field",
"message": "`customer.phone` is required, in international format with the country code (e.g. 201001234567).",
"field": "customer.phone"
}
} | Status | code | المعنى |
|---|---|---|
400 | invalid_json | الـ body مش JSON object. |
401 | missing_api_key / invalid_api_key | مفيش مفتاح، أو المفتاح غلط أو اتغيّر، أو المتجر اتفصل. |
404 | order_not_found | مفيش طلب بالـ id ده اتبعت للمتجر ده. |
405 | method_not_allowed | الـ method غلط للـ endpoint ده. |
409 | no_webhook_url | الحدث التجريبي محتاج رابط webhook الأول. |
413 | body_too_large | الـ body أكبر من 64 KB. |
422 | missing_field / invalid_field / invalid_order | حقل ناقص أو غلط؛ الحقل مكتوب في field. |
429 | rate_limited | أكتر من 120 طلب في الدقيقة للمتجر. |
500 | server_error | الطلب ماتحفظش؛ ابعته تاني. |
واقف في حاجة، أو محتاج حقل مش موجود؟ فريقنا بيرد على واتساب.