# Billing, payments, and invoices

## Payment methods

Super Admins configure billing at `/superadmin/settings`.

- Enable or disable Razorpay and manual payments independently. At least one method must remain enabled; disabled methods are also rejected by the server.
- Razorpay's Key ID, API key secret, and webhook secret are stored encrypted with AES-256-GCM. The encryption key is derived from `APP_KEY`.
- Manual payment instructions, UPI ID, and QR image are configured in the same settings page. Store owners submit a UTR and a JPG, PNG, or WebP screenshot up to 3 MiB. Proof files are randomly named in private storage and can only be viewed by a Super Admin.
- Manual submissions remain pending until a Super Admin approves or rejects them. Approval activates/extends the subscription once, creates an invoice, records an audit event, and emails the owner. Rejection stores the review reason and emails the owner.

Set a stable, private `APP_KEY` in the application `.env` before saving gateway secrets. For a new environment, generate a value with:

```powershell
php -r "echo bin2hex(random_bytes(32)), PHP_EOL;"
```

Keep the same key across deployments and back it up securely. Changing or losing it makes already-encrypted payment credentials unreadable.

Razorpay orders are created by the server using the selected plan price from MySQL. The browser callback is verified server-side with the Razorpay payment signature and the captured amount. Signed webhook events are accepted at `POST /webhooks/razorpay`; `payment.captured`, `payment.failed`, and `order.paid` are recorded in `webhook_logs` and processed idempotently.

The application stylesheet is built locally and served from `public/assets/css/app.css`; no Tailwind, Alpine, or web-font CDN is used for application UI. Rebuild it after changing templates or theme classes:

```powershell
npm ci
npm run build:css
```

## Test the payment code

The integration test provisions and migrates a temporary MySQL database, runs the app over a local HTTP server, and uses a mock Razorpay transport and local mock SMTP server. It exercises manual submission/review, CSRF and role checks, invoice access, signature vectors, amount mismatch, webhook replay, and disabled methods:

```powershell
C:\xampp\php\php.exe tests\payments_test.php
```

This verifies the application flow without charging a card or contacting Razorpay. The live Razorpay API and hosted checkout network were not exercised in this test environment.

## Exercise Razorpay test mode

1. Use the Razorpay Dashboard in **Test Mode** and obtain its test Key ID and Key Secret. Never use live credentials for local testing.
2. Set `APP_KEY`, start the application and MySQL, then sign in as a Super Admin and open `/superadmin/settings`.
3. Enable Razorpay, keep at least one payment method enabled, and save the Test Mode Key ID and Key Secret. The API secret is never sent to the browser.
4. Start a public HTTPS tunnel to the local app (for example, `ngrok http 8000` when the app is listening on port `8000`). In the Razorpay Dashboard's Test Mode webhook settings, add `https://<your-tunnel-host>/webhooks/razorpay`, select `payment.captured`, `payment.failed`, and `order.paid`, and copy the webhook signing secret into the Super Admin webhook-secret field. Use the webhook secret shown by Razorpay, not the API Key Secret.
5. Sign in as a store owner, open `/dashboard/billing`, select a plan, choose Razorpay, and complete a test-mode checkout. The displayed amount is derived from the plan in MySQL. Confirm the subscription and invoice in the owner billing page; inspect webhook deliveries and `webhook_logs` in the Dashboard and database.
6. Disable Razorpay in Super Admin settings and confirm a new Razorpay order is rejected server-side. Do not remove the other payment method unless you first enable it.

The checkout page loads Razorpay's required hosted script from `https://checkout.razorpay.com/v1/checkout.js`; this provider script is required for Razorpay Checkout. Application fonts and invoice assets are served locally.

## Invoices

Approved and captured payments create private PDF invoices numbered `ENO-YYYY-0001`, with an annual database-backed sequence. The PDF includes seller and store-owner details, plan and billing period, subtotal, configured tax, and total. Noto Sans Tamil is bundled locally for the PDF.

Owners can download invoices for their own store from `/dashboard/billing`; Super Admins can download them from `/superadmin/payments`. Invoice records and PDFs are stored outside the public document tree, and owner lookup is constrained by the store ID in the authenticated session.

Seller tax identifiers and the tax label/rate are settings placeholders, not validated tax registrations or tax advice. Configure them with the business's verified details before issuing production invoices. Customer tax/address fields are not collected by the current billing flow.
