An open source Rails plugin which enables subscription billings in your application
Switch branches/tags
Nothing to show
Fetching latest commit…
Cannot retrieve the latest commit at this time.
Failed to load latest commit information.



notice: not yet used in production

SaasRamp is an open source Rails plugin which enables subscription billings in your application. I decided to take a somewhat different approach than others I have seen. It is built as a wrapper on ActiveMerchant, stores credit cards in the gateway, handles its own daily processing, and is completely independent of the authorization and authentication you choose.

Key Features

Built as a wrapper to ActiveMerchant

  • Uses the AM gateways

  • Uses the AM credit card validation

  • Requires gateways that support store/unstore credit cards, and transactions using the vault key

Recurring billing and other daily tasks and notifications run on your server

  • Does not use recurring billing at the gateway (for more control and to avoid synchronization problems)

  • Run “rake saas:daily” (e.g. as a daily cron job)

  • Billing task can be run any time, skip a day, or multiple times a day without fear of sending duplicate billings or messages

Decouples subscriptions from authentication and authorization

  • You can use Restful Authentication, Authlogic or anything else

  • Declare your model (e.g. User or Account) with “acts_as_subscriber”

Separates the subscription, customer profile (credit card), and transaction history

Subscription model

  • When a model “acts_as_subscriber” it has one subscription

  • Subscription states - :free, :trial, :active, :past_due, :expired

  • #renew method processes recurring billing

  • #change_plan method for changing plans

  • #charge_balance to bill credit card current account balance

Subscription plan model

  • Define plans with different name, rate, interval

  • Migrate your own attributes (e.g. to define limitations, like max_memory, etc)

  • Plans defined in db/subscription_plans.yml file, loaded with “rake saas:plans” task

Subscription profile model

  • subscription has one profile

  • Responsible for handling the credit card information

  • Automatic validation, storing, and unstoring in vault on the gateway

  • Profile states - :no_info, :authorized, :error

Subscription transaction model

  • subscription has many transactions

  • Provides a transaction history

  • Wraps ActiveMerchant, unifying inconsistent gateway api

  • Handles exceptions and gateway responses

Subscription observer and mailer

  • observe transactions to send out email notifications as needed

  • email delivery issued from one file, un-clutters the models

  • includes mailer templates you can use or change

Example workflow

  • New subscriptions can default to a free plan

  • New (non free) subscriptions start in :trial state (optional)

  • A warning email is sent out a few days before trial expires (trial period configurable)

  • When the trial period is over, and billing is successful, the subscription becomes :active

  • When renewals are due, :active subscriptions are billed and next renewal date is updated

  • If there's a billing error, subscription becomes :past_due

  • Past due subscriptions have a grace period (optional) and warnings are sent before subscription becomes :expired

  • Expired subscriptions can revert to a limited plan rather than shut down the account


Requires the following gems

  • ActiveMerchant - for gateways and credit card validation

  • Money - for currency numerics

  • state_machine - a better state machine

  • lockfile - for rake tasks

Testing requires gems

  • rspec, rspec-on-rails


  • cucumber

  • no-peeping-toms (plugin)


$ script/plugin install git://

Easy configuration and customization

  • Configuration via a config/subscription.yml file (can vary per environment)

  • Populate and maintain current plans via a db/subscription_plans.yml file (can vary per environment)

  • Initializer generator for the default migration and configuration files

  • Scaffold generator for example controllers and views

  • Rake task for daily processing, you create a cron job

  • Gateway monkeypatches in config/initializers/gateways/

Add to environment.rb

config.gem 'activemerchant', :lib => 'active_merchant' config.gem “money” config.gem 'state_machine' config.gem 'lockfile'

Generate initialize

$ ./script/generate saasramp

Generate migrations

$ ./script/generate saas_migration (Review the file, adjust as needed, including custom attributes if any) (Note, you can migrate existing subscribers data at the same time) $ rake db:migrate

Define and populate the subscription plans

  • Edit the file db/subscription_plans.yml

  • Load into database

$ rake saas:plans

Edit the configuration file config/subscription.yml

  • gateway name and login parameters

  • default settings

  • environment specific settings

  • custom attributes (if any)

Acts as subscriber

To the model that will own the subscription (e.g. User or Account), add


Optionally, migrate existing subscribers

If you already have subscribers in your database (e.g. User or Account records), you need to create a subscription child object (and default plan) for them. This is easy, just re-save the objects. You can do this in console, or in another migration. For example,

User.all.each {|a| }

