akahu_client/client/transactions.rs
1//! Transaction Endpoints
2//!
3//! This module contains methods for retrieving settled and pending transactions.
4
5use crate::{AccountId, Cursor, PaginatedResponse, PendingTransaction, Transaction, UserToken};
6
7use super::AkahuClient;
8use reqwest::Method;
9use std::collections::HashMap;
10
11impl AkahuClient {
12 /// Get a list of the user's settled transactions within a specified time range.
13 ///
14 /// This endpoint returns settled transactions for all accounts that the user has connected
15 /// to your application. The response is paginated - use the `cursor.next` value to fetch
16 /// subsequent pages.
17 ///
18 /// **Important Notes:**
19 /// - Time range defaults to the entire range accessible to your app if not specified
20 /// - Transactions will look different depending on your app's permissions
21 /// - All transaction timestamps are in UTC
22 /// - The start query parameter is exclusive (transactions after this timestamp)
23 /// - The end query parameter is inclusive (transactions through this timestamp)
24 /// - All Akahu timestamps use millisecond resolution (e.g. 2025-01-01T11:59:59.999Z)
25 /// - Each page contains a maximum of 100 transactions
26 /// - When querying multiple pages, use the same start/end parameters with the cursor
27 ///
28 /// # Arguments
29 ///
30 /// * `user_token` - The user's access token obtained through OAuth
31 /// * `query` - Optional query parameters to filter by date range and paginate
32 ///
33 /// # Returns
34 ///
35 /// A paginated response containing transactions and a cursor for fetching more pages.
36 ///
37 /// [<https://developers.akahu.nz/reference/get_transactions>]
38 pub async fn get_transactions(
39 &self,
40 user_token: &UserToken,
41 start: Option<chrono::DateTime<chrono::Utc>>,
42 end: Option<chrono::DateTime<chrono::Utc>>,
43 cursor: Option<Cursor>,
44 ) -> crate::error::AkahuResult<PaginatedResponse<Transaction>> {
45 const URI: &str = "transactions";
46
47 let headers = self.build_user_headers(user_token)?;
48
49 let mut query_params = HashMap::new();
50
51 if let Some(start) = start {
52 query_params.insert(
53 "start",
54 start.to_rfc3339_opts(chrono::SecondsFormat::Millis, true),
55 );
56 }
57
58 if let Some(end) = end {
59 query_params.insert(
60 "end",
61 end.to_rfc3339_opts(chrono::SecondsFormat::Millis, true),
62 );
63 }
64
65 if let Some(cursor) = cursor {
66 query_params.insert("cursor", cursor.to_string());
67 }
68
69 let url =
70 reqwest::Url::parse_with_params(&format!("{}/{}", self.base_url, URI), &query_params)?;
71
72 let req = self
73 .client
74 .request(Method::GET, url)
75 .headers(headers)
76 .build()?;
77
78 self.execute_request(req).await
79 }
80
81 /// Get a list of the user's pending transactions.
82 ///
83 /// This endpoint returns pending transactions for all accounts that the user has connected
84 /// to your application. Pending transactions are not stable - the date or description may
85 /// change due to the unreliable nature of underlying NZ bank data. They are not assigned
86 /// unique identifiers and are not enriched by Akahu.
87 ///
88 /// **Important Notes:**
89 /// - Pending transactions may change before they settle
90 /// - They do not have unique IDs
91 /// - They are not enriched with merchant/category data
92 /// - All timestamps are in UTC
93 /// - The `updated_at` field indicates when the transaction was last fetched
94 /// - This endpoint is not paginated and returns all pending transactions
95 ///
96 /// # Arguments
97 ///
98 /// * `user_token` - The user's access token obtained through OAuth
99 ///
100 /// # Returns
101 ///
102 /// A vector containing all pending transactions.
103 ///
104 /// [<https://developers.akahu.nz/reference/get_transactions-pending>]
105 pub async fn get_pending_transactions(
106 &self,
107 user_token: &UserToken,
108 ) -> crate::error::AkahuResult<Vec<PendingTransaction>> {
109 const URI: &str = "transactions/pending";
110
111 let headers = self.build_user_headers(user_token)?;
112
113 let req = self
114 .client
115 .request(Method::GET, format!("{}/{}", self.base_url, URI))
116 .headers(headers)
117 .build()?;
118
119 let response: crate::models::ListResponse<PendingTransaction> =
120 self.execute_request(req).await?;
121
122 Ok(response.items)
123 }
124
125 /// Get settled transactions for a specific account within a specified time range.
126 ///
127 /// This endpoint returns settled transactions for a specific connected account.
128 /// The response is paginated - use the `cursor.next` value to fetch subsequent pages.
129 ///
130 /// **Important Notes:**
131 /// - Time range defaults to the entire range accessible to your app if not specified
132 /// - All transaction timestamps are in UTC
133 /// - The start query parameter is exclusive (transactions after this timestamp)
134 /// - The end query parameter is inclusive (transactions through this timestamp)
135 /// - All Akahu timestamps use millisecond resolution
136 /// - Each page contains a maximum of 100 transactions
137 /// - When querying multiple pages, use the same start/end parameters with the cursor
138 ///
139 /// # Arguments
140 ///
141 /// * `user_token` - The user's access token obtained through OAuth
142 /// * `account_id` - The unique identifier for the account (prefixed with `acc_`)
143 /// * `query` - Optional query parameters to filter by date range and paginate
144 ///
145 /// # Returns
146 ///
147 /// A paginated response containing transactions and a cursor for fetching more pages.
148 ///
149 /// [<https://developers.akahu.nz/reference/get_accounts-id-transactions>]
150 pub async fn get_account_transactions(
151 &self,
152 user_token: &UserToken,
153 account_id: &AccountId,
154 start: Option<chrono::DateTime<chrono::Utc>>,
155 end: Option<chrono::DateTime<chrono::Utc>>,
156 cursor: Option<Cursor>,
157 ) -> crate::error::AkahuResult<PaginatedResponse<Transaction>> {
158 let uri = format!("accounts/{}/transactions", account_id.as_str());
159
160 let headers = self.build_user_headers(user_token)?;
161
162 let mut query_params = HashMap::new();
163
164 if let Some(start) = start {
165 query_params.insert(
166 "start",
167 start.to_rfc3339_opts(chrono::SecondsFormat::Millis, true),
168 );
169 }
170
171 if let Some(end) = end {
172 query_params.insert(
173 "end",
174 end.to_rfc3339_opts(chrono::SecondsFormat::Millis, true),
175 );
176 }
177
178 if let Some(cursor) = cursor {
179 query_params.insert("cursor", cursor.to_string());
180 }
181
182 let url =
183 reqwest::Url::parse_with_params(&format!("{}/{}", self.base_url, uri), &query_params)?;
184
185 let req = self
186 .client
187 .request(Method::GET, url)
188 .headers(headers)
189 .build()?;
190
191 self.execute_request(req).await
192 }
193
194 /// Get pending transactions for a specific account.
195 ///
196 /// This endpoint returns pending transactions for a specific connected account.
197 /// Pending transactions are not stable - the date or description may change due to
198 /// the unreliable nature of underlying NZ bank data. They are not assigned unique
199 /// identifiers and are not enriched by Akahu.
200 ///
201 /// **Important Notes:**
202 /// - Pending transactions may change before they settle
203 /// - They do not have unique IDs
204 /// - They are not enriched with merchant/category data
205 /// - All timestamps are in UTC
206 /// - The `updated_at` field indicates when the transaction was last fetched
207 /// - This endpoint is not paginated and returns all pending transactions
208 ///
209 /// # Arguments
210 ///
211 /// * `user_token` - The user's access token obtained through OAuth
212 /// * `account_id` - The unique identifier for the account (prefixed with `acc_`)
213 ///
214 /// # Returns
215 ///
216 /// A vector containing all pending transactions for the account.
217 ///
218 /// [<https://developers.akahu.nz/reference/get_accounts-id-transactions-pending>]
219 pub async fn get_account_pending_transactions(
220 &self,
221 user_token: &UserToken,
222 account_id: &AccountId,
223 ) -> crate::error::AkahuResult<Vec<PendingTransaction>> {
224 let uri = format!("accounts/{}/transactions/pending", account_id.as_str());
225
226 let headers = self.build_user_headers(user_token)?;
227
228 let req = self
229 .client
230 .request(Method::GET, format!("{}/{}", self.base_url, uri))
231 .headers(headers)
232 .build()?;
233
234 let response: crate::models::ListResponse<PendingTransaction> =
235 self.execute_request(req).await?;
236
237 Ok(response.items)
238 }
239}