Implementing Seat-Based Billing

Intro

Many of Paid’s customers have a predominantly “seat-based” pricing model: Charge $X per seat per month. It’s a popular business model that scales the more people use your product.

Paid has focused on making metered billing pricing models as easy to implement as a few lines of code. In this tutorial, we’ll show you how to implement a seat-based pricing model with a little ruby-esque psuedocode. Note: This implementation does not consider proration.

The Example

In order to do a more detailed example, we will use an example pricing model for the remainder of this article. It can easily be adapated to any new cost or timeframe. Let’s assume you charge $149.99 per seat per month for your service.

The Steps

There are 4 steps to our guide:

  1. Create Recurring Invoice
  2. Consume Webhooks
  3. Validate Webhooks
  4. Compute and Save Metric

1. Create Recurring Invoice

Recurring invoices are Paid’s version of subscriptions. One special difference from subscriptions in most other systems is that Paid offers the ability to have metered attributes for recurring invoices. Let’s create a simple recurring invoice for our working example.

Once you have logged in:

1. Go to the Recurring Invoice creation page.

2. Create a new Invoice Template

3. In the new invoice template screen, let’s add a name like “$149.99 per seat per month.” The default for an invoice template is 1 month, so we will leave that alone.

4. Now we will need to add a new product, which can be reused on future recurring invoices. To do so, click the “Attach A Product” button.

5. In the new pop up, click the “New Product” tab at the top.

6. Paid offers a number of different product types. Each have different functionality when it comes to computing the “cost” of a product. In this example, we want to select “Variable Quantity” under the metered section on the left.

7. Some new fields will pop up. Let’s set the Internal Name to “$149.99 per Seat.” The Transaction Description field is what will show on the invoice. We’ll also use “$149.99 per Seat.” The next required field is the External Metric ID. This field we will use in code later, but it is what you can use to identify what type of information Paid is requesting. For this example, we will use seat_count. Finally, we’ll put in the price of $149.99. Just hit “Create Product” to finish up.

8. With the new product attached, click “Save Invoice Template.”

9. Select a customer or create a new one.

10. With a customer selected, just pick a start date for the recurring invoice and click “Create Recurring Invoice” at the top.

Now that we have a recurring invoice, we can get started implementing the backend code.

2. Consume Webhooks

Just some quick definitions:

Trigger
A Recurring Invoice trigger is when the recurring invoice should be created. For example, if you set the interval to be 1 month starting on 1/16/2017, then the first trigger would be on 1/16/2017. The next would be on 2/16/2017, 3/16/2017, etc.

Order
When a recurring invoice triggers, it creates what we call an Order. An Order is basically how we collect and store all the information around these trigger events.

Order Item
An order can have one or many Order Items. One Order Item is generated per product in the Recurring Invoice. In this example, we only have a single product so each Order will only have a single Order Item. Order Items are what will be requesting information (i.e. how many seats were used this month), and it is what you will update with an API call.

Ok, enough defining. Let’s talk about webhooks. You’ll need to make sure you have webhooks enabled and configured by checking your Account Settings. Assuming everything is correct, you will start receiving events that occur in Paid. There are many events that you can consume and process, but the one we are concerned abou here is order_item.data_request. Every time an Order Item gets created but doesn’t have enough information to compute a cost, it will request information using webhooks.

The structure of a webhook will look something like this (with only the useful keys):

event = {
  "data": {
    "customer": {
      "email": "ryan@paidlabs.com",
      "external_id": "1234",
      "id": "cus_WM9yK5tXRcMzrcfzlSd3SA",
      "name": "Ryan Jackson",
      "object": "customer",
      ...
    },
    "id": "ordr_itm_oIwrTPRXrJ1794maS5rwA",
    "object": "order_item",
    "order": "ordr_t6SU0QYmM8Pjz2G4kw",
    "pricing": {
      "currency": "usd",
      "price": 14999,
      "quantity": null,
      "type": "quantity"
    },
    "product": {
      "external_metric_id": "seat_count",
      "id": "prod_3qdrQuvEXCUegQeaNo1cHw",
      "name": "$149.99 per Seat",
      "object": "product",
      "pricing": {
        "currency": "usd",
        "price": 14999,
        "type": "quantity"
      },
      "transaction_description": "$149.99 per Seat",
      ...
    },
    "service_ends_on": 1484179200,
    "service_starts_on": 1481587200,
    ...
  },
  "id": "evt_Wag4PF39jWk5lPfVhsNw",
  "object": "event",
  "type": "order_item.data_request",
  ...
}

Here are the important parts:

  • event['type'] is how we filter the various events that are sent to the webhook. In this case we are looking for order_item.data_request
  • event['data']['production']['external_metric_id'] is our the External Metric ID we set on the product (in this case seat_count) so we know what we are computing.
  • event['data']['service_starts_on'] and event['data']['service_ends_on'] specifies the range of time we are looking for.

We will need to build a function that takes in a webhook and parses out the event we are looking for:

def handle_webhook(event)
  case event['type']
  when 'order_item.data_request'
    process_order_item(event['data']['id'])
  end
end

3. Compute and Save Metric

We just need to write the process_order_item method from above. So we can do so using something like the following:

def process_order_item(order_item_id)
   # retrieve Order Item via API using Order Item ID
  order_item = GET(url: 'https://api.paidlabs.com/v0/order_items/' + order_item_id)

  case order_item['product']['external_metric_id']
  when 'seat_count'
    PUT(
      url: 'https://api.paidlabs.com/v0/order_items/' + order_item_id,
      body: {
        pricing: {
          quantity: compute_seat_count_for(
            order_item['data']['service_starts_on'],
            order_item['data']['service_ends_on'],
          )
        }
      }
    ) 
    # The API will return an error if the order is already processed,
    # meaning all the necessary information to compute cost has already been collected.
  end
end

def compute_seat_count_for(start_date, end_date)
  # Write code here to return the number of seats used in a month.
  # One strategy is just taking the number of seats used at the end,
  # ignoring ups and downs during the period.
end

And that’s it!

Now you have a way to bill by number of seats each month. So a few things to note:

  1. Need to subscribe a new customer to the same plan? No problem! You can create as many Recurring Invoices using the same Invoice Template as you want. Just repeat Step 1 and just select the existing Invoice Template.

  2. Need to change the price per seat? Just create a new Invoice Template with a new Metered Variable Quantity product. Assign it the new price and you can use that Invoice Template going forward.

  3. Want to charge based on a different metric? When creating a product, just change the External Metric ID and be sure to update your code from Step 3 to compute and return the value.

There’s are many things you can do with Paid’s Recurring Invoices. If you need help mapping your pricing model, just let us know by emailing us at hello@paidlabs.com.