Problem/Motivation
Subscription Manager is at 1.0.0-beta2 with a queue of known work spanning Drupal 12 compatibility, an access-control bug, multi-connector support, and pre-stable hardening. This meta orders that work into a dependency-aware sequence so contributors know what to pick up next and what blocks what. Everything listed below should land (or be explicitly deferred with a comment here) before tagging 1.0.0.
Proposed resolution
Resolve in this order. Within a phase, issues are independent and can proceed in parallel; phases are ordered by dependency.
Phase 1: Foundation. Land first because nearly every later patch touches the same files, and because subsequent work deserves trustworthy role handling and a testable admin UI.
- #3603421: Automated Drupal 12 compatibility fixes for subscription_manager 1.0.x-dev Drupal 12 compatibility (hook class conversion plus manual fixes). First because the OOP hook conversion rewrites the
.modulefile; landing it before everything else avoids rebasing every subsequent patch across the conversion. The|| ^12constraint flip waits for 12.0.0-beta1 testing (week of 14 September 2026) and may land separately. - #3616929: Role revocation depends on a live plan lookup, so users can keep paid roles after expiry or cancellation Role-revocation snapshots (critical). The one access-control bug; everything that follows assumes role handling can be trusted.
- #3616930: Entity annotations and route providers reference four permissions that are never defined, locking admin UI to user 1 Undefined permission strings. Small and independent; unblocks non-uid-1 admin testing of all later work.
- #3616932: Subscribe routes fatal on fresh installs: default_connector is absent from config/install and passed unguarded to createInstance() Fresh-install default_connector guard. Small; removes a fatal that bites every clean test environment.
Create a release node for the beta3 release. (This was accidentally forgotten, and needed additional maintainer permisssions.)Cut a beta 4 tag.Create a release node for the beta4 release. (It was only tagged due to lack of maintainer permissions)
.
Phase 2: Multi-connector correctness. The structural work that makes multiple simultaneous connectors safe.
- #3616880: Move _subscription_manager_sync_remote_subscriptions() into SubscriptionManagerService and convert the cron queue drain to a QueueWorker plugin Move the sync helper into SubscriptionManagerService and convert the cron drain to a QueueWorker. Lands before the sync rewrite so that fix has an injectable, testable home (also resolves the queue's poison-item stall noted in its comments).
- #3616778: Define and enforce single-active-subscription semantics (loadLocalSubscription() returns an arbitrary first match) Single-active-subscription semantics. The Option A/B decision gates #3616770: Self-healing remote subscription sync only queries the default connector's short-circuit and shapes #3616769: Add a connector chooser to the subscribe flow when multiple connectors provide plans's switch flow, so decide early even if enforcement lands with this issue.
- #3616770: Self-healing remote subscription sync only queries the default connector Multi-connector self-healing sync (implemented in the refactored service method).
- #3616776: SubscriptionEntity::getPlan() ignores connector_plugin_id, allowing cross-connector plan_id collisions getPlan() connector-aware lookup.
- #3616773: shouldRedirectCurrentUserOnLogin() only consults the default connector Login-redirect polling across connectors.
Cut abeta5tag.Create a release node for the beta5 release. (It was only tagged due to lack of maintainer permissions.)
Phase 3: Features.
- #3618739: Add a charge-based connector mode with a local billing cycle engine
- #3616769: Add a connector chooser to the subscribe flow when multiple connectors provide plans Subscribe-flow connector chooser. Depends on Phase 2 for safe coexistence; composes with #3616932: Subscribe routes fatal on fresh installs: default_connector is absent from config/install and passed unguarded to createInstance()'s single-connector auto-default.
- #3615735: Add a rating step for usage-based charges, delivered per connector scheduling mode Usage-based (metered) billing support. Interface addition; sequenced here so connector implementations (including #3616779: GoCardless connector module (bank debit: Bacs, SEPA, ACH, PAD)) target a settled connector interface.
- #3618741: Add a pluggable tax/invoice service seam (calculation, evidence, invoice reference), keeping tax engines out of core
- #3621282: Show subscribers their invoice history in the membership portal
Cut abeta6tag.Create a release node for the beta6 release. (It was only tagged due to lack of maintainer permissions.)
Phase 4: Pre-tag hardening and cleanup. Ordered last but required before 1.0.0; several change the public surface and are cheapest to change pre-stable.
- #3618740: Add a dunning framework: past_due state, scheduled retries, grace period, and lifecycle notifications
- #3619074: Succeeded charge records are terminal, so payer-initiated reversals (SEPA refunds, Bacs indemnity claims, chargebacks) have nowhere to land: Sequenced directly after the dunning framework because it consumes both the charge records from #3618739: Add a charge-based connector mode with a local billing cycle engine and the state machine from #3618740: Add a dunning framework: past_due state, scheduled retries, grace period, and lifecycle notifications.
- #3616931: /subscription-manager/api/my-subscription returns full entity arrays including raw remote data, and responds 202 to GET Curate the my-subscription API response (response-shape change: must precede stable).
- #3616933: Harden post-purchase auto-login tokens: single-use and a shorter default TTL Single-use, shorter-TTL post-purchase tokens (coordinate connector follow-ups).
- #3616934: Deleting a user deletes local subscription entities but never cancels the remote contract, so billing continues Remote cancellation on user deletion.
- #3616936: SubscribeMenuLink declares the user.roles cache context but varies per user, leaking Join/Upgrade labels across users SubscribeMenuLink cache context and label translatability.
- #3616937: Cleanup omnibus: dead config keys, missing token dependency, per-role user saves, page_attachments overhead, drush alias, field length, translatability, misc Cleanup omnibus (dead config keys, drush alias, field length, translatability, misc).
- #3621280: Add update-path test coverage from a pre-roadmap database fixture
- Cut a
beta7tag. - Create a release node for the beta7 release.
Tracked here but not blocking 1.0.0:
- #3616779: GoCardless connector module (bank debit: Bacs, SEPA, ACH, PAD) GoCardless connector: a separate project, sequenced after #3615735: Add a rating step for usage-based charges, delivered per connector scheduling mode settles the interface. Ecosystem goal, not a core-module release blocker.
- #3618742: Helcim connector module (charge-based card payments, CAD/USD)
- The
|| ^12compatibility claim from #3603421: Automated Drupal 12 compatibility fixes for subscription_manager 1.0.x-dev, which can ship in a 1.0.x patch release once tested against Drupal 12 betas.
Release criteria for 1.0.0: all Phase 1 through 4 issues Fixed or explicitly deferred with rationale in this meta; test suite green on supported cores; Upgrade Status clean apart from intentionally deferred items; change records published for every API or behavior change; and, immediately after tagging, opt in to security-team coverage. Note the venue change that opt-in brings: security-relevant reports move from this public queue to the security team once a stable, covered release exists.
Remaining tasks
Set this issue as parent on the child issues above; keep this summary updated as issues close or new blockers are filed; close #3275714: Mailchimp entity in the way as outdated separately (its hardcoded Mailchimp requires were removed before beta1 and are absent from 1.0.0-beta2).
User interface changes
Per child issue.
API changes
Per child issue; all pre-stable, so no BC obligations beyond change records.
Data model changes
Per child issue.
Comments
Comment #2
colanComment #3
colanComment #4
colanComment #5
colanAdded #3618739: Add a charge-based connector mode with a local billing cycle engine.
Comment #6
colanComment #7
colanComment #8
colanComment #9
colanComment #10
colanComment #11
colanAdded #3619074: Succeeded charge records are terminal, so payer-initiated reversals (SEPA refunds, Bacs indemnity claims, chargebacks) have nowhere to land to Phase 4, immediately after #3618740: Add a dunning framework: past_due state, scheduled retries, grace period, and lifecycle notifications: every scheme the charge-based rails target allows the payer to reverse a charge after it succeeds (SEPA's eight-week refund right, Bacs indemnity claims, ACH/PAD consumer returns, card chargebacks), so the charge-record lifecycle needs a terminal reversed state that suppresses automatic retries and emits a credit through the #3618741: Add a pluggable tax/invoice service seam (calculation, evidence, invoice reference), keeping tax engines out of core seam. Also corrected the child-issue count in Remaining tasks (the phase lists now hold twenty issues).
Comment #12
colanNeed to wait for release-cutting permission. Request sent to @grasmash.
Comment #13
colanComment #14
colanPhase 2 done,
beta5release tagged.As soon as I get permission, I'll create release nodes for
beta3,beta4, andbeta5.Comment #15
colanUpdated releases.
Comment #16
colanUpdated the name/ID of an upcoming release.
Comment #17
colanAdded #3621280: Add update-path test coverage from a pre-roadmap database fixture, and did some cleanup.
Comment #18
colanAdded #3621282: Show subscribers their invoice history in the membership portal.
Comment #19
colanUpdated now that
beta6is tagged.Comment #20
colanI got the proper access as per #3622722: Offering to maintain Subscription Manager. Published beta3, 4, 5 & 6 release nodes & notes.
Comment #21
colanDunning is in!