Skip to content

Don't open Stripe checkout sessions for scripts and scanners - #203

Merged
danielehrhardt merged 1 commit into
mainfrom
fix/checkout-no-bot-sessions
Oct 18, 2026
Merged

danielehrhardt merged 1 commit into
mainfrom
fix/checkout-no-bot-sessions

Conversation

@danielehrhardt

Copy link
Copy Markdown
Contributor

Why

Stripe shows 6 expired Godmode Pro checkouts since 15.10 with no email. Matching them against D1 (orders + events):

Session (UTC) What it was
16.10 08:53 yearly + 08:53 monthly one DE desktop visitor, 6 s and 4 s on Stripe, then /download (most likely an internal check)
17.10 07:24 our own X agent's curl -X POST /api/checkout health check (tool call 07:24:06, session 07:24:17)
16.10 21:35, 21:43, 17.10 13:13 no landing, referrer or UTM and no tracked site visit within an hour: scripted hits, not visitors

None of them was a real visitor leaving at the card step. They inflate "expired checkouts" and hide the real funnel.

What

  • POST /api/checkout runs the same BOTS user-agent check as GET (curl, python, scanners, monitors) and answers 303 /#pricing (or 403 JSON) without creating a Stripe session.
  • The GET handoff to POST now keeps the original request headers, so the per-IP CHECKOUT_LIMIT and the bot check see the real IP and user agent (before, every GET was rate-limited under the key unknown).

Test

Local wrangler dev build:

  • curl -X POST -d plan=monthly /api/checkout -> 303 /#pricing, no session
  • curl /api/checkout?plan=monthly -> 303 /#pricing
  • browser UA POST and GET (utm_source=app) -> go on to Stripe session creation (fails locally on the dummy key as expected)
  • pnpm typecheck: 0 errors

POST /api/checkout now applies the same bot check as GET, and the GET
handoff keeps the visitor's headers so the per-IP rate limit and the
bot check still see the real IP and user agent.
@danielehrhardt
danielehrhardt merged commit 831a941 into main Oct 18, 2026
8 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant