1use crate::error_reason::{AsPaymentProblem, ErrorReason, PaymentProblem};
19
20#[derive(Debug, thiserror::Error)]
25#[non_exhaustive]
26pub enum VerificationError {
27 #[error("invalid format: {0}")]
29 InvalidFormat(String),
30
31 #[error("payment amount is below requirements")]
36 InvalidPaymentAmount,
37
38 #[error("payment authorization is not yet valid")]
40 Early,
41
42 #[error("payment authorization has expired")]
44 Expired,
45
46 #[error("chain id mismatch")]
48 ChainIdMismatch,
49
50 #[error("payment recipient mismatch")]
52 RecipientMismatch,
53
54 #[error("payment asset mismatch")]
56 AssetMismatch,
57
58 #[error("insufficient on-chain balance")]
60 InsufficientFunds,
61
62 #[error("permit2 allowance required")]
68 Permit2AllowanceRequired,
69
70 #[error("invalid signature: {0}")]
72 InvalidSignature(String),
73
74 #[error("simulation failed: {0}")]
76 SimulationFailed(String),
77
78 #[error("unsupported chain")]
80 UnsupportedChain,
81
82 #[error("unsupported scheme")]
84 UnsupportedScheme,
85
86 #[error("accepted details do not match requirements")]
88 AcceptedRequirementsMismatch,
89
90 #[error("authorization nonce already used")]
92 NonceAlreadyUsed,
93
94 #[error("duplicate settlement attempt")]
96 DuplicateSettlement,
97
98 #[error("memo data mismatch")]
100 MemoMismatch,
101
102 #[error("memo instruction count invalid (expected 1, got {count})")]
104 MemoInstructionCountInvalid {
105 count: usize,
107 },
108
109 #[error("settlement amount {requested} exceeds authorised maximum {authorised}")]
116 SettlementAmountExceedsPermitted {
117 requested: String,
119 authorised: String,
121 },
122
123 #[error("witness.facilitator {witness} is not authorised on this facilitator")]
130 UptoFacilitatorMismatch {
131 witness: String,
133 },
134
135 #[error("on-chain proxy rejected settle: msg.sender does not match witness.facilitator")]
141 UptoUnauthorizedFacilitator,
142
143 #[error("on-chain proxy rejected settle: amount exceeds permitted maximum")]
149 UptoAmountExceedsPermitted,
150}
151
152impl From<serde_json::Error> for VerificationError {
153 fn from(err: serde_json::Error) -> Self {
154 Self::InvalidFormat(err.to_string())
155 }
156}
157
158impl AsPaymentProblem for VerificationError {
159 fn as_payment_problem(&self) -> PaymentProblem {
160 let reason = match self {
165 Self::InvalidFormat(_) | Self::InvalidSignature(_) | Self::Early | Self::Expired => {
166 ErrorReason::InvalidPayload
167 }
168 Self::InvalidPaymentAmount
169 | Self::RecipientMismatch
170 | Self::AssetMismatch
171 | Self::AcceptedRequirementsMismatch => ErrorReason::InvalidPaymentRequirements,
172 Self::ChainIdMismatch | Self::UnsupportedChain => ErrorReason::InvalidNetwork,
173 Self::InsufficientFunds => ErrorReason::InsufficientFunds,
174 Self::Permit2AllowanceRequired => ErrorReason::Permit2AllowanceRequired,
175 Self::SimulationFailed(_) => ErrorReason::InvalidTransactionState,
176 Self::UnsupportedScheme => ErrorReason::UnsupportedScheme,
177 Self::NonceAlreadyUsed => ErrorReason::NonceAlreadyUsed,
178 Self::DuplicateSettlement => ErrorReason::DuplicateSettlement,
179 Self::MemoMismatch => ErrorReason::InvalidExactSolanaPayloadMemoMismatch,
180 Self::MemoInstructionCountInvalid { .. } => {
181 ErrorReason::InvalidExactSolanaPayloadMemoCount
182 }
183 Self::SettlementAmountExceedsPermitted { .. } => {
184 ErrorReason::InvalidUptoEvmPayloadSettlementExceedsAmount
185 }
186 Self::UptoFacilitatorMismatch { .. } => ErrorReason::UptoFacilitatorMismatch,
187 Self::UptoUnauthorizedFacilitator => ErrorReason::UptoUnauthorizedFacilitator,
188 Self::UptoAmountExceedsPermitted => ErrorReason::UptoAmountExceedsPermitted,
189 };
190 PaymentProblem::new(reason, self.to_string())
191 }
192}
193
194#[derive(Debug, thiserror::Error)]
196#[non_exhaustive]
197pub enum SettlementError {
198 #[error("on-chain settlement failed: {0}")]
200 Onchain(String),
201
202 #[error("settlement timed out")]
204 Timeout,
205
206 #[error("duplicate settlement attempt")]
208 Duplicate,
209}
210
211impl AsPaymentProblem for SettlementError {
212 fn as_payment_problem(&self) -> PaymentProblem {
213 let reason = match self {
214 Self::Onchain(_) => ErrorReason::InvalidTransactionState,
215 Self::Timeout => ErrorReason::UnexpectedSettleError,
216 Self::Duplicate => ErrorReason::DuplicateSettlement,
217 };
218 PaymentProblem::new(reason, self.to_string())
219 }
220}
221
222#[derive(Debug, thiserror::Error)]
224#[non_exhaustive]
225pub enum FacilitatorError {
226 #[error(transparent)]
228 Verification(#[from] VerificationError),
229
230 #[error(transparent)]
232 Settlement(#[from] SettlementError),
233
234 #[error("{reason}: {message}")]
236 Aborted {
237 reason: String,
239 message: String,
241 },
242
243 #[error("on-chain error: {0}")]
245 Onchain(String),
246
247 #[error(transparent)]
249 Internal(Box<dyn std::error::Error + Send + Sync>),
250}
251
252impl FacilitatorError {
253 #[must_use]
255 pub fn aborted(reason: impl Into<String>, message: impl Into<String>) -> Self {
256 Self::Aborted {
257 reason: reason.into(),
258 message: message.into(),
259 }
260 }
261
262 #[must_use]
264 pub fn internal<E>(err: E) -> Self
265 where
266 E: Into<Box<dyn std::error::Error + Send + Sync>>,
267 {
268 Self::Internal(err.into())
269 }
270}
271
272impl AsPaymentProblem for FacilitatorError {
273 fn as_payment_problem(&self) -> PaymentProblem {
274 match self {
275 Self::Verification(e) => e.as_payment_problem(),
276 Self::Settlement(e) => e.as_payment_problem(),
277 Self::Aborted { reason, message } => PaymentProblem::new(
280 ErrorReason::from_wire(reason),
281 format!("{reason}: {message}"),
282 ),
283 Self::Onchain(message) => {
284 PaymentProblem::new(ErrorReason::InvalidTransactionState, message.clone())
285 }
286 Self::Internal(e) => {
287 PaymentProblem::new(ErrorReason::UnexpectedVerifyError, e.to_string())
288 }
289 }
290 }
291}
292
293#[derive(Debug, thiserror::Error)]
295#[non_exhaustive]
296pub enum ClientError {
297 #[error("no matching payment option")]
299 NoMatchingPaymentOption,
300
301 #[error("request is not cloneable (streaming body?)")]
303 RequestNotCloneable,
304
305 #[error("failed to parse 402 response: {0}")]
307 Parse(String),
308
309 #[error("failed to sign payment: {0}")]
311 Signing(String),
312
313 #[error("payment pre-condition not met: {0}")]
315 PreConditionFailed(String),
316
317 #[error(transparent)]
319 Json(#[from] serde_json::Error),
320}