mtnmomo 0.1.9

MTN Momo Payment API for Rust, with support for both the Sandbox and Production environments. All products are supported: Collections, Disbursements and Remittances.
Documentation
use crate::responses::token_response::TokenResponse;
use chrono::Utc;
use once_cell::sync::Lazy;
use std::sync::Arc;
use tokio::sync::RwLock;

static TOKEN_STORAGE: Lazy<Arc<RwLock<TokenStorage>>> =
    Lazy::new(|| Arc::new(RwLock::new(TokenStorage::new())));

#[derive(Debug, Clone)]
struct TokenStorage {
    collection_token: Option<TokenResponse>,
    disbursement_token: Option<TokenResponse>,
    remittance_token: Option<TokenResponse>,
}

impl TokenStorage {
    fn new() -> Self {
        TokenStorage {
            collection_token: None,
            disbursement_token: None,
            remittance_token: None,
        }
    }
}

#[derive(Debug, Clone, Copy)]
pub enum ProductType {
    Collection,
    Disbursement,
    Remittance,
}

pub struct TokenManager;

impl TokenManager {
    pub async fn get_valid_token(product_type: ProductType) -> Option<TokenResponse> {
        let storage = TOKEN_STORAGE.read().await;
        let token = match product_type {
            ProductType::Collection => &storage.collection_token,
            ProductType::Disbursement => &storage.disbursement_token,
            ProductType::Remittance => &storage.remittance_token,
        };

        if let Some(token) = token {
            if Self::is_token_valid(token) {
                return Some(token.clone());
            }
        }
        None
    }

    pub async fn store_token(product_type: ProductType, token: TokenResponse) {
        let mut storage = TOKEN_STORAGE.write().await;
        match product_type {
            ProductType::Collection => storage.collection_token = Some(token),
            ProductType::Disbursement => storage.disbursement_token = Some(token),
            ProductType::Remittance => storage.remittance_token = Some(token),
        }
    }

    pub async fn clear_token(product_type: ProductType) {
        let mut storage = TOKEN_STORAGE.write().await;
        match product_type {
            ProductType::Collection => storage.collection_token = None,
            ProductType::Disbursement => storage.disbursement_token = None,
            ProductType::Remittance => storage.remittance_token = None,
        }
    }

    fn is_token_valid(token: &TokenResponse) -> bool {
        let now = Utc::now();
        let expiry_time =
            token.created_at.unwrap() + chrono::Duration::seconds(token.expires_in as i64);
        now < expiry_time
    }
}