Seev Business API

Rust SDK

The official Seev SDK for Rust. Async-first with Tokio support, works with Axum, Actix-web, and any Rust environment.

This SDK is currently a work in progress. The API surface below reflects the planned interface and may change before the stable release. Follow the changelog for updates.

The Seev Rust SDK (seev) wraps the Seev REST API with a type-safe, async-first Rust interface built on reqwest and tokio. It works with any async runtime — Axum, Actix-web, Warp, or standalone Tokio applications.

Installation

Add the crate to your Cargo.toml:

[dependencies]
seev = "0.1"
tokio = { version = "1", features = ["full"] }

Initialise

Call Seev::init() once at startup before making any API calls.

use seev::Seev;

#[tokio::main]
async fn main() {
    Seev::init(seev::Config {
        public_key: "pk_your_public_key".to_string(),
        private_key: "sk_your_secret_key".to_string(),
        ..Default::default()
    })
    .expect("Failed to initialise Seev SDK");
}

Never expose your private_key in client-side code or commit it to version control. Use environment variables.

Create a charge

Seev::charge() creates a checkout session and returns a struct containing the URL to redirect your customer to.

use seev::{Seev, ChargeRequest, CheckoutRecipient};

let charge = Seev::charge(ChargeRequest {
    charge_type: "checkout".to_string(),
    recipient: CheckoutRecipient {
        name: "Jane Doe".to_string(),
        email: "jane@example.com".to_string(),
        phone: Some("+233501234567".to_string()),
        ..Default::default()
    },
    amount: Some(10000), // smallest currency unit — 10000 = GHS 100.00
    currency: "GHS".to_string(),
    channels: Some(vec!["card".to_string(), "mobile_money".to_string()]),
    redirect_url: "https://myapp.com/callback".to_string(),
    ..Default::default()
})
.await?;

println!("{}", charge.id);           // checkout session ID
println!("{}", charge.checkout_url); // redirect your customer here

Charge from items

Pass items instead of amount to have Seev calculate the total:

use seev::{Seev, ChargeRequest, CheckoutRecipient, CheckoutItem};

let charge = Seev::charge(ChargeRequest {
    charge_type: "checkout".to_string(),
    recipient: CheckoutRecipient {
        name: "John Doe".to_string(),
        email: "john@example.com".to_string(),
        ..Default::default()
    },
    items: Some(vec![
        CheckoutItem { name: "Product A".to_string(), price: 5000, quantity: Some(2), ..Default::default() },
        CheckoutItem { name: "Product B".to_string(), price: 3000, quantity: Some(1), ..Default::default() },
    ]),
    currency: "GHS".to_string(),
    redirect_url: "https://myapp.com/callback".to_string(),
    ..Default::default()
})
.await?;

Charge struct

Field / MethodTypeDescription
charge.idStringCheckout session ID
charge.checkout_urlStringURL to redirect the customer to
charge.statusStringInitial session status
charge.amounti64Resolved charge amount
charge.currencyStringCharge currency
charge.get_response()serde_json::ValueFull API response
charge.get_raw(key)Option<serde_json::Value>Access any field from the raw response

Handle the callback

Seev::callback() verifies the session after the customer returns from the checkout page.

// Axum handler example
use axum::{extract::Query, response::Json};
use std::collections::HashMap;

async fn handle_callback(Query(params): Query<HashMap<String, String>>) -> Json<serde_json::Value> {
    let session_id = params.get("session_id").cloned().unwrap_or_default();

    let session = Seev::callback(&session_id).await.unwrap();

    if session.is_successful() {
        Json(serde_json::json!({ "status": "success" }))
    } else {
        Json(serde_json::json!({ "status": session.status }))
    }
}

Callback struct

Field / MethodTypeDescription
session.idStringSession ID
session.statusStringPayment status
session.referenceStringTransaction reference
session.is_successful()booltrue if payment completed
session.get_session()serde_json::ValueFull session object

Handle webhooks

Seev::webhook() parses incoming Seev webhook events. Always return 200 quickly and process asynchronously.

// Axum handler example
use axum::{extract::Json as BodyJson, response::Json};
use serde_json::Value;

async fn handle_webhook(BodyJson(body): BodyJson<Value>) -> Json<serde_json::Value> {
    let event = Seev::webhook(body, None).await.unwrap();

    if event.is("checkout.completed") {
        let data = event.get_data();
        println!("Payment completed: {:?}", data);

    } else if event.is("checkout.failed") {
        println!("Payment failed: {:?}", event.get_data());

    } else if event.is("refund.issued") {
        println!("Refund issued: {:?}", event.get_data());

    } else {
        println!("Unhandled event: {}", event.event_type());
    }

    Json(serde_json::json!({ "received": true }))
}

Webhook struct

Field / MethodDescription
event.event_type()Event type string, e.g. "checkout.completed"
event.is(type)true if the event matches the given type
event.get_data()Event payload as serde_json::Value
event.user()Associated user (if passed as second arg to webhook())
event.get_raw_event()Full raw event object

Common webhook events:

EventDescription
checkout.completedPayment succeeded
checkout.failedPayment failed
checkout.pendingPayment is pending confirmation
refund.issuedA refund was issued
invoice.createdInvoice was created
invoice.paidInvoice was paid

