vantezzen/pay
Self-host

Deployment

Deploy the vantezzen/pay Next.js app and connect it to the provider service.

vantezzen/pay's public surface is a standard Next.js app. Provider API keys live in the separate provider service; the web app calls it through PAY_PROVIDER_SERVICE_URL.

Vercel

  1. Import the repository.
  2. Set the environment variables from Environment variables.
  3. Make sure the provider service is deployed and reachable by the web app.
  4. Deploy.
  5. Run bun run db:migrate once against production.
  6. Visit /register and create your admin account.
  7. Set PAY_ALLOW_SIGNUPS=false.

The build script generates the shadcn registry JSON before building the app, so the registry is served from /r.

With Vercel Services, the included vercel.json defines a web service and a provider-service service. The provider service URL is injected into the web app as PAY_PROVIDER_SERVICE_URL over Vercel's internal service binding.

Docker Compose

The repository includes a production web image (Dockerfile), encrypted-key provider service, and Postgres in docker-compose.yml. Create a root .env from the values in Environment variables, then:

docker compose --profile secure build
docker compose --profile secure up -d

All application services (web, provider-service, migrate) use the secure profile. Running docker compose up without --profile secure starts only Postgres. Always pass --profile secure for a full stack.

Migrations run automatically before web starts - docker compose up is all you need. The migrate service uses drizzle's idempotent migration runner, so re-running up on an already-migrated database is safe.

The web image is built with NEXT_PUBLIC_APP_URL, because Next.js embeds public environment variables in browser code at build time. Set the final HTTPS URL before build, then rebuild the image if that URL changes.

The Compose file exposes web on port 3000. The provider service is on an internal network only - it is not published to the host, and only the web container can reach it. Put a TLS-terminating reverse proxy in front of the web service.

Other hosts

Your host must support:

  • Node-compatible Next.js runtime.
  • Long enough request handling for provider SDK calls.
  • HTTPS public URL for webhooks.
  • Persistent Postgres database.
  • Environment variables.
  • Private network access to the provider service, or a protected provider service URL with PAY_PROVIDER_SERVICE_SECRET.

Trusted proxy

Per-IP protections (anonymous wallet creation throttling, publishable-key rate limits) read the client address from x-forwarded-for. That header is only trustworthy behind a proxy that overwrites it - Vercel and Cloudflare do, and a well-configured nginx/Caddy does too. Don't expose the Node process to the internet directly, or clients can spoof the header and bypass those limits.

Public URL

The public URL is important. It is used for:

  • Dashboard auth callbacks.
  • Provider webhook URLs.
  • Hosted checkout success/fallback pages.
  • Consumer app registry install URLs.
  • API calls from consuming apps.

If the domain changes, update BETTER_AUTH_URL, NEXT_PUBLIC_APP_URL, provider webhooks, and consumer app environment variables.