Refactor(core): make save_payment_method as post_update_tracker trait function (#4307)

Co-authored-by: Narayan Bhat <narayan.bhat@juspay.in>
Co-authored-by: hyperswitch-bot[bot] <148525504+hyperswitch-bot[bot]@users.noreply.github.com>
This commit is contained in:
Prajjwal Kumar
2024-04-25 13:31:43 +05:30
committed by GitHub
parent 4c793c3c00
commit 5f40eee3fa
20 changed files with 543 additions and 654 deletions

View File

@ -340,156 +340,134 @@ where
.change_context(errors::ApiErrorResponse::MandateUpdateFailed)?;
Ok(resp)
}
pub async fn mandate_procedure<F, FData>(
state: &AppState,
mut resp: types::RouterData<F, FData, types::PaymentsResponseData>,
maybe_customer: &Option<domain::Customer>,
resp: &types::RouterData<F, FData, types::PaymentsResponseData>,
customer_id: &Option<String>,
pm_id: Option<String>,
merchant_connector_id: Option<String>,
storage_scheme: MerchantStorageScheme,
) -> errors::RouterResult<types::RouterData<F, FData, types::PaymentsResponseData>>
) -> errors::RouterResult<Option<String>>
where
FData: MandateBehaviour,
{
match resp.response {
Err(_) => {}
Ok(_) => match resp.request.get_mandate_id() {
Some(mandate_id) => {
if let Some(ref mandate_id) = mandate_id.mandate_id {
let orig_mandate = state
.store
.find_mandate_by_merchant_id_mandate_id(
resp.merchant_id.as_ref(),
mandate_id,
storage_scheme,
)
.await
.to_not_found_response(errors::ApiErrorResponse::MandateNotFound)?;
let mandate = match orig_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,
},
orig_mandate,
storage_scheme,
)
.await
.change_context(errors::ApiErrorResponse::MandateUpdateFailed),
storage_enums::MandateType::MultiUse => state
.store
.update_mandate_by_merchant_id_mandate_id(
&resp.merchant_id,
mandate_id,
storage::MandateUpdate::CaptureAmountUpdate {
amount_captured: Some(
orig_mandate.amount_captured.unwrap_or(0)
+ resp.request.get_amount(),
),
},
orig_mandate,
storage_scheme,
)
.await
.change_context(errors::ApiErrorResponse::MandateUpdateFailed),
}?;
metrics::SUBSEQUENT_MANDATE_PAYMENT.add(
&metrics::CONTEXT,
1,
&[metrics::request::add_attributes(
"connector",
mandate.connector,
)],
);
resp.payment_method_id = Some(mandate.payment_method_id);
}
}
None => {
if resp.request.get_setup_mandate_details().is_some() {
resp.payment_method_id = pm_id.clone();
let (mandate_reference, network_txn_id) = match resp.response.as_ref().ok() {
Some(types::PaymentsResponseData::TransactionResponse {
mandate_reference,
network_txn_id,
..
}) => (mandate_reference.clone(), network_txn_id.clone()),
_ => (None, None),
};
let Ok(ref response) = resp.response else {
return Ok(None);
};
let mandate_ids = mandate_reference
.as_ref()
.map(|md| {
md.encode_to_value()
.change_context(
errors::ApiErrorResponse::MandateSerializationFailed,
)
.map(masking::Secret::new)
})
.transpose()?;
match resp.request.get_mandate_id() {
Some(mandate_id) => {
let Some(ref mandate_id) = mandate_id.mandate_id else {
return Ok(None);
};
let orig_mandate = state
.store
.find_mandate_by_merchant_id_mandate_id(
resp.merchant_id.as_ref(),
mandate_id,
storage_scheme,
)
.await
.to_not_found_response(errors::ApiErrorResponse::MandateNotFound)?;
let mandate = match orig_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,
},
orig_mandate,
storage_scheme,
)
.await
.change_context(errors::ApiErrorResponse::MandateUpdateFailed),
storage_enums::MandateType::MultiUse => state
.store
.update_mandate_by_merchant_id_mandate_id(
&resp.merchant_id,
mandate_id,
storage::MandateUpdate::CaptureAmountUpdate {
amount_captured: Some(
orig_mandate.amount_captured.unwrap_or(0)
+ resp.request.get_amount(),
),
},
orig_mandate,
storage_scheme,
)
.await
.change_context(errors::ApiErrorResponse::MandateUpdateFailed),
}?;
metrics::SUBSEQUENT_MANDATE_PAYMENT.add(
&metrics::CONTEXT,
1,
&[metrics::request::add_attributes(
"connector",
mandate.connector,
)],
);
Ok(Some(mandate_id.clone()))
}
None => {
let Some(_mandate_details) = resp.request.get_setup_mandate_details() else {
return Ok(None);
};
let (mandate_reference, network_txn_id) = match &response {
types::PaymentsResponseData::TransactionResponse {
mandate_reference,
network_txn_id,
..
} => (mandate_reference.clone(), network_txn_id.clone()),
_ => (None, None),
};
if let Some(new_mandate_data) = payment_helper::generate_mandate(
resp.merchant_id.clone(),
resp.payment_id.clone(),
resp.connector.clone(),
resp.request.get_setup_mandate_details().cloned(),
maybe_customer,
pm_id.get_required_value("payment_method_id")?,
mandate_ids,
network_txn_id,
get_insensitive_payment_method_data_if_exists(&resp),
mandate_reference,
merchant_connector_id,
)? {
let connector = new_mandate_data.connector.clone();
logger::debug!("{:?}", new_mandate_data);
resp.request
.set_mandate_id(Some(api_models::payments::MandateIds {
mandate_id: Some(new_mandate_data.mandate_id.clone()),
mandate_reference_id: new_mandate_data
.connector_mandate_ids
.clone()
.map(|ids| {
Some(ids)
.parse_value::<api_models::payments::ConnectorMandateReferenceId>(
"ConnectorMandateId",
)
.change_context(errors::ApiErrorResponse::MandateDeserializationFailed)
})
.transpose()?
.map_or(
new_mandate_data.network_transaction_id.clone().map(|id| {
api_models::payments::MandateReferenceId::NetworkMandateId(
id,
)
}),
|connector_id| Some(api_models::payments::MandateReferenceId::ConnectorMandateId(
api_models::payments::ConnectorMandateReferenceId {
connector_mandate_id: connector_id.connector_mandate_id,
payment_method_id: connector_id.payment_method_id,
update_history:None,
let mandate_ids = mandate_reference
.as_ref()
.map(|md| {
md.encode_to_value()
.change_context(errors::ApiErrorResponse::MandateSerializationFailed)
.map(masking::Secret::new)
})
.transpose()?;
}
)))
}));
state
.store
.insert_mandate(new_mandate_data, storage_scheme)
.await
.to_duplicate_response(errors::ApiErrorResponse::DuplicateMandate)?;
metrics::MANDATE_COUNT.add(
&metrics::CONTEXT,
1,
&[metrics::request::add_attributes("connector", connector)],
);
};
}
}
},
let Some(new_mandate_data) = payment_helper::generate_mandate(
resp.merchant_id.clone(),
resp.payment_id.clone(),
resp.connector.clone(),
resp.request.get_setup_mandate_details().cloned(),
customer_id,
pm_id.get_required_value("payment_method_id")?,
mandate_ids,
network_txn_id,
get_insensitive_payment_method_data_if_exists(resp),
mandate_reference,
merchant_connector_id,
)?
else {
return Ok(None);
};
let connector = new_mandate_data.connector.clone();
logger::debug!("{:?}", new_mandate_data);
let res_mandate_id = new_mandate_data.mandate_id.clone();
state
.store
.insert_mandate(new_mandate_data, storage_scheme)
.await
.to_duplicate_response(errors::ApiErrorResponse::DuplicateMandate)?;
metrics::MANDATE_COUNT.add(
&metrics::CONTEXT,
1,
&[metrics::request::add_attributes("connector", connector)],
);
Ok(Some(res_mandate_id))
}
}
Ok(resp)
}
#[instrument(skip(state))]