Optionally, generate scaffolds (controllers and views)

$ ./script/generate saas_scaffold

Optionally, add email notification observers, in environment.rb

config.active_record.observers = :subscription_observer

Optionally, create your own mailer, using the SubscriptionMailer and templates as an example

and modify subscription.yml with your mailer class name


Extensions/fixes to the ActiveMerchant gateways are in config/initializers/active_merchant/

I've only tested wrappers for the Authorize.Net CIM and Braintree gateways. The AN-CIM one is temporary until ActiveMerchant integrates CIM into the regular AuthorizeNet gateway.

The gateway is expected to support the following API:

Credit card based authorized/void, if you enable credit card validation at the gateway authorize( amount, credit_card ) # => response.authorization is the reference id void( reference ) Credit card storage and unstore store( credit_card ) # => response.token is the vault profile_key unstore( profile_key ) update( profile_key, credit_card ) # optional, if not we will unstore/store

Purchase based on customer profile key (vault key) (we'll use either of the following methods) purchase( amount, profile_key ) # => response.authorization is the reference id or: authorize( amount, profile_key ) # => response.authorization is the reference id capture( amount, reference ) Credit/refund (we'll use either of the following methods) credit( amount, profile_key ) or: refund( reference, :amount => amount ) See subscription_transaction.rb and spec/remote/*_spec.rb for more details.

Plan limits checker

In your subscriber model you can declare a callback, #subscription_plan_check, that checks whether a subscriber has exceeded limits for his plan. This is used by Subscription#allowed_plans. The method is expected to return a blank value if ok (nil, false, [], {}), anything else means subscriber has exceeded limits. For example,

def subscription_plan_check(plan) (memory_used > plan.max_memory) || (file_count > plan.max_files) end # Or, def subscription_plan_check(plan) exceeded = {} exceeded = plan.max_memory if memory_used > plan.max_memory exceeded = plan.max_files if file_count > plan.max_files exceeded end

Setup recurring billing

Review the rake task (tasks/saasramp_tasks.rake) and make sure the business logic meets your requirements. The task can be run any time from the command line,

$ rake saas:daily RAILS_ENV=production You can re-run the task multiple times a day without fear of accidental duplicate billings or notification emails.

Remember that email notifications are sent not by the rake task but through the SubscriptionObserver whenever a SubscriptionTransaction is created. You can modify that behavior by editing the subscription_observer.rb file, or simply not enabling the observer in your environment.rb.

To setup recurring billing on your server, use a cron tab manager to run the task, for example,

“cd ~/myapp && rake saas:daily RAILS_ENV=production”

To setup from the command line to run every day at 3am, for example:

$ echo “0 3 * * * cd ~/myapp && rake saas:daily RAILS_ENV=production” > daily.txt $ crontab daily.txt

Test the plugin

Uses RSpec, which requires a dummy app to run the specs.

$ rails saastest $ cd saastest edit environment.rb config.gem 'activemerchant', :lib => 'active_merchant' config.gem “money” config.gem 'state_machine' edit environments/test.rb config.gem “rspec”, :lib => false, :version => “>= 1.2.0” config.gem “rspec-rails”, :lib => false, :version => “>= 1.2.0”

$ script/generate spec $ script/install plugin git:… $ script/generate saasramp $ cd vendor/plugins/saasramp/ $ rake spec $ rake remote_spec

Bonus: Example Cucumber features and steps included

$ script/generate subscription_features


  • Peepcode ActiveMerchant pdf tutorial by Cody Fauser

  • Railscasts ActiveMerchant screencasts (144, 145)

  • The Bala Paranj screencasts on ActiveMerchant +

  • Freemium

  • Saasy

Developed for the ReviewRamp ( application


We appreciate a donation of $250 for one site, $1000 for multiple sites. (Just kidding).

jonathan at linowes dat com


  • Uses the Money class for money but haven't implemented currency or exchange rates

  • I built this for a “freemium” business model ( (sign up free, pay for more features). It should work for “subscribe or nothing” but I havent worked through those scenarios. I figure you'll always want people to be able to log in and adjust their account even if they're not a paying subscriber at the moment.

  • Works with acts_as_paranoid. Declare your subscriber model a_a_paranoid before a_a_subscriber. I've had problems with the aap gem, the plugin works (and use the edendevelopment fork, see The subscription and its children are NOT paranoid, and they stick around until the subscriber is really really destroy! (bang).

Copyright © 2009 Jonathan Linowes, released under the MIT license