first commit

This commit is contained in:
Hamza-Ayed
2026-05-23 16:17:20 +03:00
commit 2bbaa1ee16
195 changed files with 11126 additions and 0 deletions

289
docs/PERMISSIONS.md Normal file
View File

@@ -0,0 +1,289 @@
# Flash Call OTP System — Permissions Guide
## Table of Contents
1. [Android Caller App Permissions](#1-android-caller-app-permissions)
2. [Flutter Receiver App Permissions](#2-flutter-receiver-app-permissions)
3. [Server-Side Security](#3-server-side-security)
4. [iOS-Specific Notes](#4-ios-specific-notes)
5. [Permission Request Flow](#5-permission-request-flow)
---
## 1. Android Caller App Permissions
### CALL_PHONE
- **Permission String**: `android.permission.CALL_PHONE`
- **Protection Level**: Dangerous
- **Required Since**: API 1
- **Purpose**: Required to programmatically place phone calls without user interaction. The app uses `ACTION_CALL` with `Uri.parse("tel:$phone")` to automatically dial the target number and hang up after 2.5 seconds.
- **Why Not ACTION_DIAL**: `ACTION_DIAL` only opens the dialer and requires the user to tap the call button. Flash call verification must be completely automatic — no user interaction.
- **Android Version Notes**:
- API 26-27 (Android 8.0-8.1): Standard dangerous permission, requested at runtime
- API 28+ (Android 9+): Works as standard dangerous permission
- If denied: The app cannot make flash calls. The service will report "failed" for all call tasks.
### READ_PHONE_STATE
- **Permission String**: `android.permission.READ_PHONE_STATE`
- **Protection Level**: Dangerous
- **Required Since**: API 1
- **Purpose**: Required to read telephony state information including call state, network type, and SIM information. Used to detect when a call is active so the app knows when to hang up.
- **Android Version Notes**:
- API 26-28 (Android 8-9): Standard dangerous permission
- API 29+ (Android 10+): More restricted; the app only needs this for call state detection
- If denied: Call state detection may not work, but the 2.5-second timer still hangs up reliably
### SEND_SMS
- **Permission String**: `android.permission.SEND_SMS`
- **Protection Level**: Dangerous
- **Required Since**: API 1
- **Purpose**: Required to send SMS messages containing OTP codes to iOS users. When the backend has an SMS delivery task, the Caller App sends the OTP via SMS using `SmsManager`.
- **Why Needed**: iOS devices cannot receive flash calls for automatic OTP extraction. The system falls back to SMS delivery, and the Caller Android device acts as the SMS sender.
- **Android Version Notes**:
- API 26-30 (Android 8-11): Uses `SmsManager.getDefault()`
- API 31+ (Android 12+): Uses `context.getSystemService(SmsManager::class.java)` for per-subscription SMS handling
- If denied: SMS tasks will fail. Flash calls for Android devices still work.
### FOREGROUND_SERVICE
- **Permission String**: `android.permission.FOREGROUND_SERVICE`
- **Protection Level**: Normal
- **Required Since**: API 26 (Android 8.0)
- **Purpose**: Required to run a foreground service that continuously polls the backend for pending tasks. The service must be a foreground service (not a background service) to avoid being killed by the system.
- **Android Version Notes**:
- API 26+ (Android 8.0+): Mandatory for any long-running service
- Without this: The service would be killed within a few minutes by the system
### FOREGROUND_SERVICE_PHONE_CALL
- **Permission String**: `android.permission.FOREGROUND_SERVICE_PHONE_CALL`
- **Protection Level**: Normal
- **Required Since**: API 34 (Android 14)
- **Purpose**: Specifies the foreground service type as "phone call". Required on Android 14+ for services that place phone calls.
- **Android Version Notes**:
- API 34+ (Android 14): Must be declared in manifest AND specified in `startForeground()` call
- API 26-33: Not required, but harmless to declare
- Without this on Android 14: The foreground service will crash with SecurityException
### RECEIVE_BOOT_COMPLETED
- **Permission String**: `android.permission.RECEIVE_BOOT_COMPLETED`
- **Protection Level**: Normal
- **Required Since**: API 1
- **Purpose**: Allows the app to receive the `BOOT_COMPLETED` broadcast and automatically restart the CallerService after the device reboots. Without this, the user would have to manually open the app and start the service after every restart.
- **Android Version Notes**:
- All versions: Works as a normal permission (auto-granted)
- Note: Some OEMs (Xiaomi, Huawei, Oppo) restrict auto-start by default. Users must enable "Auto-start" in security settings.
### REQUEST_IGNORE_BATTERY_OPTIMIZATIONS
- **Permission String**: `android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS`
- **Protection Level**: Normal
- **Required Since**: API 23 (Android 6.0)
- **Purpose**: Prompts the user to add the app to the battery optimization whitelist. This is critical because Android's Doze mode and app standby can kill the foreground service, preventing the device from receiving OTP tasks.
- **How It Works**: The app sends an `ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS` intent with the app's package URI. The system shows a dialog asking the user to allow the app to ignore battery optimization.
- **Android Version Notes**:
- API 23+ (Android 6.0+): Works as normal permission with system dialog
- Some OEMs: May require additional steps (e.g., Xiaomi: Security → Autostart; Samsung: Device Care → Battery → App Power Management)
- Without this: The service may be killed after 10-30 minutes of screen-off time
### INTERNET
- **Permission String**: `android.permission.INTERNET`
- **Protection Level**: Normal
- **Required Since**: API 1
- **Purpose**: Required for all network communication — polling the backend API and reporting call/SMS results.
- **Note**: Automatically granted on all Android versions.
---
## 2. Flutter Receiver App Permissions
### READ_CALL_LOG (Android Only)
- **Permission String**: `android.permission.READ_CALL_LOG`
- **Protection Level**: Dangerous
- **Required Since**: API 16
- **Purpose**: Required to read the device's call log and detect incoming missed calls from the flash call. The app queries the call log every 1.5 seconds looking for missed calls since the OTP was requested.
- **How OTP Extraction Works**: When a flash call comes in, it appears as a missed call in the call log. The app reads the caller's phone number (e.g., +962791234829) and extracts the last 4 digits (4829) as the OTP code.
- **Android Version Notes**:
- API 16-22 (Android 4.1-5.1): Granted at install time
- API 23+ (Android 6.0+): Must be requested at runtime
- If denied: Automatic OTP detection will not work. User must manually enter the OTP code via the fallback input.
### READ_PHONE_STATE (Android Only)
- **Permission String**: `android.permission.READ_PHONE_STATE`
- **Protection Level**: Dangerous
- **Required Since**: API 1
- **Purpose**: Companion permission for READ_CALL_LOG. Some Android versions require both permissions to properly access call log data.
- **Android Version Notes**:
- API 29+ (Android 10+): More restricted; may need to request both READ_CALL_LOG and READ_PHONE_STATE together
- If denied: Call log access may fail on some devices
### No Extra Permissions on iOS
- iOS does not allow reading call logs or SMS messages programmatically
- SMS AutoFill works automatically through `AutofillHints.oneTimeCode` without any special permissions
- The only requirement is that the SMS follows Apple's format with `@domain #code` at the end
---
## 3. Server-Side Security
### API Key Authentication
- **APP_KEY**: Used by the Flutter Receiver App for `request-otp` and `verify-otp` endpoints
- **DEVICE_KEY**: Used by the Android Caller App for `pending-call`, `pending-sms`, `call-done`, `sms-done`, and `register-device` endpoints
- **Implementation**: Uses `hash_equals()` for timing-safe comparison to prevent timing attacks
- **Key Generation**: Use `openssl rand -hex 32` to generate cryptographically secure keys
### Rate Limiting
- **Per-Phone Limit**: Max 3 OTP requests per phone number per 10-minute window
- **Per-IP Limit**: Max 30 requests per IP per minute for `request-otp`
- **General API**: Max 60 requests per IP per minute across all endpoints
- **Implementation**: Redis-based with atomic increment operations and TTL-based expiration
### Input Validation
- **Phone Format**: E.164 format validation (e.g., +9627XXXXXXXX) — regex: `/^\+[1-9]\d{6,14}$/`
- **OTP Format**: Exactly 4 digits — regex: `/^\d{4}$/`
- **Device ID**: Length between 5-50 characters
- **SQL Injection**: All queries use PDO prepared statements
- **XSS**: JSON-only responses (no HTML rendering)
### Data Protection
- OTP codes stored in Redis with 120-second TTL (auto-expire)
- OTP codes in MySQL are for audit purposes only
- Failed verification attempts tracked in Redis (max 5 attempts)
- Request logging masks sensitive fields (app_key, otp, password)
- API logs auto-cleaned after 30 days
---
## 4. iOS-Specific Notes
### Why Flash Call Doesn't Work on iOS
Apple restricts access to the call log through `CoreTelephony` framework. There is no public API to:
- Read incoming/outgoing call history
- Get caller ID from recent calls programmatically
- Detect missed calls in real-time
This is a deliberate privacy decision by Apple. The workaround is to use SMS delivery with AutoFill.
### SMS AutoFill Mechanism
iOS can automatically suggest OTP codes from SMS messages in the keyboard suggestion bar. This requires:
1. **Correct SMS format**: The message must include the domain and code at the end:
```
رمز التحقق في Intaleq هو 4829
لا تشاركه مع أحد.
@otp.intaleqapp.com #4829
```
2. **Associated Domains**: The app must have `otp.intaleqapp.com` as an associated domain in its entitlements (for the `#code` to be recognized)
3. **Text field configuration**: The OTP input must have `autofillHints: [AutofillHints.oneTimeCode]` and `keyboardType: TextInputType.number`
### iOS AutoFill Requirements
| Requirement | Details |
|-------------|---------|
| SMS format | Must end with `@domain #code` |
| Associated domain | Configured in Xcode → Signing & Capabilities |
| Text field | `AutofillHints.oneTimeCode` + number keyboard |
| Real device | AutoFill does not work on simulators |
| SMS sender | Must come from a phone number (not alphanumeric) |
---
## 5. Permission Request Flow
### Android Caller App — First Launch
```
App Launched
┌─────────────────────────┐
│ PermissionHelper │
│ .requestAllPermissions()│
└──────────┬──────────────┘
┌──────────────┐
│ CALL_PHONE │──── Denied ──→ Show rationale dialog
│ │ │
│ │──── Granted ──→ Continue
└──────┬───────┘
┌──────────────┐
│READ_PHONE_STATE│── Denied ──→ Show rationale dialog
│ │ │
│ │── Granted ──→ Continue
└──────┬───────┘
┌──────────────┐
│ SEND_SMS │──── Denied ──→ Show rationale dialog
│ │ │
│ │──── Granted ──→ Continue
└──────┬───────┘
┌──────────────────────────────────┐
│ Battery Optimization Exemption │
│ (Intent to system settings) │
│ │
│ User selects "Don't optimize" │
└──────────────────────────────────┘
Ready for Registration
```
### Flutter Receiver App — OTP Flow
```
User taps "Send OTP"
┌─────────────────────────┐
│ Platform check │
└──────┬──────────────────┘
├──── Android ──→ Check READ_CALL_LOG permission
│ │
│ ├── Granted ──→ Start call log polling
│ │
│ ├── Denied ──→ Show rationale:
│ │ "نحتاج صلاحية قراءة سجل المكالمات
│ │ للتحقق التلقائي دون الحاجة لإدخال أي كود"
│ │
│ ├── Permanently Denied ──→ Redirect to Settings
│ │ + Show manual OTP input fallback
│ │
│ └── Still Denied ──→ Show manual OTP input
└──── iOS ──→ Skip call log entirely
Show manual 4-digit input
with AutofillHints.oneTimeCode
iOS keyboard shows SMS code
suggestion automatically
```
### Arabic Permission Explanations
| Permission | Arabic Explanation |
|-----------|-------------------|
| CALL_PHONE | "صلاحية إجراء المكالمات مطلوبة للاتصال التلقائي بأرقام التحقق" |
| READ_PHONE_STATE | "صلاحية قراءة حالة الهاتف مطلوبة لإدارة المكالمات" |
| SEND_SMS | "صلاحية إرسال الرسائل مطلوبة للتحقق من مستخدمي iOS" |
| READ_CALL_LOG | "نحتاج صلاحية قراءة سجل المكالمات للتحقق التلقائي دون الحاجة لإدخال أي كود" |
| Battery Optimization | "يجب تعطيل تحسين البطارية لضمان عمل الخدمة بشكل مستمر" |
---
## Appendix: Permission Summary by Component
| Permission | Caller App | Receiver App | Type | Min API |
|-----------|:----------:|:------------:|------|---------|
| CALL_PHONE | ✅ | ❌ | Dangerous | 1 |
| READ_PHONE_STATE | ✅ | ✅ | Dangerous | 1 |
| SEND_SMS | ✅ | ❌ | Dangerous | 1 |
| READ_CALL_LOG | ❌ | ✅ | Dangerous | 16 |
| FOREGROUND_SERVICE | ✅ | ❌ | Normal | 26 |
| FOREGROUND_SERVICE_PHONE_CALL | ✅ | ❌ | Normal | 34 |
| RECEIVE_BOOT_COMPLETED | ✅ | ❌ | Normal | 1 |
| REQUEST_IGNORE_BATTERY_OPTIMIZATIONS | ✅ | ❌ | Normal | 23 |
| INTERNET | ✅ | ✅ | Normal | 1 |

497
docs/SETUP.md Normal file
View File

@@ -0,0 +1,497 @@
# Flash Call OTP System — Setup Guide
## Table of Contents
1. [Prerequisites](#1-prerequisites)
2. [Server Setup (PHP Backend)](#2-server-setup-php-backend)
3. [Android Caller App Setup](#3-android-caller-app-setup)
4. [Flutter Receiver App Setup](#4-flutter-receiver-app-setup)
5. [End-to-End Testing](#5-end-to-end-testing)
6. [Troubleshooting](#6-troubleshooting)
---
## 1. Prerequisites
### Server Requirements
- Ubuntu 20.04+ or similar Linux distribution
- PHP 8.1+ with phpredis extension
- MySQL 8.0+ or MariaDB 10.6+
- Redis 6.0+
- Nginx 1.18+
- Certbot (Let's Encrypt) for SSL
- Composer (optional, for dependency management)
### Android Caller App Requirements
- Android Studio Hedgehog (2023.1.1) or newer
- Android device running Android 8.0 (API 26) or higher
- Active SIM card installed in the device
- Stable internet connection (Wi-Fi recommended)
### Flutter Receiver App Requirements
- Flutter 3.16+ with Dart 3.x
- Android device for full auto-read testing
- iOS device for SMS AutoFill testing (optional)
- Xcode 15+ (for iOS builds)
---
## 2. Server Setup (PHP Backend)
### 2.1 Install Required Packages
```bash
sudo apt update
sudo apt install -y nginx php8.1-fpm php8.1-mysql php8.1-redis php8.1-mbstring php8.1-xml php8.1-curl mysql-server redis-server
```
### 2.2 Configure MySQL
```bash
sudo mysql_secure_installation
```
Log in to MySQL and create the database and user:
```bash
sudo mysql -u root -p
```
```sql
CREATE DATABASE otp-db CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
CREATE USER 'otp-user'@'localhost' IDENTIFIED BY 'tCO1zuHWVKjTnryDDInL';
GRANT SELECT, INSERT, UPDATE, DELETE ON otp_db.* TO 'otp-user'@'localhost';
FLUSH PRIVILEGES;
EXIT;
```
### 2.3 Import Database Schema
```bash
mysql -u otp_user -p otp_db < /path/to/backend/database.sql
```
Verify the tables were created:
```bash
mysql -u otp_user -p otp_db -e "SHOW TABLES;"
```
You should see: `api_logs`, `caller_devices`, `otp_requests`
### 2.4 Configure Redis
Edit Redis configuration for security:
```bash
sudo nano /etc/redis/redis.conf
```
Set the following:
```
bind 127.0.0.1
protected-mode yes
# requirepass YOUR_REDIS_PASSWORD # Uncomment and set if needed
```
Restart Redis:
```bash
sudo systemctl restart redis-server
sudo systemctl enable redis-server
```
Test connection:
```bash
redis-cli ping
# Should return: PONG
```
### 2.5 Deploy PHP Backend
Create the web root directory:
```bash
sudo mkdir -p /var/www/otp.intaleqapp.com/public
sudo chown -R www-data:www-data /var/www/otp.intaleqapp.com
```
Copy the backend files:
```bash
# Copy the entire backend directory
cp -r backend/* /var/www/otp.intaleqapp.com/
# The structure should be:
# /var/www/otp.intaleqapp.com/
# ├── api/
# │ ├── request-otp.php
# │ ├── pending-call.php
# │ ├── call-done.php
# │ ├── verify-otp.php
# │ ├── register-device.php
# │ ├── pending-sms.php
# │ └── sms-done.php
# ├── includes/
# │ ├── Database.php
# │ ├── Redis.php
# │ ├── RateLimit.php
# │ ├── Auth.php
# │ └── Logger.php
# ├── config.php
# ├── .htaccess
# ├── database.sql
# ├── nginx.conf
# └── logs/
```
Set permissions:
```bash
sudo chown -R www-data:www-data /var/www/otp.intaleqapp.com
sudo chmod -R 755 /var/www/otp.intaleqapp.com
sudo chmod -R 775 /var/www/otp.intaleqapp.com/logs
```
### 2.6 Configure config.php
Edit the configuration file with your production credentials:
```bash
sudo nano /var/www/otp.intaleqapp.com/config.php
```
Update these critical values:
```php
define('DB_PASS', 'YOUR_ACTUAL_STRONG_PASSWORD');
define('APP_KEY', 'GENERATE_A_SECURE_RANDOM_KEY_HERE');
define('DEVICE_KEY', 'GENERATE_A_DIFFERENT_SECURE_KEY_HERE');
```
Generate secure keys:
```bash
openssl rand -hex 32
```
### 2.7 Configure Nginx
Copy the nginx configuration:
```bash
sudo cp /var/www/otp.intaleqapp.com/nginx.conf /etc/nginx/sites-available/otp.intaleqapp.com
sudo ln -s /etc/nginx/sites-available/otp.intaleqapp.com /etc/nginx/sites-enabled/
```
Test the configuration:
```bash
sudo nginx -t
```
Reload Nginx:
```bash
sudo systemctl reload nginx
```
### 2.8 Install SSL Certificate
```bash
sudo apt install certbot python3-certbot-nginx
sudo certbot --nginx -d otp.intaleqapp.com
```
Follow the prompts. Certbot will automatically modify the Nginx config for SSL.
Set up auto-renewal:
```bash
sudo crontab -e
# Add: 0 3 * * * certbot renew --quiet --post-hook "systemctl reload nginx"
```
### 2.9 Configure PHP-FPM
Ensure PHP-FPM is running:
```bash
sudo systemctl enable php8.1-fpm
sudo systemctl start php8.1-fpm
```
Check the socket path matches your nginx config:
```bash
ls -la /var/run/php/php8.1-fpm.sock
```
### 2.10 Verify the Backend
Test the health endpoint:
```bash
curl https://otp.intaleqapp.com/health
# Should return: {"status":"ok"}
```
Test the OTP request (will fail without valid app_key, but confirms routing works):
```bash
curl -X POST https://otp.intaleqapp.com/api/request-otp \
-H "Content-Type: application/json" \
-d '{"phone":"+962790000000","app_key":"wrong_key"}'
# Should return: {"success":false,"message":"invalid_app_key"}
```
---
## 3. Android Caller App Setup
### 3.1 Build the App
1. Open Android Studio
2. Select "Open an existing project"
3. Navigate to the `caller-app/` directory
4. Wait for Gradle sync to complete
5. Connect your Android device (with USB debugging enabled)
6. Click Run ▶️
### 3.2 First-Time Setup on the Device
1. **Install the APK** on the cheap Android phone with SIM card
2. **Grant all permissions** when prompted:
- Phone (CALL_PHONE)
- Phone State (READ_PHONE_STATE)
- SMS (SEND_SMS)
3. **Disable Battery Optimization** — the app will prompt you; select "Don't optimize" for Flash OTP Caller
4. On the main screen:
- **Device ID**: Auto-generated UUID (note this down)
- **Phone Number**: Enter the phone number of the SIM in this device (e.g., +96279XXXXXXX)
- **App Key**: Enter the DEVICE_KEY from your server's config.php
- **SIM Slot**: Select 0 (or 1 for dual SIM)
5. Tap **"Register Device"** — you should see a success message
6. Tap **"Start Service"** — the notification should show "Flash OTP Caller — Active"
### 3.3 Verify Service is Running
- Check the persistent notification in the status bar
- The status indicator should show "Service Running ✅"
- The live log area should start showing "Polling..." messages
- Verify on the server that the device is registered:
```bash
mysql -u otp_user -p otp_db -e "SELECT * FROM caller_devices;"
```
### 3.4 Keep the App Running
- **Lock the app** in recent apps (prevent it from being killed)
- On Xiaomi/OPPO/Vivo: Enable "Auto-start" in security settings
- On Samsung: Disable "Put app to sleep" in battery settings
- Keep the phone plugged into charger if possible
- Use a dedicated SIM card with an active plan that can make calls and send SMS
---
## 4. Flutter Receiver App Setup
### 4.1 Build the App
```bash
cd receiver-app/
flutter pub get
flutter run
```
For APK build:
```bash
flutter build apk --release
```
For iOS (macOS only):
```bash
cd receiver-app/
flutter pub get
cd ios/
pod install
cd ..
flutter run
```
### 4.2 Testing on Android
1. Install the app on a test phone (Phone B)
2. Open the app
3. Enter the phone number of Phone B (with +962 prefix)
4. Ensure "Android" is selected as device type
5. Tap "Send OTP"
6. Phone A (Caller device) should call Phone B within 3-6 seconds
7. The call will come from a number like +96279XX{OTP4digits}
8. Phone B's call log will detect the missed call
9. The app extracts the last 4 digits as OTP
10. Auto-verifies with the backend
11. Success screen appears
### 4.3 Testing on iOS
1. Install the app on an iPhone
2. Open the app
3. Enter the phone number of the iPhone (with +962 prefix)
4. Toggle device type to "iOS"
5. Tap "Send OTP"
6. The Caller Android device will send an SMS
7. iOS keyboard will show the AutoFill suggestion
8. Tap the suggestion (one tap only)
9. The app auto-submits the OTP
10. Success screen appears
---
## 5. End-to-End Testing
### Complete Test Flow
1. **Server**: Confirm health endpoint returns `{"status":"ok"}`
2. **Caller Device**: Ensure service is running with "Service Running ✅"
3. **Receiver App**: Enter phone number and tap "Send OTP"
4. **Timing**: Call should arrive within 3-6 seconds
5. **Auto-detect**: Receiver app should detect the missed call within 1-2 seconds
6. **Verification**: Success screen should appear without any user input
7. **Database check**: Verify the record in `otp_requests` has `verified_at` set
### Testing Individual API Endpoints
```bash
# Request OTP
curl -X POST https://otp.intaleqapp.com/api/request-otp \
-H "Content-Type: application/json" \
-d '{"phone":"+962790000001","app_key":"YOUR_APP_KEY","device_type":"android"}'
# Verify OTP
curl -X POST https://otp.intaleqapp.com/api/verify-otp \
-H "Content-Type: application/json" \
-d '{"phone":"+962790000001","otp":"1234","app_key":"YOUR_APP_KEY"}'
# Check pending calls (Caller App)
curl "https://otp.intaleqapp.com/api/pending-call?device_id=DEVICE_XXX&app_key=YOUR_DEVICE_KEY"
# Check pending SMS (Caller App)
curl "https://otp.intaleqapp.com/api/pending-sms?device_id=DEVICE_XXX&app_key=YOUR_DEVICE_KEY"
# Register device
curl -X POST https://otp.intaleqapp.com/api/register-device \
-H "Content-Type: application/json" \
-d '{"device_id":"DEVICE_001","phone_number":"+962790000000","sim_slot":0,"app_key":"YOUR_DEVICE_KEY"}'
```
### Monitoring
Check server logs:
```bash
# Nginx access log
sudo tail -f /var/log/nginx/otp.intaleqapp.com.access.log
# PHP error log
sudo tail -f /var/log/php8.1-fpm/error.log
# Application log
tail -f /var/www/otp.intaleqapp.com/logs/api.log
```
Check Redis:
```bash
redis-cli
> KEYS otp:*
> TTL otp:+962790000001
> GET otp:+962790000001
```
Check MySQL:
```sql
-- Recent OTP requests
SELECT * FROM otp_requests ORDER BY created_at DESC LIMIT 10;
-- Active devices
SELECT * FROM caller_devices WHERE is_active = 1;
-- Today's stats
SELECT status, COUNT(*) as count
FROM otp_requests
WHERE DATE(created_at) = CURDATE()
GROUP BY status;
```
---
## 6. Troubleshooting
### Caller Device Not Receiving Tasks
1. Check the device is registered: `SELECT * FROM caller_devices WHERE device_id = 'YOUR_DEVICE_ID';`
2. Check `is_active = 1` and `last_seen` is recent
3. Check the Caller App has internet access
4. Check the app_key matches DEVICE_KEY in config.php
5. Restart the CallerService
### Calls Not Going Through
1. Verify CALL_PHONE permission is granted
2. Check the SIM card has credit/plan for making calls
3. Check the phone number format in the task
4. Try manually dialing the number from the phone app
5. Check if Do Not Disturb mode is off
### SMS Not Sending
1. Verify SEND_SMS permission is granted
2. Check the SIM card can send SMS
3. Check the destination number format
4. Test sending a manual SMS from the device
### OTP Auto-detect Not Working (Flutter App)
1. Verify READ_CALL_LOG and READ_PHONE_STATE permissions are granted
2. Check the phone actually received a missed call
3. Verify the call type is detected as "missed" (not "rejected")
4. If the user answers the call, it won't be in the missed call log
5. Try the manual entry fallback
### iOS AutoFill Not Working
1. Ensure the SMS format includes `@otp.intaleqapp.com #{OTP}` at the end
2. The domain must match the app's associated domain
3. Test with a real device (simulators don't receive SMS)
4. Check that `AutofillHints.oneTimeCode` is set on the text field
5. The SMS must come from a phone number, not an alphanumeric sender
### Rate Limiting Issues
1. Check Redis is running: `redis-cli ping`
2. Clear rate limit for a phone: `redis-cli DEL rate_limit:otp:+962790000001`
3. Adjust limits in config.php if needed
### Database Connection Errors
1. Verify MySQL is running: `sudo systemctl status mysql`
2. Check credentials in config.php
3. Test connection: `mysql -u otp_user -p -h localhost otp_db`
4. Check PHP-FPM error log
### High Server Load
1. Check the polling interval (3 seconds default)
2. Consider increasing to 5 seconds if many devices are connected
3. Use Redis for session data, MySQL for persistent storage only
4. Monitor with `top`, `htop`, or `mysqladmin processlist`

258
docs/SMART_OTP_GATEWAY.md Normal file
View File

@@ -0,0 +1,258 @@
# دليل تصميم وهندسة نظام بوابة التحقق الذكي (Smart OTP Gateway)
<div dir="rtl" align="right">
يوفر هذا المستند تقييماً تفصيلياً وتصميماً هندسياً لبوابة التحقق الذكية المقترحة لدمج قنوات وتساب وتيليجرام، مع الحفاظ على أنظمة مكالمات الفلاش والرسائل النصية كقنوات بديلة.
</div>
---
## ١. تقييم المفهوم: تجربة المستخدم البشرية ضد أنظمة كشف الروبوتات
<div dir="rtl" align="right">
يعد تحقيق التوازن بين تجربة المستخدم (UX) وفلاتر الحماية التلقائية (خصوصاً فلاتر حظر الأرقام وأنظمة التعرف البصري على النصوص في وتساب) أحد أكبر التحديات في أنظمة التحقق.
- المبالغة في الحماية (مثل الصور المشوهة جداً) تزيد من صعوبة الاستخدام ونسبة إلغاء العملية من المستخدم.
- تسهيل العملية بشكل مبالغ فيه (مثل إرسال الرمز كنص عادي) يزيد من نسب اكتشاف الرسائل كرسائل ترويجية أو سبام وحظر أرقام الإرسال.
الحل الهندسي يكمن في تطبيق "واجهة تحقق متعددة الطبقات" مدعومة بـ "شبكة تحقق ديناميكية متكيفة".
</div>
```
┌─────────────────────────────────────────────────────────────┐
│ OTP Delivery Message │
│ │
│ ┌───────────────────────┐ │
│ │ IMAGE LAYER │ <-- 80% Anti-Detection Entropy │
│ │ [ 5 4 1 2 ] │ (Dynamic Generation/Reveal) │
│ └───────────────────────┘ │
│ │
│ Click the button below to verify instantly: │
│ [ Verify Now (Deep Link) ] <-- 100% Seamless UX │
│ │
│ *Hidden metadata container* <-- 20% OCR Noise │
└─────────────────────────────────────────────────────────────┘
```
<div dir="rtl" align="right">
الركائز الأساسية لواجهة التحقق متعددة الطبقات:
١. طبقة العرض المرئي الأساسي (الصور والوسائط الديناميكية): يعرض رمز التحقق داخل صورة يتم توليدها ديناميكياً مع خطوط تشويش خفيفة.
٢. البيانات الخفية المصاحبة للرسالة: تحتوي الرسالة على الرمز مشفراً أو مخفياً باستخدام علامات ترميز يونيكود مخفية (مثل المسافات الصفرية أو علامات الاتجاه) لقراءتها تلقائياً بواسطة التطبيق دون أن تثير انتباه العين البشرية أو الروبوتات.
٣. روابط التحقق الفورية (الروابط العميقة): تحتوي الرسالة على رابط عميق آمن يقوم بفتح التطبيق وتأكيد العملية تلقائياً عند النقر عليه دون الحاجة لكتابة أو نسخ أي رمز.
٤. التوجيه الديناميكي المتكيف مع المخاطر: يقوم النظام بتغيير طريقة التحقق تلقائياً بناءً على مستوى خطورة الطلب.
</div>
---
## ٢. البنية التحتية الحالية للنظام
<div dir="rtl" align="right">
يتكون النظام الحالي لمكالمات الفلاش من ثلاثة أجزاء رئيسية:
١. خادم الخلفية (PHP API): يتولى إنشاء الرموز، وإدارة الأجهزة المتصلة، وتسجيل المهام، والتحقق من الرموز.
٢. تطبيق الاتصال (Android Caller App): يعمل على هواتف أندرويد حقيقية متصلة بالإنترنت وتحتوي على شرائح اتصال نشطة، حيث يقوم بطلب المهام المعلقة وتنفيذ المكالمات والرسائل النصية تلقائياً.
٣. تطبيق الاستقبال (Flutter Receiver App): يثبت لدى المستخدم النهائي، ويقوم بالتحقق التلقائي في أندرويد عبر قراءة سجل المكالمات للتعرف على مكالمة الفلاش الفائتة، بينما يتيح التعبئة التلقائية للرسائل النصية في آيفون.
</div>
### ٢.١ مخطط سير العمل الحالي في أندرويد (مكالمة الفلاش)
```mermaid
sequenceDiagram
autonumber
actor User as User Device (Android)
participant App as Flutter Receiver App
participant BE as PHP Backend API
participant DB as Database / Redis
participant Caller as Android Caller Node
User->>App: Input Phone Number & Submit
App->>BE: POST /api/request-otp (phone, device_type: "android")
BE->>BE: Generate 4-digit OTP (e.g., 4829)
BE->>DB: Store OTP in Redis (TTL: 120s)
BE->>DB: Assign Task (round-robin) to Active Caller Node
BE->>BE: Format Caller ID Prefix + 2 Random Digits + OTP (e.g., +96279XX4829)
BE-->>App: JSON Response (otp_id, expires_in, method: "flash_call")
App->>App: Request READ_CALL_LOG permission & Start Polling
loop Polling Tasks (Every 3s)
Caller->>BE: GET /api/pending-call?device_id=X
BE-->>Caller: JSON (pending call: phone + dynamic Caller ID)
end
Caller->>User: Initiates Voice Call (displays +96279XX4829)
Caller->>Caller: Wait 2.5s & Programmatically Terminate Call (Missed Call)
Caller->>BE: POST /api/call-done (status: "success")
App->>App: Detects Missed Call in Call Log
App->>App: Extracts last 4 digits of Caller ID (4829)
App->>BE: POST /api/verify-otp (phone, otp)
BE->>DB: Verify OTP in Redis & Update DB Status
BE-->>App: {"success": true, "message": "verified"}
App->>User: Display Success Screen
```
---
## ٣. بنية بوابة التحقق الذكي المقترحة
<div dir="rtl" align="right">
تقوم البوابة الذكية بفحص توفر حساب للرقم على وتساب وتيليجرام أولاً لتحديد قناة الإرسال المثالية بدلاً من الاعتماد الكلي على نظام تشغيل الهاتف فقط.
</div>
### مخطط اتخاذ القرار وقنوات التوجيه
```mermaid
graph TD
A[Start: Request OTP] --> B{Check WhatsApp Availability}
B -- Exists --> C[Determine Risk Level]
C -- Low Risk --> D1[Send Obfuscated Text OTP]
C -- Medium Risk --> D2[Send Dynamic OTP Image + Deep Link]
C -- High Risk --> D3[Send Short APNG/MP4 + Deep Link]
B -- Does Not Exist --> F{Check Telegram Availability}
F -- Exists --> G[Send OTP via Telegram Bot/Client]
F -- Does Not Exist --> H{Check Device OS}
H -- Android --> I[Trigger Flash Call via Caller Node]
H -- iOS --> J[Trigger SMS via Caller Node]
D1 --> K[End: Await Verification]
D2 --> K
D3 --> K
G --> K
I --> K
J --> K
```
### ٣.١ تصنيف مستويات المخاطر الديناميكية
| مستوى الخطورة | القناة والمحتوى | مستوى الأمان | مستوى صعوبة تجربة المستخدم | حالات الاستخدام المستهدفة |
| :--- | :--- | :--- | :--- | :--- |
| **المستوى الأول (منخفض)** | رمز نصي عادي (مع علامات يونيكود مخفية) | منخفض | منخفض جداً (تعبئة تلقائية) | عناوين إنترنت موثوقة، مستخدمون متكررون |
| **المستوى الثاني (متوسط)** | صورة ديناميكية ثابتة + رابط تحقق فوري | متوسط | منخفض (التحقق بنقرة واحدة) | التسجيلات القياسية، التوجيه الافتراضي |
| **المستوى الثالث (مرتفع)** | صورة متحركة APNG/MP4 تظهر تدريجياً + رابط تحقق | مرتفع | منخفض (التحقق بنقرة واحدة) | عناوين إنترنت مشبوهة، طلبات متكررة بكثافة |
| **المستوى الرابع (مشبوه)** | مكالمة فلاش خارجية / رمز صوتي | أقصى حد | متوسط (إدخال يدوي وتحدي) | تخطي حدود المحاولات المسموحة |
---
## ٤. المواصفات البرمجية للتكامل
### ٤.١ بوابة وتساب (Node.js Baileys Gateway)
#### أ. إضافة واجهة التحقق من الأرقام في خادم الويب
في ملف `server.js`:
```javascript
app.post('/api/contacts/check', async (req, res) => {
const { session_key, phone } = req.body;
if (!session_key || !phone) {
return res.status(400).json({ error: 'Missing session_key or phone' });
}
try {
const result = await checkWhatsApp(session_key, phone);
res.json({ status: 'success', data: result });
} catch (err) {
res.status(500).json({ error: err.message || 'Failed to check contact' });
}
});
```
#### ب. توليد صورة رمز التحقق ورابط التحقق التلقائي (PHP Backend)
```php
function generateWhatsAppOtpPayload($otp, $phone, $riskLevel) {
// Generate Deep Link Token
$token = bin2hex(random_bytes(16));
// Store deep link token in Redis mapped to phone & OTP (expires in 120s)
$redis = RedisClient::getInstance();
$redis->setex("verify_token:{$token}", 120, json_encode([
'phone' => $phone,
'otp' => $otp
]));
$deepLink = "https://verify.nabeh.app/otp?token=" . $token;
if ($riskLevel === 'level_1') {
// Obfuscate OTP text using zero-width joiners (ZWJ) or LTR/RTL control characters
$obfuscatedOtp = "\u{202D}" . implode("\u{200B}", str_split($otp)) . "\u{202C}";
return [
'type' => 'text',
'message' => "رمز التحقق الخاص بك هو: " . $obfuscatedOtp . "\n\nأو اضغط للتحقق الفوري:\n" . $deepLink
];
}
// Create base64 dynamic image for Level 2 & 3
$imageBase64 = generateOtpImageBase64($otp);
return [
'type' => 'image',
'image' => $imageBase64,
'message' => "اضغط على الرابط أدناه لتأكيد هويتك تلقائيًا دون نسخ الرمز:\n\n" . $deepLink
];
}
```
---
## ٥. أتمتة التحقق في تطبيق الهاتف (Flutter Client)
<div dir="rtl" align="right">
لتحقيق واجهة مستخدم بدون كتابة أو تعبئة يدوية للرموز، يتم تطبيق آليتين أساسيتين في تطبيق فلاتر:
</div>
### ٥.١ معالجة الروابط العميقة (Deep Links)
<div dir="rtl" align="right">
يقوم التطبيق بتسجيل الدومين الخاص به. عند النقر على الرابط من داخل محادثة وتساب أو تيليجرام:
١. يكتشف نظام التشغيل الرابط ويفتح تطبيق الهاتف تلقائياً.
٢. يقوم التطبيق باستخراج التوكين وإرساله مباشرة للخلفية.
٣. تكتمل عملية التحقق بنجاح وينتقل المستخدم للشاشة التالية فوراُ.
</div>
### ٥.٢ قارئ الإشعارات التلقائي (Notification Listener - Android)
<div dir="rtl" align="right">
بالنسبة لهواتف أندرويد، يمكن الاستماع للإشعارات الواردة برمجياً لقراءة الرسائل وتمرير التوكين أو الرمز المخفي دون الحاجة لفتح وتساب:
</div>
```dart
void onNotificationReceived(NotificationEvent event) {
if (event.packageName == "com.whatsapp" && event.title == "Nabeh") {
final body = event.text ?? "";
final tokenRegex = RegExp(r"token=([a-f0-9]+)");
if (tokenRegex.hasMatch(body)) {
final token = tokenRegex.firstMatch(body)!.group(1);
_verifyToken(token!);
}
}
}
```
---
## ٦. تعديلات قاعدة البيانات
```sql
ALTER TABLE otp_requests
ADD COLUMN channel ENUM('flash_call', 'sms', 'whatsapp', 'telegram') NOT NULL DEFAULT 'flash_call' AFTER method,
ADD COLUMN risk_level ENUM('level_1', 'level_2', 'level_3', 'level_4') NOT NULL DEFAULT 'level_2' AFTER channel,
ADD COLUMN deep_link_token VARCHAR(64) NULL DEFAULT NULL AFTER risk_level;
```
---
## ٧. خطة التطوير والتشغيل
### المرحلة الأولى: تهيئة قنوات الفحص والروابط العميقة
<div dir="rtl" align="right">
١. كتابة وتفعيل منطق الروابط العميقة (Deep Links) في خادم PHP وتطبيق فلاتر.
٢. ربط النطاقات المرتبطة (Associated Domains) في آبل وجوجل.
</div>
### المرحلة الثانية: تحديثات وتساب (Baileys Node.js)
<div dir="rtl" align="right">
١. إضافة واجهة التحقق من وجود الرقم على خادم وتساب.
٢. تفعيل مكتبة توليد الصور برمجياً في خادم PHP لإرسال الصور التلقائية عبر وتساب.
</div>
### المرحلة الثالثة: أتمتة تطبيق الهاتف والبدائل
<div dir="rtl" align="right">
١. دمج قارئ الإشعارات في فلاتر وتجربة الأتمتة الكاملة.
٢. ربط قنوات الاتصال الفائت والرسائل النصية كقنوات بديلة نهائية في حال عدم نجاح التحقق عبر وتساب أو تيليجرام.
</div>