Client
Handle 402 responses automatically
The Fetch extension trait adds .send_with_payment() to any reqwest::RequestBuilder—when a server returns 402, it parses the Challenge, signs a stablecoin transfer, and retries with the Credential.
Quick start
use mpp::client::{Fetch, TempoProvider};
use mpp::PrivateKeySigner;
let signer = PrivateKeySigner::random();
let provider = TempoProvider::new(signer, "https://rpc.tempo.xyz")?;
let response = reqwest::Client::new()
.get("https://api.example.com/paid")
.send_with_payment(&provider)
.await?;
println!("Status: {}", response.status());When the server returns 402, send_with_payment:
- Parses the Challenge from the
WWW-Authenticateheader - Calls
provider.pay()to sign a stablecoin transfer - Retries the request with the Credential in the
Authorizationheader
TempoProvider
TempoProvider signs TIP-20 stablecoin transfers on the Tempo blockchain.
use mpp::client::TempoProvider;
use mpp::PrivateKeySigner;
let signer: PrivateKeySigner = "0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80"
.parse()?;
let provider = TempoProvider::new(signer, "https://rpc.tempo.xyz")?;With client ID
Attach an identifier to transactions for attribution:
let provider = TempoProvider::new(signer, "https://rpc.tempo.xyz")?
.with_client_id("my-app");Middleware
For automatic 402 handling on all requests, use PaymentMiddleware with reqwest-middleware. Requires the middleware feature.
mpp = { version = "0.10.3", features = ["tempo", "client", "middleware"] }use mpp::client::{PaymentMiddleware, TempoProvider};
use reqwest_middleware::ClientBuilder;
let provider = TempoProvider::new(signer, "https://rpc.tempo.xyz")?;
let client = ClientBuilder::new(reqwest::Client::new())
.with(PaymentMiddleware::new(provider))
.build();
// All requests through this client handle 402 automatically
let response = client.get("https://api.example.com/paid").send().await?;Event handling
Use ClientEvents to record the automatic 402 flow. Use helper methods for named events, on() for one event kind, and on_any() for every client event.
use mpp::client::{
ClientEvent, ClientEventKind, ClientEvents, PaymentMiddleware, TempoProvider,
};
use reqwest_middleware::ClientBuilder;
let provider = TempoProvider::new(signer, "https://rpc.tempo.xyz")?;
let events = ClientEvents::default();
// Record the selected Challenge before the provider creates a Credential.
let _challenge = events.on_challenge_received(|ctx| async move {
println!("challenge received: {}", ctx.challenge.id);
None
});
// Record the retried response after payment.
let _response = events.on(ClientEventKind::PaymentResponse, |event| async move {
if let ClientEvent::PaymentResponse(ctx) = event {
println!("payment response: {}", ctx.status);
}
});
// Catch every client payment event in one handler.
let _all = events.on_any(|event| async move {
println!("payment event: {}", event.kind().as_str());
});
let client = ClientBuilder::new(reqwest::Client::new())
.with(PaymentMiddleware::new(provider).with_events(events))
.build();on_challenge_received can return Some(PaymentCredential) to override the default provider payment flow. Other handlers only record payment handling. Keep the returned subscription handles alive while handlers remain registered.
Multiple providers
MultiProvider wraps multiple payment providers and picks the right one based on the Challenge's method and intent:
use mpp::client::{MultiProvider, TempoProvider};
let provider = MultiProvider::new()
.with(TempoProvider::new(signer, "https://rpc.tempo.xyz")?);
let response = reqwest::Client::new()
.get("https://api.example.com/paid")
.send_with_payment(&provider)
.await?;Custom provider
Implement the PaymentProvider trait to add support for custom payment methods:
use mpp::client::PaymentProvider;
use mpp::{PaymentChallenge, PaymentCredential, PaymentPayload, MppError};
#[derive(Clone)]
struct MyProvider;
impl PaymentProvider for MyProvider {
fn supports(&self, method: &str, intent: &str) -> bool {
method == "my_network" && intent == "charge"
}
async fn pay(
&self,
challenge: &PaymentChallenge,
) -> Result<PaymentCredential, MppError> {
let echo = challenge.to_echo();
Ok(PaymentCredential::new(echo, PaymentPayload::hash("0x...")))
}
}Key types
| Type | Description |
|---|---|
Fetch (PaymentExt) | Extension trait that adds send_with_payment to reqwest::RequestBuilder |
MultiProvider | Wraps multiple providers, routes by method and intent |
PaymentMiddleware | reqwest-middleware for automatic 402 handling |
PaymentProvider | Trait for custom payment method implementations |
TempoProvider | Signs Tempo stablecoin transfers |