use crate::params::to_snakecase;
use serde_derive::{Deserialize, Serialize};
use std::num::ParseIntError;
#[derive(Debug)]
pub enum Error {
Stripe(RequestError),
Http(reqwest::Error),
Io(std::io::Error),
Serialize(Box<dyn std::error::Error + Send>),
Deserialize(Box<dyn std::error::Error + Send>),
Unsupported(&'static str),
Unexpected(&'static str),
}
impl Error {
pub fn serialize<T>(err: T) -> Error
where
T: std::error::Error + Send + 'static,
{
Error::Serialize(Box::new(err))
}
pub fn deserialize<T>(err: T) -> Error
where
T: std::error::Error + Send + 'static,
{
Error::Deserialize(Box::new(err))
}
}
impl std::fmt::Display for Error {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(std::error::Error::description(self))?;
match *self {
Error::Stripe(ref err) => write!(f, ": {}", err),
Error::Http(ref err) => write!(f, ": {}", err),
Error::Io(ref err) => write!(f, ": {}", err),
Error::Serialize(ref err) => write!(f, ": {}", err),
Error::Deserialize(ref err) => write!(f, ": {}", err),
Error::Unsupported(msg) => write!(f, "{}", msg),
Error::Unexpected(msg) => write!(f, "{}", msg),
}
}
}
impl std::error::Error for Error {
fn description(&self) -> &str {
match *self {
Error::Stripe(_) => "error reported by stripe",
Error::Http(_) => "error communicating with stripe",
Error::Io(_) => "error reading response from stripe",
Error::Serialize(_) => "error serializing a request",
Error::Deserialize(_) => "error deserializing a response",
Error::Unsupported(_) => "an unsupported operation was attempted",
Error::Unexpected(_) => "an unexpected error has occurred",
}
}
fn cause(&self) -> Option<&dyn std::error::Error> {
match *self {
Error::Stripe(ref err) => Some(err),
Error::Http(ref err) => Some(err),
Error::Io(ref err) => Some(err),
Error::Serialize(ref err) => Some(&**err),
Error::Deserialize(ref err) => Some(&**err),
Error::Unsupported(_) => None,
Error::Unexpected(_) => None,
}
}
}
impl From<RequestError> for Error {
fn from(err: RequestError) -> Error {
Error::Stripe(err)
}
}
impl From<reqwest::Error> for Error {
fn from(err: reqwest::Error) -> Error {
Error::Http(err)
}
}
impl From<std::io::Error> for Error {
fn from(err: std::io::Error) -> Error {
Error::Io(err)
}
}
#[derive(Debug, PartialEq, Deserialize)]
pub enum ErrorType {
#[serde(skip_deserializing)]
Unknown,
#[serde(rename = "api_error")]
Api,
#[serde(rename = "api_connection_error")]
Connection,
#[serde(rename = "authentication_error")]
Authentication,
#[serde(rename = "card_error")]
Card,
#[serde(rename = "invalid_request_error")]
InvalidRequest,
#[serde(rename = "rate_limit_error")]
RateLimit,
#[serde(rename = "validation_error")]
Validation,
}
impl Default for ErrorType {
fn default() -> Self {
ErrorType::Unknown
}
}
impl std::fmt::Display for ErrorType {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", to_snakecase(&format!("{:?}Error", self)))
}
}
#[derive(Clone, Copy, Debug, Deserialize, Serialize, Eq, PartialEq, Hash)]
#[serde(rename_all = "snake_case")]
pub enum ErrorCode {
AccountAlreadyExists,
AccountCountryInvalidAddress,
AccountInvalid,
AccountNumberInvalid,
AlipayUpgradeRequired,
AmountTooLarge,
AmountTooSmall,
ApiKeyExpired,
BalanceInsufficient,
BankAccountExists,
BankAccountUnusable,
BankAccountUnverified,
BitcoinUpgradeRequired,
CardDeclined,
ChargeAlreadyCaptured,
ChargeAlreadyRefunded,
ChargeDisputed,
ChargeExpiredForCapture,
CountryUnsupported,
CouponExpired,
CustomerMaxSubscriptions,
EmailInvalid,
ExpiredCard,
IncorrectAddress,
IncorrectCvc,
IncorrectNumber,
IncorrectZip,
InstantPayoutsUnsupported,
InvalidCardType,
InvalidChargeAmount,
InvalidCvc,
InvalidExpiryMonth,
InvalidExpiryYear,
InvalidNumber,
InvalidSourceUsage,
InvoiceNoCustomerLineItems,
InvoiceNoSubscriptionLineItems,
InvoiceNotEditable,
InvoiceUpcomingNone,
LivemodeMismatch,
Missing,
OrderCreationFailed,
OrderRequiredSettings,
OrderStatusInvalid,
OrderUpstreamTimeout,
OutOfInventory,
ParameterInvalidEmpty,
ParameterInvalidInteger,
ParameterInvalidStringBlank,
ParameterInvalidStringEmpty,
ParameterMissing,
ParameterUnknown,
PaymentMethodUnactivated,
PayoutsNotAllowed,
PlatformApiKeyExpired,
PostalCodeInvalid,
ProcessingError,
ProductInactive,
RateLimit,
ResourceAlreadyExists,
ResourceMissing,
RoutingNumberInvalid,
SecretKeyRequired,
SepaUnsupportedAccount,
ShippingCalculationFailed,
SkuInactive,
StateUnsupported,
TaxIdInvalid,
TaxesCalculationFailed,
TestmodeChargesOnly,
TlsVersionUnsupported,
TokenAlreadyUsed,
TokenInUse,
TransfersNotAllowed,
UpstreamOrderCreationFailed,
UrlInvalid,
#[doc(hidden)]
__NonExhaustive,
}
impl std::fmt::Display for ErrorCode {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", to_snakecase(&format!("{:?}", self)))
}
}
#[derive(Debug, Default, Deserialize)]
pub struct RequestError {
#[serde(skip_deserializing)]
pub http_status: u16,
#[serde(rename = "type")]
pub error_type: ErrorType,
#[serde(default)]
pub message: Option<String>,
pub code: Option<ErrorCode>,
pub decline_code: Option<String>,
pub charge: Option<String>,
}
impl std::fmt::Display for RequestError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}({})", self.error_type, self.http_status)?;
if let Some(ref message) = self.message {
write!(f, ": {}", message)?;
}
Ok(())
}
}
impl std::error::Error for RequestError {
fn description(&self) -> &str {
self.message.as_ref().map(|s| s.as_str()).unwrap_or("request error")
}
}
#[derive(Deserialize)]
pub struct ErrorResponse {
pub error: RequestError,
}
#[derive(Debug)]
pub enum WebhookError {
BadKey,
BadHeader(ParseIntError),
BadSignature,
BadTimestamp(i64),
BadParse(serde_json::Error),
}
impl std::fmt::Display for WebhookError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(std::error::Error::description(self))?;
match *self {
WebhookError::BadKey => Ok(()),
WebhookError::BadHeader(ref err) => write!(f, ": {}", err),
WebhookError::BadSignature => Ok(()),
WebhookError::BadTimestamp(ref err) => write!(f, ": {}", err),
WebhookError::BadParse(ref err) => write!(f, ": {}", err),
}
}
}
impl std::error::Error for WebhookError {
fn description(&self) -> &str {
match *self {
WebhookError::BadKey => "invalid key length",
WebhookError::BadHeader(_) => "error parsing timestamp",
WebhookError::BadSignature => "error comparing signatures",
WebhookError::BadTimestamp(_) => "error comparing timestamps - over tolerance",
WebhookError::BadParse(_) => "error parsing event object",
}
}
fn cause(&self) -> Option<&dyn std::error::Error> {
match *self {
WebhookError::BadKey => None,
WebhookError::BadHeader(ref err) => Some(err),
WebhookError::BadSignature => None,
WebhookError::BadTimestamp(_) => None,
WebhookError::BadParse(ref err) => Some(err),
}
}
}