<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Raffi Sarkissian</title>
    <description>The latest articles on DEV Community by Raffi Sarkissian (@sarkissianraffi).</description>
    <link>https://dev.to/sarkissianraffi</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F919114%2F20256d02-3168-46ec-ba31-83cda1025c8c.png</url>
      <title>DEV Community: Raffi Sarkissian</title>
      <link>https://dev.to/sarkissianraffi</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/sarkissianraffi"/>
    <language>en</language>
    <item>
      <title>Time zones are a nightmare for engineers</title>
      <dc:creator>Raffi Sarkissian</dc:creator>
      <pubDate>Tue, 14 Mar 2023 14:04:25 +0000</pubDate>
      <link>https://dev.to/lago/handling-time-zones-in-our-own-product-8ma</link>
      <guid>https://dev.to/lago/handling-time-zones-in-our-own-product-8ma</guid>
      <description>&lt;p&gt;Time zones suck. There’s a litany of posts advocating for the &lt;a href="https://news.ycombinator.com/item?id=12887007"&gt;“end of Time zones”&lt;/a&gt;. There are pros and cons but the net is they won’t go anywhere.&lt;/p&gt;

&lt;p&gt;The most fascinating thing about this is that there’s a lot of literature on “how to work across time zones” but so little on “how to build a product that has to handle timezones”. &lt;/p&gt;

&lt;p&gt;We’ve come across this tweet from &lt;a href="https://twitter.com/peer_rich/status/1621824222417788928"&gt;Peer&lt;/a&gt; at Cal.com, and the meme by &lt;a href="https://twitter.com/daniel_nguyenx"&gt;Daniel&lt;/a&gt; that is just on point.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--8DlHEZ0J--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/65ffhaha6zffs1wisb3p.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--8DlHEZ0J--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/65ffhaha6zffs1wisb3p.jpeg" alt="Meme on Timezone and building an app" width="640" height="458"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;So here is how we approached time zones at &lt;a href="http://www.getlago.com"&gt;Lago&lt;/a&gt; (we build open-source metering and usage-based billing) and why you might need to think about time zones for your own product too, if you’re addressing a global customer base.&lt;/p&gt;




&lt;h2&gt;
  
  
  Billing and Time Zones: why it matters
&lt;/h2&gt;

&lt;p&gt;Yes. Your timezone may not match your customer's. This could lead to discrepancies in usage ingestion, subscriptions, and invoicing, which may not occur on the date you anticipated.&lt;br&gt;
If your customer doesn’t understand what they’re billed for, it erodes trust and will incur “billing disputes”. &lt;/p&gt;
&lt;h3&gt;
  
  
  Expiry dates
&lt;/h3&gt;

&lt;p&gt;The most telling example is how time zones impact expiry dates. &lt;br&gt;
Expiry dates can be set for contracts, coupons, or wallets that hold prepaid credits to cover future usage.&lt;br&gt;
If you’re a company based in Paris, France, and have a customer in San Francisco, USA, with a coupon expiring on &lt;code&gt;2023-02-01T00:00UTC&lt;/code&gt; (February 1st, 2023 at midnight). Your customer might expect to be able to use their prepaid credits until the end January 31st, 2023, their time: Pacific Standard Time. &lt;/p&gt;

&lt;p&gt;With this day finishing nine hours earlier in the French time zone, that might trigger a “bad surprise” for the user. That kind of discrepancies can also impact subscriptions (when they start or end) and gaps in your customers’ financial records. &lt;/p&gt;
&lt;h3&gt;
  
  
  Double counting or missing consumption when the time zone changes
&lt;/h3&gt;

&lt;p&gt;Then comes more tedious edge cases that can have considerable impact on billing and the end user’s experience. &lt;/p&gt;

&lt;p&gt;Managing the boundaries of a billing period when a customer or an organization changes their timezone is one of them. For example, let’s say a customer had an active subscription, whose timezone is UTC, and for which the billing frequency is “monthly”. In February, we must take into account all the events that can impact their billing (e.g., additional consumption) that occurred between &lt;code&gt;February 1, 2023, at 00:00:00 UTC&lt;/code&gt; and &lt;code&gt;February 28, 2023, at 23:59:59 UTC&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;If on February 15, this customer changes their time zone to Tokyo time (UTC+9) within our product, the "new billing period" that will be calculated will run from &lt;code&gt;January 31, 2023, at 14:00:00 UTC&lt;/code&gt; to &lt;code&gt;February 28, 2023, at 13:59:59 UTC&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The risk is that events received between &lt;code&gt;January 31, 2023, at 14:00:00 UTC&lt;/code&gt; and &lt;code&gt;February 1, 2023, at 00:00:00 UTC&lt;/code&gt; will be charged twice, once for January UTC and again for February UTC+9.&lt;/p&gt;

&lt;p&gt;Therefore, we need to "realign" the start of the period to &lt;code&gt;February 1, 2023, at 00:00:00 UTC&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;If, instead of changing to Tokyo time, the customer switches to Los Angeles time (UTC-8), the "new billing period" will run from &lt;code&gt;February 1, 2023, at 08:00:00 UTC&lt;/code&gt; to &lt;code&gt;March 1, 2023, at 07:59:59 UTC&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;In that case, we need to "realign" the start of the period to &lt;code&gt;January 31, 2023, at 13:59:59 UTC&lt;/code&gt;.&lt;/p&gt;


&lt;h2&gt;
  
  
  Our learnings along the way
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://www.getlago.com/blog/the-4-biggest-problems-with-homemade-billing-systems"&gt;Vincent&lt;/a&gt; in our team was the lead on this feature, here are his own words: &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. What solutions have you considered for "translating" UTC into the end customer's timezone?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;In general, it is considered best practice to store dates in a unified and comparable format from one record to another. That's why we decided to store dates in UTC (in the ISO 8601 format) because it provides a unique reference and is not sensitive to changes in time or modifications to the organization's or customer's timezone. Dates are only converted to the user's format at the last moment for display. We had considered storing dates in the database along with timezone information, but ultimately it made the recording process much more complicated, raised questions about updates, and introduced significant complexity in date comparisons because the organization's and customer's time zones may not be the same.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. How did you learn or find best practices about this topic?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;To be honest, I’ve scrolled the web to learn as many things as I could, I think Stackoverflow was the best source of info, for instance:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://stackoverflow.com/questions/33535929/rails-postgres-and-timezone"&gt;https://stackoverflow.com/questions/33535929/rails-postgres-and-timezone&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://stackoverflow.com/tags/timezone/info"&gt;https://stackoverflow.com/tags/timezone/info&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;3. Did you iterate on the approach? What were your key learnings?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This is not the first time I have had to deal with this challenge, so I relied heavily on my personal experience and the path was "fairly clear" for me. However, we iterated on the "edge cases" which are numerous with this kind of problem, and making things understandable for the end-user is a constant challenge.&lt;/p&gt;

