some fixes

This commit is contained in:
Edgar 2022-05-12 09:58:36 +02:00
parent 89b1fdf113
commit a4c4838aaf
No known key found for this signature in database
GPG key ID: 8731E6C0166EAA85
11 changed files with 66 additions and 54 deletions

View file

@ -4,7 +4,7 @@
//! Customers with a PayPal account can log in and pay the invoice with PayPal. Alternatively, //! 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. //! 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: <https://developer.paypal.com/docs/api/invoicing/v2/>
use std::borrow::Cow; use std::borrow::Cow;
@ -53,8 +53,8 @@ impl Endpoint for GenerateInvoiceNumber {
reqwest::Method::POST reqwest::Method::POST
} }
fn body(&self) -> Option<&Self::Body> { fn body(&self) -> Option<Self::Body> {
Some(&self.invoice_number) Some(self.invoice_number.clone())
} }
} }
@ -88,8 +88,8 @@ impl Endpoint for CreateDraftInvoice {
reqwest::Method::POST reqwest::Method::POST
} }
fn body(&self) -> Option<&Self::Body> { fn body(&self) -> Option<Self::Body> {
Some(&self.invoice) Some(self.invoice.clone())
} }
} }
@ -153,8 +153,8 @@ impl Endpoint for ListInvoices {
reqwest::Method::GET reqwest::Method::GET
} }
fn query(&self) -> Option<&Self::Query> { fn query(&self) -> Option<Self::Query> {
Some(&self.query) Some(self.query.clone())
} }
} }
@ -234,12 +234,12 @@ impl Endpoint for UpdateInvoice {
reqwest::Method::PUT reqwest::Method::PUT
} }
fn body(&self) -> Option<&Self::Body> { fn body(&self) -> Option<Self::Body> {
Some(&self.invoice) Some(self.invoice.clone())
} }
fn query(&self) -> Option<&Self::Query> { fn query(&self) -> Option<Self::Query> {
Some(&self.query) Some(self.query.clone())
} }
} }
@ -274,8 +274,8 @@ impl Endpoint for CancelInvoice {
reqwest::Method::POST reqwest::Method::POST
} }
fn body(&self) -> Option<&Self::Body> { fn body(&self) -> Option<Self::Body> {
Some(&self.reason) Some(self.reason.clone())
} }
} }
@ -383,7 +383,7 @@ mod tests {
let invoice = CreateDraftInvoice::new(payload); let invoice = CreateDraftInvoice::new(payload);
let res = client.execute(&invoice).await?; let _res = client.execute(&invoice).await?;
Ok(()) Ok(())
} }
} }

View file

@ -2,4 +2,4 @@
pub mod invoice; pub mod invoice;
pub mod orders; pub mod orders;
pub mod payments; pub mod payments;

View file

