akahu_client/models/
account.rs

1//! Rust structs representing the Account Model Akahu uses, the documentation
2//! for the Akahu model this is derived from is
3//! [here](https://developers.akahu.nz/docs/the-account-model).
4
5use serde::{Deserialize, Serialize};
6
7use crate::{AccountId, AuthorizationId, BankAccountNumber};
8
9/// An Akahu account is something that has a balance. Some connections (like
10/// banks) have lots of accounts, while others (like KiwiSaver providers) may
11/// only have one. Different types of accounts have different attributes and
12/// abilities, which can get a bit confusing! The rest of this page should help
13/// you figure everything out, from an account's provider to whether it can make
14/// payments.
15///
16/// Keep in mind that we limit what information is available depending on your
17/// app permissions. This is done in order to protect user privacy, however it
18/// also means that some of the data here may not be visible to you.
19#[derive(Debug, Deserialize, Serialize, Clone, PartialEq, Eq)]
20pub struct Account {
21    /// The `id` key is a unique identifier for the account in the Akahu system.
22    ///
23    /// It is always be prefixed by acc_ so that you can tell that it belongs to
24    /// an account.
25    ///
26    /// [<https://developers.akahu.nz/docs/the-account-model#_id>]
27    #[serde(rename = "_id")]
28    pub id: AccountId,
29
30    /// The identifier of this account's predecessor.
31    ///
32    /// This attribute is only present if the account has been migrated to an
33    /// official open banking connection from a classic Akahu connection.
34    ///
35    /// Read more about official open banking, and migrating to it
36    /// [here](https://developers.akahu.nz/docs/official-open-banking).
37    ///
38    /// [<https://developers.akahu.nz/docs/the-account-model#_migrated>]
39    #[serde(default, skip_serializing_if = "Option::is_none", rename = "_migrated")]
40    pub migrated: Option<String>,
41
42    /// Financial accounts are connected to Akahu via an authorisation with the
43    /// user's financial institution. Multiple accounts can be connected during
44    /// a single authorisation, causing them to have the same authorisation
45    /// identifier. This identifier can also be used to link a specific account
46    /// to identity data for the
47    /// [party](https://developers.akahu.nz/reference/get_parties) who completed
48    /// the authorisation.
49    ///
50    /// This identifier can also be used to revoke access to all the accounts
51    /// connected to that authorisation.
52    ///
53    /// For example, if you have 3 ANZ accounts, they will all have the same
54    /// `authorisation`. Your ANZ accounts and your friend's ANZ accounts have
55    /// different logins, so they will have a different `authorisation key`. The
56    /// `authorisation` key is in no way derived or related to your login
57    /// credentials - it's just a random ID.
58    ///
59    /// [<https://developers.akahu.nz/docs/the-account-model#_authorisation>]
60    #[serde(rename = "_authorisation")]
61    pub authorisation: AuthorizationId,
62
63    /// Deprecated: Please use `authorisation` instead.
64    ///
65    /// [<https://developers.akahu.nz/docs/the-account-model#_credentials-deprecated>]
66    #[deprecated(note = "Please use `authorisation` instead.")]
67    #[serde(
68        rename = "_credentials",
69        default,
70        skip_serializing_if = "Option::is_none"
71    )]
72    pub credentials: Option<AuthorizationId>,
73
74    /// This is the name of the account. If the connection allows customisation,
75    /// the name will be the custom name (or nickname), e.g. "Spending Account".
76    /// Otherwise Akahu falls back to the product name, e.g. "Super Saver".
77    ///
78    /// [<https://developers.akahu.nz/docs/the-account-model#name>]
79    pub name: String,
80
81    /// This attribute indicates the status of Akahu's connection to this account.
82    ///
83    /// It is possible for Akahu to lose the ability to authenticate with a
84    /// financial institution if the user revokes Akahu's access directly via
85    /// their institution, or changes their login credentials, which in some
86    /// cases can cause our long-lived access to be revoked.
87    ///
88    /// [<https://developers.akahu.nz/docs/the-account-model#status>]
89    pub status: Active,
90
91    /// If the account has a well defined account number (eg. a bank account
92    /// number, or credit card number) this will be defined here with a standard
93    /// format across connections. This field will be the value undefined for
94    /// accounts with KiwiSaver providers and investment platform accounts.
95    ///
96    /// For NZ banks, we use the common format 00-0000-0000000-00. For credit
97    /// cards, we return a redacted card number 1234-****-****-1234 or
98    /// ****-****-****-1234
99    ///
100    /// [<https://developers.akahu.nz/docs/the-account-model#formatted_account>]
101    // TODO: could hyave a strongly defined type here.
102    #[serde(default, skip_serializing_if = "Option::is_none")]
103    pub formatted_acount: Option<String>,
104
105    /// Akahu can refresh different parts of an account's data at different rates.
106    /// The timestamps in the refreshed object tell you when that account data was
107    /// last updated.
108    ///
109    /// When looking at a timestamp in here, you can think "Akahu's view of the
110    /// account (balance/metadata/transactions) is up to date as of $TIME".
111    ///
112    /// [<https://developers.akahu.nz/docs/the-account-model#refreshed>]
113    pub refreshed: RefreshDetails,
114
115    /// The account balance.
116    ///
117    /// [<https://developers.akahu.nz/docs/the-account-model#balance>]
118    pub balance: BalanceDetails,
119
120    /// What sort of account this is. Akahu provides specific bank account
121    /// types, and falls back to more general types for other types of
122    /// connection.
123    ///
124    /// [<https://developers.akahu.nz/docs/the-account-model#type>]
125    #[serde(rename = "type")]
126    pub kind: BankAccountKind,
127
128    /// The list of attributes indicates what abilities an account has.
129    ///
130    /// See [Attribute] for more information.
131    ///
132    /// [<https://developers.akahu.nz/docs/the-account-model#attributes>]
133    #[serde(default, skip_serializing_if = "Vec::is_empty")]
134    pub attributes: Vec<Attribute>,
135}
136
137/// This attribute indicates the status of Akahu's connection to this account.
138///
139/// It is possible for Akahu to lose the ability to authenticate with a
140/// financial institution if the user revokes Akahu's access directly via
141/// their institution, or changes their login credentials, which in some
142/// cases can cause our long-lived access to be revoked.
143///
144/// [<https://developers.akahu.nz/docs/the-account-model#status>]
145#[derive(Debug, Deserialize, Serialize, Clone, PartialEq, Eq)]
146#[serde(rename_all = "UPPERCASE")]
147pub enum Active {
148    /// Akahu can authenticate with the institution to retrieve data
149    /// and/or initiate payments for this account.
150    Active,
151    /// Akahu no longer has access to this account. Your
152    /// application will still be able to access Akahu's cached copy of data for
153    /// this account, but this will no longer be updated by
154    /// [refreshes](https://developers.akahu.nz/docs/data-refreshes). Write
155    /// actions such as payments or transfers will no longer be available. Once
156    /// an account is assigned the INACTIVE status, it will stay this way until
157    /// the user re-establishes the connection. When your application observes
158    /// an account with a status of INACTIVE, the user should be directed back
159    /// to the Akahu OAuth flow or to [<https://my.akahu.nz/connections>] where
160    /// they will be prompted to re-establish the connection.
161    Inactive,
162}
163
164/// This is a less defined part of our API that lets us expose data that may be
165/// specific to certain account types or financial institutions. An investment
166/// provider, for example, may expose a breakdown of investment results.
167///
168/// Akahu standardises this metadata as much as possible. However depending on
169/// the specific integration and account, some data fields may be unavailable or
170/// poorly specified. Treat all fields in the meta object as optional.
171///
172/// [<https://developers.akahu.nz/docs/the-account-model#meta>]
173#[derive(Debug, Deserialize, Serialize, Clone, PartialEq, Eq)]
174pub struct AccountMetadata {
175    /// The account holder name as exposed by the provider. In the case of bank
176    /// accounts this is the name on the bank statement.
177    #[serde(default, skip_serializing_if = "Option::is_none")]
178    pub holder: Option<String>,
179
180    /// Indicates if the account has other holders that are not listed in the
181    /// holder field. This only applies to official open banking connections
182    /// where the institution indicates a joint account, but only provides the
183    /// authorising party's name.
184    #[serde(default, skip_serializing_if = "Option::is_none")]
185    pub has_unlisted_holders: Option<bool>,
186
187    /// If the account can be paid but is not a bank account (for example a
188    /// KiwiSaver account), this field will have payment details.
189    #[serde(default, skip_serializing_if = "Option::is_none")]
190    pub payment_details: Option<PaymentDetails>,
191
192    /// Includes detailed information related to a loan account (if available
193    /// from the loan provider).
194    #[serde(default, skip_serializing_if = "Option::is_none")]
195    pub loan_details: Option<LoanDetails>,
196
197    /// An investment breakdown. Details are passed straight through from
198    /// integrations, making them very inconsistent.
199    #[serde(default, skip_serializing_if = "Option::is_none")]
200    pub breakdown: Option<serde_json::Value>,
201
202    /// An investment portfolio. Details are passed through from integrations,
203    /// so some are missing various fields. A maximum of 200 funds/instruments
204    /// are supported per investment account.
205    #[serde(default, skip_serializing_if = "Option::is_none")]
206    pub portfolio: Option<serde_json::Value>,
207}
208
209/// Details for making a payment to an account that is not a bank account.
210#[derive(Debug, Deserialize, Serialize, Clone, PartialEq, Eq)]
211pub struct PaymentDetails {
212    /// The recipient's name.
213    pub account_holder: String,
214    /// The recipient's NZ bank account number.
215    pub account_number: BankAccountNumber,
216    /// Details required to be in the payment particulars.
217    #[serde(default, skip_serializing_if = "Option::is_none")]
218    pub particulars: Option<String>,
219    /// Details required to be in the payment code.
220    #[serde(default, skip_serializing_if = "Option::is_none")]
221    pub code: Option<String>,
222    /// Details required to be in the payment reference.
223    #[serde(default, skip_serializing_if = "Option::is_none")]
224    pub reference: Option<String>,
225    /// If there is a minimum amount in order to have the payment accepted, in
226    /// dollars.
227    #[serde(
228        default,
229        skip_serializing_if = "Option::is_none",
230        with = "rust_decimal::serde::arbitrary_precision_option"
231    )]
232    pub minimum_amount: Option<rust_decimal::Decimal>,
233}
234
235/// Detailed information related to a loan account.
236#[derive(Debug, Deserialize, Serialize, Clone, PartialEq, Eq)]
237pub struct LoanDetails {
238    /// The purpose of the loan (E.g. HOME), if we can't determine the purpose,
239    /// this will be UNKNOWN.
240    pub purpose: String,
241    /// The type of loan (E.g. TABLE), if we can't determine the type, this will
242    /// be UNKNOWN.
243    // TODO: Could be an enum but we do not know all possible classifications.
244    #[serde(rename = "type")]
245    pub loan_type: String,
246    /// Interest rate information for the loan.
247    #[serde(default, skip_serializing_if = "Option::is_none")]
248    pub interest: Option<InterestDetails>,
249    /// Is the loan currently in an interest only period?
250    #[serde(default, skip_serializing_if = "Option::is_none")]
251    pub is_interest_only: Option<bool>,
252    /// When the interest only period expires, if available.
253    #[serde(default, skip_serializing_if = "Option::is_none")]
254    pub interest_only_expires_at: Option<chrono::DateTime<chrono::Utc>>,
255    /// The duration/term of the loan for it to be paid to completion from the
256    /// start date of the loan.
257    #[serde(default, skip_serializing_if = "Option::is_none")]
258    pub term: Option<String>,
259    /// When the loan matures, if available.
260    #[serde(default, skip_serializing_if = "Option::is_none")]
261    pub matures_at: Option<chrono::DateTime<chrono::Utc>>,
262    /// The loan initial principal amount, this was the original amount
263    /// borrowed.
264    #[serde(
265        default,
266        skip_serializing_if = "Option::is_none",
267        with = "rust_decimal::serde::arbitrary_precision_option"
268    )]
269    pub initial_principal: Option<rust_decimal::Decimal>,
270    /// Loan repayment information if available.
271    #[serde(default, skip_serializing_if = "Option::is_none")]
272    pub repayment: Option<RepaymentDetails>,
273}
274
275/// Interest rate information for a loan.
276#[derive(Debug, Deserialize, Serialize, Clone, PartialEq, Eq)]
277pub struct InterestDetails {
278    /// The rate of interest.
279    #[serde(with = "rust_decimal::serde::arbitrary_precision")]
280    pub rate: rust_decimal::Decimal,
281    /// The type of interest rate (E.g. FIXED).
282    // TODO: Could be an enum but we do not know all possible classifications.
283    #[serde(rename = "type")]
284    pub interest_type: String,
285    /// When this interest rate expires, if available.
286    pub expires_at: Option<chrono::DateTime<chrono::Utc>>,
287}
288
289/// Loan repayment information.
290#[derive(Debug, Deserialize, Serialize, Clone, PartialEq, Eq)]
291pub struct RepaymentDetails {
292    /// The frequency of the loan repayment (E.g. MONTHLY).
293    pub frequency: String,
294    /// The next repayment date, if available.
295    pub next_date: Option<chrono::DateTime<chrono::Utc>>,
296    /// The next instalment amount.
297    #[serde(with = "rust_decimal::serde::arbitrary_precision")]
298    pub next_amount: rust_decimal::Decimal,
299}
300
301/// Akahu can refresh different parts of an account's data at different rates.
302/// The timestamps in the refreshed object tell you when that account data was
303/// last updated.
304///
305/// When looking at a timestamp in here, you can think "Akahu's view of the
306/// account (balance/metadata/transactions) is up to date as of $TIME".
307///
308/// [<https://developers.akahu.nz/docs/the-account-model#refreshed>]
309#[derive(Debug, Deserialize, Serialize, Clone, PartialEq, Eq)]
310pub struct RefreshDetails {
311    /// When the balance was last updated.
312    #[serde(default, skip_serializing_if = "Option::is_none")]
313    pub balance: Option<chrono::DateTime<chrono::Utc>>,
314
315    /// When other account metadata was last updated (any account property apart
316    /// from balance).
317    #[serde(default, skip_serializing_if = "Option::is_none")]
318    pub meta: Option<chrono::DateTime<chrono::Utc>>,
319
320    /// When we last checked for and processed any new transactions.
321    ///
322    /// This flag may be missing when an account has first connected, as it
323    /// takes a few seconds for new transactions to be processed.
324    #[serde(default, skip_serializing_if = "Option::is_none")]
325    pub transactions: Option<chrono::DateTime<chrono::Utc>>,
326
327    /// When we last fetched identity data about the
328    /// [party](https://developers.akahu.nz/docs/enduring-identity-verification#party-data)
329    /// who has authenticated with the financial institution when connecting
330    /// this account.
331    ///
332    /// This data is updated by Akahu on a fixed 30 day interval, regardless of
333    /// your app's [data
334    /// refresh](https://developers.akahu.nz/docs/data-refreshes) configuration.
335    #[serde(default, skip_serializing_if = "Option::is_none")]
336    pub party: Option<chrono::DateTime<chrono::Utc>>,
337}
338
339/// The account balance.
340///
341/// [<https://developers.akahu.nz/docs/the-account-model#balance>]
342#[derive(Debug, Deserialize, Serialize, Clone, PartialEq, Eq)]
343pub struct BalanceDetails {
344    /// The current account balance.
345    ///
346    /// A negative balance indicates the amount owed to the account issuer. For
347    /// example a checking account in overdraft will have a negative balance,
348    /// same as the amount owed on a credit card or the principal remaining on a
349    /// loan.
350    #[serde(with = "rust_decimal::serde::arbitrary_precision")]
351    pub current: rust_decimal::Decimal,
352
353    /// The balance that is currently available to the account holder.
354    #[serde(
355        default,
356        skip_serializing_if = "Option::is_none",
357        with = "rust_decimal::serde::arbitrary_precision_option"
358    )]
359    pub available: Option<rust_decimal::Decimal>,
360
361    /// The credit limit for this account.
362    ///
363    /// For example a credit card limit or an overdraft limit. This value is
364    /// only present when provided directly by the connected financial
365    /// institution.
366    #[serde(
367        default,
368        skip_serializing_if = "Option::is_none",
369        with = "rust_decimal::serde::arbitrary_precision_option"
370    )]
371    pub limit: Option<rust_decimal::Decimal>,
372
373    /// A boolean indicating whether this account is in overdraft.
374    #[serde(default, skip_serializing_if = "Option::is_none")]
375    pub overdrawn: Option<bool>,
376
377    /// The [3 letter ISO 4217 currency code](https://www.xe.com/iso4217.php)
378    /// that this balance is in (e.g. NZD).
379    pub currency: iso_currency::Currency,
380}
381
382/// What sort of account this is. Akahu provides specific bank account types,
383/// and falls back to more general types for other types of connection.
384///
385/// [<https://developers.akahu.nz/docs/the-account-model#type>]
386#[derive(Debug, Deserialize, Serialize, Clone, PartialEq, Eq)]
387#[serde(rename_all = "UPPERCASE")]
388pub enum BankAccountKind {
389    /// An everyday spending account.
390    Checking,
391    /// A savings account.
392    ///
393    /// NOTE: A savings account is not necessarily a regular bank account. It might
394    /// not have transactions associated, or be able to receive payments. Check
395    /// the attributes field to see what this account can do.
396    Savings,
397    /// A credit card.
398    #[serde(rename = "CREDITCARD")]
399    CreditCard,
400    /// A loan account.
401    Loan,
402    /// A KiwiSaver investment product.
403    Kiwisaver,
404    /// A general investment product.
405    Investment,
406    /// A term deposit.
407    #[serde(rename = "TERMDEPOSIT")]
408    TermDeposit,
409    /// An account holding a foreign currency.
410    Foreign,
411    /// An account with tax authorities.
412    Tax,
413    /// An account for rewards points, e.g. Fly Buys or True Rewards.
414    Rewards,
415    /// Available cash for investment or withdrawal from an investment provider.
416    Wallet,
417}
418
419/// The list of attributes indicates what abilities an account has.
420///
421/// [<https://developers.akahu.nz/docs/the-account-model#attributes>]
422#[derive(Debug, Deserialize, Serialize, Clone, PartialEq, Eq)]
423#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
424pub enum Attribute {
425    /// Akahu can fetch available transactions from this account.
426    Transactions,
427    /// This account can receive transfers from accounts belonging to the same
428    /// set of credentials.
429    TransferTo,
430    /// This account can initiate transfers to accounts belonging to the same
431    /// set of credentials.
432    TransferFrom,
433    /// This account can receive payments from another bank account.
434    PaymentTo,
435    /// This account can initiate payments to another bank account.
436    PaymentFrom,
437}