mirror of
https://github.com/juspay/hyperswitch.git
synced 2025-10-29 17:19:15 +08:00
feat(payment_methods): Filter payment methods based on pm client secret (#4249)
Co-authored-by: hyperswitch-bot[bot] <148525504+hyperswitch-bot[bot]@users.noreply.github.com>
This commit is contained in:
@ -365,5 +365,6 @@ pub(crate) async fn fetch_raw_secrets(
|
||||
connector_onboarding,
|
||||
cors: conf.cors,
|
||||
unmasked_headers: conf.unmasked_headers,
|
||||
saved_payment_methods: conf.saved_payment_methods,
|
||||
}
|
||||
}
|
||||
|
||||
@ -119,6 +119,7 @@ pub struct Settings<S: SecretState> {
|
||||
#[cfg(feature = "olap")]
|
||||
pub connector_onboarding: SecretStateContainer<ConnectorOnboarding, S>,
|
||||
pub unmasked_headers: UnmaskedHeaders,
|
||||
pub saved_payment_methods: EligiblePaymentMethods,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, Clone, Default)]
|
||||
@ -165,6 +166,11 @@ pub struct PaymentMethodAuth {
|
||||
pub pm_auth_key: Secret<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, Clone, Default)]
|
||||
pub struct EligiblePaymentMethods {
|
||||
pub sdk_eligible_payment_methods: HashSet<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, Clone, Default)]
|
||||
pub struct DefaultExchangeRates {
|
||||
pub base_currency: String,
|
||||
|
||||
@ -441,6 +441,8 @@ pub async fn add_payment_method_data(
|
||||
status: Some(enums::PaymentMethodStatus::Active),
|
||||
locker_id: Some(locker_id),
|
||||
payment_method: req.payment_method,
|
||||
payment_method_issuer: req.payment_method_issuer,
|
||||
payment_method_type: req.payment_method_type,
|
||||
};
|
||||
|
||||
db.update_payment_method(
|
||||
@ -555,7 +557,7 @@ pub async fn add_payment_method(
|
||||
match duplication_check {
|
||||
Some(duplication_check) => match duplication_check {
|
||||
payment_methods::DataDuplicationCheck::Duplicated => {
|
||||
get_or_insert_payment_method(
|
||||
let existing_pm = get_or_insert_payment_method(
|
||||
db,
|
||||
req.clone(),
|
||||
&mut resp,
|
||||
@ -564,6 +566,8 @@ pub async fn add_payment_method(
|
||||
key_store,
|
||||
)
|
||||
.await?;
|
||||
|
||||
resp.client_secret = existing_pm.client_secret;
|
||||
}
|
||||
payment_methods::DataDuplicationCheck::MetaDataChanged => {
|
||||
if let Some(card) = req.card.clone() {
|
||||
@ -577,6 +581,8 @@ pub async fn add_payment_method(
|
||||
)
|
||||
.await?;
|
||||
|
||||
let client_secret = existing_pm.client_secret.clone();
|
||||
|
||||
delete_card_from_locker(
|
||||
&state,
|
||||
&customer_id,
|
||||
@ -653,6 +659,8 @@ pub async fn add_payment_method(
|
||||
.await
|
||||
.change_context(errors::ApiErrorResponse::InternalServerError)
|
||||
.attach_printable("Failed to add payment method in db")?;
|
||||
|
||||
resp.client_secret = client_secret;
|
||||
}
|
||||
}
|
||||
},
|
||||
@ -667,7 +675,7 @@ pub async fn add_payment_method(
|
||||
None
|
||||
};
|
||||
resp.payment_method_id = generate_id(consts::ID_LENGTH, "pm");
|
||||
insert_payment_method(
|
||||
let pm = insert_payment_method(
|
||||
db,
|
||||
&resp,
|
||||
req,
|
||||
@ -682,6 +690,8 @@ pub async fn add_payment_method(
|
||||
merchant_account.storage_scheme,
|
||||
)
|
||||
.await?;
|
||||
|
||||
resp.client_secret = pm.client_secret;
|
||||
}
|
||||
}
|
||||
|
||||
@ -744,6 +754,18 @@ pub async fn update_customer_payment_method(
|
||||
.await
|
||||
.to_not_found_response(errors::ApiErrorResponse::PaymentMethodNotFound)?;
|
||||
|
||||
if pm.status == enums::PaymentMethodStatus::AwaitingData {
|
||||
return Err(report!(errors::ApiErrorResponse::NotSupported {
|
||||
message: "Payment method is awaiting data so it cannot be updated".into()
|
||||
}));
|
||||
}
|
||||
|
||||
if pm.payment_method_data.is_none() {
|
||||
return Err(report!(errors::ApiErrorResponse::GenericNotFoundError {
|
||||
message: "payment_method_data not found".to_string()
|
||||
}));
|
||||
}
|
||||
|
||||
// Fetch the existing payment method data from db
|
||||
let existing_card_data = decrypt::<serde_json::Value, masking::WithType>(
|
||||
pm.payment_method_data.clone(),
|
||||
@ -812,7 +834,7 @@ pub async fn update_customer_payment_method(
|
||||
wallet: req.wallet,
|
||||
metadata: req.metadata,
|
||||
customer_id: Some(pm.customer_id.clone()),
|
||||
client_secret: None,
|
||||
client_secret: pm.client_secret.clone(),
|
||||
payment_method_data: None,
|
||||
card_network: req
|
||||
.card_network
|
||||
@ -901,7 +923,7 @@ pub async fn update_customer_payment_method(
|
||||
installment_payment_enabled: false,
|
||||
payment_experience: Some(vec![api_models::enums::PaymentExperience::RedirectToUrl]),
|
||||
last_used_at: Some(common_utils::date_time::now()),
|
||||
client_secret: None,
|
||||
client_secret: pm.client_secret.clone(),
|
||||
}
|
||||
};
|
||||
|
||||
@ -1691,12 +1713,21 @@ pub async fn list_payment_methods(
|
||||
let db = &*state.store;
|
||||
let pm_config_mapping = &state.conf.pm_filters;
|
||||
|
||||
let payment_intent = helpers::verify_payment_intent_time_and_client_secret(
|
||||
db,
|
||||
&merchant_account,
|
||||
req.client_secret.clone(),
|
||||
)
|
||||
.await?;
|
||||
let payment_intent = if let Some(cs) = &req.client_secret {
|
||||
if cs.starts_with("pm_") {
|
||||
validate_payment_method_and_client_secret(cs, db, &merchant_account).await?;
|
||||
None
|
||||
} else {
|
||||
helpers::verify_payment_intent_time_and_client_secret(
|
||||
db,
|
||||
&merchant_account,
|
||||
req.client_secret.clone(),
|
||||
)
|
||||
.await?
|
||||
}
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let shipping_address = payment_intent
|
||||
.as_ref()
|
||||
@ -1839,6 +1870,7 @@ pub async fn list_payment_methods(
|
||||
pm_config_mapping,
|
||||
&state.conf.mandates.supported_payment_methods,
|
||||
&state.conf.mandates.update_mandate_supported,
|
||||
&state.conf.saved_payment_methods,
|
||||
)
|
||||
.await?;
|
||||
}
|
||||
@ -2535,6 +2567,34 @@ pub async fn list_payment_methods(
|
||||
))
|
||||
}
|
||||
|
||||
async fn validate_payment_method_and_client_secret(
|
||||
cs: &String,
|
||||
db: &dyn db::StorageInterface,
|
||||
merchant_account: &domain::MerchantAccount,
|
||||
) -> Result<(), error_stack::Report<errors::ApiErrorResponse>> {
|
||||
let pm_vec = cs.split("_secret").collect::<Vec<&str>>();
|
||||
let pm_id = pm_vec
|
||||
.first()
|
||||
.ok_or(errors::ApiErrorResponse::MissingRequiredField {
|
||||
field_name: "client_secret",
|
||||
})?;
|
||||
|
||||
let payment_method = db
|
||||
.find_payment_method(pm_id, merchant_account.storage_scheme)
|
||||
.await
|
||||
.change_context(errors::ApiErrorResponse::PaymentMethodNotFound)
|
||||
.attach_printable("Unable to find payment method")?;
|
||||
|
||||
let client_secret_expired =
|
||||
authenticate_pm_client_secret_and_check_expiry(cs, &payment_method)?;
|
||||
if client_secret_expired {
|
||||
return Err::<(), error_stack::Report<errors::ApiErrorResponse>>(
|
||||
(errors::ApiErrorResponse::ClientSecretExpired).into(),
|
||||
);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn call_surcharge_decision_management(
|
||||
state: routes::AppState,
|
||||
merchant_account: &domain::MerchantAccount,
|
||||
@ -2644,6 +2704,7 @@ pub async fn filter_payment_methods(
|
||||
config: &settings::ConnectorFilters,
|
||||
supported_payment_methods_for_mandate: &settings::SupportedPaymentMethodsForMandate,
|
||||
supported_payment_methods_for_update_mandate: &settings::SupportedPaymentMethodsForMandate,
|
||||
saved_payment_methods: &settings::EligiblePaymentMethods,
|
||||
) -> errors::CustomResult<(), errors::ApiErrorResponse> {
|
||||
for payment_method in payment_methods.into_iter() {
|
||||
let parse_result = serde_json::from_value::<PaymentMethodsEnabled>(payment_method);
|
||||
@ -2761,6 +2822,20 @@ pub async fn filter_payment_methods(
|
||||
})
|
||||
.unwrap_or(true);
|
||||
|
||||
let filter9 = req
|
||||
.client_secret
|
||||
.as_ref()
|
||||
.map(|cs| {
|
||||
if cs.starts_with("pm_") {
|
||||
saved_payment_methods
|
||||
.sdk_eligible_payment_methods
|
||||
.contains(payment_method.to_string().as_str())
|
||||
} else {
|
||||
true
|
||||
}
|
||||
})
|
||||
.unwrap_or(true);
|
||||
|
||||
let connector = connector.clone();
|
||||
|
||||
let response_pm_type = ResponsePaymentMethodIntermediate::new(
|
||||
@ -2777,6 +2852,7 @@ pub async fn filter_payment_methods(
|
||||
&& filter6
|
||||
&& filter7
|
||||
&& filter8
|
||||
&& filter9
|
||||
{
|
||||
resp.push(response_pm_type);
|
||||
}
|
||||
|
||||
@ -374,7 +374,7 @@ pub fn mk_add_card_response_hs(
|
||||
installment_payment_enabled: false, // #[#256]
|
||||
payment_experience: Some(vec![api_models::enums::PaymentExperience::RedirectToUrl]),
|
||||
last_used_at: Some(common_utils::date_time::now()), // [#256]
|
||||
client_secret: None,
|
||||
client_secret: req.client_secret,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user