snippe 0.1.0

Async Rust client for the Snippe payments API (Tanzania) — collections, hosted checkout sessions, disbursements, and verified webhooks.
Documentation
//! Collections — `/v1/payments`.

use crate::client::{encode_path_segment, Client};
use crate::idempotency::IdempotencyKey;
use crate::models::common::ListParams;
use crate::models::payment::{
    Balance, CreatePaymentRequest, ListPaymentsParams, Payment, SearchPaymentsParams,
};

/// Handle returned by [`Client::payments`](crate::Client::payments).
///
/// Cheap to construct — borrows the client.
#[derive(Debug, Clone, Copy)]
pub struct Payments<'c> {
    client: &'c Client,
}

impl<'c> Payments<'c> {
    pub(crate) fn new(client: &'c Client) -> Self {
        Self { client }
    }

    /// Create a payment — `POST /v1/payments`.
    ///
    /// Pass an [`IdempotencyKey`] (≤ 30 bytes) so retries are safe. Repeating
    /// the same key with the same body returns the cached response for 24
    /// hours; repeating with a different body returns a `422`.
    pub async fn create(
        &self,
        request: &CreatePaymentRequest,
        idempotency_key: Option<&IdempotencyKey>,
    ) -> crate::Result<Payment> {
        let mut req = self.client.post("/v1/payments").json(request);
        if let Some(k) = idempotency_key {
            req = req.idempotency_key(k);
        }
        req.send().await
    }

    /// Retrieve a payment by reference — `GET /v1/payments/{reference}`.
    pub async fn get(&self, reference: &str) -> crate::Result<Payment> {
        self.client
            .get(&format!("/v1/payments/{}", encode_path_segment(reference)))
            .send()
            .await
    }

    /// List payments — `GET /v1/payments`.
    pub async fn list(&self, params: &ListPaymentsParams) -> crate::Result<Vec<Payment>> {
        self.client.get("/v1/payments").query(params).send().await
    }

    /// List payments with generic page / limit / cursor params.
    ///
    /// Convenience wrapper for callers that want a basic paginator without
    /// the status / type filters in [`ListPaymentsParams`].
    pub async fn list_with(&self, params: &ListParams) -> crate::Result<Vec<Payment>> {
        self.client.get("/v1/payments").query(params).send().await
    }

    /// Search payments — `GET /v1/payments/search`.
    pub async fn search(&self, params: &SearchPaymentsParams) -> crate::Result<Vec<Payment>> {
        self.client
            .get("/v1/payments/search")
            .query(params)
            .send()
            .await
    }

    /// Re-trigger the USSD push for a pending mobile payment —
    /// `POST /v1/payments/{reference}/push`.
    ///
    /// Useful when the customer didn't receive the prompt the first time.
    /// Has no effect on payments that aren't `pending` or aren't mobile.
    pub async fn trigger_push(&self, reference: &str) -> crate::Result<Payment> {
        self.client
            .post(&format!(
                "/v1/payments/{}/push",
                encode_path_segment(reference)
            ))
            .send()
            .await
    }

    /// Get the merchant balance — `GET /v1/payments/balance`.
    ///
    /// `available` is the amount you can spend on payouts immediately;
    /// `balance` includes funds still settling. Use `available` for payout
    /// preflight checks.
    pub async fn balance(&self) -> crate::Result<Balance> {
        self.client.get("/v1/payments/balance").send().await
    }
}