mirror of
https://github.com/juspay/hyperswitch.git
synced 2025-11-01 11:06:50 +08:00
feat(core): decide flow based on setup_future_usage (#3569)
Co-authored-by: hyperswitch-bot[bot] <148525504+hyperswitch-bot[bot]@users.noreply.github.com>
This commit is contained in:
@ -77,6 +77,9 @@ pub struct MandateCardDetails {
|
|||||||
pub card_network: Option<api_enums::CardNetwork>,
|
pub card_network: Option<api_enums::CardNetwork>,
|
||||||
/// The type of the payment card
|
/// The type of the payment card
|
||||||
pub card_type: Option<String>,
|
pub card_type: Option<String>,
|
||||||
|
/// The nick_name of the card holder
|
||||||
|
#[schema(value_type = Option<String>)]
|
||||||
|
pub nick_name: Option<Secret<String>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, Deserialize, ToSchema, Serialize)]
|
#[derive(Clone, Debug, Deserialize, ToSchema, Serialize)]
|
||||||
|
|||||||
@ -103,6 +103,7 @@ impl Feature<api::Authorize, types::PaymentsAuthorizeData> for types::PaymentsAu
|
|||||||
merchant_account,
|
merchant_account,
|
||||||
self.request.payment_method_type,
|
self.request.payment_method_type,
|
||||||
key_store,
|
key_store,
|
||||||
|
is_mandate,
|
||||||
))
|
))
|
||||||
.await?;
|
.await?;
|
||||||
Ok(mandate::mandate_procedure(
|
Ok(mandate::mandate_procedure(
|
||||||
@ -134,6 +135,7 @@ impl Feature<api::Authorize, types::PaymentsAuthorizeData> for types::PaymentsAu
|
|||||||
&merchant_account,
|
&merchant_account,
|
||||||
self.request.payment_method_type,
|
self.request.payment_method_type,
|
||||||
&key_store,
|
&key_store,
|
||||||
|
is_mandate,
|
||||||
))
|
))
|
||||||
.await;
|
.await;
|
||||||
|
|
||||||
|
|||||||
@ -76,6 +76,9 @@ impl Feature<api::SetupMandate, types::SetupMandateRequestData> for types::Setup
|
|||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
.to_setup_mandate_failed_response()?;
|
.to_setup_mandate_failed_response()?;
|
||||||
|
|
||||||
|
let is_mandate = resp.request.setup_mandate_details.is_some();
|
||||||
|
|
||||||
let pm_id = Box::pin(tokenization::save_payment_method(
|
let pm_id = Box::pin(tokenization::save_payment_method(
|
||||||
state,
|
state,
|
||||||
connector,
|
connector,
|
||||||
@ -84,6 +87,7 @@ impl Feature<api::SetupMandate, types::SetupMandateRequestData> for types::Setup
|
|||||||
merchant_account,
|
merchant_account,
|
||||||
self.request.payment_method_type,
|
self.request.payment_method_type,
|
||||||
key_store,
|
key_store,
|
||||||
|
is_mandate,
|
||||||
))
|
))
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
@ -280,6 +284,8 @@ impl types::SetupMandateRouterData {
|
|||||||
|
|
||||||
let payment_method_type = self.request.payment_method_type;
|
let payment_method_type = self.request.payment_method_type;
|
||||||
|
|
||||||
|
let is_mandate = resp.request.setup_mandate_details.is_some();
|
||||||
|
|
||||||
let pm_id = Box::pin(tokenization::save_payment_method(
|
let pm_id = Box::pin(tokenization::save_payment_method(
|
||||||
state,
|
state,
|
||||||
connector,
|
connector,
|
||||||
@ -288,6 +294,7 @@ impl types::SetupMandateRouterData {
|
|||||||
merchant_account,
|
merchant_account,
|
||||||
payment_method_type,
|
payment_method_type,
|
||||||
key_store,
|
key_store,
|
||||||
|
is_mandate,
|
||||||
))
|
))
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
|
|||||||
@ -8,7 +8,7 @@ use data_models::{
|
|||||||
payments::payment_attempt::PaymentAttempt,
|
payments::payment_attempt::PaymentAttempt,
|
||||||
};
|
};
|
||||||
use diesel_models::ephemeral_key;
|
use diesel_models::ephemeral_key;
|
||||||
use error_stack::{self, ResultExt};
|
use error_stack::{self, report, ResultExt};
|
||||||
use masking::PeekInterface;
|
use masking::PeekInterface;
|
||||||
use router_derive::PaymentOperation;
|
use router_derive::PaymentOperation;
|
||||||
use router_env::{instrument, tracing};
|
use router_env::{instrument, tracing};
|
||||||
@ -629,6 +629,17 @@ impl<F: Send + Clone, Ctx: PaymentMethodRetrieve> ValidateRequest<F, api::Paymen
|
|||||||
|
|
||||||
helpers::validate_payment_method_fields_present(request)?;
|
helpers::validate_payment_method_fields_present(request)?;
|
||||||
|
|
||||||
|
if request.mandate_data.is_none()
|
||||||
|
&& request
|
||||||
|
.setup_future_usage
|
||||||
|
.map(|fut_usage| fut_usage == enums::FutureUsage::OffSession)
|
||||||
|
.unwrap_or(false)
|
||||||
|
{
|
||||||
|
Err(report!(errors::ApiErrorResponse::PreconditionFailed {
|
||||||
|
message: "`setup_future_usage` cannot be `off_session` for normal payments".into()
|
||||||
|
}))?
|
||||||
|
}
|
||||||
|
|
||||||
let mandate_type =
|
let mandate_type =
|
||||||
helpers::validate_mandate(request, payments::is_operation_confirm(self))?;
|
helpers::validate_mandate(request, payments::is_operation_confirm(self))?;
|
||||||
|
|
||||||
|
|||||||
@ -682,6 +682,17 @@ impl<F: Send + Clone, Ctx: PaymentMethodRetrieve> ValidateRequest<F, api::Paymen
|
|||||||
|
|
||||||
helpers::validate_payment_method_fields_present(request)?;
|
helpers::validate_payment_method_fields_present(request)?;
|
||||||
|
|
||||||
|
if request.mandate_data.is_none()
|
||||||
|
&& request
|
||||||
|
.setup_future_usage
|
||||||
|
.map(|fut_usage| fut_usage == storage_enums::FutureUsage::OffSession)
|
||||||
|
.unwrap_or(false)
|
||||||
|
{
|
||||||
|
Err(report!(errors::ApiErrorResponse::PreconditionFailed {
|
||||||
|
message: "`setup_future_usage` cannot be `off_session` for normal payments".into()
|
||||||
|
}))?
|
||||||
|
}
|
||||||
|
|
||||||
let mandate_type = helpers::validate_mandate(request, false)?;
|
let mandate_type = helpers::validate_mandate(request, false)?;
|
||||||
|
|
||||||
Ok((
|
Ok((
|
||||||
|
|||||||
@ -22,6 +22,7 @@ use crate::{
|
|||||||
};
|
};
|
||||||
|
|
||||||
#[instrument(skip_all)]
|
#[instrument(skip_all)]
|
||||||
|
#[allow(clippy::too_many_arguments)]
|
||||||
pub async fn save_payment_method<F: Clone, FData>(
|
pub async fn save_payment_method<F: Clone, FData>(
|
||||||
state: &AppState,
|
state: &AppState,
|
||||||
connector: &api::ConnectorData,
|
connector: &api::ConnectorData,
|
||||||
@ -30,6 +31,7 @@ pub async fn save_payment_method<F: Clone, FData>(
|
|||||||
merchant_account: &domain::MerchantAccount,
|
merchant_account: &domain::MerchantAccount,
|
||||||
payment_method_type: Option<storage_enums::PaymentMethodType>,
|
payment_method_type: Option<storage_enums::PaymentMethodType>,
|
||||||
key_store: &domain::MerchantKeyStore,
|
key_store: &domain::MerchantKeyStore,
|
||||||
|
is_mandate: bool,
|
||||||
) -> RouterResult<Option<String>>
|
) -> RouterResult<Option<String>>
|
||||||
where
|
where
|
||||||
FData: mandate::MandateBehaviour,
|
FData: mandate::MandateBehaviour,
|
||||||
@ -62,8 +64,16 @@ where
|
|||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
};
|
};
|
||||||
|
let future_usage_validation = resp
|
||||||
|
.request
|
||||||
|
.get_setup_future_usage()
|
||||||
|
.map(|future_usage| {
|
||||||
|
(future_usage == storage_enums::FutureUsage::OffSession && is_mandate)
|
||||||
|
|| (future_usage == storage_enums::FutureUsage::OnSession && !is_mandate)
|
||||||
|
})
|
||||||
|
.unwrap_or(false);
|
||||||
|
|
||||||
let pm_id = if resp.request.get_setup_future_usage().is_some() {
|
let pm_id = if future_usage_validation {
|
||||||
let customer = maybe_customer.to_owned().get_required_value("customer")?;
|
let customer = maybe_customer.to_owned().get_required_value("customer")?;
|
||||||
let payment_method_create_request = helpers::get_payment_method_create_request(
|
let payment_method_create_request = helpers::get_payment_method_create_request(
|
||||||
Some(&resp.request.get_payment_method_data()),
|
Some(&resp.request.get_payment_method_data()),
|
||||||
|
|||||||
@ -112,6 +112,7 @@ impl From<api::payment_methods::CardDetailFromLocker> for MandateCardDetails {
|
|||||||
card_issuer: card_details_from_locker.card_issuer,
|
card_issuer: card_details_from_locker.card_issuer,
|
||||||
card_network: card_details_from_locker.card_network,
|
card_network: card_details_from_locker.card_network,
|
||||||
card_type: card_details_from_locker.card_type,
|
card_type: card_details_from_locker.card_type,
|
||||||
|
nick_name: card_details_from_locker.nick_name,
|
||||||
}
|
}
|
||||||
.into()
|
.into()
|
||||||
}
|
}
|
||||||
|
|||||||
@ -9098,6 +9098,11 @@
|
|||||||
"type": "string",
|
"type": "string",
|
||||||
"description": "The type of the payment card",
|
"description": "The type of the payment card",
|
||||||
"nullable": true
|
"nullable": true
|
||||||
|
},
|
||||||
|
"nick_name": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "The nick_name of the card holder",
|
||||||
|
"nullable": true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
@ -62,7 +62,7 @@
|
|||||||
"card_cvc": "737"
|
"card_cvc": "737"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"setup_future_usage": "off_session",
|
"setup_future_usage": "on_session",
|
||||||
"browser_info": {
|
"browser_info": {
|
||||||
"user_agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.110 Safari/537.36",
|
"user_agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.110 Safari/537.36",
|
||||||
"accept_header": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8",
|
"accept_header": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8",
|
||||||
@ -79,14 +79,8 @@
|
|||||||
},
|
},
|
||||||
"url": {
|
"url": {
|
||||||
"raw": "{{baseUrl}}/payments/:id/confirm",
|
"raw": "{{baseUrl}}/payments/:id/confirm",
|
||||||
"host": [
|
"host": ["{{baseUrl}}"],
|
||||||
"{{baseUrl}}"
|
"path": ["payments", ":id", "confirm"],
|
||||||
],
|
|
||||||
"path": [
|
|
||||||
"payments",
|
|
||||||
":id",
|
|
||||||
"confirm"
|
|
||||||
],
|
|
||||||
"variable": [
|
"variable": [
|
||||||
{
|
{
|
||||||
"key": "id",
|
"key": "id",
|
||||||
|
|||||||
Reference in New Issue
Block a user