initial commit
This commit is contained in:
parent
62c582ab72
commit
620e449f18
3
.gitignore
vendored
Normal file
3
.gitignore
vendored
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
/target
|
||||||
|
Cargo.lock
|
||||||
|
.env
|
17
Cargo.toml
Normal file
17
Cargo.toml
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
[package]
|
||||||
|
name = "paypal-rs"
|
||||||
|
version = "0.0.1"
|
||||||
|
authors = ["Edgar <git@edgarluque.com>"]
|
||||||
|
description = "A library that wraps the paypal api asynchronously."
|
||||||
|
keywords = ["paypal", "api", "async"]
|
||||||
|
categories = ["api-bindings", "asynchronous"]
|
||||||
|
readme = "README.md"
|
||||||
|
edition = "2018"
|
||||||
|
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
reqwest = { version = "0.10", features = ["json"] }
|
||||||
|
tokio = { version = "0.2", features = ["full"] }
|
||||||
|
serde = { version = "1.0", features = ["derive"] }
|
||||||
|
log = "0.4"
|
||||||
|
dotenv = "0.15.0"
|
5
README.md
Normal file
5
README.md
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
# paypal-rs
|
||||||
|
|
||||||
|
A rust library that wraps the [paypal api](https://developer.paypal.com/docs/api) asynchronously.
|
||||||
|
|
||||||
|
Currently in early development.
|
13
src/errors.rs
Normal file
13
src/errors.rs
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
use std::fmt;
|
||||||
|
use std::error::Error;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct GetAccessTokenError;
|
||||||
|
|
||||||
|
impl fmt::Display for GetAccessTokenError {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
write!(f, "error getting access token")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Error for GetAccessTokenError {}
|
83
src/lib.rs
Normal file
83
src/lib.rs
Normal file
|
@ -0,0 +1,83 @@
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests;
|
||||||
|
|
||||||
|
pub mod errors;
|
||||||
|
|
||||||
|
use serde::Deserialize;
|
||||||
|
use std::time::{Duration, Instant};
|
||||||
|
|
||||||
|
pub const LIVE_ENDPOINT: &str = "https://api.paypal.com";
|
||||||
|
pub const SANDBOX_ENDPOINT: &str = "https://api.sandbox.paypal.com";
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize)]
|
||||||
|
pub struct AccessToken {
|
||||||
|
pub scope: String,
|
||||||
|
pub access_token: String,
|
||||||
|
pub token_type: String,
|
||||||
|
pub app_id: String,
|
||||||
|
pub expires_in: u64,
|
||||||
|
pub nonce: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct Auth<'a> {
|
||||||
|
pub client_id: &'a str,
|
||||||
|
pub secret: &'a str,
|
||||||
|
pub access_token: Option<AccessToken>,
|
||||||
|
pub expires: Option<Instant>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct Client<'a> {
|
||||||
|
pub client: reqwest::Client,
|
||||||
|
pub sandbox: bool,
|
||||||
|
pub auth: Auth<'a>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Client<'a> {
|
||||||
|
pub fn new(client_id: &'a str, secret: &'a str, sandbox: bool) -> Client<'a> {
|
||||||
|
Client {
|
||||||
|
client: reqwest::Client::new(),
|
||||||
|
sandbox,
|
||||||
|
auth: Auth {
|
||||||
|
client_id,
|
||||||
|
secret,
|
||||||
|
access_token: None,
|
||||||
|
expires: None,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn endpoint(&self) -> &str {
|
||||||
|
if self.sandbox {
|
||||||
|
SANDBOX_ENDPOINT
|
||||||
|
} else {
|
||||||
|
LIVE_ENDPOINT
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn get_access_token(&mut self) -> Result<(), Box<dyn std::error::Error>> {
|
||||||
|
let res = self
|
||||||
|
.client
|
||||||
|
.post(format!("{}/v1/oauth2/token", self.endpoint()).as_str())
|
||||||
|
.basic_auth(self.auth.client_id, Some(self.auth.secret))
|
||||||
|
.header("Content-Type", "x-www-form-urlencoded")
|
||||||
|
.header("Accept", "application/json")
|
||||||
|
.body("grant_type=client_credentials")
|
||||||
|
.send()
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
if res.status().is_success() {
|
||||||
|
let token = res.json::<AccessToken>().await?;
|
||||||
|
self.auth.expires = Some(Instant::now() + Duration::new(token.expires_in, 0));
|
||||||
|
self.auth.access_token = Some(token);
|
||||||
|
println!("{:#?}", self.auth);
|
||||||
|
} else {
|
||||||
|
println!("status = {:#?}", res.status());
|
||||||
|
println!("res = {:#?}", res);
|
||||||
|
return Err(Box::new(errors::GetAccessTokenError));
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
19
src/tests.rs
Normal file
19
src/tests.rs
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
use crate::*;
|
||||||
|
use dotenv::dotenv;
|
||||||
|
use std::env;
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn it_works() {
|
||||||
|
dotenv().ok();
|
||||||
|
let clientid = env::var("PAYPAL_CLIENTID").unwrap();
|
||||||
|
let secret = env::var("PAYPAL_SECRET").unwrap();
|
||||||
|
|
||||||
|
let mut client = Client::new(
|
||||||
|
clientid.as_str(),
|
||||||
|
secret.as_str(),
|
||||||
|
true,
|
||||||
|
);
|
||||||
|
|
||||||
|
assert_eq!(client.get_access_token().await.is_err(), false, "should not error");
|
||||||
|
println!("{:#?}", client);
|
||||||
|
}
|
Loading…
Reference in a new issue