Subscription Manager now supports two connector scheduling modes (#3618739: Add a charge-based connector mode with a local billing cycle engine). Self-scheduling connectors (all existing ones) let the remote billing service own the subscription lifecycle, unchanged. Charge-based connectors provide only a payment primitive — a reusable authorization (bank-debit mandate, vaulted card) charged arbitrary amounts on demand — and Subscription Manager owns the billing cycle.
For connector authors
- Declare supported modes via the new
supported_modeskey on theSubscriptionManagerConnectorattribute or annotation; it defaults to['self_scheduling'], so existing connectors need no changes. - A charge-based connector additionally implements
ChargeBasedConnectorInterface:charge($subscription, $amount, $currency, $context): ChargeResult(use$context['charge_uuid']as the remote idempotency key),setupInstrument()(one hosted-flow method serving zero-amount signup, first setup, and replacement completion), andrefund().ChargeResultanswers succeeded, pending (asynchronous rails), or failed. Optionally implementInstrumentReplacingConnectorInterfacefor the portal's instrument-replacement action. - Asynchronous rails resolve pending charges from their webhook handling via
subscription_manager.cycle_engine'sresolveCharge(), which advances the cycle anchor on success.
For site builders
- Plans gain a Scheduling mode setting, validated against the connector's declared modes. Charge-based subscriptions carry a billing interval (monthly or yearly), a cycle anchor, an amount basis and currency, and a payment-instrument reference; cron runs the cycle through a queue with per-item isolation.
- Each billing period produces a
subscription_chargerecord (admin listing under Content), the audit trail dunning (#3618740: Add a dunning framework: past_due state, scheduled retries, grace period, and lifecycle notifications) and the tax seam (#3618741: Add a pluggable tax/invoice service seam (calculation, evidence, invoice reference), keeping tax engines out of core) build on. Whether zero-amount periods (e.g. a free tier) record a settled-locally row is controlled by the new Record zero-amount periods setting, default on; disabling stops future rows only. - Upgrades take effect immediately (prorated difference charged now, roles now, next full charge on schedule at the new rate); downgrades take effect at the next cycle anchor with no mid-cycle credits.
Data model
New entity type subscription_charge; new subscription base fields (billing_interval, cycle_anchor, billing_day, amount_basis, currency, instrument_id, pending_plan_id, pending_amount_basis); new plan field scheduling_mode; new setting record_zero_amount_periods. All installed via updates 10015–10018.