Can You Supercharge Your FastAPI App with Stripe for Seamless Payments?

Empowering Your FastAPI with Stripe: A Seamless Payment Integration Adventure

Can You Supercharge Your FastAPI App with Stripe for Seamless Payments?

Imagine the scene: You’re sitting at your computer, coffee within reach, and you’ve got this brilliant idea for a web application. Of course, no modern app is complete without payment processing. Enter Stripe—a treasure trove for handling online payments. But how do you marry Stripe with FastAPI, your go-to for building fast and efficient web APIs? It’s not only possible but also simpler than you might think.

First things first, setting up your Stripe account is a breeze. Just hop over to Stripe’s website and sign up. You’ll find your API keys in the dashboard once you’re logged in. These keys are like the magic incantations that allow your app to talk to Stripe. During development, use the test API keys—no one wants to accidentally charge real customers while testing!

Next up, let’s get your development environment ready. Fire up your terminal and run:

pip install fastapi uvicorn python-dotenv stripe

Voila! FastAPI, Uvicorn (for running the server), and the Stripe library are installed. Your toolkit is nearly complete.

Now, it’s another good habit to stash sensitive stuff like API keys in environment variables. Create a file .env in your project’s root directory and add this:

STRIPE_PUBLISHABLE_KEY=pk_test_PUBLIC_KEY_HERE
STRIPE_SECRET_KEY=pk_test_SECRET_KEY_HERE

These will be your shields, protecting your secrets from prying eyes.

Alright, it’s time to get our hands dirty with some code. Start by setting up a basic FastAPI app and configuring it to use Stripe. Here’s a snippet to kick things off:

import os
from fastapi import FastAPI, HTTPException
from fastapi.middleware.cors import CORSMiddleware
import stripe

# Load environment variables
stripe.api_key = os.environ['STRIPE_SECRET_KEY']
YOUR_DOMAIN = os.environ['YOUR_DOMAIN']

# Initialize FastAPI app
app = FastAPI()

# CORS configuration
origins = ["*"]
app.add_middleware(
    CORSMiddleware,
    allow_origins=origins,
    allow_credentials=True,
    allow_methods=["*"],
    allow_headers=["*"],
)

With your FastAPI app initialized, let’s move to the heart of the integration—creating a checkout session. This session will transport your user to Stripe’s secure checkout page, where they can safely input their payment details.

Here’s a quick-and-dirty version of how you can set this up:

from pydantic import BaseModel

class Item(BaseModel):
    price: int
    quantity: int

@app.post("/create-checkout-session")
async def create_checkout_session(item: Item):
    try:
        checkout_session = stripe.checkout.Session.create(
            payment_method_types=["card"],
            line_items=[
                {
                    "price_data": {
                        "currency": "usd",
                        "unit_amount": item.price,
                        "product_data": {
                            "name": "Your Product",
                        },
                    },
                    "quantity": item.quantity,
                },
            ],
            mode="payment",
            success_url=YOUR_DOMAIN + "/success",
            cancel_url=YOUR_DOMAIN + "/cancel",
        )
        return {"id": checkout_session.id}
    except Exception as e:
        raise HTTPException(status_code=500, detail=str(e))

That’s it! Your FastAPI app now knows how to create a Stripe checkout session. Now, let’s make sure everything is working by running your app. Head to your project directory and run:

uvicorn main:app --reload

This command will spin up your server on port 8000, so you can now access your app at http://127.0.0.1:8000.

But what about the frontend, you ask? Good question. You’ll need to add a button that redirects the user to Stripe’s checkout page. Here’s a small HTML snippet that does exactly that:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Payment Page</title>
</head>
<body>
    <button id="checkout-button">Checkout</button>

    <script>
        const checkoutButton = document.getElementById('checkout-button');
        checkoutButton.addEventListener('click', async () => {
            const response = await fetch('/create-checkout-session', {
                method: 'POST',
                headers: {
                    'Content-Type': 'application/json',
                },
                body: JSON.stringify({
                    price: 1000, // Price in cents
                    quantity: 1,
                }),
            });

            const session = await response.json();
            const stripe = Stripe('YOUR_PUBLISHABLE_STRIPE_API_KEY');
            stripe.redirectToCheckout({ sessionId: session.id });
        });
    </script>
</body>
</html>

Replace the placeholder YOUR_PUBLISHABLE_STRIPE_API_KEY with your actual Stripe publishable API key. And boom! Your frontend is now ready to handle payments.

But what happens after the payment? Stripe will redirect the user to your specified success or cancel URLs. You can handle these URL routes in FastAPI to display grateful or apologetic messages.

@app.get("/success")
async def success():
    return {"message": "Payment successful!"}

@app.get("/cancel")
async def cancel():
    return {"message": "Payment cancelled."}

Now, if you fancy more control over the payment process, consider setting up a custom checkout page. This approach is trickier but offers full control over design and functionality.

First, you’ll need an endpoint to create a payment intent:

@app.post("/create-payment-intent")
async def create_payment_intent(item: Item):
    try:
        payment_intent = stripe.PaymentIntent.create(
            amount=item.price * item.quantity,
            currency="usd",
            payment_method_types=["card"],
        )
        return {"client_secret": payment_intent.client_secret}
    except Exception as e:
        raise HTTPException(status_code=500, detail=str(e))

On the frontend, use Stripe’s JavaScript library to confirm the payment with the client secret:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Custom Payment Page</title>
    <script src="https://js.stripe.com/v3/"></script>
</head>
<body>
    <form id="payment-form">
        <label for="card-element">Credit or debit card</label>
        <div id="card-element"></div>

        <button id="submit">Submit Payment</button>
        <p id="payment-status"></p>
    </form>

    <script>
        const stripe = Stripe('YOUR_PUBLISHABLE_STRIPE_API_KEY');
        const elements = stripe.elements();
        const cardElement = elements.create('card');
        cardElement.mount('#card-element');

        const form = document.getElementById('payment-form');
        form.addEventListener('submit', async (event) => {
            event.preventDefault();

            const response = await fetch('/create-payment-intent', {
                method: 'POST',
                headers: {
                    'Content-Type': 'application/json',
                },
                body: JSON.stringify({
                    price: 1000,
                    quantity: 1,
                }),
            });

            const { client_secret } = await response.json();

            stripe.confirmCardPayment(client_secret, {
                payment_method: {
                    card: cardElement,
                    billing_details: {
                        name: 'Jenny Rosen',
                    },
                },
            }).then((result) => {
                if (result.error) {
                    document.getElementById('payment-status').textContent = result.error.message;
                } else {
                    if (result.paymentIntent.status === 'succeeded') {
                        document.getElementById('payment-status').textContent = 'Payment succeeded!';
                    }
                }
            });
        });
    </script>
</body>
</html>

Sure, this is just scratching the surface, but it illustrates the basic structure for a custom checkout process. Of course, you’ll need to handle errors and other edge cases more carefully in a real-world app.

The gist is that integrating Stripe with FastAPI doesn’t have to be a headache. Whether you use Stripe’s built-in checkout or create a custom flow, these steps provide a solid roadmap for getting started. So go forth, build cool stuff, and let the payments flow effortlessly!