# عدم التكرار (/ar/integrate/idempotency)

أعد محاولة طلبات الكتابة بأمان بـ Idempotency-Key. XPay بيسجّل النتيجة الأولى وبيعيد إرسالها، فالاستدعاء المعاد عمره ما بيكرّر الأثر الجانبي.

استدعاءات الشبكة بتفشل بطرق بتسيبك مش متأكد إذا كان الطلب عدّى ولا لأ. الـ `Idempotency-Key` بيخلّيك تعيد محاولة الكتابة بأمان: XPay بيسجّل نتيجة أول طلب استخدم مفتاح معيّن وبيرجّع نفس النتيجة دي لكل محاولة إعادة، فالعملية بتشتغل مرة واحدة حتى لو بعتّها خمس مرات.

<Callout type="info">
  ابعت `Idempotency-Key` على أي عملية كتابة ممكن تعيدها. ده مهم أكتر حاجة على `POST /refunds`، اللي فيها
  إعادة محاولة عشوائية بعد مهلة منتهية ممكن ترد للعميل مرتين.
</Callout>

## إزاي بيشتغل [#إزاي-بيشتغل]

1. بتولّد مفتاح فريد لعملية منطقية واحدة وتبعته في ترويسة `Idempotency-Key`.
2. XPay بيسجّل رد أول طلب استخدم المفتاح ده.
3. كل طلب لاحق بنفس المفتاح بيرجّع الرد المخزَّن ده، مع ترويسة `Idempotent-Replayed: true`. وعمليتك بتشتغل مرة واحدة.

المفاتيح مرتبطة بحسابك ووضعك (اختبار أو حساب فعلي)، وبتنتهي صلاحيتها بعد 24 ساعة.

## ابعت Idempotency-Key [#ابعت-idempotency-key]

ضيف الترويسة لأي عملية كتابة مدعومة. المفتاح من تولّيدك أنت. الـ UUID من الإصدار الرابع اختيار افتراضي كويس.

<Tabs items="[&#x22;cURL&#x22;, &#x22;Node.js&#x22;]">
  <Tab value="cURL">
    ```bash
    curl -X POST https://api.xpay.app/refunds \
      -H "Authorization: Bearer sk_test_..." \
      -H "Idempotency-Key: 5f0c9b1a-4e2d-4a6f-9c3b-7d1e0a2f4b88" \
      -H "Content-Type: application/json" \
      -d '{
        "paymentIntentId": "pi_test_xyz789",
        "amount": 50000,
        "reason": "requested_by_customer"
      }'
    ```
  </Tab>

  <Tab value="Node.js">
    ```typescript
    import { randomUUID } from "node:crypto";

    const res = await fetch("https://api.xpay.app/refunds", {
      method: "POST",
      headers: {
        Authorization: `Bearer ${process.env.XPAY_SECRET_KEY}`,
        "Idempotency-Key": randomUUID(),
        "Content-Type": "application/json",
      },
      body: JSON.stringify({
        paymentIntentId: "pi_test_xyz789",
        amount: 50000,
        reason: "requested_by_customer",
      }),
    });
    ```
  </Tab>
</Tabs>

أعد استخدام نفس المفتاح لما تعيد نفس العملية. وولّد مفتاح جديد لعملية جديدة فعلًا.

## اختيار مفتاح [#اختيار-مفتاح]

* استخدم مفتاح واحد لكل عملية منطقية: محاولة استرداد واحدة، إنشاء جلسة دفع واحدة. احفظه جنب العملية اللي بتعيدها علشان الإعادة تبعت نفس القيمة بالظبط.
* خلّيه فريد وصعب التخمين. الـ UUID من الإصدار الرابع بيشتغل كويس. المفتاح بحد أقصى 255 حرف.
* ما تعيدش استخدام مفتاح واحد لعمليات مش مرتبطة. استردادين مختلفين محتاجين مفتاحين مختلفين.

## إعادة استخدام مفتاح [#إعادة-استخدام-مفتاح]

اللي XPay بيرجّعه على طلب مكرر بيعتمد على إذا كان الطلب مطابق لأول واحد استخدم المفتاح:

