CRITICAL SECURITY FIXES:
- Add webhook secret validation to prevent signature bypass
- Implement idempotency protection across all webhook handlers
- Add atomic database updates to prevent race conditions
- Improve CORS security with origin validation and logging
- Remove .env from git tracking to protect secrets
STRIPE INTEGRATION:
- Add support for checkout.session.expired webhook event
- Add Stripe publishable key to environment configuration
- Fix webhook handlers with proper idempotency checks
- Update Order model with atomic updatePaymentAndStatus method
- Add comprehensive logging for webhook processing
DEPLOYMENT ARCHITECTURE:
- Split into two Docker images (frontend-latest, backend-latest)
- Update CI/CD to build separate frontend and backend images
- Configure backend on port 3801 (internal 3001)
- Add production-ready docker-compose.yml
- Remove redundant docker-compose.portainer.yml
- Update nginx configuration for both frontend and backend
DOCUMENTATION:
- Add PRODUCTION-SETUP.md with complete deployment guide
- Add docs/stripe-security-fixes.md with security audit details
- Add docs/stripe-checkout-sessions.md with integration docs
- Add docs/stripe-webhooks.md with webhook configuration
- Update .env.example with all required variables including Stripe publishable key
CONFIGURATION:
- Consolidate to single .env.example template
- Update .gitignore to protect all .env variants
- Add server/Dockerfile for backend container
- Update DEPLOYMENT.md with new architecture
🔒 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Updated the Stripe API version from 2024-12-18.acacia to the latest
version 2025-10-29.clover as requested.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Prevent accidental creation of real carbon offsets during development:
- Add WREN_DRY_RUN environment variable (default: true for dev)
- Update webhook fulfillment to use env variable instead of hardcoded value
- Log warning when in dry run mode for visibility
- Production deployments should set WREN_DRY_RUN=false
This allows safe testing with Stripe test cards without creating real Wren offset orders.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
## Stripe Payment Integration
- Add Express.js backend server with Stripe Checkout Sessions
- Create SQLite database for order tracking
- Implement Stripe webhook handlers for payment events
- Integrate with Wren Climate API for carbon offset fulfillment
- Add CheckoutSuccess and CheckoutCancel pages
- Create checkout API client for frontend
- Update OffsetOrder component to redirect to Stripe Checkout
- Add processing fee calculation (3% of base amount)
- Implement order status tracking (pending → paid → fulfilled)
Backend (server/):
- Express server with CORS and middleware
- SQLite database with Order schema
- Stripe configuration and client
- Order CRUD operations model
- Checkout session creation endpoint
- Webhook handler for payment confirmation
- Wren API client for offset fulfillment
Frontend:
- CheckoutSuccess page with order details display
- CheckoutCancel page with retry encouragement
- Updated OffsetOrder to use Stripe checkout flow
- Added checkout routes to App.tsx
- TypeScript interfaces for checkout flow
## Visual & UX Enhancements
- Add CertificationBadge component for project verification status
- Create PortfolioDonutChart for visual portfolio allocation
- Implement RadialProgress for percentage displays
- Add reusable form components (FormInput, FormTextarea, FormSelect, FormFieldWrapper)
- Refactor OffsetOrder with improved layout and animations
- Add offset percentage slider with visual feedback
- Enhance MobileOffsetOrder with better responsive design
- Improve TripCalculator with cleaner UI structure
- Update CurrencySelect with better styling
- Add portfolio distribution visualization
- Enhance project cards with hover effects and animations
- Improve color palette and gradient usage throughout
## Configuration
- Add VITE_API_BASE_URL environment variable
- Create backend .env.example template
- Update frontend .env.example with API URL
- Add Stripe documentation references
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Create logger utility that only logs in development mode
- Update wrenClient.ts to use logger instead of console.log/warn
- Update OffsetOrder.tsx to use logger for debug messages
- Update config.ts to only log environment loading in dev mode
- Keeps console.error for actual errors (always shown)
Fixes: Console clutter in production deployment
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Add fallback to read from container environment variables
- Maintains backward compatibility with .env file approach
- Allows setting VITE_* vars directly in Portainer UI
This enables flexible deployment:
- File-based: Mount .env file (docker-compose)
- Env-based: Set env vars in Portainer container settings
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Match working workflow pattern from other server.
Let runner provide Docker environment directly.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Let Buildx use default driver with --privileged container.
The 'docker' driver caused endpoint property errors.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Runner already mounts /var/run/docker.sock automatically.
Keep only --privileged flag to allow socket access.
Fixes: Error response from daemon: Duplicate mount point
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Add --privileged flag and explicit Docker socket mount to container
- Configure Buildx to use 'docker' driver instead of 'docker-container'
- This avoids nested container permission issues while using full Ubuntu image
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Match the working workflow pattern from other server by using
repository variables instead of hardcoded values:
- REGISTRY_HOST (code.puffinoffset.com)
- REGISTRY_USERNAME (matt)
- IMAGE_NAME (puffin-app)
- REGISTRY_TOKEN (secret with write:package permission)
This makes the workflow portable and matches the proven
working configuration.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Based on Zen multi-model consensus analysis:
Root cause: act_runner v0.2.13+3 maps ubuntu-latest to bare
ubuntu:22.04 which lacks Docker CLI. Newer runners use full images.
Solution: Override job container to use ghcr.io/catthehacker/ubuntu:full-22.04
which includes Docker, Buildx, Node.js, and standard CI tools.
This is the recommended approach from act_runner maintainers for
GitHub Actions compatibility.
Consensus from Gemini-2.5-pro and o3 models.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Switch to the pattern that works on other server:
- Run directly on ubuntu-latest (no container)
- Use docker/login-action@v3
- Use docker/setup-buildx-action@v3
- Use docker/build-push-action@v6
This matches the working workflow from another repository
and should work with the Gitea runner configuration.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
The docker:dind image doesn't include Node.js which is required
by actions/checkout@v4. Use manual git commands instead.
Changes:
- Install git via apk (Alpine package manager)
- Clone repository directly
- Checkout specific commit SHA
This approach works with docker:dind's minimal Alpine base.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Root cause: act_runner provides minimal environment without sudo
or Docker CLI. Cannot install packages in workflow.
Solution: Use docker:24-dind container which includes Docker CLI
and daemon. Runs with --privileged to allow nested containers.
Changes:
- Use docker:24-dind as job container
- Remove installation steps (Docker pre-installed)
- Keep simple login, build, push workflow
Also added alternative solution file showing how to configure
runner with Docker CLI for better performance.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Root cause: Gitea act_runner mounts Docker socket but doesn't
include Docker CLI by default to keep the image lightweight.
Solution: Install docker-ce-cli package before running any
docker commands. This allows the workflow to communicate with
the Docker daemon via the mounted socket.
Changes:
- Add step to install Docker CLI from official Docker repository
- Verify installation with docker version
- Continue with login, build, and push steps
Based on Zen expert analysis of the runner environment.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Replace Kaniko approach with direct Docker CLI commands.
The Gitea runner mounts Docker socket, making Docker available
in ubuntu-latest environment.
Key changes:
- Remove container specification (Kaniko lacks shell utilities)
- Use docker login with password-stdin for authentication
- Build with docker build using multiple -t tags
- Push both latest and commit SHA tags
- Works with Docker-based Gitea runners
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
The Kaniko executor image doesn't have traditional user management
and runs with appropriate permissions by default.
Fixes: unable to find user root: no matching entries in passwd file
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Replace Docker-based build with Kaniko executor to support
Docker-based Gitea runners that don't have Docker daemon access.
Kaniko builds container images without requiring Docker,
making it ideal for containerized CI environments.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Create Gitea Actions workflow for automated Docker builds on push to main
- Add docker-compose.portainer.yml for production Portainer deployment
- Create comprehensive DEPLOYMENT.md guide with step-by-step instructions
- Update CLAUDE.md with CI/CD pipeline documentation
Images are built and pushed to Gitea registry at:
code.puffinoffset.com/matt/puffin-app:latest
code.puffinoffset.com/matt/puffin-app:main-<sha>
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Add comma-separated number formatting for better readability in all calculator inputs
- Move offset percentage selection from calculator to offset order page for clearer workflow
- Improve project card layout with consistent height alignment in OffsetOrder
- Change number inputs to text inputs to support formatted display
- Update form messages to reflect chosen offset percentage
- Add CLAUDE.md documentation for repository guidance
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Increase max widths and improve responsive spacing across components
- Add responsive grid columns (xl:grid-cols-4) for better large screen layout
- Remove redundant click area overlay and hover effects for cleaner code
- Consolidate padding management to main container level
- Install framer-motion dependency (v12.15.0)
- Add smooth transitions to forms and buttons in TripCalculator
- Implement hover and tap animations for interactive elements
- Add entrance/exit animations for component state changes
- Enhance user experience with motion effects in Home and OffsetOrder components