# أخطاء الدفع (/ar/integrate/errors/payment-errors)

لما عملية دفع لعميل تفشل، الخطأ بيقعد على حقل lastPaymentError بتاع Payment Intent. الشكل، والـ adviceCode، وإزاي تقسّم النص بين عميلك وفريقك.

الصفحة دي بتغطّي مكان الفشل **غير المتزامن**. لما عملية دفع لعميل تفشل (بطاقة مرفوضة، أو انتهاء مهلة المعالج، أو بطاقة منتهية، إلخ)، الخطأ ما بيرجعش كرد HTTP للسيرفر بتاعك. بيعيش على حقل `lastPaymentError` بتاع الـ Payment Intent لباقي عمره.

للأخطاء المتزامنة اللي بترجع من استدعاءات الـ API بتاعتك إنت، شوف [أخطاء API](/integrate/errors/api-errors).

## فين الخطأ بيعيش [#فين-الخطأ-بيعيش]

عملية الدفع الفاشلة بتسيب ثلاثة آثار مختلفة:

* **`paymentIntent.lastPaymentError`**: التفصيل المنظَّم الكامل. اقراه لكل اللي محتاج تعرضه في واجهتك التشغيلية، أو تقرّر استراتيجية إعادة المحاولة، أو تبني النص الموجّه للعميل.
* **`charge.failureCode` و`charge.failureMessage`**: نفس الخطأ الرئيسي، مربوط بالـ Charge المحدّد اللي فشل. مفيد لما تربط تنفيذ الطلب بالـ Charge.
* **webhook الـ `charge.failed`**: بيتطلق في اللحظة اللي محاولة الـ charge بتفشل فيها. اشترك فيه لو محتاج تتفاعل مع حالات الفشل في الوقت الفعلي.

أكمل بيانات بتبقى على `lastPaymentError`. لو معالِجك استقبل `charge.failed`، جيب الـ Payment Intent علشان تاخد الحقول المنظَّمة الموصوفة تحت.

## شكل `lastPaymentError` [#شكل-lastpaymenterror]

```json
{
  "type": "card_error",
  "code": "card_declined",
  "declineCode": "insufficient_funds",
  "networkDeclineCode": "51",
  "message": "Your card has insufficient funds.",
  "merchantMessage": "Declined: insufficient funds. Customer should use a different payment method.",
  "adviceCode": "try_again_later",
  "docUrl": "https://docs.xpay.app/integrate/errors/decline-codes#insufficient_funds",
  "param": null,
  "chargeId": "ch_test_AbC123",
  "paymentMethodType": "card",
  "paymentMethod": { "card": { "brand": "mastercard", "last4": "0008" } },
  "processorCode": "...",
  "processorMessage": "..."
}
```

| الحقل                               | المعنى                                                                                                                                                    |
| ----------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `type`                              | فئة عريضة: `card_error`، و`payment_method_error` (BNPL، أو محفظة، أو كشك، أو تحويل بنكي)، و`processor_error`، و`api_error`.                               |
| `code`                              | سبب الخطأ المحدّد. من قائمة [أكواد أخطاء الدفع](/integrate/errors/payment-error-codes).                                                                   |
| `declineCode`                       | لرفض البطاقات، سبب البنك المُصدِر. من قائمة [أكواد الرفض](/integrate/errors/decline-codes). `null` لحالات الفشل اللي مش رفض.                              |
| `networkDeclineCode`                | الكود من 2-4 أرقام اللي رجع من شبكة البطاقة (مثلًا `51` لعدم كفاية الرصيد في ISO 8583). المعنى بيعتمد على ماركة البطاقة. `null` لما الشبكة ما قدمتش واحد. |
| `message`                           | رسالة قصيرة آمنة للعرض للعميل.                                                                                                                            |
| `merchantMessage`                   | رسالة تفصيلية لفريق الدعم بتاعك، بتشمل سياق العميل ما المفروضش يشوفه.                                                                                     |
| `adviceCode`                        | اللي تعمله بعد كده: `confirm_card_data`، أو `try_again_later`، أو `do_not_try_again`. أكتر حقل قابل للتنفيذ.                                              |
| `docUrl`                            | رابط مباشر للصف المطابق في مرجع الأكواد.                                                                                                                  |
| `param`                             | حقل جسم الطلب اللي فشل، لو منطبق. عادةً `null` على المكان ده.                                                                                             |
| `chargeId`                          | الـ `ch_*` ID بتاع الـ Charge اللي فشل.                                                                                                                   |
| `paymentMethodType`                 | نوع طريقة الدفع (`card`، `valu`، `fawry`، إلخ).                                                                                                           |
| `paymentMethod`                     | لقطة لطريقة الدفع وقت الفشل (الماركة، آخر 4 أرقام للبطاقات).                                                                                              |
| `processorCode`، `processorMessage` | أكواد ورسائل خام من المعالج، غير مترجمة. للتصحيح العميق بس.                                                                                               |

