# Custom domains

Custom domains are available only when the store's current subscription enables `custom_domain` (the Growth and Pro plans by default). The check is performed by `SubscriptionService` when saving, verifying, and resolving a domain; hiding the settings tab is not the security boundary.

## Configure DNS

1. In `config/config.php`, set `server_ip` to the public IPv4 address that serves the application. The same value can be supplied as `SERVER_IP` in the process environment or `.env`.
2. In the owner's **Dashboard → Settings → Domain** tab, enter a hostname such as `shop.example.com` and save it.
3. In the DNS provider, create:
   - An `A` record for the hostname pointing to `SERVER_IP`.
   - A `TXT` record at the hostname with the exact value shown by the dashboard: `enodastore-verify=<unique token>`.
   - Optionally, a `CNAME` for `www.shop.example.com` pointing to `shop.example.com`. The dashboard displays this suggested record; only the A and TXT records are required for verification.
4. Wait for DNS propagation and choose **Verify now**. The app uses PHP `dns_get_record()` through `NativeDnsResolver`; the A address and TXT token must match. The result, check time, and reason are persisted. The daily cron rechecks records at least once per day.

`DnsResolver` is injectable and tests use a deterministic mock. The application cannot guarantee DNS propagation timing or availability of the host's resolver.

## cPanel and TLS

- Add the hostname as an **Addon Domain** or **Parked/Alias Domain** in cPanel and point it to the same document root as the main Enodastore site. If the main site uses the project-root rewrite, share that root; if it is configured directly to `public/`, use that same `public/` root.
- Enable **AutoSSL** for the added hostname in cPanel and wait for cPanel to provision its certificate before directing customers to HTTPS.
- Enodastore verifies DNS and serves the verified store; it does not provision certificates or configure cPanel. TLS/AutoSSL status must be checked in the hosting control panel.

## Routing and lifecycle

- The main site keeps its marketing and `/{store_slug}` routes.
- A verified, plan-eligible custom hostname serves that store at `/`, `/products`, `/product/{slug}`, `/cart`, `/checkout`, and the other store paths. Storefront links and canonical tags use the custom hostname. Slug-based store URLs permanently redirect to it.
- Pending/failed domains, and domains whose store has lost the feature through plan downgrade, show a localized “Domain not connected” response. The slug storefront continues to work after downgrade.
- Owners can remove a domain, including after a plan downgrade. Removing it immediately stops custom-host routing.

## Testing

Run `php -d extension=php_gd.dll tests/domains_hardening_test.php` with MySQL available. The integration test creates and drops its own database, checks mock DNS success/failure, simulates HTTP `Host` headers, verifies redirects and downgrade behavior, and does not modify the configured application database.
