> ## Documentation Index
> Fetch the complete documentation index at: https://ifdocdemo.lumivescence.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Protect an endpoint in Python, Go, or Rust

> Accept MPP and x402 with InFlow Payments in Python, Go, and Rust.

In Step 1 of the InFlow integration, accept MPP and x402 with the Python, Go, or Rust SDK. For Node.js, read [Protect an endpoint](/sellers/protect-an-endpoint).

## Before you start

| Item | Detail |
| - | - |
| Account | A Seller account and an API key. See [Create a Seller account](/sellers/create-a-seller-account). |
| Environment variables | `INFLOW_API_KEY`. MPP sellers also need `MPP_SECRET_KEY`. Generate it with `openssl rand -base64 32`. Keep it stable across deploys. |
| Environment | The examples use the sandbox. Use production when you go live. The value must match the dashboard that issued the key. |
| Runtime | Python 3.11, Go 1.26, or Rust 1.93, or later. |

Integrate both protocols in one service with shared pricing and fulfillment logic. Keep each request on one charging path. Features differ by SDK. See [SDKs](/reference/sdk-packages#feature-differences).

## Protect an endpoint

<Tabs>
  <Tab title="Python">
    The examples use FastAPI. The clients also work with other asynchronous frameworks.

    ### MPP

    ```bash theme={null}
    pip install 'inflowpay[mpp,fastapi]' uvicorn
    ```

    ```python theme={null}
    import os
    from contextlib import asynccontextmanager

    from fastapi import FastAPI, Request
    from mpp import Credential, Receipt
    from mpp.server.decorator import pay
    from starlette.responses import JSONResponse

    from inflowpay import ClientOptions
    from inflowpay.mpp.seller import Seller


    @asynccontextmanager
    async def application():
        async with await Seller.create(
            ClientOptions(environment="sandbox", api_key=os.environ["INFLOW_API_KEY"])
        ) as seller:
            app = FastAPI()

            @app.get("/report")
            @pay(
                intent=seller,
                method=seller.method,
                request=seller.charge_request({"amount": "0.50", "currency": "USDC"}),
                realm="api.example.com",
                secret_key=os.environ["MPP_SECRET_KEY"],
            )
            async def report(
                request: Request, credential: Credential, receipt: Receipt
            ) -> JSONResponse:
                return JSONResponse(
                    {"report": "Paid content"},
                    headers={"Payment-Receipt": receipt.to_payment_receipt()},
                )

            yield app
    ```

    ### x402

    ```bash theme={null}
    pip install 'inflowpay[x402,fastapi]' uvicorn
    ```

    ```python theme={null}
    import asyncio
    import os

    import uvicorn
    from fastapi import FastAPI
    from x402 import x402ResourceServer
    from x402.http.middleware.fastapi import payment_middleware
    from x402.http.types import RouteConfig

    from inflowpay import ClientOptions
    from inflowpay.x402.facilitator import Facilitator
    from inflowpay.x402.seller import Seller


    async def serve() -> None:
        options = ClientOptions(environment="sandbox", api_key=os.environ["INFLOW_API_KEY"])
        async with (
            await Seller.create(options) as seller,
            await Facilitator.create(options) as facilitator,
        ):
            resource_server = x402ResourceServer(facilitator)
            for registration in await seller.scheme_registrations():
                resource_server.register(registration["network"], registration["server"])

            routes = {
                "GET /report": RouteConfig(accepts=await seller.offers("$0.01")),
            }
            app = FastAPI()
            app.middleware("http")(payment_middleware(routes, resource_server))

            @app.get("/report")
            async def report() -> dict[str, str]:
                return {"report": "Your paid report"}

            await uvicorn.Server(uvicorn.Config(app, host="127.0.0.1", port=8000)).serve()


    asyncio.run(serve())
    ```

    The Python SDK does not support seller subscription endpoints or more than one InFlow offer on one endpoint.
  </Tab>

  <Tab title="Go">
    ```bash theme={null}
    go get github.com/inflowpayai/inflow-go@v0.3.1
    ```

    ### MPP

    `Protect` returns a standard `http.Handler`. Mount it on your `ServeMux`.

    ```go theme={null}
    client, err := seller.New(inflow.Options{
        Environment: inflow.Sandbox,
        APIKey: os.Getenv("INFLOW_API_KEY"),
    })
    if err != nil {
        return err
    }
    handler, err := client.Protect(seller.Route{
        Realm: "api.example.com",
        SecretKey: os.Getenv("MPP_SECRET_KEY"),
        Offers: []seller.Offer{{Charge: &mpp.ChargeRequest{
            Amount: "0.01",
            Currency: "USDC",
        }}},
    }, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        w.Write([]byte("paid response"))
    }))
    if err != nil {
        return err
    }
    mux.Handle("/paid", handler)
    ```

    ### x402

    The x402 seller uses the upstream x402 `nethttp` middleware.

    ```go theme={null}
    import (
        "context"
        "errors"
        "net/http"
        "os"

        inflow "github.com/inflowpayai/inflow-go"
        "github.com/inflowpayai/inflow-go/x402/seller"
        foundation "github.com/x402-foundation/x402/go/v2"
        xhttp "github.com/x402-foundation/x402/go/v2/http"
        "github.com/x402-foundation/x402/go/v2/http/nethttp"
    )

    func paidHandler(ctx context.Context, handler http.Handler) (http.Handler, error) {
        options := inflow.Options{Environment: inflow.Sandbox, APIKey: os.Getenv("INFLOW_API_KEY")}
        client, err := seller.New(options)
        if err != nil { return nil, err }
        facilitator, err := seller.NewFacilitator(options)
        if err != nil { return nil, err }
        route, err := client.Route(ctx, seller.RouteOptions{
            AcceptsOptions: seller.AcceptsOptions{Price: seller.PriceSpec{Amount: "$0.01"}},
        })
        if err != nil { return nil, err }
        if len(route.Accepts) == 0 { return nil, errors.New("no payment offers match this route") }
        registrations, err := client.SchemeRegistrations(ctx, seller.RegistrationOptions{})
        if err != nil { return nil, err }
        server := xhttp.Newx402HTTPResourceServer(
            xhttp.RoutesConfig{"GET /api/data": route},
            foundation.WithFacilitatorClient(facilitator),
        )
        for _, registration := range registrations {
            server.Register(registration.Network, registration.Server)
        }
        if err := server.Initialize(ctx); err != nil { return nil, err }
        return nethttp.PaymentMiddlewareFromHTTPServer(
            server, nethttp.WithSyncFacilitatorOnStart(false),
        )(handler), nil
    }
    ```
  </Tab>

  <Tab title="Rust">
    ### MPP

    Add `inflow-mpp-seller`, `inflow-mpp`, and `serde_json`. Create the seller and an offer.

    ```rust theme={null}
    use inflow_mpp_seller::{
        Authentication, CancellationToken, ChallengeOptions, ClientOptions,
        Environment, Method, Offer, Seller, SellerOptions,
    };
    use serde_json::json;

    async fn configure() -> Result<Offer, Box<dyn std::error::Error>> {
        let seller = Seller::new(
            ClientOptions {
                environment: Environment::Sandbox,
                authentication: Authentication::ApiKey(std::env::var("INFLOW_API_KEY")?),
                ..Default::default()
            },
            SellerOptions {
                realm: "api.example.com".into(),
                secret_key: std::env::var("MPP_SECRET_KEY")?,
            },
            &CancellationToken::new(),
        ).await?;
        Ok(seller.offer(
            Method::Inflow,
            json!({"amount": "0.10", "currency": "USDC"}),
            ChallengeOptions {
                description: Some("One search request".into()),
                requires_auth: true,
                ..Default::default()
            },
        )?)
    }
    ```

    Use the offer in your request handler. An unpaid request gets a `402` challenge. A paid request gets a receipt.

    ```rust theme={null}
    use inflow_mpp_seller::{
        CancellationToken, Error, Offer, decode_credential, render_challenge,
    };

    async fn payment_response(
        offer: &Offer,
        payment_authorization: Option<&str>,
        body: &[u8],
        cancellation: &CancellationToken,
    ) -> Result<(u16, &'static str, String, &'static str), Error> {
        let Some(header) = payment_authorization else {
            let challenge = offer.challenge(Some(body))?;
            return Ok((402, "WWW-Authenticate", render_challenge(&challenge)?, ""));
        };
        let (_, encoded) = header.split_once(' ')
            .filter(|(scheme, _)| scheme.eq_ignore_ascii_case("Payment"))
            .ok_or_else(|| Error::new("INVALID_MPP_DATA", "expected Payment credential"))?;
        let credential = decode_credential(encoded.trim())?;
        let receipt = offer.accept(&credential, Some(body), cancellation).await?;
        let header = inflow_mpp::encode(&serde_json::to_value(receipt)
            .map_err(|_| Error::new("INVALID_MPP_DATA", "invalid receipt"))?)?;
        // Run the paid operation only after accept returns a receipt.
        Ok((200, "Payment-Receipt", header, "paid result"))
    }
    ```

    The handler runs inside any HTTP framework that uses the Tokio runtime of your application.

    ### x402 with Axum

    `inflow-x402-axum` adds a payment layer to an Axum endpoint.

    ```rust theme={null}
    use axum::{Router, routing::get};
    use inflow_x402_axum::payment_layer;
    use inflow_x402_seller::{Seller, OfferOptions, CancellationToken};

    async fn example(seller: Seller) -> Result<Router, Box<dyn std::error::Error>> {
        let route = seller.route(&OfferOptions::new("$0.01"),
            &CancellationToken::new()).await?;
        let layer = payment_layer(seller.facilitator(), route,
            "https://service.example/report")?;
        let app = Router::new().route("/report",
            get(|| async { "Your report" }).route_layer(layer));
        Ok(app)
    }
    ```

    The Rust SDK does not support seller subscriptions. The Axum layer rejects automatic `upto` endpoints, because the upstream middleware settles the full authorized amount.
  </Tab>
</Tabs>

Each SDK repository has runnable sandbox examples. See [SDKs](/reference/sdk-packages#examples).

## Next step

<Card title="Test a payment" icon="flask" href="/sellers/test-a-payment">
  Confirm the `402` challenge. Then pay your endpoint with the CLI in the sandbox.
</Card>


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.