# InnoInvoice SaaS upgrade — installation and operations

## 1. Protect your existing data first

1. Stop using the application while backing it up.
2. In phpMyAdmin, select the existing InnoInvoice database and use **Export → Quick → SQL**. Save the dump outside the web root.
3. Copy the whole existing project folder as a file backup.
4. Do not import `database/schema.sql` over an existing installation. It is the original starter schema; use only `database/migrations/001_saas_upgrade.sql` for this upgrade.

## 2. Apply the database migration

1. Confirm `config/database.php` points to the intended database.
2. Open phpMyAdmin, select that database, choose **Import**, and import `database/migrations/001_saas_upgrade.sql`.
3. This migration is designed for a one-time application to the schema included with the project. It adds SaaS tables and two user columns; it does not drop or replace customer, invoice, invoice-item, payment, expense, or user records.
4. The migration seeds sample Starter/Professional/Business plans in NGN. Review and change the prices in the Super Admin plan page before accepting real payments.
5. If your live database has drifted from the supplied schema, inspect its columns and constraints and adapt the migration on a copy first. Do not blindly re-run the migration; `ALTER TABLE ... ADD COLUMN` is intentionally a one-time operation.

The migration attempts to preserve legacy users whose old `subscription_status` is `active` and whose old `subscription_expires_at` is still in the future. Other users must purchase a subscription to unlock paid features. Existing business records remain stored.

## 3. Local XAMPP

- Put the project in `C:\xampp\htdocs\innoinvoice`.
- Start Apache and MySQL.
- Import the migration as above.
- Update `config/database.php` if your database name differs.
- Install dependencies from the project directory if Composer is installed:
  `composer install`
- Open `http://localhost/innoinvoice/public/`.
- Create the first Super Admin from a terminal:
  `php bin/create-super-admin.php`
- Open `http://localhost/innoinvoice/public/index.php?page=admin-login`.

The CLI utility refuses to create another admin if one already exists. Keep administrator credentials private.

## 4. Paystack test mode

1. Use a Paystack test account and its **test secret key** only.
2. Set `INNO_PAYSTACK_SECRET` in the PHP/Apache environment, or enter the test secret in `config/config.php`'s `paystack_secret_key` field for local testing. Never put a secret key in JavaScript or commit it.
3. Confirm PHP cURL is enabled in XAMPP.
4. In Paystack dashboard, configure the webhook URL to:
   `https://YOUR_PUBLIC_HOST/index.php?page=paystack-webhook`
   A local `localhost` URL cannot receive Paystack webhooks from the public internet; use the verified callback flow locally or a secure HTTPS tunnel for webhook testing.
5. The callback always verifies the reference with Paystack and compares exact amount in minor units and currency before granting access. Webhooks validate `x-paystack-signature` with HMAC-SHA512 and deduplicate events.
6. Test successful, failed, and duplicate events using Paystack test transactions before live mode.

**Live mode:** switch to live credentials only after test acceptance, HTTPS, backups, correct callback/webhook URLs, SMTP, and production database permissions are confirmed. Never test a live payment with an unverified webhook endpoint.

## 5. Recurring billing limitation

The current implementation performs a real Paystack one-time transaction for each subscription period and supports manual renewal. It does **not** simulate automatic recurring charges. Automatic billing requires a Paystack subscription plan created in the Paystack dashboard, a valid plan code, customer authorization suitable for recurring charges, and handling of the subscription/invoice/payment webhook events. Configure those with your Paystack account and implement/verify the recurring-plan linking before advertising automatic renewal. The user's renewal preference control does not claim to cancel a remote Paystack recurring subscription.

## 6. SMTP email

- Install PHPMailer using `composer install`.
- Configure `INNO_SMTP_HOST`, `INNO_SMTP_PORT` (usually 587), `INNO_SMTP_USERNAME`, `INNO_SMTP_PASSWORD`, `INNO_SMTP_ENCRYPTION` (`tls`), `INNO_SMTP_FROM_EMAIL`, and `INNO_SMTP_FROM_NAME` in the hosting environment.
- For cPanel, use the mailbox's SMTP host and credentials from **Email Accounts → Connect Devices**. Require TLS and use a strong mailbox password.
- `email_logs` records successful handoff to SMTP or a failure. A successful SMTP handoff is not proof that the recipient's mailbox delivered or displayed the message.
- `bin/cron-subscriptions.php` sends expiry reminders and moves expired subscriptions through the configured grace policy. Run it from cron, e.g. every day at 09:00:
  `php /home/CPANELUSER/path-to-project/bin/cron-subscriptions.php`
- For XAMPP, run it manually from a command prompt during testing. Set up Windows Task Scheduler if you need recurring local execution.

## 7. Server-generated PDF documents

Install Dompdf with Composer. On invoice/quotation/receipt view pages, use **View PDF** or **Download PDF**. The routes query documents by both document ID and authenticated user ID. Emailing a PDF attachment additionally requires PHPMailer and SMTP. If Composer dependencies are absent, PDF routes show a configuration error instead of pretending to create a file.

## 8. cPanel deployment

1. Back up the existing database and files.
2. Prefer setting the domain/subdomain document root to the project's `public/` folder so `app/`, `config/`, `database/`, `bin/`, `vendor/`, and `storage/` are outside the web root.
3. If your hosting requires the project under `public_html`, do not expose config/database/migration files publicly; configure server rules or move private directories outside `public_html`. Do not rely on hiding navigation.
4. Create a database/user in cPanel, grant privileges, update `config/database.php`, and import the one-time migration.
5. Use cPanel Terminal/SSH to run `composer install --no-dev --optimize-autoloader` and `php bin/create-super-admin.php`.
6. Set secrets as environment variables where supported. If you must place them in a PHP config file, restrict file access and never upload/share that file publicly.
7. Enable HTTPS, configure the Paystack webhook URL, configure SMTP, schedule the cron task, and test using test-mode credentials first.

## 9. Security and operational notes

- Existing user-owned queries are retained and new document routes require the authenticated user to own the document.
- Admin accounts live in `admin_users`, separate from business users. Public registration cannot create admin accounts.
- All state-changing admin forms use CSRF tokens and administrative actions are written to `audit_logs`.
- Business feature authorization is enforced server-side; unpaid/expired/suspended users cannot access protected business pages by entering URLs directly.
- `subscription_payments.amount_minor` stores kobo/cents and successful transaction amount/currency must exactly match the saved expected values.
- The application does not store card numbers or CVVs.
- Configure HTTPS in production; protect backups; monitor logs; keep PHP, Composer dependencies, and the hosting OS patched.
- Email verification and password-reset token tables are included in the migration, but complete public email-verification/password-reset screens are not yet wired into the existing router.
- Subscription upgrade/downgrade proration, automated Paystack recurring subscriptions, remote Paystack cancellation, and a complete administrator change-plan workflow still require implementation/configuration before being advertised as available.
