refactor(payment_methods_v2): refactor network tokenization flow for v2 (#7309)

Co-authored-by: hyperswitch-bot[bot] <148525504+hyperswitch-bot[bot]@users.noreply.github.com>
Co-authored-by: Bernard Eugine <114725419+bernard-eugine@users.noreply.github.com>
This commit is contained in:
Prasunna Soppa
2025-03-13 02:53:35 +05:30
committed by GitHub
parent 3667a7ffd2
commit bba414cd19
16 changed files with 590 additions and 413 deletions

View File

@ -446,6 +446,12 @@ pub enum NetworkTokenizationError {
NetworkTokenizationServiceNotConfigured,
#[error("Failed while calling Network Token Service API")]
ApiError,
#[error("Network Tokenization is not enabled for profile")]
NetworkTokenizationNotEnabledForProfile,
#[error("Network Tokenization is not supported for {message}")]
NotSupported { message: String },
#[error("Failed to encrypt the NetworkToken payment method details")]
NetworkTokenDetailsEncryptionFailed,
}
#[derive(Debug, thiserror::Error)]

View File

@ -933,11 +933,9 @@ pub async fn create_payment_method_core(
.await
.attach_printable("Failed to add Payment method to DB")?;
let payment_method_data =
pm_types::PaymentMethodVaultingData::from(req.payment_method_data.clone());
let payment_method_data =
populate_bin_details_for_payment_method(state, &payment_method_data).await;
let payment_method_data = domain::PaymentMethodVaultingData::from(req.payment_method_data)
.populate_bin_details_for_payment_method(state)
.await;
let vaulting_result = vault_payment_method(
state,
@ -958,15 +956,7 @@ pub async fn create_payment_method_core(
profile.is_network_tokenization_enabled,
&customer_id,
)
.await
.map_err(|e| {
services::logger::error!(
"Failed to network tokenize the payment method for customer: {}. Error: {} ",
customer_id.get_string_repr(),
e
);
})
.ok();
.await;
let (response, payment_method) = match vaulting_result {
Ok((vaulting_resp, fingerprint_id)) => {
@ -1035,86 +1025,83 @@ pub struct NetworkTokenPaymentMethodDetails {
#[cfg(all(feature = "v2", feature = "payment_methods_v2"))]
pub async fn network_tokenize_and_vault_the_pmd(
state: &SessionState,
payment_method_data: &pm_types::PaymentMethodVaultingData,
payment_method_data: &domain::PaymentMethodVaultingData,
merchant_account: &domain::MerchantAccount,
key_store: &domain::MerchantKeyStore,
network_tokenization: Option<common_types::payment_methods::NetworkTokenization>,
network_tokenization_enabled_for_profile: bool,
customer_id: &id_type::GlobalCustomerId,
) -> RouterResult<NetworkTokenPaymentMethodDetails> {
when(!network_tokenization_enabled_for_profile, || {
Err(report!(errors::ApiErrorResponse::NotSupported {
message: "Network Tokenization is not enabled for this payment method".to_string()
}))
})?;
) -> Option<NetworkTokenPaymentMethodDetails> {
let network_token_pm_details_result: errors::CustomResult<
NetworkTokenPaymentMethodDetails,
errors::NetworkTokenizationError,
> = async {
when(!network_tokenization_enabled_for_profile, || {
Err(report!(
errors::NetworkTokenizationError::NetworkTokenizationNotEnabledForProfile
))
})?;
let is_network_tokenization_enabled_for_pm = network_tokenization
.as_ref()
.map(|nt| matches!(nt.enable, common_enums::NetworkTokenizationToggle::Enable))
.unwrap_or(false);
let is_network_tokenization_enabled_for_pm = network_tokenization
.as_ref()
.map(|nt| matches!(nt.enable, common_enums::NetworkTokenizationToggle::Enable))
.unwrap_or(false);
let card_data = match payment_method_data {
pm_types::PaymentMethodVaultingData::Card(data)
if is_network_tokenization_enabled_for_pm =>
{
Ok(data)
}
_ => Err(report!(errors::ApiErrorResponse::NotSupported {
message: "Network Tokenization is not supported for this payment method".to_string()
})),
}?;
let card_data = payment_method_data
.get_card()
.and_then(|card| is_network_tokenization_enabled_for_pm.then_some(card))
.ok_or_else(|| {
report!(errors::NetworkTokenizationError::NotSupported {
message: "Payment method".to_string(),
})
})?;
let (resp, network_token_req_ref_id) =
network_tokenization::make_card_network_tokenization_request(state, card_data, customer_id)
.await
.change_context(errors::ApiErrorResponse::InternalServerError)
.attach_printable("Failed to generate network token")?;
let network_token_vaulting_data = pm_types::PaymentMethodVaultingData::NetworkToken(resp);
let vaulting_resp = vault::add_payment_method_to_vault(
state,
merchant_account,
&network_token_vaulting_data,
None,
)
.await
.change_context(errors::ApiErrorResponse::InternalServerError)
.attach_printable("Failed to vault the network token data")?;
let key_manager_state = &(state).into();
let network_token = match network_token_vaulting_data {
pm_types::PaymentMethodVaultingData::Card(card) => {
payment_method_data::PaymentMethodsData::Card(
payment_method_data::CardDetailsPaymentMethod::from(card.clone()),
let (resp, network_token_req_ref_id) =
network_tokenization::make_card_network_tokenization_request(
state,
card_data,
customer_id,
)
}
pm_types::PaymentMethodVaultingData::NetworkToken(network_token) => {
payment_method_data::PaymentMethodsData::NetworkToken(
payment_method_data::NetworkTokenDetailsPaymentMethod::from(network_token.clone()),
)
}
};
.await?;
let network_token_pmd =
cards::create_encrypted_data(key_manager_state, key_store, network_token)
.await
.change_context(errors::ApiErrorResponse::InternalServerError)
.attach_printable("Unable to encrypt Payment method data")?;
let network_token_vaulting_data = domain::PaymentMethodVaultingData::NetworkToken(resp);
let vaulting_resp = vault::add_payment_method_to_vault(
state,
merchant_account,
&network_token_vaulting_data,
None,
)
.await
.change_context(errors::NetworkTokenizationError::SaveNetworkTokenFailed)
.attach_printable("Failed to vault network token")?;
Ok(NetworkTokenPaymentMethodDetails {
network_token_requestor_reference_id: network_token_req_ref_id,
network_token_locker_id: vaulting_resp.vault_id.get_string_repr().clone(),
network_token_pmd,
})
let key_manager_state = &(state).into();
let network_token_pmd = cards::create_encrypted_data(
key_manager_state,
key_store,
network_token_vaulting_data.get_payment_methods_data(),
)
.await
.change_context(errors::NetworkTokenizationError::NetworkTokenDetailsEncryptionFailed)
.attach_printable("Failed to encrypt PaymentMethodsData")?;
Ok(NetworkTokenPaymentMethodDetails {
network_token_requestor_reference_id: network_token_req_ref_id,
network_token_locker_id: vaulting_resp.vault_id.get_string_repr().clone(),
network_token_pmd,
})
}
.await;
network_token_pm_details_result.ok()
}
#[cfg(all(feature = "v2", feature = "payment_methods_v2"))]
pub async fn populate_bin_details_for_payment_method(
state: &SessionState,
payment_method_data: &pm_types::PaymentMethodVaultingData,
) -> pm_types::PaymentMethodVaultingData {
payment_method_data: &domain::PaymentMethodVaultingData,
) -> domain::PaymentMethodVaultingData {
match payment_method_data {
pm_types::PaymentMethodVaultingData::Card(card) => {
domain::PaymentMethodVaultingData::Card(card) => {
let card_isin = card.card_number.get_card_isin();
if card.card_issuer.is_some()
@ -1122,7 +1109,7 @@ pub async fn populate_bin_details_for_payment_method(
&& card.card_type.is_some()
&& card.card_issuing_country.is_some()
{
pm_types::PaymentMethodVaultingData::Card(card.clone())
domain::PaymentMethodVaultingData::Card(card.clone())
} else {
let card_info = state
.store
@ -1132,7 +1119,7 @@ pub async fn populate_bin_details_for_payment_method(
.ok()
.flatten();
pm_types::PaymentMethodVaultingData::Card(payment_methods::CardDetail {
domain::PaymentMethodVaultingData::Card(payment_methods::CardDetail {
card_number: card.card_number.clone(),
card_exp_month: card.card_exp_month.clone(),
card_exp_year: card.card_exp_year.clone(),
@ -1163,6 +1150,67 @@ pub async fn populate_bin_details_for_payment_method(
_ => payment_method_data.clone(),
}
}
#[cfg(all(feature = "v2", feature = "payment_methods_v2"))]
#[async_trait::async_trait]
pub trait PaymentMethodExt {
async fn populate_bin_details_for_payment_method(&self, state: &SessionState) -> Self;
}
#[cfg(all(feature = "v2", feature = "payment_methods_v2"))]
#[async_trait::async_trait]
impl PaymentMethodExt for domain::PaymentMethodVaultingData {
async fn populate_bin_details_for_payment_method(&self, state: &SessionState) -> Self {
match self {
Self::Card(card) => {
let card_isin = card.card_number.get_card_isin();
if card.card_issuer.is_some()
&& card.card_network.is_some()
&& card.card_type.is_some()
&& card.card_issuing_country.is_some()
{
Self::Card(card.clone())
} else {
let card_info = state
.store
.get_card_info(&card_isin)
.await
.map_err(|error| services::logger::error!(card_info_error=?error))
.ok()
.flatten();
Self::Card(payment_methods::CardDetail {
card_number: card.card_number.clone(),
card_exp_month: card.card_exp_month.clone(),
card_exp_year: card.card_exp_year.clone(),
card_holder_name: card.card_holder_name.clone(),
nick_name: card.nick_name.clone(),
card_issuing_country: card_info.as_ref().and_then(|val| {
val.card_issuing_country
.as_ref()
.map(|c| api_enums::CountryAlpha2::from_str(c))
.transpose()
.ok()
.flatten()
}),
card_network: card_info.as_ref().and_then(|val| val.card_network.clone()),
card_issuer: card_info.as_ref().and_then(|val| val.card_issuer.clone()),
card_type: card_info.as_ref().and_then(|val| {
val.card_type
.as_ref()
.map(|c| payment_methods::CardType::from_str(c))
.transpose()
.ok()
.flatten()
}),
card_cvc: card.card_cvc.clone(),
})
}
}
_ => self.clone(),
}
}
}
#[cfg(all(feature = "v2", feature = "payment_methods_v2"))]
#[instrument(skip_all)]
@ -1557,7 +1605,7 @@ fn create_connector_token_details_update(
#[cfg(all(feature = "v2", feature = "payment_methods_v2"))]
#[allow(clippy::too_many_arguments)]
pub async fn create_pm_additional_data_update(
payment_method_vaulting_data: Option<&pm_types::PaymentMethodVaultingData>,
pmd: Option<&domain::PaymentMethodVaultingData>,
state: &SessionState,
key_store: &domain::MerchantKeyStore,
vault_id: Option<String>,
@ -1568,15 +1616,15 @@ pub async fn create_pm_additional_data_update(
payment_method_type: Option<common_enums::PaymentMethod>,
payment_method_subtype: Option<common_enums::PaymentMethodType>,
) -> RouterResult<storage::PaymentMethodUpdate> {
let encrypted_payment_method_data = payment_method_vaulting_data
let encrypted_payment_method_data = pmd
.map(
|payment_method_vaulting_data| match payment_method_vaulting_data {
pm_types::PaymentMethodVaultingData::Card(card) => {
domain::PaymentMethodVaultingData::Card(card) => {
payment_method_data::PaymentMethodsData::Card(
payment_method_data::CardDetailsPaymentMethod::from(card.clone()),
)
}
pm_types::PaymentMethodVaultingData::NetworkToken(network_token) => {
domain::PaymentMethodVaultingData::NetworkToken(network_token) => {
payment_method_data::PaymentMethodsData::NetworkToken(
payment_method_data::NetworkTokenDetailsPaymentMethod::from(
network_token.clone(),
@ -1625,7 +1673,7 @@ pub async fn create_pm_additional_data_update(
#[instrument(skip_all)]
pub async fn vault_payment_method(
state: &SessionState,
pmd: &pm_types::PaymentMethodVaultingData,
pmd: &domain::PaymentMethodVaultingData,
merchant_account: &domain::MerchantAccount,
key_store: &domain::MerchantKeyStore,
existing_vault_id: Option<domain::VaultId>,
@ -1893,11 +1941,12 @@ pub async fn update_payment_method_core(
},
)?;
let pmd = vault::retrieve_payment_method_from_vault(state, merchant_account, &payment_method)
.await
.change_context(errors::ApiErrorResponse::InternalServerError)
.attach_printable("Failed to retrieve payment method from vault")?
.data;
let pmd: domain::PaymentMethodVaultingData =
vault::retrieve_payment_method_from_vault(state, merchant_account, &payment_method)
.await
.change_context(errors::ApiErrorResponse::InternalServerError)
.attach_printable("Failed to retrieve payment method from vault")?
.data;
let vault_request_data = request.payment_method_data.map(|payment_method_data| {
pm_transforms::generate_pm_vaulting_req_from_update_request(pmd, payment_method_data)

View File

@ -30,7 +30,7 @@ use crate::{
routes::{self, metrics},
services::{self, encryption},
settings,
types::{api, domain},
types::{api, domain, payment_methods as pm_types},
};
pub const NETWORK_TOKEN_SERVICE: &str = "NETWORK_TOKEN";
@ -45,7 +45,7 @@ pub async fn mk_tokenization_req(
customer_id: id_type::CustomerId,
tokenization_service: &settings::NetworkTokenizationService,
) -> CustomResult<
(domain::CardNetworkTokenResponsePayload, Option<String>),
(pm_types::CardNetworkTokenResponsePayload, Option<String>),
errors::NetworkTokenizationError,
> {
let enc_key = tokenization_service.public_key.peek().clone();
@ -62,12 +62,12 @@ pub async fn mk_tokenization_req(
.change_context(errors::NetworkTokenizationError::SaveNetworkTokenFailed)
.attach_printable("Error on jwe encrypt")?;
let order_data = domain::OrderData {
let order_data = pm_types::OrderData {
consent_id: uuid::Uuid::new_v4().to_string(),
customer_id,
};
let api_payload = domain::ApiPayload {
let api_payload = pm_types::ApiPayload {
service: NETWORK_TOKEN_SERVICE.to_string(),
card_data: Secret::new(jwt),
order_data,
@ -103,7 +103,7 @@ pub async fn mk_tokenization_req(
.attach_printable("Error while receiving response")
.and_then(|inner| match inner {
Err(err_res) => {
let parsed_error: domain::NetworkTokenErrorResponse = err_res
let parsed_error: pm_types::NetworkTokenErrorResponse = err_res
.response
.parse_struct("Card Network Tokenization Response")
.change_context(
@ -124,7 +124,7 @@ pub async fn mk_tokenization_req(
logger::error!("Error while deserializing response: {:?}", err);
})?;
let network_response: domain::CardNetworkTokenResponse = res
let network_response: pm_types::CardNetworkTokenResponse = res
.response
.parse_struct("Card Network Tokenization Response")
.change_context(errors::NetworkTokenizationError::ResponseDeserializationFailed)?;
@ -143,7 +143,7 @@ pub async fn mk_tokenization_req(
"Failed to decrypt the tokenization response from the tokenization service",
)?;
let cn_response: domain::CardNetworkTokenResponsePayload =
let cn_response: pm_types::CardNetworkTokenResponsePayload =
serde_json::from_str(&card_network_token_response)
.change_context(errors::NetworkTokenizationError::ResponseDeserializationFailed)?;
Ok((cn_response.clone(), Some(cn_response.card_reference)))
@ -156,7 +156,7 @@ pub async fn generate_network_token(
customer_id: id_type::GlobalCustomerId,
tokenization_service: &settings::NetworkTokenizationService,
) -> CustomResult<
(domain::GenerateNetworkTokenResponsePayload, String),
(pm_types::GenerateNetworkTokenResponsePayload, String),
errors::NetworkTokenizationError,
> {
let enc_key = tokenization_service.public_key.peek().clone();
@ -173,12 +173,12 @@ pub async fn generate_network_token(
.change_context(errors::NetworkTokenizationError::SaveNetworkTokenFailed)
.attach_printable("Error on jwe encrypt")?;
let order_data = domain::OrderData {
let order_data = pm_types::OrderData {
consent_id: uuid::Uuid::new_v4().to_string(),
customer_id,
};
let api_payload = domain::ApiPayload {
let api_payload = pm_types::ApiPayload {
service: NETWORK_TOKEN_SERVICE.to_string(),
card_data: Secret::new(jwt),
order_data,
@ -214,7 +214,7 @@ pub async fn generate_network_token(
.attach_printable("Error while receiving response")
.and_then(|inner| match inner {
Err(err_res) => {
let parsed_error: domain::NetworkTokenErrorResponse = err_res
let parsed_error: pm_types::NetworkTokenErrorResponse = err_res
.response
.parse_struct("Card Network Tokenization Response")
.change_context(
@ -235,7 +235,7 @@ pub async fn generate_network_token(
logger::error!("Error while deserializing response: {:?}", err);
})?;
let network_response: domain::CardNetworkTokenResponse = res
let network_response: pm_types::CardNetworkTokenResponse = res
.response
.parse_struct("Card Network Tokenization Response")
.change_context(errors::NetworkTokenizationError::ResponseDeserializationFailed)?;
@ -255,7 +255,7 @@ pub async fn generate_network_token(
"Failed to decrypt the tokenization response from the tokenization service",
)?;
let cn_response: domain::GenerateNetworkTokenResponsePayload =
let cn_response: pm_types::GenerateNetworkTokenResponsePayload =
serde_json::from_str(&card_network_token_response)
.change_context(errors::NetworkTokenizationError::ResponseDeserializationFailed)?;
Ok((cn_response.clone(), cn_response.card_reference))
@ -271,10 +271,10 @@ pub async fn make_card_network_tokenization_request(
optional_cvc: Option<Secret<String>>,
customer_id: &id_type::CustomerId,
) -> CustomResult<
(domain::CardNetworkTokenResponsePayload, Option<String>),
(pm_types::CardNetworkTokenResponsePayload, Option<String>),
errors::NetworkTokenizationError,
> {
let card_data = domain::CardData {
let card_data = pm_types::CardData {
card_number: card.card_number.clone(),
exp_month: card.card_exp_month.clone(),
exp_year: card.card_exp_year.clone(),
@ -320,7 +320,7 @@ pub async fn make_card_network_tokenization_request(
card: &api_payment_methods::CardDetail,
customer_id: &id_type::GlobalCustomerId,
) -> CustomResult<(NetworkTokenDetails, String), errors::NetworkTokenizationError> {
let card_data = domain::CardData {
let card_data = pm_types::CardData {
card_number: card.card_number.clone(),
exp_month: card.card_exp_month.clone(),
exp_year: card.card_exp_year.clone(),
@ -376,12 +376,12 @@ pub async fn get_network_token(
customer_id: id_type::CustomerId,
network_token_requestor_ref_id: String,
tokenization_service: &settings::NetworkTokenizationService,
) -> CustomResult<domain::TokenResponse, errors::NetworkTokenizationError> {
) -> CustomResult<pm_types::TokenResponse, errors::NetworkTokenizationError> {
let mut request = services::Request::new(
services::Method::Post,
tokenization_service.fetch_token_url.as_str(),
);
let payload = domain::GetCardToken {
let payload = pm_types::GetCardToken {
card_reference: network_token_requestor_ref_id,
customer_id,
};
@ -411,7 +411,7 @@ pub async fn get_network_token(
.attach_printable("Error while receiving response")
.and_then(|inner| match inner {
Err(err_res) => {
let parsed_error: domain::NetworkTokenErrorResponse = err_res
let parsed_error: pm_types::NetworkTokenErrorResponse = err_res
.response
.parse_struct("Card Network Tokenization Response")
.change_context(
@ -429,7 +429,7 @@ pub async fn get_network_token(
Ok(res) => Ok(res),
})?;
let token_response: domain::TokenResponse = res
let token_response: pm_types::TokenResponse = res
.response
.parse_struct("Get Network Token Response")
.change_context(errors::NetworkTokenizationError::ResponseDeserializationFailed)?;
@ -444,12 +444,12 @@ pub async fn get_network_token(
customer_id: &id_type::GlobalCustomerId,
network_token_requestor_ref_id: String,
tokenization_service: &settings::NetworkTokenizationService,
) -> CustomResult<domain::TokenResponse, errors::NetworkTokenizationError> {
) -> CustomResult<pm_types::TokenResponse, errors::NetworkTokenizationError> {
let mut request = services::Request::new(
services::Method::Post,
tokenization_service.fetch_token_url.as_str(),
);
let payload = domain::GetCardToken {
let payload = pm_types::GetCardToken {
card_reference: network_token_requestor_ref_id,
customer_id: customer_id.clone(),
};
@ -479,7 +479,7 @@ pub async fn get_network_token(
.attach_printable("Error while receiving response")
.and_then(|inner| match inner {
Err(err_res) => {
let parsed_error: domain::NetworkTokenErrorResponse = err_res
let parsed_error: pm_types::NetworkTokenErrorResponse = err_res
.response
.parse_struct("Card Network Tokenization Response")
.change_context(
@ -497,7 +497,7 @@ pub async fn get_network_token(
Ok(res) => Ok(res),
})?;
let token_response: domain::TokenResponse = res
let token_response: pm_types::TokenResponse = res
.response
.parse_struct("Get Network Token Response")
.change_context(errors::NetworkTokenizationError::ResponseDeserializationFailed)?;
@ -656,7 +656,7 @@ pub async fn check_token_status_with_tokenization_service(
services::Method::Post,
tokenization_service.check_token_status_url.as_str(),
);
let payload = domain::CheckTokenStatus {
let payload = pm_types::CheckTokenStatus {
card_reference: network_token_requestor_reference_id,
customer_id: customer_id.clone(),
};
@ -683,7 +683,7 @@ pub async fn check_token_status_with_tokenization_service(
.attach_printable("Error while receiving response")
.and_then(|inner| match inner {
Err(err_res) => {
let parsed_error: domain::NetworkTokenErrorResponse = err_res
let parsed_error: pm_types::NetworkTokenErrorResponse = err_res
.response
.parse_struct("Delete Network Tokenization Response")
.change_context(
@ -704,17 +704,17 @@ pub async fn check_token_status_with_tokenization_service(
logger::error!("Error while deserializing response: {:?}", err);
})?;
let check_token_status_response: domain::CheckTokenStatusResponse = res
let check_token_status_response: pm_types::CheckTokenStatusResponse = res
.response
.parse_struct("Delete Network Tokenization Response")
.change_context(errors::NetworkTokenizationError::ResponseDeserializationFailed)?;
match check_token_status_response.payload.token_status {
domain::TokenStatus::Active => Ok((
pm_types::TokenStatus::Active => Ok((
Some(check_token_status_response.payload.token_expiry_month),
Some(check_token_status_response.payload.token_expiry_year),
)),
domain::TokenStatus::Inactive => Ok((None, None)),
pm_types::TokenStatus::Inactive => Ok((None, None)),
}
}
@ -791,7 +791,7 @@ pub async fn delete_network_token_from_tokenization_service(
services::Method::Post,
tokenization_service.delete_token_url.as_str(),
);
let payload = domain::DeleteCardToken {
let payload = pm_types::DeleteCardToken {
card_reference: network_token_requestor_reference_id,
customer_id: customer_id.clone(),
};
@ -820,7 +820,7 @@ pub async fn delete_network_token_from_tokenization_service(
.attach_printable("Error while receiving response")
.and_then(|inner| match inner {
Err(err_res) => {
let parsed_error: domain::NetworkTokenErrorResponse = err_res
let parsed_error: pm_types::NetworkTokenErrorResponse = err_res
.response
.parse_struct("Delete Network Tokenization Response")
.change_context(
@ -841,14 +841,14 @@ pub async fn delete_network_token_from_tokenization_service(
logger::error!("Error while deserializing response: {:?}", err);
})?;
let delete_token_response: domain::DeleteNetworkTokenResponse = res
let delete_token_response: pm_types::DeleteNetworkTokenResponse = res
.response
.parse_struct("Delete Network Tokenization Response")
.change_context(errors::NetworkTokenizationError::ResponseDeserializationFailed)?;
logger::info!("Delete Network Token Response: {:?}", delete_token_response);
if delete_token_response.status == domain::DeleteNetworkTokenStatus::Success {
if delete_token_response.status == pm_types::DeleteNetworkTokenStatus::Success {
Ok(true)
} else {
Err(errors::NetworkTokenizationError::DeleteNetworkTokenFailed)

View File

@ -7,9 +7,7 @@ use common_utils::{
transformers::{ForeignFrom, ForeignTryFrom},
};
use error_stack::{report, ResultExt};
use hyperswitch_domain_models::{
network_tokenization as nt_domain_types, router_request_types as domain_request_types,
};
use hyperswitch_domain_models::router_request_types as domain_request_types;
use masking::{ExposeInterface, Secret};
use router_env::logger;
@ -21,7 +19,7 @@ use crate::{
},
errors::{self, RouterResult},
services,
types::{api, domain},
types::{api, domain, payment_methods as pm_types},
SessionState,
};
@ -137,10 +135,7 @@ pub async fn tokenize_cards(
}
// Data types
type NetworkTokenizationResponse = (
nt_domain_types::CardNetworkTokenResponsePayload,
Option<String>,
);
type NetworkTokenizationResponse = (pm_types::CardNetworkTokenResponsePayload, Option<String>);
pub struct StoreLockerResponse {
pub store_card_resp: pm_transformers::StoreCardRespPayload,
@ -162,7 +157,7 @@ pub struct NetworkTokenizationBuilder<'a, S: State> {
pub card_cvc: Option<Secret<String>>,
/// Network token details
pub network_token: Option<&'a nt_domain_types::CardNetworkTokenResponsePayload>,
pub network_token: Option<&'a pm_types::CardNetworkTokenResponsePayload>,
/// Stored card details
pub stored_card: Option<&'a pm_transformers::StoreCardRespPayload>,

View File

@ -522,14 +522,14 @@ pub fn mk_add_card_response_hs(
#[cfg(all(feature = "v2", feature = "payment_methods_v2"))]
pub fn generate_pm_vaulting_req_from_update_request(
pm_create: pm_types::PaymentMethodVaultingData,
pm_create: domain::PaymentMethodVaultingData,
pm_update: api::PaymentMethodUpdateData,
) -> pm_types::PaymentMethodVaultingData {
) -> domain::PaymentMethodVaultingData {
match (pm_create, pm_update) {
(
pm_types::PaymentMethodVaultingData::Card(card_create),
domain::PaymentMethodVaultingData::Card(card_create),
api::PaymentMethodUpdateData::Card(update_card),
) => pm_types::PaymentMethodVaultingData::Card(api::CardDetail {
) => domain::PaymentMethodVaultingData::Card(api::CardDetail {
card_number: card_create.card_number,
card_exp_month: card_create.card_exp_month,
card_exp_year: card_create.card_exp_year,
@ -571,6 +571,20 @@ pub fn generate_payment_method_response(
.map(transformers::ForeignFrom::foreign_from)
.collect::<Vec<_>>()
});
let network_token_pmd = payment_method
.network_token_payment_method_data
.clone()
.map(|data| data.into_inner())
.and_then(|data| match data {
domain::PaymentMethodsData::NetworkToken(token) => {
Some(api::NetworkTokenDetailsPaymentMethod::from(token))
}
_ => None,
});
let network_token = network_token_pmd.map(|pmd| api::NetworkTokenResponse {
payment_method_data: pmd,
});
let resp = api::PaymentMethodResponse {
merchant_id: payment_method.merchant_id.to_owned(),
@ -583,6 +597,7 @@ pub fn generate_payment_method_response(
last_used_at: Some(payment_method.last_used_at),
payment_method_data: pmd,
connector_tokens,
network_token,
};
Ok(resp)
@ -839,15 +854,6 @@ pub fn get_card_detail(
Ok(card_detail)
}
#[cfg(all(feature = "v2", feature = "payment_methods_v2"))]
impl From<api::PaymentMethodCreateData> for pm_types::PaymentMethodVaultingData {
fn from(item: api::PaymentMethodCreateData) -> Self {
match item {
api::PaymentMethodCreateData::Card(card) => Self::Card(card),
}
}
}
//------------------------------------------------TokenizeService------------------------------------------------
pub fn mk_crud_locker_request(
locker: &settings::Locker,
@ -961,6 +967,21 @@ impl transformers::ForeignTryFrom<domain::PaymentMethod> for api::CustomerPaymen
.map(|billing| billing.into_inner())
.map(From::from);
let network_token_pmd = item
.network_token_payment_method_data
.clone()
.map(|data| data.into_inner())
.and_then(|data| match data {
domain::PaymentMethodsData::NetworkToken(token) => {
Some(api::NetworkTokenDetailsPaymentMethod::from(token))
}
_ => None,
});
let network_token_resp = network_token_pmd.map(|pmd| api::NetworkTokenResponse {
payment_method_data: pmd,
});
// TODO: check how we can get this field
let recurring_enabled = true;
@ -977,6 +998,7 @@ impl transformers::ForeignTryFrom<domain::PaymentMethod> for api::CustomerPaymen
requires_cvv: true,
is_default: false,
billing: payment_method_billing,
network_tokenization: network_token_resp,
})
}
}
@ -1033,7 +1055,7 @@ impl transformers::ForeignFrom<api_models::payment_methods::ConnectorTokenDetail
} = item;
Self {
connector_token: token,
connector_token: token.expose().clone(),
// TODO: check why do we need this field
payment_method_subtype: None,
original_payment_authorized_amount,
@ -1075,7 +1097,7 @@ impl
original_payment_authorized_amount,
original_payment_authorized_currency,
metadata,
token: connector_token,
token: Secret::new(connector_token),
// Token that is derived from payments mandate reference will always be multi use token
token_type: common_enums::TokenizationType::MultiUse,
}

View File

@ -1252,9 +1252,7 @@ pub async fn call_to_vault<V: pm_types::VaultingInterface>(
#[cfg(all(feature = "v2", feature = "payment_methods_v2"))]
#[instrument(skip_all)]
pub async fn get_fingerprint_id_from_vault<
D: pm_types::VaultingDataInterface + serde::Serialize,
>(
pub async fn get_fingerprint_id_from_vault<D: domain::VaultingDataInterface + serde::Serialize>(
state: &routes::SessionState,
data: &D,
key: String,
@ -1286,7 +1284,7 @@ pub async fn get_fingerprint_id_from_vault<
pub async fn add_payment_method_to_vault(
state: &routes::SessionState,
merchant_account: &domain::MerchantAccount,
pmd: &pm_types::PaymentMethodVaultingData,
pmd: &domain::PaymentMethodVaultingData,
existing_vault_id: Option<domain::VaultId>,
) -> CustomResult<pm_types::AddVaultResponse, errors::VaultError> {
let payload = pm_types::AddVaultRequest {

View File

@ -2613,7 +2613,7 @@ impl<F: Clone> PostUpdateTracker<F, PaymentConfirmData<F>, types::SetupMandateRe
original_payment_authorized_amount: Some(net_amount),
original_payment_authorized_currency: Some(currency),
metadata: None,
token,
token: masking::Secret::new(token),
token_type: common_enums::TokenizationType::MultiUse,
};

View File

@ -4,14 +4,15 @@ pub use api_models::payment_methods::{
CardNetworkTokenizeResponse, CardType, CustomerPaymentMethod,
CustomerPaymentMethodsListResponse, DeleteTokenizeByTokenRequest, GetTokenizePayloadRequest,
GetTokenizePayloadResponse, ListCountriesCurrenciesRequest, MigrateCardDetail,
PaymentMethodCollectLinkRenderRequest, PaymentMethodCollectLinkRequest, PaymentMethodCreate,
PaymentMethodCreateData, PaymentMethodDeleteResponse, PaymentMethodId,
PaymentMethodIntentConfirm, PaymentMethodIntentCreate, PaymentMethodListData,
PaymentMethodListRequest, PaymentMethodListResponse, PaymentMethodMigrate,
PaymentMethodMigrateResponse, PaymentMethodResponse, PaymentMethodResponseData,
PaymentMethodUpdate, PaymentMethodUpdateData, PaymentMethodsData, TokenizePayloadEncrypted,
TokenizePayloadRequest, TokenizedCardValue1, TokenizedCardValue2, TokenizedWalletValue1,
TokenizedWalletValue2, TotalPaymentMethodCountResponse,
NetworkTokenDetailsPaymentMethod, NetworkTokenResponse, PaymentMethodCollectLinkRenderRequest,
PaymentMethodCollectLinkRequest, PaymentMethodCreate, PaymentMethodCreateData,
PaymentMethodDeleteResponse, PaymentMethodId, PaymentMethodIntentConfirm,
PaymentMethodIntentCreate, PaymentMethodListData, PaymentMethodListRequest,
PaymentMethodListResponse, PaymentMethodMigrate, PaymentMethodMigrateResponse,
PaymentMethodResponse, PaymentMethodResponseData, PaymentMethodUpdate, PaymentMethodUpdateData,
PaymentMethodsData, TokenizePayloadEncrypted, TokenizePayloadRequest, TokenizedCardValue1,
TokenizedCardValue2, TokenizedWalletValue1, TokenizedWalletValue2,
TotalPaymentMethodCountResponse,
};
#[cfg(all(
any(feature = "v2", feature = "v1"),

View File

@ -40,6 +40,15 @@ pub mod payment_methods {
pub mod consts {
pub use hyperswitch_domain_models::consts::*;
}
pub mod payment_method_data {
pub use hyperswitch_domain_models::payment_method_data::*;
}
#[cfg(all(feature = "v2", feature = "payment_methods_v2"))]
pub mod vault {
pub use hyperswitch_domain_models::vault::*;
}
pub mod payments;
pub mod types;
#[cfg(feature = "olap")]
@ -54,8 +63,10 @@ pub use event::*;
pub use merchant_connector_account::*;
pub use merchant_key_store::*;
pub use network_tokenization::*;
pub use payment_method_data::*;
pub use payment_methods::*;
pub use payments::*;
#[cfg(feature = "olap")]
pub use user::*;
pub use user_key_store::*;
#[cfg(all(feature = "v2", feature = "payment_methods_v2"))]
pub use vault::*;

View File

@ -1,9 +1,20 @@
use std::fmt::Debug;
use api_models::enums as api_enums;
#[cfg(all(
any(feature = "v1", feature = "v2"),
not(feature = "payment_methods_v2")
))]
use cards::CardNumber;
#[cfg(all(feature = "v2", feature = "payment_methods_v2"))]
use cards::{CardNumber, NetworkToken};
#[cfg(all(feature = "v2", feature = "payment_methods_v2"))]
use common_utils::generate_id;
use common_utils::id_type;
#[cfg(all(feature = "v2", feature = "payment_methods_v2"))]
use hyperswitch_domain_models::payment_method_data::NetworkTokenDetails;
#[cfg(all(feature = "v2", feature = "payment_methods_v2"))]
use masking::Secret;
use serde::{Deserialize, Serialize};
#[cfg(all(feature = "v2", feature = "payment_methods_v2"))]
use crate::{
@ -18,11 +29,6 @@ pub trait VaultingInterface {
fn get_vaulting_flow_name() -> &'static str;
}
#[cfg(all(feature = "v2", feature = "payment_methods_v2"))]
pub trait VaultingDataInterface {
fn get_vaulting_data_key(&self) -> String;
}
#[cfg(all(feature = "v2", feature = "payment_methods_v2"))]
#[derive(Debug, serde::Deserialize, serde::Serialize)]
pub struct VaultFingerprintRequest {
@ -39,7 +45,7 @@ pub struct VaultFingerprintResponse {
#[cfg(all(feature = "v2", feature = "payment_methods_v2"))]
#[derive(Debug, serde::Deserialize, serde::Serialize)]
pub struct AddVaultRequest<D> {
pub entity_id: common_utils::id_type::MerchantId,
pub entity_id: id_type::MerchantId,
pub vault_id: domain::VaultId,
pub data: D,
pub ttl: i64,
@ -48,7 +54,7 @@ pub struct AddVaultRequest<D> {
#[cfg(all(feature = "v2", feature = "payment_methods_v2"))]
#[derive(Debug, serde::Deserialize, serde::Serialize)]
pub struct AddVaultResponse {
pub entity_id: common_utils::id_type::MerchantId,
pub entity_id: id_type::MerchantId,
pub vault_id: domain::VaultId,
pub fingerprint_id: Option<String>,
}
@ -113,23 +119,6 @@ impl VaultingInterface for VaultDelete {
}
}
#[cfg(all(feature = "v2", feature = "payment_methods_v2"))]
#[derive(Debug, serde::Deserialize, serde::Serialize, Clone)]
pub enum PaymentMethodVaultingData {
Card(api::CardDetail),
NetworkToken(NetworkTokenDetails),
}
#[cfg(all(feature = "v2", feature = "payment_methods_v2"))]
impl VaultingDataInterface for PaymentMethodVaultingData {
fn get_vaulting_data_key(&self) -> String {
match &self {
Self::Card(card) => card.card_number.to_string(),
Self::NetworkToken(network_token) => network_token.network_token.to_string(),
}
}
}
#[cfg(all(feature = "v2", feature = "payment_methods_v2"))]
pub struct SavedPMLPaymentsInfo {
pub payment_intent: storage::PaymentIntent,
@ -142,26 +131,235 @@ pub struct SavedPMLPaymentsInfo {
#[cfg(all(feature = "v2", feature = "payment_methods_v2"))]
#[derive(Debug, serde::Deserialize, serde::Serialize)]
pub struct VaultRetrieveRequest {
pub entity_id: common_utils::id_type::MerchantId,
pub entity_id: id_type::MerchantId,
pub vault_id: domain::VaultId,
}
#[cfg(all(feature = "v2", feature = "payment_methods_v2"))]
#[derive(Debug, serde::Deserialize, serde::Serialize)]
pub struct VaultRetrieveResponse {
pub data: PaymentMethodVaultingData,
pub data: domain::PaymentMethodVaultingData,
}
#[cfg(all(feature = "v2", feature = "payment_methods_v2"))]
#[derive(Debug, serde::Deserialize, serde::Serialize)]
pub struct VaultDeleteRequest {
pub entity_id: common_utils::id_type::MerchantId,
pub entity_id: id_type::MerchantId,
pub vault_id: domain::VaultId,
}
#[cfg(all(feature = "v2", feature = "payment_methods_v2"))]
#[derive(Debug, serde::Deserialize, serde::Serialize)]
pub struct VaultDeleteResponse {
pub entity_id: common_utils::id_type::MerchantId,
pub entity_id: id_type::MerchantId,
pub vault_id: domain::VaultId,
}
#[cfg(all(
any(feature = "v1", feature = "v2"),
not(feature = "payment_methods_v2")
))]
#[derive(Debug, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct CardData {
pub card_number: CardNumber,
pub exp_month: Secret<String>,
pub exp_year: Secret<String>,
pub card_security_code: Option<Secret<String>>,
}
#[cfg(all(feature = "v2", feature = "payment_methods_v2"))]
#[derive(Debug, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct CardData {
pub card_number: CardNumber,
pub exp_month: Secret<String>,
pub exp_year: Secret<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub card_security_code: Option<Secret<String>>,
}
#[cfg(all(
any(feature = "v1", feature = "v2"),
not(feature = "payment_methods_v2")
))]
#[derive(Debug, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct OrderData {
pub consent_id: String,
pub customer_id: id_type::CustomerId,
}
#[cfg(all(feature = "v2", feature = "payment_methods_v2"))]
#[derive(Debug, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct OrderData {
pub consent_id: String,
pub customer_id: id_type::GlobalCustomerId,
}
#[derive(Debug, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct ApiPayload {
pub service: String,
pub card_data: Secret<String>, //encrypted card data
pub order_data: OrderData,
pub key_id: String,
pub should_send_token: bool,
}
#[derive(Debug, Deserialize, Eq, PartialEq)]
pub struct CardNetworkTokenResponse {
pub payload: Secret<String>, //encrypted payload
}
#[cfg(all(
any(feature = "v1", feature = "v2"),
not(feature = "payment_methods_v2")
))]
#[derive(Debug, Clone, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct CardNetworkTokenResponsePayload {
pub card_brand: api_enums::CardNetwork,
pub card_fingerprint: Option<Secret<String>>,
pub card_reference: String,
pub correlation_id: String,
pub customer_id: String,
pub par: String,
pub token: CardNumber,
pub token_expiry_month: Secret<String>,
pub token_expiry_year: Secret<String>,
pub token_isin: String,
pub token_last_four: String,
pub token_status: String,
}
#[cfg(all(feature = "v2", feature = "payment_methods_v2"))]
#[derive(Debug, Clone, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct GenerateNetworkTokenResponsePayload {
pub card_brand: api_enums::CardNetwork,
pub card_fingerprint: Option<Secret<String>>,
pub card_reference: String,
pub correlation_id: String,
pub customer_id: String,
pub par: String,
pub token: NetworkToken,
pub token_expiry_month: Secret<String>,
pub token_expiry_year: Secret<String>,
pub token_isin: String,
pub token_last_four: String,
pub token_status: String,
}
#[cfg(all(
any(feature = "v1", feature = "v2"),
not(feature = "payment_methods_v2")
))]
#[derive(Debug, Serialize)]
pub struct GetCardToken {
pub card_reference: String,
pub customer_id: id_type::CustomerId,
}
#[cfg(all(feature = "v2", feature = "payment_methods_v2"))]
#[derive(Debug, Serialize)]
pub struct GetCardToken {
pub card_reference: String,
pub customer_id: id_type::GlobalCustomerId,
}
#[derive(Debug, Deserialize)]
pub struct AuthenticationDetails {
pub cryptogram: Secret<String>,
pub token: CardNumber, //network token
}
#[derive(Debug, Serialize, Deserialize)]
pub struct TokenDetails {
pub exp_month: Secret<String>,
pub exp_year: Secret<String>,
}
#[derive(Debug, Deserialize)]
pub struct TokenResponse {
pub authentication_details: AuthenticationDetails,
pub network: api_enums::CardNetwork,
pub token_details: TokenDetails,
}
#[cfg(all(
any(feature = "v1", feature = "v2"),
not(feature = "payment_methods_v2")
))]
#[derive(Debug, Serialize, Deserialize)]
pub struct DeleteCardToken {
pub card_reference: String, //network token requestor ref id
pub customer_id: id_type::CustomerId,
}
#[cfg(all(feature = "v2", feature = "payment_methods_v2"))]
#[derive(Debug, Serialize, Deserialize)]
pub struct DeleteCardToken {
pub card_reference: String, //network token requestor ref id
pub customer_id: id_type::GlobalCustomerId,
}
#[derive(Debug, Deserialize, Eq, PartialEq)]
#[serde(rename_all = "UPPERCASE")]
pub enum DeleteNetworkTokenStatus {
Success,
}
#[derive(Debug, Deserialize, Eq, PartialEq)]
pub struct NetworkTokenErrorInfo {
pub code: String,
pub developer_message: String,
}
#[derive(Debug, Deserialize, Eq, PartialEq)]
pub struct NetworkTokenErrorResponse {
pub error_message: String,
pub error_info: NetworkTokenErrorInfo,
}
#[derive(Debug, Deserialize, Eq, PartialEq)]
pub struct DeleteNetworkTokenResponse {
pub status: DeleteNetworkTokenStatus,
}
#[cfg(all(
any(feature = "v1", feature = "v2"),
not(feature = "payment_methods_v2")
))]
#[derive(Debug, Serialize, Deserialize)]
pub struct CheckTokenStatus {
pub card_reference: String,
pub customer_id: id_type::CustomerId,
}
#[cfg(all(feature = "v2", feature = "payment_methods_v2"))]
#[derive(Debug, Serialize, Deserialize)]
pub struct CheckTokenStatus {
pub card_reference: String,
pub customer_id: id_type::GlobalCustomerId,
}
#[derive(Debug, Deserialize)]
#[serde(rename_all = "UPPERCASE")]
pub enum TokenStatus {
Active,
Inactive,
}
#[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct CheckTokenStatusResponsePayload {
pub token_expiry_month: Secret<String>,
pub token_expiry_year: Secret<String>,
pub token_status: TokenStatus,
}
#[derive(Debug, Deserialize)]
pub struct CheckTokenStatusResponse {
pub payload: CheckTokenStatusResponsePayload,
}