mirror of
https://github.com/juspay/hyperswitch.git
synced 2025-10-29 00:49:42 +08:00
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:
@ -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))]
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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()),
|
||||
|
||||
@ -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())
|
||||
}
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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<
|
||||
'_,
|
||||
|
||||
@ -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<
|
||||
'_,
|
||||
|
||||
@ -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<
|
||||
'_,
|
||||
|
||||
@ -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<
|
||||
'_,
|
||||
|
||||
@ -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()),
|
||||
|
||||
@ -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> {
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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]
|
||||
|
||||
@ -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();
|
||||
|
||||
@ -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),
|
||||
)
|
||||
}))
|
||||
|
||||
Reference in New Issue
Block a user