Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
branch: master

Fetching latest commit…

Octocat-spinner-32-eaf2f5

Cannot retrieve the latest commit at this time

Octocat-spinner-32 examples
Octocat-spinner-32 lib
Octocat-spinner-32 spec
Octocat-spinner-32 .gitignore
Octocat-spinner-32 .travis.yml
Octocat-spinner-32 Gemfile
Octocat-spinner-32 Rakefile
Octocat-spinner-32 billing.gemspec
Octocat-spinner-32 readme.md
readme.md

Billing

Build Status

Billing is a simple library to credit and debit accounts. It provides a simple interface to calculate costs for individual things inside an application. It also provides a way to charge accounts with those costs. It is a very lightweight library.

Billing is designed as an abstract process where the implementation details are left to the user.

It does not do any of the following things:

  1. Accept credit cards
  2. Talk to payment processors
  3. Manage application subscriptions
  4. Manage application service plans
  5. Make any assumptions about the billing/finanical side of your application

It does do the following:

  1. Allow you to debit accounts
  2. Allow you to hold funds in accounts
  3. Allow you to release funds in accounts
  4. Define cost classes for calculating services fees for individual resources.
  5. Allow you to implement any of the things it does not do in a standard fashion.
  6. Give you a mixin to allow you to manage billing in any part of your application.
  7. Allow you to implement guards based on an account's finanical standing.
  8. Allow you to do the things it does not do by including extensions.

General Use

Billing is generally insipired by CanCan. You include functionality into what objects you want via modules. You define costs by creating one or more classes and include a module. Billing only makes 3 fundamental assumptions:

  1. You will have a class that acts like Billing::AccountExample
  2. You will have classes that include Billing::Cost
  3. You will manage the accounts by including Billing::Tab

Defining An Account

Here is an example account

class Account
  # You do not have to check if there are enough
  # funds in the account. Billing does that sort
  # of stuff for you.
  #
  # The actions should be atomic!

  def debit_authorized?(amount)
  end

  def debit(amount)
  end

  def credit(amount)
  end
end

You could implement a stateless Account like this:

class Account
  def initialize
    @balance = 0
  end

  def debit_authorized?(amount)
    @balance >= amount
  end

  def debit(amount)
    @balance = @balance - amount
  end

  def credit(amount)
    @balance = @balance + amount
  end
end

Defining Costs

Now let's say your application deals with SMS. Sending a SMS costs a fixed amount of money (for simplicity). Define a cost class to handle that calcation. You can do more complex calculations, but we'll go over that later.

class SmsCalculator
  include Billing::Cost

  def sms
    1
  end
end

Creating an Account Manager

Now the final step is to create a handler. This class should do two things:

  1. respond_to :current_tab => true
  2. include Billing::Tab

Here is an exampler

class Biller
  include Billing::Tab

  def initialize(account)
    @account = account
  end

  def current_tab
    @account
  end
end

Using the interface

At this point we are ready to start doing things on the account. Here is an example:

account = Account.new
bank = Biller.new account

# this ensures that the account has at least "1" in it. 
# It puts "1" into holds ensuring that it will be available
# when account is ready to take the debit
bank.charge_authorized? :sms 

# Charge something to their account
#
# This changes the account balance!
bank.debit :sms

# Add something to their account
#
# This change the account balance!
bank.credit :sms

# You can also use a Fixnum or Float
# if you want to credit or debit an arbitrary amount
bank.credit 591
bank.debit 382.75

# alias exists so you can use charge for `charge` for `debit` and
# `refund` for `credit`. Bang methods exist as well, altough there is not
# difference between them and the other methods. Use which ever syntax
# you prefer

bank.charge 55
bank.refund! :sms

bank.charge_for 16, :contacts
bank.refund_for! 4, :emails

Calculating Costs

Cost class methods may be called in a variety of ways. Here are some examples and the corresponding method call. Assume the cost refers to an instance of a class with Billing::Cost included. Assume bank is includes Billing::Helpers

Scenario: something always costs the same

bank.debit :sms
# means
cost.sms()

Scenario: cost depends on an instance of the object

sms = Sms.new
bank.debit sms
# means
cost.sms(sms)

Scenario: More information is needed to calculate a cost

bank.debit :sms, :region => :north_america
# means
cost.sms(:region => :north_america)
bank.debit Sms
# means
bank.debit :sms

Handling Failure & Advanced Usage

Perhaps you want to hit the account only after you do some code. You can pass the the debit and credit methods blocks. The debit or credit will only hit the account if the block does not raise an error.

bank.debit :sms, :region => :north_america do
  begin
    Gateway.deliver sms
  rescue GatewayError => ex
    # oh damn, the gateway may an error or something
    # even though the user's SMS is valid
    # they shouldn't be charged for this.
    log_exception ex
    raise RuntimeError
  end
end

You can credit/debit for multiple charges at once using _for methods. They work the same way, except take an a numeric argument first to multiply the charge by.

bank.debit_for 5, :sms

bank.credit_for 13, :searches

bank.credit_for 5, :search, :engine => :hoovers do
  # whatever you need to do
end

Extensions

Billings is designed to be extended via Modules. You can include more modules into the managing class. Here is the current extension list:

  1. Logging (can also be used for auditing)
  2. Callbacks
  3. Observing

Logging

You can log (and audit) all transcations made through your managers by including the Billing::Extensions::Logging into your class. The default logger is Logger.new($stdout). Here is an example

class Bank
  include Billings::Tab
  include Billings::Extensions::Logging

  # redefine the logger method if you want to use your own
  # custom logger.
  #
  # The class uses the "info" log level
  def logger
    Log4r.new $stdout
  end
end

Callbacks

Enables before_* and after_ callbacks on debit and credit

class Bank
  include Billings::Tab
  include Billings::Extensions::Callbacks

  after_debit :send_charge_notification

  after_credit :send_refund_notification

  def send_charge_notification
    # weeee
  end

  def send_refund_notification
    # weee
  end
end

Observering

Enables before_* and after_ callbacks on debit and credit for observers

class Bank
  include Billings::Tab
  include Billings::Extensions::Observers
end

class BankObserver < ActiveModel::Observer
  def before_debit(*args)
  end

  def after_debit(*args)
  end

  def before_credit(*args)
  end

  def after_credit(*args)
  end
end

The Future

Billing's future is solving the things it does not do by adding them through extensions. Billing will never do everything financially related. There are things Billing will never know about your application so it will always try to remain as abstract as possible. It will slowly creep toward this boundary: charging credit cards. I would like to see Billing go into the direction of doing everything but that while remaining as abstract as possible leaving only one implementation detail: charging credit cards.

I will add functionality as I need it. Look at extensions and other billings gems.

Something went wrong with that request. Please try again.