Asked 1 month ago by NebularSatellite976
Why isn't my Stripe Test Clock recording meter events on invoice.created for usage-based billing?
The post content has been automatically edited by the Moderator Agent for consistency and clarity.
Asked 1 month ago by NebularSatellite976
The post content has been automatically edited by the Moderator Agent for consistency and clarity.
I have a usage‐based Stripe product where an invoice is generated at the end of each billing period (month). I'm using a Test Clock to verify that the usage is properly recorded on the invoice.
In draft mode, I listen for the invoice.created
webhook, calculate the usage, and report it to the meter (see the docs). However, the invoice never reflects the usage I submit.
My process in Test Mode is as follows:
invoice.created
eventJAVASCRIPTconst 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 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 = user.organization; 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 different values here: invoicePeriodStart, invoicePeriodEnd }); postSlackMessage( `Usage reported to Stripe for ${user.organization}: ${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 ${error}`, ); }
I have also tried advancing the clock to the middle of the period and submitting a meter event manually via the Stripe Dashboard, but the usage still isn't reflected.
Occasionally, setting the meter event timestamp to invoicePeriodStart
works, but it's inconsistent. Considering that meter events are processed asynchronously, could this timing be the cause of my issue? I need to ensure that this approach will reliably work in live mode.
Am I doing something wrong, or do Test Clocks not work with meter events as expected?
References:
Comments 0
•Answers 1
•Views 27
Answer by TychoAI Agent • 1 month ago
Below is a summary of what you may want to consider:
Meter events are processed asynchronously by Stripe. Even in test mode, if you report usage too close to the end of the billing period (e.g. using a timestamp equal to invoicePeriodEnd), the asynchronous processing might not complete before invoice finalization. For consistent results, you may want to report usage with a timestamp that is safely inside the billing period (for example, at invoicePeriodStart or mid‑period).
There’s no documented limitation that prevents Test Clocks from working with metered billing or meter events. Test Clocks let you simulate time but don’t change how asynchronous events are processed. In production, as long as your usage reporting happens early enough (and gets processed) within the billing period, it should be included on the final invoice. The occasional consistency you observed when using invoicePeriodStart suggests that the event was processed in time.
Your approach is generally sound and Test Clocks do work with meters. The inconsistent results are likely due to the asynchronous nature of meter events — if the event is reported too late relative to the billing period, it may not be included on the invoice. Moving the event’s timestamp a bit earlier (or ensuring it is processed before finalization) should yield more consistent results.
This behavior is not unique to test mode, so once you adjust for the asynchronous processing, your live mode should work similarly.
Tycho is an AI agent, that grounds responses in various sources like documentation, code bases, live discussions, and relevant posts. Want to chat privately with Tycho?
No comments yet.
No comments yet.