# بعد إتمام الدفع (/ar/integrate/checkout-session/after-completion)

رجّع العميل لموقعك، أو سيب XPay يعرض صفحة شكر مستضافة. بالإضافة لإزاي تتعامل مع عمليات الدفع الفاشلة وليه الـ webhook هو مصدر الحقيقة.

`afterCompletion` بيقرّر اللي العميل بيشوفه في اللحظة اللي الدفع بينجح فيها. شكلين: **redirect** لرابط على موقعك، أو صفحة **تأكيد مستضافة** بيعرضها XPay. اختار واحد لكل جلسة؛ وتقدر تغيّره في `PATCH` طول ما الجلسة لسه مفتوحة.

فيه حقل منفصل، `cancelUrl`، بيقرّر العميل يروح فين لو **فشلت** عملية دفع على الصفحة المستضافة (بطاقة مرفوضة، أو 3D Secure مرفوض، أو انتهاء مهلة وسيلة محلية). اختياري وصالح بس لـ `uiMode: "hosted"`.

<Callout type="info">
  أي خيار تختاره، &#x2A;*صفحة الـ redirect أو التأكيد مش هي المكان اللي بتأكّد فيه الدفع.** السيرفر بتاعك
  لازم يستنى webhook الـ `checkout.session.completed` علشان يعلّم الطلب مدفوع فعلًا. العملاء ممكن يقفلوا
  التبويب، أو يضربوا الرابط بالغلط، أو ما يوصلوش للـ redirect خالص.
</Callout>

## الخيارين [#الخيارين]

| `afterCompletion.type` | اللي العميل بيشوفه عند النجاح                                     | إمتى تختاره                                                                 | متاح على                        |
| ---------------------- | ----------------------------------------------------------------- | --------------------------------------------------------------------------- | ------------------------------- |
| `redirect`             | XPay بيبعته لـ `redirect.url` بتاعك. وإنت بتعرض صفحة الشكر بنفسك. | عندك صفحة عودة بتناسب علامتك التجارية وسياق الطلب. الموصى به لأغلب المواقع. | كل قيم `uiMode`                 |
| `hosted_confirmation`  | XPay بيعرض صفحة نجاح جاهزة برسالة مخصّصة اختيارية وزر عودة.       | معندكش صفحة عودة (لسه)، أو لعمليات دفع لمرة واحدة بناء صفحة ليها مبالغة.    | `uiMode: "hosted"` وروابط الدفع |

`afterCompletion` نفسه هو الحقل الوحيد المطلوب في `POST /checkout/sessions`. عدم إرسال أي من الحقلين أصلًا بيبقى خطأ تحقّق.

<Callout type="warn">
  مع `uiMode: "embedded"` (Drop-in) و`uiMode: "custom"` (Elements)، لازم `type` يكون `"redirect"`.
  `hosted_confirmation` بيعرض صفحة مستضافة عند XPay، والدمجين دول بيشتغلوا بالكامل على موقعك انت. شوف
  [دمج الـ SDK محتاج رابط رجوع](#دمج-الـ-sdk-محتاج-رابط-رجوع).
</Callout>

## redirect لرابطك [#redirect-لرابطك]

ظبّط `type: "redirect"` وقدّم رابط HTTPS على نطاق إنت متحكّم فيه. XPay بيبعت العميل للرابط ده على طول بعد ما الدفع يتم.

<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": [ { "price": "price_test_xyz", "quantity": 1 } ]
      }'
    ```
  </Tab>

  <Tab value="Node.js">
    ```typescript
    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: [{ price: "price_test_xyz", quantity: 1 }],
      }),
    });
    ```
  </Tab>

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

    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": [{"price": "price_test_xyz", "quantity": 1}],
        },
        timeout=10,
    )
    ```
  </Tab>
</Tabs>

كام قاعدة:

* **`redirect.url` مطلوب** لما `type: "redirect"`. إرسال `redirect` مع `type: "hosted_confirmation"` بيتم رفضه.
* **HTTPS، على نطاق إنت متحكّم فيه.** `localhost` ماشي في وضع الاختبار بس مرفوض في الحساب الفعلي.
* **مفيش query parameters بيضيفها XPay.** العميل بيوصل لرابطك باللي إنت حطّيته فيه. لو محتاج تعرف الجلسة دي أنهي واحدة، استخدم رمز القالب اللي تحت.

