gauge-highMetered Usage
How to add metered usage to your Stripe subscription
Before you start
Creating a metered usage subscription is similar to: Adding a subscription The main differences being:
- In Stripe you create a Meter and you create Products/Prices that are metered
- In Petal Pro you record and synchronise usage with Stripe
The following will guide you through the process of adding metered usage to the AI chat feature.
Starting with a fresh app
For this tutorial you will need:
-
Access to Petal Pro (you can purchase it on petal.build)
-
Elixir & Erlang installed (you can follow our installation guide to get up and running)
-
PostgreSQL running: Postgres.app works well for Mac or we have a docker-compose.yaml file that will run Postgres for you
-
An account on Stripe
-
For deployment (optional) you will need:
-
An account on Fly.io
Download
Head over to the projects page and create a new project, then download v3.0.0. You can unzip it in your terminal with the command unzip petal_pro_3.0.0.zip.
Run
First, rename the project:
mv petal_pro_3.0.0 sub_demo
cd sub_demo
mix rename PetalPro SubDemo
Next, start the demo app:
mix setup
mix phx.server
Configure Stripe
Before we do anything inside the demo app, we need to add some Stripe Products. Login using your account. Make sure that Stripe is in “test mode” - this will allow you to test purchasing without a credit card.
ℹ️ Information: Stripe has introduced isolated test environments through [Sandboxes](https://docs.stripe.com/sandboxes). We recommend using a Sandbox in place of "test mode".
Install the Stripe CLI
Follow these directions to install the Stripe CLI. Then login via the command line (the instructions to login are also on this page):
stripe login
ℹ️ Information: Commands run using the Stripe CLI default to test mode
Create a product with usage-based prices
First create the “Essential” product:
stripe products create --name="Essential"
Take note of the id returned, it will look like prod_xxxxx.
Create the Monthly price for metered usage:
stripe prices create \
--unit-amount=5 \
--currency=usd \
-d "transform_quantity[divide_by]"=1000 \
-d "transform_quantity[round]"=up \
-d "recurring[interval]"=month \
-d "recurring[usage_type]"=metered \
--product="prod_xxxxx"
Make sure you replace prod_xxxxx with the id returned from the previous call. usage_type is set to “metered” - which means that the unit price isn’t fixed. The price is 5 cents per 1000 units (rounding up - meaning a minimum of $0.05 will be charged on single use).
ℹ️ Information: Note that --unit-amount is in cents, rather than dollars
Add the “Business” and “Enterprise” products
In this guide we’ll stick to per month pricing to keep it simple for the end user.
Repeat the process (above) to create a “Business” and an “Enterprise” product. Use the table below as a point of reference for suggested prices:
| Product | Essential | Business | Enterprise |
|---|---|---|---|
| Monthly price | $0.05 per 1000 units | $0.5 per 15,000 units | $5 per 200,000 units |
Enable switching plans for the Customer Portal
When purchasing a subscription, Petal Pro will redirect to Stripe. In the case where the user switches from an existing plan to another - you need to enable some settings for the Customer Portal. Once logged into Stripe:
-
Click on the cog on the top right (
Settings) -
In the section titled “Billing”, click on
Customer Portal - Expand the “Subscriptions” section
-
Enable the setting,
Customers can switch plans - Using “Find a test product…” add prices from the Essential, Business and Enterprise products
ℹ️ Information: If you can't add usage meter prices - you may have to remove all prices and then try again
In addition, you may want to review the “Business information” section - to customise the output of the Customer Portal.
Create a Meter
The final step is to create a Meter. A Meter allows you to record usage against a particular Subscription/Price. Think of it as a way of grouping cost associated with a feature. In this guide we’re going to create a Meter for the AI chat feature:
stripe billing meters create \
--display-name="AI tokens" \
--event-name=ai_tokens \
-d "default_aggregation[formula]"=sum
ℹ️ Information: At the time of writing, billing is a recent addition to the Stripe CLI. Make sure that you're on the latest version of the Stripe CLI
Configure Petal Pro
Petal Pro comes pre-configured for Stripe integration. There are three things you need to do to test the integration locally:
- Run the CLI web hook
- Add Stripe settings to your development environment
- Update the Petal Pro config with product/price details (created above) from your Stripe account
Run the CLI web hook
In order to test your Stripe integration on your dev machine, you’ll need to run a web hook. This is done using the Stripe CLI (see the Install the Stripe CLI section for more information).
Once you are logged in via the Stripe CLI, you can run the following command:
stripe listen --forward-to localhost:4000/webhooks/stripe
> Ready! You are using Stripe API Version [2022-11-15]. Your webhook signing secret is whsec_xxxxxxxxxxxxxxx (^C to quit)
Take note of the whsec_xxxxxxxxxxxxxxx secret. This will be used as your STRIPE_WEBHOOK_SECRET in the next section.
Add Stripe settings to your development environment
First, you’ll need your STRIPE_SECRET and your STRIPE_WEBHOOK_SECRET. To get your STRIPE_SECRET:
- Make sure you’re in Sandbox or Test mode
-
At the bottom left of the screen, click on
Developersthen clickAPI keys -
Under “Standard Keys”, look for the “Secret key”
To get the
STRIPE_WEBHOOK_SECRET, see the instructions under Run the CLI web hook.
The next step is to add these as environment variables to your demo app. Make sure that you install direnv and that you’re in the root of the demo project:
cp .envrc.example .envrc
Uncomment these lines at the bottom of the file:
Update the values you obtained (above) for STRIPE_SECRET and STRIPE_WEBHOOK_SECRET.
The .envrc file is listed in .gitignore. Meaning that the secrets will live on your development machine and will not be accidentally pushed to the git repo.
To activate the new environment variables:
Update the Product config
The following shows how you can update the Essential plan:
amount has been replaced with unit_amount and per_units - this controls what the user sees for each plan on the subscribe page. Eliminating trial_days means that you don’t have to wait until the trial is over to see upcoming charges.
In this guide we’re focusing on monthly pricing - the essential-yearly product has been removed.
When the user selects a plan, Petal Pro will use these settings to make a Stripe call. In the line where we’re adding price_xxxxx we’re omitting quantity. If the quantity were present it would be passed up too. In the case of a usage-based price, passing up quantity will result in a Stripe error.
Replace price_xxxxx with a valid price id. To get the list of prices for the “Essential” product:
Repeat the above process for the Business and Enterprise plans.
Update the Meter config
Add a reference to our “AI Tokens” meter:
To get the current list of meters:
Find the meter that has the event_name of ai_tokens. Use it’s id to replace mtr_xxx_xxxxx.
Enable the CollectMeterEvents GenServer
The CollectMeterEvents GenServer is used to record meter usage events without blocking the calling process. Uncomment the CollectMeterEvents GenServer to enable it:
defmodule PetalPro.Application do
def start(_type, _args) do
children = [
...
# Start the meter collection GenServer
- # PetalPro.Billing.Meters.CollectMeterEvents,
+ PetalPro.Billing.Meters.CollectMeterEvents,
...
]
opts = [strategy: :one_for_one, name: PetalPro.Supervisor]
Supervisor.start_link(children, opts)
end
end
Enable the synchronisation job
Synchronisation with Stripe is handled by the MeterSyncWorker Oban job. Uncomment the Oban configuration to enable it:
config :petal_pro, Oban,
repo: PetalPro.Repo,
queues: [default: 5, billing: 5],
plugins: [
{Oban.Plugins.Pruner, max_age: 3600 * 24},
{Oban.Plugins.Cron,
crontab: [
# {"@daily", PetalPro.Workers.ExampleWorker}
- # {"@daily", PetalPro.Billing.Providers.Stripe.Workers.MeterSyncWorker},
+ {"@daily", PetalPro.Billing.Providers.Stripe.Workers.MeterSyncWorker},
# {"* * * * *", EveryMinuteWorker},
# {"0 * * * *", EveryHourWorker},
# {"0 */6 * * *", EverySixHoursWorker},
# {"0 0 * * SUN", EverySundayWorker},
# More examples: https://crontab.guru/examples.html
]}
]
ℹ️ Information: Note that there's two Oban queues. Billing/Stripe jobs utilise the billing queue - including the MeterSyncWorker job. Oban Web allows you to view/isolate separate queues!
Handling metered usage
There are two steps with regards to handling metered usage:
Record the usage
Metered usage is stored in the Petal Pro database. To record an event, use the record_event function:
alias PetalPro.Billing.Meters
Meters.record_event(meter_id, customer_id, subscription_id, quantity)
record_event is non-blocking and enqueues the event using the CollectMeterEvents GenServer. In the background, the CollectMeterEvents GenServer writes the event to the database as soon as it can.
Sync the usage with Stripe
When it comes to metered usage events, Petal Pro is considered the source of truth. For example, when you record an event you specify the subscription_id. You can’t filter by subscription_id when retrieving meter usage events via the Stripe API. However, you can when you return meter usage events from the database. This is used in the billing page to match meter usage events to the upcoming invoice.
Synchronisation with Stripe is handled by the MeterSyncWorker Oban job - which will upload unprocessed events to Stripe. A metered usage event is considered processed if:
- The event has been uploaded successfully (it will be marked as sent)
- There is an error (it will be marked with the error)
🚨 Danger: triangle-exclamation
Meter events with errors will not synchronise with Stripe. If something is not right, check the billing_meter_events table for error messages. Events will be re-processed if the error_message is removed
ℹ️ Information: The default user name for the admin user is [[emailprotected]](/cdn-cgi/l/email-protection). The default password is password
Subscribe page for an Organisation
Choose a plan to subscribe to. Then use Stripe to complete the process - you can use `4242 4242 4242 4242` as your credit card for testing.ℹ️ Information: You must have the Stripe [web hook](/petal-pro/guides/metered-usage#run-the-cli-web-hook) running so that you can complete this process
Org AI Chat!
Ask a question - this will trigger the code that collects metered usage. Now go to the Admin page and click on "Oban Web": Oban Web
Make sure that the `MeterSyncWorker` job has successfully completed. Finally, go to your Organisation, then click on "Org Settings" and "Billing". You should see something similar to this: Organisation Billing page
If everything went well, you should see an "Upcoming" amount and "AI Tokens" usage data . If you configure multiple Meters, they will be automatically grouped on this screen.ℹ️ Information: It takes time for the upcoming invoice to be updated in Stripe. If you're quick, give it at least 30 seconds!