snippe 0.1.0

Async Rust client for the Snippe payments API (Tanzania) — collections, hosted checkout sessions, disbursements, and verified webhooks.
Documentation
//! Standalone snippet showing how to verify a webhook payload outside of any
//! HTTP framework. In a real handler you'd plug `Verifier::verify_typed` into
//! axum / actix / warp / rocket — see the rustdoc on `webhook::Verifier`.

use snippe::webhook::{EventData, Verifier, SIGNATURE_HEADER, TIMESTAMP_HEADER};

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let signing_key =
        std::env::var("SNIPPE_WEBHOOK_SECRET").expect("set SNIPPE_WEBHOOK_SECRET");

    // In a real handler these would come from the incoming HTTP request.
    let raw_body = br#"{"id":"evt_demo","type":"payment.completed","api_version":"2026-01-25","created_at":"2026-01-24T10:30:00Z","data":{"reference":"pi_demo","status":"completed","amount":{"value":50000,"currency":"TZS"}}}"#;
    let timestamp = "1737712200"; // header value
    let signature = "<the X-Webhook-Signature header>";

    println!("expected headers: {TIMESTAMP_HEADER}, {SIGNATURE_HEADER}");

    let verifier = Verifier::new(signing_key);
    match verifier.verify_typed(raw_body, timestamp, signature) {
        Ok(event) => match event.data {
            EventData::PaymentCompleted(p) => {
                println!("payment {} completed, amount {} TZS", p.reference, p.amount.value);
            }
            EventData::PaymentFailed(p) => {
                println!("payment {} failed: {:?}", p.reference, p.failure_reason);
            }
            other => println!("other event: {other:?}"),
        },
        Err(e) => eprintln!("rejected: {e}"),
    }

    Ok(())
}