# Drop-in (/ar/integrate/integration-patterns/drop-in)

سيرفر زائد كام سطر واجهة. افتح دفع XPay كنافذة منبثقة أو iframe داخلي على نطاقك. العميل عمره ما بيغادر موقعك.

Drop-in بيفتح دفع XPay الكامل جوّه iframe على نطاقك. السيرفر بتاعك بينشئ جلسة دفع، والواجهة بتاعتك بتستدعي `xpay.checkout({ ... })`، والعميل بيدفع من غير ما ينتقل بعيد أبدًا. بتكتب حوالي عشر سطور كود واجهة؛ والـ iframe بيتولّى وسائل الدفع، وفورم البطاقة، والـ 3D Secure، والوسائل المحلية زي ڤاليو وفوري.

اختار النمط ده لما تكون عايز العميل يفضل على نطاقك بس مش عايز تبني فورم الدفع بنفسك. لو محتاج واجهة مخصّصة، استخدم [Elements](/integrate/integration-patterns/elements). لو مش عايز أي كود واجهة، استخدم [الدفع المستضاف](/integrate/integration-patterns/hosted-checkout) أو [رابط دفع](/integrate/integration-patterns/payment-links).

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

<Callout type="info">
  Drop-in عبارة عن غلاف SDK رفيع حوالين نفس جلسة الدفع اللي بيستخدمها كل نمط تاني. لمجموعة الحقول
  الكاملة اللي تقدر تظبطها على `POST /checkout/sessions` (البنود، وتحصيل بيانات العميل، والعلامة
  التجارية، ووسائل الدفع، والرسوم، والبيانات الوصفية، إلخ)، شوف مرجع [جلسة
  الدفع](/integrate/checkout-session/overview).
</Callout>

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

1. **السيرفر بتاعك** بيستدعي `POST /checkout/sessions` بـ `uiMode: "embedded"` والبنود. XPay بيرجّع جلسة فيها `clientSecret` و`paymentMethodTypes`.
2. **السيرفر بتاعك** بيشحن الـ `clientSecret` للواجهة (عادةً جوّه عرض صفحة HTML أو رد fetch).
3. **الواجهة بتاعتك** بتحمّل الـ SDK بمفتاحك القابل للنشر وبتستدعي `xpay.checkout({ clientSecret, mode: "modal" })`. استدعاء `checkout.open()` بيعرض الـ iframe.
4. **العميل** بيختار وسيلة، ويملا الفورم، ويكمّل الـ 3D Secure لو لازم، ويدفع. كل ده من غير ما ينتقل بعيد.
5. **الـ SDK** بيطلق الـ `onComplete` callback بتاعك بمعرّف الـ Payment Intent. والسيرفر بتاعك بيستلم `checkout.session.completed` بشكل مستقل علشان ينفّذ الطلب.

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

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

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

محتاج المفتاحين لـ Drop-in:

* **`sk_test_*`** للسيرفر بتاعك. بيُستخدم في إنشاء جلسة الدفع.
* **`pk_test_*`** للواجهة بتاعتك. بيُستخدم في تحميل الـ SDK. آمن إنك تشحنه للمتصفّح.

في لوحة التحكم، روح **المطورين ← مفاتيح API** وانسخ الاتنين. المفاتيح الفعلية (`sk_live_*` / `pk_live_*`) بتشتغل بنفس الطريقة بمجرد ما حسابك يتعتمد.

### 2. أنشئ جلسة دفع على السيرفر بتاعك [#2-أنشئ-جلسة-دفع-على-السيرفر-بتاعك]

شرطين خاصين بـ Drop-in:

* **`uiMode: "embedded"`** بيقول لـ XPay إن الجلسة دي لـ iframe.
* رد الجلسة فيه &#x2A;*`clientSecret`** بيحصر الـ SDK على الجلسة دي بالظبط. هتشحنه للواجهة.

<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": "embedded",
        "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: "embedded",
        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": "embedded",
            "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>

الرد فيه نفس شكل جلسة الدفع اللي بيرجّعه الدفع المستضاف. الحقول اللي بتشحنها للواجهة:

```json
{
  "id": "cs_test_AbC123...",
  "clientSecret": "cs_test_AbC123..._secret_xyz789",
  "amountTotal": 149900,
  "currency": "EGP"
}
```

كام قاعدة:

