1use serde::{Deserialize, Serialize};
2
3use crate::domain::ProtocolExtensions;
4use crate::kernel::PaymentsKernelError;
5
6#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
8#[serde(rename_all = "snake_case")]
9pub enum OrderState {
10 Draft,
11 PendingPayment,
12 Authorized,
13 FulfillmentPending,
14 Fulfilled,
15 Completed,
16 Canceled,
17 Refunded,
18 PartiallyRefunded,
19 Failed,
20}
21
22impl OrderState {
23 #[must_use]
26 pub fn can_transition_to(self, next: Self) -> bool {
27 use OrderState::{
28 Authorized, Canceled, Completed, Draft, Failed, Fulfilled, FulfillmentPending,
29 PartiallyRefunded, PendingPayment, Refunded,
30 };
31
32 match (self, next) {
33 (from, to) if from == to => true,
34 (Draft, PendingPayment | Canceled | Failed) => true,
35 (PendingPayment, Authorized | Canceled | Failed) => true,
36 (Authorized, FulfillmentPending | Completed | Canceled | Failed) => true,
37 (FulfillmentPending, Fulfilled | Completed | Failed) => true,
38 (Fulfilled, Completed | PartiallyRefunded | Refunded | Failed) => true,
39 (Completed, PartiallyRefunded | Refunded) => true,
40 (PartiallyRefunded, PartiallyRefunded | Refunded) => true,
41 _ => false,
42 }
43 }
44}
45
46#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
48#[serde(rename_all = "snake_case")]
49pub enum ReceiptState {
50 NotRequested,
51 Pending,
52 Authorized,
53 Settled,
54 Failed,
55 PartiallyRefunded,
56 Refunded,
57 Voided,
58}
59
60impl ReceiptState {
61 #[must_use]
64 pub fn can_transition_to(self, next: Self) -> bool {
65 use ReceiptState::{
66 Authorized, Failed, NotRequested, PartiallyRefunded, Pending, Refunded, Settled, Voided,
67 };
68
69 match (self, next) {
70 (from, to) if from == to => true,
71 (NotRequested, Pending | Authorized | Failed) => true,
72 (Pending, Authorized | Settled | Failed | Voided) => true,
73 (Authorized, Settled | Failed | Voided) => true,
74 (Settled, PartiallyRefunded | Refunded) => true,
75 (PartiallyRefunded, PartiallyRefunded | Refunded) => true,
76 _ => false,
77 }
78 }
79}
80
81#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
83#[serde(rename_all = "camelCase")]
84pub struct OrderSnapshot {
85 #[serde(default, skip_serializing_if = "Option::is_none")]
86 pub order_id: Option<String>,
87 #[serde(default, skip_serializing_if = "Option::is_none")]
88 pub receipt_id: Option<String>,
89 pub state: OrderState,
90 pub receipt_state: ReceiptState,
91 #[serde(default, skip_serializing_if = "ProtocolExtensions::is_empty")]
92 pub extensions: ProtocolExtensions,
93}
94
95impl OrderSnapshot {
96 pub fn transition_order_state(
103 &mut self,
104 next: OrderState,
105 ) -> std::result::Result<(), PaymentsKernelError> {
106 if !self.state.can_transition_to(next) {
107 return Err(PaymentsKernelError::InvalidOrderTransition { from: self.state, to: next });
108 }
109
110 self.state = next;
111 Ok(())
112 }
113
114 pub fn transition_receipt_state(
121 &mut self,
122 next: ReceiptState,
123 ) -> std::result::Result<(), PaymentsKernelError> {
124 if !self.receipt_state.can_transition_to(next) {
125 return Err(PaymentsKernelError::InvalidReceiptTransition {
126 from: self.receipt_state,
127 to: next,
128 });
129 }
130
131 self.receipt_state = next;
132 Ok(())
133 }
134}