Documentation Index Fetch the complete documentation index at: https://docs.oneclickdz.com/llms.txt
Use this file to discover all available pages before exploring further.
بنية استجابة الخطأ تتبع جميع الأخطاء تنسيقًا موحدًا: {
"success" : false ,
"error" : {
"code" : "ERROR_CODE" ,
"message" : "Human-readable error message" ,
"details" : {
// additional context
}
},
"requestId" : "req_abc123"
}
رموز حالة HTTP الحالة الفئة الوصف 400خطأ العميل معلمات طلب غير صالحة أو خطأ في التحقق 401المصادقة مفتاح API غير صالح أو مفقود 403التفويض صلاحيات أو رصيد غير كافٍ 404غير موجود المورد غير موجود 429حد المعدل طلبات كثيرة جدًا 500خطأ الخادم خطأ داخلي في الخادم 503غير متاح الخدمة غير متاحة مؤقتًا
رموز الخطأ الشائعة
MISSING_ACCESS_TOKEN
الرسالة : Access token is required
السبب : ترويسة X-Access-Token مفقودة
الإجراء : أدرج مفتاح API في الترويسة
// ✅ Correct
fetch ( url , {
headers: { 'X-Access-Token' : 'YOUR_API_KEY' }
})
INVALID_ACCESS_TOKEN / ERR_AUTH
الرسالة : The provided access token is invalid
السبب : مفتاح API غير صحيح أو منتهي الصلاحية أو ملغى
الإجراء :
تحقق من صحة مفتاح API
أنشئ مفتاحًا جديدًا إذا لزم الأمر
لا تسجّل قيم المفاتيح الحساسة
تواصل مع الدعم إذا استمرت المشكلة
NO_BALANCE / INSUFFICIENT_BALANCE
الرسالة : Insufficient balance
السبب : رصيد الحساب منخفض جدًا
الإجراء :
تحقق من الرصيد عبر /v3/account/balance قبل العمليات
اعرض الرصيد الحالي للمستخدم
اعرض خيار الشحن
لا تعيد المحاولة دون إضافة أموال
const balanceRes = await fetch ( 'https://api.oneclickdz.com/v3/account/balance' , {
headers: { 'X-Access-Token' : API_KEY }
});
const { data } = await balanceRes . json ();
if ( data . balance < requiredAmount ) {
throw new Error ( 'Insufficient balance' );
}
DUPLICATED_REF
الرسالة : This reference ID is already in use
السبب : تم استخدام المرجع في طلب سابق
الإجراء :
تحقق من حالة الطلب الموجود
أنشئ مرجعًا فريدًا جديدًا
لا تنشئ طلبات مكررة
const ref = `order- ${ Date . now () } - ${ Math . random (). toString ( 36 ). substr ( 2 , 9 ) } ` ;
IP_BLOCKED
الرسالة : Your IP has been temporarily blocked
السبب : محاولات مصادقة فاشلة كثيرة جدًا
الإجراء :
انتظر 15 دقيقة لإلغاء الحظر التلقائي
تحقق من صحة مفتاح API
تواصل مع الدعم إذا استمر الأمر
IP_NOT_ALLOWED
الرسالة : Your IP address is not whitelisted
السبب : قائمة IP البيضاء مفعّلة
الإجراء : أضف عنوان IP الخاص بك إلى القائمة البيضاء في لوحة التحكم
ERR_VALIDATION
الرسالة : Validation error
السبب : معلمات الطلب لا تستوفي المتطلبات
المشكلات الشائعة : حقول مفقودة، أنواع بيانات غير صالحة، عدم تطابق النمط
الإجراء :
تحقق من المدخلات على جانب العميل أولًا
تحقق من error.details للمشكلات الخاصة بالحقول
لا تعيد المحاولة دون إصلاح المشكلة
// Validate before sending
function validateTopupRequest ( data ) {
const errors = [];
if ( ! data . plan_code ) {
errors . push ({ field: 'plan_code' , message: 'Plan code is required' });
}
if ( ! data . MSSIDN || ! / ^ 0 [ 567 ][ 0-9 ] {8} $ / . test ( data . MSSIDN )) {
errors . push ({ field: 'MSSIDN' , message: 'Invalid phone number format' });
}
if ( isDynamicPlan ( data . plan_code )) {
if ( ! data . amount || data . amount < 50 || data . amount > 5000 ) {
errors . push ({ field: 'amount' , message: 'Amount must be between 50 and 5000' });
}
}
return errors . length > 0 ? { valid: false , errors } : { valid: true };
}
ERR_PHONE
الرسالة : Invalid phone number
السبب : رقم الهاتف غير صحيح أو غير موجود
الإجراء : استخدم /v3/internet/check-number للتحقق أولًا
const checkRes = await fetch (
`https://api.oneclickdz.com/v3/internet/check-number?type=ADSL&number= ${ number } ` ,
{ headers: { 'X-Access-Token' : API_KEY } }
);
if ( ! checkRes . ok ) {
throw new Error ( 'Invalid phone number' );
}
ERR_STOCK
الرسالة : Product out of stock
السبب : المنتج/قيمة البطاقة المطلوبة غير متاحة
الإجراء :
تحقق من المخزون قبل الطلب
اعرض فئات بديلة
حاول مرة أخرى لاحقًا
NOT_FOUND
الرسالة : Resource not found
السبب : المورد المطلوب غير موجود
الحالات الشائعة : معرّف طلب غير صالح، مرجع غير صالح، مورد محذوف
الإجراء : تحقق من صحة المعرّف/المرجع، تحقق من الأخطاء الإملائية، تعامل بأناقة في واجهة المستخدم
RATE_LIMIT_EXCEEDED
الرسالة : Too many requests
السبب : تجاوز حد المعدل
الحدود : Sandbox: 60 طلب/دقيقة، الإنتاج: 120 طلب/دقيقة
الإجراء : نفّذ تراجعًا أسيًا مع منطق إعادة المحاولة
async function fetchWithRetry ( url , options , maxRetries = 3 ) {
for ( let i = 0 ; i < maxRetries ; i ++ ) {
const response = await fetch ( url , options );
if ( response . status === 429 ) {
const retryAfter = response . headers . get ( 'Retry-After' ) || Math . pow ( 2 , i );
await new Promise ( resolve => setTimeout ( resolve , retryAfter * 1000 ));
continue ;
}
return response ;
}
throw new Error ( 'Max retries exceeded' );
}
INTERNAL_SERVER_ERROR / INTERNAL_ERROR
الرسالة : Developer was notified and will check shortly
السبب : خطأ غير متوقع على خوادمنا
الإجراء :
لا تستردّ الأموال فورًا - انتظر 24 ساعة
احفظ requestId للدعم الفني
نفّذ منطق إعادة المحاولة مع تراجع
تواصل مع الدعم بالتفاصيل
نحن نُخطَر تلقائيًا
if ( response . status === 500 ) {
console . error ( 'Server error:' , data . requestId );
// Mark for review - don't refund yet
await scheduleStatusCheck ( orderId , 24 * 60 * 60 * 1000 );
}
ERR_SERVICE
الرسالة : Service temporarily unavailable
السبب : صيانة الخدمة أو مشكلة مؤقتة
الإجراء :
اعرض رسالة صيانة
أعد المحاولة بعد تأخير
راقب للحصول على حل
أفضل ممارسات التنفيذ 1. تحقق دائمًا من حقل success const response = await fetch ( url , options );
const data = await response . json ();
if ( data . success ) {
return data . data ;
} else {
throw new Error ( ` ${ data . error . code } : ${ data . error . message } ` );
}
2. تعامل مع رموز الخطأ المحددة async function sendTopUp ( params ) {
const response = await fetch ( url , {
method: "POST" ,
headers: {
"Content-Type" : "application/json" ,
"X-Access-Token" : API_KEY ,
},
body: JSON . stringify ( params ),
});
const data = await response . json ();
if ( ! data . success ) {
switch ( data . error . code ) {
case "NO_BALANCE" :
case "INSUFFICIENT_BALANCE" :
throw new Error ( "Insufficient balance. Please add funds." );
case "DUPLICATED_REF" :
return await checkOrderStatus ( params . ref );
case "ERR_VALIDATION" :
throw new Error ( `Invalid input: ${ data . error . message } ` );
case "INTERNAL_SERVER_ERROR" :
case "INTERNAL_ERROR" :
await scheduleStatusCheck ( params . ref );
throw new Error ( "Temporary error, checking status later" );
default :
throw new Error ( ` ${ data . error . code } : ${ data . error . message } ` );
}
}
return data . data ;
}
3. منطق إعادة المحاولة الذكي مع التراجع الأسي async function safeApiCall ( apiFunction , options = {}) {
const { maxRetries = 3 , retryDelay = 1000 , retryOn = [ 500 , 503 ] } = options ;
let lastError ;
for ( let attempt = 1 ; attempt <= maxRetries ; attempt ++ ) {
try {
return await apiFunction ();
} catch ( error ) {
lastError = error ;
// Don't retry on client errors (4xx)
if ( error . status >= 400 && error . status < 500 ) {
throw error ;
}
// Retry on specific status codes
if ( retryOn . includes ( error . status ) && attempt < maxRetries ) {
const delay = retryDelay * Math . pow ( 2 , attempt - 1 );
console . log ( `Retry ${ attempt } / ${ maxRetries } after ${ delay } ms` );
await new Promise (( resolve ) => setTimeout ( resolve , delay ));
continue ;
}
throw error ;
}
}
throw lastError ;
}
4. رسائل خطأ مناسبة للمستخدم const errorMessages = {
MISSING_ACCESS_TOKEN: "Authentication required. Please check your API key." ,
INVALID_ACCESS_TOKEN: "Invalid API key. Please verify your credentials." ,
ERR_AUTH: "Authentication failed. Please check your API key." ,
NO_BALANCE: "Insufficient balance. Please top up your account." ,
INSUFFICIENT_BALANCE: "Insufficient balance. Please top up your account." ,
DUPLICATED_REF: "This order was already processed." ,
ERR_VALIDATION: "Please check your input and try again." ,
ERR_PHONE: "Invalid phone number. Please verify and try again." ,
ERR_STOCK: "This product is currently out of stock." ,
NOT_FOUND: "The requested resource was not found." ,
RATE_LIMIT_EXCEEDED: "Too many requests. Please wait a moment and try again." ,
ERR_SERVICE: "Service temporarily unavailable. Please try again." ,
INTERNAL_SERVER_ERROR: "An error occurred. Our team has been notified." ,
INTERNAL_ERROR: "An error occurred. Our team has been notified." ,
};
function getUserFriendlyMessage ( errorCode , defaultMessage ) {
return (
errorMessages [ errorCode ] ||
defaultMessage ||
"An unexpected error occurred."
);
}
5. سجّل Request IDs function logError ( error , context ) {
const errorLog = {
timestamp: new Date (). toISOString (),
requestId: error . requestId ,
code: error . code ,
message: error . message ,
context: {
userId: context . userId ,
operation: context . operation ,
},
};
logger . error ( "API Error" , errorLog );
if ( error . status >= 500 ) {
alertOps ( "API Error" , errorLog );
}
}
أنماط متقدمة نمط قاطع الدائرة (Circuit Breaker) امنع الإخفاقات المتتالية بإيقاف الطلبات عند ارتفاع معدل الخطأ: class CircuitBreaker {
constructor ( threshold = 5 , timeout = 60000 ) {
this . failureCount = 0 ;
this . threshold = threshold ;
this . timeout = timeout ;
this . state = "CLOSED" ; // CLOSED, OPEN, HALF_OPEN
this . nextAttempt = Date . now ();
}
async execute ( fn ) {
if ( this . state === "OPEN" ) {
if ( Date . now () < this . nextAttempt ) {
throw new Error ( "Circuit breaker is OPEN" );
}
this . state = "HALF_OPEN" ;
}
try {
const result = await fn ();
this . onSuccess ();
return result ;
} catch ( error ) {
this . onFailure ();
throw error ;
}
}
onSuccess () {
this . failureCount = 0 ;
this . state = "CLOSED" ;
}
onFailure () {
this . failureCount ++ ;
if ( this . failureCount >= this . threshold ) {
this . state = "OPEN" ;
this . nextAttempt = Date . now () + this . timeout ;
}
}
}
مراقبة الأخطاء تتبّع أنماط الأخطاء للكشف المبكر عن المشكلات: class ApiClient {
constructor ( apiKey ) {
this . apiKey = apiKey ;
this . errorStats = new Map ();
}
trackError ( errorCode ) {
const count = this . errorStats . get ( errorCode ) || 0 ;
this . errorStats . set ( errorCode , count + 1 );
}
async request ( url , options ) {
try {
const response = await fetch ( url , {
... options ,
headers: {
... options . headers ,
"X-Access-Token" : this . apiKey ,
},
});
const data = await response . json ();
if ( ! data . success ) {
this . trackError ( data . error . code );
// Alert if too many errors
if ( this . errorStats . get ( data . error . code ) > 10 ) {
this . sendAlert ( `High error rate: ${ data . error . code } ` );
}
}
return data ;
} catch ( error ) {
this . trackError ( "NETWORK_ERROR" );
throw error ;
}
}
sendAlert ( message ) {
console . error ( "ALERT:" , message );
// Send to your monitoring service
}
}
معالجة UNKNOWN_ERROR هام: لا تُصدر أي استرداد فور ظهور UNKNOWN_ERROR. انتظر دائماً 24 ساعة لحل المشكلة.
async function handleUnknownError ( orderId , topupId ) {
// Mark for review
await db . orders . update ({
where: { id: orderId },
data: {
status: "REVIEW_NEEDED" ,
reviewReason: "UNKNOWN_ERROR from API" ,
reviewScheduled: new Date ( Date . now () + 24 * 60 * 60 * 1000 ),
},
});
// Show message to user
await notifyUser ( orderId , {
title: "Order Under Review" ,
message: "Your order is being verified. Status will update within 24 hours." ,
});
// Schedule automatic recheck
await scheduleJob ( "recheck-unknown-error" , {
orderId ,
topupId ,
runAt: new Date ( Date . now () + 24 * 60 * 60 * 1000 ),
});
}
اختبار سيناريوهات الخطأ استخدم وضع Sandbox لاختبار معالجة الأخطاء: // Test insufficient balance
// Set your sandbox balance very low
// Test validation errors
await sendTopUp ({
plan_code: 'INVALID_PLAN' ,
MSSIDN: '123456789' , // Invalid format
amount: - 100 // Negative amount
});
// Test duplicate reference
const ref = 'test-duplicate-123' ;
await sendTopUp ({ ref , ... }); // First request
await sendTopUp ({ ref , ... }); // Should fail with DUPLICATED_REF
مرجع سريع
التحقق المبكر تحقق من المدخلات من جهة العميل قبل استدعاءات API
إعادة المحاولة بذكاء استخدم backoff الأسي لأخطاء 5xx، ولا تُعِد المحاولة أبداً لأخطاء 4xx
سجّل السياق أدرج دائماً requestId والسياق في السجلات لتسهيل التصحيح
ردود فعل واضحة أظهر للمستخدمين رسائل خطأ واضحة وقابلة للتنفيذ
الخطوات التالية
Endpoints الأساسية استكشف جميع الـ endpoints
تنسيق الاستجابة فهم استجابات API