Skip to content

Add demo e-commerce app with Stripe Elements-like checkout#6

Merged
Segfaultd merged 22 commits intomainfrom
demo-app
Apr 10, 2026
Merged

Add demo e-commerce app with Stripe Elements-like checkout#6
Segfaultd merged 22 commits intomainfrom
demo-app

Conversation

@Segfaultd
Copy link
Copy Markdown
Contributor

@Segfaultd Segfaultd commented Apr 10, 2026

Summary

  • Adds an Astro SSR demo app in demo/ that consumes Strimulator as a Stripe API replacement, showcasing a full e-commerce checkout flow
  • Custom card form mimicking Stripe Elements with 4 test card scenarios (Visa success, Mastercard success, decline, 3DS required)
  • Single-command orchestration via bun run demo that starts both Strimulator and the Astro dev server

Test Plan

  • Run bun run demo, open http://localhost:4321
  • Buy a product with "Visa (success)" — verify redirect to success page with correct amount
  • Buy a product with "Declined card" — verify inline error message
  • Buy a product with "3DS Required" — verify modal appears, click Authorize, verify success
  • Click "View in Dashboard" on success page — verify Strimulator dashboard shows created objects
  • Run bun test — all 3560 tests pass

Next.js requires @types/node for TypeScript type checking during
build. Without it, the docs CI build fails.
Comprehensive coverage across all services, lib utilities, and middleware:

- Customers: 22 → 202 tests
- Payment Intents: 32 → 351 tests (full state machine coverage)
- Payment Methods: 26 → 200 tests (all magic tokens)
- Subscriptions: 20 → 300 tests (items, trials, billing)
- Invoices: 25 → 250 tests (draft→open→paid/void lifecycle)
- Charges: 0 → 150 tests (new file)
- Refunds: 17 → 206 tests (partial/full refund scenarios)
- Products: 24 → 149 tests
- Prices: 22 → 156 tests (one-time + recurring)
- Setup Intents: 24 → 200 tests
- Events: 26 → 114 tests
- Test Clocks: 16 → 122 tests (advance + billing integration)
- Webhook Endpoints: 5 → 80 tests
- Webhook Delivery: 11 → 120 tests (signatures, matching, retries)
- Lib utilities: 44 → 258 tests (expand, id-gen, pagination, search, timestamps, event-bus, action-flags)
- Middleware: 22 → 86 tests (auth, form-parser, idempotency)
- Errors: 4 → 47 tests
- DB: 4 → 19 tests
8 new test files with 376 tests that exercise full Stripe API flows
through the official SDK, testing actual business logic and state
transitions rather than field-level assertions:

- e-commerce.test.ts (59): Checkout, manual capture, partial capture,
  declined cards, 3DS authentication, refunds, idempotency, expansion
- saas-subscription.test.ts (55): Onboarding, trials, plan upgrades,
  cancellation, billing cycles via test clocks, multi-subscription
- billing-invoices.test.ts (49): Invoice lifecycle (draft→open→paid/void),
  state machine enforcement, subscription invoicing, search
- webhook-ecosystem.test.ts (49): Event delivery with HMAC verification,
  customer/payment/subscription webhooks, routing, signature validation
- product-catalog.test.ts (40): Product/price CRUD, SaaS pricing page
  setup, catalog-to-subscription flow
- search-and-pagination.test.ts (40): Customer/PI search, cursor-based
  pagination, expand related resources
- setup-and-future-payments.test.ts (39): Save card via SetupIntent,
  charge later, 3DS setup, cancel flows
- error-handling-and-edge-cases.test.ts (45): Auth errors, 404s,
  validation errors, state transition errors, idempotency
…lined

Three bugs discovered during SDK testing:

1. Refund route didn't parseInt the amount from form-encoded body,
   causing string concatenation when accumulating partial refunds
   (e.g. 0 + "2000" = "02000" instead of 2000)

2. Expand params only supported expand[]=field format (curl/raw) but
   not expand[0]=field (Stripe SDK). Added parseExpandParams() helper
   that handles both formats, updated all 4 routes that use expansion.

3. No magic token for declined cards — added tok_chargeDeclined which
   creates a card with last4 "0002" (triggers automatic decline in
   the payment simulation). Previously, decline testing required the
   internal actionFlags.failNextPayment mechanism.

Added 10 new SDK tests verifying all three fixes work end-to-end.
All list endpoints used `gt(created, cursor.created)` for cursor-based
pagination, which broke when multiple items shared the same second-
precision timestamp — the cursor would skip items or return empty pages.

Fix: Added `cursorCondition()` helper that uses a composite cursor:
  (created > cursor.created) OR (created = cursor.created AND id > cursor.id)

Combined with `ORDER BY created, id` on all list queries, this ensures
deterministic, gap-free pagination regardless of creation timing.

Also fixed:
- Events service: was using eq() instead of lt() for desc-ordered cursor
- Events service: added secondary desc(id) sort for same-second ordering
- Test clocks service: cursor was validated but completely ignored
- Webhook endpoints service: cursor was validated but completely ignored
Removed all workarounds that weakened pagination test assertions:

- Replaced toBeGreaterThanOrEqual(0) with exact count assertions
- Removed createSIsWithDistinctTimestamps() manual timestamp patching
- Removed setTimeout(1100ms) delays between item creation
- Removed "same-second limitation" comments and test name suffixes
- Removed 2 trivial workaround-only tests (prices, products)
- Rewrote charges pagination tests to use service.create() instead
  of manual DB insertion with fake timestamps

All pagination tests now properly assert:
- Exact item counts per page
- No duplicates across pages
- All items collected across full traversal
Astro SSR app consuming Strimulator with custom Stripe Elements-like
card form, test card scenario selector, and single-command orchestration.
11 tasks covering Astro scaffold, Stripe integration, pages,
card form component, API endpoints, 3DS flow, and orchestration.
Decline errors show inline on checkout (better UX) so failed.astro
was unreachable. Also reset the 3DS authorize button text and
disabled state after a failed authorization attempt.
@Segfaultd Segfaultd merged commit e8cb297 into main Apr 10, 2026
2 checks passed
@Segfaultd Segfaultd deleted the demo-app branch April 10, 2026 15:49
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