Skip to main content

stripe_shared/
refund.rs

1/// Refund objects allow you to refund a previously created charge that isn't
2/// refunded yet. Funds are refunded to the credit or debit card that's
3/// initially charged.
4///
5/// Related guide: [Refunds](https://docs.stripe.com/refunds)
6///
7/// For more details see <<https://stripe.com/docs/api/refunds/object>>.
8#[derive(Clone)]
9#[cfg_attr(not(feature = "redact-generated-debug"), derive(Debug))]
10#[cfg_attr(feature = "deserialize", derive(serde::Deserialize))]
11pub struct Refund {
12    /// Amount, in cents (or local equivalent).
13    pub amount: i64,
14    /// Balance transaction that describes the impact on your account balance.
15    pub balance_transaction: Option<stripe_types::Expandable<stripe_shared::BalanceTransaction>>,
16    /// ID of the charge that's refunded.
17    pub charge: Option<stripe_types::Expandable<stripe_shared::Charge>>,
18    /// Time at which the object was created. Measured in seconds since the Unix epoch.
19    pub created: stripe_types::Timestamp,
20    /// Three-letter [ISO currency code](https://www.iso.org/iso-4217-currency-codes.html), in lowercase.
21    /// Must be a [supported currency](https://stripe.com/docs/currencies).
22    pub currency: stripe_types::Currency,
23    /// ID of the customer of this refund.
24    pub customer: Option<stripe_types::Expandable<stripe_shared::Customer>>,
25    /// ID of the account of this refund.
26    pub customer_account: Option<String>,
27    /// An arbitrary string attached to the object.
28    /// You can use this for displaying to users (available on non-card refunds only).
29    pub description: Option<String>,
30    pub destination_details: Option<stripe_shared::RefundDestinationDetails>,
31    /// After the refund fails, this balance transaction describes the adjustment made on your account balance that reverses the initial balance transaction.
32    pub failure_balance_transaction:
33        Option<stripe_types::Expandable<stripe_shared::BalanceTransaction>>,
34    /// Provides the reason for the refund failure.
35    /// Possible values are: `lost_or_stolen_card`, `expired_or_canceled_card`, `charge_for_pending_refund_disputed`, `insufficient_funds`, `declined`, `merchant_request`, or `unknown`.
36    pub failure_reason: Option<String>,
37    /// Unique identifier for the object.
38    pub id: stripe_shared::RefundId,
39    /// For payment methods without native refund support (for example, Konbini, PromptPay), provide an email address for the customer to receive refund instructions.
40    pub instructions_email: Option<String>,
41    /// Set of [key-value pairs](https://docs.stripe.com/api/metadata) that you can attach to an object.
42    /// This can be useful for storing additional information about the object in a structured format.
43    pub metadata: Option<std::collections::HashMap<String, String>>,
44    pub next_action: Option<stripe_shared::RefundNextAction>,
45    /// ID of the PaymentIntent that's refunded.
46    pub payment_intent: Option<stripe_types::Expandable<stripe_shared::PaymentIntent>>,
47    /// ID of the payment method associated with this refund.
48    pub payment_method: Option<stripe_types::Expandable<stripe_shared::PaymentMethod>>,
49    /// Provides the reason for why the refund is pending.
50    /// Possible values are: `processing`, `insufficient_funds`, or `charge_pending`.
51    pub pending_reason: Option<RefundPendingReason>,
52    pub presentment_details: Option<stripe_shared::PaymentFlowsPaymentIntentPresentmentDetails>,
53    /// Reason for the refund, which is either user-provided (`duplicate`, `fraudulent`, or `requested_by_customer`) or generated by Stripe internally (`expired_uncaptured_charge`).
54    pub reason: Option<RefundReason>,
55    /// This is the transaction number that appears on email receipts sent for this refund.
56    pub receipt_number: Option<String>,
57    /// The transfer reversal that's associated with the refund.
58    /// Only present if the charge came from another Stripe account.
59    pub source_transfer_reversal: Option<stripe_types::Expandable<stripe_shared::TransferReversal>>,
60    /// Status of the refund.
61    /// This can be `pending`, `requires_action`, `succeeded`, `failed`, or `canceled`.
62    /// Learn more about [failed refunds](https://docs.stripe.com/refunds#failed-refunds).
63    pub status: Option<String>,
64    /// This refers to the transfer reversal object if the accompanying transfer reverses.
65    /// This is only applicable if the charge was created using the destination parameter.
66    pub transfer_reversal: Option<stripe_types::Expandable<stripe_shared::TransferReversal>>,
67}
68#[cfg(feature = "redact-generated-debug")]
69impl std::fmt::Debug for Refund {
70    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
71        f.debug_struct("Refund").finish_non_exhaustive()
72    }
73}
74#[doc(hidden)]
75pub struct RefundBuilder {
76    amount: Option<i64>,
77    balance_transaction:
78        Option<Option<stripe_types::Expandable<stripe_shared::BalanceTransaction>>>,
79    charge: Option<Option<stripe_types::Expandable<stripe_shared::Charge>>>,
80    created: Option<stripe_types::Timestamp>,
81    currency: Option<stripe_types::Currency>,
82    customer: Option<Option<stripe_types::Expandable<stripe_shared::Customer>>>,
83    customer_account: Option<Option<String>>,
84    description: Option<Option<String>>,
85    destination_details: Option<Option<stripe_shared::RefundDestinationDetails>>,
86    failure_balance_transaction:
87        Option<Option<stripe_types::Expandable<stripe_shared::BalanceTransaction>>>,
88    failure_reason: Option<Option<String>>,
89    id: Option<stripe_shared::RefundId>,
90    instructions_email: Option<Option<String>>,
91    metadata: Option<Option<std::collections::HashMap<String, String>>>,
92    next_action: Option<Option<stripe_shared::RefundNextAction>>,
93    payment_intent: Option<Option<stripe_types::Expandable<stripe_shared::PaymentIntent>>>,
94    payment_method: Option<Option<stripe_types::Expandable<stripe_shared::PaymentMethod>>>,
95    pending_reason: Option<Option<RefundPendingReason>>,
96    presentment_details: Option<Option<stripe_shared::PaymentFlowsPaymentIntentPresentmentDetails>>,
97    reason: Option<Option<RefundReason>>,
98    receipt_number: Option<Option<String>>,
99    source_transfer_reversal:
100        Option<Option<stripe_types::Expandable<stripe_shared::TransferReversal>>>,
101    status: Option<Option<String>>,
102    transfer_reversal: Option<Option<stripe_types::Expandable<stripe_shared::TransferReversal>>>,
103}
104
105#[allow(
106    unused_variables,
107    irrefutable_let_patterns,
108    clippy::let_unit_value,
109    clippy::match_single_binding,
110    clippy::single_match
111)]
112const _: () = {
113    use miniserde::de::{Map, Visitor};
114    use miniserde::json::Value;
115    use miniserde::{Deserialize, Result, make_place};
116    use stripe_types::miniserde_helpers::FromValueOpt;
117    use stripe_types::{MapBuilder, ObjectDeser};
118
119    make_place!(Place);
120
121    impl Deserialize for Refund {
122        fn begin(out: &mut Option<Self>) -> &mut dyn Visitor {
123            Place::new(out)
124        }
125    }
126
127    struct Builder<'a> {
128        out: &'a mut Option<Refund>,
129        builder: RefundBuilder,
130    }
131
132    impl Visitor for Place<Refund> {
133        fn map(&mut self) -> Result<Box<dyn Map + '_>> {
134            Ok(Box::new(Builder { out: &mut self.out, builder: RefundBuilder::deser_default() }))
135        }
136    }
137
138    impl MapBuilder for RefundBuilder {
139        type Out = Refund;
140        fn key(&mut self, k: &str) -> Result<&mut dyn Visitor> {
141            Ok(match k {
142                "amount" => Deserialize::begin(&mut self.amount),
143                "balance_transaction" => Deserialize::begin(&mut self.balance_transaction),
144                "charge" => Deserialize::begin(&mut self.charge),
145                "created" => Deserialize::begin(&mut self.created),
146                "currency" => Deserialize::begin(&mut self.currency),
147                "customer" => Deserialize::begin(&mut self.customer),
148                "customer_account" => Deserialize::begin(&mut self.customer_account),
149                "description" => Deserialize::begin(&mut self.description),
150                "destination_details" => Deserialize::begin(&mut self.destination_details),
151                "failure_balance_transaction" => {
152                    Deserialize::begin(&mut self.failure_balance_transaction)
153                }
154                "failure_reason" => Deserialize::begin(&mut self.failure_reason),
155                "id" => Deserialize::begin(&mut self.id),
156                "instructions_email" => Deserialize::begin(&mut self.instructions_email),
157                "metadata" => Deserialize::begin(&mut self.metadata),
158                "next_action" => Deserialize::begin(&mut self.next_action),
159                "payment_intent" => Deserialize::begin(&mut self.payment_intent),
160                "payment_method" => Deserialize::begin(&mut self.payment_method),
161                "pending_reason" => Deserialize::begin(&mut self.pending_reason),
162                "presentment_details" => Deserialize::begin(&mut self.presentment_details),
163                "reason" => Deserialize::begin(&mut self.reason),
164                "receipt_number" => Deserialize::begin(&mut self.receipt_number),
165                "source_transfer_reversal" => {
166                    Deserialize::begin(&mut self.source_transfer_reversal)
167                }
168                "status" => Deserialize::begin(&mut self.status),
169                "transfer_reversal" => Deserialize::begin(&mut self.transfer_reversal),
170                _ => <dyn Visitor>::ignore(),
171            })
172        }
173
174        fn deser_default() -> Self {
175            Self {
176                amount: None,
177                balance_transaction: Some(None),
178                charge: Some(None),
179                created: None,
180                currency: None,
181                customer: Some(None),
182                customer_account: Some(None),
183                description: Some(None),
184                destination_details: Some(None),
185                failure_balance_transaction: Some(None),
186                failure_reason: Some(None),
187                id: None,
188                instructions_email: Some(None),
189                metadata: Some(None),
190                next_action: Some(None),
191                payment_intent: Some(None),
192                payment_method: Some(None),
193                pending_reason: Some(None),
194                presentment_details: Some(None),
195                reason: Some(None),
196                receipt_number: Some(None),
197                source_transfer_reversal: Some(None),
198                status: Some(None),
199                transfer_reversal: Some(None),
200            }
201        }
202
203        fn take_out(&mut self) -> Option<Self::Out> {
204            let (
205                Some(amount),
206                Some(balance_transaction),
207                Some(charge),
208                Some(created),
209                Some(currency),
210                Some(customer),
211                Some(customer_account),
212                Some(description),
213                Some(destination_details),
214                Some(failure_balance_transaction),
215                Some(failure_reason),
216                Some(id),
217                Some(instructions_email),
218                Some(metadata),
219                Some(next_action),
220                Some(payment_intent),
221                Some(payment_method),
222                Some(pending_reason),
223                Some(presentment_details),
224                Some(reason),
225                Some(receipt_number),
226                Some(source_transfer_reversal),
227                Some(status),
228                Some(transfer_reversal),
229            ) = (
230                self.amount,
231                self.balance_transaction.take(),
232                self.charge.take(),
233                self.created,
234                self.currency.take(),
235                self.customer.take(),
236                self.customer_account.take(),
237                self.description.take(),
238                self.destination_details.take(),
239                self.failure_balance_transaction.take(),
240                self.failure_reason.take(),
241                self.id.take(),
242                self.instructions_email.take(),
243                self.metadata.take(),
244                self.next_action.take(),
245                self.payment_intent.take(),
246                self.payment_method.take(),
247                self.pending_reason.take(),
248                self.presentment_details.take(),
249                self.reason.take(),
250                self.receipt_number.take(),
251                self.source_transfer_reversal.take(),
252                self.status.take(),
253                self.transfer_reversal.take(),
254            )
255            else {
256                return None;
257            };
258            Some(Self::Out {
259                amount,
260                balance_transaction,
261                charge,
262                created,
263                currency,
264                customer,
265                customer_account,
266                description,
267                destination_details,
268                failure_balance_transaction,
269                failure_reason,
270                id,
271                instructions_email,
272                metadata,
273                next_action,
274                payment_intent,
275                payment_method,
276                pending_reason,
277                presentment_details,
278                reason,
279                receipt_number,
280                source_transfer_reversal,
281                status,
282                transfer_reversal,
283            })
284        }
285    }
286
287    impl Map for Builder<'_> {
288        fn key(&mut self, k: &str) -> Result<&mut dyn Visitor> {
289            self.builder.key(k)
290        }
291
292        fn finish(&mut self) -> Result<()> {
293            *self.out = self.builder.take_out();
294            Ok(())
295        }
296    }
297
298    impl ObjectDeser for Refund {
299        type Builder = RefundBuilder;
300    }
301
302    impl FromValueOpt for Refund {
303        fn from_value(v: Value) -> Option<Self> {
304            let Value::Object(obj) = v else {
305                return None;
306            };
307            let mut b = RefundBuilder::deser_default();
308            for (k, v) in obj {
309                match k.as_str() {
310                    "amount" => b.amount = FromValueOpt::from_value(v),
311                    "balance_transaction" => b.balance_transaction = FromValueOpt::from_value(v),
312                    "charge" => b.charge = FromValueOpt::from_value(v),
313                    "created" => b.created = FromValueOpt::from_value(v),
314                    "currency" => b.currency = FromValueOpt::from_value(v),
315                    "customer" => b.customer = FromValueOpt::from_value(v),
316                    "customer_account" => b.customer_account = FromValueOpt::from_value(v),
317                    "description" => b.description = FromValueOpt::from_value(v),
318                    "destination_details" => b.destination_details = FromValueOpt::from_value(v),
319                    "failure_balance_transaction" => {
320                        b.failure_balance_transaction = FromValueOpt::from_value(v)
321                    }
322                    "failure_reason" => b.failure_reason = FromValueOpt::from_value(v),
323                    "id" => b.id = FromValueOpt::from_value(v),
324                    "instructions_email" => b.instructions_email = FromValueOpt::from_value(v),
325                    "metadata" => b.metadata = FromValueOpt::from_value(v),
326                    "next_action" => b.next_action = FromValueOpt::from_value(v),
327                    "payment_intent" => b.payment_intent = FromValueOpt::from_value(v),
328                    "payment_method" => b.payment_method = FromValueOpt::from_value(v),
329                    "pending_reason" => b.pending_reason = FromValueOpt::from_value(v),
330                    "presentment_details" => b.presentment_details = FromValueOpt::from_value(v),
331                    "reason" => b.reason = FromValueOpt::from_value(v),
332                    "receipt_number" => b.receipt_number = FromValueOpt::from_value(v),
333                    "source_transfer_reversal" => {
334                        b.source_transfer_reversal = FromValueOpt::from_value(v)
335                    }
336                    "status" => b.status = FromValueOpt::from_value(v),
337                    "transfer_reversal" => b.transfer_reversal = FromValueOpt::from_value(v),
338                    _ => {}
339                }
340            }
341            b.take_out()
342        }
343    }
344};
345#[cfg(feature = "serialize")]
346impl serde::Serialize for Refund {
347    fn serialize<S: serde::Serializer>(&self, s: S) -> Result<S::Ok, S::Error> {
348        use serde::ser::SerializeStruct;
349        let mut s = s.serialize_struct("Refund", 25)?;
350        s.serialize_field("amount", &self.amount)?;
351        s.serialize_field("balance_transaction", &self.balance_transaction)?;
352        s.serialize_field("charge", &self.charge)?;
353        s.serialize_field("created", &self.created)?;
354        s.serialize_field("currency", &self.currency)?;
355        s.serialize_field("customer", &self.customer)?;
356        s.serialize_field("customer_account", &self.customer_account)?;
357        s.serialize_field("description", &self.description)?;
358        s.serialize_field("destination_details", &self.destination_details)?;
359        s.serialize_field("failure_balance_transaction", &self.failure_balance_transaction)?;
360        s.serialize_field("failure_reason", &self.failure_reason)?;
361        s.serialize_field("id", &self.id)?;
362        s.serialize_field("instructions_email", &self.instructions_email)?;
363        s.serialize_field("metadata", &self.metadata)?;
364        s.serialize_field("next_action", &self.next_action)?;
365        s.serialize_field("payment_intent", &self.payment_intent)?;
366        s.serialize_field("payment_method", &self.payment_method)?;
367        s.serialize_field("pending_reason", &self.pending_reason)?;
368        s.serialize_field("presentment_details", &self.presentment_details)?;
369        s.serialize_field("reason", &self.reason)?;
370        s.serialize_field("receipt_number", &self.receipt_number)?;
371        s.serialize_field("source_transfer_reversal", &self.source_transfer_reversal)?;
372        s.serialize_field("status", &self.status)?;
373        s.serialize_field("transfer_reversal", &self.transfer_reversal)?;
374
375        s.serialize_field("object", "refund")?;
376        s.end()
377    }
378}
379/// Provides the reason for why the refund is pending.
380/// Possible values are: `processing`, `insufficient_funds`, or `charge_pending`.
381#[derive(Clone, Eq, PartialEq)]
382#[non_exhaustive]
383pub enum RefundPendingReason {
384    ChargePending,
385    InsufficientFunds,
386    Processing,
387    /// An unrecognized value from Stripe. Should not be used as a request parameter.
388    Unknown(String),
389}
390impl RefundPendingReason {
391    pub fn as_str(&self) -> &str {
392        use RefundPendingReason::*;
393        match self {
394            ChargePending => "charge_pending",
395            InsufficientFunds => "insufficient_funds",
396            Processing => "processing",
397            Unknown(v) => v,
398        }
399    }
400}
401
402impl std::str::FromStr for RefundPendingReason {
403    type Err = std::convert::Infallible;
404    fn from_str(s: &str) -> Result<Self, Self::Err> {
405        use RefundPendingReason::*;
406        match s {
407            "charge_pending" => Ok(ChargePending),
408            "insufficient_funds" => Ok(InsufficientFunds),
409            "processing" => Ok(Processing),
410            v => {
411                tracing::warn!("Unknown value '{}' for enum '{}'", v, "RefundPendingReason");
412                Ok(Unknown(v.to_owned()))
413            }
414        }
415    }
416}
417impl std::fmt::Display for RefundPendingReason {
418    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
419        f.write_str(self.as_str())
420    }
421}
422
423#[cfg(not(feature = "redact-generated-debug"))]
424impl std::fmt::Debug for RefundPendingReason {
425    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
426        f.write_str(self.as_str())
427    }
428}
429#[cfg(feature = "redact-generated-debug")]
430impl std::fmt::Debug for RefundPendingReason {
431    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
432        f.debug_struct(stringify!(RefundPendingReason)).finish_non_exhaustive()
433    }
434}
435#[cfg(feature = "serialize")]
436impl serde::Serialize for RefundPendingReason {
437    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
438    where
439        S: serde::Serializer,
440    {
441        serializer.serialize_str(self.as_str())
442    }
443}
444impl miniserde::Deserialize for RefundPendingReason {
445    fn begin(out: &mut Option<Self>) -> &mut dyn miniserde::de::Visitor {
446        crate::Place::new(out)
447    }
448}
449
450impl miniserde::de::Visitor for crate::Place<RefundPendingReason> {
451    fn string(&mut self, s: &str) -> miniserde::Result<()> {
452        use std::str::FromStr;
453        self.out = Some(RefundPendingReason::from_str(s).expect("infallible"));
454        Ok(())
455    }
456}
457
458stripe_types::impl_from_val_with_from_str!(RefundPendingReason);
459#[cfg(feature = "deserialize")]
460impl<'de> serde::Deserialize<'de> for RefundPendingReason {
461    fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
462        use std::str::FromStr;
463        let s: std::borrow::Cow<'de, str> = serde::Deserialize::deserialize(deserializer)?;
464        Ok(Self::from_str(&s).expect("infallible"))
465    }
466}
467/// Reason for the refund, which is either user-provided (`duplicate`, `fraudulent`, or `requested_by_customer`) or generated by Stripe internally (`expired_uncaptured_charge`).
468#[derive(Clone, Eq, PartialEq)]
469#[non_exhaustive]
470pub enum RefundReason {
471    Duplicate,
472    ExpiredUncapturedCharge,
473    Fraudulent,
474    RequestedByCustomer,
475    /// An unrecognized value from Stripe. Should not be used as a request parameter.
476    Unknown(String),
477}
478impl RefundReason {
479    pub fn as_str(&self) -> &str {
480        use RefundReason::*;
481        match self {
482            Duplicate => "duplicate",
483            ExpiredUncapturedCharge => "expired_uncaptured_charge",
484            Fraudulent => "fraudulent",
485            RequestedByCustomer => "requested_by_customer",
486            Unknown(v) => v,
487        }
488    }
489}
490
491impl std::str::FromStr for RefundReason {
492    type Err = std::convert::Infallible;
493    fn from_str(s: &str) -> Result<Self, Self::Err> {
494        use RefundReason::*;
495        match s {
496            "duplicate" => Ok(Duplicate),
497            "expired_uncaptured_charge" => Ok(ExpiredUncapturedCharge),
498            "fraudulent" => Ok(Fraudulent),
499            "requested_by_customer" => Ok(RequestedByCustomer),
500            v => {
501                tracing::warn!("Unknown value '{}' for enum '{}'", v, "RefundReason");
502                Ok(Unknown(v.to_owned()))
503            }
504        }
505    }
506}
507impl std::fmt::Display for RefundReason {
508    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
509        f.write_str(self.as_str())
510    }
511}
512
513#[cfg(not(feature = "redact-generated-debug"))]
514impl std::fmt::Debug for RefundReason {
515    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
516        f.write_str(self.as_str())
517    }
518}
519#[cfg(feature = "redact-generated-debug")]
520impl std::fmt::Debug for RefundReason {
521    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
522        f.debug_struct(stringify!(RefundReason)).finish_non_exhaustive()
523    }
524}
525#[cfg(feature = "serialize")]
526impl serde::Serialize for RefundReason {
527    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
528    where
529        S: serde::Serializer,
530    {
531        serializer.serialize_str(self.as_str())
532    }
533}
534impl miniserde::Deserialize for RefundReason {
535    fn begin(out: &mut Option<Self>) -> &mut dyn miniserde::de::Visitor {
536        crate::Place::new(out)
537    }
538}
539
540impl miniserde::de::Visitor for crate::Place<RefundReason> {
541    fn string(&mut self, s: &str) -> miniserde::Result<()> {
542        use std::str::FromStr;
543        self.out = Some(RefundReason::from_str(s).expect("infallible"));
544        Ok(())
545    }
546}
547
548stripe_types::impl_from_val_with_from_str!(RefundReason);
549#[cfg(feature = "deserialize")]
550impl<'de> serde::Deserialize<'de> for RefundReason {
551    fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
552        use std::str::FromStr;
553        let s: std::borrow::Cow<'de, str> = serde::Deserialize::deserialize(deserializer)?;
554        Ok(Self::from_str(&s).expect("infallible"))
555    }
556}
557impl stripe_types::Object for Refund {
558    type Id = stripe_shared::RefundId;
559    fn id(&self) -> &Self::Id {
560        &self.id
561    }
562
563    fn into_id(self) -> Self::Id {
564        self.id
565    }
566}
567stripe_types::def_id!(RefundId);