Skip to Content
TroubleshootingStripe + Convex Issues

Troubleshooting Stripe and Convex

If checkout completes but access does not, assume configuration drift before you assume application logic is wrong.

Run the built-in health checks

Start here:

bun run stripe:listen:dev bun run stripe:dev:doctor bun run clerk:test-user:prepare:entitled bun run stripe:dev:verify

These commands tell you more, faster, than hand-inspecting random env files.

The most common failures

1. Missing Clerk convex JWT template

If that template is missing or misnamed, authenticated server reads can fail even while the user appears signed in.

2. Clerk issuer / Convex mismatch

If Clerk and Convex disagree on the issuer/domain, access checks can fail after payment.

3. Stripe secret drift

These values must match in both places:

  • .env.local
  • the active Convex env

The dev helper syncs both values. If the listener rotated the webhook secret, restart the app.

4. Wrong Stripe account or wrong pinned CLI profile

The app STRIPE_SECRET_KEY is the runtime source of truth. The Stripe CLI profile is the local guardrail.

In this repo, bun run stripe:dev:doctor compares the app account against the pinned Stripe CLI project ship-by-sunday. If the doctor says the pinned profile is missing or mismatched, repair it with:

stripe login --project-name ship-by-sunday

If those differ, you can see real Stripe events without the starter ever receiving a valid matching webhook.

5. Webhook endpoint not actually registered

If Stripe does not reach POST {NEXT_PUBLIC_CONVEX_SITE_URL}/stripe/webhook, checkout can finish while access grant never happens.

If the app reaches Stripe and returns 202 or 200 but nothing shows up in Convex-side observability or access state, verify whether the app also needs NEXT_PUBLIC_CONVEX_SITE_URL for HTTP actions. In this repo, .convex.cloud is not enough for observability ingest or other Convex HTTP-action endpoints when the deployment serves those on .convex.site.

6. Payment succeeded but access still did not grant

If the Stripe event exists and the buyer still cannot reach /start, check:

  • whether the webhook used the current signing secret
  • whether Convex accepted the same webhook secret and Stripe secret key
  • whether the browser still has the pending checkout session cookie if the buyer recovered from a lost Stripe return tab
  • whether the signed-in account’s verified primary email matches the Stripe checkout email before the claim step runs

Fast debug order

Use this sequence:

  1. run the Stripe listener
  2. run stripe:dev:doctor
  3. if /access-required is visible, read the signed-in email shown on that page before assuming the webhook failed
  4. confirm secret parity
  5. confirm the pinned Stripe CLI project ship-by-sunday matches the app account
  6. confirm Clerk convex template
  7. confirm the app and Convex agree on both NEXT_PUBLIC_CONVEX_URL and NEXT_PUBLIC_CONVEX_SITE_URL when HTTP actions are involved
  8. rerun a real local checkout against your hosted Convex dev deployment

What success looks like

Stripe and Convex are aligned when:

  • Stripe forwards checkout.session.completed
  • the webhook accepts the signature
  • Convex writes or updates the access grant
  • the buyer leaves /checkout/success and lands in /start
Last updated on