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
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
use crate::client::endpoint::{EmptyResponseBody, Endpoint};
use crate::client::error::PayPalError;
use crate::client::paypal::Client;
use crate::resources::enums::order_intent::OrderIntent;
use crate::resources::enums::order_status::OrderStatus;
use crate::resources::enums::processing_instruction::ProcessingInstruction;
use crate::resources::link_description::LinkDescription;
use crate::resources::order_application_context::OrderApplicationContext;
use crate::resources::patch::Patch;
use crate::resources::payer::Payer;
use crate::resources::payment_source::PaymentSource;
use crate::resources::payment_source_response::PaymentSourceResponse;
use crate::resources::purchase_unit::PurchaseUnit;
use crate::resources::purchase_unit_request::PurchaseUnitRequest;
use reqwest::Method;
use serde::{Deserialize, Serialize};
use serde_with::skip_serializing_none;
use std::borrow::Cow;

#[skip_serializing_none]
#[derive(Clone, Debug, Default, Deserialize, Serialize)]
pub struct Order {
    /// The date and time when the transaction occurred, in Internet date and time format.
    pub create_time: Option<String>,

    /// The date and time when the transaction was last updated, in Internet date and time format.
    pub update_time: Option<String>,

    /// The ID of the order.
    pub id: Option<String>,

    /// The payment source used to fund the payment.
    pub payment_source: Option<PaymentSourceResponse>,

    /// The intent to either capture payment immediately or authorize a payment for an order after order creation.
    pub intent: Option<OrderIntent>,

    /// The customer who approves and pays for the order. The customer is also known as the payer.
    #[deprecated]
    pub payer: Option<Payer>,

    /// An array of purchase units. Each purchase unit establishes a contract between a customer and merchant. Each purchase unit
    /// represents either a full or partial order that the customer intends to purchase from the merchant.
    pub purchase_units: Option<Vec<PurchaseUnit>>,

    /// The instruction to process an order.
    pub processing_instruction: Option<ProcessingInstruction>,

    /// The order status.
    pub status: Option<OrderStatus>,

    /// An array of request-related HATEOAS links. To complete payer approval, use the approve link to redirect the payer.
    /// The API caller has 3 hours (default setting, this which can be changed by your account manager to
    /// 24/48/72 hours to accommodate your use case) from the time the order is created, to redirect your payer.
    /// Once redirected, the API caller has 3 hours for the payer to approve the order and either authorize or capture the order.
    /// If you are not using the PayPal JavaScript SDK to initiate PayPal Checkout (in context) ensure that you include
    /// application_context.return_url is specified or you will get "We're sorry, Things don't appear to be working at the moment"
    /// after the payer approves the payment.
    pub links: Option<Vec<LinkDescription>>,
}

impl Order {
    /// Creates an order.
    pub async fn create(client: &mut Client, dto: CreateOrderDto) -> Result<Order, PayPalError> {
        client.post(&CreateOrder::new(dto)).await
    }

    /// Shows details for an order, by ID.
    pub async fn show_details(client: &mut Client, id: &str) -> Result<Order, PayPalError> {
        client.get(&ShowOrderDetails::new(id.to_string())).await
    }

    /// Updates an order with a CREATED or APPROVED status. You cannot update an order with the COMPLETED status.
    ///
    /// To make an update, you must provide a reference_id. If you omit this value with an order
    /// that contains only one purchase unit, PayPal sets the value to default which enables you to
    /// use the path: "/purchase_units/@reference_id=='default'/{attribute-or-object}"
    pub async fn patch(
        client: &mut Client,
        id: &str,
        dto: PatchOrderDto,
    ) -> Result<(), PayPalError> {
        client.patch(&PatchOrder::new(id.to_string(), dto)).await
    }

    /// Authorizes payment for an order. To successfully authorize payment for an order, the buyer
    /// must first approve the order or a valid payment_source must be provided in the request.
    /// A buyer can approve the order upon being redirected to the rel:approve URL that was returned
    /// in the HATEOAS links in the create order response.
    pub async fn authorize_payment(
        client: &mut Client,
        id: &str,
    ) -> Result<AuthorizePaymentForOrderResponse, PayPalError> {
        client
            .post(&AuthorizePaymentForOrder::new(id.to_string()))
            .await
    }
}

