# Elements (/ar/integrate/integration-patterns/elements)

ابنِ واجهة الدفع بتاعتك. فورمك بيجمّع بيانات العميل، والـ PaymentElement بتاعنا بيتولّى البطاقات والوسائل المحلية، وكودك بيستدعي confirm(). أقصى تحكم.

Elements بيخلّيك تبني واجهة الدفع كاملة على صفحتك: فورم التواصل بتاعك، وملخص الطلب بتاعك، وزراريرك، وتنسيقك. XPay بيوفّر مكوّن drop-in واحد (`<PaymentElement />` أو `paymentElement.mount(...)` في الـ vanilla) بيتولّى مختار وسيلة الدفع، وفورم البطاقة، والـ 3D Secure، والوسائل المحلية زي ڤاليو وفوري. وكل حاجة تانية كودك.

اختار النمط ده لما تحتاج تحكم كامل في التخطيط، أو عايز تخلط حقول الدفع مع تجربة المستخدم بتاعتك (خطوات، وأشرطة تقدّم، وإكمال تلقائي للعنوان)، أو بتدمج في نظام تصميم موجود. لو "شكله حلو كنافذة منبثقة" كفاية، يبقى [Drop-in](/integrate/integration-patterns/drop-in) نص الكود.

أمثلة الكود تحت بتغطي الاتنين **Vanilla JavaScript** (أي إطار، أو HTML عادي) و**React**. الـ SDK الاتنين بيشتركوا في نفس الـ API؛ اختار التاب اللي يناسب الستاك بتاعك. كل صفحة في القسم ده تغطية مزدوجة فأطر العمل غير React (Vue، Svelte، Solid، Angular، Lit، وHTML عادي) بتاخد معاملة متساوية.

<Callout type="info">
  Elements هو النمط الوحيد اللي بيتطلّب `uiMode: "custom"` على جلسة الدفع. الوضع ده بيرفض كل مفاتيح
  التحصيل من ناحية السيرفر (`nameCollection`، `phoneNumberCollection`، `billingAddressCollection`،
  `shippingAddressCollection`)، و`submitType`، و`cancelUrl`، و أسعار النوع CUSTOM، لإن فورمك هو اللي
  بيملك كل ده. للمرجع الكامل للجلسة، شوف [جلسة الدفع](/integrate/checkout-session/overview).
</Callout>

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

1. **السيرفر بتاعك** بيستدعي `POST /checkout/sessions` بـ `uiMode: "custom"` والبنود. XPay بيرجّع جلسة فيها `clientSecret`.
2. **السيرفر بتاعك** بيشحن الـ `clientSecret` للواجهة بتاعتك.
3. **الواجهة بتاعتك** بتحمّل الـ SDK، وبتهيّئ الدفع بالـ `clientSecret`، وبتركّب الـ payment element جوّه فورمك.
4. **العميل** بيختار وسيلة دفع و(للبطاقات) بيملا فورم البطاقة. وفورمك بيجمّع كل حاجة تانية.
5. **كودك** بيستدعي `checkout.confirm({ customerDetails })`. الـ Promise بيتحلّ بالنتيجة، أو الصفحة بتتنقّل بعيد لو ظبطت `redirect: "always"`.
6. **XPay** بيبعت webhook `checkout.session.completed` لنقطة النهاية بتاعتك بشكل مستقل.

الـ payment element بيشتغل جوّه iframe بيشاور على `https://checkout.xpay.app`. بيانات البطاقة عمرها ما بتدخل الـ DOM بتاعك.

## نفّذه [#نفّذه]

### 1. خُد مفاتيح API للاختبار [#1-خُد-مفاتيح-api-للاختبار]

زي Drop-in بالظبط: `sk_test_*` للسيرفر بتاعك، و`pk_test_*` للواجهة بتاعتك. لاقي الاتنين في لوحة التحكم تحت **المطورين ← مفاتيح API**.

### 2. أنشئ جلسة دفع بـ `uiMode: "custom"` [#2-أنشئ-جلسة-دفع-بـ-uimode-custom]

الجلسة شكلها زي العادية، بفرقين أساسيين:

* **`uiMode: "custom"`** مطلوب.
* **ما تظبطش `nameCollection`، ولا `phoneNumberCollection`، ولا `billingAddressCollection`، ولا `shippingAddressCollection`، ولا `submitType`، ولا `cancelUrl`.** كلهم بيترفضوا وقت الإنشاء. فورمك هو المسؤول عنهم.

