Skip to content
This repository has been archived by the owner on Oct 16, 2018. It is now read-only.

Subscriptions

Alex Bradley edited this page Jan 27, 2018 · 44 revisions

Installation

  1. Add Payola to your Gemfile:

    gem 'payola-payments'
  2. Run the installer and install the migrations:

    $ rails g payola:install
    $ rake db:migrate
  3. Configure Payola for your needs. You can find out more in https://github.com/peterkeen/payola/wiki/Configuration-options.

  4. Add the models

    Payola has it's own Payola::Subscription model used for tracking subscriptions, but you'll need to create one to represent your plans. We'll create a SubscriptionPlan model here:

    $ rails g model SubscriptionPlan \
          amount:integer \
          interval:string \
          stripe_id:string \
          name:string

    Make sure you include Payola::Plan:

    class SubscriptionPlan < ActiveRecord::Base
      include Payola::Plan
    end

    A plan model requires a few attributes and has some more optional ones:

    • amount:integer, (attribute) an amount in the format that Stripe expects. For USD this is cents.
    • interval:string, (attribute) one of 'day','week','month', or 'year'
    • stripe_id:string, (attribute) a unique identifier used at Stripe to identify this plan
    • name:string, (attribute) a name describing this plan that will appear on customer invoices
    • interval_count:integer, (attribute) optional the number of intervals between each subscription
    • trial_period_days:integer, (attribute) optional the number of days for the trial period on this plan

You can of course also add your own attributes to use in relation to the plan.

IMPORTANT NOTE: creating a new plan will queue a background job to create it on Stripe if it does not exists.

Checkout form

Currently we only support custom forms and the checkout button. Here's an example of a custom subscription form that you can drop right into app/views/subscriptions/new.html.erb:

 <!-- this header can go in <head> or at the bottom of <body> -->
<%= render 'payola/transactions/stripe_header' %>

<%= form_tag('/subscriptions',
      class: 'payola-onestep-subscription-form',
      'data-payola-base-path' => '/payola',
      'data-payola-plan-type' => @plan.plan_class,
      'data-payola-plan-id' => @plan.id
  ) do |f| %>
  <span class="payola-payment-error"></span>
  Email:<br>
  <input type="email" name="stripeEmail" data-payola="email"></input><br>
  Card Number<br>
  <input type="text" data-stripe="number"></input><br>
  Exp Month<br>
  <input type="text" data-stripe="exp_month"></input><br>
  Exp Year<br>
  <input type="text" data-stripe="exp_year"></input><br>
  CVC<br>
  <input type="text" data-stripe="cvc"></input><br>
  <input type="submit"></input>
<% end %>

Payola's subscription behavior is triggered by the class payola-onestep-subscription-form on the form and configured with data attributes. There are currently three data attributes that all must be present:

  • payola-base-path is the path where you've mounted Payola in your routes, which is usually /payola.
  • payola-plan-type is the value returned by plan_type on the object that includes Payola::Plan.
  • payola-plan-id is the value returned by id on the object that includes Payola::Plan.

When the customer fills out this form and hits the submit button, Payola will:

  1. Contact Stripe to get a token from the credit card information
  2. POST your form asynchronously (see below for an example controller)
  3. Creates the Stripe::Subscription record asynchronously
  4. On the client, waits for subscription processing to finish before redirecting to the URL specified by SubscriptionPlan#redirect_path. Here's what that should look like:
class SubscriptionPlan < ActiveRecord::Base
  include Payola::Plan

  def redirect_path(subscription)
    # you can return any path here, possibly referencing the given subscription
    '/'
  end
end

Here's what your subscriptions controller should look like:

class SubscriptionsController < ApplicationController
  # bring in the `render_payola_status` helper.
  include Payola::StatusBehavior

  def new
    @plan = SubscriptionPlan.first
  end

  def create
    # do any required setup here, including finding or creating the owner object
    owner = current_user # this is just an example for Devise

    # set your plan in the params hash
    params[:plan] = SubscriptionPlan.find_by(id: params[:plan_id])

    # call Payola::CreateSubscription
    subscription = Payola::CreateSubscription.call(params, owner)

    # Render the status json that Payola's javascript expects
    render_payola_status(subscription)
  end
end

Make sure to add it to your config/routes.rb as well:

resources :subscriptions

You can add a relation between the owner and the subscription.

