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 does not match requirements")]
33 InvalidPaymentAmount,
34
35 #[error("payment authorization is not yet valid")]
37 Early,
38
39 #[error("payment authorization has expired")]
41 Expired,
42
43 #[error("chain id mismatch")]
45 ChainIdMismatch,
46
47 #[error("payment recipient mismatch")]
49 RecipientMismatch,
50
51 #[error("payment asset mismatch")]
53 AssetMismatch,
54
55 #[error("insufficient on-chain balance")]
57 InsufficientFunds,
58
59 #[error("permit2 allowance required")]
65 Permit2AllowanceRequired,
66
67 #[error("invalid signature: {0}")]
69 InvalidSignature(String),
70
71 #[error("simulation failed: {0}")]
73 SimulationFailed(String),
74
75 #[error("unsupported chain")]
77 UnsupportedChain,
78
79 #[error("unsupported scheme")]
81 UnsupportedScheme,
82
83 #[error("accepted details do not match requirements")]
85 AcceptedRequirementsMismatch,
86
87 #[error("authorization nonce already used")]
89 NonceAlreadyUsed,
90
91 #[error("duplicate settlement attempt")]
93 DuplicateSettlement,
94
95 #[error("memo data mismatch")]
97 MemoMismatch,
98
99 #[error("memo instruction count invalid (expected 1, got {count})")]
101 MemoInstructionCountInvalid {
102 count: usize,
104 },
105
106 #[error("settlement amount {requested} exceeds authorised maximum {authorised}")]
113 SettlementAmountExceedsPermitted {
114 requested: String,
116 authorised: String,
118 },
119
120 #[error("witness.facilitator {witness} is not authorised on this facilitator")]
127 UptoFacilitatorMismatch {
128 witness: String,
130 },
131
132 #[error("on-chain proxy rejected settle: msg.sender does not match witness.facilitator")]
138 UptoUnauthorizedFacilitator,
139
140 #[error("on-chain proxy rejected settle: amount exceeds permitted maximum")]
146 UptoAmountExceedsPermitted,
147}
148
149impl From<serde_json::Error> for VerificationError {
150 fn from(err: serde_json::Error) -> Self {
151 Self::InvalidFormat(err.to_string())
152 }
153}
154
155impl AsPaymentProblem for VerificationError {
156 fn as_payment_problem(&self) -> PaymentProblem {
157 let reason = match self {
162 Self::InvalidFormat(_) | Self::InvalidSignature(_) | Self::Early | Self::Expired => {
163 ErrorReason::InvalidPayload
164 }
165 Self::InvalidPaymentAmount
166 | Self::RecipientMismatch
167 | Self::AssetMismatch
168 | Self::AcceptedRequirementsMismatch => ErrorReason::InvalidPaymentRequirements,
169 Self::ChainIdMismatch | Self::UnsupportedChain => ErrorReason::InvalidNetwork,
170 Self::InsufficientFunds => ErrorReason::InsufficientFunds,
171 Self::Permit2AllowanceRequired => ErrorReason::Permit2AllowanceRequired,
172 Self::SimulationFailed(_) => ErrorReason::InvalidTransactionState,
173 Self::UnsupportedScheme => ErrorReason::UnsupportedScheme,
174 Self::NonceAlreadyUsed => ErrorReason::NonceAlreadyUsed,
175 Self::DuplicateSettlement => ErrorReason::DuplicateSettlement,
176 Self::MemoMismatch => ErrorReason::InvalidExactSolanaPayloadMemoMismatch,
177 Self::MemoInstructionCountInvalid { .. } => {
178 ErrorReason::InvalidExactSolanaPayloadMemoCount
179 }
180 Self::SettlementAmountExceedsPermitted { .. } => {
181 ErrorReason::InvalidUptoEvmPayloadSettlementExceedsAmount
182 }
183 Self::UptoFacilitatorMismatch { .. } => ErrorReason::UptoFacilitatorMismatch,
184 Self::UptoUnauthorizedFacilitator => ErrorReason::UptoUnauthorizedFacilitator,
185 Self::UptoAmountExceedsPermitted => ErrorReason::UptoAmountExceedsPermitted,
186 };
187 PaymentProblem::new(reason, self.to_string())
188 }
189}
190
191#[derive(Debug, thiserror::Error)]
193#[non_exhaustive]
194pub enum SettlementError {
195 #[error("on-chain settlement failed: {0}")]
197 Onchain(String),
198
199 #[error("settlement timed out")]
201 Timeout,
202
203 #[error("duplicate settlement attempt")]
205 Duplicate,
206}
207
208impl AsPaymentProblem for SettlementError {
209 fn as_payment_problem(&self) -> PaymentProblem {
210 let reason = match self {
211 Self::Onchain(_) => ErrorReason::InvalidTransactionState,
212 Self::Timeout => ErrorReason::UnexpectedSettleError,
213 Self::Duplicate => ErrorReason::DuplicateSettlement,
214 };
215 PaymentProblem::new(reason, self.to_string())
216 }
217}
218
219#[derive(Debug, thiserror::Error)]
221#[non_exhaustive]
222pub enum FacilitatorError {
223 #[error(transparent)]
225 Verification(#[from] VerificationError),
226
227 #[error(transparent)]
229 Settlement(#[from] SettlementError),
230
231 #[error("{reason}: {message}")]
233 Aborted {
234 reason: String,
236 message: String,
238 },
239
240 #[error("on-chain error: {0}")]
242 Onchain(String),
243
244 #[error(transparent)]
246 Internal(Box<dyn std::error::Error + Send + Sync>),
247}
248
249impl FacilitatorError {
250 #[must_use]
252 pub fn aborted(reason: impl Into<String>, message: impl Into<String>) -> Self {
253 Self::Aborted {
254 reason: reason.into(),
255 message: message.into(),
256 }
257 }
258
259 #[must_use]
261 pub fn internal<E>(err: E) -> Self
262 where
263 E: Into<Box<dyn std::error::Error + Send + Sync>>,
264 {
265 Self::Internal(err.into())
266 }
267}
268
269impl AsPaymentProblem for FacilitatorError {
270 fn as_payment_problem(&self) -> PaymentProblem {
271 match self {
272 Self::Verification(e) => e.as_payment_problem(),
273 Self::Settlement(e) => e.as_payment_problem(),
274 Self::Aborted { reason, message } => PaymentProblem::new(
277 ErrorReason::from_wire(reason),
278 format!("{reason}: {message}"),
279 ),
280 Self::Onchain(message) => {
281 PaymentProblem::new(ErrorReason::InvalidTransactionState, message.clone())
282 }
283 Self::Internal(e) => {
284 PaymentProblem::new(ErrorReason::UnexpectedVerifyError, e.to_string())
285 }
286 }
287 }
288}
289
290#[derive(Debug, thiserror::Error)]
292#[non_exhaustive]
293pub enum ClientError {
294 #[error("no matching payment option")]
296 NoMatchingPaymentOption,
297
298 #[error("request is not cloneable (streaming body?)")]
300 RequestNotCloneable,
301
302 #[error("failed to parse 402 response: {0}")]
304 Parse(String),
305
306 #[error("failed to sign payment: {0}")]
308 Signing(String),
309
310 #[error("payment pre-condition not met: {0}")]
312 PreConditionFailed(String),
313
314 #[error(transparent)]
316 Json(#[from] serde_json::Error),
317}