<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 '{
        "uiMode": "custom",
        "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({
        uiMode: "custom",
        afterCompletion: {
          type: "redirect",
          redirect: { url: "https://yourshop.example/order/{CHECKOUT_SESSION_ID}" },
        },
        lineItems: [
          {
            priceData: {
              currency: "EGP",
              unitAmount: 149900,
              productData: { name: "Test product" },
            },
            quantity: 1,
          },
        ],
      }),
    });
    const session = await res.json();
    // Send `session.clientSecret` to your frontend
    ```
  </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={
            "uiMode": "custom",
            "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()
    # Send `session["clientSecret"]` to your frontend
    ```
  </Tab>
</Tabs>

كام قاعدة تستاهل تعرفها من الأول:

* **أسعار النوع CUSTOM بتترفض مع `uiMode: "custom"`.** ابنِ الجلسة بـ `unitAmount` ثابت بدالها. أسعار النوع CUSTOM محتاجة خانة إدخال المبلغ المستضافة بتاعتنا، وده مش بتاخده مع Elements.
* **`paymentMethodTypes` و`paymentMethodConfigurationId` لسه بيشتغلوا** لتقييد الوسائل اللي الـ payment element بيعرضها.
* **`brandingSettings` لسه بيشتغل** كخط أساس. الـ `appearance` بتاعك في وقت التشغيل بيتجاوزه.

### 3. اظبط الـ SDK [#3-اظبط-الـ-sdk]

<Tabs items="[&#x22;Vanilla JavaScript&#x22;, &#x22;React&#x22;]">
  <Tab value="Vanilla JavaScript">
    ثبّت الـ JS SDK:

    ```bash
    npm install @xpayeg/sdk
    ```

    حمّل XPay على مستوى الموديول (أو بالـ CDN script tag لو مش بتعمل bundling)، وبعدين استدعي `xpay.initCheckout({ clientSecret })` بمجرد ما يبقى عندك جلسة. كائن الـ `checkout` اللي بيرجع بيحمل بيانات الجلسة ودوال الإجراءات مع بعض.

    ```html
    <form id="checkout-form">
      <!-- Your contact-info fields, your order summary, your styling -->
      <input id="email" type="email" required />
      <input id="name" type="text" required />

      <!-- The one piece you don't build -->
      <div id="payment-element"></div>

      <button id="pay-button" type="submit" disabled>Pay</button>
      <p id="error" role="alert"></p>
    </form>
    ```

    ```ts
    import { loadXPay } from "@xpayeg/sdk";

    // Module level: call once, share across the page.
    const xpayPromise = loadXPay("pk_test_...");

    async function start(clientSecret: string) {
      const xpay = await xpayPromise;
      if (!xpay) return; // SSR returns null on the server

      // Returns a single object with session fields and action methods merged together.
      const checkout = await xpay.initCheckout({ clientSecret });

      // Handle terminal session states before mounting anything.
      if (checkout.status.type === "expired") {
        showExpiredView();
        return;
      }
      if (checkout.status.type === "complete") {
        showAlreadyPaidView();
        return;
      }

      // Mount the payment element (next step).
      mountPaymentElement(checkout);
    }
    ```

    لو مش على bundler، حطّ script tag واستخدم الـ factory العام `XPay` اللي بيكشفه وقت التشغيل:

    ```html
    <script src="https://checkout.xpay.app/v1/sdk.js"></script>
    <script type="module">
      const xpay = XPay("pk_test_...");
      const checkout = await xpay.initCheckout({ clientSecret });
    </script>
    ```
  </Tab>

  <Tab value="React">
    ثبّت الـ package الاتنين:

    ```bash
    npm install @xpayeg/sdk @xpayeg/react
    ```

    غلّف الجزء من تطبيقك اللي بيعرض الدفع في `<XPayProvider>`. حمّل الـ SDK على مستوى الموديول، مش جوّه مكوّن. الـ Provider بيقبل الـ Promise مباشرة.

    ```tsx
    import { XPayProvider } from "@xpayeg/react";
    import { loadXPay } from "@xpayeg/sdk";

    // Module level (runs once per page)
    const xpayPromise = loadXPay("pk_test_...");

    export default function CheckoutPage({ clientSecret }: { clientSecret: string }) {
      return (
        <XPayProvider xpay={xpayPromise} options={{ clientSecret }}>
          <CheckoutForm />
        </XPayProvider>
      );
    }
    ```

    `options.clientSecret` بيقبل الاتنين `string` و`Promise<string>`، فلو بتجيب الجلسة من الـ backend بتاعك تقدر تمرّر الـ Promise مباشرة من غير ما تدير حالة تحميل.
  </Tab>
