Skip to content

Instantly share code, notes, and snippets.

@jonsgreen
Last active August 22, 2018 23:07
Show Gist options
  • Save jonsgreen/c351b2bf4e670a26afa904ac7d7a1418 to your computer and use it in GitHub Desktop.
Save jonsgreen/c351b2bf4e670a26afa904ac7d7a1418 to your computer and use it in GitHub Desktop.
Subscription Flow Further Backend Details

Payment

here is the User Experience that we are shooting for initially:

  1. An owner arrives at /signup.
  2. they enter account details and create the account
  3. they select a subscription level
  4. enter cc information (card#159)
  5. they arrive at /new/studio, having bypassed 'choose user type screen'
  6. they enter the name and site Id for their studio. 6a. Show link to activation code
  7. they activate in MB (UX may change)
  8. We sync their account - just counts
  9. they see a summary of the sync and are prompted to accept or upgrade from the subscription they chose in step 6. (card #160)
  10. we charge the cc. (card#160)
  11. they see confirmation and instructions to download or open the app.

Here are some thoughts about what we need to do on the backend:

supporting step 3

Using Stripe For Modeling

  • Jeff enters products/plans in Stripe similar to the following.
  • We restrict Jeff to having only one active plan per product at one time.
  • Our code has three similar plans that are linked to the product through env vars. While the active plans may change should pricing or terms be adjusted, product changes will require code changes. We can get the active plan_ids for each product to get the latest subscription offerings.
  • Our component will render the subscription page based on the logic that we code for our plans. The Basic Plan will be for a one plan subscription while the Premium Plan will be a multiple plan subscription using a base fee plan and a metered plan see this example. The goal would be to support this basic plan structure:
    1. Basic Plan: 1 location and up to 10 instructors, n dollars per mo.
    2. Premium Plan: up to 3 locations and unlimited instructors, n dollars per mo.
    3. Studio addon: add a location to one of the above plans for an additional n dollars per mo.
    4. Users who sign up for the Premium Plan agree to be billed at the increased rate for the month in which a location is added above 3 and going forward..
  • The logic would rely on metadata to define any parameters not supported by Stripe that Jeff might want to tweak over time. We may want to discourage Jeff from editing metadata and instead creating new plans when parameters change but this might not be necessary because of the next bullet point:
  • At some point in this process (and this might be it) we would create a customer object in Stripe and then locally. I am going to propose that whatever objects that we create locally to sync with Stripe that we use a very simple approach. Basically we create data tables with similar names following some convention that map to Stripe (e.g. stride_customers, stride_subscriptions) and that these tables would just have the stride_id for syncing with Stripe and a json field that would capture everything else. That way we could use Stripe code itself to generate objects locally that would match what the stripe gem returns from API calls. The stripe gem seems to be very well organized and executed and this sort of thing just works:
customer_data = JSON.parse(studio.stride_customer.object_data) #this is pseudoish code
customer = Stripe::Customer.construct_from(customer_data)
=begin
 #<Stripe::Customer:0x3fcf7d28e86c id=cus_DSZuB41nvUkMfG> JSON: {
  "id": "cus_DSZuB41nvUkMfG",
  "object": "customer",
  "account_balance": 0,
  "created": 1534876731,
  "currency": null,
  "default_source": "card_1D1ekhKmjVBQM6R107R6Hc2d",
  "delinquent": false,
  "description": "Customer for jenny.rosen@example.com",
  "discount": null,
  "email": null,
  "invoice_prefix": "4822A7D",
  "livemode": false,
  "metadata": {},
  "shipping": null,
  "sources": {"object":"list","data":[{"id":"card_1D1ekhKmjVBQM6R107R6Hc2d","object":"card","address_city":null,"address_country":null,"address_line1":null,"address_line1_check":null,"address_line2":null,"address_state":null,"address_zip":null,"address_zip_check":null,"brand":"American Express","country":"US","customer":"cus_DSZuB41nvUkMfG","cvc_check":null,"dynamic_last4":null,"exp_month":8,"exp_year":2019,"fingerprint":"9F9yiPmbKv7idesv","funding":"credit","last4":"8431","metadata":{},"name":null,"tokenization_method":null}],"has_more":false,"total_count":1,"url":"/v1/customers/cus_DSZuB41nvUkMfG/sources"},
  "subscriptions": {"object":"list","data":[],"has_more":false,"total_count":0,"url":"/v1/customers/cus_DSZuB41nvUkMfG/subscriptions"},
  "tax_info": null,
  "tax_info_verification": null
}
=end 

Note that those are real objects with callable attributes including enumerable nested array object (called lists in Stripe) like sources and subscriptions.

  • I am hoping that we can create a Customer with their Subscription nested but with credit card sources to be added later.

supporting step 4

  • For credit card storing we will likely use Stripe.js and Elements or more likely stripe-react-elements to create a source for the customer in Stripe.
  • I believe we will be returned a Stripe Source object that our react component can submit to the server where we can add it to the customer. We would update our own records as well. Storing everything nested in the Customer json would be easiest but we could also create separate local Source objects if it seems like there would be features that would support.

supporting step 8

  • We could adjust the sync to update the studio with counts as quickly as possible
  • We could add status and/or errors to their local Subscription based on the counts to help drive the next step.

supporting step 9

  • The react component would enforce that they select a valid subscription and submit to our server
  • The server would add the subscription to the customer in Stripe and sync locally. Note that the Subscription Object has plans nested in it so we could rely on this object to display information to the user and extract metadata parameters to drive logic for maintaining subscription plan limits and metered plan updates.
  • Though again the customer subscription could just be nested in the customer this might be worth breaking out into its own local object for easier reification and association to the studio.
  • For studios on Premium Plan and exceeding the base limit we would submit the first Usage Record.
  • At this point Stripe should take care of regularly billing the customer according to their plan and if there is no trial period as part of the plan then the billing cycle should start immediately.

Additional Backend Support

Subscription Monitoring During MB Sync

  • As part of the MB Sync there will be an additional step that monitors whether the studio is still compliant with their subscription. Presumably this would be same or shared code from the initial subscription check and would use the local stamped studio customer subscription plans to monitor compliance.
  • For Premium plan subscriptions this would be the point in which an Usage Record would be created. Simplest would be to just always create one since we will likely be using max for the aggregate_usage parameter of the AddOn Plan.
  • Requirements of how non-compliant subscriptions are handled need to be gathered and fleshed out.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment