mirror of
https://github.com/juspay/hyperswitch.git
synced 2025-10-27 19:46:48 +08:00
feat(payments): add tokenization action handling to payment flow for braintree (#9506)
This commit is contained in:
@ -224,7 +224,7 @@ where
|
||||
let mut connector_http_status_code = None;
|
||||
let (payment_data, connector_response_data) = match connector {
|
||||
ConnectorCallType::PreDetermined(connector_data) => {
|
||||
let (mca_type_details, updated_customer, router_data) =
|
||||
let (mca_type_details, updated_customer, router_data, tokenization_action) =
|
||||
call_connector_service_prerequisites(
|
||||
state,
|
||||
req_state.clone(),
|
||||
@ -264,6 +264,7 @@ where
|
||||
mca_type_details,
|
||||
router_data,
|
||||
updated_customer,
|
||||
tokenization_action,
|
||||
)
|
||||
.await?;
|
||||
|
||||
@ -304,7 +305,7 @@ where
|
||||
let mut connectors = connectors.clone().into_iter();
|
||||
let connector_data = get_connector_data(&mut connectors)?;
|
||||
|
||||
let (mca_type_details, updated_customer, router_data) =
|
||||
let (mca_type_details, updated_customer, router_data, tokenization_action) =
|
||||
call_connector_service_prerequisites(
|
||||
state,
|
||||
req_state.clone(),
|
||||
@ -344,6 +345,7 @@ where
|
||||
mca_type_details,
|
||||
router_data,
|
||||
updated_customer,
|
||||
tokenization_action,
|
||||
)
|
||||
.await?;
|
||||
|
||||
@ -4397,6 +4399,7 @@ pub async fn call_connector_service<F, RouterDReq, ApiRequest, D>(
|
||||
merchant_connector_account_type_details: domain::MerchantConnectorAccountTypeDetails,
|
||||
mut router_data: RouterData<F, RouterDReq, router_types::PaymentsResponseData>,
|
||||
updated_customer: Option<storage::CustomerUpdate>,
|
||||
tokenization_action: TokenizationAction,
|
||||
) -> RouterResult<RouterData<F, RouterDReq, router_types::PaymentsResponseData>>
|
||||
where
|
||||
F: Send + Clone + Sync,
|
||||
@ -4426,7 +4429,20 @@ where
|
||||
&mut router_data,
|
||||
&call_connector_action,
|
||||
);
|
||||
|
||||
let payment_method_token_response = router_data
|
||||
.add_payment_method_token(
|
||||
state,
|
||||
&connector,
|
||||
&tokenization_action,
|
||||
should_continue_further,
|
||||
)
|
||||
.await?;
|
||||
let should_continue_further = tokenization::update_router_data_with_payment_method_token_result(
|
||||
payment_method_token_response,
|
||||
&mut router_data,
|
||||
is_retry_payment,
|
||||
should_continue_further,
|
||||
);
|
||||
let should_continue = match router_data
|
||||
.create_order_at_connector(state, &connector, should_continue_further)
|
||||
.await?
|
||||
@ -4527,6 +4543,7 @@ pub async fn call_connector_service_prerequisites<F, RouterDReq, ApiRequest, D>(
|
||||
domain::MerchantConnectorAccountTypeDetails,
|
||||
Option<storage::CustomerUpdate>,
|
||||
RouterData<F, RouterDReq, router_types::PaymentsResponseData>,
|
||||
TokenizationAction,
|
||||
)>
|
||||
where
|
||||
F: Send + Clone + Sync,
|
||||
@ -4582,10 +4599,16 @@ where
|
||||
)
|
||||
.await?;
|
||||
|
||||
let tokenization_action = operation
|
||||
.to_domain()?
|
||||
.get_connector_tokenization_action(state, payment_data)
|
||||
.await?;
|
||||
|
||||
Ok((
|
||||
merchant_connector_account_type_details,
|
||||
updated_customer,
|
||||
router_data,
|
||||
tokenization_action,
|
||||
))
|
||||
}
|
||||
|
||||
@ -4650,25 +4673,29 @@ where
|
||||
.await?,
|
||||
));
|
||||
|
||||
let (merchant_connector_account_type_details, updated_customer, router_data) =
|
||||
call_connector_service_prerequisites(
|
||||
state,
|
||||
req_state,
|
||||
merchant_context,
|
||||
connector,
|
||||
operation,
|
||||
payment_data,
|
||||
customer,
|
||||
call_connector_action,
|
||||
schedule_time,
|
||||
header_payload,
|
||||
frm_suggestion,
|
||||
business_profile,
|
||||
is_retry_payment,
|
||||
should_retry_with_pan,
|
||||
all_keys_required,
|
||||
)
|
||||
.await?;
|
||||
let (
|
||||
merchant_connector_account_type_details,
|
||||
updated_customer,
|
||||
router_data,
|
||||
_tokenization_action,
|
||||
) = call_connector_service_prerequisites(
|
||||
state,
|
||||
req_state,
|
||||
merchant_context,
|
||||
connector,
|
||||
operation,
|
||||
payment_data,
|
||||
customer,
|
||||
call_connector_action,
|
||||
schedule_time,
|
||||
header_payload,
|
||||
frm_suggestion,
|
||||
business_profile,
|
||||
is_retry_payment,
|
||||
should_retry_with_pan,
|
||||
all_keys_required,
|
||||
)
|
||||
.await?;
|
||||
Ok((
|
||||
merchant_connector_account_type_details,
|
||||
external_vault_merchant_connector_account_type_details,
|
||||
@ -4897,6 +4924,7 @@ pub async fn decide_unified_connector_service_call<F, RouterDReq, ApiRequest, D>
|
||||
merchant_connector_account_type_details: domain::MerchantConnectorAccountTypeDetails,
|
||||
mut router_data: RouterData<F, RouterDReq, router_types::PaymentsResponseData>,
|
||||
updated_customer: Option<storage::CustomerUpdate>,
|
||||
tokenization_action: TokenizationAction,
|
||||
) -> RouterResult<RouterData<F, RouterDReq, router_types::PaymentsResponseData>>
|
||||
where
|
||||
F: Send + Clone + Sync,
|
||||
@ -4993,6 +5021,7 @@ where
|
||||
merchant_connector_account_type_details,
|
||||
router_data,
|
||||
updated_customer,
|
||||
tokenization_action,
|
||||
)
|
||||
.await
|
||||
}
|
||||
@ -6938,6 +6967,48 @@ where
|
||||
Ok(merchant_connector_account)
|
||||
}
|
||||
|
||||
#[cfg(feature = "v2")]
|
||||
fn is_payment_method_tokenization_enabled_for_connector(
|
||||
state: &SessionState,
|
||||
connector_name: &str,
|
||||
payment_method: storage::enums::PaymentMethod,
|
||||
payment_method_type: Option<storage::enums::PaymentMethodType>,
|
||||
mandate_flow_enabled: storage_enums::FutureUsage,
|
||||
) -> RouterResult<bool> {
|
||||
let connector_tokenization_filter = state.conf.tokenization.0.get(connector_name);
|
||||
|
||||
Ok(connector_tokenization_filter
|
||||
.map(|connector_filter| {
|
||||
connector_filter
|
||||
.payment_method
|
||||
.clone()
|
||||
.contains(&payment_method)
|
||||
&& is_payment_method_type_allowed_for_connector(
|
||||
payment_method_type,
|
||||
connector_filter.payment_method_type.clone(),
|
||||
)
|
||||
&& is_payment_flow_allowed_for_connector(
|
||||
mandate_flow_enabled,
|
||||
connector_filter.flow.clone(),
|
||||
)
|
||||
})
|
||||
.unwrap_or(false))
|
||||
}
|
||||
// Determines connector tokenization eligibility: if no flow restriction, allow for one-off/CIT with raw cards; if flow = “mandates”, only allow MIT off-session with stored tokens.
|
||||
#[cfg(feature = "v2")]
|
||||
fn is_payment_flow_allowed_for_connector(
|
||||
mandate_flow_enabled: storage_enums::FutureUsage,
|
||||
payment_flow: Option<PaymentFlow>,
|
||||
) -> bool {
|
||||
if payment_flow.is_none() {
|
||||
true
|
||||
} else {
|
||||
matches!(payment_flow, Some(PaymentFlow::Mandates))
|
||||
&& matches!(mandate_flow_enabled, storage_enums::FutureUsage::OffSession)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "v1")]
|
||||
fn is_payment_method_tokenization_enabled_for_connector(
|
||||
state: &SessionState,
|
||||
connector_name: &str,
|
||||
@ -6975,7 +7046,7 @@ fn is_payment_method_tokenization_enabled_for_connector(
|
||||
})
|
||||
.unwrap_or(false))
|
||||
}
|
||||
|
||||
#[cfg(feature = "v1")]
|
||||
fn is_payment_flow_allowed_for_connector(
|
||||
mandate_flow_enabled: Option<storage_enums::FutureUsage>,
|
||||
payment_flow: Option<PaymentFlow>,
|
||||
@ -7198,6 +7269,7 @@ fn is_payment_method_type_allowed_for_connector(
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "v1")]
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
async fn decide_payment_method_tokenize_action(
|
||||
state: &SessionState,
|
||||
@ -7267,7 +7339,7 @@ pub struct GooglePayPaymentProcessingDetails {
|
||||
pub google_pay_root_signing_keys: Secret<String>,
|
||||
pub google_pay_recipient_id: Secret<String>,
|
||||
}
|
||||
|
||||
#[cfg(feature = "v1")]
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum TokenizationAction {
|
||||
TokenizeInRouter,
|
||||
@ -7278,23 +7350,10 @@ pub enum TokenizationAction {
|
||||
}
|
||||
|
||||
#[cfg(feature = "v2")]
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub async fn get_connector_tokenization_action_when_confirm_true<F, Req, D>(
|
||||
_state: &SessionState,
|
||||
_operation: &BoxedOperation<'_, F, Req, D>,
|
||||
payment_data: &mut D,
|
||||
_validate_result: &operations::ValidateResult,
|
||||
_merchant_key_store: &domain::MerchantKeyStore,
|
||||
_customer: &Option<domain::Customer>,
|
||||
_business_profile: &domain::Profile,
|
||||
) -> RouterResult<(D, TokenizationAction)>
|
||||
where
|
||||
F: Send + Clone,
|
||||
D: OperationSessionGetters<F> + OperationSessionSetters<F> + Send + Sync + Clone,
|
||||
{
|
||||
// TODO: Implement this function
|
||||
let payment_data = payment_data.to_owned();
|
||||
Ok((payment_data, TokenizationAction::SkipConnectorTokenization))
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum TokenizationAction {
|
||||
TokenizeInConnector,
|
||||
SkipConnectorTokenization,
|
||||
}
|
||||
|
||||
#[cfg(feature = "v1")]
|
||||
|
||||
@ -94,6 +94,8 @@ pub use self::{
|
||||
payment_session_intent::PaymentSessionIntent,
|
||||
};
|
||||
use super::{helpers, CustomerDetails, OperationSessionGetters, OperationSessionSetters};
|
||||
#[cfg(feature = "v2")]
|
||||
use crate::core::payments;
|
||||
use crate::{
|
||||
core::errors::{self, CustomResult, RouterResult},
|
||||
routes::{app::ReqState, SessionState},
|
||||
@ -426,6 +428,16 @@ pub trait Domain<F: Clone, R, D>: Send + Sync {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Get connector tokenization action
|
||||
#[cfg(feature = "v2")]
|
||||
async fn get_connector_tokenization_action<'a>(
|
||||
&'a self,
|
||||
_state: &SessionState,
|
||||
_payment_data: &D,
|
||||
) -> RouterResult<(payments::TokenizationAction)> {
|
||||
Ok(payments::TokenizationAction::SkipConnectorTokenization)
|
||||
}
|
||||
|
||||
// #[cfg(feature = "v2")]
|
||||
// async fn call_connector<'a, RouterDataReq>(
|
||||
// &'a self,
|
||||
|
||||
@ -538,6 +538,60 @@ impl<F: Clone + Send + Sync> Domain<F, PaymentsConfirmIntentRequest, PaymentConf
|
||||
.set_connector_in_payment_attempt(Some(connector_data.connector_name.to_string()));
|
||||
Ok(connector_data)
|
||||
}
|
||||
|
||||
async fn get_connector_tokenization_action<'a>(
|
||||
&'a self,
|
||||
state: &SessionState,
|
||||
payment_data: &PaymentConfirmData<F>,
|
||||
) -> RouterResult<payments::TokenizationAction> {
|
||||
let connector = payment_data.payment_attempt.connector.to_owned();
|
||||
|
||||
let is_connector_mandate_flow = payment_data
|
||||
.mandate_data
|
||||
.as_ref()
|
||||
.and_then(|mandate_details| mandate_details.mandate_reference_id.as_ref())
|
||||
.map(|mandate_reference| match mandate_reference {
|
||||
api_models::payments::MandateReferenceId::ConnectorMandateId(_) => true,
|
||||
api_models::payments::MandateReferenceId::NetworkMandateId(_)
|
||||
| api_models::payments::MandateReferenceId::NetworkTokenWithNTI(_) => false,
|
||||
})
|
||||
.unwrap_or(false);
|
||||
|
||||
let tokenization_action = match connector {
|
||||
Some(_) if is_connector_mandate_flow => {
|
||||
payments::TokenizationAction::SkipConnectorTokenization
|
||||
}
|
||||
Some(connector) => {
|
||||
let payment_method = payment_data
|
||||
.payment_attempt
|
||||
.get_payment_method()
|
||||
.ok_or_else(|| errors::ApiErrorResponse::InternalServerError)
|
||||
.attach_printable("Payment method not found")?;
|
||||
let payment_method_type: Option<common_enums::PaymentMethodType> =
|
||||
payment_data.payment_attempt.get_payment_method_type();
|
||||
|
||||
let mandate_flow_enabled = payment_data.payment_intent.setup_future_usage;
|
||||
|
||||
let is_connector_tokenization_enabled =
|
||||
payments::is_payment_method_tokenization_enabled_for_connector(
|
||||
state,
|
||||
&connector,
|
||||
payment_method,
|
||||
payment_method_type,
|
||||
mandate_flow_enabled,
|
||||
)?;
|
||||
|
||||
if is_connector_tokenization_enabled {
|
||||
payments::TokenizationAction::TokenizeInConnector
|
||||
} else {
|
||||
payments::TokenizationAction::SkipConnectorTokenization
|
||||
}
|
||||
}
|
||||
None => payments::TokenizationAction::SkipConnectorTokenization,
|
||||
};
|
||||
|
||||
Ok(tokenization_action)
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
|
||||
Reference in New Issue
Block a user