#[skip_serializing_none]
#[derive(Clone, Debug, Serialize)]
pub struct CreateOrderDto {
    /// The intent to either capture payment immediately or authorize a payment for an order after order creation.
    ///
    /// The possible values are:
    /// - CAPTURE. The merchant intends to capture payment immediately after the customer makes a payment.
    /// - AUTHORIZE. The merchant intends to authorize a payment and place funds on hold after the customer makes a payment.
    ///  Authorized payments are best captured within three days of authorization but are available to capture for up to 29 days.
    ///  After the three-day honor period, the original authorized payment expires and you must re-authorize the payment.
    ///  You must make a separate request to capture payments on demand. This intent is not supported when you have more than one
    ///  `purchase_unit` within your order.
    pub intent: OrderIntent,

    /// The customer who approves and pays for the order. The customer is also known as the payer.
    pub payer: Option<Payer>,

    /// An array of purchase units. Each purchase unit establishes a contract between a payer and the payee. Each purchase unit represents
    /// either a full or partial order that the payer intends to purchase from the payee.
    pub purchase_units: Vec<PurchaseUnitRequest>,

    /// Customize the payer experience during the approval process for the payment with PayPal.
    pub application_context: Option<OrderApplicationContext>,
}

#[derive(Debug)]
struct CreateOrder {
    pub order: CreateOrderDto,
}

impl CreateOrder {
    pub fn new(order: CreateOrderDto) -> Self {
        Self { order }
    }
}

impl Endpoint for CreateOrder {
    type QueryParams = ();
    type RequestBody = CreateOrderDto;
    type ResponseBody = Order;

    fn path(&self) -> Cow<str> {
        Cow::Borrowed("v2/checkout/orders")
    }

    fn request_body(&self) -> Option<Self::RequestBody> {
        Some(self.order.clone())
    }

    fn request_method(&self) -> Method {
        Method::POST
    }
}

#[derive(Debug)]
struct ShowOrderDetails {
    /// The ID of the order for which to show details.
    order_id: String,
}

impl ShowOrderDetails {
    pub fn new(order_id: String) -> Self {
        Self { order_id }
    }
}

impl Endpoint for ShowOrderDetails {
    type QueryParams = ();
    type RequestBody = ();
    type ResponseBody = Order;

    fn path(&self) -> Cow<str> {
        Cow::Owned(format!("v2/checkout/orders/{}", self.order_id))
    }
}

#[derive(Debug)]
pub struct PatchOrderDto {
    pub patch: Vec<Patch>,
}

type PatchOrderResponse = EmptyResponseBody;

#[derive(Debug)]
pub struct PatchOrder {
    order_id: String,
    order: PatchOrderDto,
}

impl PatchOrder {
    pub fn new(order_id: String, order: PatchOrderDto) -> Self {
        Self { order_id, order }
    }
}

impl Endpoint for PatchOrder {
    type QueryParams = ();
    type RequestBody = Vec<Patch>;
    type ResponseBody = PatchOrderResponse;

    fn path(&self) -> Cow<str> {
        Cow::Owned(format!("v2/checkout/orders/{}", self.order_id))
    }

    fn request_body(&self) -> Option<Self::RequestBody> {
        Some(self.order.patch.clone())
    }

    fn request_method(&self) -> Method {
        Method::PATCH
    }
}

/// Authorizes payment for an order. To successfully authorize payment for an order, the buyer must
/// first approve the order or a valid payment_source must be provided in the request.
/// A buyer can approve the order upon being redirected to the rel:approve URL that was returned in
/// the HATEOAS links in the create order response.
#[derive(Debug)]
struct AuthorizePaymentForOrder {
    /// The ID of the order for which to authorize.
    order_id: String,
}

impl AuthorizePaymentForOrder {
    pub fn new(order_id: String) -> Self {
        Self { order_id }
    }
}

#[skip_serializing_none]
#[derive(Debug, Deserialize, Default)]
pub struct AuthorizePaymentForOrderResponse {
    /// The date and time when the transaction occurred, in Internet date and time format.
    pub create_time: Option<String>,

    /// The date and time when the transaction occurred, in Internet date and time format.
    pub update_time: Option<String>,

