Documentation Index Fetch the complete documentation index at: https://docs.launchmystore.io/llms.txt
Use this file to discover all available pages before exploring further.
Usage-Based Billing
Usage-based billing charges merchants based on their actual usage of your app, such as API calls, orders processed, emails sent, or storage used. This model aligns your revenue with the value you provide.
How It Works
Stripe Setup
1. Create Metered Price
Create a metered price in Stripe:
import Stripe from 'stripe' ;
const stripe = new Stripe ( process . env . STRIPE_SECRET_KEY );
async function createMeteredPrice () {
// Create product
const product = await stripe . products . create ({
name: 'Orders Processed'
});
// Create metered price
const price = await stripe . prices . create ({
product: product . id ,
currency: 'usd' ,
recurring: {
interval: 'month' ,
usage_type: 'metered'
},
billing_scheme: 'tiered' ,
tiers_mode: 'graduated' , // Each tier applies to portion of usage
tiers: [
{
up_to: 100 ,
unit_amount: 0 // First 100 free
},
{
up_to: 1000 ,
unit_amount: 10 // $0.10 per order
},
{
up_to: 10000 ,
unit_amount: 5 // $0.05 per order
},
{
up_to: 'inf' ,
unit_amount: 2 // $0.02 per order
}
]
});
return price ;
}
2. Create Subscription with Metered Item
async function createMeteredSubscription ( customerId , priceId ) {
const subscription = await stripe . subscriptions . create ({
customer: customerId ,
items: [
{
price: priceId
}
],
// Optionally add a base fee
add_invoice_items: [
{
price: 'price_base_fee' // $9.99 platform fee
}
]
});
// Save the subscription item ID for usage reporting
const subscriptionItemId = subscription . items . data [ 0 ]. id ;
return { subscription , subscriptionItemId };
}
Recording Usage
Basic Usage Reporting
Report usage to Stripe:
async function reportUsage ( subscriptionItemId , quantity , timestamp = null ) {
const usageRecord = await stripe . subscriptionItems . createUsageRecord (
subscriptionItemId ,
{
quantity ,
timestamp: timestamp || Math . floor ( Date . now () / 1000 ),
action: 'increment' // Add to existing usage
}
);
return usageRecord ;
}
Usage Events Service
Create a service to handle usage recording:
// services/usageService.js
import Stripe from 'stripe' ;
import { Redis } from 'ioredis' ;
const stripe = new Stripe ( process . env . STRIPE_SECRET_KEY );
const redis = new Redis ( process . env . REDIS_URL );
class UsageService {
constructor () {
this . batchSize = 100 ;
this . flushInterval = 60000 ; // 1 minute
}
/**
* Record a usage event
*/
async recordUsage ( shopId , metric , quantity = 1 , metadata = {}) {
const event = {
shopId ,
metric ,
quantity ,
timestamp: Date . now (),
metadata
};
// Buffer in Redis
await redis . lpush ( `usage:buffer: ${ shopId } : ${ metric } ` , JSON . stringify ( event ));
// Check if we should flush
const bufferSize = await redis . llen ( `usage:buffer: ${ shopId } : ${ metric } ` );
if ( bufferSize >= this . batchSize ) {
await this . flushUsage ( shopId , metric );
}
}
/**
* Flush buffered usage to Stripe
*/
async flushUsage ( shopId , metric ) {
const key = `usage:buffer: ${ shopId } : ${ metric } ` ;
// Get and clear buffer atomically
const events = await redis . lrange ( key , 0 , - 1 );
await redis . del ( key );
if ( events . length === 0 ) return ;
// Calculate total quantity
const totalQuantity = events . reduce (( sum , event ) => {
const parsed = JSON . parse ( event );
return sum + parsed . quantity ;
}, 0 );
// Get subscription item ID
const subscriptionItemId = await this . getSubscriptionItemId ( shopId , metric );
if ( subscriptionItemId ) {
await stripe . subscriptionItems . createUsageRecord (
subscriptionItemId ,
{
quantity: totalQuantity ,
timestamp: Math . floor ( Date . now () / 1000 ),
action: 'increment'
}
);
}
// Store for analytics
await this . storeUsageAnalytics ( shopId , metric , totalQuantity , events );
}
/**
* Get current period usage
*/
async getCurrentUsage ( shopId , metric ) {
const subscriptionItemId = await this . getSubscriptionItemId ( shopId , metric );
if ( ! subscriptionItemId ) return 0 ;
const usageSummary = await stripe . subscriptionItems . listUsageRecordSummaries (
subscriptionItemId ,
{ limit: 1 }
);
return usageSummary . data [ 0 ]?. total_usage || 0 ;
}
async getSubscriptionItemId ( shopId , metric ) {
return redis . get ( `subscription_item: ${ shopId } : ${ metric } ` );
}
}
export const usageService = new UsageService ();
Usage Recording Middleware
Automatically record usage for API calls:
// middleware/usageTracking.js
import { usageService } from '../services/usageService' ;
export function trackUsage ( metric ) {
return async ( req , res , next ) => {
// Store original end function
const originalEnd = res . end ;
res . end = function ( ... args ) {
// Only track successful requests
if ( res . statusCode >= 200 && res . statusCode < 300 ) {
usageService . recordUsage ( req . shopId , metric , 1 , {
endpoint: req . path ,
method: req . method
}). catch ( console . error );
}
return originalEnd . apply ( this , args );
};
next ();
};
}
// Usage in routes
app . post ( '/api/orders/process' , trackUsage ( 'orders' ), processOrder );
app . post ( '/api/emails/send' , trackUsage ( 'emails' ), sendEmail );
Usage Dashboard
Show merchants their current usage:
import { useState , useEffect } from 'react' ;
function UsageDashboard ({ shopId }) {
const [ usage , setUsage ] = useState ( null );
const [ loading , setLoading ] = useState ( true );
useEffect (() => {
fetchUsage ();
}, []);
const fetchUsage = async () => {
const response = await fetch ( `/api/billing/usage?shopId= ${ shopId } ` );
const data = await response . json ();
setUsage ( data );
setLoading ( false );
};
if ( loading ) return < div > Loading... </ div > ;
return (
< div className = "usage-dashboard" >
< h2 > Current Billing Period </ h2 >
< div className = "usage-metrics" >
{ usage . metrics . map ( metric => (
< div key = { metric . name } className = "metric-card" >
< h3 > { metric . name } </ h3 >
< div className = "metric-value" >
{ metric . current . toLocaleString () } / { metric . included . toLocaleString () }
</ div >
< div className = "progress-bar" >
< div
className = "progress"
style = { { width: ` ${ Math . min ( 100 , ( metric . current / metric . included ) * 100 ) } %` } }
/>
</ div >
< div className = "metric-details" >
< span > Included: { metric . included . toLocaleString () } </ span >
{ metric . current > metric . included && (
< span className = "overage" >
Overage: { ( metric . current - metric . included ). toLocaleString () }
( { formatCurrency ( metric . overageCost ) } )
</ span >
) }
</ div >
</ div >
)) }
</ div >
< div className = "estimated-bill" >
< h3 > Estimated Bill </ h3 >
< div className = "bill-breakdown" >
< div className = "line-item" >
< span > Base Fee </ span >
< span > { formatCurrency ( usage . baseFee ) } </ span >
</ div >
{ usage . metrics . map ( metric => (
metric . overageCost > 0 && (
< div key = { metric . name } className = "line-item" >
< span > { metric . name } Overage </ span >
< span > { formatCurrency ( metric . overageCost ) } </ span >
</ div >
)
)) }
< div className = "line-item total" >
< span > Estimated Total </ span >
< span > { formatCurrency ( usage . estimatedTotal ) } </ span >
</ div >
</ div >
</ div >
</ div >
);
}
Usage Alerts
Notify merchants when approaching limits:
// services/usageAlerts.js
async function checkUsageAlerts ( shopId ) {
const subscription = await getShopSubscription ( shopId );
const usage = await getCurrentUsage ( shopId );
for ( const metric of usage . metrics ) {
const percentage = ( metric . current / metric . included ) * 100 ;
// 80% warning
if ( percentage >= 80 && percentage < 100 ) {
const alreadyNotified = await redis . get (
`usage_alert: ${ shopId } : ${ metric . name } :80`
);
if ( ! alreadyNotified ) {
await sendUsageAlert ( shopId , metric , 80 );
await redis . set (
`usage_alert: ${ shopId } : ${ metric . name } :80` ,
'1' ,
'EX' ,
86400 * 7 // Reset after 7 days
);
}
}
// 100% notification
if ( percentage >= 100 ) {
const alreadyNotified = await redis . get (
`usage_alert: ${ shopId } : ${ metric . name } :100`
);
if ( ! alreadyNotified ) {
await sendUsageAlert ( shopId , metric , 100 );
await redis . set (
`usage_alert: ${ shopId } : ${ metric . name } :100` ,
'1' ,
'EX' ,
86400 * 7
);
}
}
}
}
async function sendUsageAlert ( shopId , metric , threshold ) {
const shop = await getShop ( shopId );
await sendEmail ( shop . email , 'usage_alert' , {
shopName: shop . name ,
metricName: metric . name ,
currentUsage: metric . current ,
includedUsage: metric . included ,
threshold ,
overageRate: formatCurrency ( metric . overageRate )
});
}
Rate Limiting
Optionally limit usage to prevent unexpected bills:
// middleware/usageLimiter.js
import { usageService } from '../services/usageService' ;
export function limitUsage ( metric , hardLimit ) {
return async ( req , res , next ) => {
const shopId = req . shopId ;
const currentUsage = await usageService . getCurrentUsage ( shopId , metric );
if ( currentUsage >= hardLimit ) {
return res . status ( 429 ). json ({
error: 'Usage limit exceeded' ,
message: `You've reached your ${ metric } limit. Please upgrade your plan.` ,
currentUsage ,
limit: hardLimit
});
}
next ();
};
}
// Usage
app . post (
'/api/orders/process' ,
limitUsage ( 'orders' , 10000 ),
trackUsage ( 'orders' ),
processOrder
);
Billing Period End
Handle end-of-period usage finalization:
// Cron job: Run at end of each billing period
async function finalizeBillingPeriod ( shopId ) {
// Flush all remaining usage
await usageService . flushAllUsage ( shopId );
// Get final usage summary
const usage = await getUsageSummary ( shopId );
// Store for historical records
await storeUsageHistory ( shopId , usage );
// Reset alert flags
await resetUsageAlerts ( shopId );
}
Pricing Tiers
Implement graduated pricing:
function calculateUsageCost ( usage , tiers ) {
let cost = 0 ;
let remaining = usage ;
for ( let i = 0 ; i < tiers . length ; i ++ ) {
const tier = tiers [ i ];
const prevLimit = i > 0 ? tiers [ i - 1 ]. upTo : 0 ;
const tierLimit = tier . upTo === 'inf' ? Infinity : tier . upTo ;
const tierQuantity = Math . min ( remaining , tierLimit - prevLimit );
if ( tierQuantity > 0 ) {
cost += tierQuantity * tier . unitAmount ;
remaining -= tierQuantity ;
}
if ( remaining <= 0 ) break ;
}
return cost ;
}
// Example
const tiers = [
{ upTo: 100 , unitAmount: 0 },
{ upTo: 1000 , unitAmount: 10 },
{ upTo: 10000 , unitAmount: 5 },
{ upTo: 'inf' , unitAmount: 2 }
];
calculateUsageCost ( 5000 , tiers );
// = (100 * 0) + (900 * 10) + (4000 * 5) = $290.00 (29000 cents)
Best Practices
Don’t report every event individually. Buffer and batch to reduce API calls.
Merchants want to see their current usage. Provide a dashboard or API endpoint.
Notify merchants at 80% and 100% of included usage to avoid bill shock.
Let merchants set spending limits or hard usage caps for peace of mind.
A free tier reduces friction and lets merchants see value before paying.
Use graduated tiers with decreasing per-unit costs as usage grows.
Common Metrics
Metric Use Case ordersOrder management apps emailsEmail marketing apps api_callsAPI-heavy integrations storage_gbStorage apps products_syncedMarketplace integrations sms_sentSMS notification apps shipmentsShipping apps
See Also