diff --git a/crates/api_models/src/connector_onboarding.rs b/crates/api_models/src/connector_onboarding.rs index 759d3cb97f..7e8288d974 100644 --- a/crates/api_models/src/connector_onboarding.rs +++ b/crates/api_models/src/connector_onboarding.rs @@ -52,3 +52,9 @@ pub struct PayPalOnboardingDone { pub struct PayPalIntegrationDone { pub connector_id: String, } + +#[derive(serde::Deserialize, serde::Serialize, Debug, Clone)] +pub struct ResetTrackingIdRequest { + pub connector_id: String, + pub connector: enums::Connector, +} diff --git a/crates/api_models/src/events/connector_onboarding.rs b/crates/api_models/src/events/connector_onboarding.rs index 998dc384d6..0da89f61da 100644 --- a/crates/api_models/src/events/connector_onboarding.rs +++ b/crates/api_models/src/events/connector_onboarding.rs @@ -2,11 +2,13 @@ use common_utils::events::{ApiEventMetric, ApiEventsType}; use crate::connector_onboarding::{ ActionUrlRequest, ActionUrlResponse, OnboardingStatus, OnboardingSyncRequest, + ResetTrackingIdRequest, }; common_utils::impl_misc_api_event_type!( ActionUrlRequest, ActionUrlResponse, OnboardingSyncRequest, - OnboardingStatus + OnboardingStatus, + ResetTrackingIdRequest ); diff --git a/crates/router/src/consts.rs b/crates/router/src/consts.rs index ed020b0c7e..387da3c064 100644 --- a/crates/router/src/consts.rs +++ b/crates/router/src/consts.rs @@ -77,6 +77,9 @@ pub const VERIFY_CONNECTOR_ID_PREFIX: &str = "conn_verify"; #[cfg(feature = "olap")] pub const VERIFY_CONNECTOR_MERCHANT_ID: &str = "test_merchant"; +#[cfg(feature = "olap")] +pub const CONNECTOR_ONBOARDING_CONFIG_PREFIX: &str = "onboarding"; + /// Max payment session expiry pub const MAX_SESSION_EXPIRY: u32 = 7890000; diff --git a/crates/router/src/core/connector_onboarding.rs b/crates/router/src/core/connector_onboarding.rs index e48026edc2..e6c1fc9d37 100644 --- a/crates/router/src/core/connector_onboarding.rs +++ b/crates/router/src/core/connector_onboarding.rs @@ -1,5 +1,4 @@ use api_models::{connector_onboarding as api, enums}; -use error_stack::ResultExt; use masking::Secret; use crate::{ @@ -19,16 +18,23 @@ pub trait AccessToken { pub async fn get_action_url( state: AppState, + user_from_token: auth::UserFromToken, request: api::ActionUrlRequest, ) -> RouterResponse { + utils::check_if_connector_exists(&state, &request.connector_id, &user_from_token.merchant_id) + .await?; + let connector_onboarding_conf = state.conf.connector_onboarding.clone(); let is_enabled = utils::is_enabled(request.connector, &connector_onboarding_conf); + let tracking_id = + utils::get_tracking_id_from_configs(&state, &request.connector_id, request.connector) + .await?; match (is_enabled, request.connector) { (Some(true), enums::Connector::Paypal) => { let action_url = Box::pin(paypal::get_action_url_from_paypal( state, - request.connector_id, + tracking_id, request.return_url, )) .await?; @@ -49,40 +55,42 @@ pub async fn sync_onboarding_status( user_from_token: auth::UserFromToken, request: api::OnboardingSyncRequest, ) -> RouterResponse { - let merchant_account = user_from_token - .get_merchant_account(state.clone()) - .await - .change_context(ApiErrorResponse::MerchantAccountNotFound)?; + utils::check_if_connector_exists(&state, &request.connector_id, &user_from_token.merchant_id) + .await?; + let connector_onboarding_conf = state.conf.connector_onboarding.clone(); let is_enabled = utils::is_enabled(request.connector, &connector_onboarding_conf); + let tracking_id = + utils::get_tracking_id_from_configs(&state, &request.connector_id, request.connector) + .await?; match (is_enabled, request.connector) { (Some(true), enums::Connector::Paypal) => { let status = Box::pin(paypal::sync_merchant_onboarding_status( state.clone(), - request.connector_id.clone(), + tracking_id, )) .await?; if let api::OnboardingStatus::PayPal(api::PayPalOnboardingStatus::Success( - ref inner_data, + ref paypal_onboarding_data, )) = status { let connector_onboarding_conf = state.conf.connector_onboarding.clone(); let auth_details = oss_types::ConnectorAuthType::SignatureKey { api_key: connector_onboarding_conf.paypal.client_secret, key1: connector_onboarding_conf.paypal.client_id, - api_secret: Secret::new(inner_data.payer_id.clone()), + api_secret: Secret::new(paypal_onboarding_data.payer_id.clone()), }; - let some_data = paypal::update_mca( + let update_mca_data = paypal::update_mca( &state, - &merchant_account, + user_from_token.merchant_id, request.connector_id.to_owned(), auth_details, ) .await?; return Ok(ApplicationResponse::Json(api::OnboardingStatus::PayPal( - api::PayPalOnboardingStatus::ConnectorIntegrated(some_data), + api::PayPalOnboardingStatus::ConnectorIntegrated(update_mca_data), ))); } Ok(ApplicationResponse::Json(status)) @@ -94,3 +102,15 @@ pub async fn sync_onboarding_status( .into()), } } + +pub async fn reset_tracking_id( + state: AppState, + user_from_token: auth::UserFromToken, + request: api::ResetTrackingIdRequest, +) -> RouterResponse<()> { + utils::check_if_connector_exists(&state, &request.connector_id, &user_from_token.merchant_id) + .await?; + utils::set_tracking_id_in_configs(&state, &request.connector_id, request.connector).await?; + + Ok(ApplicationResponse::StatusOk) +} diff --git a/crates/router/src/core/connector_onboarding/paypal.rs b/crates/router/src/core/connector_onboarding/paypal.rs index 30aa69067b..f18681f8cf 100644 --- a/crates/router/src/core/connector_onboarding/paypal.rs +++ b/crates/router/src/core/connector_onboarding/paypal.rs @@ -23,11 +23,11 @@ fn build_referral_url(state: AppState) -> String { async fn build_referral_request( state: AppState, - connector_id: String, + tracking_id: String, return_url: String, ) -> RouterResult { let access_token = utils::paypal::generate_access_token(state.clone()).await?; - let request_body = types::paypal::PartnerReferralRequest::new(connector_id, return_url); + let request_body = types::paypal::PartnerReferralRequest::new(tracking_id, return_url); utils::paypal::build_paypal_post_request( build_referral_url(state), @@ -38,12 +38,12 @@ async fn build_referral_request( pub async fn get_action_url_from_paypal( state: AppState, - connector_id: String, + tracking_id: String, return_url: String, ) -> RouterResult { let referral_request = Box::pin(build_referral_request( state.clone(), - connector_id, + tracking_id, return_url, )) .await?; @@ -137,7 +137,7 @@ async fn find_paypal_merchant_by_tracking_id( pub async fn update_mca( state: &AppState, - merchant_account: &oss_types::domain::MerchantAccount, + merchant_id: String, connector_id: String, auth_details: oss_types::ConnectorAuthType, ) -> RouterResult { @@ -159,13 +159,9 @@ pub async fn update_mca( connector_webhook_details: None, pm_auth_config: None, }; - let mca_response = admin::update_payment_connector( - state.clone(), - &merchant_account.merchant_id, - &connector_id, - request, - ) - .await?; + let mca_response = + admin::update_payment_connector(state.clone(), &merchant_id, &connector_id, request) + .await?; match mca_response { ApplicationResponse::Json(mca_data) => Ok(mca_data), diff --git a/crates/router/src/routes/app.rs b/crates/router/src/routes/app.rs index 0b2acaf4e5..77253d1d75 100644 --- a/crates/router/src/routes/app.rs +++ b/crates/router/src/routes/app.rs @@ -961,5 +961,6 @@ impl ConnectorOnboarding { .app_data(web::Data::new(state)) .service(web::resource("/action_url").route(web::post().to(get_action_url))) .service(web::resource("/sync").route(web::post().to(sync_onboarding_status))) + .service(web::resource("/reset_tracking_id").route(web::post().to(reset_tracking_id))) } } diff --git a/crates/router/src/routes/connector_onboarding.rs b/crates/router/src/routes/connector_onboarding.rs index b7c39b3c1d..f5555f5bf9 100644 --- a/crates/router/src/routes/connector_onboarding.rs +++ b/crates/router/src/routes/connector_onboarding.rs @@ -20,7 +20,7 @@ pub async fn get_action_url( state, &http_req, req_payload.clone(), - |state, _: auth::UserFromToken, req| core::get_action_url(state, req), + core::get_action_url, &auth::JWTAuth(Permission::MerchantAccountWrite), api_locking::LockAction::NotApplicable, )) @@ -45,3 +45,22 @@ pub async fn sync_onboarding_status( )) .await } + +pub async fn reset_tracking_id( + state: web::Data, + http_req: HttpRequest, + json_payload: web::Json, +) -> HttpResponse { + let flow = Flow::ResetTrackingId; + let req_payload = json_payload.into_inner(); + Box::pin(api::server_wrap( + flow.clone(), + state, + &http_req, + req_payload.clone(), + core::reset_tracking_id, + &auth::JWTAuth(Permission::MerchantAccountWrite), + api_locking::LockAction::NotApplicable, + )) + .await +} diff --git a/crates/router/src/routes/lock_utils.rs b/crates/router/src/routes/lock_utils.rs index 55c6cbc23d..c560f0d988 100644 --- a/crates/router/src/routes/lock_utils.rs +++ b/crates/router/src/routes/lock_utils.rs @@ -183,7 +183,9 @@ impl From for ApiIdentifier { Self::UserRole } - Flow::GetActionUrl | Flow::SyncOnboardingStatus => Self::ConnectorOnboarding, + Flow::GetActionUrl | Flow::SyncOnboardingStatus | Flow::ResetTrackingId => { + Self::ConnectorOnboarding + } } } } diff --git a/crates/router/src/utils/connector_onboarding.rs b/crates/router/src/utils/connector_onboarding.rs index e8afcd68a4..03735e61cc 100644 --- a/crates/router/src/utils/connector_onboarding.rs +++ b/crates/router/src/utils/connector_onboarding.rs @@ -1,6 +1,11 @@ +use diesel_models::{ConfigNew, ConfigUpdate}; +use error_stack::ResultExt; + +use super::errors::StorageErrorExt; use crate::{ + consts, core::errors::{api_error_response::NotImplementedMessage, ApiErrorResponse, RouterResult}, - routes::app::settings, + routes::{app::settings, AppState}, types::{self, api::enums}, }; @@ -34,3 +39,105 @@ pub fn is_enabled( _ => None, } } + +pub async fn check_if_connector_exists( + state: &AppState, + connector_id: &str, + merchant_id: &str, +) -> RouterResult<()> { + let key_store = state + .store + .get_merchant_key_store_by_merchant_id( + merchant_id, + &state.store.get_master_key().to_vec().into(), + ) + .await + .to_not_found_response(ApiErrorResponse::MerchantAccountNotFound)?; + + let _connector = state + .store + .find_by_merchant_connector_account_merchant_id_merchant_connector_id( + merchant_id, + connector_id, + &key_store, + ) + .await + .to_not_found_response(ApiErrorResponse::MerchantConnectorAccountNotFound { + id: connector_id.to_string(), + })?; + + Ok(()) +} + +pub async fn set_tracking_id_in_configs( + state: &AppState, + connector_id: &str, + connector: enums::Connector, +) -> RouterResult<()> { + let timestamp = common_utils::date_time::now_unix_timestamp().to_string(); + let find_config = state + .store + .find_config_by_key(&build_key(connector_id, connector)) + .await; + + if find_config.is_ok() { + state + .store + .update_config_by_key( + &build_key(connector_id, connector), + ConfigUpdate::Update { + config: Some(timestamp), + }, + ) + .await + .change_context(ApiErrorResponse::InternalServerError) + .attach_printable("Error updating data in configs table")?; + } else if find_config + .as_ref() + .map_err(|e| e.current_context().is_db_not_found()) + .err() + .unwrap_or(false) + { + state + .store + .insert_config(ConfigNew { + key: build_key(connector_id, connector), + config: timestamp, + }) + .await + .change_context(ApiErrorResponse::InternalServerError) + .attach_printable("Error inserting data in configs table")?; + } else { + find_config.change_context(ApiErrorResponse::InternalServerError)?; + } + + Ok(()) +} + +pub async fn get_tracking_id_from_configs( + state: &AppState, + connector_id: &str, + connector: enums::Connector, +) -> RouterResult { + let timestamp = state + .store + .find_config_by_key_unwrap_or( + &build_key(connector_id, connector), + Some(common_utils::date_time::now_unix_timestamp().to_string()), + ) + .await + .change_context(ApiErrorResponse::InternalServerError) + .attach_printable("Error getting data from configs table")? + .config; + + Ok(format!("{}_{}", connector_id, timestamp)) +} + +fn build_key(connector_id: &str, connector: enums::Connector) -> String { + format!( + "{}_{}_{}", + consts::CONNECTOR_ONBOARDING_CONFIG_PREFIX, + connector, + connector_id, + ) +} diff --git a/crates/router_env/src/logger/types.rs b/crates/router_env/src/logger/types.rs index a6ac1b1e0a..8f0b9bad3e 100644 --- a/crates/router_env/src/logger/types.rs +++ b/crates/router_env/src/logger/types.rs @@ -317,6 +317,8 @@ pub enum Flow { GetActionUrl, /// Sync connector onboarding status SyncOnboardingStatus, + /// Reset tracking id + ResetTrackingId, /// Verify email Token VerifyEmail, /// Send verify email