diff --git a/deploy/install.sh b/deploy/install.sh index 4e52d6a..9456ae6 100755 --- a/deploy/install.sh +++ b/deploy/install.sh @@ -330,10 +330,17 @@ Still to do, in this order: 1. Point the DNS records at ${PUBLIC_IP} and put TLS in front (Zoraxy, Caddy or nginx). ${ADMIN_DOMAIN} must NOT be publicly resolvable. - 2. Enter STRIPE_WEBHOOK_SECRET in $INSTALL_DIR/.env — Stripe dashboard → - Developers → Webhooks → https://${APP_DOMAIN}/webhooks/stripe, events - checkout.session.completed and checkout.session.async_payment_succeeded. - 3. Sign in and switch the website to hidden under Settings until you are + 2. Enter STRIPE_SECRET and STRIPE_WEBHOOK_SECRET in $INSTALL_DIR/.env — + Stripe dashboard → Developers → Webhooks → + https://${APP_DOMAIN}/webhooks/stripe, subscribing all six events: + checkout.session.completed checkout.session.async_payment_succeeded + invoice.paid invoice.payment_failed + customer.subscription.updated customer.subscription.deleted + 3. Mirror the plan catalogue into Stripe — nothing can be sold until it knows + what it is billing for. Look first; a Stripe Price cannot be deleted: + docker compose exec app php artisan stripe:sync-catalogue --dry-run + docker compose exec app php artisan stripe:sync-catalogue + 4. Sign in and switch the website to hidden under Settings until you are ready to be found (Settings → Website visibility). Updating later — as ${APP_USER}, not as root: diff --git a/docs/deployment.md b/docs/deployment.md index 02d7c26..2a03ef4 100644 --- a/docs/deployment.md +++ b/docs/deployment.md @@ -65,9 +65,36 @@ kept failing until the worker was restarted. the hostnames in `ADMIN_HOSTS` and 404s everywhere else, but keeping it out of public DNS is the layer above that. - **`STRIPE_WEBHOOK_SECRET`.** Stripe dashboard → Developers → Webhooks → - `https:///webhooks/stripe`, events `checkout.session.completed` - and `checkout.session.async_payment_succeeded`. The secret starts with - `whsec_` and differs between test and live mode. + `https:///webhooks/stripe`. The secret starts with `whsec_` and + differs between test and live mode. Subscribe all six events the application + handles — the first two open a contract, the rest are the billing cycle, + which is Stripe's: + + ``` + checkout.session.completed + checkout.session.async_payment_succeeded + invoice.paid + invoice.payment_failed + customer.subscription.updated + customer.subscription.deleted + ``` + +- **The Stripe catalogue.** Nothing can be sold until Stripe knows what it is + billing for: + + ``` + docker compose exec app php artisan stripe:sync-catalogue --dry-run # look first + docker compose exec app php artisan stripe:sync-catalogue + ``` + + Run it again after publishing any new plan version. It is idempotent, and it + has to be: a Stripe Price cannot be edited or deleted, so a duplicate is + permanent. `--dry-run` lists exactly what would be created. + +- **A look at the catalogue itself.** `php artisan plans:check` reports + overlapping availability windows, plans on sale with no live version, and + missing prices. Worth running after any plan change — all of it is computed + when read, so a mistake announces itself at a customer's checkout otherwise. - **Backups.** At minimum the MariaDB volume and `/etc/wireguard` in the `wireguard` volume — losing the hub key means re-peering every host.