# بنود الفاتورة والتسعير (/ar/integrate/checkout-session/line-items-and-pricing)

إزاي بتشتغل `lineItems` على جلسة الدفع: الأسعار الموجودة مقابل `priceData` المضمَّن، والكمية الثابتة والقابلة للتعديل، وبنود المبلغ المخصّص، والعملة، وبوابة الإتاحة.

مصفوفة `lineItems` في `POST /checkout/sessions` بتسرد اللي العميل بيدفع مقابله. كل بند بيحمل مرجع سعر، وكمية، وقواعد اختيارية تسمح للعميل يعدّل الكمية وقت الدفع. الجلسة بتجمّعهم في الإجماليات اللي بتظهر على الصفحة المستضافة (`amountSubtotal`، `amountTotal`)، ونفس شكل البند ده هو اللي بيرجع في رد جلسة الدفع.

حاجتين بيحدّدوا كل بند فاتورة: أنهي <ApiLink href="/api-reference/objects/price">Price</ApiLink> بيشير ليه، وكام واحد من السعر ده العميل هيتخصم عليه. وكل حاجة تانية (العملة، والإتاحة، وسعر صرف العرض) بتطلع من السعر.

## الأسعار الموجودة مقابل `priceData` المضمَّن [#الأسعار-الموجودة-مقابل-pricedata-المضمَّن]

البند بيشير لسعر بإحدى طريقتين. قدّم واحدة بالظبط. إرسال الاتنين، أو ولا واحدة، بيتم رفضه.

| الحقل       | إمتى تستخدمه                                                                                                                         |
| ----------- | ------------------------------------------------------------------------------------------------------------------------------------ |
| `price`     | الـ `price_*` ID بتاع الـ <ApiLink href="/api-reference/objects/price">Price</ApiLink>. استخدمه لما الـ SKU موجود في الكتالوج بتاعك. |
| `priceData` | منتج وسعر مؤقت بيتعملوا في اللحظة للجلسة دي. استخدمه للفواتير لمرة واحدة، والطلبات المخصّصة، وصفحات التبرّع.                         |

```jsonc
// Catalog reference
{ "price": "price_test_AbC123", "quantity": 1 }

// Inline ad-hoc price
{
  "priceData": {
    "currency": "EGP",
    "unitAmount": 149900,
    "productData": { "name": "Custom order #4231" }
  },
  "quantity": 1
}
```

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

`priceData.unitAmount` بالوحدات الصغرى. `149900` معناه 1,499.00 EGP. شكل `priceData` الكامل:

