Skip to main content

alat/
config.rs

1//! Client configuration: gateway host, credentials, and transport tuning.
2//!
3//! # The ALAT gateway topology (important)
4//!
5//! ALAT by Wema publishes its APIs through **Azure API Management (APIM)**. A
6//! single APIM gateway fronts many *products*, each mounted under a path prefix
7//! (e.g. `/funds-transfer-open`, `/bills-payment`, `/wallet-creation`). Every
8//! request is therefore `https://<gateway-host>/<product-prefix>/...`.
9//!
10//! Wema exposes **two public developer sandboxes**, and — crucially — they are
11//! *separate* APIM instances with *separate* subscription keys and *different*
12//! sets of products:
13//!
14//! | Sandbox       | Sign-up portal                                    | API gateway (calls go here)                 |
15//! |---------------|---------------------------------------------------|---------------------------------------------|
16//! | **Playground**| `https://playground.alat.ng`                      | `https://playground.azure-api.net`          |
17//! | **APIM Dev**  | `https://wema-alatdev-apimgt.developer.azure-api.net` | `https://wema-alatdev-apimgt.azure-api.net` |
18//!
19//! > The portal host (where you sign up / read keys) is **not** the gateway host
20//! > (where API calls go). The named constructors below target the **gateway**.
21//!
22//! Because a [`Client`](crate::Client) is bound to exactly one gateway + key,
23//! exercising endpoints that live on *both* sandboxes requires two clients (see
24//! the crate-level docs). In **production**, Wema typically fronts all of your
25//! subscribed products behind a single gateway host that they issue to you —
26//! pass that host to [`Config::new`].
27//!
28//! > The previous revision of this SDK defaulted to `https://sandbox.alat.ng`,
29//! > which does not resolve. There is intentionally **no hidden default host**
30//! > here: you must name the gateway explicitly (or use a named sandbox
31//! > constructor), so a typo can never silently point at the wrong bank.
32
33use std::time::Duration;
34
35/// The default per-request timeout applied if the caller does not override it.
36///
37/// Banking calls should never hang indefinitely; 30s is generous for the
38/// synchronous "accepted/pending" responses these endpoints return.
39pub const DEFAULT_TIMEOUT: Duration = Duration::from_secs(30);
40
41/// Connection details and credentials for a single ALAT gateway.
42///
43/// A `Config` is consumed by [`Client::new`](crate::Client::new). It is cheap to
44/// clone and holds no live connections.
45///
46/// # Authentication
47///
48/// APIM gates every product with a **subscription key**
49/// (`Ocp-Apim-Subscription-Key`). On top of that, ALAT identifies the calling
50/// software *channel* with an **API key** (`x-api-key`). A subset of products
51/// (bills & airtime) additionally require an **access key** sent in an `access`
52/// header — supply it via [`Config::with_access_key`] when you use those
53/// endpoints.
54#[derive(Debug, Clone)]
55pub struct Config {
56    /// Absolute base URL of the APIM **gateway** (where API calls go, not the
57    /// sign-up portal), e.g. `https://playground.azure-api.net`. Stored without a
58    /// trailing slash.
59    pub base_url: String,
60
61    /// APIM subscription key, sent as the `Ocp-Apim-Subscription-Key` header.
62    /// Obtained from your subscription on the ALAT developer portal.
63    pub subscription_key: String,
64
65    /// Channel API key, sent as the `x-api-key` header. Identifies your
66    /// registered application/partner to ALAT's core systems.
67    pub api_key: String,
68
69    /// Optional "access" credential required by the bills & airtime products,
70    /// sent as the `access` header. `None` if you do not use those endpoints.
71    pub access_key: Option<String>,
72
73    /// Maximum duration to wait for each request before failing with a
74    /// [`Network`](crate::Error::Network) timeout error.
75    pub timeout: Duration,
76}
77
78impl Config {
79    /// Creates a configuration pointing at an explicit gateway host.
80    ///
81    /// This is the honest, production-oriented constructor: you name the gateway
82    /// Wema issued to you. Trailing slashes are trimmed.
83    ///
84    /// ```
85    /// use alat::Config;
86    /// let config = Config::new("https://your-gateway.wemabank.com", "sub_key", "api_key");
87    /// assert_eq!(config.base_url, "https://your-gateway.wemabank.com");
88    /// ```
89    pub fn new(
90        base_url: impl Into<String>,
91        subscription_key: impl Into<String>,
92        api_key: impl Into<String>,
93    ) -> Self {
94        let base_url = base_url.into().trim_end_matches('/').to_string();
95        Self {
96            base_url,
97            subscription_key: subscription_key.into(),
98            api_key: api_key.into(),
99            access_key: None,
100            timeout: DEFAULT_TIMEOUT,
101        }
102    }
103
104    /// Configuration for the **Playground** sandbox gateway
105    /// (`https://playground.azure-api.net`; sign up at `https://playground.alat.ng`).
106    ///
107    /// Use this for wallet creation, account maintenance, statements, bills, and
108    /// airtime — the products published on that portal.
109    pub fn playground(
110        subscription_key: impl Into<String>,
111        api_key: impl Into<String>,
112    ) -> Self {
113        Self::new("https://playground.azure-api.net", subscription_key, api_key)
114    }
115
116    /// Configuration for the **APIM Dev** sandbox
117    /// (`https://wema-alatdev-apimgt.azure-api.net`).
118    ///
119    /// Use this for the funds-transfer / name-enquiry product
120    /// (`/funds-transfer-open`), which is published on that portal rather than
121    /// on Playground.
122    pub fn apim_dev(
123        subscription_key: impl Into<String>,
124        api_key: impl Into<String>,
125    ) -> Self {
126        Self::new(
127            "https://wema-alatdev-apimgt.azure-api.net",
128            subscription_key,
129            api_key,
130        )
131    }
132
133    /// Attaches the `access` credential required by bills & airtime endpoints
134    /// (builder style).
135    ///
136    /// ```
137    /// use alat::Config;
138    /// let config = Config::playground("sub", "api").with_access_key("bills_access_token");
139    /// assert!(config.access_key.is_some());
140    /// ```
141    pub fn with_access_key(mut self, access_key: impl Into<String>) -> Self {
142        self.access_key = Some(access_key.into());
143        self
144    }
145
146    /// Overrides the default per-request [`timeout`](Self::timeout) (builder style).
147    pub fn with_timeout(mut self, timeout: Duration) -> Self {
148        self.timeout = timeout;
149        self
150    }
151}