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(router): send poll_config in next_action of confirm response for external 3ds flow #4443

Merged
merged 6 commits into from Apr 29, 2024
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
12 changes: 12 additions & 0 deletions crates/api_models/src/payments.rs
Expand Up @@ -2825,6 +2825,8 @@ pub struct ThreeDsData {
pub three_ds_authorize_url: String,
/// ThreeDS method details
pub three_ds_method_details: ThreeDsMethodData,
/// Poll config for a connector
pub poll_config: PollConfigResponse,
}

#[derive(Clone, Debug, Eq, PartialEq, serde::Serialize, ToSchema)]
Expand All @@ -2841,6 +2843,16 @@ pub enum ThreeDsMethodData {
},
}

#[derive(Clone, Debug, Eq, PartialEq, serde::Serialize, ToSchema)]
pub struct PollConfigResponse {
/// Poll Id
pub poll_id: String,
/// Interval of the poll
pub delay_in_secs: i8,
/// Frequency of the poll
pub frequency: i8,
}

#[derive(Clone, Debug, serde::Serialize, serde::Deserialize)]
#[serde(rename_all = "snake_case")]
#[serde(untagged)]
Expand Down
1 change: 1 addition & 0 deletions crates/openapi/src/openapi.rs
Expand Up @@ -405,6 +405,7 @@ Never share your secret api keys. Keep them guarded and secure.
api_models::payments::PaymentCreatePaymentLinkConfig,
api_models::payments::ThreeDsData,
api_models::payments::ThreeDsMethodData,
api_models::payments::PollConfigResponse,
api_models::payments::ExternalAuthenticationDetailsResponse,
api_models::payment_methods::RequiredFieldInfo,
api_models::payment_methods::DefaultPaymentMethod,
Expand Down
35 changes: 31 additions & 4 deletions crates/router/src/core/authentication.rs
Expand Up @@ -5,7 +5,10 @@ pub mod types;

use api_models::payments;
use common_enums::Currency;
use common_utils::{errors::CustomResult, ext_traits::ValueExt};
use common_utils::{
errors::CustomResult,
ext_traits::{Encode, StringExt, ValueExt},
};
use error_stack::{report, ResultExt};
use masking::{ExposeInterface, PeekInterface};

