Production-grade backend API server for an eSIM (embedded SIM) digital marketplace. Built with Flask, PostgreSQL, Redis, and Celery, serving a multi-platform eSIM store with wallet payments, real-time updates, and a full admin panel.
- Overview
- Architecture
- Tech Stack
- Features
- Quick Start
- Configuration
- API Reference
- Provider Integrations
- Authentication & Security
- Database Schema
- Deployment
- Project Structure
- Contributing
- License
eSIM Ego Server is the backend for a complete eSIM marketplace platform. Customers can browse data plans, purchase them via local payment methods (ZainCash, FIB, QiCard), and have eSIM profiles activated on their devices instantly.
The system supports:
- Dual-mode inventory — pre-purchased ICCIDs (bulk) and real-time on-demand activation
- Wallet system — user balances, deposits, freezes, and transactions
- Multi-currency — IQD, USD, and dynamic exchange rates
- Multi-language — 12 languages including Arabic, Kurdish, Turkish, Persian, and English
- Admin panel API — full CRUD for users, orders, plans, inventory, coupons, taxes, refunds, and more
- Real-time notifications — WebSocket-powered order and wallet updates
┌──────────────┐ ┌──────────────────┐ ┌─────────────────┐
│ Flutter App │────▶│ eSIM Ego Server │────▶│ PostgreSQL │
│ (Dashboard) │ │ (Flask API) │ │ │
└──────────────┘ │ │ └─────────────────┘
│ ┌─────────────┐ │ ┌─────────────────┐
┌──────────────┐ │ │ Celery │◀┼────▶│ Redis │
│ Web Client │────▶│ │ (Workers) │ │ │ (Cache/Queue) │
└──────────────┘ │ └─────────────┘ │ └─────────────────┘
│ │
┌──────────────┐ │ ┌─────────────┐ │ ┌─────────────────┐
│ Providers │◀────┼──│ SocketIO │ │ │ Firebase │
│ (eSIM/SMS/ │ │ │ (Realtime) │ │ │ Cloud Msging │
│ Payment) │────▶│ └─────────────┘ │ └─────────────────┘
└──────────────┘ └──────────────────┘
Request flow:
- Client authenticates via OTP/phone, receives JWT tokens
- All subsequent requests include
Authorization: Bearer <access_token> - Middleware validates token, enforces rate limits, checks IP whitelist (admin)
- Route handler processes business logic via service layer
- Response returned in unified format with automatic translation
| Category | Technology | Purpose |
|---|---|---|
| Framework | Flask 3.1 | Web server & API |
| Database | PostgreSQL 16 | Primary data store |
| Cache & Queue | Redis 7 | Token blacklist, rate limits, Celery broker, SocketIO MQ |
| ORM | SQLAlchemy 2.0 | Database abstraction |
| Migrations | Alembic 1.15 | Schema versioning (18 migrations) |
| Task Queue | Celery 5.5 | Async background jobs |
| Real-time | Flask-SocketIO | WebSocket events |
| Auth | JWT (HS256) + bcrypt + TOTP | Access/refresh tokens, 2FA |
| Validation | Pydantic 2.11 | Request/response validation |
| Rate Limiting | Flask-Limiter 3.10 | Redis-backed |
| Push | Firebase Cloud Messaging | Push notifications |
| WSGI | Gunicorn + gevent | Production server |
| fpdf2 | Invoices & reports | |
| Excel | openpyxl | Report export |
- Phone-based OTP registration/login
- Browse data plans with caching
- Create and pay for orders (wallet, ZainCash, FIB, QiCard)
- View order history and eSIM installation details
- Wallet: balance, deposits, transactions
- Support tickets with messaging
- Referral program with rewards
- Push notifications
- Dashboard — revenue, orders, users, growth analytics
- Orders — list, filter, approve, cancel, reprocess
- Users — list, search, ban/unban, role management, wallet adjust
- Plans — CRUD, sort order, toggle active
- Inventory — ICCID management, bulk import, stats
- Finance — exchange rates, tax rates, coupons, refunds
- Support — ticket management, assign, close
- Reports — financial reports (PDF/XLSX)
- Backups — create, list, download, cleanup
- Audit Log — full admin action trail
- System Settings — dynamic config management
- 2FA — TOTP two-factor authentication
- Referral — reward stats, settings
- Multi-language (12 languages)
- Multi-currency with auto-fetch exchange rates
- Dual inventory mode (hybrid / inventory-only / on-demand-only)
- Idempotency guarantees for payments
- Rate limiting per endpoint group
- Comprehensive audit trail
- Provider plugin system (drop-in new providers)
git clone https://github.com/omermask/esim-ego-server.git
cd esim-ego-server
cp .env.example .env
# Edit .env with your secrets
docker compose up -dThis starts PostgreSQL, Redis, the Flask API (with auto-migration), Celery worker, and Celery beat — all with a single command.
- Python 3.11+
- PostgreSQL 16+
- Redis 7+
git clone https://github.com/omermask/esim-ego-server.git
cd esim-ego-server
python3 -m venv venv
source venv/bin/activate
pip install -r requirements.txtcp .env.example .env
# Edit .env with your credentials (database, Redis, secrets)# Create PostgreSQL database
createdb esim_ego
# Run migrations
alembic upgrade head# Development
python run.py
# Production
gunicorn -c gunicorn.conf.py 'app:create_app()'curl http://localhost:5000/health
# {"status":"healthy","database":"connected","redis":"connected"}Key environment variables (see .env.example for full list):
| Variable | Required | Default | Description |
|---|---|---|---|
ENVIRONMENT |
No | development |
development, staging, or production |
SECRET_KEY |
Yes | — | Min 32 characters |
API_KEYS_ENCRYPTION_KEY |
Yes | — | Min 16 characters |
DB_HOST |
Yes | localhost |
PostgreSQL host |
DB_NAME |
Yes | esim_ego |
Database name |
REDIS_HOST |
No | localhost |
Redis host |
JWT_ACCESS_TOKEN_EXPIRY_MINUTES |
No | 15 |
Access token TTL |
JWT_REFRESH_TOKEN_EXPIRY_DAYS |
No | 7 |
Refresh token TTL |
CORS_ORIGINS |
No | * |
Comma-separated origins |
PURCHASE_MODE |
No | hybrid |
hybrid, inventory_only, on_demand_only |
ADMIN_IP_WHITELIST |
No | — | CIDR ranges for admin endpoints |
| Endpoint Group | Limit |
|---|---|
Auth (/auth/*) |
5 requests/min |
| General API | 30 requests/min |
| Admin endpoints | 60 requests/min |
All endpoints are prefixed with /api/v1.
| Method | Path | Description |
|---|---|---|
| POST | /auth/register |
Register with phone number |
| POST | /auth/login |
Request OTP |
| POST | /auth/verify-otp |
Verify OTP, get tokens |
| POST | /auth/refresh |
Refresh access token |
| POST | /auth/logout |
Revoke tokens |
| Method | Path | Description |
|---|---|---|
| GET | /plans |
List active plans (cached 300s) |
| GET | /plans/{id} |
Plan details |
| Method | Path | Description |
|---|---|---|
| POST | /orders |
Create order |
| GET | /orders |
List user orders |
| GET | /orders/{id} |
Order details |
| Method | Path | Description |
|---|---|---|
| GET | /wallet |
Get balance |
| GET | /wallet/transactions |
Transaction history |
| POST | /wallet/deposit |
Request deposit |
| Method | Path | Description |
|---|---|---|
| GET | /payments/{id} |
Payment status |
| POST | /payments/deposit/confirm |
Confirm deposit |
| POST | /payments/{id}/cancel |
Cancel payment |
| Method | Path | Description |
|---|---|---|
| GET | /admin/users |
List users (paginated) |
| GET | /admin/users/{id} |
User details |
| PUT | /admin/users/{id}/role |
Change role |
| PUT | /admin/users/{id}/ban |
Ban/unban user |
| Method | Path | Description |
|---|---|---|
| GET | /admin/orders |
List orders (filtered) |
| GET | /admin/orders/{id} |
Order details |
| PUT | /admin/orders/{id}/status |
Update status |
| POST | /admin/orders/{id}/cancel |
Cancel order |
| POST | /admin/orders/{id}/reprocess |
Reprocess failed |
| Method | Path | Description |
|---|---|---|
| POST | /admin/plans |
Create plan |
| PUT | /admin/plans/{id} |
Update plan |
| DELETE | /admin/plans/{id} |
Delete plan |
| PUT | /admin/plans/{id}/toggle-active |
Toggle active |
| PUT | /admin/plans/sort-order |
Reorder plans |
| Method | Path | Description |
|---|---|---|
| GET/POST | /admin/exchange-rates |
List/create rates |
| PUT | /admin/exchange-rates/{id} |
Update rate |
| GET/POST | /admin/tax-rates |
List/create tax rates |
| GET/POST/DELETE | /admin/coupons |
CRUD coupons |
| POST | /admin/refund |
Process refund |
| GET | /admin/wallet/adjust |
Adjust user wallet |
| Method | Path | Description |
|---|---|---|
| GET | /admin/analytics/dashboard |
Dashboard stats |
| GET | /admin/analytics/revenue |
Revenue analytics |
| GET | /admin/analytics/orders |
Order analytics |
| GET | /admin/analytics/users |
User analytics |
| GET | /admin/reports/financial |
Financial report (PDF/XLSX) |
| Method | Path | Description |
|---|---|---|
| GET | /admin/support/tickets |
List tickets |
| PUT | /admin/support/tickets/{id}/status |
Open/close ticket |
| POST | /admin/support/tickets/{id}/assign |
Assign to admin |
| Method | Path | Description |
|---|---|---|
| POST | /admin/backup/create |
Create backup |
| GET | /admin/backup/list |
List backups |
| GET | /admin/backup/download/{id} |
Download backup |
| DELETE | /admin/backup/{id} |
Delete backup |
| Method | Path | Description |
|---|---|---|
| GET | /admin/inventory |
List inventory |
| POST | /admin/inventory/import |
Bulk import ICCIDs |
| POST | /admin/inventory/activate |
Activate specific ICCID |
| GET | /admin/inventory/stats |
Inventory statistics |
| Method | Path | Description |
|---|---|---|
| GET/POST/PUT/DELETE | /admin/settings |
System settings CRUD |
| GET | /admin/referral/stats |
Referral statistics |
| PUT | /admin/referral/settings |
Update referral config |
| Method | Path | Source | Security |
|---|---|---|---|
| POST | /esim/callback |
eSIM provider | HMAC signature |
| POST | /callback/otpiq |
OTPIQ SMS | IP restrict + HMAC |
| Event | Direction | Description |
|---|---|---|
connect |
Client→Server | Auth via Authorization header |
order_update |
Server→Client | Real-time order status changes |
wallet_update |
Server→Client | Real-time balance changes |
push |
Server→Client | Push notification delivery |
The system is built with a fully decoupled plugin architecture. The core code never references any specific provider by name — it discovers them automatically at runtime. This means any developer can add a new provider (eSIM, SMS, or payment) by simply creating a single Python file in the right directory, without modifying any core code.
app/providers/
├── base.py # Abstract base classes (ESIMProviderBase, SMSProviderBase, PaymentProviderBase)
├── registry.py # Auto-discovery: scans directories, registers decorated classes
├── esim/
│ └── your_provider.py # ← Drop a new file here, it's auto-loaded
├── payment/
│ └── your_provider.py # ← Drop a new file here, it's auto-loaded
└── sms/
└── your_provider.py # ← Drop a new file here, it's auto-loaded
To add a new provider, you only need to:
- Create a new Python file in the appropriate folder (e.g.,
app/providers/esim/my_provider.py) - Create a class that inherits from the base class (e.g.,
ESIMProviderBase) - Decorate it with the registration decorator (e.g.,
@esim_provider("my_provider")) - Add your API credentials to
.env— done.
No modifications to routes, services, models, or configuration files are needed. The registry (app/providers/registry.py) uses pkgutil.iter_modules to scan for new files on startup and registers them automatically.
| Provider | File | Status | Features |
|---|---|---|---|
| eSIMGo | app/providers/esim/esimgo.py |
✅ Active | Catalogue, order, bundle activation, install details, usage callbacks |
To add a new eSIM provider: create app/providers/esim/your_company.py, extend ESIMProviderBase, add @esim_provider("your_company").
| Provider | File | Status | Features |
|---|---|---|---|
| BulkSMSIraq | app/providers/sms/bulksmsiraq.py |
✅ Active | OTP, SMS, balance check, 28 error codes, sandbox |
| OTPIQ | app/providers/sms/otpiq.py |
✅ Active | OTP, webhook callbacks, HMAC signing |
To add a new SMS provider: create app/providers/sms/your_company.py, extend SMSProviderBase, add @sms_provider("your_company").
| Provider | File | Status | Features |
|---|---|---|---|
| ZainCash | app/providers/payment/zaincash.py |
✅ Active | Iraqi mobile wallet, JWT-signed, UAT/Production |
| FIB | app/providers/payment/fib.py |
✅ Active | Fast Iraq Bank, OAuth2 client credentials |
| QiCard (SuperQI) | app/providers/payment/superqi.py |
✅ Active | Credit/debit card, full state machine, webhooks, refunds |
To add a new payment provider: create app/providers/payment/your_company.py, extend PaymentProviderBase, add @payment_provider("your_company").
| Base Class | Required Methods |
|---|---|
ESIMProviderBase |
activate_bundle(), get_bundle_status(), create_order(), get_install_details(), get_catalogue(), handle_usage_callback() |
SMSProviderBase |
send_otp(), send_sms(), verify_otp(), get_balance() |
PaymentProviderBase |
initiate_payment(), verify_webhook() |
Once registered, the provider is selected dynamically at runtime via the admin settings:
ALLOWED_SMS_PROVIDERS=["bulksmsiraq","otpiq","your_new_provider"]
ALLOWED_ESIM_PROVIDERS=["esimgo","your_new_provider"]
ALLOWED_PAYMENT_PROVIDERS=["zaincash","qicard","fib","your_new_provider"]
Provider credentials are securely encrypted at rest using API_KEYS_ENCRYPTION_KEY and stored in the database. Each provider's API calls are fully logged to EsimProviderTransaction / SMSProviderTransaction for audit and debugging.
┌─────────┐ ┌───────────┐ ┌──────────┐
│ Phone │────▶│ OTP Verify│────▶│ Tokens │
│ Login │ │ (3 tries)│ │ Access │
└─────────┘ └───────────┘ │ (15 min) │
│ Refresh │
│ (7 days) │
└──────────┘
- bcrypt password hashing (12 rounds)
- JWT HS256 dual token (access + refresh)
- TOTP 2FA via pyotp (optional, admin-only)
- Token blacklisting via Redis
- Token versioning — logout-all instantly invalidates all sessions
- Rate limiting — per-endpoint-group
- IP whitelist — CIDR-based admin access control
- Idempotency — prevents duplicate payments
- Security headers —
X-Content-Type-Options,X-Frame-Options, no Server header - HMAC verification — callback webhook signing
The database consists of 25+ tables managed through 18 Alembic migrations.
Core tables:
users,otp_codes,device_sessions,device_tokensplans,orders,order_items,paymentswallets,wallet_transactions,wallet_freezescoupons,coupon_usages,tax_rates,exchange_ratesesim_inventory,import_batches,esim_provider_transactionssupport_tickets,support_messagesrefunds,referral_rewardsaudit_logs,backup_records,system_settingsnotification_templates,idempotency_records
Run alembic upgrade head to apply all migrations.
docker compose up -dStarts PostgreSQL, Redis, Flask API, Celery worker, and Celery beat. Configure via .env.
gunicorn -c gunicorn.conf.py 'app:create_app()'Default config: 4 gevent workers, 0.0.0.0:5000, 30s timeout.
- PostgreSQL 16+
- Redis 7+
- Celery worker (for background tasks)
celery -A app.celery_app worker --loglevel=info --concurrency=2
celery -A app.celery_app beat --loglevel=info| Check | Why |
|---|---|
SECRET_KEY set |
JWT signing, session security |
API_KEYS_ENCRYPTION_KEY set |
Provider credential encryption |
| Database created + migrated | Data persistence |
| Redis running | Rate limiting, token blacklist, Celery |
.env excluded from git |
Never commit secrets |
esim-ego-server/
├── app/
│ ├── __init__.py # Flask app factory, blueprint registration
│ ├── celery_app.py # Celery application instance
│ ├── socketio.py # WebSocket events
│ ├── core/
│ │ ├── constants.py # App-wide constants
│ │ ├── database.py # SQLAlchemy engine + session
│ │ ├── errors.py # Custom error classes
│ │ ├── i18n.py # Translation manager
│ │ ├── middleware.py # Request/response middleware
│ │ ├── response.py # Unified response format
│ │ ├── security.py # JWT, bcrypt, TOTP, rate limiting
│ │ └── validators.py # Pydantic request schemas
│ ├── models/ # SQLAlchemy models (21 files)
│ ├── routes/ # API route blueprints (17 files)
│ ├── services/ # Business logic layer (25 files)
│ ├── providers/ # Plugin-based integrations
│ │ ├── esim/ # eSIM activation providers
│ │ ├── payment/ # Payment gateway providers
│ │ └── sms/ # SMS/OTP providers
│ ├── tasks/ # Celery background tasks
│ └── translations/ # 12 language JSON files
├── alembic/ # Database migrations (18 versions)
├── scripts/ # Utility scripts
├── config.py # Pydantic settings model
├── Dockerfile # Production Docker image
├── docker-compose.yml # Multi-service orchestration
├── run.py # Entry point (dev/prod)
├── gunicorn.conf.py # Production server config
├── requirements.txt # Python dependencies
├── .env.example # Environment template
├── CONTRIBUTING.md # Contribution guidelines
├── CODE_OF_CONDUCT.md # Community standards
├── SECURITY.md # Security policy
└── .github/ # Issue templates & CI workflows
| Pattern | Description |
|---|---|
| Service layer | All business logic in app/services/, routes are thin |
| Provider plugins | Auto-discovered via decorators, zero config for new providers |
| Unified response | All endpoints return {success, status, code, message, data, meta} |
| Auto-translation | Error messages translated via Accept-Language header |
| Pagination | All list endpoints support page, per_page (max 100) |
Please read our Contributing Guide and Code of Conduct before submitting changes.
Quick overview:
- Fork the repository
- Create a feature branch (
git checkout -b feature/amazing-feature) - Commit your changes (
git commit -m 'feat: add amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
- Write tests for new services
- Follow existing provider plugin pattern for new integrations
- Add translation keys for user-facing messages
- Run
alembic upgrade headafter pulling new migrations - Format with
blackand lint withflake8
You can contribute by building frontend applications (mobile or web) that connect to this API. The server is fully API-driven — any client can consume it:
- Admin Dashboard — A Flutter admin panel. Contribute new screens, widgets, or features.
- User App — Build a customer-facing app (Flutter, React Native, or web) using the public endpoints (
/auth,/plans,/orders,/wallet, etc.) - New Admin Features — Add new admin screens or improve existing ones in the Flutter dashboard
The API is fully documented in API.md with all endpoints, request/response formats, and authentication details. Any HTTP client can integrate.
See SECURITY.md for our responsible disclosure policy.
This project is licensed under the MIT License. See LICENSE for details.
omer jasim — oj33593@gmail.com
- Issues: GitHub Issues
- Documentation: See API.md for endpoint details
- Admin Dashboard: Available at esim-ego-dashboard (Flutter)