Skip to main content

fly402_core/
errors.rs

1use serde::{Deserialize, Serialize};
2use thiserror::Error;
3
4/// Base error type for all X402 operations
5#[derive(Debug, Error, Clone, Serialize, Deserialize)]
6#[serde(tag = "type", content = "details")]
7pub enum X402Error {
8    #[error("Payment required: {0}")]
9    PaymentRequired(String),
10
11    #[error("Payment expired: {0}")]
12    PaymentExpired(String),
13
14    #[error("Insufficient funds: {0}")]
15    InsufficientFunds(String),
16
17    #[error("Payment verification failed: {0}")]
18    PaymentVerification(String),
19
20    #[error("Transaction broadcast failed: {0}")]
21    TransactionBroadcast(String),
22
23    #[error("Invalid payment request: {0}")]
24    InvalidPaymentRequest(String),
25
26    #[error("Invalid payment authorization: {0}")]
27    InvalidPaymentAuthorization(String),
28
29    #[error("Configuration error: {0}")]
30    Configuration(String),
31
32    #[error("Network error: {0}")]
33    Network(String),
34
35    #[error("Blockchain error: {0}")]
36    Blockchain(String),
37
38    #[error("Serialization error: {0}")]
39    Serialization(String),
40}
41
42impl X402Error {
43    /// Get the error code for this error type
44    pub fn code(&self) -> &'static str {
45        match self {
46            X402Error::PaymentRequired(_) => "PAYMENT_REQUIRED",
47            X402Error::PaymentExpired(_) => "PAYMENT_EXPIRED",
48            X402Error::InsufficientFunds(_) => "INSUFFICIENT_FUNDS",
49            X402Error::PaymentVerification(_) => "PAYMENT_VERIFICATION_FAILED",
50            X402Error::TransactionBroadcast(_) => "TRANSACTION_BROADCAST_FAILED",
51            X402Error::InvalidPaymentRequest(_) => "INVALID_PAYMENT_REQUEST",
52            X402Error::InvalidPaymentAuthorization(_) => "INVALID_PAYMENT_AUTHORIZATION",
53            X402Error::Configuration(_) => "CONFIGURATION_ERROR",
54            X402Error::Network(_) => "NETWORK_ERROR",
55            X402Error::Blockchain(_) => "BLOCKCHAIN_ERROR",
56            X402Error::Serialization(_) => "SERIALIZATION_ERROR",
57        }
58    }
59
60    /// Get the error message
61    pub fn message(&self) -> String {
62        self.to_string()
63    }
64}
65
66/// Result type alias for X402 operations
67pub type X402Result<T> = Result<T, X402Error>;
68
69// Implement From conversions for common error types
70impl From<serde_json::Error> for X402Error {
71    fn from(err: serde_json::Error) -> Self {
72        X402Error::Serialization(err.to_string())
73    }
74}
75
76impl From<base64::DecodeError> for X402Error {
77    fn from(err: base64::DecodeError) -> Self {
78        X402Error::Serialization(format!("Base64 decode error: {}", err))
79    }
80}
81
82impl From<solana_sdk::signer::SignerError> for X402Error {
83    fn from(err: solana_sdk::signer::SignerError) -> Self {
84        X402Error::Blockchain(format!("Signer error: {}", err))
85    }
86}
87
88impl From<solana_client::client_error::ClientError> for X402Error {
89    fn from(err: solana_client::client_error::ClientError) -> Self {
90        X402Error::Blockchain(format!("Solana client error: {}", err))
91    }
92}
93
94#[cfg(test)]
95mod tests {
96    use super::*;
97
98    #[test]
99    fn test_error_codes() {
100        assert_eq!(
101            X402Error::PaymentRequired("test".to_string()).code(),
102            "PAYMENT_REQUIRED"
103        );
104        assert_eq!(
105            X402Error::PaymentExpired("test".to_string()).code(),
106            "PAYMENT_EXPIRED"
107        );
108        assert_eq!(
109            X402Error::InsufficientFunds("test".to_string()).code(),
110            "INSUFFICIENT_FUNDS"
111        );
112    }
113
114    #[test]
115    fn test_error_serialization() {
116        let error = X402Error::PaymentRequired("Test payment required".to_string());
117        let json = serde_json::to_string(&error).unwrap();
118        let deserialized: X402Error = serde_json::from_str(&json).unwrap();
119        assert_eq!(error.code(), deserialized.code());
120    }
121}