Getting Started
Getting Started
This guide takes a fresh server to a working QoinPay Enterprise installation. QoinPay ships as a self-contained PHP application that you deploy on your own infrastructure and connect to a MariaDB or MySQL database. There is no mandatory outbound connectivity beyond an initial license activation, which can also be performed offline for air-gapped sites.
Environment requirements
Provision a single host (or the app tier of a multi-node cluster) that meets the following:
- PHP 8.2 or newer (8.5 recommended) with the extensions:
pdo_mysql,openssl,sodium,mbstring,gd, andzip. Thesodiumextension is mandatory — it backs license verification, TOTP secret encryption, and webhook signing. - MariaDB 10.6+ or MySQL 8.0+, using the
utf8mb4character set andInnoDBstorage engine. - A web server (nginx or Apache) with a PHP-FPM pool, or the bundled PHP runtime for evaluation only.
- A writable
storage/directory tree for logs, generated payslips, queued jobs, and the activated license file. The web server user must ownstorage/andstorage/cache/.
Verify extensions before you begin:
php -m | grep -E 'pdo_mysql|sodium|openssl|mbstring|gd|zip'
php -r 'echo PHP_VERSION, PHP_EOL;'
1. Unpack and set permissions
Extract the release tarball into your deployment root and set ownership so PHP-FPM can write runtime state but cannot modify code:
tar -xzf qoinpay-enterprise-<version>.tar.gz -C /opt/qoinpay
cd /opt/qoinpay
chown -R www-data:www-data storage/
chmod -R 750 storage/
Point your web server document root at public/. Never expose storage/, config/, or vendor/ to the web.
2. Configure the database connection
Create an empty database and a dedicated user with full privileges on it:
CREATE DATABASE qoinpay CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
CREATE USER 'qoinpay'@'10.%' IDENTIFIED BY 'change-me';
GRANT ALL PRIVILEGES ON qoinpay.* TO 'qoinpay'@'10.%';
Copy .env.example to .env and set the connection and application secrets:
APP_ENV=production
APP_KEY= # generated by the wizard if blank
DB_HOST=10.0.1.20
DB_PORT=3306
DB_NAME=qoinpay
DB_USER=qoinpay
DB_PASS=change-me
3. Run the first-run install wizard
Browse to https://your-host/install. The wizard runs only while the application is uninitialised and disables itself once setup completes. It performs, in order:
- Preflight — re-checks PHP version, extensions, and
storage/writability, refusing to continue on any failure. - Application key — generates and writes
APP_KEYif one is not already present. This key encrypts secrets at rest; back it up, because losing it makes stored TOTP secrets and other encrypted columns unrecoverable. - Database migrations — applies the schema (see below).
To run migrations from the CLI instead — required for automated or air-gapped deployments — use:
php bin/console migrate --no-interaction
Migrations are idempotent and versioned; running them again after an upgrade applies only the pending changes. Check status with php bin/console migrate:status.
4. Create the first administrator
The first admin is created through the wizard or non-interactively:
php bin/console admin:create --email=ops@example.com --name="Ops Admin"
Two-factor authentication (TOTP) is mandatory for every administrator and cannot be disabled. On first sign-in the account presents a QR code and a set of one-time recovery codes. Store the recovery codes offline before leaving the screen — they are the only way back in if the authenticator device is lost. TOTP secrets are stored encrypted with APP_KEY.
5. Activate your license
QoinPay runs unlicensed in a restricted evaluation mode until activated. Enter your license key (QPAY-XXXXX-XXXXX-XXXXX) under Settings → Licensing, or drop your .qplic file for offline sites. See Licensing & Activation for the full flow.
6. Set up the cron worker
Background work — payroll run processing, payslip generation, webhook delivery, license heartbeats, and scheduled reports — is handled by a queue worker driven by a single cron entry. Install it for the web server user:
* * * * * cd /opt/qoinpay && php bin/console schedule:run >> storage/logs/cron.log 2>&1
The schedule:run command is a lightweight dispatcher; it claims due jobs and returns quickly, so a one-minute cadence is correct. For higher throughput run a long-lived worker under a process supervisor instead:
php bin/console queue:work --sleep=1 --max-jobs=500
Supervise it with systemd or supervisord so it restarts on exit. Never run both a long-lived worker and the cron dispatcher against the same queue.
Go-live checklist
- [ ] TLS terminates in front of
public/with a valid certificate; HTTP redirects to HTTPS. - [ ]
APP_KEYand the.envfile are backed up in your secrets manager. - [ ]
migrate:statusshows no pending migrations. - [ ] License shows Active with the expected seat count.
- [ ] The cron worker is installed and
storage/logs/cron.logshows recent activity. - [ ] Automated database and
storage/backups are scheduled and test-restored. - [ ] The
/installroute is confirmed disabled.
With these complete, your installation is production-ready. Continue with Deployment for hardening and sizing, and the API Overview to integrate surrounding systems.