mirror of
https://github.com/juspay/hyperswitch.git
synced 2025-10-30 17:47:54 +08:00
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:
committed by
GitHub
parent
cbf7cf4fcf
commit
c3a1db16f3
@ -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)]
|
||||||
|
|||||||
@ -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,
|
||||||
|
|||||||
@ -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,9 +211,12 @@ 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,
|
||||||
.await?;
|
authentication_connector_name.clone(),
|
||||||
|
router_data,
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
let acquirer_details: types::AcquirerDetails = payment_connector_account
|
let acquirer_details: types::AcquirerDetails = payment_connector_account
|
||||||
.get_metadata()
|
.get_metadata()
|
||||||
.get_required_value("merchant_connector_account.metadata")?
|
.get_required_value("merchant_connector_account.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);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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
|
||||||
.change_context(errors::ApiErrorResponse::InternalServerError)
|
.parse_struct("PollConfig")
|
||||||
.attach_printable("Error while parsing PollConfig")?
|
.change_context(errors::ApiErrorResponse::InternalServerError)
|
||||||
.unwrap_or(default_poll_config);
|
.attach_printable("Error while parsing PollConfig")?;
|
||||||
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)]
|
||||||
|
|||||||
@ -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 {
|
||||||
|
|||||||
@ -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 {
|
||||||
|
|||||||
@ -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 {
|
||||||
|
|||||||
@ -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 {
|
||||||
|
|||||||
@ -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 {
|
||||||
|
|||||||
@ -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 {
|
||||||
|
|||||||
@ -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 {
|
||||||
|
|||||||
@ -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 {
|
||||||
|
|||||||
@ -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 {
|
||||||
|
|||||||
@ -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 {
|
||||||
|
|||||||
@ -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 {
|
||||||
|
|||||||
@ -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 {
|
||||||
|
|||||||
@ -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{
|
||||||
|
|||||||
@ -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 {
|
||||||
|
|||||||
@ -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"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
Reference in New Issue
Block a user