I have a usage-based Stripe product for which an invoice is generated at the end of the billing period (month). I am trying to use a Test Clock to verify that the usage is recorded on the invoice.
I am adding the usage to the invoice while it is in draft mode by listening for the invoice.created
webhook, calculating the usage, and reporting it to the meter with a timestamp within the billing period for the invoice (see the docs). However, the Invoice in the Subscription never sees the usage I submit.
Restated in more detail, I follow these steps in Test Mode:
- Create a Test Clock
- Create a Customer in the Test Clock
- Create a monthly Subscription in the Test Clock for the newly created Customer which starts at the current time
- Advance the Test Clock one month, which triggers the
invoice.created
event on the webhook. - My code in the webhook runs to record the usage:
const invoice = event.data.object;
const {
id,
customer: stripeCustomerId,
customer_email: email,
period_start: invoicePeriodStart,
period_end: invoicePeriodEnd,
subscription: stripeSubscriptionId,
status,
} = invoice;
try {
// A finalized invoice is created when the user first signs up for the usage-based subscription; we want to ignore this one
if (status !== 'draft') return;
// Manually created invoices do not have a subscription and are not of interest for our purposes here
if (!stripeSubscriptionId) return;
// Only report usage for invoices that are for the usage-based subscription
const subscription = await stripe.subscriptions.retrieve(stripeSubscriptionId);
if (subscription.items.data[0].price.lookup_key !== myLookupKey) {
console.log(
`Subscription lookup key ${subscription.items.data[0].price.lookup_key} !== ${myLookupKey}`,
);
return;
}
const user = await users.findByEmail(email);
// Call another server to get the usage for the previous period
const organization = useranization;
const startDate = new Date(invoicePeriodStart * 1000);
const endDate = new Date(invoicePeriodEnd * 1000);
const usageString = await crossServerPostRequest(
new URL(`<URL FOR GETTING USAGE FOR PERIOD>`),
{
organization,
startDate,
endDate,
},
);
await stripe.billing.meterEvents.create({
event_name: 'event_name',
identifier: crypto.randomUUID(),
payload: {
value: parseInt(usageString, 10),
stripe_customer_id: stripeCustomerId,
},
timestamp: invoicePeriodEnd, invoicePeriodStart + (invoicePeriodEnd - invoicePeriodStart) / 2, // I've tried a few different values here: invoicePeriodStart, invoicePeriodEnd
});
postSlackMessage(
`Usage reported to Stripe for ${useranization}: ${usageString} (${startDate.toDateString()} - ${endDate.toDateString()})`,
);
} catch (error) {
console.error(error);
postSlackMessage(
`Error during invoice.created Stripe webhook for ${id} for ${request.body.email}; usage may not have been reported\n${error}`,
);
}
- I observe the event on the Stripe Dashboard for the Meter and it is associated with my Test Clock customer
- I advance the Test Clock another hour so that the invoice will finalize
- I observe the finalized invoice and it does not include the usage
Am I doing something wrong? Do Test Clocks not work with Meters like this? Nothing is noted about Meters in the Stripe docs Test Clock Limitations.
I also tried advancing the test clock to the middle of the period, putting in a Meter Event manually on the Stripe Dashboard for the Test Clock customer, and advancing the test clock to the end and I did not see the usage in that instance either.
I got this to work a few times with the Meter Event timestamp set to invoicePeriodStart
, but not consistently. I know Meter events are processed asynchronously - maybe this is causing inconsistent results? I want to be confident my process will work in live mode.