### قالب `{CHECKOUT_SESSION_ID}` [#قالب-checkout_session_id]

حُط الرمز الحرفي `{CHECKOUT_SESSION_ID}` في أي مكان في `redirect.url` وXPay بيستبدله بمعرّف الجلسة قبل ما يحفظ الرابط. مفيد لما ما تكونش عايز تمرّر المعرّف عبر حالتك إنت.

```jsonc
{
  "redirect": {
    "url": "https://yourshop.example/order/{CHECKOUT_SESSION_ID}",
  },
}
```

لجلسة بـ `id: "cs_test_AbC123..."`، العميل بيتحوّل لـ `https://yourshop.example/order/cs_test_AbC123...`. اقرا الـ `cs_*` من المسار على صفحة العودة بتاعتك لو عايز تعرض تفاصيل الطلب (استدعِ `GET /checkout/sessions/:id` علشان تجيب آخر حالة).

## اعرض صفحة تأكيد مستضافة [#اعرض-صفحة-تأكيد-مستضافة]

ظبّط `type: "hosted_confirmation"` لو معندكش صفحة عودة. XPay بيعرض للعميل صفحة شكر باسم نشاطك التجاري وعلامة صح، وده آخر المسار.

متاح مع `uiMode: "hosted"` ومع روابط الدفع (اللي دايمًا بتشتغل كـ hosted). إرساله مع `uiMode: "embedded"` أو `"custom"` بيرجّع `400` عند الإنشاء والتحديث.

<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": "hosted_confirmation",
          "hostedConfirmation": {
            "customMessage": "Thanks! Your order will ship within 2 business days.",
            "returnUrl": "https://yourshop.example"
          }
        },
        "lineItems": [ { "price": "price_test_xyz", "quantity": 1 } ]
      }'
    ```
  </Tab>

  <Tab value="Node.js">
    ```typescript
    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: "hosted_confirmation",
          hostedConfirmation: {
            customMessage: "Thanks! Your order will ship within 2 business days.",
            returnUrl: "https://yourshop.example",
          },
        },
        lineItems: [{ price: "price_test_xyz", quantity: 1 }],
      }),
    });
    ```
  </Tab>

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

    requests.post(
        "https://api.xpay.app/checkout/sessions",
        headers={
            "Authorization": f"Bearer {os.environ['XPAY_SECRET_KEY']}",
            "Content-Type": "application/json",
        },
        json={
            "afterCompletion": {
                "type": "hosted_confirmation",
                "hostedConfirmation": {
                    "customMessage": "Thanks! Your order will ship within 2 business days.",
                    "returnUrl": "https://yourshop.example",
                },
            },
            "lineItems": [{"price": "price_test_xyz", "quantity": 1}],
        },
        timeout=10,
    )
    ```
  </Tab>
</Tabs>

الحقلين المتداخلين الاتنين اختياريين.

| الحقل           | الافتراضي                                                                                               | ملاحظات                                                                       |
| --------------- | ------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------- |
| `customMessage` | سطر على شكل عبارة بينتهي باسم نشاطك التجاري (الاسم المعروض من إعدادات العلامة التجارية في لوحة التحكم). | لحد 500 حرف. بيستبدل النص الافتراضي.                                          |
| `returnUrl`     | مفيش. الصفحة بتتعرض من غير زر عودة.                                                                     | لما يتظبّط، الصفحة بتعرض زر "العودة لـ *اسم نشاطك التجاري*" بيوصّل للرابط ده. |

`redirect` مينفعش يكون متقدّم لما `type: "hosted_confirmation"`. إرسال الاتنين بيتم رفضه.

## `cancelUrl`: تبعت العميل فين عند فشل الدفع [#cancelurl-تبعت-العميل-فين-عند-فشل-الدفع]

`cancelUrl` هو الرابط اللي XPay بيحوّل ليه العميل لما **تفشل محاولة دفع** على صفحة الدفع المستضافة. اختياري، ومنفصل عن `afterCompletion`، وصالح بس لما `uiMode: "hosted"`.