| الحقل                     | مطلوب | ملاحظات                                                                                            |
| ------------------------- | ----- | -------------------------------------------------------------------------------------------------- |
| `currency`                | نعم   | ISO 4217. لازم يطابق العملة اللي بتظهر للعميل في الجلسة (شوف [العملة](#العملة-وتعدد-العملات) تحت). |
| `unitAmount`              | نعم   | مبلغ الوحدة بالوحدات الصغرى. بيتضرب في `quantity` علشان يطلع إجمالي البند.                         |
| `productData.name`        | نعم   | الاسم المعروض في ملخص الطلب على الصفحة المستضافة.                                                  |
| `productData.description` | لا    | سطر فرعي بيظهر تحت اسم المنتج.                                                                     |
| `productData.image`       | لا    | رابط صورة عام. بيتجاب ويتخزّن في السيرفر.                                                          |
| `productData.unitLabel`   | لا    | تسمية مخصّصة لعروض "كمية 2" (زي "مقعد"، "ترخيص").                                                  |
| `productData.metadata`    | لا    | حقيبة مفتاح-قيمة بتاعتك إنت، مبهمة بالنسبة لـ XPay.                                                |

## الكمية والكمية القابلة للتعديل [#الكمية-والكمية-القابلة-للتعديل]

دور بند الفاتورة بيتحدّد من حقلين: `quantity` و`adjustableQuantity`. مفيش حقل نوع منفصل؛ التوليفة بينهم هي اللي بتقرّر هل العميل لازم يشتري البند، ولا يقدر يشيله، ولا يبدأ من غيره أصلًا.

| الدور              | `quantity` المبدئية | `adjustableQuantity`                     | اللي العميل بيشوفه                                                       |
| ------------------ | ------------------- | ---------------------------------------- | ------------------------------------------------------------------------ |
| **مطلوب**          | 1 أو أكتر           | مقفولة، أو مفتوحة بـ `minimum` 1 أو أكتر | بند ثابت مش بيقدر يشيله. العدّاد بيظهر بس لما الكمية تكون قابلة للتعديل. |
| **قابل للإزالة**   | 1 أو أكتر           | مفتوحة، `minimum` 0                      | بيبدأ جوّه الطلب. العميل يقدر ينزّله لـ 0 علشان يشيله.                   |
| **إضافة اختيارية** | 0                   | مفتوحة، `minimum` 0                      | بيبدأ بره الطلب، معروض تحت "أضِف لطلبك" مع زرار **إضافة**.               |

الحالة الشائعة هي بند ثابت: ظبّط `quantity` وسيب `adjustableQuantity` مقفولة.

```jsonc
{ "price": "price_test_AbC123", "quantity": 1 }
```

علشان تسمح للعميل يغيّر العدد وقت الدفع، فعّل `adjustableQuantity`:

```jsonc
{
  "price": "price_test_AbC123",
  "quantity": 1,
  "adjustableQuantity": {
    "enabled": true,
    "minimum": 1,
    "maximum": 10,
  },
}
```

لما `adjustableQuantity.enabled` يكون `true`، الصفحة المستضافة بتعرض عدّاد `−` / `+` جنب البند ده. العدّاد بيحترم `minimum` (الافتراضي 1) و`maximum` (الافتراضي 99). وبيحترم كمان `stock` المتبقّي للسعر: لو السعر ليه مخزون محدود، العدّاد بيتوقّف عند اللي فاضل، وبيظهر تلميح "باقي N بس" تحت العدّاد لما المخزون يكون 10 أو أقل.

### البنود القابلة للإزالة والاختيارية [#البنود-القابلة-للإزالة-والاختيارية]

ظبّط `adjustableQuantity.minimum` على `0` علشان تسمح للعميل يشيل البند بالكامل من الطلب.

* **قابل للإزالة.** ابدأ `quantity` بـ 1 أو أكتر مع `minimum: 0`. البند بيبقى جوّه الطلب من البداية، وعند الكمية 1 زرار `−` بيتحوّل لزرار إزالة بينزّله لـ 0.
* **إضافة اختيارية.** ابدأ `quantity` بـ `0` مع `minimum: 0`. البند بيبدأ بره الطلب، معروض تحت قسم "أضِف لطلبك". العميل بيدخّله بكمية 1 عن طريق زرار **إضافة**.

```jsonc
// إضافة اختيارية: بتبدأ بره العربة، والعميل يقدر يضيف لحد 3
{
  "price": "price_test_giftwrap",
  "quantity": 0,
  "adjustableQuantity": { "enabled": true, "minimum": 0, "maximum": 3 },
}
```

`quantity` بقيمة `0` صحيحة بس على البند اللي `adjustableQuantity.enabled` بتاعه `true` و`minimum` بتاعه `0`. أي بند تاني بيتبعت بـ `quantity: 0` بيتم رفضه وقت الإنشاء.

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

الصفحة المستضافة بتتولّى أي تغيير في الكمية من الأول للآخر: الإجماليات بتتحدّث على الصفحة والجلسة بتتحدّث في السيرفر. مش هتكتب أي كود للمسار ده. علشان تغيّر بنود الفاتورة من الـ backend بتاعك، استخدم `PATCH /checkout/sessions/:id` (استبدال كامل لـ `lineItems`، شوف [نظرة عامة ← إيه اللي يتعدّل](/integrate/checkout-session/overview#إيه-اللي-يتعدّل)).

## العملة وتعدد العملات [#العملة-وتعدد-العملات]

كل Price ليه عملة أصلية. جوّه الجلسة الواحدة، كل بند فاتورة لازم يشارك نفس العملة دي: خلط بنود EGP وUSD على نفس الجلسة بيتم رفضه وقت الإنشاء.

XPay بيعالج التسويات بالـ EGP حاليًا. لما السعر يكون بالـ EGP، العملة الواحدة دي بتمشي من الأول للآخر. ولما السعر يكون بعملة تانية (**عملة العرض**)، الجلسة بتثبّت سعر صرف عند الإنشاء وبتحتفظ بعرض موازي لـ "اللي العميل بيشوفه" جنب قيم المعالجة بالـ EGP.

بشكل ملموس:

* الجلسة وكل بند فاتورة بيحملوا **مسارَين للمبلغ** في الرد: مسار المعالجة (`amountSubtotal`، `amountTotal`، بوحدات EGP الصغرى) وملحق مرآة للعرض (`presentmentDetails.amountSubtotal`، `presentmentDetails.amountTotal`، بعملة العميل).
* سعر الصرف **بيتثبّت عند إنشاء الجلسة**. تغييرات الكمية، والخصومات، والتحديثات كلها بتعيد استخدامه. العميل عمره ما بيشوف السعر بيتحرّك وقت الدفع.
* تطبيق الدفع المستضاف بيقرا قيم العرض لما تكون موجودة وبيعرض الصفحة كلها بعملة واحدة. والسيرفر بتاعك بيقرا قيم المعالجة للسجل والتسوية.

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

## إتاحة السعر [#إتاحة-السعر]

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

| الحالة                  | السبب                                                                       | اللي العميل بيشوفه            |
| ----------------------- | --------------------------------------------------------------------------- | ----------------------------- |
| `archived`              | السعر اتأرشف في لوحة التحكم (`active: false`).                              | شارة "غير متاح"، والبند باهت. |
| `scheduled`             | السعر ليه `startDate` في المستقبل.                                          | شارة "لسه مش متاح".           |
| `expired`               | السعر ليه `expirationDate` عدّى.                                            | شارة "منتهي الصلاحية".        |
| `sold_out`              | الـ `stock` المحدود أقل من الكمية المطلوبة.                                 | شارة "خلص المخزون".           |
| `recurring_unsupported` | الـ `type` بتاع السعر هو `RECURRING`. الأسعار المتكرّرة مش مقبولة في الدفع. | شارة "نوع سعر غير مدعوم".     |
| `available`             | ولا واحدة مما سبق.                                                          | عرض عادي.                     |

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

`stock` لما يكون `null` (الافتراضي) معناه غير محدود. بنود `priceData` المضمَّنة عمرها ما بتحمل سقف مخزون.

البند الاختياري اللي قاعد على `quantity` 0 مش بيتشري، فبوابة الإتاحة بتتخطّاه: الإضافة اللي نفد مخزونها عمرها ما بتمنع دفع باقي الطلب. هو بيتفحص بس بعد ما العميل يضيفه والكمية تعدّي 0.

## بنود المبلغ المخصّص [#بنود-المبلغ-المخصّص]

لما السعر يكون `type: CUSTOM`، العميل بيدخل المبلغ بنفسه وقت الدفع. استخدمه للتبرّعات، أو تسعير "ادفع اللي إنت عايزه"، أو الفواتير اللي التاجر بيعرف مبلغها في لوحة التحكم قبل إرسال الرابط بلحظة.

سعر المبلغ المخصّص بيحمل تهيئة `customUnitAmount`:

```jsonc
{
  "id": "price_test_donate",
  "type": "CUSTOM",
  "currency": "EGP",
  "customUnitAmount": {
    "minimum": 5000, // 50.00 EGP
    "maximum": 10000000, // 100,000.00 EGP
    "preset": 50000, // 500.00 EGP shown by default
  },
}
```

البند اللي بيستقر على سعر CUSTOM بيبدأ مبلغ الجلسة من `customUnitAmount.preset` (أو 0 لو مفيش preset متظبّط). الصفحة المستضافة بتعرض مبلغ كبير مع زر **تغيير المبلغ** بيفتح حقل إدخال. لما العميل يأكّد، الصفحة المستضافة بتتحقق من القيمة المدخلة مقابل الحدود وإجماليات الجلسة بتتحدّث في مكانها.

بنود CUSTOM ليها كام قاعدة صارمة:

* **بند CUSTOM واحد على الأكتر في الجلسة،** ولازم يكون البند **الوحيد**. خلطه مع بنود تانية بيتم رفضه.
* **`quantity` بتتفرض على 1.** الكمية القابلة للتعديل مش مسموحة.
* **الخصومات وأكواد الترويج مش مدعومة** على الجلسات اللي فيها بند CUSTOM. الفكرة كلها إن العميل هو اللي اختار المبلغ؛ وإضافة كوبون فوقه بيكسر الاتفاق.
* **`priceData` المضمَّن مينفعش يكون CUSTOM.** بس مراجع `price_*` الموجودة المتعملة في لوحة التحكم أو عن طريق الـ Prices API هي اللي تقدر تستقر على CUSTOM. الحدود (`customUnitAmount`) بتتظبّط على الـ Price، مش على البند.
* **`uiMode: "custom"` (Elements SDK) غير متوافق.** ابنِ الجلسة بـ `unitAmount` ثابت بدلًا منه، أو استخدم `uiMode: "hosted"` / `"embedded"`، والاتنين بيستخدموا واجهة إدخال المبلغ بتاعة الدفع المستضاف.

## إيه اللي على كل بند فاتورة في الرد [#إيه-اللي-على-كل-بند-فاتورة-في-الرد]

كل بند فاتورة في رد جلسة الدفع بيحمل نفس الحقول:

| الحقل                | إيه هو                                                                                                                                                                                                                        |
| -------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `id`                 | معرّف البند (`li_*`).                                                                                                                                                                                                         |
| `quantity`           | الكمية المستقرّة. `1` لبنود CUSTOM؛ و`0` للإضافة الاختيارية اللي العميل ماضافهاش؛ وغير كده كمية البند نفسها، يا القيمة الثابتة اللي التاجر حطّها يا اللي العميل ظبّطه بالعدّاد في البند القابل للتعديل.                       |
| `price`              | كائن <ApiLink href="/api-reference/objects/price">Price</ApiLink> الكامل، بما فيه منتجه، ونوعه (`ONE_TIME` / `CUSTOM`)، و`customUnitAmount` (لو موجود)، وحقول دورة الحياة (`active`، `stock`، `startDate`، `expirationDate`). |
| `adjustableQuantity` | بيرجع زي ما اتظبّط عند الإنشاء. بيحرّك عرض العدّاد على الصفحة المستضافة.                                                                                                                                                      |
| `amountSubtotal`     | `unitAmount × quantity`، بعملة معالجة الجلسة.                                                                                                                                                                                 |
| `amountTotal`        | بعد أي خصم أو ضريبة موزّعين على البند ده.                                                                                                                                                                                     |
| `amountDiscount`     | الخصم الموزّع على البند ده. `0` لما مفيش خصم منطبق. مجموعه عبر البنود بيساوي خصم الجلسة.                                                                                                                                      |
| `amountTax`          | الضريبة الموزّعة على البند ده. `0` لما مفيش ضريبة منطبقة.                                                                                                                                                                     |
| `currency`           | عملة حقول `amount*` اللي فوق. على جلسة الدفع دي عملة المعالجة.                                                                                                                                                                |
| `presentmentDetails` | ملحق بـ `unitAmount`، و`amountSubtotal`، و`amountDiscount`، و`amountTotal`، و`currency` بعملة العميل. موجود بس لما الجلسة تكون متعددة العملات.                                                                                |

البنود الاختيارية اللي العميل ماضافهاش مش بتبقى جزء من الطلب المكتمل. بمجرد ما الدفع ينجح، الـ `GET /checkout/sessions/:id` المكتمل وحدث `checkout.session.completed` بيحملوا بس البنود اللي العميل اشتراها. والبند اللي قاعد على `quantity` 0 بيتشال من الاتنين.

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

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

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

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