DEEP DIVE
Stripe is safe.
Your backend isn't.
Simon Schubert
Security Engineer · 6 min read
Every week I talk to a vibe coder who has just shipped a paid product and feels good about it. They used Stripe. They followed the docs. The checkout flow works. And they genuinely believe that means their payment stack is secure.
It isn't. Stripe protects one thing: your customers' card data. The moment money moves from their card to your Stripe account, Stripe's job is done. What happens next — the webhook that fires, the fulfillment your server triggers, the database row that gets updated — that's entirely your code. And in most vibe-coded apps, that code has never been reviewed by anyone who's broken into a payment system before.
I spent years on the offensive side of that equation. Here are the four places I look first.
1. Unverified webhooks
Stripe fires a webhook to your server every time something happens — a payment succeeds, a subscription renews, a refund is issued. Your server listens for these events and acts on them: unlocking features, sending receipts, updating user records.
The problem is that anyone on the internet can POST to your webhook endpoint. If you're not verifying the request actually came from Stripe, an attacker can replay an old payment_intent.succeeded event — or fabricate a new one — and your server will process it as legitimate.
/api/webhook with a fake payment body immediately upgraded an account to the paid plan. No card needed.The fix is one function call. Stripe gives you a signing secret and an SDK method to verify it:
2. Missing idempotency keys
Networks fail. Users double-click. Servers retry. Without idempotency keys, a single purchase can be processed multiple times — and depending on your logic, that can mean double charges, double fulfillment, or both.
Stripe supports idempotency keys on all write operations. Pass one and Stripe will deduplicate for you — the same key will always return the same result, even if the request is sent ten times.
3. Price manipulation
This one is subtle and crushingly common in AI-generated code. Here's the pattern: your frontend sends the product ID and the price to your server, your server passes that price directly to Stripe when creating the payment intent.
An attacker intercepts the request, changes the amount to 1 cent, and your server dutifully charges them 1 cent and sends a payment_intent.succeeded event. Your webhook fires, sees a success, and unlocks whatever they bought.
4. Refund and cancellation logic
Refund endpoints are often thrown together quickly and rarely tested beyond the happy path. The two failures I see most often: no authentication check (meaning anyone can refund any charge ID they find), and no ownership verification (meaning an authenticated user can refund a charge that belongs to a different user).
The pattern here
Every one of these vulnerabilities follows the same shape: trust flows in the wrong direction. Client data is trusted server-side. External requests are trusted without verification. Operations are trusted without ownership checks.
Stripe can't fix that for you — it doesn't know what your application logic is supposed to do. That's the part you own. And if you've built with an AI assistant, there's a decent chance at least one of these is sitting in your codebase right now, waiting.
ARGUS DEEP SCAN
We test your live site,
not your source code.
