تكرار السؤال: …
الإجابة المختصرة
باستقبال ترويسة Idempotency-Key وتخزين استجابة الطلب الأول في Redis؛ فإذا تكرر نفس المفتاح تُعاد نفس الاستجابة المخزنة فوراً دون تكرار العملية المالية.
الشرح
في بوابات الدفع وشبكات الاتصال غير المستقرة، قد يضغط المستخدم زر الشراء مرتين، أو ينقطع الاتصال قبل وصول الاستجابة فيعيد التطبيق إرسال الطلب. باستخدام وسيط (Middleware)، يتم حجز المفتاح في Redis عبر SET NX؛ فإذا كان مسجلاً يُعاد الرد السابق، وإن لم يكن تُنفذ العملية ويُخزن الرد لمدة 24 ساعة، مانعاً الخصم المزدوج.
مثال بالكود
class IdempotencyMiddleware
{
public function handle(Request $request, Closure $next)
{
$key = $request->header('Idempotency-Key');
if (! $key) {
return $next($request);
}
$cacheKey = "idem:{$request->user()->id}:{$key}";
if ($cached = Cache::get($cacheKey)) {
return response()->json($cached['body'], $cached['status']); // replay the first response
}
if (! Cache::add("{$cacheKey}:lock", 1, 30)) {
return response()->json(['message' => 'Request already in progress'], 409);
}
$response = $next($request);
Cache::put($cacheKey, ['body' => $response->getData(true), 'status' => $response->status()], now()->addDay());
return $response;
}
}أخطاء شائعة
- تخزين المفتاح دون قفله ذريةً (Atomic Lock)، مما يتيح للطلبين المتزامنين تماماً النفاذ معاً.