mirror of
https://github.com/juspay/hyperswitch.git
synced 2025-11-03 13:30:39 +08:00
refactor: Mandate unification (#191)
This commit is contained in:
@ -1,12 +1,15 @@
|
||||
use error_stack::{report, ResultExt};
|
||||
use router_env::{tracing, tracing::instrument};
|
||||
use storage_models::enums as storage_enums;
|
||||
|
||||
use super::payments::helpers;
|
||||
use crate::{
|
||||
core::errors::{self, RouterResponse, StorageErrorExt},
|
||||
db::StorageInterface,
|
||||
routes::AppState,
|
||||
services,
|
||||
types::{
|
||||
self,
|
||||
api::{
|
||||
customers,
|
||||
mandates::{self, MandateResponseExt},
|
||||
@ -79,3 +82,103 @@ pub async fn get_customer_mandates(
|
||||
Ok(services::BachResponse::Json(response_vec))
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn mandate_procedure<F, FData>(
|
||||
state: &AppState,
|
||||
mut resp: types::RouterData<F, FData, types::PaymentsResponseData>,
|
||||
maybe_customer: &Option<storage::Customer>,
|
||||
) -> errors::RouterResult<types::RouterData<F, FData, types::PaymentsResponseData>>
|
||||
where
|
||||
FData: MandateBehaviour,
|
||||
{
|
||||
match resp.request.get_mandate_id() {
|
||||
Some(mandate_id) => {
|
||||
let mandate = state
|
||||
.store
|
||||
.find_mandate_by_merchant_id_mandate_id(resp.merchant_id.as_ref(), mandate_id)
|
||||
.await
|
||||
.change_context(errors::ApiErrorResponse::MandateNotFound)?;
|
||||
let mandate = match mandate.mandate_type {
|
||||
storage_enums::MandateType::SingleUse => state
|
||||
.store
|
||||
.update_mandate_by_merchant_id_mandate_id(
|
||||
&resp.merchant_id,
|
||||
mandate_id,
|
||||
storage::MandateUpdate::StatusUpdate {
|
||||
mandate_status: storage_enums::MandateStatus::Revoked,
|
||||
},
|
||||
)
|
||||
.await
|
||||
.change_context(errors::ApiErrorResponse::MandateNotFound),
|
||||
storage_enums::MandateType::MultiUse => state
|
||||
.store
|
||||
.update_mandate_by_merchant_id_mandate_id(
|
||||
&resp.merchant_id,
|
||||
mandate_id,
|
||||
storage::MandateUpdate::CaptureAmountUpdate {
|
||||
amount_captured: Some(
|
||||
mandate.amount_captured.unwrap_or(0) + resp.request.get_amount(),
|
||||
),
|
||||
},
|
||||
)
|
||||
.await
|
||||
.change_context(errors::ApiErrorResponse::MandateNotFound),
|
||||
}?;
|
||||
|
||||
resp.payment_method_id = Some(mandate.payment_method_id);
|
||||
}
|
||||
None => {
|
||||
if resp.request.get_setup_future_usage().is_some() {
|
||||
let payment_method_id = helpers::call_payment_method(
|
||||
state,
|
||||
&resp.merchant_id,
|
||||
Some(&resp.request.get_payment_method_data()),
|
||||
Some(resp.payment_method),
|
||||
maybe_customer,
|
||||
)
|
||||
.await?
|
||||
.payment_method_id;
|
||||
|
||||
resp.payment_method_id = Some(payment_method_id.clone());
|
||||
let mandate_reference = match resp.response.as_ref().ok() {
|
||||
Some(types::PaymentsResponseData::TransactionResponse {
|
||||
mandate_reference,
|
||||
..
|
||||
}) => mandate_reference.clone(),
|
||||
_ => None,
|
||||
};
|
||||
|
||||
if let Some(new_mandate_data) = helpers::generate_mandate(
|
||||
resp.merchant_id.clone(),
|
||||
resp.connector.clone(),
|
||||
None,
|
||||
maybe_customer,
|
||||
payment_method_id,
|
||||
mandate_reference,
|
||||
) {
|
||||
resp.request
|
||||
.set_mandate_id(new_mandate_data.mandate_id.clone());
|
||||
state
|
||||
.store
|
||||
.insert_mandate(new_mandate_data)
|
||||
.await
|
||||
.map_err(|err| {
|
||||
err.to_duplicate_response(
|
||||
errors::ApiErrorResponse::DuplicateRefundRequest,
|
||||
)
|
||||
})?;
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(resp)
|
||||
}
|
||||
|
||||
pub trait MandateBehaviour {
|
||||
fn get_amount(&self) -> i64;
|
||||
fn get_setup_future_usage(&self) -> Option<storage_models::enums::FutureUsage>;
|
||||
fn get_mandate_id(&self) -> Option<&String>;
|
||||
fn set_mandate_id(&mut self, new_mandate_id: String);
|
||||
fn get_payment_method_data(&self) -> api_models::payments::PaymentMethod;
|
||||
}
|
||||
|
||||
@ -1,11 +1,11 @@
|
||||
use async_trait::async_trait;
|
||||
use error_stack::ResultExt;
|
||||
|
||||
use super::{ConstructFlowSpecificData, Feature};
|
||||
use crate::{
|
||||
core::{
|
||||
errors::{self, ConnectorErrorExt, RouterResult, StorageErrorExt},
|
||||
payments::{self, helpers, transformers, PaymentData},
|
||||
errors::{ConnectorErrorExt, RouterResult},
|
||||
mandate,
|
||||
payments::{self, transformers, PaymentData},
|
||||
},
|
||||
routes::AppState,
|
||||
scheduler::metrics,
|
||||
@ -91,7 +91,7 @@ impl PaymentsAuthorizeRouterData {
|
||||
PaymentsAuthorizeData,
|
||||
PaymentsResponseData,
|
||||
> = connector.connector.get_connector_integration();
|
||||
let mut resp = services::execute_connector_processing_step(
|
||||
let resp = services::execute_connector_processing_step(
|
||||
state,
|
||||
connector_integration,
|
||||
self,
|
||||
@ -99,93 +99,28 @@ impl PaymentsAuthorizeRouterData {
|
||||
)
|
||||
.await
|
||||
.map_err(|error| error.to_payment_failed_response())?;
|
||||
match &self.request.mandate_id {
|
||||
Some(mandate_id) => {
|
||||
let mandate = state
|
||||
.store
|
||||
.find_mandate_by_merchant_id_mandate_id(
|
||||
resp.merchant_id.as_ref(),
|
||||
mandate_id,
|
||||
)
|
||||
.await
|
||||
.change_context(errors::ApiErrorResponse::MandateNotFound)?;
|
||||
let mandate = match mandate.mandate_type {
|
||||
storage_enums::MandateType::SingleUse => state
|
||||
.store
|
||||
.update_mandate_by_merchant_id_mandate_id(
|
||||
&resp.merchant_id,
|
||||
mandate_id,
|
||||
storage::MandateUpdate::StatusUpdate {
|
||||
mandate_status: storage_enums::MandateStatus::Revoked,
|
||||
},
|
||||
)
|
||||
.await
|
||||
.change_context(errors::ApiErrorResponse::MandateNotFound),
|
||||
storage_enums::MandateType::MultiUse => state
|
||||
.store
|
||||
.update_mandate_by_merchant_id_mandate_id(
|
||||
&resp.merchant_id,
|
||||
mandate_id,
|
||||
storage::MandateUpdate::CaptureAmountUpdate {
|
||||
amount_captured: Some(
|
||||
mandate.amount_captured.unwrap_or(0)
|
||||
+ self.request.amount,
|
||||
),
|
||||
},
|
||||
)
|
||||
.await
|
||||
.change_context(errors::ApiErrorResponse::MandateNotFound),
|
||||
}?;
|
||||
|
||||
resp.payment_method_id = Some(mandate.payment_method_id);
|
||||
}
|
||||
None => {
|
||||
if self.request.setup_future_usage.is_some() {
|
||||
let payment_method_id = helpers::call_payment_method(
|
||||
state,
|
||||
&self.merchant_id,
|
||||
Some(&self.request.payment_method_data),
|
||||
Some(self.payment_method),
|
||||
maybe_customer,
|
||||
)
|
||||
.await?
|
||||
.payment_method_id;
|
||||
|
||||
resp.payment_method_id = Some(payment_method_id.clone());
|
||||
|
||||
resp.payment_method_id = Some(payment_method_id.clone());
|
||||
let mandate_reference = match resp.response.as_ref().ok() {
|
||||
Some(types::PaymentsResponseData::TransactionResponse {
|
||||
mandate_reference,
|
||||
..
|
||||
}) => mandate_reference.clone(),
|
||||
_ => None,
|
||||
};
|
||||
|
||||
if let Some(new_mandate_data) = helpers::generate_mandate(
|
||||
self.merchant_id.clone(),
|
||||
self.connector.clone(),
|
||||
None,
|
||||
maybe_customer,
|
||||
payment_method_id,
|
||||
mandate_reference,
|
||||
) {
|
||||
resp.request.mandate_id = Some(new_mandate_data.mandate_id.clone());
|
||||
state.store.insert_mandate(new_mandate_data).await.map_err(
|
||||
|err| {
|
||||
err.to_duplicate_response(
|
||||
errors::ApiErrorResponse::DuplicateRefundRequest,
|
||||
)
|
||||
},
|
||||
)?;
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(resp)
|
||||
Ok(mandate::mandate_procedure(state, resp, maybe_customer).await?)
|
||||
}
|
||||
_ => Ok(self.clone()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl mandate::MandateBehaviour for types::PaymentsAuthorizeData {
|
||||
fn get_amount(&self) -> i64 {
|
||||
self.amount
|
||||
}
|
||||
fn get_mandate_id(&self) -> Option<&String> {
|
||||
self.mandate_id.as_ref()
|
||||
}
|
||||
fn get_payment_method_data(&self) -> api_models::payments::PaymentMethod {
|
||||
self.payment_method_data.clone()
|
||||
}
|
||||
fn get_setup_future_usage(&self) -> Option<storage_models::enums::FutureUsage> {
|
||||
self.setup_future_usage
|
||||
}
|
||||
fn set_mandate_id(&mut self, new_mandate_id: String) {
|
||||
self.mandate_id = Some(new_mandate_id);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,11 +1,11 @@
|
||||
use async_trait::async_trait;
|
||||
use error_stack::ResultExt;
|
||||
|
||||
use super::{ConstructFlowSpecificData, Feature};
|
||||
use crate::{
|
||||
core::{
|
||||
errors::{self, ConnectorErrorExt, RouterResult, StorageErrorExt},
|
||||
payments::{self, helpers, transformers, PaymentData},
|
||||
errors::{ConnectorErrorExt, RouterResult},
|
||||
mandate,
|
||||
payments::{self, transformers, PaymentData},
|
||||
},
|
||||
routes::AppState,
|
||||
services,
|
||||
@ -74,7 +74,7 @@ impl types::VerifyRouterData {
|
||||
types::VerifyRequestData,
|
||||
types::PaymentsResponseData,
|
||||
> = connector.connector.get_connector_integration();
|
||||
let mut resp = services::execute_connector_processing_step(
|
||||
let resp = services::execute_connector_processing_step(
|
||||
state,
|
||||
connector_integration,
|
||||
self,
|
||||
@ -82,60 +82,31 @@ impl types::VerifyRouterData {
|
||||
)
|
||||
.await
|
||||
.map_err(|err| err.to_verify_failed_response())?;
|
||||
|
||||
match &self.request.mandate_id {
|
||||
Some(mandate_id) => {
|
||||
let mandate = state
|
||||
.store
|
||||
.find_mandate_by_merchant_id_mandate_id(&resp.merchant_id, mandate_id)
|
||||
.await
|
||||
.change_context(errors::ApiErrorResponse::MandateNotFound)?;
|
||||
resp.payment_method_id = Some(mandate.payment_method_id);
|
||||
}
|
||||
None => {
|
||||
if self.request.setup_future_usage.is_some() {
|
||||
let payment_method_id = helpers::call_payment_method(
|
||||
state,
|
||||
&self.merchant_id,
|
||||
Some(&self.request.payment_method_data),
|
||||
Some(self.payment_method),
|
||||
maybe_customer,
|
||||
)
|
||||
.await?
|
||||
.payment_method_id;
|
||||
|
||||
resp.payment_method_id = Some(payment_method_id.clone());
|
||||
let mandate_reference = match resp.response.as_ref().ok() {
|
||||
Some(types::PaymentsResponseData::TransactionResponse {
|
||||
mandate_reference,
|
||||
..
|
||||
}) => mandate_reference.clone(),
|
||||
_ => None,
|
||||
};
|
||||
|
||||
if let Some(new_mandate_data) = helpers::generate_mandate(
|
||||
self.merchant_id.clone(),
|
||||
self.connector.clone(),
|
||||
self.request.setup_mandate_details.clone(),
|
||||
maybe_customer,
|
||||
payment_method_id,
|
||||
mandate_reference,
|
||||
) {
|
||||
resp.request.mandate_id = Some(new_mandate_data.mandate_id.clone());
|
||||
state.store.insert_mandate(new_mandate_data).await.map_err(
|
||||
|err| {
|
||||
err.to_duplicate_response(
|
||||
errors::ApiErrorResponse::DuplicateMandate,
|
||||
)
|
||||
},
|
||||
)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(resp)
|
||||
Ok(mandate::mandate_procedure(state, resp, maybe_customer).await?)
|
||||
}
|
||||
_ => Ok(self.clone()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl mandate::MandateBehaviour for types::VerifyRequestData {
|
||||
fn get_amount(&self) -> i64 {
|
||||
0
|
||||
}
|
||||
|
||||
fn get_setup_future_usage(&self) -> Option<storage_models::enums::FutureUsage> {
|
||||
self.setup_future_usage
|
||||
}
|
||||
|
||||
fn get_mandate_id(&self) -> Option<&String> {
|
||||
self.mandate_id.as_ref()
|
||||
}
|
||||
|
||||
fn set_mandate_id(&mut self, new_mandate_id: String) {
|
||||
self.mandate_id = Some(new_mandate_id);
|
||||
}
|
||||
|
||||
fn get_payment_method_data(&self) -> api_models::payments::PaymentMethod {
|
||||
self.payment_method_data.clone()
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user