Billing Hub
Billing Hub provides a gateway-agnostic system for managing subscriptions, invoices, taxes, and dunning within Drupal. It integrates with Membership Manager for access control and supports multiple payment gateways like Stripe, PayPal, and Braintree through a plugin API.
Gateway-agnostic billing layer for Drupal 10 / 11. Manages subscriptions,
invoices, tax, and dunning. Integrates with Membership Manager for access
control. Supports Stripe, PayPal, Braintree, and custom payment gateways via a Plugin API.
Requirements
- Drupal 10.2+ or Drupal 11
- PHP 8.2+
drupal:user(core)membership_managermodule
Gateway dependencies
# Stripe composer require stripe/stripe-php # PayPal composer require paypal/paypal-server-sdk
Installation
composer require drupal/billing_hub drush en billing_hub billing_hub_stripe drush cr
Visit /admin/billing to configure.
Architecture Overview
CheckoutService → BillingManagerService → GatewayPlugin BillingPortalController → BillingManagerService → SubscriptionService WebhookController → WebhookDispatcherService → BillingManagerService DunningService → Queue → DunningQueueWorker → GatewayPlugin::retryInvoice() TaxService ← BillingPlan + TaxRate AnalyticsService ← billing_subscription + billing_invoice tables
BillingManagerService is the central entry point.
Billing Plans
Field Description Machine name Used in code Gateway Gateway plugin Price Base price Currency ISO 4217 Interval day / week / month / year Interval count Billing frequency Trial days Trial duration Proration Enable credits Dunning policy cancel / downgrade / suspend Tax mode exclusive / inclusive / none Membership plan Linked membershipGateway Configuration
Field Description Plugin ID stripe / paypal / braintree Test mode Sandbox mode Settings API keysStripe example
secret_key: sk_live_... publishable_key: pk_live_... webhook_secret: whsec_... test_mode: false
Checkout Flow
PHP Example
$checkout = \Drupal::service('billing_hub.checkout'); $preview = $checkout->preview( plan_id: 'pro_monthly', country_code: 'DE', tax_id: 'DE123456789' ); $result = $checkout->checkout( user: $user, plan_id: 'pro_monthly' );
AJAX Preview
fetch('/billing/checkout/pro_monthly/preview', { method: 'POST', headers: {'Content-Type': 'application/json'}, body: JSON.stringify({ country_code: 'DE' }) });
Subscription Lifecycle
active → canceled active → paused → active active → past_due → unpaid trialing → active
Webhooks
Gateway URL Stripe /billing/webhook/stripe PayPal /billing/webhook/paypal Braintree /billing/webhook/braintreeDunning
Retry schedule: 0, 1, 3, 7, 14 days
Policy Effect cancel Subscription canceled downgrade Assign fallback plan suspend Keep recordTax
Modes
Mode Description exclusive Tax added inclusive Tax included none No taxVAT validation hook
function mymodule_billing_hub_vat_validate(string $vat_number): bool { return my_vies_client()->validate($vat_number); }
Analytics
$analytics = \Drupal::service('billing_hub.analytics'); $analytics->getMRR(); $analytics->getARR(); $analytics->getChurnRate(30);
User Billing Portal
Route Description /billing/portal Overview /billing/portal/invoices Invoices /billing/portal/cancel/{id} Cancel /billing/portal/change-plan/{id} Change planMembership Integration
BillingPlan.membership_plan_id → assignMembership() → MembershipManagerService::assign()
Custom Gateway
/** * @Gateway( * id = "my_gateway", * label = @Translation("My Gateway") * ) */ class MyGateway extends GatewayPluginBase {}
Hooks
Coupon
function mymodule_billing_hub_coupon_validate(string $code): float { return 10.0; }
Redirect
function mymodule_billing_hub_checkout_success_url(...) { return '/members/welcome'; }
Maintainers
Dmytro Porokhnya
License
GPL-2.0-or-later
Depends on
Dependencies of the latest stable release
No dependencies recorded for this project.
Required by
Tracked projects that depend on this one
No tracked projects depend on this one yet.
Activity
Releases
| Version | Type | Core | Release date | |
|---|---|---|---|---|
| 1.0.0-alpha1 | Pre-release | 10–11 | Apr 1, 2026 |