From c3a1db16f32bd0b5aa49dfc831156a10d6d15838 Mon Sep 17 00:00:00 2001 From: Sai Harsha Vardhan <56996463+sai-harsha-vardhan@users.noreply.github.com> Date: Mon, 29 Apr 2024 11:33:15 +0530 Subject: [PATCH] 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> --- crates/api_models/src/payments.rs | 12 +++++++ crates/openapi/src/openapi.rs | 1 + crates/router/src/core/authentication.rs | 35 ++++++++++++++++--- crates/router/src/core/payments.rs | 19 ++++++---- .../payments/operations/payment_approve.rs | 1 + .../payments/operations/payment_cancel.rs | 1 + .../payments/operations/payment_capture.rs | 1 + .../operations/payment_complete_authorize.rs | 1 + .../payments/operations/payment_confirm.rs | 1 + .../payments/operations/payment_create.rs | 1 + .../payments/operations/payment_reject.rs | 1 + .../payments/operations/payment_session.rs | 1 + .../core/payments/operations/payment_start.rs | 1 + .../payments/operations/payment_status.rs | 1 + .../payments/operations/payment_update.rs | 1 + .../payments_incremental_authorization.rs | 1 + .../router/src/core/payments/transformers.rs | 3 ++ crates/router/src/types.rs | 6 ++++ openapi/openapi_spec.json | 30 +++++++++++++++- 19 files changed, 106 insertions(+), 12 deletions(-) diff --git a/crates/api_models/src/payments.rs b/crates/api_models/src/payments.rs index 1a68b649bb..5d35ba3e49 100644 --- a/crates/api_models/src/payments.rs +++ b/crates/api_models/src/payments.rs @@ -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)] @@ -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)] diff --git a/crates/openapi/src/openapi.rs b/crates/openapi/src/openapi.rs index 6e9ad5848c..bb9ef73199 100644 --- a/crates/openapi/src/openapi.rs +++ b/crates/openapi/src/openapi.rs @@ -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, diff --git a/crates/router/src/core/authentication.rs b/crates/router/src/core/authentication.rs index f1a34dc773..f9161f2ccc 100644 --- a/crates/router/src/core/authentication.rs +++ b/crates/router/src/core/authentication.rs @@ -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}; @@ -208,9 +211,12 @@ pub async fn perform_pre_authentication( &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")? @@ -231,6 +237,27 @@ pub async fn perform_pre_authentication( || 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( + &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); } diff --git a/crates/router/src/core/payments.rs b/crates/router/src/core/payments.rs index 2eb080f952..67f095450c 100644 --- a/crates/router/src/core/payments.rs +++ b/crates/router/src/core/payments.rs @@ -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}; @@ -1278,17 +1282,17 @@ impl PaymentRedirectFlow for PaymentAuthenticat let poll_config = state .store .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), ) .await .change_context(errors::ApiErrorResponse::InternalServerError) .attach_printable("The poll config was not found in the DB")?; - let poll_config = - serde_json::from_str::>(&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() @@ -2497,6 +2501,7 @@ where pub authentication: Option, pub frm_metadata: Option, pub recurring_details: Option, + pub poll_config: Option, } #[derive(Clone, serde::Serialize, Debug)] diff --git a/crates/router/src/core/payments/operations/payment_approve.rs b/crates/router/src/core/payments/operations/payment_approve.rs index be73d0836d..d9744cff77 100644 --- a/crates/router/src/core/payments/operations/payment_approve.rs +++ b/crates/router/src/core/payments/operations/payment_approve.rs @@ -178,6 +178,7 @@ impl frm_metadata: None, authentication: None, recurring_details: None, + poll_config: None, }; let get_trackers_response = operations::GetTrackerResponse { diff --git a/crates/router/src/core/payments/operations/payment_cancel.rs b/crates/router/src/core/payments/operations/payment_cancel.rs index cb1b2fbd67..3e53128083 100644 --- a/crates/router/src/core/payments/operations/payment_cancel.rs +++ b/crates/router/src/core/payments/operations/payment_cancel.rs @@ -188,6 +188,7 @@ impl frm_metadata: None, authentication: None, recurring_details: None, + poll_config: None, }; let get_trackers_response = operations::GetTrackerResponse { diff --git a/crates/router/src/core/payments/operations/payment_capture.rs b/crates/router/src/core/payments/operations/payment_capture.rs index a20358cb95..d4c3f0acad 100644 --- a/crates/router/src/core/payments/operations/payment_capture.rs +++ b/crates/router/src/core/payments/operations/payment_capture.rs @@ -231,6 +231,7 @@ impl frm_metadata: None, authentication: None, recurring_details: None, + poll_config: None, }; let get_trackers_response = operations::GetTrackerResponse { diff --git a/crates/router/src/core/payments/operations/payment_complete_authorize.rs b/crates/router/src/core/payments/operations/payment_complete_authorize.rs index 89bdd0b84c..d4dc52ce27 100644 --- a/crates/router/src/core/payments/operations/payment_complete_authorize.rs +++ b/crates/router/src/core/payments/operations/payment_complete_authorize.rs @@ -307,6 +307,7 @@ impl authentication: None, frm_metadata: None, recurring_details, + poll_config: None, }; let customer_details = Some(CustomerDetails { diff --git a/crates/router/src/core/payments/operations/payment_confirm.rs b/crates/router/src/core/payments/operations/payment_confirm.rs index de0e660409..e73bf33f64 100644 --- a/crates/router/src/core/payments/operations/payment_confirm.rs +++ b/crates/router/src/core/payments/operations/payment_confirm.rs @@ -634,6 +634,7 @@ impl frm_metadata: request.frm_metadata.clone(), authentication, recurring_details, + poll_config: None, }; let get_trackers_response = operations::GetTrackerResponse { diff --git a/crates/router/src/core/payments/operations/payment_create.rs b/crates/router/src/core/payments/operations/payment_create.rs index 5d510f2afd..7560257ab2 100644 --- a/crates/router/src/core/payments/operations/payment_create.rs +++ b/crates/router/src/core/payments/operations/payment_create.rs @@ -447,6 +447,7 @@ impl authentication: None, frm_metadata: request.frm_metadata.clone(), recurring_details, + poll_config: None, }; let get_trackers_response = operations::GetTrackerResponse { diff --git a/crates/router/src/core/payments/operations/payment_reject.rs b/crates/router/src/core/payments/operations/payment_reject.rs index 413a6fb4f0..8845fe836e 100644 --- a/crates/router/src/core/payments/operations/payment_reject.rs +++ b/crates/router/src/core/payments/operations/payment_reject.rs @@ -174,6 +174,7 @@ impl authentication: None, frm_metadata: None, recurring_details: None, + poll_config: None, }; let get_trackers_response = operations::GetTrackerResponse { diff --git a/crates/router/src/core/payments/operations/payment_session.rs b/crates/router/src/core/payments/operations/payment_session.rs index d963c6864b..808eb20eb1 100644 --- a/crates/router/src/core/payments/operations/payment_session.rs +++ b/crates/router/src/core/payments/operations/payment_session.rs @@ -199,6 +199,7 @@ impl authentication: None, frm_metadata: None, recurring_details: None, + poll_config: None, }; let get_trackers_response = operations::GetTrackerResponse { diff --git a/crates/router/src/core/payments/operations/payment_start.rs b/crates/router/src/core/payments/operations/payment_start.rs index 70fb33851d..c7042908b6 100644 --- a/crates/router/src/core/payments/operations/payment_start.rs +++ b/crates/router/src/core/payments/operations/payment_start.rs @@ -186,6 +186,7 @@ impl authentication: None, frm_metadata: None, recurring_details: None, + poll_config: None, }; let get_trackers_response = operations::GetTrackerResponse { diff --git a/crates/router/src/core/payments/operations/payment_status.rs b/crates/router/src/core/payments/operations/payment_status.rs index c079f93caa..4c0359c28f 100644 --- a/crates/router/src/core/payments/operations/payment_status.rs +++ b/crates/router/src/core/payments/operations/payment_status.rs @@ -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 { diff --git a/crates/router/src/core/payments/operations/payment_update.rs b/crates/router/src/core/payments/operations/payment_update.rs index b201453068..ecf1fe998b 100644 --- a/crates/router/src/core/payments/operations/payment_update.rs +++ b/crates/router/src/core/payments/operations/payment_update.rs @@ -453,6 +453,7 @@ impl authentication: None, frm_metadata: request.frm_metadata.clone(), recurring_details, + poll_config: None, }; let get_trackers_response = operations::GetTrackerResponse { diff --git a/crates/router/src/core/payments/operations/payments_incremental_authorization.rs b/crates/router/src/core/payments/operations/payments_incremental_authorization.rs index 7f2311eb88..ddba82fc04 100644 --- a/crates/router/src/core/payments/operations/payments_incremental_authorization.rs +++ b/crates/router/src/core/payments/operations/payments_incremental_authorization.rs @@ -155,6 +155,7 @@ impl authentication: None, frm_metadata: None, recurring_details: None, + poll_config: None, }; let get_trackers_response = operations::GetTrackerResponse { diff --git a/crates/router/src/core/payments/transformers.rs b/crates/router/src/core/payments/transformers.rs index c8de432fc0..eba5768a9c 100644 --- a/crates/router/src/core/payments/transformers.rs +++ b/crates/router/src/core/payments/transformers.rs @@ -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")?; @@ -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}, }, }) }else{ diff --git a/crates/router/src/types.rs b/crates/router/src/types.rs index 951be887ed..b642d2903d 100644 --- a/crates/router/src/types.rs +++ b/crates/router/src/types.rs @@ -1154,6 +1154,12 @@ pub struct PollConfig { 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 { fn default() -> Self { Self { diff --git a/openapi/openapi_spec.json b/openapi/openapi_spec.json index b64756fc43..bb8945c530 100644 --- a/openapi/openapi_spec.json +++ b/openapi/openapi_spec.json @@ -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": { "type": "object", "required": [ @@ -17874,7 +17898,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": { @@ -17887,6 +17912,9 @@ }, "three_ds_method_details": { "$ref": "#/components/schemas/ThreeDsMethodData" + }, + "poll_config": { + "$ref": "#/components/schemas/PollConfigResponse" } } },