Memberships are not all the same. Some memberships last a lifetime, others last a year, or a month. Some memberships are for an individual, others for a couple, others for an entire family.
Some memberships are for a particular time period -- the 2017 season, the 2017/18 school year, Summer -- others are for a particular length of time starting when you purchase it.
And then there's the behind the scenes stuff -- the "CRM" (Customer Relationship Management) systems of reminders to renew, lists of memberships up for renewal, grace periods between when a membership technically expires and the hard cut-off date, and renewal policies -- does it start on the day you pay for the renewal, or the day your previous membership expired?
What if a member wants to upgrade or downgrade their membership in the middle of their membership term? How do you account for a cost difference between membership levels? What if a student starts part way into a season, do they get a pro-rated price? What if one parent signs a child up for one session, and a different parent registers them for a later session?
In short, membership programs are complicated things. Every membership organization has its own business policies, its own rules, its own benefits.
These are a few of the variations we've run across so far, when implementing membership plans for a client.
Memberships in Drupal 8
Drupal has long been a great platform for a membership site, largely because it is so flexible. With Drupal, memberships can be easily configured to create roles, add users to groups, provide special pricing in a commerce site, grant access to content or the ability to create content, or whatever. Figure out your membership structure and the rest is easy.
However, until Drupal 8, creating memberships with the ability to provide the variations outlined above pretty much always involved a fair amount of custom work. The relatively new Membership Entity module, combined with Membership Entity Commerce goes a long ways towards making a flexible membership system, though there are still gaps, such as offering memberships that expire on a particular date.
We recently launched our first membership site in Drupal 8. We took our experience from nearly a dozen different membership sites over the years, with the full range of variations, and built a brand new membership term and role system with these variations in mind. We are working with the new maintainer of the Membership module to get is is all going to get published on Drupal.org, but for the time being, we've posted our code on Github.
Today this system is built around 1-year membership terms, with a 60-day renewal grace period -- during that grace period, the member still has full access, but if they renew, the renewal starts on the expiration date of the previous term. This seems to be a very common pattern among associations.
The other things that work swimmingly well are email reminders that get sent as the membership term expires. Our client wanted a sequence of 4 different messages that get sent 4 weeks before the expiration date, 7 days before the expiration, 1 week after the expiration date, and a "last chance" message 1 week before the end of the grace period. We built a new Scheduled Message module to handle this, basing the architecture on the algorithm recently added to an experimental Webform submodule. The cool thing about the way we implemented this, is that these messages get suppressed if the member renews. And we can send a "thank you for renewing" message as this is confirmed.
The membership module we fleshed out follows a similar architecture as the Drupal 7 membership_entity module. Memberships are the main entity, and can be active or expired. Memberships may be attached to multiple user accounts, and roles can be granted or revoked based on the status of a membership.
The "active" period of a membership is a separate entity, the "membership term". Each membership can have one active term, but any number of other terms in other states -- expired, expiring, renewed, pending, etc.
Many associations have a grace period -- a period of time where membership benefits are still active, but the term is effectively expired. And so this functionality is baked into the new membership_term module, with a corresponding "expiring" state.
The bulk of the work we did was all around managing what happens when a membership term expires, and gets renewed. For the first site, we set up rules that specify:
- When a member renews a membership_term that is "active" or "expiring", the effective dates of the new term are calculated from the expiration date of the old term. For example, if a term expires on July 20, 2017, any renewal that is done before that date or up until September 19 extends the valid membership to July 20, 2018.
- When a new membership_term is added, the old one is set to the "renewed" state, suppressing all future email reminders associated with that term.
- When a member renews a membership_term that is expired, the new membership term starts on the date of purchase. (This assumes the member renews after the grace period, and has lost their membership privileges.)
- An administrator can edit the active dates, and/or the revoke date of any membership_term, and the scheduled message send dates will adjust accordingly.
Module development in Drupal 8
This was our first foray into building a complex module for Drupal 8, and I found it to be a great learning experience. It exposed us to a large part of the Drupal API, including:
- The plugin system - Creating plugins, plugin types
- Dependency Injection, specifying interfaces rather than objects to facilitate tests with mock objects
- The Event system
- Creating Services and using the container
- The state_machine module -- we still need to port to the newer "workflow" module that has been added to core
- The Queue API
Next up, hooking up self-registration and e-commerce.
If your organization needs a custom membership solution, let us know! We'd love to help...