&lt;p&gt;If I had to summarize our approach:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;To manage time zones, we should not handle data in the "date" format (without the time information) because a date only makes sense in a particular time zone. This impacts things like usage limits for coupons, or the start and end of a subscription.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;For certain objects, such as invoices, we need a blocked "accounting date" because it is displayed in a document. We have decided to always define it in the timezone applicable to the customer.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Automatic tasks should be processed on an hourly basis, never on a daily basis, because some elements might have “switched” day depending on the time zone, and that impacts billing. This means that instead of wondering "who should I bill today?", the real question is "what are the subscriptions for which we are currently on YYYY-MM-DD?" And safeguards need to be added to avoid billing the same subscription multiple times.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Postgres (our database) is very helpful for managing time zones, particularly thanks to the &lt;a href="https://www.postgresql.org/docs/15/functions-datetime.html#FUNCTIONS-DATETIME-ZONECONVERT"&gt;AT TIME ZONE&lt;/a&gt; operator.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;There are many different time zones, depending on the offset from the GMT reference, daylight saving time, and other fun subtleties (for example, some zones in Asia/Oceania have offsets set on quarter-hours). We use a simplified list of 134 zones (&lt;a href="https://api.rubyonrails.org/classes/ActiveSupport/TimeZone.html"&gt;source&lt;/a&gt;).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;And another fun fact (We had a lot of fun!): Time Zones often have a "friendly name" in the format Continent/City|Island except for UTC and... GMT+12, which only covers two uninhabited American islands :D&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  The result
&lt;/h3&gt;

&lt;p&gt;By default, Lago is ingesting usage and invoicing customers based on the system-wide UTC. However, you can decide to change the value at your organization level to make sure that every single customer is billed based on your timezone.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;LAGO_URL&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&amp;lt;https://api.getlago.com&amp;gt;"&lt;/span&gt;
&lt;span class="nv"&gt;API_KEY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"__YOUR_API_KEY__"&lt;/span&gt;

curl &lt;span class="nt"&gt;--location&lt;/span&gt; &lt;span class="nt"&gt;--request&lt;/span&gt; PUT &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$LAGO_URL&lt;/span&gt;&lt;span class="s2"&gt;/api/v1/organizations"&lt;/span&gt; &lt;span class="se"&gt;\\&lt;/span&gt;
  &lt;span class="nt"&gt;--header&lt;/span&gt; &lt;span class="s2"&gt;"Authorization: Bearer &lt;/span&gt;&lt;span class="nv"&gt;$API_KEY&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\\&lt;/span&gt;
  &lt;span class="nt"&gt;--header&lt;/span&gt; &lt;span class="s1"&gt;'Content-Type: application/json'&lt;/span&gt; &lt;span class="se"&gt;\\&lt;/span&gt;
  &lt;span class="nt"&gt;--data-raw&lt;/span&gt; &lt;span class="s1"&gt;'{
    "organization": {
      "name": "Name1",
      "webhook_url": "&amp;lt;https://test-example.example&amp;gt;",
      "country": "CZ",
      "address_line1": "address1",
      "address_line2": null,
      "state": "state1",
      "zipcode": "10000",
      "email": "org@email.com",
      "city": "city125",
      "legal_name": null,
      "legal_number": null,
      "timezone": "Europe/Paris", //Changes billing record to your organization timezone
      "billing_configuration": {
        "invoice_footer": "footer custom",
        "invoice_grace_period": 3,
        "vat_rate": 15.0
      }
    }
  }'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In addition to this, you can decide to overwrite this value per customer. The entire billing engine will then follow the exact time zone sets at a customer level. Usage ingestion, expiry dates and invoicing will be triggered based on the timezone of your customer.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;LAGO_URL&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&amp;lt;https://api.getlago.com&amp;gt;"&lt;/span&gt;
&lt;span class="nv"&gt;API_KEY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"__YOUR_API_KEY__"&lt;/span&gt;

curl &lt;span class="nt"&gt;--location&lt;/span&gt; &lt;span class="nt"&gt;--request&lt;/span&gt; POST &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$LAGO_URL&lt;/span&gt;&lt;span class="s2"&gt;/api/v1/customers"&lt;/span&gt; &lt;span class="se"&gt;\\&lt;/span&gt;
  &lt;span class="nt"&gt;--header&lt;/span&gt; &lt;span class="s2"&gt;"Authorization: Bearer &lt;/span&gt;&lt;span class="nv"&gt;$API_KEY&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\\&lt;/span&gt;
  &lt;span class="nt"&gt;--header&lt;/span&gt; &lt;span class="s1"&gt;'Content-Type: application/json'&lt;/span&gt; &lt;span class="se"&gt;\\&lt;/span&gt;
  &lt;span class="nt"&gt;--data-raw&lt;/span&gt; &lt;span class="s1"&gt;'{
    "customer": {
      "external_id": "5eb02857-a71e-4ea2-bcf9-57d3a41bc6ba",
      "address_line1": "5230 Penfield Ave",
      "address_line2": null,
      "city": "Woodland Hills",
      "timezone": "America/Los_Angeles", //Overwrite the value per customer
      "url": "&amp;lt;http://hooli.com&amp;gt;",
      "zipcode": "91364",
      "billing_configuration": {
        "payment_provider": "stripe",
        "provider_customer_id": "cus_12345",
      }
    }
  }'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This translates the entire billing logic to follow your customer’s timezone.&lt;/p&gt;

&lt;p&gt;If this seems very simple within Lago, it’s on purpose: we worked hard to abstract the time zone logic, so that you don’t even have to think about it!&lt;/p&gt;

&lt;p&gt;Et voilà !&lt;/p&gt;

</description>
      <category>opensource</category>
      <category>billing</category>
    </item>
    <item>
      <title>Using Ruby’s BigDecimal to build a billing engine</title>
      <dc:creator>Raffi Sarkissian</dc:creator>
      <pubDate>Mon, 05 Dec 2022 15:01:17 +0000</pubDate>
      <link>https://dev.to/lago/using-rubys-bigdecimal-to-build-a-billing-engine-73k</link>
      <guid>https://dev.to/lago/using-rubys-bigdecimal-to-build-a-billing-engine-73k</guid>
      <description>&lt;p&gt;We’ve recently gone deep down the rabbit hole of 'BigDecimal'. Although we accumulated decades of experience in Ruby, this was more challenging than we initially thought, because of the few things we discovered along the way (that led to quite a bit of rework!).&lt;/p&gt;

&lt;h2&gt;
  
  
  Why we chose BigDecimal
&lt;/h2&gt;

&lt;p&gt;Context: We build &lt;a href="https://github.com/getlago/lago"&gt;Lago&lt;/a&gt;, the open-source metering and usage-based billing.&lt;/p&gt;

&lt;h3&gt;
  
  
  Our need:
&lt;/h3&gt;

&lt;p&gt;We handle objects with up to 5 decimals, without transforming them. Why? We need to keep each object as precise as possible, and delay the rounding to 2 decimal places to the last step: when our users choose to display a charge or an invoice in a specific currency, for instance.&lt;/p&gt;

&lt;p&gt;Why didn’t we just store values in decimal? We need to support multiple decimals for complex pricing : e.g. $0,0015 per hour. We want to give the maximum flexibility to our users to fit their pricing needs.&lt;/p&gt;

&lt;h3&gt;
  
  
  The potential solutions:
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;BigInt:&lt;/strong&gt; the ‘by default’ option with Ruby doesn’t support decimals, so we ruled it out.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Float objects:&lt;/strong&gt; we hit accuracy limits very quickly as float in Ruby (like in many other languages) can lead to rounding issues, it’s designed for performance and not for precise calculations.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;BigDecimal:&lt;/strong&gt; we knew it ranked lower on the performance side, but it seemed to be the only relevant option. Moreover, PostgreSQL provides a Decimal type that is very well integrated with Rails Active Record, which, combined with BigDecimal provides an ‘out-of-the-box’ validation for scale and precision.&lt;/p&gt;

