use anyhow::Result; use rsa::pkcs1v15::SigningKey; use rsa::RsaPrivateKey; use rsa::sha2::Sha256; use rsa::signature::RandomizedSigner; use serde::{de::DeserializeOwned, Deserialize}; pub use config::BunqConfig; pub use monetary_account::MonetaryAccount; pub use payment::Payment; mod config; mod monetary_account; mod payment; const BASE_URL: &str = "https://api.bunq.com"; pub struct BunqClient { token: String, #[allow(dead_code)] // Required for signing bodies. Not used yet. keypair: RsaPrivateKey, user_id: i64, } impl BunqClient { pub fn monetary_accounts(&self) -> Result> { monetary_account::get(self) } pub fn payments(&self, acc: &MonetaryAccount) -> Result> { self.payments_from_to(acc, None, None) } pub fn payments_from_to( &self, acc: &MonetaryAccount, from: Option, to: Option, ) -> anyhow::Result> { payment::get_from_to(self, acc, from, to) } } #[derive(Deserialize, Debug)] struct Pagination { #[allow(dead_code)] // Used for refresh. Not implemented yet. future_url: Option, #[allow(dead_code)] // Used for finding newer items. Not necessary yet. newer_url: Option, older_url: Option, } struct Response { response: T, pagination: Option, } #[derive(Deserialize)] #[serde(rename_all = "PascalCase")] struct RawResponse { response: Vec, pagination: Option, } impl RawResponse { fn decode_retarded(self) -> Result> { let mut map = serde_json::Map::new(); for e in self.response { if let serde_json::Value::Object(e) = e { let (k, v) = e .into_iter() .next() .ok_or_else(|| anyhow::anyhow!("malformed response"))?; map.insert(k, v); } } Ok(Response { response: serde_json::from_value(map.into())?, pagination: self.pagination, }) } } fn deserialize_retarded_response(r: &str) -> Result> { let r: RawResponse = serde_json::from_str(r)?; r.decode_retarded() } fn deserialize_normal_response(r: &str) -> Result> { let r: RawResponse = serde_json::from_str(r)?; Ok(Response { response: serde_json::from_value(r.response.into())?, pagination: r.pagination, }) } fn sign(body: &str, key: &RsaPrivateKey) -> Result { use base64::prelude::{BASE64_STANDARD, Engine}; let signing_key = SigningKey::::new(key.clone()); let mut rng = rand::thread_rng(); let signature = signing_key.sign_with_rng(&mut rng, body.as_bytes()); Ok(BASE64_STANDARD.encode(signature.to_string())) }