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 }