</Tabs>

### 4. ركّب الـ payment element واحكم على زرار الدفع [#4-ركّب-الـ-payment-element-واحكم-على-زرار-الدفع]

<Tabs items="[&#x22;Vanilla JavaScript&#x22;, &#x22;React&#x22;]">
  <Tab value="Vanilla JavaScript">
    خُد نسخة `Elements` من الـ checkout، أنشئ `PaymentElement`، ركّبه جوّه حاوية فورمك، واستنى أحداث الـ `change` علشان تتابع اكتمال الفورم.

    ```ts
    function mountPaymentElement(checkout) {
      const elements = checkout.getElements();
      const paymentElement = elements.create("payment");
      paymentElement.mount("#payment-element");

      const payButton = document.getElementById("pay-button") as HTMLButtonElement;
      let paymentReady = false;

      paymentElement.on("change", (event) => {
        paymentReady = event.complete;
        payButton.disabled = !paymentReady || !checkout.canConfirm;
      });

      // Update the button label when totals change (promo codes, quantities, etc.)
      checkout.on("change", (session) => {
        payButton.textContent = `Pay ${session.currency} ${(session.amountTotal / 100).toFixed(2)}`;
      });
    }
    ```

    كام حاجة بتحصل هنا:

    * **`event.complete` من الـ payment element.** بيتابع إذا كان العميل ملا الفورم بما يكفي علشان يحاول الدفع. استخدمه علشان تعطّل زرار الدفع بتاعك.
    * **`checkout.canConfirm`** هو البوابة الخاصة بـ XPay. لازم العلَمين الاتنين يكونوا true قبل ما تستدعي `confirm()`.
    * **`checkout.on("change", session => ...)`** بيطلق كل مرة الجلسة بتتغيّر (تطبيق كود خصم، تحديث كمية، إعادة حساب رسوم). استخدمه علشان تحدّث الـ DOM بتاعك. مع `initCheckout`، حقول كائن الـ `checkout` مش تفاعلية لوحدها، فاشترك في الـ `change` لأي إجماليات أو ملخصات بتعرضها.
  </Tab>

  <Tab value="React">
    `useCheckout()` بيرجّع tagged union. ضيّق على الـ `type` قبل ما تقرا بيانات الـ `checkout` أو تستدعي دوال الإجراءات.

    ```tsx
    "use client";

    import { useState } from "react";
    import { PaymentElement, useCheckout } from "@xpayeg/react";

    function CheckoutForm() {
      const state = useCheckout();
      const [paymentReady, setPaymentReady] = useState(false);

      if (state.type === "loading") return <Skeleton />;
      if (state.type === "error") return <ErrorView message={state.error.message} />;

      const { checkout } = state;

      // Handle terminal states
      if (checkout.status.type === "expired") return <ExpiredView />;
      if (checkout.status.type === "complete") return <AlreadyPaidView />;

      return (
        <form>
          {/* Your contact-info form, your order summary, your styling */}
          <YourContactFields />

          {/* The one piece you don't build */}
          <PaymentElement
            onChange={(event) => setPaymentReady(event.complete)}
            onLoadError={(err) => console.error(err.message)}
          />

          <PayButton
            disabled={!paymentReady || !checkout.canConfirm}
            amount={checkout.amountTotal}
            currency={checkout.currency}
          />
        </form>
      );
    }
    ```

    كام حاجة بتحصل هنا:

    * **تضييق `state.type`.** `loading` أثناء تحميل الجلسة، و`error` لو فشلت في التحميل (شبكة، سرّ غلط، جلسة منتهية)، و`success` بمجرد ما كل حاجة تجهز.
    * **تضييق `checkout.status.type`.** جوّه `success`، الجلسة نفسها ممكن تكون `open` أو `expired` أو `complete`. اعرض واجهة مختلفة لكل واحدة. `open` بس هي اللي المفروض تخلّي العميل يحاول يدفع.
    * **`event.complete` من `<PaymentElement />`.** بيتابع إذا كان العميل ملا الفورم بما يكفي علشان يحاول الدفع. استخدمه علشان تعطّل زرار الدفع بتاعك. فيه كمان `event.empty`، و`event.collapsed`، و`event.value.type` (الوسيلة المختارة)، و`event.session` (لقطة الجلسة الكاملة).
    * **`checkout.canConfirm`** هو البوابة الخاصة بـ XPay. لازم العلَمين الاتنين يكونوا true قبل ما تستدعي `confirm()`.
    * **مفيش حاجة لمستمع `change` يدوي.** `useCheckout()` بيعيد العرض تلقائيًا لما الإجماليات أو البنود أو الخصومات بتتغيّر، فقراءة `checkout.amountTotal` بترجّعلك آخر قيمة دايمًا.
  </Tab>
