puffin-app/server/routes/checkout.js

182 lines
5.2 KiB
JavaScript
Raw Normal View History

Integrate Stripe Checkout and add comprehensive UI enhancements ## 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>
2025-10-29 21:45:14 +01:00
import express from 'express';
import { stripe } from '../config/stripe.js';
import { Order } from '../models/Order.js';
const router = express.Router();
// Portfolio pricing configuration (price per ton in USD)
const PORTFOLIO_PRICING = {
1: 15, // Balanced portfolio
2: 18, // High-impact portfolio
3: 20 // Premium portfolio
};
// Processing fee percentage
const PROCESSING_FEE_PERCENT = 0.03; // 3%
/**
* Calculate pricing with processing fee
* @param {number} tons - Number of tons
* @param {number} portfolioId - Portfolio ID
* @returns {Object} Pricing breakdown
*/
function calculatePricing(tons, portfolioId) {
const pricePerTon = PORTFOLIO_PRICING[portfolioId] || 18; // Default to $18/ton
const baseAmount = Math.round(tons * pricePerTon * 100); // Convert to cents
const processingFee = Math.round(baseAmount * PROCESSING_FEE_PERCENT);
const totalAmount = baseAmount + processingFee;
return {
baseAmount,
processingFee,
totalAmount,
pricePerTon
};
}
/**
* POST /api/checkout/create-session
* Create a Stripe Checkout Session
*/
router.post('/create-session', async (req, res) => {
try {
const { tons, portfolioId, customerEmail } = req.body;
// Validation
if (!tons || tons <= 0) {
return res.status(400).json({ error: 'Invalid tons value' });
}
// Accept any valid positive integer portfolio ID (from Wren API)
if (!portfolioId || !Number.isInteger(portfolioId) || portfolioId <= 0) {
console.error('❌ Invalid portfolio ID received:', portfolioId);
Integrate Stripe Checkout and add comprehensive UI enhancements ## 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>
2025-10-29 21:45:14 +01:00
return res.status(400).json({ error: 'Invalid portfolio ID' });
}
console.log(`📦 Creating checkout for portfolio ID: ${portfolioId}, tons: ${tons}`);
Integrate Stripe Checkout and add comprehensive UI enhancements ## 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>
2025-10-29 21:45:14 +01:00
// Calculate pricing
const { baseAmount, processingFee, totalAmount, pricePerTon } = calculatePricing(tons, portfolioId);
// Create line items for Stripe
const lineItems = [
{
price_data: {
currency: 'usd',
product_data: {
name: `Carbon Offset - ${tons} tons`,
description: `Portfolio ${portfolioId} at $${pricePerTon}/ton`,
Implement comprehensive Stripe security fixes and production deployment 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>
2025-10-30 12:18:57 +01:00
// images: ['https://puffinoffset.com/images/carbon-offset.png'], // Optional: Add your logo when available
Integrate Stripe Checkout and add comprehensive UI enhancements ## 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>
2025-10-29 21:45:14 +01:00
},
unit_amount: baseAmount, // Base amount in cents
},
quantity: 1,
},
{
price_data: {
currency: 'usd',
product_data: {
name: 'Processing Fee (3%)',
description: 'Transaction processing fee',
},
unit_amount: processingFee, // Processing fee in cents
},
quantity: 1,
},
];
// Create Stripe Checkout Session
const session = await stripe.checkout.sessions.create({
payment_method_types: ['card'],
line_items: lineItems,
mode: 'payment',
success_url: `${process.env.FRONTEND_URL}/checkout/success?session_id={CHECKOUT_SESSION_ID}`,
cancel_url: `${process.env.FRONTEND_URL}/checkout/cancel`,
customer_email: customerEmail,
metadata: {
tons: tons.toString(),
portfolioId: portfolioId.toString(),
baseAmount: baseAmount.toString(),
processingFee: processingFee.toString(),
},
});
// Store order in database
const order = Order.create({
stripeSessionId: session.id,
customerEmail: customerEmail || null,
tons,
portfolioId,
baseAmount,
processingFee,
totalAmount,
currency: 'USD',
});
console.log(`✅ Created checkout session: ${session.id}`);
console.log(` Order ID: ${order.id}`);
console.log(` Amount: $${(totalAmount / 100).toFixed(2)} (${tons} tons @ $${pricePerTon}/ton + 3% fee)`);
res.json({
sessionId: session.id,
url: session.url,
orderId: order.id,
});
} catch (error) {
console.error('❌ Checkout session creation error:', error);
res.status(500).json({
error: 'Failed to create checkout session',
message: error.message,
});
}
});
/**
* GET /api/checkout/session/:sessionId
* Retrieve checkout session details
*/
router.get('/session/:sessionId', async (req, res) => {
try {
const { sessionId } = req.params;
// Get order from database
const order = Order.findBySessionId(sessionId);
if (!order) {
return res.status(404).json({ error: 'Order not found' });
}
// Get Stripe session (optional - for additional details)
const session = await stripe.checkout.sessions.retrieve(sessionId);
res.json({
order: {
id: order.id,
tons: order.tons,
portfolioId: order.portfolio_id,
baseAmount: order.base_amount,
processingFee: order.processing_fee,
totalAmount: order.total_amount,
currency: order.currency,
status: order.status,
wrenOrderId: order.wren_order_id,
createdAt: order.created_at,
},
session: {
paymentStatus: session.payment_status,
customerEmail: session.customer_details?.email,
},
});
} catch (error) {
console.error('❌ Session retrieval error:', error);
res.status(500).json({
error: 'Failed to retrieve session',
message: error.message,
});
}
});
export default router;