mirror of
https://github.com/juspay/hyperswitch.git
synced 2025-10-27 19:46:48 +08:00
feat(router): add support for googlepay step up flow (#2744)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: Prasunna Soppa <prasunna.soppa@juspay.in> Co-authored-by: Prasunna Soppa <70575890+prasunna09@users.noreply.github.com> Co-authored-by: sai-harsha-vardhan <harsha111hero@gmail.com> Co-authored-by: hyperswitch-bot[bot] <148525504+hyperswitch-bot[bot]@users.noreply.github.com>
This commit is contained in:
@ -1123,6 +1123,7 @@ pub struct GpayTokenizationSpecification {
|
||||
pub struct GpayAllowedMethodsParameters {
|
||||
pub allowed_auth_methods: Vec<String>,
|
||||
pub allowed_card_networks: Vec<String>,
|
||||
pub assurance_details_required: Option<bool>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Default, Debug, Deserialize, Serialize)]
|
||||
@ -1343,6 +1344,7 @@ impl From<GpayAllowedMethodsParameters> for api_models::payments::GpayAllowedMet
|
||||
allowed_card_networks: value.allowed_card_networks,
|
||||
billing_address_required: None,
|
||||
billing_address_parameters: None,
|
||||
assurance_details_required: value.assurance_details_required,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -246,6 +246,20 @@ pub trait RouterDataAuthorize {
|
||||
|
||||
impl RouterDataAuthorize for types::PaymentsAuthorizeRouterData {
|
||||
fn decide_authentication_type(&mut self) {
|
||||
if let hyperswitch_domain_models::payment_method_data::PaymentMethodData::Wallet(
|
||||
hyperswitch_domain_models::payment_method_data::WalletData::GooglePay(google_pay_data),
|
||||
) = &self.request.payment_method_data
|
||||
{
|
||||
if let Some(assurance_details) = google_pay_data.info.assurance_details.as_ref() {
|
||||
// Step up the transaction to 3DS when either assurance_details.card_holder_authenticated or assurance_details.account_verified is false
|
||||
if !assurance_details.card_holder_authenticated
|
||||
|| !assurance_details.account_verified
|
||||
{
|
||||
logger::info!("Googlepay transaction stepped up to 3DS");
|
||||
self.auth_type = diesel_models::enums::AuthenticationType::ThreeDs;
|
||||
}
|
||||
}
|
||||
}
|
||||
if self.auth_type == diesel_models::enums::AuthenticationType::ThreeDs
|
||||
&& !self.request.enrolled_for_3ds
|
||||
{
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
use async_trait::async_trait;
|
||||
use router_env::logger;
|
||||
|
||||
use super::{ConstructFlowSpecificData, Feature};
|
||||
use crate::{
|
||||
@ -50,7 +51,7 @@ impl
|
||||
#[async_trait]
|
||||
impl Feature<api::SetupMandate, types::SetupMandateRequestData> for types::SetupMandateRouterData {
|
||||
async fn decide_flows<'a>(
|
||||
self,
|
||||
mut self,
|
||||
state: &SessionState,
|
||||
connector: &api::ConnectorData,
|
||||
call_connector_action: payments::CallConnectorAction,
|
||||
@ -62,7 +63,21 @@ impl Feature<api::SetupMandate, types::SetupMandateRequestData> for types::Setup
|
||||
types::SetupMandateRequestData,
|
||||
types::PaymentsResponseData,
|
||||
> = connector.connector.get_connector_integration();
|
||||
|
||||
// Change the authentication_type to ThreeDs, for google_pay wallet if card_holder_authenticated or account_verified in assurance_details is false
|
||||
if let hyperswitch_domain_models::payment_method_data::PaymentMethodData::Wallet(
|
||||
hyperswitch_domain_models::payment_method_data::WalletData::GooglePay(google_pay_data),
|
||||
) = &self.request.payment_method_data
|
||||
{
|
||||
if let Some(assurance_details) = google_pay_data.info.assurance_details.as_ref() {
|
||||
// Step up the transaction to 3DS when either assurance_details.card_holder_authenticated or assurance_details.account_verified is false
|
||||
if !assurance_details.card_holder_authenticated
|
||||
|| !assurance_details.account_verified
|
||||
{
|
||||
logger::info!("Googlepay transaction stepped up to 3DS");
|
||||
self.auth_type = diesel_models::enums::AuthenticationType::ThreeDs;
|
||||
}
|
||||
}
|
||||
}
|
||||
let resp = services::execute_connector_processing_step(
|
||||
state,
|
||||
connector_integration,
|
||||
|
||||
@ -762,6 +762,13 @@ async fn payment_response_update_tracker<F: Clone, T: types::Capturable>(
|
||||
});
|
||||
let (capture_update, mut payment_attempt_update) = match router_data.response.clone() {
|
||||
Err(err) => {
|
||||
let auth_update = if Some(router_data.auth_type)
|
||||
!= payment_data.payment_attempt.authentication_type
|
||||
{
|
||||
Some(router_data.auth_type)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
let (capture_update, attempt_update) = match payment_data.multiple_capture_data {
|
||||
Some(multiple_capture_data) => {
|
||||
let capture_update = storage::CaptureUpdate::ErrorUpdate {
|
||||
@ -777,7 +784,15 @@ async fn payment_response_update_tracker<F: Clone, T: types::Capturable>(
|
||||
multiple_capture_data.get_latest_capture().clone(),
|
||||
capture_update,
|
||||
)];
|
||||
(Some((multiple_capture_data, capture_update_list)), None)
|
||||
(
|
||||
Some((multiple_capture_data, capture_update_list)),
|
||||
auth_update.map(|auth_type| {
|
||||
storage::PaymentAttemptUpdate::AuthenticationTypeUpdate {
|
||||
authentication_type: auth_type,
|
||||
updated_by: storage_scheme.to_string(),
|
||||
}
|
||||
}),
|
||||
)
|
||||
}
|
||||
None => {
|
||||
let connector_name = router_data.connector.to_string();
|
||||
@ -835,6 +850,7 @@ async fn payment_response_update_tracker<F: Clone, T: types::Capturable>(
|
||||
unified_message: option_gsm.map(|gsm| gsm.unified_message),
|
||||
connector_transaction_id: err.connector_transaction_id,
|
||||
payment_method_data: additional_payment_method_data,
|
||||
authentication_type: auth_update,
|
||||
}),
|
||||
)
|
||||
}
|
||||
@ -929,6 +945,14 @@ async fn payment_response_update_tracker<F: Clone, T: types::Capturable>(
|
||||
.change_context(errors::ApiErrorResponse::InternalServerError)
|
||||
.attach_printable("Could not parse the connector response")?;
|
||||
|
||||
let auth_update = if Some(router_data.auth_type)
|
||||
!= payment_data.payment_attempt.authentication_type
|
||||
{
|
||||
Some(router_data.auth_type)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
// incase of success, update error code and error message
|
||||
let error_status = if router_data.status == enums::AttemptStatus::Charged {
|
||||
Some(None)
|
||||
@ -965,7 +989,15 @@ async fn payment_response_update_tracker<F: Clone, T: types::Capturable>(
|
||||
multiple_capture_data.get_latest_capture().clone(),
|
||||
capture_update,
|
||||
)];
|
||||
(Some((multiple_capture_data, capture_update_list)), None)
|
||||
(
|
||||
Some((multiple_capture_data, capture_update_list)),
|
||||
auth_update.map(|auth_type| {
|
||||
storage::PaymentAttemptUpdate::AuthenticationTypeUpdate {
|
||||
authentication_type: auth_type,
|
||||
updated_by: storage_scheme.to_string(),
|
||||
}
|
||||
}),
|
||||
)
|
||||
}
|
||||
None => (
|
||||
None,
|
||||
@ -973,7 +1005,7 @@ async fn payment_response_update_tracker<F: Clone, T: types::Capturable>(
|
||||
status: updated_attempt_status,
|
||||
connector: None,
|
||||
connector_transaction_id: connector_transaction_id.clone(),
|
||||
authentication_type: None,
|
||||
authentication_type: auth_update,
|
||||
amount_capturable: router_data
|
||||
.request
|
||||
.get_amount_capturable(&payment_data, updated_attempt_status)
|
||||
|
||||
@ -423,6 +423,13 @@ where
|
||||
}
|
||||
Err(ref error_response) => {
|
||||
let option_gsm = get_gsm(state, &router_data).await?;
|
||||
let auth_update = if Some(router_data.auth_type)
|
||||
!= payment_data.payment_attempt.authentication_type
|
||||
{
|
||||
Some(router_data.auth_type)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
db.update_payment_attempt_with_attempt_id(
|
||||
payment_data.payment_attempt.clone(),
|
||||
@ -438,6 +445,7 @@ where
|
||||
unified_message: option_gsm.map(|gsm| gsm.unified_message),
|
||||
connector_transaction_id: error_response.connector_transaction_id.clone(),
|
||||
payment_method_data: additional_payment_method_data,
|
||||
authentication_type: auth_update,
|
||||
},
|
||||
storage_scheme,
|
||||
)
|
||||
|
||||
@ -134,6 +134,7 @@ impl ProcessTrackerWorkflow<SessionState> for PaymentsSyncWorkflow {
|
||||
unified_message: None,
|
||||
connector_transaction_id: None,
|
||||
payment_method_data: None,
|
||||
authentication_type: None,
|
||||
};
|
||||
|
||||
payment_data.payment_attempt = db
|
||||
|
||||
@ -96,6 +96,7 @@ async fn should_authorize_gpay_payment() {
|
||||
info: domain::GooglePayPaymentMethodInfo {
|
||||
card_network: "VISA".to_string(),
|
||||
card_details: "1234".to_string(),
|
||||
assurance_details: None,
|
||||
},
|
||||
tokenization_data: domain::GpayTokenizationData {
|
||||
token_type: "payu".to_string(),
|
||||
|
||||
@ -69,6 +69,7 @@ async fn should_authorize_gpay_payment() {
|
||||
info: domain::GooglePayPaymentMethodInfo {
|
||||
card_network: "VISA".to_string(),
|
||||
card_details: "1234".to_string(),
|
||||
assurance_details: None,
|
||||
},
|
||||
tokenization_data: domain::GpayTokenizationData {
|
||||
token_type: "worldpay".to_string(),
|
||||
|
||||
Reference in New Issue
Block a user