Prices from the server. Webhooks verified.
Money is where a small mistake stops being small. The amount that came from the browser, the webhook that trusts whoever calls it, the secret key in a public bundle: each is one line, and each is a rule.
Sound familiar?
The checkout posts an amount and the server charges it. Anyone who can edit a request can name their own price.
A handler that reads the event body and marks the order paid, without checking the signature, marks any order paid for anyone who asks.
sk_live in JavaScript is the whole account. It happens because the publishable and the secret key look alike at 2am.
The guard was there. A refactor moved it. Nothing failed, because nothing was watching for a protection to disappear.
Traced, verified, and kept that way.
Taint analysis follows request input to the payment call. Webhook handlers are read for a signature check before the body is trusted. Both become invariants, so a future push that breaks them fails its pull request.
- Price traced from request to charge
An amount that flows from the request into the payment call is a finding; that prices come from the server is an invariant re-checked on every push. source.billing.price-from-client-input
- Signature or nothing
A webhook handler that uses the body without a visible verification - constructEvent, a timing-safe compare, a provider SDK - is named, and the invariant holds only when every handler shows one. source.webhook.missing-signature-verification
- Three Stripe key shapes
The live secret, the test secret and the publishable key are told apart. The first two in public JavaScript are findings; the third is expected there and never reported as a leak. runtime.javascript.credential-exposed
- A protection removed is a finding
An admin check that disappears from a refund route is raised on its own, on the pull request that removed it. 4 more rules watch paid endpoints that are unauthenticated, unlimited or unthrottled. source.authorization.protection-removed
- Webhooks verify provider signaturesHoldingwebhooks.signatures-verified
- Prices come from trusted backend dataHoldingbilling.prices-from-backend
- Reads are scoped to the tenantBrokentenant.reads-scoped:<Model>
- Mutating routes require authenticationHoldingauth.mutations-guarded
- Admin operations require admin permissionHoldingauth.admin-routes-require-admin
- CORS uses an explicit origin allowlistHoldingcors.explicit-allowlist
What the first week looks like
- 01Scan the deployed site
The report says whether a Stripe key is in your JavaScript, which kind, and whether the payment provider is in test mode on a production host.
- 02Connect the repository
The inventory finds the payment calls, the webhook handlers and the refund routes, and derives the two invariants.
- 03Read the first findings
Each says what is wrong, why it matters, how it would be abused, how to fix it and how the fix is verified.
- 04Let the check watch the refactor
The pull request that drops the admin check fails. The one that restores it passes.
The rules about money live in source, so they need the repository. Pro connects it and re-checks the invariants on every push.
- One site with its repository connected
- The billing, webhook and cost-abuse source rules
- Invariants re-checked on every pull request
- Fix with AI as a draft pull request
Start on Free, which does not expire. Upgrade when it pays for itself.
Find out where the money is exposed.
The free scan reads the bundle. The repository reads the rest, and the invariants keep it read.