Skip to content

Latest commit

 

History

History
363 lines (274 loc) · 10.8 KB

README.md

File metadata and controls

363 lines (274 loc) · 10.8 KB

Statum Java SDK (SMS, Airtime, & Accounts)

Java CI Maven Central License: MIT

Official Java SDK for Statum APIs. Built for secure, production-grade enterprise usage with strict typing, immutable DTO records, and built-in HTTP client connection pooling. Easily send SMS alerts, automate airtime disbursements, and query real-time account balances.


Table of Contents


Features

  • JDK 17+ Records: Uses modern Java Records and sealed types for absolute DTO immutability.
  • Zero Heavy Dependencies: Uses the native java.net.http.HttpClient under the hood (only requires Jackson for lightweight JSON binding).
  • Thread-Safe: Designed to be instantiated once and shared across multiple application threads.
  • Service-Oriented: Logical division between Airtime, SMS, and Account Management APIs.
  • Extensive Exceptions: Maps standard HTTP status code failures (401, 403, 422, 5xx) to concrete Java exception classes.

Getting Started

  1. Sign up for a Statum account: app.statum.co.ke
  2. Get your API credentials: Retrieve your Consumer Key and Consumer Secret from the Statum Dashboard.
  3. Read the full API documentation: docs.statum.co.ke
  4. Install the SDK: Follow the Installation guidelines below.

Installation

Maven

Add the dependency to your pom.xml:

<dependency>
    <groupId>ke.co.statum</groupId>
    <artifactId>statum-java-sdk</artifactId>
    <version>1.0.3</version>
</dependency>

Gradle

Add the dependency to your build.gradle:

implementation 'ke.co.statum:statum-java-sdk:1.0.3'

Quick Start

1. Plain Java Setup

Ensure your credentials are saved in your system environment variables:

export STATUM_CONSUMER_KEY="your-consumer-key"
export STATUM_CONSUMER_SECRET="your-consumer-secret"

Instantiate and reuse the thread-safe client:

import ke.co.statum.sdk.StatumClient;
import ke.co.statum.sdk.config.StatumConfig;

StatumConfig config = new StatumConfig(
    System.getenv("STATUM_CONSUMER_KEY"),
    System.getenv("STATUM_CONSUMER_SECRET")
);
StatumClient client = new StatumClient(config);

2. Spring Boot Setup

Add the properties to your application.properties:

statum.consumer-key=${STATUM_CONSUMER_KEY}
statum.consumer-secret=${STATUM_CONSUMER_SECRET}
statum.base-url=https://api.statum.co.ke/api/v2
statum.timeout-seconds=30

Create a configuration class to register the StatumClient bean:

import ke.co.statum.sdk.StatumClient;
import ke.co.statum.sdk.config.StatumConfig;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.time.Duration;

@Configuration
public class StatumConfiguration {

    @Bean
    public StatumClient statumClient(
            @Value("${statum.consumer-key}") String consumerKey,
            @Value("${statum.consumer-secret}") String consumerSecret,
            @Value("${statum.base-url:https://api.statum.co.ke/api/v2}") String baseUrl,
            @Value("${statum.timeout-seconds:30}") int timeoutSeconds) {

        StatumConfig config = new StatumConfig(
            consumerKey,
            consumerSecret,
            baseUrl,
            Duration.ofSeconds(timeoutSeconds)
        );
        return new StatumClient(config);
    }
}

Now, inject the client into any @Service or @RestController:

import ke.co.statum.sdk.StatumClient;
import ke.co.statum.sdk.model.ApiResponse;
import org.springframework.stereotype.Service;

@Service
public class NotificationService {

    private final StatumClient statumClient;

    public NotificationService(StatumClient statumClient) {
        this.statumClient = statumClient;
    }

    public void alertUser(String phoneNumber, String message) {
        ApiResponse response = statumClient.getSmsService()
                .sendSms(phoneNumber, "STATUM", message);
                
        System.out.println("Alert sent. ID: " + response.requestId());
    }
}

Core Integration Examples

Account Details

Fetch organization profile, available balances, and service configuration:

import ke.co.statum.sdk.model.AccountDetailsResponse;

AccountDetailsResponse response = client.getAccountService().getAccountDetails();

System.out.println("Status Code: " + response.statusCode());
System.out.println("Organization: " + response.organization().name());
System.out.println("Available Balance: KES " + response.organization().details().availableBalance());

// List registered services and account codes
response.organization().accounts().forEach(account -> {
    System.out.println("Service: " + account.serviceName() + " | Code: " + account.account());
});

Sending SMS

Send transactional or promotional SMS alerts to a Kenyan phone number:

