Handle expired agreements and rewrite README

- Implement robust End User Agreement expiry detection and handling
- Add graceful error recovery for failed accounts
- Rewrite README.md to focus on user benefits
- Add documentation guidelines to AGENTS.md
This commit is contained in:
2025-11-21 17:04:31 +01:00
parent aeb8e9bdcb
commit 0b2549ddb4
13 changed files with 362 additions and 183 deletions

View File

@@ -3,7 +3,7 @@ use reqwest_middleware::ClientWithMiddleware;
use serde::{Deserialize, Serialize};
use thiserror::Error;
use tracing::{debug, instrument};
use crate::models::{TokenResponse, PaginatedResponse, Requisition, Account, TransactionsResponse};
use crate::models::{TokenResponse, PaginatedResponse, Requisition, Account, TransactionsResponse, EndUserAgreement};
#[derive(Error, Debug)]
pub enum GoCardlessError {
@@ -92,6 +92,39 @@ impl GoCardlessClient {
self.get_authenticated(url).await
}
#[instrument(skip(self))]
pub async fn get_agreements(&self) -> Result<PaginatedResponse<EndUserAgreement>, GoCardlessError> {
let url = self.base_url.join("/api/v2/agreements/enduser/")?;
self.get_authenticated(url).await
}
#[instrument(skip(self))]
pub async fn get_agreement(&self, id: &str) -> Result<EndUserAgreement, GoCardlessError> {
let url = self.base_url.join(&format!("/api/v2/agreements/enduser/{}/", id))?;
self.get_authenticated(url).await
}
#[instrument(skip(self))]
pub async fn is_agreement_expired(&self, agreement_id: &str) -> Result<bool, GoCardlessError> {
let agreement = self.get_agreement(agreement_id).await?;
// If not accepted, it's not valid
let Some(accepted_str) = agreement.accepted else {
return Ok(true);
};
// Parse acceptance date
let accepted = chrono::DateTime::parse_from_rfc3339(&accepted_str)
.map_err(|e| GoCardlessError::ApiError(format!("Invalid date format: {}", e)))?
.with_timezone(&chrono::Utc);
// Get validity period (default 90 days)
let valid_days = agreement.access_valid_for_days.unwrap_or(90) as i64;
let expiry = accepted + chrono::Duration::days(valid_days);
Ok(chrono::Utc::now() > expiry)
}
#[instrument(skip(self))]
pub async fn get_account(&self, id: &str) -> Result<Account, GoCardlessError> {
let url = self.base_url.join(&format!("/api/v2/accounts/{}/", id))?;