Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(connector_onboarding): Add Connector onboarding APIs #3050

Merged
merged 14 commits into from
Dec 5, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions config/config.example.toml
Original file line number Diff line number Diff line change
Expand Up @@ -477,3 +477,9 @@ connection_timeout = 10 # Timeout for database connection in seconds
[kv_config]
# TTL for KV in seconds
ttl = 900

[paypal_onboarding]
client_id = "paypal_client_id" # Client ID for PayPal onboarding
client_secret = "paypal_secret_key" # Secret key for PayPal onboarding
partner_id = "paypal_partner_id" # Partner ID for PayPal onboarding
enabled = true # Switch to enable or disable PayPal onboarding
8 changes: 7 additions & 1 deletion config/development.toml
Original file line number Diff line number Diff line change
Expand Up @@ -504,4 +504,10 @@ port = 5432
dbname = "hyperswitch_db"
pool_size = 5
connection_timeout = 10
queue_strategy = "Fifo"
queue_strategy = "Fifo"

[connector_onboarding.paypal]
client_id = ""
client_secret = ""
partner_id = ""
enabled = true
6 changes: 6 additions & 0 deletions config/docker_compose.toml
Original file line number Diff line number Diff line change
Expand Up @@ -362,3 +362,9 @@ queue_strategy = "Fifo"

[kv_config]
ttl = 900 # 15 * 60 seconds

[connector_onboarding.paypal]
client_id = ""
client_secret = ""
partner_id = ""
enabled = true
54 changes: 54 additions & 0 deletions crates/api_models/src/connector_onboarding.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
use super::{admin, enums};

#[derive(serde::Deserialize, serde::Serialize, Debug, Clone)]
pub struct ActionUrlRequest {
pub connector: enums::Connector,
pub connector_id: String,
pub return_url: String,
}

#[derive(serde::Serialize, Debug, Clone)]
#[serde(rename_all = "lowercase")]
pub enum ActionUrlResponse {
PayPal(PayPalActionUrlResponse),
}

#[derive(serde::Deserialize, serde::Serialize, Debug, Clone)]
pub struct OnboardingSyncRequest {
pub profile_id: String,
pub connector_id: String,
pub connector: enums::Connector,
}

#[derive(serde::Serialize, Debug, Clone)]
pub struct PayPalActionUrlResponse {
pub action_url: String,
}

#[derive(serde::Serialize, Debug, Clone)]
#[serde(rename_all = "lowercase")]
pub enum OnboardingStatus {
PayPal(PayPalOnboardingStatus),
}

#[derive(serde::Serialize, Debug, Clone)]
#[serde(rename_all = "snake_case")]
pub enum PayPalOnboardingStatus {
AccountNotFound,
PaymentsNotReceivable,
PpcpCustomDenied,
MorePermissionsNeeded,
EmailNotVerified,
Success(PayPalOnboardingDone),
ConnectorIntegrated(admin::MerchantConnectorResponse),
}

#[derive(serde::Serialize, Debug, Clone)]
pub struct PayPalOnboardingDone {
pub payer_id: String,
}

#[derive(serde::Serialize, Debug, Clone)]
pub struct PayPalIntegrationDone {
pub connector_id: String,
}
1 change: 1 addition & 0 deletions crates/api_models/src/events.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
pub mod connector_onboarding;
pub mod customer;
pub mod gsm;
mod locker_migration;
Expand Down
12 changes: 12 additions & 0 deletions crates/api_models/src/events/connector_onboarding.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
use common_utils::events::{ApiEventMetric, ApiEventsType};

use crate::connector_onboarding::{
ActionUrlRequest, ActionUrlResponse, OnboardingStatus, OnboardingSyncRequest,
};

common_utils::impl_misc_api_event_type!(
ActionUrlRequest,
ActionUrlResponse,
OnboardingSyncRequest,
OnboardingStatus
);
1 change: 1 addition & 0 deletions crates/api_models/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ pub mod api_keys;
pub mod bank_accounts;
pub mod cards_info;
pub mod conditional_configs;
pub mod connector_onboarding;
pub mod currency;
pub mod customers;
pub mod disputes;
Expand Down
33 changes: 33 additions & 0 deletions crates/router/src/configs/kms.rs
Original file line number Diff line number Diff line change
Expand Up @@ -69,3 +69,36 @@ impl KmsDecrypt for settings::Database {
})
}
}

