TE
科技回声
首页24小时热榜最新最佳问答展示工作
GitHubTwitter
首页

科技回声

基于 Next.js 构建的科技新闻平台,提供全球科技新闻和讨论内容。

GitHubTwitter

首页

首页最新最佳问答展示工作

资源链接

HackerNews API原版 HackerNewsNext.js

© 2025 科技回声. 版权所有。

Ask HN: How to design database schema for usage based billing?

73 点作者 punkpeye超过 2 年前
The service I am building is usage-based, i.e.<p><pre><code> * I am charging per second the service was used, e.g. USD 0.01&#x2F;second * I would like to give credits upon sign-up. * There is no monthly minimum. </code></pre> At the moment, I am thinking that I can achieve this with just a few tables:<p><pre><code> * `billing_account (id, billing_rate_id, running_balance)` * `billing_rate (id, rate)` * `account_credit (id, billing_account_id, timestamp, amount, function_execution_id)` * `account_debit (id, billing_account_id, timestamp, amount, source [platform_credit or stripe])` </code></pre> When a user creates an account, I would create an entry in `billing_account` and associate whatever the current `billing_rate`. I would also create an `account_debit` entry with `source=platform_credit` and update `billing_account` `running_balance` value to reflect their balance after the `platform_credit`.<p>Then whenever they run a function that costs them, I create `account_credit` account with an entry equal to the amount they spent and update `billing_account` `running_balance` value.<p>When they top-up their account, I would just add entry to `account_debit` and update the balance again.<p>This appears to cover all my use cases, but I wanted to check with anyone who&#x27;s designed such database schemas before.

17 条评论