&lt;p&gt;Example of a migration: With BigDecimal, we can specify precision and scale in the decimal column (PostgreSQL).&lt;br&gt;
&lt;code&gt;t.decimal :rate_amount, null: false, default: 0, precision: 5&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Precision:&lt;/strong&gt; total number of digits (including before and after the decimal point).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Scale:&lt;/strong&gt; number of digits after the decimal point.&lt;/p&gt;

&lt;h2&gt;
  
  
  The problems we ran into
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Rounding errors and execution time
&lt;/h3&gt;

&lt;p&gt;Float rounding leading to potential errors&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Bj3dmJCD--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/xbz4kdauvmjqxnvbplf6.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Bj3dmJCD--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/xbz4kdauvmjqxnvbplf6.png" alt="Image description" width="880" height="479"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Execution time for a Float calculation&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--qHpNJz8S--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/j6j926fl56s7ho6cd5r9.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--qHpNJz8S--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/j6j926fl56s7ho6cd5r9.png" alt="Image description" width="880" height="557"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Execution Time for the same calculation, using BigDecimal&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--SWQuzlcC--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ofh3qkh2xp3jsz5eghyt.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--SWQuzlcC--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ofh3qkh2xp3jsz5eghyt.png" alt="Image description" width="880" height="474"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Front-End &amp;amp; Back-End coordination
&lt;/h3&gt;

&lt;p&gt;Handling numbers can be tricky on the front-end side. We either have different types (string, float, integers), format (cents, decimal) or purpose (money, percent). For all of them we also need to change the UI display, depending on currency and user’s locale.&lt;/p&gt;

&lt;h2&gt;
  
  
  How we solved them
&lt;/h2&gt;

&lt;p&gt;Back-End and Front-End agreed on some rules when dealing with numbers. If a value sent or received contains a decimal (no matter the precision), we’ll manage it as type ‘String’. On the other hand, all other values that won’t contain any decimal will be of type ‘Number’.&lt;/p&gt;

&lt;p&gt;The Front-End component libraries can accept both types as inputs, so we don’t have to think about type management when building our interfaces. Internally, for manipulation and formatting, those components will systematically transform values into type ‘String’.&lt;/p&gt;

&lt;p&gt;For display purposes, we use Javascript APIs that accept options to define the number of decimals or the display format (currency, percent).&lt;/p&gt;

&lt;h2&gt;
  
  
  Final words
&lt;/h2&gt;

&lt;p&gt;We shared these notes and code snippets in the simplest form possible, because it’s what we were looking for when we researched the issue. From the questions we spotted in the Ruby community, it looks like other people were confused as well, so we hope this post proves useful to you as well.&lt;/p&gt;

</description>
      <category>ruby</category>
      <category>billing</category>
    </item>
    <item>
      <title>6 steps to build a usage-based billing system with Lago (YC S21)</title>
      <dc:creator>Raffi Sarkissian</dc:creator>
      <pubDate>Thu, 08 Sep 2022 13:13:41 +0000</pubDate>
      <link>https://dev.to/lago/6-steps-to-build-a-usage-based-billing-system-with-lago-yc-s21-1pbj</link>
      <guid>https://dev.to/lago/6-steps-to-build-a-usage-based-billing-system-with-lago-yc-s21-1pbj</guid>
      <description>&lt;p&gt;&lt;a href="https://getlago.com"&gt;Lago&lt;/a&gt; is an open-source billing API that helps engineers implement their billing systems faster. We do think the future of pricing is hybrid, which includes subscription fees and usage-based charges. Over the past few years, engineers have struggled to implement exotic billing systems but this is over!&lt;/p&gt;

&lt;p&gt;Lago also provides a cloud-hosted application for those who don’t want to run it on their own environment.&lt;/p&gt;

&lt;p&gt;We are going to build an entire hybrid billing system, including plans and usage-based features. Let’s try to replicate &lt;a href="https://segment.com/pricing/"&gt;Segment’s pricing&lt;/a&gt; for instance!&lt;/p&gt;

&lt;h2&gt;
  
  
  Prerequisites
&lt;/h2&gt;

&lt;p&gt;You can find the main Lago repository on GitHub.&lt;/p&gt;

&lt;p&gt;Before we get started, you need to:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Install &lt;a href="https://docs.docker.com/get-docker/"&gt;Docker&lt;/a&gt; on your machine;&lt;/li&gt;
&lt;li&gt;Make sure &lt;a href="https://docs.docker.com/compose/install/"&gt;Docker Compose&lt;/a&gt; is installed and available (it should be the case if you’ve chosen to install Docker via Docker Desktop);&lt;/li&gt;
&lt;li&gt;Make sure &lt;a href="https://git-scm.com/book/en/v2/Getting-Started-Installing-Git"&gt;Git&lt;/a&gt; is installed on your machine; and&lt;/li&gt;
&lt;li&gt;A cup of tea (or coffee)! ☕️&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This demo is made through basic CURL API calls, but Lago also provides API clients in order to make the implementation process easier.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Setting up your Lago billing engine
&lt;/h2&gt;

&lt;p&gt;Start running Lago on your own environment. You will have access to &lt;strong&gt;Lago API&lt;/strong&gt; and &lt;strong&gt;Lago UI&lt;/strong&gt; (frontend admin panel to perform no-code tasks).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Get the code&lt;/span&gt;
git clone https://github.com/getlago/lago.git

&lt;span class="c"&gt;# Go to Lago folder&lt;/span&gt;
&lt;span class="nb"&gt;cd &lt;/span&gt;lago

&lt;span class="c"&gt;# Set up environment configuration&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"LAGO_RSA_PRIVATE_KEY=&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="sb"&gt;`&lt;/span&gt;openssl genrsa 2048 | &lt;span class="nb"&gt;base64&lt;/span&gt;&lt;span class="sb"&gt;`&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; .env
&lt;span class="nb"&gt;source&lt;/span&gt; .env

&lt;span class="c"&gt;# Start (make sure docker is started)&lt;/span&gt;
docker-compose up
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can now open your browser and go to &lt;a href="http://localhost"&gt;http://localhost/&lt;/a&gt; to connect to the application. Lago's API is exposed at &lt;a href="http://localhost:3000"&gt;http://localhost:3000/&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;If needed, you can override your environment variables (&lt;a href="https://doc.getlago.com/docs/guide/self-hosting/docker#environment-variables"&gt;learn more&lt;/a&gt;).&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Defining your billable metrics
&lt;/h2&gt;

&lt;p&gt;The usage-based feature billed by Segment is the number of &lt;strong&gt;monthly tracked users (MTUs)&lt;/strong&gt;, which is related to the total number of unique users tracked monthly.&lt;/p&gt;

&lt;p&gt;Lago’s billable metrics allow you to track and automatically aggregate usage to determine the number of &lt;strong&gt;units to be charged&lt;/strong&gt; at the end of a billing period.&lt;/p&gt;

&lt;p&gt;Here we’re going to define MTUs as a &lt;code&gt;metered billable metric&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;LAGO_URL&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"https://api.getlago.com"&lt;/span&gt;
&lt;span class="nv"&gt;API_KEY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"__YOUR_API_KEY__"&lt;/span&gt;