#[cfg(feature = "olap")]
#[async_trait::async_trait]
impl KmsDecrypt for settings::PayPalOnboarding {
type Output = Self;

async fn decrypt_inner(
mut self,
kms_client: &KmsClient,
) -> CustomResult<Self::Output, KmsError> {
self.client_id = kms_client.decrypt(self.client_id.expose()).await?.into();
self.client_secret = kms_client
.decrypt(self.client_secret.expose())
.await?
.into();
self.partner_id = kms_client.decrypt(self.partner_id.expose()).await?.into();
Ok(self)
}
}

#[cfg(feature = "olap")]
#[async_trait::async_trait]
impl KmsDecrypt for settings::ConnectorOnboarding {
type Output = Self;

async fn decrypt_inner(
mut self,
kms_client: &KmsClient,
) -> CustomResult<Self::Output, KmsError> {
self.paypal = self.paypal.decrypt_inner(kms_client).await?;
Ok(self)
}
}
17 changes: 17 additions & 0 deletions crates/router/src/configs/settings.rs
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,8 @@ pub struct Settings {
#[cfg(feature = "olap")]
pub report_download_config: ReportConfig,
pub events: EventsConfig,
#[cfg(feature = "olap")]
pub connector_onboarding: ConnectorOnboarding,
}

#[derive(Debug, Deserialize, Clone)]
Expand Down Expand Up @@ -884,3 +886,18 @@ impl<'de> Deserialize<'de> for LockSettings {
})
}
}

#[cfg(feature = "olap")]
#[derive(Debug, Deserialize, Clone, Default)]
pub struct ConnectorOnboarding {
pub paypal: PayPalOnboarding,
}

#[cfg(feature = "olap")]
#[derive(Debug, Deserialize, Clone, Default)]
pub struct PayPalOnboarding {
pub client_id: masking::Secret<String>,
pub client_secret: masking::Secret<String>,
pub partner_id: masking::Secret<String>,
pub enabled: bool,
}
2 changes: 2 additions & 0 deletions crates/router/src/core.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ pub mod cache;
pub mod cards_info;
pub mod conditional_config;
pub mod configs;
#[cfg(feature = "olap")]
pub mod connector_onboarding;
#[cfg(any(feature = "olap", feature = "oltp"))]
pub mod currency;
pub mod customers;
Expand Down
96 changes: 96 additions & 0 deletions crates/router/src/core/connector_onboarding.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
use api_models::{connector_onboarding as api, enums};
use error_stack::ResultExt;
use masking::Secret;

use crate::{
core::errors::{ApiErrorResponse, RouterResponse, RouterResult},
services::{authentication as auth, ApplicationResponse},
types::{self as oss_types},
utils::connector_onboarding as utils,
AppState,
};

pub mod paypal;

#[async_trait::async_trait]
pub trait AccessToken {
async fn access_token(state: &AppState) -> RouterResult<oss_types::AccessToken>;
}

pub async fn get_action_url(
state: AppState,
request: api::ActionUrlRequest,
) -> RouterResponse<api::ActionUrlResponse> {
let connector_onboarding_conf = state.conf.connector_onboarding.clone();
let is_enabled = utils::is_enabled(request.connector, &connector_onboarding_conf);

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,
request.return_url,
))
.await?;
Ok(ApplicationResponse::Json(api::ActionUrlResponse::PayPal(
api::PayPalActionUrlResponse { action_url },
)))
}
_ => Err(ApiErrorResponse::FlowNotSupported {
flow: "Connector onboarding".to_string(),
connector: request.connector.to_string(),
}
.into()),
}
}

pub async fn sync_onboarding_status(
state: AppState,
user_from_token: auth::UserFromToken,
request: api::OnboardingSyncRequest,
) -> RouterResponse<api::OnboardingStatus> {
let merchant_account = user_from_token
.get_merchant_account(state.clone())
.await
.change_context(ApiErrorResponse::MerchantAccountNotFound)?;
let connector_onboarding_conf = state.conf.connector_onboarding.clone();
let is_enabled = utils::is_enabled(request.connector, &connector_onboarding_conf);

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(),
))
.await?;
if let api::OnboardingStatus::PayPal(api::PayPalOnboardingStatus::Success(
ref inner_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()),
};
let some_data = paypal::update_mca(
&state,
&merchant_account,
request.connector_id.to_owned(),
auth_details,
)
.await?;

return Ok(ApplicationResponse::Json(api::OnboardingStatus::PayPal(
api::PayPalOnboardingStatus::ConnectorIntegrated(some_data),
)));
}
Ok(ApplicationResponse::Json(status))
}
_ => Err(ApiErrorResponse::FlowNotSupported {
flow: "Connector onboarding".to_string(),
connector: request.connector.to_string(),
}
.into()),
}
}
Loading
Loading