* **`cancelUrl` بيترفض مع `uiMode: "embedded"`.** Drop-in بيطلّع حالات الفشل جوّه الـ iframe مع إعادة محاولة. مفيش مسار "وَدّي العميل لـ URL فشل".
* **`clientSecret` لجلسة واحدة.** بيحصر الـ SDK على الجلسة دي بالظبط ومينفعش يتعاد استخدامه. ما تخزّنوش بعد عمر الجلسة.
* **`afterCompletion` مطلوب، ولازم يكون `type: "redirect"`.** `hosted_confirmation` مرفوض مع `uiMode: "embedded"`: هو بيعرض صفحة مستضافة عند XPay، والدمج ده بيشتغل على موقعك انت. لما الدفعة تنجح، الـ SDK بيمرّر `redirect.url` بتاعك لـ `onComplete` كـ `redirectUrl` وكودك هو اللي بيقرّر يتنقّل ولا لأ. ونفس الرابط ده هو اللي العميل بيرجع ليه لو التحقق البنكي اضطر ياخد التاب كله، فلازم يكون موجود قبل ما الدفعة تبدأ.

### 3. حمّل الـ SDK على الواجهة بتاعتك [#3-حمّل-الـ-sdk-على-الواجهة-بتاعتك]

<Tabs items="[&#x22;Vanilla JavaScript&#x22;, &#x22;React&#x22;]">
  <Tab value="Vanilla JavaScript">
    لو بتستخدم bundler (Vite، أو webpack، إلخ)، ثبّت المحمّل من npm، وبعدين استورد `loadXPay` واعمله `await`:

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

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

    const xpay = await loadXPay("pk_test_..."); // your publishable key

    const checkout = xpay.checkout({
      clientSecret: "cs_test_..._secret_xyz789", // from your server
      mode: "modal",
      onComplete: (result) => {
        // result.paymentIntentId is the pi_*
        // result.redirectUrl is your afterCompletion.redirect.url (if set)
        if (result.redirectUrl) {
          window.location.href = result.redirectUrl;
        }
      },
      onClose: () => {
        console.log("Customer closed the modal");
      },
    });

    document.getElementById("pay-button")?.addEventListener("click", () => {
      checkout.open();
    });
    ```

    على HTML عادي من غير build step، ما تستوردش. حطّ الـ CDN script tag واستدعي الـ factory العام `XPay` اللي الـ runtime بيعرضه. نفس الـ API، من غير `await`:

    ```html
    <script src="https://checkout.xpay.app/v1/sdk.js"></script>
    <script>
      // الـ factory العام، مش loadXPay. `loadXPay` موجود بس في الـ package بتاع npm.
      const xpay = XPay("pk_test_...");

      const checkout = xpay.checkout({
        clientSecret: "cs_test_..._secret_xyz789", // from your server
        mode: "modal",
        onComplete: (result) => {
          if (result.redirectUrl) window.location.href = result.redirectUrl;
        },
      });

      document.getElementById("pay-button")?.addEventListener("click", () => {
        checkout.open();
      });
    </script>
    ```
  </Tab>

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

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

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

    // Load once at module level (not inside a component)
    const xpayPromise = loadXPay("pk_test_...");

    export function PayPage({ clientSecret }: { clientSecret: string }) {
      return (
        <XPayProvider xpay={xpayPromise}>
          <CheckoutButton
            clientSecret={clientSecret}
            checkoutOptions={{
              onComplete: (result) => {
                if (result.redirectUrl) window.location.href = result.redirectUrl;
              },
            }}
          >
            Pay now
          </CheckoutButton>
        </XPayProvider>
      );
    }
    ```

    `<XPayProvider>` بيقبل يا إما `XPayInstance` أو `Promise<XPayInstance>`، فتمرير `xpayPromise` مباشرة هو النمط الأصح. ما تستدعيش `loadXPay()` جوّه مكوّن. حمّله مرة واحدة لكل صفحة على مستوى الموديول.
  </Tab>
</Tabs>

الـ SDK بيتحمّل من `https://checkout.xpay.app/v1/sdk.js` مرة واحدة لكل صفحة؛ واستدعاءات `loadXPay()` اللي بعدها بترجّع نفس النسخة المخزّنة.

### 4. تعامل مع النتيجة [#4-تعامل-مع-النتيجة]

`xpay.checkout(...)` بيرجّع `CheckoutInstance` تقدر تشترك فيه عن طريق الـ callbacks في الـ constructor أو `.on(event, handler)`. خمس أحداث بتطلق على مدى عمر عملية دفع واحدة.