curl &lt;span class="nt"&gt;--location&lt;/span&gt; &lt;span class="nt"&gt;--request&lt;/span&gt; POST &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$LAGO_URL&lt;/span&gt;&lt;span class="s2"&gt;/api/v1/billable_metrics"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--header&lt;/span&gt; &lt;span class="s2"&gt;"Authorization: Bearer &lt;/span&gt;&lt;span class="nv"&gt;$API_KEY&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--header&lt;/span&gt; &lt;span class="s1"&gt;'Content-Type: application/json'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--data-raw&lt;/span&gt; &lt;span class="s1"&gt;'{
    "billable_metric": {
      "name": "Monthly Tracked Users",
      "code": "mtu",
      "description": "Number of unique users tracked by Segment",
      "aggregation_type": "unique_count_agg",
      "field_name": "user_id"
    }
  }'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For this billable metric, the aggregation type is &lt;code&gt;unique_count_agg&lt;/code&gt;, which is used to count the number of unique &lt;code&gt;user_id&lt;/code&gt; values recorded during the billing period.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. Creating your first plan
&lt;/h2&gt;

&lt;p&gt;Now that we have our billable metric, we can create our first plan. The plan defines the billing period and all fees (i.e. subscription and usage-based charges).&lt;/p&gt;

&lt;p&gt;Let’s take the example of &lt;strong&gt;Segment’s Team plan&lt;/strong&gt;. The base subscription costs $120 per month month, including 10,000 free MTUs. If you track more users, you will be charged for the overage, according to Segment’s graduated pricing model.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;LAGO_URL&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"https://api.getlago.com"&lt;/span&gt;
&lt;span class="nv"&gt;API_KEY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"__YOUR_API_KEY__"&lt;/span&gt;

curl &lt;span class="nt"&gt;--location&lt;/span&gt; &lt;span class="nt"&gt;--request&lt;/span&gt; POST &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$LAGO_URL&lt;/span&gt;&lt;span class="s2"&gt;/api/v1/plans"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nt"&gt;--header&lt;/span&gt; &lt;span class="s2"&gt;"Authorization: Bearer &lt;/span&gt;&lt;span class="nv"&gt;$API_KEY&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nt"&gt;--header&lt;/span&gt; &lt;span class="s1"&gt;'Content-Type: application/json'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nt"&gt;--data-raw&lt;/span&gt; &lt;span class="s1"&gt;'{
  "plan": {
    "name": "Team",
    "code": "team",
    "interval": "monthly",
    "description": "Team plan, ideal for startups",
    "amount_cents": 12000,
    "amount_currency": "USD",
    "trial_period": 0,
    "pay_in_advance": true,
    "bill_charges_monthly": true,
    "charges": [
      {
        "billable_metric_id": "__BILLABLE_METRIC_ID__",
        "charge_model": "graduated",
        "properties": [
          {
            "to_value": 10000,
            "from_value": 0,
            "flat_amount": "0",
            "per_unit_amount": "0.00"
          },
          {
            "to_value": 25000,
            "from_value": 10001,
            "flat_amount": "0",
            "per_unit_amount": "0.012"
          },
          {
            "to_value": 100000,
            "from_value": 25001,
            "flat_amount": "0",
            "per_unit_amount": "0.011"
          },
          {
            "to_value": null,
            "from_value": 100001,
            "flat_amount": "0",
            "per_unit_amount": "0.010"
          }
        ]
      }
    ]
  }
}'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let’s take a closer look at the request above.&lt;/p&gt;

&lt;h3&gt;
  
  
  Basic plan model
&lt;/h3&gt;

&lt;p&gt;The request creates a basic plan at $120/month that is paid in advance (beginning of period), with no free trial. If can customise it using the following fields:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;• interval:&lt;/strong&gt; can also be set to yearly or weekly;&lt;br&gt;
&lt;strong&gt;• amount_currency:&lt;/strong&gt; all currencies are available;&lt;br&gt;
&lt;strong&gt;• trial_period:&lt;/strong&gt; set a number of trial days; and&lt;br&gt;
&lt;strong&gt;• pay_in_advance:&lt;/strong&gt; if set to false, the subscription fee will be paid in arrears (end of period).&lt;/p&gt;
&lt;h3&gt;
  
  
  Usage-based charges
&lt;/h3&gt;

&lt;p&gt;In this example, we selected a &lt;strong&gt;graduated charge model&lt;/strong&gt; for our usage-based feature (i.e. MTUs). Here’s what we can see in the user interface.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--LvydcFhD--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/2i830vm5v0ycqxj1zzvc.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--LvydcFhD--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/2i830vm5v0ycqxj1zzvc.png" alt="Usage based graduated charge" width="880" height="925"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can create as many charges as you want and there are many charge models available (e.g. volume pricing, standard pricing, percentage pricing, package pricing).&lt;/p&gt;
&lt;h2&gt;
  
  
  4. Creating your first customer
&lt;/h2&gt;

&lt;p&gt;Congratulations, your pricing is ready! You can now create a customer, assign them a plan and start monitoring usage.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;LAGO_URL&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"https://api.getlago.com"&lt;/span&gt;
&lt;span class="nv"&gt;API_KEY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"__YOUR_API_KEY__"&lt;/span&gt;

curl &lt;span class="nt"&gt;--location&lt;/span&gt; &lt;span class="nt"&gt;--request&lt;/span&gt; POST &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$LAGO_URL&lt;/span&gt;&lt;span class="s2"&gt;/api/v1/customers"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--header&lt;/span&gt; &lt;span class="s2"&gt;"Authorization: Bearer &lt;/span&gt;&lt;span class="nv"&gt;$API_KEY&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--header&lt;/span&gt; &lt;span class="s1"&gt;'Content-Type: application/json'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--data-raw&lt;/span&gt; &lt;span class="s1"&gt;'{
    "customer": {
      "external_id": "5eb02857-a71e-4ea2-bcf9-57d3a41bc6ba",
      "address_line1": "5230 Penfield Ave",
      "address_line2": null,
      "city": "Woodland Hills",
      "country": "US",
      "email": "dinesh@piedpiper.test",
      "legal_name": "Coleman-Blair",
      "legal_number": "49-008-2965",
      "logo_url": "http://hooli.com/logo.png",
      "name": "Gavin Belson",
      "phone": "1-171-883-3711 x245",
      "state": "CA",
      "url": "http://hooli.com",
      "vat_rate": 12.5,
      "zipcode": "91364",
      "billing_configuration": {
        "payment_provider": "stripe",
        "provider_customer_id": "cus_12345",
        "sync": true
      }
    }
  }'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This request allows you to create a customer and also to define the default payment provider (i.e. the payment service provider that will be used to collect payments for this customer). We also set a tax rate of 12.5% for this customer.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--gzTSN3vk--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/712ukabsvi1itf3ji1gd.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--gzTSN3vk--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/712ukabsvi1itf3ji1gd.png" alt="Creating a customer" width="880" height="334"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  5. Assigning a plan to a customer
&lt;/h2&gt;

&lt;p&gt;To start billing a customer, you need to assign them a plan. This action will create a &lt;strong&gt;subscription&lt;/strong&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;LAGO_URL&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"https://api.getlago.com"&lt;/span&gt;
&lt;span class="nv"&gt;API_KEY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"__YOUR_API_KEY__"&lt;/span&gt;

