ecash_client/
api.rs

1use crate::error::{ClientError, Result};
2use ecash_core::{BlindedToken, BlindSignature, Token};
3use reqwest::Client;
4use serde::{Deserialize, Serialize};
5
6#[derive(Debug, Clone, Deserialize)]
7pub struct PublicKeyResponse {
8    pub key_id: String,
9    pub institution_id: String,
10    pub public_key_n: String,
11    pub public_key_e: String,
12    pub denominations: Vec<u64>,
13}
14
15#[derive(Debug, Clone, Serialize)]
16pub struct WithdrawRequest {
17    pub amount: u64,
18    pub denomination: u64,
19    pub blinded_tokens: Vec<BlindedToken>,
20}
21
22#[derive(Debug, Clone, Deserialize)]
23pub struct WithdrawResponse {
24    pub blind_signatures: Vec<BlindSignature>,
25    pub key_id: String,
26    pub expires_at: String,
27    pub transaction_id: String,
28}
29
30#[derive(Debug, Clone, Serialize)]
31pub struct RedeemRequest {
32    pub tokens: Vec<Token>,
33    pub merchant_id: Option<String>,
34}
35
36#[derive(Debug, Clone, Deserialize)]
37pub struct RedeemResponse {
38    pub accepted_count: usize,
39    pub total_amount: u64,
40    pub transaction_id: String,
41    pub timestamp: String,
42}
43
44#[derive(Debug, Clone, Deserialize)]
45pub struct ApiErrorResponse {
46    pub error: String,
47}
48
49pub struct ApiClient {
50    client: Client,
51    base_url: String,
52}
53
54impl ApiClient {
55    pub fn new(base_url: String) -> Self {
56        Self {
57            client: Client::new(),
58            base_url,
59        }
60    }
61
62    pub async fn get_public_key(&self) -> Result<PublicKeyResponse> {
63        let url = format!("{}/api/v1/keys", self.base_url);
64        let response = self.client.get(&url).send().await?;
65        
66        if !response.status().is_success() {
67            let error: ApiErrorResponse = response.json().await?;
68            return Err(ClientError::ApiError(error.error));
69        }
70        
71        Ok(response.json().await?)
72    }
73
74    pub async fn withdraw(&self, request: WithdrawRequest) -> Result<WithdrawResponse> {
75        let url = format!("{}/api/v1/withdraw", self.base_url);
76        let response = self.client.post(&url).json(&request).send().await?;
77        
78        if !response.status().is_success() {
79            let error: ApiErrorResponse = response.json().await?;
80            return Err(ClientError::ApiError(error.error));
81        }
82        
83        Ok(response.json().await?)
84    }
85
86    pub async fn redeem(&self, request: RedeemRequest) -> Result<RedeemResponse> {
87        let url = format!("{}/api/v1/redeem", self.base_url);
88        let response = self.client.post(&url).json(&request).send().await?;
89        
90        if !response.status().is_success() {
91            let error: ApiErrorResponse = response.json().await?;
92            return Err(ClientError::ApiError(error.error));
93        }
94        
95        Ok(response.json().await?)
96    }
97
98    pub async fn health_check(&self) -> Result<bool> {
99        let url = format!("{}/health", self.base_url);
100        let response = self.client.get(&url).send().await?;
101        Ok(response.status().is_success())
102    }
103}