نظرة عامة
بعد التحقق من صحة جميع المدخلات، أنت جاهز لإرسال طلب الشحن إلى API الخاص بـ OneClickDz. يغطي هذا الدليل تقديم الطلبات، ومعالجة الردود، وإدارة نظام المراجع الخاص بك.مرجع API
اطلع على توثيق API الكامل لـ endpoint POST /v3/mobile/send
طلب الشحن الأساسي
مثال بسيط
curl -X POST https://api.oneclickdz.com/v3/mobile/send \
-H "Content-Type: application/json" \
-H "X-Access-Token: YOUR_API_KEY" \
-d '{
"plan_code": "PREPAID_DJEZZY",
"MSSIDN": "0778037340",
"amount": 500,
"ref": "order-1730462400-user123"
}'
const fetch = require('node-fetch');
async function sendTopUp({ planCode, phone, amount, ref }) {
const response = await fetch('https://api.oneclickdz.com/v3/mobile/send', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-Access-Token': process.env.ONECLICKDZ_API_KEY
},
body: JSON.stringify({
plan_code: planCode,
MSSIDN: phone,
amount: amount,
ref: ref
})
});
if (!response.ok) {
const error = await response.json();
throw new Error(error.error?.message || 'Request failed');
}
const data = await response.json();
if (!data.success) {
throw new Error(data.error?.message || 'Top-up failed');
}
return {
topupId: data.data.topupId,
topupRef: data.data.topupRef,
newBalance: data.data.newBalance
};
}
// Usage
try {
const result = await sendTopUp({
planCode: 'PREPAID_DJEZZY',
phone: '0778037340',
amount: 500,
ref: `order-${Date.now()}-user123`
});
console.log('✅ Top-up sent:', result.topupId);
console.log('Reference:', result.topupRef);
console.log('New balance:', result.newBalance);
} catch (error) {
console.error('❌ Failed:', error.message);
}
import requests
import json
import os
import time
def send_topup(plan_code, phone, amount, ref):
"""Send a mobile top-up request"""
response = requests.post(
'https://api.oneclickdz.com/v3/mobile/send',
headers={
'Content-Type': 'application/json',
'X-Access-Token': os.getenv('ONECLICKDZ_API_KEY')
},
json={
'plan_code': plan_code,
'MSSIDN': phone,
'amount': amount,
'ref': ref
}
)
response.raise_for_status()
data = response.json()
if not data['success']:
raise Exception(data['error']['message'])
return {
'topup_id': data['data']['topupId'],
'topup_ref': data['data']['topupRef'],
'new_balance': data['data'].get('newBalance')
}
# Usage
try:
result = send_topup(
plan_code='PREPAID_DJEZZY',
phone='0778037340',
amount=500,
ref=f'order-{int(time.time())}-user123'
)
print(f"✅ Top-up sent: {result['topup_id']}")
print(f"Reference: {result['topup_ref']}")
print(f"New balance: {result.get('new_balance')}")
except Exception as error:
print(f"❌ Failed: {error}")
<?php
function sendTopUp($planCode, $phone, $amount, $ref) {
$ch = curl_init('https://api.oneclickdz.com/v3/mobile/send');
$data = [
'plan_code' => $planCode,
'MSSIDN' => $phone,
'amount' => $amount,
'ref' => $ref
];
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
'Content-Type: application/json',
'X-Access-Token: ' . getenv('ONECLICKDZ_API_KEY')
],
CURLOPT_POSTFIELDS => json_encode($data)
]);
$response = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
if ($httpCode !== 200) {
throw new Exception("HTTP error! status: $httpCode");
}
$result = json_decode($response, true);
if (!$result['success']) {
throw new Exception($result['error']['message'] ?? 'Top-up failed');
}
return [
'topup_id' => $result['data']['topupId'],
'topup_ref' => $result['data']['topupRef'],
'new_balance' => $result['data']['newBalance'] ?? null
];
}
// Usage
try {
$result = sendTopUp(
'PREPAID_DJEZZY',
'0778037340',
500,
'order-' . time() . '-user123'
);
echo "✅ Top-up sent: {$result['topup_id']}\n";
echo "Reference: {$result['topup_ref']}\n";
echo "New balance: {$result['new_balance']}\n";
} catch (Exception $error) {
echo "❌ Failed: {$error->getMessage()}\n";
}
استجابة النجاح
{
"success": true,
"data": {
"topupId": "6901616fe9e88196b4eb64b0",
"topupRef": "order-1730462400-user123"
},
"meta": {
"timestamp": "2025-11-01T11:00:00.454Z"
},
"requestId": "req_1730462400_abc123"
}
توليد مراجع فريدة
احرص دائماً على توفير مراجع فريدة لمنع التكرار وتمكين التتبع:function generateOrderRef(userId) {
const timestamp = Date.now();
const random = Math.random().toString(36).substring(2, 8);
return `${userId}-${timestamp}-${random}`;
}
// Examples:
// user123-1730462400-x7k2p9
// user456-1730462401-m5n8q3
import time
import random
import string
def generate_order_ref(user_id):
timestamp = int(time.time() * 1000)
random_str = ''.join(random.choices(string.ascii_lowercase + string.digits, k=6))
return f"{user_id}-{timestamp}-{random_str}"
# Examples:
# user123-1730462400-x7k2p9
# user456-1730462401-m5n8q3
<?php
function generateOrderRef($userId) {
$timestamp = round(microtime(true) * 1000);
$random = substr(str_shuffle('abcdefghijklmnopqrstuvwxyz0123456789'), 0, 6);
return "{$userId}-{$timestamp}-{$random}";
}
// Examples:
// user123-1730462400-x7k2p9
// user456-1730462401-m5n8q3
التحقق من الرصيد قبل الإرسال
تحقق دائماً من كفاية الرصيد لتجنب الطلبات الفاشلة:مرجع API الرصيد
تعرّف على endpoint GET /v3/account/balance
async function checkBalance() {
const response = await fetch('https://api.oneclickdz.com/v3/account/balance', {
headers: {
'X-Access-Token': process.env.ONECLICKDZ_API_KEY
}
});
const data = await response.json();
if (!data.success) {
throw new Error('Failed to check balance');
}
return data.data.balance;
}
async function sendTopUpWithBalanceCheck({ planCode, phone, amount, ref, requiredBalance }) {
// Check balance first
const balance = await checkBalance();
if (balance < requiredBalance) {
throw new Error(`Insufficient balance. Required: ${requiredBalance} DZD, Available: ${balance} DZD`);
}
// Proceed with top-up
return await sendTopUp({ planCode, phone, amount, ref });
}
def check_balance():
"""Check account balance"""
response = requests.get(
'https://api.oneclickdz.com/v3/account/balance',
headers={'X-Access-Token': os.getenv('ONECLICKDZ_API_KEY')}
)
response.raise_for_status()
data = response.json()
if not data['success']:
raise Exception('Failed to check balance')
return data['data']['balance']
def send_topup_with_balance_check(plan_code, phone, amount, ref, required_balance):
"""Send top-up after checking balance"""
# Check balance first
balance = check_balance()
if balance < required_balance:
raise Exception(f'Insufficient balance. Required: {required_balance} DZD, Available: {balance} DZD')
# Proceed with top-up
return send_topup(plan_code, phone, amount, ref)
<?php
function checkBalance() {
$ch = curl_init('https://api.oneclickdz.com/v3/account/balance');
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
'X-Access-Token: ' . getenv('ONECLICKDZ_API_KEY')
]
]);
$response = curl_exec($ch);
curl_close($ch);
$data = json_decode($response, true);
if (!$data['success']) {
throw new Exception('Failed to check balance');
}
return $data['data']['balance'];
}
function sendTopUpWithBalanceCheck($planCode, $phone, $amount, $ref, $requiredBalance) {
// Check balance first
$balance = checkBalance();
if ($balance < $requiredBalance) {
throw new Exception("Insufficient balance. Required: {$requiredBalance} DZD, Available: {$balance} DZD");
}
// Proceed with top-up
return sendTopUp($planCode, $phone, $amount, $ref);
}
معالجة الأخطاء
تعامل مع أخطاء API الشائعة:راجع مرجع معالجة الأخطاء للاطلاع على رموز الأخطاء الكاملة واستراتيجيات المعالجة.
async function sendTopUpSafe(data) {
try {
return await sendTopUp(data);
} catch (error) {
// Parse error response
if (error.response) {
const errorData = error.response.data;
switch (errorData.error?.code) {
case 'ERR_VALIDATION':
console.error('Validation error:', errorData.error.message);
console.error('Field:', errorData.error.details?.field);
break;
case 'INSUFFICIENT_BALANCE':
console.error('Insufficient balance');
console.error('Required:', errorData.error.details?.required);
console.error('Available:', errorData.error.details?.available);
break;
case 'DUPLICATED_REF':
console.error('Duplicate reference');
console.error('Existing topup:', errorData.error.details?.existingTopupId);
// Check status of existing topup
break;
case 'INTERNAL_ERROR':
console.error('Server error, contact support');
console.error('Request ID:', errorData.requestId);
break;
default:
console.error('Unknown error:', errorData.error?.message);
}
}
throw error;
}
}
def send_topup_safe(data):
"""Send top-up with error handling"""
try:
return send_topup(**data)
except requests.HTTPError as error:
response_data = error.response.json()
error_code = response_data['error']['code']
if error_code == 'ERR_VALIDATION':
print('Validation error:', response_data['error']['message'])
print('Field:', response_data['error'].get('details', {}).get('field'))
elif error_code == 'INSUFFICIENT_BALANCE':
print('Insufficient balance')
print('Required:', response_data['error']['details']['required'])
print('Available:', response_data['error']['details']['available'])
elif error_code == 'DUPLICATED_REF':
print('Duplicate reference')
print('Existing topup:', response_data['error']['details']['existingTopupId'])
elif error_code == 'INTERNAL_ERROR':
print('Server error, contact support')
print('Request ID:', response_data['requestId'])
else:
print('Unknown error:', response_data['error']['message'])
raise
<?php
function sendTopUpSafe($data) {
try {
return sendTopUp(
$data['plan_code'],
$data['phone'],
$data['amount'],
$data['ref']
);
} catch (Exception $error) {
// Try to parse error response
$message = $error->getMessage();
if (strpos($message, 'Validation error') !== false) {
echo "Validation error\n";
} elseif (strpos($message, 'Insufficient balance') !== false) {
echo "Insufficient balance\n";
} elseif (strpos($message, 'Duplicate reference') !== false) {
echo "Duplicate reference - check existing order\n";
} else {
echo "Error: {$message}\n";
}
throw $error;
}
}
التكامل مع قاعدة البيانات
سجّل الطلبات قبل وبعد استدعاءات API:async function createTopUpOrder({ userId, planCode, phone, amount }) {
// Start database transaction
const transaction = await db.transaction();
try {
// 1. Generate reference
const ref = generateOrderRef(userId);
// 2. Get plan details
const plan = await db.mobilePlans.findOne({
where: { code: planCode }
});
const cost = amount * plan.wholesaleCost;
// 3. Check user balance
const user = await db.users.findOne({
where: { id: userId },
lock: true, // Lock row for update
transaction
});
if (user.balance < cost) {
await transaction.rollback();
throw new Error('Insufficient user balance');
}
// 4. Create order record
const order = await db.orders.create({
userId,
ref,
planCode,
phone,
amount,
cost,
status: 'PENDING',
createdAt: new Date()
}, { transaction });
// 5. Deduct user balance
await db.users.update({
balance: user.balance - cost
}, {
where: { id: userId },
transaction
});
// 6. Send to API
const result = await sendTopUp({
planCode,
phone,
amount,
ref
});
// 7. Update order with API response
await db.orders.update({
apiTopupId: result.topupId,
apiTopupRef: result.topupRef,
status: 'PROCESSING'
}, {
where: { id: order.id },
transaction
});
// Commit transaction
await transaction.commit();
return {
orderId: order.id,
topupId: result.topupId,
ref: ref
};
} catch (error) {
// Rollback on any error
await transaction.rollback();
throw error;
}
}
def create_topup_order(user_id, plan_code, phone, amount, db):
"""Create top-up order with database transaction"""
with db.transaction() as transaction:
try:
# 1. Generate reference
ref = generate_order_ref(user_id)
# 2. Get plan details
plan = db.mobile_plans.find_one({'code': plan_code})
cost = amount * plan['wholesale_cost']
# 3. Check user balance
user = db.users.find_one_and_lock({'_id': user_id})
if user['balance'] < cost:
transaction.rollback()
raise Exception('Insufficient user balance')
# 4. Create order record
order = db.orders.insert_one({
'user_id': user_id,
'ref': ref,
'plan_code': plan_code,
'phone': phone,
'amount': amount,
'cost': cost,
'status': 'PENDING',
'created_at': datetime.now()
})
# 5. Deduct user balance
db.users.update_one(
{'_id': user_id},
{'$inc': {'balance': -cost}}
)
# 6. Send to API
result = send_topup(plan_code, phone, amount, ref)
# 7. Update order with API response
db.orders.update_one(
{'_id': order.inserted_id},
{
'$set': {
'api_topup_id': result['topup_id'],
'api_topup_ref': result['topup_ref'],
'status': 'PROCESSING'
}
}
)
transaction.commit()
return {
'order_id': str(order.inserted_id),
'topup_id': result['topup_id'],
'ref': ref
}
except Exception as error:
transaction.rollback()
raise
<?php
function createTopUpOrder($userId, $planCode, $phone, $amount, $db) {
$db->beginTransaction();
try {
// 1. Generate reference
$ref = generateOrderRef($userId);
// 2. Get plan details
$plan = $db->query(
"SELECT * FROM mobile_plans WHERE code = ? FOR UPDATE",
[$planCode]
)->fetch(PDO::FETCH_ASSOC);
$cost = $amount * $plan['wholesale_cost'];
// 3. Check user balance
$user = $db->query(
"SELECT * FROM users WHERE id = ? FOR UPDATE",
[$userId]
)->fetch(PDO::FETCH_ASSOC);
if ($user['balance'] < $cost) {
$db->rollBack();
throw new Exception('Insufficient user balance');
}
// 4. Create order record
$db->query(
"INSERT INTO orders (user_id, ref, plan_code, phone, amount, cost, status, created_at)
VALUES (?, ?, ?, ?, ?, ?, 'PENDING', NOW())",
[$userId, $ref, $planCode, $phone, $amount, $cost]
);
$orderId = $db->lastInsertId();
// 5. Deduct user balance
$db->query(
"UPDATE users SET balance = balance - ? WHERE id = ?",
[$cost, $userId]
);
// 6. Send to API
$result = sendTopUp($planCode, $phone, $amount, $ref);
// 7. Update order with API response
$db->query(
"UPDATE orders
SET api_topup_id = ?, api_topup_ref = ?, status = 'PROCESSING'
WHERE id = ?",
[$result['topup_id'], $result['topup_ref'], $orderId]
);
$db->commit();
return [
'order_id' => $orderId,
'topup_id' => $result['topup_id'],
'ref' => $ref
];
} catch (Exception $error) {
$db->rollBack();
throw $error;
}
}
المعالجة غير المتزامنة
للحصول على أداء أفضل، عالج الشحنات بشكل غير متزامن:const Queue = require('bull');
const topupQueue = new Queue('topup-processing');
// Add job to queue
async function queueTopUp(orderData) {
const order = await db.orders.create({
...orderData,
status: 'QUEUED'
});
await topupQueue.add({
orderId: order.id
});
return order;
}
// Process queue
topupQueue.process(async (job) => {
const { orderId } = job.data;
const order = await db.orders.findOne({ where: { id: orderId } });
try {
// Send top-up
const result = await sendTopUp({
planCode: order.planCode,
phone: order.phone,
amount: order.amount,
ref: order.ref
});
// Update order
await db.orders.update({
apiTopupId: result.topupId,
status: 'PROCESSING'
}, {
where: { id: orderId }
});
// Start status polling (another queue)
await pollQueue.add({ topupId: result.topupId, orderId });
} catch (error) {
// Handle error
await db.orders.update({
status: 'FAILED',
errorMessage: error.message
}, {
where: { id: orderId }
});
// Refund user
await refundUser(order.userId, order.cost);
}
});
from celery import Celery
app = Celery('topup', broker='redis://localhost:6379/0')
@app.task
def process_topup(order_id):
"""Process top-up asynchronously"""
order = db.orders.find_one({'_id': order_id})
try:
# Send top-up
result = send_topup(
order['plan_code'],
order['phone'],
order['amount'],
order['ref']
)
# Update order
db.orders.update_one(
{'_id': order_id},
{
'$set': {
'api_topup_id': result['topup_id'],
'status': 'PROCESSING'
}
}
)
# Start status polling
poll_topup_status.delay(result['topup_id'], order_id)
except Exception as error:
# Handle error
db.orders.update_one(
{'_id': order_id},
{
'$set': {
'status': 'FAILED',
'error_message': str(error)
}
}
)
# Refund user
refund_user(order['user_id'], order['cost'])
# Queue top-up
def queue_topup(order_data):
order = db.orders.insert_one({
**order_data,
'status': 'QUEUED'
})
process_topup.delay(str(order.inserted_id))
return order
منطق إعادة المحاولة
طبّق إعادة المحاولة لأخطاء الشبكة:async function sendTopUpWithRetry(data, maxRetries = 3) {
let lastError;
for (let attempt = 1; attempt <= maxRetries; attempt++) {
try {
return await sendTopUp(data);
} catch (error) {
lastError = error;
// Don't retry on validation or duplicate errors
if (error.code === 'ERR_VALIDATION' || error.code === 'DUPLICATED_REF') {
throw error;
}
// Wait before retry (exponential backoff)
if (attempt < maxRetries) {
const delay = Math.pow(2, attempt) * 1000; // 2s, 4s, 8s
console.log(`Retry ${attempt}/${maxRetries} after ${delay}ms...`);
await new Promise(resolve => setTimeout(resolve, delay));
}
}
}
throw lastError;
}
import time
def send_topup_with_retry(data, max_retries=3):
"""Send top-up with retry logic"""
last_error = None
for attempt in range(1, max_retries + 1):
try:
return send_topup(**data)
except Exception as error:
last_error = error
# Don't retry on validation or duplicate errors
error_msg = str(error)
if 'Validation' in error_msg or 'Duplicate' in error_msg:
raise
# Wait before retry (exponential backoff)
if attempt < max_retries:
delay = 2 ** attempt # 2s, 4s, 8s
print(f'Retry {attempt}/{max_retries} after {delay}s...')
time.sleep(delay)
raise last_error
<?php
function sendTopUpWithRetry($data, $maxRetries = 3) {
$lastError = null;
for ($attempt = 1; $attempt <= $maxRetries; $attempt++) {
try {
return sendTopUp(
$data['plan_code'],
$data['phone'],
$data['amount'],
$data['ref']
);
} catch (Exception $error) {
$lastError = $error;
// Don't retry on validation or duplicate errors
$errorMsg = $error->getMessage();
if (strpos($errorMsg, 'Validation') !== false ||
strpos($errorMsg, 'Duplicate') !== false) {
throw $error;
}
// Wait before retry (exponential backoff)
if ($attempt < $maxRetries) {
$delay = pow(2, $attempt); // 2s, 4s, 8s
echo "Retry {$attempt}/{$maxRetries} after {$delay}s...\n";
sleep($delay);
}
}
}
throw $lastError;
}
أفضل الممارسات
استخدام مراجع فريدة دائماً
استخدام مراجع فريدة دائماً
أنشئ
ref فريداً لكل طلب لمنع التكرار وتمكين التتبع.التحقق من الرصيد مسبقاً
التحقق من الرصيد مسبقاً
تحقق من كفاية الرصيد قبل الإرسال لتجنب الطلبات الفاشلة.
استخدام معاملات قاعدة البيانات
استخدام معاملات قاعدة البيانات
غلّف إنشاء الطلب وخصم الرصيد في معاملات لضمان اتساق البيانات.
المعالجة غير المتزامنة
المعالجة غير المتزامنة
استخدم قوائم الانتظار للحصول على أداء أفضل وتجربة مستخدم محسّنة.
تطبيق منطق إعادة المحاولة
تطبيق منطق إعادة المحاولة
أعد المحاولة عند أخطاء الشبكة مع التراجع الأسي، لكن لا تُعد المحاولة عند أخطاء التحقق من الصحة.
تسجيل كل شيء
تسجيل كل شيء
سجّل جميع الطلبات والردود لأغراض التنقيح والتدقيق.
الخطوات التالية
الخطوة 4: استطلاع الحالة
تعلّم كيفية استطلاع وتتبع حالة الشحن
API التحقق بالمرجع
توثيق API لـ GET /v3/mobile/check-ref/:ref
API التحقق بالمعرّف
توثيق API لـ GET /v3/mobile/check-id/:id
مرجع معالجة الأخطاء
دليل معالجة الأخطاء والتعافي منها
إعداد Webhooks
إعداد إشعارات الحالة في الوقت الفعلي
استراتيجيات الاستطلاع
تحسين تطبيق الاستطلاع الخاص بك
API سجل المعاملات
اطلع على سجل معاملاتك
API الرصيد
endpoint التحقق من رصيد الحساب

