bitwarden/server
Billing
Active contributors: billing team.
Purpose
src/Billing/ is a small, focused HTTP service whose only job is to receive webhooks from the third-party payment providers Bitwarden integrates with, validate them, and apply the resulting state changes to the database. It does not contain any user-facing UI; that lives in the Api (self-service) and Admin (operations) apps. The heavy lifting (subscription creation, plan changes, proration, license generation) lives in src/Core/Billing/ services.
Directory layout
src/Billing/
├── Program.cs / Startup.cs
├── Controllers/
│ ├── StripeController.cs # /stripe/webhook (subscription, invoice, payment_method, customer.* events)
│ ├── PayPalController.cs # /paypal/ipn (Instant Payment Notification)
│ ├── BitPayController.cs # /bitpay/ipn (BitPay/crypto)
│ ├── AppleController.cs # /apple/iap-webhook (Apple App Store server notifications)
│ ├── RecoveryController.cs # /recovery/* (admin re-process of dropped webhook events)
│ └── InfoController.cs # /alive, /version
├── Services/
│ ├── StripeEventService.cs # Translates Stripe webhook events → domain calls
│ ├── StripeFacade.cs # Thin Stripe SDK adapter
│ ├── StripeEventProcessor.cs # Dedupes / processes events by id
│ ├── StripeWebhookUtility.cs
│ └── ...
├── Constants/ # Stripe event-name constants, log-context keys
├── Jobs/ # AliveJob, retry jobs
├── Models/ # Webhook payload DTOs
├── BillingSettings.cs # Strongly-typed config (ApiKey, WebhookKeys, FreshDesk, Onboarding)
├── appsettings.{Development,Production,QA}.json
└── Dockerfile / build.sh / entrypoint.shKey abstractions
| Type | Path | Description |
|---|---|---|
Startup |
src/Billing/Startup.cs |
Registers Stripe SDK, mail, repos, identity auth, billing services + Quartz jobs. |
StripeController |
src/Billing/Controllers/StripeController.cs |
Validates the Stripe-Signature header against globalSettings.Stripe.WebhookKeys, then dispatches to IStripeEventProcessor. |
IStripeEventService / StripeEventService |
src/Billing/Services/ |
Maps Stripe events (customer.subscription.updated, invoice.created, customer.discount.deleted, …) to domain operations like IUserService.AdjustStorageAsync or ISubscriberService.UpdateSubscription. |
PayPalController |
src/Billing/Controllers/PayPalController.cs |
Verifies PayPal IPN signatures, then routes to IPaymentService.HandleIpn. |
BitPayController |
src/Billing/Controllers/BitPayController.cs |
Crypto-payment notifications. |
AppleController |
src/Billing/Controllers/AppleController.cs |
App Store Server Notifications V2 (mobile premium upgrades). |
RecoveryController |
src/Billing/Controllers/RecoveryController.cs |
Operator endpoint to re-fire missed Stripe events. |
Domain services in src/Core/Billing/Services/ |
Where the actual business logic lives — ProviderBillingService, OrganizationBillingService, IPriceIncreaseScheduler, etc. |
How it works
sequenceDiagram
participant Stripe
participant Billing as Billing app
participant Core as Core domain
participant DB
Stripe->>Billing: POST /stripe/webhook (signed)
Billing->>Billing: Verify Stripe-Signature
Billing->>Core: IStripeEventProcessor.ProcessAsync(event)
Core->>DB: Update Subscription / Invoice / TaxRate / Transaction
Core->>DB: IEventService.LogOrganizationEventAsync
Billing-->>Stripe: 200 OKThe pattern repeats per provider — each controller verifies the provider-specific signature, deduplicates by event id, and calls into src/Core/Billing/. Idempotency is achieved by writing event IDs to a BillingEventLog style table.
Long-running work in 2025-2026
A long-running Stripe price-migration project landed throughout 2025-2026 (IPriceIncreaseScheduler — PR #7305, 2026-03-26; schedule-aware storage commands — PR #7350; cancel/reinstate path migration — PR #7331, 2026-03-31). The general direction is to replace ad-hoc proration calculations with Stripe-native subscription schedules, gated by feature flags so the cloud rollout is reversible.
Integration points
- Stripe — primary payment provider. The Stripe SDK is configured in
Startup.cswithStripeConfiguration.ApiKey = globalSettings.Stripe.ApiKey. - Database — same shared repositories as
Api(src/Infrastructure.Dapper/src/Infrastructure.EntityFramework). - Mail —
IMailServicefor receipt emails on certain events. - Event log —
IEventService.LogOrganizationEventAsyncfor org-scope billing events visible in audit logs. Admin— re-fires events viaRecoveryControllerif a webhook was missed or processing failed.
Self-host
Self-hosted instances do not subscribe via Stripe; their billing flows go through the licensing API on the cloud. The Billing host is therefore unused on self-host (the bundled stack does not start it).
Entry points for modification
- New Stripe event type → add a constant in
src/Billing/Constants/StripeEvents.csand a handler inIStripeEventService/StripeEventService. - New payment provider → add a controller with signature verification, define a new
IXxxEventService, and register it inStartup.cs. - New domain logic for an existing event → modify the appropriate
src/Core/Billing/Services/...Service.cs. Don't add domain logic in the controller layer. - New scheduled billing job → register it in
JobsHostedService(src/Billing/Jobs/).
For the broader picture see features/billing-and-subscriptions.
Built by Factory AutoWiki from public repository content. It is a generated preview for codebase exploration, not source-maintained documentation.