mirror of
https://github.com/juspay/hyperswitch.git
synced 2025-11-01 19:42:27 +08:00
feat(router): Add support for network token migration (#6300)
Co-authored-by: hyperswitch-bot[bot] <148525504+hyperswitch-bot[bot]@users.noreply.github.com>
This commit is contained in:
@ -57,9 +57,12 @@ use masking::Secret;
|
||||
use router_env::{instrument, metrics::add_attributes, tracing};
|
||||
use strum::IntoEnumIterator;
|
||||
|
||||
use super::surcharge_decision_configs::{
|
||||
perform_surcharge_decision_management_for_payment_method_list,
|
||||
perform_surcharge_decision_management_for_saved_cards,
|
||||
use super::{
|
||||
migration::RecordMigrationStatusBuilder,
|
||||
surcharge_decision_configs::{
|
||||
perform_surcharge_decision_management_for_payment_method_list,
|
||||
perform_surcharge_decision_management_for_saved_cards,
|
||||
},
|
||||
};
|
||||
#[cfg(all(
|
||||
any(feature = "v2", feature = "v1"),
|
||||
@ -376,7 +379,7 @@ pub async fn migrate_payment_method(
|
||||
merchant_id: &id_type::MerchantId,
|
||||
merchant_account: &domain::MerchantAccount,
|
||||
key_store: &domain::MerchantKeyStore,
|
||||
) -> errors::RouterResponse<api::PaymentMethodResponse> {
|
||||
) -> errors::RouterResponse<api::PaymentMethodMigrateResponse> {
|
||||
let mut req = req;
|
||||
let card_details = req.card.as_ref().get_required_value("card")?;
|
||||
|
||||
@ -405,34 +408,92 @@ pub async fn migrate_payment_method(
|
||||
.await?;
|
||||
};
|
||||
|
||||
match card_number_validation_result {
|
||||
let should_require_connector_mandate_details = req.network_token.is_none();
|
||||
|
||||
let mut migration_status = RecordMigrationStatusBuilder::new();
|
||||
|
||||
let resp = match card_number_validation_result {
|
||||
Ok(card_number) => {
|
||||
let payment_method_create_request =
|
||||
api::PaymentMethodCreate::get_payment_method_create_from_payment_method_migrate(
|
||||
card_number,
|
||||
&req,
|
||||
);
|
||||
|
||||
logger::debug!("Storing the card in locker and migrating the payment method");
|
||||
get_client_secret_or_add_payment_method_for_migration(
|
||||
&state,
|
||||
payment_method_create_request,
|
||||
merchant_account,
|
||||
key_store,
|
||||
&mut migration_status,
|
||||
)
|
||||
.await
|
||||
.await?
|
||||
}
|
||||
Err(card_validation_error) => {
|
||||
logger::debug!("Card number to be migrated is invalid, skip saving in locker {card_validation_error}");
|
||||
skip_locker_call_and_migrate_payment_method(
|
||||
state,
|
||||
&state,
|
||||
&req,
|
||||
merchant_id.to_owned(),
|
||||
key_store,
|
||||
merchant_account,
|
||||
card_bin_details.clone(),
|
||||
should_require_connector_mandate_details,
|
||||
&mut migration_status,
|
||||
)
|
||||
.await
|
||||
.await?
|
||||
}
|
||||
}
|
||||
};
|
||||
let payment_method_response = match resp {
|
||||
services::ApplicationResponse::Json(response) => response,
|
||||
_ => Err(errors::ApiErrorResponse::InternalServerError)
|
||||
.attach_printable("Failed to fetch the payment method response")?,
|
||||
};
|
||||
|
||||
let pm_id = payment_method_response.payment_method_id.clone();
|
||||
|
||||
let network_token = req.network_token.clone();
|
||||
|
||||
let network_token_migrated = match network_token {
|
||||
Some(nt_detail) => {
|
||||
logger::debug!("Network token migration");
|
||||
let network_token_requestor_ref_id = nt_detail.network_token_requestor_ref_id.clone();
|
||||
let network_token_data = &nt_detail.network_token_data;
|
||||
|
||||
Some(
|
||||
save_network_token_and_update_payment_method(
|
||||
&state,
|
||||
&req,
|
||||
key_store,
|
||||
merchant_account,
|
||||
network_token_data,
|
||||
network_token_requestor_ref_id,
|
||||
pm_id,
|
||||
)
|
||||
.await
|
||||
.map_err(|err| logger::error!(?err, "Failed to save network token"))
|
||||
.ok()
|
||||
.unwrap_or_default(),
|
||||
)
|
||||
}
|
||||
None => {
|
||||
logger::debug!("Network token data is not available");
|
||||
None
|
||||
}
|
||||
};
|
||||
migration_status.network_token_migrated(network_token_migrated);
|
||||
let migrate_status = migration_status.build();
|
||||
|
||||
Ok(services::ApplicationResponse::Json(
|
||||
api::PaymentMethodMigrateResponse {
|
||||
payment_method_response,
|
||||
card_migrated: migrate_status.card_migrated,
|
||||
network_token_migrated: migrate_status.network_token_migrated,
|
||||
connector_mandate_details_migrated: migrate_status.connector_mandate_details_migrated,
|
||||
network_transaction_id_migrated: migrate_status.network_transaction_migrated,
|
||||
},
|
||||
))
|
||||
}
|
||||
|
||||
#[cfg(all(feature = "v2", feature = "payment_methods_v2"))]
|
||||
@ -442,7 +503,7 @@ pub async fn migrate_payment_method(
|
||||
_merchant_id: &id_type::MerchantId,
|
||||
_merchant_account: &domain::MerchantAccount,
|
||||
_key_store: &domain::MerchantKeyStore,
|
||||
) -> errors::RouterResponse<api::PaymentMethodResponse> {
|
||||
) -> errors::RouterResponse<api::PaymentMethodMigrateResponse> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
@ -645,31 +706,49 @@ impl
|
||||
not(feature = "payment_methods_v2"),
|
||||
not(feature = "customer_v2")
|
||||
))]
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub async fn skip_locker_call_and_migrate_payment_method(
|
||||
state: routes::SessionState,
|
||||
state: &routes::SessionState,
|
||||
req: &api::PaymentMethodMigrate,
|
||||
merchant_id: id_type::MerchantId,
|
||||
key_store: &domain::MerchantKeyStore,
|
||||
merchant_account: &domain::MerchantAccount,
|
||||
card: api_models::payment_methods::CardDetailFromLocker,
|
||||
should_require_connector_mandate_details: bool,
|
||||
migration_status: &mut RecordMigrationStatusBuilder,
|
||||
) -> errors::RouterResponse<api::PaymentMethodResponse> {
|
||||
let db = &*state.store;
|
||||
let customer_id = req.customer_id.clone().get_required_value("customer_id")?;
|
||||
|
||||
// In this case, since we do not have valid card details, recurring payments can only be done through connector mandate details.
|
||||
let connector_mandate_details_req = req
|
||||
.connector_mandate_details
|
||||
.clone()
|
||||
.get_required_value("connector mandate details")?;
|
||||
//if network token data is present, then connector mandate details are not mandatory
|
||||
|
||||
let connector_mandate_details = serde_json::to_value(&connector_mandate_details_req)
|
||||
.change_context(errors::ApiErrorResponse::InternalServerError)
|
||||
.attach_printable("Failed to parse connector mandate details")?;
|
||||
let key_manager_state = (&state).into();
|
||||
let connector_mandate_details = if should_require_connector_mandate_details {
|
||||
let connector_mandate_details_req = req
|
||||
.connector_mandate_details
|
||||
.clone()
|
||||
.get_required_value("connector mandate details")?;
|
||||
|
||||
Some(
|
||||
serde_json::to_value(&connector_mandate_details_req)
|
||||
.change_context(errors::ApiErrorResponse::InternalServerError)
|
||||
.attach_printable("Failed to parse connector mandate details")?,
|
||||
)
|
||||
} else {
|
||||
req.connector_mandate_details
|
||||
.clone()
|
||||
.map(|connector_mandate_details_req| {
|
||||
serde_json::to_value(&connector_mandate_details_req)
|
||||
.change_context(errors::ApiErrorResponse::InternalServerError)
|
||||
.attach_printable("Failed to parse connector mandate details")
|
||||
})
|
||||
.transpose()?
|
||||
};
|
||||
let key_manager_state = &state.into();
|
||||
let payment_method_billing_address: Option<Encryptable<Secret<serde_json::Value>>> = req
|
||||
.billing
|
||||
.clone()
|
||||
.async_map(|billing| create_encrypted_data(&key_manager_state, key_store, billing))
|
||||
.async_map(|billing| create_encrypted_data(key_manager_state, key_store, billing))
|
||||
.await
|
||||
.transpose()
|
||||
.change_context(errors::ApiErrorResponse::InternalServerError)
|
||||
@ -677,7 +756,7 @@ pub async fn skip_locker_call_and_migrate_payment_method(
|
||||
|
||||
let customer = db
|
||||
.find_customer_by_customer_id_merchant_id(
|
||||
&(&state).into(),
|
||||
&state.into(),
|
||||
&customer_id,
|
||||
&merchant_id,
|
||||
key_store,
|
||||
@ -690,7 +769,7 @@ pub async fn skip_locker_call_and_migrate_payment_method(
|
||||
PaymentMethodsData::Card(CardDetailsPaymentMethod::from(card.clone()));
|
||||
|
||||
let payment_method_data_encrypted: Option<Encryptable<Secret<serde_json::Value>>> = Some(
|
||||
create_encrypted_data(&key_manager_state, key_store, payment_method_card_details)
|
||||
create_encrypted_data(key_manager_state, key_store, payment_method_card_details)
|
||||
.await
|
||||
.change_context(errors::ApiErrorResponse::InternalServerError)
|
||||
.attach_printable("Unable to encrypt Payment method card details")?,
|
||||
@ -699,13 +778,26 @@ pub async fn skip_locker_call_and_migrate_payment_method(
|
||||
let payment_method_metadata: Option<serde_json::Value> =
|
||||
req.metadata.as_ref().map(|data| data.peek()).cloned();
|
||||
|
||||
let network_transaction_id = req.network_transaction_id.clone();
|
||||
|
||||
migration_status.network_transaction_id_migrated(network_transaction_id.as_ref().map(|_| true));
|
||||
|
||||
migration_status.connector_mandate_details_migrated(
|
||||
connector_mandate_details
|
||||
.as_ref()
|
||||
.map(|_| true)
|
||||
.or_else(|| req.connector_mandate_details.as_ref().map(|_| false)),
|
||||
);
|
||||
|
||||
migration_status.card_migrated(false);
|
||||
|
||||
let payment_method_id = generate_id(consts::ID_LENGTH, "pm");
|
||||
|
||||
let current_time = common_utils::date_time::now();
|
||||
|
||||
let response = db
|
||||
.insert_payment_method(
|
||||
&(&state).into(),
|
||||
&state.into(),
|
||||
key_store,
|
||||
domain::PaymentMethod {
|
||||
customer_id: customer_id.to_owned(),
|
||||
@ -718,11 +810,11 @@ pub async fn skip_locker_call_and_migrate_payment_method(
|
||||
scheme: req.card_network.clone().or(card.scheme.clone()),
|
||||
metadata: payment_method_metadata.map(Secret::new),
|
||||
payment_method_data: payment_method_data_encrypted.map(Into::into),
|
||||
connector_mandate_details: Some(connector_mandate_details),
|
||||
connector_mandate_details,
|
||||
customer_acceptance: None,
|
||||
client_secret: None,
|
||||
status: enums::PaymentMethodStatus::Active,
|
||||
network_transaction_id: None,
|
||||
network_transaction_id,
|
||||
payment_method_issuer_code: None,
|
||||
accepted_currency: None,
|
||||
token: None,
|
||||
@ -753,7 +845,7 @@ pub async fn skip_locker_call_and_migrate_payment_method(
|
||||
|
||||
if customer.default_payment_method_id.is_none() && req.payment_method.is_some() {
|
||||
let _ = set_default_payment_method(
|
||||
&state,
|
||||
state,
|
||||
&merchant_id,
|
||||
key_store.clone(),
|
||||
&customer_id,
|
||||
@ -768,6 +860,117 @@ pub async fn skip_locker_call_and_migrate_payment_method(
|
||||
))
|
||||
}
|
||||
|
||||
#[cfg(all(
|
||||
any(feature = "v1", feature = "v2"),
|
||||
not(feature = "payment_methods_v2"),
|
||||
not(feature = "customer_v2")
|
||||
))]
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub async fn save_network_token_and_update_payment_method(
|
||||
state: &routes::SessionState,
|
||||
req: &api::PaymentMethodMigrate,
|
||||
key_store: &domain::MerchantKeyStore,
|
||||
merchant_account: &domain::MerchantAccount,
|
||||
network_token_data: &api_models::payment_methods::MigrateNetworkTokenData,
|
||||
network_token_requestor_ref_id: String,
|
||||
pm_id: String,
|
||||
) -> errors::RouterResult<bool> {
|
||||
let payment_method_create_request =
|
||||
api::PaymentMethodCreate::get_payment_method_create_from_payment_method_migrate(
|
||||
network_token_data.network_token_number.clone(),
|
||||
req,
|
||||
);
|
||||
let customer_id = req.customer_id.clone().get_required_value("customer_id")?;
|
||||
|
||||
let network_token_details = api::CardDetail {
|
||||
card_number: network_token_data.network_token_number.clone(),
|
||||
card_exp_month: network_token_data.network_token_exp_month.clone(),
|
||||
card_exp_year: network_token_data.network_token_exp_year.clone(),
|
||||
card_holder_name: network_token_data.card_holder_name.clone(),
|
||||
nick_name: network_token_data.nick_name.clone(),
|
||||
card_issuing_country: network_token_data.card_issuing_country.clone(),
|
||||
card_network: network_token_data.card_network.clone(),
|
||||
card_issuer: network_token_data.card_issuer.clone(),
|
||||
card_type: network_token_data.card_type.clone(),
|
||||
};
|
||||
|
||||
logger::debug!(
|
||||
"Adding network token to locker for customer_id: {:?}",
|
||||
customer_id
|
||||
);
|
||||
|
||||
let token_resp = Box::pin(add_card_to_locker(
|
||||
state,
|
||||
payment_method_create_request.clone(),
|
||||
&network_token_details,
|
||||
&customer_id,
|
||||
merchant_account,
|
||||
None,
|
||||
))
|
||||
.await
|
||||
.change_context(errors::ApiErrorResponse::InternalServerError)
|
||||
.attach_printable("Add Network Token failed");
|
||||
let key_manager_state = &state.into();
|
||||
|
||||
match token_resp {
|
||||
Ok(resp) => {
|
||||
logger::debug!("Network token added to locker");
|
||||
let (token_pm_resp, _duplication_check) = resp;
|
||||
let pm_token_details = token_pm_resp
|
||||
.card
|
||||
.as_ref()
|
||||
.map(|card| PaymentMethodsData::Card(CardDetailsPaymentMethod::from(card.clone())));
|
||||
let pm_network_token_data_encrypted = pm_token_details
|
||||
.async_map(|pm_card| create_encrypted_data(key_manager_state, key_store, pm_card))
|
||||
.await
|
||||
.transpose()
|
||||
.change_context(errors::ApiErrorResponse::InternalServerError)
|
||||
.attach_printable("Unable to encrypt payment method data")?;
|
||||
|
||||
let pm_update = storage::PaymentMethodUpdate::NetworkTokenDataUpdate {
|
||||
network_token_requestor_reference_id: Some(network_token_requestor_ref_id),
|
||||
network_token_locker_id: Some(token_pm_resp.payment_method_id),
|
||||
network_token_payment_method_data: pm_network_token_data_encrypted.map(Into::into),
|
||||
};
|
||||
let db = &*state.store;
|
||||
let existing_pm = db
|
||||
.find_payment_method(
|
||||
&state.into(),
|
||||
key_store,
|
||||
&pm_id,
|
||||
merchant_account.storage_scheme,
|
||||
)
|
||||
.await
|
||||
.change_context(errors::ApiErrorResponse::InternalServerError)
|
||||
.attach_printable(format!(
|
||||
"Failed to fetch payment method for existing pm_id: {:?} in db",
|
||||
pm_id
|
||||
))?;
|
||||
|
||||
db.update_payment_method(
|
||||
&state.into(),
|
||||
key_store,
|
||||
existing_pm,
|
||||
pm_update,
|
||||
merchant_account.storage_scheme,
|
||||
)
|
||||
.await
|
||||
.change_context(errors::ApiErrorResponse::InternalServerError)
|
||||
.attach_printable(format!(
|
||||
"Failed to update payment method for existing pm_id: {:?} in db",
|
||||
pm_id
|
||||
))?;
|
||||
|
||||
logger::debug!("Network token added to locker and payment method updated");
|
||||
Ok(true)
|
||||
}
|
||||
Err(err) => {
|
||||
logger::debug!("Network token added to locker failed {:?}", err);
|
||||
Ok(false)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// need to discuss regarding the migration APIs for v2
|
||||
#[cfg(all(
|
||||
feature = "v2",
|
||||
@ -899,6 +1102,7 @@ pub async fn get_client_secret_or_add_payment_method_for_migration(
|
||||
req: api::PaymentMethodCreate,
|
||||
merchant_account: &domain::MerchantAccount,
|
||||
key_store: &domain::MerchantKeyStore,
|
||||
migration_status: &mut RecordMigrationStatusBuilder,
|
||||
) -> errors::RouterResponse<api::PaymentMethodResponse> {
|
||||
let merchant_id = merchant_account.get_id();
|
||||
let customer_id = req.customer_id.clone().get_required_value("customer_id")?;
|
||||
@ -908,6 +1112,7 @@ pub async fn get_client_secret_or_add_payment_method_for_migration(
|
||||
#[cfg(feature = "payouts")]
|
||||
let condition = req.card.is_some() || req.bank_transfer.is_some() || req.wallet.is_some();
|
||||
let key_manager_state = state.into();
|
||||
|
||||
let payment_method_billing_address: Option<Encryptable<Secret<serde_json::Value>>> = req
|
||||
.billing
|
||||
.clone()
|
||||
@ -930,6 +1135,7 @@ pub async fn get_client_secret_or_add_payment_method_for_migration(
|
||||
req,
|
||||
merchant_account,
|
||||
key_store,
|
||||
migration_status,
|
||||
))
|
||||
.await
|
||||
} else {
|
||||
@ -946,7 +1152,7 @@ pub async fn get_client_secret_or_add_payment_method_for_migration(
|
||||
None,
|
||||
None,
|
||||
key_store,
|
||||
connector_mandate_details,
|
||||
connector_mandate_details.clone(),
|
||||
Some(enums::PaymentMethodStatus::AwaitingData),
|
||||
None,
|
||||
merchant_account.storage_scheme,
|
||||
@ -957,6 +1163,14 @@ pub async fn get_client_secret_or_add_payment_method_for_migration(
|
||||
None,
|
||||
)
|
||||
.await?;
|
||||
migration_status.connector_mandate_details_migrated(
|
||||
connector_mandate_details
|
||||
.clone()
|
||||
.map(|_| true)
|
||||
.or_else(|| req.connector_mandate_details.clone().map(|_| false)),
|
||||
);
|
||||
|
||||
migration_status.card_migrated(false);
|
||||
|
||||
if res.status == enums::PaymentMethodStatus::AwaitingData {
|
||||
add_payment_method_status_update_task(
|
||||
@ -1482,6 +1696,7 @@ pub async fn save_migration_payment_method(
|
||||
req: api::PaymentMethodCreate,
|
||||
merchant_account: &domain::MerchantAccount,
|
||||
key_store: &domain::MerchantKeyStore,
|
||||
migration_status: &mut RecordMigrationStatusBuilder,
|
||||
) -> errors::RouterResponse<api::PaymentMethodResponse> {
|
||||
req.validate()?;
|
||||
let db = &*state.store;
|
||||
@ -1505,6 +1720,17 @@ pub async fn save_migration_payment_method(
|
||||
.transpose()
|
||||
.change_context(errors::ApiErrorResponse::InternalServerError)?;
|
||||
|
||||
let network_transaction_id = req.network_transaction_id.clone();
|
||||
|
||||
migration_status.network_transaction_id_migrated(network_transaction_id.as_ref().map(|_| true));
|
||||
|
||||
migration_status.connector_mandate_details_migrated(
|
||||
connector_mandate_details
|
||||
.as_ref()
|
||||
.map(|_| true)
|
||||
.or_else(|| req.connector_mandate_details.as_ref().map(|_| false)),
|
||||
);
|
||||
|
||||
let response = match payment_method {
|
||||
#[cfg(feature = "payouts")]
|
||||
api_enums::PaymentMethod::BankTransfer => match req.bank_transfer.clone() {
|
||||
@ -1564,6 +1790,7 @@ pub async fn save_migration_payment_method(
|
||||
|
||||
let (mut resp, duplication_check) = response?;
|
||||
|
||||
migration_status.card_migrated(true);
|
||||
match duplication_check {
|
||||
Some(duplication_check) => match duplication_check {
|
||||
payment_methods::DataDuplicationCheck::Duplicated => {
|
||||
@ -1714,7 +1941,7 @@ pub async fn save_migration_payment_method(
|
||||
None,
|
||||
locker_id,
|
||||
connector_mandate_details,
|
||||
req.network_transaction_id.clone(),
|
||||
network_transaction_id,
|
||||
merchant_account.storage_scheme,
|
||||
payment_method_billing_address.map(Into::into),
|
||||
None,
|
||||
|
||||
@ -149,3 +149,64 @@ fn validate_card_exp_year(year: String) -> Result<(), errors::ValidationError> {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct RecordMigrationStatus {
|
||||
pub card_migrated: Option<bool>,
|
||||
pub network_token_migrated: Option<bool>,
|
||||
pub connector_mandate_details_migrated: Option<bool>,
|
||||
pub network_transaction_migrated: Option<bool>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct RecordMigrationStatusBuilder {
|
||||
pub card_migrated: Option<bool>,
|
||||
pub network_token_migrated: Option<bool>,
|
||||
pub connector_mandate_details_migrated: Option<bool>,
|
||||
pub network_transaction_migrated: Option<bool>,
|
||||
}
|
||||
|
||||
impl RecordMigrationStatusBuilder {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
card_migrated: None,
|
||||
network_token_migrated: None,
|
||||
connector_mandate_details_migrated: None,
|
||||
network_transaction_migrated: None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn card_migrated(&mut self, card_migrated: bool) {
|
||||
self.card_migrated = Some(card_migrated);
|
||||
}
|
||||
|
||||
pub fn network_token_migrated(&mut self, network_token_migrated: Option<bool>) {
|
||||
self.network_token_migrated = network_token_migrated;
|
||||
}
|
||||
|
||||
pub fn connector_mandate_details_migrated(
|
||||
&mut self,
|
||||
connector_mandate_details_migrated: Option<bool>,
|
||||
) {
|
||||
self.connector_mandate_details_migrated = connector_mandate_details_migrated;
|
||||
}
|
||||
|
||||
pub fn network_transaction_id_migrated(&mut self, network_transaction_migrated: Option<bool>) {
|
||||
self.network_transaction_migrated = network_transaction_migrated;
|
||||
}
|
||||
|
||||
pub fn build(self) -> RecordMigrationStatus {
|
||||
RecordMigrationStatus {
|
||||
card_migrated: self.card_migrated,
|
||||
network_token_migrated: self.network_token_migrated,
|
||||
connector_mandate_details_migrated: self.connector_mandate_details_migrated,
|
||||
network_transaction_migrated: self.network_transaction_migrated,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for RecordMigrationStatusBuilder {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
@ -973,7 +973,7 @@ pub async fn save_network_token_in_locker(
|
||||
{
|
||||
Ok((token_response, network_token_requestor_ref_id)) => {
|
||||
// Only proceed if the tokenization was successful
|
||||
let card_data = api::CardDetail {
|
||||
let network_token_data = api::CardDetail {
|
||||
card_number: token_response.token.clone(),
|
||||
card_exp_month: token_response.token_expiry_month.clone(),
|
||||
card_exp_year: token_response.token_expiry_year.clone(),
|
||||
@ -988,7 +988,7 @@ pub async fn save_network_token_in_locker(
|
||||
let (res, dc) = Box::pin(payment_methods::cards::add_card_to_locker(
|
||||
state,
|
||||
payment_method_request,
|
||||
&card_data,
|
||||
&network_token_data,
|
||||
&customer_id,
|
||||
merchant_account,
|
||||
None,
|
||||
|
||||
@ -7,9 +7,10 @@ pub use api_models::payment_methods::{
|
||||
PaymentMethodCreateData, PaymentMethodDeleteResponse, PaymentMethodId,
|
||||
PaymentMethodIntentConfirm, PaymentMethodIntentConfirmInternal, PaymentMethodIntentCreate,
|
||||
PaymentMethodListData, PaymentMethodListRequest, PaymentMethodListResponse,
|
||||
PaymentMethodMigrate, PaymentMethodResponse, PaymentMethodResponseData, PaymentMethodUpdate,
|
||||
PaymentMethodUpdateData, PaymentMethodsData, TokenizePayloadEncrypted, TokenizePayloadRequest,
|
||||
TokenizedCardValue1, TokenizedCardValue2, TokenizedWalletValue1, TokenizedWalletValue2,
|
||||
PaymentMethodMigrate, PaymentMethodMigrateResponse, PaymentMethodResponse,
|
||||
PaymentMethodResponseData, PaymentMethodUpdate, PaymentMethodUpdateData, PaymentMethodsData,
|
||||
TokenizePayloadEncrypted, TokenizePayloadRequest, TokenizedCardValue1, TokenizedCardValue2,
|
||||
TokenizedWalletValue1, TokenizedWalletValue2,
|
||||
};
|
||||
#[cfg(all(
|
||||
any(feature = "v2", feature = "v1"),
|
||||
@ -22,9 +23,9 @@ pub use api_models::payment_methods::{
|
||||
PaymentMethodCollectLinkRenderRequest, PaymentMethodCollectLinkRequest, PaymentMethodCreate,
|
||||
PaymentMethodCreateData, PaymentMethodDeleteResponse, PaymentMethodId,
|
||||
PaymentMethodListRequest, PaymentMethodListResponse, PaymentMethodMigrate,
|
||||
PaymentMethodResponse, PaymentMethodUpdate, PaymentMethodsData, TokenizePayloadEncrypted,
|
||||
TokenizePayloadRequest, TokenizedCardValue1, TokenizedCardValue2, TokenizedWalletValue1,
|
||||
TokenizedWalletValue2,
|
||||
PaymentMethodMigrateResponse, PaymentMethodResponse, PaymentMethodUpdate, PaymentMethodsData,
|
||||
TokenizePayloadEncrypted, TokenizePayloadRequest, TokenizedCardValue1, TokenizedCardValue2,
|
||||
TokenizedWalletValue1, TokenizedWalletValue2,
|
||||
};
|
||||
use error_stack::report;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user