@ -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. //! 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/ //! <https://developer.paypal.com/docs/api/orders/v2/>
use std::borrow::Cow; use std::borrow::Cow;
@ -41,8 +41,8 @@ impl Endpoint for CreateOrder {
reqwest::Method::POST reqwest::Method::POST
} }
fn body(&self) -> Option<&Self::Body> { fn body(&self) -> Option<Self::Body> {
Some(&self.order) Some(self.order.clone())
} }
} }
@ -97,10 +97,11 @@ pub struct PaymentSource {
} }
/// The capture order endpoint body. /// The capture order endpoint body.
#[derive(Debug, Serialize, Clone)] #[derive(Debug, Serialize, Clone, Default)]
pub struct PaymentSourceBody { pub struct PaymentSourceBody {
/// The payment source definition. /// The payment source definition.
pub payment_source: PaymentSource, #[serde(skip_serializing_if = "Option::is_none")]
pub payment_source: Option<PaymentSource>,
} }
/// Captures payment for an order. To successfully capture payment for an order, /// Captures payment for an order. To successfully capture payment for an order,
@ -111,7 +112,7 @@ pub struct CaptureOrder {
/// The id of the order. /// The id of the order.
pub order_id: String, pub order_id: String,
/// The endpoint body. /// The endpoint body.
pub body: Option<PaymentSourceBody>, pub body: PaymentSourceBody,
} }
impl CaptureOrder { impl CaptureOrder {
@ -119,7 +120,7 @@ impl CaptureOrder {
pub fn new(order_id: &str) -> Self { pub fn new(order_id: &str) -> Self {
Self { Self {
order_id: order_id.to_string(), order_id: order_id.to_string(),
body: None, body: PaymentSourceBody::default(),
} }
} }
} }
@ -139,8 +140,8 @@ impl Endpoint for CaptureOrder {
reqwest::Method::POST reqwest::Method::POST
} }
fn body(&self) -> Option<&Self::Body> { fn body(&self) -> Option<Self::Body> {
self.body.as_ref() Some(self.body.clone())
} }
} }
@ -152,7 +153,7 @@ pub struct AuthorizeOrder {
/// The order id. /// The order id.
order_id: String, order_id: String,
/// The endpoint body. /// The endpoint body.
pub body: Option<PaymentSourceBody>, pub body: PaymentSourceBody,
} }
impl AuthorizeOrder { impl AuthorizeOrder {
@ -160,7 +161,7 @@ impl AuthorizeOrder {
pub fn new(order_id: &str) -> Self { pub fn new(order_id: &str) -> Self {
Self { Self {
order_id: order_id.to_string(), order_id: order_id.to_string(),
body: None, body: PaymentSourceBody::default(),
} }
} }
} }
@ -180,8 +181,8 @@ impl Endpoint for AuthorizeOrder {
reqwest::Method::POST reqwest::Method::POST
} }
fn body(&self) -> Option<&Self::Body> { fn body(&self) -> Option<Self::Body> {
self.body.as_ref() Some(self.body.clone())
} }
} }
@ -194,7 +195,7 @@ mod tests {
#[tokio::test] #[tokio::test]
async fn test_order() -> anyhow::Result<()> { async fn test_order() -> anyhow::Result<()> {
let mut client = create_client().await; 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() let order = OrderPayloadBuilder::default()
.intent(Intent::Authorize) .intent(Intent::Authorize)
@ -219,7 +220,11 @@ mod tests {
..Default::default() ..Default::default()
}, },
) )
.await?; .await;
assert!(order_created.is_ok());
let order_created = order_created?;
assert_ne!(order_created.id, ""); assert_ne!(order_created.id, "");
assert_eq!(order_created.status, OrderStatus::Created); assert_eq!(order_created.status, OrderStatus::Created);
@ -235,14 +240,19 @@ mod tests {
..Default::default() ..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.id, show_order_result.id);
assert_eq!(order_created.status, show_order_result.status); 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(()) Ok(())
} }

View file

@ -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. //! 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: <https://developer.paypal.com/docs/api/payments/v2/>
use std::borrow::Cow; use std::borrow::Cow;
@ -42,8 +42,4 @@ impl Endpoint for GetAuthorizedPayment {
fn method(&self) -> reqwest::Method { fn method(&self) -> reqwest::Method {
reqwest::Method::GET reqwest::Method::GET
} }
fn body(&self) -> Option<&Self::Body> {
None
}
} }

View file