curl &lt;span class="nt"&gt;--location&lt;/span&gt; &lt;span class="nt"&gt;--request&lt;/span&gt; POST &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$LAGO_URL&lt;/span&gt;&lt;span class="s2"&gt;/api/v1/subscriptions"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--header&lt;/span&gt; &lt;span class="s2"&gt;"Authorization: Bearer &lt;/span&gt;&lt;span class="nv"&gt;$API_KEY&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--header&lt;/span&gt; &lt;span class="s1"&gt;'Content-Type: application/json'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--data-raw&lt;/span&gt; &lt;span class="s1"&gt;'{
    "subscription": {
            "external_id": "my_subscription_1",
      "external_customer_id": "5eb02857-a71e-4ea2-bcf9-57d3a41bc6ba",
      "plan_code": "team",
      "billing_time": "anniversary"
    }
  }'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Z16_PabI--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/7w2z1ixvnwflz5l6mkbr.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Z16_PabI--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/7w2z1ixvnwflz5l6mkbr.png" alt="Create a subscription" width="880" height="416"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;When assigning a subscription, you can:&lt;/p&gt;

&lt;p&gt;• Define your own &lt;code&gt;external_id&lt;/code&gt;;&lt;br&gt;
• Choose between &lt;code&gt;anniversary&lt;/code&gt; or &lt;code&gt;calendar&lt;/code&gt; for the billing time - a subscription based on calendar dates is billed on the first day of each week/month/year, while a subscription based on the anniversary date is billed on the day it was created (e.g. every 4 of the month); and&lt;br&gt;
• Enter a subscription name that will be displayed on the invoice and will allow you to differentiate subscriptions that share the same plan (e.g. Project 1 on the Free plan and Project 2 also on the Free plan).&lt;/p&gt;

&lt;p&gt;As the base amount of the Team plan has to be paid in advance, Lago automatically generates a first invoice. The usage-based charge related to MTUs will be billed at the end of the period.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--UdsIH3qB--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/mlg4haeuua5ldbhvqbzh.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--UdsIH3qB--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/mlg4haeuua5ldbhvqbzh.png" alt="Emitting the invoice" width="880" height="814"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This invoice can be customized with information about your organization, your logo and a custom footer. For compliance purposes, invoices have an incremental &lt;code&gt;sequential_id&lt;/code&gt; and incremental &lt;code&gt;invoice_number&lt;/code&gt; as well.&lt;/p&gt;
&lt;h2&gt;
  
  
  6. Ingesting usage-based events
&lt;/h2&gt;

&lt;p&gt;Lago has an event-based architecture. When a subscription is associated with a customer, you can start pushing events related to your billable metrics. By using a unique &lt;code&gt;transaction_id&lt;/code&gt;, we make sure that an event is not ingested twice. It’s what we call an idempotency key.&lt;/p&gt;

&lt;p&gt;Below are three events ingested by Lago:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Event #1&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;LAGO_URL&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"https://api.getlago.com"&lt;/span&gt;
&lt;span class="nv"&gt;API_KEY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"__YOUR_API_KEY__"&lt;/span&gt;

curl &lt;span class="nt"&gt;--location&lt;/span&gt; &lt;span class="nt"&gt;--request&lt;/span&gt; POST &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$LAGO_URL&lt;/span&gt;&lt;span class="s2"&gt;/api/v1/events"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nt"&gt;--header&lt;/span&gt; &lt;span class="s2"&gt;"Authorization: Bearer &lt;/span&gt;&lt;span class="nv"&gt;$API_KEY&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nt"&gt;--header&lt;/span&gt; &lt;span class="s1"&gt;'Content-Type: application/json'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nt"&gt;--data-raw&lt;/span&gt; &lt;span class="s1"&gt;'{
      "event": {
          "transaction_id": "transaction_1",
          "external_customer_id": "5eb02857-a71e-4ea2-bcf9-57d3a41bc6ba",
          "code": "mtu",
          "timestamp": 1662462118,
          "properties": {
            "user_id": "1234_5678_9012_3456"
          }
      }
  }'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Event #2&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;LAGO_URL&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"https://api.getlago.com"&lt;/span&gt;
&lt;span class="nv"&gt;API_KEY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"__YOUR_API_KEY__"&lt;/span&gt;

curl &lt;span class="nt"&gt;--location&lt;/span&gt; &lt;span class="nt"&gt;--request&lt;/span&gt; POST &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$LAGO_URL&lt;/span&gt;&lt;span class="s2"&gt;/api/v1/events"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nt"&gt;--header&lt;/span&gt; &lt;span class="s2"&gt;"Authorization: Bearer &lt;/span&gt;&lt;span class="nv"&gt;$API_KEY&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nt"&gt;--header&lt;/span&gt; &lt;span class="s1"&gt;'Content-Type: application/json'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nt"&gt;--data-raw&lt;/span&gt; &lt;span class="s1"&gt;'{
      "event": {
          "transaction_id": "transaction_2",
          "external_customer_id": "5eb02857-a71e-4ea2-bcf9-57d3a41bc6ba",
          "code": "mtu",
          "timestamp": 1662548611,
          "properties": {
            "user_id": "1234_5678_9012_3456"
          }
      }
  }'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Event #3&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;LAGO_URL&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"https://api.getlago.com"&lt;/span&gt;
&lt;span class="nv"&gt;API_KEY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"__YOUR_API_KEY__"&lt;/span&gt;

curl &lt;span class="nt"&gt;--location&lt;/span&gt; &lt;span class="nt"&gt;--request&lt;/span&gt; POST &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$LAGO_URL&lt;/span&gt;&lt;span class="s2"&gt;/api/v1/events"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nt"&gt;--header&lt;/span&gt; &lt;span class="s2"&gt;"Authorization: Bearer &lt;/span&gt;&lt;span class="nv"&gt;$API_KEY&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nt"&gt;--header&lt;/span&gt; &lt;span class="s1"&gt;'Content-Type: application/json'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nt"&gt;--data-raw&lt;/span&gt; &lt;span class="s1"&gt;'{
      "event": {
          "transaction_id": "transaction_3",
          "external_customer_id": "5eb02857-a71e-4ea2-bcf9-57d3a41bc6ba",
          "code": "mtu",
          "timestamp": 1662721411,
          "properties": {
            "user_id": "0000-1111-2222-3333"
          }
      }
  }'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--bxCckEVB--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/qtj5428ajerkfr257bdy.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--bxCckEVB--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/qtj5428ajerkfr257bdy.png" alt="Lago events debugger" width="880" height="434"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We have three different events, however, two of them are sending the same &lt;code&gt;user_id&lt;/code&gt;. As we use a unique count aggregation type for the billable metric MTUs, &lt;strong&gt;the number of units to be charged is 2.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Lago then automatically calculates the fees depending on the charge model of the plan. With Segment’s pricing, the first 10,000 MTUs are free of charge. Therefore, these two units cost $0.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--zJbiwEcJ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/mps63mwpoy7v7aecsalb.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--zJbiwEcJ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/mps63mwpoy7v7aecsalb.png" alt="Current billing usage" width="880" height="401"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This is how Lago can help you implement a usage-based billing system in just a few hours (not months). It’s easy to build but also easy to maintain.&lt;/p&gt;

&lt;p&gt;In addition to this, Lago manages upgrades/downgrades, taxes, discounts, prepaid credits and prorated fees. &lt;a href="https://www.getlago.com/get-started"&gt;Ready to give it a try?&lt;/a&gt;&lt;/p&gt;

