stateset_core/errors/
payment.rs1use uuid::Uuid;
4
5#[derive(Debug, thiserror::Error)]
16#[non_exhaustive]
17pub enum PaymentError {
18 #[error("payment not found: {0}")]
20 NotFound(Uuid),
21
22 #[error("payment declined: {reason}")]
24 Declined {
25 reason: String,
27 },
28
29 #[error("refund failed: {reason}")]
31 RefundFailed {
32 reason: String,
34 },
35
36 #[error("currency mismatch: expected {expected}, got {got}")]
38 CurrencyMismatch {
39 expected: String,
41 got: String,
43 },
44
45 #[error("{0}")]
47 InvalidTransition(super::StateTransitionError<String>),
48
49 #[error(transparent)]
51 Other(Box<dyn std::error::Error + Send + Sync>),
52}
53
54impl PaymentError {
55 #[inline]
57 #[track_caller]
58 #[must_use]
59 pub const fn not_found(id: Uuid) -> Self {
60 Self::NotFound(id)
61 }
62
63 #[track_caller]
65 pub fn declined(reason: impl Into<String>) -> Self {
66 Self::Declined { reason: reason.into() }
67 }
68
69 #[track_caller]
71 pub fn refund_failed(reason: impl Into<String>) -> Self {
72 Self::RefundFailed { reason: reason.into() }
73 }
74
75 #[track_caller]
77 pub fn currency_mismatch(expected: impl Into<String>, got: impl Into<String>) -> Self {
78 Self::CurrencyMismatch { expected: expected.into(), got: got.into() }
79 }
80
81 #[track_caller]
83 pub fn invalid_transition(from: impl std::fmt::Display, to: impl std::fmt::Display) -> Self {
84 Self::InvalidTransition(super::StateTransitionError::new(from.to_string(), to.to_string()))
85 }
86}
87
88#[cfg(test)]
89mod tests {
90 use super::*;
91
92 #[test]
93 fn not_found_display() {
94 let err = PaymentError::not_found(Uuid::nil());
95 assert!(err.to_string().contains("not found"));
96 }
97
98 #[test]
99 fn declined_display() {
100 let err = PaymentError::declined("insufficient funds");
101 assert!(err.to_string().contains("insufficient funds"));
102 }
103
104 #[test]
105 fn refund_failed_display() {
106 let err = PaymentError::refund_failed("already refunded");
107 assert!(err.to_string().contains("already refunded"));
108 }
109
110 #[test]
111 fn currency_mismatch_display() {
112 let err = PaymentError::currency_mismatch("USD", "EUR");
113 let msg = err.to_string();
114 assert!(msg.contains("USD"));
115 assert!(msg.contains("EUR"));
116 }
117
118 #[test]
119 fn invalid_transition_display() {
120 let err = PaymentError::invalid_transition("pending", "refunded");
121 assert!(err.to_string().contains("pending"));
122 assert!(err.to_string().contains("refunded"));
123 }
124
125 #[test]
126 fn converts_to_commerce_error() {
127 use crate::errors::CommerceError;
128
129 let pay_err = PaymentError::not_found(Uuid::nil());
130 let commerce_err: CommerceError = pay_err.into();
131 assert!(commerce_err.is_not_found());
132 }
133}