نظرة عامة
الخطوة الأولى في دمج شحنات الهاتف المحمول هي جلب قائمة الخطط المتاحة من API. تشمل الخطط جميع مشغلي الشبكات (Mobilis، Djezzy، Ooredoo، Pixx) مع خيارات المبالغ الديناميكية والثابتة.الخطط مستقرة ونادراً ما تتغير. احفظها في قاعدة بياناتك لتقليل استدعاءات API وتحسين الأداء.
مرجع API
اطلع على توثيق API الكامل لـ endpoint GET /v3/mobile/plans
فهم أنواع الخطط
الخطط الديناميكية
تتيح الخطط الديناميكية مبالغ متغيرة ضمن نطاق min/max:{
"code": "PREPAID_DJEZZY",
"name": "Djezzy Prepaid",
"operator": "Djezzy",
"cost": 0.98,
"isEnabled": true,
"min_amount": 50,
"max_amount": 5000
}
- شحن الخطوط المدفوعة مسبقاً (رصيد)
- دفع فواتير الخطوط المؤجلة (facture)
- رصيد المكالمات الدولية
الخطط الثابتة
الخطط الثابتة لها مبالغ محددة مسبقاً:{
"code": "PIXX_500",
"name": "Pixx 500 DA",
"operator": "Pixx",
"cost": 0.97,
"isEnabled": true,
"amount": 500
}
- العروض الترويجية الخاصة
- باقات الإنترنت
- تفعيل الخدمات
خطط GetMenu
خطط خاصة تسترجع العروض المتاحة لرقم هاتف محدد:{
"code": "GETMENU_Mobilis",
"name": "Mobilis GetMenu",
"operator": "Mobilis",
"cost": 0.98,
"isEnabled": true,
"min_amount": 50,
"max_amount": 5000
}
suggested_offers تحتوي على جميع الخطط المتاحة لذلك الرقم تحديداً.جلب الخطط من API
طلب أساسي
curl https://api.oneclickdz.com/v3/mobile/plans \
-H "X-Access-Token: YOUR_API_KEY"
const fetch = require('node-fetch');
async function fetchMobilePlans() {
const response = await fetch('https://api.oneclickdz.com/v3/mobile/plans', {
headers: {
'X-Access-Token': process.env.ONECLICKDZ_API_KEY
}
});
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const data = await response.json();
if (!data.success) {
throw new Error('Failed to fetch plans');
}
return {
dynamicPlans: data.data.dynamicPlans,
fixedPlans: data.data.fixedPlans
};
}
// Usage
const { dynamicPlans, fixedPlans } = await fetchMobilePlans();
console.log(`Loaded ${dynamicPlans.length} dynamic plans and ${fixedPlans.length} fixed plans`);
import requests
import os
def fetch_mobile_plans():
"""Fetch mobile plans from API"""
response = requests.get(
'https://api.oneclickdz.com/v3/mobile/plans',
headers={'X-Access-Token': os.getenv('ONECLICKDZ_API_KEY')}
)
response.raise_for_status()
data = response.json()
if not data['success']:
raise Exception('Failed to fetch plans')
return {
'dynamic_plans': data['data']['dynamicPlans'],
'fixed_plans': data['data']['fixedPlans']
}
# Usage
plans = fetch_mobile_plans()
print(f"Loaded {len(plans['dynamic_plans'])} dynamic and {len(plans['fixed_plans'])} fixed plans")
<?php
function fetchMobilePlans() {
$ch = curl_init('https://api.oneclickdz.com/v3/mobile/plans');
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
'X-Access-Token: ' . getenv('ONECLICKDZ_API_KEY')
]
]);
$response = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
if ($httpCode !== 200) {
throw new Exception("HTTP error! status: $httpCode");
}
$data = json_decode($response, true);
if (!$data['success']) {
throw new Exception('Failed to fetch plans');
}
return [
'dynamic_plans' => $data['data']['dynamicPlans'],
'fixed_plans' => $data['data']['fixedPlans']
];
}
// Usage
$plans = fetchMobilePlans();
echo "Loaded " . count($plans['dynamic_plans']) . " dynamic and " . count($plans['fixed_plans']) . " fixed plans\n";
بنية الاستجابة
{
"success": true,
"data": {
"dynamicPlans": [
{
"code": "PREPAID_MOBILIS",
"name": "📱 PREPAID | عادي",
"operator": "Mobilis",
"cost": 0.96,
"isEnabled": true,
"min_amount": 40,
"max_amount": 3999
},
{
"code": "GROS_MOBILIS",
"name": "📱 GROS MOBILIS | جملة",
"operator": "GMobilis",
"isEnabled": true,
"min_amount": 5000,
"max_amount": 300000
}
],
"fixedPlans": [
{
"code": "MIX500_MOBILIS",
"name": "📱🌐 AUTO | MIX 500",
"operator": "Mobilis",
"isEnabled": true,
"cost": 0.96,
"amount": 500
},
{
"code": "MIX1000_OOREDOO",
"name": "📱🌐 AUTO | MIX 1000",
"operator": "Ooredoo",
"isEnabled": true,
"cost": 0.99,
"amount": 1000
}
]
},
"meta": {
"timestamp": "2025-10-29T00:35:59.220Z"
}
}
فهم حقل Cost
يمثل حقلcost معامل سعر الجملة الخاص بك:- آلية العمل
- حساب سعر البيع
- ملاحظة مهمة
مثال: cost = 0.98لشحن بقيمة 1000 دج:
- القيمة الاسمية: 1000 دج (ما يستلمه المستخدم)
- تكلفتك: 1000 × 0.98 = 980 دج (ما تدفعه)
- هامش ربحك: 1000 - 980 = 20 دج (2%)
أضف هامش ربحك:
const faceValue = 1000;
const cost = 0.98;
const ourCost = faceValue * cost; // 980 DZD
// Add 5% profit margin
const markup = 0.05;
const sellPrice = ourCost * (1 + markup); // 1029 DZD
const profit = sellPrice - ourCost; // 49 DZD
لا تكشف أبداً عن حقل
cost للمستخدمين النهائيين!هذا هو تسعير الجملة الخاص بك. احرص دائماً على حساب وعرض أسعار التجزئة الخاصة بك.تخزين الخطط في قاعدة البيانات
بما أن الخطط نادراً ما تتغير، احفظها محلياً مع أسعارك الخاصة:مخطط قاعدة البيانات
CREATE TABLE mobile_plans (
id SERIAL PRIMARY KEY,
code VARCHAR(100) UNIQUE NOT NULL,
name VARCHAR(255) NOT NULL,
operator VARCHAR(50) NOT NULL,
type VARCHAR(20) NOT NULL, -- 'dynamic' or 'fixed'
-- Cost info
wholesale_cost DECIMAL(5,4) NOT NULL,
sell_price_multiplier DECIMAL(5,4) NOT NULL,
-- Amount limits
min_amount INTEGER,
max_amount INTEGER,
fixed_amount INTEGER,
-- Status
is_enabled BOOLEAN DEFAULT true,
is_visible BOOLEAN DEFAULT true,
-- Metadata
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
last_synced_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
CREATE INDEX idx_mobile_plans_operator ON mobile_plans(operator);
CREATE INDEX idx_mobile_plans_enabled ON mobile_plans(is_enabled);
CREATE INDEX idx_mobile_plans_code ON mobile_plans(code);
const mobilePlanSchema = new mongoose.Schema({
code: { type: String, required: true, unique: true },
name: { type: String, required: true },
operator: { type: String, required: true, index: true },
type: { type: String, enum: ['dynamic', 'fixed'], required: true },
wholesaleCost: { type: Number, required: true },
sellPriceMultiplier: { type: Number, required: true },
minAmount: { type: Number },
maxAmount: { type: Number },
fixedAmount: { type: Number },
isEnabled: { type: Boolean, default: true, index: true },
isVisible: { type: Boolean, default: true },
lastSyncedAt: { type: Date, default: Date.now }
}, {
timestamps: true
});
const MobilePlan = mongoose.model('MobilePlan', mobilePlanSchema);
دالة المزامنة
const db = require('./database');
async function syncMobilePlans() {
try {
console.log('🔄 Syncing mobile plans...');
// Fetch plans from API
const plans = await fetchMobilePlans();
// Calculate your pricing
const PROFIT_MARGIN = 0.05; // 5% profit
for (const plan of plans) {
const isFixed = !!plan.amount;
const planData = {
code: plan.code,
name: plan.name,
operator: plan.operator,
type: isFixed ? 'fixed' : 'dynamic',
wholesaleCost: plan.cost,
sellPriceMultiplier: plan.cost * (1 + PROFIT_MARGIN),
minAmount: plan.min_amount || null,
maxAmount: plan.max_amount || null,
fixedAmount: plan.amount || null,
isEnabled: plan.isEnabled,
lastSyncedAt: new Date()
};
// Upsert (insert or update)
await db.mobilePlans.upsert({
where: { code: plan.code },
update: planData,
create: planData
});
}
console.log(`✅ Successfully synced ${plans.length} plans`);
// Log statistics
const stats = {
total: plans.length,
dynamic: plans.filter(p => !p.amount).length,
fixed: plans.filter(p => p.amount).length,
enabled: plans.filter(p => p.isEnabled).length
};
console.log('📊 Plan statistics:', stats);
return stats;
} catch (error) {
console.error('❌ Failed to sync plans:', error);
throw error;
}
}
// Run sync on startup
syncMobilePlans();
// Schedule daily sync at midnight
const cron = require('node-cron');
cron.schedule('0 0 * * *', () => {
console.log('⏰ Running scheduled plan sync...');
syncMobilePlans();
});
from datetime import datetime
import schedule
import time
def sync_mobile_plans():
"""Sync mobile plans to database"""
try:
print('🔄 Syncing mobile plans...')
# Fetch plans from API
plans = fetch_mobile_plans()
# Calculate your pricing
PROFIT_MARGIN = 0.05 # 5% profit
for plan in plans:
is_fixed = 'amount' in plan
plan_data = {
'code': plan['code'],
'name': plan['name'],
'operator': plan['operator'],
'type': 'fixed' if is_fixed else 'dynamic',
'wholesale_cost': plan['cost'],
'sell_price_multiplier': plan['cost'] * (1 + PROFIT_MARGIN),
'min_amount': plan.get('min_amount'),
'max_amount': plan.get('max_amount'),
'fixed_amount': plan.get('amount'),
'is_enabled': plan['isEnabled'],
'last_synced_at': datetime.now()
}
# Upsert to database
db.mobile_plans.upsert(
filter={'code': plan['code']},
data=plan_data
)
print(f'✅ Successfully synced {len(plans)} plans')
# Log statistics
stats = {
'total': len(plans),
'dynamic': len([p for p in plans if 'amount' not in p]),
'fixed': len([p for p in plans if 'amount' in p]),
'enabled': len([p for p in plans if p['isEnabled']])
}
print('📊 Plan statistics:', stats)
return stats
except Exception as error:
print(f'❌ Failed to sync plans: {error}')
raise
# Run sync on startup
sync_mobile_plans()
# Schedule daily sync at midnight
schedule.every().day.at("00:00").do(sync_mobile_plans)
# Keep scheduler running
while True:
schedule.run_pending()
time.sleep(60)
<?php
function syncMobilePlans($db) {
try {
echo "🔄 Syncing mobile plans...\n";
// Fetch plans from API
$plans = fetchMobilePlans();
// Calculate your pricing
$profitMargin = 0.05; // 5% profit
foreach ($plans as $plan) {
$isFixed = isset($plan['amount']);
$planData = [
'code' => $plan['code'],
'name' => $plan['name'],
'operator' => $plan['operator'],
'type' => $isFixed ? 'fixed' : 'dynamic',
'wholesale_cost' => $plan['cost'],
'sell_price_multiplier' => $plan['cost'] * (1 + $profitMargin),
'min_amount' => $plan['min_amount'] ?? null,
'max_amount' => $plan['max_amount'] ?? null,
'fixed_amount' => $plan['amount'] ?? null,
'is_enabled' => $plan['isEnabled'],
'last_synced_at' => date('Y-m-d H:i:s')
];
// Upsert to database
$db->query(
"INSERT INTO mobile_plans
(code, name, operator, type, wholesale_cost, sell_price_multiplier,
min_amount, max_amount, fixed_amount, is_enabled, last_synced_at)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
ON DUPLICATE KEY UPDATE
name = VALUES(name),
operator = VALUES(operator),
type = VALUES(type),
wholesale_cost = VALUES(wholesale_cost),
sell_price_multiplier = VALUES(sell_price_multiplier),
min_amount = VALUES(min_amount),
max_amount = VALUES(max_amount),
fixed_amount = VALUES(fixed_amount),
is_enabled = VALUES(is_enabled),
last_synced_at = VALUES(last_synced_at)",
array_values($planData)
);
}
echo "✅ Successfully synced " . count($plans) . " plans\n";
// Log statistics
$stats = [
'total' => count($plans),
'dynamic' => count(array_filter($plans, fn($p) => !isset($p['amount']))),
'fixed' => count(array_filter($plans, fn($p) => isset($p['amount']))),
'enabled' => count(array_filter($plans, fn($p) => $p['isEnabled']))
];
echo "📊 Plan statistics: " . json_encode($stats) . "\n";
return $stats;
} catch (Exception $error) {
echo "❌ Failed to sync plans: " . $error->getMessage() . "\n";
throw $error;
}
}
// Run sync on startup
syncMobilePlans($db);
// For scheduled execution, use cron:
// 0 0 * * * /usr/bin/php /path/to/sync_plans.php
عرض الخطط للمستخدمين
عند طلب المستخدمين للخطط، قدّمها من قاعدة بياناتك مع أسعارك الخاصة:async function getUserPlans(filters = {}) {
const { operator, type, enabled = true } = filters;
const query = { isEnabled: enabled, isVisible: true };
if (operator) query.operator = operator;
if (type) query.type = type;
const plans = await db.mobilePlans.findMany({
where: query,
orderBy: [
{ operator: 'asc' },
{ fixedAmount: 'asc' },
{ name: 'asc' }
]
});
// Transform for user display (remove wholesale cost!)
return plans.map(plan => ({
code: plan.code,
name: plan.name,
operator: plan.operator,
type: plan.type,
minAmount: plan.minAmount,
maxAmount: plan.maxAmount,
amount: plan.fixedAmount,
// Don't expose wholesale cost!
// Only expose what user needs
}));
}
// API endpoint
app.get('/api/mobile/plans', async (req, res) => {
try {
const { operator } = req.query;
const plans = await getUserPlans({ operator });
res.json({ success: true, data: { plans } });
} catch (error) {
res.status(500).json({
success: false,
error: error.message
});
}
});
def get_user_plans(operator=None, plan_type=None, enabled=True):
"""Get plans for user display"""
query = {
'is_enabled': enabled,
'is_visible': True
}
if operator:
query['operator'] = operator
if plan_type:
query['type'] = plan_type
plans = db.mobile_plans.find(query).sort([
('operator', 1),
('fixed_amount', 1),
('name', 1)
])
# Transform for user display (remove wholesale cost!)
return [{
'code': plan['code'],
'name': plan['name'],
'operator': plan['operator'],
'type': plan['type'],
'minAmount': plan.get('min_amount'),
'maxAmount': plan.get('max_amount'),
'amount': plan.get('fixed_amount')
# Don't expose wholesale cost!
} for plan in plans]
# Flask endpoint
@app.route('/api/mobile/plans', methods=['GET'])
def api_mobile_plans():
try:
operator = request.args.get('operator')
plans = get_user_plans(operator=operator)
return jsonify({'success': True, 'data': {'plans': plans}})
except Exception as error:
return jsonify({'success': False, 'error': str(error)}), 500
<?php
function getUserPlans($db, $operator = null, $type = null, $enabled = true) {
$conditions = ['is_enabled = ?', 'is_visible = ?'];
$params = [$enabled, true];
if ($operator) {
$conditions[] = 'operator = ?';
$params[] = $operator;
}
if ($type) {
$conditions[] = 'type = ?';
$params[] = $type;
}
$where = implode(' AND ', $conditions);
$query = "SELECT code, name, operator, type, min_amount, max_amount, fixed_amount
FROM mobile_plans
WHERE $where
ORDER BY operator, fixed_amount, name";
$plans = $db->query($query, $params)->fetchAll(PDO::FETCH_ASSOC);
// Transform for user display
return array_map(function($plan) {
return [
'code' => $plan['code'],
'name' => $plan['name'],
'operator' => $plan['operator'],
'type' => $plan['type'],
'minAmount' => $plan['min_amount'],
'maxAmount' => $plan['max_amount'],
'amount' => $plan['fixed_amount']
// Don't expose wholesale cost!
];
}, $plans);
}
// API endpoint
if ($_SERVER['REQUEST_METHOD'] === 'GET' && $_SERVER['REQUEST_URI'] === '/api/mobile/plans') {
try {
$operator = $_GET['operator'] ?? null;
$plans = getUserPlans($db, $operator);
header('Content-Type: application/json');
echo json_encode(['success' => true, 'data' => ['plans' => $plans]]);
} catch (Exception $error) {
http_response_code(500);
echo json_encode(['success' => false, 'error' => $error->getMessage()]);
}
}
تصفية الخطط
حسب المشغل
const mobilisPlans = await getUserPlans({ operator: 'Mobilis' });
const djezzyPlans = await getUserPlans({ operator: 'Djezzy' });
const ooredooPlans = await getUserPlans({ operator: 'Ooredoo' });
mobilis_plans = get_user_plans(operator='Mobilis')
djezzy_plans = get_user_plans(operator='Djezzy')
ooredoo_plans = get_user_plans(operator='Ooredoo')
$mobilisPlans = getUserPlans($db, 'Mobilis');
$djezzyPlans = getUserPlans($db, 'Djezzy');
$ooredooPlans = getUserPlans($db, 'Ooredoo');
حسب رقم الهاتف
function getOperatorFromPhone(phone) {
const prefixMap = {
'065': ['Mobilis'],
'066': ['Mobilis', 'Ooredoo'],
'067': ['Mobilis'],
'077': ['Djezzy', 'Mobilis'],
'078': ['Djezzy'],
'055': ['Ooredoo', 'Djezzy', 'Mobilis'],
'056': ['Ooredoo'],
'068': ['Pixx']
};
const prefix = phone.substring(0, 3);
return prefixMap[prefix] || [];
}
async function getPlansForPhone(phone) {
const operators = getOperatorFromPhone(phone);
if (operators.length === 0) {
throw new Error('Invalid phone number prefix');
}
const plans = await db.mobilePlans.findMany({
where: {
operator: { in: operators },
isEnabled: true,
isVisible: true
}
});
return plans;
}
def get_operator_from_phone(phone):
prefix_map = {
'065': ['Mobilis'],
'066': ['Mobilis', 'Ooredoo'],
'067': ['Mobilis'],
'077': ['Djezzy', 'Mobilis'],
'078': ['Djezzy'],
'055': ['Ooredoo', 'Djezzy', 'Mobilis'],
'056': ['Ooredoo'],
'068': ['Pixx']
}
prefix = phone[:3]
return prefix_map.get(prefix, [])
def get_plans_for_phone(phone):
operators = get_operator_from_phone(phone)
if not operators:
raise ValueError('Invalid phone number prefix')
plans = db.mobile_plans.find({
'operator': {'$in': operators},
'is_enabled': True,
'is_visible': True
})
return list(plans)
<?php
function getOperatorFromPhone($phone) {
$prefixMap = [
'065' => ['Mobilis'],
'066' => ['Mobilis', 'Ooredoo'],
'067' => ['Mobilis'],
'077' => ['Djezzy', 'Mobilis'],
'078' => ['Djezzy'],
'055' => ['Ooredoo', 'Djezzy', 'Mobilis'],
'056' => ['Ooredoo'],
'068' => ['Pixx']
];
$prefix = substr($phone, 0, 3);
return $prefixMap[$prefix] ?? [];
}
function getPlansForPhone($db, $phone) {
$operators = getOperatorFromPhone($phone);
if (empty($operators)) {
throw new Exception('Invalid phone number prefix');
}
$placeholders = implode(',', array_fill(0, count($operators), '?'));
$query = "SELECT * FROM mobile_plans
WHERE operator IN ($placeholders)
AND is_enabled = 1
AND is_visible = 1";
return $db->query($query, $operators)->fetchAll(PDO::FETCH_ASSOC);
}
أفضل الممارسات
التخزين المؤقت المحلي
التخزين المؤقت المحلي
احرص دائماً على تخزين الخطط في قاعدة بياناتك. الخطط نادراً ما تتغير، والتخزين المؤقت يحسّن الأداء بشكل ملحوظ.
إخفاء تكاليف الجملة
إخفاء تكاليف الجملة
لا تكشف أبداً عن حقل
cost للمستخدمين النهائيين. اعرض فقط أسعار التجزئة الخاصة بك.المزامنة المنتظمة
المزامنة المنتظمة
أنشئ مهمة cron يومية لمزامنة الخطط. هذا يضمن حصولك على أحدث العروض والتوافر.
التحقق من isEnabled
التحقق من isEnabled
تحقق دائماً من
isEnabled قبل عرض الخطط. الخطط المعطلة قد تكون غير متاحة مؤقتاً بسبب مشاكل المشغل.إضافة هامش الربح
إضافة هامش الربح
احسب سعر البيع بإضافة هامش ربح معقول (عادةً 3-10%) إلى تكلفة الجملة.
فهرسة قاعدة البيانات
فهرسة قاعدة البيانات
أنشئ فهارس على الحقول التي يُستعلم عنها بتكرار (operator، code، isEnabled) للبحث السريع.
الخطوات التالية
الخطوة 2: التحقق من الصحة
تعلّم كيفية التحقق من أرقام الهاتف ومدخلات المستخدم
مرجع API الخطط
توثيق API الكامل لـ GET /v3/mobile/plans
API رصيد الحساب
تعلّم كيفية التحقق من رصيد حسابك
دليل التسعير
فهم تسعير API وهيكل التكاليف
صيغة الاستجابة
تعرّف على هياكل استجابة API
مرجع النماذج
اطلع على جميع نماذج البيانات والمخططات

