(feat) transactions
This commit is contained in:
parent
35adb141ab
commit
779ae4d498
8 changed files with 214 additions and 5 deletions
|
|
@ -1,7 +1,7 @@
|
|||
// src/api/accounts.rs
|
||||
// Account data retrieval
|
||||
|
||||
use super::{client::{BankClient, BankingError}, models::{ApiResponse, AccountData}};
|
||||
use super::{client::{BankClient, BankingError}, models::{ApiResponse, AccountData, TransactionData}};
|
||||
|
||||
impl BankClient {
|
||||
pub async fn get_accounts(
|
||||
|
|
@ -28,4 +28,5 @@ impl BankClient {
|
|||
}),
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -62,4 +62,32 @@ impl BankClient {
|
|||
consent
|
||||
})
|
||||
}
|
||||
|
||||
pub async fn delete_consent(&self, consent_id: &str) -> Result<(), BankingError> {
|
||||
info!("🗑️ Deleting consent: {}", consent_id);
|
||||
|
||||
let token = self.get_token().await?;
|
||||
|
||||
let response = self.http_client
|
||||
.delete(self.base_url.join(&format!("/account-consents/{}", consent_id))?)
|
||||
.bearer_auth(token)
|
||||
.header("x-fapi-interaction-id", format!("team275-{}", chrono::Utc::now().timestamp()))
|
||||
.send()
|
||||
.await?;
|
||||
|
||||
match response.status().as_u16() {
|
||||
204 => {
|
||||
info!("✅ Consent deleted successfully");
|
||||
Ok(())
|
||||
},
|
||||
status => {
|
||||
error!("❌ Failed to delete consent: {}", status);
|
||||
Err(BankingError::ApiError {
|
||||
status,
|
||||
body: response.text().await.unwrap_or_default(),
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@
|
|||
|
||||
use sqlx::PgPool;
|
||||
use chrono::{DateTime, Utc};
|
||||
use tracing::{info};
|
||||
|
||||
pub struct StoredConsent {
|
||||
pub user_id: String,
|
||||
|
|
@ -55,3 +56,20 @@ pub async fn get_valid_consent(
|
|||
|
||||
Ok(result.map(|r| r.consent_id))
|
||||
}
|
||||
|
||||
pub async fn delete_consent(
|
||||
pool: &PgPool,
|
||||
consent_id: &str,
|
||||
) -> Result<(), sqlx::Error> {
|
||||
info!("🗑️ Deleting consent from DB: {}", consent_id);
|
||||
|
||||
sqlx::query!(
|
||||
"DELETE FROM user_consents WHERE consent_id = $1",
|
||||
consent_id
|
||||
)
|
||||
.execute(pool)
|
||||
.await?;
|
||||
|
||||
info!("✅ Consent deleted from DB");
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@ pub fn router(app_state: AppState) -> Router {
|
|||
.route("/api/consent/{bank}/{user_id}",
|
||||
post(handlers::create_consent_handler)
|
||||
.get(handlers::get_consent_handler)
|
||||
.delete(handlers::delete_consent_handler)
|
||||
)
|
||||
.route("/api/accounts/{bank}/{user_id}",
|
||||
get(handlers::get_accounts_handler)
|
||||
|
|
|
|||
|
|
@ -83,7 +83,7 @@ pub async fn create_consent_handler(
|
|||
StatusCode::CREATED,
|
||||
Json(json!({
|
||||
"consent_id": consent_response.consent_id,
|
||||
"expires_at": consent_response.created_at + &chrono::Duration::days(365).to_string(),
|
||||
"expires_at": expires_at,
|
||||
"status": consent_response.status,
|
||||
"message": consent_response.message,
|
||||
}))
|
||||
|
|
@ -134,12 +134,31 @@ pub async fn get_accounts_handler(
|
|||
|
||||
let client = state.banking_clients.get_client(bank);
|
||||
|
||||
client.get_accounts(&user_id, &consent_id)
|
||||
let accounts_response = client.get_accounts(&user_id, &consent_id)
|
||||
.await
|
||||
.map(|accounts| Json(serde_json::to_value(accounts).unwrap()))
|
||||
.map_err(map_banking_error)
|
||||
.map_err(map_banking_error)?;
|
||||
|
||||
// ✨ NEW: Save accounts to database
|
||||
for account in &accounts_response.data.account {
|
||||
let _ = db::accounts::store_account(
|
||||
&state.db_pool,
|
||||
&account.account_id,
|
||||
&user_id,
|
||||
bank.code(),
|
||||
account.status.as_deref().unwrap_or("unknown"),
|
||||
&account.currency,
|
||||
&account.account_type,
|
||||
account.account_sub_type.as_deref(),
|
||||
&account.nickname,
|
||||
account.description.as_deref(),
|
||||
account.opening_date,
|
||||
).await;
|
||||
}
|
||||
|
||||
Ok(Json(serde_json::to_value(accounts_response).unwrap()))
|
||||
}
|
||||
|
||||
|
||||
// --- Transaction Management ---
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
|
|
@ -172,6 +191,35 @@ pub async fn get_transactions_handler(
|
|||
.map_err(map_banking_error)
|
||||
}
|
||||
|
||||
pub async fn delete_consent_handler(
|
||||
State(state): State<AppState>,
|
||||
Path((bank_code, user_id)): Path<(String, String)>,
|
||||
) -> impl IntoResponse {
|
||||
let bank = bank_code.parse::<Bank>()
|
||||
.map_err(|_| (StatusCode::BAD_REQUEST, Json(json!({ "error": "Invalid bank code" }))))?;
|
||||
|
||||
let consent_id = db::consents::get_valid_consent(&state.db_pool, &user_id, bank.code())
|
||||
.await
|
||||
.map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, Json(json!({ "error": e.to_string() }))))?
|
||||
.ok_or_else(|| (StatusCode::NOT_FOUND, Json(json!({ "error": "Consent not found" }))))?;
|
||||
|
||||
let client = state.banking_clients.get_client(bank);
|
||||
|
||||
// Delete from bank first
|
||||
client.delete_consent(&consent_id)
|
||||
.await
|
||||
.map_err(map_banking_error)?;
|
||||
|
||||
// Then delete from our DB
|
||||
db::consents::delete_consent(&state.db_pool, &consent_id)
|
||||
.await
|
||||
.map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, Json(json!({ "error": e.to_string() }))))?;
|
||||
|
||||
Ok::<_, (StatusCode, Json<serde_json::Value>)>((StatusCode::OK, Json(json!({ "status": "deleted" }))))
|
||||
}
|
||||
|
||||
|
||||
|
||||
// --- Error Mapping ---
|
||||
|
||||
fn map_banking_error(err: BankingError) -> (StatusCode, Json<serde_json::Value>) {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue