Payments Cloud
Cloud Billing Worker
Cloudflare Worker + D1 backend for Stripe Checkout, Customer Portal, webhooks, entitlement state, device activation, and license status.
Cloud Billing Form
Billing rehearsalChecking command-center API...
Do not paste Stripe keys, webhook secrets, license signing secrets, API tokens, or customer data into dashboard files.
Cloud Billing Snapshot
Billing Worker Config Sync
npm run cloud:billing:config:syncThis turns Cloud Billing page answers into non-secret production Worker config, including ALLOWED_ORIGIN, D1 database_id, and Stripe API version. Stripe keys, webhook secrets, license secrets, and customer data stay out of repo files.
Applied
- No config values changed on the last sync.
Waiting
- Production billing D1 database_id: Create the billing D1 database and enter billingCloud.d1DatabaseId in the dashboard.
Needs Fix
- No invalid billing config values found.
Routes
| Route | Status |
|---|---|
| GET /health | Ready |
| POST /api/billing/checkout | Ready |
| POST /api/billing/portal | Ready |
| POST /api/billing/webhook | Ready |
| GET /api/license/status | Ready |
Secrets
| Secret | Where |
|---|---|
STRIPE_SECRET_KEY | Secret store |
STRIPE_WEBHOOK_SECRET | Secret store |
NYRA_STRIPE_PRICE_PRO_MONTHLY | Secret store |
NYRA_BILLING_SUCCESS_URL | Secret store |
NYRA_BILLING_CANCEL_URL | Secret store |
NYRA_BILLING_RETURN_URL | Secret store |
NYRA_LICENSE_API_TOKEN | Secret store |
NYRA_LICENSE_SIGNING_SECRET | Secret store |
Live Blockers
- Cloudflare account and D1 database ID
- Billing Worker custom domain or workers.dev URL
- Stripe test/live secrets in Wrangler secret store
- Stripe product and monthly Price ID after final monthly price
- Customer Portal configuration
- Stripe webhook endpoint and signing secret
- Support inbox roundtrip
- Attorney/accountant review
Deployment Manifest
{
"service": "nyra-billing-api",
"scope": "Cloudflare Worker and D1 billing backend for NyrA Swarm Little Buddy paid beta",
"runtime": "Cloudflare Workers",
"storage": {
"type": "Cloudflare D1",
"binding": "DB",
"databaseName": "nyra_billing",
"migrationsDir": "cloud/billing-worker/migrations",
"tables": [
"billing_customers",
"processed_events",
"device_activations"
]
},
"routes": [
"GET /health",
"POST /api/billing/checkout",
"POST /api/billing/portal",
"POST /api/billing/webhook",
"GET /api/license/status"
],
"requiredSecrets": [
"STRIPE_SECRET_KEY",
"STRIPE_WEBHOOK_SECRET",
"NYRA_STRIPE_PRICE_PRO_MONTHLY",
"NYRA_BILLING_SUCCESS_URL",
"NYRA_BILLING_CANCEL_URL",
"NYRA_BILLING_RETURN_URL",
"NYRA_LICENSE_API_TOKEN",
"NYRA_LICENSE_SIGNING_SECRET"
],
"requiredVars": [
"ALLOWED_ORIGIN",
"STRIPE_API_VERSION"
],
"checks": [
"npm run cloud:billing:check",
"npm run test:cloud-billing-worker"
],
"liveBlockers": [
"Cloudflare account and D1 database ID",
"Billing Worker custom domain",
"Stripe test/live secrets in Wrangler secret store",
"Stripe product/price after final monthly price",
"Customer Portal configuration",
"Stripe webhook endpoint and signing secret",
"Support inbox roundtrip",
"Attorney/accountant review"
]
}Wrangler Config
{
"$schema": "../../node_modules/wrangler/config-schema.json",
"name": "nyra-billing-api",
"main": "src/index.mjs",
"compatibility_date": "2026-05-31",
"compatibility_flags": [
"nodejs_compat"
],
"observability": {
"enabled": true
},
"vars": {
"ALLOWED_ORIGIN": "http://localhost:5173",
"STRIPE_API_VERSION": "2026-02-25.clover"
},
"d1_databases": [
{
"binding": "DB",
"database_name": "nyra_billing",
"database_id": "replace-with-cloudflare-d1-database-id",
"migrations_dir": "migrations"
}
],
"env": {
"local": {
"vars": {
"ALLOWED_ORIGIN": "http://localhost:5173",
"STRIPE_API_VERSION": "2026-02-25.clover"
},
"d1_databases": [
{
"binding": "DB",
"database_name": "nyra_billing",
"database_id": "replace-with-cloudflare-d1-database-id",
"migrations_dir": "migrations"
}
]
},
"production": {
"vars": {
"ALLOWED_ORIGIN": "https://porterlabz.com",
"STRIPE_API_VERSION": "2026-02-25.clover"
},
"d1_databases": [
{
"binding": "DB",
"database_name": "nyra_billing",
"database_id": "replace-with-cloudflare-d1-database-id",
"migrations_dir": "migrations"
}
]
}
}
}
D1 Migration
CREATE TABLE IF NOT EXISTS billing_customers (
customer_id TEXT PRIMARY KEY,
checkout_session_id TEXT,
subscription_id TEXT,
device_id TEXT,
email TEXT,
status TEXT,
entitlement TEXT,
price_id TEXT,
current_period_end INTEGER,
cancel_at_period_end INTEGER NOT NULL DEFAULT 0,
last_invoice_id TEXT,
last_invoice_status TEXT,
stripe_entitlements_json TEXT,
updated_at TEXT NOT NULL
) STRICT;
CREATE INDEX IF NOT EXISTS idx_billing_customers_checkout_device
ON billing_customers (checkout_session_id, device_id);
CREATE INDEX IF NOT EXISTS idx_billing_customers_subscription
ON billing_customers (subscription_id);
CREATE TABLE IF NOT EXISTS processed_events (
event_id TEXT PRIMARY KEY,
type TEXT NOT NULL,
processed_at TEXT NOT NULL
) STRICT;
CREATE TABLE IF NOT EXISTS device_activations (
customer_id TEXT NOT NULL,
device_id TEXT NOT NULL,
activated_at TEXT NOT NULL,
last_checked_at TEXT NOT NULL,
last_active INTEGER NOT NULL DEFAULT 0,
PRIMARY KEY (customer_id, device_id)
) STRICT;
NyrA Cloud Billing Worker
This Worker is the Cloudflare-native billing backend path for NyrA Swarm Little Buddy. It mirrors the local Node billing service routes while replacing file-backed entitlement storage with D1.
It does not contain live Stripe keys, webhook secrets, certificate material, or customer data.
Routes
| Route | Purpose |
|---|---|
GET /health |
Verifies required secrets and D1 reachability. |
POST /api/billing/checkout |
Creates a Stripe Checkout Session in subscription mode. |
POST /api/billing/portal |
Creates a Stripe Customer Portal Session for an authenticated customer/device. |
POST /api/billing/webhook |
Verifies Stripe webhook signatures and records subscription/customer state. |
GET /api/license/status |
Returns active entitlement, offline grace metadata, device activation, license blob, and device access token. |
Device access tokens are signed bearer tokens for the paid device and include expiresAt. They expire at the subscription offline-grace boundary when Stripe period data is available, or after a 24-hour fallback window while checkout/subscription state is still settling.
Storage
D1 tables are defined in migrations/0001_billing.sql:
billing_customersprocessed_eventsdevice_activations
This path removes the paid-beta need for NYRA_ENTITLEMENTS_FILE when the Worker is selected as the hosted backend, but it still requires a real Cloudflare account, D1 database, custom domain, Stripe secrets, webhook endpoint, and support/legal readiness before live payments.
Required Secrets
Set these with wrangler secret put or the Cloudflare dashboard secret store. Do not commit them.
STRIPE_SECRET_KEY
STRIPE_WEBHOOK_SECRET
NYRA_STRIPE_PRICE_PRO_MONTHLY
NYRA_BILLING_SUCCESS_URL
NYRA_BILLING_CANCEL_URL
NYRA_BILLING_RETURN_URL
NYRA_LICENSE_API_TOKEN
NYRA_LICENSE_SIGNING_SECRET
Local Checks
npm run cloud:billing:check
npm run test:cloud-billing-worker
Deploy Rehearsal
npx wrangler d1 create nyra_billing
npx wrangler d1 migrations apply nyra_billing --cwd cloud/billing-worker --local
npx wrangler dev --cwd cloud/billing-worker --env local
For production, replace the D1 database ID in wrangler.jsonc, set secrets outside the repo, configure the Stripe webhook endpoint to https://<billing-domain>/api/billing/webhook, and use Stripe test mode first.
After saving the Cloud Billing dashboard fields, run:
npm run cloud:billing:config:sync
npm run test:cloud-billing-config-sync
The sync writes only non-secret production Worker config values such as ALLOWED_ORIGIN, D1 database_id, and the pinned Stripe API version. Stripe keys, webhook secrets, license secrets, and customer data must stay in Wrangler secrets, Stripe, or temporary shell variables.
No-Go Rules
- Do not run live mode until the command center has a monthly price, hosted AI cap, confirmed support inbox, domain/backend URL, Customer Portal setup, attorney/accountant review, and signed/store-trusted release path.
- Do not put Stripe secrets, webhook secrets, license signing secrets, API tokens, or customer records in the repo or dashboard JSON.
- Do not issue permanent device access tokens. Paid device tokens must expire and refresh through
GET /api/license/status. - Do not expose checkout publicly until
deployability.statusis no longerNO_GO_LIVE_MONEY.
Cloud Billing Worker Deployment Checklist
This checklist is for the Cloudflare Worker + D1 hosted billing path. It is not legal, tax, accounting, or Stripe support advice.
Before Deployment
- Cloudflare account exists.
- Billing Worker custom domain or workers.dev URL is approved.
- D1 database
nyra_billingis created. - Cloud Billing dashboard fields are saved, then
npm run cloud:billing:config:synchas updated non-secret Worker config. cloud/billing-worker/wrangler.jsonchas the real D1 database ID and productionALLOWED_ORIGIN.- Migrations are applied.
- Stripe product and monthly Price ID exist after the final dashboard price is chosen.
- Customer Portal cancellation, invoices, and payment method update settings are configured.
- Support inbox is created, secured, and tested.
- Attorney/accountant review is approved for paid beta.
Required Cloudflare Secrets
Use wrangler secret put or Cloudflare dashboard secrets:
STRIPE_SECRET_KEYSTRIPE_WEBHOOK_SECRETNYRA_STRIPE_PRICE_PRO_MONTHLYNYRA_BILLING_SUCCESS_URLNYRA_BILLING_CANCEL_URLNYRA_BILLING_RETURN_URLNYRA_LICENSE_API_TOKENNYRA_LICENSE_SIGNING_SECRET
Required Verification
Run locally:
npm run cloud:billing:check
npm run cloud:billing:config:sync
npm run test:cloud-billing-config-sync
npm run test:cloud-billing-worker
npm run billing:live-preflight -- --mode test --allow-blocked
Then, against the hosted Worker in Stripe test mode:
GET /healthreportsok: true.- Checkout Session creation returns a Stripe-hosted Checkout URL.
- Stripe webhook
checkout.session.completedrecords the customer and device metadata. - Stripe webhook
invoice.paidrecords active subscription state. - Stripe Customer Portal creation works with a device access token.
- Desktop
Refresh Activationreturns active license state after checkout/webhook sync. - License status returns
deviceAccessTokenExpiresAtIso, and the signed device access token contains the sameexpiresAtvalue. - Duplicate webhook delivery is idempotent.
- Bad webhook signature is rejected.
Rollback
- Disable public checkout links first.
- Pause the Stripe Price or stop sending customers to Checkout if activation breaks.
- Roll back the Worker deployment through Cloudflare.
- Preserve D1 data before destructive changes.
- Route affected customers through the support runbook.