# نظرة عامة (/ar/integrate/checkout-session/overview)

جلسة الدفع هي الكائن المركزي اللي بينشئه أي دمج. نفس الشكل بيرجعلك من POST، وGET، وعميل الـ SDK، ومن كل webhook خاص بـ checkout.session.

جلسة الدفع بتمثّل محاولة عميل واحد إنه يدفعلك. السيرفر بتاعك بينشئها، وXPay بيرجّعلك كائن الجلسة، ومن هنا كل نمط دمج هو مجرد طريقة مختلفة لعرض نفس الكائن: صفحة مستضافة، أو رابط دفع، أو SDK drop-in، أو Elements مخصّصة بالكامل.

كائن الجلسة اللي بيرجعلك من `POST /checkout/sessions`، واللي بتعمله `GET` بعدين، والـ `data.object` اللي بيتحمل على كل <ApiLink href="/api-reference/objects/checkout-session">`checkout.session`</ApiLink> webhook. كلهم نفس الشكل. فيه DTO واحد وmapper واحد ورا الثلاثة. لو بنيت على رد الإنشاء، معالِج الـ webhook بتاعك هيقرا نفس الحقول بنفس الأسماء.

علشان تعرف علاقة جلسة الدفع بـ Payment Intent، وCharge، وRefund، والعميل، وBalance Transaction، شوف [نموذج الكائنات](/integrate/object-model).

## إزاي بتتركّب [#إزاي-بتتركّب]

اختار نمط الدمج اللي يناسب قد إيه عايز تتحكم في الواجهة. كل دليل نمط بيشرح البناء، وطريقة الاختبار، ومعالِج الـ webhook، وقائمة التحقق قبل الإنتاج للسطح ده. كلها بتنشئ جلسة دفع بنفس الطريقة.

<Cards>
  <Card icon="<LinkIcon />" title="روابط الدفع" href="/integrate/integration-patterns/payment-links">
    من غير كود. شارك رابط `https://checkout.xpay.app/p/plink_...`. وبتتنشئ الجلسة أول ما العميل
    يتفاعل.
  </Card>

  <Card icon="<ExternalLink />" title="الدفع المستضاف" href="/integrate/integration-patterns/hosted-checkout">
    من السيرفر بس. أنشئ جلسة، وحوّل العميل لـ `session.url`، وأكّد الدفع بـ webhook.
  </Card>

  <Card icon="<AppWindow />" title="Drop-in" href="/integrate/integration-patterns/drop-in">
    سيرفر زائد كام سطر في الواجهة. افتح صفحة الدفع كنافذة منبثقة أو iframe متضمَّن جوّه نطاقك.
  </Card>

  <Card icon="<Component />" title="Elements" href="/integrate/integration-patterns/elements">
    سيرفر زائد واجهة مخصّصة. ابنِ الفورم بنفسك بـ `<PaymentElement />` و`confirmPayment()`.
  </Card>
</Cards>

## دورة الحياة [#دورة-الحياة]

الجلسة بتمر بآلتين حالة قصيّرتين: `status` للجلسة نفسها و`paymentStatus` للفلوس.

### `status` [#status]

| الحالة     | إمتى بتتحدد                                                                                            | إنت بتعمل إيه                                      |
| ---------- | ------------------------------------------------------------------------------------------------------ | -------------------------------------------------- |
| `open`     | بتتحدد عند الإنشاء. الجلسة بتقبل التعديلات وقابلة للعرض في واجهة الدفع.                                | استنى العميل يدفع، أو الجلسة تنتهي صلاحيتها.       |
| `complete` | بتتحدد لما الدفع ينجح. الجلسة بتتقفل: مفيش تعديلات تانية، ومفيش محاولات دفع.                           | نفّذ الطلب بناءً على `checkout.session.completed`. |
| `expired`  | بتتحدد لما `expiresAt` يعدّي، أو لما تستدعي `POST /checkout/sessions/:id/expire`. الجلسة مينفعش تتعاد. | أنشئ جلسة جديدة لو العميل عايز يحاول تاني.         |

### `paymentStatus` [#paymentstatus]

