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:
Amisha Prabhat
2024-02-07 21:39:49 +05:30
committed by GitHub
parent ab6b5ab7b4
commit ef302dd398
9 changed files with 55 additions and 11 deletions

View File

@ -77,6 +77,9 @@ pub struct MandateCardDetails {
pub card_network: Option<api_enums::CardNetwork>,
/// The type of the payment card
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)]

View File

@ -103,6 +103,7 @@ impl Feature<api::Authorize, types::PaymentsAuthorizeData> for types::PaymentsAu
merchant_account,
self.request.payment_method_type,
key_store,
is_mandate,
))
.await?;
Ok(mandate::mandate_procedure(
@ -134,6 +135,7 @@ impl Feature<api::Authorize, types::PaymentsAuthorizeData> for types::PaymentsAu
&merchant_account,
self.request.payment_method_type,
&key_store,
is_mandate,
))
.await;

View File

@ -76,6 +76,9 @@ impl Feature<api::SetupMandate, types::SetupMandateRequestData> for types::Setup
)
.await
.to_setup_mandate_failed_response()?;
let is_mandate = resp.request.setup_mandate_details.is_some();
let pm_id = Box::pin(tokenization::save_payment_method(
state,
connector,
@ -84,6 +87,7 @@ impl Feature<api::SetupMandate, types::SetupMandateRequestData> for types::Setup
merchant_account,
self.request.payment_method_type,
key_store,
is_mandate,
))
.await?;
@ -280,6 +284,8 @@ impl types::SetupMandateRouterData {
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(
state,
connector,
@ -288,6 +294,7 @@ impl types::SetupMandateRouterData {
merchant_account,
payment_method_type,
key_store,
is_mandate,
))
.await?;

View File

@ -8,7 +8,7 @@ use data_models::{
payments::payment_attempt::PaymentAttempt,
};
use diesel_models::ephemeral_key;
use error_stack::{self, ResultExt};
use error_stack::{self, report, ResultExt};
use masking::PeekInterface;
use router_derive::PaymentOperation;
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)?;
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 =
helpers::validate_mandate(request, payments::is_operation_confirm(self))?;

View File

@ -682,6 +682,17 @@ impl<F: Send + Clone, Ctx: PaymentMethodRetrieve> ValidateRequest<F, api::Paymen
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)?;
Ok((

View File

@ -22,6 +22,7 @@ use crate::{
};
#[instrument(skip_all)]
#[allow(clippy::too_many_arguments)]
pub async fn save_payment_method<F: Clone, FData>(
state: &AppState,
connector: &api::ConnectorData,
@ -30,6 +31,7 @@ pub async fn save_payment_method<F: Clone, FData>(
merchant_account: &domain::MerchantAccount,
payment_method_type: Option<storage_enums::PaymentMethodType>,
key_store: &domain::MerchantKeyStore,
is_mandate: bool,
) -> RouterResult<Option<String>>
where
FData: mandate::MandateBehaviour,
@ -62,8 +64,16 @@ where
} else {
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 payment_method_create_request = helpers::get_payment_method_create_request(
Some(&resp.request.get_payment_method_data()),

View File

@ -112,6 +112,7 @@ impl From<api::payment_methods::CardDetailFromLocker> for MandateCardDetails {
card_issuer: card_details_from_locker.card_issuer,
card_network: card_details_from_locker.card_network,
card_type: card_details_from_locker.card_type,
nick_name: card_details_from_locker.nick_name,
}
.into()
}

View File

@ -9098,6 +9098,11 @@
"type": "string",
"description": "The type of the payment card",
"nullable": true
},
"nick_name": {
"type": "string",
"description": "The nick_name of the card holder",
"nullable": true
}
}
},

View File

@ -62,7 +62,7 @@
"card_cvc": "737"
}
},
"setup_future_usage": "off_session",
"setup_future_usage": "on_session",
"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",
"accept_header": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8",
@ -79,14 +79,8 @@
},
"url": {
"raw": "{{baseUrl}}/payments/:id/confirm",
"host": [
"{{baseUrl}}"
],
"path": [
"payments",
":id",
"confirm"
],
"host": ["{{baseUrl}}"],
"path": ["payments", ":id", "confirm"],
"variable": [
{
"key": "id",