/home /blog 23 Dec 2024 | Get ipynb

Simple Pay-As-You-Go Accounting for SaaS

While building saas products, especially one like AllBox, people pay to use your software. A lot of our software is billed pay-as-you-go where people only pay for what they have used, not a blanket cover-all cost. This post is about how one would model such a system in a very minimal / simple way and it invites discussion about how to best handle this situation.

If you're not familiar with the basics of accounting Beancount and Plain text accounting are good places to start

First let's describe the problem we are out to solve.

Parts

Goals

  1. We cannot LOSE or CREATE money.
  2. We cannot hold invalid amounts of money. For example 0.5 (cents or paise) do not exist so we cannot operate on them.
  3. If we every want to, we should be able to trace every single amount and figure out where it went.

Why not Tally / Quickbooks etc ?

There are a few lines of thought we went through:

  1. This will introduce a new services into our tech stack. We don't want that since Goal 3 will be dependent on how well supported debugging is in those tools. We absolutely do not want to go debugging using account statement sheets as our ONLY tool. We also want to keep our stack simple:
    • https://thmsmlr.com/cheap-infra
    • https://www.amazingcto.com/postgres-for-everything/
    • https://boringtechnology.club/
  2. Interacting with any 3rd party system will involve some sort of communication. APIs / SDKs etc.
    • Communnication failure means that we will have more moving parts, making debugging hard.
    • Failures can also happen on the 3rd party side due to no fault of our own and we're left wondering if we'll ever get any customer support.
    • If we get caught in an inconsistent communication state, it can conflict with Goal 1 and we might create/destroy money.
  3. We don't want to mix our company accounting with SaaS accounting. For example HR / Payroll has nothing to do with the product customer accounting and so should remain separate. Our goal is to make sure that customers should only pay for what they consume and vice versa. Nothing more.

What should we definitely do?

  1. Everything should be immutable. We don't want to lose information EVER when it comes to money.
  2. Use normal accounting terminology so that when needed company accountants can review and help us debug.
  3. Make sure we can show balances / statements / breakdowns of expenses for each client whenever required.
  4. Automate the entire process so that we don't need manual intervention at any point.

The solution

We create 3 tables in our database.

Account {
  id        integer [primary key]
  name      string
  user_id   integer
}

Transaction {
  id        integer [primary key]
  refno     varchar
  metadata  JSON
}

Posting {
  id        integer [primary key]
  refno     varchar
  amount    integer
  metadata  JSON
  // -------FOREIGN KEYS
  txid      Transaction(id)
  accid     Account(id)
}

Note that the Account table is not your SAAS account. It represents "accounting" accounts. With this we create a few entries in the Account table to represent "accounting" accounts. Each account has a name and is potentially linked to a user_id indicating that it belongs to that user. Accounts that don't belong to any particluar user don't have user_id associated with them. Accounts with <user_id> in each of them are created with a foreign key to the given user. Remaining accounts are having user_id = null.

Workflows

Customers pays Razorpay

Razorpay pays Us

Customer consumes Services

Now, how to actually "consume" services.

By creating this per-service-per-customer account we also retain information about which of our services are being used to what extent by our customers.

Operations

Wallet balance

Statements

Tracking where the money went

How much have we earned?

Conclusion

This kind of setup is very easy to create in hindsight and works very well for a lot of simple operations. Perhaps you would like to add something? Do email in your comments :D


End of page
You can select any text and comment on it. Current selection is: Email new comment
End of comments