Answers · Billing

What is a statement descriptor and why set it before charging?

It is the text that appears on a customer's card statement next to the charge. Set it in the Stripe dashboard before you take any money. Changing it later only affects future charges, so early customers keep whatever was there, and an unrecognizable descriptor is one of the most common causes of a chargeback.

Set it where the customer will recognize it

Use the product name people bought, not your legal entity. Someone who bought "Acme Reports" and sees "NORTHSTAR HOLDINGS LLC" on their statement does not remember the purchase, and the fastest path available to them is to call their bank.

Stripe caps the descriptor at 22 characters and rejects several symbols. Test it by making a real charge on your own card in live mode for the smallest amount you sell, then look at your own statement. That is the only way to see exactly what the customer sees.

Why it cannot be fixed retroactively

The descriptor is recorded with the charge at the time of the charge. Updating the account setting changes the next one. So the cost of getting this wrong scales with how long you wait to notice, and the people most likely to dispute are your earliest customers, who are also the ones you least want to lose.

The disputes you do not see coming

A chargeback costs the disputed amount plus a fee, and it counts against your account's dispute rate regardless of whether you win. One Shot subscribes to charge.dispute.created and radar.early_fraud_warning.created so both land in your app rather than in an email you skim. The handler is in app/controllers/webhooks/stripe_controller.rb.

The early fraud warning is the useful one: it arrives before the dispute, and refunding at that point usually avoids the chargeback entirely.

The rest of the pre-charge checklist

bin/rails runner 'puts Rails.application.config.x.app_name'

Three things belong to the same "before any real money" pass: the statement descriptor, a support email address that a confused customer can actually reach, and the business name on the Stripe account itself, which appears on receipts. The support address the app publishes is set in config/initializers/app_identity.rb, so that is the one to check. See Stripe Setup for the full sequence.

What this does not affect

Nothing in your application code reads the descriptor. It is entirely a Stripe account setting, so there is no deploy involved and no way to get it wrong in the repo.

Related questions

Do I have to handle failed payments and refunds myself?

No. Stripe retries failed charges, emails customers, and processes refunds through its dashboard. Your app subscribes to invoice.payment_failed, invoice.payment_succeeded, charge.refunded and customer.subscription.deleted, and keeps one local Subscription row in sync. That is the whole of your responsibility.

Which Stripe webhook events does a subscription app need?

Eight: checkout.session.completed, invoice.payment_succeeded, invoice.payment_failed, customer.subscription.updated, customer.subscription.deleted, charge.refunded, charge.dispute.created and radar.early_fraud_warning.created. The first five keep entitlement correct; the last three are the money events you want to know about rather than discover in a statement.

Why does my Stripe webhook keep returning a 400 error?

Signature verification failed. Either STRIPE_WEBHOOK_SECRET does not match the endpoint sending the event, or something read and re-encoded the request body before verification ran. Each endpoint in Stripe has its own secret, and the CLI's local secret is different again, which is the usual cause of a 400 that appears only in production.