Initial commit - WASL Digital Wallet
This commit is contained in:
40
Backend/config/activitylog.php
Normal file
40
Backend/config/activitylog.php
Normal file
@@ -0,0 +1,40 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
|
||||
'enabled' => env('ACTIVITY_LOGGER_ENABLED', true),
|
||||
|
||||
/*
|
||||
|-------------------------------------------------------------------
|
||||
| Log deleted records
|
||||
|-------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
'delete_records_on_delete' => false,
|
||||
|
||||
'default_log_name' => 'default',
|
||||
|
||||
/*
|
||||
|-------------------------------------------------------------------
|
||||
| Default driver
|
||||
|-------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
'driver' => \Spatie\Activitylog\ActivitylogServiceProvider::class,
|
||||
|
||||
/*
|
||||
|-------------------------------------------------------------------
|
||||
| Show activity log model
|
||||
|-------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
'activity_model' => \App\Models\Activitylog\Activity::class,
|
||||
|
||||
/*
|
||||
|-------------------------------------------------------------------
|
||||
| The database connection used for activity logs.
|
||||
|-------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
'database_connection' => env('ACTIVITYLOG_DB_CONNECTION', 'pgsql'),
|
||||
];
|
||||
97
Backend/config/app.php
Normal file
97
Backend/config/app.php
Normal file
@@ -0,0 +1,97 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Application Name
|
||||
|--------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
'name' => env('APP_NAME', 'WASL'),
|
||||
'short_name' => env('APP_SHORT_NAME', 'وَصْل'),
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Application Environment
|
||||
|--------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
'env' => env('APP_ENV', 'production'),
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Application Debug Mode
|
||||
|--------------------------------------------------------------------------
|
||||
| SECURITY: NEVER enable debug mode in production for a financial system.
|
||||
*/
|
||||
|
||||
'debug' => (bool) env('APP_DEBUG', false),
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Application URL
|
||||
|--------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
'url' => env('APP_URL', 'http://localhost'),
|
||||
'asset_url' => env('ASSET_URL'),
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Application Timezone
|
||||
|--------------------------------------------------------------------------
|
||||
| Syria uses Asia/Damascus (UTC+03:00, no DST since 2022)
|
||||
*/
|
||||
|
||||
'timezone' => 'Asia/Damascus',
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Application Locale Configuration
|
||||
|--------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
'locale' => 'ar',
|
||||
'fallback_locale' => 'en',
|
||||
'faker_locale' => 'ar_SY',
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Encryption Key
|
||||
|--------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
'key' => env('APP_KEY'),
|
||||
'cipher' => env('APP_CIPHER', 'aes-256-cbc'),
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Maintenance Mode Driver
|
||||
|--------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
'maintenance' => [
|
||||
'driver' => env('APP_MAINTENANCE_DRIVER', 'file'),
|
||||
'store' => env('APP_MAINTENANCE_STORE', 'db'),
|
||||
],
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| WASL-specific config reference
|
||||
|--------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
'wasl' => [
|
||||
'country_code' => 'SY',
|
||||
'default_currency' => 'SYP',
|
||||
'minor_unit_decimals' => [
|
||||
'SYP' => 2, // 1 SYP = 100 piasters → minor unit
|
||||
'USD' => 2,
|
||||
'EUR' => 2,
|
||||
'AED' => 2,
|
||||
'USDT' => 8, // crypto precision
|
||||
'BTC' => 8,
|
||||
],
|
||||
],
|
||||
];
|
||||
40
Backend/config/auth.php
Normal file
40
Backend/config/auth.php
Normal file
@@ -0,0 +1,40 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
|
||||
'defaults' => [
|
||||
'guard' => 'api',
|
||||
],
|
||||
|
||||
'guards' => [
|
||||
'api' => [
|
||||
'driver' => 'jwt',
|
||||
'provider' => 'users',
|
||||
],
|
||||
|
||||
// Filament admin panel
|
||||
'web' => [
|
||||
'driver' => 'session',
|
||||
'provider' => 'users',
|
||||
],
|
||||
],
|
||||
|
||||
'providers' => [
|
||||
'users' => [
|
||||
'driver' => 'eloquent',
|
||||
'model' => env('AUTH_MODEL', App\Models\User::class),
|
||||
],
|
||||
],
|
||||
|
||||
'passwords' => [
|
||||
'users' => [
|
||||
'provider' => 'users',
|
||||
'table' => 'password_reset_tokens',
|
||||
'expire' => 60,
|
||||
'throttle' => 60,
|
||||
],
|
||||
],
|
||||
|
||||
'password_timeout' => 10800,
|
||||
|
||||
];
|
||||
123
Backend/config/database.php
Normal file
123
Backend/config/database.php
Normal file
@@ -0,0 +1,123 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Support\ServiceProvider;
|
||||
|
||||
return [
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Default Database Connection Name
|
||||
|--------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
'default' => env('DB_CONNECTION', 'pgsql'),
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Database Connections
|
||||
|--------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
'connections' => [
|
||||
|
||||
'pgsql' => [
|
||||
'driver' => 'pgsql',
|
||||
'url' => env('DB_URL'),
|
||||
'host' => env('DB_HOST', '127.0.0.1'),
|
||||
'port' => env('DB_PORT', '5432'),
|
||||
'database' => env('DB_DATABASE', 'wasl'),
|
||||
'username' => env('DB_USERNAME', 'wasl'),
|
||||
'password' => env('DB_PASSWORD', ''),
|
||||
'charset' => 'utf8',
|
||||
'prefix' => '',
|
||||
'prefix_indexes' => true,
|
||||
'search_path' => 'public',
|
||||
'sslmode' => env('DB_SSL_MODE', 'prefer'),
|
||||
'sslcert' => env('DB_SSL_CERT'),
|
||||
'sslkey' => env('DB_SSL_KEY'),
|
||||
'sslrootcert' => env('DB_SSL_ROOT_CERT'),
|
||||
'schema' => 'public',
|
||||
'engine' => null,
|
||||
],
|
||||
|
||||
'testing' => [
|
||||
'driver' => 'pgsql',
|
||||
'host' => env('DB_TEST_HOST', '127.0.0.1'),
|
||||
'port' => env('DB_TEST_PORT', '5432'),
|
||||
'database' => env('DB_TEST_DATABASE', 'wasl_testing'),
|
||||
'username' => env('DB_TEST_USERNAME', 'wasl'),
|
||||
'password' => env('DB_TEST_PASSWORD', ''),
|
||||
'charset' => 'utf8',
|
||||
'search_path' => 'public',
|
||||
],
|
||||
],
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Migration Repository Table
|
||||
|--------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
'migrations' => [
|
||||
'table' => 'migrations',
|
||||
'update_date_on_publish' => true,
|
||||
],
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Redis Databases / Connection
|
||||
| Separate logical DBs isolate concerns: cache, queue, idempotency,
|
||||
| throttling, sessions — so a flush of one never impacts the others.
|
||||
|--------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
'redis' => [
|
||||
|
||||
'client' => env('REDIS_CLIENT', 'predis'),
|
||||
|
||||
'options' => [
|
||||
'cluster' => env('REDIS_CLUSTER', 'redis'),
|
||||
'prefix' => env('REDIS_PREFIX', 'wasl_'),
|
||||
'persistent' => env('REDIS_PERSISTENT', false),
|
||||
],
|
||||
|
||||
'default' => [
|
||||
'url' => env('REDIS_URL'),
|
||||
'host' => env('REDIS_HOST', '127.0.0.1'),
|
||||
'username' => env('REDIS_USERNAME'),
|
||||
'password' => env('REDIS_PASSWORD'),
|
||||
'port' => env('REDIS_PORT', '6379'),
|
||||
'database' => env('REDIS_DB', '0'),
|
||||
],
|
||||
|
||||
'cache' => [
|
||||
'url' => env('REDIS_URL'),
|
||||
'host' => env('REDIS_HOST', '127.0.0.1'),
|
||||
'username' => env('REDIS_USERNAME'),
|
||||
'password' => env('REDIS_PASSWORD'),
|
||||
'port' => env('REDIS_PORT', '6379'),
|
||||
'database' => env('REDIS_CACHE_DB', '1'),
|
||||
],
|
||||
|
||||
// Separate DB for idempotency keys (short TTL)
|
||||
'idempotency' => [
|
||||
'url' => env('REDIS_URL'),
|
||||
'host' => env('REDIS_HOST', '127.0.0.1'),
|
||||
'username' => env('REDIS_USERNAME'),
|
||||
'password' => env('REDIS_PASSWORD'),
|
||||
'port' => env('REDIS_PORT', '6379'),
|
||||
'database' => env('REDIS_IDEMPOTENCY_DB', '2'),
|
||||
],
|
||||
|
||||
// Separate DB for rate limiting / throttling
|
||||
'throttle' => [
|
||||
'url' => env('REDIS_URL'),
|
||||
'host' => env('REDIS_HOST', '127.0.0.1'),
|
||||
'username' => env('REDIS_USERNAME'),
|
||||
'password' => env('REDIS_PASSWORD'),
|
||||
'port' => env('REDIS_PORT', '6379'),
|
||||
'database' => env('REDIS_THROTTLE_DB', '3'),
|
||||
],
|
||||
],
|
||||
|
||||
];
|
||||
62
Backend/config/filesystems.php
Normal file
62
Backend/config/filesystems.php
Normal file
@@ -0,0 +1,62 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
|
||||
'default' => env('FILESYSTEM_DISK', 'local'),
|
||||
|
||||
'disks' => [
|
||||
|
||||
'local' => [
|
||||
'driver' => 'local',
|
||||
'root' => storage_path('app/private'),
|
||||
'serve' => true,
|
||||
'throw' => true,
|
||||
],
|
||||
|
||||
'public' => [
|
||||
'driver' => 'local',
|
||||
'root' => storage_path('app/public'),
|
||||
'url' => env('APP_URL').'/storage',
|
||||
'visibility' => 'public',
|
||||
'throw' => true,
|
||||
],
|
||||
|
||||
// MinIO — used for KYC documents (encrypted before upload)
|
||||
's3' => [
|
||||
'driver' => 's3',
|
||||
'key' => env('AWS_ACCESS_KEY_ID'),
|
||||
'secret' => env('AWS_SECRET_ACCESS_KEY'),
|
||||
'region' => env('AWS_DEFAULT_REGION', 'us-east-1'),
|
||||
'bucket' => env('AWS_BUCKET'),
|
||||
'url' => env('AWS_URL'),
|
||||
'endpoint' => env('AWS_ENDPOINT'), // MinIO endpoint
|
||||
'use_path_style_endpoint' => env('AWS_USE_PATH_STYLE_ENDPOINT', true),
|
||||
'throw' => true,
|
||||
],
|
||||
|
||||
// Dedicated bucket for sensitive KYC docs (private, encrypted-at-rest)
|
||||
'kyc' => [
|
||||
'driver' => 's3',
|
||||
'key' => env('AWS_ACCESS_KEY_ID'),
|
||||
'secret' => env('AWS_SECRET_ACCESS_KEY'),
|
||||
'region' => env('AWS_DEFAULT_REGION', 'us-east-1'),
|
||||
'bucket' => env('WASL_KYC_BUCKET', 'wasl-kyc'),
|
||||
'url' => env('AWS_URL'),
|
||||
'endpoint' => env('AWS_ENDPOINT'),
|
||||
'use_path_style_endpoint' => env('AWS_USE_PATH_STYLE_ENDPOINT', true),
|
||||
'throw' => true,
|
||||
'visibility' => 'private',
|
||||
],
|
||||
|
||||
],
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Symbolic Links
|
||||
|--------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
'links' => [
|
||||
public_path('storage') => storage_path('app/public'),
|
||||
],
|
||||
];
|
||||
57
Backend/config/jwt.php
Normal file
57
Backend/config/jwt.php
Normal file
@@ -0,0 +1,57 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* |--------------------------------------------------------------------------
|
||||
* | JWT Auth (tymon/jwt-auth) configuration for WASL mobile API
|
||||
* | Mobile clients receive short-lived JWT access tokens. Refresh tokens are
|
||||
* | rotated and stored hashed server-side (see Security module).
|
||||
* |--------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
return [
|
||||
|
||||
'secret' => env('JWT_SECRET'),
|
||||
|
||||
// Asymmetric keys (recommended for production)
|
||||
'keys' => [
|
||||
'public' => env('JWT_PUBLIC_KEY'),
|
||||
'private' => env('JWT_PRIVATE_KEY'),
|
||||
'passphrase' => env('JWT_PASSPHRASE'),
|
||||
],
|
||||
|
||||
'ttl' => env('JWT_TTL', 15), // 15 minutes — short-lived access token
|
||||
|
||||
'refresh_ttl' => env('JWT_REFRESH_TTL', 20160), // 14 days
|
||||
|
||||
'algo' => env('JWT_ALGO', 'HS256'),
|
||||
|
||||
'required_claims' => [
|
||||
'iss',
|
||||
'iat',
|
||||
'exp',
|
||||
'nbf',
|
||||
'sub',
|
||||
'jti',
|
||||
],
|
||||
|
||||
'persistent_claims' => [
|
||||
'dev', // device_id — bound to the token
|
||||
'kyc', // kyc_level — embedded for authorization checks
|
||||
],
|
||||
|
||||
'lock_subject' => true,
|
||||
|
||||
'leeway' => env('JWT_LEEWAY', 0),
|
||||
|
||||
'blacklist_enabled' => env('JWT_BLACKLIST_ENABLED', true),
|
||||
|
||||
'blacklist_grace_period' => env('JWT_BLACKLIST_GRACE_PERIOD', 0),
|
||||
|
||||
'decrypt_cookies' => false,
|
||||
|
||||
'providers' => [
|
||||
'jwt' => Tymon\JWTAuth\Providers\JWT\Lcobucci::class,
|
||||
'auth' => Tymon\JWTAuth\Providers\Auth\Illuminate::class,
|
||||
'storage' => Tymon\JWTAuth\Providers\Storage\Illuminate::class,
|
||||
],
|
||||
];
|
||||
85
Backend/config/logging.php
Normal file
85
Backend/config/logging.php
Normal file
@@ -0,0 +1,85 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
|
||||
'default' => env('LOG_CHANNEL', 'stack'),
|
||||
|
||||
'deprecations' => [
|
||||
'channel' => env('LOG_DEPRECATIONS_CHANNEL', 'null'),
|
||||
'trace' => env('LOG_DEPRECATIONS_TRACE', false),
|
||||
],
|
||||
|
||||
'channels' => [
|
||||
|
||||
'stack' => [
|
||||
'driver' => 'stack',
|
||||
'channels' => explode(',', env('LOG_STACK', 'single')),
|
||||
'ignore_exceptions' => false,
|
||||
],
|
||||
|
||||
'single' => [
|
||||
'driver' => 'single',
|
||||
'path' => storage_path('logs/laravel.log'),
|
||||
'level' => env('LOG_LEVEL', 'debug'),
|
||||
'replace_placeholders' => true,
|
||||
],
|
||||
|
||||
'daily' => [
|
||||
'driver' => 'daily',
|
||||
'path' => storage_path('logs/laravel.log'),
|
||||
'level' => env('LOG_LEVEL', 'debug'),
|
||||
'days' => env('LOG_DAILY_DAYS', 14),
|
||||
'replace_placeholders' => true,
|
||||
],
|
||||
|
||||
// Separate channel for financial transaction logs (longer retention)
|
||||
'transactions' => [
|
||||
'driver' => 'daily',
|
||||
'path' => storage_path('logs/transactions.log'),
|
||||
'level' => 'info',
|
||||
'days' => 90,
|
||||
'replace_placeholders' => true,
|
||||
],
|
||||
|
||||
// Security events (login, failed login, fraud alerts) — long retention
|
||||
'security' => [
|
||||
'driver' => 'daily',
|
||||
'path' => storage_path('logs/security.log'),
|
||||
'level' => 'info',
|
||||
'days' => 180,
|
||||
'replace_placeholders' => true,
|
||||
],
|
||||
|
||||
'stderr' => [
|
||||
'driver' => 'monolog',
|
||||
'level' => env('LOG_LEVEL', 'info'),
|
||||
'handler' => Monolog\Handler\StreamHandler::class,
|
||||
'formatter' => env('LOG_STDERR_FORMATTER'),
|
||||
'with' => [
|
||||
'stream' => 'php://stderr',
|
||||
],
|
||||
'processors' => [
|
||||
\App\Logging\MaskSensitiveProcessor::class,
|
||||
],
|
||||
],
|
||||
|
||||
'syslog' => [
|
||||
'driver' => 'syslog',
|
||||
'level' => env('LOG_LEVEL', 'debug'),
|
||||
],
|
||||
|
||||
'errorlog' => [
|
||||
'driver' => 'errorlog',
|
||||
'level' => env('LOG_LEVEL', 'debug'),
|
||||
],
|
||||
|
||||
'null' => [
|
||||
'driver' => 'monolog',
|
||||
'handler' => Monolog\Handler\NullHandler::class,
|
||||
],
|
||||
|
||||
'emergency' => [
|
||||
'path' => storage_path('logs/laravel.log'),
|
||||
],
|
||||
],
|
||||
];
|
||||
146
Backend/config/octane.php
Normal file
146
Backend/config/octane.php
Normal file
@@ -0,0 +1,146 @@
|
||||
<?php
|
||||
|
||||
use Laravel\Octane\Contracts\Operation;
|
||||
use Laravel\Octane\Events\RequestHandled;
|
||||
use Laravel\Octane\Events\RequestReceived;
|
||||
use Laravel\Octane\Events\RequestTerminated;
|
||||
use Laravel\Octane\Events\TaskReceived;
|
||||
use Laravel\Octane\Events\TaskTerminated;
|
||||
use Laravel\Octane\Events\TickReceived;
|
||||
use Laravel\Octane\Events\TickTerminated;
|
||||
use Laravel\Octane\Octane;
|
||||
|
||||
return [
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Octane Servers
|
||||
|--------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
'servers' => [
|
||||
// RoadRunner via RPC socket
|
||||
'roadrunner' => [
|
||||
'rpc' => [
|
||||
'tcp' => '127.0.0.1:7233',
|
||||
],
|
||||
'logs' => [
|
||||
'mode' => env('OCTANE_LOGS_MODE', 'production' === env('APP_ENV') ? 'none' : 'dev'),
|
||||
'level' => env('OCTANE_LOGS_LEVEL', 'info'),
|
||||
],
|
||||
'reload' => [
|
||||
'watch' => env('OCTANE_RELOAD_WATCH', 'app,config,database,resources/views,routes'),
|
||||
'interval' => env('OCTANE_RELOAD_INTERVAL', 2000),
|
||||
],
|
||||
'http2' => [
|
||||
'enabled' => env('OCTANE_HTTP2_ENABLED', true),
|
||||
],
|
||||
'maxTotalWorkerMemory' => env('OCTANE_MAX_TOTAL_WORKER_MEMORY', 512), // MB — flush worker if exceeded
|
||||
'worker' => [
|
||||
'maxRequestCount' => env('OCTANE_WORKER_MAX_REQUEST_COUNT', 10000), // recycle to clear leaks
|
||||
'maxMemoryMB' => env('OCTANE_WORKER_MAX_MEMORY_MB', 512),
|
||||
],
|
||||
],
|
||||
|
||||
// Swoole driver — recommended for WASL (best performance)
|
||||
'swoole' => [
|
||||
'mode' => env('OCTANE_MODE', 'SWOOLE_PROCESS'),
|
||||
'options' => [
|
||||
'log_file' => storage_path('logs/swoole.log'),
|
||||
'package_max_length' => 8 * 1024 * 1024, // 8 MB
|
||||
'worker_num' => env('OCTANE_WORKER_NUM', swoole_cpu_num()),
|
||||
'task_worker_num' => env('OCTANE_TASK_WORKER_NUM', swoole_cpu_num()),
|
||||
'max_request' => env('OCTANE_MAX_REQUEST', 10000),
|
||||
'max_request_grace' => env('OCTANE_MAX_REQUEST_GRACE', 1000),
|
||||
'max_request_execution_time' => env('OCTANE_MAX_REQUEST_EXECUTION_TIME', 30),
|
||||
'reload_async' => true,
|
||||
'enable_coroutine' => true,
|
||||
'send_timeout' => 10,
|
||||
],
|
||||
'cache' => [
|
||||
'rows' => env('OCTANE_CACHE_ROWS', 1000),
|
||||
'bytes' => env('OCTANE_CACHE_BYTES', 10485760), // 10 MB per worker
|
||||
],
|
||||
'websocket' => [
|
||||
'enabled' => env('OCTANE_WEBSOCKET_ENABLED', false),
|
||||
],
|
||||
'hot' => [
|
||||
'enable' => env('OCTANE_HOT_RELOAD', 'local' === env('APP_ENV')),
|
||||
'watch' => ['app', 'config', 'resources/views', 'routes'],
|
||||
],
|
||||
],
|
||||
],
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Octane Cache Table
|
||||
|--------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
'cache' => [
|
||||
'rows' => env('OCTANE_CACHE_ROWS', 1000),
|
||||
],
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Octane Watchers / Listeners
|
||||
| Flush state between requests to prevent leaks across requests in the
|
||||
| long-running Octane process (critical for financial correctness).
|
||||
|--------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
'listeners' => [
|
||||
RequestReceived::class => [
|
||||
...Octane::prepareApplicationForNextOperation(),
|
||||
...Octane::prepareApplicationForNextRequest(),
|
||||
],
|
||||
RequestHandled::class => [
|
||||
//
|
||||
],
|
||||
RequestTerminated::class => [
|
||||
// Clear request-scoped state between requests
|
||||
],
|
||||
TaskReceived::class => [
|
||||
...Octane::prepareApplicationForNextOperation(),
|
||||
],
|
||||
TaskTerminated::class => [
|
||||
//
|
||||
],
|
||||
TickReceived::class => [
|
||||
...Octane::prepareApplicationForNextOperation(),
|
||||
],
|
||||
TickTerminated::class => [
|
||||
//
|
||||
],
|
||||
Operation::class => [
|
||||
// Octane::flushState(),
|
||||
],
|
||||
],
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Warm Specific Tags / Fifo
|
||||
|--------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
'warm' => [
|
||||
...Octane::defaultServicesToWarm(),
|
||||
'view' => fn ($app) => $app->make('view'),
|
||||
],
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Garbage Collection Threshold (prevent memory leaks)
|
||||
|--------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
'garbage_collection_threshold' => env('OCTANE_GARBAGE_COLLECTION_THRESHOLD', 50),
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Maximum Execution Time (seconds) — financial ops should be fast
|
||||
|--------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
'max_execution_time' => env('OCTANE_MAX_EXECUTION_TIME', 30),
|
||||
];
|
||||
71
Backend/config/permission.php
Normal file
71
Backend/config/permission.php
Normal file
@@ -0,0 +1,71 @@
|
||||
<?php
|
||||
|
||||
use App\Enums\Role;
|
||||
|
||||
return [
|
||||
|
||||
'models' => [
|
||||
|
||||
/*
|
||||
* When using the "HasPermissions" trait from this package, we need to
|
||||
* know which entity should be used to retrieve your permissions.
|
||||
*/
|
||||
|
||||
'permission' => Spatie\Permission\Models\Permission::class,
|
||||
|
||||
/*
|
||||
* When using the "HasRoles" trait from this package, we need to
|
||||
* know which entity should be used to retrieve your roles.
|
||||
*/
|
||||
|
||||
'role' => Spatie\Permission\Models\Role::class,
|
||||
|
||||
],
|
||||
|
||||
'table_names' => [
|
||||
|
||||
'roles' => 'roles',
|
||||
'permissions' => 'permissions',
|
||||
'model_has_permissions' => 'model_has_permissions',
|
||||
'model_has_roles' => 'model_has_roles',
|
||||
'role_has_permissions' => 'role_has_permissions',
|
||||
],
|
||||
|
||||
'column_names' => [
|
||||
'role_pivot_key' => null,
|
||||
'permission_pivot_key' => null,
|
||||
'model_morph_key' => 'model_id',
|
||||
'team_foreign_key' => 'team_id',
|
||||
],
|
||||
|
||||
/*
|
||||
* When set to true, the method for checking permissions will be registered
|
||||
* on the gate. Set to false to disable.
|
||||
*/
|
||||
|
||||
'register_permission_check_method' => true,
|
||||
|
||||
/*
|
||||
* When set to true, required permissions will be checked on the model
|
||||
* to determine access.
|
||||
*/
|
||||
|
||||
'teams' => false,
|
||||
|
||||
'use_passport_client_credentials' => false,
|
||||
|
||||
'display_permission_in_exception' => (bool) env('APP_DEBUG', false),
|
||||
|
||||
'display_role_in_exception' => (bool) env('APP_DEBUG', false),
|
||||
|
||||
'enable_wildcard_permission' => false,
|
||||
|
||||
'cache' => [
|
||||
|
||||
'expiration_time' => \DateInterval::createFromDateString('24 hours'),
|
||||
|
||||
'key' => 'spatie.permission.cache',
|
||||
|
||||
'store' => 'default',
|
||||
],
|
||||
];
|
||||
132
Backend/config/wasl.php
Normal file
132
Backend/config/wasl.php
Normal file
@@ -0,0 +1,132 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* |--------------------------------------------------------------------------
|
||||
* | WASL — Domain-specific configuration for the wallet platform
|
||||
* | Centralized business rules, limits, and feature flags. Changing behavior
|
||||
* | should NEVER require a code change — only an .env flip.
|
||||
* |--------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
return [
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Wallet & Money
|
||||
|--------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
'wallet' => [
|
||||
'default_currency' => env('WASL_DEFAULT_CURRENCY', 'SYP'),
|
||||
'supported_currencies' => ['SYP', 'USD', 'EUR', 'AED'],
|
||||
|
||||
// Per-KYC-tier limits (in minor units of SYP)
|
||||
// Tier 0: no KYC, Tier 1: phone verified, Tier 2: ID verified, Tier 3: full
|
||||
'limits' => [
|
||||
0 => ['balance' => 0, 'daily_tx' => 0, 'monthly_tx' => 0],
|
||||
1 => ['balance' => 5000000, 'daily_tx' => 1000000, 'monthly_tx' => 20000000], // 50k / 10k / 200k SYP
|
||||
2 => ['balance' => 50000000, 'daily_tx' => 10000000, 'monthly_tx' => 200000000], // 500k / 100k / 2M SYP
|
||||
3 => ['balance' => 500000000, 'daily_tx' => 100000000, 'monthly_tx' => 2000000000], // 5M / 1M / 20M SYP
|
||||
],
|
||||
|
||||
// Fees in minor units (basis points * amount, or flat)
|
||||
'fees' => [
|
||||
'p2p' => [
|
||||
'enabled' => env('WASL_FEE_P2P_ENABLED', false),
|
||||
'percent' => env('WASL_FEE_P2P_PERCENT', 0), // e.g. 0.5 = 0.5%
|
||||
'flat_minor' => env('WASL_FEE_P2P_FLAT', 0),
|
||||
'min_minor' => env('WASL_FEE_P2P_MIN', 0),
|
||||
'max_minor' => env('WASL_FEE_P2P_MAX', 0),
|
||||
],
|
||||
],
|
||||
],
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Security
|
||||
|--------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
'security' => [
|
||||
'pin' => [
|
||||
'min_length' => 6,
|
||||
'max_attempts' => env('WASL_PIN_MAX_ATTEMPTS', 5),
|
||||
'lock_minutes' => env('WASL_PIN_LOCK_MINUTES', 30),
|
||||
'hash_algo' => 'argon2id',
|
||||
],
|
||||
|
||||
'otp' => [
|
||||
'length' => env('WASL_OTP_LENGTH', 6),
|
||||
'ttl_seconds' => env('WASL_OTP_TTL', 300), // 5 minutes
|
||||
'max_attempts' => env('WASL_OTP_MAX_ATTEMPTS', 3),
|
||||
'resend_cooldown' => env('WASL_OTP_RESEND_COOLDOWN', 60), // 1 minute
|
||||
],
|
||||
|
||||
'login' => [
|
||||
'max_attempts' => env('WASL_LOGIN_MAX_ATTEMPTS', 5),
|
||||
'lock_minutes' => env('WASL_LOGIN_LOCK_MINUTES', 30),
|
||||
],
|
||||
|
||||
// Encryption keys for field-level encryption (phone, national_id, cards)
|
||||
'encryption' => [
|
||||
'cipher' => env('WASL_ENC_CIPHER', 'aes-256-cbc'),
|
||||
// Separate key from APP_KEY to allow key rotation without re-encrypting all data
|
||||
'field_key' => env('WASL_FIELD_ENCRYPTION_KEY'),
|
||||
],
|
||||
|
||||
'idempotency' => [
|
||||
'enabled' => env('WASL_IDEMPOTENCY_ENABLED', true),
|
||||
'ttl_seconds' => env('WASL_IDEMPOTENCY_TTL', 86400), // 24h
|
||||
],
|
||||
],
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| KYC
|
||||
|--------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
'kyc' => [
|
||||
'auto_approve_in_local' => env('WASL_KYC_AUTO_APPROVE_LOCAL', false),
|
||||
'max_document_size_mb' => env('WASL_KYC_MAX_DOC_MB', 5),
|
||||
'allowed_mime_types' => ['image/jpeg', 'image/png', 'application/pdf'],
|
||||
],
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Rate Limiting (per-IP / per-user)
|
||||
|--------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
'throttle' => [
|
||||
'login' => ['max' => 5, 'minutes' => 1],
|
||||
'otp_request' => ['max' => 3, 'minutes' => 1],
|
||||
'transfer' => ['max' => 10, 'minutes' => 60],
|
||||
'api' => ['max' => 60, 'minutes' => 1],
|
||||
],
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Reference Code Format
|
||||
| P2P transfer reference codes visible to users.
|
||||
|--------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
'reference' => [
|
||||
'prefix' => env('WASL_REF_PREFIX', 'WASL'),
|
||||
'length' => env('WASL_REF_LENGTH', 8), // alphanumeric chars after prefix
|
||||
],
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Feature Flags
|
||||
|--------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
'features' => [
|
||||
'crypto' => env('WASL_FEATURE_CRYPTO', false),
|
||||
'cards' => env('WASL_FEATURE_CARDS', false),
|
||||
'international' => env('WASL_FEATURE_INTERNATIONAL', false),
|
||||
'merchant' => env('WASL_FEATURE_MERCHANT', true),
|
||||
],
|
||||
];
|
||||
Reference in New Issue
Block a user