mirror of
https://github.com/juspay/hyperswitch.git
synced 2026-03-13 09:02:06 +08:00
feat(connector): [Cybersource] added 3ds support to setup mandate flow in Cybersource (#11419)
This commit is contained in:
@@ -132,6 +132,8 @@ pub struct CybersourceZeroMandateRequest {
|
||||
payment_information: PaymentInformation,
|
||||
order_information: OrderInformationWithBill,
|
||||
client_reference_information: ClientReferenceInformation,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
consumer_authentication_information: Option<CybersourceConsumerAuthInformation>,
|
||||
}
|
||||
|
||||
impl TryFrom<&SetupMandateRouterData> for CybersourceZeroMandateRequest {
|
||||
@@ -191,7 +193,11 @@ impl TryFrom<&SetupMandateRouterData> for CybersourceZeroMandateRequest {
|
||||
code: Some(item.connector_request_reference_id.clone()),
|
||||
};
|
||||
|
||||
let (payment_information, solution) = match item.request.payment_method_data.clone() {
|
||||
let (payment_information, solution, consumer_authentication_information) = match item
|
||||
.request
|
||||
.payment_method_data
|
||||
.clone()
|
||||
{
|
||||
PaymentMethodData::Card(ccard) => {
|
||||
let card_type = match ccard
|
||||
.card_network
|
||||
@@ -202,6 +208,70 @@ impl TryFrom<&SetupMandateRouterData> for CybersourceZeroMandateRequest {
|
||||
None => ccard.get_card_issuer().ok().map(String::from),
|
||||
};
|
||||
|
||||
// For all card payments, we are explicitly setting `pares_status` to `AuthenticationSuccessful`
|
||||
// to indicate that the Payer Authentication was successful, regardless of actual ACS response.
|
||||
// This is a default behavior and may be adjusted based on future integration requirements.
|
||||
let pares_status = Some(CybersourceParesStatus::AuthenticationSuccessful);
|
||||
|
||||
let consumer_authentication_information =
|
||||
item.request.authentication_data.as_ref().map(|authn_data| {
|
||||
let effective_authentication_type =
|
||||
authn_data.authentication_type.map(Into::into);
|
||||
let (ucaf_authentication_data, cavv, ucaf_collection_indicator) =
|
||||
if ccard.card_network == Some(common_enums::CardNetwork::Mastercard) {
|
||||
(Some(authn_data.cavv.clone()), None, Some("2".to_string()))
|
||||
} else {
|
||||
(None, Some(authn_data.cavv.clone()), None)
|
||||
};
|
||||
let authentication_date = date_time::format_date(
|
||||
authn_data.created_at,
|
||||
date_time::DateFormat::YYYYMMDDHHmmss,
|
||||
)
|
||||
.ok();
|
||||
let network_score = (ccard.card_network
|
||||
== Some(common_enums::CardNetwork::CartesBancaires))
|
||||
.then_some(authn_data.message_extension.as_ref())
|
||||
.flatten()
|
||||
.map(|secret| secret.clone().expose())
|
||||
.and_then(|exposed| {
|
||||
serde_json::from_value::<Vec<MessageExtensionAttribute>>(exposed)
|
||||
.map_err(|err| {
|
||||
router_env::logger::error!(
|
||||
"Failed to deserialize message_extension: {:?}",
|
||||
err
|
||||
);
|
||||
})
|
||||
.ok()
|
||||
.and_then(|exts| extract_score_id(&exts))
|
||||
});
|
||||
|
||||
let cavv_algorithm = Some("2".to_string());
|
||||
|
||||
CybersourceConsumerAuthInformation {
|
||||
pares_status,
|
||||
ucaf_collection_indicator,
|
||||
cavv,
|
||||
ucaf_authentication_data,
|
||||
xid: None,
|
||||
directory_server_transaction_id: authn_data
|
||||
.ds_trans_id
|
||||
.clone()
|
||||
.map(Secret::new),
|
||||
specification_version: authn_data.message_version.clone(),
|
||||
pa_specification_version: authn_data.message_version.clone(),
|
||||
veres_enrolled: Some("Y".to_string()),
|
||||
eci_raw: authn_data.eci.clone(),
|
||||
authentication_date,
|
||||
effective_authentication_type,
|
||||
challenge_code: authn_data.challenge_code.clone(),
|
||||
signed_pares_status_reason: authn_data.challenge_code_reason.clone(),
|
||||
challenge_cancel_code: authn_data.challenge_cancel.clone(),
|
||||
network_score,
|
||||
acs_transaction_id: authn_data.acs_trans_id.clone(),
|
||||
cavv_algorithm,
|
||||
}
|
||||
});
|
||||
|
||||
(
|
||||
PaymentInformation::Cards(Box::new(CardPaymentInformation {
|
||||
card: Card {
|
||||
@@ -214,6 +284,7 @@ impl TryFrom<&SetupMandateRouterData> for CybersourceZeroMandateRequest {
|
||||
},
|
||||
})),
|
||||
None,
|
||||
consumer_authentication_information,
|
||||
)
|
||||
}
|
||||
PaymentMethodData::Wallet(wallet_data) => match wallet_data {
|
||||
@@ -241,6 +312,7 @@ impl TryFrom<&SetupMandateRouterData> for CybersourceZeroMandateRequest {
|
||||
},
|
||||
)),
|
||||
Some(PaymentSolution::ApplePay),
|
||||
None,
|
||||
)
|
||||
}
|
||||
PaymentMethodToken::Token(_) => Err(unimplemented_payment_method!(
|
||||
@@ -275,6 +347,7 @@ impl TryFrom<&SetupMandateRouterData> for CybersourceZeroMandateRequest {
|
||||
},
|
||||
)),
|
||||
Some(PaymentSolution::ApplePay),
|
||||
None,
|
||||
)
|
||||
}
|
||||
},
|
||||
@@ -302,11 +375,13 @@ impl TryFrom<&SetupMandateRouterData> for CybersourceZeroMandateRequest {
|
||||
},
|
||||
)),
|
||||
Some(PaymentSolution::GooglePay),
|
||||
None,
|
||||
),
|
||||
WalletData::SamsungPay(samsung_pay_data) => (
|
||||
(get_samsung_pay_payment_information(&samsung_pay_data)
|
||||
.attach_printable("Failed to get samsung pay payment information")?),
|
||||
Some(PaymentSolution::SamsungPay),
|
||||
None,
|
||||
),
|
||||
WalletData::AliPayQr(_)
|
||||
| WalletData::AliPayRedirect(_)
|
||||
@@ -382,6 +457,7 @@ impl TryFrom<&SetupMandateRouterData> for CybersourceZeroMandateRequest {
|
||||
payment_information,
|
||||
order_information,
|
||||
client_reference_information,
|
||||
consumer_authentication_information,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1705,6 +1705,7 @@ pub struct SetupMandateRequestData {
|
||||
pub tokenization: Option<common_enums::Tokenization>,
|
||||
pub partner_merchant_identifier_details:
|
||||
Option<common_types::payments::PartnerMerchantIdentifierDetails>,
|
||||
pub authentication_data: Option<AuthenticationData>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
|
||||
@@ -4144,27 +4144,54 @@ impl PaymentRedirectFlow for PaymentAuthenticateCompleteAuthorize {
|
||||
}),
|
||||
..Default::default()
|
||||
};
|
||||
Box::pin(payments_core::<
|
||||
api::Authorize,
|
||||
api::PaymentsResponse,
|
||||
_,
|
||||
_,
|
||||
_,
|
||||
_,
|
||||
>(
|
||||
state.clone(),
|
||||
req_state,
|
||||
platform.clone(),
|
||||
None,
|
||||
PaymentConfirm,
|
||||
payment_confirm_req,
|
||||
services::api::AuthFlow::Merchant,
|
||||
connector_action,
|
||||
None,
|
||||
None,
|
||||
HeaderPayload::with_source(enums::PaymentSource::ExternalAuthenticator),
|
||||
))
|
||||
.await?
|
||||
let is_setup_mandate = payment_intent.amount == MinorUnit::zero()
|
||||
&& payment_intent.setup_future_usage
|
||||
== Some(storage_enums::FutureUsage::OffSession);
|
||||
if is_setup_mandate {
|
||||
Box::pin(payments_core::<
|
||||
api::SetupMandate,
|
||||
api::PaymentsResponse,
|
||||
_,
|
||||
_,
|
||||
_,
|
||||
_,
|
||||
>(
|
||||
state.clone(),
|
||||
req_state,
|
||||
platform.clone(),
|
||||
None,
|
||||
PaymentConfirm,
|
||||
payment_confirm_req,
|
||||
services::api::AuthFlow::Merchant,
|
||||
connector_action,
|
||||
None,
|
||||
None,
|
||||
HeaderPayload::with_source(enums::PaymentSource::ExternalAuthenticator),
|
||||
))
|
||||
.await?
|
||||
} else {
|
||||
Box::pin(payments_core::<
|
||||
api::Authorize,
|
||||
api::PaymentsResponse,
|
||||
_,
|
||||
_,
|
||||
_,
|
||||
_,
|
||||
>(
|
||||
state.clone(),
|
||||
req_state,
|
||||
platform.clone(),
|
||||
None,
|
||||
PaymentConfirm,
|
||||
payment_confirm_req,
|
||||
services::api::AuthFlow::Merchant,
|
||||
connector_action,
|
||||
None,
|
||||
None,
|
||||
HeaderPayload::with_source(enums::PaymentSource::ExternalAuthenticator),
|
||||
))
|
||||
.await?
|
||||
}
|
||||
} else {
|
||||
let payment_sync_req = api::PaymentsRetrieveRequest {
|
||||
resource_id: req.resource_id,
|
||||
|
||||
@@ -1577,6 +1577,7 @@ pub async fn construct_payment_router_data_for_setup_mandate<'a>(
|
||||
billing_descriptor: None,
|
||||
split_payments: None,
|
||||
partner_merchant_identifier_details: None,
|
||||
authentication_data: None,
|
||||
};
|
||||
let connector_mandate_request_reference_id = payment_data
|
||||
.payment_attempt
|
||||
@@ -6152,6 +6153,16 @@ impl<F: Clone> TryFrom<PaymentAdditionalData<'_, F>> for types::SetupMandateRequ
|
||||
partner_merchant_identifier_details: payment_data
|
||||
.payment_intent
|
||||
.partner_merchant_identifier_details,
|
||||
authentication_data: payment_data
|
||||
.authentication
|
||||
.as_ref()
|
||||
.map(AuthenticationData::foreign_try_from)
|
||||
.transpose()?
|
||||
.or(payment_data
|
||||
.external_authentication_data
|
||||
.as_ref()
|
||||
.map(AuthenticationData::foreign_try_from)
|
||||
.transpose()?),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user