Skip to main content

payrail_core/
error.rs

1use crate::{CountryCode, CurrencyCode, PaymentProvider};
2
3/// Redacted provider error details safe to expose to application code.
4#[derive(Debug, Clone, PartialEq, Eq)]
5pub struct ProviderErrorDetails {
6    /// Provider that returned the error.
7    pub provider: PaymentProvider,
8    /// HTTP status code.
9    pub status: u16,
10    /// Safe provider error code.
11    pub code: Option<String>,
12    /// Safe provider request ID.
13    pub request_id: Option<String>,
14    /// Redacted message.
15    pub message: String,
16}
17
18/// PayRail error type.
19#[derive(Debug, thiserror::Error)]
20#[non_exhaustive]
21pub enum PaymentError {
22    /// Invalid positive amount.
23    #[error("invalid amount: {0}")]
24    InvalidAmount(i64),
25
26    /// Invalid currency code.
27    #[error("invalid currency code: {0}")]
28    InvalidCurrencyCode(String),
29
30    /// Invalid country code.
31    #[error("invalid country code: {0}")]
32    InvalidCountryCode(String),
33
34    /// Invalid reference.
35    #[error("invalid reference: {0}")]
36    InvalidReference(String),
37
38    /// Invalid idempotency key.
39    #[error("invalid idempotency key: {0}")]
40    InvalidIdempotencyKey(String),
41
42    /// Invalid phone number.
43    #[error("invalid phone number")]
44    InvalidPhoneNumber(String),
45
46    /// Invalid URL.
47    #[error("invalid url: {0}")]
48    InvalidUrl(String),
49
50    /// A required field was missing.
51    #[error("missing required field: {0}")]
52    MissingRequiredField(&'static str),
53
54    /// Invalid configuration.
55    #[error("invalid configuration: {0}")]
56    InvalidConfiguration(String),
57
58    /// Connector is not configured.
59    #[error("connector not configured: {provider:?}")]
60    ConnectorNotConfigured { provider: PaymentProvider },
61
62    /// Unsupported payment method.
63    #[error("unsupported payment method: {0}")]
64    UnsupportedPaymentMethod(String),
65
66    /// Unsupported country.
67    #[error("unsupported country: {0:?}")]
68    UnsupportedCountry(CountryCode),
69
70    /// Unsupported currency.
71    #[error("unsupported currency: {0:?}")]
72    UnsupportedCurrency(CurrencyCode),
73
74    /// Unsupported route.
75    #[error("unsupported payment route: method={method}, country={country:?}")]
76    UnsupportedPaymentRoute {
77        /// Method description.
78        method: String,
79        /// Optional country.
80        country: Option<CountryCode>,
81    },
82
83    /// Provider authentication failed.
84    #[error("provider authentication failed")]
85    AuthenticationFailed,
86
87    /// Provider request failed.
88    #[error("provider request failed: {provider:?}, status={status}, message={message}")]
89    ProviderRequestFailed {
90        /// Provider.
91        provider: PaymentProvider,
92        /// HTTP status.
93        status: u16,
94        /// Redacted message.
95        message: String,
96    },
97
98    /// Redacted provider details.
99    #[error("provider request failed: {details:?}")]
100    ProviderDetails {
101        /// Details.
102        details: ProviderErrorDetails,
103    },
104
105    /// Provider unavailable.
106    #[error("provider unavailable: {0:?}")]
107    ProviderUnavailable(PaymentProvider),
108
109    /// Provider rate limited.
110    #[error("rate limited by provider: {0:?}")]
111    RateLimited(PaymentProvider),
112
113    /// Webhook verification failed.
114    #[error("webhook verification failed")]
115    WebhookVerificationFailed,
116
117    /// Invalid webhook payload.
118    #[error("webhook payload invalid: {0}")]
119    InvalidWebhookPayload(String),
120
121    /// Unsupported operation.
122    #[error("operation not supported: {0}")]
123    UnsupportedOperation(String),
124
125    /// HTTP error.
126    #[error("http error: {0}")]
127    Http(#[from] reqwest::Error),
128
129    /// JSON error.
130    #[error("json error: {0}")]
131    Json(#[from] serde_json::Error),
132}
133
134#[cfg(test)]
135mod tests {
136    use super::*;
137
138    #[test]
139    fn invalid_phone_error_does_not_display_value() {
140        let error = PaymentError::InvalidPhoneNumber("+260971234567".to_owned());
141
142        assert_eq!(error.to_string(), "invalid phone number");
143    }
144}