snippe 0.1.0

Async Rust client for the Snippe payments API (Tanzania) — collections, hosted checkout sessions, disbursements, and verified webhooks.
Documentation
//! # Snippe Rust SDK
//!
//! Async Rust client for [Snippe](https://snippe.sh), the payments API for
//! Tanzania. The SDK targets the `2026-01-25` API version and supports:
//!
//! - **Collections** — mobile money (M-Pesa, Airtel, Mixx, Halotel), card
//!   payments via the hosted Selcom checkout, and dynamic QR codes.
//! - **Hosted sessions** — pre-built checkout pages and short payment links
//!   for SMS / WhatsApp distribution.
//! - **Disbursements** — payouts to mobile wallets and 25+ Tanzanian banks.
//! - **Webhooks** — HMAC-SHA256 signature verification with replay protection
//!   and typed event dispatch.
//!
//! ## Quick start
//!
//! ```no_run
//! use snippe::{Client, IdempotencyKey};
//! use snippe::models::common::Customer;
//! use snippe::models::payment::{CreatePaymentRequest, MobilePayment};
//!
//! # async fn run() -> Result<(), snippe::Error> {
//! let client = Client::new("snp_live_xxx")?;
//!
//! let request = CreatePaymentRequest::Mobile(MobilePayment::new(
//!     500,
//!     "255781000000",
//!     Customer::new("Jane", "Doe", "jane@example.com"),
//! ).with_webhook_url("https://yoursite.com/webhooks/snippe"));
//!
//! let key = IdempotencyKey::new("ord-12345-att-1")?;
//! let payment = client.payments().create(&request, Some(&key)).await?;
//! println!("created payment {}", payment.reference);
//! # Ok(()) }
//! ```
//!
//! ## Critical rules
//!
//! These will save you debugging time later — the API enforces them all
//! server-side, but several have non-obvious failure modes:
//!
//! - **Currency is TZS only.** Amounts are integers in the smallest unit;
//!   `500` means 500 TZS, not 5.00.
//! - **Minimum amounts**: 500 TZS for payments, 5,000 TZS for payouts.
//! - **Phone numbers**: `255XXXXXXXXX` or `+255XXXXXXXXX`. Local formats
//!   like `0781000000` are rejected.
//! - **Idempotency keys must be ≤ 30 characters.** This SDK enforces the
//!   length at construction time via [`IdempotencyKey`] so the cryptic
//!   `PAY_001` error from oversized keys can't reach the API.
//! - **Webhook payloads have `data.amount` as `{value, currency}`**, not a
//!   plain integer like request bodies. Use [`webhook::Verifier`] which
//!   models this correctly.
//! - **Always verify webhook signatures against the raw request bytes** —
//!   parsing-then-re-serialising the JSON breaks the HMAC.
//!
//! ## Module overview
//!
//! - [`client`] — the [`Client`] type and its builder.
//! - [`api`] — endpoint handles for [`api::Payments`], [`api::Sessions`],
//!   [`api::Payouts`].
//! - [`models`] — request and response types, organised by resource.
//! - [`webhook`] — HMAC-SHA256 verifier and typed event enums.
//! - [`error`] — the [`Error`] type and the [`ErrorCode`] enum for
//!   programmatic dispatch.

#![warn(missing_docs)]
#![warn(rust_2018_idioms)]

pub mod api;
pub mod client;
pub mod config;
pub mod error;
pub mod idempotency;
pub mod models;
pub mod webhook;

mod envelope;

pub use client::{Client, ClientBuilder};
pub use config::Environment;
pub use error::{ApiError, Error, ErrorCode};
pub use idempotency::IdempotencyKey;

/// Convenient `Result` alias used throughout the crate.
pub type Result<T, E = Error> = std::result::Result<T, E>;