Drupal case study · 02

Business Networking Membership Management

A business networking organization needed one system to manage membership plans, dues, renewals, payments, and the staff work around them. I developed a custom Drupal 10 membership application that brought those workflows together. It was deployed in production to support the organization’s ongoing membership operations, using reusable components designed to support other membership models.

Organization
A business networking organization with members on different plans, dues, and renewal schedules.
Need
Manage members across the full lifecycle, from enrollment and dues through renewals, plan changes, failed payments, and staff exceptions, without hard-coding organization-specific rules.
Solution
A custom Drupal 10 membership application with configurable plans and pricing rules, scheduled renewals through a shared payment service, and staff dashboards, history, and repair tools.
Result
Deployed in production to support the organization’s ongoing membership operations, with consistent member workflows and a clear membership history for staff.
My role
Designed and built the membership records and history, pricing plugins, lifecycle validation, scheduled renewals, staff tools, and the integration with the shared payment service I designed.
  • Content Entities
  • Config Entities
  • Plugin API
  • Queue API
  • Lock API
  • Database API
  • Views
  • Drush

The challenge

A business networking organization needed more than a way to collect an initial membership payment. It had to manage members across the whole membership lifecycle: different plans and prices, varying renewal schedules, plan changes, payment processing, failed transactions, and the administrative exceptions that come with real members. When something went wrong, staff needed a trustworthy history so they could investigate and help.

I had built an earlier member portal for a medical society with more than 3,000 members that ran for over ten years. That experience showed me where membership systems become difficult over time: renewals, exceptions, and history, not initial enrollment.

The rules also needed to stay out of the code. Some organizations renew members on the anniversary of the day they joined, while others renew everyone on a common calendar date. Plans, prices, eligibility requirements, and renewal policies had to be configuration, so the system could support this organization’s model without becoming a one-off application.

The solution

I designed and developed a Drupal 10 membership application with configurable membership plans and policies. Enrollment, dues calculation, payment, renewal, plan changes, and cancellation all ran through one consistent set of lifecycle workflows.

Dues and renewals were processed through the platform’s shared payment service, and renewals ran automatically on schedule. Staff had dashboards, membership history, simulation and diagnostic tools, and controlled repair capabilities for the cases that needed a person.

Results and capabilities

The application was deployed in production and supported the organization’s ongoing membership operations. It gave the organization one place to manage membership plans, dues, renewals, payment history, and administrative workflows.

Members went through consistent enrollment, renewal, and plan-change processes. Staff could review membership history, investigate issues, and manage exceptions by looking at the membership lifecycle itself instead of reconstructing it from payment records. Because plans and policies were configuration rather than code, the same foundation was built to support organizations with different membership models.

My role

I designed and built the membership entities and their history, the pricing plugin system, lifecycle validation, scheduled renewal workers, the staff diagnostic and repair tools, and the integration with the shared payment service I had designed for the platform.

I also established the rule that every entry point had to go through the same lifecycle logic, whether the change came from a member form, scheduled worker, Drush command, or update hook. I reviewed the connected work across the rest of the product.

Technical implementation

Content entities stored each member’s current status and auditable history. Config entities described deployable membership types and policies. Pricing plugins handled calculations that varied among products without scattering customer-specific conditions through forms and renewal jobs. Focused services owned enrollment, renewal, plan changes, cancellation, and repair. Membership used the platform’s shared payment service for dues and renewals rather than implementing its own payment integration.

Scheduled work ran outside member requests. Queue workers used Lock API and database transactions to prevent overlapping renewals, while a stable reference made each renewal safe to retry without creating a second charge. Drupal events notified mail, dashboards, and other features only after the official membership change completed. Forms, staff tools, and scheduled workers all used the same lifecycle services, so the same rules applied regardless of how a membership changed.

How renewal worked

Enrollment resolved the requested membership type, checked eligibility, and asked a pricing plugin to calculate the charge from server-controlled facts. After a verified payment, the membership became active and received its first effective period.

When renewal became due, a worker claimed that scheduled renewal, acquired a shared lock, reloaded the latest record, and rechecked the current policy. A confirmed payment extended the membership and recorded the financial result. A failed or uncertain result followed the configured policy and remained visible for staff attention. Future plan changes were also rechecked when they became effective because eligibility and configuration might have changed.

Drupal under the hood

Content and Config Entity APIs
Separated member-specific records from deployable product and policy configuration.
Plugin and Validation APIs
Provided configurable pricing while protecting eligibility and data rules in every entry path.
Queue, Lock, and Database APIs
Processed renewals outside member requests and protected critical financial changes.
Views and Drush
Supported staff dashboards, exports, read-only audits, simulations, and controlled repair.

Why I chose this approach

I chose to build a custom membership system because the organizations we worked with had very different membership rules, and membership needed to work directly with our payment service and eventually influence other parts of the platform, such as access to courses or community groups. Forcing those requirements into a simpler membership model would have pushed organization-specific conditions into forms, payment code, and renewal jobs.

Inside the system, I kept three things separate. Membership records, meaning each member’s current status and history, were content entities because they change at runtime and have to remain auditable. Membership types and policies were config entities, so they could be deployed and adjusted without touching member data. Lifecycle processing lived in focused services that every entry point had to use.

That separation kept organization-specific rules in configuration and pricing plugins rather than in code paths, meant the same rules applied however a membership changed, and gave staff a history they could trust. It was more work to build than a simpler model, but it let the system support this organization’s membership rules and keep extending membership as new needs came up.