1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
use std::num::ParseIntError;

use serde::{Deserialize, Serialize};
use thiserror::Error;

use crate::params::to_snakecase;

/// An error encountered when communicating with the Stripe API.
#[derive(Debug, Error)]
pub enum StripeError {
    #[error("error reported by stripe: {0}")]
    Stripe(#[from] RequestError),
    #[error("error serializing or deserializing a querystring: {0}")]
    QueryStringSerialize(#[from] serde_path_to_error::Error<serde_qs::Error>),
    #[error("error serializing or deserializing a request")]
    JSONSerialize(#[from] serde_path_to_error::Error<serde_json::Error>),
    #[error("attempted to access an unsupported version of the api")]
    UnsupportedVersion,
    #[error("error communicating with stripe: {0}")]
    ClientError(String),
    #[error("timeout communicating with stripe")]
    Timeout,
}

#[cfg(feature = "hyper")]
impl From<hyper::Error> for StripeError {
    fn from(err: hyper::Error) -> StripeError {
        StripeError::ClientError(err.to_string())
    }
}

impl From<http_types::Error> for StripeError {
    fn from(err: http_types::Error) -> StripeError {
        StripeError::ClientError(err.to_string())
    }
}

/// The list of possible values for a RequestError's type.
#[derive(Debug, PartialEq, Deserialize, Default)]
pub enum ErrorType {
    #[serde(skip_deserializing)]
    #[default]
    Unknown,
    #[serde(rename = "api_error")]
    Api,
    #[serde(rename = "api_connection_error")]
    Connection,
    #[serde(rename = "authentication_error")]
    Authentication,
    #[serde(rename = "card_error")]
    Card,
    #[serde(rename = "idempotency_error")]
    IdempotencyError,
    #[serde(rename = "invalid_request_error")]
    InvalidRequest,
    #[serde(rename = "rate_limit_error")]
    RateLimit,
    #[serde(rename = "validation_error")]
    Validation,
}

impl std::fmt::Display for ErrorType {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(f, "{}", to_snakecase(&format!("{:?}Error", self)))
    }
}

/// The list of possible values for a RequestError's code.
#[derive(Clone, Copy, Debug, Deserialize, Serialize, Eq, PartialEq, Hash)]
#[serde(rename_all = "snake_case")]
#[non_exhaustive]
pub enum ErrorCode {
    AccountAlreadyExists,
    AccountCountryInvalidAddress,
    AccountInvalid,
    AccountNumberInvalid,
    AlipayUpgradeRequired,
    AmountTooLarge,
    AmountTooSmall,
    ApiKeyExpired,
    BalanceInsufficient,
    BankAccountExists,
    BankAccountUnusable,
    BankAccountUnverified,
    BankAccountVerificationFailed,
    BitcoinUpgradeRequired,
    CardDeclined,
    ChargeAlreadyCaptured,
    ChargeAlreadyRefunded,
    ChargeDisputed,
    ChargeExpiredForCapture,
    CountryUnsupported,
    CouponExpired,
    CustomerMaxSubscriptions,
    EmailInvalid,
    ExpiredCard,
    IdempotencyKeyInUse,
    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,
    PaymentIntentUnexpectedState,
    PayoutsNotAllowed,
    PlatformApiKeyExpired,
    PostalCodeInvalid,
    ProcessingError,
    ProductInactive,
    RateLimit,
    ResourceAlreadyExists,
    ResourceMissing,
    RoutingNumberInvalid,
    SecretKeyRequired,
    SepaUnsupportedAccount,
    ShippingCalculationFailed,
    SkuInactive,
    StateUnsupported,
    TaxIdInvalid,
    TaxesCalculationFailed,
    TestmodeChargesOnly,
    TlsVersionUnsupported,
    TokenAlreadyUsed,
    TokenInUse,
    TransfersNotAllowed,
    UpstreamOrderCreationFailed,
    UrlInvalid,
}

impl std::fmt::Display for ErrorCode {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(f, "{}", to_snakecase(&format!("{:?}", self)))
    }
}

/// An error reported by stripe in a request's response.
///
/// For more details see <https://stripe.com/docs/api#errors>.
#[derive(Debug, Default, Deserialize, Error)]
#[error("{error_type} ({http_status}){}", message.as_ref().map(|msg| {
    format!(" with message: {msg:?}")
}).unwrap_or_default())]
pub struct RequestError {
    /// The HTTP status in the response.
    #[serde(skip_deserializing)]
    pub http_status: u16,

    /// The type of error returned.
    #[serde(rename = "type")]
    pub error_type: ErrorType,

    /// A human-readable message providing more details about the error.
    /// For card errors, these messages can be shown to end users.
    #[serde(default)]
    pub message: Option<String>,

    /// For card errors, a value describing the kind of card error that occured.
    pub code: Option<ErrorCode>,

    /// For card errors resulting from a bank decline, a string indicating the
    /// bank's reason for the decline if they provide one.
    pub decline_code: Option<String>,

    /// The ID of the failed charge, if applicable.
    pub charge: Option<String>,
}

/// The structure of the json body when an error is included in
/// the response from Stripe.
#[derive(Deserialize)]
pub struct ErrorResponse {
    pub error: RequestError,
}

/// An error encountered when communicating with the Stripe API webhooks.
#[derive(Debug, Error)]
pub enum WebhookError {
    #[error("invalid key length")]
    BadKey,
    #[error("error parsing timestamp")]
    BadHeader(#[from] ParseIntError),
    #[error("error comparing signatures")]
    BadSignature,
    #[error("error comparing timestamps - over tolerance")]
    BadTimestamp(i64),
    #[error("error parsing event object")]
    BadParse(#[from] serde_json::Error),
}