</description>
      <category>billing</category>
      <category>opensource</category>
    </item>
    <item>
      <title>The 5 reasons why we chose open source for our Billing API</title>
      <dc:creator>Raffi Sarkissian</dc:creator>
      <pubDate>Mon, 05 Sep 2022 13:33:11 +0000</pubDate>
      <link>https://dev.to/lago/the-5-reasons-why-we-chose-open-source-for-our-billing-api-e70</link>
      <guid>https://dev.to/lago/the-5-reasons-why-we-chose-open-source-for-our-billing-api-e70</guid>
      <description>&lt;p&gt;When we started &lt;a href="https://getlago.com"&gt;Lago&lt;/a&gt;, we knew billing was still an unsolved problem, but we never considered launching another traditional billing SaaS.&lt;/p&gt;

&lt;p&gt;We had experienced the pain of billing so deeply and for so long (our team built a billing system from scratch for a fintech unicorn) that we thought a traditional approach would lead to traditional results.&lt;/p&gt;

&lt;p&gt;We want to &lt;strong&gt;make pricing and billing more programmable, open, and fair&lt;/strong&gt; for all software companies.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Open architecture
&lt;/h2&gt;

&lt;p&gt;Traditional billing or payment SaaS have become the source of financial truth for companies. However, it’s unclear how they interconnect with other internal business tools, and sometimes they don’t, by design: for instance, Stripe Billing can only be used with Stripe Payments.&lt;/p&gt;

&lt;p&gt;Billing is at the heart of every B2B company’s survival but as there are myriad internal tool stacks (including accounting, payments, CRM, CPQ), the setup needs to be different for each company. Therefore, we decided to open-source our code and make the interconnections as easy as possible.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Developers first
&lt;/h2&gt;

&lt;p&gt;None of the tools were made for engineers, so &lt;a href="https://www.getlago.com/blog/why-billing-systems-are-a-nightmare-for-engineers"&gt;billing is still an engineering nightmare&lt;/a&gt;. It’s the most high-value, underrated and unsystematized business process we’ve seen.&lt;/p&gt;

&lt;p&gt;It’s hard to know exactly why, but we believe the root cause is that existing billing solutions have catered to Finance people first. Just as payment solutions did before Stripe Payments came in with its API. Billing should address the needs of the Finance teams (obviously), but the implementation and maintenance of the system will always depend on engineers.&lt;/p&gt;

&lt;p&gt;Therefore, we chose to go open-source, provide full transparency and &lt;strong&gt;be as close as possible to developers&lt;/strong&gt;: they can submit issues on &lt;a href="https://github.com/getlago/lago"&gt;Lago's GitHub repo&lt;/a&gt;, talk to us on Slack, give us feedback on what we’ve built and comment on our product roadmap.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. Fair pricing
&lt;/h2&gt;

&lt;p&gt;We’re infuriated by the ‘rent seeker approach’ chosen by existing billing solutions. When you’re a billing middleman, a 1% fee on your customers’ hard-earned revenue doesn’t sound right.&lt;/p&gt;

&lt;p&gt;That’s one of the reasons why we chose to build our home-made billing solution at our previous company, but it wasn’t perfect either: a lot of resources were pulled out of our core product to be allocated to this project.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Lago is open-source, forever free&lt;/strong&gt;. We’ll also offer a cloud version for teams that don’t want to deal with the infrastructure themselves, which will allow us to monetize our solution and keep working towards a more programmable, open, and fair billing system for all software companies.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. Composability
&lt;/h2&gt;

&lt;p&gt;Each company’s pricing is different. The time of a one-size-fits-all subscription model is over and much ink has already been spilled by VCs on ‘usage-based pricing’. It’s a big shift but we’re convinced 80% of companies will switch to hybrid pricing: a mix of subscription and consumption-based model, with a million nuances in between.&lt;/p&gt;

&lt;p&gt;A closed-source SaaS can’t embrace this diversity and, through Lago, we want to help build as many pricing models as possible.&lt;/p&gt;

&lt;p&gt;Any company should be able to customize and deploy a billing system similar to that of Snowflake, Algolia, or Segment, 100x faster. And early-stage companies should be able to do so without hiring an army of engineers.&lt;/p&gt;

&lt;h2&gt;
  
  
  5. Participative approach
&lt;/h2&gt;

&lt;p&gt;The most complicated part of billing is the long list of edge cases (we covered this topic in our previous article).&lt;/p&gt;

&lt;p&gt;These are too small to be prioritized in any closed-source SaaS product roadmap, but they keep coming back on Stack Overflow, Hacker News, Reddit, and all the places where builders seek advice.&lt;/p&gt;

&lt;p&gt;Beyond our product, we’re also aiming at creating this hub for shared knowledge, so that no more engineers have to struggle with billing edge cases.&lt;/p&gt;

&lt;p&gt;If you share our vision, deploy our &lt;a href="https://github.com/getlago/lago"&gt;&lt;strong&gt;self-hosed version&lt;/strong&gt;&lt;/a&gt; for free, or ask for an access to our &lt;strong&gt;cloud hosted version&lt;/strong&gt;: &lt;a href="https://www.getlago.com/get-started"&gt;https://www.getlago.com/get-started&lt;/a&gt;&lt;/p&gt;

</description>
      <category>opensource</category>
      <category>api</category>
    </item>
    <item>
      <title>😵‍💫 Why billing systems are a nightmare for engineers</title>
      <dc:creator>Raffi Sarkissian</dc:creator>
      <pubDate>Thu, 01 Sep 2022 13:22:32 +0000</pubDate>
      <link>https://dev.to/lago/why-billing-systems-are-a-nightmare-for-engineers-45j3</link>
      <guid>https://dev.to/lago/why-billing-systems-are-a-nightmare-for-engineers-45j3</guid>
      <description>&lt;p&gt;This article was initially published on &lt;a href="https://getlago.com/" rel="noopener noreferrer"&gt;Lago&lt;/a&gt;'s blog, an open-source billing API, and was ranked #1 on HackerNews for 2 consecutive days.&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2j5522twa1bff12lcj42.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2j5522twa1bff12lcj42.png" alt="Billing is a nightmare for engineers"&gt;&lt;/a&gt;&lt;/p&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;"On my first day, I was told: 'Payment will come later, shouldn't be hard right?' I was worried. We were not selling and delivering goods, but SSDs and CPU cores, petabytes and milliseconds, space and time. Instantly, via API. Fungible, at the smallest unit. On all continents. That was the vision. After a week, I felt like I was the only one really concerned about the long road ahead. In ambitious enterprise projects, complexity compounds quickly: multi-tenancy, multi-users, multi-roles, multi-currency, multi-tax codes, multi-everything. These systems were no fun, some were ancient, and often 'spaghetti-like'. What should have been a one-year R&amp;amp;D project ended up taking seven years of my professional life, in which I grew the billing team from 0 to 12 people. So yes, if you ask me, billing is hard. Harder than you think. It's time to solve that once and for all."&lt;/em&gt;&lt;br&gt;
&lt;em&gt;&lt;strong&gt;Kevin Deldycke, former VP Engineering at Scaleway&lt;/strong&gt;&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This is a typical conversation we have with engineers on a daily basis.&lt;/p&gt;

&lt;p&gt;After my last post about my '&lt;a href="https://www.getlago.com/blog/pricing-my-only-growth-hack-at-qonto" rel="noopener noreferrer"&gt;pricing hack&lt;/a&gt;', some of you asked me why billing was that complex. My co-founder Raffi took up the challenge of explaining why it's still an unsolved problem for engineers.&lt;/p&gt;