`code` بيكون متظبّط دايمًا. `declineCode` و`networkDeclineCode` بيتظبّطوا على رفض البطاقات؛ والباقي بيتعبّى بناءً على اللي XPay يقدر يستخرجه من رد المعالج.

## مساحات الأكواد الثلاثة [#مساحات-الأكواد-الثلاثة]

`lastPaymentError` بيحمل لحد ثلاثة أكواد مختلفة لنفس الفشل. كل واحد هو مصدر الحقيقة لسؤال مختلف:

| الحقل                | السؤال اللي بيرد عليه                 | فين تلاقي الأوصاف                                          |
| -------------------- | ------------------------------------- | ---------------------------------------------------------- |
| `code`               | الفشل ده كان من أي نوع؟               | [أكواد أخطاء الدفع](/integrate/errors/payment-error-codes) |
| `declineCode`        | ليه البنك المُصدِر رفض؟ (البطاقات بس) | [أكواد الرفض](/integrate/errors/decline-codes)             |
| `networkDeclineCode` | أنهي كود خام رجّعته شبكة البطاقة؟     | خاص بالماركة؛ مرّره للدعم لو احتجت                         |

لأغلب المعالِجات، تفرّع على `adviceCode` الأول، واعرض `merchantMessage` في واجهتك التشغيلية، واستخدم `code` أو `declineCode` لاختيار النص الموجّه للعميل.

## نمط المعالِج [#نمط-المعالِج]

الإجراء اللي بتاخده بيتقرّر بـ `adviceCode`، مش بـ `code`. قائمة الأكواد بتكبر؛ وقيم الإرشاد الثلاثة لأ.

| `adviceCode`        | معناه إيه                                                                                   | اللي تعمله                                                                                         |
| ------------------- | ------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------- |
| `confirm_card_data` | بيانات البطاقة على الأرجح كانت غلط (عدم تطابق CVV، أو رقم غلط، أو عدم تطابق الرمز البريدي). | قول للعميل يدخّل بطاقته تاني. ما تقترحش بطاقة تانية؛ نفس البطاقة ببيانات مصحّحة على الأرجح هتشتغل. |
| `try_again_later`   | مؤقت: عدم كفاية الرصيد، أو انتهاء مهلة المعالج، أو خلل شبكة.                                | قول للعميل يحاول تاني، أو اعرض خيار "جرّب طريقة دفع تانية". آمن إعادة المحاولة مرة واحدة.          |
| `do_not_try_again`  | رفض قاطع: بطاقة مسروقة، أو ضايعة، أو احتيال، أو منتهية.                                     | ما تعيدش محاولة نفس البطاقة. اعرض للعميل "استخدم طريقة دفع تانية". بلّغ فريقك لو المعدّل ارتفع.    |

```typescript
function handlePaymentFailure(error: LastPaymentError) {
  // Always log first. The merchantMessage is what your support team needs.
  log.error("payment failed", {
    chargeId: error.chargeId,
    type: error.type,
    code: error.code,
    declineCode: error.declineCode,
    networkDeclineCode: error.networkDeclineCode,
    merchantMessage: error.merchantMessage,
    docUrl: error.docUrl,
  });

  // Decide UX from adviceCode.
  switch (error.adviceCode) {
    case "confirm_card_data":
      return showRetryWithCard(error.message);

    case "try_again_later":
      return showRetryWithFallback(error.message);

    case "do_not_try_again":
      return showUseDifferentMethod(error.message);

    default:
      // Missing adviceCode is rare. Treat as do_not_try_again to be safe.
      return showUseDifferentMethod(error.message);
  }
}
```

## نص العميل مقابل نص التاجر [#نص-العميل-مقابل-نص-التاجر]

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

| الحقل             | الجمهور                                                                          | مثال                                                                            |
| ----------------- | -------------------------------------------------------------------------------- | ------------------------------------------------------------------------------- |
| `message`         | عميلك. مفلتر بالفعل لما هو آمن للعرض.                                            | "Your card has insufficient funds."                                             |
| `merchantMessage` | فريقك (الدعم، اللوحات التشغيلية). بيشمل سياق مفيد داخليًا لكن غير مناسب للعملاء. | "Declined: insufficient funds. Customer should use a different payment method." |