| الحالة                | إمتى بتتحدد                                                    |
| --------------------- | -------------------------------------------------------------- |
| `unpaid`              | الافتراضي. ما حصلتش أي عملية دفع ناجحة.                        |
| `paid`                | فيه عملية دفع نجحت بكامل مبلغ الجلسة.                          |
| `no_payment_required` | الجلسة اتنشئت في وضع `setup` (جمع بيانات الدفع بس من غير خصم). |

حقل `paymentIntent` في الرد بيبقى `null` لحد ما العميل يبعت الفورم لأول مرة. بعد كده بيحمل شكل <ApiLink href="/api-reference/objects/payment-intent">Payment Intent</ApiLink> بالكامل، نفس اللي بيرجعه `GET /payment-intents/:id`. الـ <ApiLink href="/api-reference/objects/charge">Charges</ApiLink> والـ <ApiLink href="/api-reference/objects/refund">Refunds</ApiLink> بتبقى متضمَّنة جوّه الـ Payment Intent.

## إنشاء جلسة [#إنشاء-جلسة]

الحقل الوحيد المطلوب في `POST /checkout/sessions` هو `afterCompletion`. وكل حاجة تانية ليها قيمة افتراضية. عمليًا هتبعت كمان `lineItems` علشان العميل يشوف بيدفع مقابل إيه. والبنود ممكن تكون ثابتة، أو قابلة للتعديل، أو إضافات اختيارية العميل بيختار يضيفها، والجلسة اللي كلها اختيارية صحيحة: بتفتح بإجمالي 0 والعميل بيضيف بنود قبل ما يدفع. شوف [بنود الفاتورة والتسعير](/integrate/checkout-session/line-items-and-pricing).

<Tabs items="[&#x22;cURL&#x22;, &#x22;Node.js&#x22;, &#x22;Python&#x22;]">
  <Tab value="cURL">
    ```bash
    curl -X POST https://api.xpay.app/checkout/sessions \
      -H "Authorization: Bearer sk_test_..." \
      -H "Content-Type: application/json" \
      -d '{
        "afterCompletion": {
          "type": "redirect",
          "redirect": { "url": "https://yourshop.example/order/{CHECKOUT_SESSION_ID}" }
        },
        "lineItems": [
          {
            "priceData": {
              "currency": "EGP",
              "unitAmount": 149900,
              "productData": { "name": "Test product" }
            },
            "quantity": 1
          }
        ]
      }'
    ```
  </Tab>

  <Tab value="Node.js">
    ```typescript
    const res = await fetch("https://api.xpay.app/checkout/sessions", {
      method: "POST",
      headers: {
        Authorization: `Bearer ${process.env.XPAY_SECRET_KEY}`,
        "Content-Type": "application/json",
      },
      body: JSON.stringify({
        afterCompletion: {
          type: "redirect",
          redirect: { url: "https://yourshop.example/order/{CHECKOUT_SESSION_ID}" },
        },
        lineItems: [
          {
            priceData: {
              currency: "EGP",
              unitAmount: 149900, // 1,499.00 EGP, in minor units
              productData: { name: "Test product" },
            },
            quantity: 1,
          },
        ],
      }),
    });

    const session = await res.json();
    ```
  </Tab>

  <Tab value="Python">
    ```python
    import os, requests

    res = requests.post(
        "https://api.xpay.app/checkout/sessions",
        headers={
            "Authorization": f"Bearer {os.environ['XPAY_SECRET_KEY']}",
            "Content-Type": "application/json",
        },
        json={
            "afterCompletion": {
                "type": "redirect",
                "redirect": {"url": "https://yourshop.example/order/{CHECKOUT_SESSION_ID}"},
            },
            "lineItems": [
                {
                    "priceData": {
                        "currency": "EGP",
                        "unitAmount": 149900,
                        "productData": {"name": "Test product"},
                    },
                    "quantity": 1,
                },
            ],
        },
        timeout=10,
    )

    session = res.json()
    ```
  </Tab>
</Tabs>

الرد بيحمل الجلسة كاملة، وفيها الاعتمادَين اللي الدمج بتاعك هيستخدمهم بعد كده:

```json
{
  "id": "cs_test_AbC123...",
  "object": "checkout.session",
  "status": "open",
  "paymentStatus": "unpaid",
  "url": "https://checkout.xpay.app/c/cs_test_AbC123...",
  "clientSecret": "cs_test_AbC123..._secret_xyz",
  "amountTotal": 149900,
  "currency": "EGP",
  "afterCompletion": { "type": "redirect", "redirect": { "url": "..." } },
  "paymentIntent": null,
  "livemode": false
}
```

استخدم `url` علشان تحوّل العميل (الدفع المستضاف، روابط الدفع). واستخدم `clientSecret` علشان تركّب الـ SDK (Drop-in، Elements). الرمز `{CHECKOUT_SESSION_ID}` الحرفي في `redirect.url` بيتستبدل في السيرفر بـ `id` بتاع الجلسة قبل ما الرابط يتخزّن، فصفحة العودة بتاعتك تقدر تقراه من المسار من غير ما تضطر تمرّره في query parameters.

المجموعة الكاملة من الحقول اللي تقدر تظبطها متجمّعة حسب الغرض. وكل مجموعة ليها صفحتها بالتفصيل حقل بحقل.

<Cards>
  <Card icon="<ShoppingCart />" title="بنود الفاتورة والتسعير" href="/integrate/checkout-session/line-items-and-pricing">
    الأسعار الموجودة مقابل `priceData` المضمَّن، والكميات، والكميات القابلة للتعديل، وبنود المبلغ
    المخصّص، والعملة.
  </Card>

  <Card icon="<User />" title="دورة حياة العميل" href="/integrate/checkout-session/customer-lifecycle">
    `customerId` مقابل `customerDetails`، و`customerCreation`، و`customerUpdate`، والاسم، والتليفون،
    والعنوان، والشحن، والحقول المخصّصة.
  </Card>

  <Card icon="<TimerReset />" title="بعد إتمام الدفع" href="/integrate/checkout-session/after-completion">
    `redirect` مقابل `hosted_confirmation`، و`cancelUrl`، ورابط زر العودة، والرسالة المخصّصة.
  </Card>

  <Card icon="<SlidersHorizontal />" title="الإعدادات المتقدّمة" href="/integrate/checkout-session/advanced-configuration">
    العلامة التجارية، واللغة، وأنواع طرق الدفع، والرسوم وتمرير ضريبة القيمة المضافة، وانتهاء
    الصلاحية، والبيانات الوصفية.
  </Card>
</Cards>

## الأوضاع [#الأوضاع]

اثنان من الـ enums على الجلسة بيحدّدوا نوع الدفع اللي بتمثّله وإزاي العميل بيتعامل معاها. الاتنين بيتحددوا عند الإنشاء وثابتين طول حياة الجلسة.

### `mode` [#mode]

`mode` هو نوع حركة الفلوس. القيمة الافتراضية `payment`.

| القيمة         | المعنى                                                                           |
| -------------- | -------------------------------------------------------------------------------- |
| `payment`      | عملية دفع لمرة واحدة. الافتراضي لأغلب عمليات الدمج تقريبًا.                      |
| `subscription` | فوترة متكرّرة. لسه مش مدعومة على الـ checkout API.                               |
| `setup`        | جمع طريقة دفع من غير خصم منها. `paymentStatus` بيستقر على `no_payment_required`. |

### `uiMode` [#uimode]

`uiMode` هو الطريقة اللي العميل بيوصل بيها للفورم. القيمة الافتراضية `hosted`. الاختيار مرتبط بنمط الدمج اللي اخترته فوق؛ سلوك الجلسة، واستدعاء الـ SDK اللي بتعمله، والحقول اللي تقدر تظبطها، كلها بتتغير بيه.