View File

@ -119,7 +119,7 @@ where
// To perform router related operation for PaymentResponse
PaymentResponse: Operation<F, FData, Ctx>,
FData: Send + Sync,
FData: Send + Sync + Clone,
Ctx: PaymentMethodRetrieve,
{
let operation: BoxedOperation<'_, F, Req, Ctx> = Box::new(operation);
@ -273,11 +273,11 @@ where
req_state,
&merchant_account,
&key_store,
connector,
connector.clone(),
&operation,
&mut payment_data,
&customer,
call_connector_action,
call_connector_action.clone(),
&validate_result,
schedule_time,
header_payload,
@ -294,6 +294,18 @@ where
external_latency = router_data.external_latency;
//add connector http status code metrics
add_connector_http_status_code_metrics(connector_http_status_code);
operation
.to_post_update_tracker()?
.save_pm_and_mandate(
state,
&router_data,
&merchant_account,
&key_store,
&mut payment_data,
)
.await?;
operation
.to_post_update_tracker()?
.update_tracker(
@ -333,7 +345,7 @@ where
&operation,
&mut payment_data,
&customer,
call_connector_action,
call_connector_action.clone(),
&validate_result,
schedule_time,
header_payload,
@ -361,7 +373,7 @@ where
req_state,
&mut payment_data,
connectors,
connector_data,
connector_data.clone(),
router_data,
&merchant_account,
&key_store,
@ -383,6 +395,18 @@ where
external_latency = router_data.external_latency;
//add connector http status code metrics
add_connector_http_status_code_metrics(connector_http_status_code);
operation
.to_post_update_tracker()?
.save_pm_and_mandate(
state,
&router_data,
&merchant_account,
&key_store,
&mut payment_data,
)
.await?;
operation
.to_post_update_tracker()?
.update_tracker(
@ -697,7 +721,7 @@ pub async fn payments_core<F, Res, Req, Op, FData, Ctx>(
) -> RouterResponse<Res>
where
F: Send + Clone + Sync,
FData: Send + Sync,
FData: Send + Sync + Clone,
Op: Operation<F, Req, Ctx> + Send + Sync + Clone,
Req: Debug + Authenticate + Clone,
Res: transformers::ToResponse<PaymentData<F>, Op>,
@ -1531,16 +1555,7 @@ where
// and rely on previous status set in router_data
router_data.status = payment_data.payment_attempt.status;
router_data
.decide_flows(
state,
&connector,
customer,
call_connector_action,
merchant_account,
connector_request,
key_store,
payment_data.payment_intent.profile_id.clone(),
)
.decide_flows(state, &connector, call_connector_action, connector_request)
.await
} else {
Ok(router_data)
@ -1663,12 +1678,8 @@ where
let res = router_data.decide_flows(
state,
&session_connector_data.connector,
customer,
CallConnectorAction::Trigger,
merchant_account,
None,
key_store,
payment_data.payment_intent.profile_id.clone(),
);
join_handlers.push(res);

View File

@ -44,12 +44,8 @@ pub trait Feature<F, T> {
self,
state: &AppState,
connector: &api::ConnectorData,
maybe_customer: &Option<domain::Customer>,
call_connector_action: payments::CallConnectorAction,
merchant_account: &domain::MerchantAccount,
connector_request: Option<services::Request>,
key_store: &domain::MerchantKeyStore,
profile_id: Option<String>,
) -> RouterResult<Self>
where
Self: Sized,

View File

@ -49,12 +49,8 @@ impl Feature<api::Approve, types::PaymentsApproveData>
self,
_state: &AppState,
_connector: &api::ConnectorData,
_customer: &Option<domain::Customer>,
_call_connector_action: payments::CallConnectorAction,
_merchant_account: &domain::MerchantAccount,
_connector_request: Option<services::Request>,
_key_store: &domain::MerchantKeyStore,
_profile_id: Option<String>,
) -> RouterResult<Self> {
Err(ApiErrorResponse::NotImplemented {
message: NotImplementedMessage::Reason("Flow not supported".to_string()),

View File

@ -61,12 +61,8 @@ impl Feature<api::Authorize, types::PaymentsAuthorizeData> for types::PaymentsAu
mut self,
state: &AppState,
connector: &api::ConnectorData,
maybe_customer: &Option<domain::Customer>,
call_connector_action: payments::CallConnectorAction,
merchant_account: &domain::MerchantAccount,
connector_request: Option<services::Request>,
key_store: &domain::MerchantKeyStore,
profile_id: Option<String>,
) -> RouterResult<Self> {
let connector_integration: services::BoxedConnectorIntegration<
'_,
@ -78,7 +74,7 @@ impl Feature<api::Authorize, types::PaymentsAuthorizeData> for types::PaymentsAu
if self.should_proceed_with_authorize() {
self.decide_authentication_type();
logger::debug!(auth_type=?self.auth_type);
let mut resp = services::execute_connector_processing_step(
let resp = services::execute_connector_processing_step(
state,
connector_integration,
&self,
@ -89,98 +85,7 @@ impl Feature<api::Authorize, types::PaymentsAuthorizeData> for types::PaymentsAu
.to_payment_failed_response()?;
metrics::PAYMENT_COUNT.add(&metrics::CONTEXT, 1, &[]); // Metrics
let is_mandate = resp.request.setup_mandate_details.is_some();
if is_mandate {
let (payment_method_id, payment_method_status) =
Box::pin(tokenization::save_payment_method(
state,
connector,
resp.to_owned(),
maybe_customer,
merchant_account,
self.request.payment_method_type,
key_store,
Some(resp.request.amount),
Some(resp.request.currency),
profile_id,
))
.await?;
resp.payment_method_id = payment_method_id.clone();
resp.payment_method_status = payment_method_status;
Ok(mandate::mandate_procedure(
state,
resp,
maybe_customer,
payment_method_id,
connector.merchant_connector_id.clone(),
merchant_account.storage_scheme,
)
.await?)
} else {
let response = resp.clone();
logger::info!("Call to save_payment_method in locker");
let pm = Box::pin(tokenization::save_payment_method(
state,
connector,
response,
maybe_customer,
merchant_account,
self.request.payment_method_type,
key_store,
Some(resp.request.amount),
Some(resp.request.currency),
profile_id,
))
.await;
match pm {
Ok((payment_method_id, payment_method_status)) => {
resp.payment_method_id = payment_method_id.clone();
resp.payment_method_status = payment_method_status;
}
Err(err) => logger::error!("Save pm to locker failed : {err:?}"),
}
Ok(resp)
}
// Async locker code (Commenting out the code for near future refactors)
// logger::info!("Call to save_payment_method in locker");
// let _task_handle = tokio::spawn(
// async move {
// logger::info!("Starting async call to save_payment_method in locker");
//
// let result = Box::pin(tokenization::save_payment_method(
// &state,
// &connector,
// response,
// &maybe_customer,
// &merchant_account,
// self.request.payment_method_type,
// &key_store,
// Some(resp.request.amount),
// Some(resp.request.currency),
// ))
// .await;
//
// if let Err(err) = result {
// logger::error!(
// "Asynchronously saving card in locker failed : {:?}",
// err
// );
// }
// }
// .in_current_span(),
// );
//
// Ok(resp)
// }
Ok(resp)
} else {
Ok(self.clone())
}

View File

@ -48,12 +48,8 @@ impl Feature<api::Void, types::PaymentsCancelData>
self,
state: &AppState,
connector: &api::ConnectorData,
_customer: &Option<domain::Customer>,
call_connector_action: payments::CallConnectorAction,
_merchant_account: &domain::MerchantAccount,
connector_request: Option<services::Request>,
_key_store: &domain::MerchantKeyStore,
_profile_id: Option<String>,
) -> RouterResult<Self> {
metrics::PAYMENT_CANCEL_COUNT.add(
&metrics::CONTEXT,

View File

@ -49,12 +49,8 @@ impl Feature<api::Capture, types::PaymentsCaptureData>
self,
state: &AppState,
connector: &api::ConnectorData,
_customer: &Option<domain::Customer>,
call_connector_action: payments::CallConnectorAction,
_merchant_account: &domain::MerchantAccount,
connector_request: Option<services::Request>,
_key_store: &domain::MerchantKeyStore,
_profile_id: Option<String>,
) -> RouterResult<Self> {
let connector_integration: services::BoxedConnectorIntegration<
'_,

View File

@ -63,12 +63,8 @@ impl Feature<api::CompleteAuthorize, types::CompleteAuthorizeData>
mut self,
state: &AppState,
connector: &api::ConnectorData,
_customer: &Option<domain::Customer>,
call_connector_action: payments::CallConnectorAction,
_merchant_account: &domain::MerchantAccount,
connector_request: Option<services::Request>,
_key_store: &domain::MerchantKeyStore,
_profile_id: Option<String>,
) -> RouterResult<Self> {
let connector_integration: services::BoxedConnectorIntegration<
'_,

View File

@ -56,12 +56,8 @@ impl Feature<api::IncrementalAuthorization, types::PaymentsIncrementalAuthorizat
self,
state: &AppState,
connector: &api::ConnectorData,
_customer: &Option<domain::Customer>,
call_connector_action: payments::CallConnectorAction,
_merchant_account: &domain::MerchantAccount,
connector_request: Option<services::Request>,
_key_store: &domain::MerchantKeyStore,
_profile_id: Option<String>,
) -> RouterResult<Self> {
let connector_integration: services::BoxedConnectorIntegration<
'_,

View File

@ -52,12 +52,8 @@ impl Feature<api::PSync, types::PaymentsSyncData>
mut self,
state: &AppState,
connector: &api::ConnectorData,
_customer: &Option<domain::Customer>,
call_connector_action: payments::CallConnectorAction,
_merchant_account: &domain::MerchantAccount,
connector_request: Option<services::Request>,
_key_store: &domain::MerchantKeyStore,
_profile_id: Option<String>,
) -> RouterResult<Self> {
let connector_integration: services::BoxedConnectorIntegration<
'_,

View File

@ -48,12 +48,8 @@ impl Feature<api::Reject, types::PaymentsRejectData>
self,
_state: &AppState,
_connector: &api::ConnectorData,
_customer: &Option<domain::Customer>,
_call_connector_action: payments::CallConnectorAction,
_merchant_account: &domain::MerchantAccount,
_connector_request: Option<services::Request>,
_key_store: &domain::MerchantKeyStore,
_profile_id: Option<String>,
) -> RouterResult<Self> {
Err(ApiErrorResponse::NotImplemented {
message: NotImplementedMessage::Reason("Flow not supported".to_string()),

View File

@ -53,12 +53,8 @@ impl Feature<api::Session, types::PaymentsSessionData> for types::PaymentsSessio
self,
state: &routes::AppState,
connector: &api::ConnectorData,
customer: &Option<domain::Customer>,
call_connector_action: payments::CallConnectorAction,
_merchant_account: &domain::MerchantAccount,
_connector_request: Option<services::Request>,
_key_store: &domain::MerchantKeyStore,
_profile_id: Option<String>,
) -> RouterResult<Self> {
metrics::SESSION_TOKEN_CREATED.add(
&metrics::CONTEXT,
@ -68,14 +64,8 @@ impl Feature<api::Session, types::PaymentsSessionData> for types::PaymentsSessio
connector.connector_name.to_string(),
)],
);
self.decide_flow(
state,
connector,
customer,
Some(true),
call_connector_action,
)
.await
self.decide_flow(state, connector, Some(true), call_connector_action)
.await
}
async fn add_access_token<'a>(
@ -521,7 +511,6 @@ impl types::PaymentsSessionRouterData {
&'b self,
state: &'a routes::AppState,
connector: &api::ConnectorData,
_customer: &Option<domain::Customer>,
_confirm: Option<bool>,
call_connector_action: payments::CallConnectorAction,
) -> RouterResult<Self> {

View File

@ -1,14 +1,10 @@
use api_models::enums::{PaymentMethod, PaymentMethodType};
use async_trait::async_trait;
use error_stack::ResultExt;
use super::{ConstructFlowSpecificData, Feature};
use crate::{
configs::settings,
core::{
errors::{self, ConnectorErrorExt, RouterResult, StorageErrorExt},
errors::{self, ConnectorErrorExt, RouterResult},
mandate,
payment_methods::cards,
payments::{
self, access_token, customers, helpers, tokenization, transformers, PaymentData,
},
@ -57,76 +53,26 @@ impl Feature<api::SetupMandate, types::SetupMandateRequestData> for types::Setup
self,
state: &AppState,
connector: &api::ConnectorData,
maybe_customer: &Option<domain::Customer>,
call_connector_action: payments::CallConnectorAction,
merchant_account: &domain::MerchantAccount,
connector_request: Option<services::Request>,
key_store: &domain::MerchantKeyStore,
profile_id: Option<String>,
) -> RouterResult<Self> {
if let Some(mandate_id) = self
.request
.setup_mandate_details
.as_ref()
.and_then(|mandate_data| mandate_data.update_mandate_id.clone())
{
Box::pin(self.update_mandate_flow(
state,
merchant_account,
mandate_id,
connector,
key_store,
call_connector_action,
&state.conf.mandates.update_mandate_supported,
connector_request,
maybe_customer,
profile_id,
))
.await
} else {
let connector_integration: services::BoxedConnectorIntegration<
'_,
api::SetupMandate,
types::SetupMandateRequestData,
types::PaymentsResponseData,
> = connector.connector.get_connector_integration();
let connector_integration: services::BoxedConnectorIntegration<
'_,
api::SetupMandate,
types::SetupMandateRequestData,
types::PaymentsResponseData,
> = connector.connector.get_connector_integration();
let mut resp = services::execute_connector_processing_step(
state,
connector_integration,
&self,
call_connector_action.clone(),
connector_request,
)
.await
.to_setup_mandate_failed_response()?;
let (pm_id, payment_method_status) = Box::pin(tokenization::save_payment_method(
state,
connector,
resp.to_owned(),
maybe_customer,
merchant_account,
self.request.payment_method_type,
key_store,
resp.request.amount,
Some(resp.request.currency),
profile_id,
))
.await?;
resp.payment_method_id = pm_id.clone();
resp.payment_method_status = payment_method_status;
mandate::mandate_procedure(
state,
resp,
maybe_customer,
pm_id,
connector.merchant_connector_id.clone(),
merchant_account.storage_scheme,
)
.await
}
let resp = services::execute_connector_processing_step(
state,
connector_integration,
&self,
call_connector_action.clone(),
connector_request,
)
.await
.to_setup_mandate_failed_response()?;
Ok(resp)
}
async fn add_access_token<'a>(
@ -210,222 +156,6 @@ impl TryFrom<types::SetupMandateRequestData> for types::ConnectorCustomerData {
}
}
#[allow(clippy::too_many_arguments)]
impl types::SetupMandateRouterData {
pub async fn decide_flow<'a, 'b>(
&'b self,
state: &'a AppState,
connector: &api::ConnectorData,
maybe_customer: &Option<domain::Customer>,
confirm: Option<bool>,
call_connector_action: payments::CallConnectorAction,
merchant_account: &domain::MerchantAccount,
key_store: &domain::MerchantKeyStore,
profile_id: Option<String>,
) -> RouterResult<Self> {
match confirm {
Some(true) => {
let connector_integration: services::BoxedConnectorIntegration<
'_,
api::SetupMandate,
types::SetupMandateRequestData,
types::PaymentsResponseData,
> = connector.connector.get_connector_integration();
let mut resp = services::execute_connector_processing_step(
state,
connector_integration,
self,
call_connector_action,
None,
)
.await
.to_setup_mandate_failed_response()?;
let payment_method_type = self.request.payment_method_type;
let (pm_id, payment_method_status) = Box::pin(tokenization::save_payment_method(
state,
connector,
resp.to_owned(),
maybe_customer,
merchant_account,
payment_method_type,
key_store,
resp.request.amount,
Some(resp.request.currency),
profile_id,
))
.await?;
resp.payment_method_id = pm_id.clone();
resp.payment_method_status = payment_method_status;
Ok(mandate::mandate_procedure(
state,
resp,
maybe_customer,
pm_id,
connector.merchant_connector_id.clone(),
merchant_account.storage_scheme,
)
.await?)
}
_ => Ok(self.clone()),
}
}
async fn update_mandate_flow(
self,
state: &AppState,
merchant_account: &domain::MerchantAccount,
mandate_id: String,
connector: &api::ConnectorData,
key_store: &domain::MerchantKeyStore,
call_connector_action: payments::CallConnectorAction,
supported_connectors_for_update_mandate: &settings::SupportedPaymentMethodsForMandate,
connector_request: Option<services::Request>,
maybe_customer: &Option<domain::Customer>,
profile_id: Option<String>,
) -> RouterResult<Self> {
let payment_method_type = self.request.payment_method_type;
let payment_method = self.request.payment_method_data.get_payment_method();
let supported_connectors_config = payment_method.zip(payment_method_type).map_or_else(
|| {
if payment_method == Some(PaymentMethod::Card) {
cards::filter_pm_based_on_update_mandate_support_for_connector(
supported_connectors_for_update_mandate,
&PaymentMethod::Card,
&PaymentMethodType::Credit,
connector.connector_name,
) && cards::filter_pm_based_on_update_mandate_support_for_connector(
supported_connectors_for_update_mandate,
&PaymentMethod::Card,
&PaymentMethodType::Debit,
connector.connector_name,
)
} else {
false
}
},
|(pm, pmt)| {
cards::filter_pm_based_on_update_mandate_support_for_connector(
supported_connectors_for_update_mandate,
&pm,
&pmt,
connector.connector_name,
)
},
);
if supported_connectors_config {
let connector_integration: services::BoxedConnectorIntegration<
'_,
api::SetupMandate,
types::SetupMandateRequestData,
types::PaymentsResponseData,
> = connector.connector.get_connector_integration();
let resp = services::execute_connector_processing_step(
state,
connector_integration,
&self,
call_connector_action.clone(),
connector_request,
)
.await
.to_setup_mandate_failed_response()?;
let pm_id = Box::pin(tokenization::save_payment_method(
state,
connector,
resp.to_owned(),
maybe_customer,
merchant_account,
self.request.payment_method_type,
key_store,
resp.request.amount,
Some(resp.request.currency),
profile_id,
))
.await?
.0;
let mandate = state
.store
.find_mandate_by_merchant_id_mandate_id(
&merchant_account.merchant_id,
&mandate_id,
merchant_account.storage_scheme,
)
.await
.to_not_found_response(errors::ApiErrorResponse::MandateNotFound)?;
let profile_id = mandate::helpers::get_profile_id_for_mandate(
state,
merchant_account,
mandate.clone(),
)
.await?;
match resp.response {
Ok(types::PaymentsResponseData::TransactionResponse { .. }) => {
let connector_integration: services::BoxedConnectorIntegration<
'_,
types::api::MandateRevoke,
types::MandateRevokeRequestData,
types::MandateRevokeResponseData,
> = connector.connector.get_connector_integration();
let merchant_connector_account = helpers::get_merchant_connector_account(
state,
&merchant_account.merchant_id,
None,
key_store,
&profile_id,
&mandate.connector,
mandate.merchant_connector_id.as_ref(),
)
.await?;
let router_data = mandate::utils::construct_mandate_revoke_router_data(
merchant_connector_account,
merchant_account,
mandate.clone(),
)
.await?;
let _response = services::execute_connector_processing_step(
state,
connector_integration,
&router_data,
call_connector_action,
None,
)
.await
.change_context(errors::ApiErrorResponse::InternalServerError)?;
// TODO:Add the revoke mandate task to process tracker
mandate::update_mandate_procedure(
state,
resp,
mandate,
&merchant_account.merchant_id,
pm_id,
merchant_account.storage_scheme,
)
.await
}
Ok(_) => Err(errors::ApiErrorResponse::InternalServerError)
.attach_printable("Unexpected response received")?,
Err(_) => Ok(resp),
}
} else {
Err(errors::ApiErrorResponse::PreconditionFailed {
message: format!(
"Update Mandate flow not implemented for the connector {:?}",
connector.connector_name
),
}
.into())
}
}
}
impl mandate::MandateBehaviour for types::SetupMandateRequestData {
fn get_amount(&self) -> i64 {
0

View File

@ -1247,7 +1247,7 @@ pub(crate) async fn get_payment_method_create_request(
payment_method_data: Option<&domain::PaymentMethodData>,
payment_method: Option<storage_enums::PaymentMethod>,
payment_method_type: Option<storage_enums::PaymentMethodType>,
customer: &domain::Customer,
customer_id: &Option<String>,
billing_name: Option<masking::Secret<String>>,
) -> RouterResult<api::PaymentMethodCreate> {
match payment_method_data {
@ -1265,7 +1265,6 @@ pub(crate) async fn get_payment_method_create_request(
card_issuer: card.card_issuer.clone(),
card_type: card.card_type.clone(),
};
let customer_id = customer.customer_id.clone();
let payment_method_request = api::PaymentMethodCreate {
payment_method: Some(payment_method),
payment_method_type,
@ -1277,7 +1276,7 @@ pub(crate) async fn get_payment_method_create_request(
wallet: None,
card: Some(card_detail),
metadata: None,
customer_id: Some(customer_id),
customer_id: customer_id.clone(),
card_network: card
.card_network
.as_ref()
@ -1299,7 +1298,7 @@ pub(crate) async fn get_payment_method_create_request(
wallet: None,
card: None,
metadata: None,
customer_id: Some(customer.customer_id.to_owned()),
customer_id: customer_id.clone(),
card_network: None,
client_secret: None,
payment_method_data: None,
@ -2618,7 +2617,7 @@ pub fn generate_mandate(
payment_id: String,
connector: String,
setup_mandate_details: Option<MandateData>,
customer: &Option<domain::Customer>,
customer_id: &Option<String>,
payment_method_id: String,
connector_mandate_id: Option<pii::SecretSerdeValue>,
network_txn_id: Option<String>,
@ -2626,8 +2625,8 @@ pub fn generate_mandate(
mandate_reference: Option<MandateReference>,
merchant_connector_id: Option<String>,
) -> CustomResult<Option<storage::MandateNew>, errors::ApiErrorResponse> {
match (setup_mandate_details, customer) {
(Some(data), Some(cus)) => {
match (setup_mandate_details, customer_id) {
(Some(data), Some(cus_id)) => {
let mandate_id = utils::generate_id(consts::ID_LENGTH, "man");
// The construction of the mandate new must be visible
@ -2638,7 +2637,7 @@ pub fn generate_mandate(
.get_required_value("customer_acceptance")?;
new_mandate
.set_mandate_id(mandate_id)
.set_customer_id(cus.customer_id.clone())
.set_customer_id(cus_id.clone())
.set_merchant_id(merchant_id)
.set_original_payment_id(Some(payment_id))
.set_payment_method_id(payment_method_id)

View File

@ -212,7 +212,7 @@ pub trait UpdateTracker<F, D, Req, Ctx: PaymentMethodRetrieve>: Send {
}
#[async_trait]
pub trait PostUpdateTracker<F, D, R>: Send {
pub trait PostUpdateTracker<F, D, R: Send>: Send {
async fn update_tracker<'b>(
&'b self,
db: &'b AppState,
@ -222,7 +222,21 @@ pub trait PostUpdateTracker<F, D, R>: Send {
storage_scheme: enums::MerchantStorageScheme,
) -> RouterResult<D>
where
F: 'b + Send;
F: 'b + Send + Sync;
async fn save_pm_and_mandate<'b>(
&self,
_state: &AppState,
_resp: &types::RouterData<F, R, PaymentsResponseData>,
_merchant_account: &domain::MerchantAccount,
_key_store: &domain::MerchantKeyStore,
_payment_data: &mut PaymentData<F>,
) -> CustomResult<(), errors::ApiErrorResponse>
where
F: 'b + Clone + Send + Sync,
{
Ok(())
}
}
#[async_trait]

View File

@ -15,7 +15,7 @@ use super::{Operation, PostUpdateTracker};
use crate::{
connector::utils::PaymentResponseRouterData,
core::{
errors::{self, RouterResult, StorageErrorExt},
errors::{self, CustomResult, RouterResult, StorageErrorExt},
mandate,
payment_methods::{self, PaymentMethodRetrieve},
payments::{
@ -24,6 +24,7 @@ use crate::{
self as payments_helpers,
update_additional_payment_data_with_connector_response_pm_data,
},
tokenization,
types::MultipleCaptureData,
PaymentData,
},
@ -31,7 +32,7 @@ use crate::{
},
routes::{metrics, AppState},
types::{
self, api,
self, api, domain,
storage::{self, enums},
transformers::{ForeignFrom, ForeignTryFrom},
CaptureSyncResponse, ErrorResponse,
@ -42,12 +43,12 @@ use crate::{
#[derive(Debug, Clone, Copy, router_derive::PaymentOperation)]
#[operation(
operations = "post_update_tracker",
flow = "sync_data, authorize_data, cancel_data, capture_data, complete_authorize_data, approve_data, reject_data, setup_mandate_data, session_data,incremental_authorization_data"
flow = "sync_data, cancel_data, authorize_data, capture_data, complete_authorize_data, approve_data, reject_data, setup_mandate_data, session_data,incremental_authorization_data"
)]
pub struct PaymentResponse;
#[async_trait]
impl<F: Clone> PostUpdateTracker<F, PaymentData<F>, types::PaymentsAuthorizeData>
impl<F: Send + Clone> PostUpdateTracker<F, PaymentData<F>, types::PaymentsAuthorizeData>
for PaymentResponse
{
async fn update_tracker<'b>(
@ -63,7 +64,7 @@ impl<F: Clone> PostUpdateTracker<F, PaymentData<F>, types::PaymentsAuthorizeData
storage_scheme: enums::MerchantStorageScheme,
) -> RouterResult<PaymentData<F>>
where
F: 'b + Send,
F: 'b,
{
payment_data.mandate_id = payment_data
.mandate_id
@ -80,6 +81,151 @@ impl<F: Clone> PostUpdateTracker<F, PaymentData<F>, types::PaymentsAuthorizeData
Ok(payment_data)
}
async fn save_pm_and_mandate<'b>(
&self,
state: &AppState,
resp: &types::RouterData<F, types::PaymentsAuthorizeData, types::PaymentsResponseData>,
merchant_account: &domain::MerchantAccount,
key_store: &domain::MerchantKeyStore,
payment_data: &mut PaymentData<F>,
) -> CustomResult<(), errors::ApiErrorResponse>
where
F: 'b + Clone + Send + Sync,
{
let customer_id = payment_data.payment_intent.customer_id.clone();
let save_payment_data = tokenization::SavePaymentMethodData::from(resp);
let profile_id = payment_data.payment_intent.profile_id.clone();
let connector_name = payment_data
.payment_attempt
.connector
.clone()
.ok_or_else(|| {
logger::error!("Missing required Param connector_name");
errors::ApiErrorResponse::MissingRequiredField {
field_name: "connector_name",
}
})?;
let merchant_connector_id = payment_data.payment_attempt.merchant_connector_id.clone();
let billing_name = resp
.address
.get_payment_method_billing()
.and_then(|billing_details| billing_details.address.as_ref())
.and_then(|address| address.get_optional_full_name());
let save_payment_call_future = Box::pin(tokenization::save_payment_method(
state,
connector_name.clone(),
merchant_connector_id.clone(),
save_payment_data,
customer_id.clone(),
merchant_account,
resp.request.payment_method_type,
key_store,
Some(resp.request.amount),
Some(resp.request.currency),
profile_id,
billing_name.clone(),
));
let is_connector_mandate = resp.request.customer_acceptance.is_some()
&& matches!(
resp.request.setup_future_usage,
Some(enums::FutureUsage::OffSession)
);
let is_legacy_mandate = resp.request.setup_mandate_details.is_some()
&& matches!(
resp.request.setup_future_usage,
Some(enums::FutureUsage::OffSession)
);
if is_legacy_mandate {
// Mandate is created on the application side and at the connector.
let (payment_method_id, _payment_method_status) = save_payment_call_future.await?;
let mandate_id = mandate::mandate_procedure(
state,
resp,
&customer_id.clone(),
payment_method_id.clone(),
merchant_connector_id.clone(),
merchant_account.storage_scheme,
)
.await?;
payment_data.payment_attempt.payment_method_id = payment_method_id;
payment_data.payment_attempt.mandate_id = mandate_id;
Ok(())
} else if is_connector_mandate {
// The mandate is created on connector's end.
let (payment_method_id, _payment_method_status) = save_payment_call_future.await?;
payment_data.payment_attempt.payment_method_id = payment_method_id;
Ok(())
} else {
// Save card flow
let save_payment_data = tokenization::SavePaymentMethodData::from(resp);
let merchant_account = merchant_account.clone();
let key_store = key_store.clone();
let state = state.clone();
let customer_id = payment_data.payment_intent.customer_id.clone();
let profile_id = payment_data.payment_intent.profile_id.clone();
let merchant_connector_id = payment_data.payment_attempt.merchant_connector_id.clone();
let payment_attempt = payment_data.payment_attempt.clone();
let amount = resp.request.amount;
let currency = resp.request.currency;
let payment_method_type = resp.request.payment_method_type;
let storage_scheme = merchant_account.clone().storage_scheme;
logger::info!("Call to save_payment_method in locker");
let _task_handle = tokio::spawn(
async move {
logger::info!("Starting async call to save_payment_method in locker");
let result = Box::pin(tokenization::save_payment_method(
&state,
connector_name,
merchant_connector_id,
save_payment_data,
customer_id,
&merchant_account,
payment_method_type,
&key_store,
Some(amount),
Some(currency),
profile_id,
billing_name,
))
.await;
if let Err(err) = result {
logger::error!("Asynchronously saving card in locker failed : {:?}", err);
} else if let Ok((payment_method_id, _pm_status)) = result {
let payment_attempt_update =
storage::PaymentAttemptUpdate::PaymentMethodDetailsUpdate {
payment_method_id,
updated_by: storage_scheme.clone().to_string(),
};
let respond = state
.store
.update_payment_attempt_with_attempt_id(
payment_attempt,
payment_attempt_update,
storage_scheme,
)
.await;
if let Err(err) = respond {
logger::error!("Error updating payment attempt: {:?}", err);
};
}
}
.in_current_span(),
);
Ok(())
}
}
}
#[async_trait]
@ -234,6 +380,28 @@ impl<F: Clone> PostUpdateTracker<F, PaymentData<F>, types::PaymentsSyncData> for
))
.await
}
async fn save_pm_and_mandate<'b>(
&self,
state: &AppState,
resp: &types::RouterData<F, types::PaymentsSyncData, types::PaymentsResponseData>,
merchant_account: &domain::MerchantAccount,
_key_store: &domain::MerchantKeyStore,
payment_data: &mut PaymentData<F>,
) -> CustomResult<(), errors::ApiErrorResponse>
where
F: 'b + Clone + Send + Sync,
{
update_payment_method_status_and_ntid(
state,
payment_data,
resp.status,
resp.response.clone(),
merchant_account.storage_scheme,
)
.await?;
Ok(())
}
}
#[async_trait]
@ -411,6 +579,67 @@ impl<F: Clone> PostUpdateTracker<F, PaymentData<F>, types::SetupMandateRequestDa
Ok(payment_data)
}
async fn save_pm_and_mandate<'b>(
&self,
state: &AppState,
resp: &types::RouterData<F, types::SetupMandateRequestData, types::PaymentsResponseData>,
merchant_account: &domain::MerchantAccount,
key_store: &domain::MerchantKeyStore,
payment_data: &mut PaymentData<F>,
) -> CustomResult<(), errors::ApiErrorResponse>
where
F: 'b + Clone + Send + Sync,
{
let billing_name = resp
.address
.get_payment_method_billing()
.and_then(|billing_details| billing_details.address.as_ref())
.and_then(|address| address.get_optional_full_name());
let save_payment_data = tokenization::SavePaymentMethodData::from(resp);
let customer_id = payment_data.payment_intent.customer_id.clone();
let profile_id = payment_data.payment_intent.profile_id.clone();
let connector_name = payment_data
.payment_attempt
.connector
.clone()
.ok_or_else(|| {
logger::error!("Missing required Param connector_name");
errors::ApiErrorResponse::MissingRequiredField {
field_name: "connector_name",
}
})?;
let merchant_connector_id = payment_data.payment_attempt.merchant_connector_id.clone();
let (payment_method_id, _payment_method_status) =
Box::pin(tokenization::save_payment_method(
state,
connector_name,
merchant_connector_id.clone(),
save_payment_data,
customer_id.clone(),
merchant_account,
resp.request.payment_method_type,
key_store,
resp.request.amount,
Some(resp.request.currency),
profile_id,
billing_name,
))
.await?;
let mandate_id = mandate::mandate_procedure(
state,
resp,
&customer_id,
payment_method_id.clone(),
merchant_connector_id.clone(),
merchant_account.storage_scheme,
)
.await?;
payment_data.payment_attempt.payment_method_id = payment_method_id;
payment_data.payment_attempt.mandate_id = mandate_id;
Ok(())
}
}
#[async_trait]
@ -437,6 +666,28 @@ impl<F: Clone> PostUpdateTracker<F, PaymentData<F>, types::CompleteAuthorizeData
))
.await
}
async fn save_pm_and_mandate<'b>(
&self,
state: &AppState,
resp: &types::RouterData<F, types::CompleteAuthorizeData, types::PaymentsResponseData>,
merchant_account: &domain::MerchantAccount,
_key_store: &domain::MerchantKeyStore,
payment_data: &mut PaymentData<F>,
) -> CustomResult<(), errors::ApiErrorResponse>
where
F: 'b + Clone + Send + Sync,
{
update_payment_method_status_and_ntid(
state,
payment_data,
resp.status,
resp.response.clone(),
merchant_account.storage_scheme,
)
.await?;
Ok(())
}
}
#[instrument(skip_all)]
@ -587,7 +838,10 @@ async fn payment_response_update_tracker<F: Clone, T: types::Capturable>(
let payment_attempt_update =
storage::PaymentAttemptUpdate::PreprocessingUpdate {
status: updated_attempt_status,
payment_method_id: router_data.payment_method_id,
payment_method_id: payment_data
.payment_attempt
.payment_method_id
.clone(),
connector_metadata,
preprocessing_step_id,
connector_transaction_id,
@ -642,7 +896,7 @@ async fn payment_response_update_tracker<F: Clone, T: types::Capturable>(
metrics::SUCCESSFUL_PAYMENT.add(&metrics::CONTEXT, 1, &[]);
}
let payment_method_id = router_data.payment_method_id.clone();
let payment_method_id = payment_data.payment_attempt.payment_method_id.clone();
utils::add_apple_pay_payment_status_metrics(
router_data.status,
@ -677,10 +931,7 @@ async fn payment_response_update_tracker<F: Clone, T: types::Capturable>(
.request
.get_amount_capturable(&payment_data, updated_attempt_status),
payment_method_id,
mandate_id: payment_data
.mandate_id
.clone()
.and_then(|mandate| mandate.mandate_id),
mandate_id: payment_data.payment_attempt.mandate_id.clone(),
connector_metadata,
payment_token: None,
error_code: error_status.clone(),
@ -862,14 +1113,6 @@ async fn payment_response_update_tracker<F: Clone, T: types::Capturable>(
},
};
update_payment_method_status_and_ntid(
state,
&mut payment_data,
router_data.status,
router_data.response.clone(),
storage_scheme,
)
.await?;
let m_db = state.clone().store;
let m_payment_data_payment_intent = payment_data.payment_intent.clone();
let m_payment_intent_update = payment_intent_update.clone();

View File

@ -29,38 +29,62 @@ use crate::{
utils::{generate_id, OptionExt},
};
pub struct SavePaymentMethodData<Req> {
request: Req,
response: Result<types::PaymentsResponseData, types::ErrorResponse>,
payment_method_token: Option<types::PaymentMethodToken>,
payment_method: PaymentMethod,
attempt_status: common_enums::AttemptStatus,
}
impl<F, Req: Clone> From<&types::RouterData<F, Req, types::PaymentsResponseData>>
for SavePaymentMethodData<Req>
{
fn from(router_data: &types::RouterData<F, Req, types::PaymentsResponseData>) -> Self {
Self {
request: router_data.request.clone(),
response: router_data.response.clone(),
payment_method_token: router_data.payment_method_token.clone(),
payment_method: router_data.payment_method,
attempt_status: router_data.status,
}
}
}
#[instrument(skip_all)]
#[allow(clippy::too_many_arguments)]
pub async fn save_payment_method<F: Clone, FData>(
pub async fn save_payment_method<FData>(
state: &AppState,
connector: &api::ConnectorData,
resp: types::RouterData<F, FData, types::PaymentsResponseData>,
maybe_customer: &Option<domain::Customer>,
connector_name: String,
merchant_connector_id: Option<String>,
save_payment_method_data: SavePaymentMethodData<FData>,
customer_id: Option<String>,
merchant_account: &domain::MerchantAccount,
payment_method_type: Option<storage_enums::PaymentMethodType>,
key_store: &domain::MerchantKeyStore,
amount: Option<i64>,
currency: Option<storage_enums::Currency>,
profile_id: Option<String>,
billing_name: Option<masking::Secret<String>>,
) -> RouterResult<(Option<String>, Option<common_enums::PaymentMethodStatus>)>
where
FData: mandate::MandateBehaviour,
FData: mandate::MandateBehaviour + Clone,
{
let mut pm_status = None;
match resp.response {
match save_payment_method_data.response {
Ok(responses) => {
let db = &*state.store;
let token_store = state
.conf
.tokenization
.0
.get(&connector.connector_name.to_string())
.get(&connector_name.to_string())
.map(|token_filter| token_filter.long_lived_token)
.unwrap_or(false);
let network_transaction_id = match responses.clone() {
let network_transaction_id = match &responses {
types::PaymentsResponseData::TransactionResponse { network_txn_id, .. } => {
network_txn_id
network_txn_id.clone()
}
_ => None,
};
@ -82,7 +106,7 @@ where
.attach_printable("The pg_agnostic config was not found in the DB")?;
if &pg_agnostic.config == "true"
&& resp.request.get_setup_future_usage()
&& save_payment_method_data.request.get_setup_future_usage()
== Some(storage_enums::FutureUsage::OffSession)
{
Some(network_transaction_id)
@ -95,7 +119,7 @@ where
};
let connector_token = if token_store {
let tokens = resp
let tokens = save_payment_method_data
.payment_method_token
.to_owned()
.get_required_value("payment_token")?;
@ -107,17 +131,17 @@ where
})?
}
};
Some((connector, token))
Some((connector_name, token))
} else {
None
};
let mandate_data_customer_acceptance = resp
let mandate_data_customer_acceptance = save_payment_method_data
.request
.get_setup_mandate_details()
.and_then(|mandate_data| mandate_data.customer_acceptance.clone());
let customer_acceptance = resp
let customer_acceptance = save_payment_method_data
.request
.get_customer_acceptance()
.or(mandate_data_customer_acceptance.clone().map(From::from))
@ -139,8 +163,11 @@ where
}
_ => None,
};
let check_for_mit_mandates = resp.request.get_setup_mandate_details().is_none()
&& resp
let check_for_mit_mandates = save_payment_method_data
.request
.get_setup_mandate_details()
.is_none()
&& save_payment_method_data
.request
.get_setup_future_usage()
.map(|future_usage| future_usage == storage_enums::FutureUsage::OffSession)
@ -151,7 +178,7 @@ where
payment_method_type,
amount,
currency,
connector.merchant_connector_id.clone(),
merchant_connector_id.clone(),
connector_mandate_id.clone(),
)
} else {
@ -163,23 +190,16 @@ where
.attach_printable("Unable to serialize customer acceptance to value")?;
let pm_id = if customer_acceptance.is_some() {
let customer = maybe_customer.to_owned().get_required_value("customer")?;
let billing_name = resp
.address
.get_payment_method_billing()
.and_then(|billing_details| billing_details.address.as_ref())
.and_then(|address| address.get_optional_full_name());
let payment_method_create_request = helpers::get_payment_method_create_request(
Some(&resp.request.get_payment_method_data()),
Some(resp.payment_method),
Some(&save_payment_method_data.request.get_payment_method_data()),
Some(save_payment_method_data.payment_method),
payment_method_type,
&customer,
&customer_id.clone(),
billing_name,
)
.await?;
let customer_id = customer_id.to_owned().get_required_value("customer_id")?;
let merchant_id = &merchant_account.merchant_id;
let (mut resp, duplication_check) = if !state.conf.locker.locker_enabled {
skip_saving_card_in_locker(
merchant_account,
@ -187,7 +207,9 @@ where
)
.await?
} else {
pm_status = Some(common_enums::PaymentMethodStatus::from(resp.status));
pm_status = Some(common_enums::PaymentMethodStatus::from(
save_payment_method_data.attempt_status,
));
Box::pin(save_in_locker(
state,
merchant_account,
@ -280,7 +302,7 @@ where
payment_method_type,
amount,
currency,
connector.merchant_connector_id.clone(),
merchant_connector_id.clone(),
connector_mandate_id.clone(),
)?;
@ -297,7 +319,7 @@ where
payment_methods::cards::create_payment_method(
db,
&payment_method_create_request,
&customer.customer_id,
customer_id.as_str(),
&resp.payment_method_id,
locker_id,
merchant_id,
@ -371,7 +393,7 @@ where
payment_method_type,
amount,
currency,
connector.merchant_connector_id.clone(),
merchant_connector_id.clone(),
connector_mandate_id.clone(),
)?;
@ -390,7 +412,7 @@ where
payment_method_create_request.clone(),
key_store,
&merchant_account.merchant_id,
&customer.customer_id,
customer_id.as_str(),
resp.metadata.clone().map(|val| val.expose()),
customer_acceptance,
locker_id,
@ -413,7 +435,7 @@ where
payment_methods::cards::delete_card_from_locker(
state,
&customer.customer_id,
customer_id.as_str(),
merchant_id,
existing_pm
.locker_id
@ -426,7 +448,7 @@ where
state,
payment_method_create_request,
&card,
customer.customer_id.clone(),
customer_id.clone(),
merchant_account,
api::enums::LockerChoice::HyperswitchCardVault,
Some(
@ -516,7 +538,7 @@ where
payment_methods::cards::create_payment_method(
db,
&payment_method_create_request,
&customer.customer_id,
customer_id.as_str(),
&resp.payment_method_id,
locker_id,
merchant_id,
@ -683,7 +705,7 @@ pub async fn save_in_locker(
pub fn create_payment_method_metadata(
metadata: Option<&pii::SecretSerdeValue>,
connector_token: Option<(&api::ConnectorData, String)>,
connector_token: Option<(String, String)>,
) -> RouterResult<Option<serde_json::Value>> {
let mut meta = match metadata {
None => serde_json::Map::new(),
@ -698,7 +720,7 @@ pub fn create_payment_method_metadata(
};
Ok(connector_token.and_then(|connector_and_token| {
meta.insert(
connector_and_token.0.connector_name.to_string(),
connector_and_token.0,
serde_json::Value::String(connector_and_token.1),
)
}))