fix(connector_onboarding): Check if connector exists for the merchant account and add reset tracking id API (#3229)

This commit is contained in:
Mani Chandra
2024-01-16 13:06:47 +05:30
committed by GitHub
parent 1bbd9d5df0
commit 58cc8d6109
10 changed files with 186 additions and 28 deletions

View File

@ -52,3 +52,9 @@ pub struct PayPalOnboardingDone {
pub struct PayPalIntegrationDone { pub struct PayPalIntegrationDone {
pub connector_id: String, pub connector_id: String,
} }
#[derive(serde::Deserialize, serde::Serialize, Debug, Clone)]
pub struct ResetTrackingIdRequest {
pub connector_id: String,
pub connector: enums::Connector,
}

View File

@ -2,11 +2,13 @@ use common_utils::events::{ApiEventMetric, ApiEventsType};
use crate::connector_onboarding::{ use crate::connector_onboarding::{
ActionUrlRequest, ActionUrlResponse, OnboardingStatus, OnboardingSyncRequest, ActionUrlRequest, ActionUrlResponse, OnboardingStatus, OnboardingSyncRequest,
ResetTrackingIdRequest,
}; };
common_utils::impl_misc_api_event_type!( common_utils::impl_misc_api_event_type!(
ActionUrlRequest, ActionUrlRequest,
ActionUrlResponse, ActionUrlResponse,
OnboardingSyncRequest, OnboardingSyncRequest,
OnboardingStatus OnboardingStatus,
ResetTrackingIdRequest
); );

View File

@ -77,6 +77,9 @@ pub const VERIFY_CONNECTOR_ID_PREFIX: &str = "conn_verify";
#[cfg(feature = "olap")] #[cfg(feature = "olap")]
pub const VERIFY_CONNECTOR_MERCHANT_ID: &str = "test_merchant"; pub const VERIFY_CONNECTOR_MERCHANT_ID: &str = "test_merchant";
#[cfg(feature = "olap")]
pub const CONNECTOR_ONBOARDING_CONFIG_PREFIX: &str = "onboarding";
/// Max payment session expiry /// Max payment session expiry
pub const MAX_SESSION_EXPIRY: u32 = 7890000; pub const MAX_SESSION_EXPIRY: u32 = 7890000;

View File

@ -1,5 +1,4 @@
use api_models::{connector_onboarding as api, enums}; use api_models::{connector_onboarding as api, enums};
use error_stack::ResultExt;
use masking::Secret; use masking::Secret;
use crate::{ use crate::{
@ -19,16 +18,23 @@ pub trait AccessToken {
pub async fn get_action_url( pub async fn get_action_url(
state: AppState, state: AppState,
user_from_token: auth::UserFromToken,
request: api::ActionUrlRequest, request: api::ActionUrlRequest,
) -> RouterResponse<api::ActionUrlResponse> { ) -> RouterResponse<api::ActionUrlResponse> {
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 connector_onboarding_conf = state.conf.connector_onboarding.clone();
let is_enabled = utils::is_enabled(request.connector, &connector_onboarding_conf); 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) { match (is_enabled, request.connector) {
(Some(true), enums::Connector::Paypal) => { (Some(true), enums::Connector::Paypal) => {
let action_url = Box::pin(paypal::get_action_url_from_paypal( let action_url = Box::pin(paypal::get_action_url_from_paypal(
state, state,
request.connector_id, tracking_id,
request.return_url, request.return_url,
)) ))
.await?; .await?;
@ -49,40 +55,42 @@ pub async fn sync_onboarding_status(
user_from_token: auth::UserFromToken, user_from_token: auth::UserFromToken,
request: api::OnboardingSyncRequest, request: api::OnboardingSyncRequest,
) -> RouterResponse<api::OnboardingStatus> { ) -> RouterResponse<api::OnboardingStatus> {
let merchant_account = user_from_token utils::check_if_connector_exists(&state, &request.connector_id, &user_from_token.merchant_id)
.get_merchant_account(state.clone()) .await?;
.await
.change_context(ApiErrorResponse::MerchantAccountNotFound)?;
let connector_onboarding_conf = state.conf.connector_onboarding.clone(); let connector_onboarding_conf = state.conf.connector_onboarding.clone();
let is_enabled = utils::is_enabled(request.connector, &connector_onboarding_conf); 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) { match (is_enabled, request.connector) {
(Some(true), enums::Connector::Paypal) => { (Some(true), enums::Connector::Paypal) => {
let status = Box::pin(paypal::sync_merchant_onboarding_status( let status = Box::pin(paypal::sync_merchant_onboarding_status(
state.clone(), state.clone(),
request.connector_id.clone(), tracking_id,
)) ))
.await?; .await?;
if let api::OnboardingStatus::PayPal(api::PayPalOnboardingStatus::Success( if let api::OnboardingStatus::PayPal(api::PayPalOnboardingStatus::Success(
ref inner_data, ref paypal_onboarding_data,
)) = status )) = status
{ {
let connector_onboarding_conf = state.conf.connector_onboarding.clone(); let connector_onboarding_conf = state.conf.connector_onboarding.clone();
let auth_details = oss_types::ConnectorAuthType::SignatureKey { let auth_details = oss_types::ConnectorAuthType::SignatureKey {
api_key: connector_onboarding_conf.paypal.client_secret, api_key: connector_onboarding_conf.paypal.client_secret,
key1: connector_onboarding_conf.paypal.client_id, 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, &state,
&merchant_account, user_from_token.merchant_id,
request.connector_id.to_owned(), request.connector_id.to_owned(),
auth_details, auth_details,
) )
.await?; .await?;
return Ok(ApplicationResponse::Json(api::OnboardingStatus::PayPal( return Ok(ApplicationResponse::Json(api::OnboardingStatus::PayPal(
api::PayPalOnboardingStatus::ConnectorIntegrated(some_data), api::PayPalOnboardingStatus::ConnectorIntegrated(update_mca_data),
))); )));
} }
Ok(ApplicationResponse::Json(status)) Ok(ApplicationResponse::Json(status))
@ -94,3 +102,15 @@ pub async fn sync_onboarding_status(
.into()), .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)
}

View File

@ -23,11 +23,11 @@ fn build_referral_url(state: AppState) -> String {
async fn build_referral_request( async fn build_referral_request(
state: AppState, state: AppState,
connector_id: String, tracking_id: String,
return_url: String, return_url: String,
) -> RouterResult<Request> { ) -> RouterResult<Request> {
let access_token = utils::paypal::generate_access_token(state.clone()).await?; 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( utils::paypal::build_paypal_post_request(
build_referral_url(state), build_referral_url(state),
@ -38,12 +38,12 @@ async fn build_referral_request(
pub async fn get_action_url_from_paypal( pub async fn get_action_url_from_paypal(
state: AppState, state: AppState,
connector_id: String, tracking_id: String,
return_url: String, return_url: String,
) -> RouterResult<String> { ) -> RouterResult<String> {
let referral_request = Box::pin(build_referral_request( let referral_request = Box::pin(build_referral_request(
state.clone(), state.clone(),
connector_id, tracking_id,
return_url, return_url,
)) ))
.await?; .await?;
@ -137,7 +137,7 @@ async fn find_paypal_merchant_by_tracking_id(
pub async fn update_mca( pub async fn update_mca(
state: &AppState, state: &AppState,
merchant_account: &oss_types::domain::MerchantAccount, merchant_id: String,
connector_id: String, connector_id: String,
auth_details: oss_types::ConnectorAuthType, auth_details: oss_types::ConnectorAuthType,
) -> RouterResult<oss_api_types::MerchantConnectorResponse> { ) -> RouterResult<oss_api_types::MerchantConnectorResponse> {
@ -159,13 +159,9 @@ pub async fn update_mca(
connector_webhook_details: None, connector_webhook_details: None,
pm_auth_config: None, pm_auth_config: None,
}; };
let mca_response = admin::update_payment_connector( let mca_response =
state.clone(), admin::update_payment_connector(state.clone(), &merchant_id, &connector_id, request)
&merchant_account.merchant_id, .await?;
&connector_id,
request,
)
.await?;
match mca_response { match mca_response {
ApplicationResponse::Json(mca_data) => Ok(mca_data), ApplicationResponse::Json(mca_data) => Ok(mca_data),

View File

@ -961,5 +961,6 @@ impl ConnectorOnboarding {
.app_data(web::Data::new(state)) .app_data(web::Data::new(state))
.service(web::resource("/action_url").route(web::post().to(get_action_url))) .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("/sync").route(web::post().to(sync_onboarding_status)))
.service(web::resource("/reset_tracking_id").route(web::post().to(reset_tracking_id)))
} }
} }

View File

@ -20,7 +20,7 @@ pub async fn get_action_url(
state, state,
&http_req, &http_req,
req_payload.clone(), req_payload.clone(),
|state, _: auth::UserFromToken, req| core::get_action_url(state, req), core::get_action_url,
&auth::JWTAuth(Permission::MerchantAccountWrite), &auth::JWTAuth(Permission::MerchantAccountWrite),
api_locking::LockAction::NotApplicable, api_locking::LockAction::NotApplicable,
)) ))
@ -45,3 +45,22 @@ pub async fn sync_onboarding_status(
)) ))
.await .await
} }
pub async fn reset_tracking_id(
state: web::Data<AppState>,
http_req: HttpRequest,
json_payload: web::Json<api_types::ResetTrackingIdRequest>,
) -> 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
}

View File

@ -183,7 +183,9 @@ impl From<Flow> for ApiIdentifier {
Self::UserRole Self::UserRole
} }
Flow::GetActionUrl | Flow::SyncOnboardingStatus => Self::ConnectorOnboarding, Flow::GetActionUrl | Flow::SyncOnboardingStatus | Flow::ResetTrackingId => {
Self::ConnectorOnboarding
}
} }
} }
} }

View File

@ -1,6 +1,11 @@
use diesel_models::{ConfigNew, ConfigUpdate};
use error_stack::ResultExt;
use super::errors::StorageErrorExt;
use crate::{ use crate::{
consts,
core::errors::{api_error_response::NotImplementedMessage, ApiErrorResponse, RouterResult}, core::errors::{api_error_response::NotImplementedMessage, ApiErrorResponse, RouterResult},
routes::app::settings, routes::{app::settings, AppState},
types::{self, api::enums}, types::{self, api::enums},
}; };
@ -34,3 +39,105 @@ pub fn is_enabled(
_ => None, _ => 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<String> {
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,
)
}

View File

@ -317,6 +317,8 @@ pub enum Flow {
GetActionUrl, GetActionUrl,
/// Sync connector onboarding status /// Sync connector onboarding status
SyncOnboardingStatus, SyncOnboardingStatus,
/// Reset tracking id
ResetTrackingId,
/// Verify email Token /// Verify email Token
VerifyEmail, VerifyEmail,
/// Send verify email /// Send verify email