تلميح المعاملة في لوحة التحكم بيستخدم `merchantMessage`. ابنِ واجهتك التشغيلية بنفس الطريقة. للمكان الموجّه للعميل (صفحة الدفع بتاعتك، وإيميلات المعاملات بتاعتك)، استخدم `message` أو نصّك إنت المربوط بـ `code` / `declineCode`.

ما تعرضش `merchantMessage` للعملاء. ممكن يسرّب سياق داخلي. وما تعرضش `processorCode` أو `processorMessage` للعملاء كمان؛ دول تمرير خام من المعالج، غير مترجم.

## قراءة `lastPaymentError` في الـ webhooks [#قراءة-lastpaymenterror-في-الـ-webhooks]

webhook الـ `charge.failed` بيحمل Charge في `event.data.object`. الـ Charge عنده الفشل الرئيسي (`failureCode`، `failureMessage`) لكن مش التفصيل المنظَّم الكامل. علشان تقرا `lastPaymentError` كامل، جيب الـ Payment Intent الأب:

```typescript
async function onChargeFailed(event: { data: { object: Charge } }) {
  const charge = event.data.object;

  // Quick action: the Charge alone has the headline.
  log.warn("charge failed", {
    chargeId: charge.id,
    failureCode: charge.failureCode,
    failureMessage: charge.failureMessage,
  });

  // Full detail: fetch the PI.
  const pi = await getPaymentIntent(charge.paymentIntentId);
  if (pi.lastPaymentError) {
    handlePaymentFailure(pi.lastPaymentError);
  }
}
```

`lastPaymentError` بيتظبّط على الـ PI طول عمر المورد. بيسجّل آخر محاولة فاشلة؛ ومحاولات إعادة المحاولة اللي بعدها على نفس الـ PI بتكتب فوقه.

## اللي ما تعملوش [#اللي-ما-تعملوش]

* **ما تتفرّعش على `code` لوحده.** قائمة الأكواد بتكبر على مدار الوقت. تفرّع على `adviceCode` للي تعمله، بعدين استخدم `code` لقرارات النص الضيّقة.
* **ما تعرضش `merchantMessage` لعملائك.** ممكن يشمل تفاصيل غير مناسبة لسطح المستهلك.
* **ما تتجاهلش `adviceCode: "do_not_try_again"`.** إعادة المحاولة بعد رفض بطاقة مسروقة أو احتيال ما بتفيدش وممكن تعلّم على حسابك عند البنك المُصدِر.
* **ما تعيدش المحاولة بشكل أعمى على `try_again_later`.** أعِد المحاولة مرة واحدة، بعدين انقل العميل لطريقة تانية أو أنهِ المسار. التكرار على رفض `do_not_honor` بينتج نفس النتيجة كل مرة.
* **ما تستخدمش `networkDeclineCode` كتفرّعك الأساسي.** هو تمرير خام بيعتمد على ماركة البطاقة. استخدم `declineCode` (شكل XPay المطبّع) بدلًا منه.

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

<Cards>
  <Card icon="<ListTree />" title="أكواد أخطاء الدفع" href="/integrate/errors/payment-error-codes">
    كل قيمة لـ `lastPaymentError.code`، مع النص الآمن للعميل والنص الموجّه للتاجر اللي XPay بيرجّعه.
  </Card>

  <Card icon="<Ban />" title="أكواد الرفض" href="/integrate/errors/decline-codes">
    سبب البنك المُصدِر للرفض (`lastPaymentError.declineCode`). أكثر كود محدّد بيحمله XPay.
  </Card>

  <Card icon="<TriangleAlert />" title="أخطاء API" href="/integrate/errors/api-errors">
    المكان التاني: أخطاء متزامنة بترجع في رد HTTP بتاع استدعاء الـ API بتاعك.
  </Card>

  <Card icon="<Activity />" title="لوحة الأحداث" href="/integrate/workbench/events-panel">
    شوف كل حدث `charge.failed` في لوحة التحكم. أعِد الإرسال، وادخل في تفاصيل المحاولة، وانط للـ
    Payment Intent.
  </Card>

  <Card icon="<Terminal />" title="لوحة السجلات" href="/integrate/workbench/logs-panel">
    استدعاء الـ API الأصلي اللي نتج عنه الـ Charge الفاشل. مفيد للربط المتبادل.
  </Card>

  <Card icon="<Layers />" title="مقدمة الأخطاء" href="/integrate/errors/introduction">
    رجوع للأول. خريطة المكانين.
  </Card>
</Cards>