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}