Change errors

This commit is contained in:
eraden 2022-04-26 20:03:42 +02:00
parent 9aeb78d64e
commit 4886a76a76
2 changed files with 138 additions and 96 deletions

View File

@ -96,7 +96,7 @@ impl From<Product> for pay_u::Product {
} }
#[derive(Debug, actix::Message)] #[derive(Debug, actix::Message)]
#[rtype(result = "Result<PaymentResult>")] #[rtype(result = "Result<pay_u::OrderId>")]
pub struct RequestPayment { pub struct RequestPayment {
pub products: Vec<Product>, pub products: Vec<Product>,
pub redirect_uri: String, pub redirect_uri: String,
@ -106,11 +106,13 @@ pub struct RequestPayment {
pub customer_ip: String, pub customer_ip: String,
} }
pay_async_handler!(RequestPayment, request_payment, pay_u::OrderId);
pub(crate) async fn request_payment( pub(crate) async fn request_payment(
msg: RequestPayment, msg: RequestPayment,
client: PayUClient, client: PayUClient,
) -> Result<pay_u::OrderId> { ) -> Result<pay_u::OrderId> {
let mut client = &mut *client.lock(); let client = &mut *client.lock();
let order = client let order = client
.create_order( .create_order(
OrderCreateRequest::new(msg.buyer.into(), msg.customer_ip, msg.currency) OrderCreateRequest::new(msg.buyer.into(), msg.customer_ip, msg.currency)

View File

@ -1,7 +1,6 @@
mod deserialize; mod deserialize;
mod serialize; mod serialize;
use std::fmt::Display;
use std::sync::Arc; use std::sync::Arc;
use reqwest::redirect; use reqwest::redirect;
@ -40,19 +39,39 @@ pub enum Error {
Refund, Refund,
#[error("Create order returned invalid response")] #[error("Create order returned invalid response")]
CreateOrder, CreateOrder,
<<<<<<< HEAD
#[error("Operation failed with {0:?}")]
OpFailed(Status),
=======
#[error("Failed to fetch order transactions")] #[error("Failed to fetch order transactions")]
OrderTransactions, OrderTransactions,
>>>>>>> 3ec4b209cd6a2ebefb4419083f9379794784d11c #[error("PayU rejected to create order. {status_code:?}")]
CreateFailed {
status_code: String,
status_desc: Option<String>,
code: Option<String>,
severity: Option<String>,
code_literal: Option<CodeLiteral>,
},
#[error("PayU rejected to perform refund. {status_code:?}")]
RefundFailed {
status_code: String,
status_desc: Option<String>,
code: Option<String>,
severity: Option<String>,
code_literal: Option<CodeLiteral>,
},
} }
pub type Result<T> = std::result::Result<T, Error>; pub type Result<T> = std::result::Result<T, Error>;
/// PayU internal order id /// PayU internal order id
#[derive(derive_more::Display, derive_more::From, derive_more::Deref)] #[derive(
Debug,
Clone,
serde::Deserialize,
serde::Serialize,
derive_more::Display,
derive_more::From,
derive_more::Deref,
)]
#[serde(transparent)]
pub struct OrderId(pub String); pub struct OrderId(pub String);
impl OrderId { impl OrderId {
@ -546,8 +565,8 @@ impl Status {
} }
} }
pub mod result { pub mod res {
use crate::{Refund, Status}; use crate::{OrderId, Refund, Status};
#[derive(serde::Deserialize, Debug)] #[derive(serde::Deserialize, Debug)]
#[serde(rename_all = "camelCase")] #[serde(rename_all = "camelCase")]
@ -557,7 +576,7 @@ pub mod result {
/// Client should be redirected to this URI /// Client should be redirected to this URI
pub redirect_uri: String, pub redirect_uri: String,
/// This should be connected to your own order /// This should be connected to your own order
pub order_id: String, pub order_id: OrderId,
/// This is YOUR_EXT_ORDER_ID /// This is YOUR_EXT_ORDER_ID
pub ext_order_id: Option<String>, pub ext_order_id: Option<String>,
} }
@ -569,6 +588,73 @@ pub mod result {
pub refund: Option<Refund>, pub refund: Option<Refund>,
pub status: Status, pub status: Status,
} }
#[derive(serde::Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct TransactionPayMethod {
pub value: String,
}
#[derive(serde::Deserialize)]
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
pub enum CardProfile {
Consumer,
Business,
}
#[derive(serde::Deserialize)]
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
pub enum CardClassification {
Debit,
Credit,
}
#[derive(serde::Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct TransactionCartData {
/// // "543402******4014",
pub card_number_masked: String,
/// MC (MasterCard/Maestro), VS (Visa)
/// Example; "MC"
pub card_scheme: String,
pub card_profile: CardProfile,
pub card_classification: CardClassification,
/// Example: "000"
pub card_response_code: String,
/// Example: "000 - OK"
pub card_response_code_desc: String,
/// Example: "5"
pub card_eci_code: String,
/// Example: "AY",
pub card3ds_status: String,
/// Example: "PL",
pub card_bin_country: String,
/// Example: "MCC0111LL1121"
pub first_transaction_id: String,
}
#[derive(serde::Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct TransactionCardInstallmentProposal {}
#[derive(serde::Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct TransactionCart {
pub cart_data: TransactionCartData,
pub card_installment_proposal: TransactionCardInstallmentProposal,
}
#[derive(serde::Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct Transaction {
pub pay_method: TransactionPayMethod,
pub payment_flow: String,
}
#[derive(serde::Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct Transactions {
pub transactions: Vec<Transaction>,
}
} }
#[derive(Deserialize, Serialize, Debug)] #[derive(Deserialize, Serialize, Debug)]
@ -614,9 +700,8 @@ pub struct Refund {
pub mod notify { pub mod notify {
use serde::Deserialize; use serde::Deserialize;
use crate::OrderId;
use super::deserialize; use super::deserialize;
use crate::OrderId;
/// Payment notification object received on [super::Order].[notify_url] /// Payment notification object received on [super::Order].[notify_url]
#[derive(Deserialize, Debug)] #[derive(Deserialize, Debug)]
@ -692,76 +777,6 @@ pub mod notify {
} }
} }
pub mod res {
#[derive(serde::Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct TransactionPayMethod {
pub value: String,
}
#[derive(serde::Deserialize)]
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
pub enum CardProfile {
Consumer,
Business,
}
#[derive(serde::Deserialize)]
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
pub enum CardClassification {
Debit,
Credit,
}
#[derive(serde::Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct TransactionCartData {
/// // "543402******4014",
pub card_number_masked: String,
/// MC (MasterCard/Maestro), VS (Visa)
/// Example; "MC"
pub card_scheme: String,
pub card_profile: CardProfile,
pub card_classification: CardClassification,
/// Example: "000"
pub card_response_code: String,
/// Example: "000 - OK"
pub card_response_code_desc: String,
/// Example: "5"
pub card_eci_code: String,
/// Example: "AY",
pub card3ds_status: String,
/// Example: "PL",
pub card_bin_country: String,
/// Example: "MCC0111LL1121"
pub first_transaction_id: String,
}
#[derive(serde::Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct TransactionCardInstallmentProposal {}
#[derive(serde::Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct TransactionCart {
pub cart_data: TransactionCartData,
pub card_installment_proposal: TransactionCardInstallmentProposal,
}
#[derive(serde::Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct Transaction {
pub pay_method: TransactionPayMethod,
pub payment_flow: String,
}
#[derive(serde::Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct Transactions {
pub transactions: Vec<Transaction>,
}
}
pub struct Client { pub struct Client {
sandbox: bool, sandbox: bool,
merchant_pos_id: MerchantPosId, merchant_pos_id: MerchantPosId,
@ -856,7 +871,7 @@ impl Client {
/// .await; /// .await;
/// } /// }
/// ``` /// ```
pub async fn create_order(&mut self, order: OrderCreateRequest) -> Result<result::CreateOrder> { pub async fn create_order(&mut self, order: OrderCreateRequest) -> Result<res::CreateOrder> {
self.authorize().await?; self.authorize().await?;
if order.total_amount if order.total_amount
!= order != order
@ -887,12 +902,25 @@ impl Client {
.text() .text()
.await?; .await?;
log::trace!("Response: {}", text); log::trace!("Response: {}", text);
let res: result::CreateOrder = serde_json::from_str(&text).map_err(|e| { let res: res::CreateOrder = serde_json::from_str(&text).map_err(|e| {
log::error!("{e:?}"); log::error!("{e:?}");
Error::CreateOrder Error::CreateOrder
})?; })?;
if res.status.status_code != "Success" { if res.status.status_code != "Success" {
return Err(Error::OpFailed(res.status)); let Status {
status_code,
status_desc,
code,
severity,
code_literal,
} = res.status;
return Err(Error::CreateFailed {
status_desc,
status_code,
code,
severity,
code_literal,
});
} }
Ok(res) Ok(res)
} }
@ -927,7 +955,7 @@ impl Client {
/// .sandbox(); /// .sandbox();
/// let res = client /// let res = client
/// .partial_refund( /// .partial_refund(
/// "H9LL64F37H160126GUEST000P01", /// OrderId::new("H9LL64F37H160126GUEST000P01"),
/// RefundRequest::new("Refund", 1000), /// RefundRequest::new("Refund", 1000),
/// ) /// )
/// .await; /// .await;
@ -937,7 +965,7 @@ impl Client {
&mut self, &mut self,
order_id: OrderId, order_id: OrderId,
refund: RefundRequest, refund: RefundRequest,
) -> Result<result::PartialRefund> { ) -> Result<res::PartialRefund> {
self.authorize().await?; self.authorize().await?;
if refund.description().trim().is_empty() { if refund.description().trim().is_empty() {
return Err(Error::NoDescription); return Err(Error::NoDescription);
@ -955,12 +983,25 @@ impl Client {
.text() .text()
.await?; .await?;
log::trace!("Response: {}", text); log::trace!("Response: {}", text);
let res: result::PartialRefund = serde_json::from_str(&text).map_err(|e| { let res: res::PartialRefund = serde_json::from_str(&text).map_err(|e| {
log::error!("Invalid PayU response {e:?}"); log::error!("Invalid PayU response {e:?}");
Error::Refund Error::Refund
})?; })?;
if res.status.status_code.as_str() != "Success" { if res.status.status_code.as_str() != "Success" {
return Err(Error::OpFailed(res.status)); let Status {
status_code,
status_desc,
code,
severity,
code_literal,
} = res.status;
return Err(Error::RefundFailed {
status_desc,
status_code,
code,
severity,
code_literal,
});
} }
Ok(res) Ok(res)
} }
@ -984,7 +1025,7 @@ impl Client {
/// .with_bearer("d9a4536e-62ba-4f60-8017-6053211d3f47", 2000) /// .with_bearer("d9a4536e-62ba-4f60-8017-6053211d3f47", 2000)
/// .sandbox(); /// .sandbox();
/// let res = client /// let res = client
/// .order_transactions("H9LL64F37H160126GUEST000P01") /// .order_transactions(OrderId::new("H9LL64F37H160126GUEST000P01"))
/// .await; /// .await;
/// } /// }
/// ``` /// ```
@ -1099,7 +1140,6 @@ mod tests {
RefundRequest::new("Refund", 1000), RefundRequest::new("Refund", 1000),
) )
.await; .await;
eprintln!("{:?}", res);
assert!(matches!(res, Ok(_))); assert!(matches!(res, Ok(_)));
} }
@ -1108,7 +1148,7 @@ mod tests {
#[test] #[test]
fn check_accepted_refund_json() { fn check_accepted_refund_json() {
let res = serde_json::from_str::<result::PartialRefund>(include_str!( let res = serde_json::from_str::<res::PartialRefund>(include_str!(
"../tests/responses/accepted_refund.json" "../tests/responses/accepted_refund.json"
)); ));
assert!(res.is_ok()); assert!(res.is_ok());
@ -1150,14 +1190,14 @@ mod tests {
} }
#[test] #[test]
fn check_rejection_json() { fn check_rejection_json() {
let res = serde_json::from_str::<result::PartialRefund>(include_str!( let res = serde_json::from_str::<res::PartialRefund>(include_str!(
"../tests/responses/rejection.json" "../tests/responses/rejection.json"
)); ));
assert!(res.is_ok()); assert!(res.is_ok());
} }
#[test] #[test]
fn check_custom_literal_json() { fn check_custom_literal_json() {
let res = serde_json::from_str::<result::PartialRefund>(include_str!( let res = serde_json::from_str::<res::PartialRefund>(include_str!(
"../tests/responses/custom_code_literal.json" "../tests/responses/custom_code_literal.json"
)); ));
assert!(res.is_ok()); assert!(res.is_ok());