When it comes to recurring billing there is a bewilderment of services available: Recurly.com, Chargify.com, Spreedly.com, CheddarGetter.com, and so on. Each has pros and cons, but our billing needs were a bit involved, and since we couldn’t find a service that seemed to be a perfect match we decided to create our own system, using Braintree as our credit card payment processor.
Full disclosure, it did end up being quite complicated, and for those with simpler needs it is certainly worth investigating any of the above - Braintree even has its own simple recurring billing system built in. However, we’ve now got a system we’re confident in, which is super easy to customize as our needs mature, over which we have full control, and which will never go out of business on us or suffer downtime or start charging us more or…you get the idea.
This isn’t so much a guide as a collection of stuff that we figured out along the way. It may be useful for you if you are considering taking the plunge yourself.
We needed a system that would allow the following:
- Credit card as well as invoice-based billing
- Usage-based payment packages, with a minimum monthly fee
- Trial period, customizable per customer
- Millicents-based (usage is based on attributions, which cost e.g. €0.000125 each)
- Customer or account manager can upgrade/downgrade package any time
- VAT based on customer location and VAT status
Our customers’ payment details are stored in an
Account model, the
billing-relevant parts of which look like this:
braintree_customer_id: string, # braintree's 'vault' id for stored credit cards billing_start: date, # prior to this date a customer is in their trial period billing_day: integer, # the day this customer will be charged each month invoice: boolean # customers can pay via invoice or credit card vat_number: string # don't charge VAT to EU customers with a valid VAT number
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
As you can see, when an account is created we set up a few things.
PaymentPlanTemplate refers to our three ‘default’ payment plans - Basic,
Business and Enterprise. Each
PaymentPlan has a different attribution cost and
minimum fee. Since each account has its own collection of
account managers can easily edit a customer’s available payment plans as needed.
You may wonder why
billing_day is limited to 28 - well, that’s our cheeky way of
bypassing the complexity involved in end-of-month calculations. If
billing_day can be anything up to and including 31 then things get pretty
gnarly in shorter months (god forbid you should ever experience a leap year).
We had a stab at it (here’s a
representative sample), recoiled in horror, and decided it wouldn’t kill anybody
who signs up on the 29th to pay on the 28th of each month.
Saving credit cards
Braintree takes all of the complication out of storing customers’ payment details. Using their braintree_ruby gem’s awesome transparent redirect feature we can easily set up a form in our user’s profile to save a card without any sensitive data ever touching our system:
1 2 3 4 5 6 7 8 9
1 2 3
…yep, that’s it! The form gets submitted directly to Braintree, never
touching your server, and braintree responds with success or failure allowing
you to store the customer’s
braintree_customer_id for use at billing time. The
Braintree docs are a great place
to go looking for more info if you can’t quite believe it’s that simple.
The recurring part
As you would expect, at the heart of our billing system is a recurring cron job
running a rake task that checks who should be billed today and does the
necessary leg-work. Simple scopes on the Account model provide us with our
1 2 3 4 5 6 7 8 9
process_async! comes from the excellent Sidekiq gem
which handles all of our background jobs.
Most of the complexity in our
Receipt.generate method comes from figuring out
what period we are billing the customer for. Everything else (how many
attributions the account had, which payment plan is active etc.) can easily be
figured out from there and is much more dependent on how you calculate cost,
so I won’t go into detail.
billing_day? That’s all you need to work out which billing period an
account is/was/will be in on any given date:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
Hopefully this is fairly self explanatory - if the date’s day-of-the-month is smaller than (ie. before) the account’s billing day, then the period must have started the month before and will end this month; if bigger (after) then it will start this month and end the month after.
Happily, ActiveSupport takes care of a lot of the complexity here, as we can use
crazy magic talk like
date - 1.month to cover a variety of ills.
Once we know what period we’re talking about, it’s trivial to work out how many attributions the account had and multiply them by the active payment plan’s attribution cost.
We end up with a receipt that looks something like this:
1 2 3 4 5
The billing part
So we now have a receipt all calculated and ready to go. What happens next depends on what sort of customer we are dealing with:
Credit card customers
This is dead simple, thanks to Braintree. We submit a transaction like so:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
CalculatorService stuff is there to calculate VAT etc, which is based on
the customer’s location and whether or not they have submitted a valid VAT
number. We use
composed_of in the
Receipt model to enable us to call methods
to_euros on a value:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
This is even simpler, as we simply generate a PDF invoice from the receipt’s show action, using our wonderful shrimp gem, and email it to the customer, who cheerfully pays us. Job done!
Obviously when money is involved you want to be as transparent as possible. One big advantage of everything being in our own system is that it’s simple to show our customers all the information they need about their billing status, such as their previous bills and current status:
or which cards they have saved with us:
Not having to rely on external APIs for anything except credit cards also makes the entire dashboard a lot snappier, which is a big plus!
The hardest part of building a recurring billing system is designing it in the first place. We were lucky enough to be able to take it at our own pace and the design grew out of the implementation process quite naturally. No one part of it is particularly complicated, but taken all together there is indeed a great deal of complexity involved, which is why so many people advise against ‘reinventing the wheel’ and making your own. I haven’t even touched on how we deal with trial periods and payment-based authorization - this will come in a later post - but I hope this gives some idea of what is required and what you should think about if you decide to roll your own. Above all - ENJOY RESPONSIBLY.