new·The score now tells you which way it movedA brain's exam only ever grows: its own material writes questions, and so does every question a real caller asked and did not get answered. The score is a percentage over that growing set, so a brain that learned more could post a smaller number — and this week three did. One of them answered two MORE questions than the week before and showed eighteen points less. Printed as a single percentage, that reads as decline to a reader and as punishment to anyone who contributes material.all news →
mozg.beta
Sign in

Stripe in Production · all subjects

Billing edge cases

7 notes, read out of this brain and free to use. Each one was extracted from a source and is re-checked against its exam.

Why is my invoice $0.50 when the plan is $50? (timestamps and anchors)

Almost always a timestamp or anchoring bug. Stripe uses Unix timestamps in SECONDS everywhere — trial_end, billing_cycle_anchor, current_period_start/end in payloads. Passing JavaScript Date.now() (milliseconds) creates dates 1000x in the future; dividing incorrectly shifts periods. billing_cycle_anchor pins when the recurring invoice lands: set it when creating a subscription to force, say, the 1st of the month — Stripe then prorates the partial first period, producing that odd $0.50 'weird first invoice'. If you don't want the partial charge, combine the anchor with a trial ending at the anchor date. Always console-log derived timestamps as ISO dates during development; 'my customer was billed a random amount' is nearly always this class of bug.

How much can I stuff into metadata?

Metadata limits (as of early 2026): up to 50 key-value pairs per object, keys up to 40 characters, values up to 500 characters. It's for identifiers and annotations — your internal user id, order id, feature flags — not for documents. Don't store anything large (use your DB and store the reference), anything sensitive (metadata is visible in Dashboard and included in API responses — no PII you wouldn't show an employee, definitely no secrets), and don't rely on it for querying: you can list objects but not filter server-side by arbitrary metadata on most endpoints (some support search queries, e.g. the Search API with metadata['key']:'value'). Metadata updates fire updated webhooks, which can trigger your own handlers — another reason to diff before acting.

Usage-based billing: why did the metered invoice explode?

Metered/usage billing charges in arrears: you report usage (meters events, or the legacy usage records API on metered prices), Stripe aggregates and invoices at period end. Explosion causes: retrying usage-report requests without idempotency and double-counting; reporting cumulative totals when the meter expects increments (check the meter's aggregation formula — sum vs last_during_period vs max); and dev/test events landing on the live meter. Safeguards: dedupe usage events with your own event ids, alert on usage deltas above N× the customer average, and consider billing thresholds or a spending cap so runaway usage triggers incremental invoicing instead of a single monster invoice the card declines. A declined monster invoice then enters dunning — for an amount the customer disputes ever consuming.

Can I charge a saved card whenever I want (off-session)?

Yes, with conditions — this is merchant-initiated off-session charging. Save the payment method first via a SetupIntent (or a payment with setup_future_usage), which collects SCA upfront and records mandate/consent. Later, create a PaymentIntent with customer, payment_method, off_session: true, and confirm. Reality: issuers may still soft-decline with authentication_required — you must catch that error code and bring the customer on-session (email a link to a page that confirms the same Intent) rather than blindly retrying. Also required by card networks: clear terms at setup that you'll charge later, and sending pre-charge notification for subscriptions in some jurisdictions. Charging saved cards without prior agreement invites disputes you will lose.

Customer paid but my bank payout doesn't match — how do I reconcile?

Gross charges never equal payouts: Stripe deducts fees per transaction, holds refunds/disputes, and batches payouts. The reconciliation unit is the balance transaction (balance_transaction objects): every charge, fee, refund, adjustment, and payout is a set of them, and a payout's balance transactions sum exactly to the payout amount. Workflow: for a payout, list balance transactions by payout id; group by type (charge, fee via reporting category, refund, dispute); match charges back to your orders via the charge id / metadata. Use the Sigma or the Balance Transaction API for automation; Dashboard payout pages show the same breakdown manually. If you do accounting from invoice.paid amounts alone, your books will be wrong by exactly the fees and FX spreads.

Never trust the client-side price — what does that mean concretely?

Any amount or price computed in the browser is attacker-controlled. Concretely: never accept amount, currency, or plan name from request bodies and pass them to PaymentIntent/Charge creation; never let the client pick a Stripe Price id arbitrarily without server-side validation that it belongs to your catalog and matches the product the user selected; always look up the amount server-side from the Price id (or your own price table keyed by plan code). Checkout Sessions with line_items using price ids are safe by construction because amounts come from Stripe. The exploit is real: tampered amounts = products bought for €0.01. Same rule for quantities and for 'plan' strings your webhook later maps to entitlements.

Metered invoice exploded to $100k — how do I debug what happened?

Work backwards from the invoice. Pull its line items, then inspect usage: for the legacy usage-records API, list usage record summaries for the subscription item; for meters, query the meter event summaries and the events behind them. Check usage record timestamps — records must land inside the billing period and use Unix seconds; a wrong-window or millisecond timestamp piles usage into one period. Check your side for duplicates: retries of usage-report calls without idempotency, queue consumers redelivering after a late ack, and reporters firing twice per request are the usual double-count sources. Verify the subscription's billing_cycle_anchor hasn't shifted the period. Then add guardrails: alert on invoice amount deltas versus the customer's trailing average, and use billing thresholds so runaway usage invoices incrementally.

Give your agent this brain