From cbc2a30e0e7d6440b64f6ce0a60ccda606186ee9 Mon Sep 17 00:00:00 2001 From: eraden Date: Mon, 25 Apr 2022 08:19:20 +0200 Subject: [PATCH] Add payment --- .env | 4 +- Cargo.lock | 16 +---- api/Cargo.toml | 5 +- api/src/actors/payment_manager.rs | 104 ++++++++++++++++++++++++++++-- api/src/main.rs | 16 ++++- 5 files changed, 121 insertions(+), 24 deletions(-) diff --git a/.env b/.env index 61af1b4..e7b6789 100644 --- a/.env +++ b/.env @@ -9,4 +9,6 @@ SENDGRID_SECRET=SG.CUWRM-eoQfGJNqSU2bbwkg.NW5aBy5vZueCSOwIIyWUBqq5USChGiwAFrWzre SENDGRID_API_KEY=CUWRM-eoQfGJNqSU2bbwkg SMTP_FROM=adrian.wozniak@ita-prog.pl -PAY_U_BEARER=d9a4536e-62ba-4f60-8017-6053211d3f47 +PAYU_CLIENT_ID="145227" +PAYU_CLIENT_SECRET="12f071174cb7eb79d4aac5bc2f07563f" +PAYU_CLIENT_MERCHANT_ID=300746 diff --git a/Cargo.lock b/Cargo.lock index cf8fac4..f25a38d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -613,7 +613,7 @@ dependencies = [ "oauth2", "parking_lot 0.12.0", "password-hash", - "pay_u 0.1.3", + "pay_u", "pretty_env_logger", "rand_core", "sendgrid", @@ -2133,20 +2133,6 @@ version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0c520e05135d6e763148b6426a837e239041653ba7becd2e538c076c738025fc" -[[package]] -name = "pay_u" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4eca87ee08eda0742042079ef48c3eb478a3f43dd535b499c88c72e532f6cfb6" -dependencies = [ - "chrono", - "log", - "reqwest", - "serde", - "serde_json", - "thiserror", -] - [[package]] name = "pay_u" version = "0.1.4" diff --git a/api/Cargo.toml b/api/Cargo.toml index 9d049b3..542cec3 100644 --- a/api/Cargo.toml +++ b/api/Cargo.toml @@ -65,4 +65,7 @@ jemallocator = { version = "0.3.2", features = [] } sendgrid = { version = "0.17.4", features = ["async"] } -pay_u = { version = '0.1.3', features = ["single-client"] } +pay_u = { path = "../pay_u", version = '0.1', features = ["single-client"] } + +[dev-dependencies] +pay_u = { path = "../pay_u", version = "0.1", features = ["single-client"] } diff --git a/api/src/actors/payment_manager.rs b/api/src/actors/payment_manager.rs index 426dd12..1497745 100644 --- a/api/src/actors/payment_manager.rs +++ b/api/src/actors/payment_manager.rs @@ -1,21 +1,113 @@ -use pay_u::MerchantPosId; +use std::sync::Arc; +use parking_lot::Mutex; +use pay_u::{MerchantPosId, OrderCreateRequest}; + +use crate::model; +use crate::model::Product; + +#[macro_export] +macro_rules! pay_async_handler { + ($msg: ty, $async: ident, $res: ty) => { + impl actix::Handler<$msg> for PaymentManager { + type Result = actix::ResponseActFuture>; + + fn handle(&mut self, msg: $msg, _ctx: &mut Self::Context) -> Self::Result { + use actix::WrapFuture; + let db = self.client.clone(); + Box::pin(async { $async(msg, db).await }.into_actor(self)) + } + } + }; +} + +pub type PayUClient = Arc>; + +#[derive(Debug, thiserror::Error)] +pub enum Error { + #[error("{0}")] + PayU(#[from] pay_u::Error), +} + +pub type Result = std::result::Result; + +#[derive(Clone)] pub struct PaymentManager { - client: pay_u::Client, + client: PayUClient, } impl PaymentManager { - pub fn new( + pub async fn build( client_id: ClientId, client_secret: ClientSecret, merchant_pos_id: MerchantPosId, - ) -> Self + ) -> Result where ClientId: Into, ClientSecret: Into, { let mut client = pay_u::Client::new(client_id, client_secret, merchant_pos_id); - client - Self { client } + client.authorize().await?; + Ok(Self { + client: Arc::new(Mutex::new(client)), + }) } } + +impl actix::Actor for PaymentManager { + type Context = actix::Context; +} + +pub struct PaymentResult { + pub redirect_uri: String, + pub payu_order_id: String, +} + +pub struct Buyer { + /// Required customer e-mail + pub email: String, + /// Required customer phone number + pub phone: String, + /// Required customer first name + pub first_name: String, + /// Required customer last name + pub last_name: String, + /// Required customer language + pub language: String, +} + +impl From for pay_u::Buyer { + fn from(b: Buyer) -> Self { + pay_u::Buyer::new(b.email, b.phone, b.first_name, b.last_name, b.language) + } +} + +impl From for pay_u::Product { + fn from(p: Product) -> Self { + todo!() + } +} + +#[derive(Debug, actix::Message)] +#[rtype(result = "Result")] +pub struct RequestPayment { + pub products: Vec, + pub redirect_uri: String, + pub currency: String, + pub description: String, + pub buyer: Buyer, + pub customer_ip: String, +} + +pub(crate) async fn request_payment( + msg: RequestPayment, + client: PayUClient, +) -> Result { + let mut client = &mut *client.lock(); + client.create_order( + OrderCreateRequest::new(msg.buyer.into(), msg.customer_ip, msg.currency) + .with_description(msg.description) + .with_notify_url(msg.redirect_uri) + .with_products(msg.products.into_iter().map(Into::into)), + ) +} diff --git a/api/src/main.rs b/api/src/main.rs index 173bf15..1ca762c 100644 --- a/api/src/main.rs +++ b/api/src/main.rs @@ -13,7 +13,7 @@ use jemallocator::Jemalloc; use password_hash::SaltString; use validator::{validate_email, validate_length}; -use crate::actors::{database, email_manager, order_manager, token_manager}; +use crate::actors::{database, email_manager, order_manager, payment_manager, token_manager}; use crate::email_manager::TestMail; use crate::logic::encrypt_password; use crate::model::{Email, Login, PassHash, Password, Role}; @@ -183,6 +183,19 @@ async fn server(opts: ServerOpts) -> Result<()> { let db = database::Database::build(&opts.db_url()).await?.start(); let token_manager = token_manager::TokenManager::new(db.clone()).start(); let order_manager = order_manager::OrderManager::new(db.clone()).start(); + let payment_manager = { + let client_id = std::env::var("PAYU_CLIENT_ID").expect("Missing PAYU_CLIENT_ID env"); + let client_secret = + std::env::var("PAYU_CLIENT_SECRET").expect("Missing PAYU_CLIENT_SECRET env"); + let merchant_id = std::env::var("PAYU_CLIENT_MERCHANT_ID") + .expect("Missing PAYU_CLIENT_MERCHANT_ID env") + .parse() + .expect("Variable PAYU_CLIENT_MERCHANT_ID must be number"); + payment_manager::PaymentManager::build(client_id, client_secret, merchant_id) + .await + .expect("Failed to start payment manager") + .start() + }; HttpServer::new(move || { App::new() @@ -197,6 +210,7 @@ async fn server(opts: ServerOpts) -> Result<()> { .app_data(Data::new(db.clone())) .app_data(Data::new(token_manager.clone())) .app_data(Data::new(order_manager.clone())) + .app_data(Data::new(payment_manager.clone())) .configure(routes::configure) // .default_service(web::to(HttpResponse::Ok)) })