&lt;p&gt;We also gathered insights from other friends who went through the same painful journey, including Algolia, Segment, Pleo... don't miss them! Passing the mic to Raffi.&lt;/p&gt;

&lt;p&gt;When you're thinking about automating billing, this means your company is getting traction. That's good news!&lt;/p&gt;

&lt;p&gt;You might then wonder: should we build it in-house? It does not look complex, and the logic seems specific to your business. Also, you might want to preserve your precious margins and therefore avoid existing billing solutions like Stripe Billing or Chargebee, that take a cut of your revenue. Honestly, who likes this 'rent-seeker' approach?&lt;/p&gt;

&lt;p&gt;Our team at Lago still has some painful memories of &lt;a href="https://qonto.com/" rel="noopener noreferrer"&gt;Qonto's&lt;/a&gt; internal billing system, that we had to build and maintain. Why was it so painful? In this article, I will provide a high-level view of the technical challenges we faced while implementing hybrid pricing (based on subscription and usage) and what we learned during this journey.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fe8y6p55z6lo5d21qry9d.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fe8y6p55z6lo5d21qry9d.png" alt="Billing challenges"&gt;&lt;/a&gt;&lt;/p&gt;


&lt;h2&gt;
  
  
  TL;DR: Billing is just 100x harder than you think
&lt;/h2&gt;

&lt;p&gt;"Let's bill yearly as well, should be pretty straightforward" claims the Revenue team. Great! Everyone is excited to start working on it. Everyone, except the Tech team. When you start building your internal billing system, it's hard to think of all the complexity that will pop up down the road, unless you've experienced it before.&lt;/p&gt;

&lt;p&gt;It's common to start a business with a simple pricing. You define one or two price plans and limit this pricing to a defined number of features. However, when the company is growing, your pricing gets more and more complex, just like your entire codebase.&lt;/p&gt;

&lt;p&gt;At Qonto, our first users could only onboard on a €9 plan. We quickly decided to add plans, and 'pay-as-you-go' features (e.g. ATM withdrawals, foreign currency payments, capital deposit, etc.) to grow revenue.&lt;/p&gt;

&lt;p&gt;Also, as Qonto is a neobank, we wanted to charge our customers' wallets directly, through a ledger connected to our internal billing system. The team started from a duo of full-time engineers building a billing system (which is already a considerable investment) to a dedicated cross-functional Pricing team.&lt;/p&gt;

&lt;p&gt;This is not specific to Qonto of course. Pleo, another fintech unicorn from Denmark faced similar issues:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"I've learned to appreciate that billing systems are hard to build, hard to design, and hard to get working for you if you deviate from 'the standard' even by a tiny bit."&lt;br&gt;
&lt;em&gt;&lt;strong&gt;Arnon Shimoni, Product Billing Infrastructure Lead at Pleo&lt;/strong&gt;&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This is not even specific to fintechs. The Algolia team ended up creating a whole pricing department, now led by Djay, a pricing and monetization veteran from Twilio, VMWare, Service Now. They switched to a 'pay-as-you-go' pricing model based on the number of monthly API searches.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"It looks easy on paper — however, it's a challenge to bring automation and transparency to a customer, so they can easily understand. There is a lot of behind-the-scenes work that goes into this, and it takes a lot of engineering and investment to do it the right way."&lt;br&gt;
&lt;em&gt;&lt;strong&gt;Bernardette Nixon, CEO at Venture Beat&lt;/strong&gt;&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;


&lt;h2&gt;
  
  
  #1 - Dates
&lt;/h2&gt;

&lt;p&gt;When implementing a billing system, dealing with dates is often the number 1 complexity. Somehow, all your subscriptions and charges are based on a number of days. Whether you make your customers pay weekly, monthly or yearly, you need to define the billing period.&lt;/p&gt;

&lt;p&gt;Here is a non-exhaustive list of difficulties for engineers:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;How to deal with leap years?&lt;/li&gt;
&lt;li&gt;Do your subscriptions start at the beginning of the month or at the creation date of the customer?&lt;/li&gt;
&lt;li&gt;How many days/months of trial do you offer?&lt;/li&gt;
&lt;li&gt;Who decided February only holds 28 days?&lt;/li&gt;
&lt;li&gt;Wait, point 1 is also important for February...&lt;/li&gt;
&lt;li&gt;How to calculate usage-based charges (price per second, hour, day)?&lt;/li&gt;
&lt;li&gt;Do I resume the consumption or do I stack it month over month? Year over year?&lt;/li&gt;
&lt;li&gt;Do I apply a pro-rata based on the number of days consumed by my customer?&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Although every decision is reversible, billing cycle questions are often the most important source of customer support tickets, and iterating on them is quite complex. For instance, Qonto switched from 'anniversary' dates to calendar dates for its billing periods (see here). This change was not trivial.&lt;/p&gt;


&lt;h2&gt;
  
  
  #2 - Upgrades &amp;amp; downgrades
&lt;/h2&gt;

&lt;p&gt;You need to allow your customers to upgrade or downgrade their subscriptions. Moving from plan A to plan B seems pretty easy, but it's not. Let's zoom on potential edge cases.&lt;/p&gt;

&lt;p&gt;What should we do when the user:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Upgrades or downgrades in the middle of a period;&lt;/li&gt;
&lt;li&gt;Has paid for the original plan in advance;&lt;/li&gt;
&lt;li&gt;Needs to pay for the original plan in arrears;&lt;/li&gt;
&lt;li&gt;Downgrades from a yearly plan to a monthly plan;&lt;/li&gt;
&lt;li&gt;Upgrades from a monthly plan to a yearly plan;&lt;/li&gt;
&lt;li&gt;Upgrades or downgrades from a plan paid in advance to a plan paid in arrears (and vice-versa); or&lt;/li&gt;
&lt;li&gt;Has a discount and upgrades/downgrades?&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;We did not have a free trial period at the time at Qonto, but Arnon from Pleo describes this situation &lt;a href="https://arnon.dk/5-things-i-learned-developing-billing-system/" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;


&lt;h2&gt;
  
  
  #3 - Usage-based computations
&lt;/h2&gt;

&lt;p&gt;Subscription-based billing is the first step when implementing a billing system. Each customer needs to be affiliated to a plan in order to start charging the right amount at the right time.&lt;/p&gt;

&lt;p&gt;But for a growing number of companies, like Qonto, other charges come alongside this subscription. These charges are based on what customers really consume. This is what we call 'usage-based billing'. Most companies end up having a hybrid pricing model: a monthly subscription fee and 'add-ons' or 'pay-as-you-go' charges on top of it.&lt;/p&gt;

&lt;p&gt;These consumption-based charges are tough to track at scale, because they often come with calculation rules performed on a high volume of events.&lt;/p&gt;

&lt;p&gt;Here are some examples:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://segment.com/pricing/" rel="noopener noreferrer"&gt;Segment's&lt;/a&gt; pricing is based on the number of monthly tracked users. This means that they need to count the distinct number of users each month and reset this value to zero for the next billing period. In order to retrieve the number of unique visitors, they need to apply a 'distinct' function to deduplicate them.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"transaction_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2345"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"timestamp"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2022-03-16"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"event_type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"tracked_users"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"properties"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"user_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"123456789"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt;
    &lt;span class="n"&gt;DATE_TRUNC&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'month'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;timestamp&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="k"&gt;month&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="k"&gt;COUNT&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;DISTINCT&lt;/span&gt; &lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;unique_users&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;events&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;event_type&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'tracked_used'&lt;/span&gt;
