diff --git a/crates/api_models/src/admin.rs b/crates/api_models/src/admin.rs index 93cf7574d988..2e288140a9f2 100644 --- a/crates/api_models/src/admin.rs +++ b/crates/api_models/src/admin.rs @@ -729,8 +729,11 @@ pub struct FrmPaymentMethod { ///payment methods(card, wallet, etc) that can be used in the payment #[schema(value_type = PaymentMethod,example = "card")] pub payment_method: Option, - ///payment method types(credit, debit) that can be used in the payment - pub payment_method_types: Vec, + ///payment method types(credit, debit) that can be used in the payment. This field is deprecated. It has not been removed to provide backward compatibility. + pub payment_method_types: Option>, + ///frm flow type to be used, can be pre/post + #[schema(value_type = Option)] + pub flow: Option, } ///Details of FrmPaymentMethodType are mentioned here... it should be passed in payment connector create api call, and stored in merchant_connector_table @@ -743,7 +746,7 @@ pub struct FrmPaymentMethodType { ///card networks(like visa mastercard) types that can be used in the payment #[schema(value_type = CardNetwork)] pub card_networks: Option>, - ///frm flow type to be used...can be pre/post + ///frm flow type to be used, can be pre/post #[schema(value_type = FrmPreferredFlowTypes)] pub flow: api_enums::FrmPreferredFlowTypes, ///action that the frm would take, in case fraud is detected diff --git a/crates/api_models/src/payments.rs b/crates/api_models/src/payments.rs index c0b7b69ca9e6..7c15f145e2f9 100644 --- a/crates/api_models/src/payments.rs +++ b/crates/api_models/src/payments.rs @@ -464,7 +464,8 @@ pub struct PaymentsRequest { pub session_expiry: Option, /// additional data related to some frm connectors - pub frm_metadata: Option, + #[schema(value_type = Option, example = r#"{ "coverage_request" : "fraud", "fulfillment_method" : "delivery" }"#)] + pub frm_metadata: Option, /// Whether to perform external authentication (if applicable) #[schema(example = true)] @@ -3410,6 +3411,10 @@ pub struct PaymentsResponse { #[schema(example = "2022-09-10T10:11:12Z")] #[serde(default, with = "common_utils::custom_serde::iso8601::option")] pub updated: Option, + + /// You can specify up to 50 keys, with key names up to 40 characters long and values up to 500 characters long. FRM Metadata is useful for storing additional, structured information on an object related to FRM. + #[schema(value_type = Option, example = r#"{ "fulfillment_method" : "deliver", "coverage_request" : "fraud" }"#)] + pub frm_metadata: Option, } #[derive(Setter, Clone, Default, Debug, PartialEq, serde::Serialize, ToSchema)] diff --git a/crates/diesel_models/src/payment_intent.rs b/crates/diesel_models/src/payment_intent.rs index ee6e3960b735..d65fa6868b98 100644 --- a/crates/diesel_models/src/payment_intent.rs +++ b/crates/diesel_models/src/payment_intent.rs @@ -58,6 +58,7 @@ pub struct PaymentIntent { pub session_expiry: Option, pub fingerprint_id: Option, pub request_external_three_ds_authentication: Option, + pub frm_metadata: Option, } #[derive( @@ -111,6 +112,7 @@ pub struct PaymentIntentNew { pub session_expiry: Option, pub fingerprint_id: Option, pub request_external_three_ds_authentication: Option, + pub frm_metadata: Option, } #[derive(Debug, Clone, Serialize, Deserialize)] @@ -167,6 +169,7 @@ pub enum PaymentIntentUpdate { session_expiry: Option, fingerprint_id: Option, request_external_three_ds_authentication: Option, + frm_metadata: Option, }, PaymentAttemptAndAttemptCountUpdate { active_attempt_id: String, @@ -236,6 +239,7 @@ pub struct PaymentIntentUpdateInternal { pub session_expiry: Option, pub fingerprint_id: Option, pub request_external_three_ds_authentication: Option, + pub frm_metadata: Option, } impl PaymentIntentUpdate { @@ -271,6 +275,7 @@ impl PaymentIntentUpdate { session_expiry, fingerprint_id, request_external_three_ds_authentication, + frm_metadata, } = self.into(); PaymentIntent { amount: amount.unwrap_or(source.amount), @@ -308,6 +313,8 @@ impl PaymentIntentUpdate { session_expiry: session_expiry.or(source.session_expiry), request_external_three_ds_authentication: request_external_three_ds_authentication .or(source.request_external_three_ds_authentication), + + frm_metadata: frm_metadata.or(source.frm_metadata), ..source } } @@ -337,6 +344,7 @@ impl From for PaymentIntentUpdateInternal { session_expiry, fingerprint_id, request_external_three_ds_authentication, + frm_metadata, } => Self { amount: Some(amount), currency: Some(currency), @@ -359,6 +367,7 @@ impl From for PaymentIntentUpdateInternal { session_expiry, fingerprint_id, request_external_three_ds_authentication, + frm_metadata, ..Default::default() }, PaymentIntentUpdate::MetadataUpdate { @@ -542,7 +551,8 @@ mod tests { "incremental_authorization_allowed": null, "authorization_count": null, "session_expiry": null, - "fingerprint_id": null + "fingerprint_id": null, + "frm_metadata": null }"#; let deserialized_payment_intent = serde_json::from_str::(serialized_payment_intent); diff --git a/crates/diesel_models/src/schema.rs b/crates/diesel_models/src/schema.rs index e63c14eef156..427f9560e034 100644 --- a/crates/diesel_models/src/schema.rs +++ b/crates/diesel_models/src/schema.rs @@ -846,6 +846,7 @@ diesel::table! { #[max_length = 64] fingerprint_id -> Nullable, request_external_three_ds_authentication -> Nullable, + frm_metadata -> Nullable, } } diff --git a/crates/hyperswitch_domain_models/src/payments.rs b/crates/hyperswitch_domain_models/src/payments.rs index 8a7dfad5fcbe..cea948d4f999 100644 --- a/crates/hyperswitch_domain_models/src/payments.rs +++ b/crates/hyperswitch_domain_models/src/payments.rs @@ -60,4 +60,5 @@ pub struct PaymentIntent { #[serde(with = "common_utils::custom_serde::iso8601::option")] pub session_expiry: Option, pub request_external_three_ds_authentication: Option, + pub frm_metadata: Option, } diff --git a/crates/hyperswitch_domain_models/src/payments/payment_intent.rs b/crates/hyperswitch_domain_models/src/payments/payment_intent.rs index a5e631b5ac33..84dc56403c6f 100644 --- a/crates/hyperswitch_domain_models/src/payments/payment_intent.rs +++ b/crates/hyperswitch_domain_models/src/payments/payment_intent.rs @@ -81,6 +81,7 @@ pub struct PaymentIntentNew { pub description: Option, pub return_url: Option, pub metadata: Option, + pub frm_metadata: Option, pub connector_id: Option, pub shipping_address_id: Option, pub billing_address_id: Option, @@ -164,6 +165,7 @@ pub enum PaymentIntentUpdate { statement_descriptor_suffix: Option, order_details: Option>, metadata: Option, + frm_metadata: Option, payment_confirm_source: Option, updated_by: String, fingerprint_id: Option, @@ -237,6 +239,7 @@ pub struct PaymentIntentUpdateInternal { pub fingerprint_id: Option, pub session_expiry: Option, pub request_external_three_ds_authentication: Option, + pub frm_metadata: Option, } impl From for PaymentIntentUpdateInternal { @@ -263,6 +266,7 @@ impl From for PaymentIntentUpdateInternal { fingerprint_id, session_expiry, request_external_three_ds_authentication, + frm_metadata, } => Self { amount: Some(amount), currency: Some(currency), @@ -285,6 +289,7 @@ impl From for PaymentIntentUpdateInternal { fingerprint_id, session_expiry, request_external_three_ds_authentication, + frm_metadata, ..Default::default() }, PaymentIntentUpdate::MetadataUpdate { diff --git a/crates/hyperswitch_domain_models/src/router_data.rs b/crates/hyperswitch_domain_models/src/router_data.rs index cbaaeeda42b5..d0481cecc539 100644 --- a/crates/hyperswitch_domain_models/src/router_data.rs +++ b/crates/hyperswitch_domain_models/src/router_data.rs @@ -57,7 +57,7 @@ pub struct RouterData { /// Contains apple pay flow type simplified or manual pub apple_pay_flow: Option, - pub frm_metadata: Option, + pub frm_metadata: Option, pub dispute_id: Option, pub refund_id: Option, diff --git a/crates/router/src/connector/signifyd/transformers/api.rs b/crates/router/src/connector/signifyd/transformers/api.rs index b56c7f7011ef..2f742b71570a 100644 --- a/crates/router/src/connector/signifyd/transformers/api.rs +++ b/crates/router/src/connector/signifyd/transformers/api.rs @@ -162,7 +162,9 @@ impl TryFrom<&frm_types::FrmSaleRouterData> for SignifydPaymentsSaleRequest { field_name: "frm_metadata", })? .parse_value("Signifyd Frm Metadata") - .change_context(errors::ConnectorError::RequestEncodingFailed)?; + .change_context(errors::ConnectorError::InvalidDataFormat { + field_name: "frm_metadata", + })?; let ship_address = item.get_shipping_address()?; let billing_address = item.get_billing()?; let street_addr = ship_address.get_line1()?; diff --git a/crates/router/src/core/fraud_check.rs b/crates/router/src/core/fraud_check.rs index 4d614662a4dd..b5427d489c22 100644 --- a/crates/router/src/core/fraud_check.rs +++ b/crates/router/src/core/fraud_check.rs @@ -1,6 +1,6 @@ use std::fmt::Debug; -use api_models::{admin::FrmConfigs, enums as api_enums, payments::AdditionalPaymentData}; +use api_models::{admin::FrmConfigs, enums as api_enums}; use common_enums::CaptureMethod; use error_stack::ResultExt; use masking::{ExposeInterface, PeekInterface}; @@ -20,10 +20,7 @@ use super::errors::{ConnectorErrorExt, RouterResponse}; use crate::{ core::{ errors::{self, RouterResult}, - payments::{ - self, flows::ConstructFlowSpecificData, helpers::get_additional_payment_data, - operations::BoxedOperation, - }, + payments::{self, flows::ConstructFlowSpecificData, operations::BoxedOperation}, utils as core_utils, }, db::StorageInterface, @@ -185,15 +182,14 @@ where .expose() .parse_value("FrmConfigs") .change_context(errors::ApiErrorResponse::InvalidDataFormat { - field_name: "frm_configs".to_string(), - expected_format: r#"[{ "gateway": "stripe", "payment_methods": [{ "payment_method": "card","payment_method_types": [{"payment_method_type": "credit","card_networks": ["Visa"],"flow": "pre","action": "cancel_txn"}]}]}]"#.to_string(), - }) + field_name: "frm_configs".to_string(), + expected_format: r#"[{ "gateway": "stripe", "payment_methods": [{ "payment_method": "card","flow": "post"}]}]"#.to_string(), + }) }) .collect::, _>>()?; let mut is_frm_connector_enabled = false; let mut is_frm_pm_enabled = false; - let mut is_frm_pmt_enabled = false; let filtered_frm_config = frm_configs_struct .iter() .filter(|frm_config| { @@ -243,76 +239,11 @@ where }) .collect::>() .concat(); - - let additional_payment_data = match &payment_data.payment_method_data { - Some(pmd) => { - let additional_payment_data = - get_additional_payment_data(pmd, db, &profile_id).await; - Some(additional_payment_data) - } - None => payment_data - .payment_attempt - .payment_method_data - .as_ref() - .map(|pm_data| { - pm_data.clone().parse_value::( - "AdditionalPaymentData", - ) - }) - .transpose() - .unwrap_or_default(), // Making this default in case of error as we don't want to fail payment for frm errors - }; - let filtered_payment_method_types = filtered_payment_methods - .iter() - .map(|frm_pm_config| { - let filtered_pm_config_by_pmt = frm_pm_config - .payment_method_types - .iter() - .filter(|frm_pm_config_by_pmt| { - match ( - &payment_data - .clone() - .payment_attempt - .payment_method_type, - frm_pm_config_by_pmt.payment_method_type, - ) { - (Some(curr), Some(conf)) - if curr.to_string() == conf.to_string() => - { - is_frm_pmt_enabled = true; - true - } - (None, Some(conf)) => match additional_payment_data - .clone() - { - Some(AdditionalPaymentData::Card(card)) => { - let card_type = card - .card_type - .unwrap_or_else(|| "debit".to_string()); - let is_enabled = card_type.to_lowercase() - == conf.to_string().to_lowercase(); - if is_enabled { - is_frm_pmt_enabled = true; - } - is_enabled - } - _ => false, - }, - _ => false, - } - }) - .collect::>(); - filtered_pm_config_by_pmt - }) - .collect::>() - .concat(); - let is_frm_enabled = - is_frm_connector_enabled && is_frm_pm_enabled && is_frm_pmt_enabled; + let is_frm_enabled = is_frm_connector_enabled && is_frm_pm_enabled; logger::debug!( - "is_frm_connector_enabled {:?}, is_frm_pm_enabled: {:?},is_frm_pmt_enabled : {:?}, is_frm_enabled :{:?}", + "is_frm_connector_enabled {:?}, is_frm_pm_enabled: {:?}, is_frm_enabled :{:?}", is_frm_connector_enabled, is_frm_pm_enabled, - is_frm_pmt_enabled, is_frm_enabled ); // filtered_frm_config... @@ -324,18 +255,19 @@ where frm_enabled_pm: filtered_payment_methods .first() .and_then(|pm| pm.payment_method), - frm_enabled_pm_type: filtered_payment_method_types + // flow type should be consumed from payment_method.flow. To provide backward compatibility, if we don't find it there, we consume it from payment_method.payment_method_types[0].flow_type. + frm_preferred_flow_type: filtered_payment_methods .first() - .and_then(|pmt| pmt.payment_method_type), - frm_action: filtered_payment_method_types - // .clone() - .first() - .map(|pmt| pmt.action.clone()) - .unwrap_or(api_enums::FrmAction::ManualReview), - frm_preferred_flow_type: filtered_payment_method_types - .first() - .map(|pmt| pmt.flow.clone()) - .unwrap_or(api_enums::FrmPreferredFlowTypes::Pre), + .and_then(|pm| pm.flow.clone()) + .or(filtered_payment_methods.first().and_then(|pm| { + pm.payment_method_types.as_ref().and_then(|pmt| { + pmt.first().map(|pmts| pmts.flow.clone()) + }) + })) + .ok_or(errors::ApiErrorResponse::InvalidDataFormat { + field_name: "frm_configs".to_string(), + expected_format: r#"[{ "gateway": "stripe", "payment_methods": [{ "payment_method": "card","flow": "post"}]}]"#.to_string(), + })?, }; logger::debug!( "frm_routing_configs: {:?} {:?} {:?} {:?}", @@ -411,13 +343,13 @@ where let payment_to_frm_data = PaymentToFrmData { amount: payment_data.amount, - payment_intent: payment_data.payment_intent, + payment_intent: payment_data.payment_intent.to_owned(), payment_attempt: payment_data.payment_attempt, merchant_account: merchant_account.to_owned(), address: payment_data.address.clone(), connector_details: frm_connector_details.clone(), order_details, - frm_metadata: payment_data.frm_metadata.clone(), + frm_metadata: payment_data.payment_intent.frm_metadata, }; let fraud_check_operation: operation::BoxedFraudCheckOperation = @@ -495,12 +427,7 @@ where payment_data.frm_message = Some(frm_fraud_check.clone()); if matches!(frm_fraud_check.frm_status, FraudCheckStatus::Fraud) { *should_continue_transaction = false; - if matches!(frm_configs.frm_action, api_enums::FrmAction::CancelTxn) { - 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); - } + frm_info.suggested_action = Some(FrmSuggestion::FrmCancelTransaction); } logger::debug!( "frm_updated_data: {:?} {:?}", @@ -511,6 +438,9 @@ where } else if matches!( frm_configs.frm_preferred_flow_type, api_enums::FrmPreferredFlowTypes::Post + ) && !matches!( + frm_data.fraud_check.frm_status, + FraudCheckStatus::TransactionFailure // Incase of TransactionFailure frm status(No frm decision is taken by frm processor), if capture method is automatic we should not change it to manual. ) { *should_continue_capture = false; Some(frm_data.to_owned()) @@ -571,11 +501,7 @@ where let mut frm_suggestion = None; payment_data.frm_message = Some(frm_fraud_check.clone()); if matches!(frm_fraud_check.frm_status, FraudCheckStatus::Fraud) { - if matches!(frm_configs.frm_action, api_enums::FrmAction::CancelTxn) { - frm_info.suggested_action = Some(FrmSuggestion::FrmCancelTransaction); - } else if matches!(frm_configs.frm_action, api_enums::FrmAction::ManualReview) { - frm_info.suggested_action = Some(FrmSuggestion::FrmManualReview); - } + frm_info.suggested_action = Some(FrmSuggestion::FrmCancelTransaction); } else if matches!(frm_fraud_check.frm_status, FraudCheckStatus::ManualReview) { frm_info.suggested_action = Some(FrmSuggestion::FrmManualReview); } diff --git a/crates/router/src/core/fraud_check/operation/fraud_check_post.rs b/crates/router/src/core/fraud_check/operation/fraud_check_post.rs index 758574809c29..6a310aa131dd 100644 --- a/crates/router/src/core/fraud_check/operation/fraud_check_post.rs +++ b/crates/router/src/core/fraud_check/operation/fraud_check_post.rs @@ -25,7 +25,7 @@ use crate::{ services::{self, api}, types::{ api::{ - enums::{AttemptStatus, FrmAction, IntentStatus}, + enums::{AttemptStatus, IntentStatus}, fraud_check as frm_api, payments as payment_types, Capture, Void, }, domain, @@ -186,7 +186,7 @@ impl Domain for FraudCheckPost { req_state: ReqState, frm_data: &mut FrmData, merchant_account: &domain::MerchantAccount, - frm_configs: FrmConfigsObject, + _frm_configs: FrmConfigsObject, frm_suggestion: &mut Option, key_store: domain::MerchantKeyStore, payment_data: &mut payments::PaymentData, @@ -194,7 +194,6 @@ impl Domain for FraudCheckPost { _should_continue_capture: &mut bool, ) -> RouterResult> { if matches!(frm_data.fraud_check.frm_status, FraudCheckStatus::Fraud) - && matches!(frm_configs.frm_action, FrmAction::CancelTxn) && matches!( frm_data.fraud_check.last_step, FraudCheckLastStep::CheckoutOrSale @@ -242,9 +241,10 @@ impl Domain for FraudCheckPost { ) .await?; frm_data.fraud_check.last_step = FraudCheckLastStep::TransactionOrRecordRefund; - } else if matches!(frm_data.fraud_check.frm_status, FraudCheckStatus::Fraud) - && matches!(frm_configs.frm_action, FrmAction::ManualReview) - { + } else if matches!( + frm_data.fraud_check.frm_status, + FraudCheckStatus::ManualReview + ) { *frm_suggestion = Some(FrmSuggestion::FrmManualReview); } else if matches!(frm_data.fraud_check.frm_status, FraudCheckStatus::Legit) && matches!( @@ -477,25 +477,34 @@ impl UpdateTracker for FraudCheckPost { }; if let Some(frm_suggestion) = frm_suggestion { - let (payment_attempt_status, payment_intent_status) = match frm_suggestion { - FrmSuggestion::FrmCancelTransaction => { - (AttemptStatus::Failure, IntentStatus::Failed) - } - FrmSuggestion::FrmManualReview => ( - AttemptStatus::Unresolved, - IntentStatus::RequiresMerchantAction, - ), - FrmSuggestion::FrmAuthorizeTransaction => { - (AttemptStatus::Authorized, IntentStatus::RequiresCapture) - } - }; + let (payment_attempt_status, payment_intent_status, merchant_decision, error_message) = + match frm_suggestion { + FrmSuggestion::FrmCancelTransaction => ( + AttemptStatus::Failure, + IntentStatus::Failed, + Some(MerchantDecision::Rejected.to_string()), + Some(Some(CANCEL_INITIATED.to_string())), + ), + FrmSuggestion::FrmManualReview => ( + AttemptStatus::Unresolved, + IntentStatus::RequiresMerchantAction, + None, + None, + ), + FrmSuggestion::FrmAuthorizeTransaction => ( + AttemptStatus::Authorized, + IntentStatus::RequiresCapture, + None, + None, + ), + }; payment_data.payment_attempt = db .update_payment_attempt_with_attempt_id( payment_data.payment_attempt.clone(), PaymentAttemptUpdate::RejectUpdate { status: payment_attempt_status, error_code: Some(Some(frm_data.fraud_check.frm_status.to_string())), - error_message: Some(Some(CANCEL_INITIATED.to_string())), + error_message, updated_by: frm_data.merchant_account.storage_scheme.to_string(), }, frm_data.merchant_account.storage_scheme, @@ -508,7 +517,7 @@ impl UpdateTracker for FraudCheckPost { payment_data.payment_intent.clone(), PaymentIntentUpdate::RejectUpdate { status: payment_intent_status, - merchant_decision: Some(MerchantDecision::Rejected.to_string()), + merchant_decision, updated_by: frm_data.merchant_account.storage_scheme.to_string(), }, frm_data.merchant_account.storage_scheme, diff --git a/crates/router/src/core/fraud_check/types.rs b/crates/router/src/core/fraud_check/types.rs index 5acd722077fb..d2a9b9204573 100644 --- a/crates/router/src/core/fraud_check/types.rs +++ b/crates/router/src/core/fraud_check/types.rs @@ -5,7 +5,7 @@ use api_models::{ refunds::RefundResponse, }; use common_enums::FrmSuggestion; -use common_utils::pii::Email; +use common_utils::pii::{Email, SecretSerdeValue}; use hyperswitch_domain_models::payments::{payment_attempt::PaymentAttempt, PaymentIntent}; use masking::Serialize; use serde::Deserialize; @@ -56,7 +56,7 @@ pub struct FrmData { pub connector_details: ConnectorDetailsCore, pub order_details: Option>, pub refund: Option, - pub frm_metadata: Option, + pub frm_metadata: Option, } #[derive(Debug)] @@ -80,15 +80,13 @@ pub struct PaymentToFrmData { pub address: PaymentAddress, pub connector_details: ConnectorDetailsCore, pub order_details: Option>, - pub frm_metadata: Option, + pub frm_metadata: Option, } #[derive(Debug, Clone, Serialize, Deserialize)] pub struct FrmConfigsObject { pub frm_enabled_pm: Option, - pub frm_enabled_pm_type: Option, pub frm_enabled_gateway: Option, - pub frm_action: api_enums::FrmAction, pub frm_preferred_flow_type: api_enums::FrmPreferredFlowTypes, } diff --git a/crates/router/src/core/payments.rs b/crates/router/src/core/payments.rs index 668a4242b06a..1b47c267af15 100644 --- a/crates/router/src/core/payments.rs +++ b/crates/router/src/core/payments.rs @@ -2450,7 +2450,6 @@ where pub incremental_authorization_details: Option, pub authorizations: Vec, pub authentication: Option, - pub frm_metadata: Option, pub recurring_details: Option, pub poll_config: Option, } diff --git a/crates/router/src/core/payments/helpers.rs b/crates/router/src/core/payments/helpers.rs index 38acf574a7c6..59bb8c6807c2 100644 --- a/crates/router/src/core/payments/helpers.rs +++ b/crates/router/src/core/payments/helpers.rs @@ -2965,6 +2965,7 @@ mod tests { .saturating_add(time::Duration::seconds(consts::DEFAULT_SESSION_EXPIRY)), ), request_external_three_ds_authentication: None, + frm_metadata: None, }; let req_cs = Some("1".to_string()); assert!(authenticate_client_secret(req_cs.as_ref(), &payment_intent).is_ok()); @@ -3023,6 +3024,7 @@ mod tests { .saturating_add(time::Duration::seconds(consts::DEFAULT_SESSION_EXPIRY)), ), request_external_three_ds_authentication: None, + frm_metadata: None, }; let req_cs = Some("1".to_string()); assert!(authenticate_client_secret(req_cs.as_ref(), &payment_intent,).is_err()) @@ -3080,6 +3082,7 @@ mod tests { .saturating_add(time::Duration::seconds(consts::DEFAULT_SESSION_EXPIRY)), ), request_external_three_ds_authentication: None, + frm_metadata: None, }; let req_cs = Some("1".to_string()); assert!(authenticate_client_secret(req_cs.as_ref(), &payment_intent).is_err()) diff --git a/crates/router/src/core/payments/operations/payment_approve.rs b/crates/router/src/core/payments/operations/payment_approve.rs index 1a92ab0c528c..52325db753a6 100644 --- a/crates/router/src/core/payments/operations/payment_approve.rs +++ b/crates/router/src/core/payments/operations/payment_approve.rs @@ -172,7 +172,6 @@ impl GetTracker, api::PaymentsCaptureRequest> payment_link_data: None, incremental_authorization_details: None, authorizations: vec![], - frm_metadata: None, authentication: None, recurring_details: None, poll_config: None, diff --git a/crates/router/src/core/payments/operations/payment_cancel.rs b/crates/router/src/core/payments/operations/payment_cancel.rs index 73fecd202591..64114fc40e65 100644 --- a/crates/router/src/core/payments/operations/payment_cancel.rs +++ b/crates/router/src/core/payments/operations/payment_cancel.rs @@ -185,7 +185,6 @@ impl GetTracker, api::PaymentsCancelRequest> payment_link_data: None, incremental_authorization_details: None, authorizations: vec![], - frm_metadata: None, authentication: None, recurring_details: None, poll_config: None, diff --git a/crates/router/src/core/payments/operations/payment_capture.rs b/crates/router/src/core/payments/operations/payment_capture.rs index 29f9c14e549f..bf416b141be4 100644 --- a/crates/router/src/core/payments/operations/payment_capture.rs +++ b/crates/router/src/core/payments/operations/payment_capture.rs @@ -228,7 +228,6 @@ impl GetTracker, api::PaymentsCaptu payment_link_data: None, incremental_authorization_details: None, authorizations: vec![], - frm_metadata: None, authentication: None, recurring_details: None, poll_config: None, diff --git a/crates/router/src/core/payments/operations/payment_complete_authorize.rs b/crates/router/src/core/payments/operations/payment_complete_authorize.rs index 06627920d711..a9736516725c 100644 --- a/crates/router/src/core/payments/operations/payment_complete_authorize.rs +++ b/crates/router/src/core/payments/operations/payment_complete_authorize.rs @@ -303,7 +303,6 @@ impl GetTracker, api::PaymentsRequest> for Co incremental_authorization_details: None, authorizations: vec![], authentication: None, - frm_metadata: None, recurring_details, poll_config: None, }; diff --git a/crates/router/src/core/payments/operations/payment_confirm.rs b/crates/router/src/core/payments/operations/payment_confirm.rs index e48a6ce717f7..a0062fb81c35 100644 --- a/crates/router/src/core/payments/operations/payment_confirm.rs +++ b/crates/router/src/core/payments/operations/payment_confirm.rs @@ -393,6 +393,7 @@ impl GetTracker, api::PaymentsRequest> for Pa .attach_printable("Error converting feature_metadata to Value")? .or(payment_intent.feature_metadata); payment_intent.metadata = request.metadata.clone().or(payment_intent.metadata); + payment_intent.frm_metadata = request.frm_metadata.clone().or(payment_intent.frm_metadata); payment_intent.request_incremental_authorization = request .request_incremental_authorization .map(|request_incremental_authorization| { @@ -632,7 +633,6 @@ impl GetTracker, api::PaymentsRequest> for Pa payment_link_data: None, incremental_authorization_details: None, authorizations: vec![], - frm_metadata: request.frm_metadata.clone(), authentication: None, recurring_details, poll_config: None, @@ -1062,6 +1062,7 @@ impl UpdateTracker, api::PaymentsRequest> for Paymen .take(); let order_details = payment_data.payment_intent.order_details.clone(); let metadata = payment_data.payment_intent.metadata.clone(); + let frm_metadata = payment_data.payment_intent.frm_metadata.clone(); let authorized_amount = payment_data .surcharge_details .as_ref() @@ -1155,6 +1156,7 @@ impl UpdateTracker, api::PaymentsRequest> for Paymen let m_statement_descriptor_suffix = statement_descriptor_suffix.clone(); let m_order_details = order_details.clone(); let m_metadata = metadata.clone(); + let m_frm_metadata = frm_metadata.clone(); let m_db = state.clone().store; let m_storage_scheme = storage_scheme.to_string(); let session_expiry = m_payment_data_payment_intent.session_expiry; @@ -1184,6 +1186,7 @@ impl UpdateTracker, api::PaymentsRequest> for Paymen fingerprint_id: None, session_expiry, request_external_three_ds_authentication: None, + frm_metadata: m_frm_metadata, }, storage_scheme, ) diff --git a/crates/router/src/core/payments/operations/payment_create.rs b/crates/router/src/core/payments/operations/payment_create.rs index 7ad47deff4d8..0c68289fb5f0 100644 --- a/crates/router/src/core/payments/operations/payment_create.rs +++ b/crates/router/src/core/payments/operations/payment_create.rs @@ -446,7 +446,6 @@ impl GetTracker, api::PaymentsRequest> for Pa incremental_authorization_details: None, authorizations: vec![], authentication: None, - frm_metadata: request.frm_metadata.clone(), recurring_details, poll_config: None, }; @@ -1038,6 +1037,7 @@ impl PaymentCreate { session_expiry: Some(session_expiry), request_external_three_ds_authentication: request .request_external_three_ds_authentication, + frm_metadata: request.frm_metadata.clone(), }) } diff --git a/crates/router/src/core/payments/operations/payment_reject.rs b/crates/router/src/core/payments/operations/payment_reject.rs index e03f3ce6cb15..60061d655c1c 100644 --- a/crates/router/src/core/payments/operations/payment_reject.rs +++ b/crates/router/src/core/payments/operations/payment_reject.rs @@ -170,7 +170,6 @@ impl GetTracker, PaymentsCancelRequest> for P incremental_authorization_details: None, authorizations: vec![], authentication: None, - frm_metadata: None, recurring_details: None, poll_config: None, }; diff --git a/crates/router/src/core/payments/operations/payment_session.rs b/crates/router/src/core/payments/operations/payment_session.rs index db8b3131c588..e4d91d4a0681 100644 --- a/crates/router/src/core/payments/operations/payment_session.rs +++ b/crates/router/src/core/payments/operations/payment_session.rs @@ -197,7 +197,6 @@ impl GetTracker, api::PaymentsSessionRequest> incremental_authorization_details: None, authorizations: vec![], authentication: None, - frm_metadata: None, recurring_details: None, poll_config: None, }; diff --git a/crates/router/src/core/payments/operations/payment_start.rs b/crates/router/src/core/payments/operations/payment_start.rs index cf012aa09ebe..e5567e5b4748 100644 --- a/crates/router/src/core/payments/operations/payment_start.rs +++ b/crates/router/src/core/payments/operations/payment_start.rs @@ -182,7 +182,6 @@ impl GetTracker, api::PaymentsStartRequest> f incremental_authorization_details: None, authorizations: vec![], authentication: None, - frm_metadata: None, recurring_details: None, poll_config: None, }; diff --git a/crates/router/src/core/payments/operations/payment_status.rs b/crates/router/src/core/payments/operations/payment_status.rs index 4d5b87b671c1..0bedd70b921e 100644 --- a/crates/router/src/core/payments/operations/payment_status.rs +++ b/crates/router/src/core/payments/operations/payment_status.rs @@ -471,7 +471,6 @@ async fn get_tracker_for_sync< incremental_authorization_details: None, authorizations, authentication, - frm_metadata: None, recurring_details: None, poll_config: None, }; diff --git a/crates/router/src/core/payments/operations/payment_update.rs b/crates/router/src/core/payments/operations/payment_update.rs index 0809a810074f..e601b3ece7e8 100644 --- a/crates/router/src/core/payments/operations/payment_update.rs +++ b/crates/router/src/core/payments/operations/payment_update.rs @@ -250,6 +250,7 @@ impl GetTracker, api::PaymentsRequest> for Pa .attach_printable("Error converting feature_metadata to Value")? .or(payment_intent.feature_metadata); payment_intent.metadata = request.metadata.clone().or(payment_intent.metadata); + payment_intent.frm_metadata = request.frm_metadata.clone().or(payment_intent.frm_metadata); Self::populate_payment_intent_with_request(&mut payment_intent, request); let token = token.or_else(|| payment_attempt.payment_token.clone()); @@ -451,7 +452,6 @@ impl GetTracker, api::PaymentsRequest> for Pa incremental_authorization_details: None, authorizations: vec![], authentication: None, - frm_metadata: request.frm_metadata.clone(), recurring_details, poll_config: None, }; @@ -690,6 +690,7 @@ impl UpdateTracker, api::PaymentsRequest> for Paymen .clone(); let order_details = payment_data.payment_intent.order_details.clone(); let metadata = payment_data.payment_intent.metadata.clone(); + let frm_metadata = payment_data.payment_intent.frm_metadata.clone(); let session_expiry = payment_data.payment_intent.session_expiry; payment_data.payment_intent = state @@ -719,6 +720,7 @@ impl UpdateTracker, api::PaymentsRequest> for Paymen request_external_three_ds_authentication: payment_data .payment_intent .request_external_three_ds_authentication, + frm_metadata, }, storage_scheme, ) diff --git a/crates/router/src/core/payments/operations/payments_incremental_authorization.rs b/crates/router/src/core/payments/operations/payments_incremental_authorization.rs index c81773a7342d..ead62ae02eda 100644 --- a/crates/router/src/core/payments/operations/payments_incremental_authorization.rs +++ b/crates/router/src/core/payments/operations/payments_incremental_authorization.rs @@ -151,7 +151,6 @@ impl }), authorizations: vec![], authentication: None, - frm_metadata: None, recurring_details: None, poll_config: None, }; diff --git a/crates/router/src/core/payments/transformers.rs b/crates/router/src/core/payments/transformers.rs index 681f4028440a..77ec848fb442 100644 --- a/crates/router/src/core/payments/transformers.rs +++ b/crates/router/src/core/payments/transformers.rs @@ -781,6 +781,7 @@ where .set_customer(customer_details_response.clone()) .set_browser_info(payment_attempt.browser_info) .set_updated(Some(payment_intent.modified_at)) + .set_frm_metadata(payment_intent.frm_metadata) .to_owned(), headers, )) diff --git a/crates/router/src/utils/user/sample_data.rs b/crates/router/src/utils/user/sample_data.rs index 2216cb105548..8cf5caf5f1eb 100644 --- a/crates/router/src/utils/user/sample_data.rs +++ b/crates/router/src/utils/user/sample_data.rs @@ -218,6 +218,7 @@ pub async fn generate_sample_data( fingerprint_id: None, session_expiry: Some(session_expiry), request_external_three_ds_authentication: None, + frm_metadata: Default::default(), }; let payment_attempt = PaymentAttemptBatchNew { attempt_id: attempt_id.clone(), diff --git a/crates/storage_impl/src/mock_db/payment_intent.rs b/crates/storage_impl/src/mock_db/payment_intent.rs index 1dfb7ac9d91e..8ad010be1330 100644 --- a/crates/storage_impl/src/mock_db/payment_intent.rs +++ b/crates/storage_impl/src/mock_db/payment_intent.rs @@ -108,6 +108,7 @@ impl PaymentIntentInterface for MockDb { fingerprint_id: new.fingerprint_id, session_expiry: new.session_expiry, request_external_three_ds_authentication: new.request_external_three_ds_authentication, + frm_metadata: new.frm_metadata, }; payment_intents.push(payment_intent.clone()); Ok(payment_intent) diff --git a/crates/storage_impl/src/payments/payment_intent.rs b/crates/storage_impl/src/payments/payment_intent.rs index 2cbcbaaf01e6..c7592e48211c 100644 --- a/crates/storage_impl/src/payments/payment_intent.rs +++ b/crates/storage_impl/src/payments/payment_intent.rs @@ -83,6 +83,7 @@ impl PaymentIntentInterface for KVRouterStore { description: new.description.clone(), return_url: new.return_url.clone(), metadata: new.metadata.clone(), + frm_metadata: new.frm_metadata.clone(), connector_id: new.connector_id.clone(), shipping_address_id: new.shipping_address_id.clone(), billing_address_id: new.billing_address_id.clone(), @@ -806,6 +807,7 @@ impl DataModelExt for PaymentIntentNew { description: self.description, return_url: self.return_url, metadata: self.metadata, + frm_metadata: self.frm_metadata, connector_id: self.connector_id, shipping_address_id: self.shipping_address_id, billing_address_id: self.billing_address_id, @@ -852,6 +854,7 @@ impl DataModelExt for PaymentIntentNew { description: storage_model.description, return_url: storage_model.return_url, metadata: storage_model.metadata, + frm_metadata: storage_model.frm_metadata, connector_id: storage_model.connector_id, shipping_address_id: storage_model.shipping_address_id, billing_address_id: storage_model.billing_address_id, @@ -935,6 +938,7 @@ impl DataModelExt for PaymentIntent { fingerprint_id: self.fingerprint_id, session_expiry: self.session_expiry, request_external_three_ds_authentication: self.request_external_three_ds_authentication, + frm_metadata: self.frm_metadata, } } @@ -983,6 +987,7 @@ impl DataModelExt for PaymentIntent { session_expiry: storage_model.session_expiry, request_external_three_ds_authentication: storage_model .request_external_three_ds_authentication, + frm_metadata: storage_model.frm_metadata, } } } @@ -1070,6 +1075,7 @@ impl DataModelExt for PaymentIntentUpdate { fingerprint_id, session_expiry, request_external_three_ds_authentication, + frm_metadata, } => DieselPaymentIntentUpdate::Update { amount, currency, @@ -1091,6 +1097,7 @@ impl DataModelExt for PaymentIntentUpdate { fingerprint_id, session_expiry, request_external_three_ds_authentication, + frm_metadata, }, Self::PaymentAttemptAndAttemptCountUpdate { active_attempt_id, diff --git a/migrations/2024-05-10-074332_add_frm_metadata_to_payment_intent/down.sql b/migrations/2024-05-10-074332_add_frm_metadata_to_payment_intent/down.sql new file mode 100644 index 000000000000..17c9260fe30e --- /dev/null +++ b/migrations/2024-05-10-074332_add_frm_metadata_to_payment_intent/down.sql @@ -0,0 +1,2 @@ +-- This file should undo anything in `up.sql` +ALTER TABLE payment_intent DROP COLUMN IF EXISTS frm_metadata; \ No newline at end of file diff --git a/migrations/2024-05-10-074332_add_frm_metadata_to_payment_intent/up.sql b/migrations/2024-05-10-074332_add_frm_metadata_to_payment_intent/up.sql new file mode 100644 index 000000000000..6918adb82a8c --- /dev/null +++ b/migrations/2024-05-10-074332_add_frm_metadata_to_payment_intent/up.sql @@ -0,0 +1,2 @@ +-- Your SQL goes here +ALTER TABLE payment_intent ADD COLUMN IF NOT EXISTS frm_metadata JSONB DEFAULT NULL; \ No newline at end of file diff --git a/openapi/openapi_spec.json b/openapi/openapi_spec.json index 7b13762c50f5..a3cd88d2ee94 100644 --- a/openapi/openapi_spec.json +++ b/openapi/openapi_spec.json @@ -9304,8 +9304,7 @@ "type": "object", "description": "Details of FrmPaymentMethod are mentioned here... it should be passed in payment connector create api call, and stored in merchant_connector_table", "required": [ - "payment_method", - "payment_method_types" + "payment_method" ], "properties": { "payment_method": { @@ -9316,7 +9315,16 @@ "items": { "$ref": "#/components/schemas/FrmPaymentMethodType" }, - "description": "payment method types(credit, debit) that can be used in the payment" + "description": "payment method types(credit, debit) that can be used in the payment. This field is deprecated. It has not been removed to provide backward compatibility.", + "nullable": true + }, + "flow": { + "allOf": [ + { + "$ref": "#/components/schemas/FrmPreferredFlowTypes" + } + ], + "nullable": true } }, "additionalProperties": false @@ -13849,6 +13857,7 @@ "minimum": 0 }, "frm_metadata": { + "type": "object", "description": "additional data related to some frm connectors", "nullable": true }, @@ -14221,6 +14230,7 @@ "minimum": 0 }, "frm_metadata": { + "type": "object", "description": "additional data related to some frm connectors", "nullable": true }, @@ -14727,6 +14737,7 @@ "minimum": 0 }, "frm_metadata": { + "type": "object", "description": "additional data related to some frm connectors", "nullable": true }, @@ -15268,6 +15279,11 @@ "description": "Date time at which payment was updated", "example": "2022-09-10T10:11:12Z", "nullable": true + }, + "frm_metadata": { + "type": "object", + "description": "You can specify up to 50 keys, with key names up to 40 characters long and values up to 500 characters long. FRM Metadata is useful for storing additional, structured information on an object related to FRM.", + "nullable": true } } }, @@ -15735,6 +15751,7 @@ "minimum": 0 }, "frm_metadata": { + "type": "object", "description": "additional data related to some frm connectors", "nullable": true },