Skip to main content

stripe_shared/
issuing_dispute.rs

1/// As a [card issuer](https://docs.stripe.com/issuing), you can dispute transactions that the cardholder does not recognize, suspects to be fraudulent, or has other issues with.
2///
3/// Related guide: [Issuing disputes](https://docs.stripe.com/issuing/purchases/disputes)
4///
5/// For more details see <<https://stripe.com/docs/api/issuing/disputes/object>>.
6#[derive(Clone)]
7#[cfg_attr(not(feature = "redact-generated-debug"), derive(Debug))]
8#[cfg_attr(feature = "deserialize", derive(serde::Deserialize))]
9pub struct IssuingDispute {
10    /// Disputed amount in the card's currency and in the [smallest currency unit](https://docs.stripe.com/currencies#zero-decimal).
11    /// Usually the amount of the `transaction`, but can differ (usually because of currency fluctuation).
12    pub amount: i64,
13    /// List of balance transactions associated with the dispute.
14    pub balance_transactions: Option<Vec<stripe_shared::BalanceTransaction>>,
15    /// Time at which the object was created. Measured in seconds since the Unix epoch.
16    pub created: stripe_types::Timestamp,
17    /// The currency the `transaction` was made in.
18    pub currency: stripe_types::Currency,
19    pub evidence: stripe_shared::IssuingDisputeEvidence,
20    /// Unique identifier for the object.
21    pub id: stripe_shared::IssuingDisputeId,
22    /// If the object exists in live mode, the value is `true`.
23    /// If the object exists in test mode, the value is `false`.
24    pub livemode: bool,
25    /// The enum that describes the dispute loss outcome.
26    /// If the dispute is not lost, this field will be absent.
27    /// New enum values may be added in the future, so be sure to handle unknown values.
28    pub loss_reason: Option<IssuingDisputeLossReason>,
29    /// Set of [key-value pairs](https://docs.stripe.com/api/metadata) that you can attach to an object.
30    /// This can be useful for storing additional information about the object in a structured format.
31    pub metadata: std::collections::HashMap<String, String>,
32    /// Current status of the dispute.
33    pub status: stripe_shared::IssuingDisputeStatus,
34    /// The transaction being disputed.
35    pub transaction: stripe_types::Expandable<stripe_shared::IssuingTransaction>,
36    /// [Treasury](https://docs.stripe.com/api/treasury) details related to this dispute if it was created on a [FinancialAccount](/docs/api/treasury/financial_accounts.
37    pub treasury: Option<stripe_shared::IssuingDisputeTreasury>,
38}
39#[cfg(feature = "redact-generated-debug")]
40impl std::fmt::Debug for IssuingDispute {
41    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
42        f.debug_struct("IssuingDispute").finish_non_exhaustive()
43    }
44}
45#[doc(hidden)]
46pub struct IssuingDisputeBuilder {
47    amount: Option<i64>,
48    balance_transactions: Option<Option<Vec<stripe_shared::BalanceTransaction>>>,
49    created: Option<stripe_types::Timestamp>,
50    currency: Option<stripe_types::Currency>,
51    evidence: Option<stripe_shared::IssuingDisputeEvidence>,
52    id: Option<stripe_shared::IssuingDisputeId>,
53    livemode: Option<bool>,
54    loss_reason: Option<Option<IssuingDisputeLossReason>>,
55    metadata: Option<std::collections::HashMap<String, String>>,
56    status: Option<stripe_shared::IssuingDisputeStatus>,
57    transaction: Option<stripe_types::Expandable<stripe_shared::IssuingTransaction>>,
58    treasury: Option<Option<stripe_shared::IssuingDisputeTreasury>>,
59}
60
61#[allow(
62    unused_variables,
63    irrefutable_let_patterns,
64    clippy::let_unit_value,
65    clippy::match_single_binding,
66    clippy::single_match
67)]
68const _: () = {
69    use miniserde::de::{Map, Visitor};
70    use miniserde::json::Value;
71    use miniserde::{Deserialize, Result, make_place};
72    use stripe_types::miniserde_helpers::FromValueOpt;
73    use stripe_types::{MapBuilder, ObjectDeser};
74
75    make_place!(Place);
76
77    impl Deserialize for IssuingDispute {
78        fn begin(out: &mut Option<Self>) -> &mut dyn Visitor {
79            Place::new(out)
80        }
81    }
82
83    struct Builder<'a> {
84        out: &'a mut Option<IssuingDispute>,
85        builder: IssuingDisputeBuilder,
86    }
87
88    impl Visitor for Place<IssuingDispute> {
89        fn map(&mut self) -> Result<Box<dyn Map + '_>> {
90            Ok(Box::new(Builder {
91                out: &mut self.out,
92                builder: IssuingDisputeBuilder::deser_default(),
93            }))
94        }
95    }
96
97    impl MapBuilder for IssuingDisputeBuilder {
98        type Out = IssuingDispute;
99        fn key(&mut self, k: &str) -> Result<&mut dyn Visitor> {
100            Ok(match k {
101                "amount" => Deserialize::begin(&mut self.amount),
102                "balance_transactions" => Deserialize::begin(&mut self.balance_transactions),
103                "created" => Deserialize::begin(&mut self.created),
104                "currency" => Deserialize::begin(&mut self.currency),
105                "evidence" => Deserialize::begin(&mut self.evidence),
106                "id" => Deserialize::begin(&mut self.id),
107                "livemode" => Deserialize::begin(&mut self.livemode),
108                "loss_reason" => Deserialize::begin(&mut self.loss_reason),
109                "metadata" => Deserialize::begin(&mut self.metadata),
110                "status" => Deserialize::begin(&mut self.status),
111                "transaction" => Deserialize::begin(&mut self.transaction),
112                "treasury" => Deserialize::begin(&mut self.treasury),
113                _ => <dyn Visitor>::ignore(),
114            })
115        }
116
117        fn deser_default() -> Self {
118            Self {
119                amount: None,
120                balance_transactions: Some(None),
121                created: None,
122                currency: None,
123                evidence: None,
124                id: None,
125                livemode: None,
126                loss_reason: Some(None),
127                metadata: None,
128                status: None,
129                transaction: None,
130                treasury: Some(None),
131            }
132        }
133
134        fn take_out(&mut self) -> Option<Self::Out> {
135            let (
136                Some(amount),
137                Some(balance_transactions),
138                Some(created),
139                Some(currency),
140                Some(evidence),
141                Some(id),
142                Some(livemode),
143                Some(loss_reason),
144                Some(metadata),
145                Some(status),
146                Some(transaction),
147                Some(treasury),
148            ) = (
149                self.amount,
150                self.balance_transactions.take(),
151                self.created,
152                self.currency.take(),
153                self.evidence.take(),
154                self.id.take(),
155                self.livemode,
156                self.loss_reason.take(),
157                self.metadata.take(),
158                self.status.take(),
159                self.transaction.take(),
160                self.treasury.take(),
161            )
162            else {
163                return None;
164            };
165            Some(Self::Out {
166                amount,
167                balance_transactions,
168                created,
169                currency,
170                evidence,
171                id,
172                livemode,
173                loss_reason,
174                metadata,
175                status,
176                transaction,
177                treasury,
178            })
179        }
180    }
181
182    impl Map for Builder<'_> {
183        fn key(&mut self, k: &str) -> Result<&mut dyn Visitor> {
184            self.builder.key(k)
185        }
186
187        fn finish(&mut self) -> Result<()> {
188            *self.out = self.builder.take_out();
189            Ok(())
190        }
191    }
192
193    impl ObjectDeser for IssuingDispute {
194        type Builder = IssuingDisputeBuilder;
195    }
196
197    impl FromValueOpt for IssuingDispute {
198        fn from_value(v: Value) -> Option<Self> {
199            let Value::Object(obj) = v else {
200                return None;
201            };
202            let mut b = IssuingDisputeBuilder::deser_default();
203            for (k, v) in obj {
204                match k.as_str() {
205                    "amount" => b.amount = FromValueOpt::from_value(v),
206                    "balance_transactions" => b.balance_transactions = FromValueOpt::from_value(v),
207                    "created" => b.created = FromValueOpt::from_value(v),
208                    "currency" => b.currency = FromValueOpt::from_value(v),
209                    "evidence" => b.evidence = FromValueOpt::from_value(v),
210                    "id" => b.id = FromValueOpt::from_value(v),
211                    "livemode" => b.livemode = FromValueOpt::from_value(v),
212                    "loss_reason" => b.loss_reason = FromValueOpt::from_value(v),
213                    "metadata" => b.metadata = FromValueOpt::from_value(v),
214                    "status" => b.status = FromValueOpt::from_value(v),
215                    "transaction" => b.transaction = FromValueOpt::from_value(v),
216                    "treasury" => b.treasury = FromValueOpt::from_value(v),
217                    _ => {}
218                }
219            }
220            b.take_out()
221        }
222    }
223};
224#[cfg(feature = "serialize")]
225impl serde::Serialize for IssuingDispute {
226    fn serialize<S: serde::Serializer>(&self, s: S) -> Result<S::Ok, S::Error> {
227        use serde::ser::SerializeStruct;
228        let mut s = s.serialize_struct("IssuingDispute", 13)?;
229        s.serialize_field("amount", &self.amount)?;
230        s.serialize_field("balance_transactions", &self.balance_transactions)?;
231        s.serialize_field("created", &self.created)?;
232        s.serialize_field("currency", &self.currency)?;
233        s.serialize_field("evidence", &self.evidence)?;
234        s.serialize_field("id", &self.id)?;
235        s.serialize_field("livemode", &self.livemode)?;
236        s.serialize_field("loss_reason", &self.loss_reason)?;
237        s.serialize_field("metadata", &self.metadata)?;
238        s.serialize_field("status", &self.status)?;
239        s.serialize_field("transaction", &self.transaction)?;
240        s.serialize_field("treasury", &self.treasury)?;
241
242        s.serialize_field("object", "issuing.dispute")?;
243        s.end()
244    }
245}
246/// The enum that describes the dispute loss outcome.
247/// If the dispute is not lost, this field will be absent.
248/// New enum values may be added in the future, so be sure to handle unknown values.
249#[derive(Clone, Eq, PartialEq)]
250#[non_exhaustive]
251pub enum IssuingDisputeLossReason {
252    CardholderAuthenticationIssuerLiability,
253    Eci5TokenTransactionWithTavv,
254    ExcessDisputesInTimeframe,
255    HasNotMetTheMinimumDisputeAmountRequirements,
256    InvalidDuplicateDispute,
257    InvalidIncorrectAmountDispute,
258    InvalidNoAuthorization,
259    InvalidUseOfDisputes,
260    MerchandiseDeliveredOrShipped,
261    MerchandiseOrServiceAsDescribed,
262    NotCancelled,
263    Other,
264    RefundIssued,
265    SubmittedBeyondAllowableTimeLimit,
266    Transaction3dsRequired,
267    TransactionApprovedAfterPriorFraudDispute,
268    TransactionAuthorized,
269    TransactionElectronicallyRead,
270    TransactionQualifiesForVisaEasyPaymentService,
271    TransactionUnattended,
272    /// An unrecognized value from Stripe. Should not be used as a request parameter.
273    Unknown(String),
274}
275impl IssuingDisputeLossReason {
276    pub fn as_str(&self) -> &str {
277        use IssuingDisputeLossReason::*;
278        match self {
279            CardholderAuthenticationIssuerLiability => "cardholder_authentication_issuer_liability",
280            Eci5TokenTransactionWithTavv => "eci5_token_transaction_with_tavv",
281            ExcessDisputesInTimeframe => "excess_disputes_in_timeframe",
282            HasNotMetTheMinimumDisputeAmountRequirements => {
283                "has_not_met_the_minimum_dispute_amount_requirements"
284            }
285            InvalidDuplicateDispute => "invalid_duplicate_dispute",
286            InvalidIncorrectAmountDispute => "invalid_incorrect_amount_dispute",
287            InvalidNoAuthorization => "invalid_no_authorization",
288            InvalidUseOfDisputes => "invalid_use_of_disputes",
289            MerchandiseDeliveredOrShipped => "merchandise_delivered_or_shipped",
290            MerchandiseOrServiceAsDescribed => "merchandise_or_service_as_described",
291            NotCancelled => "not_cancelled",
292            Other => "other",
293            RefundIssued => "refund_issued",
294            SubmittedBeyondAllowableTimeLimit => "submitted_beyond_allowable_time_limit",
295            Transaction3dsRequired => "transaction_3ds_required",
296            TransactionApprovedAfterPriorFraudDispute => {
297                "transaction_approved_after_prior_fraud_dispute"
298            }
299            TransactionAuthorized => "transaction_authorized",
300            TransactionElectronicallyRead => "transaction_electronically_read",
301            TransactionQualifiesForVisaEasyPaymentService => {
302                "transaction_qualifies_for_visa_easy_payment_service"
303            }
304            TransactionUnattended => "transaction_unattended",
305            Unknown(v) => v,
306        }
307    }
308}
309
310impl std::str::FromStr for IssuingDisputeLossReason {
311    type Err = std::convert::Infallible;
312    fn from_str(s: &str) -> Result<Self, Self::Err> {
313        use IssuingDisputeLossReason::*;
314        match s {
315            "cardholder_authentication_issuer_liability" => {
316                Ok(CardholderAuthenticationIssuerLiability)
317            }
318            "eci5_token_transaction_with_tavv" => Ok(Eci5TokenTransactionWithTavv),
319            "excess_disputes_in_timeframe" => Ok(ExcessDisputesInTimeframe),
320            "has_not_met_the_minimum_dispute_amount_requirements" => {
321                Ok(HasNotMetTheMinimumDisputeAmountRequirements)
322            }
323            "invalid_duplicate_dispute" => Ok(InvalidDuplicateDispute),
324            "invalid_incorrect_amount_dispute" => Ok(InvalidIncorrectAmountDispute),
325            "invalid_no_authorization" => Ok(InvalidNoAuthorization),
326            "invalid_use_of_disputes" => Ok(InvalidUseOfDisputes),
327            "merchandise_delivered_or_shipped" => Ok(MerchandiseDeliveredOrShipped),
328            "merchandise_or_service_as_described" => Ok(MerchandiseOrServiceAsDescribed),
329            "not_cancelled" => Ok(NotCancelled),
330            "other" => Ok(Other),
331            "refund_issued" => Ok(RefundIssued),
332            "submitted_beyond_allowable_time_limit" => Ok(SubmittedBeyondAllowableTimeLimit),
333            "transaction_3ds_required" => Ok(Transaction3dsRequired),
334            "transaction_approved_after_prior_fraud_dispute" => {
335                Ok(TransactionApprovedAfterPriorFraudDispute)
336            }
337            "transaction_authorized" => Ok(TransactionAuthorized),
338            "transaction_electronically_read" => Ok(TransactionElectronicallyRead),
339            "transaction_qualifies_for_visa_easy_payment_service" => {
340                Ok(TransactionQualifiesForVisaEasyPaymentService)
341            }
342            "transaction_unattended" => Ok(TransactionUnattended),
343            v => {
344                tracing::warn!("Unknown value '{}' for enum '{}'", v, "IssuingDisputeLossReason");
345                Ok(Unknown(v.to_owned()))
346            }
347        }
348    }
349}
350impl std::fmt::Display for IssuingDisputeLossReason {
351    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
352        f.write_str(self.as_str())
353    }
354}
355
356#[cfg(not(feature = "redact-generated-debug"))]
357impl std::fmt::Debug for IssuingDisputeLossReason {
358    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
359        f.write_str(self.as_str())
360    }
361}
362#[cfg(feature = "redact-generated-debug")]
363impl std::fmt::Debug for IssuingDisputeLossReason {
364    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
365        f.debug_struct(stringify!(IssuingDisputeLossReason)).finish_non_exhaustive()
366    }
367}
368#[cfg(feature = "serialize")]
369impl serde::Serialize for IssuingDisputeLossReason {
370    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
371    where
372        S: serde::Serializer,
373    {
374        serializer.serialize_str(self.as_str())
375    }
376}
377impl miniserde::Deserialize for IssuingDisputeLossReason {
378    fn begin(out: &mut Option<Self>) -> &mut dyn miniserde::de::Visitor {
379        crate::Place::new(out)
380    }
381}
382
383impl miniserde::de::Visitor for crate::Place<IssuingDisputeLossReason> {
384    fn string(&mut self, s: &str) -> miniserde::Result<()> {
385        use std::str::FromStr;
386        self.out = Some(IssuingDisputeLossReason::from_str(s).expect("infallible"));
387        Ok(())
388    }
389}
390
391stripe_types::impl_from_val_with_from_str!(IssuingDisputeLossReason);
392#[cfg(feature = "deserialize")]
393impl<'de> serde::Deserialize<'de> for IssuingDisputeLossReason {
394    fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
395        use std::str::FromStr;
396        let s: std::borrow::Cow<'de, str> = serde::Deserialize::deserialize(deserializer)?;
397        Ok(Self::from_str(&s).expect("infallible"))
398    }
399}
400impl stripe_types::Object for IssuingDispute {
401    type Id = stripe_shared::IssuingDisputeId;
402    fn id(&self) -> &Self::Id {
403        &self.id
404    }
405
406    fn into_id(self) -> Self::Id {
407        self.id
408    }
409}
410stripe_types::def_id!(IssuingDisputeId);
411#[derive(Clone, Eq, PartialEq)]
412#[non_exhaustive]
413pub enum IssuingDisputeStatus {
414    Expired,
415    Lost,
416    Submitted,
417    Unsubmitted,
418    Won,
419    /// An unrecognized value from Stripe. Should not be used as a request parameter.
420    Unknown(String),
421}
422impl IssuingDisputeStatus {
423    pub fn as_str(&self) -> &str {
424        use IssuingDisputeStatus::*;
425        match self {
426            Expired => "expired",
427            Lost => "lost",
428            Submitted => "submitted",
429            Unsubmitted => "unsubmitted",
430            Won => "won",
431            Unknown(v) => v,
432        }
433    }
434}
435
436impl std::str::FromStr for IssuingDisputeStatus {
437    type Err = std::convert::Infallible;
438    fn from_str(s: &str) -> Result<Self, Self::Err> {
439        use IssuingDisputeStatus::*;
440        match s {
441            "expired" => Ok(Expired),
442            "lost" => Ok(Lost),
443            "submitted" => Ok(Submitted),
444            "unsubmitted" => Ok(Unsubmitted),
445            "won" => Ok(Won),
446            v => {
447                tracing::warn!("Unknown value '{}' for enum '{}'", v, "IssuingDisputeStatus");
448                Ok(Unknown(v.to_owned()))
449            }
450        }
451    }
452}
453impl std::fmt::Display for IssuingDisputeStatus {
454    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
455        f.write_str(self.as_str())
456    }
457}
458
459#[cfg(not(feature = "redact-generated-debug"))]
460impl std::fmt::Debug for IssuingDisputeStatus {
461    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
462        f.write_str(self.as_str())
463    }
464}
465#[cfg(feature = "redact-generated-debug")]
466impl std::fmt::Debug for IssuingDisputeStatus {
467    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
468        f.debug_struct(stringify!(IssuingDisputeStatus)).finish_non_exhaustive()
469    }
470}
471impl serde::Serialize for IssuingDisputeStatus {
472    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
473    where
474        S: serde::Serializer,
475    {
476        serializer.serialize_str(self.as_str())
477    }
478}
479impl miniserde::Deserialize for IssuingDisputeStatus {
480    fn begin(out: &mut Option<Self>) -> &mut dyn miniserde::de::Visitor {
481        crate::Place::new(out)
482    }
483}
484
485impl miniserde::de::Visitor for crate::Place<IssuingDisputeStatus> {
486    fn string(&mut self, s: &str) -> miniserde::Result<()> {
487        use std::str::FromStr;
488        self.out = Some(IssuingDisputeStatus::from_str(s).expect("infallible"));
489        Ok(())
490    }
491}
492
493stripe_types::impl_from_val_with_from_str!(IssuingDisputeStatus);
494#[cfg(feature = "deserialize")]
495impl<'de> serde::Deserialize<'de> for IssuingDisputeStatus {
496    fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
497        use std::str::FromStr;
498        let s: std::borrow::Cow<'de, str> = serde::Deserialize::deserialize(deserializer)?;
499        Ok(Self::from_str(&s).expect("infallible"))
500    }
501}