* **نفس المفتاح، نفس الطلب** بيرجّع نفس الرد اللي بعته أول مرة، مع `Idempotent-Replayed: true`. المعالِج مش بيشتغل تاني، فمفيش استرداد ولا خصم ولا سجل تاني بيتعمل.
* **نفس المفتاح، طلب مختلف** بيرجّع `400` بـ `error.code` قيمته `idempotency_key_in_use`. المفتاح بيرتبط بأول طلب اتستخدم معاه (الطريقة، والمسار، والاستعلام، والمحتوى). علشان تبعت طلب مختلف، استخدم مفتاح جديد. شوف [`idempotency_key_in_use`](/integrate/errors/api-error-codes#idempotency_key_in_use).

## طلبات لسه بتشتغل [#طلبات-لسه-بتشتغل]

لو طلب تاني وصل بنفس المفتاح والأول لسه شغّال، XPay بيرجّع `409` مع ترويسة `Retry-After`. استنى عدد الثواني اللي في `Retry-After`، وبعدين أعد المحاولة. وقتها الطلب الأول هيكون خلص، والإعادة بترجّع نتيجته المخزَّنة.

## بيتخزّن إيه، ولمدة قد إيه [#بيتخزّن-إيه-ولمدة-قد-إيه]

XPay بيخزّن أول نتيجة **حاسمة** لكل مفتاح:

* النجاح، أو الخطأ اللي الطلب أنتجه بعد ما بدأ المعالجة (زي بطاقة مرفوضة)، بيتخزّن وبيتعاد إرساله. علشان تشغّل العملية دي تاني، استخدم مفتاح جديد.
* الطلب المرفوض قبل المعالجة (خطأ تحقق)، أو اللي صادفه فشل مؤقت، **مش** بيتخزّن. أعد محاولته بنفس المفتاح وهيشتغل من جديد.

النتائج المخزَّنة بتنتهي صلاحيتها بعد 24 ساعة. بعدها، نفس المفتاح بيتعامل معاه على إنه جديد.

## أنهي طلبات بتدعمه [#أنهي-طلبات-بتدعمه]

ابعت `Idempotency-Key` على نقاط الكتابة اللي بتنشئ أو بتغيّر مورد:

* إنشاء <ApiLink href="/api-reference/objects/refund">Refund</ApiLink>، عن طريق `POST /refunds`. أهم
  واحدة.
* إنشاء وتحديث وانتهاء <ApiLink href="/api-reference/objects/checkout-session">Checkout Session</ApiLink>.
* إنشاء وتحديث المنتج، وإنشاء وتحديث السعر.
* إنشاء وتحديث رابط الدفع.
* إنشاء العميل.

طلبات `GET` و`DELETE` بلا تكرار أصلًا، فالترويسة مالهاش تأثير عليها.

## عدم التكرار مقابل أحداث الـ webhook [#عدم-التكرار-مقابل-أحداث-الـ-webhook]

الصفحة دي بتتكلم عن الطلبات اللي بتبعتها لـ XPay. فيه مسألة عدم تكرار منفصلة للـ webhooks اللي XPay بيبعتهالك: نفس الحدث ممكن يوصل أكتر من مرة، فلازم المعالِج بتاعك يتخطّى المكررات حسب `event.id`. الاتنين مستقلين عن بعض.

| الجانب                  | عدم تكرار الطلبات (الصفحة دي)         | عدم تكرار أحداث الـ webhook |
| ----------------------- | ------------------------------------- | --------------------------- |
| الاتجاه                 | أنت بتنادي XPay                       | XPay بينادي عليك            |
| المفتاح                 | ترويسة `Idempotency-Key` اللي بتبعتها | `event.id` اللي بتقراه      |
| مين بيتعامل مع المكررات | XPay                                  | المعالِج بتاعك              |

علشان ناحية الـ webhook، شوف [التحقق من التوقيعات ← عدم التكرار](/integrate/webhooks/verifying-signatures#عدم-التكرار).

## رايح فين بعد كده [#رايح-فين-بعد-كده]

<Cards>
  <Card icon="<RefreshCw />" title="المبالغ المستردة" href="/integrate/refunds">
    المسار اللي فيه الـ Idempotency-Key بيبقى أهم حاجة.
  </Card>

  <Card icon="<KeyRound />" title="أكواد أخطاء الـ API" href="/integrate/errors/api-error-codes">
    خطأ `idempotency_key_in_use` وإزاي تحلّه.
  </Card>

  <Card icon="<Webhook />" title="التحقق من التوقيعات" href="/integrate/webhooks/verifying-signatures">
    أزل تكرار تسليمات الـ webhook على `event.id`.
  </Card>

  <Card icon="<Compass />" title="اختار طريقة الدمج" href="/get-started/choose-your-integration">
    اختار نمط الدمج اللي يناسب بنيتك التقنية.
  </Card>
</Cards>