Bitcoin checkout on serverless hosts

Serverless hosts run your server code in short-lived instances that start on demand and stop when traffic does. OpenReceive runs in them as is, because it has no process of its own: each request to its payment routes does its own work, and everything it keeps is in your app’s database. Your server code creates each Lightning invoice, and the payment goes straight to your wallet. There is no OpenReceive account, no API key and no background worker.

Optional swaps let customers pay with USDT, USDC, SOL, and ETH. A swap provider you configure converts the payment to BTC over Lightning, and it settles into the same wallet. Available assets and networks depend on the provider.

Start from your framework’s quickstart: Next.js, Express, Fastify, FastAPI or Django. This guide covers what changes when the server is serverless.

What every serverless app needs

Pooled connection strings

A serverless app can start many instances at once, and each one opens its own database connections. A pooler sits between them and the database and shares a few real connections among them. Use the pooled connection string for your app, and the direct one for migrations.

OpenReceive locks each order inside a transaction (pg_advisory_xact_lock), which transaction poolers support. Session-level locks, which poolers break, are never used. CI runs the payment storage through PgBouncer in transaction mode with prepared statements off, as strict as the strictest host below: Node with pg, Knex, Prisma (with @prisma/adapter-pg) and TypeORM, and Python with SQLAlchemy and psycopg.

Database Pooled connection for the app Notes
Neon, including Vercel’s Neon integration The host with -pooler in its name. On Vercel that is DATABASE_URL; DATABASE_URL_UNPOOLED is the direct one PgBouncer in transaction mode
Supabase The transaction pooler, port 6543 Node and Python must trust Supabase’s own certificate authority: see Supabase
Cloud SQL for PostgreSQL Managed Connection Pooling in transaction mode (Enterprise Plus edition), or the instance’s Unix socket with a small pool Cloud Run allows 100 connections to Cloud SQL per instance
Amazon RDS and Aurora PostgreSQL RDS Proxy AWS documents that transaction-level advisory locks, the only kind OpenReceive takes, do not pin a connection
Aurora DSQL Not supported It has no advisory locks, and it resolves conflicts at commit instead

Keep each instance’s pool small: max: 1 to 5 with pg, pool_size=1 to 5 with SQLAlchemy. Many instances times a large pool runs the database out of connections.

Python on a pooler

A transaction pooler hands each transaction whichever database connection is free. Once a statement has run five times on a connection, psycopg prepares it on the database, and under load a later transaction fails with prepared statement "_pg3_0" already exists. Turn preparing off on the engine you give OpenReceive:

from sqlalchemy import create_engine

engine = create_engine(
    os.environ["DATABASE_URL"],  # postgresql+psycopg://… the pooled URL
    connect_args={"prepare_threshold": None},
    pool_size=2,
)

Django does not prepare statements on PostgreSQL by default, so it needs no change. If your own code uses .iterator(), add "DISABLE_SERVER_SIDE_CURSORS": True to the database settings.

Vercel

Vercel runs Next.js route handlers as Node functions, and OpenReceive’s Next.js package works there as is. Vercel and v0 covers it, with a starter that deploys with a Neon database.

Netlify

AWS Lambda

Google Cloud Run

Google AI Studio

AI Studio’s Build mode makes full-stack apps with a Node server, and each one it deploys is a Cloud Run service, so the Cloud Run notes above apply.

Cloudflare Workers

A Worker is not a Node server, and it cannot keep a database pool between requests. The TanStack Start recipe shows the route, which builds and closes its pool and its OpenReceive stack in each request. With Supabase, it stores payments through Supabase’s HTTPS API instead: see Lovable.

What we test

More detail: Deploying, Payment storage, Supabase, Security.