This fork is intended to add functionality for active merchants ‘integrations’ powered payment solutions (those like PayPal Express)
-
Added support for currency other than USD (Notice: there can only be one currency used in the system)
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.
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 Transaction 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
-
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
optional:
-
cucumber
-
no-peeping-toms (plugin)
$ script/plugin install git://github.com/linoj/saasramp.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/
config.gem ‘activemerchant’, :lib => ‘active_merchant’ config.gem “money” config.gem ‘state_machine’ config.gem ‘lockfile’
$ ./script/generate saasramp
$ ./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
-
Edit the file db/subscription_plans.yml
-
Load into database
$ rake saas:plans
-
gateway name and login parameters
-
default settings
-
environment specific settings
-
custom attributes (if any)
To the model that will own the subscription (e.g. User or Account), add
acts_as_subscriber
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| a.save }
$ ./script/generate saas_scaffold
config.active_record.observers = :subscription_transaction_observer
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.
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
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 SubscriptionTransactionObserver whenever a SubscriptionTransaction is created. You can modify that behavior by editing the subscription_transaction_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
If you want to further customize how Saasramp subscriptions are act, the recommended way to do so, is to create your own subscription model and include Saasramps subscription methods into it:
class Subscription < ActiveRecord::Base include Saasramp::Subscription end
This allows you to define callbacks on the subscription or in an observer.
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 + Authorize.net
-
Freemium
-
Saasy
Developed for the ReviewRamp (www.reviewramp.com) 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 (en.wikipedia.org/wiki/Freemium) (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 www.vaporbase.com/postings/stack_level_too_deep). 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