Automated Action 2adbcd0535 Complete multi-tenant SaaS platform with external integrations
- Implemented comprehensive multi-tenant data isolation using database-level security
- Built JWT authentication system with role-based access control (Super Admin, Org Admin, User, Viewer)
- Created RESTful API endpoints for user and organization operations
- Added complete audit logging for all data modifications with IP tracking
- Implemented API rate limiting and input validation with security middleware
- Built webhook processing engine with async event handling and retry logic
- Created external API call handlers with circuit breaker pattern and error handling
- Implemented data synchronization between external services and internal data
- Added integration health monitoring and status tracking
- Created three mock external services (User Management, Payment, Communication)
- Implemented idempotency for webhook processing to handle duplicates gracefully
- Added comprehensive security headers and XSS/CSRF protection
- Set up Alembic database migrations with proper SQLite configuration
- Included extensive documentation and API examples

Architecture features:
- Multi-tenant isolation at database level
- Circuit breaker pattern for external API resilience
- Async background task processing
- Complete audit trail with user context
- Role-based permission system
- Webhook signature verification
- Request validation and sanitization
- Health monitoring endpoints

Co-Authored-By: Claude <noreply@anthropic.com>
2025-06-27 21:14:30 +00:00

156 lines
5.2 KiB
Python

from fastapi import FastAPI, HTTPException
from pydantic import BaseModel
from typing import List, Dict, Any, Optional
from datetime import datetime
import uuid
from enum import Enum
app = FastAPI(title="Mock Payment Service", version="1.0.0")
class SubscriptionStatus(str, Enum):
ACTIVE = "active"
CANCELLED = "cancelled"
EXPIRED = "expired"
class PaymentStatus(str, Enum):
PENDING = "pending"
SUCCEEDED = "succeeded"
FAILED = "failed"
class Subscription(BaseModel):
id: Optional[str] = None
organization_id: int
plan_name: str
status: SubscriptionStatus = SubscriptionStatus.ACTIVE
amount: float
currency: str = "USD"
billing_cycle: str = "monthly"
created_at: Optional[datetime] = None
updated_at: Optional[datetime] = None
class Payment(BaseModel):
id: Optional[str] = None
organization_id: int
subscription_id: Optional[str] = None
amount: float
currency: str = "USD"
status: PaymentStatus = PaymentStatus.PENDING
description: Optional[str] = None
created_at: Optional[datetime] = None
updated_at: Optional[datetime] = None
# Mock data storage
subscriptions_db: Dict[str, Dict[str, Any]] = {}
payments_db: Dict[str, Dict[str, Any]] = {}
organization_billing: Dict[int, List[str]] = {}
@app.get("/health")
async def health_check():
return {"status": "healthy", "service": "payment", "timestamp": datetime.utcnow()}
@app.post("/subscriptions", response_model=Subscription)
async def create_subscription(subscription: Subscription):
subscription_id = str(uuid.uuid4())
subscription.id = subscription_id
subscription.created_at = datetime.utcnow()
subscriptions_db[subscription_id] = subscription.dict()
# Add to organization billing
if subscription.organization_id not in organization_billing:
organization_billing[subscription.organization_id] = []
organization_billing[subscription.organization_id].append(subscription_id)
# Send webhook (simulated)
await send_webhook("subscription.created", subscription.dict())
return subscription
@app.get("/subscriptions/{subscription_id}", response_model=Subscription)
async def get_subscription(subscription_id: str):
if subscription_id not in subscriptions_db:
raise HTTPException(status_code=404, detail="Subscription not found")
return Subscription(**subscriptions_db[subscription_id])
@app.delete("/subscriptions/{subscription_id}")
async def cancel_subscription(subscription_id: str):
if subscription_id not in subscriptions_db:
raise HTTPException(status_code=404, detail="Subscription not found")
subscription_data = subscriptions_db[subscription_id].copy()
subscription_data["status"] = SubscriptionStatus.CANCELLED
subscription_data["updated_at"] = datetime.utcnow()
subscriptions_db[subscription_id] = subscription_data
# Send webhook (simulated)
await send_webhook("subscription.cancelled", subscription_data)
return {"message": "Subscription cancelled successfully"}
@app.post("/payments", response_model=Payment)
async def process_payment(payment: Payment):
payment_id = str(uuid.uuid4())
payment.id = payment_id
payment.created_at = datetime.utcnow()
# Simulate payment processing
import random
success_rate = 0.85 # 85% success rate
if random.random() < success_rate:
payment.status = PaymentStatus.SUCCEEDED
event_type = "payment.succeeded"
else:
payment.status = PaymentStatus.FAILED
event_type = "payment.failed"
payment.updated_at = datetime.utcnow()
payments_db[payment_id] = payment.dict()
# Add to organization billing
if payment.organization_id not in organization_billing:
organization_billing[payment.organization_id] = []
organization_billing[payment.organization_id].append(payment_id)
# Send webhook (simulated)
await send_webhook(event_type, payment.dict())
return payment
@app.get("/organizations/{organization_id}/billing")
async def get_billing_history(organization_id: int):
if organization_id not in organization_billing:
return {"subscriptions": [], "payments": []}
org_subscriptions = []
org_payments = []
for item_id in organization_billing[organization_id]:
if item_id in subscriptions_db:
org_subscriptions.append(subscriptions_db[item_id])
elif item_id in payments_db:
org_payments.append(payments_db[item_id])
return {
"subscriptions": org_subscriptions,
"payments": org_payments,
"total_subscriptions": len(org_subscriptions),
"total_payments": len(org_payments)
}
async def send_webhook(event_type: str, data: Dict[str, Any]):
"""Simulate sending webhook to main service"""
webhook_data = {
"event_id": str(uuid.uuid4()),
"event_type": event_type,
"timestamp": datetime.utcnow().isoformat(),
"data": data
}
# In a real implementation, this would send HTTP request to main service
print(f"Webhook sent: {event_type} - {webhook_data['event_id']}")
return webhook_data
if __name__ == "__main__":
import uvicorn
uvicorn.run(app, host="0.0.0.0", port=8002)