plasma超过 2 年前
I&#x27;ve written billing&#x2F;usage systems before, something your missing is the audit trail, you will get a query on a bill by a customer (for example, why did my bill increase this month so much, or spike on Wednesday?) and you need to be able to dig into the raw data (eg, your raw usage logs that contain high detail of start&#x2F;stop events or usage information for each service call) to provide comfort to yourself and your customer about the billing query and validate there was no billing error.<p>Since you will want to plan for that, one thing you can do is treat the end of month billing calculation (or ongoing usage per day) as a calculation based off your raw usage logs.<p>So your usage of the product (service usage start&#x2F;stop times) are log events (eg, pushed to S3 if its a huge system or start with just another database table like billing_usage_event) that describes each service start&#x2F;stop and calculated rate etc.<p>Then your bill is the aggregation of the raw events for the day&#x2F;month, allowing you to both do an audit if there is a billing query (find out why the bill spike occurred by looking at billing_usage_event) and also provide peace of mind to you and your customer the billing is accurate.<p>(&quot;Accounting for Developers&quot; was posted recently on HN, it was a great read - <a href="https:&#x2F;&#x2F;news.ycombinator.com&#x2F;item?id=32495724" rel="nofollow">https:&#x2F;&#x2F;news.ycombinator.com&#x2F;item?id=32495724</a>)
评论 #32671027 未加载
kondro超过 2 年前
I know it feels like overkill, but I’d recommend either outsourcing (even Stripe has usage-based billing if you can keep your updates under 100&#x2F;s) or using something off-the-shelf like <a href="https:&#x2F;&#x2F;killbill.io" rel="nofollow">https:&#x2F;&#x2F;killbill.io</a>.<p>I know it seems like a simple problem, but billing systems quickly turn into Eldridge horrors, even if you have the best intentions, and it’s always nicer to have someone else to maintain that for you.
评论 #32670937 未加载
评论 #32670202 未加载
Rafsark超过 2 年前
We built an open source usage based billing with Lago (YC S21). You deploy a Postgres database with all useful objects to run your billing (events, subscriptions, plans, prepaid credits, customers, invoices …).<p>Check it out here: - GitHub <a href="https:&#x2F;&#x2F;github.com&#x2F;getlago&#x2F;lago&#x2F;issues" rel="nofollow">https:&#x2F;&#x2F;github.com&#x2F;getlago&#x2F;lago&#x2F;issues</a> - website <a href="https:&#x2F;&#x2F;www.getlago.com&#x2F;" rel="nofollow">https:&#x2F;&#x2F;www.getlago.com&#x2F;</a> - documentation doc.getlago.com
AnhTho_FR超过 2 年前
Have you checked <a href="https:&#x2F;&#x2F;github.com&#x2F;getlago&#x2F;lago" rel="nofollow">https:&#x2F;&#x2F;github.com&#x2F;getlago&#x2F;lago</a> ? Open source billing API for SaaS.<p>Disclaimer: I am one of the cofounders, but looks like this could really fit your needs, especially as you add more complex logics afterwards: prepaid&#x2F;offered credits, probably discounts, etc.<p>Also, if it’s the first time you’re building a billing system, you might want to check this thread about billing nightmares. It seems very simple at first but it becomes messy very fast. <a href="https:&#x2F;&#x2F;news.ycombinator.com&#x2F;item?id=31424450" rel="nofollow">https:&#x2F;&#x2F;news.ycombinator.com&#x2F;item?id=31424450</a><p>Happy to help more (whether you use Lago or not!)!
评论 #32687586 未加载
motogpjimbo超过 2 年前
If a customer ever changes their billing rate, you will need to keep track of when it was changed and what their previous rates were, otherwise you&#x27;ll have no audit trail and no way to bill customers correctly.<p>One approach would be to have a billing_period (account_id, billing_rate_id, start_date, end_date) table. Another would be to store the billing_rate_id in your account_credit table so each usage can be tied to the billing rate the customer should be charged for it.<p>I also wouldn&#x27;t keep a running_balance column. I forget which rule of normalisation it is because it&#x27;s been so long, but you shouldn&#x27;t normally store a value that you can compute from other data.
spookthesunset超过 2 年前
Unless your product is a billing system, stop right now and go look at using a third party. Zuora, Stripe, etc all have billing platforms you can use. Trust me from hard earned experience that billing system seem &quot;easy&quot; but they really aren&#x27;t. They are very, very hard to get right and if you fuck up you are gonna overcharge, undercharge or god knows what else and piss your customers off.<p>Not only that but you&#x27;ll be stuck in a loop writing features that work out of the box with third party software. Important ones like the ability to change the price, customize the price for a single account, refund money, export to quickbooks, add new products, offer coupons, offer sales, provide quotes to prospects, etc. You don&#x27;t want to do this! A third party does all this and more.<p>So yeah. Don&#x27;t build it! You&#x27;ll regret doing so! Trust me!
sgc超过 2 年前
I am no sql expert, but have some recent experience designing similar systems and went through the db structures of several big oss projects for inspiration (some lost HN post, if anybody remembers).<p>I think your account_debit and account_credit tables have their names swapped.<p>You should have a products table, and a billing tier table with timestamps for start and end, so that accounts billing tier * products billing base rate at the time of use = their cost. You are not going to do only one thing or have only one price forever.<p>So at least:<p>accounts (acct_id, timestamp_start, timestamp_end, tier_id) - this gets more than one row per acct based on whether they change rate tiers. Same thing true for products and tiers below.<p>products (prod_id, timestamp_start, timestamp_end, base_rate)<p>tiers (tier_id, rate_multiplier, product_id, timestamp_start, timestamp_end) - include product_id if you want use different multipliers for the same account on different products in the future. timestamp_start and _end so you can use the right multiplier when prices change for active accounts.<p>you can include an `active` boolean on any of those table to speed up queries if necessary.<p>Then, in account_debit (labelled account_credit above), I would just log usage (debit_id, timestamp_start, timestamp_end, product_id, account_id), and calculate out only when actually billed. The way you have it now you could never deal with a usage dispute. You need better logging of raw data, never store calculation results when you can trivially store numbers.<p>account_credit looks fine, maybe store source_transaction_id for ease of lookup. I don&#x27;t have the experience in footguns as indicated with foreign key constraints by others, that seems worth listening to ;). I think using timestamp_start and _end so that there are never any changes to tier rate rows, product base price rows, etc, should go a long way towards mitigating that here.
评论 #32670397 未加载
qsort超过 2 年前
Barring performance&#x2F;memory&#x2F;misc concerns, this seems pretty much the same as I&#x27;d design it.<p>If I understand your problem correctly, you have two facts: user makes a payment (account gets credited) and user consumes credits (account gets debited). Everything else is a dimension (accounts, rates).<p>The only choice you have to make is how you deal with changing rates. You can write the premultiplied amount on the debit table, or you can treat rates as a slowly changing dimension and decompose &quot;amount&quot; into &quot;time&quot; and the foreign key to the rate table.<p>Which one is better depends on what the application is doing, there&#x27;s nothing wrong in principle with either.
评论 #32669876 未加载
kerblang超过 2 年前
Those &quot;id&quot; names are really confusing; I assume they are all shorthand for &quot;acct_id&quot;, or... maybe not.<p>I think account_credit &amp; account_debit need foreign keys back to billing_account, and they probably are gonna need their own primary columns eventually (so you can add on addl metadata like 3rd party transaction tracking).<p>I&#x27;m not sure what billing_rate&#x27;s relationship is to billing_account, if any...
评论 #32669724 未加载
punkpeye超过 2 年前
Just based on what I am reading about building such systems, it seems like the common advice is to not have a single balance column such as `running_balance`, but instead calculate this value on the go every time it is needed.<p>That&#x27;s fine. What else?
mattbee超过 2 年前
I would get rid of the billing_rate table or at least copy its contents into billing_account - nothing good can come of changing that reference!<p>Also consider how you will build invoicing on top of this - i.e. A monthly PDF that covers (for a period) prepayments made, services rendered, balance remaining.<p>It seems like your billing model makes that quite easy but bear in mind you have to be able to sum that up on a particular date, and for that summary to be immutable.
xory超过 2 年前
Avoid building it if you can. Stripe is great. The last billing system I built had a similar schema to yours. As the business grew, we got bigger customers who hated that we just provided a balance due, which was a sum of all debits and credits and not actual invoices they could pay or submit to their finance team for payment. Wrangling recurring charges yourself will unnecessarily take years off of your life.
gabereiser超过 2 年前
if the billing rate for running something is a delta (like you posted, 0.01&#x2F;second) of time, then having a timer (or cron, or batch, or kafka, whatever) that records the usage is also atomically deducting it from the account_balance. The account_balance is then topped up with whatever payment schema you want to create. The main issue is that you track the usage as a variable of a constant value to deduct from the account_balance variable of a constant value.<p>Your design is sound. Multiple rates against a running balance, that running balance&#x27;s constant value is, well, USD. So the rate of usage is USD. Credits&#x2F;Debits table is really there for reporting and has no bearing on the running_balance.<p>Good luck!
评论 #32670060 未加载
jdoconnor超过 2 年前
consider not storing running_balance (or at least expect it to be written to a LOT). Based on the above description that&#x27;s going to be a hot table with a write&#x2F;update of 1 per second per row (of active customers).<p>If you are thinking sql, that running balance could be calculated from a VERY tall and skinny table of time_spent per session (which would be calculated in the application) or an append only table of (billing_account_id, timestamp) where you write a new row every second.
评论 #32669815 未加载
评论 #32669772 未加载
tzs超过 2 年前
Should account_credit record their payments and account_debit their spending?
评论 #32670219 未加载
sixdimensional超过 2 年前
How about open sourcing and putting this on Github and letting anyone submit pull requests with suggestions&#x2F;ideas?
kyle_v超过 2 年前
Stripe allows you to bill based on reported usage which seems considerably easier than crafting an equivalent feature from scratch. This is how we handle billing at skusavvy<p><a href="https:&#x2F;&#x2F;stripe.com&#x2F;docs&#x2F;products-prices&#x2F;pricing-models#usage-based-pricing" rel="nofollow">https:&#x2F;&#x2F;stripe.com&#x2F;docs&#x2F;products-prices&#x2F;pricing-models#usage...</a>