&lt;span class="k"&gt;GROUP&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="k"&gt;month&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://www.algolia.com/pricing/" rel="noopener noreferrer"&gt;Algolia&lt;/a&gt; tracks the number of API searches per month. This means they need to sum the number of monthly searches for each customer and reset this value to zero for the next billing period.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"transaction_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2345"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"timestamp"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2022-03-16T00:00:00Z"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"event_type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"searches"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"properties"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"api_search"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"1"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt;
    &lt;span class="n"&gt;DATE_TRUNC&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'month'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;timestamp&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="k"&gt;month&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="k"&gt;SUM&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;api_search&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;monthly_searches&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;events&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;event_type&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'searches'&lt;/span&gt;
&lt;span class="k"&gt;GROUP&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="k"&gt;month&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It becomes even more complex when you start calculating charges based on a timeframe. For instance, &lt;a href="https://www.snowflake.com/en/" rel="noopener noreferrer"&gt;Snowflake&lt;/a&gt; charges for the compute usage of a data warehouse per second. This means that they sum the number of gigabytes or terabytes consumed, multiplied by the number of seconds of compute time.&lt;/p&gt;

&lt;p&gt;Consider the example of a utility company that charges $10 per kilowatt of electricity per hour. The illustration below shows what needs to be modeled and automated by the billing system.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2pjlur2rm22ydl0cka9q.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2pjlur2rm22ydl0cka9q.png" alt="Usage based billing"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;• Hour 1: *&lt;em&gt;10 KW used for 0.5 hour = 5 KW (10 x 0.5)&lt;br&gt;
*&lt;/em&gt;• Hour 2:&lt;/strong&gt; 20 KW used for 1 hour = 20 KW (20 x 1)&lt;br&gt;
&lt;strong&gt;• Hour 3: *&lt;em&gt;0 KW used for 1 hour = 0 KW (0 x 1)&lt;br&gt;
*&lt;/em&gt;• Hour 4:&lt;/strong&gt; 30 KW used for 0.5 hour = 15 KW (30 x 0.5)&lt;br&gt;
&lt;strong&gt;• TOTAL = 40 KW used x $10 ⇒ $40&lt;/strong&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  #4 - Idempotency done right
&lt;/h2&gt;

&lt;p&gt;Billing errors sometimes occur. Charging a user twice for the same product is obviously bad for the customer experience, but failing to charge when needed hurts revenue. That's partly why Finance and BI teams spend so much time on revenue recognition.&lt;/p&gt;

&lt;p&gt;For 'pay-as-you-go' companies, the billing system will process a high volume of events. When an event needs to be replayed, it needs to happen without billing the user again. Engineers call it &lt;code&gt;idempotency&lt;/code&gt;, which means the &lt;strong&gt;ability to apply the same operation multiple times without changing the result beyond the first try.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;It's a simple design principle, however, maintaining it at all times is hard.&lt;/p&gt;




&lt;h2&gt;
  
  
  #5 - The case for a Cash Collection Officer
&lt;/h2&gt;

&lt;p&gt;Cash collection is the process of collecting the money that customers owe you. The pet peeve of cash collection is dunning: when payments fail, the merchant needs to persist and make repeated payment requests to their customers, while trying not to damage the relationship.&lt;/p&gt;

&lt;p&gt;At Qonto, we called these 'waiting funds'. A client's status was 'waiting funds' when they successfully went through the sign-up, KYC and KYB process but their account balance was still 0.&lt;/p&gt;

&lt;p&gt;For a neobank, the impact is twofold: you can't charge your service fees (e.g. monthly subscription) and your customer does not generate interchange revenues (e.g. when you make a payment of $100 with your card, the card issuer earns $0.5-$1 of interchange revenue through the merchant's fees).&lt;/p&gt;

&lt;p&gt;Therefore, your two main revenue streams are 'null' but you did pay to acquire, onboard, KYC the user and then to produce and send them a card. We often half-joked about the need to hire a 'chief waiting funds officer': the financial impact of this is just as high as the problem is underestimated.&lt;/p&gt;

&lt;p&gt;All companies face dunning challenges. For engineers, on top of the billing architecture, this means that they need to design and build:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;A **retry logic **to ask for a new payment intent;&lt;/li&gt;
&lt;li&gt;An &lt;strong&gt;invoice reconciliation system&lt;/strong&gt; (e.g. if several months of charges are recovered);&lt;/li&gt;
&lt;li&gt;An &lt;strong&gt;app logic to disable access&lt;/strong&gt; to the service; and&lt;/li&gt;
&lt;li&gt;An &lt;strong&gt;emailing workflow&lt;/strong&gt; to urge a user to proceed to the payment.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Some SaaS are even on a mission to fight dunnings and have built full-fledged companies around cash collection, such as Upflow, which is used by successful B2B scale-ups, including Front and Lattice.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Sending quality and personalized reminders took us a lot of time and, as Lattice was growing fast, it was essential for us to scale our cash collection processes. We use Upflow to personalize how we ask our customers for money, repeatedly, while keeping a good relationship. We now collect 99% of our invoices, effortlessly."&lt;br&gt;
&lt;em&gt;&lt;strong&gt;Jason Lopez, Controller at Lattice&lt;/strong&gt;&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  #6 - The labyrinth of taxes and VAT
&lt;/h2&gt;

&lt;p&gt;Taxes are challenging and depend on multiple dimensions. Taxes depend on what you are selling, your home country and your customer's home country. In the simplest situations, your tax decision tree should look like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmbdv6dvih6i10s1h2mbj.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmbdv6dvih6i10s1h2mbj.png" alt="Labyrinth of taxes"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now, imagine that you sell different types of goods/services to different types of customers in 100+ countries. If you think the logic on paper looks complex, the engineering challenge is tenfold. Engineers need to think of an entire tax logic within the application. This pyramidal logic is based on customers and products, including:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Taxes at company level:&lt;/strong&gt; your company will have a general tax rate that applies to all customers by default;&lt;br&gt;
&lt;strong&gt;2. Taxes at customer level:&lt;/strong&gt; the default tax rate can be overridden for each customer, depending on the dimensions mentioned above (see illustration); and&lt;br&gt;
&lt;strong&gt;3. Taxes at feature level:&lt;/strong&gt; this is mostly the case for the banking industry. At Qonto for instance, banking fees are not subject to taxes but a 20% VAT rate applies to non-banking features.&lt;/p&gt;

&lt;p&gt;With billing, the devil's in the detail, which is why I always cringe when I see engineering teams build a home-made system, just because they think "it's not that complex".&lt;/p&gt;

&lt;p&gt;If you've already tackled the topics listed above and think it's a good investment of your engineering time, go ahead and build it in-house, but make sure to budget for the maintenance work that is always needed. Another option is to rely on existing billing platforms, built by specialized teams.&lt;/p&gt;

&lt;p&gt;To solve this problem at scale, we've adopted a radical approach: we're building an open-source billing API for product-led SaaS. Our API and architecture are open, so that you can embed, fork and customize them as much as your pricing and internal processes require.&lt;/p&gt;

&lt;p&gt;If you're interested, you can reach out to the Lago team or take a look at our open-source repo: &lt;a href="https://github.com/getlago/lago" rel="noopener noreferrer"&gt;https://github.com/getlago/lago&lt;/a&gt;&lt;/p&gt;

</description>
      <category>billing</category>
      <category>opensource</category>
    </item>
  </channel>
</rss>
