Conversation
- Add 'type': 'module' to package.json for Vite 7.x ESM-only compatibility - Add test:integration script to package.json - Increase Node heap size to 4GB (--max-old-space-size=4096) to prevent OOM errors - Update test scripts to use explicit vitest.mjs path with Node memory limit - Update GitHub Actions workflows with NODE_OPTIONS for memory - Exclude T037 mocked tests from integration suite (need conversion to true integration tests) - Document T037 status in artifacts/T037-api-test-coverage-status.md BREAKING CHANGE: package.json now has type: module (ESM-first)
Introduces a script to audit REST API routes for common violations and outputs a JSON report. Adds documentation and tests for payment intent validation robustness, including idempotency, retry logic, and timeout handling. Updates several API route handlers and tasks to support these features.
Added a detailed summary documenting the standardization of API response formats for five endpoints, removing non-standard success flags and aligning with the project-standard { data, error, meta } pattern. Includes migration guidance, testing impact, and next steps for production deployment.
|
👋 Thanks for opening your first pull request in StormCom! A maintainer will review your PR soon. Please make sure:
We appreciate your contribution to making StormCom better! 🚀 |
T042 deliverables: - Automated audit script (scripts/audit-rest-api.ts) - Comprehensive audit report (77 violations identified) - Remediation plan with prioritization - HIGH severity fixes (5 PUT endpoints with idempotency) - COMPLETE - MEDIUM severity fixes (5 response format issues) - COMPLETE - LOW severity fixes (67 OPTIONS handlers) - Optional Phase 4 All critical REST violations have been identified and remediated.
There was a problem hiding this comment.
Pull Request Overview
This PR implements comprehensive security and infrastructure hardening for the checkout flow, multi-tenant isolation, and newsletter functionality. The changes introduce server-side price recalculation to prevent client-side tampering, payment intent pre-validation, atomic transactions, audit logging, and a robust multi-tenant store resolution system.
Key Changes:
- Checkout Security: Server-side price recalculation (FR-002), payment intent validation (T012), and atomic transactions (T013) to prevent fraudulent checkouts
- Multi-Tenant Infrastructure: Store resolution from domain/subdomain with canonical redirects, request context propagation, and consistent storeId enforcement
- API Standardization: Unified middleware pipeline (T027), standardized error responses (FR-008), idempotency handling for PUT/PATCH operations, and X-Request-Id header propagation
Reviewed Changes
Copilot reviewed 86 out of 178 changed files in this pull request and generated 9 comments.
Show a summary per file
| File | Description |
|---|---|
tests/e2e/checkout-security.spec.ts |
E2E tests validating price tampering prevention, payment validation, and multi-tenant isolation |
tests/integration/*/route.spec.ts |
Placeholder integration tests for analytics and attributes API routes |
src/services/transaction.ts |
Transaction wrapper utilities for atomic multi-step operations with retry logic |
src/services/pricing-service.ts |
Server-side pricing calculation service that recalculates all monetary values from database |
src/services/payments/intent-validator.ts |
Payment intent pre-validator with retry logic, idempotency, and timeout handling |
src/services/newsletter-service.ts |
Newsletter subscription service with consent recording and audit logging |
src/services/export-service.ts |
Async CSV export service with job queue integration for large datasets |
src/services/audit-service.ts |
Centralized audit logging service for checkout, payment, and order events |
src/lib/store/resolve-store.ts |
Store resolution from domain/subdomain with custom domain support |
src/lib/store/resolve-from-headers.ts |
Server-side helper to resolve store from request headers |
src/lib/store/canonical-redirect.ts |
Canonical domain redirect for SEO optimization |
src/lib/request-context.ts |
AsyncLocalStorage-based request context for correlation IDs and tenant isolation |
src/lib/job-queue-stub.ts |
Lightweight job queue for development/testing with retry and exponential backoff |
src/lib/idempotency.ts |
Idempotency handling for PUT/PATCH requests using Vercel KV or in-memory cache |
src/lib/errors.ts |
Typed error class hierarchy with HTTP status mapping |
src/lib/cache/tags.ts |
Cache tag registry for Next.js revalidateTag |
src/lib/api-response.ts |
Standardized API response formatting with X-Request-Id headers |
src/lib/api-middleware/index.ts |
Composable middleware pipeline for authentication, rate limiting, and validation |
src/components/ConsentBanner.tsx |
GDPR-compliant newsletter consent banner with accessibility features |
src/app/shop/newsletter/actions.ts |
Server actions for newsletter subscription with rate limiting |
src/app/api/stores/[id]/theme/route.ts |
Idempotency support added to theme update endpoint |
src/app/api/stores/[id]/route.ts |
Idempotency support added to store update endpoint |
src/app/api/products/[id]/route.ts |
Idempotency support added to product update endpoint |
src/app/api/orders/route.ts |
Migrated to middleware pipeline with standardized responses |
src/app/api/orders/export/route.ts |
CSV export with streaming for ≤10k rows, async jobs for >10k |
src/app/api/orders/[id]/status/route.ts |
Idempotency support added to order status updates |
src/app/api/checkout/complete/route.ts |
Complete security overhaul with price recalculation, payment validation, and audit logging |
src/app/(dashboard)/products/page.tsx |
Enforced storeId requirement with redirect for missing tenant context |
src/app/(dashboard)/analytics/page.tsx |
Enforced storeId requirement with redirect for missing tenant context |
| } | ||
|
|
||
| // Multi-tenant: Get storeId from session (never trust client) | ||
| const storeId = (session.user as any).storeId; |
There was a problem hiding this comment.
Using as any bypasses TypeScript's type safety. The session.user type should be extended in next-auth.d.ts to include storeId and other custom fields. Create a type declaration file to properly type the session object instead of using type assertions.
| } | ||
|
|
||
| const customerId = (session.user as any).id; | ||
| const userId = session.user.id; |
There was a problem hiding this comment.
customerId and userId are assigned the same value (session.user.id), which suggests duplicated logic. If these represent the same entity, use a single variable. If they represent different concepts, add a comment explaining the distinction.
| const userId = session.user.id; |
| export function rateLimitMiddleware(): Middleware { | ||
| return async (context, next) => { | ||
| // Pass the actual Request object to checkSimpleRateLimit | ||
| const result = await checkSimpleRateLimit(context.request); |
There was a problem hiding this comment.
[nitpick] The checkSimpleRateLimit function is called synchronously for every request in the middleware chain. Consider implementing distributed rate limiting with Vercel KV or Upstash Redis for production to avoid in-memory limitations and ensure consistent rate limiting across serverless function instances.
| // TODO: Integrate with actual Stripe SDK | ||
| // For now, check database payment records |
There was a problem hiding this comment.
This TODO indicates incomplete payment validation logic. The current implementation only checks the database and validates payment intent ID format (pi_* prefix) but does not verify with Stripe API. This is a critical security gap that must be addressed before production use. Add a tracking issue reference or timeline for Stripe integration.
| headers: headersList, | ||
| }); | ||
|
|
||
| const rateLimitResult = checkSimpleRateLimit(request); |
There was a problem hiding this comment.
checkSimpleRateLimit returns a Promise but is not being awaited. This will cause the rate limit check to be bypassed. Add await: const rateLimitResult = await checkSimpleRateLimit(request);
| const rateLimitResult = checkSimpleRateLimit(request); | |
| const rateLimitResult = await checkSimpleRateLimit(request); |
| payload: T, | ||
| scheduledFor?: Date | ||
| ): Promise<string> { | ||
| const id = `${type}_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`; |
There was a problem hiding this comment.
Using Math.random() for ID generation can lead to collisions in high-concurrency scenarios. Use crypto.randomUUID() or a more robust ID generation library for production-grade uniqueness guarantees.
Description
Why
Fixes #
Related to #
What
Type of Change
Please check the relevant option(s):
Checklist
Please ensure your PR meets the following requirements:
Code Quality
npm run format)npm run lint)npm run type-check)anytypes used (except for documented third-party library interfaces)Testing
npm run test)Security & Best Practices
Documentation
Database (if applicable)
Accessibility (if UI changes were made)
Performance (if applicable)
Build & Deployment
npm run build)Screenshots (if applicable)
Before
After
Additional Context
Reviewer Notes
By submitting this pull request, I confirm that: