From 5255ba9170c633899cd4c3bbe24a44b429546f15 Mon Sep 17 00:00:00 2001 From: Kashif <46213975+kashif-m@users.noreply.github.com> Date: Fri, 19 Jan 2024 12:14:33 +0530 Subject: [PATCH] fix(frm): update FRM manual review flow (#3176) Co-authored-by: Kashif --- crates/router/src/core/fraud_check.rs | 10 +- crates/router/src/core/payments.rs | 36 ++- .../payments/operations/payment_approve.rs | 266 +++------------- .../payments/operations/payment_reject.rs | 21 +- .../payments/operations/payment_response.rs | 301 ++++++++++-------- crates/router/src/core/payments/retry.rs | 5 + crates/router/src/routes/payments.rs | 16 +- 7 files changed, 261 insertions(+), 394 deletions(-) diff --git a/crates/router/src/core/fraud_check.rs b/crates/router/src/core/fraud_check.rs index ad3a7638774e..0e3f67c051b8 100644 --- a/crates/router/src/core/fraud_check.rs +++ b/crates/router/src/core/fraud_check.rs @@ -431,6 +431,7 @@ pub async fn pre_payment_frm_core<'a, F>( frm_configs: FrmConfigsObject, customer: &Option, should_continue_transaction: &mut bool, + should_continue_capture: &mut bool, key_store: domain::MerchantKeyStore, ) -> RouterResult> where @@ -466,13 +467,12 @@ where .await?; let frm_fraud_check = frm_data_updated.fraud_check.clone(); payment_data.frm_message = Some(frm_fraud_check.clone()); - if matches!(frm_fraud_check.frm_status, FraudCheckStatus::Fraud) - //DontTakeAction - { - *should_continue_transaction = false; + if matches!(frm_fraud_check.frm_status, FraudCheckStatus::Fraud) { if matches!(frm_configs.frm_action, api_enums::FrmAction::CancelTxn) { + *should_continue_transaction = false; frm_info.suggested_action = Some(FrmSuggestion::FrmCancelTransaction); } else if matches!(frm_configs.frm_action, api_enums::FrmAction::ManualReview) { + *should_continue_capture = false; frm_info.suggested_action = Some(FrmSuggestion::FrmManualReview); } } @@ -582,6 +582,7 @@ pub async fn call_frm_before_connector_call<'a, F, Req, Ctx>( frm_info: &mut Option>, customer: &Option, should_continue_transaction: &mut bool, + should_continue_capture: &mut bool, key_store: domain::MerchantKeyStore, ) -> RouterResult> where @@ -615,6 +616,7 @@ where frm_configs, customer, should_continue_transaction, + should_continue_capture, key_store, ) .await?; diff --git a/crates/router/src/core/payments.rs b/crates/router/src/core/payments.rs index 49a9bcf66645..10aa00f5963c 100644 --- a/crates/router/src/core/payments.rs +++ b/crates/router/src/core/payments.rs @@ -181,6 +181,8 @@ where #[allow(unused_variables, unused_mut)] let mut should_continue_transaction: bool = true; #[cfg(feature = "frm")] + let mut should_continue_capture: bool = true; + #[cfg(feature = "frm")] let frm_configs = if state.conf.frm.enabled { frm_core::call_frm_before_connector_call( db, @@ -191,6 +193,7 @@ where &mut frm_info, &customer, &mut should_continue_transaction, + &mut should_continue_capture, key_store.clone(), ) .await? @@ -199,12 +202,25 @@ where }; #[cfg(feature = "frm")] logger::debug!( - "should_cancel_transaction: {:?} {:?} ", + "frm_configs: {:?}\nshould_cancel_transaction: {:?}\nshould_continue_capture: {:?}", frm_configs, - should_continue_transaction + should_continue_transaction, + should_continue_capture, ); if should_continue_transaction { + #[cfg(feature = "frm")] + match ( + should_continue_capture, + payment_data.payment_attempt.capture_method, + ) { + (false, Some(storage_enums::CaptureMethod::Automatic)) + | (false, Some(storage_enums::CaptureMethod::Scheduled)) => { + payment_data.payment_attempt.capture_method = + Some(storage_enums::CaptureMethod::Manual); + } + _ => (), + }; payment_data = match connector_details { api::ConnectorCallType::PreDetermined(connector) => { let schedule_time = if should_add_task_to_process_tracker { @@ -233,6 +249,10 @@ where &validate_result, schedule_time, header_payload, + #[cfg(feature = "frm")] + frm_info.as_ref().and_then(|fi| fi.suggested_action), + #[cfg(not(feature = "frm"))] + None, ) .await?; let operation = Box::new(PaymentResponse); @@ -284,6 +304,10 @@ where &validate_result, schedule_time, header_payload, + #[cfg(feature = "frm")] + frm_info.as_ref().and_then(|fi| fi.suggested_action), + #[cfg(not(feature = "frm"))] + None, ) .await?; @@ -311,6 +335,10 @@ where &customer, &validate_result, schedule_time, + #[cfg(feature = "frm")] + frm_info.as_ref().and_then(|fi| fi.suggested_action), + #[cfg(not(feature = "frm"))] + None, ) .await?; }; @@ -996,6 +1024,7 @@ pub async fn call_connector_service( validate_result: &operations::ValidateResult<'_>, schedule_time: Option, header_payload: HeaderPayload, + frm_suggestion: Option, ) -> RouterResult> where F: Send + Clone + Sync, @@ -1172,7 +1201,7 @@ where merchant_account.storage_scheme, updated_customer, key_store, - None, + frm_suggestion, header_payload, ) .await?; @@ -2110,6 +2139,7 @@ pub fn should_call_connector( } "CompleteAuthorize" => true, "PaymentApprove" => true, + "PaymentReject" => true, "PaymentSession" => true, "PaymentIncrementalAuthorization" => matches!( payment_data.payment_intent.status, diff --git a/crates/router/src/core/payments/operations/payment_approve.rs b/crates/router/src/core/payments/operations/payment_approve.rs index cddbc89acff1..6d3697caabdf 100644 --- a/crates/router/src/core/payments/operations/payment_approve.rs +++ b/crates/router/src/core/payments/operations/payment_approve.rs @@ -2,54 +2,51 @@ use std::marker::PhantomData; use api_models::enums::FrmSuggestion; use async_trait::async_trait; -use data_models::mandates::MandateData; -use error_stack::{report, IntoReport, ResultExt}; +use error_stack::{IntoReport, ResultExt}; use router_derive::PaymentOperation; use router_env::{instrument, tracing}; use super::{BoxedOperation, Domain, GetTracker, Operation, UpdateTracker, ValidateRequest}; use crate::{ core::{ - errors::{self, CustomResult, RouterResult, StorageErrorExt}, + errors::{self, RouterResult, StorageErrorExt}, payment_methods::PaymentMethodRetrieve, - payments::{self, helpers, operations, CustomerDetails, PaymentAddress, PaymentData}, + payments::{helpers, operations, PaymentAddress, PaymentData}, utils as core_utils, }, - db::StorageInterface, routes::AppState, services, types::{ - self, api::{self, PaymentIdTypeExt}, domain, storage::{self, enums as storage_enums}, }, - utils::{self, OptionExt}, + utils::OptionExt, }; #[derive(Debug, Clone, Copy, PaymentOperation)] -#[operation(operations = "all", flow = "authorize")] +#[operation(operations = "all", flow = "capture")] pub struct PaymentApprove; #[async_trait] impl - GetTracker, api::PaymentsRequest, Ctx> for PaymentApprove + GetTracker, api::PaymentsCaptureRequest, Ctx> for PaymentApprove { #[instrument(skip_all)] async fn get_trackers<'a>( &'a self, state: &'a AppState, payment_id: &api::PaymentIdType, - request: &api::PaymentsRequest, - mandate_type: Option, + _request: &api::PaymentsCaptureRequest, + _mandate_type: Option, merchant_account: &domain::MerchantAccount, key_store: &domain::MerchantKeyStore, _auth_flow: services::AuthFlow, - ) -> RouterResult> { + ) -> RouterResult> { let db = &*state.store; let merchant_id = &merchant_account.merchant_id; let storage_scheme = merchant_account.storage_scheme; - let (mut payment_intent, mut payment_attempt, currency, amount); + let (mut payment_intent, payment_attempt, currency, amount); let payment_id = payment_id .get_payment_intent_id() @@ -59,9 +56,6 @@ impl .find_payment_intent_by_payment_id_merchant_id(&payment_id, merchant_id, storage_scheme) .await .to_not_found_response(errors::ApiErrorResponse::PaymentNotFound)?; - payment_intent.setup_future_usage = request - .setup_future_usage - .or(payment_intent.setup_future_usage); helpers::validate_payment_status_against_not_allowed_statuses( &payment_intent.status, @@ -69,7 +63,7 @@ impl storage_enums::IntentStatus::Failed, storage_enums::IntentStatus::Succeeded, ], - "confirm", + "approve", )?; let profile_id = payment_intent @@ -87,31 +81,6 @@ impl id: profile_id.to_string(), })?; - let ( - token, - payment_method, - payment_method_type, - setup_mandate, - recurring_mandate_payment_data, - mandate_connector, - ) = helpers::get_token_pm_type_mandate_details( - state, - request, - mandate_type.clone(), - merchant_account, - key_store, - ) - .await?; - - let browser_info = request - .browser_info - .clone() - .map(|x| utils::Encode::::encode_to_value(&x)) - .transpose() - .change_context(errors::ApiErrorResponse::InvalidDataValue { - field_name: "browser_info", - })?; - let attempt_id = payment_intent.active_attempt.get_id().clone(); payment_attempt = db .find_payment_attempt_by_payment_id_merchant_id_attempt_id( @@ -123,35 +92,12 @@ impl .await .to_not_found_response(errors::ApiErrorResponse::PaymentNotFound)?; - let token = token.or_else(|| payment_attempt.payment_token.clone()); - - helpers::validate_pm_or_token_given( - &request.payment_method, - &request.payment_method_data, - &request.payment_method_type, - &mandate_type, - &token, - )?; - - payment_attempt.payment_method = payment_method.or(payment_attempt.payment_method); - payment_attempt.browser_info = browser_info; - payment_attempt.payment_method_type = - payment_method_type.or(payment_attempt.payment_method_type); - payment_attempt.payment_experience = request.payment_experience; currency = payment_attempt.currency.get_required_value("currency")?; amount = payment_attempt.get_total_amount().into(); - helpers::validate_customer_id_mandatory_cases( - request.setup_future_usage.is_some(), - &payment_intent - .customer_id - .clone() - .or_else(|| request.customer_id.clone()), - )?; - let shipping_address = helpers::create_or_find_address_for_payment_by_request( db, - request.shipping.as_ref(), + None, payment_intent.shipping_address_id.as_deref(), merchant_id, payment_intent.customer_id.as_ref(), @@ -162,7 +108,7 @@ impl .await?; let billing_address = helpers::create_or_find_address_for_payment_by_request( db, - request.billing.as_ref(), + None, payment_intent.billing_address_id.as_deref(), merchant_id, payment_intent.customer_id.as_ref(), @@ -172,47 +118,8 @@ impl ) .await?; - let redirect_response = request - .feature_metadata - .as_ref() - .and_then(|fm| fm.redirect_response.clone()); - payment_intent.shipping_address_id = shipping_address.clone().map(|i| i.address_id); payment_intent.billing_address_id = billing_address.clone().map(|i| i.address_id); - payment_intent.return_url = request - .return_url - .as_ref() - .map(|a| a.to_string()) - .or(payment_intent.return_url); - - payment_intent.allowed_payment_method_types = request - .get_allowed_payment_method_types_as_value() - .change_context(errors::ApiErrorResponse::InternalServerError) - .attach_printable("Error converting allowed_payment_types to Value")? - .or(payment_intent.allowed_payment_method_types); - - payment_intent.connector_metadata = request - .get_connector_metadata_as_value() - .change_context(errors::ApiErrorResponse::InternalServerError) - .attach_printable("Error converting connector_metadata to Value")? - .or(payment_intent.connector_metadata); - - payment_intent.feature_metadata = request - .get_feature_metadata_as_value() - .change_context(errors::ApiErrorResponse::InternalServerError) - .attach_printable("Error converting feature_metadata to Value")? - .or(payment_intent.feature_metadata); - - payment_intent.metadata = request.metadata.clone().or(payment_intent.metadata); - - // The operation merges mandate data from both request and payment_attempt - let setup_mandate = setup_mandate.map(|mandate_data| MandateData { - customer_acceptance: mandate_data.customer_acceptance, - mandate_type: payment_attempt - .mandate_details - .clone() - .or(mandate_data.mandate_type), - }); let frm_response = db .find_fraud_check_by_payment_id(payment_intent.payment_id.clone(), merchant_account.merchant_id.clone()) @@ -228,49 +135,41 @@ impl payment_attempt, currency, amount, - email: request.email.clone(), + email: None, mandate_id: None, - mandate_connector, - setup_mandate, - token, + mandate_connector: None, + setup_mandate: None, + token: None, address: PaymentAddress { shipping: shipping_address.as_ref().map(|a| a.into()), billing: billing_address.as_ref().map(|a| a.into()), }, - confirm: request.confirm, - payment_method_data: request.payment_method_data.clone(), + confirm: None, + payment_method_data: None, force_sync: None, refunds: vec![], disputes: vec![], attempts: None, sessions_token: vec![], - card_cvc: request.card_cvc.clone(), + card_cvc: None, creds_identifier: None, pm_token: None, connector_customer_id: None, - recurring_mandate_payment_data, + recurring_mandate_payment_data: None, ephemeral_key: None, multiple_capture_data: None, - redirect_response, + redirect_response: None, surcharge_details: None, frm_message: frm_response.ok(), payment_link_data: None, incremental_authorization_details: None, authorizations: vec![], - frm_metadata: request.frm_metadata.clone(), + frm_metadata: None, }; - let customer_details = Some(CustomerDetails { - customer_id: request.customer_id.clone(), - name: request.name.clone(), - email: request.email.clone(), - phone: request.phone.clone(), - phone_country_code: request.phone_country_code.clone(), - }); - let get_trackers_response = operations::GetTrackerResponse { operation: Box::new(self), - customer_details, + customer_details: None, payment_data, business_profile, }; @@ -279,91 +178,9 @@ impl } } -#[async_trait] -impl Domain - for PaymentApprove -{ - #[instrument(skip_all)] - async fn get_or_create_customer_details<'a>( - &'a self, - db: &dyn StorageInterface, - payment_data: &mut PaymentData, - request: Option, - key_store: &domain::MerchantKeyStore, - ) -> CustomResult< - ( - BoxedOperation<'a, F, api::PaymentsRequest, Ctx>, - Option, - ), - errors::StorageError, - > { - helpers::create_customer_if_not_exist( - Box::new(self), - db, - payment_data, - request, - &key_store.merchant_id, - key_store, - ) - .await - } - - #[instrument(skip_all)] - async fn make_pm_data<'a>( - &'a self, - state: &'a AppState, - payment_data: &mut PaymentData, - _storage_scheme: storage_enums::MerchantStorageScheme, - merchant_key_store: &domain::MerchantKeyStore, - customer: &Option, - ) -> RouterResult<( - BoxedOperation<'a, F, api::PaymentsRequest, Ctx>, - Option, - )> { - let (op, payment_method_data) = helpers::make_pm_data( - Box::new(self), - state, - payment_data, - merchant_key_store, - customer, - ) - .await?; - - utils::when(payment_method_data.is_none(), || { - Err(errors::ApiErrorResponse::PaymentMethodNotFound) - })?; - - Ok((op, payment_method_data)) - } - - #[instrument(skip_all)] - async fn add_task_to_process_tracker<'a>( - &'a self, - _state: &'a AppState, - _payment_attempt: &storage::PaymentAttempt, - _requeue: bool, - _schedule_time: Option, - ) -> CustomResult<(), errors::ApiErrorResponse> { - Ok(()) - } - - async fn get_connector<'a>( - &'a self, - _merchant_account: &domain::MerchantAccount, - state: &AppState, - request: &api::PaymentsRequest, - _payment_intent: &storage::PaymentIntent, - _key_store: &domain::MerchantKeyStore, - ) -> CustomResult { - // Use a new connector in the confirm call or use the same one which was passed when - // creating the payment or if none is passed then use the routing algorithm - helpers::get_connector_default(state, request.routing.clone()).await - } -} - #[async_trait] impl - UpdateTracker, api::PaymentsRequest, Ctx> for PaymentApprove + UpdateTracker, api::PaymentsCaptureRequest, Ctx> for PaymentApprove { #[instrument(skip_all)] async fn update_trackers<'b>( @@ -377,7 +194,7 @@ impl _frm_suggestion: Option, _header_payload: api::HeaderPayload, ) -> RouterResult<( - BoxedOperation<'b, F, api::PaymentsRequest, Ctx>, + BoxedOperation<'b, F, api::PaymentsCaptureRequest, Ctx>, PaymentData, )> where @@ -401,16 +218,16 @@ impl } } -impl ValidateRequest - for PaymentApprove +impl + ValidateRequest for PaymentApprove { #[instrument(skip_all)] fn validate_request<'a, 'b>( &'b self, - request: &api::PaymentsRequest, + request: &api::PaymentsCaptureRequest, merchant_account: &'a domain::MerchantAccount, ) -> RouterResult<( - BoxedOperation<'b, F, api::PaymentsRequest, Ctx>, + BoxedOperation<'b, F, api::PaymentsCaptureRequest, Ctx>, operations::ValidateResult<'a>, )> { let request_merchant_id = request.merchant_id.as_deref(); @@ -420,28 +237,17 @@ impl ValidateRequest - GetTracker, PaymentsRejectRequest, Ctx> for PaymentReject + GetTracker, PaymentsCancelRequest, Ctx> for PaymentReject { #[instrument(skip_all)] async fn get_trackers<'a>( &'a self, state: &'a AppState, payment_id: &api::PaymentIdType, - _request: &PaymentsRejectRequest, + _request: &PaymentsCancelRequest, _mandate_type: Option, merchant_account: &domain::MerchantAccount, key_store: &domain::MerchantKeyStore, _auth_flow: services::AuthFlow, - ) -> RouterResult> { + ) -> RouterResult> { let db = &*state.store; let merchant_id = &merchant_account.merchant_id; let storage_scheme = merchant_account.storage_scheme; @@ -57,6 +57,7 @@ impl helpers::validate_payment_status_against_not_allowed_statuses( &payment_intent.status, &[ + enums::IntentStatus::Cancelled, enums::IntentStatus::Failed, enums::IntentStatus::Succeeded, enums::IntentStatus::Processing, @@ -176,7 +177,7 @@ impl #[async_trait] impl - UpdateTracker, PaymentsRejectRequest, Ctx> for PaymentReject + UpdateTracker, PaymentsCancelRequest, Ctx> for PaymentReject { #[instrument(skip_all)] async fn update_trackers<'b>( @@ -190,7 +191,7 @@ impl _should_decline_transaction: Option, _header_payload: api::HeaderPayload, ) -> RouterResult<( - BoxedOperation<'b, F, PaymentsRejectRequest, Ctx>, + BoxedOperation<'b, F, PaymentsCancelRequest, Ctx>, PaymentData, )> where @@ -242,16 +243,16 @@ impl } } -impl ValidateRequest +impl ValidateRequest for PaymentReject { #[instrument(skip_all)] fn validate_request<'a, 'b>( &'b self, - request: &PaymentsRejectRequest, + request: &PaymentsCancelRequest, merchant_account: &'a domain::MerchantAccount, ) -> RouterResult<( - BoxedOperation<'b, F, PaymentsRejectRequest, Ctx>, + BoxedOperation<'b, F, PaymentsCancelRequest, Ctx>, operations::ValidateResult<'a>, )> { Ok(( diff --git a/crates/router/src/core/payments/operations/payment_response.rs b/crates/router/src/core/payments/operations/payment_response.rs index 9ab0b4f817f5..e5552f0d156d 100644 --- a/crates/router/src/core/payments/operations/payment_response.rs +++ b/crates/router/src/core/payments/operations/payment_response.rs @@ -512,113 +512,132 @@ async fn payment_response_update_tracker( }; (capture_update, attempt_update) } - Ok(payments_response) => match payments_response { - types::PaymentsResponseData::PreProcessingResponse { - pre_processing_id, - connector_metadata, - connector_response_reference_id, - .. - } => { - let connector_transaction_id = match pre_processing_id.to_owned() { - types::PreprocessingResponseId::PreProcessingId(_) => None, - types::PreprocessingResponseId::ConnectorTransactionId(connector_txn_id) => { - Some(connector_txn_id) - } - }; - let preprocessing_step_id = match pre_processing_id { - types::PreprocessingResponseId::PreProcessingId(pre_processing_id) => { - Some(pre_processing_id) + Ok(payments_response) => { + let attempt_status = payment_data.payment_attempt.status.to_owned(); + let connector_status = router_data.status.to_owned(); + let updated_attempt_status = match ( + connector_status, + attempt_status, + payment_data.frm_message.to_owned(), + ) { + ( + enums::AttemptStatus::Authorized, + enums::AttemptStatus::Unresolved, + Some(frm_message), + ) => match frm_message.frm_status { + enums::FraudCheckStatus::Fraud | enums::FraudCheckStatus::ManualReview => { + attempt_status } - types::PreprocessingResponseId::ConnectorTransactionId(_) => None, - }; - let payment_attempt_update = storage::PaymentAttemptUpdate::PreprocessingUpdate { - status: router_data.get_attempt_status_for_db_update(&payment_data), - payment_method_id: Some(router_data.payment_method_id), + _ => router_data.get_attempt_status_for_db_update(&payment_data), + }, + _ => router_data.get_attempt_status_for_db_update(&payment_data), + }; + match payments_response { + types::PaymentsResponseData::PreProcessingResponse { + pre_processing_id, connector_metadata, - preprocessing_step_id, - connector_transaction_id, connector_response_reference_id, - updated_by: storage_scheme.to_string(), - }; - - (None, Some(payment_attempt_update)) - } - types::PaymentsResponseData::TransactionResponse { - resource_id, - redirection_data, - connector_metadata, - connector_response_reference_id, - incremental_authorization_allowed, - .. - } => { - payment_data - .payment_intent - .incremental_authorization_allowed = - core_utils::get_incremental_authorization_allowed_value( - incremental_authorization_allowed, - payment_data - .payment_intent - .request_incremental_authorization, - ); - let connector_transaction_id = match resource_id { - types::ResponseId::NoResponseId => None, - types::ResponseId::ConnectorTransactionId(id) - | types::ResponseId::EncodedData(id) => Some(id), - }; - - let encoded_data = payment_data.payment_attempt.encoded_data.clone(); - - let authentication_data = redirection_data - .map(|data| utils::Encode::::encode_to_value(&data)) - .transpose() - .change_context(errors::ApiErrorResponse::InternalServerError) - .attach_printable("Could not parse the connector response")?; - - // incase of success, update error code and error message - let error_status = if router_data.status == enums::AttemptStatus::Charged { - Some(None) - } else { - None - }; + .. + } => { + let connector_transaction_id = match pre_processing_id.to_owned() { + types::PreprocessingResponseId::PreProcessingId(_) => None, + types::PreprocessingResponseId::ConnectorTransactionId( + connector_txn_id, + ) => Some(connector_txn_id), + }; + let preprocessing_step_id = match pre_processing_id { + types::PreprocessingResponseId::PreProcessingId(pre_processing_id) => { + Some(pre_processing_id) + } + types::PreprocessingResponseId::ConnectorTransactionId(_) => None, + }; + let payment_attempt_update = + storage::PaymentAttemptUpdate::PreprocessingUpdate { + status: updated_attempt_status, + payment_method_id: Some(router_data.payment_method_id), + connector_metadata, + preprocessing_step_id, + connector_transaction_id, + connector_response_reference_id, + updated_by: storage_scheme.to_string(), + }; - if router_data.status == enums::AttemptStatus::Charged { - metrics::SUCCESSFUL_PAYMENT.add(&metrics::CONTEXT, 1, &[]); + (None, Some(payment_attempt_update)) } + types::PaymentsResponseData::TransactionResponse { + resource_id, + redirection_data, + connector_metadata, + connector_response_reference_id, + incremental_authorization_allowed, + .. + } => { + payment_data + .payment_intent + .incremental_authorization_allowed = + core_utils::get_incremental_authorization_allowed_value( + incremental_authorization_allowed, + payment_data + .payment_intent + .request_incremental_authorization, + ); + let connector_transaction_id = match resource_id { + types::ResponseId::NoResponseId => None, + types::ResponseId::ConnectorTransactionId(id) + | types::ResponseId::EncodedData(id) => Some(id), + }; - utils::add_apple_pay_payment_status_metrics( - router_data.status, - router_data.apple_pay_flow.clone(), - payment_data.payment_attempt.connector.clone(), - payment_data.payment_attempt.merchant_id.clone(), - ); + let encoded_data = payment_data.payment_attempt.encoded_data.clone(); - let (capture_updates, payment_attempt_update) = match payment_data - .multiple_capture_data - { - Some(multiple_capture_data) => { - let capture_update = storage::CaptureUpdate::ResponseUpdate { - status: enums::CaptureStatus::foreign_try_from(router_data.status)?, - connector_capture_id: connector_transaction_id.clone(), - connector_response_reference_id, - }; - let capture_update_list = vec![( - multiple_capture_data.get_latest_capture().clone(), - capture_update, - )]; - (Some((multiple_capture_data, capture_update_list)), None) + let authentication_data = redirection_data + .map(|data| utils::Encode::::encode_to_value(&data)) + .transpose() + .change_context(errors::ApiErrorResponse::InternalServerError) + .attach_printable("Could not parse the connector response")?; + + // incase of success, update error code and error message + let error_status = if router_data.status == enums::AttemptStatus::Charged { + Some(None) + } else { + None + }; + + if router_data.status == enums::AttemptStatus::Charged { + metrics::SUCCESSFUL_PAYMENT.add(&metrics::CONTEXT, 1, &[]); } - None => { - let status = router_data.get_attempt_status_for_db_update(&payment_data); - ( + + utils::add_apple_pay_payment_status_metrics( + router_data.status, + router_data.apple_pay_flow.clone(), + payment_data.payment_attempt.connector.clone(), + payment_data.payment_attempt.merchant_id.clone(), + ); + + let (capture_updates, payment_attempt_update) = match payment_data + .multiple_capture_data + { + Some(multiple_capture_data) => { + let capture_update = storage::CaptureUpdate::ResponseUpdate { + status: enums::CaptureStatus::foreign_try_from(router_data.status)?, + connector_capture_id: connector_transaction_id.clone(), + connector_response_reference_id, + }; + let capture_update_list = vec![( + multiple_capture_data.get_latest_capture().clone(), + capture_update, + )]; + (Some((multiple_capture_data, capture_update_list)), None) + } + None => ( None, Some(storage::PaymentAttemptUpdate::ResponseUpdate { - status, + status: updated_attempt_status, connector: None, connector_transaction_id: connector_transaction_id.clone(), authentication_type: None, amount_capturable: router_data .request - .get_amount_capturable(&payment_data, status), + .get_amount_capturable(&payment_data, updated_attempt_status), payment_method_id: Some(router_data.payment_method_id), mandate_id: payment_data .mandate_id @@ -636,56 +655,58 @@ async fn payment_response_update_tracker( authentication_data, encoded_data, }), - ) - } - }; + ), + }; - (capture_updates, payment_attempt_update) - } - types::PaymentsResponseData::TransactionUnresolvedResponse { - resource_id, - reason, - connector_response_reference_id, - } => { - let connector_transaction_id = match resource_id { - types::ResponseId::NoResponseId => None, - types::ResponseId::ConnectorTransactionId(id) - | types::ResponseId::EncodedData(id) => Some(id), - }; - ( - None, - Some(storage::PaymentAttemptUpdate::UnresolvedResponseUpdate { - status: router_data.get_attempt_status_for_db_update(&payment_data), - connector: None, - connector_transaction_id, - payment_method_id: Some(router_data.payment_method_id), - error_code: Some(reason.clone().map(|cd| cd.code)), - error_message: Some(reason.clone().map(|cd| cd.message)), - error_reason: Some(reason.map(|cd| cd.message)), - connector_response_reference_id, - updated_by: storage_scheme.to_string(), - }), - ) - } - types::PaymentsResponseData::SessionResponse { .. } => (None, None), - types::PaymentsResponseData::SessionTokenResponse { .. } => (None, None), - types::PaymentsResponseData::TokenizationResponse { .. } => (None, None), - types::PaymentsResponseData::ConnectorCustomerResponse { .. } => (None, None), - types::PaymentsResponseData::ThreeDSEnrollmentResponse { .. } => (None, None), - types::PaymentsResponseData::IncrementalAuthorizationResponse { .. } => (None, None), - types::PaymentsResponseData::MultipleCaptureResponse { - capture_sync_response_list, - } => match payment_data.multiple_capture_data { - Some(multiple_capture_data) => { - let capture_update_list = response_to_capture_update( - &multiple_capture_data, - capture_sync_response_list, - )?; - (Some((multiple_capture_data, capture_update_list)), None) + (capture_updates, payment_attempt_update) } - None => (None, None), - }, - }, + types::PaymentsResponseData::TransactionUnresolvedResponse { + resource_id, + reason, + connector_response_reference_id, + } => { + let connector_transaction_id = match resource_id { + types::ResponseId::NoResponseId => None, + types::ResponseId::ConnectorTransactionId(id) + | types::ResponseId::EncodedData(id) => Some(id), + }; + ( + None, + Some(storage::PaymentAttemptUpdate::UnresolvedResponseUpdate { + status: updated_attempt_status, + connector: None, + connector_transaction_id, + payment_method_id: Some(router_data.payment_method_id), + error_code: Some(reason.clone().map(|cd| cd.code)), + error_message: Some(reason.clone().map(|cd| cd.message)), + error_reason: Some(reason.map(|cd| cd.message)), + connector_response_reference_id, + updated_by: storage_scheme.to_string(), + }), + ) + } + types::PaymentsResponseData::SessionResponse { .. } => (None, None), + types::PaymentsResponseData::SessionTokenResponse { .. } => (None, None), + types::PaymentsResponseData::TokenizationResponse { .. } => (None, None), + types::PaymentsResponseData::ConnectorCustomerResponse { .. } => (None, None), + types::PaymentsResponseData::ThreeDSEnrollmentResponse { .. } => (None, None), + types::PaymentsResponseData::IncrementalAuthorizationResponse { .. } => { + (None, None) + } + types::PaymentsResponseData::MultipleCaptureResponse { + capture_sync_response_list, + } => match payment_data.multiple_capture_data { + Some(multiple_capture_data) => { + let capture_update_list = response_to_capture_update( + &multiple_capture_data, + capture_sync_response_list, + )?; + (Some((multiple_capture_data, capture_update_list)), None) + } + None => (None, None), + }, + } + } }; payment_data.multiple_capture_data = match capture_update { Some((mut multiple_capture_data, capture_updates)) => { diff --git a/crates/router/src/core/payments/retry.rs b/crates/router/src/core/payments/retry.rs index 0fd45c5af3b5..8d74eb3fa961 100644 --- a/crates/router/src/core/payments/retry.rs +++ b/crates/router/src/core/payments/retry.rs @@ -40,6 +40,7 @@ pub async fn do_gsm_actions( customer: &Option, validate_result: &operations::ValidateResult<'_>, schedule_time: Option, + frm_suggestion: Option, ) -> RouterResult> where F: Clone + Send + Sync, @@ -90,6 +91,7 @@ where validate_result, schedule_time, true, + frm_suggestion, ) .await?; } @@ -133,6 +135,7 @@ where schedule_time, //this is an auto retry payment, but not step-up false, + frm_suggestion, ) .await?; @@ -275,6 +278,7 @@ pub async fn do_retry( validate_result: &operations::ValidateResult<'_>, schedule_time: Option, is_step_up: bool, + frm_suggestion: Option, ) -> RouterResult> where F: Clone + Send + Sync, @@ -310,6 +314,7 @@ where validate_result, schedule_time, api::HeaderPayload::default(), + frm_suggestion, ) .await } diff --git a/crates/router/src/routes/payments.rs b/crates/router/src/routes/payments.rs index 34f41c49cddf..379cd4f2f1fc 100644 --- a/crates/router/src/routes/payments.rs +++ b/crates/router/src/routes/payments.rs @@ -968,7 +968,7 @@ pub async fn payments_approve( payload.clone(), |state, auth, req| { payments::payments_core::< - api_types::Authorize, + api_types::Capture, payment_types::PaymentsResponse, _, _, @@ -979,10 +979,8 @@ pub async fn payments_approve( auth.merchant_account, auth.key_store, payments::PaymentApprove, - payment_types::PaymentsRequest { - payment_id: Some(payment_types::PaymentIdType::PaymentIntentId( - req.payment_id, - )), + payment_types::PaymentsCaptureRequest { + payment_id: req.payment_id, ..Default::default() }, api::AuthFlow::Merchant, @@ -1030,7 +1028,7 @@ pub async fn payments_reject( payload.clone(), |state, auth, req| { payments::payments_core::< - api_types::Reject, + api_types::Void, payment_types::PaymentsResponse, _, _, @@ -1041,7 +1039,11 @@ pub async fn payments_reject( auth.merchant_account, auth.key_store, payments::PaymentReject, - req, + payment_types::PaymentsCancelRequest { + payment_id: req.payment_id, + cancellation_reason: Some("Rejected by merchant".to_string()), + ..Default::default() + }, api::AuthFlow::Merchant, payments::CallConnectorAction::Trigger, None,