Python / FastAPI Example
A complete implementation using Python, FastAPI, and Stripe.
Setup
pip install fastapi uvicorn stripe python-dotenvMain Server
from fastapi import FastAPI, Request, HTTPException
from pydantic import BaseModel
import stripe
import hmac
import hashlib
import base64
import uuid
from datetime import datetime, timedelta
app = FastAPI()
stripe.api_key = "sk_test_..."
SIGNING_SECRET = "your_secret"
sessions = {}
class CartItem(BaseModel):
product_id: str
quantity: int
class CreateSessionRequest(BaseModel):
cart_items: list[CartItem]
currency: str = "USD"
class PaymentData(BaseModel):
token: str
provider: str = "stripe"
class CompleteRequest(BaseModel):
payment_data: PaymentData
def verify_signature(request: Request):
signature = request.headers.get("signature")
timestamp = request.headers.get("timestamp")
body = request.body()
expected = base64.b64encode(
hmac.new(
SIGNING_SECRET.encode(),
f"{timestamp}.{body.decode()}".encode(),
hashlib.sha256
).digest()
).decode()
if not hmac.compare_digest(signature, expected):
raise HTTPException(401, {"error": {"code": "invalid_signature"}})
@app.post("/checkout_sessions")
async def create_session(body: CreateSessionRequest):
session_id = f"cs_{uuid.uuid4().hex[:24]}"
session = {
"checkout_session_id": session_id,
"state": "ready_for_payment",
"cart": {"items": [item.dict() for item in body.cart_items]},
"totals": {"total": 2999},
"expires_at": (datetime.utcnow() + timedelta(minutes=30)).isoformat(),
}
sessions[session_id] = session
return session
@app.post("/checkout_sessions/{session_id}/complete")
async def complete_checkout(session_id: str, body: CompleteRequest):
session = sessions.get(session_id)
if not session:
raise HTTPException(404, {"error": {"code": "session_not_found"}})
try:
stripe.PaymentIntent.create(
amount=session["totals"]["total"],
currency="usd",
payment_method=body.payment_data.token,
confirm=True,
payment_method_options={"card": {"request_delegated_payment": True}},
)
session["state"] = "completed"
session["order"] = {
"order_id": f"ord_{uuid.uuid4().hex[:16]}",
"status": "confirmed",
}
return session
except stripe.error.CardError:
raise HTTPException(400, {"error": {"code": "payment_declined"}})
if __name__ == "__main__":
import uvicorn
uvicorn.run(app, host="0.0.0.0", port=8000)Running
uvicorn main:app --reload --port 8000