diff --git a/api-reference/v2/openapi_spec_v2.json b/api-reference/v2/openapi_spec_v2.json index 9a8bebc893..0304eaf3b0 100644 --- a/api-reference/v2/openapi_spec_v2.json +++ b/api-reference/v2/openapi_spec_v2.json @@ -13442,16 +13442,17 @@ }, "MerchantConnectorDetails": { "type": "object", + "required": [ + "connector_name", + "merchant_connector_creds" + ], "properties": { - "connector_account_details": { - "type": "object", - "description": "Account details of the Connector. You can specify up to 50 keys, with key names up to 40 characters long and values up to 500 characters long. Useful for storing additional, structured information on an object.", - "nullable": true + "connector_name": { + "$ref": "#/components/schemas/Connector" }, - "metadata": { + "merchant_connector_creds": { "type": "object", - "description": "Metadata is useful for storing additional, unstructured information on an object.", - "nullable": true + "description": "The merchant connector credentials used for the payment" } } }, @@ -18091,6 +18092,14 @@ "type": "string", "example": "187282ab-40ef-47a9-9206-5099ba31e432", "nullable": true + }, + "merchant_connector_details": { + "allOf": [ + { + "$ref": "#/components/schemas/MerchantConnectorDetails" + } + ], + "nullable": true } }, "additionalProperties": false @@ -18288,6 +18297,14 @@ "type": "boolean", "description": "Indicates if 3ds challenge is forced", "nullable": true + }, + "merchant_connector_details": { + "allOf": [ + { + "$ref": "#/components/schemas/MerchantConnectorDetails" + } + ], + "nullable": true } }, "additionalProperties": false @@ -19074,6 +19091,14 @@ "type": "boolean", "description": "Indicates if the redirection has to open in the iframe", "nullable": true + }, + "merchant_connector_details": { + "allOf": [ + { + "$ref": "#/components/schemas/MerchantConnectorDetails" + } + ], + "nullable": true } }, "additionalProperties": false @@ -19250,12 +19275,20 @@ "type": "boolean", "description": "Indicates if the redirection has to open in the iframe", "nullable": true + }, + "merchant_reference_id": { + "type": "string", + "description": "Unique identifier for the payment. This ensures idempotency for multiple payments\nthat have been done by a single merchant.", + "example": "pay_mbabizu24mvu3mela5njyhpit4", + "nullable": true, + "maxLength": 30, + "minLength": 30 } } }, "PaymentsRetrieveRequest": { "type": "object", - "description": "Request for Payment Status", + "description": "Request body for Payment Status", "properties": { "force_sync": { "type": "boolean", @@ -19274,6 +19307,14 @@ "type": "boolean", "description": "If enabled, provides whole connector response", "nullable": true + }, + "merchant_connector_details": { + "allOf": [ + { + "$ref": "#/components/schemas/MerchantConnectorDetails" + } + ], + "nullable": true } } }, @@ -19308,6 +19349,30 @@ } } }, + "PaymentsStatusRequest": { + "type": "object", + "description": "Request for Payment Status", + "properties": { + "force_sync": { + "type": "boolean", + "description": "A boolean used to indicate if the payment status should be fetched from the connector\nIf this is set to true, the status will be fetched from the connector" + }, + "expand_attempts": { + "type": "boolean", + "description": "A boolean used to indicate if all the attempts needs to be fetched for the intent.\nIf this is set to true, attempts list will be available in the response." + }, + "param": { + "type": "string", + "description": "These are the query params that are sent in case of redirect response.\nThese can be ingested by the connector to take necessary actions.", + "nullable": true + }, + "all_keys_required": { + "type": "boolean", + "description": "If enabled, provides whole connector response", + "nullable": true + } + } + }, "PaymentsUpdateIntentRequest": { "type": "object", "properties": { diff --git a/crates/api_models/src/payments.rs b/crates/api_models/src/payments.rs index ef4d417ddc..7b14351a49 100644 --- a/crates/api_models/src/payments.rs +++ b/crates/api_models/src/payments.rs @@ -269,6 +269,26 @@ pub struct PaymentsCreateIntentRequest { /// Indicates if 3ds challenge is forced pub force_3ds_challenge: Option, + + /// Merchant connector details used to make payments. + pub merchant_connector_details: Option, +} + +#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, ToSchema)] +#[cfg(feature = "v2")] +pub struct MerchantConnectorDetails { + /// The connector used for the payment + #[schema(value_type = Connector)] + pub connector_name: api_enums::Connector, + + /// The merchant connector credentials used for the payment + #[schema(value_type = Object, example = r#"{ + "merchant_connector_creds": { + "auth_type": "HeaderKey", + "api_key":"sk_test_xxxxxexamplexxxxxx12345" + }, + }"#)] + pub merchant_connector_creds: pii::SecretSerdeValue, } #[cfg(feature = "v2")] @@ -5348,6 +5368,9 @@ pub struct PaymentsConfirmIntentRequest { #[schema(example = "187282ab-40ef-47a9-9206-5099ba31e432")] pub payment_token: Option, + + /// Merchant connector details used to make payments. + pub merchant_connector_details: Option, } #[cfg(feature = "v2")] @@ -5520,6 +5543,9 @@ pub struct PaymentsRequest { /// Indicates if the redirection has to open in the iframe pub is_iframe_redirection_enabled: Option, + + /// Merchant connector details used to make payments. + pub merchant_connector_details: Option, } #[cfg(feature = "v2")] @@ -5554,6 +5580,7 @@ impl From<&PaymentsRequest> for PaymentsCreateIntentRequest { .request_external_three_ds_authentication .clone(), force_3ds_challenge: request.force_3ds_challenge, + merchant_connector_details: request.merchant_connector_details.clone(), } } } @@ -5571,6 +5598,7 @@ impl From<&PaymentsRequest> for PaymentsConfirmIntentRequest { browser_info: request.browser_info.clone(), payment_method_id: request.payment_method_id.clone(), payment_token: None, + merchant_connector_details: request.merchant_connector_details.clone(), } } } @@ -5578,7 +5606,7 @@ impl From<&PaymentsRequest> for PaymentsConfirmIntentRequest { // Serialize is implemented because, this will be serialized in the api events. // Usually request types should not have serialize implemented. // -/// Request for Payment Status +/// Request body for Payment Status #[cfg(feature = "v2")] #[derive(Debug, serde::Deserialize, serde::Serialize, ToSchema)] pub struct PaymentsRetrieveRequest { @@ -5595,6 +5623,27 @@ pub struct PaymentsRetrieveRequest { pub param: Option, /// If enabled, provides whole connector response pub all_keys_required: Option, + /// Merchant connector details used to make payments. + pub merchant_connector_details: Option, +} + +#[cfg(feature = "v2")] +#[derive(Debug, serde::Deserialize, serde::Serialize, ToSchema)] +/// Request for Payment Status +pub struct PaymentsStatusRequest { + /// A boolean used to indicate if the payment status should be fetched from the connector + /// If this is set to true, the status will be fetched from the connector + #[serde(default)] + pub force_sync: bool, + /// A boolean used to indicate if all the attempts needs to be fetched for the intent. + /// If this is set to true, attempts list will be available in the response. + #[serde(default)] + pub expand_attempts: bool, + /// These are the query params that are sent in case of redirect response. + /// These can be ingested by the connector to take necessary actions. + pub param: Option, + /// If enabled, provides whole connector response + pub all_keys_required: Option, } /// Error details for the payment @@ -5740,6 +5789,16 @@ pub struct PaymentsResponse { /// Indicates if the redirection has to open in the iframe pub is_iframe_redirection_enabled: Option, + + /// Unique identifier for the payment. This ensures idempotency for multiple payments + /// that have been done by a single merchant. + #[schema( + value_type = Option, + min_length = 30, + max_length = 30, + example = "pay_mbabizu24mvu3mela5njyhpit4" + )] + pub merchant_reference_id: Option, } #[cfg(feature = "v2")] diff --git a/crates/hyperswitch_domain_models/src/customer.rs b/crates/hyperswitch_domain_models/src/customer.rs index dd7d87a290..6b364ce580 100644 --- a/crates/hyperswitch_domain_models/src/customer.rs +++ b/crates/hyperswitch_domain_models/src/customer.rs @@ -20,6 +20,8 @@ use masking::{PeekInterface, Secret, SwitchStrategy}; use rustc_hash::FxHashMap; use time::PrimitiveDateTime; +#[cfg(feature = "v2")] +use crate::merchant_connector_account::MerchantConnectorAccountTypeDetails; use crate::{behaviour, merchant_key_store::MerchantKeyStore, type_encryption as types}; #[cfg(feature = "v1")] @@ -101,12 +103,18 @@ impl Customer { #[cfg(feature = "v2")] pub fn get_connector_customer_id( &self, - merchant_connector_id: &id_type::MerchantConnectorAccountId, + merchant_connector_account: &MerchantConnectorAccountTypeDetails, ) -> Option<&str> { - self.connector_customer - .as_ref() - .and_then(|connector_customer_map| connector_customer_map.get(merchant_connector_id)) - .map(|connector_customer_id| connector_customer_id.as_str()) + match merchant_connector_account { + MerchantConnectorAccountTypeDetails::MerchantConnectorAccount(account) => { + let connector_account_id = account.get_id(); + self.connector_customer + .as_ref()? + .get(&connector_account_id) + .map(|connector_customer_id| connector_customer_id.as_str()) + } + MerchantConnectorAccountTypeDetails::MerchantConnectorDetails(_) => None, + } } } diff --git a/crates/hyperswitch_domain_models/src/merchant_connector_account.rs b/crates/hyperswitch_domain_models/src/merchant_connector_account.rs index 0fc0b34a44..5c46593798 100644 --- a/crates/hyperswitch_domain_models/src/merchant_connector_account.rs +++ b/crates/hyperswitch_domain_models/src/merchant_connector_account.rs @@ -93,6 +93,92 @@ impl MerchantConnectorAccount { } } +#[cfg(feature = "v2")] +#[derive(Clone, Debug)] +pub enum MerchantConnectorAccountTypeDetails { + MerchantConnectorAccount(Box), + MerchantConnectorDetails(api_models::payments::MerchantConnectorDetails), +} + +#[cfg(feature = "v2")] +impl MerchantConnectorAccountTypeDetails { + pub fn get_connector_account_details( + &self, + ) -> error_stack::Result + { + match self { + Self::MerchantConnectorAccount(merchant_connector_account) => { + merchant_connector_account + .connector_account_details + .peek() + .clone() + .parse_value("ConnectorAuthType") + } + Self::MerchantConnectorDetails(merchant_connector_details) => { + merchant_connector_details + .merchant_connector_creds + .peek() + .clone() + .parse_value("ConnectorAuthType") + } + } + } + + pub fn is_disabled(&self) -> bool { + match self { + Self::MerchantConnectorAccount(merchant_connector_account) => { + merchant_connector_account.disabled.unwrap_or(false) + } + Self::MerchantConnectorDetails(_) => false, + } + } + + pub fn get_metadata(&self) -> Option> { + match self { + Self::MerchantConnectorAccount(merchant_connector_account) => { + merchant_connector_account.metadata.to_owned() + } + Self::MerchantConnectorDetails(_) => None, + } + } + + pub fn get_id(&self) -> Option { + match self { + Self::MerchantConnectorAccount(merchant_connector_account) => { + Some(merchant_connector_account.id.clone()) + } + Self::MerchantConnectorDetails(_) => None, + } + } + + pub fn get_mca_id(&self) -> Option { + match self { + Self::MerchantConnectorAccount(merchant_connector_account) => { + Some(merchant_connector_account.get_id()) + } + Self::MerchantConnectorDetails(_) => None, + } + } + + pub fn get_connector_name(&self) -> Option { + match self { + Self::MerchantConnectorAccount(merchant_connector_account) => { + Some(merchant_connector_account.connector_name) + } + Self::MerchantConnectorDetails(_) => None, + } + } + + pub fn get_inner_db_merchant_connector_account(&self) -> Option<&MerchantConnectorAccount> { + match self { + Self::MerchantConnectorAccount(merchant_connector_account) => { + Some(merchant_connector_account) + } + Self::MerchantConnectorDetails(_) => None, + } + } +} + #[cfg(feature = "v2")] #[derive(Clone, Debug, router_derive::ToEncryption)] pub struct MerchantConnectorAccount { diff --git a/crates/hyperswitch_domain_models/src/payments.rs b/crates/hyperswitch_domain_models/src/payments.rs index d460cfa466..8c892dcc08 100644 --- a/crates/hyperswitch_domain_models/src/payments.rs +++ b/crates/hyperswitch_domain_models/src/payments.rs @@ -2,7 +2,7 @@ use std::marker::PhantomData; #[cfg(feature = "v2")] -use api_models::payments::{SessionToken, VaultSessionDetails}; +use api_models::payments::{MerchantConnectorDetails, SessionToken, VaultSessionDetails}; use common_types::primitive_wrappers::{ AlwaysRequestExtendedAuthorization, RequestExtendedAuthorizationBool, }; @@ -42,8 +42,9 @@ use self::payment_attempt::PaymentAttempt; #[cfg(feature = "v2")] use crate::{ address::Address, business_profile, customer, errors, merchant_account, - merchant_connector_account, merchant_context, payment_address, payment_method_data, - payment_methods, revenue_recovery, routing, ApiModelToDieselModelConvertor, + merchant_connector_account, merchant_connector_account::MerchantConnectorAccountTypeDetails, + merchant_context, payment_address, payment_method_data, payment_methods, revenue_recovery, + routing, ApiModelToDieselModelConvertor, }; #[cfg(feature = "v1")] use crate::{payment_method_data, RemoteStorageObject}; @@ -858,6 +859,7 @@ where pub payment_address: payment_address::PaymentAddress, pub mandate_data: Option, pub payment_method: Option, + pub merchant_connector_details: Option, } #[cfg(feature = "v2")] @@ -865,15 +867,17 @@ impl PaymentConfirmData { pub fn get_connector_customer_id( &self, customer: Option<&customer::Customer>, - merchant_connector_account: &merchant_connector_account::MerchantConnectorAccount, + merchant_connector_account: &MerchantConnectorAccountTypeDetails, ) -> Option { - match customer - .and_then(|cust| cust.get_connector_customer_id(&merchant_connector_account.get_id())) - { - Some(id) => Some(id.to_string()), - None => self - .payment_intent - .get_connector_customer_id_from_feature_metadata(), + match merchant_connector_account { + MerchantConnectorAccountTypeDetails::MerchantConnectorAccount(_) => customer + .and_then(|customer| customer.get_connector_customer_id(merchant_connector_account)) + .map(|id| id.to_string()) + .or_else(|| { + self.payment_intent + .get_connector_customer_id_from_feature_metadata() + }), + MerchantConnectorAccountTypeDetails::MerchantConnectorDetails(_) => None, } } @@ -908,6 +912,7 @@ where /// Should the payment status be synced with connector /// This will depend on the payment status and the force sync flag in the request pub should_sync_with_connector: bool, + pub merchant_connector_details: Option, } #[cfg(feature = "v2")] diff --git a/crates/hyperswitch_domain_models/src/payments/payment_attempt.rs b/crates/hyperswitch_domain_models/src/payments/payment_attempt.rs index 12de76ed77..56e0942431 100644 --- a/crates/hyperswitch_domain_models/src/payments/payment_attempt.rs +++ b/crates/hyperswitch_domain_models/src/payments/payment_attempt.rs @@ -1757,7 +1757,7 @@ pub enum PaymentAttemptUpdate { status: storage_enums::AttemptStatus, updated_by: String, connector: String, - merchant_connector_id: id_type::MerchantConnectorAccountId, + merchant_connector_id: Option, authentication_type: storage_enums::AuthenticationType, connector_request_reference_id: Option, }, @@ -2559,7 +2559,7 @@ impl From for diesel_models::PaymentAttemptUpdateInternal error_code: None, error_reason: None, updated_by, - merchant_connector_id: Some(merchant_connector_id), + merchant_connector_id, unified_code: None, unified_message: None, connector_payment_id: None, diff --git a/crates/hyperswitch_domain_models/src/revenue_recovery.rs b/crates/hyperswitch_domain_models/src/revenue_recovery.rs index 529c035eeb..837d5d9083 100644 --- a/crates/hyperswitch_domain_models/src/revenue_recovery.rs +++ b/crates/hyperswitch_domain_models/src/revenue_recovery.rs @@ -215,6 +215,7 @@ impl From<&RevenueRecoveryInvoiceData> for api_payments::PaymentsCreateIntentReq frm_metadata: None, request_external_three_ds_authentication: None, force_3ds_challenge: None, + merchant_connector_details: None, } } } diff --git a/crates/openapi/src/openapi_v2.rs b/crates/openapi/src/openapi_v2.rs index d8e08a296d..822397767c 100644 --- a/crates/openapi/src/openapi_v2.rs +++ b/crates/openapi/src/openapi_v2.rs @@ -424,6 +424,8 @@ Never share your secret api keys. Keep them guarded and secure. api_models::payments::PaymentsListResponseItem, api_models::payments::PaymentRetrieveBody, api_models::payments::PaymentsRetrieveRequest, + api_models::payments::MerchantConnectorDetails, + api_models::payments::PaymentsStatusRequest, api_models::payments::PaymentsCaptureRequest, api_models::payments::PaymentsSessionRequest, api_models::payments::PaymentsSessionResponse, 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 4068453862..786192f8e9 100644 --- a/crates/router/src/core/fraud_check/flows/checkout_flow.rs +++ b/crates/router/src/core/fraud_check/flows/checkout_flow.rs @@ -35,7 +35,7 @@ impl ConstructFlowSpecificData, - _merchant_connector_account: &domain::MerchantConnectorAccount, + _merchant_connector_account: &domain::MerchantConnectorAccountTypeDetails, _merchant_recipient_data: Option, _header_payload: Option, ) -> RouterResult> 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 58b044c585..ea34b8af17 100644 --- a/crates/router/src/core/fraud_check/flows/record_return.rs +++ b/crates/router/src/core/fraud_check/flows/record_return.rs @@ -33,7 +33,7 @@ impl ConstructFlowSpecificData, - _merchant_connector_account: &domain::MerchantConnectorAccount, + _merchant_connector_account: &domain::MerchantConnectorAccountTypeDetails, _merchant_recipient_data: Option, _header_payload: Option, ) -> RouterResult> diff --git a/crates/router/src/core/fraud_check/flows/sale_flow.rs b/crates/router/src/core/fraud_check/flows/sale_flow.rs index 00223e524a..efdeeec774 100644 --- a/crates/router/src/core/fraud_check/flows/sale_flow.rs +++ b/crates/router/src/core/fraud_check/flows/sale_flow.rs @@ -31,7 +31,7 @@ impl ConstructFlowSpecificData, - _merchant_connector_account: &domain::MerchantConnectorAccount, + _merchant_connector_account: &domain::MerchantConnectorAccountTypeDetails, _merchant_recipient_data: Option, _header_payload: Option, ) -> RouterResult> { diff --git a/crates/router/src/core/fraud_check/flows/transaction_flow.rs b/crates/router/src/core/fraud_check/flows/transaction_flow.rs index 2f75e4796c..eae995be4b 100644 --- a/crates/router/src/core/fraud_check/flows/transaction_flow.rs +++ b/crates/router/src/core/fraud_check/flows/transaction_flow.rs @@ -36,7 +36,7 @@ impl _connector_id: &str, _merchant_context: &domain::MerchantContext, _customer: &Option, - _merchant_connector_account: &domain::MerchantConnectorAccount, + _merchant_connector_account: &domain::MerchantConnectorAccountTypeDetails, _merchant_recipient_data: Option, _header_payload: Option, ) -> RouterResult< diff --git a/crates/router/src/core/payment_methods.rs b/crates/router/src/core/payment_methods.rs index d6182c28f6..c851b4842e 100644 --- a/crates/router/src/core/payment_methods.rs +++ b/crates/router/src/core/payment_methods.rs @@ -1859,7 +1859,7 @@ pub async fn vault_payment_method_external( state: &SessionState, pmd: &domain::PaymentMethodVaultingData, merchant_account: &domain::MerchantAccount, - merchant_connector_account: payment_helpers::MerchantConnectorAccountType, + merchant_connector_account: domain::MerchantConnectorAccountTypeDetails, ) -> RouterResult { let router_data = core_utils::construct_vault_router_data( state, @@ -1962,19 +1962,17 @@ pub async fn vault_payment_method( .attach_printable("mca_id not present for external vault")?; let merchant_connector_account = - payments_core::helpers::get_merchant_connector_account( - state, - merchant_context.get_merchant_account().get_id(), - None, - merchant_context.get_merchant_key_store(), - profile.get_id(), - "", - Some(&external_vault_source), - ) - .await - .attach_printable( - "failed to fetch merchant connector account for external vault insert", - )?; + domain::MerchantConnectorAccountTypeDetails::MerchantConnectorAccount(Box::new( + payments_core::helpers::get_merchant_connector_account_v2( + state, + merchant_context.get_merchant_key_store(), + Some(&external_vault_source), + ) + .await + .attach_printable( + "failed to fetch merchant connector account for external vault insert", + )?, + )); vault_payment_method_external( state, @@ -2927,6 +2925,7 @@ fn construct_zero_auth_payments_request( browser_info: None, force_3ds_challenge: None, is_iframe_redirection_enabled: None, + merchant_connector_details: None, }) } diff --git a/crates/router/src/core/payment_methods/vault.rs b/crates/router/src/core/payment_methods/vault.rs index 4e4aa1b77f..a57cf31e71 100644 --- a/crates/router/src/core/payment_methods/vault.rs +++ b/crates/router/src/core/payment_methods/vault.rs @@ -1387,7 +1387,7 @@ pub async fn retrieve_payment_method_from_vault_external( state: &routes::SessionState, merchant_account: &domain::MerchantAccount, pm: &domain::PaymentMethod, - merchant_connector_account: payment_helpers::MerchantConnectorAccountType, + merchant_connector_account: domain::MerchantConnectorAccountTypeDetails, ) -> RouterResult { let connector_vault_id = pm .locker_id @@ -1563,19 +1563,17 @@ pub async fn retrieve_payment_method_from_vault( let external_vault_source = pm.external_vault_source.as_ref(); let merchant_connector_account = - payments_core::helpers::get_merchant_connector_account( - state, - merchant_context.get_merchant_account().get_id(), - None, - merchant_context.get_merchant_key_store(), - profile.get_id(), - "", - external_vault_source, - ) - .await - .attach_printable( - "failed to fetch merchant connector account for external vault retrieve", - )?; + domain::MerchantConnectorAccountTypeDetails::MerchantConnectorAccount(Box::new( + payments_core::helpers::get_merchant_connector_account_v2( + state, + merchant_context.get_merchant_key_store(), + external_vault_source, + ) + .await + .attach_printable( + "failed to fetch merchant connector account for external vault retrieve", + )?, + )); retrieve_payment_method_from_vault_external( state, @@ -1633,7 +1631,7 @@ pub async fn delete_payment_method_data_from_vault_internal( pub async fn delete_payment_method_data_from_vault_external( state: &routes::SessionState, merchant_account: &domain::MerchantAccount, - merchant_connector_account: payment_helpers::MerchantConnectorAccountType, + merchant_connector_account: domain::MerchantConnectorAccountTypeDetails, vault_id: domain::VaultId, ) -> RouterResult { let connector_vault_id = vault_id.get_string_repr().to_owned(); @@ -1736,19 +1734,17 @@ pub async fn delete_payment_method_data_from_vault( let external_vault_source = pm.external_vault_source.as_ref(); let merchant_connector_account = - payments_core::helpers::get_merchant_connector_account( - state, - merchant_context.get_merchant_account().get_id(), - None, - merchant_context.get_merchant_key_store(), - profile.get_id(), - "", - external_vault_source, - ) - .await - .attach_printable( - "failed to fetch merchant connector account for external vault delete", - )?; + domain::MerchantConnectorAccountTypeDetails::MerchantConnectorAccount(Box::new( + payments_core::helpers::get_merchant_connector_account_v2( + state, + merchant_context.get_merchant_key_store(), + external_vault_source, + ) + .await + .attach_printable( + "failed to fetch merchant connector account for external vault delete", + )?, + )); delete_payment_method_data_from_vault_external( state, diff --git a/crates/router/src/core/payments.rs b/crates/router/src/core/payments.rs index 3d05948dff..854af3f833 100644 --- a/crates/router/src/core/payments.rs +++ b/crates/router/src/core/payments.rs @@ -322,6 +322,81 @@ where Ok((payment_data, req, customer, None, None)) } +#[cfg(feature = "v2")] +#[allow(clippy::too_many_arguments, clippy::type_complexity)] +#[instrument(skip_all, fields(payment_id, merchant_id))] +pub async fn internal_payments_operation_core( + state: &SessionState, + req_state: ReqState, + merchant_context: domain::MerchantContext, + profile: &domain::Profile, + operation: Op, + req: Req, + get_tracker_response: operations::GetTrackerResponse, + call_connector_action: CallConnectorAction, + header_payload: HeaderPayload, +) -> RouterResult<(D, Req, Option, Option)> +where + F: Send + Clone + Sync, + Req: Send + Sync + Authenticate, + Op: Operation + Send + Sync, + D: OperationSessionGetters + OperationSessionSetters + Send + Sync + Clone, + + // To create connector flow specific interface data + D: ConstructFlowSpecificData, + RouterData: Feature, + + // To construct connector flow specific api + dyn api::Connector: + services::api::ConnectorIntegration, + + RouterData: + hyperswitch_domain_models::router_data::TrackerPostUpdateObjects, + + // To perform router related operation for PaymentResponse + PaymentResponse: Operation, + FData: Send + Sync + Clone, +{ + let operation: BoxedOperation<'_, F, Req, D> = Box::new(operation); + + // Get the trackers related to track the state of the payment + let operations::GetTrackerResponse { mut payment_data } = get_tracker_response; + + let connector_data = operation + .to_domain()? + .get_connector_from_request(state, &req, &mut payment_data) + .await?; + + let router_data = internal_call_connector_service( + state, + req_state.clone(), + &merchant_context, + connector_data, + &operation, + &mut payment_data, + call_connector_action.clone(), + header_payload.clone(), + profile, + req.get_all_keys_required(), + ) + .await?; + + let payments_response_operation = Box::new(PaymentResponse); + + let payment_data = payments_response_operation + .to_post_update_tracker()? + .update_tracker( + state, + payment_data, + router_data, + merchant_context.get_merchant_key_store(), + merchant_context.get_merchant_account().storage_scheme, + ) + .await?; + + Ok((payment_data, req, None, None)) +} + #[cfg(feature = "v1")] #[allow(clippy::too_many_arguments, clippy::type_complexity)] #[instrument(skip_all, fields(payment_id, merchant_id))] @@ -1885,6 +1960,7 @@ pub async fn record_attempt_core( attempts: None, should_sync_with_connector: false, payment_address: payment_data.payment_address.clone(), + merchant_connector_details: None, }; let payment_status_data = (req.triggered_by == common_enums::TriggeredBy::Internal) @@ -1907,6 +1983,7 @@ pub async fn record_attempt_core( expand_attempts: false, param: None, all_keys_required: None, + merchant_connector_details: None, }, operations::GetTrackerResponse { payment_data: PaymentStatusData { @@ -1916,6 +1993,7 @@ pub async fn record_attempt_core( attempts: None, should_sync_with_connector: true, payment_address: payment_data.payment_address.clone(), + merchant_connector_details: None, }, }, CallConnectorAction::Trigger, @@ -2129,19 +2207,38 @@ where ) .await?; - let (payment_data, _req, customer, connector_http_status_code, external_latency) = - payments_operation_core::<_, _, _, _, _>( - &state, - req_state, - merchant_context.clone(), - &profile, - operation.clone(), - req, - get_tracker_response, - call_connector_action, - header_payload.clone(), - ) - .await?; + let (payment_data, connector_http_status_code, external_latency) = + if state.conf.merchant_id_auth.merchant_id_auth_enabled { + let (payment_data, _req, connector_http_status_code, external_latency) = + internal_payments_operation_core::<_, _, _, _, _>( + &state, + req_state, + merchant_context.clone(), + &profile, + operation.clone(), + req, + get_tracker_response, + call_connector_action, + header_payload.clone(), + ) + .await?; + (payment_data, connector_http_status_code, external_latency) + } else { + let (payment_data, _req, _customer, connector_http_status_code, external_latency) = + payments_operation_core::<_, _, _, _, _>( + &state, + req_state, + merchant_context.clone(), + &profile, + operation.clone(), + req, + get_tracker_response, + call_connector_action, + header_payload.clone(), + ) + .await?; + (payment_data, connector_http_status_code, external_latency) + }; payment_data.generate_response( &state, @@ -2777,6 +2874,7 @@ impl PaymentRedirectFlow for PaymentRedirectSync { force_sync: true, expand_attempts: false, all_keys_required: None, + merchant_connector_details: todo!(), // TODO: Implement for connectors requiring 3DS or redirection-based authentication flows. }; let operation = operations::PaymentGet; @@ -3490,24 +3588,15 @@ where { let stime_connector = Instant::now(); - let merchant_connector_id = connector - .merchant_connector_id - .as_ref() - .get_required_value("merchant_connector_id") - .change_context(errors::ApiErrorResponse::InternalServerError) - .attach_printable("connector id is not set")?; - - let merchant_connector_account = state - .store - .find_merchant_connector_account_by_id( - &state.into(), - merchant_connector_id, - merchant_context.get_merchant_key_store(), - ) - .await - .to_not_found_response(errors::ApiErrorResponse::MerchantConnectorAccountNotFound { - id: merchant_connector_id.get_string_repr().to_owned(), - })?; + let merchant_connector_account = + domain::MerchantConnectorAccountTypeDetails::MerchantConnectorAccount(Box::new( + helpers::get_merchant_connector_account_v2( + state, + merchant_context.get_merchant_key_store(), + connector.merchant_connector_id.as_ref(), + ) + .await?, + )); operation .to_domain()? @@ -3822,24 +3911,15 @@ where { let stime_connector = Instant::now(); - let merchant_connector_id = connector - .merchant_connector_id - .as_ref() - .get_required_value("merchant_connector_id") - .change_context(errors::ApiErrorResponse::InternalServerError) - .attach_printable("connector id is not set")?; - - let merchant_connector_account = state - .store - .find_merchant_connector_account_by_id( - &state.into(), - merchant_connector_id, - merchant_context.get_merchant_key_store(), - ) - .await - .to_not_found_response(errors::ApiErrorResponse::MerchantConnectorAccountNotFound { - id: merchant_connector_id.get_string_repr().to_owned(), - })?; + let merchant_connector_account = + domain::MerchantConnectorAccountTypeDetails::MerchantConnectorAccount(Box::new( + helpers::get_merchant_connector_account_v2( + state, + merchant_context.get_merchant_key_store(), + connector.merchant_connector_id.as_ref(), + ) + .await?, + )); let mut router_data = payment_data .construct_router_data( @@ -3916,6 +3996,137 @@ where Ok(router_data) } +#[cfg(feature = "v2")] +#[allow(clippy::too_many_arguments)] +#[instrument(skip_all)] +pub async fn internal_call_connector_service( + state: &SessionState, + req_state: ReqState, + merchant_context: &domain::MerchantContext, + connector: api::ConnectorData, + operation: &BoxedOperation<'_, F, ApiRequest, D>, + payment_data: &mut D, + call_connector_action: CallConnectorAction, + header_payload: HeaderPayload, + business_profile: &domain::Profile, + all_keys_required: Option, +) -> RouterResult> +where + F: Send + Clone + Sync, + RouterDReq: Send + Sync, + + // To create connector flow specific interface data + D: OperationSessionGetters + OperationSessionSetters + Send + Sync + Clone, + D: ConstructFlowSpecificData, + RouterData: Feature + Send, + // To construct connector flow specific api + dyn api::Connector: + services::api::ConnectorIntegration, +{ + let stime_connector = Instant::now(); + + let merchant_connector_details = + payment_data + .get_merchant_connector_details() + .ok_or_else(|| { + error_stack::report!(errors::ApiErrorResponse::InternalServerError) + .attach_printable("Merchant connector details not found in payment data") + })?; + let merchant_connector_account = + domain::MerchantConnectorAccountTypeDetails::MerchantConnectorDetails( + merchant_connector_details, + ); + + operation + .to_domain()? + .populate_payment_data( + state, + payment_data, + merchant_context, + business_profile, + &connector, + ) + .await?; + + let mut router_data = payment_data + .construct_router_data( + state, + connector.connector.id(), + merchant_context, + &None, + &merchant_connector_account, + None, + None, + ) + .await?; + + let add_access_token_result = router_data + .add_access_token( + state, + &connector, + merchant_context, + payment_data.get_creds_identifier(), + ) + .await?; + + router_data = router_data.add_session_token(state, &connector).await?; + + let mut should_continue_further = access_token::update_router_data_with_access_token_result( + &add_access_token_result, + &mut router_data, + &call_connector_action, + ); + + let should_continue_further = router_data + .create_order_at_connector(state, &connector, should_continue_further) + .await?; + + let (connector_request, should_continue_further) = if should_continue_further { + router_data + .build_flow_specific_connector_request(state, &connector, call_connector_action.clone()) + .await? + } else { + (None, false) + }; + + (_, *payment_data) = operation + .to_update_tracker()? + .update_trackers( + state, + req_state, + payment_data.clone(), + None, + merchant_context.get_merchant_account().storage_scheme, + None, + merchant_context.get_merchant_key_store(), + None, + header_payload.clone(), + ) + .await?; + + let router_data = if should_continue_further { + router_data + .decide_flows( + state, + &connector, + call_connector_action, + connector_request, + business_profile, + header_payload.clone(), + all_keys_required, + ) + .await + } else { + Ok(router_data) + }?; + + let etime_connector = Instant::now(); + let duration_connector = etime_connector.saturating_duration_since(stime_connector); + tracing::info!(duration = format!("Duration taken: {}", duration_connector.as_millis())); + + Ok(router_data) +} + pub async fn add_decrypted_payment_method_token( tokenization_action: TokenizationAction, payment_data: &D, @@ -4168,7 +4379,7 @@ pub async fn call_multiple_connectors_service( ) -> RouterResult where Op: Debug, - F: Send + Clone, + F: Send + Clone + Sync, // To create connector flow specific interface data D: OperationSessionGetters + OperationSessionSetters + Send + Sync + Clone, @@ -4182,25 +4393,19 @@ where let call_connectors_start_time = Instant::now(); let mut join_handlers = Vec::with_capacity(connectors.len()); for session_connector_data in connectors.iter() { - let merchant_connector_id = session_connector_data - .connector - .merchant_connector_id - .as_ref() - .get_required_value("merchant_connector_id") - .change_context(errors::ApiErrorResponse::InternalServerError) - .attach_printable("connector id is not set")?; - // TODO: make this DB call parallel - let merchant_connector_account = state - .store - .find_merchant_connector_account_by_id( - &state.into(), - merchant_connector_id, - merchant_context.get_merchant_key_store(), - ) - .await - .to_not_found_response(errors::ApiErrorResponse::MerchantConnectorAccountNotFound { - id: merchant_connector_id.get_string_repr().to_owned(), - })?; + let merchant_connector_account = + domain::MerchantConnectorAccountTypeDetails::MerchantConnectorAccount(Box::new( + helpers::get_merchant_connector_account_v2( + state, + merchant_context.get_merchant_key_store(), + session_connector_data + .connector + .merchant_connector_id + .as_ref(), + ) + .await?, + )); + let connector_id = session_connector_data.connector.connector.id(); let router_data = payment_data .construct_router_data( @@ -4692,7 +4897,7 @@ pub async fn call_create_connector_customer_if_required( state: &SessionState, customer: &Option, merchant_context: &domain::MerchantContext, - merchant_connector_account: &domain::MerchantConnectorAccount, + merchant_connector_account: &domain::MerchantConnectorAccountTypeDetails, payment_data: &mut D, ) -> RouterResult> where @@ -4716,17 +4921,15 @@ where &state.conf.connectors, &connector_name, api::GetToken::Connector, - Some(merchant_connector_account.get_id()), + merchant_connector_account.get_id(), )?; - let merchant_connector_id = merchant_connector_account.get_id(); - let (should_call_connector, existing_connector_customer_id) = customers::should_call_connector_create_customer( state, &connector, customer, - &merchant_connector_id, + merchant_connector_account, ); if should_call_connector { @@ -4748,7 +4951,7 @@ where .await?; let customer_update = customers::update_connector_customer_in_customers( - merchant_connector_id, + merchant_connector_account, customer.as_ref(), connector_customer_id.clone(), ) @@ -8549,6 +8752,10 @@ pub trait OperationSessionGetters { fn get_all_keys_required(&self) -> Option; fn get_capture_method(&self) -> Option; fn get_merchant_connector_id_in_attempt(&self) -> Option; + #[cfg(feature = "v2")] + fn get_merchant_connector_details( + &self, + ) -> Option; fn get_connector_customer_id(&self) -> Option; fn get_whole_connector_response(&self) -> Option; @@ -9045,6 +9252,12 @@ impl OperationSessionGetters for PaymentIntentData { self.connector_customer_id.clone() } + fn get_merchant_connector_details( + &self, + ) -> Option { + todo!() + } + fn get_billing_address(&self) -> Option { todo!() } @@ -9224,6 +9437,12 @@ impl OperationSessionGetters for PaymentConfirmData { &self.payment_intent } + fn get_merchant_connector_details( + &self, + ) -> Option { + self.merchant_connector_details.clone() + } + fn get_payment_method_info(&self) -> Option<&domain::PaymentMethod> { todo!() } @@ -9503,6 +9722,12 @@ impl OperationSessionGetters for PaymentStatusData { &self.payment_intent } + fn get_merchant_connector_details( + &self, + ) -> Option { + self.merchant_connector_details.clone() + } + fn get_payment_method_info(&self) -> Option<&domain::PaymentMethod> { todo!() } @@ -9750,7 +9975,7 @@ impl OperationSessionSetters for PaymentStatusData { } fn set_connector_in_payment_attempt(&mut self, connector: Option) { - todo!() + self.payment_attempt.connector = connector; } fn set_connector_request_reference_id(&mut self, reference_id: Option) { @@ -9778,6 +10003,12 @@ impl OperationSessionGetters for PaymentCaptureData { &self.payment_intent } + fn get_merchant_connector_details( + &self, + ) -> Option { + todo!() + } + fn get_payment_method_info(&self) -> Option<&domain::PaymentMethod> { todo!() } diff --git a/crates/router/src/core/payments/customers.rs b/crates/router/src/core/payments/customers.rs index c520926504..210a6dd09f 100644 --- a/crates/router/src/core/payments/customers.rs +++ b/crates/router/src/core/payments/customers.rs @@ -104,23 +104,32 @@ pub fn should_call_connector_create_customer<'a>( state: &SessionState, connector: &api::ConnectorData, customer: &'a Option, - merchant_connector_id: &common_utils::id_type::MerchantConnectorAccountId, + merchant_connector_account: &domain::MerchantConnectorAccountTypeDetails, ) -> (bool, Option<&'a str>) { // Check if create customer is required for the connector - let connector_needs_customer = state - .conf - .connector_customer - .connector_list - .contains(&connector.connector_name); + match merchant_connector_account { + domain::MerchantConnectorAccountTypeDetails::MerchantConnectorAccount(_) => { + let connector_needs_customer = state + .conf + .connector_customer + .connector_list + .contains(&connector.connector_name); - if connector_needs_customer { - let connector_customer_details = customer - .as_ref() - .and_then(|customer| customer.get_connector_customer_id(merchant_connector_id)); - let should_call_connector = connector_customer_details.is_none(); - (should_call_connector, connector_customer_details) - } else { - (false, None) + if connector_needs_customer { + let connector_customer_details = customer + .as_ref() + .and_then(|cust| cust.get_connector_customer_id(merchant_connector_account)); + let should_call_connector = connector_customer_details.is_none(); + (should_call_connector, connector_customer_details) + } else { + (false, None) + } + } + + // TODO: Construct connector_customer for MerchantConnectorDetails if required by connector. + domain::MerchantConnectorAccountTypeDetails::MerchantConnectorDetails(_) => { + todo!("Handle connector_customer construction for MerchantConnectorDetails"); + } } } @@ -154,18 +163,26 @@ pub async fn update_connector_customer_in_customers( #[cfg(feature = "v2")] #[instrument] pub async fn update_connector_customer_in_customers( - merchant_connector_id: common_utils::id_type::MerchantConnectorAccountId, + merchant_connector_account: &domain::MerchantConnectorAccountTypeDetails, customer: Option<&domain::Customer>, connector_customer_id: Option, ) -> Option { - connector_customer_id.map(|connector_customer_id| { - let mut connector_customer_map = customer - .and_then(|customer| customer.connector_customer.clone()) - .unwrap_or_default(); - connector_customer_map.insert(merchant_connector_id, connector_customer_id); - - storage::CustomerUpdate::ConnectorCustomer { - connector_customer: Some(connector_customer_map), + match merchant_connector_account { + domain::MerchantConnectorAccountTypeDetails::MerchantConnectorAccount(account) => { + connector_customer_id.map(|new_conn_cust_id| { + let connector_account_id = account.get_id().clone(); + let mut connector_customer_map = customer + .and_then(|customer| customer.connector_customer.clone()) + .unwrap_or_default(); + connector_customer_map.insert(connector_account_id, new_conn_cust_id); + storage::CustomerUpdate::ConnectorCustomer { + connector_customer: Some(connector_customer_map), + } + }) } - }) + // TODO: Construct connector_customer for MerchantConnectorDetails if required by connector. + domain::MerchantConnectorAccountTypeDetails::MerchantConnectorDetails(_) => { + todo!("Handle connector_customer construction for MerchantConnectorDetails"); + } + } } diff --git a/crates/router/src/core/payments/flows.rs b/crates/router/src/core/payments/flows.rs index 38ecb041f8..212aab9826 100644 --- a/crates/router/src/core/payments/flows.rs +++ b/crates/router/src/core/payments/flows.rs @@ -75,7 +75,7 @@ pub trait ConstructFlowSpecificData { _connector_id: &str, _merchant_context: &domain::MerchantContext, _customer: &Option, - _merchant_connector_account: &domain::MerchantConnectorAccount, + _merchant_connector_account: &domain::MerchantConnectorAccountTypeDetails, _merchant_recipient_data: Option, _header_payload: Option, ) -> RouterResult>; diff --git a/crates/router/src/core/payments/flows/approve_flow.rs b/crates/router/src/core/payments/flows/approve_flow.rs index 7c9bb5b450..30b7b834f5 100644 --- a/crates/router/src/core/payments/flows/approve_flow.rs +++ b/crates/router/src/core/payments/flows/approve_flow.rs @@ -23,7 +23,7 @@ impl connector_id: &str, merchant_context: &domain::MerchantContext, customer: &Option, - merchant_connector_account: &domain::MerchantConnectorAccount, + merchant_connector_account: &domain::MerchantConnectorAccountTypeDetails, merchant_recipient_data: Option, header_payload: Option, ) -> RouterResult { diff --git a/crates/router/src/core/payments/flows/authorize_flow.rs b/crates/router/src/core/payments/flows/authorize_flow.rs index 2cd42f39e6..ee27620917 100644 --- a/crates/router/src/core/payments/flows/authorize_flow.rs +++ b/crates/router/src/core/payments/flows/authorize_flow.rs @@ -40,7 +40,7 @@ impl connector_id: &str, merchant_context: &domain::MerchantContext, customer: &Option, - merchant_connector_account: &domain::MerchantConnectorAccount, + merchant_connector_account: &domain::MerchantConnectorAccountTypeDetails, merchant_recipient_data: Option, header_payload: Option, ) -> RouterResult< diff --git a/crates/router/src/core/payments/flows/cancel_flow.rs b/crates/router/src/core/payments/flows/cancel_flow.rs index e6eebba312..fc27a60042 100644 --- a/crates/router/src/core/payments/flows/cancel_flow.rs +++ b/crates/router/src/core/payments/flows/cancel_flow.rs @@ -22,7 +22,7 @@ impl ConstructFlowSpecificData, - _merchant_connector_account: &domain::MerchantConnectorAccount, + _merchant_connector_account: &domain::MerchantConnectorAccountTypeDetails, _merchant_recipient_data: Option, _header_payload: Option, ) -> RouterResult { diff --git a/crates/router/src/core/payments/flows/capture_flow.rs b/crates/router/src/core/payments/flows/capture_flow.rs index 9b681e107a..545b3fc263 100644 --- a/crates/router/src/core/payments/flows/capture_flow.rs +++ b/crates/router/src/core/payments/flows/capture_flow.rs @@ -66,7 +66,7 @@ impl connector_id: &str, merchant_context: &domain::MerchantContext, customer: &Option, - merchant_connector_account: &domain::MerchantConnectorAccount, + merchant_connector_account: &domain::MerchantConnectorAccountTypeDetails, merchant_recipient_data: Option, header_payload: Option, ) -> RouterResult< diff --git a/crates/router/src/core/payments/flows/complete_authorize_flow.rs b/crates/router/src/core/payments/flows/complete_authorize_flow.rs index 768614b130..4fb4e7574a 100644 --- a/crates/router/src/core/payments/flows/complete_authorize_flow.rs +++ b/crates/router/src/core/payments/flows/complete_authorize_flow.rs @@ -60,7 +60,7 @@ impl connector_id: &str, merchant_context: &domain::MerchantContext, customer: &Option, - merchant_connector_account: &domain::MerchantConnectorAccount, + merchant_connector_account: &domain::MerchantConnectorAccountTypeDetails, merchant_recipient_data: Option, header_payload: Option, ) -> RouterResult< diff --git a/crates/router/src/core/payments/flows/incremental_authorization_flow.rs b/crates/router/src/core/payments/flows/incremental_authorization_flow.rs index 74419a5cfb..092be33219 100644 --- a/crates/router/src/core/payments/flows/incremental_authorization_flow.rs +++ b/crates/router/src/core/payments/flows/incremental_authorization_flow.rs @@ -53,7 +53,7 @@ impl connector_id: &str, merchant_context: &domain::MerchantContext, customer: &Option, - merchant_connector_account: &domain::MerchantConnectorAccount, + merchant_connector_account: &domain::MerchantConnectorAccountTypeDetails, merchant_recipient_data: Option, header_payload: Option, ) -> RouterResult { diff --git a/crates/router/src/core/payments/flows/post_session_tokens_flow.rs b/crates/router/src/core/payments/flows/post_session_tokens_flow.rs index 483d04e94d..e29739cf01 100644 --- a/crates/router/src/core/payments/flows/post_session_tokens_flow.rs +++ b/crates/router/src/core/payments/flows/post_session_tokens_flow.rs @@ -53,7 +53,7 @@ impl connector_id: &str, merchant_context: &domain::MerchantContext, customer: &Option, - merchant_connector_account: &domain::MerchantConnectorAccount, + merchant_connector_account: &domain::MerchantConnectorAccountTypeDetails, merchant_recipient_data: Option, header_payload: Option, ) -> RouterResult { diff --git a/crates/router/src/core/payments/flows/psync_flow.rs b/crates/router/src/core/payments/flows/psync_flow.rs index de435d47b1..38fc08a1df 100644 --- a/crates/router/src/core/payments/flows/psync_flow.rs +++ b/crates/router/src/core/payments/flows/psync_flow.rs @@ -69,7 +69,7 @@ impl ConstructFlowSpecificData, - merchant_connector_account: &domain::MerchantConnectorAccount, + merchant_connector_account: &domain::MerchantConnectorAccountTypeDetails, merchant_recipient_data: Option, header_payload: Option, ) -> RouterResult< diff --git a/crates/router/src/core/payments/flows/reject_flow.rs b/crates/router/src/core/payments/flows/reject_flow.rs index f333518db2..852058dc37 100644 --- a/crates/router/src/core/payments/flows/reject_flow.rs +++ b/crates/router/src/core/payments/flows/reject_flow.rs @@ -49,7 +49,7 @@ impl ConstructFlowSpecificData, - merchant_connector_account: &domain::MerchantConnectorAccount, + merchant_connector_account: &domain::MerchantConnectorAccountTypeDetails, merchant_recipient_data: Option, header_payload: Option, ) -> RouterResult { diff --git a/crates/router/src/core/payments/flows/session_flow.rs b/crates/router/src/core/payments/flows/session_flow.rs index 6e9ed9e2e6..3c768a40b1 100644 --- a/crates/router/src/core/payments/flows/session_flow.rs +++ b/crates/router/src/core/payments/flows/session_flow.rs @@ -40,7 +40,7 @@ impl connector_id: &str, merchant_context: &domain::MerchantContext, customer: &Option, - merchant_connector_account: &domain::MerchantConnectorAccount, + merchant_connector_account: &domain::MerchantConnectorAccountTypeDetails, merchant_recipient_data: Option, header_payload: Option, ) -> RouterResult { diff --git a/crates/router/src/core/payments/flows/session_update_flow.rs b/crates/router/src/core/payments/flows/session_update_flow.rs index 321f368199..e717ff50c2 100644 --- a/crates/router/src/core/payments/flows/session_update_flow.rs +++ b/crates/router/src/core/payments/flows/session_update_flow.rs @@ -53,7 +53,7 @@ impl connector_id: &str, merchant_context: &domain::MerchantContext, customer: &Option, - merchant_connector_account: &domain::MerchantConnectorAccount, + merchant_connector_account: &domain::MerchantConnectorAccountTypeDetails, _merchant_recipient_data: Option, _header_payload: Option, ) -> RouterResult { diff --git a/crates/router/src/core/payments/flows/setup_mandate_flow.rs b/crates/router/src/core/payments/flows/setup_mandate_flow.rs index fa2f94873a..d4722a05c8 100644 --- a/crates/router/src/core/payments/flows/setup_mandate_flow.rs +++ b/crates/router/src/core/payments/flows/setup_mandate_flow.rs @@ -76,7 +76,7 @@ impl connector_id: &str, merchant_context: &domain::MerchantContext, customer: &Option, - merchant_connector_account: &domain::MerchantConnectorAccount, + merchant_connector_account: &domain::MerchantConnectorAccountTypeDetails, merchant_recipient_data: Option, header_payload: Option, ) -> RouterResult { diff --git a/crates/router/src/core/payments/flows/update_metadata_flow.rs b/crates/router/src/core/payments/flows/update_metadata_flow.rs index 85d942f52c..a6a3583325 100644 --- a/crates/router/src/core/payments/flows/update_metadata_flow.rs +++ b/crates/router/src/core/payments/flows/update_metadata_flow.rs @@ -52,7 +52,7 @@ impl connector_id: &str, merchant_context: &domain::MerchantContext, customer: &Option, - merchant_connector_account: &domain::MerchantConnectorAccount, + merchant_connector_account: &domain::MerchantConnectorAccountTypeDetails, merchant_recipient_data: Option, header_payload: Option, ) -> RouterResult { diff --git a/crates/router/src/core/payments/helpers.rs b/crates/router/src/core/payments/helpers.rs index d2c490a4d4..cfec9d6eaa 100644 --- a/crates/router/src/core/payments/helpers.rs +++ b/crates/router/src/core/payments/helpers.rs @@ -95,7 +95,7 @@ use crate::{ }, }; #[cfg(feature = "v2")] -use crate::{core::admin as core_admin, headers}; +use crate::{core::admin as core_admin, headers, types::ConnectorAuthType}; #[cfg(feature = "v1")] use crate::{ core::payment_methods::cards::create_encrypted_data, types::storage::CustomerUpdate::Update, @@ -1599,6 +1599,28 @@ pub async fn get_connector_default( )) } +#[cfg(feature = "v2")] +pub async fn get_connector_data_from_request( + state: &SessionState, + req: Option, +) -> CustomResult { + let connector = req + .as_ref() + .map(|connector_details| connector_details.connector_name.to_string()) + .ok_or(errors::ApiErrorResponse::MissingRequiredField { + field_name: "merchant_connector_details", + })?; + let connector_data: api::ConnectorData = api::ConnectorData::get_connector_by_name( + &state.conf.connectors, + &connector, + api::GetToken::Connector, + None, + ) + .change_context(errors::ApiErrorResponse::InternalServerError) + .attach_printable("Invalid connector name received")?; + Ok(connector_data) +} + #[cfg(feature = "v2")] #[instrument(skip_all)] #[allow(clippy::type_complexity)] @@ -3950,15 +3972,6 @@ impl MerchantConnectorAccountType { Self::CacheVal(_) => None, } } - - pub fn get_inner_db_merchant_connector_account( - &self, - ) -> Option<&domain::MerchantConnectorAccount> { - match self { - Self::DbVal(db_val) => Some(db_val), - Self::CacheVal(_) => None, - } - } } /// Query for merchant connector account either by business label or profile id @@ -7323,114 +7336,24 @@ pub async fn allow_payment_update_enabled_for_client_auth( } } -/// Query for merchant connector account either by business label #[cfg(feature = "v2")] #[instrument(skip_all)] pub async fn get_merchant_connector_account_v2( state: &SessionState, - merchant_id: &id_type::MerchantId, - creds_identifier: Option<&str>, key_store: &domain::MerchantKeyStore, merchant_connector_id: Option<&id_type::MerchantConnectorAccountId>, -) -> RouterResult { +) -> RouterResult { let db = &*state.store; - let key_manager_state: &KeyManagerState = &state.into(); - match creds_identifier { - Some(creds_identifier) => { - let key = merchant_id.get_creds_identifier_key(creds_identifier); - let cloned_key = key.clone(); - let redis_fetch = || async { - db.get_redis_conn() - .change_context(errors::ApiErrorResponse::InternalServerError) - .attach_printable("Failed to get redis connection") - .async_and_then(|redis| async move { - redis - .get_and_deserialize_key(&key.as_str().into(), "String") - .await - .change_context( - errors::ApiErrorResponse::MerchantConnectorAccountNotFound { - id: key.clone(), - }, - ) - .attach_printable(key.clone() + ": Not found in Redis") - }) - .await - }; - - let db_fetch = || async { - db.find_config_by_key(cloned_key.as_str()) - .await - .to_not_found_response( - errors::ApiErrorResponse::MerchantConnectorAccountNotFound { - id: cloned_key.to_owned(), - }, - ) - }; - - let mca_config: String = redis_fetch() - .await - .map_or_else( - |_| { - Either::Left(async { - match db_fetch().await { - Ok(config_entry) => Ok(config_entry.config), - Err(e) => Err(e), - } - }) - }, - |result| Either::Right(async { Ok(result) }), - ) - .await?; - - let private_key = state - .conf - .jwekey - .get_inner() - .tunnel_private_key - .peek() - .as_bytes(); - - let decrypted_mca = services::decrypt_jwe(mca_config.as_str(), services::KeyIdCheck::SkipKeyIdCheck, private_key, jwe::RSA_OAEP_256) - .await - .change_context(errors::ApiErrorResponse::UnprocessableEntity{ - message: "decoding merchant_connector_details failed due to invalid data format!".into()}) - .attach_printable( - "Failed to decrypt merchant_connector_details sent in request and then put in cache", - )?; - - let res = String::into_bytes(decrypted_mca) - .parse_struct("MerchantConnectorDetails") - .change_context(errors::ApiErrorResponse::InternalServerError) - .attach_printable( - "Failed to parse merchant_connector_details sent in request and then put in cache", - )?; - - Ok(MerchantConnectorAccountType::CacheVal(res)) - } - None => { - let mca: RouterResult = if let Some( - merchant_connector_id, - ) = merchant_connector_id - { - db.find_merchant_connector_account_by_id( - &state.into(), - merchant_connector_id, - key_store, - ) - .await - .to_not_found_response( - errors::ApiErrorResponse::MerchantConnectorAccountNotFound { - id: merchant_connector_id.get_string_repr().to_string(), - }, - ) - } else { - Err(errors::ApiErrorResponse::MissingRequiredField { - field_name: "merchant_connector_id", - }).attach_printable( - "merchant_connector_id is required when creds_identifier is not provided for get_merchant_connector_account_v2", - ) - }; - mca.map(Box::new).map(MerchantConnectorAccountType::DbVal) - } + match merchant_connector_id { + Some(merchant_connector_id) => db + .find_merchant_connector_account_by_id(&state.into(), merchant_connector_id, key_store) + .await + .to_not_found_response(errors::ApiErrorResponse::MerchantConnectorAccountNotFound { + id: merchant_connector_id.get_string_repr().to_string(), + }), + None => Err(errors::ApiErrorResponse::MissingRequiredField { + field_name: "merchant_connector_id", + }) + .attach_printable("merchant_connector_id is not provided"), } } diff --git a/crates/router/src/core/payments/operations.rs b/crates/router/src/core/payments/operations.rs index ff873b1377..223ab65d0f 100644 --- a/crates/router/src/core/payments/operations.rs +++ b/crates/router/src/core/payments/operations.rs @@ -291,6 +291,17 @@ pub trait Domain: Send + Sync { payment_intent: &storage::PaymentIntent, ) -> CustomResult; + #[cfg(feature = "v2")] + async fn get_connector_from_request<'a>( + &'a self, + state: &SessionState, + request: &R, + payment_data: &mut D, + ) -> CustomResult { + Err(report!(errors::ApiErrorResponse::InternalServerError)) + .attach_printable_lazy(|| "get connector for tunnel not implemented".to_string()) + } + #[cfg(feature = "v2")] async fn perform_routing<'a>( &'a self, diff --git a/crates/router/src/core/payments/operations/payment_confirm_intent.rs b/crates/router/src/core/payments/operations/payment_confirm_intent.rs index 94ed262473..4f58107fa9 100644 --- a/crates/router/src/core/payments/operations/payment_confirm_intent.rs +++ b/crates/router/src/core/payments/operations/payment_confirm_intent.rs @@ -1,6 +1,6 @@ use api_models::{enums::FrmSuggestion, payments::PaymentsConfirmIntentRequest}; use async_trait::async_trait; -use common_utils::{ext_traits::Encode, fp_utils::when, types::keymanager::ToEncryptable}; +use common_utils::{ext_traits::Encode, fp_utils::when, id_type, types::keymanager::ToEncryptable}; use error_stack::ResultExt; use hyperswitch_domain_models::payments::PaymentConfirmData; use hyperswitch_interfaces::api::ConnectorSpecifications; @@ -151,7 +151,7 @@ impl GetTracker, PaymentsConfir async fn get_trackers<'a>( &'a self, state: &'a SessionState, - payment_id: &common_utils::id_type::GlobalPaymentId, + payment_id: &id_type::GlobalPaymentId, request: &PaymentsConfirmIntentRequest, merchant_context: &domain::MerchantContext, profile: &domain::Profile, @@ -261,6 +261,8 @@ impl GetTracker, PaymentsConfir Some(true), ); + let merchant_connector_details = request.merchant_connector_details.clone(); + let payment_data = PaymentConfirmData { flow: std::marker::PhantomData, payment_intent, @@ -269,6 +271,7 @@ impl GetTracker, PaymentsConfir payment_address, mandate_data: None, payment_method: None, + merchant_connector_details, }; let get_trackers_response = operations::GetTrackerResponse { payment_data }; @@ -356,7 +359,6 @@ impl Domain, ) -> CustomResult { payments::connector_selection( @@ -506,6 +508,23 @@ impl Domain( + &'a self, + state: &SessionState, + request: &PaymentsConfirmIntentRequest, + payment_data: &mut PaymentConfirmData, + ) -> CustomResult { + let connector_data = helpers::get_connector_data_from_request( + state, + request.merchant_connector_details.clone(), + ) + .await?; + payment_data + .set_connector_in_payment_attempt(Some(connector_data.connector_name.to_string())); + Ok(connector_data) + } } #[async_trait] @@ -542,13 +561,19 @@ impl UpdateTracker, PaymentsConfirmInt .change_context(errors::ApiErrorResponse::InternalServerError) .attach_printable("Connector is none when constructing response")?; - let merchant_connector_id = payment_data - .payment_attempt - .merchant_connector_id - .clone() - .get_required_value("merchant_connector_id") - .change_context(errors::ApiErrorResponse::InternalServerError) - .attach_printable("Merchant connector id is none when constructing response")?; + // If `merchant_connector_details` are present in the payment request, `merchant_connector_id` will not be populated. + let merchant_connector_id = match &payment_data.merchant_connector_details { + Some(_details) => None, + None => Some( + payment_data + .payment_attempt + .merchant_connector_id + .clone() + .get_required_value("merchant_connector_id") + .change_context(errors::ApiErrorResponse::InternalServerError) + .attach_printable("Merchant connector id is none when constructing response")?, + ), + }; let payment_intent_update = hyperswitch_domain_models::payments::payment_intent::PaymentIntentUpdate::ConfirmIntent { @@ -565,12 +590,16 @@ impl UpdateTracker, PaymentsConfirmInt .clone(); let payment_attempt_update = match &payment_data.payment_method { + // In the case of a tokenized payment method, we update the payment attempt with the tokenized payment method details. Some(payment_method) => { hyperswitch_domain_models::payments::payment_attempt::PaymentAttemptUpdate::ConfirmIntentTokenized { status: attempt_status, updated_by: storage_scheme.to_string(), connector, - merchant_connector_id, + merchant_connector_id: merchant_connector_id.ok_or_else( || { + error_stack::report!(errors::ApiErrorResponse::InternalServerError) + .attach_printable("Merchant connector id is none when constructing response") + })?, authentication_type, payment_method_id : payment_method.get_id().clone() } diff --git a/crates/router/src/core/payments/operations/payment_get.rs b/crates/router/src/core/payments/operations/payment_get.rs index 91c89f422b..63f64fb233 100644 --- a/crates/router/src/core/payments/operations/payment_get.rs +++ b/crates/router/src/core/payments/operations/payment_get.rs @@ -234,6 +234,8 @@ impl GetTracker, PaymentsRetriev false => None, }; + let merchant_connector_details = request.merchant_connector_details.clone(); + let payment_data = PaymentStatusData { flow: std::marker::PhantomData, payment_intent, @@ -241,6 +243,7 @@ impl GetTracker, PaymentsRetriev payment_address, attempts, should_sync_with_connector, + merchant_connector_details, }; let get_trackers_response = operations::GetTrackerResponse { payment_data }; @@ -340,6 +343,26 @@ impl Domain( + &'a self, + state: &SessionState, + request: &PaymentsRetrieveRequest, + payment_data: &mut PaymentStatusData, + ) -> CustomResult { + use crate::core::payments::OperationSessionSetters; + + let connector_data = helpers::get_connector_data_from_request( + state, + request.merchant_connector_details.clone(), + ) + .await?; + + payment_data + .set_connector_in_payment_attempt(Some(connector_data.connector_name.to_string())); + Ok(connector_data) + } } #[async_trait] diff --git a/crates/router/src/core/payments/operations/proxy_payments_intent.rs b/crates/router/src/core/payments/operations/proxy_payments_intent.rs index e7c97ca680..838cbf9ded 100644 --- a/crates/router/src/core/payments/operations/proxy_payments_intent.rs +++ b/crates/router/src/core/payments/operations/proxy_payments_intent.rs @@ -262,6 +262,7 @@ impl GetTracker, ProxyPaymentsR payment_address, mandate_data: Some(mandate_data_input), payment_method: None, + merchant_connector_details: None, }; let get_trackers_response = operations::GetTrackerResponse { payment_data }; @@ -362,13 +363,15 @@ impl UpdateTracker, ProxyPaymentsReque .change_context(errors::ApiErrorResponse::InternalServerError) .attach_printable("Connector is none when constructing response")?; - let merchant_connector_id = payment_data - .payment_attempt - .merchant_connector_id - .clone() - .get_required_value("merchant_connector_id") - .change_context(errors::ApiErrorResponse::InternalServerError) - .attach_printable("Merchant connector id is none when constructing response")?; + let merchant_connector_id = Some( + payment_data + .payment_attempt + .merchant_connector_id + .clone() + .get_required_value("merchant_connector_id") + .change_context(errors::ApiErrorResponse::InternalServerError) + .attach_printable("Merchant connector id is none when constructing response")?, + ); let payment_intent_update = hyperswitch_domain_models::payments::payment_intent::PaymentIntentUpdate::ConfirmIntent { diff --git a/crates/router/src/core/payments/transformers.rs b/crates/router/src/core/payments/transformers.rs index f467eb39dd..7094c73be5 100644 --- a/crates/router/src/core/payments/transformers.rs +++ b/crates/router/src/core/payments/transformers.rs @@ -185,7 +185,7 @@ pub async fn construct_payment_router_data_for_authorize<'a>( connector_id: &str, merchant_context: &domain::MerchantContext, customer: &'a Option, - merchant_connector_account: &domain::MerchantConnectorAccount, + merchant_connector_account: &domain::MerchantConnectorAccountTypeDetails, _merchant_recipient_data: Option, header_payload: Option, ) -> RouterResult { @@ -225,11 +225,17 @@ pub async fn construct_payment_router_data_for_authorize<'a>( None, )); - let webhook_url = Some(helpers::create_webhook_url( - router_base_url, - &attempt.merchant_id, - merchant_connector_account.get_id().get_string_repr(), - )); + let webhook_url = match merchant_connector_account { + domain::MerchantConnectorAccountTypeDetails::MerchantConnectorAccount( + merchant_connector_account, + ) => Some(helpers::create_webhook_url( + router_base_url, + &attempt.merchant_id, + merchant_connector_account.get_id().get_string_repr(), + )), + // TODO: Implement for connectors that require a webhook URL to be included in the request payload. + domain::MerchantConnectorAccountTypeDetails::MerchantConnectorDetails(_) => None, + }; let router_return_url = payment_data .payment_intent @@ -408,7 +414,7 @@ pub async fn construct_payment_router_data_for_capture<'a>( connector_id: &str, merchant_context: &domain::MerchantContext, customer: &'a Option, - merchant_connector_account: &domain::MerchantConnectorAccount, + merchant_connector_account: &domain::MerchantConnectorAccountTypeDetails, _merchant_recipient_data: Option, header_payload: Option, ) -> RouterResult { @@ -572,7 +578,7 @@ pub async fn construct_router_data_for_psync<'a>( connector_id: &str, merchant_context: &domain::MerchantContext, customer: &'a Option, - merchant_connector_account: &domain::MerchantConnectorAccount, + merchant_connector_account: &domain::MerchantConnectorAccountTypeDetails, _merchant_recipient_data: Option, header_payload: Option, ) -> RouterResult { @@ -698,7 +704,7 @@ pub async fn construct_payment_router_data_for_sdk_session<'a>( connector_id: &str, merchant_context: &domain::MerchantContext, customer: &'a Option, - merchant_connector_account: &domain::MerchantConnectorAccount, + merchant_connector_account: &domain::MerchantConnectorAccountTypeDetails, _merchant_recipient_data: Option, header_payload: Option, ) -> RouterResult { @@ -880,7 +886,7 @@ pub async fn construct_payment_router_data_for_setup_mandate<'a>( connector_id: &str, merchant_context: &domain::MerchantContext, customer: &'a Option, - merchant_connector_account: &domain::MerchantConnectorAccount, + merchant_connector_account: &domain::MerchantConnectorAccountTypeDetails, _merchant_recipient_data: Option, header_payload: Option, ) -> RouterResult { @@ -905,7 +911,7 @@ pub async fn construct_payment_router_data_for_setup_mandate<'a>( let connector_customer_id = customer.as_ref().and_then(|customer| { customer - .get_connector_customer_id(&merchant_connector_account.get_id()) + .get_connector_customer_id(merchant_connector_account) .map(String::from) }); @@ -921,11 +927,19 @@ pub async fn construct_payment_router_data_for_setup_mandate<'a>( None, )); - let webhook_url = Some(helpers::create_webhook_url( - router_base_url, - &attempt.merchant_id, - merchant_connector_account.get_id().get_string_repr(), - )); + let webhook_url = match merchant_connector_account { + domain::MerchantConnectorAccountTypeDetails::MerchantConnectorAccount( + merchant_connector_account, + ) => Some(helpers::create_webhook_url( + router_base_url, + &attempt.merchant_id, + merchant_connector_account.get_id().get_string_repr(), + )), + // TODO: Implement for connectors that require a webhook URL to be included in the request payload. + domain::MerchantConnectorAccountTypeDetails::MerchantConnectorDetails(_) => { + todo!("Add webhook URL to request for this connector") + } + }; let router_return_url = payment_data .payment_intent @@ -1843,12 +1857,7 @@ where .change_context(errors::ApiErrorResponse::InternalServerError) .attach_printable("Connector is none when constructing response")?; - let merchant_connector_id = payment_attempt - .merchant_connector_id - .clone() - .get_required_value("merchant_connector_id") - .change_context(errors::ApiErrorResponse::InternalServerError) - .attach_printable("Merchant connector id is none when constructing response")?; + let merchant_connector_id = payment_attempt.merchant_connector_id.clone(); let error = payment_attempt .error @@ -1913,7 +1922,7 @@ where connector_transaction_id: payment_attempt.connector_payment_id.clone(), connector_reference_id: None, connector_token_details, - merchant_connector_id: Some(merchant_connector_id), + merchant_connector_id, browser_info: None, error, return_url, @@ -1924,6 +1933,7 @@ where billing: None, //TODO: add this shipping: None, //TODO: add this is_iframe_redirection_enabled: None, + merchant_reference_id: payment_intent.merchant_reference_id.clone(), }; Ok(services::ApplicationResponse::JsonWithHeaders(( @@ -2018,6 +2028,7 @@ where attempts, return_url, is_iframe_redirection_enabled: payment_intent.is_iframe_redirection_enabled, + merchant_reference_id: payment_intent.merchant_reference_id.clone(), }; Ok(services::ApplicationResponse::JsonWithHeaders(( diff --git a/crates/router/src/core/payments/vault_session.rs b/crates/router/src/core/payments/vault_session.rs index bb9b714a9b..51a2badfe2 100644 --- a/crates/router/src/core/payments/vault_session.rs +++ b/crates/router/src/core/payments/vault_session.rs @@ -76,14 +76,15 @@ where .as_ref() .map(|details| &details.vault_connector_id); - let merchant_connector_account = helpers::get_merchant_connector_account_v2( - state, - merchant_context.get_merchant_account().get_id(), - None, - merchant_context.get_merchant_key_store(), - external_vault_source, - ) - .await?; + let merchant_connector_account = + domain::MerchantConnectorAccountTypeDetails::MerchantConnectorAccount(Box::new( + helpers::get_merchant_connector_account_v2( + state, + merchant_context.get_merchant_key_store(), + external_vault_source, + ) + .await?, + )); let updated_customer = call_create_connector_customer_if_required( state, @@ -132,7 +133,7 @@ pub async fn call_create_connector_customer_if_required( state: &SessionState, customer: &Option, merchant_context: &domain::MerchantContext, - merchant_connector_account_type: &payment_helpers::MerchantConnectorAccountType, + merchant_connector_account_type: &domain::MerchantConnectorAccountTypeDetails, payment_data: &mut D, ) -> RouterResult> where @@ -168,7 +169,7 @@ where state, &connector, customer, - &merchant_connector_id, + merchant_connector_account_type, ); if should_call_connector { @@ -179,7 +180,7 @@ where connector.connector.id(), merchant_context, customer, - merchant_connector_account, + merchant_connector_account_type, None, None, ) @@ -190,7 +191,7 @@ where .await?; let customer_update = customers::update_connector_customer_in_customers( - merchant_connector_id, + merchant_connector_account_type, customer.as_ref(), connector_customer_id.clone(), ) @@ -219,7 +220,7 @@ where pub async fn generate_vault_session_details( state: &SessionState, merchant_context: &domain::MerchantContext, - merchant_connector_account_type: &payment_helpers::MerchantConnectorAccountType, + merchant_connector_account_type: &domain::MerchantConnectorAccountTypeDetails, connector_customer_id: Option, ) -> RouterResult> { let connector_name = merchant_connector_account_type @@ -230,7 +231,6 @@ pub async fn generate_vault_session_details( .change_context(errors::ApiErrorResponse::InternalServerError)?; let connector_auth_type: router_types::ConnectorAuthType = merchant_connector_account_type .get_connector_account_details() - .parse_value("ConnectorAuthType") .map_err(|err| { err.change_context(errors::ApiErrorResponse::InternalServerError) .attach_printable("Failed to parse connector auth type") @@ -285,7 +285,7 @@ pub async fn generate_vault_session_details( async fn generate_hyperswitch_vault_session_details( state: &SessionState, merchant_context: &domain::MerchantContext, - merchant_connector_account_type: &payment_helpers::MerchantConnectorAccountType, + merchant_connector_account_type: &domain::MerchantConnectorAccountTypeDetails, connector_customer_id: Option, connector_name: String, vault_publishable_key: masking::Secret, @@ -328,7 +328,7 @@ async fn call_external_vault_create( state: &SessionState, merchant_context: &domain::MerchantContext, connector_name: String, - merchant_connector_account_type: &payment_helpers::MerchantConnectorAccountType, + merchant_connector_account_type: &domain::MerchantConnectorAccountTypeDetails, connector_customer_id: Option, ) -> RouterResult> where diff --git a/crates/router/src/core/payouts/helpers.rs b/crates/router/src/core/payouts/helpers.rs index 743b5330fa..1003ee699f 100644 --- a/crates/router/src/core/payouts/helpers.rs +++ b/crates/router/src/core/payouts/helpers.rs @@ -1030,7 +1030,7 @@ pub fn should_call_payout_connector_create_customer<'a>( state: &'a SessionState, connector: &'a api::ConnectorData, customer: &'a Option, - merchant_connector_id: &'a id_type::MerchantConnectorAccountId, + merchant_connector_id: &'a domain::MerchantConnectorAccountTypeDetails, ) -> (bool, Option<&'a str>) { // Check if create customer is required for the connector match enums::PayoutConnectors::try_from(connector.connector_name) { diff --git a/crates/router/src/core/revenue_recovery/api.rs b/crates/router/src/core/revenue_recovery/api.rs index b58d716683..3627d5062c 100644 --- a/crates/router/src/core/revenue_recovery/api.rs +++ b/crates/router/src/core/revenue_recovery/api.rs @@ -32,6 +32,7 @@ pub async fn call_psync_api( param: None, expand_attempts: true, all_keys_required: None, + merchant_connector_details: None, }; let merchant_context_from_revenue_recovery_data = MerchantContext::NormalMerchant(Box::new(Context( diff --git a/crates/router/src/core/utils.rs b/crates/router/src/core/utils.rs index d2713e0f18..b9a27caf0f 100644 --- a/crates/router/src/core/utils.rs +++ b/crates/router/src/core/utils.rs @@ -2038,7 +2038,7 @@ pub(crate) fn validate_profile_id_from_auth_layer( state: &SessionState, merchant_account: &domain::MerchantAccount, - merchant_connector_account: &payment_helpers::MerchantConnectorAccountType, + merchant_connector_account: &domain::MerchantConnectorAccountTypeDetails, payment_method_vaulting_data: Option, connector_vault_id: Option, connector_customer_id: Option, @@ -2047,9 +2047,8 @@ pub async fn construct_vault_router_data( .get_connector_name() .ok_or(errors::ApiErrorResponse::InternalServerError) .attach_printable("Connector name not present for external vault")?; // always get the connector name from the merchant_connector_account - let connector_auth_type: types::ConnectorAuthType = merchant_connector_account + let connector_auth_type = merchant_connector_account .get_connector_account_details() - .parse_value("ConnectorAuthType") .change_context(errors::ApiErrorResponse::InternalServerError)?; let resource_common_data = VaultConnectorFlowData { diff --git a/crates/router/src/core/webhooks/incoming_v2.rs b/crates/router/src/core/webhooks/incoming_v2.rs index a7b4302436..c6ff1e73ab 100644 --- a/crates/router/src/core/webhooks/incoming_v2.rs +++ b/crates/router/src/core/webhooks/incoming_v2.rs @@ -474,6 +474,7 @@ async fn payments_incoming_webhook_flow( expand_attempts: false, param: None, all_keys_required: None, + merchant_connector_details: None, }, get_trackers_response, consume_or_trigger_flow, @@ -678,6 +679,7 @@ where attempts: None, should_sync_with_connector: true, payment_address, + merchant_connector_details: None, }, }) } diff --git a/crates/router/src/core/webhooks/recovery_incoming.rs b/crates/router/src/core/webhooks/recovery_incoming.rs index 18a51c3e9b..cc4e08861a 100644 --- a/crates/router/src/core/webhooks/recovery_incoming.rs +++ b/crates/router/src/core/webhooks/recovery_incoming.rs @@ -487,6 +487,7 @@ impl RevenueRecoveryAttempt { expand_attempts: true, param: None, all_keys_required: None, + merchant_connector_details: None, }, payment_intent.payment_id.clone(), payments::CallConnectorAction::Avoid, diff --git a/crates/router/src/routes/app.rs b/crates/router/src/routes/app.rs index 1380379a2a..e271cb5d62 100644 --- a/crates/router/src/routes/app.rs +++ b/crates/router/src/routes/app.rs @@ -655,7 +655,11 @@ impl Payments { web::resource("/create-external-sdk-tokens") .route(web::post().to(payments::payments_connector_session)), ) - .service(web::resource("").route(web::get().to(payments::payment_status))) + .service( + web::resource("") + .route(web::get().to(payments::payment_status)) + .route(web::post().to(payments::payments_status_with_gateway_creds)), + ) .service( web::resource("/start-redirection") .route(web::get().to(payments::payments_start_redirection)), diff --git a/crates/router/src/routes/payments.rs b/crates/router/src/routes/payments.rs index 90105032cc..ae2fcf2cf1 100644 --- a/crates/router/src/routes/payments.rs +++ b/crates/router/src/routes/payments.rs @@ -2868,7 +2868,7 @@ pub async fn proxy_confirm_intent( pub async fn payment_status( state: web::Data, req: actix_web::HttpRequest, - payload: web::Query, + payload: web::Query, path: web::Path, ) -> impl Responder { use hyperswitch_domain_models::payments::PaymentStatusData; @@ -2881,9 +2881,17 @@ pub async fn payment_status( let global_payment_id = path.into_inner(); tracing::Span::current().record("payment_id", global_payment_id.get_string_repr()); + let payload = payment_types::PaymentsRetrieveRequest { + force_sync: payload.force_sync, + expand_attempts: payload.expand_attempts, + param: payload.param.clone(), + all_keys_required: payload.all_keys_required, + merchant_connector_details: None, + }; + let internal_payload = internal_payload_types::PaymentsGenericRequestWithResourceId { global_payment_id, - payload: payload.into_inner(), + payload, }; let header_payload = match HeaderPayload::foreign_try_from(req.headers()) { @@ -2944,6 +2952,100 @@ pub async fn payment_status( .await } +#[cfg(feature = "v2")] +#[instrument(skip(state, req), fields(flow, payment_id))] +pub async fn payments_status_with_gateway_creds( + state: web::Data, + req: actix_web::HttpRequest, + payload: web::Json, + path: web::Path, +) -> impl Responder { + use hyperswitch_domain_models::payments::PaymentStatusData; + + let flow = match payload.force_sync { + true => Flow::PaymentsRetrieveForceSync, + false => Flow::PaymentsRetrieve, + }; + tracing::Span::current().record("flow", flow.to_string()); + + let global_payment_id = path.into_inner(); + tracing::Span::current().record("payment_id", global_payment_id.get_string_repr()); + + let internal_payload = internal_payload_types::PaymentsGenericRequestWithResourceId { + global_payment_id, + payload: payload.into_inner(), + }; + + let header_payload = match HeaderPayload::foreign_try_from(req.headers()) { + Ok(headers) => headers, + Err(err) => { + return api::log_and_return_error_response(err); + } + }; + + let locking_action = internal_payload.get_locking_input(flow.clone()); + + let auth_type = if state.conf.merchant_id_auth.merchant_id_auth_enabled { + &auth::MerchantIdAuth + } else { + match env::which() { + env::Env::Production => &auth::V2ApiKeyAuth { + is_connected_allowed: false, + is_platform_allowed: false, + }, + _ => auth::auth_type( + &auth::V2ApiKeyAuth { + is_connected_allowed: false, + is_platform_allowed: false, + }, + &auth::JWTAuth { + permission: Permission::ProfilePaymentWrite, + }, + req.headers(), + ), + } + }; + + Box::pin(api::server_wrap( + flow, + state, + &req, + internal_payload, + |state, auth: auth::AuthenticationData, req, req_state| async { + let payment_id = req.global_payment_id; + let request = req.payload; + + let operation = payments::operations::PaymentGet; + + let merchant_context = domain::MerchantContext::NormalMerchant(Box::new( + domain::Context(auth.merchant_account, auth.key_store), + )); + Box::pin(payments::payments_core::< + api_types::PSync, + api_models::payments::PaymentsResponse, + _, + _, + _, + PaymentStatusData, + >( + state, + req_state, + merchant_context, + auth.profile, + operation, + request, + payment_id, + payments::CallConnectorAction::Trigger, + header_payload.clone(), + )) + .await + }, + auth_type, + locking_action, + )) + .await +} + #[cfg(feature = "v2")] #[instrument(skip(state, req), fields(flow, payment_id))] pub async fn payment_get_intent_using_merchant_reference_id(