| الحدث       | إمتى                                                                                                            | الحمولة                                                                                         |
| ----------- | --------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------- |
| `ready`     | الجلسة اتحمّلت والـ iframe جاهز للعرض.                                                                          | الـ <ApiLink href="/api-reference/objects/checkout-session">Checkout Session</ApiLink> الكاملة. |
| `confirmed` | العميل ضغط ادفع. مفيد لو عايز تعطّل زرار الإغلاق أو تعرض حالة "بنعالج".                                         | لا شيء.                                                                                         |
| `complete`  | الدفعة نجحت. الـ iframe بيقفل النافذة المنبثقة تلقائيًا بعد 300 مللي ثانية.                                     | `{ status: "succeeded", paymentIntentId, chargeId?, redirectUrl? }`.                            |
| `close`     | النافذة المنبثقة اتقفلت. بتطلق للإغلاق اليدوي، أو بعد `complete`، أو لو `checkout.close()` اتنادى.              | لا شيء.                                                                                         |
| `error`     | الجلسة فشلت في التحميل أو الـ SDK واجه خطأ قاتل. ما بتطلقش للأخطاء اللي العميل ممكن يتعافى منها زي رفض البطاقة. | `{ message, code? }`.                                                                           |

حدث الـ `complete` مجاملة لتجربة المستخدم. &#x2A;*لازم السيرفر بتاعك يفضل يستنى webhook `checkout.session.completed`** علشان يعلّم الطلب مدفوع. الـ iframe ممكن يتقفل بدري، والشبكة ممكن تفصل، والـ `onComplete` handler بتاعك ممكن يرمي خطأ. الـ webhook هو مصدر الحقيقة.

في React، نفس الـ callbacks بتركب على الـ `checkoutOptions` prop بتاع `<CheckoutButton />`: `onComplete`، `onClose`، `onReady`، `onConfirmed`، `onError`. بتتطابق واحد لواحد مع الأحداث اللي فوق.

## Modal مقابل inline [#modal-مقابل-inline]

`mode` بيتحكم في إن الـ iframe يغطّي الصفحة ولا يقعد جوّه حاوية.

<Cards>
  <Card icon="<PanelTop />" title="mode: modal">
    تغطية بملء الشاشة بـ iframe في النص. بتستدعي `checkout.open()` علشان تعرضه. اضغط بره، أو دوس
    Escape، أو استدعي `checkout.close()` علشان تخفيه. الأنسب لزراير "ادفع دلوقتي"، وصفحات ما بعد
    السلة، وأي حاجة مدفوعة بالأحداث.
  </Card>

  <Card icon="<LayoutGrid />" title="mode: inline">
    iframe متركّب جوّه حاوية بتوفّرها أنت. مفيش فتح/قفل: بيبقى موجود بمجرد ما تنشئ النسخة. مرّر
    `container` كـ CSS selector أو HTMLElement. الأنسب لصفحات "الدفع" المضمّنة اللي الفورم فيها هو
    الصفحة.
  </Card>
</Cards>

```typescript
// Inline mode
const checkout = xpay.checkout({
  clientSecret,
  mode: "inline",
  container: "#checkout-container", // or document.getElementById(...)
  onComplete: (result) => {
    /* ... */
  },
});

// On unmount or before re-creating, release the iframe
checkout.destroy();
```

الـ iframe المضمّن بيعيد ضبط حجمه تلقائيًا على حسب ارتفاع محتواه. مش محتاج تظبط ارتفاع ثابت في الـ CSS بتاعك.

**استدعي `checkout.destroy()` دايمًا لما تفكّك المكوّن** اللي بيملك الدفع المضمّن. من غيرها، الـ iframe بيفضل في الـ DOM، واستدعاء `xpay.checkout({ mode: "inline" })` جديد هيركّب واحد تاني. وضع الـ modal بيتولّى تنظيفه بنفسه عند الإغلاق، لكن لسه تقدر تستدعي `destroy()` علشان تحرّر المستمعين بدري.

`<CheckoutButton />` بيشتغل **modal بس**. للنمط المضمّن في React، انزل للـ JS SDK عن طريق `useXPay()` واستدعي `xpay.checkout({ mode: "inline" })` بنفسك. وافتكر تستدعي `destroy()` في `useEffect` للتنظيف.

## تخصيص المظهر واللغة [#تخصيص-المظهر-واللغة]

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

<Tabs items="[&#x22;Vanilla JavaScript&#x22;, &#x22;React&#x22;]">
  <Tab value="Vanilla JavaScript">
    ```typescript
    const checkout = xpay.checkout({
      clientSecret,
      mode: "modal",
      locale: "ar",
      appearance: {
        colorMode: "dark",
        borderStyle: "rounded",
        colors: { primary: "#635bff" },
      },
    });
    ```
  </Tab>

  <Tab value="React">
    ```tsx
    <CheckoutButton
      clientSecret={clientSecret}
      checkoutOptions={{
        locale: "ar",
        appearance: {
          colorMode: "dark",
          borderStyle: "rounded",
          colors: { primary: "#635bff" },
        },
        onComplete: (result) => {
          /* ... */
        },
      }}
    >
      Pay now
    </CheckoutButton>
    ```
  </Tab>
</Tabs>

