bus-integration-stripe — Stripe integration
bus-integration-stripe — Stripe integration
bus-integration-stripe owns Stripe-specific billing protocol behavior behind
the provider-neutral Bus billing boundary. It verifies Stripe webhooks, creates
Stripe Checkout and Customer Portal sessions when deployment secrets are
configured, records Stripe meter events, and defines Stripe-specific event DTOs.
Public billing APIs and auth logic do not import Stripe code.
Use this integration when a Bus deployment sells paid features through Stripe.
End users still interact with bus billing and the Billing API; operators
configure Stripe credentials, webhooks, products, prices, meters, and Customer
Portal in the deployment.
Events
bus.stripe.checkout_session.create.request asks the Stripe integration to
create a Checkout Session. Requests can include a Bus feature scope such as
llm:proxy; this is written to Stripe metadata for later webhook mapping.
bus.stripe.checkout_session.create.response returns a hosted Checkout URL. In
development without BUS_STRIPE_SECRET_KEY, the URL is deterministic and local.
With Stripe configuration, the worker creates a real Stripe Checkout Session
for the configured test or live account.
bus.stripe.portal_session.create.request asks for a Stripe Customer Portal
session.
bus.stripe.portal_session.create.response returns a hosted portal URL. Real
Stripe Portal sessions require Stripe Customer Portal to be configured for the
test/live account.
bus.stripe.webhook.verify.request asks the integration to verify a Stripe
webhook signature over the raw request body.
bus.stripe.webhook.verify.response returns verified Stripe event metadata.
For supported webhook types, verification also emits
bus.billing.subscription.update for the generic billing integration. Supported
types are checkout.session.completed, customer.subscription.created,
customer.subscription.updated, and customer.subscription.deleted.
Webhook objects must include Bus account and feature metadata:
bus_account_id or checkout client_reference_id, plus bus_feature or
comma-separated bus_features.
bus.stripe.meter_event.record.request asks the integration to record a Stripe
meter event with an idempotency key. Requests include the Bus account_id for
ownership and Stripe provider_customer_id for Stripe meter customer mapping.
bus.stripe.meter_event.record.response returns provider acceptance metadata.
The request idempotency key is used for Stripe event identity and retry safety.
Secrets
Stripe API keys and webhook secrets are deployment secrets. Do not commit them to source control or print them in logs.
Use these environment variables in deployments or untracked local operator files:
-
BUS_STRIPE_SECRET_KEYis required for Stripe API calls. -
BUS_STRIPE_WEBHOOK_SECRETis required for webhook verification. -
BUS_STRIPE_API_VERSIONis optional; when set, it pins the Stripe API version used by requests. -
BUS_STRIPE_DEFAULT_PRICE_IDis optional; set it only when a deployment needs a fallback Stripe price for checkout/session creation.
For old Stripe test accounts, pin BUS_STRIPE_API_VERSION explicitly.
BUS_STRIPE_SECRET_KEY is the Stripe secret key, normally beginning with
sk_test_ for test mode or sk_live_ for live mode. BUS_STRIPE_WEBHOOK_SECRET
is the endpoint signing secret beginning with whsec_; it is not the Stripe
publishable key. Browser publishable keys beginning with pk_ are not needed
by this server-side integration unless another frontend-specific flow is added.
Pass secrets through the deployment environment, secret manager, or an untracked local environment file. Do not pass Stripe secret keys as command-line arguments.
Stripe Setup
Create products, prices, and meters from a provider-neutral Bus catalog when
possible. The billing catalog commands need a Bus operator token with catalog
scopes, such as billing:catalog:read and billing:catalog:write, and the
operator API URL for the deployment:
export BUS_API_BASE_URL=https://api.example.test
export BUS_API_TOKEN="$(cat /run/secrets/bus-billing-operator.token)"
export BUS_STRIPE_SECRET_KEY="$(cat ./local/stripe-secret-key)"
bus operator billing catalog template > catalog.json
bus operator stripe catalog sync --file catalog.json
bus operator billing catalog put --file catalog.json
BUS_STRIPE_SECRET_KEY must be present before bus operator stripe catalog
sync can write products, prices, or meters to Stripe.
After synchronization, bus operator stripe test should report that the Stripe
API key is usable. bus operator billing catalog get should return the catalog
you published, including the plan, price, meter, and quota entries.
Using from .bus files
Inside a .bus file, write the module target without the bus prefix:
# same as: bus integration stripe --events-url "$BUS_EVENTS_API_URL"
integration stripe --events-url "$BUS_EVENTS_API_URL" --webhook-addr 127.0.0.1:8084
Configure Stripe Customer Portal in the Stripe Dashboard before enabling
bus billing portal. Configure a Stripe webhook endpoint that forwards events
to the Bus deployment path that verifies webhooks through this integration:
/api/internal/stripe/webhook. The endpoint signing secret from that webhook
becomes BUS_STRIPE_WEBHOOK_SECRET.
Run the webhook ingress on the Stripe integration service. Set
BUS_EVENTS_API_URL to the Bus Events API base URL and provide a Bus token with
billing:subscription:write so the ingress can publish verified subscription
updates:
export BUS_EVENTS_API_URL=http://127.0.0.1:8081
BUS_STRIPE_WEBHOOK_SECRET="$(cat ./local/stripe-webhook-secret)" \
BUS_API_TOKEN="$(cat ./local/billing-worker.token)" \
bus-integration-stripe \
--events-url "$BUS_EVENTS_API_URL" \
--webhook-addr 127.0.0.1:8084
The ingress accepts only signed Stripe POST requests, preserves the raw body
for signature verification, and publishes the verified checkout/subscription
state as bus.billing.subscription.update through Bus Events. The Bus token
used by the ingress needs billing:subscription:write. In production, expose
/api/internal/stripe/webhook through your HTTPS reverse proxy to this
integration service, or configure Stripe directly with the service URL when it
is already reachable over HTTPS.
In local development, Stripe CLI can forward signed webhooks to the local Bus
webhook route. The CLI prints the whsec_... signing secret for that local
listener.
stripe listen --forward-to http://127.0.0.1:8084/api/internal/stripe/webhook
Copy the printed whsec_... value into BUS_STRIPE_WEBHOOK_SECRET and
restart or relaunch bus-integration-stripe with that value before triggering
events; otherwise signature verification fails.
In another terminal, trigger a signed test event:
stripe trigger checkout.session.completed
The Stripe CLI should report a delivered POST; that confirms local signed
webhook delivery and signature verification. A
bus.billing.subscription.update event is emitted only when the delivered
Stripe event includes Bus metadata such as bus_account_id and bus_feature,
which the default Stripe CLI fixture may not include.
The BusDK superproject compose.yaml starts this integration as bus-stripe
with webhook ingress on port 8084. Nginx exposes it at
/api/internal/stripe/webhook on the local API port. The compose default
BUS_STRIPE_WEBHOOK_SECRET=whsec_local_not_secret is only a local development
placeholder; use the signing secret printed by Stripe CLI or a deployment
secret for real webhook verification. Without BUS_STRIPE_SECRET_KEY, checkout
and portal responses remain deterministic local responses.
Test Mode And Live Mode
Use Stripe test-mode credentials while configuring a new deployment. Test mode lets operators create products, prices, meters, customers, Checkout Sessions, and webhooks without charging real payment methods.
Switch to live-mode credentials only as part of an explicit production cutover. Keep live Stripe credentials isolated from local development environments.