mirror of
https://github.com/juspay/hyperswitch.git
synced 2025-11-02 04:04:43 +08:00
feat(router): add an api to migrate the payment method (#5186)
Co-authored-by: hyperswitch-bot[bot] <148525504+hyperswitch-bot[bot]@users.noreply.github.com>
This commit is contained in:
@ -130,6 +130,9 @@ pub async fn call_to_locker(
|
||||
card_network: card.card_brand,
|
||||
client_secret: None,
|
||||
payment_method_data: None,
|
||||
billing: None,
|
||||
connector_mandate_details: None,
|
||||
network_transaction_id: None,
|
||||
};
|
||||
|
||||
let add_card_result = cards::add_card_hs(
|
||||
|
||||
@ -95,6 +95,7 @@ pub async fn create_payment_method(
|
||||
network_transaction_id: Option<String>,
|
||||
storage_scheme: MerchantStorageScheme,
|
||||
payment_method_billing_address: Option<Encryption>,
|
||||
card_scheme: Option<String>,
|
||||
) -> errors::CustomResult<storage::PaymentMethod, errors::ApiErrorResponse> {
|
||||
let customer = db
|
||||
.find_customer_by_customer_id_merchant_id(
|
||||
@ -123,7 +124,7 @@ pub async fn create_payment_method(
|
||||
payment_method: req.payment_method,
|
||||
payment_method_type: req.payment_method_type,
|
||||
payment_method_issuer: req.payment_method_issuer.clone(),
|
||||
scheme: req.card_network.clone(),
|
||||
scheme: req.card_network.clone().or(card_scheme),
|
||||
metadata: pm_metadata.map(Secret::new),
|
||||
payment_method_data,
|
||||
connector_mandate_details,
|
||||
@ -244,7 +245,7 @@ pub async fn get_or_insert_payment_method(
|
||||
insert_payment_method(
|
||||
db,
|
||||
resp,
|
||||
req,
|
||||
&req,
|
||||
key_store,
|
||||
&merchant_account.merchant_id,
|
||||
customer_id,
|
||||
@ -252,7 +253,7 @@ pub async fn get_or_insert_payment_method(
|
||||
None,
|
||||
locker_id,
|
||||
None,
|
||||
None,
|
||||
req.network_transaction_id.clone(),
|
||||
merchant_account.storage_scheme,
|
||||
None,
|
||||
)
|
||||
@ -266,6 +267,314 @@ pub async fn get_or_insert_payment_method(
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn migrate_payment_method(
|
||||
state: routes::SessionState,
|
||||
req: api::PaymentMethodMigrate,
|
||||
) -> errors::RouterResponse<api::PaymentMethodResponse> {
|
||||
let merchant_id = &req.merchant_id;
|
||||
let key_store = state
|
||||
.store
|
||||
.get_merchant_key_store_by_merchant_id(
|
||||
merchant_id,
|
||||
&state.store.get_master_key().to_vec().into(),
|
||||
)
|
||||
.await
|
||||
.to_not_found_response(errors::ApiErrorResponse::MerchantAccountNotFound)?;
|
||||
|
||||
let merchant_account = state
|
||||
.store
|
||||
.find_merchant_account_by_merchant_id(merchant_id, &key_store)
|
||||
.await
|
||||
.to_not_found_response(errors::ApiErrorResponse::MerchantAccountNotFound)?;
|
||||
|
||||
let card_details = req.card.as_ref().get_required_value("card")?;
|
||||
|
||||
let card_number_validation_result =
|
||||
cards::CardNumber::from_str(card_details.card_number.peek());
|
||||
|
||||
if let Some(connector_mandate_details) = &req.connector_mandate_details {
|
||||
helpers::validate_merchant_connector_ids_in_connector_mandate_details(
|
||||
&*state.store,
|
||||
&key_store,
|
||||
connector_mandate_details,
|
||||
merchant_id,
|
||||
)
|
||||
.await?;
|
||||
};
|
||||
|
||||
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,
|
||||
);
|
||||
get_client_secret_or_add_payment_method(
|
||||
state,
|
||||
payment_method_create_request,
|
||||
&merchant_account,
|
||||
&key_store,
|
||||
)
|
||||
.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,
|
||||
&req,
|
||||
merchant_id.into(),
|
||||
&key_store,
|
||||
&merchant_account,
|
||||
)
|
||||
.await
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn skip_locker_call_and_migrate_payment_method(
|
||||
state: routes::SessionState,
|
||||
req: &api::PaymentMethodMigrate,
|
||||
merchant_id: String,
|
||||
key_store: &domain::MerchantKeyStore,
|
||||
merchant_account: &domain::MerchantAccount,
|
||||
) -> 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")?;
|
||||
|
||||
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 payment_method_billing_address = create_encrypted_data(key_store, req.billing.clone())
|
||||
.await
|
||||
.map(|details| details.into());
|
||||
|
||||
let customer = db
|
||||
.find_customer_by_customer_id_merchant_id(
|
||||
&customer_id,
|
||||
&merchant_id,
|
||||
key_store,
|
||||
merchant_account.storage_scheme,
|
||||
)
|
||||
.await
|
||||
.to_not_found_response(errors::ApiErrorResponse::CustomerNotFound)?;
|
||||
|
||||
let card = if let Some(card_details) = &req.card {
|
||||
helpers::validate_card_expiry(&card_details.card_exp_month, &card_details.card_exp_year)?;
|
||||
let card_number = card_details.card_number.clone();
|
||||
|
||||
let (card_isin, last4_digits) = get_card_bin_and_last4_digits_for_masked_card(
|
||||
card_number.peek(),
|
||||
)
|
||||
.change_context(errors::ApiErrorResponse::InvalidRequestData {
|
||||
message: "Invalid card number".to_string(),
|
||||
})?;
|
||||
|
||||
if card_details.card_issuer.is_some()
|
||||
&& card_details.card_network.is_some()
|
||||
&& card_details.card_type.is_some()
|
||||
&& card_details.card_issuing_country.is_some()
|
||||
{
|
||||
Some(api::CardDetailFromLocker {
|
||||
scheme: card_details
|
||||
.card_network
|
||||
.clone()
|
||||
.or(card_details.card_network.clone())
|
||||
.map(|card_network| card_network.to_string()),
|
||||
last4_digits: Some(last4_digits.clone()),
|
||||
issuer_country: card_details
|
||||
.card_issuing_country
|
||||
.clone()
|
||||
.or(card_details.card_issuing_country.clone()),
|
||||
card_number: None,
|
||||
expiry_month: Some(card_details.card_exp_month.clone()),
|
||||
expiry_year: Some(card_details.card_exp_year.clone()),
|
||||
card_token: None,
|
||||
card_fingerprint: None,
|
||||
card_holder_name: card_details.card_holder_name.clone(),
|
||||
nick_name: card_details.nick_name.clone(),
|
||||
card_isin: Some(card_isin.clone()),
|
||||
card_issuer: card_details
|
||||
.card_issuer
|
||||
.clone()
|
||||
.or(card_details.card_issuer.clone()),
|
||||
card_network: card_details
|
||||
.card_network
|
||||
.clone()
|
||||
.or(card_details.card_network.clone()),
|
||||
card_type: card_details
|
||||
.card_type
|
||||
.clone()
|
||||
.or(card_details.card_type.clone()),
|
||||
saved_to_locker: false,
|
||||
})
|
||||
} else {
|
||||
Some(
|
||||
db.get_card_info(&card_isin)
|
||||
.await
|
||||
.map_err(|error| services::logger::error!(card_info_error=?error))
|
||||
.ok()
|
||||
.flatten()
|
||||
.map(|card_info| {
|
||||
logger::debug!("Fetching bin details");
|
||||
api::CardDetailFromLocker {
|
||||
scheme: card_details
|
||||
.card_network
|
||||
.clone()
|
||||
.or(card_info.card_network.clone())
|
||||
.map(|card_network| card_network.to_string()),
|
||||
last4_digits: Some(last4_digits.clone()),
|
||||
issuer_country: card_details
|
||||
.card_issuing_country
|
||||
.clone()
|
||||
.or(card_info.card_issuing_country),
|
||||
card_number: None,
|
||||
expiry_month: Some(card_details.card_exp_month.clone()),
|
||||
expiry_year: Some(card_details.card_exp_year.clone()),
|
||||
card_token: None,
|
||||
card_fingerprint: None,
|
||||
card_holder_name: card_details.card_holder_name.clone(),
|
||||
nick_name: card_details.nick_name.clone(),
|
||||
card_isin: Some(card_isin.clone()),
|
||||
card_issuer: card_details.card_issuer.clone().or(card_info.card_issuer),
|
||||
card_network: card_details
|
||||
.card_network
|
||||
.clone()
|
||||
.or(card_info.card_network),
|
||||
card_type: card_details.card_type.clone().or(card_info.card_type),
|
||||
saved_to_locker: false,
|
||||
}
|
||||
})
|
||||
.unwrap_or_else(|| {
|
||||
logger::debug!("Failed to fetch bin details");
|
||||
api::CardDetailFromLocker {
|
||||
scheme: card_details
|
||||
.card_network
|
||||
.clone()
|
||||
.map(|card_network| card_network.to_string()),
|
||||
last4_digits: Some(last4_digits.clone()),
|
||||
issuer_country: card_details.card_issuing_country.clone(),
|
||||
card_number: None,
|
||||
expiry_month: Some(card_details.card_exp_month.clone()),
|
||||
expiry_year: Some(card_details.card_exp_year.clone()),
|
||||
card_token: None,
|
||||
card_fingerprint: None,
|
||||
card_holder_name: card_details.card_holder_name.clone(),
|
||||
nick_name: card_details.nick_name.clone(),
|
||||
card_isin: Some(card_isin.clone()),
|
||||
card_issuer: card_details.card_issuer.clone(),
|
||||
card_network: card_details.card_network.clone(),
|
||||
card_type: card_details.card_type.clone(),
|
||||
saved_to_locker: false,
|
||||
}
|
||||
}),
|
||||
)
|
||||
}
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let payment_method_card_details = card
|
||||
.as_ref()
|
||||
.map(|card| PaymentMethodsData::Card(CardDetailsPaymentMethod::from(card.clone())));
|
||||
|
||||
let payment_method_data_encrypted =
|
||||
create_encrypted_data(key_store, payment_method_card_details)
|
||||
.await
|
||||
.map(|details| details.into());
|
||||
|
||||
let payment_method_metadata: Option<serde_json::Value> =
|
||||
req.metadata.as_ref().map(|data| data.peek()).cloned();
|
||||
|
||||
let payment_method_id = generate_id(consts::ID_LENGTH, "pm");
|
||||
|
||||
let current_time = common_utils::date_time::now();
|
||||
|
||||
let response = db
|
||||
.insert_payment_method(
|
||||
storage::PaymentMethodNew {
|
||||
customer_id: customer_id.to_owned(),
|
||||
merchant_id: merchant_id.to_string(),
|
||||
payment_method_id: payment_method_id.to_string(),
|
||||
locker_id: None,
|
||||
payment_method: req.payment_method,
|
||||
payment_method_type: req.payment_method_type,
|
||||
payment_method_issuer: req.payment_method_issuer.clone(),
|
||||
scheme: req.card_network.clone(),
|
||||
metadata: payment_method_metadata.map(Secret::new),
|
||||
payment_method_data: payment_method_data_encrypted,
|
||||
connector_mandate_details: Some(connector_mandate_details),
|
||||
customer_acceptance: None,
|
||||
client_secret: None,
|
||||
status: enums::PaymentMethodStatus::Active,
|
||||
network_transaction_id: None,
|
||||
payment_method_issuer_code: None,
|
||||
accepted_currency: None,
|
||||
token: None,
|
||||
cardholder_name: None,
|
||||
issuer_name: None,
|
||||
issuer_country: None,
|
||||
payer_country: None,
|
||||
is_stored: None,
|
||||
swift_code: None,
|
||||
direct_debit_token: None,
|
||||
created_at: current_time,
|
||||
last_modified: current_time,
|
||||
last_used_at: current_time,
|
||||
payment_method_billing_address,
|
||||
updated_by: None,
|
||||
},
|
||||
merchant_account.storage_scheme,
|
||||
)
|
||||
.await
|
||||
.change_context(errors::ApiErrorResponse::InternalServerError)
|
||||
.attach_printable("Failed to add payment method in db")?;
|
||||
|
||||
logger::debug!("Payment method inserted in db");
|
||||
|
||||
if customer.default_payment_method_id.is_none() && req.payment_method.is_some() {
|
||||
let _ = set_default_payment_method(
|
||||
&*state.store,
|
||||
merchant_id.to_string(),
|
||||
key_store.clone(),
|
||||
&customer_id,
|
||||
payment_method_id.to_owned(),
|
||||
merchant_account.storage_scheme,
|
||||
)
|
||||
.await
|
||||
.map_err(|err| logger::error!(error=?err,"Failed to set the payment method as default"));
|
||||
}
|
||||
Ok(services::api::ApplicationResponse::Json(
|
||||
api::PaymentMethodResponse::foreign_from((card, response)),
|
||||
))
|
||||
}
|
||||
|
||||
pub fn get_card_bin_and_last4_digits_for_masked_card(
|
||||
masked_card_number: &str,
|
||||
) -> Result<(String, String), cards::CardNumberValidationErr> {
|
||||
let last4_digits = masked_card_number
|
||||
.chars()
|
||||
.rev()
|
||||
.take(4)
|
||||
.collect::<String>()
|
||||
.chars()
|
||||
.rev()
|
||||
.collect::<String>();
|
||||
|
||||
let card_isin = masked_card_number.chars().take(6).collect::<String>();
|
||||
|
||||
cards::validate::validate_card_number_chars(&card_isin)
|
||||
.and_then(|_| cards::validate::validate_card_number_chars(&last4_digits))?;
|
||||
|
||||
Ok((card_isin, last4_digits))
|
||||
}
|
||||
|
||||
#[instrument(skip_all)]
|
||||
pub async fn get_client_secret_or_add_payment_method(
|
||||
state: routes::SessionState,
|
||||
@ -282,6 +591,17 @@ pub async fn get_client_secret_or_add_payment_method(
|
||||
#[cfg(feature = "payouts")]
|
||||
let condition = req.card.is_some() || req.bank_transfer.is_some() || req.wallet.is_some();
|
||||
|
||||
let payment_method_billing_address = create_encrypted_data(key_store, req.billing.clone())
|
||||
.await
|
||||
.map(|details| details.into());
|
||||
|
||||
let connector_mandate_details = req
|
||||
.connector_mandate_details
|
||||
.clone()
|
||||
.map(serde_json::to_value)
|
||||
.transpose()
|
||||
.change_context(errors::ApiErrorResponse::InternalServerError)?;
|
||||
|
||||
if condition {
|
||||
Box::pin(add_payment_method(state, req, merchant_account, key_store)).await
|
||||
} else {
|
||||
@ -298,10 +618,11 @@ pub async fn get_client_secret_or_add_payment_method(
|
||||
None,
|
||||
None,
|
||||
key_store,
|
||||
None,
|
||||
connector_mandate_details,
|
||||
Some(enums::PaymentMethodStatus::AwaitingData),
|
||||
None,
|
||||
merchant_account.storage_scheme,
|
||||
payment_method_billing_address,
|
||||
None,
|
||||
)
|
||||
.await?;
|
||||
@ -322,7 +643,7 @@ pub async fn get_client_secret_or_add_payment_method(
|
||||
}
|
||||
|
||||
Ok(services::api::ApplicationResponse::Json(
|
||||
api::PaymentMethodResponse::foreign_from(res),
|
||||
api::PaymentMethodResponse::foreign_from((None, res)),
|
||||
))
|
||||
}
|
||||
}
|
||||
@ -544,6 +865,16 @@ pub async fn add_payment_method(
|
||||
let merchant_id = &merchant_account.merchant_id;
|
||||
let customer_id = req.customer_id.clone().get_required_value("customer_id")?;
|
||||
let payment_method = req.payment_method.get_required_value("payment_method")?;
|
||||
let payment_method_billing_address = create_encrypted_data(key_store, req.billing.clone())
|
||||
.await
|
||||
.map(|details| details.into());
|
||||
|
||||
let connector_mandate_details = req
|
||||
.connector_mandate_details
|
||||
.clone()
|
||||
.map(serde_json::to_value)
|
||||
.transpose()
|
||||
.change_context(errors::ApiErrorResponse::InternalServerError)?;
|
||||
|
||||
let response = match payment_method {
|
||||
#[cfg(feature = "payouts")]
|
||||
@ -567,11 +898,20 @@ pub async fn add_payment_method(
|
||||
},
|
||||
api_enums::PaymentMethod::Card => match req.card.clone() {
|
||||
Some(card) => {
|
||||
helpers::validate_card_expiry(&card.card_exp_month, &card.card_exp_year)?;
|
||||
let mut card_details = card;
|
||||
card_details = helpers::populate_bin_details_for_payment_method_create(
|
||||
card_details.clone(),
|
||||
db,
|
||||
)
|
||||
.await;
|
||||
helpers::validate_card_expiry(
|
||||
&card_details.card_exp_month,
|
||||
&card_details.card_exp_year,
|
||||
)?;
|
||||
Box::pin(add_card_to_locker(
|
||||
&state,
|
||||
req.clone(),
|
||||
&card,
|
||||
&card_details,
|
||||
&customer_id,
|
||||
merchant_account,
|
||||
None,
|
||||
@ -731,17 +1071,17 @@ pub async fn add_payment_method(
|
||||
let pm = insert_payment_method(
|
||||
db,
|
||||
&resp,
|
||||
req,
|
||||
&req,
|
||||
key_store,
|
||||
merchant_id,
|
||||
&customer_id,
|
||||
pm_metadata.cloned(),
|
||||
None,
|
||||
locker_id,
|
||||
None,
|
||||
None,
|
||||
connector_mandate_details,
|
||||
req.network_transaction_id.clone(),
|
||||
merchant_account.storage_scheme,
|
||||
None,
|
||||
payment_method_billing_address,
|
||||
)
|
||||
.await?;
|
||||
|
||||
@ -756,7 +1096,7 @@ pub async fn add_payment_method(
|
||||
pub async fn insert_payment_method(
|
||||
db: &dyn db::StorageInterface,
|
||||
resp: &api::PaymentMethodResponse,
|
||||
req: api::PaymentMethodCreate,
|
||||
req: &api::PaymentMethodCreate,
|
||||
key_store: &domain::MerchantKeyStore,
|
||||
merchant_id: &str,
|
||||
customer_id: &id_type::CustomerId,
|
||||
@ -770,7 +1110,7 @@ pub async fn insert_payment_method(
|
||||
) -> errors::RouterResult<diesel_models::PaymentMethod> {
|
||||
let pm_card_details = resp
|
||||
.card
|
||||
.as_ref()
|
||||
.clone()
|
||||
.map(|card| PaymentMethodsData::Card(CardDetailsPaymentMethod::from(card.clone())));
|
||||
let pm_data_encrypted = create_encrypted_data(key_store, pm_card_details)
|
||||
.await
|
||||
@ -778,7 +1118,7 @@ pub async fn insert_payment_method(
|
||||
|
||||
create_payment_method(
|
||||
db,
|
||||
&req,
|
||||
req,
|
||||
customer_id,
|
||||
&resp.payment_method_id,
|
||||
locker_id,
|
||||
@ -792,6 +1132,10 @@ pub async fn insert_payment_method(
|
||||
network_transaction_id,
|
||||
storage_scheme,
|
||||
payment_method_billing_address,
|
||||
resp.card.clone().and_then(|card| {
|
||||
card.card_network
|
||||
.map(|card_network| card_network.to_string())
|
||||
}),
|
||||
)
|
||||
.await
|
||||
}
|
||||
@ -904,6 +1248,9 @@ pub async fn update_customer_payment_method(
|
||||
client_secret: pm.client_secret.clone(),
|
||||
payment_method_data: None,
|
||||
card_network: None,
|
||||
billing: None,
|
||||
connector_mandate_details: None,
|
||||
network_transaction_id: None,
|
||||
};
|
||||
new_pm.validate()?;
|
||||
|
||||
@ -1135,7 +1482,7 @@ pub async fn add_card_to_locker(
|
||||
errors::VaultError,
|
||||
> {
|
||||
metrics::STORED_TO_LOCKER.add(&metrics::CONTEXT, 1, &[]);
|
||||
let add_card_to_hs_resp = common_utils::metrics::utils::record_operation_time(
|
||||
let add_card_to_hs_resp = Box::pin(common_utils::metrics::utils::record_operation_time(
|
||||
async {
|
||||
add_card_hs(
|
||||
state,
|
||||
@ -1162,7 +1509,7 @@ pub async fn add_card_to_locker(
|
||||
&metrics::CARD_ADD_TIME,
|
||||
&metrics::CONTEXT,
|
||||
&[router_env::opentelemetry::KeyValue::new("locker", "rust")],
|
||||
)
|
||||
))
|
||||
.await?;
|
||||
|
||||
logger::debug!("card added to hyperswitch-card-vault");
|
||||
@ -3677,12 +4024,8 @@ pub async fn get_card_details_with_locker_fallback(
|
||||
});
|
||||
|
||||
Ok(if let Some(mut crd) = card_decrypted {
|
||||
if crd.saved_to_locker {
|
||||
crd.scheme.clone_from(&pm.scheme);
|
||||
Some(crd)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
crd.scheme.clone_from(&pm.scheme);
|
||||
Some(crd)
|
||||
} else {
|
||||
logger::debug!(
|
||||
"Getting card details from locker as it is not found in payment methods table"
|
||||
|
||||
@ -348,7 +348,10 @@ pub fn mk_add_card_response_hs(
|
||||
let card_isin = card_number.get_card_isin();
|
||||
|
||||
let card = api::CardDetailFromLocker {
|
||||
scheme: None,
|
||||
scheme: card
|
||||
.card_network
|
||||
.clone()
|
||||
.map(|card_network| card_network.to_string()),
|
||||
last4_digits: Some(last4_digits),
|
||||
issuer_country: card.card_issuing_country,
|
||||
card_number: Some(card.card_number.clone()),
|
||||
|
||||
@ -1385,6 +1385,9 @@ pub(crate) async fn get_payment_method_create_request(
|
||||
.map(|card_network| card_network.to_string()),
|
||||
client_secret: None,
|
||||
payment_method_data: None,
|
||||
billing: None,
|
||||
connector_mandate_details: None,
|
||||
network_transaction_id: None,
|
||||
};
|
||||
Ok(payment_method_request)
|
||||
}
|
||||
@ -1404,6 +1407,9 @@ pub(crate) async fn get_payment_method_create_request(
|
||||
card_network: None,
|
||||
client_secret: None,
|
||||
payment_method_data: None,
|
||||
billing: None,
|
||||
connector_mandate_details: None,
|
||||
network_transaction_id: None,
|
||||
};
|
||||
|
||||
Ok(payment_method_request)
|
||||
@ -4074,6 +4080,63 @@ pub async fn get_additional_payment_data(
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn populate_bin_details_for_payment_method_create(
|
||||
card_details: api_models::payment_methods::CardDetail,
|
||||
db: &dyn StorageInterface,
|
||||
) -> api_models::payment_methods::CardDetail {
|
||||
let card_isin: Option<_> = Some(card_details.card_number.get_card_isin());
|
||||
if card_details.card_issuer.is_some()
|
||||
&& card_details.card_network.is_some()
|
||||
&& card_details.card_type.is_some()
|
||||
&& card_details.card_issuing_country.is_some()
|
||||
{
|
||||
api::CardDetail {
|
||||
card_issuer: card_details.card_issuer.to_owned(),
|
||||
card_network: card_details.card_network.clone(),
|
||||
card_type: card_details.card_type.to_owned(),
|
||||
card_issuing_country: card_details.card_issuing_country.to_owned(),
|
||||
card_exp_month: card_details.card_exp_month.clone(),
|
||||
card_exp_year: card_details.card_exp_year.clone(),
|
||||
card_holder_name: card_details.card_holder_name.clone(),
|
||||
card_number: card_details.card_number.clone(),
|
||||
nick_name: card_details.nick_name.clone(),
|
||||
}
|
||||
} else {
|
||||
let card_info = card_isin
|
||||
.clone()
|
||||
.async_and_then(|card_isin| async move {
|
||||
db.get_card_info(&card_isin)
|
||||
.await
|
||||
.map_err(|error| services::logger::error!(card_info_error=?error))
|
||||
.ok()
|
||||
})
|
||||
.await
|
||||
.flatten()
|
||||
.map(|card_info| api::CardDetail {
|
||||
card_issuer: card_info.card_issuer,
|
||||
card_network: card_info.card_network.clone(),
|
||||
card_type: card_info.card_type,
|
||||
card_issuing_country: card_info.card_issuing_country,
|
||||
card_exp_month: card_details.card_exp_month.clone(),
|
||||
card_exp_year: card_details.card_exp_year.clone(),
|
||||
card_holder_name: card_details.card_holder_name.clone(),
|
||||
card_number: card_details.card_number.clone(),
|
||||
nick_name: card_details.nick_name.clone(),
|
||||
});
|
||||
card_info.unwrap_or_else(|| api::CardDetail {
|
||||
card_issuer: None,
|
||||
card_network: None,
|
||||
card_type: None,
|
||||
card_issuing_country: None,
|
||||
card_exp_month: card_details.card_exp_month.clone(),
|
||||
card_exp_year: card_details.card_exp_year.clone(),
|
||||
card_holder_name: card_details.card_holder_name.clone(),
|
||||
card_number: card_details.card_number.clone(),
|
||||
nick_name: card_details.nick_name.clone(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub fn validate_customer_access(
|
||||
payment_intent: &PaymentIntent,
|
||||
auth_flow: services::AuthFlow,
|
||||
@ -5057,3 +5120,36 @@ pub async fn override_setup_future_usage_to_on_session<F: Clone>(
|
||||
};
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn validate_merchant_connector_ids_in_connector_mandate_details(
|
||||
db: &dyn StorageInterface,
|
||||
key_store: &domain::MerchantKeyStore,
|
||||
connector_mandate_details: &api_models::payment_methods::PaymentsMandateReference,
|
||||
merchant_id: &str,
|
||||
) -> CustomResult<(), errors::ApiErrorResponse> {
|
||||
let merchant_connector_account_list = db
|
||||
.find_merchant_connector_account_by_merchant_id_and_disabled_list(
|
||||
merchant_id,
|
||||
true,
|
||||
key_store,
|
||||
)
|
||||
.await
|
||||
.to_not_found_response(errors::ApiErrorResponse::InternalServerError)?;
|
||||
|
||||
let merchant_connector_ids: Vec<String> = merchant_connector_account_list
|
||||
.iter()
|
||||
.map(|merchant_connector_account| merchant_connector_account.merchant_connector_id.clone())
|
||||
.collect();
|
||||
|
||||
for merchant_connector_id in connector_mandate_details.0.keys() {
|
||||
if !merchant_connector_ids.contains(merchant_connector_id) {
|
||||
Err(errors::ApiErrorResponse::InvalidDataValue {
|
||||
field_name: "merchant_connector_id",
|
||||
})
|
||||
.attach_printable_lazy(|| {
|
||||
format!("{merchant_connector_id} invalid merchant connector id in connector_mandate_details")
|
||||
})?
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@ -318,6 +318,10 @@ where
|
||||
network_transaction_id,
|
||||
merchant_account.storage_scheme,
|
||||
encrypted_payment_method_billing_address,
|
||||
resp.card.and_then(|card| {
|
||||
card.card_network
|
||||
.map(|card_network| card_network.to_string())
|
||||
}),
|
||||
)
|
||||
.await
|
||||
} else {
|
||||
@ -397,7 +401,7 @@ where
|
||||
payment_methods::cards::insert_payment_method(
|
||||
db,
|
||||
&resp,
|
||||
payment_method_create_request.clone(),
|
||||
&payment_method_create_request.clone(),
|
||||
key_store,
|
||||
&merchant_account.merchant_id,
|
||||
&customer_id,
|
||||
@ -602,6 +606,10 @@ where
|
||||
network_transaction_id,
|
||||
merchant_account.storage_scheme,
|
||||
encrypted_payment_method_billing_address,
|
||||
resp.card.and_then(|card| {
|
||||
card.card_network
|
||||
.map(|card_network| card_network.to_string())
|
||||
}),
|
||||
)
|
||||
.await?;
|
||||
};
|
||||
|
||||
@ -391,6 +391,9 @@ pub async fn save_payout_data_to_locker(
|
||||
card_network: None,
|
||||
client_secret: None,
|
||||
payment_method_data: None,
|
||||
billing: None,
|
||||
connector_mandate_details: None,
|
||||
network_transaction_id: None,
|
||||
};
|
||||
|
||||
let pm_data = card_isin
|
||||
@ -472,6 +475,9 @@ pub async fn save_payout_data_to_locker(
|
||||
card_network: None,
|
||||
client_secret: None,
|
||||
payment_method_data: None,
|
||||
billing: None,
|
||||
connector_mandate_details: None,
|
||||
network_transaction_id: None,
|
||||
},
|
||||
)
|
||||
};
|
||||
@ -495,6 +501,7 @@ pub async fn save_payout_data_to_locker(
|
||||
None,
|
||||
merchant_account.storage_scheme,
|
||||
None,
|
||||
None,
|
||||
)
|
||||
.await?;
|
||||
}
|
||||
|
||||
@ -478,7 +478,6 @@ async fn store_bank_details_in_payment_methods(
|
||||
last_used_at: now,
|
||||
connector_mandate_details: None,
|
||||
customer_acceptance: None,
|
||||
|
||||
network_transaction_id: None,
|
||||
client_secret: None,
|
||||
payment_method_billing_address: None,
|
||||
|
||||
Reference in New Issue
Block a user