The pre-1.0 cleanup (#3616937: Cleanup omnibus: dead config keys, missing token dependency, per-role user saves, page_attachments overhead, drush alias, field length, translatability, misc) touches configuration, two entity fields, the module's Drupal 10 compatibility layer and the Drush commands. Run drush updb after updating: two update hooks ship.
Settings removed
site_id, api_key and log_webhooks are removed from subscription_manager.settings (update 10026 clears them). Nothing in Subscription Manager or in any connector read them; each connector has its own webhook-logging setting. custom_subscribe_url is kept: Stripe Subscription and Shopify Subscription read it from here to redirect would-be subscribers to a site-provided page. Its description on the settings form now says so. The token browser next to it appears only when the Token module is installed; token replacement itself does not need that module.
Name fields widened
The name base field on both the subscription and the subscription plan entity grows from 50 to 255 characters (update 10027 alters the columns in place; existing data is unaffected). Plan names come from the remote service and could exceed 50 characters before.
The plan's show boolean field now defaults to FALSE rather than an empty string, so a freshly created plan passes validation without the field being set explicitly. No storage change.
Drupal 10 compatibility layer removed
Following the ^11.3 requirement from #3616936: SubscribeMenuLink declares the user.roles cache context but varies per user, leaking Join/Upgrade labels across users, the #[LegacyHook] procedural stubs are removed from subscription_manager.module, and composer.json now requires drupal/core ^11.3. Hooks are the #[Hook] classes under src/Hook only. The two form submit callbacks (subscription_manager_user_login_submit(), subscription_manager_user_register_form_submit()) and the two reconcilers used by update hooks remain procedural.
The module's classes are autowired: the queue workers, the constraint validator, the controller, the settings form and the hooks service no longer define create() factories. subscription_manager.services.yml declares class-name aliases for the module's services so they can be autowired by type from any module:
Drupal\subscription_manager\SubscriptionManagerService Drupal\subscription_manager\SubscriptionManagerConnectorManager Drupal\subscription_manager\CycleEngine Drupal\subscription_manager\DunningManager Drupal\subscription_manager\Service\RemoteCancellation Drupal\subscription_manager\Service\PostPurchaseTokenService Drupal\subscription_manager\Service\SubscriptionRefreshThrottle Drupal\subscription_manager\Service\DuplicateSubscriptionResolver Drupal\subscription_manager\Service\SubscriptionReconciler
Connector plugins can do the same: declare a constructor and let PluginBase::create() wire it. Two things to know: a service without an interface alias (such as the module's logger channel) is named with #[Autowire(service: '…')], and autowired plugin properties must not be readonly, because DependencySerializationTrait cannot restore them.
Attributes everywhere
The SubscriptionEntity and SubscriptionPlanEntity entity types and the two Views field plugins are now declared with PHP attributes. Connector plugins may still use the @SubscriptionManagerConnector annotation (the module tests that it works), but the attribute is recommended.
Drush command logic as services
The logic behind subscription_manager:cleanup-duplicates, subscription_manager:find-duplicates and subscription_manager:reconcile lives in two new services the commands only format:
subscription_manager.duplicate_resolver(DuplicateSubscriptionResolver):cleanupInactiveDuplicates(?int $uid, bool $dry_run),findDuplicates(?int $uid, bool $include_same_connector),resolveDuplicates(array $duplicates, ?string $priority, bool $include_same_connector, bool $dry_run). Results are arrays of counts and rows; actions are the class'sACTION_*constants. Cancelling goes throughRemoteCancellation(#3616934: Deleting a user deletes local subscription entities but never cancels the remote contract, so billing continues) and deactivates the local subscription only on success.subscription_manager.reconciler(SubscriptionReconciler):connectorIds(?string $connector_id)andreconcileConnector(string $connector_id, string $status_filter, bool $dry_run, ?callable $tick), returning per-connector counts and status changes.
The SubscriptionManagerCommands constructor changed accordingly (it takes the two services instead of the remote cancellation service); sites that subclass or re-register the command class must update. Command names, options and output are unchanged, except that sync-plans lost its leftover foo alias in favour of sm-sync-plans.
Smaller changes
- Role changes from a subscription save the user account once, not once per role.
- The Views field plugins Remote status and Will renew render translated labels with CSS classes (
subscription-manager-status--good|bad|warning|muted, from thesubscription_manager/statuslibrary) instead of inline colours; themes overriding the old inline styles should target the classes. - The routes
subscription_manager.manage_subscriptionandsubscription_manager.billing_historyhave leading slashes in their path definitions; the paths themselves are unchanged.