mpay 0.2.0

Machine Payment Protocol - Rust library for Web Payment Auth
Documentation
# reqwest Client Example

Using `mpay` with [reqwest](https://docs.rs/reqwest) for client-side 402 handling.

## Dependencies

```toml
[dependencies]
mpay = { version = "0.1", features = ["http", "tempo"] }
reqwest = { version = "0.12", features = ["json"] }
tokio = { version = "1", features = ["full"] }
```

## Using Fetch (Recommended)

The `Fetch` trait provides a `.send_with_payment()` method for opt-in
per-request payment handling. This is the most idiomatic Rust approach.

```rust
use mpay::client::{Fetch, TempoProvider};
use mpay::PrivateKeySigner;  // Requires `evm` or `tempo` feature
use reqwest::Client;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    // Set up your signer (from private key, hardware wallet, etc.)
    let signer = PrivateKeySigner::random();
    
    // Create a Tempo payment provider
    let provider = TempoProvider::new(signer, "https://rpc.moderato.tempo.xyz")?;
    
    // Use standard reqwest client
    let client = Client::new();
    
    // Make a request - automatically handles 402 responses
    let resp = client
        .get("https://api.example.com/paid-resource")
        .send_with_payment(&provider)
        .await?;
    
    println!("Status: {}", resp.status());
    println!("Body: {}", resp.text().await?);
    
    Ok(())
}
```

## Flow

When using `.send_with_payment()`:

1. Initial request is sent
2. If response is 402 Payment Required:
   - Challenge is parsed from `WWW-Authenticate` header
   - Provider executes payment (signs and broadcasts transaction)
   - Request is retried with credential in `Authorization` header
3. Final response is returned

## Manual 402 Handling

For full control, handle the 402 flow manually:

```rust
use mpay::{parse_www_authenticate, PaymentCredential, PaymentPayload, format_authorization};
use reqwest::Client;

async fn fetch_paid_resource(
    client: &Client,
    url: &str,
) -> Result<String, Box<dyn std::error::Error>> {
    // Initial request
    let resp = client.get(url).send().await?;

    if resp.status() == reqwest::StatusCode::PAYMENT_REQUIRED {
        // Parse challenge from WWW-Authenticate header
        let header = resp
            .headers()
            .get("www-authenticate")
            .ok_or("missing www-authenticate")?
            .to_str()?;

        let challenge = parse_www_authenticate(header)?;

        // Execute payment (your logic here)
        let tx_hash = execute_payment(&challenge).await?;

        // Build credential
        let credential = PaymentCredential::with_source(
            challenge.to_echo(),
            "did:pkh:eip155:42431:0xYourAddress",
            PaymentPayload::hash(&tx_hash),
        );

        // Retry with payment credential
        let auth_header = format_authorization(&credential)?;
        let resp = client
            .get(url)
            .header("authorization", auth_header)
            .send()
            .await?;

        return Ok(resp.text().await?);
    }

    Ok(resp.text().await?)
}
```

## With Receipt Parsing

```rust
use mpay::{Receipt, parse_receipt};

async fn fetch_with_receipt(
    client: &Client,
    url: &str,
    auth_header: &str,
) -> Result<(String, Option<Receipt>), Box<dyn std::error::Error>> {
    let resp = client
        .get(url)
        .header("authorization", auth_header)
        .send()
        .await?;

    // Parse receipt if present
    let receipt = resp
        .headers()
        .get("payment-receipt")
        .and_then(|h| h.to_str().ok())
        .and_then(|h| parse_receipt(h).ok());

    Ok((resp.text().await?, receipt))
}
```

## Custom Provider

Implement `PaymentProvider` for custom payment methods:

```rust
use mpay::client::PaymentProvider;
use mpay::{PaymentChallenge, PaymentCredential, PaymentPayload, MppError};

#[derive(Clone)]
struct MyProvider {
    api_key: String,
}

impl PaymentProvider for MyProvider {
    async fn pay(&self, challenge: &PaymentChallenge) -> Result<PaymentCredential, MppError> {
        // Your payment logic here
        // 1. Parse the challenge request
        // 2. Execute payment via your method
        // 3. Return credential with proof
        
        Ok(PaymentCredential::new(
            challenge.to_echo(),
            PaymentPayload::hash("0x..."),
        ))
    }
}
```