</Tabs>

### 5. أكّد الدفعة [#5-أكّد-الدفعة]

اربط زرار الدفع بتاعك بـ `checkout.confirm()`. أنت بتوفّر بيانات العميل اللي فورمك جمّعها؛ وXPay بيتولّى الـ 3D Secure، والوسائل القائمة على التحويل، والنتيجة.

<Tabs items="[&#x22;Vanilla JavaScript&#x22;, &#x22;React&#x22;]">
  <Tab value="Vanilla JavaScript">
    ```ts
    document.getElementById("checkout-form")!.addEventListener("submit", async (e) => {
      e.preventDefault();

      const errorEl = document.getElementById("error")!;
      errorEl.textContent = "";

      const result = await checkout.confirm({
        customerDetails: {
          email: (document.getElementById("email") as HTMLInputElement).value,
          name: (document.getElementById("name") as HTMLInputElement).value,
          // Optional billing/shipping details
        },
        // "if_required" (default): result returns to your code; navigate yourself
        // "always": redirects to afterCompletion.redirect.url on success
        redirect: "if_required",
      });

      if (result.type === "error") {
        errorEl.textContent = result.error.message;
        return;
      }

      // Success: navigate, show a success view, etc.
      window.location.href = `/success?session_id=${checkout.id}`;
    });
    ```
  </Tab>

  <Tab value="React">
    ```tsx
    async function handleSubmit() {
      setSubmitting(true);
      setError("");

      const result = await checkout.confirm({
        customerDetails: {
          email,
          name,
          phone,
          // Optional billing/shipping details
          billingDetails: { address: { line1, city, country } },
        },
        // "if_required" (default): result returns to your code; navigate yourself
        // "always": redirects to afterCompletion.redirect.url on success
        redirect: "if_required",
      });

      if (result.type === "error") {
        setError(result.error.message);
        setSubmitting(false);
        return;
      }

      // Success: navigate, show a success view, etc.
      router.push(`/success?session_id=${checkout.id}`);
    }
    ```
  </Tab>
</Tabs>

شكل الـ `result` (واحد في الاتنين):

* **`{ type: "success", session }`**: الدفعة نجحت. الـ `session` هي <ApiLink href="/api-reference/objects/checkout-session">Checkout Session</ApiLink> المحدّثة بـ `status: { type: "complete", paymentStatus: "paid" }`.
* **`{ type: "error", error }`**: الدفعة فشلت. الـ `error` بيحمل `type`، و`code`، و`message`، وحقول خاصة بالرفض زي `declineCode` لأخطاء البطاقة. شوف [أخطاء الدفع](/integrate/errors/payment-errors).

تحدّيات الـ 3D Secure، ونوافذ تأكيد ڤاليو، ونوافذ مرجع فوري المنبثقة: كلهم بيتعالجوا جوّه الـ payment element وفي وقت ما الـ `confirm()` مستني. الـ Promise بيتحلّ بس بعد ما مسار الدفع الكامل يكتمل (أو يفشل).

## تعامل مع تغيّرات حالة الجلسة [#تعامل-مع-تغيّرات-حالة-الجلسة]

دوال الإجراءات بترجّع `Promise<ActionResult>` بنفس شكل الـ `success` / `error` بتاع `confirm()`. الـ API واحد بين الـ vanilla وReact؛ الفرق هو إزاي الواجهة بتاعتك بترد.

```ts
// Promotion code
const result = await checkout.applyPromotionCode("SAVE20");
if (result.type === "error") setPromoError(result.error.message);

// Remove the applied code
await checkout.removePromotionCode();

// Update a line item's quantity
await checkout.updateLineItemQuantity({ lineItem: "li_test_abc", quantity: 3 });

// Re-fetch the session from the server (after a server-side change)
await checkout.fetchUpdates();
```