import ke.co.statum.sdk.model.ApiResponse;

ApiResponse response = client.getSmsService().sendSms(
    "254721553678",           // Phone number in international format
    "STATUM",                 // Your approved Sender ID
    "Hello from the Statum Java SDK!" // Message content
);

System.out.println("Status Code: " + response.statusCode());
System.out.println("Description: " + response.description());
System.out.println("Request ID: " + response.requestId());

Disbursing Airtime

Disburse airtime rewards or incentives (supports amounts from KES 5 to KES 10,000):

import ke.co.statum.sdk.model.ApiResponse;

ApiResponse response = client.getAirtimeService().sendAirtime(
    "254721553678",           // Phone number in international format
    "100"                     // Amount must be passed as a String representation
);

System.out.println("Status Code: " + response.statusCode());
System.out.println("Description: " + response.description());
System.out.println("Request ID: " + response.requestId());

Error & Exception Handling

The SDK maps standard HTTP responses to concrete exception classes that inherit from ke.co.statum.sdk.exceptions.ApiException.

import ke.co.statum.sdk.exceptions.*;
import ke.co.statum.sdk.model.ApiResponse;

try {
    ApiResponse response = client.getSmsService().sendSms("2547XXXXXXXX", "STATUM", "Message");
} catch (AuthenticationException e) {
    // Credentials failed validation (HTTP 401)
    System.err.println("Auth Failure: Check Consumer Key and Secret.");
} catch (AuthorizationException e) {
    // Access denied or forbidden resource (HTTP 403)
    System.err.println("Access Denied: " + e.getMessage());
} catch (ValidationException e) {
    // API-side validation parameters failed (HTTP 422)
    System.err.println("Validation failed. Body: " + e.getResponseBody());
    e.getValidationErrors().forEach((field, errors) -> {
        System.err.println("Field: " + field + " | Errors: " + String.join(", ", errors));
    });
} catch (NetworkException e) {
    // DNS, connection timeouts, or socket failures
    System.err.println("Connection error: " + e.getMessage());
} catch (ApiException e) {
    // General API errors (e.g. 402 Insufficient Funds, 500 Server Error)
    System.err.println("HTTP Status Code: " + e.getStatusCode());
    System.err.println("Error Body: " + e.getResponseBody());
}

API JSON Payload Specifications

Here are the wire-level JSON schemas transmitted and returned by the APIs under the hood:

1. SMS API

  • Endpoint: POST /sms
  • Headers: Authorization: Basic <base64(key:secret)>
JSON Request
{
  "phone_number": "254721553678",
  "sender_id": "STATUM",
  "message": "Hello from Statum SDK!"
}
JSON Response (Success - 200)
{
  "status_code": 200,
  "description": "Operation successful.",
  "request_id": "d173a8b3-0f3a-463f-8a03-29826b9a2d78"
}

2. Airtime API

  • Endpoint: POST /airtime
JSON Request
{
  "phone_number": "254721553678",
  "amount": "100"
}
JSON Response (Success - 200)
{
  "status_code": 200,
  "description": "Operation successful.",
  "request_id": "6e0213d5-6df9-47bf-ba2d-9b9470d96854"
}

3. Account Details API

  • Endpoint: GET /account-details
JSON Response (Success - 200)
{
  "status_code": 200,
  "description": "Operation successful.",
  "request_id": "5a45bc7b-bf99-49ae-b089-9daf5f4adbb0",
  "organization": {
    "name": "Statum Test",
    "details": {
      "available_balance": 695.15,
      "location": "Nairobi - Westlands",
      "website": "www.statum.co.ke",
      "office_email": "admin@statum.co.ke",
      "office_mobile": "+254722199199",
      "mpesa_account_top_up_code": "B9E573"
    },
    "accounts": [
      { "account": "Statum", "service_name": "sms" },
      { "account": "CONNECT", "service_name": "sms" }
    ]
  }
}

Integration Guidelines & Gotchas

  1. Sender ID Approval: SMS requests will throw an HTTP 422 ValidationException if the senderId is not registered and approved under your Statum account profile.
  2. Phone Number Formatting: Ensure phone numbers are passed in the international format (with or without + prefix), e.g. 254721553678 or +254721553678.
  3. Airtime Limits: Airtime amounts must be passed as strings (e.g. '100') and must fall strictly within the KES 5 to KES 10,000 range per transaction.
  4. Jackson Deserialization Dependency: If running outside of Spring Boot (which manages Jackson dependencies automatically), ensure that jackson-databind is present on your classpath.

License

This project is licensed under the MIT License. See LICENSE for details.