diff --git a/crates/api_models/src/payments.rs b/crates/api_models/src/payments.rs index 9150697aa1..90792e9633 100644 --- a/crates/api_models/src/payments.rs +++ b/crates/api_models/src/payments.rs @@ -1180,6 +1180,12 @@ pub struct PaymentsRequest { /// If enabled, provides whole connector response pub all_keys_required: Option, + + /// Indicates whether the `payment_id` was provided by the merchant + /// This value is inferred internally based on the request + #[serde(skip_deserializing)] + #[remove_in(PaymentsUpdateRequest, PaymentsCreateRequest, PaymentsConfirmRequest)] + pub is_payment_id_from_merchant: bool, } #[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize, ToSchema)] diff --git a/crates/diesel_models/src/payment_attempt.rs b/crates/diesel_models/src/payment_attempt.rs index 95bb5a875d..361db5f135 100644 --- a/crates/diesel_models/src/payment_attempt.rs +++ b/crates/diesel_models/src/payment_attempt.rs @@ -206,6 +206,7 @@ pub struct PaymentAttempt { pub created_by: Option, pub setup_future_usage_applied: Option, pub routing_approach: Option, + pub connector_request_reference_id: Option, } #[cfg(feature = "v1")] @@ -426,6 +427,7 @@ pub struct PaymentAttemptNew { pub created_by: Option, pub setup_future_usage_applied: Option, pub routing_approach: Option, + pub connector_request_reference_id: Option, } #[cfg(feature = "v1")] @@ -501,6 +503,7 @@ pub enum PaymentAttemptUpdate { connector_mandate_detail: Option, card_discovery: Option, routing_approach: Option, + connector_request_reference_id: Option, }, VoidUpdate { status: storage_enums::AttemptStatus, @@ -1052,6 +1055,7 @@ pub struct PaymentAttemptUpdateInternal { pub issuer_error_message: Option, pub setup_future_usage_applied: Option, pub routing_approach: Option, + pub connector_request_reference_id: Option, } #[cfg(feature = "v1")] @@ -1242,6 +1246,7 @@ impl PaymentAttemptUpdate { issuer_error_message, setup_future_usage_applied, routing_approach, + connector_request_reference_id, } = PaymentAttemptUpdateInternal::from(self).populate_derived_fields(&source); PaymentAttempt { amount: amount.unwrap_or(source.amount), @@ -1309,6 +1314,8 @@ impl PaymentAttemptUpdate { setup_future_usage_applied: setup_future_usage_applied .or(source.setup_future_usage_applied), routing_approach: routing_approach.or(source.routing_approach), + connector_request_reference_id: connector_request_reference_id + .or(source.connector_request_reference_id), ..source } } @@ -2368,6 +2375,7 @@ impl From for PaymentAttemptUpdateInternal { issuer_error_message: None, setup_future_usage_applied: None, routing_approach: None, + connector_request_reference_id: None, }, PaymentAttemptUpdate::AuthenticationTypeUpdate { authentication_type, @@ -2431,6 +2439,7 @@ impl From for PaymentAttemptUpdateInternal { issuer_error_message: None, setup_future_usage_applied: None, routing_approach: None, + connector_request_reference_id: None, }, PaymentAttemptUpdate::ConfirmUpdate { amount, @@ -2468,6 +2477,7 @@ impl From for PaymentAttemptUpdateInternal { connector_mandate_detail, card_discovery, routing_approach, + connector_request_reference_id, } => Self { amount: Some(amount), currency: Some(currency), @@ -2527,6 +2537,7 @@ impl From for PaymentAttemptUpdateInternal { issuer_error_message: None, setup_future_usage_applied: None, routing_approach, + connector_request_reference_id, }, PaymentAttemptUpdate::VoidUpdate { status, @@ -2591,6 +2602,7 @@ impl From for PaymentAttemptUpdateInternal { issuer_error_message: None, setup_future_usage_applied: None, routing_approach: None, + connector_request_reference_id: None, }, PaymentAttemptUpdate::RejectUpdate { status, @@ -2656,6 +2668,7 @@ impl From for PaymentAttemptUpdateInternal { issuer_error_message: None, setup_future_usage_applied: None, routing_approach: None, + connector_request_reference_id: None, }, PaymentAttemptUpdate::BlocklistUpdate { status, @@ -2721,6 +2734,7 @@ impl From for PaymentAttemptUpdateInternal { issuer_error_message: None, setup_future_usage_applied: None, routing_approach: None, + connector_request_reference_id: None, }, PaymentAttemptUpdate::ConnectorMandateDetailUpdate { connector_mandate_detail, @@ -2784,6 +2798,7 @@ impl From for PaymentAttemptUpdateInternal { issuer_error_message: None, setup_future_usage_applied: None, routing_approach: None, + connector_request_reference_id: None, }, PaymentAttemptUpdate::PaymentMethodDetailsUpdate { payment_method_id, @@ -2847,6 +2862,7 @@ impl From for PaymentAttemptUpdateInternal { issuer_error_message: None, setup_future_usage_applied: None, routing_approach: None, + connector_request_reference_id: None, }, PaymentAttemptUpdate::ResponseUpdate { status, @@ -2938,6 +2954,7 @@ impl From for PaymentAttemptUpdateInternal { issuer_error_message: None, setup_future_usage_applied, routing_approach: None, + connector_request_reference_id: None, } } PaymentAttemptUpdate::ErrorUpdate { @@ -3020,6 +3037,7 @@ impl From for PaymentAttemptUpdateInternal { charges: None, setup_future_usage_applied: None, routing_approach: None, + connector_request_reference_id: None, } } PaymentAttemptUpdate::StatusUpdate { status, updated_by } => Self { @@ -3081,6 +3099,7 @@ impl From for PaymentAttemptUpdateInternal { issuer_error_message: None, setup_future_usage_applied: None, routing_approach: None, + connector_request_reference_id: None, }, PaymentAttemptUpdate::UpdateTrackers { payment_token, @@ -3151,6 +3170,7 @@ impl From for PaymentAttemptUpdateInternal { issuer_error_message: None, setup_future_usage_applied: None, routing_approach, + connector_request_reference_id: None, }, PaymentAttemptUpdate::UnresolvedResponseUpdate { status, @@ -3227,6 +3247,7 @@ impl From for PaymentAttemptUpdateInternal { issuer_error_message: None, setup_future_usage_applied: None, routing_approach: None, + connector_request_reference_id: None, } } PaymentAttemptUpdate::PreprocessingUpdate { @@ -3302,6 +3323,7 @@ impl From for PaymentAttemptUpdateInternal { issuer_error_message: None, setup_future_usage_applied: None, routing_approach: None, + connector_request_reference_id: None, } } PaymentAttemptUpdate::CaptureUpdate { @@ -3367,6 +3389,7 @@ impl From for PaymentAttemptUpdateInternal { issuer_error_message: None, setup_future_usage_applied: None, routing_approach: None, + connector_request_reference_id: None, }, PaymentAttemptUpdate::AmountToCaptureUpdate { status, @@ -3431,6 +3454,7 @@ impl From for PaymentAttemptUpdateInternal { issuer_error_message: None, setup_future_usage_applied: None, routing_approach: None, + connector_request_reference_id: None, }, PaymentAttemptUpdate::ConnectorResponse { authentication_data, @@ -3504,6 +3528,7 @@ impl From for PaymentAttemptUpdateInternal { issuer_error_message: None, setup_future_usage_applied: None, routing_approach: None, + connector_request_reference_id: None, } } PaymentAttemptUpdate::IncrementalAuthorizationAmountUpdate { @@ -3568,6 +3593,7 @@ impl From for PaymentAttemptUpdateInternal { issuer_error_message: None, setup_future_usage_applied: None, routing_approach: None, + connector_request_reference_id: None, }, PaymentAttemptUpdate::AuthenticationUpdate { status, @@ -3634,6 +3660,7 @@ impl From for PaymentAttemptUpdateInternal { issuer_error_message: None, setup_future_usage_applied: None, routing_approach: None, + connector_request_reference_id: None, }, PaymentAttemptUpdate::ManualUpdate { status, @@ -3709,6 +3736,7 @@ impl From for PaymentAttemptUpdateInternal { issuer_error_message: None, setup_future_usage_applied: None, routing_approach: None, + connector_request_reference_id: None, } } PaymentAttemptUpdate::PostSessionTokensUpdate { @@ -3773,6 +3801,7 @@ impl From for PaymentAttemptUpdateInternal { issuer_error_message: None, setup_future_usage_applied: None, routing_approach: None, + connector_request_reference_id: None, }, } } diff --git a/crates/diesel_models/src/payment_intent.rs b/crates/diesel_models/src/payment_intent.rs index bcaefa512d..72ee13a95b 100644 --- a/crates/diesel_models/src/payment_intent.rs +++ b/crates/diesel_models/src/payment_intent.rs @@ -63,6 +63,7 @@ pub struct PaymentIntent { pub processor_merchant_id: Option, pub created_by: Option, pub is_iframe_redirection_enabled: Option, + pub is_payment_id_from_merchant: Option, pub merchant_reference_id: Option, pub billing_address: Option, pub shipping_address: Option, @@ -155,6 +156,7 @@ pub struct PaymentIntent { pub created_by: Option, pub is_iframe_redirection_enabled: Option, pub extended_return_url: Option, + pub is_payment_id_from_merchant: Option, } #[derive(Clone, Debug, serde::Deserialize, serde::Serialize, diesel::AsExpression, PartialEq)] @@ -352,6 +354,7 @@ pub struct PaymentIntentNew { pub routing_algorithm_id: Option, pub created_by: Option, pub is_iframe_redirection_enabled: Option, + pub is_payment_id_from_merchant: Option, } #[cfg(feature = "v1")] @@ -427,6 +430,7 @@ pub struct PaymentIntentNew { pub created_by: Option, pub is_iframe_redirection_enabled: Option, pub extended_return_url: Option, + pub is_payment_id_from_merchant: Option, } #[cfg(feature = "v2")] @@ -744,6 +748,7 @@ impl PaymentIntentUpdateInternal { frm_merchant_decision: source.frm_merchant_decision, enable_payment_link: source.enable_payment_link, id: source.id, + is_payment_id_from_merchant: source.is_payment_id_from_merchant, } } } diff --git a/crates/diesel_models/src/schema.rs b/crates/diesel_models/src/schema.rs index 1629780458..7d5d7c95cb 100644 --- a/crates/diesel_models/src/schema.rs +++ b/crates/diesel_models/src/schema.rs @@ -959,6 +959,8 @@ diesel::table! { created_by -> Nullable, setup_future_usage_applied -> Nullable, routing_approach -> Nullable, + #[max_length = 255] + connector_request_reference_id -> Nullable, } } @@ -1053,6 +1055,7 @@ diesel::table! { is_iframe_redirection_enabled -> Nullable, #[max_length = 2048] extended_return_url -> Nullable, + is_payment_id_from_merchant -> Nullable, } } diff --git a/crates/diesel_models/src/schema_v2.rs b/crates/diesel_models/src/schema_v2.rs index c931cec977..f41585eca3 100644 --- a/crates/diesel_models/src/schema_v2.rs +++ b/crates/diesel_models/src/schema_v2.rs @@ -987,6 +987,7 @@ diesel::table! { #[max_length = 255] created_by -> Nullable, is_iframe_redirection_enabled -> Nullable, + is_payment_id_from_merchant -> Nullable, #[max_length = 64] merchant_reference_id -> Nullable, billing_address -> Nullable, diff --git a/crates/diesel_models/src/user/sample_data.rs b/crates/diesel_models/src/user/sample_data.rs index 743d175e34..f892ac8e86 100644 --- a/crates/diesel_models/src/user/sample_data.rs +++ b/crates/diesel_models/src/user/sample_data.rs @@ -217,6 +217,7 @@ pub struct PaymentAttemptBatchNew { pub created_by: Option, pub setup_future_usage_applied: Option, pub routing_approach: Option, + pub connector_request_reference_id: Option, } #[cfg(feature = "v1")] @@ -303,6 +304,7 @@ impl PaymentAttemptBatchNew { created_by: self.created_by, setup_future_usage_applied: self.setup_future_usage_applied, routing_approach: self.routing_approach, + connector_request_reference_id: self.connector_request_reference_id, } } } diff --git a/crates/hyperswitch_connectors/src/connectors/redsys.rs b/crates/hyperswitch_connectors/src/connectors/redsys.rs index f7589ba0c5..96af23ad7b 100644 --- a/crates/hyperswitch_connectors/src/connectors/redsys.rs +++ b/crates/hyperswitch_connectors/src/connectors/redsys.rs @@ -903,4 +903,20 @@ impl ConnectorSpecifications for Redsys { fn get_supported_webhook_flows(&self) -> Option<&'static [common_enums::EventClass]> { Some(&REDSYS_SUPPORTED_WEBHOOK_FLOWS) } + + #[cfg(feature = "v1")] + fn generate_connector_request_reference_id( + &self, + payment_intent: &hyperswitch_domain_models::payments::PaymentIntent, + payment_attempt: &hyperswitch_domain_models::payments::payment_attempt::PaymentAttempt, + is_config_enabled_to_send_payment_id_as_connector_request_id: bool, + ) -> String { + if is_config_enabled_to_send_payment_id_as_connector_request_id + && payment_intent.is_payment_id_from_merchant.unwrap_or(false) + { + payment_attempt.payment_id.get_string_repr().to_owned() + } else { + connector_utils::generate_12_digit_number().to_string() + } + } } diff --git a/crates/hyperswitch_connectors/src/connectors/redsys/transformers.rs b/crates/hyperswitch_connectors/src/connectors/redsys/transformers.rs index efd1f74fbe..07e2dc14a7 100644 --- a/crates/hyperswitch_connectors/src/connectors/redsys/transformers.rs +++ b/crates/hyperswitch_connectors/src/connectors/redsys/transformers.rs @@ -456,36 +456,48 @@ impl TryFrom<&RedsysRouterData<&PaymentsPreProcessingRouterData>> for RedsysTran connector: "redsys", })? }; - let redsys_preprocessing_request = - if item.router_data.auth_type == enums::AuthenticationType::ThreeDs { - let ds_merchant_emv3ds = Some(EmvThreedsData::new(RedsysThreeDsInfo::CardData)); - let ds_merchant_transactiontype = if item.router_data.request.is_auto_capture()? { - RedsysTransactionType::Payment - } else { - RedsysTransactionType::Preauthorization - }; - let ds_merchant_order = connector_utils::generate_12_digit_number().to_string(); - let card_data = - RedsysCardData::try_from(&item.router_data.request.payment_method_data)?; - Ok(PaymentsRequest { - ds_merchant_emv3ds, - ds_merchant_transactiontype, - ds_merchant_currency: item.currency.iso_4217().to_owned(), - ds_merchant_pan: card_data.card_number, - ds_merchant_merchantcode: auth.merchant_id.clone(), - ds_merchant_terminal: auth.terminal_id.clone(), - ds_merchant_order, - ds_merchant_amount: item.amount.clone(), - ds_merchant_expirydate: card_data.expiry_date, - ds_merchant_cvv2: card_data.cvv2, - }) + let redsys_preprocessing_request = if item.router_data.auth_type + == enums::AuthenticationType::ThreeDs + { + let ds_merchant_emv3ds = Some(EmvThreedsData::new(RedsysThreeDsInfo::CardData)); + let ds_merchant_transactiontype = if item.router_data.request.is_auto_capture()? { + RedsysTransactionType::Payment } else { - Err(errors::ConnectorError::FlowNotSupported { - flow: "PreProcessing".to_string(), - connector: "redsys".to_string(), + RedsysTransactionType::Preauthorization + }; + + let ds_merchant_order = if item.router_data.connector_request_reference_id.len() <= 12 { + Ok(item.router_data.connector_request_reference_id.clone()) + } else { + Err(errors::ConnectorError::MaxFieldLengthViolated { + connector: "Redsys".to_string(), + field_name: "ds_merchant_order".to_string(), + max_length: 12, + received_length: item.router_data.connector_request_reference_id.len(), }) }?; + let card_data = + RedsysCardData::try_from(&item.router_data.request.payment_method_data)?; + Ok(PaymentsRequest { + ds_merchant_emv3ds, + ds_merchant_transactiontype, + ds_merchant_currency: item.currency.iso_4217().to_owned(), + ds_merchant_pan: card_data.card_number, + ds_merchant_merchantcode: auth.merchant_id.clone(), + ds_merchant_terminal: auth.terminal_id.clone(), + ds_merchant_order, + ds_merchant_amount: item.amount.clone(), + ds_merchant_expirydate: card_data.expiry_date, + ds_merchant_cvv2: card_data.cvv2, + }) + } else { + Err(errors::ConnectorError::FlowNotSupported { + flow: "PreProcessing".to_string(), + connector: "redsys".to_string(), + }) + }?; + Self::try_from((&redsys_preprocessing_request, &auth)) } } diff --git a/crates/hyperswitch_connectors/src/utils.rs b/crates/hyperswitch_connectors/src/utils.rs index 1353d06819..fffe734d80 100644 --- a/crates/hyperswitch_connectors/src/utils.rs +++ b/crates/hyperswitch_connectors/src/utils.rs @@ -6210,6 +6210,7 @@ pub(crate) fn convert_payment_authorize_router_response( authentication_id: data.authentication_id.clone(), psd2_sca_exemption_type: data.psd2_sca_exemption_type, raw_connector_response: data.raw_connector_response.clone(), + is_payment_id_from_merchant: data.is_payment_id_from_merchant, } } diff --git a/crates/hyperswitch_domain_models/src/payments.rs b/crates/hyperswitch_domain_models/src/payments.rs index ef29b08499..0445b2b9a2 100644 --- a/crates/hyperswitch_domain_models/src/payments.rs +++ b/crates/hyperswitch_domain_models/src/payments.rs @@ -115,6 +115,7 @@ pub struct PaymentIntent { pub force_3ds_challenge: Option, pub force_3ds_challenge_trigger: Option, pub is_iframe_redirection_enabled: Option, + pub is_payment_id_from_merchant: Option, } impl PaymentIntent { @@ -502,6 +503,10 @@ pub struct PaymentIntent { /// Indicates if the redirection has to open in the iframe pub is_iframe_redirection_enabled: Option, + + /// Indicates whether the payment_id was provided by the merchant (true), + /// or generated internally by Hyperswitch (false) + pub is_payment_id_from_merchant: Option, } #[cfg(feature = "v2")] @@ -668,6 +673,7 @@ impl PaymentIntent { processor_merchant_id: merchant_context.get_merchant_account().get_id().clone(), created_by: None, is_iframe_redirection_enabled: None, + is_payment_id_from_merchant: None, }) } diff --git a/crates/hyperswitch_domain_models/src/payments/payment_attempt.rs b/crates/hyperswitch_domain_models/src/payments/payment_attempt.rs index 9078f1d49d..2b568b70af 100644 --- a/crates/hyperswitch_domain_models/src/payments/payment_attempt.rs +++ b/crates/hyperswitch_domain_models/src/payments/payment_attempt.rs @@ -905,6 +905,7 @@ pub struct PaymentAttempt { pub created_by: Option, pub setup_future_usage_applied: Option, pub routing_approach: Option, + pub connector_request_reference_id: Option, } #[cfg(feature = "v1")] @@ -1160,6 +1161,7 @@ pub struct PaymentAttemptNew { pub created_by: Option, pub setup_future_usage_applied: Option, pub routing_approach: Option, + pub connector_request_reference_id: Option, } #[cfg(feature = "v1")] @@ -1229,6 +1231,7 @@ pub enum PaymentAttemptUpdate { connector_mandate_detail: Option, card_discovery: Option, routing_approach: Option, + connector_request_reference_id: Option, }, RejectUpdate { status: storage_enums::AttemptStatus, @@ -1490,6 +1493,7 @@ impl PaymentAttemptUpdate { connector_mandate_detail, card_discovery, routing_approach, + connector_request_reference_id, } => DieselPaymentAttemptUpdate::ConfirmUpdate { amount: net_amount.get_order_amount(), currency, @@ -1526,6 +1530,7 @@ impl PaymentAttemptUpdate { connector_mandate_detail, card_discovery, routing_approach, + connector_request_reference_id, }, Self::VoidUpdate { status, @@ -1930,6 +1935,7 @@ impl behaviour::Conversion for PaymentAttempt { processor_merchant_id: Some(self.processor_merchant_id), created_by: self.created_by.map(|cb| cb.to_string()), routing_approach: self.routing_approach, + connector_request_reference_id: self.connector_request_reference_id, }) } @@ -2026,6 +2032,7 @@ impl behaviour::Conversion for PaymentAttempt { .and_then(|created_by| created_by.parse::().ok()), setup_future_usage_applied: storage_model.setup_future_usage_applied, routing_approach: storage_model.routing_approach, + connector_request_reference_id: storage_model.connector_request_reference_id, }) } .await @@ -2115,6 +2122,7 @@ impl behaviour::Conversion for PaymentAttempt { created_by: self.created_by.map(|cb| cb.to_string()), setup_future_usage_applied: self.setup_future_usage_applied, routing_approach: self.routing_approach, + connector_request_reference_id: self.connector_request_reference_id, }) } } diff --git a/crates/hyperswitch_domain_models/src/payments/payment_intent.rs b/crates/hyperswitch_domain_models/src/payments/payment_intent.rs index d7c41c88b5..43e41f0095 100644 --- a/crates/hyperswitch_domain_models/src/payments/payment_intent.rs +++ b/crates/hyperswitch_domain_models/src/payments/payment_intent.rs @@ -1640,6 +1640,7 @@ impl behaviour::Conversion for PaymentIntent { processor_merchant_id, created_by, is_iframe_redirection_enabled, + is_payment_id_from_merchant, } = self; Ok(DieselPaymentIntent { skip_external_tax_calculation: Some(amount_details.get_external_tax_action_as_bool()), @@ -1725,6 +1726,7 @@ impl behaviour::Conversion for PaymentIntent { processor_merchant_id: Some(processor_merchant_id), created_by: created_by.map(|cb| cb.to_string()), is_iframe_redirection_enabled, + is_payment_id_from_merchant, }) } async fn convert_back( @@ -1866,6 +1868,7 @@ impl behaviour::Conversion for PaymentIntent { .created_by .and_then(|created_by| created_by.parse::().ok()), is_iframe_redirection_enabled: storage_model.is_iframe_redirection_enabled, + is_payment_id_from_merchant: storage_model.is_payment_id_from_merchant, }) } .await @@ -1953,6 +1956,7 @@ impl behaviour::Conversion for PaymentIntent { created_by: self.created_by.map(|cb| cb.to_string()), is_iframe_redirection_enabled: self.is_iframe_redirection_enabled, routing_algorithm_id: self.routing_algorithm_id, + is_payment_id_from_merchant: self.is_payment_id_from_merchant, }) } } @@ -2027,6 +2031,7 @@ impl behaviour::Conversion for PaymentIntent { force_3ds_challenge_trigger: self.force_3ds_challenge_trigger, is_iframe_redirection_enabled: self.is_iframe_redirection_enabled, extended_return_url: self.return_url, + is_payment_id_from_merchant: self.is_payment_id_from_merchant, }) } @@ -2127,6 +2132,7 @@ impl behaviour::Conversion for PaymentIntent { force_3ds_challenge: storage_model.force_3ds_challenge, force_3ds_challenge_trigger: storage_model.force_3ds_challenge_trigger, is_iframe_redirection_enabled: storage_model.is_iframe_redirection_enabled, + is_payment_id_from_merchant: storage_model.is_payment_id_from_merchant, }) } .await @@ -2199,6 +2205,7 @@ impl behaviour::Conversion for PaymentIntent { force_3ds_challenge_trigger: self.force_3ds_challenge_trigger, is_iframe_redirection_enabled: self.is_iframe_redirection_enabled, extended_return_url: self.return_url, + is_payment_id_from_merchant: self.is_payment_id_from_merchant, }) } } diff --git a/crates/hyperswitch_domain_models/src/router_data.rs b/crates/hyperswitch_domain_models/src/router_data.rs index 851ac69a81..a18f9e5dc6 100644 --- a/crates/hyperswitch_domain_models/src/router_data.rs +++ b/crates/hyperswitch_domain_models/src/router_data.rs @@ -105,6 +105,10 @@ pub struct RouterData { /// Contains stringified connector raw response body pub raw_connector_response: Option, + + /// Indicates whether the payment ID was provided by the merchant (true), + /// or generated internally by Hyperswitch (false) + pub is_payment_id_from_merchant: Option, } // Different patterns of authentication. diff --git a/crates/hyperswitch_interfaces/src/api.rs b/crates/hyperswitch_interfaces/src/api.rs index db65b14c35..03ca4c62ac 100644 --- a/crates/hyperswitch_interfaces/src/api.rs +++ b/crates/hyperswitch_interfaces/src/api.rs @@ -383,6 +383,22 @@ pub trait ConnectorSpecifications { None } + #[cfg(not(feature = "v2"))] + /// Generate connector request reference ID + fn generate_connector_request_reference_id( + &self, + _payment_intent: &hyperswitch_domain_models::payments::PaymentIntent, + payment_attempt: &hyperswitch_domain_models::payments::payment_attempt::PaymentAttempt, + is_config_enabled_to_send_payment_id_as_connector_request_id: bool, + ) -> String { + // Send payment_id if config is enabled for a merchant, else send attempt_id + if is_config_enabled_to_send_payment_id_as_connector_request_id { + payment_attempt.payment_id.get_string_repr().to_owned() + } else { + payment_attempt.attempt_id.to_owned() + } + } + #[cfg(feature = "v2")] /// Generate connector request reference ID fn generate_connector_request_reference_id( diff --git a/crates/hyperswitch_interfaces/src/connector_integration_interface.rs b/crates/hyperswitch_interfaces/src/connector_integration_interface.rs index 3bab4b3666..602931a1a0 100644 --- a/crates/hyperswitch_interfaces/src/connector_integration_interface.rs +++ b/crates/hyperswitch_interfaces/src/connector_integration_interface.rs @@ -519,6 +519,27 @@ impl ConnectorSpecifications for ConnectorEnum { } } + #[cfg(feature = "v1")] + fn generate_connector_request_reference_id( + &self, + payment_intent: &hyperswitch_domain_models::payments::PaymentIntent, + payment_attempt: &hyperswitch_domain_models::payments::payment_attempt::PaymentAttempt, + is_config_enabled_to_send_payment_id_as_connector_request_id: bool, + ) -> String { + match self { + Self::Old(connector) => connector.generate_connector_request_reference_id( + payment_intent, + payment_attempt, + is_config_enabled_to_send_payment_id_as_connector_request_id, + ), + Self::New(connector) => connector.generate_connector_request_reference_id( + payment_intent, + payment_attempt, + is_config_enabled_to_send_payment_id_as_connector_request_id, + ), + } + } + #[cfg(feature = "v2")] /// Generate connector request reference ID fn generate_connector_request_reference_id( diff --git a/crates/hyperswitch_interfaces/src/conversion_impls.rs b/crates/hyperswitch_interfaces/src/conversion_impls.rs index ae5f071a20..6e027b8836 100644 --- a/crates/hyperswitch_interfaces/src/conversion_impls.rs +++ b/crates/hyperswitch_interfaces/src/conversion_impls.rs @@ -85,6 +85,7 @@ fn get_default_router_data( authentication_id: None, psd2_sca_exemption_type: None, raw_connector_response: None, + is_payment_id_from_merchant: None, } } diff --git a/crates/router/src/core/authentication/transformers.rs b/crates/router/src/core/authentication/transformers.rs index 91b7c23f05..843a8988df 100644 --- a/crates/router/src/core/authentication/transformers.rs +++ b/crates/router/src/core/authentication/transformers.rs @@ -207,6 +207,7 @@ pub fn construct_router_data( authentication_id: None, psd2_sca_exemption_type, raw_connector_response: None, + is_payment_id_from_merchant: None, }) } diff --git a/crates/router/src/core/fraud_check/flows/checkout_flow.rs b/crates/router/src/core/fraud_check/flows/checkout_flow.rs index a33dcc89a9..99debc44fd 100644 --- a/crates/router/src/core/fraud_check/flows/checkout_flow.rs +++ b/crates/router/src/core/fraud_check/flows/checkout_flow.rs @@ -162,6 +162,7 @@ impl ConstructFlowSpecificData( .attach_printable("profile_id is not set in payment_intent")? .clone(); + let connector_id = connector.clone(); + let merchant_connector_account = helpers::get_merchant_connector_account( state, merchant_context.get_merchant_account().get_id(), @@ -107,8 +109,10 @@ pub async fn construct_fulfillment_router_data<'a>( connector_request_reference_id: core_utils::get_connector_request_reference_id( &state.conf, merchant_context.get_merchant_account().get_id(), + payment_intent, payment_attempt, - ), + &connector_id, + )?, #[cfg(feature = "payouts")] payout_method_data: None, #[cfg(feature = "payouts")] @@ -129,6 +133,7 @@ pub async fn construct_fulfillment_router_data<'a>( authentication_id: None, psd2_sca_exemption_type: None, raw_connector_response: None, + is_payment_id_from_merchant: None, }; Ok(router_data) } diff --git a/crates/router/src/core/fraud_check/flows/record_return.rs b/crates/router/src/core/fraud_check/flows/record_return.rs index d33cd51e57..bf24097add 100644 --- a/crates/router/src/core/fraud_check/flows/record_return.rs +++ b/crates/router/src/core/fraud_check/flows/record_return.rs @@ -130,6 +130,7 @@ impl ConstructFlowSpecificData { external_vault_session_details: Option, ); fn set_routing_approach_in_attempt(&mut self, routing_approach: Option); + + fn set_connector_request_reference_id_in_payment_attempt( + &mut self, + connector_request_reference_id: String, + ); } #[cfg(feature = "v1")] @@ -9402,6 +9411,13 @@ impl OperationSessionSetters for PaymentData { fn set_connector_response_reference_id(&mut self, reference_id: Option) { self.payment_attempt.connector_response_reference_id = reference_id; } + + fn set_connector_request_reference_id_in_payment_attempt( + &mut self, + connector_request_reference_id: String, + ) { + self.payment_attempt.connector_request_reference_id = Some(connector_request_reference_id); + } } #[cfg(feature = "v2")] @@ -9691,6 +9707,13 @@ impl OperationSessionSetters for PaymentIntentData { ) { todo!() } + + fn set_connector_request_reference_id_in_payment_attempt( + &mut self, + connector_request_reference_id: String, + ) { + todo!() + } } #[cfg(feature = "v2")] @@ -9983,6 +10006,13 @@ impl OperationSessionSetters for PaymentConfirmData { ) { todo!() } + + fn set_connector_request_reference_id_in_payment_attempt( + &mut self, + connector_request_reference_id: String, + ) { + todo!() + } } #[cfg(feature = "v2")] @@ -10270,6 +10300,13 @@ impl OperationSessionSetters for PaymentStatusData { ) { todo!() } + + fn set_connector_request_reference_id_in_payment_attempt( + &mut self, + connector_request_reference_id: String, + ) { + todo!() + } } #[cfg(feature = "v2")] @@ -10560,6 +10597,13 @@ impl OperationSessionSetters for PaymentCaptureData { ) { todo!() } + + fn set_connector_request_reference_id_in_payment_attempt( + &mut self, + connector_request_reference_id: String, + ) { + todo!() + } } #[cfg(feature = "v2")] diff --git a/crates/router/src/core/payments/helpers.rs b/crates/router/src/core/payments/helpers.rs index 5eaee11c18..d855ca84e1 100644 --- a/crates/router/src/core/payments/helpers.rs +++ b/crates/router/src/core/payments/helpers.rs @@ -3692,6 +3692,7 @@ mod tests { force_3ds_challenge: None, force_3ds_challenge_trigger: None, is_iframe_redirection_enabled: None, + is_payment_id_from_merchant: None, }; let req_cs = Some("1".to_string()); assert!(authenticate_client_secret(req_cs.as_ref(), &payment_intent).is_ok()); @@ -3768,6 +3769,7 @@ mod tests { force_3ds_challenge: None, force_3ds_challenge_trigger: None, is_iframe_redirection_enabled: None, + is_payment_id_from_merchant: None, }; let req_cs = Some("1".to_string()); assert!(authenticate_client_secret(req_cs.as_ref(), &payment_intent,).is_err()) @@ -3842,6 +3844,7 @@ mod tests { force_3ds_challenge: None, force_3ds_challenge_trigger: None, is_iframe_redirection_enabled: None, + is_payment_id_from_merchant: None, }; let req_cs = Some("1".to_string()); assert!(authenticate_client_secret(req_cs.as_ref(), &payment_intent).is_err()) @@ -4176,6 +4179,7 @@ pub fn router_data_type_conversion( authentication_id: router_data.authentication_id, psd2_sca_exemption_type: router_data.psd2_sca_exemption_type, raw_connector_response: router_data.raw_connector_response, + is_payment_id_from_merchant: router_data.is_payment_id_from_merchant, } } @@ -4393,6 +4397,7 @@ impl AttemptType { created_by: old_payment_attempt.created_by, setup_future_usage_applied: None, routing_approach: old_payment_attempt.routing_approach, + connector_request_reference_id: None, } } diff --git a/crates/router/src/core/payments/operations/payment_confirm.rs b/crates/router/src/core/payments/operations/payment_confirm.rs index 8d850c064a..31a910a494 100644 --- a/crates/router/src/core/payments/operations/payment_confirm.rs +++ b/crates/router/src/core/payments/operations/payment_confirm.rs @@ -1756,6 +1756,10 @@ impl UpdateTracker, api::PaymentsRequest> for let connector = payment_data.payment_attempt.connector.clone(); let merchant_connector_id = payment_data.payment_attempt.merchant_connector_id.clone(); + let connector_request_reference_id = payment_data + .payment_attempt + .connector_request_reference_id + .clone(); let straight_through_algorithm = payment_data .payment_attempt @@ -1936,6 +1940,7 @@ impl UpdateTracker, api::PaymentsRequest> for .connector_mandate_detail, card_discovery, routing_approach: payment_data.payment_attempt.routing_approach, + connector_request_reference_id, }, 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 413aa121a9..b3246a9ed9 100644 --- a/crates/router/src/core/payments/operations/payment_create.rs +++ b/crates/router/src/core/payments/operations/payment_create.rs @@ -311,6 +311,7 @@ impl GetTracker, api::PaymentsRequest> profile_id.clone(), session_expiry, &business_profile, + request.is_payment_id_from_merchant, ) .await?; @@ -1366,7 +1367,8 @@ impl PaymentCreate { processor_merchant_id: merchant_id.to_owned(), created_by: None, setup_future_usage_applied: request.setup_future_usage, - routing_approach: Some(common_enums::RoutingApproach::default()) + routing_approach: Some(common_enums::RoutingApproach::default()), + connector_request_reference_id: None, }, additional_pm_data, @@ -1388,6 +1390,7 @@ impl PaymentCreate { profile_id: common_utils::id_type::ProfileId, session_expiry: PrimitiveDateTime, business_profile: &domain::Profile, + is_payment_id_from_merchant: bool, ) -> RouterResult { let created_at @ modified_at @ last_synced = common_utils::date_time::now(); @@ -1600,6 +1603,7 @@ impl PaymentCreate { is_iframe_redirection_enabled: request .is_iframe_redirection_enabled .or(business_profile.is_iframe_redirection_enabled), + is_payment_id_from_merchant: Some(is_payment_id_from_merchant), }) } diff --git a/crates/router/src/core/payments/retry.rs b/crates/router/src/core/payments/retry.rs index 1cc886be82..728396ef53 100644 --- a/crates/router/src/core/payments/retry.rs +++ b/crates/router/src/core/payments/retry.rs @@ -703,6 +703,7 @@ pub fn make_new_payment_attempt( created_by: old_payment_attempt.created_by, setup_future_usage_applied: setup_future_usage_intent, // setup future usage is picked from intent for new payment attempt routing_approach: old_payment_attempt.routing_approach, + connector_request_reference_id: Default::default(), } } diff --git a/crates/router/src/core/payments/transformers.rs b/crates/router/src/core/payments/transformers.rs index 71ff92b0bb..2379dddb8f 100644 --- a/crates/router/src/core/payments/transformers.rs +++ b/crates/router/src/core/payments/transformers.rs @@ -148,8 +148,10 @@ where connector_request_reference_id: core_utils::get_connector_request_reference_id( &state.conf, merchant_context.get_merchant_account().get_id(), + &payment_data.payment_intent, &payment_data.payment_attempt, - ), + connector_id, + )?, preprocessing_id: None, #[cfg(feature = "payouts")] payout_method_data: None, @@ -172,6 +174,7 @@ where authentication_id: None, psd2_sca_exemption_type: None, raw_connector_response: None, + is_payment_id_from_merchant: payment_data.payment_intent.is_payment_id_from_merchant, }; Ok(router_data) } @@ -400,6 +403,7 @@ pub async fn construct_payment_router_data_for_authorize<'a>( authentication_id: None, psd2_sca_exemption_type: None, raw_connector_response: None, + is_payment_id_from_merchant: payment_data.payment_intent.is_payment_id_from_merchant, }; Ok(router_data) @@ -564,6 +568,7 @@ pub async fn construct_payment_router_data_for_capture<'a>( psd2_sca_exemption_type: None, authentication_id: None, raw_connector_response: None, + is_payment_id_from_merchant: None, }; Ok(router_data) @@ -691,6 +696,7 @@ pub async fn construct_router_data_for_psync<'a>( authentication_id: None, psd2_sca_exemption_type: None, raw_connector_response: None, + is_payment_id_from_merchant: None, }; Ok(router_data) @@ -873,6 +879,7 @@ pub async fn construct_payment_router_data_for_sdk_session<'a>( psd2_sca_exemption_type: None, authentication_id: None, raw_connector_response: None, + is_payment_id_from_merchant: None, }; Ok(router_data) @@ -1090,6 +1097,7 @@ pub async fn construct_payment_router_data_for_setup_mandate<'a>( authentication_id: None, psd2_sca_exemption_type: None, raw_connector_response: None, + is_payment_id_from_merchant: None, }; Ok(router_data) @@ -1261,8 +1269,10 @@ where connector_request_reference_id: core_utils::get_connector_request_reference_id( &state.conf, merchant_context.get_merchant_account().get_id(), + &payment_data.payment_intent, &payment_data.payment_attempt, - ), + connector_id, + )?, preprocessing_id: payment_data.payment_attempt.preprocessing_step_id, #[cfg(feature = "payouts")] payout_method_data: None, @@ -1289,6 +1299,7 @@ where authentication_id: None, psd2_sca_exemption_type: payment_data.payment_intent.psd2_sca_exemption_type, raw_connector_response: None, + is_payment_id_from_merchant: payment_data.payment_intent.is_payment_id_from_merchant, }; Ok(router_data) @@ -1451,8 +1462,10 @@ pub async fn construct_payment_router_data_for_update_metadata<'a>( connector_request_reference_id: core_utils::get_connector_request_reference_id( &state.conf, merchant_context.get_merchant_account().get_id(), + &payment_data.payment_intent, &payment_data.payment_attempt, - ), + connector_id, + )?, preprocessing_id: payment_data.payment_attempt.preprocessing_step_id, #[cfg(feature = "payouts")] payout_method_data: None, @@ -1479,6 +1492,7 @@ pub async fn construct_payment_router_data_for_update_metadata<'a>( authentication_id: None, psd2_sca_exemption_type: payment_data.payment_intent.psd2_sca_exemption_type, raw_connector_response: None, + is_payment_id_from_merchant: payment_data.payment_intent.is_payment_id_from_merchant, }; Ok(router_data) diff --git a/crates/router/src/core/relay/utils.rs b/crates/router/src/core/relay/utils.rs index 1adacfbe94..e46abcd9e2 100644 --- a/crates/router/src/core/relay/utils.rs +++ b/crates/router/src/core/relay/utils.rs @@ -144,6 +144,7 @@ pub async fn construct_relay_refund_router_data( authentication_id: None, psd2_sca_exemption_type: None, raw_connector_response: None, + is_payment_id_from_merchant: None, }; Ok(router_data) diff --git a/crates/router/src/core/unified_authentication_service/utils.rs b/crates/router/src/core/unified_authentication_service/utils.rs index 20ac12a994..89b6c05909 100644 --- a/crates/router/src/core/unified_authentication_service/utils.rs +++ b/crates/router/src/core/unified_authentication_service/utils.rs @@ -125,6 +125,7 @@ pub fn construct_uas_router_data( authentication_id, psd2_sca_exemption_type: None, raw_connector_response: None, + is_payment_id_from_merchant: None, }) } diff --git a/crates/router/src/core/utils.rs b/crates/router/src/core/utils.rs index c097b3fbfe..83642c7f62 100644 --- a/crates/router/src/core/utils.rs +++ b/crates/router/src/core/utils.rs @@ -26,6 +26,7 @@ use hyperswitch_domain_models::{ use hyperswitch_domain_models::{ router_data_v2::flow_common_types::VaultConnectorFlowData, types::VaultRouterDataV2, }; +use hyperswitch_interfaces::api::ConnectorSpecifications; #[cfg(feature = "v2")] use masking::ExposeOptionInterface; use masking::Secret; @@ -235,6 +236,7 @@ pub async fn construct_payout_router_data<'a, F>( authentication_id: None, psd2_sca_exemption_type: None, raw_connector_response: None, + is_payment_id_from_merchant: None, }; Ok(router_data) @@ -402,6 +404,7 @@ pub async fn construct_refund_router_data<'a, F>( authentication_id: None, psd2_sca_exemption_type: None, raw_connector_response: None, + is_payment_id_from_merchant: None, }; Ok(router_data) @@ -605,6 +608,7 @@ pub async fn construct_refund_router_data<'a, F>( authentication_id: None, psd2_sca_exemption_type: None, raw_connector_response: None, + is_payment_id_from_merchant: None, }; Ok(router_data) @@ -1006,8 +1010,10 @@ pub async fn construct_accept_dispute_router_data<'a>( connector_request_reference_id: get_connector_request_reference_id( &state.conf, merchant_context.get_merchant_account().get_id(), + payment_intent, payment_attempt, - ), + &dispute.connector, + )?, #[cfg(feature = "payouts")] payout_method_data: None, #[cfg(feature = "payouts")] @@ -1029,6 +1035,7 @@ pub async fn construct_accept_dispute_router_data<'a>( authentication_id: None, psd2_sca_exemption_type: None, raw_connector_response: None, + is_payment_id_from_merchant: None, }; Ok(router_data) } @@ -1105,8 +1112,10 @@ pub async fn construct_submit_evidence_router_data<'a>( connector_request_reference_id: get_connector_request_reference_id( &state.conf, merchant_context.get_merchant_account().get_id(), + payment_intent, payment_attempt, - ), + connector_id, + )?, #[cfg(feature = "payouts")] payout_method_data: None, #[cfg(feature = "payouts")] @@ -1127,6 +1136,7 @@ pub async fn construct_submit_evidence_router_data<'a>( authentication_id: None, psd2_sca_exemption_type: None, raw_connector_response: None, + is_payment_id_from_merchant: None, }; Ok(router_data) } @@ -1209,8 +1219,10 @@ pub async fn construct_upload_file_router_data<'a>( connector_request_reference_id: get_connector_request_reference_id( &state.conf, merchant_context.get_merchant_account().get_id(), + payment_intent, payment_attempt, - ), + connector_id, + )?, #[cfg(feature = "payouts")] payout_method_data: None, #[cfg(feature = "payouts")] @@ -1231,6 +1243,7 @@ pub async fn construct_upload_file_router_data<'a>( authentication_id: None, psd2_sca_exemption_type: None, raw_connector_response: None, + is_payment_id_from_merchant: None, }; Ok(router_data) } @@ -1331,8 +1344,10 @@ pub async fn construct_payments_dynamic_tax_calculation_router_data( connector_request_reference_id: get_connector_request_reference_id( &state.conf, merchant_context.get_merchant_account().get_id(), + payment_intent, payment_attempt, - ), + &merchant_connector_account.connector_name, + )?, #[cfg(feature = "payouts")] payout_method_data: None, #[cfg(feature = "payouts")] @@ -1354,6 +1369,7 @@ pub async fn construct_payments_dynamic_tax_calculation_router_data( authentication_id: None, psd2_sca_exemption_type: None, raw_connector_response: None, + is_payment_id_from_merchant: None, }; Ok(router_data) } @@ -1433,8 +1449,10 @@ pub async fn construct_defend_dispute_router_data<'a>( connector_request_reference_id: get_connector_request_reference_id( &state.conf, merchant_context.get_merchant_account().get_id(), + payment_intent, payment_attempt, - ), + connector_id, + )?, #[cfg(feature = "payouts")] payout_method_data: None, #[cfg(feature = "payouts")] @@ -1455,6 +1473,7 @@ pub async fn construct_defend_dispute_router_data<'a>( authentication_id: None, psd2_sca_exemption_type: None, raw_connector_response: None, + is_payment_id_from_merchant: None, }; Ok(router_data) } @@ -1550,6 +1569,7 @@ pub async fn construct_retrieve_file_router_data<'a>( authentication_id: None, psd2_sca_exemption_type: None, raw_connector_response: None, + is_payment_id_from_merchant: None, }; Ok(router_data) } @@ -1568,16 +1588,30 @@ pub fn is_merchant_enabled_for_payment_id_as_connector_request_id( pub fn get_connector_request_reference_id( conf: &Settings, merchant_id: &common_utils::id_type::MerchantId, + payment_intent: &hyperswitch_domain_models::payments::PaymentIntent, payment_attempt: &hyperswitch_domain_models::payments::payment_attempt::PaymentAttempt, -) -> String { - let is_config_enabled_for_merchant = + connector_name: &str, +) -> CustomResult { + let is_config_enabled_to_send_payment_id_as_connector_request_id = is_merchant_enabled_for_payment_id_as_connector_request_id(conf, merchant_id); - // Send payment_id if config is enabled for a merchant, else send attempt_id - if is_config_enabled_for_merchant { - payment_attempt.payment_id.get_string_repr().to_owned() - } else { - payment_attempt.attempt_id.to_owned() - } + + let connector_data = api::ConnectorData::get_connector_by_name( + &conf.connectors, + connector_name, + api::GetToken::Connector, + payment_attempt.merchant_connector_id.clone(), + ) + .change_context(errors::ApiErrorResponse::InternalServerError) + .attach_printable_lazy(|| "Failed to construct connector data")?; + + let connector_request_reference_id = connector_data + .connector + .generate_connector_request_reference_id( + payment_intent, + payment_attempt, + is_config_enabled_to_send_payment_id_as_connector_request_id, + ); + Ok(connector_request_reference_id) } // TODO: Based on the connector configuration, the connector_request_reference_id should be generated diff --git a/crates/router/src/core/webhooks/utils.rs b/crates/router/src/core/webhooks/utils.rs index c0c362a9dd..65c3349c1a 100644 --- a/crates/router/src/core/webhooks/utils.rs +++ b/crates/router/src/core/webhooks/utils.rs @@ -134,6 +134,7 @@ pub async fn construct_webhook_router_data( authentication_id: None, psd2_sca_exemption_type: None, raw_connector_response: None, + is_payment_id_from_merchant: None, }; Ok(router_data) } diff --git a/crates/router/src/routes/payments.rs b/crates/router/src/routes/payments.rs index 163bc2dab2..74e6c580ff 100644 --- a/crates/router/src/routes/payments.rs +++ b/crates/router/src/routes/payments.rs @@ -2283,6 +2283,11 @@ pub fn get_or_generate_payment_id( let payment_id = given_payment_id.unwrap_or(common_utils::id_type::PaymentId::default()); + payload.is_payment_id_from_merchant = matches!( + &payload.payment_id, + Some(payment_types::PaymentIdType::PaymentIntentId(_)) + ); + payload.payment_id = Some(api_models::payments::PaymentIdType::PaymentIntentId( payment_id, )); diff --git a/crates/router/src/services/kafka/payment_intent.rs b/crates/router/src/services/kafka/payment_intent.rs index f0b4438bd2..e5caffc4a5 100644 --- a/crates/router/src/services/kafka/payment_intent.rs +++ b/crates/router/src/services/kafka/payment_intent.rs @@ -232,6 +232,7 @@ impl<'a> KafkaPaymentIntent<'a> { processor_merchant_id, created_by, is_iframe_redirection_enabled, + is_payment_id_from_merchant, } = intent; Self { diff --git a/crates/router/src/services/kafka/payment_intent_event.rs b/crates/router/src/services/kafka/payment_intent_event.rs index f7d1bca5b3..3e0733eeb7 100644 --- a/crates/router/src/services/kafka/payment_intent_event.rs +++ b/crates/router/src/services/kafka/payment_intent_event.rs @@ -244,6 +244,7 @@ impl<'a> KafkaPaymentIntentEvent<'a> { processor_merchant_id, created_by, is_iframe_redirection_enabled, + is_payment_id_from_merchant, } = intent; Self { diff --git a/crates/router/src/types.rs b/crates/router/src/types.rs index dd64c5b370..2b64da4f5b 100644 --- a/crates/router/src/types.rs +++ b/crates/router/src/types.rs @@ -1152,6 +1152,7 @@ impl ForeignFrom<(&RouterData, T2) authentication_id: data.authentication_id.clone(), psd2_sca_exemption_type: data.psd2_sca_exemption_type, raw_connector_response: data.raw_connector_response.clone(), + is_payment_id_from_merchant: data.is_payment_id_from_merchant, } } } @@ -1220,6 +1221,7 @@ impl additional_merchant_data: data.additional_merchant_data.clone(), connector_mandate_request_reference_id: None, raw_connector_response: None, + is_payment_id_from_merchant: data.is_payment_id_from_merchant, } } } diff --git a/crates/router/src/types/api/verify_connector.rs b/crates/router/src/types/api/verify_connector.rs index 8298be10aa..83066578d0 100644 --- a/crates/router/src/types/api/verify_connector.rs +++ b/crates/router/src/types/api/verify_connector.rs @@ -128,6 +128,7 @@ impl VerifyConnectorData { authentication_id: None, psd2_sca_exemption_type: None, raw_connector_response: None, + is_payment_id_from_merchant: None, } } } diff --git a/crates/router/src/types/storage/payment_attempt.rs b/crates/router/src/types/storage/payment_attempt.rs index 5289ed02af..32601f25c7 100644 --- a/crates/router/src/types/storage/payment_attempt.rs +++ b/crates/router/src/types/storage/payment_attempt.rs @@ -226,6 +226,7 @@ mod tests { created_by: None, setup_future_usage_applied: Default::default(), routing_approach: Default::default(), + connector_request_reference_id: Default::default(), }; let store = state @@ -317,6 +318,7 @@ mod tests { created_by: None, setup_future_usage_applied: Default::default(), routing_approach: Default::default(), + connector_request_reference_id: Default::default(), }; let store = state .stores @@ -421,6 +423,7 @@ mod tests { created_by: None, setup_future_usage_applied: Default::default(), routing_approach: Default::default(), + connector_request_reference_id: Default::default(), }; let store = state .stores diff --git a/crates/router/src/utils/user/sample_data.rs b/crates/router/src/utils/user/sample_data.rs index 52c3d29482..b04f340056 100644 --- a/crates/router/src/utils/user/sample_data.rs +++ b/crates/router/src/utils/user/sample_data.rs @@ -289,6 +289,7 @@ pub async fn generate_sample_data( force_3ds_challenge: None, force_3ds_challenge_trigger: None, is_iframe_redirection_enabled: None, + is_payment_id_from_merchant: None, }; let (connector_transaction_id, processor_transaction_data) = ConnectorTransactionId::form_id_and_data(attempt_id.clone()); @@ -382,6 +383,7 @@ pub async fn generate_sample_data( created_by: None, setup_future_usage_applied: None, routing_approach: None, + connector_request_reference_id: None, }; let refund = if refunds_count < number_of_refunds && !is_failed_payment { diff --git a/crates/router/tests/connectors/aci.rs b/crates/router/tests/connectors/aci.rs index cf0889931d..d2629fb4c2 100644 --- a/crates/router/tests/connectors/aci.rs +++ b/crates/router/tests/connectors/aci.rs @@ -134,6 +134,7 @@ fn construct_payment_router_data() -> types::PaymentsAuthorizeRouterData { authentication_id: None, psd2_sca_exemption_type: None, raw_connector_response: None, + is_payment_id_from_merchant: None, } } @@ -207,6 +208,7 @@ fn construct_refund_router_data() -> types::RefundsRouterData { authentication_id: None, psd2_sca_exemption_type: None, raw_connector_response: None, + is_payment_id_from_merchant: None, } } diff --git a/crates/router/tests/connectors/utils.rs b/crates/router/tests/connectors/utils.rs index db3aeddfb8..e3884894b3 100644 --- a/crates/router/tests/connectors/utils.rs +++ b/crates/router/tests/connectors/utils.rs @@ -554,6 +554,7 @@ pub trait ConnectorActions: Connector { psd2_sca_exemption_type: None, authentication_id: None, raw_connector_response: None, + is_payment_id_from_merchant: None, } } diff --git a/crates/storage_impl/src/mock_db/payment_attempt.rs b/crates/storage_impl/src/mock_db/payment_attempt.rs index 0de343b94e..88ddc824b2 100644 --- a/crates/storage_impl/src/mock_db/payment_attempt.rs +++ b/crates/storage_impl/src/mock_db/payment_attempt.rs @@ -236,6 +236,7 @@ impl PaymentAttemptInterface for MockDb { created_by: payment_attempt.created_by, setup_future_usage_applied: payment_attempt.setup_future_usage_applied, routing_approach: payment_attempt.routing_approach, + connector_request_reference_id: payment_attempt.connector_request_reference_id, }; payment_attempts.push(payment_attempt.clone()); Ok(payment_attempt) diff --git a/crates/storage_impl/src/payments/payment_attempt.rs b/crates/storage_impl/src/payments/payment_attempt.rs index 5de811b6a9..2c69c0766b 100644 --- a/crates/storage_impl/src/payments/payment_attempt.rs +++ b/crates/storage_impl/src/payments/payment_attempt.rs @@ -686,6 +686,9 @@ impl PaymentAttemptInterface for KVRouterStore { created_by: payment_attempt.created_by.clone(), setup_future_usage_applied: payment_attempt.setup_future_usage_applied, routing_approach: payment_attempt.routing_approach, + connector_request_reference_id: payment_attempt + .connector_request_reference_id + .clone(), }; let field = format!("pa_{}", created_attempt.attempt_id); @@ -1893,6 +1896,7 @@ impl DataModelExt for PaymentAttempt { connector_transaction_data: None, processor_merchant_id: Some(self.processor_merchant_id), created_by: self.created_by.map(|created_by| created_by.to_string()), + connector_request_reference_id: self.connector_request_reference_id, } } @@ -1984,6 +1988,7 @@ impl DataModelExt for PaymentAttempt { .and_then(|created_by| created_by.parse::().ok()), setup_future_usage_applied: storage_model.setup_future_usage_applied, routing_approach: storage_model.routing_approach, + connector_request_reference_id: storage_model.connector_request_reference_id, } } } @@ -2074,6 +2079,7 @@ impl DataModelExt for PaymentAttemptNew { created_by: self.created_by.map(|created_by| created_by.to_string()), setup_future_usage_applied: self.setup_future_usage_applied, routing_approach: self.routing_approach, + connector_request_reference_id: self.connector_request_reference_id, } } @@ -2157,6 +2163,7 @@ impl DataModelExt for PaymentAttemptNew { .and_then(|created_by| created_by.parse::().ok()), setup_future_usage_applied: storage_model.setup_future_usage_applied, routing_approach: storage_model.routing_approach, + connector_request_reference_id: storage_model.connector_request_reference_id, } } } diff --git a/migrations/2025-07-01-073253_add_connector_request_reference_id_and_is_payment_id_from_merchant/down.sql b/migrations/2025-07-01-073253_add_connector_request_reference_id_and_is_payment_id_from_merchant/down.sql new file mode 100644 index 0000000000..fc117ec216 --- /dev/null +++ b/migrations/2025-07-01-073253_add_connector_request_reference_id_and_is_payment_id_from_merchant/down.sql @@ -0,0 +1,5 @@ +-- This file should undo anything in `up.sql` +ALTER TABLE payment_intent +DROP COLUMN IF EXISTS is_payment_id_from_merchant; + +ALTER TABLE payment_attempt DROP COLUMN IF EXISTS connector_request_reference_id; \ No newline at end of file diff --git a/migrations/2025-07-01-073253_add_connector_request_reference_id_and_is_payment_id_from_merchant/up.sql b/migrations/2025-07-01-073253_add_connector_request_reference_id_and_is_payment_id_from_merchant/up.sql new file mode 100644 index 0000000000..1ae26c4330 --- /dev/null +++ b/migrations/2025-07-01-073253_add_connector_request_reference_id_and_is_payment_id_from_merchant/up.sql @@ -0,0 +1,5 @@ +-- Your SQL goes here +ALTER TABLE payment_intent +ADD COLUMN IF NOT EXISTS is_payment_id_from_merchant boolean; + +ALTER TABLE payment_attempt ADD COLUMN IF NOT EXISTS connector_request_reference_id VARCHAR(255); \ No newline at end of file diff --git a/v2_migrations/2025-01-13-081847_drop_v1_columns/down.sql b/v2_migrations/2025-01-13-081847_drop_v1_columns/down.sql index 483ef12a27..f716de7bb0 100644 --- a/v2_migrations/2025-01-13-081847_drop_v1_columns/down.sql +++ b/v2_migrations/2025-01-13-081847_drop_v1_columns/down.sql @@ -135,4 +135,6 @@ ALTER TABLE captures ADD COLUMN connector_capture_data VARCHAR(512); ALTER TABLE refund ADD COLUMN IF NOT EXISTS internal_reference_id VARCHAR(64), ADD COLUMN IF NOT EXISTS refund_id VARCHAR(64), - ADD COLUMN IF NOT EXISTS merchant_connector_id VARCHAR(64); \ No newline at end of file + ADD COLUMN IF NOT EXISTS merchant_connector_id VARCHAR(64); + +ALTER TABLE payment_attempt ADD COLUMN IF NOT EXISTS connector_request_reference_id VARCHAR(255); \ No newline at end of file diff --git a/v2_migrations/2025-01-13-081847_drop_v1_columns/up.sql b/v2_migrations/2025-01-13-081847_drop_v1_columns/up.sql index 8eac6ae97a..be5a7b845a 100644 --- a/v2_migrations/2025-01-13-081847_drop_v1_columns/up.sql +++ b/v2_migrations/2025-01-13-081847_drop_v1_columns/up.sql @@ -131,4 +131,7 @@ ALTER TABLE refund DROP COLUMN IF EXISTS refund_id, DROP COLUMN IF EXISTS merchant_connector_id; +-- Run below queries only when V1 is deprecated +ALTER TABLE payment_attempt DROP COLUMN IF EXISTS connector_request_reference_id; +