1use crate::client::{Client, Response};
6use crate::ids::{ChargeId, CustomerId, PaymentIntentId, RefundId};
7use crate::params::{Expand, Expandable, List, Metadata, Object, Paginable, RangeQuery, Timestamp};
8use crate::resources::{BalanceTransaction, Charge, Currency, PaymentIntent, TransferReversal};
9use serde::{Deserialize, Serialize};
10
11#[derive(Clone, Debug, Default, Deserialize, Serialize)]
15pub struct Refund {
16 pub id: RefundId,
18
19 pub amount: i64,
21
22 pub balance_transaction: Option<Expandable<BalanceTransaction>>,
24
25 pub charge: Option<Expandable<Charge>>,
27
28 pub created: Timestamp,
32
33 pub currency: Currency,
37
38 #[serde(skip_serializing_if = "Option::is_none")]
42 pub description: Option<String>,
43
44 #[serde(skip_serializing_if = "Option::is_none")]
45 pub destination_details: Option<RefundDestinationDetails>,
46
47 #[serde(skip_serializing_if = "Option::is_none")]
49 pub failure_balance_transaction: Option<Expandable<BalanceTransaction>>,
50
51 #[serde(skip_serializing_if = "Option::is_none")]
55 pub failure_reason: Option<String>,
56
57 #[serde(skip_serializing_if = "Option::is_none")]
59 pub instructions_email: Option<String>,
60
61 pub metadata: Option<Metadata>,
65
66 #[serde(skip_serializing_if = "Option::is_none")]
67 pub next_action: Option<RefundNextAction>,
68
69 pub payment_intent: Option<Expandable<PaymentIntent>>,
71
72 pub reason: Option<RefundReason>,
74
75 pub receipt_number: Option<String>,
77
78 pub source_transfer_reversal: Option<Expandable<TransferReversal>>,
82
83 pub status: Option<String>,
88
89 pub transfer_reversal: Option<Expandable<TransferReversal>>,
93}
94
95impl Refund {
96 pub fn list(client: &Client, params: &ListRefunds<'_>) -> Response<List<Refund>> {
100 client.get_query("/refunds", params)
101 }
102
103 pub fn create(client: &Client, params: CreateRefund<'_>) -> Response<Refund> {
115 #[allow(clippy::needless_borrows_for_generic_args)]
116 client.post_form("/refunds", ¶ms)
117 }
118
119 pub fn retrieve(client: &Client, id: &RefundId, expand: &[&str]) -> Response<Refund> {
121 client.get_query(&format!("/refunds/{}", id), Expand { expand })
122 }
123
124 pub fn update(client: &Client, id: &RefundId, params: UpdateRefund<'_>) -> Response<Refund> {
128 #[allow(clippy::needless_borrows_for_generic_args)]
129 client.post_form(&format!("/refunds/{}", id), ¶ms)
130 }
131}
132
133impl Object for Refund {
134 type Id = RefundId;
135 fn id(&self) -> Self::Id {
136 self.id.clone()
137 }
138 fn object(&self) -> &'static str {
139 "refund"
140 }
141}
142
143#[derive(Clone, Debug, Default, Deserialize, Serialize)]
144pub struct RefundDestinationDetails {
145 #[serde(skip_serializing_if = "Option::is_none")]
146 pub affirm: Option<DestinationDetailsUnimplemented>,
147
148 #[serde(skip_serializing_if = "Option::is_none")]
149 pub afterpay_clearpay: Option<DestinationDetailsUnimplemented>,
150
151 #[serde(skip_serializing_if = "Option::is_none")]
152 pub alipay: Option<DestinationDetailsUnimplemented>,
153
154 #[serde(skip_serializing_if = "Option::is_none")]
155 pub au_bank_transfer: Option<DestinationDetailsUnimplemented>,
156
157 #[serde(skip_serializing_if = "Option::is_none")]
158 pub blik: Option<RefundDestinationDetailsGeneric>,
159
160 #[serde(skip_serializing_if = "Option::is_none")]
161 pub br_bank_transfer: Option<RefundDestinationDetailsGeneric>,
162
163 #[serde(skip_serializing_if = "Option::is_none")]
164 pub card: Option<RefundDestinationDetailsCard>,
165
166 #[serde(skip_serializing_if = "Option::is_none")]
167 pub cashapp: Option<DestinationDetailsUnimplemented>,
168
169 #[serde(skip_serializing_if = "Option::is_none")]
170 pub customer_cash_balance: Option<DestinationDetailsUnimplemented>,
171
172 #[serde(skip_serializing_if = "Option::is_none")]
173 pub eps: Option<DestinationDetailsUnimplemented>,
174
175 #[serde(skip_serializing_if = "Option::is_none")]
176 pub eu_bank_transfer: Option<RefundDestinationDetailsGeneric>,
177
178 #[serde(skip_serializing_if = "Option::is_none")]
179 pub gb_bank_transfer: Option<RefundDestinationDetailsGeneric>,
180
181 #[serde(skip_serializing_if = "Option::is_none")]
182 pub giropay: Option<DestinationDetailsUnimplemented>,
183
184 #[serde(skip_serializing_if = "Option::is_none")]
185 pub grabpay: Option<DestinationDetailsUnimplemented>,
186
187 #[serde(skip_serializing_if = "Option::is_none")]
188 pub jp_bank_transfer: Option<RefundDestinationDetailsGeneric>,
189
190 #[serde(skip_serializing_if = "Option::is_none")]
191 pub klarna: Option<DestinationDetailsUnimplemented>,
192
193 #[serde(skip_serializing_if = "Option::is_none")]
194 pub mx_bank_transfer: Option<RefundDestinationDetailsGeneric>,
195
196 #[serde(skip_serializing_if = "Option::is_none")]
197 pub p24: Option<RefundDestinationDetailsGeneric>,
198
199 #[serde(skip_serializing_if = "Option::is_none")]
200 pub paynow: Option<DestinationDetailsUnimplemented>,
201
202 #[serde(skip_serializing_if = "Option::is_none")]
203 pub paypal: Option<DestinationDetailsUnimplemented>,
204
205 #[serde(skip_serializing_if = "Option::is_none")]
206 pub pix: Option<DestinationDetailsUnimplemented>,
207
208 #[serde(skip_serializing_if = "Option::is_none")]
209 pub revolut: Option<DestinationDetailsUnimplemented>,
210
211 #[serde(skip_serializing_if = "Option::is_none")]
212 pub sofort: Option<DestinationDetailsUnimplemented>,
213
214 #[serde(skip_serializing_if = "Option::is_none")]
215 pub swish: Option<RefundDestinationDetailsGeneric>,
216
217 #[serde(skip_serializing_if = "Option::is_none")]
218 pub th_bank_transfer: Option<RefundDestinationDetailsGeneric>,
219
220 #[serde(rename = "type")]
225 pub type_: String,
226
227 #[serde(skip_serializing_if = "Option::is_none")]
228 pub us_bank_transfer: Option<RefundDestinationDetailsGeneric>,
229
230 #[serde(skip_serializing_if = "Option::is_none")]
231 pub wechat_pay: Option<DestinationDetailsUnimplemented>,
232
233 #[serde(skip_serializing_if = "Option::is_none")]
234 pub zip: Option<DestinationDetailsUnimplemented>,
235}
236
237#[derive(Clone, Debug, Default, Deserialize, Serialize)]
238pub struct DestinationDetailsUnimplemented {}
239
240#[derive(Clone, Debug, Default, Deserialize, Serialize)]
241pub struct RefundDestinationDetailsCard {
242 #[serde(skip_serializing_if = "Option::is_none")]
244 pub reference: Option<String>,
245
246 #[serde(skip_serializing_if = "Option::is_none")]
250 pub reference_status: Option<String>,
251
252 #[serde(skip_serializing_if = "Option::is_none")]
254 pub reference_type: Option<String>,
255
256 #[serde(rename = "type")]
260 pub type_: RefundDestinationDetailsCardType,
261}
262
263#[derive(Clone, Debug, Default, Deserialize, Serialize)]
264pub struct RefundDestinationDetailsGeneric {
265 pub reference: Option<String>,
267
268 pub reference_status: Option<String>,
272}
273
274#[derive(Clone, Debug, Default, Deserialize, Serialize)]
275pub struct RefundNextAction {
276 pub display_details: Option<RefundNextActionDisplayDetails>,
278
279 #[serde(rename = "type")]
281 pub type_: String,
282}
283
284#[derive(Clone, Debug, Default, Deserialize, Serialize)]
285pub struct RefundNextActionDisplayDetails {
286 pub email_sent: EmailSent,
287
288 pub expires_at: Timestamp,
290}
291
292#[derive(Clone, Debug, Default, Deserialize, Serialize)]
293pub struct EmailSent {
294 pub email_sent_at: Timestamp,
296
297 pub email_sent_to: String,
299}
300
301#[derive(Clone, Debug, Serialize, Default)]
303pub struct CreateRefund<'a> {
304 #[serde(skip_serializing_if = "Option::is_none")]
305 pub amount: Option<i64>,
306
307 #[serde(skip_serializing_if = "Option::is_none")]
309 pub charge: Option<ChargeId>,
310
311 #[serde(skip_serializing_if = "Option::is_none")]
315 pub currency: Option<Currency>,
316
317 #[serde(skip_serializing_if = "Option::is_none")]
319 pub customer: Option<CustomerId>,
320
321 #[serde(skip_serializing_if = "Expand::is_empty")]
323 pub expand: &'a [&'a str],
324
325 #[serde(skip_serializing_if = "Option::is_none")]
327 pub instructions_email: Option<&'a str>,
328
329 #[serde(skip_serializing_if = "Option::is_none")]
335 pub metadata: Option<Metadata>,
336
337 #[serde(skip_serializing_if = "Option::is_none")]
339 pub origin: Option<RefundOrigin>,
340
341 #[serde(skip_serializing_if = "Option::is_none")]
343 pub payment_intent: Option<PaymentIntentId>,
344
345 #[serde(skip_serializing_if = "Option::is_none")]
350 pub reason: Option<RefundReasonFilter>,
351
352 #[serde(skip_serializing_if = "Option::is_none")]
358 pub refund_application_fee: Option<bool>,
359
360 #[serde(skip_serializing_if = "Option::is_none")]
364 pub reverse_transfer: Option<bool>,
365}
366
367impl<'a> CreateRefund<'a> {
368 pub fn new() -> Self {
369 CreateRefund {
370 amount: Default::default(),
371 charge: Default::default(),
372 currency: Default::default(),
373 customer: Default::default(),
374 expand: Default::default(),
375 instructions_email: Default::default(),
376 metadata: Default::default(),
377 origin: Default::default(),
378 payment_intent: Default::default(),
379 reason: Default::default(),
380 refund_application_fee: Default::default(),
381 reverse_transfer: Default::default(),
382 }
383 }
384}
385
386#[derive(Clone, Debug, Serialize, Default)]
388pub struct ListRefunds<'a> {
389 #[serde(skip_serializing_if = "Option::is_none")]
391 pub charge: Option<ChargeId>,
392
393 #[serde(skip_serializing_if = "Option::is_none")]
394 pub created: Option<RangeQuery<Timestamp>>,
395
396 #[serde(skip_serializing_if = "Option::is_none")]
401 pub ending_before: Option<RefundId>,
402
403 #[serde(skip_serializing_if = "Expand::is_empty")]
405 pub expand: &'a [&'a str],
406
407 #[serde(skip_serializing_if = "Option::is_none")]
411 pub limit: Option<u64>,
412
413 #[serde(skip_serializing_if = "Option::is_none")]
415 pub payment_intent: Option<PaymentIntentId>,
416
417 #[serde(skip_serializing_if = "Option::is_none")]
422 pub starting_after: Option<RefundId>,
423}
424
425impl<'a> ListRefunds<'a> {
426 pub fn new() -> Self {
427 ListRefunds {
428 charge: Default::default(),
429 created: Default::default(),
430 ending_before: Default::default(),
431 expand: Default::default(),
432 limit: Default::default(),
433 payment_intent: Default::default(),
434 starting_after: Default::default(),
435 }
436 }
437}
438impl Paginable for ListRefunds<'_> {
439 type O = Refund;
440 fn set_last(&mut self, item: Self::O) {
441 self.starting_after = Some(item.id());
442 }
443}
444#[derive(Clone, Debug, Serialize, Default)]
446pub struct UpdateRefund<'a> {
447 #[serde(skip_serializing_if = "Expand::is_empty")]
449 pub expand: &'a [&'a str],
450
451 #[serde(skip_serializing_if = "Option::is_none")]
457 pub metadata: Option<Metadata>,
458}
459
460impl<'a> UpdateRefund<'a> {
461 pub fn new() -> Self {
462 UpdateRefund { expand: Default::default(), metadata: Default::default() }
463 }
464}
465
466#[derive(Copy, Clone, Debug, Deserialize, Serialize, Eq, PartialEq)]
468#[serde(rename_all = "snake_case")]
469pub enum RefundDestinationDetailsCardType {
470 Pending,
471 Refund,
472 Reversal,
473}
474
475impl RefundDestinationDetailsCardType {
476 pub fn as_str(self) -> &'static str {
477 match self {
478 RefundDestinationDetailsCardType::Pending => "pending",
479 RefundDestinationDetailsCardType::Refund => "refund",
480 RefundDestinationDetailsCardType::Reversal => "reversal",
481 }
482 }
483}
484
485impl AsRef<str> for RefundDestinationDetailsCardType {
486 fn as_ref(&self) -> &str {
487 self.as_str()
488 }
489}
490
491impl std::fmt::Display for RefundDestinationDetailsCardType {
492 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
493 self.as_str().fmt(f)
494 }
495}
496impl std::default::Default for RefundDestinationDetailsCardType {
497 fn default() -> Self {
498 Self::Pending
499 }
500}
501
502#[derive(Copy, Clone, Debug, Deserialize, Serialize, Eq, PartialEq)]
504#[serde(rename_all = "snake_case")]
505pub enum RefundOrigin {
506 CustomerBalance,
507}
508
509impl RefundOrigin {
510 pub fn as_str(self) -> &'static str {
511 match self {
512 RefundOrigin::CustomerBalance => "customer_balance",
513 }
514 }
515}
516
517impl AsRef<str> for RefundOrigin {
518 fn as_ref(&self) -> &str {
519 self.as_str()
520 }
521}
522
523impl std::fmt::Display for RefundOrigin {
524 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
525 self.as_str().fmt(f)
526 }
527}
528impl std::default::Default for RefundOrigin {
529 fn default() -> Self {
530 Self::CustomerBalance
531 }
532}
533
534#[derive(Copy, Clone, Debug, Deserialize, Serialize, Eq, PartialEq)]
536#[serde(rename_all = "snake_case")]
537pub enum RefundReason {
538 Duplicate,
539 ExpiredUncapturedCharge,
540 Fraudulent,
541 RequestedByCustomer,
542}
543
544impl RefundReason {
545 pub fn as_str(self) -> &'static str {
546 match self {
547 RefundReason::Duplicate => "duplicate",
548 RefundReason::ExpiredUncapturedCharge => "expired_uncaptured_charge",
549 RefundReason::Fraudulent => "fraudulent",
550 RefundReason::RequestedByCustomer => "requested_by_customer",
551 }
552 }
553}
554
555impl AsRef<str> for RefundReason {
556 fn as_ref(&self) -> &str {
557 self.as_str()
558 }
559}
560
561impl std::fmt::Display for RefundReason {
562 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
563 self.as_str().fmt(f)
564 }
565}
566impl std::default::Default for RefundReason {
567 fn default() -> Self {
568 Self::Duplicate
569 }
570}
571
572#[derive(Copy, Clone, Debug, Deserialize, Serialize, Eq, PartialEq)]
574#[serde(rename_all = "snake_case")]
575pub enum RefundReasonFilter {
576 Duplicate,
577 Fraudulent,
578 RequestedByCustomer,
579}
580
581impl RefundReasonFilter {
582 pub fn as_str(self) -> &'static str {
583 match self {
584 RefundReasonFilter::Duplicate => "duplicate",
585 RefundReasonFilter::Fraudulent => "fraudulent",
586 RefundReasonFilter::RequestedByCustomer => "requested_by_customer",
587 }
588 }
589}
590
591impl AsRef<str> for RefundReasonFilter {
592 fn as_ref(&self) -> &str {
593 self.as_str()
594 }
595}
596
597impl std::fmt::Display for RefundReasonFilter {
598 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
599 self.as_str().fmt(f)
600 }
601}
602impl std::default::Default for RefundReasonFilter {
603 fn default() -> Self {
604 Self::Duplicate
605 }
606}