move/update deps, don't require explicit call to get auth token

This commit is contained in:
Edgar 2021-01-05 12:22:34 +01:00
parent 31a4a78a42
commit a2032510e3
No known key found for this signature in database
GPG key ID: 8731E6C0166EAA85
6 changed files with 155 additions and 113 deletions

View file

@ -1,6 +1,6 @@
[package]
name = "paypal-rs"
version = "0.2.0-alpha.0"
version = "0.2.0-alpha.1"
authors = ["Edgar <git@edgarluque.com>"]
description = "A library that wraps the paypal api asynchronously."
repository = "https://github.com/edg-l/paypal-rs/"
@ -14,12 +14,15 @@ edition = "2018"
[dependencies]
reqwest = { version = "0.10.10", features = ["json"] }
tokio = { version = "0.3.6", features = ["full"] }
serde = { version = "1.0.118", features = ["derive"] }
serde_json = "1.0.60"
serde_json = "1.0.61"
chrono = { version = "0.4.19", features = ["serde"] }
jsonwebtoken = "7.2.0"
base64 = "0.13.0"
log = "0.4.11"
bytes = "~0.5.6"
[dev-dependencies]
# Can't update this until reqwest updates.
tokio = { version = "0.2.24", features = ["macros", "rt-core"] }
dotenv = "0.15.0"
chrono = { version = "0.4.19", features = ["serde"] }
bytes = "0.5"

View file

@ -105,4 +105,3 @@ pub struct LinkDescription {
#[serde(skip_serializing_if = "Option::is_none")]
pub method: Option<LinkMethod>,
}

View file

@ -1,9 +1,9 @@
//! Errors created by this crate.
use std::collections::HashMap;
use std::fmt;
use std::error::Error;
use serde::{Deserialize, Serialize};
use crate::common::LinkDescription;
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use std::error::Error;
use std::fmt;
/// A paypal api response error.
#[derive(Debug, Serialize, Deserialize)]

View file

