SDK-SPEC-RUST — Rust SDK Specification¶
Status: Draft
Owner: Platform Engineering
Crate: simpaisa
Runtime: Rust 1.75+ (2024 edition)
Last Updated: 2026-04-03
1. Overview¶
Official Rust SDK for the Simpaisa Payment Gateway. Provides strongly-typed, async access to Pay-Ins, Pay-Outs, Remittances, and Cards across PK, BD, NP, and IQ corridors. Handles 270M+ annual transactions at $1B+ volume. Used internally for Unikraft-based microservices. Published to crates.io as simpaisa.
2. Crate Configuration¶
[dependencies]
simpaisa = "1"
Feature flags:
- default = ["tokio-runtime"]
- tokio-runtime — tokio async runtime (default)
- blocking — synchronous blocking client
- webhook — webhook verification support
- tracing — OpenTelemetry tracing integration
3. Module Structure¶
simpaisa/
├── src/
│ ├── lib.rs # Public API re-exports
│ ├── client.rs # SimPaisaClient struct
│ ├── config.rs # Config and Environment enum
│ ├── payin/
│ │ ├── mod.rs # PayIn client (initiate, verify, status, list)
│ │ ├── request.rs # PayInRequest struct
│ │ └── response.rs # PayInResponse struct
│ ├── payout/
│ │ ├── mod.rs # PayOut client (initiate, status, batch, cancel)
│ │ ├── request.rs
│ │ └── response.rs
│ ├── remittance/
│ │ ├── mod.rs # Remittance client (quote, initiate, status, beneficiaries)
│ │ ├── request.rs
│ │ └── response.rs
│ ├── cards/
│ │ ├── mod.rs # Cards client (pay, capture, void, refund, tokenise)
│ │ ├── request.rs
│ │ └── response.rs
│ ├── webhook/
│ │ ├── mod.rs # WebhookVerifier
│ │ └── event.rs # WebhookEvent struct
│ ├── auth/
│ │ ├── mod.rs # AuthProvider trait
│ │ └── rsa.rs # RSAAuthProvider — PEM loading, request signing
│ ├── error.rs # Error enum (thiserror)
│ ├── retry.rs # RetryPolicy with exponential backoff
│ ├── idempotency.rs # IdempotencyKeyGenerator (UUID v7)
│ └── types.rs # Shared types (Currency, Amount, etc.)
├── tests/
│ ├── integration/
│ └── common/
├── Cargo.toml
└── rust-toolchain.toml
4. Authentication — RSAAuthProvider¶
- Load merchant private key from PEM file via
ring::signature - Sign request body with RSA-PKCS1-SHA256
ringcrate for all cryptographic operations (no OpenSSL dependency)- Implement
AuthProvidertrait for extensibility - Support key rotation via
Vec<RsaKeyPair>
let auth = RsaAuthProvider::from_pem(
"MCH-001",
include_bytes!("../keys/merchant.pem"),
)?;
5. Async (tokio)¶
- All public methods SHALL be
async fnreturningResult<T, SimPaisaError> - Built on
reqwestHTTP client (tokio-native) - Connection pooling via
reqwest::Client(shared, cloneable) - Optional
blockingfeature flag for synchronous usage - No
.block_on()calls in async code paths
let result = client.payin().initiate(&request).await?;
6. Zero-Copy Deserialisation¶
serdewith#[derive(Deserialize)]on all response typesserde_jsonfor JSON parsing- Use
&strandCow<'_, str>where zero-copy is beneficial #[serde(borrow)]annotations for borrowed data- Benchmark deserialisation performance in CI
7. WebhookVerifier¶
- Verify
X-Simpaisa-Webhook-Signatureusing HMAC-SHA256 viaring::hmac fn verify(&self, payload: &[u8], signature: &str) -> Result<(), SimPaisaError>- Constant-time comparison via
ring::hmac::verify() - Replay protection: validate timestamp header within tolerance (default 300s)
- Gated behind
webhookfeature flag
8. Retry¶
- Exponential backoff with jitter (base 200ms, max 30s)
- Configurable max retries (default 3)
- Retry on: 429, 502, 503, 504, connection errors
- Never retry: 400, 401, 403, 404, 409, 422
- Respect
Retry-Afterheader on_retryclosure for observability- Tokio sleep for async delays
9. Strong Typing¶
Currencyenum (PKR, BDT, NPR, IQD) — not raw stringsAmountnewtype wrappingrust_decimal::DecimalMerchantId,TransactionIdnewtypesChannelenum for payment channels per corridorEnvironmentenum:Sandbox,Production- Compile-time validation where possible via type system
10. Error Handling¶
#[derive(Debug, thiserror::Error)]
pub enum SimPaisaError {
#[error("authentication failed: {message}")]
Authentication { code: String, message: String, status: u16 },
#[error("validation failed: {message}")]
Validation { code: String, message: String, fields: Vec<FieldError> },
#[error("rate limited, retry after {retry_after:?}")]
RateLimit { retry_after: Duration },
#[error("request timed out")]
Timeout(#[source] reqwest::Error),
#[error("idempotency conflict")]
Idempotency { request_id: String },
#[error("network error: {0}")]
Network(#[source] reqwest::Error),
#[error("webhook verification failed")]
WebhookVerification,
}
11. Configuration¶
let client = SimPaisaClient::builder()
.environment(Environment::Sandbox)
.merchant_id("MCH-001")
.private_key_pem(include_bytes!("../keys/merchant.pem"))
.timeout(Duration::from_secs(30)) // default 30s
.max_retries(3) // default 3
.build()?;
12. Example Usage¶
use simpaisa::{SimPaisaClient, Environment};
use simpaisa::payin::PayInRequest;
use simpaisa::webhook::WebhookVerifier;
#[tokio::main]
async fn main() -> Result<(), simpaisa::SimPaisaError> {
let client = SimPaisaClient::builder()
.environment(Environment::Sandbox)
.merchant_id("MCH-001")
.private_key_file("./keys/merchant.pem")
.build()?;
// Pay-In
let pay_in = client.payin().initiate(&PayInRequest {
amount: "5000".parse()?,
currency: Currency::PKR,
channel: Channel::Easypaisa,
customer_msisdn: "03001234567".into(),
callback_url: "https://merchant.com/webhook".parse()?,
}).await?;
// Webhook verification
let verifier = WebhookVerifier::new(b"whsec_...");
verifier.verify(raw_body, &signature_header)?;
Ok(())
}
13. Build and Publish¶
- Build:
cargo build(debug + release profiles) - Test:
cargo testwithcargo-nextestin CI - Linting:
cargo clippy -- -D warnings - Formatting:
cargo fmt --check - Audit:
cargo auditfor vulnerability scanning - CI: Bitbucket Pipelines — fmt → clippy → test → audit → publish
- Publish: crates.io with API token
- Versioning: Semantic versioning
- MSRV: 1.75 (documented in
Cargo.tomlandrust-toolchain.toml)
14. Unikraft Integration¶
- Compile to static binary for Unikraft unikernel deployment
- No dynamic linking (
lto = true,codegen-units = 1in release profile) - Minimal binary size via
opt-level = "z"+strip = true - Compatible with
#![no_std]for core types (future consideration)
15. Testing Requirements¶
- Unit tests for every public function (>90% coverage via
cargo-tarpaulin) - Integration tests against sandbox environment (behind
--features integration) - Property-based tests via
proptestfor serialisation round-trips - Webhook verification round-trip tests
- Retry behaviour tests with
wiremock - Benchmark tests via
criterionfor critical paths - Miri for undefined behaviour detection on unsafe code (if any)