use serde::{Deserialize, Serialize};
#[derive(Debug, thiserror::Error)]
#[non_exhaustive]
pub enum PaymentVerificationError {
#[error("Invalid format: {0}")]
InvalidFormat(String),
#[error("Payment amount is invalid with respect to the payment requirements")]
InvalidPaymentAmount,
#[error("Payment authorization is not yet valid")]
Early,
#[error("Payment authorization is expired")]
Expired,
#[error("Payment chain id is invalid with respect to the payment requirements")]
ChainIdMismatch,
#[error("Payment recipient is invalid with respect to the payment requirements")]
RecipientMismatch,
#[error("Payment asset is invalid with respect to the payment requirements")]
AssetMismatch,
#[error("Onchain balance is not enough to cover the payment amount")]
InsufficientFunds,
#[error("Permit2 allowance is not enough to cover the payment amount")]
Permit2AllowanceInsufficient,
#[error("{0}")]
InvalidSignature(String),
#[error("{0}")]
TransactionSimulation(String),
#[error("Unsupported chain")]
UnsupportedChain,
#[error("Unsupported scheme")]
UnsupportedScheme,
#[error("Accepted does not match payment requirements")]
AcceptedRequirementsMismatch,
#[error("Authorization nonce already used")]
NonceAlreadyUsed,
}
impl AsPaymentProblem for PaymentVerificationError {
fn as_payment_problem(&self) -> PaymentProblem {
let error_reason = match self {
Self::InvalidFormat(_) => ErrorReason::InvalidFormat,
Self::InvalidPaymentAmount => ErrorReason::InvalidPaymentAmount,
Self::InsufficientFunds => ErrorReason::InsufficientFunds,
Self::Permit2AllowanceInsufficient => ErrorReason::Permit2AllowanceInsufficient,
Self::Early => ErrorReason::InvalidPaymentEarly,
Self::Expired => ErrorReason::InvalidPaymentExpired,
Self::ChainIdMismatch => ErrorReason::ChainIdMismatch,
Self::RecipientMismatch => ErrorReason::RecipientMismatch,
Self::AssetMismatch => ErrorReason::AssetMismatch,
Self::InvalidSignature(_) => ErrorReason::InvalidSignature,
Self::TransactionSimulation(_) => ErrorReason::TransactionSimulation,
Self::UnsupportedChain => ErrorReason::UnsupportedChain,
Self::UnsupportedScheme => ErrorReason::UnsupportedScheme,
Self::AcceptedRequirementsMismatch => ErrorReason::AcceptedRequirementsMismatch,
Self::NonceAlreadyUsed => ErrorReason::NonceAlreadyUsed,
};
PaymentProblem::new(error_reason, self.to_string())
}
}
impl From<serde_json::Error> for PaymentVerificationError {
fn from(value: serde_json::Error) -> Self {
Self::InvalidFormat(value.to_string())
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
#[non_exhaustive]
pub enum ErrorReason {
InvalidFormat,
InvalidPaymentAmount,
InvalidPaymentEarly,
InvalidPaymentExpired,
ChainIdMismatch,
RecipientMismatch,
AssetMismatch,
AcceptedRequirementsMismatch,
InvalidSignature,
TransactionSimulation,
InsufficientFunds,
Permit2AllowanceInsufficient,
UnsupportedChain,
UnsupportedScheme,
NonceAlreadyUsed,
UnexpectedError,
}
impl ErrorReason {
#[must_use]
pub const fn as_str(self) -> &'static str {
match self {
Self::InvalidFormat => "invalid_format",
Self::InvalidPaymentAmount => "invalid_payment_amount",
Self::InvalidPaymentEarly => "invalid_payment_early",
Self::InvalidPaymentExpired => "invalid_payment_expired",
Self::ChainIdMismatch => "chain_id_mismatch",
Self::RecipientMismatch => "recipient_mismatch",
Self::AssetMismatch => "asset_mismatch",
Self::AcceptedRequirementsMismatch => "accepted_requirements_mismatch",
Self::InvalidSignature => "invalid_signature",
Self::TransactionSimulation => "transaction_simulation",
Self::InsufficientFunds => "insufficient_funds",
Self::Permit2AllowanceInsufficient => "permit2_allowance_insufficient",
Self::UnsupportedChain => "unsupported_chain",
Self::UnsupportedScheme => "unsupported_scheme",
Self::NonceAlreadyUsed => "nonce_already_used",
Self::UnexpectedError => "unexpected_error",
}
}
}
impl core::fmt::Display for ErrorReason {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.write_str(self.as_str())
}
}
pub trait AsPaymentProblem {
fn as_payment_problem(&self) -> PaymentProblem;
}
#[derive(Debug)]
pub struct PaymentProblem {
reason: ErrorReason,
details: String,
}
impl PaymentProblem {
#[must_use]
pub const fn new(reason: ErrorReason, details: String) -> Self {
Self { reason, details }
}
#[must_use]
pub const fn reason(&self) -> ErrorReason {
self.reason
}
#[must_use]
pub fn details(&self) -> &str {
&self.details
}
}