أخطاء الدفع
لما عملية دفع لعميل تفشل، الخطأ بيقعد على حقل lastPaymentError بتاع Payment Intent. الشكل، والـ adviceCode، وإزاي تقسّم النص بين عميلك وفريقك.
الصفحة دي بتغطّي مكان الفشل غير المتزامن. لما عملية دفع لعميل تفشل (بطاقة مرفوضة، أو انتهاء مهلة المعالج، أو بطاقة منتهية، إلخ)، الخطأ ما بيرجعش كرد HTTP للسيرفر بتاعك. بيعيش على حقل lastPaymentError بتاع الـ Payment Intent لباقي عمره.
للأخطاء المتزامنة اللي بترجع من استدعاءات الـ API بتاعتك إنت، شوف أخطاء API.
فين الخطأ بيعيش
عملية الدفع الفاشلة بتسيب ثلاثة آثار مختلفة:
paymentIntent.lastPaymentError: التفصيل المنظَّم الكامل. اقراه لكل اللي محتاج تعرضه في واجهتك التشغيلية، أو تقرّر استراتيجية إعادة المحاولة، أو تبني النص الموجّه للعميل.charge.failureCodeوcharge.failureMessage: نفس الخطأ الرئيسي، مربوط بالـ Charge المحدّد اللي فشل. مفيد لما تربط تنفيذ الطلب بالـ Charge.- webhook الـ
charge.failed: بيتطلق في اللحظة اللي محاولة الـ charge بتفشل فيها. اشترك فيه لو محتاج تتفاعل مع حالات الفشل في الوقت الفعلي.
أكمل بيانات بتبقى على lastPaymentError. لو معالِجك استقبل charge.failed، جيب الـ Payment Intent علشان تاخد الحقول المنظَّمة الموصوفة تحت.
شكل lastPaymentError
{
"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 | سبب الخطأ المحدّد. من قائمة أكواد أخطاء الدفع. |
declineCode | لرفض البطاقات، سبب البنك المُصدِر. من قائمة أكواد الرفض. 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 | الفشل ده كان من أي نوع؟ | أكواد أخطاء الدفع |
declineCode | ليه البنك المُصدِر رفض؟ (البطاقات بس) | أكواد الرفض |
networkDeclineCode | أنهي كود خام رجّعته شبكة البطاقة؟ | خاص بالماركة؛ مرّره للدعم لو احتجت |
لأغلب المعالِجات، تفرّع على adviceCode الأول، واعرض merchantMessage في واجهتك التشغيلية، واستخدم code أو declineCode لاختيار النص الموجّه للعميل.
نمط المعالِج
الإجراء اللي بتاخده بيتقرّر بـ adviceCode، مش بـ code. قائمة الأكواد بتكبر؛ وقيم الإرشاد الثلاثة لأ.
adviceCode | معناه إيه | اللي تعمله |
|---|---|---|
confirm_card_data | بيانات البطاقة على الأرجح كانت غلط (عدم تطابق CVV، أو رقم غلط، أو عدم تطابق الرمز البريدي). | قول للعميل يدخّل بطاقته تاني. ما تقترحش بطاقة تانية؛ نفس البطاقة ببيانات مصحّحة على الأرجح هتشتغل. |
try_again_later | مؤقت: عدم كفاية الرصيد، أو انتهاء مهلة المعالج، أو خلل شبكة. | قول للعميل يحاول تاني، أو اعرض خيار "جرّب طريقة دفع تانية". آمن إعادة المحاولة مرة واحدة. |
do_not_try_again | رفض قاطع: بطاقة مسروقة، أو ضايعة، أو احتيال، أو منتهية. | ما تعيدش محاولة نفس البطاقة. اعرض للعميل "استخدم طريقة دفع تانية". بلّغ فريقك لو المعدّل ارتفع. |
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
webhook الـ charge.failed بيحمل Charge في event.data.object. الـ Charge عنده الفشل الرئيسي (failureCode، failureMessage) لكن مش التفصيل المنظَّم الكامل. علشان تقرا lastPaymentError كامل، جيب الـ Payment Intent الأب:
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 المطبّع) بدلًا منه.
رايح فين بعد كده
أكواد أخطاء الدفع
كل قيمة لـ lastPaymentError.code، مع النص الآمن للعميل والنص الموجّه للتاجر اللي XPay بيرجّعه.
أكواد الرفض
سبب البنك المُصدِر للرفض (lastPaymentError.declineCode). أكثر كود محدّد بيحمله XPay.
أخطاء API
المكان التاني: أخطاء متزامنة بترجع في رد HTTP بتاع استدعاء الـ API بتاعك.
لوحة الأحداث
شوف كل حدث charge.failed في لوحة التحكم. أعِد الإرسال، وادخل في تفاصيل المحاولة، وانط للـ
Payment Intent.
لوحة السجلات
استدعاء الـ API الأصلي اللي نتج عنه الـ Charge الفاشل. مفيد للربط المتبادل.
مقدمة الأخطاء
رجوع للأول. خريطة المكانين.