@ -13,7 +13,7 @@ use crate::{
/// Represents the access token returned by the OAuth2 authentication. /// Represents the access token returned by the OAuth2 authentication.
/// ///
/// https://developer.paypal.com/docs/api/get-an-access-token-postman/ /// <https://developer.paypal.com/docs/api/get-an-access-token-postman/>
#[derive(Debug, Deserialize)] #[derive(Debug, Deserialize)]
pub struct AccessToken { pub struct AccessToken {
/// The OAuth2 scopes. /// The OAuth2 scopes.
@ -196,7 +196,7 @@ impl Client {
let mut url = endpoint.full_path(self.sandbox); let mut url = endpoint.full_path(self.sandbox);
if let Some(query) = endpoint.query() { 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); url.push_str(&query_string);
} }
@ -204,7 +204,7 @@ impl Client {
request = self.setup_headers(request, headers).await?; request = self.setup_headers(request, headers).await?;
if let Some(body) = endpoint.body() { if let Some(body) = endpoint.body() {
request = request.json(body); request = request.json(&body);
} }
let res = request.send().await?; let res = request.send().await?;
@ -225,7 +225,7 @@ impl Client {
/// Executes the given endpoints with the default headers. /// 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<E>(&self, endpoint: &E) -> Result<E::Response, ResponseError> pub async fn execute<E>(&self, endpoint: &E) -> Result<E::Response, ResponseError>
where where
E: Endpoint, E: Endpoint,

View file

@ -1,4 +1,4 @@
//! Generated using https://github.com/edg-l/payhelper //! Generated using <https://github.com/edg-l/payhelper>
use crate::errors::InvalidCountryError; use crate::errors::InvalidCountryError;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};

View file

@ -8,7 +8,7 @@ use std::str::FromStr;
/// The phone type. /// The phone type.
/// ///
/// https://developer.paypal.com/docs/api/orders/v2/#definition-phone_with_type /// <https://developer.paypal.com/docs/api/orders/v2/#definition-phone_with_type>
#[derive(Debug, Serialize, Deserialize, Eq, PartialEq, Copy, Clone)] #[derive(Debug, Serialize, Deserialize, Eq, PartialEq, Copy, Clone)]
#[serde(rename_all = "SCREAMING_SNAKE_CASE")] #[serde(rename_all = "SCREAMING_SNAKE_CASE")]
#[allow(missing_docs)] #[allow(missing_docs)]

View file

@ -569,7 +569,7 @@ pub struct InvoicePayload {
pub refunds: Option<Refunds>, pub refunds: Option<Refunds>,
} }
/// Definition: https://developer.paypal.com/docs/api/invoicing/v2/#invoices_get /// Definition: <https://developer.paypal.com/docs/api/invoicing/v2/#invoices_get>
#[skip_serializing_none] #[skip_serializing_none]
#[derive(Debug, Serialize, Deserialize, Clone, Builder)] #[derive(Debug, Serialize, Deserialize, Clone, Builder)]
#[builder(setter(strip_option, into))] #[builder(setter(strip_option, into))]

View file

@ -27,7 +27,7 @@ impl Default for Intent {
/// Represents a payer name. /// Represents a payer name.
/// ///
/// https://developer.paypal.com/docs/api/orders/v2/#definition-payer.name /// <https://developer.paypal.com/docs/api/orders/v2/#definition-payer.name>
#[derive(Debug, Serialize, Deserialize, Eq, PartialEq, Clone)] #[derive(Debug, Serialize, Deserialize, Eq, PartialEq, Clone)]
pub struct PayerName { pub struct PayerName {
/// When the party is a person, the party's given, or first, name. /// 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. /// 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 /// <https://developer.paypal.com/docs/api/orders/v2/#definition-payer>
#[skip_serializing_none] #[skip_serializing_none]
#[derive(Debug, Default, Serialize, Deserialize, Clone, Builder)] #[derive(Debug, Default, Serialize, Deserialize, Clone, Builder)]
pub struct Payer { pub struct Payer {
@ -428,7 +428,7 @@ pub struct PurchaseUnit {
pub id: Option<String>, pub id: Option<String>,
/// The soft descriptor is the dynamic text used to construct the statement descriptor that appears on a payer's card statement. /// 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: <https://developer.paypal.com/docs/api/orders/v2/#definition-purchase_unit_request>
pub soft_descriptor: Option<String>, pub soft_descriptor: Option<String>,
/// An array of items that the customer purchases from the merchant. /// An array of items that the customer purchases from the merchant.
pub items: Option<Vec<Item>>, pub items: Option<Vec<Item>>,

View file

@ -20,12 +20,12 @@ pub trait Endpoint {
fn method(&self) -> reqwest::Method; fn method(&self) -> reqwest::Method;
/// The query to be used when calling this endpoint. /// The query to be used when calling this endpoint.
fn query(&self) -> Option<&Self::Query> { fn query(&self) -> Option<Self::Query> {
None None
} }
/// The body to be used when calling this endpoint. /// The body to be used when calling this endpoint.
fn body(&self) -> Option<&Self::Body> { fn body(&self) -> Option<Self::Body> {
None None
} }
@ -33,10 +33,14 @@ pub trait Endpoint {
/// ///
/// Automatically implemented. /// Automatically implemented.
fn full_path(&self, is_sandbox: bool) -> String { 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 { if is_sandbox {
format!("{}{}", SANDBOX_ENDPOINT, self.relative_path()) format!("{}{}", SANDBOX_ENDPOINT, relative_path)
} else { } else {
format!("{}{}", LIVE_ENDPOINT, self.relative_path()) format!("{}{}", LIVE_ENDPOINT, relative_path)
} }
} }
} }

View file

@ -6,6 +6,8 @@
//! [![Docs](https://docs.rs/paypal-rs/badge.svg)](https://docs.rs/paypal-rs) //! [![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. //! 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. //! 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"; pub const SANDBOX_ENDPOINT: &str = "https://api-m.sandbox.paypal.com";
/// Represents the query used in most GET api requests. /// Represents the query used in most GET api requests.
/// ///
/// Reference: https://developer.paypal.com/docs/api/reference/api-requests/#query-parameters /// Reference: <https://developer.paypal.com/docs/api/reference/api-requests/#query-parameters>
/// ///
/// Note: You can avoid most fields by the Default impl like so: /// 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. /// Represents the optional header values used on paypal requests.
/// ///
/// https://developer.paypal.com/docs/api/reference/api-requests/#paypal-auth-assertion /// <https://developer.paypal.com/docs/api/reference/api-requests/#paypal-auth-assertion>
#[derive(Debug, Default, Builder, Clone)] #[derive(Debug, Default, Builder, Clone)]
pub struct HeaderParams { pub struct HeaderParams {
/// The merchant payer id used on PayPal-Auth-Assertion /// The merchant payer id used on PayPal-Auth-Assertion