1#[cfg(feature = "uniffi")]
2use chrono::NaiveDate;
3use chrono::Utc;
4use heck::ToKebabCase;
5#[cfg(feature = "uniffi")]
6use isocountry::CountryCode;
7use jsonwebtoken::{DecodingKey, EncodingKey, Header, Validation, decode_header, encode};
8pub use routex_models::{
9 AccountStatus, AccountType, Amount, ConnectionId, CredentialsModel, DialogContext,
10 DialogOption, Image, InputType, PaymentErrorCode, ProviderErrorCode, SecrecyLevel,
11 ServiceBlockedCode, UnsupportedProductReason,
12};
13#[cfg(feature = "uniffi")]
14use rust_decimal::Decimal;
15use serde::{Deserialize, Serialize};
16use serde_json::Value;
17use serde_with::base64::Base64;
18use std::fmt;
19use std::marker::PhantomData;
20use std::str::FromStr;
21use url::Url;
22
23#[cfg(feature = "uniffi")]
24uniffi::setup_scaffolding!();
25
26#[cfg(feature = "uniffi")]
27uniffi::custom_type!(ConnectionData, Vec<u8>, {
28 try_lift: |val| Ok(val.into()),
29 lower: |obj| obj.into(),
30});
31
32#[cfg(feature = "uniffi")]
33uniffi::custom_type!(Session, Vec<u8>, {
34 try_lift: |val| Ok(val.into()),
35 lower: |obj| obj.into(),
36});
37
38#[cfg(feature = "uniffi")]
39uniffi::custom_type!(CountryCode, String, {
40 remote,
41 try_lift: |val| Ok(Self::for_alpha2(&val)?),
42 lower: |obj| obj.alpha2().to_string(),
43});
44
45#[cfg(feature = "uniffi")]
46uniffi::use_remote_type!(routex_models::Decimal);
47
48#[cfg(feature = "uniffi")]
49uniffi::custom_type!(NaiveDate, String, {
50 remote,
51 try_lift: |val| Ok(val.parse()?),
52 lower: |obj| obj.to_string(),
53});
54
55type DateTime = chrono::DateTime<Utc>;
56
57#[cfg(feature = "uniffi")]
58uniffi::custom_type!(DateTime, String, {
59 remote,
60 try_lift: |val| Ok(val.parse()?),
61 lower: |obj| obj.to_string(),
62});
63
64pub type Dialog<S> = routex_models::Dialog<ConfirmationContext<S>, InputContext<S>>;
65pub type DialogInput<S> = routex_models::DialogInput<ConfirmationContext<S>, InputContext<S>>;
66
67pub struct Path(Vec<String>);
74
75impl Path {
76 fn new<I>(segments: I) -> Self
77 where
78 I: IntoIterator,
79 I::Item: ToString,
80 {
81 Self(segments.into_iter().map(|s| s.to_string()).collect())
82 }
83
84 #[must_use]
85 pub fn to_url(&self, base: &Url) -> Url {
91 let mut url = base.clone();
92 url.path_segments_mut()
93 .expect("cannot be base")
94 .extend(&self.0);
95 url
96 }
97}
98
99impl fmt::Display for Path {
100 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
101 for segment in &self.0 {
102 f.write_str("/")?;
103 f.write_str(segment)?;
104 }
105
106 Ok(())
107 }
108}
109
110pub mod headers {
111 use http::HeaderName;
112
113 pub static SESSION_ID: HeaderName = HeaderName::from_static("yaxi-session-id");
114
115 pub static TICKET: HeaderName = HeaderName::from_static("yaxi-ticket");
116
117 pub static TICKET_ID: HeaderName = HeaderName::from_static("yaxi-ticket-id");
118
119 pub static REDIRECT_URI: HeaderName = HeaderName::from_static("yaxi-redirect-uri");
120
121 pub static TRACE_ID: HeaderName = HeaderName::from_static("yaxi-trace-id");
122}
123
124pub const CURRENT_MEDIA_TYPE: &str = "application/vnd.yaxi.v5";
125
126pub mod keys {
127 use super::Path;
128 use clerk_report::PublishedVersionEntry;
129 use serde::{Deserialize, Serialize};
130 use serde_with::base64::Base64;
131
132 #[must_use]
138 pub fn settlement_path() -> Path {
139 Path::new(["key-settlement"])
140 }
141
142 #[serde_with::serde_as]
143 #[derive(Serialize, Deserialize, Clone, Debug)]
144 #[serde(rename_all = "camelCase")]
145 pub struct Request {
146 #[serde_as(as = "Base64")]
148 pub public_key: [u8; 32],
149 }
150
151 #[serde_with::serde_as]
153 #[derive(Serialize, Deserialize)]
154 #[serde(rename_all = "camelCase")]
155 pub struct SettlementBoxMessage {
156 #[serde_as(as = "Base64")]
157 pub public_key: [u8; 32],
158 #[serde_as(as = "Base64")]
159 pub session_id: [u8; 32],
160 }
161
162 #[serde_with::serde_as]
164 #[derive(Serialize, Deserialize, Clone, Debug)]
165 #[serde(rename_all = "camelCase")]
166 pub struct Response {
167 #[serde_as(as = "Base64")]
169 pub attestation_report: Vec<u8>,
170 pub vcek: String,
172 #[serde_as(as = "Base64")]
174 pub chacha_box: Vec<u8>,
175 pub system_version: PublishedVersionEntry,
178 }
179}
180
181pub mod traces {
182 use super::Path;
183
184 #[must_use]
189 pub fn path(trace_id: &str) -> Path {
190 Path::new(["traces", trace_id])
191 }
192}
193
194pub mod redirects {
195 use super::Path;
196 use serde::{Deserialize, Serialize};
197 use url::Url;
198
199 #[must_use]
205 pub fn path() -> Path {
206 Path::new(["redirects"])
207 }
208
209 #[derive(Serialize, Deserialize, Clone, Debug)]
210 #[serde(rename_all = "camelCase")]
211 pub struct Request {
212 pub handle: String,
213 #[serde(alias = "redirect_uri")]
214 pub redirect_uri: String,
215 }
216
217 #[derive(Serialize, Deserialize, Clone, Debug)]
218 #[serde(rename_all = "camelCase")]
219 pub struct Response {
220 pub redirect_url: Url,
221 }
222}
223
224pub mod info {
225 use super::Path;
226 pub use isocountry::CountryCode;
227 use routex_models::{ConnectionId, CredentialsModel};
228 use serde::{Deserialize, Serialize};
229
230 #[must_use]
236 pub fn search_path() -> Path {
237 Path::new(["search"])
238 }
239
240 #[derive(Serialize, Deserialize, Clone, Debug)]
241 #[serde(rename_all = "camelCase")]
242 pub struct Request {
243 pub filters: Vec<SearchFilter>,
244 #[serde(alias = "iban_detection")]
245 pub iban_detection: bool,
246 #[serde(skip_serializing_if = "Option::is_none")]
247 pub limit: Option<usize>,
248 }
249
250 #[derive(Serialize, Deserialize, Clone, Debug)]
254 #[serde(rename_all = "camelCase")]
255 #[serde(rename_all_fields = "camelCase")]
256 pub enum SearchFilter {
257 #[serde(alias = "Types")]
259 Types(Vec<ConnectionType>),
260 #[serde(alias = "Countries")]
262 Countries(Vec<CountryCode>),
263 #[serde(alias = "Name")]
265 Name(String),
266 #[serde(alias = "Bic")]
268 Bic(String),
269 #[serde(alias = "BankCode")]
271 BankCode(String),
272 #[serde(alias = "Term")]
274 Term(String),
275 }
276
277 #[derive(Serialize, Deserialize, Hash, PartialEq, Eq, Copy, Clone, Debug)]
279 #[non_exhaustive]
280 #[cfg_attr(feature = "uniffi", derive(uniffi::Enum))]
281 pub enum ConnectionType {
282 Production,
284 Sandboxes,
286 }
287
288 #[must_use]
293 pub fn fetch_path(connection_id: &str) -> Path {
294 Path::new(["info", connection_id])
295 }
296
297 #[allow(clippy::module_name_repetitions)]
299 #[derive(Serialize, Deserialize, Hash, Eq, PartialEq, Clone, Debug)]
300 #[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
301 #[serde(rename_all = "camelCase")]
302 #[cfg_attr(not(feature = "server"), non_exhaustive)]
303 pub struct ConnectionInfo {
304 pub id: ConnectionId,
306
307 pub countries: Vec<CountryCode>,
309
310 pub display_name: String,
312
313 pub credentials: CredentialsModel,
315
316 #[serde(skip_serializing_if = "Option::is_none")]
318 #[cfg_attr(feature = "uniffi", uniffi(default = None))]
319 pub user_id: Option<String>,
320
321 #[serde(skip_serializing_if = "Option::is_none")]
323 #[cfg_attr(feature = "uniffi", uniffi(default = None))]
324 pub advice: Option<String>,
325
326 pub logo_id: String,
328 }
329}
330
331macro_rules! context {
332 ($name:ident) => {
333 #[serde_with::serde_as]
334 #[derive(Serialize, Deserialize)]
335 #[serde(transparent)]
336 pub struct $name<S>(#[serde_as(as = "Base64")] Vec<u8>, PhantomData<fn(S)>);
337
338 impl<S> PartialEq for $name<S> {
339 fn eq(&self, other: &Self) -> bool {
340 self.0 == other.0
341 }
342 }
343
344 impl<S> Eq for $name<S> {}
345
346 impl<S> Clone for $name<S> {
347 fn clone(&self) -> Self {
348 Self(self.0.clone(), self.1)
349 }
350 }
351
352 impl<S> fmt::Debug for $name<S> {
353 fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
354 f.debug_tuple("$name")
355 .field(&self.0)
356 .field(&self.1)
357 .finish()
358 }
359 }
360
361 impl<S> From<Vec<u8>> for $name<S> {
362 fn from(value: Vec<u8>) -> Self {
363 Self(value, PhantomData)
364 }
365 }
366
367 impl<S> From<$name<S>> for Vec<u8> {
368 fn from(value: $name<S>) -> Self {
369 value.0
370 }
371 }
372
373 impl<S> AsRef<[u8]> for $name<S> {
374 fn as_ref(&self) -> &[u8] {
375 &self.0
376 }
377 }
378 };
379}
380
381context!(InputContext);
382
383context!(ConfirmationContext);
384
385#[derive(Serialize, PartialEq, Eq, Clone, Debug)]
411#[serde(transparent)]
412pub struct Authenticated<T> {
413 jwt: String,
414 _phantom: PhantomData<T>,
415}
416
417impl<'de, T> Deserialize<'de> for Authenticated<T>
418where
419 T: for<'t> Deserialize<'t>,
420{
421 fn deserialize<D>(deserializer: D) -> std::result::Result<Self, D::Error>
422 where
423 D: serde::Deserializer<'de>,
424 {
425 String::deserialize(deserializer)?
426 .parse()
427 .map_err(serde::de::Error::custom)
428 }
429}
430
431impl<T> Authenticated<T>
432where
433 T: Serialize,
434{
435 #[must_use]
436 pub fn new(data: T, key_id: impl Into<String>, key: &[u8]) -> Self {
442 let header = Header {
443 kid: Some(key_id.into()),
444 ..Default::default()
445 };
446
447 let jwt = encode(
448 &header,
449 &Claims {
450 data,
451 exp: 2_540_808_000,
453 },
454 &EncodingKey::from_secret(key),
455 )
456 .expect("Encoding should work");
457
458 Self {
459 jwt,
460 _phantom: PhantomData,
461 }
462 }
463}
464
465impl<T> FromStr for Authenticated<T>
466where
467 T: for<'de> Deserialize<'de>,
468{
469 type Err = jsonwebtoken::errors::Error;
470
471 fn from_str(jwt: &str) -> Result<Self, Self::Err> {
472 decode::<T>(jwt)?;
474 Ok(Self {
475 jwt: jwt.to_string(),
476 _phantom: PhantomData,
477 })
478 }
479}
480
481impl<T> Authenticated<T>
482where
483 T: for<'de> Deserialize<'de>,
484{
485 #[must_use]
486 pub fn as_str(&self) -> &str {
487 &self.jwt
488 }
489
490 #[must_use]
491 pub fn key_id(&self) -> Option<String> {
497 decode_header(&self.jwt).unwrap().kid
498 }
499
500 #[cfg(feature = "client")]
501 #[must_use]
502 #[allow(clippy::missing_panics_doc)]
503 pub fn to_data(&self) -> T {
504 decode(&self.jwt).unwrap()
505 }
506
507 #[cfg(feature = "server")]
508 pub fn decode<D: Decoder>(&self, decoder: &D) -> Result<T, D::Error> {
514 decoder.decode::<T>(&self.jwt).map(|d| d.data)
515 }
516}
517
518#[derive(Serialize, Deserialize, PartialEq, Eq, Clone, Debug)]
519#[serde(rename_all = "camelCase")]
520pub struct Claims<T> {
521 pub data: T,
522 pub exp: u64,
523}
524
525#[cfg(feature = "server")]
526pub trait Decoder {
527 type Error;
528
529 fn decode<T>(&self, jwt: &str) -> Result<Claims<T>, Self::Error>
535 where
536 T: for<'de> Deserialize<'de>;
537}
538
539#[derive(Serialize, Deserialize, Debug)]
544pub enum OBResponse<S: Service> {
545 #[serde(bound = "S:")]
546 Result(
547 Authenticated<OBResult<S::Output>>,
548 Option<Session>,
549 Option<ConnectionData>,
550 ),
551 #[serde(bound = "S:")]
552 Dialog(Dialog<S>),
553 #[serde(bound = "S:")]
554 Redirect(Redirect<S>),
555 #[serde(bound = "S:")]
556 RedirectHandle(RedirectHandle<S>),
557}
558
559impl<S: Service> Clone for OBResponse<S> {
560 fn clone(&self) -> Self {
561 match self {
562 OBResponse::Result(authenticated, session, connection_data) => OBResponse::Result(
563 authenticated.clone(),
564 session.clone(),
565 connection_data.clone(),
566 ),
567 OBResponse::Dialog(dialog) => OBResponse::Dialog(dialog.clone()),
568 OBResponse::Redirect(redirect) => OBResponse::Redirect(redirect.clone()),
569 OBResponse::RedirectHandle(redirect_handle) => {
570 OBResponse::RedirectHandle(redirect_handle.clone())
571 }
572 }
573 }
574}
575
576#[derive(Serialize, Deserialize, PartialEq, Eq, Clone, Debug)]
581#[serde(rename_all = "camelCase")]
582pub struct OBResult<T> {
583 pub data: T,
584 pub ticket_id: String,
585 pub timestamp: DateTime,
586}
587
588#[serde_with::serde_as]
589#[derive(Serialize, Deserialize, PartialEq, Eq, Clone, Debug)]
590pub struct Session(#[serde_as(as = "Base64")] Vec<u8>);
591
592impl From<Vec<u8>> for Session {
593 fn from(value: Vec<u8>) -> Self {
594 Session(value)
595 }
596}
597
598impl From<Session> for Vec<u8> {
599 fn from(session: Session) -> Self {
600 session.0
601 }
602}
603
604impl AsRef<[u8]> for Session {
605 fn as_ref(&self) -> &[u8] {
606 &self.0
607 }
608}
609
610#[serde_with::serde_as]
611#[derive(Serialize, Deserialize, PartialEq, Eq, Clone, Debug)]
612#[serde(transparent)]
613pub struct ConnectionData(#[serde_as(as = "Base64")] Vec<u8>);
614
615impl From<Vec<u8>> for ConnectionData {
616 fn from(value: Vec<u8>) -> Self {
617 ConnectionData(value)
618 }
619}
620
621impl From<ConnectionData> for Vec<u8> {
622 fn from(connection_data: ConnectionData) -> Self {
623 connection_data.0
624 }
625}
626
627impl AsRef<[u8]> for ConnectionData {
628 fn as_ref(&self) -> &[u8] {
629 &self.0
630 }
631}
632
633#[derive(Serialize, Deserialize)]
641#[serde(rename_all = "camelCase")]
642pub struct Redirect<S> {
643 pub url: Url,
644 #[serde(bound = "S:")]
645 pub context: ConfirmationContext<S>,
646}
647
648impl<S> PartialEq for Redirect<S> {
649 fn eq(&self, other: &Self) -> bool {
650 let Self { url, context } = self;
651 url == &other.url && context == &other.context
652 }
653}
654
655impl<S> Eq for Redirect<S> {}
656
657impl<S> Clone for Redirect<S> {
658 fn clone(&self) -> Self {
659 Self {
660 url: self.url.clone(),
661 context: self.context.clone(),
662 }
663 }
664}
665
666impl<S> fmt::Debug for Redirect<S> {
667 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
668 f.debug_struct("Redirect")
669 .field("url", &self.url)
670 .field("context", &self.context)
671 .finish()
672 }
673}
674
675#[derive(Serialize, Deserialize)]
679#[serde(rename_all = "camelCase")]
680pub struct RedirectHandle<S> {
681 pub handle: String,
682 #[serde(bound = "S:")]
683 pub context: ConfirmationContext<S>,
684}
685
686impl<S> PartialEq for RedirectHandle<S> {
687 fn eq(&self, other: &Self) -> bool {
688 let Self { handle, context } = self;
689 handle == &other.handle && context == &other.context
690 }
691}
692
693impl<S> Eq for RedirectHandle<S> {}
694
695impl<S> Clone for RedirectHandle<S> {
696 fn clone(&self) -> Self {
697 Self {
698 handle: self.handle.clone(),
699 context: self.context.clone(),
700 }
701 }
702}
703
704impl<S> fmt::Debug for RedirectHandle<S> {
705 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
706 f.debug_struct("RedirectHandle")
707 .field("handle", &self.handle)
708 .field("context", &self.context)
709 .finish()
710 }
711}
712
713fn decode<T>(jwt: &str) -> jsonwebtoken::errors::Result<T>
715where
716 T: for<'de> Deserialize<'de>,
717{
718 let empty_key = DecodingKey::from_secret(b"");
719 let mut null_validation = Validation::default();
720 null_validation.insecure_disable_signature_validation();
721 null_validation.required_spec_claims.clear();
722 null_validation.validate_exp = false;
723
724 jsonwebtoken::decode::<Claims<T>>(jwt, &empty_key, &null_validation)
725 .map(|data| data.claims.data)
726}
727
728#[derive(Serialize, Deserialize, PartialEq, Eq, Clone, Copy, Debug)]
729pub enum ServiceId {
730 Accounts,
731 CollectPayment,
732 Transactions,
733 Balances,
734}
735
736impl fmt::Display for ServiceId {
737 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
738 self.serialize(f)
739 }
740}
741
742#[derive(Serialize, Deserialize)]
802#[serde(rename_all = "camelCase")]
803pub struct Ticket<S: Service> {
804 pub service: ServiceId,
805 pub id: String,
806 pub data: S::TicketData,
807}
808
809impl<S: Service> PartialEq for Ticket<S> {
810 fn eq(&self, other: &Self) -> bool {
811 let Self { service, id, data } = self;
812 service == &other.service && id == &other.id && data == &other.data
813 }
814}
815
816impl<S: Service> Eq for Ticket<S> {}
817
818impl<S: Service> fmt::Debug for Ticket<S> {
819 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
820 f.debug_struct("Ticket")
821 .field("service", &self.service)
822 .field("id", &self.id)
823 .field("data", &self.data)
824 .finish()
825 }
826}
827
828impl<S: Service> Clone for Ticket<S> {
829 fn clone(&self) -> Self {
830 Self {
831 service: self.service,
832 id: self.id.clone(),
833 data: self.data.clone(),
834 }
835 }
836}
837
838pub trait Service {
839 const ID: ServiceId;
840
841 type TicketData: Serialize
842 + for<'de> Deserialize<'de>
843 + PartialEq
844 + Eq
845 + Clone
846 + fmt::Debug
847 + Send
848 + Sync;
849
850 type RequestData: Serialize + for<'de> Deserialize<'de> + Send + Sync + Clone;
851
852 type Output: Serialize + for<'de> Deserialize<'de> + Send + fmt::Debug + Clone;
853
854 #[must_use]
860 fn path() -> Path {
861 Path::new([&Self::ID.to_string().to_kebab_case(), "service"])
862 }
863
864 #[must_use]
865 fn response_path() -> Path {
866 Path::new([&Self::ID.to_string().to_kebab_case(), "response"])
867 }
868
869 #[must_use]
870 fn confirmation_path() -> Path {
871 Path::new([&Self::ID.to_string().to_kebab_case(), "confirmation"])
872 }
873}
874
875#[derive(Serialize, Deserialize, PartialEq, Eq, Clone, Copy, Debug)]
876#[serde(tag = "ticketStatus")]
877pub enum TicketStatus<T> {
878 Unfinished,
879 Error,
880 Success(T),
881}
882
883#[derive(Serialize, Deserialize, PartialEq, Eq, Clone, Debug)]
884#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
885#[serde(rename_all = "camelCase")]
886pub struct Credentials {
887 #[serde(alias = "connection_id")]
888 pub connection_id: ConnectionId,
889 #[serde(skip_serializing_if = "Option::is_none")]
890 #[cfg_attr(feature = "uniffi", uniffi(default = None))]
891 #[serde(alias = "user_id")]
892 pub user_id: Option<String>,
893 #[serde(skip_serializing_if = "Option::is_none")]
894 #[cfg_attr(feature = "uniffi", uniffi(default = None))]
895 pub password: Option<String>,
896 #[serde(skip_serializing_if = "Option::is_none")]
897 #[cfg_attr(feature = "uniffi", uniffi(default = None))]
898 #[serde(alias = "connection_data")]
899 pub connection_data: Option<ConnectionData>,
900}
901
902#[derive(Serialize, Deserialize)]
903#[serde(rename_all = "camelCase")]
904pub struct ResponseData<S> {
905 #[serde(bound = "S:")]
906 pub context: InputContext<S>,
907 pub response: String,
908}
909
910impl<S> PartialEq for ResponseData<S> {
911 fn eq(&self, other: &Self) -> bool {
912 let Self { context, response } = self;
913 context == &other.context && response == &other.response
914 }
915}
916
917impl<S> Eq for ResponseData<S> {}
918
919impl<S> Clone for ResponseData<S> {
920 fn clone(&self) -> Self {
921 Self {
922 context: self.context.clone(),
923 response: self.response.clone(),
924 }
925 }
926}
927
928impl<S> fmt::Debug for ResponseData<S> {
929 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
930 f.debug_struct("ResponseData")
931 .field("context", &self.context)
932 .field("response", &self.response)
933 .finish()
934 }
935}
936
937#[derive(Serialize, Deserialize)]
938#[serde(rename_all = "camelCase")]
939pub struct ConfirmationData<S> {
940 #[serde(bound = "S:")]
941 pub context: ConfirmationContext<S>,
942}
943
944impl<S> PartialEq for ConfirmationData<S> {
945 fn eq(&self, other: &Self) -> bool {
946 let Self { context } = self;
947 context == &other.context
948 }
949}
950
951impl<S> Eq for ConfirmationData<S> {}
952
953impl<S> Clone for ConfirmationData<S> {
954 fn clone(&self) -> Self {
955 Self {
956 context: self.context.clone(),
957 }
958 }
959}
960
961impl<S> fmt::Debug for ConfirmationData<S> {
962 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
963 f.debug_struct("ConfirmationData")
964 .field("context", &self.context)
965 .finish()
966 }
967}
968
969#[derive(Serialize, Deserialize, PartialEq, Eq, Clone, Debug)]
970#[serde(rename_all_fields = "camelCase")]
971#[cfg_attr(not(feature = "exhaustive-error"), non_exhaustive)]
972pub enum Error {
973 #[cfg_attr(not(feature = "server"), non_exhaustive)]
974 UnexpectedError {
975 #[serde(skip_serializing_if = "Option::is_none")]
976 user_message: Option<String>,
977 },
978 #[cfg_attr(not(feature = "server"), non_exhaustive)]
979 Canceled {},
980 #[cfg_attr(not(feature = "server"), non_exhaustive)]
981 InvalidCredentials {
982 #[serde(skip_serializing_if = "Option::is_none")]
983 user_message: Option<String>,
984 },
985 #[cfg_attr(not(feature = "server"), non_exhaustive)]
986 ServiceBlocked {
987 #[serde(skip_serializing_if = "Option::is_none")]
988 user_message: Option<String>,
989 #[serde(skip_serializing_if = "Option::is_none")]
990 code: Option<ServiceBlockedCode>,
991 },
992 #[cfg_attr(not(feature = "server"), non_exhaustive)]
993 Unauthorized {
994 #[serde(skip_serializing_if = "Option::is_none")]
995 user_message: Option<String>,
996 },
997 #[cfg_attr(not(feature = "server"), non_exhaustive)]
998 ConsentExpired {
999 #[serde(skip_serializing_if = "Option::is_none")]
1000 user_message: Option<String>,
1001 },
1002 #[cfg_attr(not(feature = "server"), non_exhaustive)]
1003 AccessExceeded {
1004 #[serde(skip_serializing_if = "Option::is_none")]
1005 user_message: Option<String>,
1006 },
1007 #[cfg_attr(not(feature = "server"), non_exhaustive)]
1008 PeriodOutOfBounds {
1009 #[serde(skip_serializing_if = "Option::is_none")]
1010 user_message: Option<String>,
1011 },
1012 #[cfg_attr(not(feature = "server"), non_exhaustive)]
1013 UnsupportedProduct {
1014 #[serde(skip_serializing_if = "Option::is_none")]
1015 reason: Option<UnsupportedProductReason>,
1016 #[serde(skip_serializing_if = "Option::is_none")]
1017 user_message: Option<String>,
1018 },
1019 #[cfg_attr(not(feature = "server"), non_exhaustive)]
1020 PaymentFailed {
1021 #[serde(skip_serializing_if = "Option::is_none")]
1022 code: Option<PaymentErrorCode>,
1023 #[serde(skip_serializing_if = "Option::is_none")]
1024 user_message: Option<String>,
1025 },
1026 #[cfg_attr(not(feature = "server"), non_exhaustive)]
1027 UnexpectedValue { error: String },
1028 #[cfg_attr(not(feature = "server"), non_exhaustive)]
1029 TicketError {
1030 error: String,
1031 code: TicketErrorCode,
1032 },
1033 #[cfg_attr(not(feature = "server"), non_exhaustive)]
1034 ProviderError {
1035 #[serde(skip_serializing_if = "Option::is_none")]
1036 code: Option<ProviderErrorCode>,
1037 #[serde(skip_serializing_if = "Option::is_none")]
1038 user_message: Option<String>,
1039 },
1040}
1041
1042#[derive(Serialize, Deserialize, PartialEq, Eq, Clone, Copy, Debug)]
1043#[cfg_attr(feature = "uniffi", derive(uniffi::Enum))]
1044#[cfg_attr(not(feature = "server"), non_exhaustive)]
1045pub enum TicketErrorCode {
1046 Missing,
1048 Invalid,
1050 MissingKey,
1052 UnknownKey,
1054 Mismatch,
1056 Expired,
1058 InvalidLifetime,
1060 ExpiredKey,
1062 KeyEnvironmentMismatch,
1064}
1065
1066#[derive(Serialize, Deserialize, Clone, Debug)]
1067#[cfg_attr(not(feature = "server"), non_exhaustive)]
1068pub enum Filter<F> {
1069 #[cfg_attr(not(feature = "server"), non_exhaustive)]
1070 Eq(F, Value),
1071 #[cfg_attr(not(feature = "server"), non_exhaustive)]
1072 NotEq(F, Value),
1073 #[cfg(feature = "server")]
1074 Contains(F, Value),
1075 #[cfg_attr(not(feature = "server"), non_exhaustive)]
1076 And(Box<Self>, Box<Self>),
1077 #[cfg_attr(not(feature = "server"), non_exhaustive)]
1078 Or(Box<Self>, Box<Self>),
1079 #[cfg_attr(not(feature = "server"), non_exhaustive)]
1080 Supports(SupportedService),
1081}
1082
1083#[derive(Serialize, Deserialize, Clone, Debug)]
1084#[cfg_attr(not(feature = "server"), non_exhaustive)]
1085#[cfg_attr(feature = "uniffi", derive(uniffi::Enum))]
1086pub enum SupportedService {
1087 CollectPayment,
1088}
1089
1090impl<F> Filter<F> {
1091 #[must_use]
1092 pub fn and(self, other: Filter<F>) -> Filter<F> {
1094 Filter::And(Box::new(self), Box::new(other))
1095 }
1096
1097 #[must_use]
1098 pub fn or(self, other: Filter<F>) -> Filter<F> {
1100 Filter::Or(Box::new(self), Box::new(other))
1101 }
1102}
1103
1104pub struct Field<F, T> {
1105 field: F,
1106 phantom: PhantomData<T>,
1107}
1108
1109impl<F, T> Field<F, T>
1110where
1111 F: Copy,
1112 T: Serialize,
1113{
1114 pub fn eq(&self, value: T) -> Filter<F> {
1120 Filter::Eq(self.field, serde_json::to_value(&value).unwrap())
1121 }
1122
1123 pub fn not_eq(&self, value: T) -> Filter<F> {
1129 Filter::NotEq(self.field, serde_json::to_value(&value).unwrap())
1130 }
1131}
1132
1133pub trait GetValue {
1134 type Model;
1135
1136 fn get(&self, model: &Self::Model) -> Value;
1137}
1138
1139macro_rules! fields {
1140 {
1141 $model:ty, $enum_name:ident
1142 $(($const_name:ident, $variant_name:ident, $field_name:ident, $($type:ty)+))+
1143 $(
1144 server:
1145 $(($server_variant_name:ident, $server_field_name:ident))+
1146 )?
1147 } => {
1148 #[derive(Serialize, Deserialize, PartialEq, Eq, Clone, Copy, Debug)]
1149 #[non_exhaustive]
1150 #[cfg_attr(feature = "uniffi", derive(uniffi::Enum))]
1151 pub enum $enum_name {
1152 $($variant_name,)+
1153 $($(
1154 #[cfg(feature = "server")]
1155 $server_variant_name,
1156 )+)?
1157 }
1158
1159 impl $enum_name {
1160 $(
1161 pub const $const_name: crate::Field<$enum_name, $($type)+> = crate::Field {
1162 field: $enum_name::$variant_name,
1163 phantom: std::marker::PhantomData,
1164 };
1165 )+
1166 }
1167
1168 impl crate::GetValue for $enum_name {
1169 type Model = $model;
1170
1171 fn get(&self, model: &$model) -> serde_json::Value {
1172 match self {
1173 $($enum_name::$variant_name => serde_json::to_value(&model.$field_name),)+
1174 $($(
1175 #[cfg(feature = "server")]
1176 $enum_name::$server_variant_name => serde_json::to_value(&model.$server_field_name),
1177 )+)?
1178 }.expect("Serialization should work")
1179 }
1180 }
1181 }
1182}
1183
1184#[derive(Serialize, Deserialize)]
1185#[serde(rename_all = "camelCase")]
1186pub struct ServiceRequest<S: Service> {
1187 pub credentials: Credentials,
1188 pub session: Option<Session>,
1189 #[serde(default, skip_serializing_if = "is_false")]
1190 pub recurring_consents: bool,
1191 #[serde(flatten, bound = "S:")]
1192 pub data: <S as Service>::RequestData,
1193}
1194
1195impl<S: Service> Clone for ServiceRequest<S> {
1196 fn clone(&self) -> Self {
1197 Self {
1198 credentials: self.credentials.clone(),
1199 session: self.session.clone(),
1200 recurring_consents: self.recurring_consents,
1201 data: self.data.clone(),
1202 }
1203 }
1204}
1205
1206#[derive(Serialize, Deserialize, Clone, Eq, PartialEq, Default, Debug)]
1207#[serde(rename_all = "camelCase")]
1208#[non_exhaustive]
1209#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
1210pub struct Account {
1211 #[serde(skip_serializing_if = "Option::is_none")]
1213 #[cfg_attr(feature = "uniffi", uniffi(default = None))]
1214 pub iban: Option<String>,
1215
1216 #[serde(skip_serializing_if = "Option::is_none")]
1218 #[cfg_attr(feature = "uniffi", uniffi(default = None))]
1219 pub number: Option<String>,
1220
1221 #[serde(skip_serializing_if = "Option::is_none")]
1223 #[cfg_attr(feature = "uniffi", uniffi(default = None))]
1224 pub bic: Option<String>,
1225
1226 #[serde(skip_serializing_if = "Option::is_none")]
1228 #[cfg_attr(feature = "uniffi", uniffi(default = None))]
1229 pub bank_code: Option<String>,
1230
1231 #[serde(skip_serializing_if = "Option::is_none")]
1233 #[cfg_attr(feature = "uniffi", uniffi(default = None))]
1234 pub currency: Option<String>,
1235
1236 #[serde(skip_serializing_if = "Option::is_none")]
1238 #[cfg_attr(feature = "uniffi", uniffi(default = None))]
1239 pub name: Option<String>,
1240
1241 #[serde(skip_serializing_if = "Option::is_none")]
1243 #[cfg_attr(feature = "uniffi", uniffi(default = None))]
1244 pub display_name: Option<String>,
1245
1246 #[serde(skip_serializing_if = "Option::is_none")]
1248 #[cfg_attr(feature = "uniffi", uniffi(default = None))]
1249 pub owner_name: Option<String>,
1250
1251 #[serde(skip_serializing_if = "Option::is_none")]
1253 #[cfg_attr(feature = "uniffi", uniffi(default = None))]
1254 pub product_name: Option<String>,
1255
1256 #[serde(skip_serializing_if = "Option::is_none")]
1258 #[cfg_attr(feature = "uniffi", uniffi(default = None))]
1259 pub status: Option<AccountStatus>,
1260
1261 #[serde(rename = "type")]
1263 #[serde(skip_serializing_if = "Option::is_none")]
1264 #[cfg_attr(feature = "uniffi", uniffi(default = None))]
1265 pub type_: Option<AccountType>,
1266}
1267
1268pub mod accounts {
1269 use super::{Account, AccountStatus, AccountType, Filter, ServiceId, SupportedService};
1270 use serde::{Deserialize, Serialize};
1271
1272 #[derive(Clone, Debug)]
1273 pub struct Service {}
1274
1275 impl super::Service for Service {
1276 const ID: ServiceId = ServiceId::Accounts;
1277
1278 type TicketData = ();
1279
1280 type RequestData = RequestData;
1281
1282 type Output = Vec<Account>;
1283 }
1284
1285 fields! {
1286 routex_models::Account, AccountField
1287 (IBAN, Iban, iban, Option<String>)
1288 (NUMBER, Number, number, Option<String>)
1289 (BIC, Bic, bic, Option<String>)
1290 (BANK_CODE, BankCode, bank_code, Option<String>)
1291 (CURRENCY, Currency, currency, String)
1292 (NAME, Name, name, Option<String>)
1293 (DISPLAY_NAME, DisplayName, display_name, Option<String>)
1294 (OWNER_NAME, OwnerName, owner_name, Option<String>)
1295 (PRODUCT_NAME, ProductName, product_name, Option<String>)
1296 (STATUS, Status, status, Option<AccountStatus>)
1297 (TYPE, Type, type_, Option<AccountType>)
1298 server:
1299 (Capabilities, capabilities)
1300 }
1301
1302 impl Account {
1303 #[must_use]
1304 pub fn supports(service: SupportedService) -> Filter<AccountField> {
1306 Filter::Supports(service)
1307 }
1308 }
1309
1310 #[derive(Serialize, Deserialize, Clone)]
1311 #[serde(rename_all = "camelCase")]
1312 pub struct RequestData {
1313 pub fields: Vec<AccountField>,
1314 pub filter: Option<Filter<AccountField>>,
1315 }
1316}
1317
1318#[derive(Serialize, Deserialize, Clone, Eq, PartialEq, Debug)]
1319#[serde(rename_all = "camelCase")]
1320#[serde(rename_all_fields = "camelCase")]
1321#[cfg_attr(feature = "uniffi", derive(uniffi::Enum))]
1322pub enum AccountIdentifier {
1323 #[serde(alias = "Iban")]
1325 Iban(String),
1326}
1327
1328#[derive(Serialize, Deserialize, PartialEq, Eq, Clone, Debug)]
1329#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
1330#[serde(rename_all = "camelCase")]
1331pub struct AccountReference {
1332 #[serde(flatten)]
1333 pub id: AccountIdentifier,
1334 #[serde(skip_serializing_if = "Option::is_none")]
1335 #[cfg_attr(feature = "uniffi", uniffi(default = None))]
1336 pub currency: Option<String>,
1337}
1338
1339pub mod balances {
1340 pub use rust_decimal::Decimal;
1341 use serde::{Deserialize, Serialize};
1342
1343 use crate::{AccountReference, ServiceId};
1344
1345 #[derive(Clone, Debug)]
1346 pub struct Service {}
1347
1348 impl super::Service for Service {
1349 const ID: ServiceId = ServiceId::Balances;
1350
1351 type TicketData = ();
1352
1353 type RequestData = RequestData;
1354
1355 type Output = Balances;
1356 }
1357
1358 #[derive(Serialize, Deserialize, Clone)]
1359 #[serde(rename_all = "camelCase")]
1360 pub struct RequestData {
1361 pub accounts: Vec<AccountReference>,
1362 }
1363
1364 #[derive(Serialize, Deserialize, Clone, Eq, PartialEq, Debug)]
1365 #[serde(rename_all = "camelCase")]
1366 #[cfg_attr(not(feature = "server"), non_exhaustive)]
1367 #[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
1368 pub struct Balances {
1369 pub balances: Vec<AccountBalances>,
1371
1372 #[serde(default, skip_serializing_if = "Vec::is_empty")]
1374 #[cfg_attr(feature = "uniffi", uniffi(default = []))]
1375 pub missing_accounts: Vec<AccountReference>,
1376 }
1377
1378 #[allow(clippy::module_name_repetitions)]
1379 #[derive(Serialize, Deserialize, Clone, Eq, PartialEq, Debug)]
1380 #[serde(rename_all = "camelCase")]
1381 #[cfg_attr(not(feature = "server"), non_exhaustive)]
1382 #[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
1383 pub struct AccountBalances {
1384 pub account: AccountReference,
1385
1386 pub balances: Vec<Balance>,
1388 }
1389
1390 #[derive(Serialize, Deserialize, Clone, Eq, PartialEq, Debug)]
1391 #[serde(rename_all = "camelCase")]
1392 #[cfg_attr(not(feature = "server"), non_exhaustive)]
1393 #[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
1394 pub struct Balance {
1395 pub amount: Decimal,
1396 pub currency: String,
1397 pub balance_type: BalanceType,
1398 }
1399
1400 #[derive(Serialize, Deserialize, Copy, Clone, Eq, PartialEq, Debug)]
1401 #[cfg_attr(feature = "uniffi", derive(uniffi::Enum))]
1402 pub enum BalanceType {
1403 Booked,
1405
1406 Available,
1408
1409 Expected,
1411 }
1412}
1413
1414pub mod transactions {
1415 use chrono::NaiveDate;
1416 use isocountry::CountryCode;
1417 pub use routex_models::{Amount, Fee, TransactionStatus};
1418 use rust_decimal::Decimal;
1419 use serde::{Deserialize, Serialize};
1420 use url::Url;
1421
1422 use crate::{AccountReference, ServiceId};
1423
1424 #[derive(Serialize, Deserialize, PartialEq, Eq, Clone, Debug)]
1425 #[serde(rename_all = "camelCase")]
1426 pub struct TicketData {
1427 pub account: AccountReference,
1428 pub range: Range,
1429 pub webhook: Option<Url>,
1430 }
1431
1432 #[derive(Clone, Debug)]
1433 pub struct Service {}
1434
1435 impl super::Service for Service {
1436 const ID: ServiceId = ServiceId::Transactions;
1437
1438 type TicketData = TicketData;
1439
1440 type RequestData = RequestData;
1441
1442 type Output = Option<Vec<Transaction>>;
1443 }
1444
1445 #[derive(Serialize, Deserialize, Clone)]
1446 #[serde(rename_all = "camelCase")]
1447 pub struct RequestData {}
1448
1449 #[derive(Serialize, Deserialize, PartialEq, Eq, Clone, Debug)]
1450 #[serde(rename_all = "camelCase")]
1451 #[serde(rename_all_fields = "camelCase")]
1452 pub enum Range {
1453 Reference(String),
1454 #[serde(untagged)]
1455 Period {
1456 from: NaiveDate,
1458 to: Option<NaiveDate>,
1460 },
1461 }
1462
1463 #[derive(Serialize, Deserialize, Clone, Eq, PartialEq, Debug)]
1464 #[serde(rename_all = "camelCase")]
1465 #[cfg_attr(not(feature = "server"), non_exhaustive)]
1466 #[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
1467 pub struct Transaction {
1468 #[serde(skip_serializing_if = "Option::is_none")]
1470 #[cfg_attr(feature = "uniffi", uniffi(default = None))]
1471 pub entry_reference: Option<String>,
1472
1473 #[serde(skip_serializing_if = "Option::is_none")]
1474 #[cfg_attr(feature = "uniffi", uniffi(default = None))]
1475 pub batch: Option<BatchData>,
1476
1477 #[serde(skip_serializing_if = "Option::is_none")]
1479 #[cfg_attr(feature = "uniffi", uniffi(default = None))]
1480 pub booking_date: Option<NaiveDate>,
1481
1482 #[serde(skip_serializing_if = "Option::is_none")]
1484 #[cfg_attr(feature = "uniffi", uniffi(default = None))]
1485 pub value_date: Option<NaiveDate>,
1486
1487 #[serde(skip_serializing_if = "Option::is_none")]
1489 #[cfg_attr(feature = "uniffi", uniffi(default = None))]
1490 pub transaction_date: Option<NaiveDate>,
1491
1492 pub status: TransactionStatus,
1494
1495 #[serde(skip_serializing_if = "Option::is_none")]
1497 #[cfg_attr(feature = "uniffi", uniffi(default = None))]
1498 pub account_servicer_reference: Option<String>,
1499
1500 #[serde(skip_serializing_if = "Option::is_none")]
1502 #[cfg_attr(feature = "uniffi", uniffi(default = None))]
1503 pub payment_id: Option<String>,
1504
1505 #[serde(skip_serializing_if = "Option::is_none")]
1507 #[cfg_attr(feature = "uniffi", uniffi(default = None))]
1508 pub transaction_id: Option<String>,
1509
1510 #[serde(skip_serializing_if = "Option::is_none")]
1512 #[cfg_attr(feature = "uniffi", uniffi(default = None))]
1513 pub end_to_end_id: Option<String>,
1514
1515 #[serde(skip_serializing_if = "Option::is_none")]
1517 #[cfg_attr(feature = "uniffi", uniffi(default = None))]
1518 pub mandate_id: Option<String>,
1519
1520 #[serde(skip_serializing_if = "Option::is_none")]
1522 #[cfg_attr(feature = "uniffi", uniffi(default = None))]
1523 pub creditor_id: Option<String>,
1524
1525 pub amount: Amount,
1527
1528 #[serde(default, skip_serializing_if = "super::is_false")]
1530 #[cfg_attr(feature = "uniffi", uniffi(default = false))]
1531 pub reversal: bool,
1532
1533 #[serde(skip_serializing_if = "Option::is_none")]
1535 #[cfg_attr(feature = "uniffi", uniffi(default = None))]
1536 pub original_amount: Option<Amount>,
1537
1538 #[serde(default, skip_serializing_if = "Vec::is_empty")]
1540 #[cfg_attr(feature = "uniffi", uniffi(default = []))]
1541 pub exchanges: Vec<ExchangeRate>,
1542
1543 #[serde(default, skip_serializing_if = "Vec::is_empty")]
1545 #[cfg_attr(feature = "uniffi", uniffi(default = []))]
1546 pub fees: Vec<Fee>,
1547
1548 #[serde(skip_serializing_if = "Option::is_none")]
1550 #[cfg_attr(feature = "uniffi", uniffi(default = None))]
1551 pub creditor: Option<Party>,
1552
1553 #[serde(skip_serializing_if = "Option::is_none")]
1555 #[cfg_attr(feature = "uniffi", uniffi(default = None))]
1556 pub debtor: Option<Party>,
1557
1558 #[serde(default, skip_serializing_if = "Vec::is_empty")]
1560 #[cfg_attr(feature = "uniffi", uniffi(default = []))]
1561 pub remittance_information: Vec<String>,
1562
1563 #[serde(skip_serializing_if = "Option::is_none")]
1565 #[cfg_attr(feature = "uniffi", uniffi(default = None))]
1566 pub purpose_code: Option<String>,
1567
1568 #[serde(default, skip_serializing_if = "Vec::is_empty")]
1570 #[cfg_attr(feature = "uniffi", uniffi(default = []))]
1571 pub bank_transaction_codes: Vec<BankTransactionCode>,
1572
1573 #[serde(skip_serializing_if = "Option::is_none")]
1577 #[cfg_attr(feature = "uniffi", uniffi(default = None))]
1578 pub additional_information: Option<String>,
1579 }
1580
1581 #[derive(Serialize, Deserialize, Clone, Eq, PartialEq, Debug)]
1582 #[serde(rename_all = "camelCase")]
1583 #[cfg_attr(not(feature = "server"), non_exhaustive)]
1584 #[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
1585 pub struct BatchData {
1586 #[serde(skip_serializing_if = "Option::is_none")]
1588 pub number_of_transactions: Option<u32>,
1589
1590 pub transactions: Vec<BatchTransactionDetails>,
1595 }
1596
1597 #[derive(Serialize, Deserialize, Clone, Eq, PartialEq, Debug)]
1598 #[serde(rename_all = "camelCase")]
1599 #[cfg_attr(not(feature = "server"), non_exhaustive)]
1600 #[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
1601 pub struct BatchTransactionDetails {
1602 #[serde(skip_serializing_if = "Option::is_none")]
1604 #[cfg_attr(feature = "uniffi", uniffi(default = None))]
1605 pub account_servicer_reference: Option<String>,
1606
1607 #[serde(skip_serializing_if = "Option::is_none")]
1609 #[cfg_attr(feature = "uniffi", uniffi(default = None))]
1610 pub payment_id: Option<String>,
1611
1612 #[serde(skip_serializing_if = "Option::is_none")]
1614 #[cfg_attr(feature = "uniffi", uniffi(default = None))]
1615 pub transaction_id: Option<String>,
1616
1617 #[serde(skip_serializing_if = "Option::is_none")]
1619 #[cfg_attr(feature = "uniffi", uniffi(default = None))]
1620 pub end_to_end_id: Option<String>,
1621
1622 #[serde(skip_serializing_if = "Option::is_none")]
1624 #[cfg_attr(feature = "uniffi", uniffi(default = None))]
1625 pub mandate_id: Option<String>,
1626
1627 #[serde(skip_serializing_if = "Option::is_none")]
1629 #[cfg_attr(feature = "uniffi", uniffi(default = None))]
1630 pub creditor_id: Option<String>,
1631
1632 #[serde(skip_serializing_if = "Option::is_none")]
1634 #[cfg_attr(feature = "uniffi", uniffi(default = None))]
1635 pub amount: Option<Amount>,
1636
1637 #[serde(default, skip_serializing_if = "super::is_false")]
1639 #[cfg_attr(feature = "uniffi", uniffi(default = false))]
1640 pub reversal: bool,
1641
1642 #[serde(skip_serializing_if = "Option::is_none")]
1644 #[cfg_attr(feature = "uniffi", uniffi(default = None))]
1645 pub original_amount: Option<Amount>,
1646
1647 #[serde(default, skip_serializing_if = "Vec::is_empty")]
1649 #[cfg_attr(feature = "uniffi", uniffi(default = []))]
1650 pub exchanges: Vec<ExchangeRate>,
1651
1652 #[serde(default, skip_serializing_if = "Vec::is_empty")]
1654 #[cfg_attr(feature = "uniffi", uniffi(default = []))]
1655 pub fees: Vec<Fee>,
1656
1657 #[serde(skip_serializing_if = "Option::is_none")]
1659 #[cfg_attr(feature = "uniffi", uniffi(default = None))]
1660 pub creditor: Option<Party>,
1661
1662 #[serde(skip_serializing_if = "Option::is_none")]
1664 #[cfg_attr(feature = "uniffi", uniffi(default = None))]
1665 pub debtor: Option<Party>,
1666
1667 #[serde(default, skip_serializing_if = "Vec::is_empty")]
1669 #[cfg_attr(feature = "uniffi", uniffi(default = []))]
1670 pub remittance_information: Vec<String>,
1671
1672 #[serde(skip_serializing_if = "Option::is_none")]
1674 #[cfg_attr(feature = "uniffi", uniffi(default = None))]
1675 pub purpose_code: Option<String>,
1676
1677 #[serde(default, skip_serializing_if = "Vec::is_empty")]
1679 #[cfg_attr(feature = "uniffi", uniffi(default = []))]
1680 pub bank_transaction_codes: Vec<BankTransactionCode>,
1681
1682 #[serde(skip_serializing_if = "Option::is_none")]
1686 #[cfg_attr(feature = "uniffi", uniffi(default = None))]
1687 pub additional_information: Option<String>,
1688 }
1689
1690 #[derive(Serialize, Deserialize, Clone, Eq, PartialEq, Debug)]
1691 #[serde(rename_all = "camelCase")]
1692 #[cfg_attr(not(feature = "server"), non_exhaustive)]
1693 #[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
1694 pub struct ExchangeRate {
1695 pub source_currency: String,
1697 #[serde(skip_serializing_if = "Option::is_none")]
1700 #[cfg_attr(feature = "uniffi", uniffi(default = None))]
1701 pub target_currency: Option<String>,
1702 #[serde(skip_serializing_if = "Option::is_none")]
1704 #[cfg_attr(feature = "uniffi", uniffi(default = None))]
1705 pub unit_currency: Option<String>,
1706 pub exchange_rate: Decimal,
1707 }
1708
1709 #[derive(Serialize, Deserialize, Clone, Eq, PartialEq, Debug)]
1710 #[serde(rename_all = "camelCase")]
1711 #[cfg_attr(not(feature = "server"), non_exhaustive)]
1712 #[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
1713 pub struct Party {
1714 #[serde(skip_serializing_if = "Option::is_none")]
1716 #[cfg_attr(feature = "uniffi", uniffi(default = None))]
1717 pub name: Option<String>,
1718
1719 #[serde(skip_serializing_if = "Option::is_none")]
1721 #[cfg_attr(feature = "uniffi", uniffi(default = None))]
1722 pub iban: Option<String>,
1723
1724 #[serde(skip_serializing_if = "Option::is_none")]
1726 #[cfg_attr(feature = "uniffi", uniffi(default = None))]
1727 pub bic: Option<String>,
1728
1729 #[serde(skip_serializing_if = "Option::is_none")]
1731 #[cfg_attr(feature = "uniffi", uniffi(default = None))]
1732 pub ultimate: Option<String>,
1733 }
1734
1735 #[derive(Serialize, Deserialize, Clone, Eq, PartialEq, Debug)]
1736 #[serde(rename_all = "camelCase")]
1737 #[serde(rename_all_fields = "camelCase")]
1738 #[non_exhaustive]
1739 #[cfg_attr(feature = "uniffi", derive(uniffi::Enum))]
1740 pub enum BankTransactionCode {
1741 Iso {
1743 domain: String,
1745
1746 family: String,
1748
1749 sub_family: String,
1751 },
1752 Swift(String),
1754 Bai(String),
1756 National { code: String, country: CountryCode },
1758 Other {
1760 code: String,
1761 #[serde(skip_serializing_if = "Option::is_none")]
1762 issuer: Option<String>,
1763 },
1764 }
1765}
1766
1767#[allow(clippy::trivially_copy_pass_by_ref)]
1768fn is_false(val: &bool) -> bool {
1769 !*val
1770}
1771
1772pub mod collect_payment {
1773 pub use routex_models::Amount;
1774 use serde::{Deserialize, Serialize};
1775
1776 use super::{AccountIdentifier, AccountReference, Path, ServiceId};
1777
1778 #[must_use]
1783 pub fn status_path(ticket_id: &str) -> Path {
1784 Path::new(["collect-payment", "status", ticket_id])
1785 }
1786
1787 #[derive(Serialize, Deserialize, PartialEq, Eq, Default, Clone, Debug)]
1788 #[serde(rename_all = "camelCase")]
1789 #[non_exhaustive]
1790 pub struct SuccessStatusData {}
1791
1792 #[derive(Serialize, Deserialize, PartialEq, Eq, Clone, Debug)]
1793 #[serde(rename_all = "camelCase")]
1794 pub struct TicketData {
1795 pub amount: Amount,
1796 pub creditor_account: AccountIdentifier,
1797 pub creditor_name: String,
1798 pub remittance: String,
1799 #[serde(skip_serializing_if = "Option::is_none")]
1800 pub instant: Option<bool>,
1801 }
1802
1803 #[derive(Clone, Debug)]
1804 pub struct Service {}
1805
1806 impl super::Service for Service {
1807 const ID: ServiceId = ServiceId::CollectPayment;
1808
1809 type TicketData = TicketData;
1810
1811 type RequestData = RequestData;
1812
1813 type Output = Option<PaymentInitiation>;
1814 }
1815
1816 #[derive(Serialize, Deserialize, Clone)]
1817 #[serde(rename_all = "camelCase")]
1818 pub struct RequestData {
1819 pub account: Option<AccountReference>,
1820 }
1821
1822 #[derive(Serialize, Deserialize, Clone, Eq, PartialEq, Default, Debug)]
1823 #[serde(rename_all = "camelCase")]
1824 #[non_exhaustive]
1825 #[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
1826 pub struct PaymentInitiation {}
1827}
1828
1829#[cfg(test)]
1830mod tests {
1831 use std::fmt::Debug;
1832
1833 use jsonwebtoken::{Algorithm, EncodingKey, Header, encode};
1834 use serde::{Deserialize, Serialize};
1835
1836 use crate::{
1837 Authenticated, Claims, ConfirmationContext, ConfirmationData, Dialog, DialogInput,
1838 InputContext, OBResult, Redirect, RedirectHandle, ResponseData, ServiceId, Session, Ticket,
1839 };
1840
1841 #[test]
1842 fn read_response() {
1843 let serialized = serde_json::to_string(
1844 &encode(
1845 &Header::new(Algorithm::HS256),
1846 &Claims {
1847 data: "data",
1848 exp: 2_540_808_000,
1849 },
1850 &EncodingKey::from_secret(b"does_not_matter"),
1851 )
1852 .unwrap(),
1853 )
1854 .unwrap();
1855
1856 let response = serde_json::from_str::<Authenticated<String>>(&serialized).unwrap();
1857
1858 assert_eq!(&super::decode::<String>(&response.jwt).unwrap(), "data");
1859 }
1860
1861 struct Service;
1862
1863 impl crate::Service for Service {
1864 const ID: ServiceId = ServiceId::Accounts;
1865
1866 type TicketData = ();
1867
1868 type RequestData = ();
1869
1870 type Output = ();
1871 }
1872
1873 #[allow(
1874 dead_code,
1875 unconditional_recursion,
1876 clippy::extra_unused_type_parameters
1877 )]
1878 fn test_bounds<T: Serialize + for<'de> Deserialize<'de> + Clone + Eq + PartialEq + Debug>() {
1879 test_bounds::<InputContext<Service>>();
1880 test_bounds::<ConfirmationContext<Service>>();
1881 test_bounds::<DialogInput<Service>>();
1882 test_bounds::<Dialog<Service>>();
1883 test_bounds::<Redirect<Service>>();
1884 test_bounds::<RedirectHandle<Service>>();
1885 test_bounds::<OBResult<()>>();
1886 test_bounds::<Session>();
1887
1888 test_bounds::<Authenticated<Ticket<Service>>>();
1889 test_bounds::<Ticket<Service>>();
1890 test_bounds::<ResponseData<Service>>();
1891 test_bounds::<ConfirmationData<Service>>();
1892 }
1893
1894 fn test_eq_<T: PartialEq + Debug>(o: &T) {
1895 assert!(o.eq(o));
1896 }
1897
1898 #[test]
1899 fn test_eq() {
1900 let ic = InputContext::<Service>::from(vec![]);
1901 let cc = ConfirmationContext::<Service>::from(vec![]);
1902
1903 test_eq_(&ic);
1904 test_eq_(&cc);
1905 test_eq_(&Redirect {
1906 url: "url:".parse().unwrap(),
1907 context: cc.clone(),
1908 });
1909 test_eq_(&RedirectHandle {
1910 handle: String::new(),
1911 context: cc.clone(),
1912 });
1913 test_eq_(&Ticket::<Service> {
1914 service: ServiceId::Accounts,
1915 id: String::new(),
1916 data: (),
1917 });
1918 test_eq_(&ResponseData {
1919 context: ic,
1920 response: String::new(),
1921 });
1922 test_eq_(&ConfirmationData { context: cc });
1923 }
1924}