Product access with prepaid credits
You can use Orb to both manage prepaid credit balances for your customers and to control access to your product based on those balances. This is useful for companies that want to ensure that customers are only able to access their product if they’ve paid for it in advance.
In this guide, we’ll walk through how to integrate Orb’s prepaid credit management functionality and alerting features to accurately manage product access. We’ll use a model that’s a very simple version of a text message API that uses credits:
- No messages are sent unless the customer has purchased credits.
- Customers must purchase credits through your product’s admin dashboard. Each credit costs $0.01.
- The customer can purchase credits in advance, and the credits will be automatically deducted from their balance as messages are sent.
- Messages can be used at very high throughput, and the customer needs to be blocked from sending messages if they run out of credits as close to the event as possible.
- Sending messages is performance critical, so it’s not possible to incur the latency of checking the customer’s current balance before each message send takes place. Incurring this latency is acceptable on a very infrequent basis.
Prerequisites
In your integration, ensure that you create an Orb customer corresponding to each of your
own customers. A customer always starts with 0 credits in each of their prepaid balances;
for the purposes of this example, we’ll work in a custom prepaid credits currency called message_credits
.
Upon creating a customer via the create customer API endpoint:
- Create a balance depleted and balance recovered alert via the API.
Ensure that you’re creating these alerts in the correct currency, passing the
identifier
of the currency (in this casemessage_credits
). For these alert types, thethresholds
body parameter is not required, since the threshold is implicitly set to 0 and these alerts fire on either side of that threshold. As long as these alert configurations are created, you won’t need to store the configuration ID in your data model. - Create a subscription for the customer. Note that subscriptions in Orb do not have to be explicitly marked as prepaid, and
the customer does not have to be specially configured to draw down from the prepaid balance. Instead, any price that’s part of the
subscription that is in the
message_credits
currency will automatically draw down from themessage_credits
prepaid balance. You can mix and match price currencies in a subscription, and Orb will automatically draw down from the appropriate balance. - In your data model, create a nullable timestamp field called
messages_blocked_until
. When this field is a non-null timestamp in the future, the customer is blocked from sending messages. When this field isnull
, the customer is not blocked from sending messages.
Managing new credit purchases
In order to support credit purchasing through your dashboard, your server-side logic should increment credits using the create ledger entry endpoint, passing in the following parameters:
- Set the currency to message_
credits
. entry_type
=increment
signifies an incrementamount
represents the additional credits being bought.per_unit_cost_basis
represents the cost of each token in your invoicing currency, USD. Set this to $0.01, since each credit costs $0.01.expiry_date
represents when themessage_credit
will automatically be depleted. Set this to one year from the current grant date.- To ensure credits are billed immediately and granted conditional on payment success, set
invoice_settings.auto_collection
toTrue
,invoice_settings.require_successful_payment
to True, andinvoice_settings.net_terms
to 0.
Sending messages
In your application logic, you need to ensure that customers are only able to send messages if they have enough credits to do so by interpreting
the messages_blocked_until
field. If this field is null
or a future timestamp, your application should allow the customer to send messages.
Use the following logic to determine eligibility based on the value of messages_blocked_until
:
- If the field is
null
, your application should allow the customer to send messages. This is the case where the customer has a positive credit balance. - If this field is
MAX_DATE
, your application should block the customer from sending messages. This is the case where the customer has no known credit blocks that may become effective. - If the field is a future timestamp, your application should disallow the customer from sending messages. This is the case where the customer has no positive credit balance, but there is a credit block that will become effective in the future.
- If the field is a non-0 timestamp in the past, your application should re-query the customer’s current balance using the current credit balance
and make a determination based on the new value of
messages_blocked_until
. This is the case where the customer had no positive credit balance, but a credit block may have become effective so a re-query is required.
Note that this check must be performed on every message send, since the customer may have purchased more credits in between messages, the customer may have exhausted their credits, or the passage of time may have caused a credit block to expire or become effective.
Blocking product access on depletion
Orb guarantees the delivery of a balance depleted alert
when there are 0 remaining message_credit
s. When testing, parse this webhook and confirm that properties.pricing_unit.name
matches message_credit
.
Simply listening to this webhook is insufficient. This is because webhook systems do not provide ordering guarantees, and you need to ensure that the customer is only blocked from sending the message under the appropriate conditions.
For example, suppose that a customer has a balance of 10 message credits, sends 10 messages and immediately purchases a new block in quick succession. In this case, the balance depleted alert may be delivered via webhook after the block has been purchased, and the customer will be erroneously blocked from sending messages.
Instead, you should always re-query the customer’s current credit balance using the API when you receive a balance depleted webhook.
This API is never cached, and will always return the most up to date balance per the usage events that Orb has ingested; it will also reflect
any other operations that have been performed on the balance, such as the purchase of new credits or the expiration of existing credits. For this request,
be sure to set include_all_blocks=True
to only query for active blocks at the current point in time.
In order to set messages_blocked_until
, use the following logic:
- If the balance is greater than 0, set
messages_blocked_until
tonull
. - If the balance is 0, set
messages_blocked_until
to the earliest futureeffective_date
of any of the customer’s credit blocks. If the customer has no credit blocks effective in the future, setmessages_blocked_until
toMAX_DATE
.
Unblocking product access
If your customer purchases more credits through your product dashboard, continue using the create ledger entry endpoint as
detailed above. It’s also important to ensure that new credits are set to be effective as of the beginning of the current subscription
billing period. You can accomplish this by setting the effective_date
of the entry to match the current_billing_period_start_date
of the
subscription (you’ll need to make sure that you have the latest subscription response).
This amounts to ‘backdating’ the credits to the beginning of the billing period, and ensures that your customer never incurs any overages
for the current billing period. Specifically, if there are seconds or minutes of lag between the consumption of credits and your system consuming
the webhook, your customer may temporarily accrue a small overage charge until product access is blocked (and more credits are bought). When the customer
purchases more credits, the above effective_date
strategy will ensure no charges remain on the current period invoice.
After creating the block, query again for the customer’s current balance using the current credit balance
endpoint and use the same exact logic detailed above, setting messages_blocked_until
to null
, MAX_DATE
, or a different future effective date.
Handling balance recovered webhooks
Orb will send a balance recovered webhook when a customer’s credit balance is no longer at 0. This may happen when a customer purchases more credits, and handles the case where this action happened outside of the context above (e.g. through a manual intervention by a customer service representative). This will also handle cases like a subscription change where a user upgrades to a subscription that includes a recurring credit allocation, because Orb will automatically create a new credit block and send a “balance recovered” webhook. Similar to the balance depleted webhook, this webhook will not provide ordering guarantees so it’s important to not solely depend on this webhook to unblock product access.
When you receive this webhook, you should re-query the customer’s current balance using the current credit balance.
Follow the same logic as above to determine the value of messages_blocked_until
.
Automated credit allocations
Orb supports the concept of a recurring allocation, which is the concept of a credit block that is automatically granted by the subscription on a recurring basis. This is useful for companies that want to provide their customers with a certain amount of credits each month as part of their subscription.
When a customer has an active allocation, future credit blocks are created automatically by the system, and Orb guarantees that the next block will exist
while the current one is active. Because of this, the above strategy of setting messages_blocked_until
to the earliest future effective date
will ensure that future allocations are accounted for in our message sending strategy.
Credit expirations
In Orb, each credit block can expire at any time after it is effective. When a credit block expires, you will receive a balance depleted alert.
As per the section above, you should re-query the customer’s current balance using the current credit balance
API and update messages_blocked_until
accordingly. If the customer has no remaining blocks after the expiration, messages_blocked_until
will be set to MAX_DATE
.
Overages due to eventual consistency
Because Orb’s event ingestion system is designed to be eventually consistent and it’s not feasible to wait for all events to be ingested before sending a message,
it’s still possible that a customer may send a very small number of messages before they’re blocked after they’ve exhausted all their message credits. We recommend
setting your conversion_rate
to $0.00 for the message_credits
currency, since this will ensure that the customer is not charged for any overages that may occur.
In the vast majority of cases, this is an acceptable tradeoff but reach out to Orb if you want to discuss how faster SLAs may bound the potential overage costs.
Was this page helpful?