Subscription Manager now has its own manage-subscription page for connectors whose billing service has no customer portal, and charge-based subscriptions can be cancelled and resumed (#3627571: Build the membership portal page on capability interfaces, replacing the unused inline-actions API). The unused "inline actions" API on the connector interface is removed in favour of capability interfaces. Every connector needs a small change.
Connector authors: what breaks
redirectToPortal()is no longer part ofSubscriptionManagerConnectorInterface. A connector with a hosted portal keeps the method and adds the new interface to its class declaration:
use Drupal\subscription_manager\HostedPortalConnectorInterface; class StripeConnector extends ... implements SubscriptionManagerConnectorInterface, HostedPortalConnectorInterface {Without it, the manage route and
api/portal-urlstop redirecting to the connector's portal and show Subscription Manager's own page instead. A connector with no portal (typically a charge-based one) simply does not implement it and can delete any stub.supportsInlineActions(),getSubscriptionDisplayData(),getAvailableActions()andexecuteAction()are removed from the interface. Nothing ever called them. Delete them, or keep them as ordinary methods if your own code uses them.RemoteCancellationno longer falls back toexecuteAction('cancel'). A connector that can cancel by API implementsRemoteCancellingConnectorInterface(#3616934: Deleting a user deletes local subscription entities but never cancels the remote contract, so billing continues); otherwise the operator gets the "cannot cancel by API" log entry.
Connector authors: what a charge-based connector gains
The native page offers each action only when the connector supports it:
- Cancel subscription / Keep my subscription — always, for charge-based subscriptions; no connector code needed.
- Update payment method — when the connector implements
InstrumentReplacingConnectorInterface. This is where the dunning mails send a customer whose payment failed. - Instrument revocation when a subscription ends — when the connector implements
RemoteCancellingConnectorInterface: cancel or detach the mandate or vaulted card named byinstrument_id.
Cancellation semantics
$engine = \Drupal::service('subscription_manager.cycle_engine'); $engine->cancel($subscription); // At the end of the paid period. $engine->cancel($subscription, TRUE); // Now. $engine->resume($subscription); // Withdraw a pending cancellation.
- By default a cancellation takes effect at the end of the period already paid for:
cancel_dateis set,expirybecomes the cycle anchor, access and roles continue until then, the engine stops billing, and a scheduled downgrade is dropped. No refund or proration. - Until the expiry it can be resumed; the stored instrument is untouched.
- At the expiry the cron sweep ends the subscription: it is deactivated (roles revoked), taken out of dunning, and its instrument revoked.
- Cancelling a past-due subscription ends it immediately — the current period is the unpaid one — and stops the retries. A charge that fails while a cancellation is pending also ends the subscription rather than starting dunning.
- Plan changes are refused while a cancellation is pending.
cancel()throws for a subscription that is not charge-based: a remote contract is cancelled at its own service.
New on the subscription entity: getCancelDate(), getExpiry(), hasPendingCancellation(), isChargeBased(); willRenew() now returns FALSE for a pending cancellation and TRUE for an active charge-based subscription. Two events, both carrying a SubscriptionCancellationEvent: CancellationEvents::SCHEDULED and CancellationEvents::REVERTED. There are no built-in cancellation mails; subscribe to the events to send one.
Switching connectors
The subscribe route used to send anyone who had a subscription to their current connector. Now:
/subscription-manager/subscribe?connector=IDis honoured for existing subscribers too, so a switch to another connector (a free rail to a paid one, say) can be started.?change=1shows the chooser to an active subscriber, with their current connector marked (currenton each item of the chooser template and of theapi/subscribe-urlresponse).- A user whose only subscription has lapsed gets the chooser like a new subscriber.
A cross-connector switch is immediate: saving the new subscription ends the old one through the single-active rule. For a charge-based old subscription that now also clears dunning and revokes its instrument, unless the new subscription charges that same instrument on the same connector.
Site builders and themers
- New template
subscription-manager-portal.html.twig(variablesstate,notice,rows,actions); the wrapper carries asubscription-manager-portal--{state}class foractive,cancelling,past_dueandended. - New routes under
/user/{user}/manage-subscription/:cancel,resumeandpayment-method, with the same access as the manage route (the owner with manage own subscriptions, or an administrator). - The
manage_routesetting still lets a site replace the page with its own. - The chooser template gains a "Your current plan" badge for
item.current.
No update hook is needed: no schema or configuration changes.