# Setup guide
## 1. Requirements
- XAMPP (PHP 8.1+) or PHP + MySQL 8 separately, with PDO MySQL, mbstring, and OpenSSL enabled
- Composer
- Browser
## 2. Local run (XAMPP)
1. Start Apache + MySQL in XAMPP Control Panel.
2. Copy project to `C:\xampp\htdocs\cashless`.
3. From the project root, run `composer install` to install PHPMailer.
4. Copy `.env.example` to `.env` and configure local database credentials.
5. Set `APP_ENV=local` and a randomly generated `INSTALLER_SECRET` of at least 32 characters. The installer returns 404 unless this secret is set; it also requires the secret on the install POST.
6. Visit `http://localhost/cashless/public/install.php` and run the installer only against a fresh disposable local database.
7. For local demos, set `DEMO_ACCOUNTS_ENABLED=true`, keep usernames `demo.admin`, `demo.cashier`, `demo.customer`, and configure a different random password of at least 16 characters for each in the private `.env`. Generate passwords securely (for example, `php -r "echo bin2hex(random_bytes(16)), PHP_EOL;"`). The installer creates or repairs the three marked accounts only in local development and stores password hashes. Production demo access is disabled by default; an exact trusted client IP allowlist can restrict it. To show and enable shared credentials publicly, set both `DEMO_ACCOUNTS_ENABLED=true` and `DEMO_CREDENTIALS_PUBLIC=true` on a separate demo deployment with no real customer or payment data. Public demo administrators can change application data. Provision and mark the demo users separately; credentials display only when all active database hashes match the configured passwords. Login never creates or resets accounts.
8. Remove `INSTALLER_SECRET` and initial-admin bootstrap values after setup; keep local demo values only in the private local `.env`. Configure Gmail SMTP before testing registration; registration OTPs are sent only by email and are never written to logs.
## 3. Manual DB import
- Back up any existing database before importing or changing the schema. For an existing deployment, test schema changes against a restored copy first.
## 4. No PHP installed?
- Install XAMPP from apachefriends.org. `php -v` only works if PHP is on PATH; XAMPP still runs via Apache.

