remove boxed errors, some updates
This commit is contained in:
parent
5ba9bfabfc
commit
5f77a90e1b
|
@ -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"
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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)]
|
||||||
|
|
103
src/invoice.rs
103
src/invoice.rs
|
@ -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(¶ms).send().await?;
|
let res = build.json(¶ms).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)?))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
97
src/lib.rs
97
src/lib.rs
|
@ -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")),
|
||||||
)
|
)
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue