mirror of
https://github.com/juspay/hyperswitch.git
synced 2026-03-13 09:02:06 +08:00
refactor(FRM): refactor frm configs (#4581)
Co-authored-by: hyperswitch-bot[bot] <148525504+hyperswitch-bot[bot]@users.noreply.github.com>
This commit is contained in:
@@ -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<common_enums::PaymentMethod>,
|
||||
///payment method types(credit, debit) that can be used in the payment
|
||||
pub payment_method_types: Vec<FrmPaymentMethodType>,
|
||||
///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<Vec<FrmPaymentMethodType>>,
|
||||
///frm flow type to be used, can be pre/post
|
||||
#[schema(value_type = Option<FrmPreferredFlowTypes>)]
|
||||
pub flow: Option<api_enums::FrmPreferredFlowTypes>,
|
||||
}
|
||||
|
||||
///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<Vec<common_enums::CardNetwork>>,
|
||||
///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
|
||||
|
||||
@@ -464,7 +464,8 @@ pub struct PaymentsRequest {
|
||||
pub session_expiry: Option<u32>,
|
||||
|
||||
/// additional data related to some frm connectors
|
||||
pub frm_metadata: Option<serde_json::Value>,
|
||||
#[schema(value_type = Option<Object>, example = r#"{ "coverage_request" : "fraud", "fulfillment_method" : "delivery" }"#)]
|
||||
pub frm_metadata: Option<pii::SecretSerdeValue>,
|
||||
|
||||
/// 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<PrimitiveDateTime>,
|
||||
|
||||
/// 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<Object>, example = r#"{ "fulfillment_method" : "deliver", "coverage_request" : "fraud" }"#)]
|
||||
pub frm_metadata: Option<pii::SecretSerdeValue>,
|
||||
}
|
||||
|
||||
#[derive(Setter, Clone, Default, Debug, PartialEq, serde::Serialize, ToSchema)]
|
||||
|
||||
@@ -58,6 +58,7 @@ pub struct PaymentIntent {
|
||||
pub session_expiry: Option<PrimitiveDateTime>,
|
||||
pub fingerprint_id: Option<String>,
|
||||
pub request_external_three_ds_authentication: Option<bool>,
|
||||
pub frm_metadata: Option<pii::SecretSerdeValue>,
|
||||
}
|
||||
|
||||
#[derive(
|
||||
@@ -111,6 +112,7 @@ pub struct PaymentIntentNew {
|
||||
pub session_expiry: Option<PrimitiveDateTime>,
|
||||
pub fingerprint_id: Option<String>,
|
||||
pub request_external_three_ds_authentication: Option<bool>,
|
||||
pub frm_metadata: Option<pii::SecretSerdeValue>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
@@ -167,6 +169,7 @@ pub enum PaymentIntentUpdate {
|
||||
session_expiry: Option<PrimitiveDateTime>,
|
||||
fingerprint_id: Option<String>,
|
||||
request_external_three_ds_authentication: Option<bool>,
|
||||
frm_metadata: Option<pii::SecretSerdeValue>,
|
||||
},
|
||||
PaymentAttemptAndAttemptCountUpdate {
|
||||
active_attempt_id: String,
|
||||
@@ -236,6 +239,7 @@ pub struct PaymentIntentUpdateInternal {
|
||||
pub session_expiry: Option<PrimitiveDateTime>,
|
||||
pub fingerprint_id: Option<String>,
|
||||
pub request_external_three_ds_authentication: Option<bool>,
|
||||
pub frm_metadata: Option<pii::SecretSerdeValue>,
|
||||
}
|
||||
|
||||
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<PaymentIntentUpdate> 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<PaymentIntentUpdate> 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::<super::PaymentIntent>(serialized_payment_intent);
|
||||
|
||||
@@ -846,6 +846,7 @@ diesel::table! {
|
||||
#[max_length = 64]
|
||||
fingerprint_id -> Nullable<Varchar>,
|
||||
request_external_three_ds_authentication -> Nullable<Bool>,
|
||||
frm_metadata -> Nullable<Jsonb>,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -60,4 +60,5 @@ pub struct PaymentIntent {
|
||||
#[serde(with = "common_utils::custom_serde::iso8601::option")]
|
||||
pub session_expiry: Option<PrimitiveDateTime>,
|
||||
pub request_external_three_ds_authentication: Option<bool>,
|
||||
pub frm_metadata: Option<pii::SecretSerdeValue>,
|
||||
}
|
||||
|
||||
@@ -81,6 +81,7 @@ pub struct PaymentIntentNew {
|
||||
pub description: Option<String>,
|
||||
pub return_url: Option<String>,
|
||||
pub metadata: Option<pii::SecretSerdeValue>,
|
||||
pub frm_metadata: Option<pii::SecretSerdeValue>,
|
||||
pub connector_id: Option<String>,
|
||||
pub shipping_address_id: Option<String>,
|
||||
pub billing_address_id: Option<String>,
|
||||
@@ -164,6 +165,7 @@ pub enum PaymentIntentUpdate {
|
||||
statement_descriptor_suffix: Option<String>,
|
||||
order_details: Option<Vec<pii::SecretSerdeValue>>,
|
||||
metadata: Option<pii::SecretSerdeValue>,
|
||||
frm_metadata: Option<pii::SecretSerdeValue>,
|
||||
payment_confirm_source: Option<storage_enums::PaymentSource>,
|
||||
updated_by: String,
|
||||
fingerprint_id: Option<String>,
|
||||
@@ -237,6 +239,7 @@ pub struct PaymentIntentUpdateInternal {
|
||||
pub fingerprint_id: Option<String>,
|
||||
pub session_expiry: Option<PrimitiveDateTime>,
|
||||
pub request_external_three_ds_authentication: Option<bool>,
|
||||
pub frm_metadata: Option<pii::SecretSerdeValue>,
|
||||
}
|
||||
|
||||
impl From<PaymentIntentUpdate> for PaymentIntentUpdateInternal {
|
||||
@@ -263,6 +266,7 @@ impl From<PaymentIntentUpdate> 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<PaymentIntentUpdate> for PaymentIntentUpdateInternal {
|
||||
fingerprint_id,
|
||||
session_expiry,
|
||||
request_external_three_ds_authentication,
|
||||
frm_metadata,
|
||||
..Default::default()
|
||||
},
|
||||
PaymentIntentUpdate::MetadataUpdate {
|
||||
|
||||
@@ -57,7 +57,7 @@ pub struct RouterData<Flow, Request, Response> {
|
||||
/// Contains apple pay flow type simplified or manual
|
||||
pub apple_pay_flow: Option<common_enums::enums::ApplePayFlow>,
|
||||
|
||||
pub frm_metadata: Option<serde_json::Value>,
|
||||
pub frm_metadata: Option<common_utils::pii::SecretSerdeValue>,
|
||||
|
||||
pub dispute_id: Option<String>,
|
||||
pub refund_id: Option<String>,
|
||||
|
||||
@@ -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()?;
|
||||
|
||||
@@ -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::<Result<Vec<_>, _>>()?;
|
||||
|
||||
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::<Vec<_>>()
|
||||
.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>(
|
||||
"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::<Vec<_>>();
|
||||
filtered_pm_config_by_pmt
|
||||
})
|
||||
.collect::<Vec<_>>()
|
||||
.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<F> =
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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<F: Send + Clone> Domain<F> for FraudCheckPost {
|
||||
req_state: ReqState,
|
||||
frm_data: &mut FrmData,
|
||||
merchant_account: &domain::MerchantAccount,
|
||||
frm_configs: FrmConfigsObject,
|
||||
_frm_configs: FrmConfigsObject,
|
||||
frm_suggestion: &mut Option<FrmSuggestion>,
|
||||
key_store: domain::MerchantKeyStore,
|
||||
payment_data: &mut payments::PaymentData<F>,
|
||||
@@ -194,7 +194,6 @@ impl<F: Send + Clone> Domain<F> for FraudCheckPost {
|
||||
_should_continue_capture: &mut bool,
|
||||
) -> RouterResult<Option<FrmData>> {
|
||||
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<F: Send + Clone> Domain<F> 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<F: Clone + Send> UpdateTracker<FrmData, F> 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<F: Clone + Send> UpdateTracker<FrmData, F> 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,
|
||||
|
||||
@@ -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<Vec<api_models::payments::OrderDetailsWithAmount>>,
|
||||
pub refund: Option<RefundResponse>,
|
||||
pub frm_metadata: Option<serde_json::Value>,
|
||||
pub frm_metadata: Option<SecretSerdeValue>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
@@ -80,15 +80,13 @@ pub struct PaymentToFrmData {
|
||||
pub address: PaymentAddress,
|
||||
pub connector_details: ConnectorDetailsCore,
|
||||
pub order_details: Option<Vec<api_models::payments::OrderDetailsWithAmount>>,
|
||||
pub frm_metadata: Option<serde_json::Value>,
|
||||
pub frm_metadata: Option<SecretSerdeValue>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct FrmConfigsObject {
|
||||
pub frm_enabled_pm: Option<PaymentMethod>,
|
||||
pub frm_enabled_pm_type: Option<PaymentMethodType>,
|
||||
pub frm_enabled_gateway: Option<api_models::enums::Connector>,
|
||||
pub frm_action: api_enums::FrmAction,
|
||||
pub frm_preferred_flow_type: api_enums::FrmPreferredFlowTypes,
|
||||
}
|
||||
|
||||
|
||||
@@ -2450,7 +2450,6 @@ where
|
||||
pub incremental_authorization_details: Option<IncrementalAuthorizationDetails>,
|
||||
pub authorizations: Vec<diesel_models::authorization::Authorization>,
|
||||
pub authentication: Option<storage::Authentication>,
|
||||
pub frm_metadata: Option<serde_json::Value>,
|
||||
pub recurring_details: Option<RecurringDetails>,
|
||||
pub poll_config: Option<router_types::PollConfig>,
|
||||
}
|
||||
|
||||
@@ -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())
|
||||
|
||||
@@ -172,7 +172,6 @@ impl<F: Send + Clone> GetTracker<F, PaymentData<F>, api::PaymentsCaptureRequest>
|
||||
payment_link_data: None,
|
||||
incremental_authorization_details: None,
|
||||
authorizations: vec![],
|
||||
frm_metadata: None,
|
||||
authentication: None,
|
||||
recurring_details: None,
|
||||
poll_config: None,
|
||||
|
||||
@@ -185,7 +185,6 @@ impl<F: Send + Clone> GetTracker<F, PaymentData<F>, api::PaymentsCancelRequest>
|
||||
payment_link_data: None,
|
||||
incremental_authorization_details: None,
|
||||
authorizations: vec![],
|
||||
frm_metadata: None,
|
||||
authentication: None,
|
||||
recurring_details: None,
|
||||
poll_config: None,
|
||||
|
||||
@@ -228,7 +228,6 @@ impl<F: Send + Clone> GetTracker<F, payments::PaymentData<F>, api::PaymentsCaptu
|
||||
payment_link_data: None,
|
||||
incremental_authorization_details: None,
|
||||
authorizations: vec![],
|
||||
frm_metadata: None,
|
||||
authentication: None,
|
||||
recurring_details: None,
|
||||
poll_config: None,
|
||||
|
||||
@@ -303,7 +303,6 @@ impl<F: Send + Clone> GetTracker<F, PaymentData<F>, api::PaymentsRequest> for Co
|
||||
incremental_authorization_details: None,
|
||||
authorizations: vec![],
|
||||
authentication: None,
|
||||
frm_metadata: None,
|
||||
recurring_details,
|
||||
poll_config: None,
|
||||
};
|
||||
|
||||
@@ -393,6 +393,7 @@ impl<F: Send + Clone> GetTracker<F, PaymentData<F>, 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<F: Send + Clone> GetTracker<F, PaymentData<F>, 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<F: Clone> UpdateTracker<F, PaymentData<F>, 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<F: Clone> UpdateTracker<F, PaymentData<F>, 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<F: Clone> UpdateTracker<F, PaymentData<F>, api::PaymentsRequest> for Paymen
|
||||
fingerprint_id: None,
|
||||
session_expiry,
|
||||
request_external_three_ds_authentication: None,
|
||||
frm_metadata: m_frm_metadata,
|
||||
},
|
||||
storage_scheme,
|
||||
)
|
||||
|
||||
@@ -446,7 +446,6 @@ impl<F: Send + Clone> GetTracker<F, PaymentData<F>, 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(),
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -170,7 +170,6 @@ impl<F: Send + Clone> GetTracker<F, PaymentData<F>, PaymentsCancelRequest> for P
|
||||
incremental_authorization_details: None,
|
||||
authorizations: vec![],
|
||||
authentication: None,
|
||||
frm_metadata: None,
|
||||
recurring_details: None,
|
||||
poll_config: None,
|
||||
};
|
||||
|
||||
@@ -197,7 +197,6 @@ impl<F: Send + Clone> GetTracker<F, PaymentData<F>, api::PaymentsSessionRequest>
|
||||
incremental_authorization_details: None,
|
||||
authorizations: vec![],
|
||||
authentication: None,
|
||||
frm_metadata: None,
|
||||
recurring_details: None,
|
||||
poll_config: None,
|
||||
};
|
||||
|
||||
@@ -182,7 +182,6 @@ impl<F: Send + Clone> GetTracker<F, PaymentData<F>, api::PaymentsStartRequest> f
|
||||
incremental_authorization_details: None,
|
||||
authorizations: vec![],
|
||||
authentication: None,
|
||||
frm_metadata: None,
|
||||
recurring_details: None,
|
||||
poll_config: None,
|
||||
};
|
||||
|
||||
@@ -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,
|
||||
};
|
||||
|
||||
@@ -250,6 +250,7 @@ impl<F: Send + Clone> GetTracker<F, PaymentData<F>, 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<F: Send + Clone> GetTracker<F, PaymentData<F>, 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<F: Clone> UpdateTracker<F, PaymentData<F>, 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<F: Clone> UpdateTracker<F, PaymentData<F>, api::PaymentsRequest> for Paymen
|
||||
request_external_three_ds_authentication: payment_data
|
||||
.payment_intent
|
||||
.request_external_three_ds_authentication,
|
||||
frm_metadata,
|
||||
},
|
||||
storage_scheme,
|
||||
)
|
||||
|
||||
@@ -151,7 +151,6 @@ impl<F: Send + Clone>
|
||||
}),
|
||||
authorizations: vec![],
|
||||
authentication: None,
|
||||
frm_metadata: None,
|
||||
recurring_details: None,
|
||||
poll_config: None,
|
||||
};
|
||||
|
||||
@@ -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,
|
||||
))
|
||||
|
||||
@@ -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(),
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -83,6 +83,7 @@ impl<T: DatabaseStore> PaymentIntentInterface for KVRouterStore<T> {
|
||||
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,
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
-- This file should undo anything in `up.sql`
|
||||
ALTER TABLE payment_intent DROP COLUMN IF EXISTS frm_metadata;
|
||||
@@ -0,0 +1,2 @@
|
||||
-- Your SQL goes here
|
||||
ALTER TABLE payment_intent ADD COLUMN IF NOT EXISTS frm_metadata JSONB DEFAULT NULL;
|
||||
@@ -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
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user