Attempting to structure a flexible subscription system in a rails application

I have an application where different type of subscriptions can exist. I am having a hard time deciding how to cleanly organize my code and I fear this is because I made a major design problem early on that I can’t ‘undo’ in my brain.

The application works fine, but updating anything related to the subscription system is a major undertaking. I would like help in understanding where I went wrong and what direction I should take to correct / prevent this problem in the future.

I am going to try to be relatively brief in my description below, don’t fret over asking me for more details about anything.

The app is a learning management system for coaches and students to share information.

I create plans that Users can subscribe to to use the app (then I use Stripe to help facilitate the billing process.

A plan in my app has an id, name, description, price, interval (how frequently the user is charged), and a trial period duration:

A subscription in my app has an id, the associated user, the associated plan, some data associated with the payment processor (stripe), a state (active/inactive/etc), the id of a subscription that may be the ‘owning’ subscription to this subscription (sometimes a user may not be paying for their own subscription, more on this later), and a list of included plans that the user can use to invite other users to sign up for the app with.

Here is an example of a real scenario:

  • A person wants to use the app as a student.
  • They go through registration, select that they are a student and are given the option of a few plans to choose from.
  • This person opts for a plan that bills him monthly and includes only the option to invite one coach to help him learn to use the app and one parent to sign in and observe their progress.

How I currently accomplish the above scenario:

  • I have a plan stored in the database that is shown as an option when the user registers.
  • I use the information from the selected plan to build a subscription for the user.
  • During the creation of the subscription a billing attempt is sent out to the billing processor (Stripe) - if this succeeds the user moves forward with registration, if not they have to re-attempt their billing information.
  • This is where I think the fragile world starts to really come apart: When Stripe says everything is ok with the payment, I call a method called build_other_plans that adds ‘included plans’ or ‘invitations’ depending on two situations:
    • If the subscribing user is a student we need to give the student permission to invite someone to become their coach: The description of said plan would be something like: “A plan to coach #{@subscription.user.full_name}”
    • If the subscribing user is is a coach, we need to give the coach ‘plans’ to invite 10 students to become their students.
  • . . . What if I want to change how many plans/invitations a student receives when they sign up for plan Y? I can’t right now. I literally have it hard coded in the app that students get X invites and coaches get Y invites. I have more roles than just a student and a coach - this gets very messy. It is not the original plan itself that contains the logic for what to do when the plan matches specific criteria, I simply check for the criteria then work through some logic right in the controller.

In practice I need to be able to handle situations as diverse as:

  • A coach signs up for a ‘summer camp plan’ that has 20 student subscriptions they can use to invite users to the app to sign up and be coached by them for the duration of the summer camp.

  • Each of these students may or may not already have an existing subscription (the app is ok with users having multiple subscriptions / being associated with multiple plans).

  • Each of these students, after being invited, should be able to invite their parents to observe them using the app.

  • If the coach stops paying for the subscription, the subscriptions that have that coach subscription listed as the ‘owning’ subscription need to be deactivated along with the coach’s subscription.

  • A student signs up for an account.

    • They can invite their real life coach to use the app.
    • They can invite their parents to sign up and ‘observe’ them within the app.
    • If the student’s subscription becomes inactive, the coach / parents subscription becomes inactive.

Right now this is a really bizarre thing to adjust because I’m hard coding logic into the work flow to check 'are you a student? Oh you probably want to do this," - it is wrong somehow and I don’t know how to elegantly fix the problem.

I feel like I’m grazing against the real solution but just not landing on it and it’s causing me to work really hard to solve very simple problems. If my ideas were posted clear enough for anyone to actually understand them, does anyone know of a clean way to structure this mess of information? Are there any good rails examples out there of someone using a similar subscription / invitation system?

I feel I have a lot of things about software architecture that will 'click 'in my head once I solve this problem, please help me get there!

After writing this out and thinking about it, it seems like I’d get a lot of benefit simply by adding more attributes to my models.

If I add attributes like the following to Plan:

  • number_of_coach_invitations
  • number_of_student_invitations

It seems like an overwhelmingly simple solution to the odd situation I have put my brain in. I’m going to continue to think about this.