From 275b62228784fa877b0215be75f04daa7b3510c9 Mon Sep 17 00:00:00 2001 From: Mani Chandra Dulam Date: Mon, 27 Nov 2023 15:03:42 +0530 Subject: [PATCH 01/13] feat: Add ability to verify connector creds before integrating the connector --- crates/router/src/consts.rs | 5 + crates/router/src/core.rs | 2 + crates/router/src/core/verify_connector.rs | 59 +++++ crates/router/src/routes.rs | 2 + crates/router/src/routes/app.rs | 6 +- crates/router/src/routes/lock_utils.rs | 4 +- crates/router/src/routes/verify_connector.rs | 27 ++ crates/router/src/types/api.rs | 1 + .../router/src/types/api/verify_connector.rs | 248 ++++++++++++++++++ crates/router/src/utils.rs | 2 + crates/router/src/utils/verify_connector.rs | 45 ++++ crates/router_env/src/logger/types.rs | 2 + 12 files changed, 401 insertions(+), 2 deletions(-) create mode 100644 crates/router/src/core/verify_connector.rs create mode 100644 crates/router/src/routes/verify_connector.rs create mode 100644 crates/router/src/types/api/verify_connector.rs create mode 100644 crates/router/src/utils/verify_connector.rs diff --git a/crates/router/src/consts.rs b/crates/router/src/consts.rs index c5490ee00e63..642440445044 100644 --- a/crates/router/src/consts.rs +++ b/crates/router/src/consts.rs @@ -60,3 +60,8 @@ pub const LOCKER_REDIS_EXPIRY_SECONDS: u32 = 60 * 15; // 15 minutes pub const JWT_TOKEN_TIME_IN_SECS: u64 = 60 * 60 * 24 * 2; // 2 days pub const ROLE_ID_ORGANIZATION_ADMIN: &str = "org_admin"; + +#[cfg(feature = "olap")] +pub const VERIFY_CONNECTOR_ID_PREFIX: &str = "conn_verify"; +#[cfg(feature = "olap")] +pub const VERIFY_CONNECTOR_MERCHANT_ID: &str = "test_merchant"; diff --git a/crates/router/src/core.rs b/crates/router/src/core.rs index a429cab482b4..035d356c8690 100644 --- a/crates/router/src/core.rs +++ b/crates/router/src/core.rs @@ -26,4 +26,6 @@ pub mod user; pub mod utils; #[cfg(all(feature = "olap", feature = "kms"))] pub mod verification; +#[cfg(feature = "olap")] +pub mod verify_connector; pub mod webhooks; diff --git a/crates/router/src/core/verify_connector.rs b/crates/router/src/core/verify_connector.rs new file mode 100644 index 000000000000..1e7b9d39d9ff --- /dev/null +++ b/crates/router/src/core/verify_connector.rs @@ -0,0 +1,59 @@ +use crate::{connector, core::errors, types::api, utils::OptionExt}; +use api_models::enums::Connector; +use error_stack::{IntoReport, ResultExt}; + +use crate::types::api::verify_connector::{self as types, VerifyConnector}; +use crate::utils::verify_connector as utils; +use crate::{services, AppState}; + +pub async fn verify_connector_credentials( + state: AppState, + req: api::MerchantConnectorCreate, +) -> errors::RouterResponse<()> { + let boxed_connector = api::ConnectorData::get_connector_by_name( + &state.conf.connectors, + &req.connector_name.to_string(), + api::GetToken::Connector, + None, + ) + .change_context(errors::ApiErrorResponse::IncorrectConnectorNameGiven)?; + let connector_auth = req + .connector_account_details + .clone() + .parse_value("ConnectorAuthType") + .change_context(errors::ApiErrorResponse::InvalidDataFormat { + field_name: "connector_account_details".to_string(), + expected_format: "auth_type and api_key".to_string(), + })?; + + match req.connector_name { + Connector::Stripe => { + connector::Stripe::verify( + &state, + types::VerifyConnectorData { + connector: *boxed_connector.connector, + connector_auth, + card_details: utils::get_test_card_details(req.connector_name)?, + }, + ) + .await + } + Connector::Paypal => connector::Paypal::get_access_token( + &state, + types::VerifyConnectorData { + connector: *boxed_connector.connector, + connector_auth, + card_details: utils::get_test_card_details(req.connector_name)?, + }, + ) + .await + .map(|_| services::ApplicationResponse::StatusOk), + _ => Err(errors::ApiErrorResponse::NotImplemented { + message: errors::api_error_response::NotImplementedMessage::Reason(format!( + "Verification for {}", + req.connector_name + )), + }) + .into_report(), + } +} diff --git a/crates/router/src/routes.rs b/crates/router/src/routes.rs index 5166e326fb91..b4b0ffebc84d 100644 --- a/crates/router/src/routes.rs +++ b/crates/router/src/routes.rs @@ -28,6 +28,8 @@ pub mod user; #[cfg(all(feature = "olap", feature = "kms"))] pub mod verification; pub mod webhooks; +#[cfg(feature = "olap")] +pub mod verify_connector; pub mod locker_migration; #[cfg(feature = "dummy_connector")] diff --git a/crates/router/src/routes/app.rs b/crates/router/src/routes/app.rs index 84848e030120..6984b820eb52 100644 --- a/crates/router/src/routes/app.rs +++ b/crates/router/src/routes/app.rs @@ -32,7 +32,7 @@ use crate::{ configs::settings, db::{StorageImpl, StorageInterface}, events::{event_logger::EventLogger, EventHandler}, - routes::cards_info::card_iin_info, + routes::{cards_info::card_iin_info, verify_connector::payment_connector_verify}, services::get_store, }; @@ -507,6 +507,10 @@ impl MerchantConnectorAccount { use super::admin::*; route = route + .service( + web::resource("/connectors/verify") + .route(web::post().to(payment_connector_verify)), + ) .service( web::resource("/{merchant_id}/connectors") .route(web::post().to(payment_connector_create)) diff --git a/crates/router/src/routes/lock_utils.rs b/crates/router/src/routes/lock_utils.rs index 219948bdd4d2..f10634296697 100644 --- a/crates/router/src/routes/lock_utils.rs +++ b/crates/router/src/routes/lock_utils.rs @@ -144,7 +144,9 @@ impl From for ApiIdentifier { | Flow::GsmRuleUpdate | Flow::GsmRuleDelete => Self::Gsm, - Flow::UserConnectAccount | Flow::ChangePassword => Self::User, + Flow::UserConnectAccount | Flow::ChangePassword | Flow::VerifyPaymentConnector => { + Self::User + } } } } diff --git a/crates/router/src/routes/verify_connector.rs b/crates/router/src/routes/verify_connector.rs new file mode 100644 index 000000000000..68411094580b --- /dev/null +++ b/crates/router/src/routes/verify_connector.rs @@ -0,0 +1,27 @@ +use super::AppState; +use crate::{ + core::{api_locking, verify_connector}, + services::{self, authentication as auth, authorization::permissions::Permission}, +}; +use actix_web::{web, HttpRequest, HttpResponse}; +use api_models::admin::MerchantConnectorCreate; +use router_env::{instrument, tracing, Flow}; + +#[instrument(skip_all, fields(flow = ?Flow::VerifyPaymentConnector))] +pub async fn payment_connector_verify( + state: web::Data, + req: HttpRequest, + json_payload: web::Json, +) -> HttpResponse { + let flow = Flow::VerifyPaymentConnector; + Box::pin(services::server_wrap( + flow, + state, + &req, + json_payload.into_inner(), + |state, _: (), req| verify_connector::verify_connector_credentials(state, req), + &auth::JWTAuth(Permission::MerchantConnectorAccountWrite), + api_locking::LockAction::NotApplicable, + )) + .await +} diff --git a/crates/router/src/types/api.rs b/crates/router/src/types/api.rs index bcb3a9add553..4b1496e4d715 100644 --- a/crates/router/src/types/api.rs +++ b/crates/router/src/types/api.rs @@ -14,6 +14,7 @@ pub mod payouts; pub mod refunds; pub mod routing; pub mod webhooks; +pub mod verify_connector; use std::{fmt::Debug, str::FromStr}; diff --git a/crates/router/src/types/api/verify_connector.rs b/crates/router/src/types/api/verify_connector.rs new file mode 100644 index 000000000000..c6b2876410d2 --- /dev/null +++ b/crates/router/src/types/api/verify_connector.rs @@ -0,0 +1,248 @@ +use crate::{ + connector, + core::errors, + services, + types::{self, api, storage::enums as storage_enums}, +}; +use error_stack::{IntoReport, ResultExt}; +use router_env as env; + +use crate::{consts, services::ConnectorIntegration, AppState}; + +#[derive(Clone, Debug)] +pub struct VerifyConnectorData { + pub connector: &'static (dyn types::api::Connector + Sync), + pub connector_auth: types::ConnectorAuthType, + pub card_details: api::Card, +} + +impl VerifyConnectorData { + fn get_payment_authorize_data(&self) -> types::PaymentsAuthorizeData { + types::PaymentsAuthorizeData { + payment_method_data: api::PaymentMethodData::Card(self.card_details.clone()), + email: None, + amount: 1000, + confirm: true, + currency: storage_enums::Currency::USD, + mandate_id: None, + webhook_url: None, + customer_id: None, + off_session: None, + browser_info: None, + session_token: None, + order_details: None, + order_category: None, + capture_method: None, + enrolled_for_3ds: false, + router_return_url: None, + surcharge_details: None, + setup_future_usage: None, + payment_experience: None, + payment_method_type: None, + statement_descriptor: None, + setup_mandate_details: None, + complete_authorize_url: None, + related_transaction_id: None, + statement_descriptor_suffix: None, + } + } + + fn get_router_data( + &self, + request_data: R1, + access_token: Option, + ) -> types::RouterData { + let attempt_id = + common_utils::generate_id_with_default_len(consts::VERIFY_CONNECTOR_ID_PREFIX); + types::RouterData { + flow: std::marker::PhantomData, + status: storage_enums::AttemptStatus::Started, + request: request_data, + response: Err(errors::ApiErrorResponse::InternalServerError.into()), + connector: self.connector.id().to_string(), + auth_type: storage_enums::AuthenticationType::NoThreeDs, + test_mode: Some(!matches!(env::which(), env::Env::Production)), + return_url: None, + attempt_id: attempt_id.clone(), + description: None, + customer_id: None, + merchant_id: consts::VERIFY_CONNECTOR_MERCHANT_ID.to_string(), + reference_id: None, + access_token, + session_token: None, + payment_method: storage_enums::PaymentMethod::Card, + amount_captured: None, + preprocessing_id: None, + payment_method_id: None, + connector_customer: None, + connector_auth_type: self.connector_auth.clone(), + connector_meta_data: None, + payment_method_token: None, + connector_api_version: None, + recurring_mandate_payment_data: None, + connector_request_reference_id: attempt_id, + address: types::PaymentAddress { + shipping: None, + billing: None, + }, + payment_id: common_utils::generate_id_with_default_len( + consts::VERIFY_CONNECTOR_ID_PREFIX, + ), + #[cfg(feature = "payouts")] + payout_method_data: None, + #[cfg(feature = "payouts")] + quote_id: None, + payment_method_balance: None, + connector_http_status_code: None, + external_latency: None, + apple_pay_flow: None, + } + } +} + +#[async_trait::async_trait] +pub trait VerifyConnector { + async fn verify( + state: &AppState, + connector_data: VerifyConnectorData, + ) -> errors::RouterResponse<()> { + let authorize_data = connector_data.get_payment_authorize_data(); + let access_token = Self::get_access_token(state, connector_data.clone()).await?; + let router_data = connector_data.get_router_data(authorize_data, access_token); + + let request = connector_data + .connector + .build_request(&router_data, &state.conf.connectors) + .change_context(errors::ApiErrorResponse::InvalidRequestData { + message: "Payment request cannot be built".to_string(), + })? + .ok_or(errors::ApiErrorResponse::InternalServerError)?; + + let response = services::call_connector_api(&state.to_owned(), request) + .await + .change_context(errors::ApiErrorResponse::InternalServerError)?; + + match response { + Ok(_) => Ok(services::ApplicationResponse::StatusOk), + Err(error_response) => { + Self::handle_payment_error_response::< + api::Authorize, + types::PaymentsAuthorizeData, + types::PaymentsResponseData, + >(connector_data.connector, error_response) + .await + } + } + } + + async fn get_access_token( + _state: &AppState, + _connector_data: VerifyConnectorData, + ) -> errors::CustomResult, errors::ApiErrorResponse> { + // AccessToken is None for the connectors without the AccessToken Flow. + // If a connector has that, then it should override this implementation. + Ok(None) + } + + async fn handle_payment_error_response( + connector: &(dyn types::api::Connector + Sync), + error_response: types::Response, + ) -> errors::RouterResponse<()> + where + dyn types::api::Connector + Sync: ConnectorIntegration, + { + let error = connector + .get_error_response(error_response) + .change_context(errors::ApiErrorResponse::InternalServerError)?; + Err(errors::ApiErrorResponse::InvalidRequestData { + message: error.reason.unwrap_or(error.message), + }) + .into_report() + } + + async fn handle_access_token_error_response( + connector: &(dyn types::api::Connector + Sync), + error_response: types::Response, + ) -> errors::RouterResult> + where + dyn types::api::Connector + Sync: ConnectorIntegration, + { + let error = connector + .get_error_response(error_response) + .change_context(errors::ApiErrorResponse::InternalServerError)?; + Err(errors::ApiErrorResponse::InvalidRequestData { + message: error.reason.unwrap_or(error.message), + }) + .into_report() + } +} + +#[async_trait::async_trait] +impl VerifyConnector for connector::Stripe { + async fn handle_payment_error_response( + connector: &(dyn types::api::Connector + Sync), + error_response: types::Response, + ) -> errors::RouterResponse<()> + where + dyn types::api::Connector + Sync: ConnectorIntegration, + { + let error = connector + .get_error_response(error_response) + .change_context(errors::ApiErrorResponse::InternalServerError)?; + match (env::which(), error.code.as_str()) { + // In situations where an attempt is made to process a payment using a + // Stripe production key along with a test card (which verify_connector is using), + // Stripe will respond with a "card_declined" error. In production, + // when this scenario occurs we will send back an "Ok" response. + (env::Env::Production, "card_declined") => Ok(services::ApplicationResponse::StatusOk), + _ => Err(errors::ApiErrorResponse::InvalidRequestData { + message: error.reason.unwrap_or(error.message), + }) + .into_report(), + } + } +} + +#[async_trait::async_trait] +impl VerifyConnector for connector::Paypal { + async fn get_access_token( + state: &AppState, + connector_data: VerifyConnectorData, + ) -> errors::CustomResult, errors::ApiErrorResponse> { + let token_data: types::AccessTokenRequestData = + connector_data.connector_auth.clone().try_into()?; + let router_data = connector_data.get_router_data(token_data, None); + + let request = connector_data + .connector + .build_request(&router_data, &state.conf.connectors) + .change_context(errors::ApiErrorResponse::InvalidRequestData { + message: "Payment request cannot be built".to_string(), + })? + .ok_or(errors::ApiErrorResponse::InternalServerError)?; + + let response = services::call_connector_api(&state.to_owned(), request) + .await + .change_context(errors::ApiErrorResponse::InternalServerError)?; + + match response { + Ok(res) => Some( + connector_data + .connector + .handle_response(&router_data, res) + .change_context(errors::ApiErrorResponse::InternalServerError)? + .response + .map_err(|_| errors::ApiErrorResponse::InternalServerError.into()), + ) + .transpose(), + Err(response_data) => { + Self::handle_access_token_error_response::< + api::AccessTokenAuth, + types::AccessTokenRequestData, + types::AccessToken, + >(connector_data.connector, response_data) + .await + } + } + } +} diff --git a/crates/router/src/utils.rs b/crates/router/src/utils.rs index 901e84997e67..b692ba2242c9 100644 --- a/crates/router/src/utils.rs +++ b/crates/router/src/utils.rs @@ -3,6 +3,8 @@ pub mod db_utils; pub mod ext_traits; #[cfg(feature = "olap")] pub mod user; +#[cfg(feature = "olap")] +pub mod verify_connector; #[cfg(feature = "kv_store")] pub mod storage_partitioning; diff --git a/crates/router/src/utils/verify_connector.rs b/crates/router/src/utils/verify_connector.rs new file mode 100644 index 000000000000..1034e83ce748 --- /dev/null +++ b/crates/router/src/utils/verify_connector.rs @@ -0,0 +1,45 @@ +use crate::{core::errors, types::api}; +use api_models::enums::Connector; + +pub fn generate_card_from_details( + card_number: String, + card_exp_year: String, + card_exp_month: String, + card_cvv: String, +) -> Result { + Ok(api::Card { + card_number: card_number + .parse() + .map_err(|_| errors::ApiErrorResponse::InternalServerError)?, + card_issuer: None, + card_cvc: masking::Secret::new(card_cvv), + card_network: None, + card_exp_year: masking::Secret::new(card_exp_year), + card_exp_month: masking::Secret::new(card_exp_month), + card_holder_name: masking::Secret::new("HyperSwitch".to_string()), + nick_name: None, + card_type: None, + card_issuing_country: None, + bank_code: None, + }) +} + +pub fn get_test_card_details( + connector_name: Connector, +) -> Result { + match connector_name { + Connector::Stripe => generate_card_from_details( + "4242424242424242".to_string(), + "2025".to_string(), + "12".to_string(), + "100".to_string(), + ), + Connector::Paypal => generate_card_from_details( + "4111111111111111".to_string(), + "2025".to_string(), + "02".to_string(), + "123".to_string(), + ), + _ => Err(errors::ApiErrorResponse::IncorrectConnectorNameGiven), + } +} diff --git a/crates/router_env/src/logger/types.rs b/crates/router_env/src/logger/types.rs index 7978e98e52c0..aef05e5ea43f 100644 --- a/crates/router_env/src/logger/types.rs +++ b/crates/router_env/src/logger/types.rs @@ -257,6 +257,8 @@ pub enum Flow { DecisionManagerRetrieveConfig, /// Change password flow ChangePassword, + /// Payment Connector Verify + VerifyPaymentConnector, } /// From 48567a05c4bf1fd64d823a6efdafc860632ba37d Mon Sep 17 00:00:00 2001 From: Mani Chandra Dulam Date: Mon, 27 Nov 2023 17:27:00 +0530 Subject: [PATCH 02/13] refactor: add separate request for verify_connector --- crates/api_models/src/events.rs | 1 + crates/api_models/src/events/verify_connector.rs | 4 ++++ crates/api_models/src/lib.rs | 1 + crates/api_models/src/verify_connector.rs | 9 +++++++++ crates/router/src/core/verify_connector.rs | 3 ++- crates/router/src/routes/app.rs | 4 +++- crates/router/src/routes/verify_connector.rs | 4 ++-- crates/router/src/types/api.rs | 3 ++- 8 files changed, 24 insertions(+), 5 deletions(-) create mode 100644 crates/api_models/src/events/verify_connector.rs create mode 100644 crates/api_models/src/verify_connector.rs diff --git a/crates/api_models/src/events.rs b/crates/api_models/src/events.rs index 782c02be7a3a..bc95f26a9edb 100644 --- a/crates/api_models/src/events.rs +++ b/crates/api_models/src/events.rs @@ -7,6 +7,7 @@ pub mod payouts; pub mod refund; pub mod routing; pub mod user; +pub mod verify_connector; use common_utils::{ events::{ApiEventMetric, ApiEventsType}, diff --git a/crates/api_models/src/events/verify_connector.rs b/crates/api_models/src/events/verify_connector.rs new file mode 100644 index 000000000000..05e1909ced9a --- /dev/null +++ b/crates/api_models/src/events/verify_connector.rs @@ -0,0 +1,4 @@ +use crate::verify_connector::VerifyConnectorRequest; +use common_utils::events::{ApiEventMetric, ApiEventsType}; + +common_utils::impl_misc_api_event_type!(VerifyConnectorRequest); diff --git a/crates/api_models/src/lib.rs b/crates/api_models/src/lib.rs index 1abeff7b6ddb..64f4d553c5da 100644 --- a/crates/api_models/src/lib.rs +++ b/crates/api_models/src/lib.rs @@ -27,3 +27,4 @@ pub mod surcharge_decision_configs; pub mod user; pub mod verifications; pub mod webhooks; +pub mod verify_connector; diff --git a/crates/api_models/src/verify_connector.rs b/crates/api_models/src/verify_connector.rs new file mode 100644 index 000000000000..5ccaa050a343 --- /dev/null +++ b/crates/api_models/src/verify_connector.rs @@ -0,0 +1,9 @@ +use common_utils::pii; +use serde::{Deserialize, Serialize}; + +use crate::enums as api_enums; +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct VerifyConnectorRequest { + pub connector_name: api_enums::Connector, + pub connector_account_details: Option, +} diff --git a/crates/router/src/core/verify_connector.rs b/crates/router/src/core/verify_connector.rs index 1e7b9d39d9ff..94db51fc1fd2 100644 --- a/crates/router/src/core/verify_connector.rs +++ b/crates/router/src/core/verify_connector.rs @@ -5,10 +5,11 @@ use error_stack::{IntoReport, ResultExt}; use crate::types::api::verify_connector::{self as types, VerifyConnector}; use crate::utils::verify_connector as utils; use crate::{services, AppState}; +use api_models::verify_connector::VerifyConnectorRequest; pub async fn verify_connector_credentials( state: AppState, - req: api::MerchantConnectorCreate, + req: VerifyConnectorRequest, ) -> errors::RouterResponse<()> { let boxed_connector = api::ConnectorData::get_connector_by_name( &state.conf.connectors, diff --git a/crates/router/src/routes/app.rs b/crates/router/src/routes/app.rs index 6984b820eb52..3243cfa11424 100644 --- a/crates/router/src/routes/app.rs +++ b/crates/router/src/routes/app.rs @@ -28,11 +28,13 @@ use super::{cache::*, health::*}; use super::{configs::*, customers::*, mandates::*, payments::*, refunds::*}; #[cfg(feature = "oltp")] use super::{ephemeral_key::*, payment_methods::*, webhooks::*}; +#[cfg(feature = "olap")] +use crate::routes::verify_connector::payment_connector_verify; use crate::{ configs::settings, db::{StorageImpl, StorageInterface}, events::{event_logger::EventLogger, EventHandler}, - routes::{cards_info::card_iin_info, verify_connector::payment_connector_verify}, + routes::cards_info::card_iin_info, services::get_store, }; diff --git a/crates/router/src/routes/verify_connector.rs b/crates/router/src/routes/verify_connector.rs index 68411094580b..e608278327cc 100644 --- a/crates/router/src/routes/verify_connector.rs +++ b/crates/router/src/routes/verify_connector.rs @@ -4,14 +4,14 @@ use crate::{ services::{self, authentication as auth, authorization::permissions::Permission}, }; use actix_web::{web, HttpRequest, HttpResponse}; -use api_models::admin::MerchantConnectorCreate; +use api_models::verify_connector::VerifyConnectorRequest; use router_env::{instrument, tracing, Flow}; #[instrument(skip_all, fields(flow = ?Flow::VerifyPaymentConnector))] pub async fn payment_connector_verify( state: web::Data, req: HttpRequest, - json_payload: web::Json, + json_payload: web::Json, ) -> HttpResponse { let flow = Flow::VerifyPaymentConnector; Box::pin(services::server_wrap( diff --git a/crates/router/src/types/api.rs b/crates/router/src/types/api.rs index 4b1496e4d715..96bcaca3ed5d 100644 --- a/crates/router/src/types/api.rs +++ b/crates/router/src/types/api.rs @@ -13,8 +13,9 @@ pub mod payments; pub mod payouts; pub mod refunds; pub mod routing; -pub mod webhooks; +#[cfg(feature = "olap")] pub mod verify_connector; +pub mod webhooks; use std::{fmt::Debug, str::FromStr}; From 57ed4f3d1e8ceddacf70c55ecfb155bef010bbe8 Mon Sep 17 00:00:00 2001 From: "hyperswitch-bot[bot]" <148525504+hyperswitch-bot[bot]@users.noreply.github.com> Date: Mon, 27 Nov 2023 12:49:17 +0000 Subject: [PATCH 03/13] chore: run formatter --- .../api_models/src/events/verify_connector.rs | 3 ++- crates/api_models/src/lib.rs | 2 +- crates/router/src/core/verify_connector.rs | 18 ++++++++++++------ crates/router/src/routes.rs | 2 +- crates/router/src/routes/verify_connector.rs | 7 ++++--- .../router/src/types/api/verify_connector.rs | 11 ++++++----- crates/router/src/utils/verify_connector.rs | 3 ++- 7 files changed, 28 insertions(+), 18 deletions(-) diff --git a/crates/api_models/src/events/verify_connector.rs b/crates/api_models/src/events/verify_connector.rs index 05e1909ced9a..96151a22f675 100644 --- a/crates/api_models/src/events/verify_connector.rs +++ b/crates/api_models/src/events/verify_connector.rs @@ -1,4 +1,5 @@ -use crate::verify_connector::VerifyConnectorRequest; use common_utils::events::{ApiEventMetric, ApiEventsType}; +use crate::verify_connector::VerifyConnectorRequest; + common_utils::impl_misc_api_event_type!(VerifyConnectorRequest); diff --git a/crates/api_models/src/lib.rs b/crates/api_models/src/lib.rs index 64f4d553c5da..767860248c48 100644 --- a/crates/api_models/src/lib.rs +++ b/crates/api_models/src/lib.rs @@ -26,5 +26,5 @@ pub mod routing; pub mod surcharge_decision_configs; pub mod user; pub mod verifications; -pub mod webhooks; pub mod verify_connector; +pub mod webhooks; diff --git a/crates/router/src/core/verify_connector.rs b/crates/router/src/core/verify_connector.rs index 94db51fc1fd2..014c626db8dc 100644 --- a/crates/router/src/core/verify_connector.rs +++ b/crates/router/src/core/verify_connector.rs @@ -1,11 +1,17 @@ -use crate::{connector, core::errors, types::api, utils::OptionExt}; -use api_models::enums::Connector; +use api_models::{enums::Connector, verify_connector::VerifyConnectorRequest}; use error_stack::{IntoReport, ResultExt}; -use crate::types::api::verify_connector::{self as types, VerifyConnector}; -use crate::utils::verify_connector as utils; -use crate::{services, AppState}; -use api_models::verify_connector::VerifyConnectorRequest; +use crate::{ + connector, + core::errors, + services, + types::{ + api, + api::verify_connector::{self as types, VerifyConnector}, + }, + utils::{verify_connector as utils, OptionExt}, + AppState, +}; pub async fn verify_connector_credentials( state: AppState, diff --git a/crates/router/src/routes.rs b/crates/router/src/routes.rs index b4b0ffebc84d..3dd921276330 100644 --- a/crates/router/src/routes.rs +++ b/crates/router/src/routes.rs @@ -27,9 +27,9 @@ pub mod routing; pub mod user; #[cfg(all(feature = "olap", feature = "kms"))] pub mod verification; -pub mod webhooks; #[cfg(feature = "olap")] pub mod verify_connector; +pub mod webhooks; pub mod locker_migration; #[cfg(feature = "dummy_connector")] diff --git a/crates/router/src/routes/verify_connector.rs b/crates/router/src/routes/verify_connector.rs index e608278327cc..bfb1b781ada4 100644 --- a/crates/router/src/routes/verify_connector.rs +++ b/crates/router/src/routes/verify_connector.rs @@ -1,11 +1,12 @@ +use actix_web::{web, HttpRequest, HttpResponse}; +use api_models::verify_connector::VerifyConnectorRequest; +use router_env::{instrument, tracing, Flow}; + use super::AppState; use crate::{ core::{api_locking, verify_connector}, services::{self, authentication as auth, authorization::permissions::Permission}, }; -use actix_web::{web, HttpRequest, HttpResponse}; -use api_models::verify_connector::VerifyConnectorRequest; -use router_env::{instrument, tracing, Flow}; #[instrument(skip_all, fields(flow = ?Flow::VerifyPaymentConnector))] pub async fn payment_connector_verify( diff --git a/crates/router/src/types/api/verify_connector.rs b/crates/router/src/types/api/verify_connector.rs index c6b2876410d2..b71f60267907 100644 --- a/crates/router/src/types/api/verify_connector.rs +++ b/crates/router/src/types/api/verify_connector.rs @@ -1,13 +1,14 @@ +use error_stack::{IntoReport, ResultExt}; +use router_env as env; + use crate::{ - connector, + connector, consts, core::errors, services, + services::ConnectorIntegration, types::{self, api, storage::enums as storage_enums}, + AppState, }; -use error_stack::{IntoReport, ResultExt}; -use router_env as env; - -use crate::{consts, services::ConnectorIntegration, AppState}; #[derive(Clone, Debug)] pub struct VerifyConnectorData { diff --git a/crates/router/src/utils/verify_connector.rs b/crates/router/src/utils/verify_connector.rs index 1034e83ce748..b24d0ef35249 100644 --- a/crates/router/src/utils/verify_connector.rs +++ b/crates/router/src/utils/verify_connector.rs @@ -1,6 +1,7 @@ -use crate::{core::errors, types::api}; use api_models::enums::Connector; +use crate::{core::errors, types::api}; + pub fn generate_card_from_details( card_number: String, card_exp_year: String, From 2dc4b03d571ee0aaca4a4e3b4382f266e80f0f32 Mon Sep 17 00:00:00 2001 From: Mani Chandra Dulam Date: Tue, 28 Nov 2023 13:43:29 +0530 Subject: [PATCH 04/13] refactor: use ConnectorAuthType enum in the request body instead of Value --- crates/api_models/src/events.rs | 1 - .../api_models/src/events/verify_connector.rs | 5 -- crates/api_models/src/lib.rs | 1 - crates/api_models/src/verify_connector.rs | 9 ---- crates/router/src/core/verify_connector.rs | 50 ++++++++----------- crates/router/src/routes/verify_connector.rs | 7 ++- .../router/src/types/api/verify_connector.rs | 21 +++++--- crates/router/src/utils/verify_connector.rs | 23 +++++---- 8 files changed, 51 insertions(+), 66 deletions(-) delete mode 100644 crates/api_models/src/events/verify_connector.rs delete mode 100644 crates/api_models/src/verify_connector.rs diff --git a/crates/api_models/src/events.rs b/crates/api_models/src/events.rs index bc95f26a9edb..782c02be7a3a 100644 --- a/crates/api_models/src/events.rs +++ b/crates/api_models/src/events.rs @@ -7,7 +7,6 @@ pub mod payouts; pub mod refund; pub mod routing; pub mod user; -pub mod verify_connector; use common_utils::{ events::{ApiEventMetric, ApiEventsType}, diff --git a/crates/api_models/src/events/verify_connector.rs b/crates/api_models/src/events/verify_connector.rs deleted file mode 100644 index 96151a22f675..000000000000 --- a/crates/api_models/src/events/verify_connector.rs +++ /dev/null @@ -1,5 +0,0 @@ -use common_utils::events::{ApiEventMetric, ApiEventsType}; - -use crate::verify_connector::VerifyConnectorRequest; - -common_utils::impl_misc_api_event_type!(VerifyConnectorRequest); diff --git a/crates/api_models/src/lib.rs b/crates/api_models/src/lib.rs index 767860248c48..1abeff7b6ddb 100644 --- a/crates/api_models/src/lib.rs +++ b/crates/api_models/src/lib.rs @@ -26,5 +26,4 @@ pub mod routing; pub mod surcharge_decision_configs; pub mod user; pub mod verifications; -pub mod verify_connector; pub mod webhooks; diff --git a/crates/api_models/src/verify_connector.rs b/crates/api_models/src/verify_connector.rs deleted file mode 100644 index 5ccaa050a343..000000000000 --- a/crates/api_models/src/verify_connector.rs +++ /dev/null @@ -1,9 +0,0 @@ -use common_utils::pii; -use serde::{Deserialize, Serialize}; - -use crate::enums as api_enums; -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct VerifyConnectorRequest { - pub connector_name: api_enums::Connector, - pub connector_account_details: Option, -} diff --git a/crates/router/src/core/verify_connector.rs b/crates/router/src/core/verify_connector.rs index 014c626db8dc..93181b4f8b3e 100644 --- a/crates/router/src/core/verify_connector.rs +++ b/crates/router/src/core/verify_connector.rs @@ -1,21 +1,14 @@ -use api_models::{enums::Connector, verify_connector::VerifyConnectorRequest}; +use crate::{connector, core::errors, types::api}; +use api_models::enums::Connector; use error_stack::{IntoReport, ResultExt}; -use crate::{ - connector, - core::errors, - services, - types::{ - api, - api::verify_connector::{self as types, VerifyConnector}, - }, - utils::{verify_connector as utils, OptionExt}, - AppState, -}; +use crate::types::api::verify_connector::{self as types, VerifyConnector}; +use crate::utils::verify_connector as utils; +use crate::{services, AppState}; pub async fn verify_connector_credentials( state: AppState, - req: VerifyConnectorRequest, + req: types::VerifyConnectorRequest, ) -> errors::RouterResponse<()> { let boxed_connector = api::ConnectorData::get_connector_by_name( &state.conf.connectors, @@ -24,14 +17,13 @@ pub async fn verify_connector_credentials( None, ) .change_context(errors::ApiErrorResponse::IncorrectConnectorNameGiven)?; - let connector_auth = req - .connector_account_details - .clone() - .parse_value("ConnectorAuthType") - .change_context(errors::ApiErrorResponse::InvalidDataFormat { - field_name: "connector_account_details".to_string(), - expected_format: "auth_type and api_key".to_string(), - })?; + + let card_details = utils::get_test_card_details(req.connector_name)? + .ok_or(errors::ApiErrorResponse::FlowNotSupported { + flow: "Verify credentials".to_string(), + connector: req.connector_name.to_string(), + }) + .into_report()?; match req.connector_name { Connector::Stripe => { @@ -39,8 +31,8 @@ pub async fn verify_connector_credentials( &state, types::VerifyConnectorData { connector: *boxed_connector.connector, - connector_auth, - card_details: utils::get_test_card_details(req.connector_name)?, + connector_auth: req.connector_account_details, + card_details, }, ) .await @@ -49,17 +41,15 @@ pub async fn verify_connector_credentials( &state, types::VerifyConnectorData { connector: *boxed_connector.connector, - connector_auth, - card_details: utils::get_test_card_details(req.connector_name)?, + connector_auth: req.connector_account_details, + card_details, }, ) .await .map(|_| services::ApplicationResponse::StatusOk), - _ => Err(errors::ApiErrorResponse::NotImplemented { - message: errors::api_error_response::NotImplementedMessage::Reason(format!( - "Verification for {}", - req.connector_name - )), + _ => Err(errors::ApiErrorResponse::FlowNotSupported { + flow: "Verify credentials".to_string(), + connector: req.connector_name.to_string(), }) .into_report(), } diff --git a/crates/router/src/routes/verify_connector.rs b/crates/router/src/routes/verify_connector.rs index bfb1b781ada4..608992508195 100644 --- a/crates/router/src/routes/verify_connector.rs +++ b/crates/router/src/routes/verify_connector.rs @@ -1,12 +1,11 @@ -use actix_web::{web, HttpRequest, HttpResponse}; -use api_models::verify_connector::VerifyConnectorRequest; -use router_env::{instrument, tracing, Flow}; - use super::AppState; use crate::{ core::{api_locking, verify_connector}, services::{self, authentication as auth, authorization::permissions::Permission}, + types::api::verify_connector::VerifyConnectorRequest, }; +use actix_web::{web, HttpRequest, HttpResponse}; +use router_env::{instrument, tracing, Flow}; #[instrument(skip_all, fields(flow = ?Flow::VerifyPaymentConnector))] pub async fn payment_connector_verify( diff --git a/crates/router/src/types/api/verify_connector.rs b/crates/router/src/types/api/verify_connector.rs index b71f60267907..2451983ced01 100644 --- a/crates/router/src/types/api/verify_connector.rs +++ b/crates/router/src/types/api/verify_connector.rs @@ -1,14 +1,23 @@ -use error_stack::{IntoReport, ResultExt}; -use router_env as env; - use crate::{ - connector, consts, + connector, core::errors, services, - services::ConnectorIntegration, types::{self, api, storage::enums as storage_enums}, - AppState, }; +use api_models::enums as api_enums; +use common_utils::events::{ApiEventMetric, ApiEventsType}; +use error_stack::{IntoReport, ResultExt}; +use router_env as env; + +use crate::{consts, services::ConnectorIntegration, AppState}; + +#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)] +pub struct VerifyConnectorRequest { + pub connector_name: api_enums::Connector, + pub connector_account_details: types::ConnectorAuthType, +} + +common_utils::impl_misc_api_event_type!(VerifyConnectorRequest); #[derive(Clone, Debug)] pub struct VerifyConnectorData { diff --git a/crates/router/src/utils/verify_connector.rs b/crates/router/src/utils/verify_connector.rs index b24d0ef35249..bf258a8db5c5 100644 --- a/crates/router/src/utils/verify_connector.rs +++ b/crates/router/src/utils/verify_connector.rs @@ -1,4 +1,6 @@ use api_models::enums::Connector; +use error_stack::ResultExt; +use error_stack::IntoReport; use crate::{core::errors, types::api}; @@ -7,11 +9,13 @@ pub fn generate_card_from_details( card_exp_year: String, card_exp_month: String, card_cvv: String, -) -> Result { +) -> errors::RouterResult { Ok(api::Card { card_number: card_number .parse() - .map_err(|_| errors::ApiErrorResponse::InternalServerError)?, + .into_report() + .change_context(errors::ApiErrorResponse::InternalServerError) + .attach_printable("Error while parsing card number")?, card_issuer: None, card_cvc: masking::Secret::new(card_cvv), card_network: None, @@ -25,22 +29,21 @@ pub fn generate_card_from_details( }) } -pub fn get_test_card_details( - connector_name: Connector, -) -> Result { +pub fn get_test_card_details(connector_name: Connector) -> errors::RouterResult> { match connector_name { - Connector::Stripe => generate_card_from_details( + Connector::Stripe => Some(generate_card_from_details( "4242424242424242".to_string(), "2025".to_string(), "12".to_string(), "100".to_string(), - ), - Connector::Paypal => generate_card_from_details( + )) + .transpose(), + Connector::Paypal => Some(generate_card_from_details( "4111111111111111".to_string(), "2025".to_string(), "02".to_string(), "123".to_string(), - ), - _ => Err(errors::ApiErrorResponse::IncorrectConnectorNameGiven), + )).transpose(), + _ => Ok(None), } } From a43e26338cbc1cd9490331a583011f39517bb414 Mon Sep 17 00:00:00 2001 From: "hyperswitch-bot[bot]" <148525504+hyperswitch-bot[bot]@users.noreply.github.com> Date: Tue, 28 Nov 2023 08:57:20 +0000 Subject: [PATCH 05/13] chore: run formatter --- crates/router/src/core/verify_connector.rs | 15 +++++++++++---- crates/router/src/routes/verify_connector.rs | 5 +++-- crates/router/src/types/api/verify_connector.rs | 15 ++++++++------- crates/router/src/utils/verify_connector.rs | 6 +++--- 4 files changed, 25 insertions(+), 16 deletions(-) diff --git a/crates/router/src/core/verify_connector.rs b/crates/router/src/core/verify_connector.rs index 93181b4f8b3e..10e2297d4a08 100644 --- a/crates/router/src/core/verify_connector.rs +++ b/crates/router/src/core/verify_connector.rs @@ -1,10 +1,17 @@ -use crate::{connector, core::errors, types::api}; use api_models::enums::Connector; use error_stack::{IntoReport, ResultExt}; -use crate::types::api::verify_connector::{self as types, VerifyConnector}; -use crate::utils::verify_connector as utils; -use crate::{services, AppState}; +use crate::{ + connector, + core::errors, + services, + types::{ + api, + api::verify_connector::{self as types, VerifyConnector}, + }, + utils::verify_connector as utils, + AppState, +}; pub async fn verify_connector_credentials( state: AppState, diff --git a/crates/router/src/routes/verify_connector.rs b/crates/router/src/routes/verify_connector.rs index 608992508195..28128050e5a4 100644 --- a/crates/router/src/routes/verify_connector.rs +++ b/crates/router/src/routes/verify_connector.rs @@ -1,11 +1,12 @@ +use actix_web::{web, HttpRequest, HttpResponse}; +use router_env::{instrument, tracing, Flow}; + use super::AppState; use crate::{ core::{api_locking, verify_connector}, services::{self, authentication as auth, authorization::permissions::Permission}, types::api::verify_connector::VerifyConnectorRequest, }; -use actix_web::{web, HttpRequest, HttpResponse}; -use router_env::{instrument, tracing, Flow}; #[instrument(skip_all, fields(flow = ?Flow::VerifyPaymentConnector))] pub async fn payment_connector_verify( diff --git a/crates/router/src/types/api/verify_connector.rs b/crates/router/src/types/api/verify_connector.rs index 2451983ced01..3e57d8e80f81 100644 --- a/crates/router/src/types/api/verify_connector.rs +++ b/crates/router/src/types/api/verify_connector.rs @@ -1,15 +1,16 @@ -use crate::{ - connector, - core::errors, - services, - types::{self, api, storage::enums as storage_enums}, -}; use api_models::enums as api_enums; use common_utils::events::{ApiEventMetric, ApiEventsType}; use error_stack::{IntoReport, ResultExt}; use router_env as env; -use crate::{consts, services::ConnectorIntegration, AppState}; +use crate::{ + connector, consts, + core::errors, + services, + services::ConnectorIntegration, + types::{self, api, storage::enums as storage_enums}, + AppState, +}; #[derive(Clone, Debug, serde::Deserialize, serde::Serialize)] pub struct VerifyConnectorRequest { diff --git a/crates/router/src/utils/verify_connector.rs b/crates/router/src/utils/verify_connector.rs index bf258a8db5c5..6ad683d63ba1 100644 --- a/crates/router/src/utils/verify_connector.rs +++ b/crates/router/src/utils/verify_connector.rs @@ -1,6 +1,5 @@ use api_models::enums::Connector; -use error_stack::ResultExt; -use error_stack::IntoReport; +use error_stack::{IntoReport, ResultExt}; use crate::{core::errors, types::api}; @@ -43,7 +42,8 @@ pub fn get_test_card_details(connector_name: Connector) -> errors::RouterResult< "2025".to_string(), "02".to_string(), "123".to_string(), - )).transpose(), + )) + .transpose(), _ => Ok(None), } } From c23218ffe2f7a7d206d003ce96de54e20a2907d8 Mon Sep 17 00:00:00 2001 From: Mani Chandra Dulam Date: Tue, 28 Nov 2023 15:03:00 +0530 Subject: [PATCH 06/13] refactor: move connector specific logic to their corresponding files --- .../router/src/types/api/verify_connector.rs | 75 +------------------ .../src/types/api/verify_connector/paypal.rs | 54 +++++++++++++ .../src/types/api/verify_connector/stripe.rs | 36 +++++++++ 3 files changed, 94 insertions(+), 71 deletions(-) create mode 100644 crates/router/src/types/api/verify_connector/paypal.rs create mode 100644 crates/router/src/types/api/verify_connector/stripe.rs diff --git a/crates/router/src/types/api/verify_connector.rs b/crates/router/src/types/api/verify_connector.rs index 3e57d8e80f81..76a295c1db49 100644 --- a/crates/router/src/types/api/verify_connector.rs +++ b/crates/router/src/types/api/verify_connector.rs @@ -1,10 +1,13 @@ +pub mod paypal; +pub mod stripe; + use api_models::enums as api_enums; use common_utils::events::{ApiEventMetric, ApiEventsType}; use error_stack::{IntoReport, ResultExt}; use router_env as env; use crate::{ - connector, consts, + consts, core::errors, services, services::ConnectorIntegration, @@ -187,73 +190,3 @@ pub trait VerifyConnector { .into_report() } } - -#[async_trait::async_trait] -impl VerifyConnector for connector::Stripe { - async fn handle_payment_error_response( - connector: &(dyn types::api::Connector + Sync), - error_response: types::Response, - ) -> errors::RouterResponse<()> - where - dyn types::api::Connector + Sync: ConnectorIntegration, - { - let error = connector - .get_error_response(error_response) - .change_context(errors::ApiErrorResponse::InternalServerError)?; - match (env::which(), error.code.as_str()) { - // In situations where an attempt is made to process a payment using a - // Stripe production key along with a test card (which verify_connector is using), - // Stripe will respond with a "card_declined" error. In production, - // when this scenario occurs we will send back an "Ok" response. - (env::Env::Production, "card_declined") => Ok(services::ApplicationResponse::StatusOk), - _ => Err(errors::ApiErrorResponse::InvalidRequestData { - message: error.reason.unwrap_or(error.message), - }) - .into_report(), - } - } -} - -#[async_trait::async_trait] -impl VerifyConnector for connector::Paypal { - async fn get_access_token( - state: &AppState, - connector_data: VerifyConnectorData, - ) -> errors::CustomResult, errors::ApiErrorResponse> { - let token_data: types::AccessTokenRequestData = - connector_data.connector_auth.clone().try_into()?; - let router_data = connector_data.get_router_data(token_data, None); - - let request = connector_data - .connector - .build_request(&router_data, &state.conf.connectors) - .change_context(errors::ApiErrorResponse::InvalidRequestData { - message: "Payment request cannot be built".to_string(), - })? - .ok_or(errors::ApiErrorResponse::InternalServerError)?; - - let response = services::call_connector_api(&state.to_owned(), request) - .await - .change_context(errors::ApiErrorResponse::InternalServerError)?; - - match response { - Ok(res) => Some( - connector_data - .connector - .handle_response(&router_data, res) - .change_context(errors::ApiErrorResponse::InternalServerError)? - .response - .map_err(|_| errors::ApiErrorResponse::InternalServerError.into()), - ) - .transpose(), - Err(response_data) => { - Self::handle_access_token_error_response::< - api::AccessTokenAuth, - types::AccessTokenRequestData, - types::AccessToken, - >(connector_data.connector, response_data) - .await - } - } - } -} diff --git a/crates/router/src/types/api/verify_connector/paypal.rs b/crates/router/src/types/api/verify_connector/paypal.rs new file mode 100644 index 000000000000..33e848f909df --- /dev/null +++ b/crates/router/src/types/api/verify_connector/paypal.rs @@ -0,0 +1,54 @@ +use error_stack::ResultExt; + +use super::{VerifyConnector, VerifyConnectorData}; +use crate::{ + connector, + core::errors, + routes::AppState, + services, + types::{self, api}, +}; + +#[async_trait::async_trait] +impl VerifyConnector for connector::Paypal { + async fn get_access_token( + state: &AppState, + connector_data: VerifyConnectorData, + ) -> errors::CustomResult, errors::ApiErrorResponse> { + let token_data: types::AccessTokenRequestData = + connector_data.connector_auth.clone().try_into()?; + let router_data = connector_data.get_router_data(token_data, None); + + let request = connector_data + .connector + .build_request(&router_data, &state.conf.connectors) + .change_context(errors::ApiErrorResponse::InvalidRequestData { + message: "Payment request cannot be built".to_string(), + })? + .ok_or(errors::ApiErrorResponse::InternalServerError)?; + + let response = services::call_connector_api(&state.to_owned(), request) + .await + .change_context(errors::ApiErrorResponse::InternalServerError)?; + + match response { + Ok(res) => Some( + connector_data + .connector + .handle_response(&router_data, res) + .change_context(errors::ApiErrorResponse::InternalServerError)? + .response + .map_err(|_| errors::ApiErrorResponse::InternalServerError.into()), + ) + .transpose(), + Err(response_data) => { + Self::handle_access_token_error_response::< + api::AccessTokenAuth, + types::AccessTokenRequestData, + types::AccessToken, + >(connector_data.connector, response_data) + .await + } + } + } +} diff --git a/crates/router/src/types/api/verify_connector/stripe.rs b/crates/router/src/types/api/verify_connector/stripe.rs new file mode 100644 index 000000000000..ece9fa15a1d9 --- /dev/null +++ b/crates/router/src/types/api/verify_connector/stripe.rs @@ -0,0 +1,36 @@ +use error_stack::{IntoReport, ResultExt}; +use router_env::env; + +use super::VerifyConnector; +use crate::{ + connector, + core::errors, + services::{self, ConnectorIntegration}, + types, +}; + +#[async_trait::async_trait] +impl VerifyConnector for connector::Stripe { + async fn handle_payment_error_response( + connector: &(dyn types::api::Connector + Sync), + error_response: types::Response, + ) -> errors::RouterResponse<()> + where + dyn types::api::Connector + Sync: ConnectorIntegration, + { + let error = connector + .get_error_response(error_response) + .change_context(errors::ApiErrorResponse::InternalServerError)?; + match (env::which(), error.code.as_str()) { + // In situations where an attempt is made to process a payment using a + // Stripe production key along with a test card (which verify_connector is using), + // Stripe will respond with a "card_declined" error. In production, + // when this scenario occurs we will send back an "Ok" response. + (env::Env::Production, "card_declined") => Ok(services::ApplicationResponse::StatusOk), + _ => Err(errors::ApiErrorResponse::InvalidRequestData { + message: error.reason.unwrap_or(error.message), + }) + .into_report(), + } + } +} From 7b0ccfb2ec6eb30357ab0e21d37802fca9ae44a6 Mon Sep 17 00:00:00 2001 From: Mani Chandra Dulam Date: Tue, 28 Nov 2023 16:17:28 +0530 Subject: [PATCH 07/13] refactor: add ConnectorAuthType in api_models --- crates/api_models/src/admin.rs | 32 ++++++++ crates/api_models/src/lib.rs | 1 + crates/api_models/src/verify_connector.rs | 11 +++ crates/kgraph_utils/src/mca.rs | 2 +- crates/router/src/core/verify_connector.rs | 8 +- crates/router/src/routes/verify_connector.rs | 2 +- crates/router/src/types.rs | 76 +++++++++++++++++++ .../router/src/types/api/verify_connector.rs | 10 --- 8 files changed, 126 insertions(+), 16 deletions(-) create mode 100644 crates/api_models/src/verify_connector.rs diff --git a/crates/api_models/src/admin.rs b/crates/api_models/src/admin.rs index efde4a048323..6bb4fd4afa0f 100644 --- a/crates/api_models/src/admin.rs +++ b/crates/api_models/src/admin.rs @@ -1,3 +1,5 @@ +use std::collections::HashMap; + use common_utils::{ crypto::{Encryptable, OptionalEncryptableName}, pii, @@ -614,6 +616,36 @@ pub struct MerchantConnectorCreate { pub status: Option, } +// Different patterns of authentication. +#[derive(Default, Debug, Clone, serde::Deserialize, serde::Serialize)] +#[serde(tag = "auth_type")] +pub enum ConnectorAuthType { + TemporaryAuth, + HeaderKey { + api_key: Secret, + }, + BodyKey { + api_key: Secret, + key1: Secret, + }, + SignatureKey { + api_key: Secret, + key1: Secret, + api_secret: Secret, + }, + MultiAuthKey { + api_key: Secret, + key1: Secret, + api_secret: Secret, + key2: Secret, + }, + CurrencyAuthKey { + auth_key_map: HashMap, + }, + #[default] + NoKey, +} + #[derive(Debug, Clone, Serialize, Deserialize, ToSchema)] #[serde(deny_unknown_fields)] pub struct MerchantConnectorWebhookDetails { diff --git a/crates/api_models/src/lib.rs b/crates/api_models/src/lib.rs index 1abeff7b6ddb..767860248c48 100644 --- a/crates/api_models/src/lib.rs +++ b/crates/api_models/src/lib.rs @@ -26,4 +26,5 @@ pub mod routing; pub mod surcharge_decision_configs; pub mod user; pub mod verifications; +pub mod verify_connector; pub mod webhooks; diff --git a/crates/api_models/src/verify_connector.rs b/crates/api_models/src/verify_connector.rs new file mode 100644 index 000000000000..1db5a19a030a --- /dev/null +++ b/crates/api_models/src/verify_connector.rs @@ -0,0 +1,11 @@ +use common_utils::events::{ApiEventMetric, ApiEventsType}; + +use crate::{admin, enums}; + +#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)] +pub struct VerifyConnectorRequest { + pub connector_name: enums::Connector, + pub connector_account_details: admin::ConnectorAuthType, +} + +common_utils::impl_misc_api_event_type!(VerifyConnectorRequest); diff --git a/crates/kgraph_utils/src/mca.rs b/crates/kgraph_utils/src/mca.rs index deea51bd8808..a7f07be7dac1 100644 --- a/crates/kgraph_utils/src/mca.rs +++ b/crates/kgraph_utils/src/mca.rs @@ -362,7 +362,7 @@ mod tests { connector_label: Some("something".to_string()), business_label: Some("food".to_string()), business_sub_label: None, - connector_account_details: masking::Secret::new(serde_json::json!({})), + connector_account_details: ConnectorAuthType::default(), test_mode: None, disabled: None, metadata: None, diff --git a/crates/router/src/core/verify_connector.rs b/crates/router/src/core/verify_connector.rs index 10e2297d4a08..e837e8b8b259 100644 --- a/crates/router/src/core/verify_connector.rs +++ b/crates/router/src/core/verify_connector.rs @@ -1,4 +1,4 @@ -use api_models::enums::Connector; +use api_models::{enums::Connector, verify_connector::VerifyConnectorRequest}; use error_stack::{IntoReport, ResultExt}; use crate::{ @@ -15,7 +15,7 @@ use crate::{ pub async fn verify_connector_credentials( state: AppState, - req: types::VerifyConnectorRequest, + req: VerifyConnectorRequest, ) -> errors::RouterResponse<()> { let boxed_connector = api::ConnectorData::get_connector_by_name( &state.conf.connectors, @@ -38,7 +38,7 @@ pub async fn verify_connector_credentials( &state, types::VerifyConnectorData { connector: *boxed_connector.connector, - connector_auth: req.connector_account_details, + connector_auth: req.connector_account_details.into(), card_details, }, ) @@ -48,7 +48,7 @@ pub async fn verify_connector_credentials( &state, types::VerifyConnectorData { connector: *boxed_connector.connector, - connector_auth: req.connector_account_details, + connector_auth: req.connector_account_details.into(), card_details, }, ) diff --git a/crates/router/src/routes/verify_connector.rs b/crates/router/src/routes/verify_connector.rs index 28128050e5a4..bfb1b781ada4 100644 --- a/crates/router/src/routes/verify_connector.rs +++ b/crates/router/src/routes/verify_connector.rs @@ -1,11 +1,11 @@ use actix_web::{web, HttpRequest, HttpResponse}; +use api_models::verify_connector::VerifyConnectorRequest; use router_env::{instrument, tracing, Flow}; use super::AppState; use crate::{ core::{api_locking, verify_connector}, services::{self, authentication as auth, authorization::permissions::Permission}, - types::api::verify_connector::VerifyConnectorRequest, }; #[instrument(skip_all, fields(flow = ?Flow::VerifyPaymentConnector))] diff --git a/crates/router/src/types.rs b/crates/router/src/types.rs index 8c9d030965c9..a179dba1f73c 100644 --- a/crates/router/src/types.rs +++ b/crates/router/src/types.rs @@ -941,6 +941,82 @@ pub enum ConnectorAuthType { NoKey, } +impl From for ConnectorAuthType { + fn from(value: api_models::admin::ConnectorAuthType) -> Self { + match value { + api_models::admin::ConnectorAuthType::TemporaryAuth => Self::TemporaryAuth, + api_models::admin::ConnectorAuthType::HeaderKey { api_key } => { + Self::HeaderKey { api_key } + } + api_models::admin::ConnectorAuthType::BodyKey { api_key, key1 } => { + Self::BodyKey { api_key, key1 } + } + api_models::admin::ConnectorAuthType::SignatureKey { + api_key, + key1, + api_secret, + } => Self::SignatureKey { + api_key, + key1, + api_secret, + }, + api_models::admin::ConnectorAuthType::MultiAuthKey { + api_key, + key1, + api_secret, + key2, + } => Self::MultiAuthKey { + api_key, + key1, + api_secret, + key2, + }, + api_models::admin::ConnectorAuthType::CurrencyAuthKey { auth_key_map } => { + Self::CurrencyAuthKey { auth_key_map } + } + api_models::admin::ConnectorAuthType::NoKey => Self::NoKey, + } + } +} + +impl Into for ConnectorAuthType { + fn into(self) -> api_models::admin::ConnectorAuthType { + match self { + Self::TemporaryAuth => api_models::admin::ConnectorAuthType::TemporaryAuth, + Self::HeaderKey { api_key } => { + api_models::admin::ConnectorAuthType::HeaderKey { api_key } + } + Self::BodyKey { api_key, key1 } => { + api_models::admin::ConnectorAuthType::BodyKey { api_key, key1 } + } + Self::SignatureKey { + api_key, + key1, + api_secret, + } => api_models::admin::ConnectorAuthType::SignatureKey { + api_key, + key1, + api_secret, + }, + Self::MultiAuthKey { + api_key, + key1, + api_secret, + key2, + } => api_models::admin::ConnectorAuthType::MultiAuthKey { + api_key, + key1, + api_secret, + key2, + }, + Self::CurrencyAuthKey { auth_key_map } => { + api_models::admin::ConnectorAuthType::CurrencyAuthKey { auth_key_map } + } + Self::NoKey => api_models::admin::ConnectorAuthType::NoKey, + } + } +} + #[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] pub struct ConnectorsList { pub connectors: Vec, diff --git a/crates/router/src/types/api/verify_connector.rs b/crates/router/src/types/api/verify_connector.rs index 76a295c1db49..900b11711168 100644 --- a/crates/router/src/types/api/verify_connector.rs +++ b/crates/router/src/types/api/verify_connector.rs @@ -1,8 +1,6 @@ pub mod paypal; pub mod stripe; -use api_models::enums as api_enums; -use common_utils::events::{ApiEventMetric, ApiEventsType}; use error_stack::{IntoReport, ResultExt}; use router_env as env; @@ -15,14 +13,6 @@ use crate::{ AppState, }; -#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)] -pub struct VerifyConnectorRequest { - pub connector_name: api_enums::Connector, - pub connector_account_details: types::ConnectorAuthType, -} - -common_utils::impl_misc_api_event_type!(VerifyConnectorRequest); - #[derive(Clone, Debug)] pub struct VerifyConnectorData { pub connector: &'static (dyn types::api::Connector + Sync), From 7f7c4889a54c09c0825b5b1b1fcb74d43e738c6f Mon Sep 17 00:00:00 2001 From: Mani Chandra Dulam Date: Tue, 28 Nov 2023 16:30:05 +0530 Subject: [PATCH 08/13] refactor: make test_mode None in router_data construction --- crates/router/src/types/api/verify_connector.rs | 2 +- crates/router/src/utils.rs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/crates/router/src/types/api/verify_connector.rs b/crates/router/src/types/api/verify_connector.rs index 900b11711168..960d774664d6 100644 --- a/crates/router/src/types/api/verify_connector.rs +++ b/crates/router/src/types/api/verify_connector.rs @@ -65,7 +65,7 @@ impl VerifyConnectorData { response: Err(errors::ApiErrorResponse::InternalServerError.into()), connector: self.connector.id().to_string(), auth_type: storage_enums::AuthenticationType::NoThreeDs, - test_mode: Some(!matches!(env::which(), env::Env::Production)), + test_mode: None, return_url: None, attempt_id: attempt_id.clone(), description: None, diff --git a/crates/router/src/utils.rs b/crates/router/src/utils.rs index 8326d4aa5397..81968cd9b628 100644 --- a/crates/router/src/utils.rs +++ b/crates/router/src/utils.rs @@ -2,12 +2,12 @@ pub mod currency; pub mod custom_serde; pub mod db_utils; pub mod ext_traits; -#[cfg(feature = "olap")] -pub mod verify_connector; #[cfg(feature = "kv_store")] pub mod storage_partitioning; #[cfg(feature = "olap")] pub mod user; +#[cfg(feature = "olap")] +pub mod verify_connector; use std::fmt::Debug; From db503259815b0282b7a2515bd0227b2115552f2e Mon Sep 17 00:00:00 2001 From: Mani Chandra Dulam Date: Tue, 28 Nov 2023 16:33:23 +0530 Subject: [PATCH 09/13] refactor: remove unused imports --- crates/router/src/types/api/verify_connector.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/crates/router/src/types/api/verify_connector.rs b/crates/router/src/types/api/verify_connector.rs index 960d774664d6..3e3511ccb98f 100644 --- a/crates/router/src/types/api/verify_connector.rs +++ b/crates/router/src/types/api/verify_connector.rs @@ -2,7 +2,6 @@ pub mod paypal; pub mod stripe; use error_stack::{IntoReport, ResultExt}; -use router_env as env; use crate::{ consts, From c99c0cceb9e9f345d39b4896b19f1ff99ec7c86e Mon Sep 17 00:00:00 2001 From: Mani Chandra Dulam Date: Tue, 28 Nov 2023 16:57:39 +0530 Subject: [PATCH 10/13] fix: clippy lints --- crates/kgraph_utils/src/mca.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/kgraph_utils/src/mca.rs b/crates/kgraph_utils/src/mca.rs index a7f07be7dac1..d78c100359df 100644 --- a/crates/kgraph_utils/src/mca.rs +++ b/crates/kgraph_utils/src/mca.rs @@ -362,7 +362,7 @@ mod tests { connector_label: Some("something".to_string()), business_label: Some("food".to_string()), business_sub_label: None, - connector_account_details: ConnectorAuthType::default(), + connector_account_details: masking::Secret::new(serde_json::json!({})),, test_mode: None, disabled: None, metadata: None, From cce31a51d28f9f1608ec99d4bd27416525a96621 Mon Sep 17 00:00:00 2001 From: Mani Chandra Dulam Date: Tue, 28 Nov 2023 16:58:16 +0530 Subject: [PATCH 11/13] fix: format --- crates/kgraph_utils/src/mca.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/kgraph_utils/src/mca.rs b/crates/kgraph_utils/src/mca.rs index d78c100359df..deea51bd8808 100644 --- a/crates/kgraph_utils/src/mca.rs +++ b/crates/kgraph_utils/src/mca.rs @@ -362,7 +362,7 @@ mod tests { connector_label: Some("something".to_string()), business_label: Some("food".to_string()), business_sub_label: None, - connector_account_details: masking::Secret::new(serde_json::json!({})),, + connector_account_details: masking::Secret::new(serde_json::json!({})), test_mode: None, disabled: None, metadata: None, From cb204f155a5d33a5f7a3f06cc1054a1c2b1e714b Mon Sep 17 00:00:00 2001 From: Mani Chandra Dulam Date: Wed, 29 Nov 2023 19:35:32 +0530 Subject: [PATCH 12/13] refactor: replace Into to ForeignFrom for ConnectorAuthType --- crates/router/src/types.rs | 31 ++++++++++++++----------------- 1 file changed, 14 insertions(+), 17 deletions(-) diff --git a/crates/router/src/types.rs b/crates/router/src/types.rs index 9e38cdbcaa8d..8830d1db07f8 100644 --- a/crates/router/src/types.rs +++ b/crates/router/src/types.rs @@ -27,6 +27,7 @@ use self::{api::payments, storage::enums as storage_enums}; pub use crate::core::payments::{CustomerDetails, PaymentAddress}; #[cfg(feature = "payouts")] use crate::core::utils::IRRELEVANT_CONNECTOR_REQUEST_REFERENCE_ID_IN_DISPUTE_FLOW; +use crate::types::transformers::ForeignFrom; use crate::{ core::{ errors::{self, RouterResult}, @@ -980,40 +981,36 @@ impl From for ConnectorAuthType { } } -impl Into for ConnectorAuthType { - fn into(self) -> api_models::admin::ConnectorAuthType { - match self { - Self::TemporaryAuth => api_models::admin::ConnectorAuthType::TemporaryAuth, - Self::HeaderKey { api_key } => { - api_models::admin::ConnectorAuthType::HeaderKey { api_key } - } - Self::BodyKey { api_key, key1 } => { - api_models::admin::ConnectorAuthType::BodyKey { api_key, key1 } - } - Self::SignatureKey { +impl ForeignFrom for api_models::admin::ConnectorAuthType { + fn foreign_from(from: ConnectorAuthType) -> Self { + match from { + ConnectorAuthType::TemporaryAuth => Self::TemporaryAuth, + ConnectorAuthType::HeaderKey { api_key } => Self::HeaderKey { api_key }, + ConnectorAuthType::BodyKey { api_key, key1 } => Self::BodyKey { api_key, key1 }, + ConnectorAuthType::SignatureKey { api_key, key1, api_secret, - } => api_models::admin::ConnectorAuthType::SignatureKey { + } => Self::SignatureKey { api_key, key1, api_secret, }, - Self::MultiAuthKey { + ConnectorAuthType::MultiAuthKey { api_key, key1, api_secret, key2, - } => api_models::admin::ConnectorAuthType::MultiAuthKey { + } => Self::MultiAuthKey { api_key, key1, api_secret, key2, }, - Self::CurrencyAuthKey { auth_key_map } => { - api_models::admin::ConnectorAuthType::CurrencyAuthKey { auth_key_map } + ConnectorAuthType::CurrencyAuthKey { auth_key_map } => { + Self::CurrencyAuthKey { auth_key_map } } - Self::NoKey => api_models::admin::ConnectorAuthType::NoKey, + ConnectorAuthType::NoKey => Self::NoKey, } } } From b5d183d8d800bd848f7de9dab8b7279cb25a80b5 Mon Sep 17 00:00:00 2001 From: "hyperswitch-bot[bot]" <148525504+hyperswitch-bot[bot]@users.noreply.github.com> Date: Wed, 29 Nov 2023 14:06:10 +0000 Subject: [PATCH 13/13] chore: run formatter --- crates/router/src/types.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/crates/router/src/types.rs b/crates/router/src/types.rs index 8830d1db07f8..c1efe4c95528 100644 --- a/crates/router/src/types.rs +++ b/crates/router/src/types.rs @@ -27,14 +27,13 @@ use self::{api::payments, storage::enums as storage_enums}; pub use crate::core::payments::{CustomerDetails, PaymentAddress}; #[cfg(feature = "payouts")] use crate::core::utils::IRRELEVANT_CONNECTOR_REQUEST_REFERENCE_ID_IN_DISPUTE_FLOW; -use crate::types::transformers::ForeignFrom; use crate::{ core::{ errors::{self, RouterResult}, payments::{PaymentData, RecurringMandatePaymentData}, }, services, - types::storage::payment_attempt::PaymentAttemptExt, + types::{storage::payment_attempt::PaymentAttemptExt, transformers::ForeignFrom}, utils::OptionExt, };