مفيد لما نفس الجلسة محتاجة تبان مختلفة عبر سطوح مختلفة (مثلًا تطابق مفتاح الوضع الداكن في صفحتك من غير ما تحفظه على الجلسة).

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

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

لما ده يحصل، XPay بتسلّم البنك التاب كله. صفحتك بتتقفل، والعميل بيتحقق على صفحة البنك نفسه، وبيرجع لـ `afterCompletion.redirect.url` بتاعك. علشان كده الرابط ده مطلوب مع `uiMode: "embedded"`: لازم يكون مسجّل قبل ما التحقق يبدأ، لأن وقتها صفحتك والنافذة المنبثقة بيكونوا راحوا.

ده معناه إيه لكودك:

* **`onComplete` عمره ما بيشتغل في المسار ده.** الصفحة اتنقلت. و`onClose` كمان.
* **الرجوع مش إشارة نجاح.** العميل بيرجع مهما كانت النتيجة، والدفعة في الغالب لسه ما اتأكدتش وقت ما يوصل. اقرا حالة الجلسة في صفحة الرجوع بدل ما تفترض.
* **حطّ `{CHECKOUT_SESSION_ID}` جوّه الرابط** علشان الصفحة دي تعرف تقرا أنهي جلسة. XPay بتستبدله قبل ما العميل يوصل.

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

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

الـ `onComplete` callback بتاع Drop-in سريع ومريح، لكنه مش موثوق. جلسة الدفع اللي السيرفر بتاعك بينشئها هي نفس الكائن اللي بينتجه كل نمط تاني، فمعالِج webhook `checkout.session.completed` اللي كنت هتكتبه للدفع المستضاف بيشتغل من غير تغيير مع Drop-in.

الحد الأدنى اللي معالِجك محتاج يعمله:

1. تحقّق من ترويسة `XPay-Signature`.
2. فُكّ محتوى الـ JSON.
3. لو نوع الحدث `checkout.session.completed` وحالة الجلسة `status` هي `complete`، نفّذ الطلب.

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

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

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

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

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

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

* **بدّل المفاتيح.** استبدل `sk_test_*` بـ `sk_live_*` على السيرفر و`pk_test_*` بـ `pk_live_*` على الواجهة. رابط الـ API مش بيتغيّر.
* **اظبط نقطة نهاية webhook فعلية.** وضع الاختبار والحساب الفعلي ليهم نقاط نهاية webhook منفصلة. كل واحدة بياخد السرّ بتاع التوقيع `whsec_*` الخاص بيها.
* **تحقّق من التواقيع على الـ webhook، مش على `onComplete`.** عامل حدث الـ `complete` كتلميح لتجربة المستخدم، مش كتصريح.
* **ما تكشفش المفتاح السرّي للمتصفّح.** مفاتيح `sk_*` للسيرفر بس. الـ SDK بياخد مفتاح قابل للنشر (`pk_*`) بالتصميم.
* **امنع تكرار تسليمات الـ webhook على `event.id`.** XPay بيعيد محاولة التسليمات عند الردود المش 2xx أو انتهاء المهلة، فنفس `checkout.session.completed` لنفس الجلسة ممكن يوصل أكتر من مرة. تتبّع معرّفات الأحداث اللي عالجتها بالفعل.
* **ابعت `Idempotency-Key` على `POST /checkout/sessions`.** لو الطلب وقعت مهلته، إعادة المحاولة بنفس المفتاح بترجّع الجلسة الأصلية بدل ما تفتح تانية. شوف [عدم التكرار](/integrate/idempotency).
* **اظبط content-security-policy بيسمح بـ `https://checkout.xpay.app`.** لو موقعك بيستخدم توجيه `frame-src` أو `script-src` صارم، لازم تسمح بالاتنين سكربت الـ SDK والـ iframe المضمّن.

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

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

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

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

  <Card icon="<ShieldCheck />" title="التحقق من التواقيع" href="/integrate/webhooks/verifying-signatures">
    طريقة التحقق الكاملة بـ HMAC-SHA256 مع الحماية من إعادة التشغيل.
  </Card>

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

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

  <Card icon="<SquareCode />" title="مثال HTML عادي" href="https://github.com/xpayeg/xpay-examples/tree/main/vanilla-html">
    `drop-in-modal.html` و`drop-in-inline.html`: نفس نمط الصفحة دي كملفات HTML عادية جاهزة للتشغيل
    بالـ factory العام `XPay()`، من غير build step.
  </Card>

  <Card icon="<Atom />" title="متجر مثال Next.js" href="https://github.com/xpayeg/xpay-examples/tree/main/nextjs-react">
    الـ drop-in modal على `/checkout?ui=embedded` ومبالغ ديناميكية على `/donate`، جوّه متجر Next.js
    كامل.
  </Card>
</Cards>