monzo_webhook/
lib.rs

1#![doc = include_str!("../README.md")]
2#![deny(unsafe_code)]
3#![deny(clippy::pedantic)]
4#![allow(
5    clippy::struct_excessive_bools,
6    reason = "structs cannot be changed due to serialization"
7)]
8
9use chrono::{DateTime, Utc};
10use serde::Deserialize;
11
12use std::collections::HashMap;
13
14#[cfg(test)]
15mod tests;
16
17#[cfg(feature = "decode_everything")]
18pub type ExtraValues = HashMap<String, serde_json::Value>;
19
20/// The main webhook data type.
21#[derive(Clone, Debug, Deserialize)]
22pub struct Webhook {
23    /// The type of webhook update.
24    /// Always one of:
25    /// - `transaction.created`
26    /// - `transaction.updated`
27    pub r#type: String,
28    pub data: WebhookData,
29    #[cfg(feature = "decode_everything")]
30    #[cfg_attr(feature = "decode_everything", serde(flatten))]
31    pub extra: ExtraValues,
32}
33
34#[derive(Clone, Debug, Deserialize)]
35pub struct WebhookData {
36    pub id: String,
37    pub created: DateTime<Utc>,
38    pub description: String,
39    /// The amount of money in the transaction, in whole pence (or equivalent for foreign currency)
40    pub amount: i64,
41    /// The ISO 4127 currency code of [`Self::amount`]
42    pub currency: String,
43    pub is_load: bool,
44    pub settled: SettledTimestamp,
45    /// The amount of money in the transaction, in whole pence (or equivalent for foreign currency)
46    pub local_amount: i64,
47    /// The ISO 4127 currency code of [`Self::local_amount`]
48    pub local_currency: String,
49    pub merchant: Option<Merchant>,
50    pub merchant_feedback_uri: String,
51    pub notes: String,
52    pub metadata: WebhookMetadata,
53    pub category: String,
54    pub updated: DateTime<Utc>,
55    pub account_id: String,
56    pub user_id: String,
57    pub counterparty: CounterpartyOrNone,
58    pub scheme: String,
59    pub dedupe_id: String,
60    pub originator: bool,
61    pub include_in_spending: bool,
62    pub can_be_excluded_from_breakdown: bool,
63    pub can_be_made_subscription: bool,
64    pub can_split_the_bill: bool,
65    pub can_add_to_tab: bool,
66    pub can_match_transactions_in_categorization: bool,
67    pub amount_is_pending: bool,
68    pub parent_account_id: String,
69    pub categories: Option<HashMap<String, i64>>,
70    #[cfg(feature = "decode_everything")]
71    #[cfg_attr(feature = "decode_everything", serde(flatten))]
72    pub extra: ExtraValues,
73}
74
75#[derive(Clone, Debug, Deserialize)]
76#[serde(untagged)]
77pub enum SettledTimestamp {
78    Settled(DateTime<Utc>),
79    /// If not yet settled, a string it returned, however it always seems to be empty.
80    NotYetSettled(String),
81    #[cfg(feature = "decode_everything")]
82    SomethingElse(std::collections::HashMap<String, serde_json::Value>),
83}
84
85#[derive(Clone, Debug, Deserialize)]
86pub struct Merchant {
87    pub id: String,
88    pub group_id: String,
89    pub name: String,
90    pub logo: String,
91    pub emoji: String,
92    pub category: String,
93    pub online: bool,
94    pub atm: bool,
95    pub address: MerchantAddress,
96    pub disable_feedback: bool,
97    pub suggested_tags: String,
98    pub metadata: MerchantMetadata,
99    #[cfg(feature = "decode_everything")]
100    #[cfg_attr(feature = "decode_everything", serde(flatten))]
101    pub extra: ExtraValues,
102}
103
104#[derive(Clone, Debug, Deserialize)]
105pub struct MerchantAddress {
106    pub short_formatted: String,
107    pub city: String,
108    pub latitude: f64,
109    pub longitude: f64,
110    pub zoom_level: u64,
111    pub approximate: bool,
112    pub formatted: String,
113    pub address: String,
114    pub region: String,
115    pub country: String,
116    pub postcode: String,
117    #[cfg(feature = "decode_everything")]
118    #[cfg_attr(feature = "decode_everything", serde(flatten))]
119    pub extra: ExtraValues,
120}
121
122#[derive(Clone, Debug, Deserialize)]
123pub struct MerchantMetadata {
124    pub suggested_tags: String,
125    pub website: String,
126    #[cfg(feature = "decode_everything")]
127    #[cfg_attr(feature = "decode_everything", serde(flatten))]
128    pub extra: ExtraValues,
129}
130
131#[derive(Clone, Debug, Deserialize)]
132pub struct WebhookMetadata {
133    #[serde(flatten)]
134    pub subtype: WebhookMetadataSubtype,
135    pub ledger_committed_timestamp_earliest: DateTime<Utc>,
136    pub ledger_committed_timestamp_latest: DateTime<Utc>,
137    #[cfg(feature = "decode_everything")]
138    #[cfg_attr(feature = "decode_everything", serde(flatten))]
139    pub extra: ExtraValues,
140}
141
142#[derive(Clone, Debug, Deserialize)]
143#[serde(untagged)]
144pub enum WebhookMetadataSubtype {
145    FasterPayment(FasterPayment),
146    MoneyTransfer(MoneyTransfer),
147    MerchantTransaction(MerchantTransaction),
148    #[cfg(feature = "decode_everything")]
149    SomethingElse(std::collections::HashMap<String, serde_json::Value>),
150}
151
152#[derive(Clone, Debug, Deserialize)]
153#[serde(untagged)]
154pub enum CounterpartyOrNone {
155    Counterparty(Counterparty),
156    None {},
157    #[cfg(feature = "decode_everything")]
158    SomethingElse(std::collections::HashMap<String, serde_json::Value>),
159}
160
161#[derive(Clone, Debug, Deserialize)]
162pub struct Counterparty {
163    pub account_number: String,
164    pub name: String,
165    pub sort_code: String,
166    pub user_id: String,
167    #[cfg(feature = "decode_everything")]
168    #[cfg_attr(feature = "decode_everything", serde(flatten))]
169    pub extra: ExtraValues,
170}
171
172/// A Faster Payments transaction
173#[derive(Clone, Debug, Deserialize)]
174pub struct FasterPayment {
175    pub faster_payment: String,
176    pub fps_fpid: String,
177    pub fps_payment_id: String,
178    pub insertion: String,
179    pub notes: String,
180    pub standin_correlation_id: String,
181    pub trn: String,
182    #[cfg(feature = "decode_everything")]
183    #[cfg_attr(feature = "decode_everything", serde(flatten))]
184    pub extra: ExtraValues,
185}
186
187/// A move of money between pots or accounts
188#[derive(Clone, Debug, Deserialize)]
189pub struct MoneyTransfer {
190    #[serde(flatten)]
191    pub subtype: MoneyTransferSubtype,
192    pub external_id: String,
193    pub ledger_insertion_id: String,
194    pub move_money_transfer_id: String,
195    pub pot_account_id: String,
196    pub pot_id: String,
197    pub transaction_description_localised: String,
198    pub transaction_locale_country: String,
199    pub trigger: String,
200    pub user_id: String,
201    #[cfg(feature = "decode_everything")]
202    #[cfg_attr(feature = "decode_everything", serde(flatten))]
203    pub extra: ExtraValues,
204}
205
206#[derive(Clone, Debug, Deserialize)]
207#[serde(untagged)]
208pub enum MoneyTransferSubtype {
209    PotWithdrawal(PotWithdrawal),
210    PotDeposit(PotDeposit),
211    #[cfg(feature = "decode_everything")]
212    SomethingElse(std::collections::HashMap<String, serde_json::Value>),
213}
214
215#[derive(Clone, Debug, Deserialize)]
216pub struct PotWithdrawal {
217    pub money_transfer_id: String,
218    pub pot_withdrawal_id: String,
219    #[cfg(feature = "decode_everything")]
220    #[cfg_attr(feature = "decode_everything", serde(flatten))]
221    pub extra: ExtraValues,
222}
223
224#[derive(Clone, Debug, Deserialize)]
225pub struct PotDeposit {
226    pub pot_deposit_id: String,
227    #[cfg(feature = "decode_everything")]
228    #[cfg_attr(feature = "decode_everything", serde(flatten))]
229    pub extra: ExtraValues,
230}
231
232#[derive(Clone, Debug, Deserialize)]
233pub struct MerchantTransaction {
234    pub mcc: String,
235    pub token_transaction_identifier: String,
236    pub tokenization_method: String,
237    pub transaction_description_localised: String,
238    pub transaction_locale_country: String,
239    pub standin_correlation_id: String,
240    pub token_unique_reference: String,
241    pub ledger_insertion_id: String,
242    pub mastercard_lifecycle_id: String,
243    pub mastercard_approval_type: String,
244    pub mastercard_auth_message_id: String,
245    pub mastercard_card_id: String,
246}