From f55f2c4a0dafd4b0347d619f91edbf876aed2b22 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adrian=20Wo=C5=BAniak?= Date: Wed, 27 Apr 2022 09:13:42 +0200 Subject: [PATCH] Fix docs, fix get info --- Cargo.lock | 2 +- pay_u/Cargo.toml | 2 +- pay_u/README.md | 28 +++++++--- pay_u/src/lib.rs | 137 ++++++++++++++++++++++++++++++++++++----------- 4 files changed, 129 insertions(+), 40 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index ad63d22..7e3d576 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2135,7 +2135,7 @@ checksum = "0c520e05135d6e763148b6426a837e239041653ba7becd2e538c076c738025fc" [[package]] name = "pay_u" -version = "0.1.4" +version = "0.1.6" dependencies = [ "chrono", "derive_more", diff --git a/pay_u/Cargo.toml b/pay_u/Cargo.toml index 4f5083b..6df8610 100644 --- a/pay_u/Cargo.toml +++ b/pay_u/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "pay_u" description = "PayU Rest API wrapper" -version = "0.1.5" +version = "0.1.6" edition = "2021" license = "MIT" diff --git a/pay_u/README.md b/pay_u/README.md index 337eded..925674e 100644 --- a/pay_u/README.md +++ b/pay_u/README.md @@ -10,8 +10,8 @@ cargo add pay_u ```rust async fn usage() { - let client_id = std::env::var("PAYU_CLIENT_ID").unwrap(); - let client_secret = std::env::var("PAYU_CLIENT_SECRET").unwrap(); + let client_id = ClientId::new(std::env::var("PAYU_CLIENT_ID").unwrap()); + let client_secret = ClientSecret::new(std::env::var("PAYU_CLIENT_SECRET").unwrap()); let merchant_id = std::env::var("PAYU_CLIENT_MERCHANT_ID").unwrap().parse::().map(MerchantPosId::from).unwrap(); let mut client = Client::new(client_id, client_secret, merchant_id); client.authorize().await.expect("Invalid credentials"); @@ -38,13 +38,27 @@ async fn usage() { .with_product(Product::new("HDMI cable", 6000, 1)), ) .await; - + + // partial refund let _res = client - .partial_refund( - "H9LL64F37H160126GUEST000P01", - RefundRequest::new("Refund", 1000), + .refund( + OrderId::new("H9LL64F37H160126GUEST000P01"), + RefundRequest::new("Refund", Some(1000)), ) .await; + // Full refund + let _res = client + .refund( + OrderId::new("H9LL64F37H160126GUEST000P01"), + RefundRequest::new("Refund", None), + ) + .await; + + // Order details + let _res = client.order_details(OrderId::new("H9LL64F37H160126GUEST000P01")).await; + + // Transactions + let _res = client.order_transactions(OrderId::new("H9LL64F37H160126GUEST000P01")).await; } ``` @@ -60,7 +74,7 @@ async fn checkout(session: Data, db: Data, payu: Data, pub refund: Option, pub status: Status, } + + #[derive(serde::Deserialize, Debug)] + #[serde(rename_all = "camelCase")] + pub struct Refunds { + pub refunds: Vec, + } + #[derive(serde::Deserialize, Debug)] #[serde(rename_all = "camelCase")] pub struct TransactionPayMethod { @@ -1068,7 +1077,7 @@ impl Client { /// /// ``` /// # use pay_u::*; - /// async fn perform_refund() { + /// async fn partial_refund() { /// let mut client = Client::new(ClientId::new("145227"), ClientSecret::new("12f071174cb7eb79d4aac5bc2f07563f"), MerchantPosId::new(300746)) /// .with_bearer("d9a4536e-62ba-4f60-8017-6053211d3f47", 2000) /// .sandbox(); @@ -1095,7 +1104,13 @@ impl Client { &mut self, order_id: OrderId, refund: RefundRequest, - ) -> Result { + ) -> Result { + #[derive(Serialize, Debug)] + #[serde(rename_all = "camelCase")] + struct RefundWrapper { + refund: RefundRequest, + } + self.authorize().await?; if refund.description().trim().is_empty() { return Err(Error::NoDescription); @@ -1107,13 +1122,13 @@ impl Client { let text = client .post(path) .bearer_auth(bearer) - .json(&refund) + .json(&RefundWrapper { refund }) .send() .await? .text() .await?; log::trace!("Response: {}", text); - let res: res::PartialRefund = serde_json::from_str(&text).map_err(|e| { + let res: res::RefundDetails = serde_json::from_str(&text).map_err(|e| { log::error!("Invalid PayU response {e:?}"); Error::Refund })?; @@ -1158,15 +1173,17 @@ impl Client { let path = format!("{}/orders/{}", self.base_url(), order_id); let client = get_client!(self); let text = client - .post(path) + .get(path) .bearer_auth(bearer) .send() .await? .text() .await?; log::trace!("Response: {}", text); + dbg!(&text); let mut res: OrdersInfo = serde_json::from_str(&text).map_err(|e| { log::error!("{e:?}"); + dbg!(e); Error::OrderDetails })?; if !res.status.is_success() { @@ -1226,19 +1243,65 @@ impl Client { let path = format!("{}/orders/{}/transactions", self.base_url(), order_id); let client = get_client!(self); let text = client - .post(path) + .get(path) .bearer_auth(bearer) .send() .await? .text() .await?; log::trace!("Response: {}", text); + dbg!(&text); serde_json::from_str(&text).map_err(|e| { log::error!("{e:?}"); + dbg!(e); Error::OrderTransactions }) } + /// The transaction retrieve request message enables you to retrieve the + /// details of transactions created for an order. + /// + /// Using this endpoint is extremely useful if you would like to get bank + /// account details or card details. + /// + /// > Please note that although card details are available right after + /// > transaction has been processed, the bank details may be available + /// > either after few minutes or on the next business day, depending on the + /// > bank. + /// + /// # Examples + /// + /// ``` + /// # use pay_u::*; + /// async fn order_transactions() { + /// let mut client = Client::new(ClientId::new("145227"), ClientSecret::new("12f071174cb7eb79d4aac5bc2f07563f"), MerchantPosId::new(300746)) + /// .with_bearer("d9a4536e-62ba-4f60-8017-6053211d3f47", 2000) + /// .sandbox(); + /// let res = client + /// .order_transactions(OrderId::new("H9LL64F37H160126GUEST000P01")) + /// .await; + /// } + /// ``` + pub async fn order_refunds(&mut self, order_id: OrderId) -> Result> { + self.authorize().await?; + let bearer = self.bearer.as_ref().cloned().unwrap_or_default(); + let path = format!("{}/orders/{}/refunds", self.base_url(), order_id); + let client = get_client!(self); + let text = client + .get(path) + .bearer_auth(bearer) + .send() + .await? + .text() + .await?; + log::trace!("Response: {}", text); + let res::Refunds { refunds, .. } = serde_json::from_str(&text).map_err(|e| { + log::error!("{e:?}"); + Error::OrderRefunds + })?; + Ok(refunds) + } + /// Get or refresh token pub async fn authorize(&mut self) -> Result { use chrono::{Duration, Utc}; @@ -1292,6 +1355,7 @@ impl Client { #[cfg(test)] mod tests { use super::*; + use crate::res::CreateOrder; fn build_client() -> Client { dotenv::dotenv().ok(); @@ -1304,9 +1368,8 @@ mod tests { .with_bearer("d9a4536e-62ba-4f60-8017-6053211d3f47", 999999) } - #[tokio::test] - async fn create_order() { - let res = build_client() + async fn perform_create_order(client: &mut Client) -> Result { + client .create_order( OrderCreateRequest::new( Buyer::new("john.doe@example.com", "654111654", "John", "Doe", "pl"), @@ -1323,7 +1386,13 @@ mod tests { .into_iter(), ), ) - .await; + .await + } + + #[tokio::test] + async fn create_order() { + let mut client = build_client(); + let res = perform_create_order(&mut client).await; if res.is_err() { eprintln!("create_order res is {res:?}"); @@ -1333,11 +1402,12 @@ mod tests { #[tokio::test] async fn partial_refund() { - let res = build_client() - .refund( - OrderId::new("H9LL64F37H160126GUEST000P01"), - RefundRequest::new("Refund", Some(1000)), - ) + let mut client = build_client(); + let CreateOrder { order_id, .. } = perform_create_order(&mut client) + .await + .expect("Failed to create"); + let res = client + .refund(order_id, RefundRequest::new("Refund", Some(10))) .await; if res.is_err() { @@ -1348,11 +1418,12 @@ mod tests { #[tokio::test] async fn full_refund() { - let res = build_client() - .refund( - OrderId::new("H9LL64F37H160126GUEST000P01"), - RefundRequest::new("Refund", None), - ) + let mut client = build_client(); + let CreateOrder { order_id, .. } = perform_create_order(&mut client) + .await + .expect("Failed to create"); + let res = client + .refund(order_id, RefundRequest::new("Refund", None)) .await; if res.is_err() { @@ -1363,21 +1434,25 @@ mod tests { #[tokio::test] async fn order_details() { - let res = build_client() - .order_details(OrderId::new("H9LL64F37H160126GUEST000P01")) - .await; + let mut client = build_client(); + let CreateOrder { order_id, .. } = perform_create_order(&mut client) + .await + .expect("Failed to create"); + let res = client.order_details(order_id).await; if res.is_err() { eprintln!("order_details res is {res:?}"); } - assert!(matches!(res, Err(Error::OrderDetails))); + assert!(matches!(res, Ok(res::OrderInfo { .. }))); } #[tokio::test] async fn order_transactions() { - let res = build_client() - .order_transactions(OrderId::new("H9LL64F37H160126GUEST000P01")) - .await; + let mut client = build_client(); + let CreateOrder { order_id, .. } = perform_create_order(&mut client) + .await + .expect("Failed to create"); + let res = client.order_transactions(order_id).await; if res.is_err() { eprintln!("order_transactions res is {res:?}"); } @@ -1386,7 +1461,7 @@ mod tests { #[test] fn check_accepted_refund_json() { - let res = serde_json::from_str::(include_str!( + let res = serde_json::from_str::(include_str!( "../tests/responses/accepted_refund.json" )); assert!(res.is_ok()); @@ -1428,14 +1503,14 @@ mod tests { } #[test] fn check_rejection_json() { - let res = serde_json::from_str::(include_str!( + let res = serde_json::from_str::(include_str!( "../tests/responses/rejection.json" )); assert!(res.is_ok()); } #[test] fn check_custom_literal_json() { - let res = serde_json::from_str::(include_str!( + let res = serde_json::from_str::(include_str!( "../tests/responses/custom_code_literal.json" )); assert!(res.is_ok());