Rakshith Aloori
How Stripe Routes Test and Live Traffic10th September 2024
Stripe API uses a single endpoint for both test and live traffic. As a user/developer, this is very convenient because switching from test mode to live mode only requires changing one variable – Stripe API Key.But how does Stripe use the same API endpoint and still keep the test and live traffic separate?The solution is simple, elegant and beautiful.*
    These are the constraints Stripe has to work with:
  1. Use the same endpoint for both test and live traffic.
  2. Keep test and live deployments seperate.
What if we use a proxy server?Yes! The server at 'https://api.stripe.com' is a proxy server.This proxy server looks at the secret key(sk_test_xxxx or sk_live_xxxx) and routes the request either to the test or live Stripe API deployment.This is what the infrastructure looks like.Stripe InfraCaveat: Using an extra layer of infrastructure adds on to your Cloud bill. But, hey, the simplicity is worth it.Here's a FastAPI implementation of the above logic.
1import base64
2import httpx
3
4from fastapi import FastAPI, Request
5from fastapi.responses import JSONResponse
6
7app = FastAPI()
8
9@app.middleware("http")
10async def get_credentials(request: Request, _):
11    auth = request.headers.get("Authorization")
12    _, data = (auth or " ").split(" ", 1)
13    secret_key, _ = base64.b64decode(data).decode().split(":", 1)
14    _, mode, _ = secret_key.split("_")
15
16    ENDPOINT = None
17    if mode == "test":
18        ENDPOINT = TEST_ENDPOINT
19    elif mode == "live":
20        ENDPOINT = LIVE_ENDPOINT
21
22    async with httpx.AsyncClient() as client:
23        # Construct the new URL with the original path
24        new_url = f"{ENDPOINT.rstrip('/')}{request.url.path}"
25        # Properly encode query parameters
26        if request.url.query:
27            new_url = f"{new_url}?{request.url.query}"
28
29        response = await client.request(
30            method=request.method,
31            url=new_url,
32            headers=request.headers.raw,
33            content=await request.body(),
34            timeout=10,
35        )
36        # Return the response
37        return JSONResponse(
38            content=response.json(),
39            status_code=response.status_code,
40            headers=dict(response.headers),
41        )
42
*This is how I think it works. Stripe may be doing something else but this solution fits.
WritingsProjectsAnthology