feat(router): send poll_config in next_action of confirm response for external 3ds flow (#4443)

Co-authored-by: Hrithikesh <61539176+hrithikesh026@users.noreply.github.com>
This commit is contained in:
Sai Harsha Vardhan
2024-04-29 11:33:15 +05:30
committed by GitHub
parent cbf7cf4fcf
commit c3a1db16f3
19 changed files with 106 additions and 12 deletions

View File

@ -2825,6 +2825,8 @@ pub struct ThreeDsData {
pub three_ds_authorize_url: String, pub three_ds_authorize_url: String,
/// ThreeDS method details /// ThreeDS method details
pub three_ds_method_details: ThreeDsMethodData, pub three_ds_method_details: ThreeDsMethodData,
/// Poll config for a connector
pub poll_config: PollConfigResponse,
} }
#[derive(Clone, Debug, Eq, PartialEq, serde::Serialize, ToSchema)] #[derive(Clone, Debug, Eq, PartialEq, serde::Serialize, ToSchema)]
@ -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)] #[derive(Clone, Debug, serde::Serialize, serde::Deserialize)]
#[serde(rename_all = "snake_case")] #[serde(rename_all = "snake_case")]
#[serde(untagged)] #[serde(untagged)]

View File

@ -405,6 +405,7 @@ Never share your secret api keys. Keep them guarded and secure.
api_models::payments::PaymentCreatePaymentLinkConfig, api_models::payments::PaymentCreatePaymentLinkConfig,
api_models::payments::ThreeDsData, api_models::payments::ThreeDsData,
api_models::payments::ThreeDsMethodData, api_models::payments::ThreeDsMethodData,
api_models::payments::PollConfigResponse,
api_models::payments::ExternalAuthenticationDetailsResponse, api_models::payments::ExternalAuthenticationDetailsResponse,
api_models::payment_methods::RequiredFieldInfo, api_models::payment_methods::RequiredFieldInfo,
api_models::payment_methods::DefaultPaymentMethod, api_models::payment_methods::DefaultPaymentMethod,

View File

@ -5,7 +5,10 @@ pub mod types;
use api_models::payments; use api_models::payments;
use common_enums::Currency; 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 error_stack::{report, ResultExt};
use masking::{ExposeInterface, PeekInterface}; use masking::{ExposeInterface, PeekInterface};
@ -208,8 +211,11 @@ pub async fn perform_pre_authentication<F: Clone + Send>(
&three_ds_connector_account, &three_ds_connector_account,
business_profile.merchant_id.clone(), business_profile.merchant_id.clone(),
)?; )?;
let router_data = let router_data = utils::do_auth_connector_call(
utils::do_auth_connector_call(state, authentication_connector_name, router_data) state,
authentication_connector_name.clone(),
router_data,
)
.await?; .await?;
let acquirer_details: types::AcquirerDetails = payment_connector_account let acquirer_details: types::AcquirerDetails = payment_connector_account
.get_metadata() .get_metadata()
@ -231,6 +237,27 @@ pub async fn perform_pre_authentication<F: Clone + Send>(
|| authentication.authentication_status.is_failed() || authentication.authentication_status.is_failed()
{ {
*should_continue_confirm_transaction = false; *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(
&core_types::PollConfig::get_poll_config_key(authentication_connector_name),
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); payment_data.authentication = Some(authentication);
} }

View File

@ -22,7 +22,11 @@ use api_models::{
mandates::RecurringDetails, mandates::RecurringDetails,
payments::{self as payments_api, HeaderPayload}, 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 data_models::mandates::{CustomerAcceptance, MandateData};
use diesel_models::{ephemeral_key, fraud_check::FraudCheck}; use diesel_models::{ephemeral_key, fraud_check::FraudCheck};
use error_stack::{report, ResultExt}; use error_stack::{report, ResultExt};
@ -1278,17 +1282,17 @@ impl<Ctx: PaymentMethodRetrieve> PaymentRedirectFlow<Ctx> for PaymentAuthenticat
let poll_config = state let poll_config = state
.store .store
.find_config_by_key_unwrap_or( .find_config_by_key_unwrap_or(
&format!("poll_config_external_three_ds_{connector}"), &router_types::PollConfig::get_poll_config_key(connector),
Some(default_config_str), Some(default_config_str),
) )
.await .await
.change_context(errors::ApiErrorResponse::InternalServerError) .change_context(errors::ApiErrorResponse::InternalServerError)
.attach_printable("The poll config was not found in the DB")?; .attach_printable("The poll config was not found in the DB")?;
let poll_config = let poll_config: router_types::PollConfig = poll_config
serde_json::from_str::<Option<router_types::PollConfig>>(&poll_config.config) .config
.parse_struct("PollConfig")
.change_context(errors::ApiErrorResponse::InternalServerError) .change_context(errors::ApiErrorResponse::InternalServerError)
.attach_printable("Error while parsing PollConfig")? .attach_printable("Error while parsing PollConfig")?;
.unwrap_or(default_poll_config);
let profile_id = payments_response let profile_id = payments_response
.profile_id .profile_id
.as_ref() .as_ref()
@ -2497,6 +2501,7 @@ where
pub authentication: Option<storage::Authentication>, pub authentication: Option<storage::Authentication>,
pub frm_metadata: Option<serde_json::Value>, pub frm_metadata: Option<serde_json::Value>,
pub recurring_details: Option<RecurringDetails>, pub recurring_details: Option<RecurringDetails>,
pub poll_config: Option<router_types::PollConfig>,
} }
#[derive(Clone, serde::Serialize, Debug)] #[derive(Clone, serde::Serialize, Debug)]

