From 252f5373ffb9f87a44ac14508b4db2b30cdcb6aa Mon Sep 17 00:00:00 2001 From: eraden Date: Wed, 4 May 2022 21:34:26 +0200 Subject: [PATCH 1/3] Add service order id --- api/src/actors/database/account_orders.rs | 41 ++++++++++++++++--- api/src/actors/payment_manager.rs | 11 ++++- api/src/main.rs | 2 +- api/src/model.rs | 2 + api/src/model/api.rs | 2 + build.rs | 5 +++ .../202204131841_init.sql | 0 .../202204160624_create_shopping_cart.sql | 0 .../202204161233_add_cart_state.sql | 0 .../202204172215_delivery_and_price.sql | 0 .../202204180708_add_payment_fields.sql | 0 .../202204181325_create_tokens.sql | 0 .../202204182135_add_uniq_add_time_format.sql | 0 .../202204191430_change_price.sql | 0 .../202204191555_add_account_state.sql | 0 .../202204271359_add_order_ext_id.sql | 0 .../202204300704_photos.sql | 0 ...04192613_add_service_order_id_to_order.sql | 2 + 18 files changed, 58 insertions(+), 7 deletions(-) create mode 100644 build.rs rename {db/migrate => migrations}/202204131841_init.sql (100%) rename {db/migrate => migrations}/202204160624_create_shopping_cart.sql (100%) rename {db/migrate => migrations}/202204161233_add_cart_state.sql (100%) rename {db/migrate => migrations}/202204172215_delivery_and_price.sql (100%) rename {db/migrate => migrations}/202204180708_add_payment_fields.sql (100%) rename {db/migrate => migrations}/202204181325_create_tokens.sql (100%) rename {db/migrate => migrations}/202204182135_add_uniq_add_time_format.sql (100%) rename {db/migrate => migrations}/202204191430_change_price.sql (100%) rename {db/migrate => migrations}/202204191555_add_account_state.sql (100%) rename {db/migrate => migrations}/202204271359_add_order_ext_id.sql (100%) rename {db/migrate => migrations}/202204300704_photos.sql (100%) create mode 100644 migrations/20220504192613_add_service_order_id_to_order.sql diff --git a/api/src/actors/database/account_orders.rs b/api/src/actors/database/account_orders.rs index d7415c4..4ec2326 100644 --- a/api/src/actors/database/account_orders.rs +++ b/api/src/actors/database/account_orders.rs @@ -31,7 +31,7 @@ pub(crate) async fn all_account_orders( ) -> Result> { sqlx::query_as( r#" -SELECT id, buyer_id, status, order_ext_id +SELECT id, buyer_id, status, order_ext_id, service_order_id FROM account_orders ORDER BY id DESC "#, @@ -74,7 +74,7 @@ pub(crate) async fn create_account_order( r#" INSERT INTO account_orders (buyer_id, status) VALUES ($1, $2, $3) -RETURNING id, buyer_id, status, order_ext_id +RETURNING id, buyer_id, status, order_ext_id, service_order_id "#, ) .bind(msg.buyer_id) @@ -148,7 +148,7 @@ pub(crate) async fn update_account_order( UPDATE account_orders SET buyer_id = $2 AND status = $3 AND order_id = $4 WHERE id = $1 -RETURNING id, buyer_id, status, order_ext_id +RETURNING id, buyer_id, status, order_ext_id, service_order_id "#, ) .bind(msg.id) @@ -185,7 +185,7 @@ pub(crate) async fn update_account_order_by_ext( UPDATE account_orders SET status = $2 WHERE order_ext_id = $1 -RETURNING id, buyer_id, status, order_ext_id +RETURNING id, buyer_id, status, order_ext_id, service_order_id "#, ) .bind(msg.order_ext_id) @@ -209,7 +209,7 @@ db_async_handler!(FindAccountOrder, find_account_order, AccountOrder); pub(crate) async fn find_account_order(msg: FindAccountOrder, db: PgPool) -> Result { sqlx::query_as( r#" -SELECT id, buyer_id, status, order_ext_id +SELECT id, buyer_id, status, order_ext_id, service_order_id FROM account_orders WHERE id = $1 "#, @@ -222,3 +222,34 @@ WHERE id = $1 super::Error::AccountOrder(Error::NotExists) }) } + +#[derive(actix::Message)] +#[rtype(result = "Result")] +pub struct SetOrderServiceId { + pub id: AccountOrderId, + pub service_order_id: String, +} + +db_async_handler!(SetOrderServiceId, set_order_service_id, AccountOrder); + +pub(crate) async fn set_order_service_id( + msg: SetOrderServiceId, + db: PgPool, +) -> Result { + sqlx::query_as( + r#" +UPDATE account_orders +SET service_order_id = $2 +WHERE id = $1 +RETURNING id, buyer_id, status, order_ext_id, service_order_id + "#, + ) + .bind(msg.id) + .bind(msg.service_order_id) + .fetch_one(&db) + .await + .map_err(|e| { + log::error!("{e:?}"); + super::Error::AccountOrder(Error::NotExists) + }) +} diff --git a/api/src/actors/payment_manager.rs b/api/src/actors/payment_manager.rs index 67d9366..108d7b2 100644 --- a/api/src/actors/payment_manager.rs +++ b/api/src/actors/payment_manager.rs @@ -236,7 +236,7 @@ pub(crate) async fn request_payment( let pay_u::res::CreateOrder { status: _, redirect_uri, - order_id: _, + order_id, ext_order_id: _, } = { client @@ -265,6 +265,15 @@ pub(crate) async fn request_payment( .await? }; + query_db!( + db, + database::SetOrderServiceId { + service_order_id: order_id.0, + id: db_order.id, + }, + Error::CreateOrder + ); + let order_items = query_db!( db, database::OrderItems { diff --git a/api/src/main.rs b/api/src/main.rs index f7b1249..416121e 100644 --- a/api/src/main.rs +++ b/api/src/main.rs @@ -100,7 +100,7 @@ async fn migrate(opts: MigrateOpts) -> Result<()> { let config = config::default_load(&opts); let db = database::Database::build(config).await?; let res: std::result::Result<(), MigrateError> = - sqlx::migrate!("../db/migrate").run(db.pool()).await; + sqlx::migrate!("../migrations").run(db.pool()).await; match res { Ok(()) => Ok(()), Err(e) => { diff --git a/api/src/model.rs b/api/src/model.rs index 5cd9c5c..d820608 100644 --- a/api/src/model.rs +++ b/api/src/model.rs @@ -535,6 +535,7 @@ pub struct AccountOrder { pub status: OrderStatus, pub order_id: Option, pub order_ext_id: uuid::Uuid, + pub service_order_id: Option, } #[derive(sqlx::FromRow, Serialize, Deserialize)] @@ -553,6 +554,7 @@ impl From for PublicAccountOrder { status, order_id, order_ext_id: _, + service_order_id: _, }: AccountOrder, ) -> Self { Self { diff --git a/api/src/model/api.rs b/api/src/model/api.rs index a421c61..684ec77 100644 --- a/api/src/model/api.rs +++ b/api/src/model/api.rs @@ -19,6 +19,7 @@ impl From<(Vec, Vec)> for AccountOrders { status, order_id, order_ext_id: _, + service_order_id: _, }| { AccountOrder { id, @@ -43,6 +44,7 @@ impl From<(model::AccountOrder, Vec)> for AccountOrder { status, order_id, order_ext_id: _, + service_order_id: _, }, mut items, ): (model::AccountOrder, Vec), diff --git a/build.rs b/build.rs new file mode 100644 index 0000000..7609593 --- /dev/null +++ b/build.rs @@ -0,0 +1,5 @@ +// generated by `sqlx migrate build-script` +fn main() { + // trigger recompilation when a new migration is added + println!("cargo:rerun-if-changed=migrations"); +} \ No newline at end of file diff --git a/db/migrate/202204131841_init.sql b/migrations/202204131841_init.sql similarity index 100% rename from db/migrate/202204131841_init.sql rename to migrations/202204131841_init.sql diff --git a/db/migrate/202204160624_create_shopping_cart.sql b/migrations/202204160624_create_shopping_cart.sql similarity index 100% rename from db/migrate/202204160624_create_shopping_cart.sql rename to migrations/202204160624_create_shopping_cart.sql diff --git a/db/migrate/202204161233_add_cart_state.sql b/migrations/202204161233_add_cart_state.sql similarity index 100% rename from db/migrate/202204161233_add_cart_state.sql rename to migrations/202204161233_add_cart_state.sql diff --git a/db/migrate/202204172215_delivery_and_price.sql b/migrations/202204172215_delivery_and_price.sql similarity index 100% rename from db/migrate/202204172215_delivery_and_price.sql rename to migrations/202204172215_delivery_and_price.sql diff --git a/db/migrate/202204180708_add_payment_fields.sql b/migrations/202204180708_add_payment_fields.sql similarity index 100% rename from db/migrate/202204180708_add_payment_fields.sql rename to migrations/202204180708_add_payment_fields.sql diff --git a/db/migrate/202204181325_create_tokens.sql b/migrations/202204181325_create_tokens.sql similarity index 100% rename from db/migrate/202204181325_create_tokens.sql rename to migrations/202204181325_create_tokens.sql diff --git a/db/migrate/202204182135_add_uniq_add_time_format.sql b/migrations/202204182135_add_uniq_add_time_format.sql similarity index 100% rename from db/migrate/202204182135_add_uniq_add_time_format.sql rename to migrations/202204182135_add_uniq_add_time_format.sql diff --git a/db/migrate/202204191430_change_price.sql b/migrations/202204191430_change_price.sql similarity index 100% rename from db/migrate/202204191430_change_price.sql rename to migrations/202204191430_change_price.sql diff --git a/db/migrate/202204191555_add_account_state.sql b/migrations/202204191555_add_account_state.sql similarity index 100% rename from db/migrate/202204191555_add_account_state.sql rename to migrations/202204191555_add_account_state.sql diff --git a/db/migrate/202204271359_add_order_ext_id.sql b/migrations/202204271359_add_order_ext_id.sql similarity index 100% rename from db/migrate/202204271359_add_order_ext_id.sql rename to migrations/202204271359_add_order_ext_id.sql diff --git a/db/migrate/202204300704_photos.sql b/migrations/202204300704_photos.sql similarity index 100% rename from db/migrate/202204300704_photos.sql rename to migrations/202204300704_photos.sql diff --git a/migrations/20220504192613_add_service_order_id_to_order.sql b/migrations/20220504192613_add_service_order_id_to_order.sql new file mode 100644 index 0000000..f212586 --- /dev/null +++ b/migrations/20220504192613_add_service_order_id_to_order.sql @@ -0,0 +1,2 @@ +alter table account_orders +ADD COLUMN service_order_id TEXT UNIQUE; From b489b4435a5c07d97e12e6a08d927ebfb6e1ef47 Mon Sep 17 00:00:00 2001 From: eraden Date: Wed, 4 May 2022 22:26:10 +0200 Subject: [PATCH 2/3] Add optional payment --- api/src/actors/database.rs | 25 ++++++ api/src/actors/database/account_orders.rs | 22 +++-- api/src/actors/database/products.rs | 56 ++++++++---- .../actors/database/shopping_cart_items.rs | 14 ++- api/src/actors/database/shopping_carts.rs | 9 +- api/src/actors/payment_manager.rs | 89 +++++++++++-------- api/src/config.rs | 6 ++ api/src/routes/public.rs | 6 +- api/src/routes/public/api_v1/restricted.rs | 5 ++ 9 files changed, 156 insertions(+), 76 deletions(-) diff --git a/api/src/actors/database.rs b/api/src/actors/database.rs index d8af411..13e2a03 100644 --- a/api/src/actors/database.rs +++ b/api/src/actors/database.rs @@ -33,6 +33,31 @@ macro_rules! db_async_handler { } } }; + ($msg: ty, $async: ident, $res: ty, $inner_async: ident) => { + async fn $inner_async(msg: $msg, pool: sqlx::PgPool) -> Result<$res> { + let mut t = pool.begin().await?; + match $async(msg, &mut t).await { + Ok(res) => { + t.commit().await?; + Ok(res) + } + Err(e) => { + let _ = t.rollback().await; + Err(e) + } + } + } + + impl actix::Handler<$msg> for Database { + type Result = actix::ResponseActFuture>; + + fn handle(&mut self, msg: $msg, _ctx: &mut Self::Context) -> Self::Result { + use actix::WrapFuture; + let pool = self.pool.clone(); + Box::pin(async { $inner_async(msg, pool).await }.into_actor(self)) + } + } + }; } #[macro_export] diff --git a/api/src/actors/database/account_orders.rs b/api/src/actors/database/account_orders.rs index 4ec2326..5954c5a 100644 --- a/api/src/actors/database/account_orders.rs +++ b/api/src/actors/database/account_orders.rs @@ -62,14 +62,17 @@ pub struct CreateAccountOrder { pub shopping_cart_id: ShoppingCartId, } -db_async_handler!(CreateAccountOrder, create_account_order, AccountOrder); +db_async_handler!( + CreateAccountOrder, + create_account_order, + AccountOrder, + inner_create_account_order +); pub(crate) async fn create_account_order( msg: CreateAccountOrder, - db: PgPool, + t: &mut sqlx::Transaction<'_, sqlx::Postgres>, ) -> Result { - let mut t = db.begin().await?; - let order: AccountOrder = match sqlx::query_as( r#" INSERT INTO account_orders (buyer_id, status) @@ -79,13 +82,12 @@ RETURNING id, buyer_id, status, order_ext_id, service_order_id ) .bind(msg.buyer_id) .bind(OrderStatus::Confirmed) - .fetch_one(&mut t) + .fetch_one(&mut *t) .await { Ok(order) => order, Err(e) => { log::error!("{e:?}"); - t.rollback().await.ok(); return Err(super::Error::AccountOrder(Error::CantCreate)); } }; @@ -97,13 +99,12 @@ RETURNING id, buyer_id, status, order_ext_id, service_order_id quantity: item.quantity, quantity_unit: item.quantity_unit, }, - &mut t, + &mut *t, ) .await { log::error!("{e:?}"); - t.rollback().await.ok(); return Err(super::Error::AccountOrder(Error::CantCreate)); } } @@ -113,18 +114,15 @@ RETURNING id, buyer_id, status, order_ext_id, service_order_id id: msg.shopping_cart_id, state: ShoppingCartState::Closed, }, - &mut t, + t, ) .await { log::error!("{e:?}"); - t.rollback().await.ok(); return Err(super::Error::AccountOrder(Error::CantCreate)); }; - t.commit().await.ok(); - Ok(order) } diff --git a/api/src/actors/database/products.rs b/api/src/actors/database/products.rs index f40e400..45c07e6 100644 --- a/api/src/actors/database/products.rs +++ b/api/src/actors/database/products.rs @@ -1,5 +1,4 @@ use actix::Message; -use sqlx::PgPool; use super::Result; use crate::database::Database; @@ -27,9 +26,12 @@ pub enum Error { #[rtype(result = "Result>")] pub struct AllProducts; -crate::db_async_handler!(AllProducts, all, Vec); +crate::db_async_handler!(AllProducts, all, Vec, inner_all); -pub(crate) async fn all(_msg: AllProducts, pool: PgPool) -> Result> { +pub(crate) async fn all<'e, E>(_msg: AllProducts, pool: E) -> Result> +where + E: sqlx::Executor<'e, Database = sqlx::Postgres>, +{ sqlx::query_as( r#" SELECT id, @@ -42,7 +44,7 @@ SELECT id, FROM products "#, ) - .fetch_all(&pool) + .fetch_all(pool) .await .map_err(|e| { log::error!("{e:?}"); @@ -61,9 +63,12 @@ pub struct CreateProduct { pub deliver_days_flag: Days, } -crate::db_async_handler!(CreateProduct, create_product, Product); +crate::db_async_handler!(CreateProduct, create_product, Product, inner_create_product); -pub(crate) async fn create_product(msg: CreateProduct, pool: PgPool) -> Result { +pub(crate) async fn create_product<'e, E>(msg: CreateProduct, pool: E) -> Result +where + E: sqlx::Executor<'e, Database = sqlx::Postgres>, +{ sqlx::query_as( r#" INSERT INTO products (name, short_description, long_description, category, price, deliver_days_flag) @@ -83,7 +88,7 @@ RETURNING id, .bind(msg.category) .bind(msg.price) .bind(msg.deliver_days_flag) - .fetch_one(&pool) + .fetch_one(pool) .await .map_err(|e| { log::error!("{e:?}"); @@ -103,9 +108,12 @@ pub struct UpdateProduct { pub deliver_days_flag: Days, } -crate::db_async_handler!(UpdateProduct, update_product, Product); +crate::db_async_handler!(UpdateProduct, update_product, Product, inner_update_product); -pub(crate) async fn update_product(msg: UpdateProduct, pool: PgPool) -> Result { +pub(crate) async fn update_product<'e, E>(msg: UpdateProduct, pool: E) -> Result +where + E: sqlx::Executor<'e, Database = sqlx::Postgres>, +{ sqlx::query_as( r#" UPDATE products @@ -132,7 +140,7 @@ RETURNING id, .bind(msg.category) .bind(msg.price) .bind(msg.deliver_days_flag) - .fetch_one(&pool) + .fetch_one(pool) .await .map_err(|e| { log::error!("{e:?}"); @@ -146,9 +154,17 @@ pub struct DeleteProduct { pub product_id: ProductId, } -crate::db_async_handler!(DeleteProduct, delete_product, Option); +crate::db_async_handler!( + DeleteProduct, + delete_product, + Option, + inner_delete_product +); -pub(crate) async fn delete_product(msg: DeleteProduct, pool: PgPool) -> Result> { +pub(crate) async fn delete_product<'e, E>(msg: DeleteProduct, pool: E) -> Result> +where + E: sqlx::Executor<'e, Database = sqlx::Postgres>, +{ sqlx::query_as( r#" DELETE FROM products @@ -163,7 +179,7 @@ RETURNING id, "#, ) .bind(msg.product_id) - .fetch_optional(&pool) + .fetch_optional(pool) .await .map_err(|e| { log::error!("{e:?}"); @@ -180,13 +196,17 @@ pub struct ShoppingCartProducts { crate::db_async_handler!( ShoppingCartProducts, shopping_cart_products, - Vec + Vec, + inner_shopping_cart_products ); -pub(crate) async fn shopping_cart_products( +pub(crate) async fn shopping_cart_products<'e, E>( msg: ShoppingCartProducts, - pool: PgPool, -) -> Result> { + pool: E, +) -> Result> +where + E: sqlx::Executor<'e, Database = sqlx::Postgres>, +{ sqlx::query_as( r#" SELECT products.id, @@ -202,7 +222,7 @@ WHERE shopping_cart_id = $1 "#, ) .bind(msg.shopping_cart_id) - .fetch_all(&pool) + .fetch_all(pool) .await .map_err(|e| { log::error!("{e:?}"); diff --git a/api/src/actors/database/shopping_cart_items.rs b/api/src/actors/database/shopping_cart_items.rs index 672689f..3840754 100644 --- a/api/src/actors/database/shopping_cart_items.rs +++ b/api/src/actors/database/shopping_cart_items.rs @@ -229,9 +229,17 @@ pub struct CartItems { pub shopping_cart_id: ShoppingCartId, } -db_async_handler!(CartItems, cart_items, Vec); +db_async_handler!( + CartItems, + cart_items, + Vec, + inner_cart_items +); -pub(crate) async fn cart_items(msg: CartItems, pool: PgPool) -> Result> { +pub(crate) async fn cart_items<'e, E>(msg: CartItems, pool: E) -> Result> +where + E: sqlx::Executor<'e, Database = sqlx::Postgres>, +{ let shopping_cart_id = msg.shopping_cart_id; sqlx::query_as( r#" @@ -241,7 +249,7 @@ WHERE shopping_cart_id = $1 "#, ) .bind(msg.shopping_cart_id) - .fetch_all(&pool) + .fetch_all(pool) .await .map_err(|e| { log::error!("{e:?}"); diff --git a/api/src/actors/database/shopping_carts.rs b/api/src/actors/database/shopping_carts.rs index b2d2f92..20ca43d 100644 --- a/api/src/actors/database/shopping_carts.rs +++ b/api/src/actors/database/shopping_carts.rs @@ -232,12 +232,13 @@ pub struct EnsureActiveShoppingCart { db_async_handler!( EnsureActiveShoppingCart, ensure_active_shopping_cart, - ShoppingCart + ShoppingCart, + inner_ensure_active_shopping_cart ); pub(crate) async fn ensure_active_shopping_cart( msg: EnsureActiveShoppingCart, - pool: PgPool, + pool: &mut sqlx::Transaction<'_, sqlx::Postgres>, ) -> Result { if let Ok(Some(cart)) = sqlx::query_as( r#" @@ -249,7 +250,7 @@ RETURNING id, buyer_id, payment_method, state; "#, ) .bind(msg.buyer_id) - .fetch_optional(&pool) + .fetch_optional(&mut *pool) .await .map_err(|e| { log::error!("{e:?}"); @@ -265,7 +266,7 @@ WHERE buyer_id = $1 AND state = 'active' "#, ) .bind(msg.buyer_id) - .fetch_one(&pool) + .fetch_one(pool) .await .map_err(|e| { log::error!("{e:?}"); diff --git a/api/src/actors/payment_manager.rs b/api/src/actors/payment_manager.rs index 108d7b2..b365847 100644 --- a/api/src/actors/payment_manager.rs +++ b/api/src/actors/payment_manager.rs @@ -159,6 +159,9 @@ pub struct RequestPayment { pub buyer: Buyer, pub customer_ip: String, pub buyer_id: AccountId, + /// False if customer is allowed to be charged on site. + /// Otherwise it should be true to use payment service for charging + pub charge_client: bool, } pay_async_handler!(RequestPayment, request_payment, CreatePaymentResult); @@ -177,6 +180,7 @@ pub(crate) async fn request_payment( format!("{}/payment/success", w.host()), ) }; + let cart: model::ShoppingCart = query_db!( db, database::EnsureActiveShoppingCart { @@ -233,46 +237,55 @@ pub(crate) async fn request_payment( Error::CreateOrder ); - let pay_u::res::CreateOrder { - status: _, - redirect_uri, - order_id, - ext_order_id: _, - } = { - client - .lock() - .create_order( - pay_u::req::OrderCreate::build( - msg.buyer.into(), - msg.customer_ip, - msg.currency, - format!("Order #{}", db_order.id), - )? - .with_products(cart_products.into_iter().map(|p| { - pay_u::Product::new( - p.name.to_string(), - **p.price, - items - .remove(&p.id) - .map(|(quantity, _)| **quantity as u32) - .unwrap_or_default(), - ) - })) - .with_ext_order_id(db_order.order_ext_id.to_string()) - .with_notify_url(notify_uri) - .with_continue_url(continue_uri), - ) - .await? + let payment_required = { + let l = config.lock(); + l.payment().optional_payment() != false }; + let redirect_uri = if msg.charge_client || payment_required { + let pay_u::res::CreateOrder { + status: _, + redirect_uri, + order_id, + ext_order_id: _, + } = { + client + .lock() + .create_order( + pay_u::req::OrderCreate::build( + msg.buyer.into(), + msg.customer_ip, + msg.currency, + format!("Order #{}", db_order.id), + )? + .with_products(cart_products.into_iter().map(|p| { + pay_u::Product::new( + p.name.to_string(), + **p.price, + items + .remove(&p.id) + .map(|(quantity, _)| **quantity as u32) + .unwrap_or_default(), + ) + })) + .with_ext_order_id(db_order.order_ext_id.to_string()) + .with_notify_url(notify_uri) + .with_continue_url(continue_uri), + ) + .await? + }; - query_db!( - db, - database::SetOrderServiceId { - service_order_id: order_id.0, - id: db_order.id, - }, - Error::CreateOrder - ); + query_db!( + db, + database::SetOrderServiceId { + service_order_id: order_id.0, + id: db_order.id, + }, + Error::CreateOrder + ); + redirect_uri + } else { + String::from("/pay-on-site") + }; let order_items = query_db!( db, diff --git a/api/src/config.rs b/api/src/config.rs index 64e3b04..8038573 100644 --- a/api/src/config.rs +++ b/api/src/config.rs @@ -38,6 +38,7 @@ pub struct PaymentConfig { payu_client_id: Option, payu_client_secret: Option, payu_client_merchant_id: Option, + optional_payment: bool, } impl Example for PaymentConfig { @@ -51,11 +52,16 @@ impl Example for PaymentConfig { )), /// "Create payu account and copy here merchant id" payu_client_merchant_id: Some(pay_u::MerchantPosId::from(0)), + optional_payment: true, } } } impl PaymentConfig { + pub fn optional_payment(&self) -> bool { + self.optional_payment + } + pub fn payu_client_id(&self) -> pay_u::ClientId { self.payu_client_id .as_ref() diff --git a/api/src/routes/public.rs b/api/src/routes/public.rs index e843990..fc06602 100644 --- a/api/src/routes/public.rs +++ b/api/src/routes/public.rs @@ -40,10 +40,14 @@ pub enum Error { #[get("/")] async fn landing() -> HttpResponse { - HttpResponse::SeeOther().append_header((actix_web::http::header::LOCATION, "")); HttpResponse::NotImplemented().body("") } +#[get("/pay-on-site")] +async fn pay_on_site() -> HttpResponse { + HttpResponse::Ok().body("

Pay on Site

") +} + pub fn configure(config: &mut ServiceConfig) { config.service(landing).configure(api_v1::configure); } diff --git a/api/src/routes/public/api_v1/restricted.rs b/api/src/routes/public/api_v1/restricted.rs index 0909d5f..3c628aa 100644 --- a/api/src/routes/public/api_v1/restricted.rs +++ b/api/src/routes/public/api_v1/restricted.rs @@ -200,6 +200,9 @@ pub struct CreateOrderInput { pub last_name: String, /// Required customer language pub language: String, + /// False if customer is allowed to be charged on site. + /// Otherwise it should be true to use payment service for charging + pub charge_client: bool, } #[post("/order")] @@ -233,6 +236,7 @@ pub(crate) async fn create_order( first_name, last_name, language, + charge_client, } = payload; let ip = match req.peer_addr() { Some(ip) => ip, @@ -252,6 +256,7 @@ pub(crate) async fn create_order( }, customer_ip: ip.to_string(), buyer_id, + charge_client }, routes::Error::Public(PublicError::DatabaseConnection) ); From d23fe886ce774dcd9c3987859ebc25b7b75c9eaa Mon Sep 17 00:00:00 2001 From: eraden Date: Wed, 4 May 2022 22:40:20 +0200 Subject: [PATCH 3/3] Add optional payment --- api/src/config.rs | 32 ++++++++++++++++++++++++++++++-- api/src/main.rs | 1 + api/src/opts.rs | 5 +++++ 3 files changed, 36 insertions(+), 2 deletions(-) diff --git a/api/src/config.rs b/api/src/config.rs index 8038573..99fadcd 100644 --- a/api/src/config.rs +++ b/api/src/config.rs @@ -37,7 +37,9 @@ impl std::ops::DerefMut for SharedAppConfig { pub struct PaymentConfig { payu_client_id: Option, payu_client_secret: Option, + /// Create payu account and copy here merchant id payu_client_merchant_id: Option, + /// Allow customers to pay on site optional_payment: bool, } @@ -50,8 +52,9 @@ impl Example for PaymentConfig { payu_client_secret: Some(pay_u::ClientSecret::new( "Create payu account and copy here client_secret", )), - /// "Create payu account and copy here merchant id" + /// Create payu account and copy here merchant id payu_client_merchant_id: Some(pay_u::MerchantPosId::from(0)), + /// Allow customers to pay on site optional_payment: true, } } @@ -348,7 +351,10 @@ fn load(config_path: &str, opts: &impl UpdateConfig) -> SharedAppConfig { Err(e) if e.kind() == std::io::ErrorKind::NotFound => { let config = AppConfig::example(); std::fs::write(config_path, toml::to_string_pretty(&config).unwrap()).unwrap(); - SharedAppConfig::new(config) + eprintln!("Config was automatically generated"); + eprintln!("Please review ./bazzar.toml, fill all fields or provide all environment variables in .env"); + eprintln!("And restart service."); + std::process::exit(1); } Err(e) => { log::error!("{e:?}"); @@ -357,6 +363,28 @@ fn load(config_path: &str, opts: &impl UpdateConfig) -> SharedAppConfig { } } +pub async fn config_info() -> crate::Result<()> { + println!( + r#"Environment variables: +PAYU_CLIENT_ID - PayU client id, you can obtain it by creating account (account requires one-time payment) +PAYU_CLIENT_SECRET - PayU client secret +PAYU_CLIENT_MERCHANT_ID - PayU client merchant id, you can obtain it by creating account (account requires one-time payment) +WEB_HOST - your domain name, it's required for PayU notifications, service emails and redirections +PASS_SALT - password encryption secret string, you can generate it with this CLI +SESSION_SECRET - 100 characters admin session encryption +JWT_SECRET - 100 characters user session encryption +BAZZAR_BIND - address to which server should be bind, typically 0.0.0.0 +BAZZAR_PORT - port which server should use, typically 80 +SENDGRID_SECRET - e-mail sending service secret +SENDGRID_API_KEY - e-mail sending service api key +SMTP_FROM - e-mail sending service authorized e-mail address used as sender e-mail address +DATABASE_URL - postgresql address (ex. postgres://postgres@localhost/bazzar) + "# + ); + + Ok(()) +} + pub fn save(config_path: &str, config: &mut AppConfig) { config.config_path = String::from(config_path); std::fs::write(config_path, toml::to_string_pretty(&config).unwrap()).unwrap(); diff --git a/api/src/main.rs b/api/src/main.rs index 416121e..a450aaa 100644 --- a/api/src/main.rs +++ b/api/src/main.rs @@ -204,5 +204,6 @@ async fn main() -> Result<()> { Command::GenerateHash(opts) => generate_hash(opts).await, Command::CreateAccount(opts) => create_account(opts).await, Command::TestMailer(opts) => test_mailer(opts).await, + Command::ConfigInfo(_) => config::config_info().await, } } diff --git a/api/src/opts.rs b/api/src/opts.rs index 6f52b8e..89efdcc 100644 --- a/api/src/opts.rs +++ b/api/src/opts.rs @@ -47,6 +47,8 @@ pub enum Command { CreateAccount(CreateAccountOpts), #[options(help = "Check mailer config")] TestMailer(TestMailerOpts), + #[options(help = "Print config information")] + ConfigInfo(ConfigInfo), } impl UpdateConfig for Command { @@ -67,6 +69,7 @@ impl UpdateConfig for Command { Command::TestMailer(opts) => { opts.update_config(config); } + Command::ConfigInfo(_) => {} } } } @@ -77,6 +80,8 @@ impl Default for Command { } } +pub struct ConfigInfo {} + #[derive(Options, Debug)] pub struct GenerateHashOpts { pub help: bool,