DEV Community πŸ‘©β€πŸ’»πŸ‘¨β€πŸ’»

DEV Community πŸ‘©β€πŸ’»πŸ‘¨β€πŸ’» is a community of 963,864 amazing developers

We're a place where coders share, stay up-to-date and grow their careers.

Create account Log in
Cover image for 6 steps to build a usage-based billing system with Lago (YC S21)
Raffi Sarkissian for Lago

Posted on • Originally published at getlago.com

6 steps to build a usage-based billing system with Lago (YC S21)

Lago 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!

Lago also provides a cloud-hosted application for those who don’t want to run it on their own environment.

We are going to build an entire hybrid billing system, including plans and usage-based features. Let’s try to replicate Segment’s pricing for instance!

Prerequisites

You can find the main Lago repository on GitHub.

Before we get started, you need to:

  1. Install Docker on your machine;
  2. Make sure Docker Compose is installed and available (it should be the case if you’ve chosen to install Docker via Docker Desktop);
  3. Make sure Git is installed on your machine; and
  4. A cup of tea (or coffee)! β˜•οΈ

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

1. Setting up your Lago billing engine

Start running Lago on your own environment. You will have access to Lago API and Lago UI (frontend admin panel to perform no-code tasks).

# Get the code
git clone https://github.com/getlago/lago.git

# Go to Lago folder
cd lago

# Set up environment configuration
echo "LAGO_RSA_PRIVATE_KEY=\"`openssl genrsa 2048 | base64`\"" >> .env
source .env

# Start (make sure docker is started)
docker-compose up
Enter fullscreen mode Exit fullscreen mode

You can now open your browser and go to http://localhost/ to connect to the application. Lago's API is exposed at http://localhost:3000/.

If needed, you can override your environment variables (learn more).

2. Defining your billable metrics

The usage-based feature billed by Segment is the number of monthly tracked users (MTUs), which is related to the total number of unique users tracked monthly.

Lago’s billable metrics allow you to track and automatically aggregate usage to determine the number of units to be charged at the end of a billing period.

Here we’re going to define MTUs as a metered billable metric:

LAGO_URL="https://api.getlago.com"
API_KEY="__YOUR_API_KEY__"

curl --location --request POST "$LAGO_URL/api/v1/billable_metrics" \
  --header "Authorization: Bearer $API_KEY" \
  --header 'Content-Type: application/json' \
  --data-raw '{
    "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"
    }
  }'
Enter fullscreen mode Exit fullscreen mode

For this billable metric, the aggregation type is unique_count_agg, which is used to count the number of unique user_id values recorded during the billing period.

3. Creating your first plan

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).

Let’s take the example of Segment’s Team plan. 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.

LAGO_URL="https://api.getlago.com"
API_KEY="__YOUR_API_KEY__"

curl --location --request POST "$LAGO_URL/api/v1/plans" \
--header "Authorization: Bearer $API_KEY" \
--header 'Content-Type: application/json' \
--data-raw '{
  "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"
          }
        ]
      }
    ]
  }
}'
Enter fullscreen mode Exit fullscreen mode

Let’s take a closer look at the request above.

Basic plan model

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:

β€’ interval: can also be set to yearly or weekly;
β€’ amount_currency: all currencies are available;
β€’ trial_period: set a number of trial days; and
β€’ pay_in_advance: if set to false, the subscription fee will be paid in arrears (end of period).

Usage-based charges

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

Usage based graduated charge

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).

4. Creating your first customer

Congratulations, your pricing is ready! You can now create a customer, assign them a plan and start monitoring usage.

LAGO_URL="https://api.getlago.com"
API_KEY="__YOUR_API_KEY__"

curl --location --request POST "$LAGO_URL/api/v1/customers" \
  --header "Authorization: Bearer $API_KEY" \
  --header 'Content-Type: application/json' \
  --data-raw '{
    "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
      }
    }
  }'
Enter fullscreen mode Exit fullscreen mode

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.

Creating a customer

5. Assigning a plan to a customer

To start billing a customer, you need to assign them a plan. This action will create a subscription.

LAGO_URL="https://api.getlago.com"
API_KEY="__YOUR_API_KEY__"

curl --location --request POST "$LAGO_URL/api/v1/subscriptions" \
  --header "Authorization: Bearer $API_KEY" \
  --header 'Content-Type: application/json' \
  --data-raw '{
    "subscription": {
            "external_id": "my_subscription_1",
      "external_customer_id": "5eb02857-a71e-4ea2-bcf9-57d3a41bc6ba",
      "plan_code": "team",
      "billing_time": "anniversary"
    }
  }'
Enter fullscreen mode Exit fullscreen mode

Create a subscription

When assigning a subscription, you can:

β€’ Define your own external_id;
β€’ Choose between anniversary or calendar 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
β€’ 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).

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.

Emitting the invoice

This invoice can be customized with information about your organization, your logo and a custom footer. For compliance purposes, invoices have an incremental sequential_id and incremental invoice_number as well.

6. Ingesting usage-based events

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 transaction_id, we make sure that an event is not ingested twice. It’s what we call an idempotency key.

Below are three events ingested by Lago:

Event #1

LAGO_URL="https://api.getlago.com"
API_KEY="__YOUR_API_KEY__"

curl --location --request POST "$LAGO_URL/api/v1/events" \
--header "Authorization: Bearer $API_KEY" \
--header 'Content-Type: application/json' \
--data-raw '{
      "event": {
          "transaction_id": "transaction_1",
          "external_customer_id": "5eb02857-a71e-4ea2-bcf9-57d3a41bc6ba",
          "code": "mtu",
          "timestamp": 1662462118,
          "properties": {
            "user_id": "1234_5678_9012_3456"
          }
      }
  }'
Enter fullscreen mode Exit fullscreen mode

Event #2

LAGO_URL="https://api.getlago.com"
API_KEY="__YOUR_API_KEY__"

curl --location --request POST "$LAGO_URL/api/v1/events" \
--header "Authorization: Bearer $API_KEY" \
--header 'Content-Type: application/json' \
--data-raw '{
      "event": {
          "transaction_id": "transaction_2",
          "external_customer_id": "5eb02857-a71e-4ea2-bcf9-57d3a41bc6ba",
          "code": "mtu",
          "timestamp": 1662548611,
          "properties": {
            "user_id": "1234_5678_9012_3456"
          }
      }
  }'
Enter fullscreen mode Exit fullscreen mode

Event #3

LAGO_URL="https://api.getlago.com"
API_KEY="__YOUR_API_KEY__"

curl --location --request POST "$LAGO_URL/api/v1/events" \
--header "Authorization: Bearer $API_KEY" \
--header 'Content-Type: application/json' \
--data-raw '{
      "event": {
          "transaction_id": "transaction_3",
          "external_customer_id": "5eb02857-a71e-4ea2-bcf9-57d3a41bc6ba",
          "code": "mtu",
          "timestamp": 1662721411,
          "properties": {
            "user_id": "0000-1111-2222-3333"
          }
      }
  }'
Enter fullscreen mode Exit fullscreen mode

Lago events debugger

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

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.

Current billing usage

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.

In addition to this, Lago manages upgrades/downgrades, taxes, discounts, prepaid credits and prorated fees. Ready to give it a try?

Top comments (0)

Update Your DEV Experience Level:

Settings

Go to your customization settings to nudge your home feed to show content more relevant to your developer experience level. πŸ›