View File

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

View File

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

View File

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

View File

@ -307,6 +307,7 @@ impl<F: Send + Clone, Ctx: PaymentMethodRetrieve>
authentication: None, authentication: None,
frm_metadata: None, frm_metadata: None,
recurring_details, recurring_details,
poll_config: None,
}; };
let customer_details = Some(CustomerDetails { let customer_details = Some(CustomerDetails {

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -570,6 +570,8 @@ where
Some(authentication) => { Some(authentication) => {
if payment_intent.status == common_enums::IntentStatus::RequiresCustomerAction && authentication.cavv.is_none() && authentication.is_separate_authn_required(){ if payment_intent.status == common_enums::IntentStatus::RequiresCustomerAction && authentication.cavv.is_none() && authentication.is_separate_authn_required(){
// if preAuthn and separate authentication needed. // 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 let payment_connector_name = payment_attempt.connector
.as_ref() .as_ref()
.get_required_value("connector")?; .get_required_value("connector")?;
@ -592,6 +594,7 @@ where
three_ds_method_data: None, three_ds_method_data: None,
three_ds_method_url: 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},
}, },
}) })
}else{ }else{

View File

@ -1154,6 +1154,12 @@ pub struct PollConfig {
pub frequency: i8, pub frequency: i8,
} }
impl PollConfig {
pub fn get_poll_config_key(connector: String) -> String {
format!("poll_config_external_three_ds_{connector}")
}
}
impl Default for PollConfig { impl Default for PollConfig {
fn default() -> Self { fn default() -> Self {
Self { Self {

View File

@ -16311,6 +16311,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": { "PollResponse": {
"type": "object", "type": "object",
"required": [ "required": [
@ -17874,7 +17898,8 @@
"required": [ "required": [
"three_ds_authentication_url", "three_ds_authentication_url",
"three_ds_authorize_url", "three_ds_authorize_url",
"three_ds_method_details" "three_ds_method_details",
"poll_config"
], ],
"properties": { "properties": {
"three_ds_authentication_url": { "three_ds_authentication_url": {
@ -17887,6 +17912,9 @@
}, },
"three_ds_method_details": { "three_ds_method_details": {
"$ref": "#/components/schemas/ThreeDsMethodData" "$ref": "#/components/schemas/ThreeDsMethodData"
},
"poll_config": {
"$ref": "#/components/schemas/PollConfigResponse"
} }
} }
}, },