| القيمة     | مكان تشغيل الفورم                                                                        | تستخدمها مع                                                                                                                      |
| ---------- | ---------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------- |
| `hosted`   | صفحة XPay المستضافة على `https://checkout.xpay.app/c/cs_test_...`. السيرفر بيرجّع `url`. | [الدفع المستضاف](/integrate/integration-patterns/hosted-checkout)، [روابط الدفع](/integrate/integration-patterns/payment-links). |
| `embedded` | iframe على موقعك، بيفتحه الـ SDK كنافذة منبثقة أو متضمَّن.                               | [Drop-in](/integrate/integration-patterns/drop-in).                                                                              |
| `custom`   | الفورم بتاعك إنت، باستخدام Elements SDK و`confirmPayment()`.                             | [Elements](/integrate/integration-patterns/elements).                                                                            |

كام قاعدة بتطلع من ده:

* `cancelUrl` صالح بس لما `uiMode: "hosted"`. ده الـ redirect اللي XPay بيبعت العميل ليه لو فشلت محاولة دفع على الصفحة المستضافة (رفض، أو رفض 3DS، أو انتهاء مهلة وسيلة محلية). مش زر إلغاء للعميل: الصفحة المستضافة ما فيهاش واحد.
* **لازم `afterCompletion.type` يكون `"redirect"` مع `embedded` و`custom`.** `hosted_confirmation` بيعرض صفحة مستضافة عند XPay، والدمجين دول بيشتغلوا على موقعك انت. شوف [بعد الإتمام](/integrate/checkout-session/after-completion#دمج-الـ-sdk-محتاج-رابط-رجوع).
* `uiMode: "custom"` معناه إن الكود بتاعك هو اللي بيتولّى جمع بيانات العميل. الجلسة بترفض `nameCollection`، و`phoneNumberCollection`، و`billingAddressCollection`، و`shippingAddressCollection`، و`submitType` في الوضع ده.
* بالنسبة لـ `embedded` و`custom`، بتوثّق الـ SDK بـ `clientSecret` بتاع الجلسة. المفتاح المنشور لوحده مش كفاية؛ الـ secret بيقصر الـ SDK على جلسة واحدة بعينها.

## الحقول المتعارضة [#الحقول-المتعارضة]

شوية أزواج في جسم الإنشاء متعارضة. الـ API بيرفض الطلبات اللي بتبعت الاتنين مع بعض، ونفس القواعد بتنطبق على `PATCH`.

| إما                  | أو                                 | ليه                                                                                                                                         |
| -------------------- | ---------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------- |
| `customerId`         | `customerDetails`                  | سجل <ApiLink href="/api-reference/objects/customer">العميل</ApiLink> الموجود هو مصدر التعبئة المسبقة، أو إنت بتقدّم بيانات خام. مش الاتنين. |
| `customerId`         | `customerCreation: "always"`       | "أنشئ عميل جديد دايمًا" بيتعارض مع "استخدم العميل الموجود ده".                                                                              |
| `paymentMethodTypes` | `paymentMethodConfigurationId`     | اختار قائمة طرق صريحة، أو اشِر لتهيئة محفوظة.                                                                                               |
| `lineItem.price`     | `lineItem.priceData`               | كل بند فاتورة بيشير لـ <ApiLink href="/api-reference/objects/price">Price</ApiLink> موجود بالـ id، أو بيضمّن بيانات المنتج والسعر داخليًا.  |
| بند من نوع `CUSTOM`  | `allowPromotionCodes`، `discounts` | بنود المبلغ المخصّص (العميل بيدخل المبلغ بنفسه) مش متوافقة مع الخصومات.                                                                     |

`customerUpdate` (اللي بيتحكم في إن الدفع يكتب البيانات المجمَّعة على العميل ولا لأ) ليه معنى بس لما `customerId` يكون متقدّم. إرساله من غير `customerId` بيبقى خطأ تحقّق.

## إيه اللي يتعدّل [#إيه-اللي-يتعدّل]

`PATCH /checkout/sessions/:id` بيحدّث جلسة مفتوحة. بيقبل كل حقل في جسم الإنشاء ما عدا اللي تحت، واللي بيتقفل عند الإنشاء:

* `mode`
* `uiMode`
* `submitType`
* `currency`
* `expiresAfterMinutes`

`lineItems` في `PATCH` هو استبدال كامل: المصفوفة الجديدة بتكتب فوق القديمة. علشان تغيّر كمية بند واحد، ابعت المصفوفة المطلوبة كاملة. الجلسات في حالة `complete` أو `expired` بتبقى للقراءة بس وبترفض `PATCH`.

## انتهاء الصلاحية [#انتهاء-الصلاحية]

كل جلسة ليها `expiresAt`. الافتراضي 24 ساعة بعد الإنشاء؛ والحد الأدنى 30 دقيقة. ظبّط `expiresAfterMinutes` في جسم الإنشاء علشان تغيّره.

حاجتين بيعلّموا الجلسة كـ `expired`:

* `expiresAt` بيعدّي. الجلسة بقت مش صالحة للدفع؛ والصفحة المستضافة بتعرض حالة نهائية "خلصت كل حاجة هنا".
* إنت بتستدعي `POST /checkout/sessions/:id/expire` صراحةً. مفيد لما تكون عايز توقّف قبول الدفع على جلسة مش ناوي تكرّمها، أو تفضّي خانات الاستخدام على كود ترويج لمرة واحدة.

المسارَين الاتنين بيطلّقوا `checkout.session.expired` webhook.

## الـ Webhooks [#الـ-webhooks]

حدثين بيتطلقوا لدورة حياة الجلسة نفسها. الـ `data.object` في كل واحد هو <ApiLink href="/api-reference/objects/checkout-session">جلسة دفع</ApiLink> كاملة، نفس شكل اللي بيرجعه `GET /checkout/sessions/:id`.

| الحدث                        | إمتى                                                                                                     |
| ---------------------------- | -------------------------------------------------------------------------------------------------------- |
| `checkout.session.completed` | الدفع نجح. `status` بقى `complete`، و`paymentStatus` بقى `paid` (أو `no_payment_required` لوضع `setup`). |
| `checkout.session.expired`   | الجلسة انتهت صلاحيتها بالوقت أو باستدعاء `/expire` صريح.                                                 |

عملية دفع ناجحة على الدفع المستضاف بتطلّق أربع أحداث. بتتبعت بالترتيب ده:

1. `payment_intent.created`
2. `charge.succeeded`
3. `payment_intent.succeeded`
4. `checkout.session.completed`

تسليم الـ webhook غير متزامن وبيتم مع إعادة محاولة، فـ **ما تعتمدش على ترتيب الوصول** في المعالِج بتاعك. اربط تنفيذ الطلب بـ `checkout.session.completed` واعتبر الأحداث الأبكر سياق إضافي. الأحداث الأبكر مفيدة لما تحتاج تتفاعل مع الـ <ApiLink href="/api-reference/objects/payment-intent">Payment Intent</ApiLink> أو الـ <ApiLink href="/api-reference/objects/charge">Charge</ApiLink> الأساسي مباشرةً. شوف [مرجع الأحداث](/integrate/webhooks/event-reference) للقائمة الكاملة.

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

<Cards>
  <Card icon="<Network />" title="نموذج الكائنات" href="/integrate/object-model">
    إزاي جلسة الدفع بتتعلّق بـ Payment Intent، وCharge، والعميل، وRefund. والمعرّفات اللي تحتفظ بيها
    في سجل الطلب بتاعك.
  </Card>

  <Card icon="<ShoppingCart />" title="بنود الفاتورة والتسعير" href="/integrate/checkout-session/line-items-and-pricing">
    ابنِ السلة: الأسعار الموجودة، و`priceData` المضمَّن، والكميات، والمبالغ المخصّصة.
  </Card>

  <Card icon="<User />" title="دورة حياة العميل" href="/integrate/checkout-session/customer-lifecycle">
    قرّر إزاي XPay بيتعامل مع العميل وأنهي حقول الفورم بيجمعها.
  </Card>

  <Card icon="<TimerReset />" title="بعد إتمام الدفع" href="/integrate/checkout-session/after-completion">
    اظبط مكان وصول العميل بعد عملية دفع ناجحة.
  </Card>

  <Card icon="<Webhook />" title="الـ Webhooks: إعداد نقطة نهاية" href="/integrate/webhooks/setting-up-an-endpoint">
    استقبل `checkout.session.completed` وتحقّق من التوقيع.
  </Card>
</Cards>