Stripe usage-based billing with Billing Meters works in four parts. You create a meter that defines how usage is aggregated (sum, count or last value). Your application sends meter events naming that meter, a customer and a value. A recurring price with `usage_type: "metered"` points at the meter and turns the period's total into an amount. The customer's subscription then invoices that amount in arrears at the end of each billing period.
This guide explains each part as Stripe documents it in October 2026: how meters aggregate, how metered prices use per-unit, package and tiered pricing, how to report meter events so a retry never bills twice, when the high-throughput meter event stream is worth it, how to correct late or wrong usage, what appears on invoices and previews, how billing credits apply, how to test with test clocks and how to move off legacy usage records. It does not repeat our guides on subscription webhooks, billing cycle anchors and prorations or entitlements; it links to them where they matter.
How does Stripe usage-based billing work?
Stripe usage-based billing records what each customer consumed during a billing period and charges for it afterwards. The meter does the counting, the metered price does the pricing and the subscription does the invoicing; your application's only job is to report accurate usage on time.
The objects map cleanly onto a pay-as-you-go product. A meter (`billing.meter`) is the definition of one usage dimension, such as API calls, tokens processed or gigabytes stored. A meter event (`billing.meter_event`) is one report of usage: an event name that matches a meter, a payload with the customer and the numeric value, an optional timestamp and an identifier. A metered price is a recurring Price whose `recurring.meter` names the meter and whose `recurring.usage_type` is `metered`. A subscription item on that price is what ties a customer's usage to an invoice.
Two properties surprise teams who come from the older usage-records model. First, a meter is not tied to a customer or a subscription. One meter tracks usage across all your customers, and Stripe's migration guide notes that you can collect usage before a customer has a subscription at all. Second, Stripe processes meter events asynchronously. The API accepts an event quickly, but aggregated totals on meter event summaries and upcoming invoices may lag, and validation errors arrive later as events rather than as a failed request. Both properties shape the reporting design described below.
Billing Meters or Metronome: which does Stripe recommend now?
This matters before you write code. Stripe's current pay-as-you-go implementation guide marks the Billing Meters approach as not recommended for new integrations and points new usage-based work to Metronome, which it describes as its primary usage-based billing platform. The same page says that existing Billing Meters integrations do not need to migrate, and Stripe's comparison of basic usage-based billing and Metronome states that Stripe continues to fully support basic usage-based billing for existing integrations.
Stripe's comparison lists the differences. Metronome adds real-time usage visibility, prepaid credits with burndown, enterprise contracts with commits and ramps, dimensional pricing and high-volume ingestion. Billing Meters, in turn, works with parts of Stripe where the comparison shows Metronome as unsupported or limited, including Connect, Adaptive Pricing, Workflows and the Stripe Dashboard, with Checkout marked limited for Metronome. So the honest decision rule is: if you already bill with meters, or you need Connect or Checkout on the same subscriptions and your pricing is straightforward, this guide applies to you. If you are starting fresh and need contracts, prepaid drawdown or live usage dashboards, read Stripe's Metronome documentation first. The rest of this article covers Billing Meters.
How do you create a Stripe billing meter?
Create a meter with a display name, an event name and a default aggregation formula, and optionally override which payload keys hold the customer and the value. Choose carefully: once a meter is configured, Stripe lets you change only its display name.
Stripe's meter configuration guide describes each attribute. These are the decisions that are hard to undo:
- Event name. The string your meter events must carry in `event_name`, up to 100 characters. An event name can belong to only one meter, so name it after the usage dimension (`api_calls`, `tokens_processed`) rather than after a plan or a customer segment.
- Aggregation formula. `sum` bills the total of all values in the period, `count` bills the number of events regardless of value, and `last` bills the most recent value reported in the period. Use `sum` for consumption, `count` when each event is exactly one billable action, and `last` for a level that you sample, such as storage in use or active projects at period end.
- Customer mapping. By default Stripe reads the customer ID from the payload key `stripe_customer_id`, with `customer_mapping.type` set to `by_id`. You can name a different key, but the value must still be the Stripe customer ID, not your own user ID.
- Value settings. By default Stripe reads the numeric value from the payload key `value`. Values may be decimals, and Stripe supports decimal quantities on invoices.
- Event ingestion. The default, raw, treats every event as standalone: two events with the same timestamp both count. If you set `event_time_window` to `hour` or `day`, the meter treats your events as pre-aggregated, and a newer event in the same UTC hour or day overwrites the earlier one instead of adding to it.
The pre-aggregated mode deserves a warning. It is useful when you already compute hourly or daily totals in a warehouse and want the latest number to win. It is dangerous if any part of your system still sends increments, because each increment silently replaces the previous one. Pick one model per meter and write it into the code that reports to it.
One meter per usage dimension is usually the right granularity. If you bill API calls and storage, that is two meters, two metered prices and two subscription items. A meter per plan is usually a pricing concern that belongs on the price instead.
How do metered prices work: per unit, package and tiers?
A metered price is an ordinary recurring Price with `recurring.usage_type` set to `metered` and `recurring.meter` set to the meter's ID. It can charge per unit, per package of units using `transform_quantity`, or by tiers in graduated or volume mode, optionally with a flat amount per tier.
Per-unit pricing multiplies the aggregated quantity by `unit_amount`, or by `unit_amount_decimal` when you need fractions of a cent. Package pricing divides the quantity first. Stripe's own example charges $0.04 per 100 tokens with `transform_quantity[divide_by]=100` and `transform_quantity[round]=up`, so 250 tokens bill as three packages. Choose `round` deliberately: `up` charges for a partly used package, `down` gives the remainder away.
Tiered pricing uses `billing_scheme: "tiered"` and a `tiers` array in which each tier has an `up_to` boundary (the last one is `inf`) and a `unit_amount`, a `flat_amount` or both. Stripe's tiered pricing documentation defines the two modes. In graduated mode, each band of usage is charged at its own tier's rate and the bands are summed. In volume mode, the whole quantity is charged at the rate of the tier it ends in, so the total can drop when a customer crosses into a cheaper tier.
Two tier behaviors catch people. First, flat amounts: Stripe documents that when the quantity is 0, it still bills the first tier's flat amount. If you want a customer with no usage to owe nothing, Stripe suggests an `up_to: 1` first tier with a `unit_amount` equal to the flat fee and no `flat_amount`. Second, volume tiers make the invoice non-monotonic. A customer at 5,000 units in the illustration above would pay $200 in volume mode, while one at 5,001 units would pay $150.03. That is a pricing decision, not a bug, but support teams should know it before the first customer asks.
Many products combine a fixed fee with usage. In Stripe terms that is simply two subscription items: a licensed recurring price for the base fee and a metered price for usage, or a metered price whose first tier is a free allowance. Keep the allowance on the price, not in your reporting code. If your code subtracts an included quota before sending meter events, you lose the raw usage record and every pricing change becomes a code change. Proration also differs between the two: as our billing cycle anchor guide explains, usage-based prices are not prorated on plan changes; they bill for what was used.
How do you report Stripe meter events without double billing?
Send every meter event with a stable `identifier` derived from your own usage record, a timestamp within Stripe's accepted window and the customer's Stripe ID, from a durable queue rather than the request path. Stripe enforces identifier uniqueness for a rolling period of at least 24 hours, which makes retries safe within that window.
The meter event API reference sets the rules. `event_name` must match a meter. `payload` must contain the customer key and the value key the meter expects. `identifier` is optional, up to 100 characters; if you omit it Stripe generates one, and Stripe recommends UUID-like identifiers. `timestamp` is in Unix seconds and must be within the past 35 calendar days and no more than 5 minutes in the future; it defaults to the time of the request.
Omitting the identifier is the most common mistake in usage reporting. A network timeout leaves you not knowing whether Stripe recorded the event. If Stripe generated the identifier, your retry is a brand-new event and the customer pays twice. If you derived the identifier from your own row ID, the retry collides with the original and Stripe discards the duplicate. Stripe describes the uniqueness check as aimed at accidental retries in short intervals, so do not treat it as permanent deduplication; your own outbox should still mark rows as sent.
Stripe's guide to recording usage with the API also documents the throughput limits that decide your architecture:
- In live mode, the meter event endpoint has its own limit of 1,000 calls per second per Stripe account, separate from your global API rate limit.
- Meter events are also limited to one concurrent call per customer per meter. A worker pool that fires parallel requests for the same busy customer will hit this.
- In a sandbox, meter event calls count towards your global rate limit, so load tests in a sandbox do not tell you how live mode will behave.
- Connect platforms that send meter events on behalf of a connected account with the `Stripe-Account` header are subject to the regular rate limits instead.
- If a meter uses dimensions, Stripe caps the number of unique dimension combinations it accepts per meter per hour and per customer, and events over the cap are rejected asynchronously.
Near these limits, Stripe suggests pre-aggregating (for example one event per customer per minute instead of one per API call) or moving to meter event streams. Pre-aggregation is usually the better first step. Either way, back off exponentially on HTTP 429 responses.
A design that holds up in production looks like this. Your application writes a usage row in the same database transaction as the work it measures. A reporter process reads unsent rows, groups them per customer and minute if volume requires it, sends each group with an identifier built from the group's key, and marks the rows sent only after Stripe responds. Rows for one customer go through one worker at a time, which respects the per-customer concurrency limit naturally. This is the same outbox discipline we describe for webhook handlers in our idempotency guide, applied in the other direction.
import Stripe from "stripe";
const stripe = new Stripe(process.env.STRIPE_SECRET_KEY);
// One-off setup, run from a script or migration, never per request.
// A meter cannot be changed after it is configured, except its display name.
export async function createApiCallsMeterAndPrice(productId) {
const meter = await stripe.billing.meters.create({
display_name: "API calls",
event_name: "api_calls", // one event name per meter
default_aggregation: { formula: "sum" }, // sum | count | last
customer_mapping: { type: "by_id", event_payload_key: "stripe_customer_id" },
value_settings: { event_payload_key: "value" },
});
// Graduated tiers, in cents. Illustrative numbers.
const price = await stripe.prices.create({
product: productId,
currency: "usd",
billing_scheme: "tiered",
tiers_mode: "graduated",
tiers: [
{ up_to: 1000, unit_amount_decimal: "5" },
{ up_to: 5000, unit_amount_decimal: "4" },
{ up_to: "inf", unit_amount_decimal: "3" },
],
recurring: { interval: "month", usage_type: "metered", meter: meter.id },
});
return { meterId: meter.id, priceId: price.id };
}
// Report usage from a durable outbox row, not from the request path.
// row.id is stable across retries, so Stripe can drop duplicates.
export async function reportUsage(row) {
return stripe.billing.meterEvents.create({
event_name: "api_calls",
identifier: "usage_" + row.id, // max 100 characters
timestamp: Math.floor(row.occurredAt.getTime() / 1000), // past 35 days, at most 5 minutes ahead
payload: {
stripe_customer_id: row.stripeCustomerId,
value: String(row.quantity),
},
});
}The timestamp should be when the usage happened, not when the reporter ran. That keeps usage in the correct billing period when the reporter falls behind near a period boundary. It also means a reporter that stops for more than 35 days can no longer deliver its backlog at all, so alert on reporter lag long before that.
Listening for meter event errors
Because Stripe validates meter events asynchronously, a 200 response does not prove the usage will bill. Stripe reports problems with two thin events: `v1.billing.meter.error_report_triggered` when a meter receives invalid events, and `v1.billing.meter.no_meter_found` when events name a meter that does not exist. Error codes include `meter_event_customer_not_found`, `meter_event_no_customer_defined`, `timestamp_too_far_in_past`, `timestamp_in_future`, `meter_event_value_not_found`, `meter_event_invalid_value`, `archived_meter` and `no_meter`. Subscribe an event destination to both with the thin payload style, alert a person and keep enough data in your outbox to correct and resend the affected rows. Stripe's documentation lists that last step explicitly.
When do you need high-volume meter event streams?
Use the API v2 meter event stream when pre-aggregation cannot keep you under 1,000 meter event calls per second. Stripe documents up to 10,000 events per second in live mode through streams, and asks you to contact sales for up to 200,000.
The stream uses stateless authentication. You first create a meter event session with your secret key; the session returns an authentication token that is valid for 15 minutes. You then call the meter event stream endpoint with that token, sending up to 100 events per request. The response is empty because validation happens later, and an expired token returns HTTP 401 with the code `billing_meter_event_session_expired`. Two operational details matter: the stream works in live mode only, so you cannot exercise it end to end in a sandbox, and Stripe does not show stream requests in the Workbench request logs because of their volume. Your own logging is the only request-level record.
import Stripe from "stripe";
const stripe = new Stripe(process.env.STRIPE_SECRET_KEY);
let session = null;
// Meter event sessions return a token that is valid for 15 minutes.
// Refresh a little early so a batch never starts on an expiring token.
async function streamClient() {
const refreshBefore = Date.now() + 60_000;
if (!session || new Date(session.expires_at).getTime() <= refreshBefore) {
session = await stripe.v2.billing.meterEventSession.create();
}
return new Stripe(session.authentication_token);
}
// Live mode only. Up to 100 events per request; validation is asynchronous,
// so bad events surface later as v1.billing.meter.error_report_triggered.
export async function sendBatch(rows) {
const client = await streamClient();
await client.v2.billing.meterEventStream.create({
events: rows.map((row) => ({
identifier: "usage_" + row.id,
event_name: "api_calls",
timestamp: row.occurredAt.toISOString(),
payload: {
stripe_customer_id: row.stripeCustomerId,
value: String(row.quantity),
},
})),
});
}Identifiers matter even more here. A batch of 100 events that times out must be resent as the same 100 identifiers, or you double-bill up to 100 events at once. Keep batch membership in your outbox so a retry rebuilds exactly the same batch. Note that the stream takes the timestamp as an ISO 8601 string, while the v1 endpoint takes Unix seconds; mixing them up is an easy way to produce `timestamp_too_far_in_past` errors.
How do you correct late or wrong usage in Stripe?
Within 24 hours of sending an event, cancel it with a meter event adjustment that names its identifier. After that, send an offsetting negative value before the invoice finalises. Once an invoice is finalised, Stripe does not update it or move the corrected usage to a later invoice.
The meter event adjustment API currently supports one type, `cancel`, which removes a single event by its identifier. Stripe's reference says that cancelling a range of events for a time period is not supported yet. Stripe's configuration guide adds the 24-hour window: you can only cancel events sent to Stripe within the last 24 hours. This is a second reason to always send your own identifier. If Stripe generated it, you need to have stored the identifier from the response, or you cannot cancel the event at all.
Outside that window, the documented option is to report a negative quantity. A sum meter adds it like any other value, so a mistaken event of 500 is neutralised by an event of -500 timestamped inside the same billing period. Stripe notes that if a period's total usage comes out negative, the invoice line shows a usage quantity of 0, so you cannot use negative events to issue a refund through the meter. This only works cleanly with `sum`; with `count` a negative event is still one more event, and with `last` it simply becomes the latest value.
The invoice finalisation grace period
Late usage is a related but separate problem: usage that happened in the last minutes of a period but reaches Stripe after the period ends. Stripe's finalisation grace period guide explains the mechanism. Every invoice has a default grace period of one hour between creation and finalisation, during which you can still report usage for the period that just ended. You can change it in the Dashboard's invoice settings, up to 72 hours, either as an account default or with rules such as "has a metered price". Stripe warns not to set it longer than the service period itself; a daily service period should not have a grace period of 24 hours or more.
The guide also lists limitations worth copying into your runbook:
- Only subscription cycle invoices and subscription schedule phase transition invoices that change metered items include usage reported during the grace period. Other invoices, including threshold invoices, reflect only usage accrued before they were created.
- If a subscription item's price changes mid-cycle, for example on an upgrade, usage reported during the grace period does not appear on the current or any later invoice. Stripe says this affects both classic and flexible billing modes.
- The first invoice of a subscription set to charge automatically finalises immediately, whatever rules you configure.
- Stripe may accept events after finalisation, within the 35-day timestamp window, and show them in meter summaries without adding them to the finalised invoice or to the next one. A summary total that is higher than the invoice is therefore not proof of a Stripe error.
What do metered invoices and invoice previews show?
A metered subscription item bills the period's aggregated usage at the end of the period, so the invoice for March usage is created when the March period closes. You can see the running figure at any time by creating an invoice preview for the subscription, keeping in mind that recent meter events may not be reflected yet.
The first invoice depends on the subscription's billing mode. Stripe's implementation guide says that with `billing_mode=flexible`, metered line items are left off the first invoice because there is no prior usage, and Stripe creates an invoice only if the subscription is backdated with accrued usage or pending invoice items exist. With `billing_mode=classic`, Stripe adds a zero-value line item for each metered subscription item. If your reporting or emails assume a first invoice always exists, check which mode your subscriptions use.
For previews, `stripe.invoices.createPreview({ subscription })` returns what the next invoice would contain now, including the metered line with its quantity. For raw usage, the meter event summaries endpoint returns a customer's aggregated value for a time range, optionally grouped by hour or day. Start and end times must sit on minute boundaries, and on hour or UTC-day boundaries when you group by hour or day. Together they give you a reconciliation loop: your ledger total, Stripe's aggregate and the amount Stripe would invoice.
import Stripe from "stripe";
const stripe = new Stripe(process.env.STRIPE_SECRET_KEY);
// Cancel one wrongly reported event. Only works for events sent to Stripe
// in the last 24 hours, and only by the identifier you sent.
export async function cancelUsage(row) {
return stripe.billing.meterEventAdjustments.create({
event_name: "api_calls",
type: "cancel",
cancel: { identifier: "usage_" + row.id },
});
}
// Compare your own ledger with Stripe's aggregate before the period closes.
// Times must sit on minute boundaries; hourly or daily windows need hour or
// UTC-day boundaries. Meter events are processed asynchronously, so allow
// for a short lag before treating a gap as real.
export async function compareWithStripe({ meterId, customerId, startTime, endTime, ourTotal, subscriptionId }) {
const summaries = await stripe.billing.meters.listEventSummaries(meterId, {
customer: customerId,
start_time: startTime,
end_time: endTime,
});
const stripeTotal = summaries.data.reduce((n, s) => n + s.aggregated_value, 0);
const preview = await stripe.invoices.createPreview({ subscription: subscriptionId });
return { ourTotal, stripeTotal, gap: ourTotal - stripeTotal, previewAmountDue: preview.amount_due };
}Run the comparison daily per active customer and once more inside the grace period. A persistent gap almost always traces to one of three causes: events rejected asynchronously (check the error events), events for the wrong customer ID, or a pre-aggregated meter overwriting increments. The webhook side of invoicing, `invoice.created`, `invoice.finalized`, `invoice.paid` and `invoice.payment_failed`, is the same as for any subscription and is covered in our subscription webhooks guide. If metered invoices fail to collect, the recovery path is the same too; see how Stripe retries failed subscription payments for subscription billing retries and dunning.
Billing credits on metered prices
Stripe's billing credits let you grant prepaid or promotional credit that is consumed by metered usage. A credit grant can apply only to subscription items on metered prices that report usage through meters; it does not apply to licensed prices, one-off invoices, one-time setup items or legacy usage-record prices. Credits apply after discounts and before taxes and the customer's invoice credit balance.
The details that affect integration design: credits are applied only when an invoice finalises, so a preview or draft can show credits that another invoice later consumes. When several grants could apply, Stripe orders them by priority, then earliest expiry, then promotional category first, then earliest effective date and creation date. A customer can hold at most 100 unused grants. Voiding an invoice returns the applied credit to the grant, but a credit note does not, so restoring credit after a credit note means creating a new grant. And because Stripe's comparison page notes that credits reconcile only at invoice time with basic usage-based billing, a customer can consume more than their balance during the period; if you need real-time burndown, that is one of the cases Stripe routes to Metronome.
How do you test usage-based billing with test clocks?
Use a sandbox, attach a test clock to a new customer, subscribe it to the metered price, report usage, then advance the clock past the period end and the grace period and inspect the invoice. Test clocks let you see a full cycle in minutes instead of a month.
Stripe's test clock documentation sets the boundaries. A clock starts at a `frozen_time` you choose and can only move forward. You can advance up to two intervals of the shortest subscription price at a time, so two months for a monthly price. A simulation holds up to three customers, each with up to three subscriptions or schedules. After an advance, wait for `test_helpers.test_clock.ready` or poll the clock's status before asserting anything. Stripe also notes that renewal invoices stay in draft for about an hour by default, which mirrors the grace period, so advance by one more hour to see finalisation.
Stripe's test clock pages do not describe how meter event timestamps relate to a clock's frozen time, so verify that behavior in your own sandbox rather than assuming it. A reliable pattern is to report usage with an explicit timestamp inside the simulated period, confirm it with an invoice preview before advancing, and then assert on the finalised invoice. Build a small matrix:
- Usage in the middle of the period appears on the cycle invoice at the right quantity and amount.
- Usage that crosses a tier boundary is priced per your graduated or volume tiers.
- A period with zero usage produces the invoice you expect, including any first-tier flat amount.
- A duplicate send with the same identifier is billed once.
- A cancelled event and a negative offsetting event both reduce the invoice.
- Usage reported during the grace period lands on the cycle invoice; usage after finalisation does not.
- A mid-cycle price change, then late usage, behaves as Stripe documents.
- A billing credit grant reduces the metered line and the credit balance updates.
- Error events: trigger `v1.billing.meter.error_report_triggered` and `v1.billing.meter.no_meter_found` with the Stripe CLI and confirm your handler alerts someone.
How do you migrate from legacy usage records to billing meters?
Create a meter, create a new metered price backed by it on the same product, start sending meter events alongside your existing usage records, and switch each subscription to the new price at the end of a billing period with a subscription schedule. Stripe removed the legacy usage records APIs in API version 2025-03-31.basil.
Stripe's migration guide is explicit about the constraints. Usage records billing is deprecated and billing meters are Stripe's only usage-based solution going forward. You must keep sending usage records until the migration is complete, and to keep sending them you need an API version on or before 2025-02-24.acacia. Updating the subscription schedules that move customers to the new price must also use a version on or before 2025-02-24.acacia, because from 2025-03-31.basil metered prices must be backed by meters. Meters do not support the legacy `max` aggregation, so a price that billed peak usage needs a different model, such as reporting the peak yourself to a `last` meter at period end.
The guide's sequence is: create the meter; create the new price on the same product as the old one; start reporting to the meter (Stripe does not put that usage on invoices until the customer is on the new price); list subscriptions on the old price with their schedules expanded; then, for each subscription, update its existing schedule or create one with `from_subscription`, and add a phase that moves the item to the new price at the end of the current period. Test it on a test-clock customer first.
The risk in this migration is double reporting at the boundary. During the overlap you send the same usage to two systems, and only one of them should bill each period. Keep the cutover per subscription at a period end, as Stripe suggests, record which system bills which period, and compare the last legacy invoice and the first meter-based invoice for every customer. If you are moving subscriptions from another billing system into Stripe at the same time, our Stripe Billing migration guide covers the wider cutover plan.
Common Stripe metered billing mistakes
Most metered billing incidents come from reporting, not from Stripe's pricing engine. These are the ones we would check first in any Stripe billing integration that charges for usage.
- No identifier on meter events. Retries after a timeout create new events and double-bill. Derive the identifier from your own usage row or batch key.
- Parallel calls for one customer. Stripe allows one concurrent meter event call per customer per meter. Serialize per customer or pre-aggregate.
- Timestamping when the reporter runs. Usage slips into the wrong period near boundaries. Timestamp when the usage happened.
- Sending increments to a pre-aggregated meter. With an hourly or daily event time window, each newer event overwrites the earlier one in that window.
- Subtracting allowances in code. Put included usage in a free first tier so the raw usage stays visible and pricing changes stay in Stripe.
- Treating a 200 response as billed usage. Validation is asynchronous. Subscribe to the meter error events and alert on them.
- Assuming the summary equals the invoice. Usage accepted after finalisation appears in summaries but on no invoice.
- Gating access on usage totals from the upcoming invoice. Billing Meters update at invoice time, not in real time. Enforce quotas from your own counters; Stripe Entitlements covers which features a plan unlocks, not how much of them a customer has used.
From here, this article is about Cevrynt
How Cevrynt approaches Stripe usage-based billing
Cevrynt, Inc. is a Stripe partner in the Stripe Partner Ecosystem. The partnership belongs to the company; the certifications belong to the people who hold them. Arindam Pradhan, our founder and engineering lead, is a Stripe Certified Professional (Implementation Architect). Anupam Pradhan and Subhadip Giri, a full stack developer, are each Stripe Certified Professionals (Payments Architect). The people who scope your billing work are the people who build it.
On usage-based billing we start by writing down what is measured, which meter and aggregation each dimension uses, how prices and tiers are set, and the correction rules: who may cancel or offset usage, inside which windows, and how a mistake found after finalisation is handled. We then build the outbox, the reporter, the error-event handler and the daily reconciliation in your existing stack, whether that is Node.js, Next.js or PHP and Laravel, and run a test-clock matrix like the one above in Stripe test mode. Your live billing keeps running until a planned cutover.
Every project is scoped and priced in writing after a technical review. Read how we work on our Stripe Billing service page, see the wider Stripe services overview, or describe your usage model and we will reply with the questions we need answered first.