## Email OTP configuration
- Install dependencies from the project root with `composer install` (or `composer install --no-dev --optimize-autoloader` for production deployments).
- In `.env`, set `EMAIL_SMTP_USERNAME` to the sending Gmail address and `EMAIL_FROM` to the same address. Keep `EMAIL_SMTP_HOST=smtp.gmail.com`, `EMAIL_SMTP_PORT=587`, and `EMAIL_FROM_NAME=Cashless Food Ordering`.
- Sign in to that Google account, enable 2-Step Verification, then create an App Password at [Google App Passwords](https://myaccount.google.com/apppasswords). Put the generated App Password in `EMAIL_SMTP_PASSWORD` (remove any display spaces). Do not use the regular Gmail password. The mailer uses authenticated STARTTLS.
- Restart the PHP/Apache server after saving `.env`, then register a new account or resend the verification code to test delivery. If delivery fails, confirm the username/from address, regenerate the App Password if needed, check that the host can make outbound TLS connections to `smtp.gmail.com:587`, and inspect the server log for the generic delivery failure entry. Do not enable SMTP protocol debugging or log the exception response in production.
- Keep the Gmail account and App Password out of source control and frontend code. Do not put secrets in `.env.example`. Registration is unavailable until email delivery is configured. OTPs are hashed in the database and are never written to logs or rendered except for the destination address and user-entered code.
- SMS settings remain optional for the separate account phone-number-change flow; they are not used for registration.
- For an existing database, back it up and restore-test a separate copy, then apply the additive `database/08_schema_email_verification.sql` migration once. It adds the verification fields while preserving existing active accounts; pending customer accounts must verify their email. Fresh local installations receive the fields from the schema and installer.
- Existing databases must also apply `database/11_email_otp_rate_limits.sql` to create the persistent coordination table used for race-safe email/IP rate limits. Fresh installations include it automatically.
- For production, run `composer install --no-dev --optimize-autoloader` during deployment, store SMTP settings in the host's private environment/secret manager, and ensure the web root remains `public/`. Never expose `vendor/`, `.env`, or deployment secrets from the repository root.
- Never enable OTP logging in production; local-only OTP logging applies only to the legacy phone-number-change SMS flow.
## Optional SMS phone-number change configuration
- Set `APP_ENV=production` in the private server environment before deployment.
- Configure `SMS_PROVIDER=generic_http`, `SMS_API_URL` (HTTPS), `SMS_API_KEY`, `SMS_API_SECRET`, and `SMS_SENDER` in the PHP/Apache environment. The generic adapter sends JSON fields `to`, `from`, and `message`, with a Bearer API key and an `X-SMS-Signature` HMAC-SHA256 header.
- Keep SMS credentials out of source control. SMS is used only for phone-number changes, not registration. `DEV_OTP_LOG` is honored only while `APP_ENV` is `local`; it applies only to that legacy SMS flow and must never be enabled in production.
- The web installer only runs when `APP_ENV=local`. Do not run it against production data; apply reviewed schema changes separately after a verified backup.
- The local installer also requires a private `INSTALLER_SECRET` of at least 32 characters. Remove it immediately after setup. Production/staging sessions always set the Secure cookie flag; enforce HTTPS at the web server/load balancer and verify TLS-proxy behavior before launch.
- Checkout remains usable without Xendit when manual GCash is configured with the official merchant name, number, and/or QR image. Customer checkout is GCash-only; counter payments are not offered. Manual QR transfers stay unpaid until an Administrator or authorized Cashier verifies the transfer in the merchant account; a screenshot is not proof.
- For an existing database, back up and restore-test a separate copy, then apply `database/07_payment_checkouts.sql` and `database/10_payment_deeplink.sql` for Xendit online GCash, `database/09_manual_payment_reviews.sql` for manual-payment review fields, `database/12_payment_receipt_ocr.sql` for receipt OCR, digest, normalized-reference, and review-history fields, and `database/13_payment_duplicate_reference_flag.sql` for duplicate-reference review fields. These additive migrations preserve existing orders and payments. Fresh disposable databases receive the payment schema through the installer.
- Sign up for Xendit and use Test Mode first. Official [Xendit account guidance](https://docs.xendit.co/docs/create-account.md) says live processing requires business verification; submit the Philippines business documents, authorized representative/identity documents, and proof of business requested in the dashboard. The guide states review can take up to 14 working days. Activate GCash under Payment Channels and create a restricted test secret API key.
- Register `https://your-domain/xendit-webhook.php` in Xendit Dashboard > Settings > Developers > Webhooks and enable Payment Requests v3 payment-status topics, including `payment.capture`, `payment.failure`, `payment.expiry`, and `payment_request.expiry`. Store the dashboard callback token, secret API key, and business ID in the private server environment only.
- Set `XENDIT_ENABLED=true`, `XENDIT_SECRET_API_KEY`, `XENDIT_WEBHOOK_TOKEN`, and `XENDIT_BUSINESS_ID` after the migration, channel, webhook, and public HTTPS `APP_URL` are ready. Never put provider secrets in templates, frontend JavaScript, or logs. Localhost cannot receive Xendit webhooks; use a public HTTPS staging endpoint/tunnel and Test Mode for callback tests.
- Checkout stores an immutable server-priced snapshot before starting payment, blocks a second unresolved payment attempt for the same customer, and creates the order only after authenticated webhook or provider API verification confirms the exact reference, amount, PHP currency, PH country, GCash channel, business ID, and succeeded state. The browser return URL is not treated as proof. Verified webhooks and status checks create the order idempotently so customers can close their browser without losing a paid order. If provider creation times out ambiguously, the attempt is not automatically repeated; reconcile it with Xendit support/dashboard to avoid double charging. If order creation fails after verified payment, the saved checkout can safely retry order creation without creating another payment request.
- Historical manual-GCash orders and payment history are preserved. The `GCASH_MANUAL_*` settings now also enable new manual GCash checkout when configured with the merchant details; do not remove historical payment columns, methods, or records.
- For manual GCash checkout, set `GCASH_MANUAL_ENABLED=true`, `GCASH_MERCHANT_NAME`, and `GCASH_MERCHANT_NUMBER` and/or `GCASH_QR_IMAGE` (an image under `public/assets`, such as `assets/gcash-qr.jpg`). Customer receipts are held outside `public/` and can be accessed by administrators, authorized cashiers for pending receipts, and the owning customer. QR transfers require merchant verification and do not provide automatic payment confirmation.
- For existing databases, back up and restore-test a separate copy before applying `database/14_two_role_admin_payment_verification.sql` and then `database/15_cashier_demo_accounts.sql` after the prerequisite payment-review schema migrations. Migration 14 disables legacy cashier/staff users without converting or deleting them; migration 15 restores the limited Cashier role and adds the demo marker. Confirm that legacy accounts remain disabled.
- To enable OCR, install Tesseract OCR on the PHP server. Set `TESSERACT_PATH` to its executable path, or leave it blank to check common Windows install paths and then the server PATH. Enable PHP GD and EXIF for image preprocessing and orientation correction; if those extensions are unavailable, Tesseract still runs its original-image layout passes. If Tesseract is not installed, customers can still enter the receipt reference manually and retry later. OCR is advisory and never confirms receipt of funds. Receipt image upload supports JPEG, PNG, and WebP (PDF remains accepted); files are validated and stored outside the public web root.
- Configure Xendit settings for automatic online GCash checkout and server-side payment verification. When Xendit is unavailable, configured manual GCash remains available; if neither method is configured, checkout is blocked with a setup message.