<Tabs items="[&#x22;Vanilla JavaScript&#x22;, &#x22;React&#x22;]">
  <Tab value="Vanilla JavaScript">
    الـ `checkout` كائن عادي، فحقوله مش بتتحدّث تلقائيًا. اشترك في الـ `change` علشان تحدّث أي حاجة بتعرضها من الجلسة.

    ```ts
    checkout.on("change", (session) => {
      totalEl.textContent = `${session.currency} ${(session.amountTotal / 100).toFixed(2)}`;
      // Re-render line items, discounts, fee breakdown, etc.
    });
    ```

    الـ handler بيستلم لقطة `CheckoutSession` الكاملة المحدّثة.
  </Tab>

  <Tab value="React">
    `useCheckout()` بيعيد العرض تلقائيًا بعد كل إجراء، فـ `checkout.amountTotal`، و`checkout.totalDetails`، و`checkout.lineItems` كلهم بيعكسوا حالة السيرفر الجديدة في العرض اللي بعده. مفيش حاجة لاشتراك `on("change", ...)` للحالة الأساسية.

    لو عايز ترد على التغيّرات بره شجرة العرض (تسجيل، تحليلات)، لسه تقدر تشترك عن طريق `useEffect`:

    ```tsx
    useEffect(() => {
      if (state.type !== "success") return;
      state.checkout.on("change", (session) => {
        analytics.track("checkout_updated", { amount: session.amountTotal });
      });
    }, [state]);
    ```
  </Tab>
</Tabs>

## خصّص المظهر في وقت التشغيل [#خصّص-المظهر-في-وقت-التشغيل]