    /// The ID of the order.
    pub id: Option<String>,

    /// The payment source used to fund the payment.
    pub payment_source: Option<PaymentSourceResponse>,

    /// The intent to either capture payment immediately or authorize a payment for an order after order creation.
    pub intent: Option<OrderIntent>,

    /// The customer who approves and pays for the order. The customer is also known as the payer.
    pub payer: Option<Payer>,

    /// An array of purchase units. Each purchase unit establishes a contract between a customer and merchant. Each purchase unit
    /// represents either a full or partial order that the customer intends to purchase from the merchant.
    pub purchase_units: Option<Vec<PurchaseUnit>>,

    /// The order status.r.
    pub status: Option<OrderStatus>,

    /// An array of request-related HATEOAS links. To complete payer approval, use the approve link to redirect the payer.
    /// The API caller has 3 hours (default setting, this which can be changed by your account manager to
    /// 24/48/72 hours to accommodate your use case) from the time the order is created, to redirect your payer.
    /// Once redirected, the API caller has 3 hours for the payer to approve the order and either authorize or capture the order.
    /// If you are not using the PayPal JavaScript SDK to initiate PayPal Checkout (in context) ensure that you include
    /// application_context.return_url is specified or you will get "We're sorry, Things don't appear to be working at the moment"
    /// after the payer approves the payment.
    pub links: Option<Vec<LinkDescription>>,
}

impl Endpoint for AuthorizePaymentForOrder {
    type QueryParams = ();
    type RequestBody = ();
    type ResponseBody = AuthorizePaymentForOrderResponse;

    fn path(&self) -> Cow<str> {
        Cow::Owned(format!("v2/checkout/orders/{}/authorize", self.order_id))
    }

    fn request_method(&self) -> Method {
        Method::POST
    }
}

struct CapturePaymentForOrder {
    /// The ID of the order for which to capture.
    order_id: String,

    /// The payment source definition
    payment_source: Option<PaymentSource>,
}

#[skip_serializing_none]
#[derive(Debug, Deserialize, Default)]
pub struct CapturePaymentForOrderResponse {
    /// The date and time when the transaction occurred, in Internet date and time format.
    pub create_time: Option<String>,

    /// The date and time when the transaction occurred, in Internet date and time format.
    pub update_time: Option<String>,

    /// The ID of the order.
    pub id: Option<String>,

    /// The payment source used to fund the payment.
    pub payment_source: Option<PaymentSourceResponse>,

    /// The intent to either capture payment immediately or authorize a payment for an order after order creation.
    pub intent: Option<OrderIntent>,

    /// The customer who approves and pays for the order. The customer is also known as the payer.
    pub payer: Option<Payer>,

    /// An array of purchase units. Each purchase unit establishes a contract between a customer and merchant. Each purchase unit
    /// represents either a full or partial order that the customer intends to purchase from the merchant.
    pub purchase_units: Option<Vec<PurchaseUnit>>,

    /// The order status.r.
    pub status: Option<OrderStatus>,

    /// An array of request-related HATEOAS links. To complete payer approval, use the approve link to redirect the payer.
    /// The API caller has 3 hours (default setting, this which can be changed by your account manager to
    /// 24/48/72 hours to accommodate your use case) from the time the order is created, to redirect your payer.
    /// Once redirected, the API caller has 3 hours for the payer to approve the order and either authorize or capture the order.
    /// If you are not using the PayPal JavaScript SDK to initiate PayPal Checkout (in context) ensure that you include
    /// application_context.return_url is specified or you will get "We're sorry, Things don't appear to be working at the moment"
    /// after the payer approves the payment.
    pub links: Option<Vec<LinkDescription>>,
}

impl Endpoint for CapturePaymentForOrder {
    type QueryParams = ();
    type RequestBody = Option<PaymentSource>;
    type ResponseBody = CapturePaymentForOrderResponse;

    fn path(&self) -> Cow<str> {
        Cow::Owned(format!("v2/checkout/orders/{}/capture", self.order_id))
    }

    fn request_body(&self) -> Option<Self::RequestBody> {
        Some(self.payment_source.clone())
    }

    fn request_method(&self) -> Method {
        Method::POST
    }
}