1use serde::{Deserialize, Serialize};
2use thiserror::Error;
3
4#[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 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 pub fn message(&self) -> String {
62 self.to_string()
63 }
64}
65
66pub type X402Result<T> = Result<T, X402Error>;
68
69impl 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}