remove boxed errors, some updates

This commit is contained in:
Edgar 2021-01-08 16:03:19 +01:00
parent 5ba9bfabfc
commit 5f77a90e1b
No known key found for this signature in database
GPG key ID: 8731E6C0166EAA85
6 changed files with 185 additions and 101 deletions

View file

@ -1,6 +1,6 @@
[package] [package]
name = "paypal-rs" name = "paypal-rs"
version = "0.2.0-alpha.2" version = "0.2.0-alpha.3"
authors = ["Edgar <git@edgarluque.com>"] authors = ["Edgar <git@edgarluque.com>"]
description = "A library that wraps the paypal api asynchronously." description = "A library that wraps the paypal api asynchronously."
repository = "https://github.com/edg-l/paypal-rs/" repository = "https://github.com/edg-l/paypal-rs/"
@ -23,6 +23,5 @@ log = "0.4.11"
bytes = "1.0.0" bytes = "1.0.0"
[dev-dependencies] [dev-dependencies]
# Can't update this until reqwest updates.
tokio = { version = "1.0.1", features = ["macros", "rt"] } tokio = { version = "1.0.1", features = ["macros", "rt"] }
dotenv = "0.15.0" dotenv = "0.15.0"

View file

@ -1,7 +1,7 @@
# paypal-rs # paypal-rs
[![Crates.io](https://meritbadge.herokuapp.com/paypal-rs)](https://crates.io/crates/paypal-rs) [![Crates.io](https://meritbadge.herokuapp.com/paypal-rs)](https://crates.io/crates/paypal-rs)
![Rust](https://github.com/edg-l/paypal-rs/workflows/Rust/badge.svg) ![Rust](https://github.com/edg-l/paypal-rs/workflows/Rust/badge.svg)
![Docs](https://docs.rs/paypal-rs/badge.svg) [![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 stringly typed manner. A rust library that wraps the [paypal api](https://developer.paypal.com/docs/api) asynchronously in a stringly typed manner.
@ -35,9 +35,7 @@ async fn main() {
let order_payload = OrderPayload::new( let order_payload = OrderPayload::new(
Intent::Authorize, Intent::Authorize,
vec![PurchaseUnit::new(Amount::new( vec![PurchaseUnit::new(Amount::new(Currency::EUR, "10.0"))],
Currency::EUR, "10.0",
))],
); );
let order = client let order = client

View file

@ -5,9 +5,10 @@ use std::collections::HashMap;
use std::error::Error; use std::error::Error;
use std::fmt; use std::fmt;
/// A paypal api response error. /// A paypal api response error.
#[derive(Debug, Serialize, Deserialize)] #[derive(Debug, Serialize, Deserialize)]
pub struct ApiResponseError { pub struct PaypalError {
/// The error name. /// The error name.
pub name: String, pub name: String,
/// The error message. /// The error message.
@ -24,13 +25,22 @@ pub struct ApiResponseError {
pub links: Vec<LinkDescription>, pub links: Vec<LinkDescription>,
} }
impl fmt::Display for ApiResponseError { impl fmt::Display for PaypalError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{:#?}", self) write!(f, "{:#?}", self)
} }
} }
impl Error for ApiResponseError {} impl Error for PaypalError {}
/// A response error, it may be paypal related or an error related to the http request itself.
#[derive(Debug)]
pub enum ResponseError {
/// A paypal api error.
ApiError(PaypalError),
/// A http error.
HttpError(reqwest::Error)
}
/// When a currency is invalid. /// When a currency is invalid.
#[derive(Debug)] #[derive(Debug)]

View file

@ -7,7 +7,8 @@
//! Reference: https://developer.paypal.com/docs/api/invoicing/v2/ //! Reference: https://developer.paypal.com/docs/api/invoicing/v2/
use crate::common::*; use crate::common::*;
use crate::errors; use crate::HeaderParams;
use crate::errors::{ResponseError, PaypalError};
use bytes::Bytes; use bytes::Bytes;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use std::collections::HashMap; use std::collections::HashMap;
@ -727,7 +728,7 @@ impl super::Client {
pub async fn generate_invoice_number( pub async fn generate_invoice_number(
&mut self, &mut self,
header_params: crate::HeaderParams, header_params: crate::HeaderParams,
) -> Result<String, Box<dyn std::error::Error>> { ) -> Result<String, ResponseError> {
let build = self let build = self
.setup_headers( .setup_headers(
self.client self.client
@ -736,13 +737,13 @@ impl super::Client {
) )
.await; .await;
let res = build.send().await?; let res = build.send().await.map_err(ResponseError::HttpError)?;
if res.status().is_success() { if res.status().is_success() {
let x = res.json::<HashMap<String, String>>().await?; let x = res.json::<HashMap<String, String>>().await.map_err(ResponseError::HttpError)?;
Ok(x.get("invoice_number").expect("to have a invoice number").clone()) Ok(x.get("invoice_number").expect("to have a invoice number").clone())
} else { } else {
Err(Box::new(res.json::<errors::ApiResponseError>().await?)) Err(ResponseError::ApiError(res.json::<PaypalError>().await.map_err(ResponseError::HttpError)?))
} }
} }
@ -751,8 +752,8 @@ impl super::Client {
pub async fn create_draft_invoice( pub async fn create_draft_invoice(
&mut self, &mut self,
invoice: InvoicePayload, invoice: InvoicePayload,
header_params: crate::HeaderParams, header_params: HeaderParams,
) -> Result<Invoice, Box<dyn std::error::Error>> { ) -> Result<Invoice, ResponseError> {
let build = self let build = self
.setup_headers( .setup_headers(
self.client self.client
@ -761,22 +762,22 @@ impl super::Client {
) )
.await; .await;
let res = build.json(&invoice).send().await?; let res = build.json(&invoice).send().await.map_err(ResponseError::HttpError)?;
if res.status().is_success() { if res.status().is_success() {
let x = res.json::<Invoice>().await?; let x = res.json::<Invoice>().await.map_err(ResponseError::HttpError)?;
Ok(x) Ok(x)
} else { } else {
Err(Box::new(res.json::<errors::ApiResponseError>().await?)) Err(ResponseError::ApiError(res.json::<PaypalError>().await.map_err(ResponseError::HttpError)?))
} }
} }
/// Get an invoice by ID. /// Get an invoice by ID.
pub async fn get_invoice<S: std::fmt::Display>( pub async fn get_invoice(
&mut self, &mut self,
invoice_id: S, invoice_id: &str,
header_params: crate::HeaderParams, header_params: HeaderParams,
) -> Result<Invoice, Box<dyn std::error::Error>> { ) -> Result<Invoice, ResponseError> {
let build = self let build = self
.setup_headers( .setup_headers(
self.client self.client
@ -785,13 +786,13 @@ impl super::Client {
) )
.await; .await;
let res = build.send().await?; let res = build.send().await.map_err(ResponseError::HttpError)?;
if res.status().is_success() { if res.status().is_success() {
let x = res.json::<Invoice>().await?; let x = res.json::<Invoice>().await.map_err(ResponseError::HttpError)?;
Ok(x) Ok(x)
} else { } else {
Err(Box::new(res.json::<errors::ApiResponseError>().await?)) Err(ResponseError::ApiError(res.json::<PaypalError>().await.map_err(ResponseError::HttpError)?))
} }
} }
@ -801,8 +802,8 @@ impl super::Client {
&mut self, &mut self,
page: i32, page: i32,
page_size: i32, page_size: i32,
header_params: crate::HeaderParams, header_params: HeaderParams,
) -> Result<InvoiceList, Box<dyn std::error::Error>> { ) -> Result<InvoiceList, ResponseError> {
let build = self let build = self
.setup_headers( .setup_headers(
self.client.get( self.client.get(
@ -818,22 +819,22 @@ impl super::Client {
) )
.await; .await;
let res = build.send().await?; let res = build.send().await.map_err(ResponseError::HttpError)?;
if res.status().is_success() { if res.status().is_success() {
let x = res.json::<InvoiceList>().await?; let x = res.json::<InvoiceList>().await.map_err(ResponseError::HttpError)?;
Ok(x) Ok(x)
} else { } else {
Err(Box::new(res.json::<errors::ApiResponseError>().await?)) Err(ResponseError::ApiError(res.json::<PaypalError>().await.map_err(ResponseError::HttpError)?))
} }
} }
/// Delete a invoice /// Delete a invoice
pub async fn delete_invoice<S: std::fmt::Display>( pub async fn delete_invoice(
&mut self, &mut self,
invoice_id: S, invoice_id: &str,
header_params: crate::HeaderParams, header_params: HeaderParams,
) -> Result<(), Box<dyn std::error::Error>> { ) -> Result<(), ResponseError> {
let build = self let build = self
.setup_headers( .setup_headers(
self.client self.client
@ -842,12 +843,12 @@ impl super::Client {
) )
.await; .await;
let res = build.send().await?; let res = build.send().await.map_err(ResponseError::HttpError)?;
if res.status().is_success() { if res.status().is_success() {
Ok(()) Ok(())
} else { } else {
Err(Box::new(res.json::<errors::ApiResponseError>().await?)) Err(ResponseError::ApiError(res.json::<PaypalError>().await.map_err(ResponseError::HttpError)?))
} }
} }
@ -857,8 +858,8 @@ impl super::Client {
invoice: Invoice, invoice: Invoice,
send_to_recipient: bool, send_to_recipient: bool,
send_to_invoicer: bool, send_to_invoicer: bool,
header_params: crate::HeaderParams, header_params: HeaderParams,
) -> Result<(), Box<dyn std::error::Error>> { ) -> Result<(), ResponseError> {
let build = self let build = self
.setup_headers( .setup_headers(
self.client.put( self.client.put(
@ -875,22 +876,22 @@ impl super::Client {
) )
.await; .await;
let res = build.send().await?; let res = build.send().await.map_err(ResponseError::HttpError)?;
if res.status().is_success() { if res.status().is_success() {
Ok(()) Ok(())
} else { } else {
Err(Box::new(res.json::<errors::ApiResponseError>().await?)) Err(ResponseError::ApiError(res.json::<PaypalError>().await.map_err(ResponseError::HttpError)?))
} }
} }
/// Cancel a invoice /// Cancel a invoice
pub async fn cancel_invoice<S: std::fmt::Display>( pub async fn cancel_invoice(
&mut self, &mut self,
invoice_id: S, invoice_id: &str,
reason: CancelReason, reason: CancelReason,
header_params: crate::HeaderParams, header_params: HeaderParams,
) -> Result<(), Box<dyn std::error::Error>> { ) -> Result<(), ResponseError> {
let build = self let build = self
.setup_headers( .setup_headers(
self.client self.client
@ -899,22 +900,22 @@ impl super::Client {
) )
.await; .await;
let res = build.json(&reason).send().await?; let res = build.json(&reason).send().await.map_err(ResponseError::HttpError)?;
if res.status().is_success() { if res.status().is_success() {
Ok(()) Ok(())
} else { } else {
Err(Box::new(res.json::<errors::ApiResponseError>().await?)) Err(ResponseError::ApiError(res.json::<PaypalError>().await.map_err(ResponseError::HttpError)?))
} }
} }
/// Generate a QR code /// Generate a QR code
pub async fn generate_qr_code<S: std::fmt::Display>( pub async fn generate_qr_code(
&mut self, &mut self,
invoice_id: S, invoice_id: &str,
params: QRCodeParams, params: QRCodeParams,
header_params: crate::HeaderParams, header_params: HeaderParams,
) -> Result<Bytes, Box<dyn std::error::Error>> { ) -> Result<Bytes, ResponseError> {
let build = self let build = self
.setup_headers( .setup_headers(
self.client.post( self.client.post(
@ -929,23 +930,23 @@ impl super::Client {
) )
.await; .await;
let res = build.json(&params).send().await?; let res = build.json(&params).send().await.map_err(ResponseError::HttpError)?;
if res.status().is_success() { if res.status().is_success() {
let b = res.bytes().await?; let b = res.bytes().await.map_err(ResponseError::HttpError)?;
Ok(b) Ok(b)
} else { } else {
Err(Box::new(res.json::<errors::ApiResponseError>().await?)) Err(ResponseError::ApiError(res.json::<PaypalError>().await.map_err(ResponseError::HttpError)?))
} }
} }
/// Records a payment for the invoice. If no payment is due, the invoice is marked as PAID. Otherwise, the invoice is marked as PARTIALLY PAID. /// Records a payment for the invoice. If no payment is due, the invoice is marked as PAID. Otherwise, the invoice is marked as PARTIALLY PAID.
pub async fn record_invoice_payment<S: std::fmt::Display>( pub async fn record_invoice_payment(
&mut self, &mut self,
invoice_id: S, invoice_id: &str,
payload: RecordPaymentPayload, payload: RecordPaymentPayload,
header_params: crate::HeaderParams, header_params: crate::HeaderParams,
) -> Result<String, Box<dyn std::error::Error>> { ) -> Result<String, ResponseError> {
let build = self let build = self
.setup_headers( .setup_headers(
self.client self.client
@ -954,13 +955,13 @@ impl super::Client {
) )
.await; .await;
let res = build.json(&payload).send().await?; let res = build.json(&payload).send().await.map_err(ResponseError::HttpError)?;
if res.status().is_success() { if res.status().is_success() {
let x = res.json::<HashMap<String, String>>().await?; let x = res.json::<HashMap<String, String>>().await.map_err(ResponseError::HttpError)?;
Ok(x.get("payment_id").unwrap().to_owned()) Ok(x.get("payment_id").unwrap().to_owned())
} else { } else {
Err(Box::new(res.json::<errors::ApiResponseError>().await?)) Err(ResponseError::ApiError(res.json::<PaypalError>().await.map_err(ResponseError::HttpError)?))
} }
} }

View file

@ -1,14 +1,82 @@
//! # paypal-rs //! [![Crates.io](https://meritbadge.herokuapp.com/paypal-rs)](https://crates.io/crates/paypal-rs)
//! ![Rust](https://github.com/edg-l/paypal-rs/workflows/Rust/badge.svg) //! ![Rust](https://github.com/edg-l/paypal-rs/workflows/Rust/badge.svg)
//! ![Docs](https://docs.rs/paypal-rs/badge.svg) //! [![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 stringly typed manner.
//! //!
//! Crate: https://crates.io/crates/paypal-rs //! Crate: https://crates.io/crates/paypal-rs
//! //!
//! Documentation: https://docs.rs/paypal-rs //! Documentation: https://docs.rs/paypal-rs
//! //!
//! Currently in early development. //! Currently in early development.
//!
//! ## Example
//!
//! ```rust
//! use paypal_rs::{
//! Client,
//! HeaderParams,
//! Prefer,
//! orders::{OrderPayload, Intent, PurchaseUnit, Amount},
//! common::Currency,
//! };
//!
//! #[tokio::main]
//! async fn main() {
//! dotenv::dotenv::ok();
//! let clientid = std::env::var("PAYPAL_CLIENTID").unwrap();
//! let secret = std::env::var("PAYPAL_SECRET").unwrap();
//!
//! let mut client = Client::new(clientid, secret, true);
//!
//! client.get_access_token().await.unwrap();
//!
//! let order_payload = OrderPayload::new(
//! Intent::Authorize,
//! vec![PurchaseUnit::new(Amount::new(Currency::EUR, "10.0"))],
//! );
//!
//! let order = client
//! .create_order(
//! order_payload,
//! HeaderParams {
//! prefer: Some(Prefer::Representation),
//! ..Default::default()
//! },
//! )
//! .await
//! .unwrap();
//! }
//! ```
//!
//! ## Testing
//! You need the enviroment variables PAYPAL_CLIENTID and PAYPAL_SECRET to be set.
//!
//! `cargo test`
//!
//! ## Roadmap
//!
//! - [x] Orders API - 0.1.0
//! - - [x] Create order
//! - - [x] Update order
//! - - [x] Show order details
//! - - [x] Authorize payment for order
//! - - [x] Capture payment for order
//! - [ ] Invoicing API - 0.2.0
//! - [ ] Payments API - 0.3.0
//! - [ ] Tracking API - 0.4.0
//! - [ ] Subscriptions API - 0.5.0
//! - [ ] Identity API - 0.6.0
//! - [ ] Disputes API - 0.7.0
//! - [ ] Catalog Products API - 0.8.0
//! - [ ] Partner Referrals API - 0.9.0
//! - [ ] Payouts API - 0.10.0
//! - [ ] Transaction Search API - 0.11.0
//! - [ ] Referenced Payouts API - 0.12.0
//! - [ ] Vault API - 0.13.0
//! - [ ] Webhooks Management API - 0.14.0
//! - [ ] Payment Experience Web Profiles API - 1.0.0
#![deny(missing_docs)] #![deny(missing_docs)]
@ -17,6 +85,7 @@ pub mod errors;
pub mod invoice; pub mod invoice;
pub mod orders; pub mod orders;
use errors::{PaypalError, ResponseError};
use reqwest::header; use reqwest::header;
use reqwest::header::HeaderMap; use reqwest::header::HeaderMap;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
@ -271,7 +340,7 @@ impl Client {
} }
/// Gets a access token used in all the api calls. /// Gets a access token used in all the api calls.
pub async fn get_access_token(&mut self) -> Result<(), Box<dyn std::error::Error>> { pub async fn get_access_token(&mut self) -> Result<(), ResponseError> {
if !self.access_token_expired() { if !self.access_token_expired() {
return Ok(()); return Ok(());
} }
@ -283,15 +352,18 @@ impl Client {
.header("Accept", "application/json") .header("Accept", "application/json")
.body("grant_type=client_credentials") .body("grant_type=client_credentials")
.send() .send()
.await?; .await
.map_err(ResponseError::HttpError)?;
if res.status().is_success() { if res.status().is_success() {
let token = res.json::<AccessToken>().await?; let token = res.json::<AccessToken>().await.map_err(ResponseError::HttpError)?;
self.auth.expires = Some((Instant::now(), Duration::new(token.expires_in, 0))); self.auth.expires = Some((Instant::now(), Duration::new(token.expires_in, 0)));
self.auth.access_token = Some(token); self.auth.access_token = Some(token);
Ok(()) Ok(())
} else { } else {
Err(Box::new(res.json::<errors::ApiResponseError>().await?)) Err(ResponseError::ApiError(
res.json::<PaypalError>().await.map_err(ResponseError::HttpError)?,
))
} }
} }
@ -307,10 +379,10 @@ impl Client {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use crate::{orders::*, Client, HeaderParams, Prefer};
use crate::common::Currency; use crate::common::Currency;
use std::str::FromStr; use crate::{orders::*, Client, HeaderParams, Prefer};
use std::env; use std::env;
use std::str::FromStr;
async fn create_client() -> Client { async fn create_client() -> Client {
dotenv::dotenv().ok(); dotenv::dotenv().ok();
@ -326,7 +398,10 @@ mod tests {
async fn test_order() { async fn test_order() {
let mut client = create_client().await; let mut client = create_client().await;
let order = OrderPayload::new(Intent::Authorize, vec![PurchaseUnit::new(Amount::new(Currency::EUR, "10.0"))]); let order = OrderPayload::new(
Intent::Authorize,
vec![PurchaseUnit::new(Amount::new(Currency::EUR, "10.0"))],
);
let ref_id = format!( let ref_id = format!(
"TEST-{:?}", "TEST-{:?}",
@ -354,7 +429,7 @@ mod tests {
client client
.update_order( .update_order(
order_created.id, &order_created.id,
Some(Intent::Capture), Some(Intent::Capture),
Some(order_created.purchase_units.expect("to exist")), Some(order_created.purchase_units.expect("to exist")),
) )

View file

@ -4,8 +4,9 @@
//! //!
//! Reference: https://developer.paypal.com/docs/api/orders/v2/ //! Reference: https://developer.paypal.com/docs/api/orders/v2/
use crate::HeaderParams;
use crate::common::*; use crate::common::*;
use crate::errors; use crate::errors::{ResponseError, PaypalError};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
/// The intent to either capture payment immediately or authorize a payment for an order after order creation. /// The intent to either capture payment immediately or authorize a payment for an order after order creation.
@ -742,8 +743,8 @@ impl super::Client {
pub async fn create_order( pub async fn create_order(
&mut self, &mut self,
order: OrderPayload, order: OrderPayload,
header_params: crate::HeaderParams, header_params: HeaderParams,
) -> Result<Order, Box<dyn std::error::Error>> { ) -> Result<Order, ResponseError> {
let builder = { let builder = {
self.setup_headers( self.setup_headers(
self.client.post(&format!("{}/v2/checkout/orders", self.endpoint())), self.client.post(&format!("{}/v2/checkout/orders", self.endpoint())),
@ -751,24 +752,24 @@ impl super::Client {
) )
.await .await
}; };
let res = builder.json(&order).send().await?; let res = builder.json(&order).send().await.map_err(ResponseError::HttpError)?;
if res.status().is_success() { if res.status().is_success() {
let order = res.json::<Order>().await?; let order = res.json::<Order>().await.map_err(ResponseError::HttpError)?;
Ok(order) Ok(order)
} else { } else {
Err(Box::new(res.json::<errors::ApiResponseError>().await?)) Err(ResponseError::ApiError(res.json::<PaypalError>().await.map_err(ResponseError::HttpError)?))
} }
} }
/// Used internally for order requests that have no body. /// Used internally for order requests that have no body.
async fn build_endpoint_order<S: std::fmt::Display, A: std::fmt::Display>( async fn build_endpoint_order(
&mut self, &mut self,
order_id: S, order_id: &str,
endpoint: A, endpoint: &str,
post: bool, post: bool,
header_params: crate::HeaderParams, header_params: crate::HeaderParams,
) -> Result<Order, Box<dyn std::error::Error>> { ) -> Result<Order, ResponseError> {
let format = format!("{}/v2/checkout/orders/{}/{}", self.endpoint(), order_id, endpoint); let format = format!("{}/v2/checkout/orders/{}/{}", self.endpoint(), order_id, endpoint);
let builder = self let builder = self
@ -781,13 +782,13 @@ impl super::Client {
) )
.await; .await;
let res = builder.send().await?; let res = builder.send().await.map_err(ResponseError::HttpError)?;
if res.status().is_success() { if res.status().is_success() {
let order = res.json::<Order>().await?; let order = res.json::<Order>().await.expect("error serializing json response");
Ok(order) Ok(order)
} else { } else {
Err(Box::new(res.json::<errors::ApiResponseError>().await?)) Err(ResponseError::ApiError(res.json::<PaypalError>().await.map_err(ResponseError::HttpError)?))
} }
} }
@ -799,12 +800,12 @@ impl super::Client {
/// Note: You can only update the intent from Authorize to Capture /// Note: You can only update the intent from Authorize to Capture
/// ///
/// More info on what you can change: https://developer.paypal.com/docs/api/orders/v2/#orders_patch /// More info on what you can change: https://developer.paypal.com/docs/api/orders/v2/#orders_patch
pub async fn update_order<S: std::fmt::Display>( pub async fn update_order(
&mut self, &mut self,
id: S, id: &str,
intent: Option<Intent>, intent: Option<Intent>,
purchase_units: Option<Vec<PurchaseUnit>>, purchase_units: Option<Vec<PurchaseUnit>>,
) -> Result<(), Box<dyn std::error::Error>> { ) -> Result<(), ResponseError> {
let mut intent_json = String::new(); let mut intent_json = String::new();
let units_json = String::new(); let units_json = String::new();
@ -812,7 +813,7 @@ impl super::Client {
let mut units_json = String::new(); let mut units_json = String::new();
for (i, unit) in p_units.iter().enumerate() { for (i, unit) in p_units.iter().enumerate() {
let unit_str = serde_json::to_string(&unit)?; let unit_str = serde_json::to_string(&unit).expect("error deserializing PurchaseUnit json");
let mut unit_json = format!( let mut unit_json = format!(
r#" r#"
{{ {{
@ -871,32 +872,32 @@ impl super::Client {
.await .await
}; };
let res = builder.body(final_json.clone()).send().await?; let res = builder.body(final_json.clone()).send().await.map_err(ResponseError::HttpError)?;
if res.status().is_success() { if res.status().is_success() {
Ok(()) Ok(())
} else { } else {
Err(Box::new(res.json::<errors::ApiResponseError>().await?)) Err(ResponseError::ApiError(res.json::<PaypalError>().await.map_err(ResponseError::HttpError)?))
} }
} }
/// Shows details for an order, by ID. /// Shows details for an order, by ID.
pub async fn show_order_details<S: std::fmt::Display>( pub async fn show_order_details(
&mut self, &mut self,
order_id: S, order_id: &str,
) -> Result<Order, Box<dyn std::error::Error>> { ) -> Result<Order, ResponseError> {
self.build_endpoint_order(order_id, "", false, crate::HeaderParams::default()) self.build_endpoint_order(order_id, "", false, HeaderParams::default())
.await .await
} }
/// Captures payment for an order. To successfully capture payment for an order, /// Captures payment for an order. To successfully capture payment for an order,
/// the buyer must first approve the order or a valid payment_source must be provided in the request. /// 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. /// 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 capture_order<S: std::fmt::Display>( pub async fn capture_order(
&mut self, &mut self,
order_id: S, order_id: &str,
header_params: crate::HeaderParams, header_params: crate::HeaderParams,
) -> Result<Order, Box<dyn std::error::Error>> { ) -> Result<Order, ResponseError> {
self.build_endpoint_order(order_id, "capture", true, header_params) self.build_endpoint_order(order_id, "capture", true, header_params)
.await .await
} }
@ -904,11 +905,11 @@ impl super::Client {
/// Authorizes payment for an order. To successfully authorize payment for an order, /// 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. /// 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. /// 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_order<S: std::fmt::Display>( pub async fn authorize_order(
&mut self, &mut self,
order_id: S, order_id: &str,
header_params: crate::HeaderParams, header_params: HeaderParams,
) -> Result<Order, Box<dyn std::error::Error>> { ) -> Result<Order, ResponseError> {
self.build_endpoint_order(order_id, "authorize", true, header_params) self.build_endpoint_order(order_id, "authorize", true, header_params)
.await .await
} }