Files
flash-call-otp/docs/SETUP.md
2026-05-23 16:17:20 +03:00

12 KiB

Flash Call OTP System — Setup Guide

Table of Contents

  1. Prerequisites
  2. Server Setup (PHP Backend)
  3. Android Caller App Setup
  4. Flutter Receiver App Setup
  5. End-to-End Testing
  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

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

sudo mysql_secure_installation

Log in to MySQL and create the database and user:

sudo mysql -u root -p
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

mysql -u otp_user -p otp_db < /path/to/backend/database.sql

Verify the tables were created:

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:

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:

sudo systemctl restart redis-server
sudo systemctl enable redis-server

Test connection:

redis-cli ping
# Should return: PONG

2.5 Deploy PHP Backend

Create the web root directory:

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:

# 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:

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:

sudo nano /var/www/otp.intaleqapp.com/config.php

Update these critical values:

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:

openssl rand -hex 32

2.7 Configure Nginx

Copy the nginx configuration:

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:

sudo nginx -t

Reload Nginx:

sudo systemctl reload nginx

2.8 Install SSL Certificate

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:

sudo crontab -e
# Add: 0 3 * * * certbot renew --quiet --post-hook "systemctl reload nginx"

2.9 Configure PHP-FPM

Ensure PHP-FPM is running:

sudo systemctl enable php8.1-fpm
sudo systemctl start php8.1-fpm

Check the socket path matches your nginx config:

ls -la /var/run/php/php8.1-fpm.sock

2.10 Verify the Backend

Test the health endpoint:

curl https://otp.intaleqapp.com/health
# Should return: {"status":"ok"}

Test the OTP request (will fail without valid app_key, but confirms routing works):

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:
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

cd receiver-app/
flutter pub get
flutter run

For APK build:

flutter build apk --release

For iOS (macOS only):

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

# 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:

# 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:

redis-cli
> KEYS otp:*
> TTL otp:+962790000001
> GET otp:+962790000001

Check MySQL:

-- 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