Expand Down Expand Up @@ -234,9 +237,12 @@ pub async fn perform_pre_authentication<F: Clone + Send>(
&three_ds_connector_account,
business_profile.merchant_id.clone(),
)?;
let router_data =
utils::do_auth_connector_call(state, authentication_connector_name, router_data)
.await?;
let router_data = utils::do_auth_connector_call(
state,
authentication_connector_name.clone(),
router_data,
)
.await?;
let acquirer_details: types::AcquirerDetails = payment_connector_account
.get_metadata()
.get_required_value("merchant_connector_account.metadata")?
Expand All @@ -257,6 +263,27 @@ pub async fn perform_pre_authentication<F: Clone + Send>(
|| authentication.authentication_status.is_failed()
{
*should_continue_confirm_transaction = false;
// If flow is going through external authentication, set the poll_config in payment_data which can be fetched while sending next_action block in confirm response
let default_poll_config = core_types::PollConfig::default();
let default_config_str = default_poll_config
.encode_to_string_of_json()
.change_context(errors::ApiErrorResponse::InternalServerError)
.attach_printable("Error while stringifying default poll config")?;
let poll_config = state
.store
.find_config_by_key_unwrap_or(
&format!("poll_config_external_three_ds_{authentication_connector_name}"),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If PollConfig is a connector property, This logic can be moved into a impl function in PollConfig.

Some(default_config_str),
)
.await
.change_context(errors::ApiErrorResponse::InternalServerError)
.attach_printable("The poll config was not found in the DB")?;
let poll_config: core_types::PollConfig = poll_config
.config
.parse_struct("PollConfig")
.change_context(errors::ApiErrorResponse::InternalServerError)
.attach_printable("Error while parsing PollConfig")?;
payment_data.poll_config = Some(poll_config)
}
payment_data.authentication = Some(authentication);
}
Expand Down
17 changes: 11 additions & 6 deletions crates/router/src/core/payments.rs
Expand Up @@ -22,7 +22,11 @@ use api_models::{
mandates::RecurringDetails,
payments::{self as payments_api, HeaderPayload},
};
use common_utils::{ext_traits::AsyncExt, pii, types::Surcharge};
use common_utils::{
ext_traits::{AsyncExt, StringExt},
pii,
types::Surcharge,
};
use data_models::mandates::{CustomerAcceptance, MandateData};
use diesel_models::{ephemeral_key, fraud_check::FraudCheck};
use error_stack::{report, ResultExt};
Expand Down Expand Up @@ -1151,11 +1155,11 @@ impl<Ctx: PaymentMethodRetrieve> PaymentRedirectFlow<Ctx> for PaymentAuthenticat
.await
.change_context(errors::ApiErrorResponse::InternalServerError)
.attach_printable("The poll config was not found in the DB")?;
let poll_config =
serde_json::from_str::<Option<router_types::PollConfig>>(&poll_config.config)
.change_context(errors::ApiErrorResponse::InternalServerError)
.attach_printable("Error while parsing PollConfig")?
.unwrap_or(default_poll_config);
let poll_config: router_types::PollConfig = poll_config
.config
.parse_struct("PollConfig")
.change_context(errors::ApiErrorResponse::InternalServerError)
.attach_printable("Error while parsing PollConfig")?;
let profile_id = payments_response
.profile_id
.as_ref()
Expand Down Expand Up @@ -2377,6 +2381,7 @@ where
pub authentication: Option<storage::Authentication>,
pub frm_metadata: Option<serde_json::Value>,
pub recurring_details: Option<RecurringDetails>,
pub poll_config: Option<router_types::PollConfig>,
}

#[derive(Clone, serde::Serialize, Debug)]
Expand Down
Expand Up @@ -178,6 +178,7 @@ impl<F: Send + Clone, Ctx: PaymentMethodRetrieve>
frm_metadata: None,
authentication: None,
recurring_details: None,
poll_config: None,
};

let get_trackers_response = operations::GetTrackerResponse {
Expand Down
Expand Up @@ -188,6 +188,7 @@ impl<F: Send + Clone, Ctx: PaymentMethodRetrieve>
frm_metadata: None,
authentication: None,
recurring_details: None,
poll_config: None,
};

let get_trackers_response = operations::GetTrackerResponse {
Expand Down
Expand Up @@ -231,6 +231,7 @@ impl<F: Send + Clone, Ctx: PaymentMethodRetrieve>
frm_metadata: None,
authentication: None,
recurring_details: None,
poll_config: None,
};

let get_trackers_response = operations::GetTrackerResponse {
Expand Down
Expand Up @@ -307,6 +307,7 @@ impl<F: Send + Clone, Ctx: PaymentMethodRetrieve>
authentication: None,
frm_metadata: None,
recurring_details,
poll_config: None,
};

