diff --git a/src/api/invoice.rs b/src/api/invoice.rs index 61e0616..87fa668 100644 --- a/src/api/invoice.rs +++ b/src/api/invoice.rs @@ -4,7 +4,7 @@ //! Customers with a PayPal account can log in and pay the invoice with PayPal. Alternatively, //! customers can pay as a guest with a debit card or credit card. For more information, see the Invoicing Overview and the Invoicing Integration Guide. //! -//! Reference: https://developer.paypal.com/docs/api/invoicing/v2/ +//! Reference: use std::borrow::Cow; @@ -53,8 +53,8 @@ impl Endpoint for GenerateInvoiceNumber { reqwest::Method::POST } - fn body(&self) -> Option<&Self::Body> { - Some(&self.invoice_number) + fn body(&self) -> Option { + Some(self.invoice_number.clone()) } } @@ -88,8 +88,8 @@ impl Endpoint for CreateDraftInvoice { reqwest::Method::POST } - fn body(&self) -> Option<&Self::Body> { - Some(&self.invoice) + fn body(&self) -> Option { + Some(self.invoice.clone()) } } @@ -153,8 +153,8 @@ impl Endpoint for ListInvoices { reqwest::Method::GET } - fn query(&self) -> Option<&Self::Query> { - Some(&self.query) + fn query(&self) -> Option { + Some(self.query.clone()) } } @@ -234,12 +234,12 @@ impl Endpoint for UpdateInvoice { reqwest::Method::PUT } - fn body(&self) -> Option<&Self::Body> { - Some(&self.invoice) + fn body(&self) -> Option { + Some(self.invoice.clone()) } - fn query(&self) -> Option<&Self::Query> { - Some(&self.query) + fn query(&self) -> Option { + Some(self.query.clone()) } } @@ -274,8 +274,8 @@ impl Endpoint for CancelInvoice { reqwest::Method::POST } - fn body(&self) -> Option<&Self::Body> { - Some(&self.reason) + fn body(&self) -> Option { + Some(self.reason.clone()) } } @@ -383,7 +383,7 @@ mod tests { let invoice = CreateDraftInvoice::new(payload); - let res = client.execute(&invoice).await?; + let _res = client.execute(&invoice).await?; Ok(()) } } diff --git a/src/api/mod.rs b/src/api/mod.rs index dff5ee0..54be66e 100644 --- a/src/api/mod.rs +++ b/src/api/mod.rs @@ -2,4 +2,4 @@ pub mod invoice; pub mod orders; -pub mod payments; +pub mod payments; \ No newline at end of file diff --git a/src/api/orders.rs b/src/api/orders.rs index 63f8b0d..7793d0d 100644 --- a/src/api/orders.rs +++ b/src/api/orders.rs @@ -1,6 +1,6 @@ //! An order represents a payment between two or more parties. Use the Orders API to create, update, retrieve, authorize, and capture orders. //! -//! https://developer.paypal.com/docs/api/orders/v2/ +//! use std::borrow::Cow; @@ -41,8 +41,8 @@ impl Endpoint for CreateOrder { reqwest::Method::POST } - fn body(&self) -> Option<&Self::Body> { - Some(&self.order) + fn body(&self) -> Option { + Some(self.order.clone()) } } @@ -97,10 +97,11 @@ pub struct PaymentSource { } /// The capture order endpoint body. -#[derive(Debug, Serialize, Clone)] +#[derive(Debug, Serialize, Clone, Default)] pub struct PaymentSourceBody { /// The payment source definition. - pub payment_source: PaymentSource, + #[serde(skip_serializing_if = "Option::is_none")] + pub payment_source: Option, } /// Captures payment for an order. To successfully capture payment for an order, @@ -111,7 +112,7 @@ pub struct CaptureOrder { /// The id of the order. pub order_id: String, /// The endpoint body. - pub body: Option, + pub body: PaymentSourceBody, } impl CaptureOrder { @@ -119,7 +120,7 @@ impl CaptureOrder { pub fn new(order_id: &str) -> Self { Self { order_id: order_id.to_string(), - body: None, + body: PaymentSourceBody::default(), } } } @@ -139,8 +140,8 @@ impl Endpoint for CaptureOrder { reqwest::Method::POST } - fn body(&self) -> Option<&Self::Body> { - self.body.as_ref() + fn body(&self) -> Option { + Some(self.body.clone()) } } @@ -152,7 +153,7 @@ pub struct AuthorizeOrder { /// The order id. order_id: String, /// The endpoint body. - pub body: Option, + pub body: PaymentSourceBody, } impl AuthorizeOrder { @@ -160,7 +161,7 @@ impl AuthorizeOrder { pub fn new(order_id: &str) -> Self { Self { order_id: order_id.to_string(), - body: None, + body: PaymentSourceBody::default(), } } } @@ -180,8 +181,8 @@ impl Endpoint for AuthorizeOrder { reqwest::Method::POST } - fn body(&self) -> Option<&Self::Body> { - self.body.as_ref() + fn body(&self) -> Option { + Some(self.body.clone()) } } @@ -194,7 +195,7 @@ mod tests { #[tokio::test] async fn test_order() -> anyhow::Result<()> { let mut client = create_client().await; - client.get_access_token().await?; + client.get_access_token().await.expect("get access token error"); let order = OrderPayloadBuilder::default() .intent(Intent::Authorize) @@ -219,7 +220,11 @@ mod tests { ..Default::default() }, ) - .await?; + .await; + + assert!(order_created.is_ok()); + + let order_created = order_created?; assert_ne!(order_created.id, ""); assert_eq!(order_created.status, OrderStatus::Created); @@ -235,14 +240,19 @@ mod tests { ..Default::default() }, ) - .await?; + .await; + + assert!(show_order_result.is_ok()); + + let show_order_result = show_order_result?; assert_eq!(order_created.id, show_order_result.id); assert_eq!(order_created.status, show_order_result.status); - let capture_order = CaptureOrder::new(&show_order_result.id); + let authorize_order = AuthorizeOrder::new(&show_order_result.id); - let _res = client.execute(&capture_order).await?; + let res = client.execute(&authorize_order).await; + assert!(res.is_err()); // Fails with ORDER_NOT_APPROVED Ok(()) } diff --git a/src/api/payments.rs b/src/api/payments.rs index 55d070e..8e1b05b 100644 --- a/src/api/payments.rs +++ b/src/api/payments.rs @@ -1,6 +1,6 @@ //! Call the Payments API to authorize payments, capture authorized payments, refund payments that have already been captured, and show payment information. //! -//! Reference: https://developer.paypal.com/docs/api/payments/v2/ +//! Reference: use std::borrow::Cow; @@ -42,8 +42,4 @@ impl Endpoint for GetAuthorizedPayment { fn method(&self) -> reqwest::Method { reqwest::Method::GET } - - fn body(&self) -> Option<&Self::Body> { - None - } } diff --git a/src/client.rs b/src/client.rs index 8f32d78..b864132 100644 --- a/src/client.rs +++ b/src/client.rs @@ -13,7 +13,7 @@ use crate::{ /// Represents the access token returned by the OAuth2 authentication. /// -/// https://developer.paypal.com/docs/api/get-an-access-token-postman/ +/// #[derive(Debug, Deserialize)] pub struct AccessToken { /// The OAuth2 scopes. @@ -196,7 +196,7 @@ impl Client { let mut url = endpoint.full_path(self.sandbox); if let Some(query) = endpoint.query() { - let query_string = serde_qs::to_string(query).expect("serialize the query correctly"); + let query_string = serde_qs::to_string(&query).expect("serialize the query correctly"); url.push_str(&query_string); } @@ -204,7 +204,7 @@ impl Client { request = self.setup_headers(request, headers).await?; if let Some(body) = endpoint.body() { - request = request.json(body); + request = request.json(&body); } let res = request.send().await?; @@ -225,7 +225,7 @@ impl Client { /// Executes the given endpoints with the default headers. /// - /// You must remember to call `get_access_token` first or this may fail due to not being authed. + /// You must remember to call [Client::get_access_token] first or this may fail due to not being authed. pub async fn execute(&self, endpoint: &E) -> Result where E: Endpoint, diff --git a/src/countries.rs b/src/countries.rs index 8629d34..0d243ee 100644 --- a/src/countries.rs +++ b/src/countries.rs @@ -1,4 +1,4 @@ -//! Generated using https://github.com/edg-l/payhelper +//! Generated using use crate::errors::InvalidCountryError; use serde::{Deserialize, Serialize}; diff --git a/src/data/common.rs b/src/data/common.rs index a1e33c7..73848bf 100644 --- a/src/data/common.rs +++ b/src/data/common.rs @@ -8,7 +8,7 @@ use std::str::FromStr; /// The phone type. /// -/// https://developer.paypal.com/docs/api/orders/v2/#definition-phone_with_type +/// #[derive(Debug, Serialize, Deserialize, Eq, PartialEq, Copy, Clone)] #[serde(rename_all = "SCREAMING_SNAKE_CASE")] #[allow(missing_docs)] diff --git a/src/data/invoice.rs b/src/data/invoice.rs index 675bee2..f0839e0 100644 --- a/src/data/invoice.rs +++ b/src/data/invoice.rs @@ -569,7 +569,7 @@ pub struct InvoicePayload { pub refunds: Option, } -/// Definition: https://developer.paypal.com/docs/api/invoicing/v2/#invoices_get +/// Definition: #[skip_serializing_none] #[derive(Debug, Serialize, Deserialize, Clone, Builder)] #[builder(setter(strip_option, into))] diff --git a/src/data/orders.rs b/src/data/orders.rs index 30350f4..5c97cf2 100644 --- a/src/data/orders.rs +++ b/src/data/orders.rs @@ -27,7 +27,7 @@ impl Default for Intent { /// Represents a payer name. /// -/// https://developer.paypal.com/docs/api/orders/v2/#definition-payer.name +/// #[derive(Debug, Serialize, Deserialize, Eq, PartialEq, Clone)] pub struct PayerName { /// When the party is a person, the party's given, or first, name. @@ -80,7 +80,7 @@ pub struct TaxInfo { /// The customer who approves and pays for the order. The customer is also known as the payer. /// -/// https://developer.paypal.com/docs/api/orders/v2/#definition-payer +/// #[skip_serializing_none] #[derive(Debug, Default, Serialize, Deserialize, Clone, Builder)] pub struct Payer { @@ -428,7 +428,7 @@ pub struct PurchaseUnit { pub id: Option, /// The soft descriptor is the dynamic text used to construct the statement descriptor that appears on a payer's card statement. /// - /// More info here: https://developer.paypal.com/docs/api/orders/v2/#definition-purchase_unit_request + /// More info here: pub soft_descriptor: Option, /// An array of items that the customer purchases from the merchant. pub items: Option>, diff --git a/src/endpoint.rs b/src/endpoint.rs index c6b8f1c..2684598 100644 --- a/src/endpoint.rs +++ b/src/endpoint.rs @@ -20,12 +20,12 @@ pub trait Endpoint { fn method(&self) -> reqwest::Method; /// The query to be used when calling this endpoint. - fn query(&self) -> Option<&Self::Query> { + fn query(&self) -> Option { None } /// The body to be used when calling this endpoint. - fn body(&self) -> Option<&Self::Body> { + fn body(&self) -> Option { None } @@ -33,10 +33,14 @@ pub trait Endpoint { /// /// Automatically implemented. fn full_path(&self, is_sandbox: bool) -> String { + let relative_path = self.relative_path(); + + assert!(relative_path.starts_with('/'), "relative path must start with '/'"); + if is_sandbox { - format!("{}{}", SANDBOX_ENDPOINT, self.relative_path()) + format!("{}{}", SANDBOX_ENDPOINT, relative_path) } else { - format!("{}{}", LIVE_ENDPOINT, self.relative_path()) + format!("{}{}", LIVE_ENDPOINT, relative_path) } } } diff --git a/src/lib.rs b/src/lib.rs index c363cfa..d30b9a6 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -6,6 +6,8 @@ //! [![Docs](https://docs.rs/paypal-rs/badge.svg)](https://docs.rs/paypal-rs) //! //! A rust library that wraps the [paypal api](https://developer.paypal.com/docs/api) asynchronously in a strongly typed manner. +//! +//! If there is a missing endpoint that you need, you may try to implement the [Endpoint](endpoint::Endpoint) and pass it to [Client::execute](client::Client::execute) //! //! Currently in early development. //! @@ -91,7 +93,7 @@ pub const LIVE_ENDPOINT: &str = "https://api-m.paypal.com"; pub const SANDBOX_ENDPOINT: &str = "https://api-m.sandbox.paypal.com"; /// Represents the query used in most GET api requests. /// -/// Reference: https://developer.paypal.com/docs/api/reference/api-requests/#query-parameters +/// Reference: /// /// Note: You can avoid most fields by the Default impl like so: /// ``` @@ -130,7 +132,7 @@ pub struct Query { /// Represents the optional header values used on paypal requests. /// -/// https://developer.paypal.com/docs/api/reference/api-requests/#paypal-auth-assertion +/// #[derive(Debug, Default, Builder, Clone)] pub struct HeaderParams { /// The merchant payer id used on PayPal-Auth-Assertion