`appearance` بيقبل نفس شكل `brandingSettings` على الجلسة. لقائمة الحقول الكاملة، شوف [الإعدادات المتقدمة → العلامة التجارية](/integrate/checkout-session/advanced-configuration#branding).

<Tabs items="[&#x22;Vanilla JavaScript&#x22;, &#x22;React&#x22;]">
  <Tab value="Vanilla JavaScript">
    مرّر `appearance` على `initCheckout`، أو استدعي `checkout.changeAppearance(...)` بعدين.

    ```ts
    const checkout = await xpay.initCheckout({
      clientSecret,
      appearance: {
        colorMode: "dark",
        borderStyle: "rounded",
        colors: { primary: "#635bff" },
      },
      locale: "ar",
    });

    // Later, sync with your site's theme toggle.
    themeToggle.addEventListener("change", () => {
      checkout.changeAppearance({
        colorMode: themeToggle.checked ? "dark" : "light",
      });
    });
    ```
  </Tab>

  <Tab value="React">
    مرّر `options.appearance` على `<XPayProvider>` علشان تظبط المظهر المبدئي، أو استدعي `checkout.changeAppearance(...)` بعدين.

    ```tsx
    <XPayProvider
      xpay={xpayPromise}
      options={{
        clientSecret,
        appearance: {
          colorMode: "dark",
          borderStyle: "rounded",
          colors: { primary: "#635bff" },
        },
        locale: "ar",
      }}
    >
      <CheckoutForm />
    </XPayProvider>
    ```

    نمط شائع: زامن مظهر XPay مع مفتاح الثيم بتاع موقعك.

    ```tsx
    function CheckoutForm({ checkout }: { checkout: Checkout }) {
      const { resolvedTheme } = useTheme(); // your app's theme hook

      useEffect(() => {
        checkout.changeAppearance({
          colorMode: resolvedTheme === "dark" ? "dark" : "light",
        });
      }, [resolvedTheme, checkout]);

      return /* ... */;
    }
    ```
  </Tab>
</Tabs>

## استراتيجيات التأكيد [#استراتيجيات-التأكيد]

`confirm()` ليه وضعين تحويل. اختار اللي يناسب توجيه ما بعد الدفع بتاعك. الـ API واحد في الـ vanilla وReact.

| `redirect`                | بيحصل إيه عند النجاح                                                                          |
| ------------------------- | --------------------------------------------------------------------------------------------- |
| `"if_required"` (افتراضي) | الـ Promise بيتحلّ بـ `{ type: "success", session }`. كودك بيتنقّل / يعرض واجهة النجاح.       |
| `"always"`                | الصفحة بتتنقّل لـ `afterCompletion.redirect.url`. الكود اللي بعد `await` بيشتغل بس عند الخطأ. |

الاتنين بيستخدموا نفس العنوان: `afterCompletion.redirect.url` بتاع الجلسة، اللي بيتحدد لما السيرفر بتاعك ينشئ الجلسة.

```ts
// Handle success in your code
const result = await checkout.confirm({ customerDetails: { email, name } });
if (result.type === "success") {
  // navigate, show a success view, etc.
}

// Or let XPay navigate to the session's afterCompletion.redirect.url
await checkout.confirm({
  customerDetails: { email, name },
  redirect: "always",
});
// ^ On success, the page navigates away. Code below only runs on error.
```

الأولى أشيع في تطبيقات الصفحة الواحدة. التانية أبسط لو عندك صفحة نجاح ثابتة.

## استنى الأخطاء غير المطلوبة [#استنى-الأخطاء-غير-المطلوبة]

أغلب الأخطاء بترجع عن طريق دالة الإجراء اللي استدعيتها (`confirm()`، `applyPromotionCode()`، إلخ). فيه كام موقف بينتج أخطاء **ما جاتش** من إجراء التاجر: جلسة بتنتهي أثناء إعادة حساب رسوم لما العميل يغيّر وسيلة الدفع، أو فشل في اكتشاف الـ BIN، أو تعثّر في الـ backend وسط الجلسة. اشترك في حدث الـ `error` علشان تتعامل معاهم.

الـ API واحد في الـ vanilla وReact: `checkout.on("error", handler)`.

<Tabs items="[&#x22;Vanilla JavaScript&#x22;, &#x22;React&#x22;]">
  <Tab value="Vanilla JavaScript">
    ```ts
    checkout.on("error", (error) => {
      bannerEl.textContent = error.message;
    });
    ```
  </Tab>

  <Tab value="React">
    ```tsx
    useEffect(() => {
      if (state.type !== "success") return;
      state.checkout.on("error", (error) => {
        setBanner(error.message);
      });
    }, [state]);
    ```
  </Tab>
</Tabs>

كائن الخطأ ليه نفس الشكل اللي دوال الإجراءات بترجّعه: `type`، و`code`، و`message`، وحقول خاصة بالرفض زي `declineCode` لأخطاء البطاقة. شوف [أخطاء الدفع](/integrate/errors/payment-errors).

## تحقّق مسبقًا قبل التأكيد [#تحقّق-مسبقًا-قبل-التأكيد]

استدعي `checkout.submit()` علشان تتحقّق من كل حقل في الـ payment element قبل ما تلتزم بـ `confirm()`. مفيد لما عايز تحكم نافذة تأكيد أو مسار متعدد الخطوات. نفس الـ API في العالمين:

```ts
const { error, selectedPaymentMethod } = await checkout.submit();
if (error) {
  setError(error.message);
  return;
}

// Fields are valid; show your confirm dialog or continue
const confirmed = await showDialog(`Pay with ${selectedPaymentMethod}?`);
if (!confirmed) return;

const result = await checkout.confirm({ customerDetails: { email, name } });
```

`submit()` بيتحلّ بنوع وسيلة الدفع المتحقَّق منها، فتقدر تذكرها في نافذتك ("ادفع بالبطاقة؟"، "ادفع بڤاليو؟").

## أكّد بـ webhook [#أكّد-بـ-webhook]

تحلّ الـ `confirm()` Promise بنجاح مجاملة لتجربة المستخدم. &#x2A;*لازم السيرفر بتاعك يفضل يستنى `checkout.session.completed`** علشان يعلّم الطلب مدفوع. العملاء ممكن يفقدوا الاتصال بين الـ `confirm()` والـ `setState` بتاعك، ومعالِجك ممكن يرمي خطأ، والـ SDK ممكن يفقد النتيجة. الـ webhook هو مصدر الحقيقة.

لشرح المعالِج الكامل (التحقق من التوقيع، وجدول حقول الحمولة، وسلوك إعادة المحاولة)، شوف [الدفع المستضاف → أكّد بـ webhook](/integrate/integration-patterns/hosted-checkout#confirm-with-a-webhook). كود المعالِج واحد بغض النظر عن النمط اللي أنتج الجلسة.

## اختبره [#اختبره]

في وضع الاختبار، بطاقة النجاح `5123 4500 0000 0008` بتاريخ انتهاء `01/39` بتشغّل المسار الناجح جوّه الـ payment element. فورم البطاقة، وتحدّي الـ 3D Secure، والنتيجة كلهم بيمرّوا من خلال `confirm()`. لقائمة بطاقات الاختبار الكاملة ومصفوفة تاريخ الانتهاء للنتيجة، شوف [وضع الاختبار وبطاقات الاختبار](/get-started/test-mode).

علشان تجرّب معالِج الـ webhook بتاعك محليًا قبل النشر، شوف [تطوير الـ webhook محليًا](/integrate/webhooks/local-development).

## قائمة التحقق قبل الإنتاج [#قائمة-التحقق-قبل-الإنتاج]

قبل ما تحوّل للحساب الفعلي:

* **بدّل المفاتيح.** `sk_live_*` على السيرفر، و`pk_live_*` على الواجهة. رابط الـ API مش بيتغيّر.
* **اظبط نقطة نهاية webhook فعلية.** وضع الاختبار والحساب الفعلي ليهم نقاط نهاية منفصلة، كل واحدة بالسرّ بتاع التوقيع `whsec_*` الخاص بيها.
* **تحقّق على الـ webhook، مش على نتيجة `confirm()`.** عامل النجاح اللي اتحلّ كتلميح للعرض، مش كتصريح.
* **ما تشحنش المفتاح السرّي.** `sk_*` للسيرفر بس. الـ SDK بياخد مفتاح قابل للنشر (`pk_*`).
* **اسمح بـ `https://checkout.xpay.app` في الـ CSP بتاعك.** الـ payment element iframe وسكربت الـ SDK الاتنين بيتحمّلوا من هناك. لو ظبطت توجيهات `frame-src` أو `script-src`، أدرجه.
* **امنع تكرار تسليمات الـ webhook على `event.id`.** XPay بيعيد المحاولة عند الردود المش 2xx أو انتهاء المهلة، فنفس الحدث ممكن يوصل مرتين.
* **ابعت `Idempotency-Key` على `POST /checkout/sessions`.** لو الطلب وقعت مهلته، إعادة المحاولة بنفس المفتاح بترجّع الجلسة الأصلية بدل ما تفتح تانية. شوف [عدم التكرار](/integrate/idempotency).
* **تعامل مع الحالات النهائية.** `checkout.status.type === "expired"` و`"complete"` المفروض الاتنين يعرضوا واجهات مخصّصة، مش يقعوا للفورم.

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

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

  <Card icon="<RotateCcw />" title="المبالغ المستردة" href="/integrate/refunds">
    اعكس دفعة ناجحة باستخدام الـ `pi_*` من `result.session.paymentIntent.id`.
  </Card>

  <Card icon="<Paintbrush />" title="العلامة التجارية واللغة" href="/integrate/checkout-session/advanced-configuration#branding">
    كل حقل بيقبله `appearance`، زائد قواعد تحديد اللغة.
  </Card>

  <Card icon="<Webhook />" title="إعداد الـ Webhooks" href="/integrate/webhooks/setting-up-an-endpoint">
    أضِف نقطة نهاية وخُد السرّ بتاع التوقيع `whsec_*`.
  </Card>

  <Card icon="<FlaskConical />" title="وضع الاختبار وبطاقات الاختبار" href="/get-started/test-mode">
    قائمة بطاقات الاختبار والنتائج اللي تقدر تحاكيها.
  </Card>

  <Card icon="<GitBranch />" title="عايز نمط مختلف؟" href="/get-started/choose-your-integration">
    قارن Elements بالدفع المستضاف، وDrop-in، وروابط الدفع.
  </Card>

  <Card icon="<SquareCode />" title="مثال HTML عادي" href="https://github.com/xpayeg/xpay-examples/tree/main/vanilla-html">
    `elements.html` بيشغّل نفس النمط ده بالظبط (فورم مخصّص، وأكواد خصم، وpayment element مركّب) في
    ملف واحد جاهز للتشغيل، من غير build step.
  </Card>

  <Card icon="<Atom />" title="متجر مثال Next.js" href="https://github.com/xpayeg/xpay-examples/tree/main/nextjs-react">
    مسار `/checkout?ui=custom` بيبني نمط الصفحة دي بـ `<XPayProvider>` + `<PaymentElement />` جوّه
    متجر كامل.
  </Card>
</Cards>