taler-rust

GNU Taler code in Rust. Largely core banking integrations.
Log | Files | Refs | Submodules | README | LICENSE

types.rs (14575B)


      1 /*
      2   This file is part of TALER
      3   Copyright (C) 2025 Taler Systems SA
      4 
      5   TALER is free software; you can redistribute it and/or modify it under the
      6   terms of the GNU Affero General Public License as published by the Free Software
      7   Foundation; either version 3, or (at your option) any later version.
      8 
      9   TALER is distributed in the hope that it will be useful, but WITHOUT ANY
     10   WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
     11   A PARTICULAR PURPOSE.  See the GNU Affero General Public License for more details.
     12 
     13   You should have received a copy of the GNU Affero General Public License along with
     14   TALER; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
     15 */
     16 
     17 use std::collections::BTreeMap;
     18 
     19 use jiff::Timestamp;
     20 use serde::Deserialize;
     21 use taler_common::types::amount::Decimal;
     22 
     23 use crate::CyclosId;
     24 
     25 #[derive(Debug, Deserialize, Clone)]
     26 #[serde(rename_all = "camelCase")]
     27 pub struct Type {
     28     pub id: CyclosId,
     29     pub internal_name: String,
     30     pub name: String,
     31 }
     32 
     33 #[derive(Debug, Deserialize, Clone)]
     34 #[serde(rename_all = "camelCase")]
     35 pub struct Currency {
     36     #[serde(flatten)]
     37     pub ty: Type,
     38     pub decimal_digits: u8,
     39     pub suffix: String,
     40     pub symbol: String,
     41 }
     42 
     43 #[derive(Debug, Deserialize, Clone)]
     44 #[serde(rename_all = "camelCase")]
     45 pub struct AccountStatus {
     46     pub available_balance: Decimal,
     47     pub balance: Decimal,
     48     pub credit_limit: Decimal,
     49     pub reserved_amount: Decimal,
     50 }
     51 
     52 #[derive(Debug, Deserialize, Clone)]
     53 #[serde(rename_all = "camelCase")]
     54 pub struct Account {
     55     pub id: CyclosId,
     56     pub currency: Currency,
     57     pub status: AccountStatus,
     58     #[serde(rename = "type")]
     59     pub ty: Type,
     60 }
     61 
     62 #[derive(Debug, Deserialize)]
     63 #[serde(rename_all = "camelCase")]
     64 pub struct User {
     65     pub id: CyclosId,
     66     pub name: String,
     67 }
     68 
     69 #[derive(Debug, Deserialize)]
     70 #[serde(rename_all = "camelCase")]
     71 pub struct PaymentData {
     72     #[serde(flatten)]
     73     pub ty: Type,
     74     pub currency: Currency,
     75 }
     76 
     77 #[derive(Debug, Deserialize)]
     78 #[serde(rename_all = "camelCase")]
     79 pub struct DataForTransaction {
     80     pub payment_type_data: PaymentData,
     81 }
     82 
     83 #[derive(Debug, Deserialize)]
     84 #[serde(rename_all = "camelCase")]
     85 pub struct RelatedAccount {
     86     pub id: CyclosId,
     87     #[serde(rename = "type")]
     88     pub ty: Type,
     89     #[serde(flatten)]
     90     pub kind: AccountKind,
     91 }
     92 
     93 #[derive(Debug, Deserialize)]
     94 #[serde(rename_all = "camelCase")]
     95 pub struct HistoryItem {
     96     pub id: CyclosId,
     97     pub date: Timestamp,
     98     pub amount: String,
     99     #[serde(rename = "type")]
    100     pub ty: Type,
    101     pub description: Option<String>,
    102     pub related_account: RelatedAccount,
    103     pub transaction: Option<TransactionMini>
    104 }
    105 
    106 #[derive(Debug, Deserialize)]
    107 #[serde(rename_all = "camelCase")]
    108 pub struct TransferView {
    109     pub id: CyclosId,
    110     pub date: Timestamp,
    111     pub amount: Decimal,
    112     #[serde(rename = "type")]
    113     pub ty: Type,
    114 }
    115 
    116 #[derive(Debug, Deserialize)]
    117 #[serde(rename_all = "camelCase")]
    118 pub struct Transfer {
    119     pub id: CyclosId,
    120     pub date: Timestamp,
    121     pub amount: Decimal,
    122     pub can_chargeback: bool,
    123     pub kind: TransferKind,
    124     pub currency: Currency,
    125     pub chargeback_of: Option<TransferView>,
    126     pub chargeback_by: Option<TransferView>,
    127     #[serde(rename = "type")]
    128     pub ty: Type,
    129 }
    130 
    131 #[derive(Debug, Deserialize)]
    132 #[serde(rename_all = "camelCase")]
    133 pub struct Transaction {
    134     pub id: CyclosId,
    135     pub date: Timestamp,
    136     pub amount: Decimal,
    137     pub kind: TxKind,
    138     pub currency: Currency,
    139     #[serde(rename = "type")]
    140     pub ty: Type,
    141 }
    142 
    143 #[derive(Debug, Deserialize)]
    144 #[serde(rename_all = "camelCase")]
    145 pub struct TransactionMini {
    146     pub id: CyclosId,
    147     pub kind: TxKind,
    148 }
    149 
    150 #[derive(Debug, serde::Deserialize)]
    151 #[serde(rename_all = "camelCase")]
    152 /// Indicates the reason the transfer was created.
    153 pub enum TransferKind {
    154     /// A transfer generated by an account fee charge
    155     AccountFee,
    156     /// A transfer which either withdraws or credits funds of an account being disposed
    157     BalanceDisposal,
    158     /// A transfer which is a chargeback of another transfer
    159     Chargeback,
    160     /// An imported transfer
    161     Import,
    162     /// A transfer which is the initial credit for a newly created account
    163     InitialCredit,
    164     /// A transfer generated when processing a scheduled / recurring payment installment / occurrence
    165     Installment,
    166     /// A transfer generated by a direct payment or accepting a webshop order
    167     Payment,
    168     /// A transfer generated by a transfer fee charge
    169     TransferFee,
    170 }
    171 
    172 #[derive(Debug, serde::Deserialize)]
    173 #[serde(rename_all = "camelCase")]
    174 pub enum TxKind {
    175     Chargeback,
    176     ExternalPayment,
    177     Import,
    178     Order,
    179     Payment,
    180     PaymentRequest,
    181     RecurringPayment,
    182     ScheduledPayment,
    183     Ticket,
    184 }
    185 
    186 #[derive(Debug, serde::Deserialize)]
    187 #[serde(rename_all = "camelCase")]
    188 pub enum InvalidDeviceConfirmation {
    189     InvalidConfirmation,
    190     InvalidDevice,
    191     MaxCheckAtemptsReached,
    192 }
    193 
    194 #[derive(Debug, serde::Deserialize)]
    195 #[serde(rename_all = "camelCase")]
    196 pub struct UserInfo {
    197     pub id: CyclosId,
    198     pub display: String,
    199 }
    200 
    201 #[derive(Debug, serde::Deserialize)]
    202 #[serde(rename_all = "camelCase")]
    203 #[serde(tag = "kind")]
    204 pub enum AccountKind {
    205     System,
    206     User { user: UserInfo },
    207 }
    208 
    209 #[derive(Debug, serde::Deserialize)]
    210 #[serde(rename_all = "camelCase")]
    211 pub enum UserStatus {
    212     Active,
    213     Blocked,
    214     Disabled,
    215     Pending,
    216     Purged,
    217     Removed,
    218 }
    219 
    220 #[derive(Debug, serde::Deserialize)]
    221 #[serde(rename_all = "camelCase")]
    222 pub enum PasswordStatus {
    223     Active,
    224     Disabled,
    225     Expired,
    226     IndefinitelyBlocked,
    227     NeverCreated,
    228     Pending,
    229     Reset,
    230     TemporarilyBlocked,
    231 }
    232 
    233 #[derive(Debug, serde::Deserialize, thiserror::Error)]
    234 #[serde(tag = "code", rename_all = "camelCase")]
    235 pub enum UnauthorizedError {
    236     #[error("blockedAccessClient - The access client used for access is blocked")]
    237     BlockedAccessClient,
    238     #[error("invalidAccessClient - The access client used for access is invalid")]
    239     InvalidAccessClient,
    240     #[error(
    241         "invalidAccessToken '{error}' - The OAuth2 / OpenID Connect access token used for access is invalid"
    242     )]
    243     InvalidAccessToken { error: String },
    244     #[error(
    245         "invalidChannelUsage - Attempt to login on a stateless-only channel, or use stateless in a stateful-only channel, or invoke as guest in a channel configuration which is only for users"
    246     )]
    247     InvalidChannelUsage,
    248     #[error(
    249         "invalidNetwork - Attempt to access a network that has been disabled, or a restricted global administrator is trying to login to a inaccessible network"
    250     )]
    251     InvalidNetwork,
    252     #[error("loggedOut - The session token used for access is invalid")]
    253     LoggedOut,
    254     #[error(
    255         "login {invalid_device_confirmation:?} {user_status:?} {pasword_status:?} - Either user identification (principal) or password are invalid. May have additional information, such as the user / password status"
    256     )]
    257     #[serde(rename = "camelCase")]
    258     Login {
    259         invalid_device_confirmation: InvalidDeviceConfirmation,
    260         user_status: UserStatus,
    261         pasword_status: PasswordStatus,
    262     },
    263     #[error(
    264         "missingAuthorization - Attempt to access an operation as guest, but the operation requires authentication"
    265     )]
    266     MissingAuthorization,
    267     #[error(
    268         "remoteAddressBlocked - The IP address being used for access has been blocked by exceeding tries with invalid users"
    269     )]
    270     RemoteAddressBlocked,
    271     #[error(
    272         "unauthorizedAddress - The user cannot access the system using an IP address that is not white-listed"
    273     )]
    274     UnauthorizedAddress,
    275     #[error(
    276         "unauthorizedUrl - The user's configuration demands access using a specific URL, and this access is being done using another one"
    277     )]
    278     UnauthorizedUrl,
    279 }
    280 
    281 #[derive(Debug, serde::Deserialize, thiserror::Error)]
    282 #[serde(tag = "code", rename_all = "camelCase")]
    283 /// Error codes for 403 Forbidden HTTP status.
    284 pub enum ForbiddenError {
    285     #[error("blockedByTotp - The user was blocked for exceeding the TOTP attempts")]
    286     BlockedByTotp,
    287     #[error("devicePinRemoved - The device pin was removed by exceeding the allowed attempts")]
    288     DevicePinRemoved,
    289     #[error("disabledPassword - The password being used was disabled")]
    290     DisabledPassword,
    291     #[error("expiredPassword - The password being used has expired")]
    292     ExpiredPassword,
    293     #[error("illegalAction - Attempt to perform an action that is not allowed on this context")]
    294     IllegalAction,
    295     #[error("inaccessibleChannel - This channel cannot be accessed by the user")]
    296     InaccessibleChannel,
    297     #[error(
    298         "inaccessibleGroup - An administrator logging in another user which is not of the managed groups. Should be handled generically, in case more group-specific login restrictions are added to Cyclos"
    299     )]
    300     InaccessibleGroup,
    301     #[error(
    302         "inaccessibleNetwork - A restricted global administrator is trying to login to a network they can't access"
    303     )]
    304     InaccessibleNetwork,
    305     #[error(
    306         "inaccessiblePrincipal - The used identification method (principal type) cannot be used in this channel"
    307     )]
    308     InaccessiblePrincipal,
    309     #[error(
    310         "indefinitelyBlocked - The password was indefinitely blocked by exceeding the allowed attempts"
    311     )]
    312     IndefinitelyBlocked,
    313     #[error("invalidDeviceActivationCode - The device activation code was no valid")]
    314     InvalidDeviceActivationCode,
    315     #[error(
    316         "invalidDeviceConfirmation - The device confirmation being used is invalid (normally as a confirmation password)"
    317     )]
    318     InvalidDeviceConfirmation,
    319     #[error(
    320         "invalidPassword - The password being used is invalid (normally the confirmation password)"
    321     )]
    322     InvalidPassword,
    323     #[error("invalidTotp - A given TOTP is invalid")]
    324     InvalidTotp,
    325     #[error("loginConfirmation - The user needs to confirm the login before proceeding")]
    326     LoginConfirmation,
    327     #[error(
    328         "operatorWithPendingAgreements - The operator cannot access because his owner member has pending agreements"
    329     )]
    330     OperatorWithPendingAgreements,
    331     #[error("otpInvalidated - The OTP was invalidated")]
    332     OtpInvalidated,
    333     #[error(
    334         "pendingAgreements - There is at least one agreement which needs to be accepted in order to access the system"
    335     )]
    336     PendingAgreements,
    337     #[error(
    338         "permissionDenied - The operation was denied because a required permission was not granted"
    339     )]
    340     PermissionDenied,
    341     #[error("resetPassword - The password being used was manually reset")]
    342     ResetPassword,
    343     #[error(
    344         "temporarilyBlocked - The password was temporarily blocked by exceeding the allowed attempts"
    345     )]
    346     TemporarilyBlocked,
    347 }
    348 
    349 #[derive(Debug, serde::Deserialize, thiserror::Error)]
    350 #[serde(tag = "code", rename_all = "camelCase")]
    351 /// Error codes for 404 Service Unavailable entity HTTP status. It means that some required service couldn't be contacted
    352 pub enum UnavailableError {
    353     #[error("emailSending - An error has occurred trying to send the a required email")]
    354     Emailsending,
    355     #[error("smsSending - An error has occurred trying to send a required SMS message")]
    356     Smssending,
    357 }
    358 
    359 #[derive(Debug, serde::Deserialize, thiserror::Error)]
    360 #[serde(tag = "code", rename_all = "camelCase")]
    361 #[error("{entity_type} {key}")]
    362 /// Error codes for 404 Not Found
    363 pub struct NotFoundError {
    364     pub entity_type: String,
    365     pub key: String,
    366 }
    367 
    368 #[derive(Debug, serde::Deserialize, thiserror::Error)]
    369 #[serde(tag = "code", rename_all = "camelCase")]
    370 /// Error types associated to the HTTP Status 500.
    371 pub enum UnexpectedError {
    372     #[error("buyVoucher - An error has occurred when buying a voucher")]
    373     BuyVoucher,
    374     #[error("forgottenPassword - An error has occurred when changing a forgotten password")]
    375     ForgottenPassword,
    376     #[error("general - An unexpected error has occurred")]
    377     General,
    378     #[error("initializeNfc - An error has occurred when initializing a NFC token")]
    379     InitializeNfc,
    380     #[error("nested - An error which has another internal error at a given property / index")]
    381     Nested,
    382     #[error("nfcAuth - An error has occurred when making an external NFC authentication")]
    383     NfcAuth,
    384     #[error("oidc - An error for an OpenID Connect / OAuth 2 operation")]
    385     Oidc,
    386     #[error("payment - An error has occurred when making a payment")]
    387     Payment,
    388     #[error("personalizeNfc - An error has occurred when personalizing a NFC token")]
    389     PersonalizeNfc,
    390     #[error("pos - An error has occurred when receiving a payment on a POS operation")]
    391     Pos,
    392     #[error("redeemVoucher - An error has occurred when redeeming a voucher")]
    393     RedeemVoucher,
    394     #[error("shoppingCart - An error has occurred when interacting with a shopping cart")]
    395     ShoppingCart,
    396     #[error("shoppingCartCheckout - An error has occurred when checking out a shopping cart")]
    397     ShoppingCartCheckout,
    398     #[error("topUpVoucher - An error has occurred on a voucher top-up")]
    399     TopUpVoucher,
    400 }
    401 
    402 #[derive(Debug, serde::Deserialize, thiserror::Error)]
    403 #[serde(tag = "code", rename_all = "camelCase")]
    404 /// Error codes for 422 Unprocessable entity HTTP status. It means there was an error with the input sent to the operation.
    405 pub enum InputError {
    406     #[error("aggregated - Represents an aggregation of other input errors")]
    407     Aggregated,
    408     #[error(
    409         "dataConversion {value} - Some data conversion has failed. For example, when sending a date with an invalid format"
    410     )]
    411     DataConversion { value: String },
    412     #[error("fileUploadSize {max_file_size} - An uploaded file size exceeds the maximum allowed")]
    413     #[serde(rename_all = "camelCase")]
    414     FileUploadSize { max_file_size: usize },
    415     #[error(
    416         "maxItems {max_items} - There was an attempt to create an item, but the maximum number of allowed items was exceeded"
    417     )]
    418     #[serde(rename_all = "camelCase")]
    419     MaxItems { max_items: usize },
    420     #[error("missingParameter {name} - Missing a required request parameter")]
    421     MissingParameter { name: String },
    422     #[error("queryParse {value} - A full-text query keywords contained an invalid text")]
    423     QueryParse { value: String },
    424     #[error(
    425         "validation {general_errors:?} {property_errors:?} - One or more of the fields sent contains invalid values"
    426     )]
    427     #[serde(rename_all = "camelCase")]
    428     Validation {
    429         #[serde(default)]
    430         general_errors: Vec<String>,
    431         #[serde(default)]
    432         properties: Vec<String>,
    433         #[serde(default)]
    434         property_errors: BTreeMap<String, Vec<String>>,
    435     },
    436 }