let customer_details = Some(CustomerDetails {
Expand Down
Expand Up @@ -634,6 +634,7 @@ impl<F: Send + Clone, Ctx: PaymentMethodRetrieve>
frm_metadata: request.frm_metadata.clone(),
authentication,
recurring_details,
poll_config: None,
};

let get_trackers_response = operations::GetTrackerResponse {
Expand Down
Expand Up @@ -447,6 +447,7 @@ impl<F: Send + Clone, Ctx: PaymentMethodRetrieve>
authentication: None,
frm_metadata: request.frm_metadata.clone(),
recurring_details,
poll_config: None,
};

let get_trackers_response = operations::GetTrackerResponse {
Expand Down
Expand Up @@ -174,6 +174,7 @@ impl<F: Send + Clone, Ctx: PaymentMethodRetrieve>
authentication: None,
frm_metadata: None,
recurring_details: None,
poll_config: None,
};

let get_trackers_response = operations::GetTrackerResponse {
Expand Down
Expand Up @@ -199,6 +199,7 @@ impl<F: Send + Clone, Ctx: PaymentMethodRetrieve>
authentication: None,
frm_metadata: None,
recurring_details: None,
poll_config: None,
};

let get_trackers_response = operations::GetTrackerResponse {
Expand Down
Expand Up @@ -186,6 +186,7 @@ impl<F: Send + Clone, Ctx: PaymentMethodRetrieve>
authentication: None,
frm_metadata: None,
recurring_details: None,
poll_config: None,
};

let get_trackers_response = operations::GetTrackerResponse {
Expand Down
Expand Up @@ -490,6 +490,7 @@ async fn get_tracker_for_sync<
authentication,
frm_metadata: None,
recurring_details: None,
poll_config: None,
};

let get_trackers_response = operations::GetTrackerResponse {
Expand Down
Expand Up @@ -453,6 +453,7 @@ impl<F: Send + Clone, Ctx: PaymentMethodRetrieve>
authentication: None,
frm_metadata: request.frm_metadata.clone(),
recurring_details,
poll_config: None,
};

let get_trackers_response = operations::GetTrackerResponse {
Expand Down
Expand Up @@ -155,6 +155,7 @@ impl<F: Send + Clone, Ctx: PaymentMethodRetrieve>
authentication: None,
frm_metadata: None,
recurring_details: None,
poll_config: None,
};

let get_trackers_response = operations::GetTrackerResponse {
Expand Down
3 changes: 3 additions & 0 deletions crates/router/src/core/payments/transformers.rs
Expand Up @@ -570,6 +570,8 @@ where
Some(authentication) => {
if payment_intent.status == common_enums::IntentStatus::RequiresCustomerAction && authentication.cavv.is_none() && authentication.is_separate_authn_required(){
// if preAuthn and separate authentication needed.
let poll_config = payment_data.poll_config.unwrap_or_default();
let request_poll_id = core_utils::get_external_authentication_request_poll_id(&payment_intent.payment_id);
let payment_connector_name = payment_attempt.connector
.as_ref()
.get_required_value("connector")?;
Expand All @@ -592,6 +594,7 @@ where
three_ds_method_data: None,
three_ds_method_url: None,
}),
poll_config: api_models::payments::PollConfigResponse {poll_id: request_poll_id, delay_in_secs: poll_config.delay_in_secs, frequency: poll_config.frequency},
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit pick.
PollConfigResponse can have function named new that would accept poll_id and poll_config and return the constructed PollConfigResponse.

},
})
}else{
Expand Down
30 changes: 29 additions & 1 deletion openapi/openapi_spec.json
Expand Up @@ -16316,6 +16316,30 @@
}
}
},
"PollConfigResponse": {
"type": "object",
"required": [
"poll_id",
"delay_in_secs",
"frequency"
],
"properties": {
"poll_id": {
"type": "string",
"description": "Poll Id"
},
"delay_in_secs": {
"type": "integer",
"format": "int32",
"description": "Interval of the poll"
},
"frequency": {
"type": "integer",
"format": "int32",
"description": "Frequency of the poll"
}
}
},
"PollResponse": {
"type": "object",
"required": [
Expand Down Expand Up @@ -17879,7 +17903,8 @@
"required": [
"three_ds_authentication_url",
"three_ds_authorize_url",
"three_ds_method_details"
"three_ds_method_details",
"poll_config"
],
"properties": {
"three_ds_authentication_url": {
Expand All @@ -17892,6 +17917,9 @@
},
"three_ds_method_details": {
"$ref": "#/components/schemas/ThreeDsMethodData"
},
"poll_config": {
"$ref": "#/components/schemas/PollConfigResponse"
}
}
},
Expand Down