libeufin

Integration and sandbox testing for FinTech APIs and data formats
Log | Files | Refs | Submodules | README | LICENSE

order.rs (8808B)


      1 /*
      2 * This file is part of LibEuFin.
      3 * Copyright (C) 2026 Taler Systems S.A.
      4 
      5 * LibEuFin is free software; you can redistribute it and/or modify
      6 * it under the terms of the GNU Affero General Public License as
      7 * published by the Free Software Foundation; either version 3, or
      8 * (at your option) any later version.
      9 
     10 * LibEuFin is distributed in the hope that it will be useful, but
     11 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
     12 * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Affero General
     13 * Public License for more details.
     14 
     15 * You should have received a copy of the GNU Affero General Public
     16 * License along with LibEuFin; see the file COPYING.  If not, see
     17 * <http://www.gnu.org/licenses/>
     18 */
     19 
     20 use compact_str::CompactString;
     21 use taler_macros::EnumMeta;
     22 
     23 #[derive(Debug, Clone, Copy, PartialEq, Eq)]
     24 pub enum Direction {
     25     Download,
     26     Upload,
     27 }
     28 
     29 #[derive(Debug, Clone)]
     30 pub struct BTF {
     31     pub service: CompactString,
     32     pub scope: Option<CompactString>,
     33     pub option: Option<CompactString>,
     34     pub container: Option<CompactString>,
     35     pub msg: CompactString,
     36     pub version: Option<CompactString>,
     37 }
     38 
     39 impl PartialEq for BTF {
     40     fn eq(&self, other: &Self) -> bool {
     41         self.service == other.service
     42             && self.scope == other.scope
     43             && self.option == other.option
     44             && self.container == other.container
     45             && self.msg == other.msg
     46         // Ignore msg version
     47     }
     48 }
     49 
     50 impl std::fmt::Display for BTF {
     51     fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
     52         let BTF {
     53             service: name,
     54             scope,
     55             option,
     56             container,
     57             msg,
     58             version,
     59         } = self;
     60         write!(f, "{name}")?;
     61         for part in [scope, container, option].into_iter().flatten() {
     62             write!(f, "-{part}")?;
     63         }
     64         write!(f, "-{msg}")?;
     65         if let Some(version) = version {
     66             write!(f, ".{version}")?;
     67         }
     68         Ok(())
     69     }
     70 }
     71 
     72 #[derive(Debug, Clone, PartialEq)]
     73 pub enum Order {
     74     /// Download of a file identified by a BTF structure (Mandatory)
     75     BTD(BTF),
     76     /// Upload of a file identified by a BTF structure (Mandatory)
     77     BTU(BTF),
     78     /// Download retrievable order types (Optional)
     79     HAA,
     80     /// Download customer acknowledgment (Mandatory)
     81     HAC,
     82     /// Send amendment of the subscriber key for identification and authentication and encryption (Mandatory)
     83     HCA,
     84     /// Transmission of the subscriber key for ES identification and authentication and encryption (Mandatory)
     85     HCS,
     86     /// Download supported EBICS versions (Mandatory)
     87     HEV,
     88     /// Transmission of the subscriber key for identification and authentication and encryption within the framework of subscriber initialization (Mandatory)
     89     HIA,
     90     /// Download customer’s customer and subscriber data (Optional)
     91     HKD,
     92     /// Transfer the public bank key (Mandatory)
     93     HPB,
     94     /// Download bank parameters (Mandatory)
     95     HPD,
     96     /// Download subscriber’s customer and subscriber data (Mandatory)
     97     HTD,
     98     /// Download subscriber’s customer and subscriber data (Optional)
     99     HVD,
    100     /// Add EDSsignature (Mandatory)
    101     HVE,
    102     /// Cancellation of orders in the EDS  (Mandatory)
    103     HVS,
    104     /// Retrieve EDS transaction details (Mandatory)
    105     HVT,
    106     /// Download EDS overview (Mandatory)
    107     HVU,
    108     /// Download EDS overview with additional information (Mandatory)
    109     HVZ,
    110     /// Transmission of all public keys (subscriber key, key for identification and authentication and key for encryption) for initialisation in case of CA-issued certificates (Optional)
    111     H3K,
    112     /// Send password initialization
    113     INI,
    114     /// Send public key for signature verification
    115     PUB,
    116     /// Suspension of access authorisation
    117     SPR,
    118     /// deprecated
    119     PTK,
    120 }
    121 
    122 impl Order {
    123     pub const WSS_PARAMS: Self = Self::BTD(BTF {
    124         service: CompactString::const_new("OTH"),
    125         scope: Some(CompactString::const_new("DE")),
    126         msg: CompactString::const_new("wssparam"),
    127         version: None,
    128         container: None,
    129         option: None,
    130     });
    131 
    132     pub fn doc(&self) -> Option<OrderDoc> {
    133         match self {
    134             Self::HAC => Some(OrderDoc::acknowledgement),
    135             Self::BTD(BTF { msg, .. }) => match msg.as_str() {
    136                 "pain.002" => Some(OrderDoc::status),
    137                 "camt.052" => Some(OrderDoc::report),
    138                 "camt.053" => Some(OrderDoc::statement),
    139                 "camt.054" => Some(OrderDoc::notification),
    140                 _ => None,
    141             },
    142             _ => None,
    143         }
    144     }
    145 
    146     /** Check if EBICS order is a downloadable one */
    147     pub fn is_downloadable(&self) -> bool {
    148         matches!(
    149             self.doc(),
    150             Some(OrderDoc::acknowledgement)
    151                 | Some(OrderDoc::status)
    152                 | Some(OrderDoc::report)
    153                 | Some(OrderDoc::statement)
    154                 | Some(OrderDoc::notification)
    155         )
    156     }
    157 
    158     /** Check if EBICS order is an uploadable one */
    159     pub fn is_upload(&self) -> bool {
    160         matches!(self, Self::BTU { .. })
    161     }
    162 
    163     pub fn schema(&self) -> &'static str {
    164         "H005"
    165     }
    166 
    167     pub fn file_type(&self) -> &str {
    168         match self {
    169             Order::BTD(BTF { container, .. }) | Order::BTU(BTF { container, .. }) => {
    170                 container.as_deref().unwrap_or("xml")
    171             }
    172             _ => "xml",
    173         }
    174     }
    175 
    176     pub fn ty(&self) -> &'static str {
    177         match self {
    178             Order::BTD { .. } => "BTD",
    179             Order::BTU { .. } => "BTU",
    180             Order::HAA => "HAA",
    181             Order::HAC => "HAC",
    182             Order::HCA => "HCA",
    183             Order::HCS => "HCS",
    184             Order::HEV => "HEV",
    185             Order::HIA => "HIA",
    186             Order::HKD => "HKD",
    187             Order::HPB => "HPB",
    188             Order::HPD => "HPD",
    189             Order::HTD => "HTD",
    190             Order::HVD => "HVD",
    191             Order::HVE => "HVE",
    192             Order::HVS => "HVS",
    193             Order::HVT => "HVT",
    194             Order::HVU => "HVU",
    195             Order::HVZ => "HVZ",
    196             Order::H3K => "H3K",
    197             Order::INI => "INI",
    198             Order::PUB => "PUB",
    199             Order::SPR => "SPR",
    200             Order::PTK => "PTK",
    201         }
    202     }
    203 
    204     pub fn from_parts(ty: &str, btf: Option<BTF>) -> Option<Self> {
    205         match (ty, btf) {
    206             ("BTU", Some(btf)) => Some(Self::BTU(btf)),
    207             ("BTD", Some(btf)) => Some(Self::BTD(btf)),
    208             ("HAA", None) => Some(Self::HAA),
    209             ("HAC", None) => Some(Self::HAC),
    210             ("HCA", None) => Some(Self::HCA),
    211             ("HCS", None) => Some(Self::HCS),
    212             ("HEV", None) => Some(Self::HEV),
    213             ("HIA", None) => Some(Self::HIA),
    214             ("HKD", None) => Some(Self::HKD),
    215             ("HPB", None) => Some(Self::HPB),
    216             ("HPD", None) => Some(Self::HPD),
    217             ("HTD", None) => Some(Self::HTD),
    218             ("HVD", None) => Some(Self::HVD),
    219             ("HVE", None) => Some(Self::HVE),
    220             ("HVS", None) => Some(Self::HVS),
    221             ("HVT", None) => Some(Self::HVT),
    222             ("HVU", None) => Some(Self::HVU),
    223             ("HVZ", None) => Some(Self::HVZ),
    224             ("H3K", None) => Some(Self::H3K),
    225             ("INI", None) => Some(Self::INI),
    226             ("PUB", None) => Some(Self::PUB),
    227             ("SPR", None) => Some(Self::SPR),
    228             ("PTK", None) => Some(Self::PTK),
    229             _ => None,
    230         }
    231     }
    232 }
    233 
    234 impl std::fmt::Display for Order {
    235     fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
    236         f.write_str(self.ty())?;
    237         match self {
    238             Order::BTD(btf) | Order::BTU(btf) => write!(f, "-{btf}"),
    239             _ => Ok(()),
    240         }
    241     }
    242 }
    243 
    244 #[derive(Debug, Clone, Copy, PartialEq, Eq, EnumMeta, PartialOrd, Ord)]
    245 #[enum_meta(Str, Description)]
    246 #[allow(non_camel_case_types)]
    247 pub enum OrderDoc {
    248     /// EBICS acknowledgement - CustomerAcknowledgement HAC pain.002
    249     acknowledgement,
    250     /// Payment status - CustomerPaymentStatusReport pain.002
    251     status,
    252     /// Debit & credit notifications - BankToCustomerDebitCreditNotification camt.054
    253     notification,
    254     /// Account statements - BankToCustomerStatement camt.053
    255     statement,
    256     /// Account intraday reports - BankToCustomerAccountReport camt.052
    257     report,
    258 }
    259 
    260 impl OrderDoc {
    261     pub fn short_description(&self) -> &'static str {
    262         match self {
    263             Self::acknowledgement => "EBICS acknowledgement",
    264             Self::status => "Payment status",
    265             Self::report => "Account intraday reports",
    266             Self::statement => "Account statements",
    267             Self::notification => "Debit & credit notifications",
    268         }
    269     }
    270 }