Vue d’ensemble
Une fois toutes les entrées validées, vous êtes prêt à envoyer la demande de recharge à l’API OneClickDz. Ce guide couvre la soumission des demandes, la gestion des réponses et la gestion de votre système de références.Référence API
Consultez la documentation API complète pour l’endpoint POST /v3/mobile/send
Demande de recharge de base
Exemple simple
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";
}
Réponse en cas de succès
{
"success": true,
"data": {
"topupId": "6901616fe9e88196b4eb64b0",
"topupRef": "order-1730462400-user123"
},
"meta": {
"timestamp": "2025-11-01T11:00:00.454Z"
},
"requestId": "req_1730462400_abc123"
}
Générer des références uniques
Fournissez toujours des références uniques pour éviter les doublons et permettre le suivi :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
Vérifier le solde avant l’envoi
Vérifiez toujours que le solde est suffisant pour éviter les demandes échouées :Référence API du solde
En savoir plus sur l’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);
}
Gestion des erreurs
Gérez les erreurs API courantes :Consultez la Référence de gestion des erreurs pour les codes d’erreur complets et les stratégies de traitement.
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;
}
}
Intégration avec la base de données
Enregistrez les commandes avant et après les appels 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;
}
}
Traitement asynchrone
Pour de meilleures performances, traitez les recharges de manière asynchrone :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
Logique de nouvelle tentative
Implémentez des nouvelles tentatives pour les erreurs réseau :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;
}
Bonnes pratiques
Toujours utiliser des références uniques
Toujours utiliser des références uniques
Générez un
ref unique pour chaque demande afin d’éviter les doublons et permettre le suivi.Vérifier le solde au préalable
Vérifier le solde au préalable
Vérifiez que le solde est suffisant avant d’envoyer la demande pour éviter les échecs.
Utiliser les transactions de base de données
Utiliser les transactions de base de données
Encapsulez la création de commande et la déduction de solde dans des transactions pour la cohérence des données.
Traiter de manière asynchrone
Traiter de manière asynchrone
Utilisez des files d’attente pour de meilleures performances et une meilleure expérience utilisateur.
Implémenter une logique de nouvelle tentative
Implémenter une logique de nouvelle tentative
Réessayez les erreurs réseau avec un backoff exponentiel, mais pas les erreurs de validation.
Tout journaliser
Tout journaliser
Journalisez toutes les demandes et réponses à des fins de débogage et d’audit.
Étapes suivantes
Étape 4 : Interrogation du statut
Apprenez à interroger et suivre le statut des recharges
API de vérification par référence
Documentation API pour GET /v3/mobile/check-ref/:ref
API de vérification par ID
Documentation API pour GET /v3/mobile/check-id/:id
Référence de gestion des erreurs
Guide complet de gestion et de récupération des erreurs
Configuration des Webhooks
Configurez des notifications de statut en temps réel
Stratégies d'interrogation
Optimisez votre implémentation d’interrogation
API Historique des transactions
Consultez l’historique de vos transactions
API de solde
Endpoint de vérification du solde du compte

