Checkout & payments
Payment gateway sandbox round-trip verification
Generated from docs/plan/core/payment-gateway-sandbox-round-trip-verification/PLAN.md in the AstroBaaS repository. Nothing described below is implemented — it is the written plan for it.
Payment integrations (Stripe, PayPal, Klarna) are theoretically correct but never verified against real gateways. This core work adds round-trip sandbox tests confirming session creation, webhook handling, and state transitions work end-to-end.
The problem
Payment integrations are implemented correctly by design, but we’ve never actually run a transaction against Stripe, PayPal, and Klarna in their sandboxes. A missed detail (wrong timestamp format, forgotten signature validation) might silently break in production.
What it does
- Stripe sandbox round-trip: create order, POST /api/payments/start, get redirect URL, simulate Stripe webhook, confirm order becomes ‘paid’
- PayPal sandbox round-trip: same flow, confirm PayPal session creation and webhook state changes work
- Klarna sandbox round-trip: same flow, confirm Klarna authorization and capture sequence works
- Idempotency: replay each webhook, confirm state does not change (no double-charge)
- Error cases: webhook with wrong signature, wrong amount, out-of-order events — confirm all are handled safely
- Test data retention: keep test orders in a ‘sandbox-test’ category so they don’t pollute reports
What it deliberately does NOT do
Each boundary carries its reason. A boundary without a reason gets crossed by the next person who reads this.
- Live production testing — that’s certification (production-payment-gateway-certification).
- Automated CI/CD integration — tests run manually before each release (can be automated later).
Data model
No new schema. Test orders have a flag/category for identification: sandbox_test=true or similar.
API
- POST /api/tests/payment-gateway/{provider} — run round-trip test against provider’s sandbox, return results
Every route added here must also appear in src/pages/openapi.json.ts — a test
fails the build if it does not.
Admin
Test panel under Payments; buttons for ‘Test Stripe’, ‘Test PayPal’, ‘Test Klarna’. Results show pass/fail for each step (session creation, webhook delivery, state transition). Log is accessible for debugging.
The seam — why this is core
Core owns the test harness and validation logic. Providers’ sandboxes are external (third-party). Tests are part of the smoke-test suite.
Payment correctness is non-negotiable. Unverified integrations are a trust issue.
Dependencies
- adyen-payment-gateway, bnpl, apple-pay-google-pay-setup, paypal-suite-expansion — each provider can be tested
Acceptance checks
Each of these must be able to fail. Before claiming this is done, break the code deliberately and watch each one go red.
- Run ‘Test Stripe’: creates order, calls Stripe API, receives session URL, simulates charge.succeeded webhook, order becomes ‘paid’ ✓
- Run ‘Test PayPal’: same flow ✓
- Run ‘Test Klarna’: same flow ✓
- Replay webhook: same event id sent twice; order state unchanged on second delivery ✓
- Wrong signature: webhook rejected with 401 ✓
- Wrong amount: webhook rejected as suspicious, audit-logged ✓
- Test orders marked ‘sandbox_test’; don’t appear in production reports or inventory calculations
Risks
False sense of correctness: passing sandbox tests doesn’t mean production is safe. A detail unique to production (certificate expiry, webhook rate limiting, DNS issues) could still break. Sandbox mirrors production but is not identical. Test flakiness: if sandbox is slow or has outages, tests fail intermittently, crying wolf. Tests should have generous timeouts and clear failure messages. Credential rotation: test sandbox credentials must be rotated regularly; a leaked sandbox credential is less severe than production, but still a risk.
Commercial context
| Suggested price | Core |
| Rival anchor | Magento: PayPal and Braintree sandbox-verified in production for a decade. |
The anchor is what the nearest equivalent charges on Shopify or Magento today. It is context for a pricing decision, not the decision.
Generated from the commerce plan. See docs/COMMERCE-PLAN.md for the full
catalogue and ../../AI-GUIDE.md for how to work on this repository.