@ -8,9 +8,9 @@
use crate::common::*;
use crate::errors;
use bytes::Bytes;
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use bytes::Bytes;
/// Paypal File reference
#[derive(Debug, Serialize, Deserialize)]
@ -725,14 +725,16 @@ impl super::Client {
///
/// For example, the next invoice number after `INVOICE-1234` is `INVOICE-1235`.
pub async fn generate_invoice_number(
&self,
&mut self,
header_params: crate::HeaderParams,
) -> Result<String, Box<dyn std::error::Error>> {
let build = self.setup_headers(
let build = self
.setup_headers(
self.client
.post(format!("{}/v2/invoicing/generate-next-invoice-number", self.endpoint()).as_str()),
header_params,
);
)
.await;
let res = build.send().await?;
@ -747,15 +749,17 @@ impl super::Client {
/// Creates a draft invoice. To move the invoice from a draft to payable state, you must send the invoice.
/// Include invoice details including merchant information. The invoice object must include an items array.
pub async fn create_draft_invoice(
&self,
&mut self,
invoice: InvoicePayload,
header_params: crate::HeaderParams,
) -> Result<Invoice, Box<dyn std::error::Error>> {
let build = self.setup_headers(
let build = self
.setup_headers(
self.client
.post(format!("{}/v2/invoicing/invoices", self.endpoint()).as_str()),
header_params,
);
)
.await;
let res = build.json(&invoice).send().await?;
@ -769,15 +773,17 @@ impl super::Client {
/// Get an invoice by ID.
pub async fn get_invoice<S: std::fmt::Display>(
&self,
&mut self,
invoice_id: S,
header_params: crate::HeaderParams,
) -> Result<Invoice, Box<dyn std::error::Error>> {
let build = self.setup_headers(
let build = self
.setup_headers(
self.client
.post(format!("{}/v2/invoicing/invoices/{}", self.endpoint(), invoice_id).as_str()),
header_params,
);
)
.await;
let res = build.send().await?;
@ -792,12 +798,13 @@ impl super::Client {
/// List invoices
/// Page size has the following limits: [1, 100].
pub async fn list_invoices(
&self,
&mut self,
page: i32,
page_size: i32,
header_params: crate::HeaderParams,
) -> Result<InvoiceList, Box<dyn std::error::Error>> {
let build = self.setup_headers(
let build = self
.setup_headers(
self.client.get(
format!(
"{}/v2/invoicing/invoices?page={}&page_size={}&total_required=true",
@ -808,7 +815,8 @@ impl super::Client {
.as_str(),
),
header_params,
);
)
.await;
let res = build.send().await?;
@ -822,15 +830,17 @@ impl super::Client {
/// Delete a invoice
pub async fn delete_invoice<S: std::fmt::Display>(
&self,
&mut self,
invoice_id: S,
header_params: crate::HeaderParams,
) -> Result<(), Box<dyn std::error::Error>> {
let build = self.setup_headers(
let build = self
.setup_headers(
self.client
.delete(format!("{}/v2/invoicing/invoices/{}", self.endpoint(), invoice_id).as_str()),
header_params,
);
)
.await;
let res = build.send().await?;
@ -843,13 +853,14 @@ impl super::Client {
/// Update a invoice
pub async fn update_invoice(
&self,
&mut self,
invoice: Invoice,
send_to_recipient: bool,
send_to_invoicer: bool,
header_params: crate::HeaderParams,
) -> Result<(), Box<dyn std::error::Error>> {
let build = self.setup_headers(
let build = self
.setup_headers(
self.client.put(
format!(
"{}/v2/invoicing/invoices/{}?send_to_recipient={}&send_to_invoicer={}",
@ -861,7 +872,8 @@ impl super::Client {
.as_str(),
),
header_params,
);
)
.await;
let res = build.send().await?;
@ -874,16 +886,18 @@ impl super::Client {
/// Cancel a invoice
pub async fn cancel_invoice<S: std::fmt::Display>(
&self,
&mut self,
invoice_id: S,
reason: CancelReason,
header_params: crate::HeaderParams,
) -> Result<(), Box<dyn std::error::Error>> {
let build = self.setup_headers(
let build = self
.setup_headers(
self.client
.post(format!("{}/v2/invoicing/invoices/{}/cancel", self.endpoint(), invoice_id,).as_str()),
header_params,
);
)
.await;
let res = build.json(&reason).send().await?;
@ -896,16 +910,24 @@ impl super::Client {
/// Generate a QR code
pub async fn generate_qr_code<S: std::fmt::Display>(
&self,
&mut self,
invoice_id: S,
params: QRCodeParams,
header_params: crate::HeaderParams,
) -> Result<Bytes, Box<dyn std::error::Error>> {
let build = self.setup_headers(
self.client
.post(format!("{}/v2/invoicing/invoices/{}/generate-qr-code", self.endpoint(), invoice_id).as_str()),
let build = self
.setup_headers(
self.client.post(
format!(
"{}/v2/invoicing/invoices/{}/generate-qr-code",
self.endpoint(),
invoice_id
)
.as_str(),
),
header_params,
);
)
.await;
let res = build.json(&params).send().await?;
@ -919,16 +941,18 @@ impl super::Client {
/// 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>(
&self,
&mut self,
invoice_id: S,
payload: RecordPaymentPayload,
header_params: crate::HeaderParams,
) -> Result<String, Box<dyn std::error::Error>> {
let build = self.setup_headers(
let build = self
.setup_headers(
self.client
.post(format!("{}/v2/invoicing/invoices/{}/payments", self.endpoint(), invoice_id).as_str()),
header_params,
);
)
.await;
let res = build.json(&payload).send().await?;

View file

@ -23,9 +23,9 @@ use serde::{Deserialize, Serialize};
use std::time::{Duration, Instant};
/// The paypal api endpoint used on a live application.
pub const LIVE_ENDPOINT: &str = "https://api.paypal.com";
pub const LIVE_ENDPOINT: &str = "https://api-m.paypal.com";
/// The paypal api endpoint used on when testing.
pub const SANDBOX_ENDPOINT: &str = "https://api.sandbox.paypal.com";
pub const SANDBOX_ENDPOINT: &str = "https://api-m.sandbox.paypal.com";
/// Represents the access token returned by the OAuth2 authentication.
///
@ -207,7 +207,16 @@ impl Client {
}
/// Sets up the request headers as required on https://developer.paypal.com/docs/api/reference/api-requests/#http-request-headers
fn setup_headers(&self, builder: reqwest::RequestBuilder, header_params: HeaderParams) -> reqwest::RequestBuilder {
async fn setup_headers(
&mut self,
builder: reqwest::RequestBuilder,
header_params: HeaderParams,
) -> reqwest::RequestBuilder {
// Check if the token hasn't expired here, since it's called before any other call.
if let Err(e) = self.get_access_token().await {
log::warn!(target: "paypal-rs", "error getting access token: {:?}", e);
}
let mut headers = HeaderMap::new();
headers.append(header::ACCEPT, "application/json".parse().unwrap());
@ -263,6 +272,9 @@ impl Client {
/// Gets a access token used in all the api calls.
pub async fn get_access_token(&mut self) -> Result<(), Box<dyn std::error::Error>> {
if !self.access_token_expired() {
return Ok(());
}
let res = self
.client
.post(format!("{}/v1/oauth2/token", self.endpoint()).as_str())
@ -303,15 +315,14 @@ mod tests {
let clientid = env::var("PAYPAL_CLIENTID").unwrap();
let secret = env::var("PAYPAL_SECRET").unwrap();
let mut client = Client::new(clientid, secret, true);
let client = Client::new(clientid, secret, true);
assert_eq!(client.get_access_token().await.is_err(), false, "should not error");
client
}
#[tokio::test]
async fn test_order() {
let client = create_client().await;
let mut client = create_client().await;
let order = OrderPayload::new(Intent::Authorize, vec![PurchaseUnit::new(Amount::new("EUR", "10.0"))]);

View file

@ -4,9 +4,9 @@
//!
//! Reference: https://developer.paypal.com/docs/api/orders/v2/
use crate::common::*;
use crate::errors;
use serde::{Deserialize, Serialize};
use crate::common::*;
/// The intent to either capture payment immediately or authorize a payment for an order after order creation.
#[derive(Debug, Serialize, Deserialize, Eq, PartialEq)]
@ -740,16 +740,16 @@ pub struct Order {
impl super::Client {
/// Creates an order. Supports orders with only one purchase unit.
pub async fn create_order(
&self,
&mut self,
order: OrderPayload,
header_params: crate::HeaderParams,
) -> Result<Order, Box<dyn std::error::Error>> {
let builder = {
self.setup_headers(
self.client
.post(&format!("{}/v2/checkout/orders", self.endpoint())),
self.client.post(&format!("{}/v2/checkout/orders", self.endpoint())),
header_params,
)
.await
};
let res = builder.json(&order).send().await?;
@ -763,7 +763,7 @@ impl super::Client {
/// Used internally for order requests that have no body.
async fn build_endpoint_order<S: std::fmt::Display, A: std::fmt::Display>(
&self,
&mut self,
order_id: S,
endpoint: A,
post: bool,
@ -771,13 +771,15 @@ impl super::Client {
) -> Result<Order, Box<dyn std::error::Error>> {
let format = format!("{}/v2/checkout/orders/{}/{}", self.endpoint(), order_id, endpoint);
let builder = self.setup_headers(
let builder = self
.setup_headers(
match post {
true => self.client.post(&format),
false => self.client.get(&format),
},
header_params,
);
)
.await;
let res = builder.send().await?;
@ -798,7 +800,7 @@ impl super::Client {
///
/// 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>(
&self,
&mut self,
id: S,
intent: Option<Intent>,
purchase_units: Option<Vec<PurchaseUnit>>,
@ -811,7 +813,8 @@ impl super::Client {
for (i, unit) in p_units.iter().enumerate() {
let unit_str = serde_json::to_string(&unit)?;
let mut unit_json = format!(r#"
let mut unit_json = format!(
r#"
{{
"op": "replace",
"path": "/purchase_units/@reference_id='{reference_id}'",
@ -819,7 +822,8 @@ impl super::Client {
}}
"#,
reference_id = unit.reference_id.clone().unwrap_or_else(|| String::from("default")),
unit = unit_str);
unit = unit_str
);
if i < p_units.len() - 1 {
unit_json += ",";
@ -832,7 +836,7 @@ impl super::Client {
if let Some(x) = intent {
let intent_str = match x {
Intent::Authorize => String::from("AUTHORIZE"),
Intent::Capture => String::from("CAPTURE")
Intent::Capture => String::from("CAPTURE"),
};
intent_json = format!(
@ -848,7 +852,7 @@ impl super::Client {
}
let final_json = {
if intent_json != "" && units_json != "" {
if !intent_json.is_empty() && !units_json.is_empty() {
format!("[{},{}]", intent_json, units_json)
} else {
format!("[{}{}]", intent_json, units_json)
@ -864,6 +868,7 @@ impl super::Client {
..Default::default()
},
)
.await
};
let res = builder.body(final_json.clone()).send().await?;
@ -877,7 +882,7 @@ impl super::Client {
/// Shows details for an order, by ID.
pub async fn show_order_details<S: std::fmt::Display>(
&self,
&mut self,
order_id: S,
) -> Result<Order, Box<dyn std::error::Error>> {
self.build_endpoint_order(order_id, "", false, crate::HeaderParams::default())
@ -888,7 +893,7 @@ impl super::Client {
/// 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 capture_order<S: std::fmt::Display>(
&self,
&mut self,
order_id: S,
header_params: crate::HeaderParams,
) -> Result<Order, Box<dyn std::error::Error>> {
@ -900,7 +905,7 @@ impl super::Client {
/// 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_order<S: std::fmt::Display>(
&self,
&mut self,
order_id: S,
header_params: crate::HeaderParams,
) -> Result<Order, Box<dyn std::error::Error>> {