Skip to content

feat: per-store Cloudflare Turnstile bot protection for checkout - #107

Merged
bighadj22 merged 2 commits into
mainfrom
fix/pr-103-hardening
Sep 10, 2026
Merged

feat: per-store Cloudflare Turnstile bot protection for checkout#107
bighadj22 merged 2 commits into
mainfrom
fix/pr-103-hardening

Conversation

@bighadj22

Copy link
Copy Markdown
Owner

Summary

Adds optional, per-store Cloudflare Turnstile bot protection to the COD checkout flow. Merchants configure their widget keys in dashboard Settings; when enabled, the storefront order form carries a Turnstile token that cod-server verifies against Cloudflare siteverify before accepting the order. Stores without a config are unaffected (Turnstile disabled by default).

Changes

cod-shared

  • store_turnstile_config table queries with a safe/raw read split — the secret key is never returned to a client
  • verifyTurnstileToken siteverify client: zero dependencies, in-band failure reporting (success: false + error codes), TRANSIENT error class for transport-level failures
  • New RBAC scopes and error codes

cod-server

  • Migration 0024_store_turnstile_config.sql (one row per store, no row = disabled)
  • Storefront turnstile gate on order placement — fail-open on provider outage, so orders are never blocked by a siteverify transport failure
  • Per-store config endpoints (create/update) with scope checks and OpenAPI schemas

cod-client-astro (dashboard)

  • Turnstile settings card in Settings (enable/disable, site key, secret key) with AR/FR/EN locale parity

cod-astro/theme01 (storefront)

  • Managed Turnstile widget on the order form, interaction-only: invisible during normal checkout, a challenge only when Cloudflare's risk engine demands one
  • Pre-submit token guard: a submit without a token is blocked with a localized "checking security" message, the submit button is disabled until the token lands, then submission is re-enabled — closing the empty-token race where implicit rendering submits early
  • retry: auto plus success/expired/error/timeout callbacks so transient widget failures and challenge timeouts self-heal
  • Content keys in all three language packs; fully inert when the store has Turnstile disabled (widget script never loads)

Docs

  • docs/TURNSTILE.md, README and CHANGELOG entries

Testing

  • cod-server: npm run typecheck clean, npm test 1896/1896 pass (135 files)
  • cod-client-astro: npm run typecheck clean, npm test 143/143 pass (33 files)
  • theme01: astro check 0 errors, npm test 18/18 pass, npm run validate (string/style validators + build) clean
  • Verified live on the storefront: the widget issues a real token invisibly on the product page, the error box stays hidden, and form submission proceeds normally when a token is present

Deploy notes

  • Apply migration 0024_store_turnstile_config to D1
  • The secret key is stored per store in D1 (like other provider integration keys) and is never returned by any API read

…eckout

- cod-shared: store_turnstile_config table + queries (secret never
  returned to clients), verifyTurnstileToken siteverify client, scopes
  and error codes
- cod-server: migration 0024, storefront turnstile gate on order
  placement (fail-open on provider outage), per-store config endpoints
  with scope checks and OpenAPI schemas
- cod-client-astro: Turnstile settings card with AR/FR/EN locale parity
- theme01: interaction-only managed widget on the order form,
  pre-submit token guard with localized wait message, retry auto +
  success/expired/error/timeout callbacks, content keys in all packs
- docs: TURNSTILE.md, README and CHANGELOG entries
@bighadj22
bighadj22 merged commit 6245d81 into main Sep 10, 2026
3 checks passed
@bighadj22
bighadj22 deleted the fix/pr-103-hardening branch September 11, 2026 13:29
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