class User < ApplicationRecord
  has_one :subscription, ->(sub) { where.not(stripe_id: nil) }, class_name: "Payola::Subscription", foreign_key: :owner_id
end

Checkout button

To subscribe using the checkout button, use the checkout partial like this:

<%= render 'payola/subscriptions/checkout', plan: YourSubscriptionPlanClass.first %>

This will insert a Stripe Checkout button.

Important note: When you use the checkout button with subscriptions payola does not use the controller setup we talked about above. Instead, it will redirect to the plan's redirect_url. You can attach an owner by setting an event subscriber for payola.subscription.active:

Payola.configure do |config|
  config.subscribe('payola.subscription.active') do |sub|
    user = User.find_by(email: sub.email)
  
    if user.nil?
      raw_token, enc_token = Devise.token_generator.generate(
                   User, :reset_password_token)
      password = SecureRandom.hex(32)
  
      user = User.create!(
        email: sub.email,
        password: password,
        password_confirmation: password,
        reset_password_token: enc_token,
        reset_password_sent_at: Time.now
      )
      WhateverYourPasswordMailerIsNamed.whatever_the_mail_method_is(user, raw_token).deliver
    end

    sub.owner = user
    sub.save!
  end
end

The checkout partial has a bunch of options:

  • plan: The plan to subscribe to. Required.
  • button_text: What to put on the button. Defaults to "Subscribe Now"
  • button_class: What class to put on the actual button. Defaults to "stripe-button-el".
  • quantity: The quantity of this subscription. Defaults to 1. (see this and this)
  • name: What to put at the top of the Checkout popup. Defaults to plan.name.
  • description: What to show as the description in the popup. Defaults to plan name + ((the formatted price * quantity) per month).
  • plan_image_path: An image to insert into the Checkout popup. Defaults to blank.
  • panel_label: The label of the button in the Checkout popup.
  • allow_remember_me: Whether to show the Remember me checkbox. Defaults to false.
  • email: Email address to pre-fill. Defaults to blank.
  • custom_fields: Data to pass to the charge_verifier (see Configuration Options)

Permissions for subscription changes

IMPORTANT NOTE: By default, for Canceling/Upgrading/Downgrading/Subscription quantity updates, Payola does not check to verify that the current user actually has permission to cancel the given subscription. To add that, implement a method in your ApplicationController named payola_can_modify_subscription?, which takes the subscription in question and returns true or false. For Devise this should look something like:

def payola_can_modify_subscription?(subscription)
  subscription.owner == current_user
end

Canceling Subscriptions

You can add a button to cancel a subscription like this:

<%= render 'payola/subscriptions/cancel', subscription: @subscription %>

The cancel partial has the following options:

  • button_class: The CSS class to give the button.
  • button_text: What to put on the button. Defaults to "Cancel Subscription"
  • button_id: the ID to assign to the button
  • confirm_text: the test for the JS alert. Defaults to "Are you sure?"
  • url: the path to submit cancelation to. Defaults to "payola.cancel_subscription_path(subscription.guid)"
  • at_period_end: if true, cancel subscription at end of billing period. Defaults to "false" (immediate)

Upgrades / Downgrades

You can upgrade and downgrade subscriptions by POSTing to payola.change_subscription_plan_path(subscription) and passing plan_class and plan_id for the new plan as params. You can also pass an optional quantity parameter if you require it. Payola provides a partial for you:

<%= render 'payola/subscriptions/change_plan',
    subscription: @subscription,
    new_plan: @new_plan,
    quantity: 1 %>

Subscription Coupons

You can pass a coupon code to Payola by adding a data-payola="coupon" input to your form. The coupon code will be passed directly to Stripe and attached to the subscription.

Subscription Quantity

Currently, a quantity of 1 is passed to Stripe upon creation of a Subscription. If you'd like to pass a different quantity (see this and this for reasons why you might want to do this), add a data-payola="quantity" input to your form. The quantity will be passed directly to Stripe and attached to the subscription.

Also, if you'd like to update the quantity of the subscription later (e.g. customer adds users to their account, and you charge $/user/month), you can POST a form to payola.change_subscription_quantity_path(subscription.guid) with a quantity parameter, or you can simply call Payola::ChangeSubscriptionQuantity.call(subscription, quantity), where subscription is a Payola::Subscription and quantity is an integer. The latter will bypass Payola's payola_can_modify_subscription? method, so check for permissions before calling the change directly.