```jsonc
{
  "afterCompletion": { "type": "redirect", "redirect": { "url": "..." } },
  "cancelUrl": "https://yourshop.example/checkout/declined",
  "lineItems": [ ... ]
}
```

اللي بيطلّق التحويل لـ `cancelUrl`:

* بطاقة اترفضت.
* 3D Secure اترفض، أو اتلغى، أو خلصت مهلته.
* وسيلة دفع محلية (فوري، ڤاليو، إلخ) خلصت مهلتها أو اترفضت من المعالج.
* المعالج رجّع خطأ.

اللي **ما بيطلّقش** التحويل لـ `cancelUrl`:

* العميل بيقفل التبويب. مفيش خطّاف إلغاء من ناحية العميل.
* العميل بيضرب زر الرجوع. الصفحة المستضافة ما فيهاش زر "إلغاء" أو "رجوع".
* الجلسة بتنتهي صلاحيتها وهي مفتوحة. ده بيعرض حالة نهائية "خلصت كل حاجة هنا" على الصفحة المستضافة بس.

لو `cancelUrl` متسابش، العميل بيشوف الفشل داخل الصفحة المستضافة ويقدر يحاول تاني هناك. تظبيطه بيديك فرصة تسجّل المحاولة، أو تعرض تجربة إعادة محاولة مخصّصة، أو تقدّم بديل زي طريقة دفع تانية.

## دمج الـ SDK محتاج رابط رجوع [#دمج-الـ-sdk-محتاج-رابط-رجوع]

مع `uiMode: "embedded"` و`uiMode: "custom"`، لازم `afterCompletion.type` يكون `"redirect"`، وده عمليًا بيخلّي `redirect.url` مطلوب. فيه تلات قواعد مختلفة بتتجمّع هنا، وعلشان نبقى دقيقين في الفرق بينهم:

| القاعدة                                           | بتطبّق على                                  |
| ------------------------------------------------- | ------------------------------------------- |
| `afterCompletion` مطلوب                           | كل جلسة دفع وكل رابط دفع                    |
| `redirect.url` مطلوب لما `type` يكون `"redirect"` | كل قيم `uiMode`                             |
| لازم `type` يكون `"redirect"`                     | `uiMode: "embedded"` و`uiMode: "custom"` بس |

روابط الدفع دايمًا بتشتغل كـ `uiMode: "hosted"`، فالقاعدة التالتة عمرها ما بتمسّهم. وتقدر تستخدم معاهم `hosted_confirmation`.

## لما التحقق ياخد الصفحة كلها [#لما-التحقق-ياخد-الصفحة-كلها]

ده بينطبق على `uiMode: "hosted"` وDrop-in. في Elements، التحقق البنكي ما بياخدش الصفحة أبدًا.

أغلب مدفوعات البطاقات بتتحقق مع البنك من غير ما تسيب الصفحة. لكن ساعات صفحة البنك ما ينفعش تتعرض هناك أصلًا.

وساعتها XPay بتسلّم البنك التاب كله بدل ما تفشّل الدفعة. العميل بيتحقق على صفحة البنك نفسه وبيرجع لـ `afterCompletion.redirect.url` بتاعك.

اكتب صفحة الرجوع بتاعتك على أساس ده:

* **دي مش صفحة نجاح.** العميل بيرجع مهما كانت النتيجة، بما فيها الرفض والتحقق اللي اتساب في نصّه.
* **الدفعة في الغالب لسه ما اتأكدتش وقت ما يوصل.** اقرا الجلسة وشوف `paymentStatus`. لو مش `paid`، قول للعميل إن الدفعة بيتم تأكيدها. ما تقولّهوش إنها فشلت، علشان ما يدفعش تاني على دفعة ماشية أصلًا.
* **مش بنضيف حاجة على الرابط بتاعك.** حطّ `{CHECKOUT_SESSION_ID}` جوّه علشان الصفحة تعرف تقرا أنهي جلسة.
* **`onComplete` مش بيشتغل.** في Drop-in، الـ callback ده بيخصّ صفحة راحت.

