Payment Service
Handles Stripe payment processing, webhooks, and checkout sessions
Payment Service
The Payment Service manages all payment-related operations for the e-commerce platform, integrating with Stripe for secure payment processing. Built with Hono and TypeScript, it handles checkout session creation, webhook processing, and payment verification.
🛠️ Technology Stack
- Runtime: Node.js 18+
- Framework: Hono with TypeScript
- Payment Provider: Stripe API and webhooks
- Validation: Zod for runtime type checking
- Events: Kafka producer for payment events
- Security: Stripe webhook signature verification
- Error Handling: Custom error classes and global error handler
- Logging: Winston for structured logging
🏗️ Architecture
Service Structure
apps/payment-service/
├── src/
│ ├── controllers/ # Request handlers
│ ├── middleware/ # Custom middleware
│ ├── routes/ # Route definitions
│ ├── services/ # Business logic services
│ ├── utils/ # Utility functions
│ ├── webhooks/ # Stripe webhook handlers
│ ├── consumers/ # Kafka event consumers
│ ├── index.ts # Application entry point
│ └── types/ # Local type definitions
├── package.jsonKey Components
- Checkout Controller: Handles checkout session creation
- Webhook Handler: Processes Stripe webhook events
- Product Verification: Validates product prices from Stripe
- Event Publisher: Publishes payment events to Kafka
🔧 Core Functionality
Checkout Session Management
Create Checkout Session
Creates a Stripe checkout session for customer payment.
Endpoint: POST /api/payments/create-checkout-session
Request Body:
{
products: Array<{
name: string;
price: number;
quantity: number;
image?: string;
}>;
successUrl: string;
cancelUrl: string;
customerEmail?: string;
metadata?: Record<string, string>;
}Response:
{
sessionId: string;
url: string; // Stripe checkout URL
clientSecret?: string;
}Checkout Session Example
// Create checkout session from cart data
const checkoutData = {
products: [
{
name: 'Premium T-Shirt',
price: 29.99,
quantity: 2,
image: 'https://example.com/tshirt.jpg',
},
{
name: 'Designer Jeans',
price: 89.99,
quantity: 1,
image: 'https://example.com/jeans.jpg',
},
],
successUrl: 'https://yourstore.com/success?session_id={CHECKOUT_SESSION_ID}',
cancelUrl: 'https://yourstore.com/cart',
customerEmail: 'customer@example.com',
};
const response = await fetch('/api/payments/create-checkout-session', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(checkoutData),
});Webhook Processing
Stripe Webhook Handler
Processes incoming Stripe webhook events securely.
Endpoint: POST /api/payments/webhooks/stripe
Security Features:
- Signature Verification: Validates Stripe webhook signatures
- Event Deduplication: Prevents processing duplicate events
- Error Handling: Graceful handling of webhook processing failures
Supported Webhook Events
checkout.session.completed- Payment successfulcheckout.session.expired- Checkout session expiredpayment_intent.succeeded- Payment intent succeededpayment_intent.payment_failed- Payment failedinvoice.payment_succeeded- Invoice payment succeeded
Webhook Processing Logic
// Webhook event handler
app.post('/webhooks/stripe', async (c) => {
const body = await c.req.text();
const signature = c.req.header('stripe-signature');
try {
// Verify webhook signature
const event = stripe.webhooks.constructEvent(
body,
signature,
process.env.STRIPE_WEBHOOK_SECRET
);
// Process event based on type
switch (event.type) {
case 'checkout.session.completed':
await handleCheckoutCompleted(event.data.object);
break;
case 'payment_intent.payment_failed':
await handlePaymentFailed(event.data.object);
break;
default:
console.log(`Unhandled event type: ${event.type}`);
}
return c.json({ received: true });
} catch (error) {
console.error('Webhook signature verification failed:', error);
return c.json({ error: 'Invalid signature' }, 400);
}
});Product Price Verification
Verify Product Prices
Validates product prices against Stripe's price list for security.
Endpoint: POST /api/payments/verify-prices
Request Body:
{
products: Array<{
productId: string;
price: number;
quantity: number;
}>;
}Response:
{
verified: boolean;
discrepancies?: Array<{
productId: string;
expectedPrice: number;
providedPrice: number;
}>;
}🔄 Event Publishing
The Payment Service publishes payment events to Kafka:
Payment Events
payment.successful
Published when a payment is successfully processed.
Event Payload:
{
eventId: string;
eventType: 'payment.successful';
timestamp: string;
data: {
userId: string;
email: string;
amount: number;
currency: string;
products: Array<{
name: string;
quantity: number;
price: number;
}>;
paymentIntentId: string;
sessionId: string;
};
}payment.failed
Published when a payment fails.
Event Payload:
{
eventId: string;
eventType: 'payment.failed';
timestamp: string;
data: {
userId: string;
email: string;
amount: number;
reason: string;
paymentIntentId?: string;
};
}🛡️ Security Features
Stripe Integration Security
- Webhook Signature Verification: Ensures webhook authenticity
- API Key Management: Secure storage of Stripe API keys
- PCI Compliance: No card data storage in the service
Input Validation
import { z } from 'zod';
export const CreateCheckoutSessionSchema = z.object({
products: z.array(z.object({
name: z.string().min(1),
price: z.number().min(0.01),
quantity: z.number().min(1),
image: z.string().url().optional(),
})).min(1),
successUrl: z.string().url(),
cancelUrl: z.string().url(),
customerEmail: z.string().email().optional(),
metadata: z.record(z.string()).optional(),
});Error Handling
// Custom error classes
export class StripeError extends Error {
constructor(message: string, public code?: string) {
super(message);
this.name = 'StripeError';
}
}
export class PriceVerificationError extends Error {
constructor(message: string) {
super(message);
this.name = 'PriceVerificationError';
}
}🚀 Performance & Scalability
Webhook Processing
- Asynchronous Processing: Non-blocking webhook event handling
- Retry Logic: Automatic retry for failed webhook processing
- Event Deduplication: Prevent duplicate event processing
Caching Strategy
- Price Cache: Cache Stripe price data for verification
- Session Cache: Cache checkout session data temporarily
📊 Monitoring & Logging
Payment Analytics
- Payment Success Rate: Track payment completion rates
- Revenue Tracking: Monitor daily/monthly revenue
- Error Tracking: Log payment failures and reasons
Structured Logging
// Payment success logging
logger.info('Payment processed successfully', {
sessionId: session.id,
paymentIntentId: session.payment_intent,
amount: session.amount_total,
currency: session.currency,
customerEmail: session.customer_details?.email,
});
// Payment failure logging
logger.error('Payment failed', {
paymentIntentId: paymentIntent.id,
amount: paymentIntent.amount,
currency: paymentIntent.currency,
lastError: paymentIntent.last_payment_error,
});🔧 Development & Deployment
Environment Configuration
# Required environment variables
STRIPE_SECRET_KEY=sk_test_...
STRIPE_WEBHOOK_SECRET=whsec_...
KAFKA_BROKERS=localhost:9092
PORT=3006Stripe Dashboard Setup
- Create Stripe Account: Set up Stripe account and get API keys
- Webhook Configuration: Configure webhook endpoint in Stripe dashboard
- Product Creation: Create products and prices in Stripe (automated via events)
Development Setup
# Install dependencies
pnpm install
# Start Stripe CLI for webhook testing
stripe listen --forward-to localhost:3006/api/payments/webhooks/stripe
# Start development server
pnpm devDocker Configuration
FROM node:18-alpine
WORKDIR /app
COPY package*.json ./
RUN pnpm install --frozen-lockfile
COPY . .
EXPOSE 3006
CMD ["pnpm", "start"]🧪 Testing
Test Structure
- Unit Tests: Test individual payment functions
- Integration Tests: Test Stripe API integration
- Webhook Tests: Test webhook event processing
Test Examples
describe('Payment Service', () => {
describe('POST /api/payments/create-checkout-session', () => {
it('should create checkout session successfully', async () => {
const checkoutData = {
products: [
{
name: 'Test Product',
price: 29.99,
quantity: 1,
},
],
successUrl: 'https://example.com/success',
cancelUrl: 'https://example.com/cancel',
};
const response = await request(app)
.post('/api/payments/create-checkout-session')
.send(checkoutData)
.expect(200);
expect(response.body).toHaveProperty('sessionId');
expect(response.body).toHaveProperty('url');
});
});
describe('Stripe Webhooks', () => {
it('should process checkout.session.completed', async () => {
const webhookEvent = {
id: 'evt_test_webhook',
type: 'checkout.session.completed',
data: {
object: {
id: 'cs_test_123',
payment_intent: 'pi_test_123',
amount_total: 2999,
currency: 'usd',
customer_details: {
email: 'test@example.com',
},
},
},
};
// Mock Stripe webhook verification
jest.spyOn(stripe.webhooks, 'constructEvent').mockReturnValue(webhookEvent);
const response = await request(app)
.post('/api/payments/webhooks/stripe')
.send(webhookEvent)
.expect(200);
expect(response.body).toEqual({ received: true });
});
});
});🔮 Future Enhancements
Planned Features
- Multiple Payment Methods: Support for PayPal, Apple Pay, Google Pay
- Subscription Management: Recurring payment support
- Payment Analytics: Advanced payment insights and reporting
- Fraud Detection: Integration with Stripe Radar
- Multi-currency Support: Support for international currencies
- Payment Links: Shareable payment links for products
Performance Improvements
- Webhook Queue: Queue-based webhook processing for high volume
- Advanced Caching: Redis integration for session and price caching
- Load Balancing: Horizontal scaling for high traffic
- Database Integration: Persistent storage for payment records
Security Enhancements
- Rate Limiting: API rate limiting for payment endpoints
- Advanced Fraud Detection: Custom fraud detection rules
- PCI Compliance: Enhanced PCI compliance measures
- Audit Logging: Comprehensive audit trail for all payment operations