Fetch a transaction

Seev::transaction() retrieves details about a specific transaction. Use this server-side to confirm payment status.

let txn = Seev::transaction("txn_abc123xyz").await?;

println!("Status: {} — Amount: {} {}", txn.status, txn.amount, txn.currency);

Transaction struct

FieldTypeDescription
txn.idStringTransaction ID
txn.statusString"completed", "pending", "failed"
txn.amounti64Transaction amount
txn.currencyStringTransaction currency
txn.referenceOption<String>Optional reference

Type reference

pub struct Config {
    pub public_key: String,
    pub private_key: String,
    pub base_url: Option<String>,   // defaults to production URL
    pub timeout_secs: Option<u64>,  // defaults to 30
}

pub struct ChargeRequest {
    pub charge_type: String,        // "checkout" | "invoice" | "payment_link"
    pub recipient: CheckoutRecipient,
    pub currency: String,           // GHS | USD | EUR | GBP | NGN | USDC | USDT | ETH | BTC | XRP
    pub channels: Option<Vec<String>>, // card | mobile_money | bank_transfer | seev | crypto
    pub amount: Option<i64>,        // omit if using items
    pub items: Option<Vec<CheckoutItem>>,
    pub discount: Option<f64>,
    pub tax: Option<f64>,
    pub redirect_url: String,
}

pub struct CheckoutRecipient {
    pub name: String,
    pub email: String,
    pub phone: Option<String>,
    pub address: Option<String>,
    pub city: Option<String>,
}

pub struct CheckoutItem {
    pub name: String,
    pub price: i64,
    pub quantity: Option<i64>,
    pub description: Option<String>,
    pub image: Option<String>,
}

Error handling

use seev::SeevError;

match Seev::charge(request).await {
    Ok(charge) => println!("Checkout URL: {}", charge.checkout_url),
    Err(SeevError::Api { code, message }) => {
        eprintln!("SDK error [{}]: {}", code, message);
    }
    Err(SeevError::Network(e)) => {
        eprintln!("Network error: {}", e);
    }
    Err(e) => eprintln!("Unexpected error: {}", e),
}

Common errors:

ErrorCause
"Seev SDK not initialised. Call init() first."Seev::init() was not called before making a request
"Failed to create checkout session: ..."Bad request data or invalid API keys
"Failed to verify checkout session: session not found or invalid"Expired or invalid session ID
"Invalid webhook payload: missing event type"Malformed webhook body
"Network error: ..."Connectivity issue or unreachable endpoint

Full integration example

use axum::{
    extract::{Json as BodyJson, Path, Query},
    response::Json,
    routing::{get, post},
    Router,
};
use seev::{ChargeRequest, CheckoutItem, CheckoutRecipient, Seev};
use serde_json::Value;
use std::collections::HashMap;

#[tokio::main]
async fn main() {
    Seev::init(seev::Config {
        public_key: std::env::var("SEEV_PUBLIC_KEY").unwrap(),
        private_key: std::env::var("SEEV_PRIVATE_KEY").unwrap(),
        ..Default::default()
    })
    .expect("Failed to initialise Seev SDK");

    let app = Router::new()
        .route("/checkout", post(create_checkout))
        .route("/callback", get(handle_callback))
        .route("/webhooks/seev", post(handle_webhook))
        .route("/transaction/:id", get(get_transaction));

    let listener = tokio::net::TcpListener::bind("0.0.0.0:8080").await.unwrap();
    axum::serve(listener, app).await.unwrap();
}

async fn create_checkout() -> Json<Value> {
    let charge = Seev::charge(ChargeRequest {
        charge_type: "checkout".to_string(),
        recipient: CheckoutRecipient {
            name: "Jane Doe".to_string(),
            email: "jane@example.com".to_string(),
            ..Default::default()
        },
        items: Some(vec![CheckoutItem {
            name: "Widget".to_string(),
            price: 2500,
            quantity: Some(1),
            ..Default::default()
        }]),
        currency: "GHS".to_string(),
        redirect_url: "https://myapp.com/callback".to_string(),
        ..Default::default()
    })
    .await
    .unwrap();

    Json(serde_json::json!({ "checkout_url": charge.checkout_url }))
}

async fn handle_callback(Query(params): Query<HashMap<String, String>>) -> Json<Value> {
    let session_id = params.get("session_id").cloned().unwrap_or_default();
    let result = Seev::callback(&session_id).await.unwrap();

    if result.is_successful() {
        Json(serde_json::json!({ "status": "success", "session_id": result.id }))
    } else {
        Json(serde_json::json!({ "status": result.status }))
    }
}

async fn handle_webhook(BodyJson(body): BodyJson<Value>) -> Json<Value> {
    let event = Seev::webhook(body, None).await.unwrap();

    if event.is("checkout.completed") {
        println!("Payment completed: {:?}", event.get_data());
    }

    Json(serde_json::json!({ "received": true }))
}

async fn get_transaction(Path(id): Path<String>) -> Json<Value> {
    let txn = Seev::transaction(&id).await.unwrap();

    Json(serde_json::json!({
        "id": txn.id,
        "status": txn.status,
        "amount": txn.amount,
        "currency": txn.currency,
    }))
}

Next steps

On this page