تنفيذ الطلب مش بيتغيّر. `checkout.session.completed` هو اللي بيعلّم الطلب مدفوع، في المسار ده بالظبط زي أي مسار تاني.

## وإيه بخصوص Drop-in وElements؟ [#وإيه-بخصوص-drop-in-وelements]

`afterCompletion` هو نفس الحقل على كل `uiMode`، لكن السلوك وقت التشغيل بيتغير.

<Cards>
  <Card icon="<ExternalLink />" title="uiMode: hosted">
    صفحة XPay المستضافة بتنقل المتصفح لـ `redirect.url` (أو بتعرض صفحة التأكيد). السيرفر بتاعك ما
    بيشغّلش حاجة من ناحية العميل. `cancelUrl` بيشتغل هنا.
  </Card>

  <Card icon="<AppWindow />" title="uiMode: embedded (Drop-in)">
    الـ SDK بيفتح الدفع المستضاف في iframe. عند النجاح، الـ SDK بيطلّق الـ callback بتاع `onComplete`
    بتاعك بالـ `redirectUrl` من `afterCompletion.redirect.url`. الكود بتاعك بيقرّر يتنقّل، ولا يقفل
    النافذة، ولا يسلّم لصفحة النجاح بتاعتك. XPay بتنقّل المتصفح لهناك بنفسها بس لما التحقق البنكي ياخد
    التاب كله. `cancelUrl` و`hosted_confirmation` الاتنين بيتم رفضهم عند إنشاء الجلسة.
  </Card>

  <Card icon="<Component />" title="uiMode: custom (Elements)">
    الكود بتاعك بيستدعي `confirmPayment()` وبيتعامل مع نتيجة النجاح/الفشل بنفسه. XPay ما بتعرضش حاجة
    على صفحتك غير الـ payment element وطبقة التحقق. المرة الوحيدة اللي الـ SDK بينقّل فيها هي
    `redirect: "always"`، اللي بتبعت العميل لـ `redirect.url` بعد الدفع الناجح. `cancelUrl`
    و`hosted_confirmation` الاتنين بيتم رفضهم عند إنشاء الجلسة.
  </Card>
</Cards>

حمولة الجلسة اللي بتوصل في `checkout.session.completed` بتحمل نفس حقول `afterCompletion` بغض النظر عن `uiMode`، فكود تنفيذ الطلب على ناحية السيرفر بيبقى متطابق عبر الأنماط.

## الـ webhook هو مصدر الحقيقة [#الـ-webhook-هو-مصدر-الحقيقة]

عرض redirect أو صفحة تأكيد مستضافة معناه إن متصفح العميل شاف حالة النجاح. ده مش معناه إن السيرفر بتاعك عنده الحقيقة.

* **العملاء بيقفلوا تبويبات.** الـ redirect عمره ما بيتطلق.
* **الشبكات بتقع.** الـ redirect بيوصل، لكن صفحة العودة بتاعتك مش قادرة توصل للـ backend بتاعك.
* **الناس بتضرب روابط بالغلط.** أي حد يقدر يصنع رابط عودة بـ `cs_*` قديم.

اعتبر صفحة العودة (أو صفحة التأكيد المستضافة) مجاملة لتجربة الاستخدام، ومعالِج webhook الـ `checkout.session.completed` بتاعك هو مصدر الحقيقة الوحيد لتنفيذ الطلب. حمولة الـ webhook بتحمل جلسة الدفع كاملة، بما فيها الـ `paymentIntent`، والـ `customer`، والـ `lineItems` المستقرّين. شوف [الـ Webhooks ← إعداد نقطة نهاية](/integrate/webhooks/setting-up-an-endpoint) لوصفة المعالِج.

لو عايز تعرض الطلب الفعلي على صفحة العودة بتاعتك، اقراه برجوع بـ `GET /checkout/sessions/:id` من المسار (على فرض إنك استخدمت قالب `{CHECKOUT_SESSION_ID}`).

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

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

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

  <Card icon="<BadgeCheck />" title="التأكيد بـ webhook" href="/integrate/integration-patterns/hosted-checkout#confirm-with-a-webhook">
    شرح المعالِج الكامل على صفحة نمط الدفع المستضاف.
  </Card>
</Cards>