منع التكرار (Idempotency)
كرر الطلب وانت مطمّن: هيدر Idempotency-Key بيضمن إن الإرسال أو الحملة أو الـ OTP يتنفذ مرة واحدة بالكتير.
الشبكة بتقع. لو الطلب عمل timeout مش هتعرف الرسالة خرجت ولا لأ. ابعت Idempotency-Key مع كل عملية كتابة وكرر بـ نفس المفتاح: مرسلات بترجعلك أول رد بدل ما تعمل الشغل تاني.
Idempotency-Key: order-1042-shipped| الحالة | الرد |
|---|---|
| أول طلب | الردبيتنفذ عادي. |
| نفس المفتاح + نفس الـ body (خلال 24 ساعة) | الردالرد المتخزن، ومعاه Idempotent-Replayed: true. مفيش حاجة بتتبعت تاني. |
| نفس المفتاح + body مختلف | الرد409 IDEMPOTENCY_KEY_REUSED — استخدم مفتاح جديد لطلب جديد. |
| نفس المفتاح والأول لسه شغال | الرد409 IDEMPOTENCY_IN_PROGRESS — استنى شوية وكرر. |
| ناقص في إرسال / حملة / OTP | الرد400 IDEMPOTENCY_KEY_REQUIRED. |
| أطول من 191 أو فيه حروف مش ASCII ظاهرة | الرد400 IDEMPOTENCY_KEY_INVALID. |
إمتى يكون إجباري
إجباري في POST /v2/messages و POST /v2/campaigns و POST /v2/otp. اختياري بس مقبول في أي عملية كتابة تانية. المفاتيح مربوطة بمفتاح الـ API اللي استخدمها: مفتاح تاني، حتى لو من نفس الحساب، عمره ما يرجّع رد غيره.
تختار المفتاح إزاي
- اعمله من الحدث بتاعك عشان التكرار يستخدمه لوحده:
order-1042-shippedأوotp-login-<sessionId>. - متعملش مفتاح عشوائي لكل محاولة — كده الفكرة كلها بتبوظ.
- الردود المتكررة فيها ids وحالات بس (عمرها ما فيها نص رسالة أو تعليق أو رابط).
تكرار آمن
async function sendWithRetry(body, key, attempts = 4) {
for (let i = 0; i < attempts; i++) {
const res = await fetch('https://mursilat.com/api/v2/messages', {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.WASLA_API_KEY}`,
'Idempotency-Key': key, // نفس المفتاح في كل محاولة
'Content-Type': 'application/json',
},
body: JSON.stringify(body),
}).catch(() => null); // خطأ شبكة ← كرر
if (res && res.ok) return res.json();
if (res && res.status < 500 && res.status !== 409 && res.status !== 429) {
throw new Error((await res.json()).error.code); // التكرار مش هيفيد
}
const wait = Number(res?.headers.get('Retry-After')) || 2 ** i;
await new Promise((r) => setTimeout(r, wait * 1000));
}
throw new Error('gave up');
}