Trước khi bắt đầu
| Mục | Chi tiết |
|---|---|
| Tài khoản | Một tài khoản Seller và một API key. Xem Tạo tài khoản Seller. |
| Biến môi trường | INFLOW_API_KEY. Người bán dùng MPP cũng cần MPP_SECRET_KEY. Tạo giá trị này bằng openssl rand -base64 32. Giữ nguyên giá trị này qua các lần deploy. |
| Môi trường | Các ví dụ dùng sandbox. Dùng production khi bạn chuyển sang production. Giá trị này phải khớp với dashboard cấp key. |
| Runtime | Python 3.11, Go 1.26 hoặc Rust 1.93, hoặc phiên bản mới hơn. |
Bảo vệ endpoint
- Python
- Go
- Rust
Các ví dụ dùng FastAPI. Các client này cũng hoạt động với các framework bất đồng bộ khác.SDK Python không hỗ trợ endpoint thuê bao của người bán hoặc nhiều hơn một offer InFlow trên một endpoint.
MPP
pip install 'inflowpay[mpp,fastapi]' uvicorn
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
pip install 'inflowpay[x402,fastapi]' uvicorn
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())
go get github.com/inflowpayai/inflow-go@v0.3.2
MPP
Protect trả về một http.Handler chuẩn. Gắn handler này vào ServeMux của bạn.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
Người bán x402 dùng middlewarenethttp của x402 upstream.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
}
MPP
Thêminflow-mpp-seller, inflow-mpp và serde_json. Tạo đối tượng người bán và một offer.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()
},
)?)
}
402. Request đã thanh toán nhận một receipt.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"))
}
x402 với Axum
inflow-x402-axum thêm một lớp thanh toán vào một endpoint Axum.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)
}
upto tự động, vì middleware upstream quyết toán toàn bộ số tiền đã ủy quyền.Bước tiếp theo
Kiểm thử thanh toán
Xác nhận challenge
402. Sau đó thanh toán cho endpoint của bạn bằng CLI trong sandbox.