Secure Stripe and Paddle payments in an AI-built app
Last updated
Create checkouts on the server from price IDs you store, never from an amount or plan the browser sends; grant access only when a signature-verified webhook confirms payment, not when the user lands on a success page; keep secret keys server-side; process each webhook event once; and enforce plan limits in server code.
The four mistakes that cost money
| Mistake | What an attacker does | Fix |
|---|---|---|
| Amount or price taken from the request | Changes the price to 1 cent | Look up the price ID on the server |
| Access granted on the success redirect | Opens /success without paying | Grant access from the verified webhook |
| Webhook signature not checked | Sends a fake "paid" event | Verify the signature with the raw body |
| Paywall only in the UI | Calls the API directly | Check the plan in every server route |
The checklist
- Store your plan to price-ID mapping on the server and create checkout sessions from it.
- Stripe: verify webhooks with stripe.webhooks.constructEvent using the raw request body and your endpoint secret.
- Paddle: verify the Paddle-Signature header with the SDK’s webhook helper and your notification secret.
- Record each event ID and skip events you have already processed, so retries do not double-grant.
- Update the user’s plan only from webhook events, and read the plan from your database in every protected route.
- Keep sk_ and Paddle API keys in server-only environment variables; only publishable or client-side tokens go to the browser.
- Test the whole flow in test mode, then do one real purchase and refund before launch.
Questions
- Can Defexity find payment bugs?
- From the live URL it finds secret payment keys in your JavaScript. With GitHub connected it flags Stripe webhook handlers that skip signature verification. Price tampering and paywall logic need a code review or a test purchase.
Related Defexity checks
- SEC-016: Secret key exposed in public HTML or JavaScript
- CODE-013: Stripe webhook without signature verification (code check, with GitHub)
- SEC-019: API route returns data without authentication
Sources
Defexity reports potential risks it can see from outside your app. It is not a guarantee of security and not legal advice.