diff --git a/crates/api_models/src/payments.rs b/crates/api_models/src/payments.rs index 2e637e13c0..559682a43d 100644 --- a/crates/api_models/src/payments.rs +++ b/crates/api_models/src/payments.rs @@ -124,7 +124,7 @@ pub struct PaymentsRequest { pub currency: Option, /// This is the instruction for capture/ debit the money from the users' card. On the other hand authorization refers to blocking the amount on the users' payment method. - #[schema(value_type = Option, example = "PaymentProcessor")] + #[schema(value_type = Option, example = "automatic")] pub capture_method: Option, /// The Amount to be captured/ debited from the users payment method. It shall be in lowest denomination of the currency. (i.e) in cents for USD denomination, in paisa for INR denomination etc., @@ -300,6 +300,11 @@ pub struct PaymentsRequest { pub profile_id: Option, } +#[derive(Default, Debug, Clone, Copy)] +pub struct HeaderPayload { + pub payment_confirm_source: Option, +} + #[derive( Default, Debug, serde::Serialize, Clone, PartialEq, ToSchema, router_derive::PolymorphicSchema, )] @@ -1775,7 +1780,7 @@ pub struct PaymentsResponse { pub capture_on: Option, /// This is the instruction for capture/ debit the money from the users' card. On the other hand authorization refers to blocking the amount on the users' payment method. - #[schema(value_type = Option, example = "PaymentProcessor")] + #[schema(value_type = Option, example = "automatic")] pub capture_method: Option, /// The payment method that is to be used diff --git a/crates/common_enums/src/enums.rs b/crates/common_enums/src/enums.rs index 13ee351fe6..d1e3a7c6b2 100644 --- a/crates/common_enums/src/enums.rs +++ b/crates/common_enums/src/enums.rs @@ -1675,6 +1675,31 @@ pub enum PayoutEntityType { Personal, } +#[derive( + Clone, + Copy, + Debug, + Default, + Eq, + PartialEq, + serde::Deserialize, + serde::Serialize, + strum::Display, + strum::EnumString, + ToSchema, + Hash, +)] +#[router_derive::diesel_enum(storage_type = "pg_enum")] +#[serde(rename_all = "snake_case")] +#[strum(serialize_all = "snake_case")] +pub enum PaymentSource { + #[default] + MerchantServer, + Postman, + Dashboard, + Sdk, +} + #[derive( Clone, Copy, diff --git a/crates/data_models/src/payments/payment_intent.rs b/crates/data_models/src/payments/payment_intent.rs index 345ac13143..a34cb8bd0f 100644 --- a/crates/data_models/src/payments/payment_intent.rs +++ b/crates/data_models/src/payments/payment_intent.rs @@ -104,6 +104,7 @@ pub struct PaymentIntent { // Denotes the action(approve or reject) taken by merchant in case of manual review. // Manual review can occur when the transaction is marked as risky by the frm_processor, payment processor or when there is underpayment/over payment incase of crypto payment pub merchant_decision: Option, + pub payment_confirm_source: Option, } #[derive(Clone, Debug, Default, Eq, PartialEq, Serialize, Deserialize)] @@ -142,6 +143,7 @@ pub struct PaymentIntentNew { pub attempt_count: i16, pub profile_id: Option, pub merchant_decision: Option, + pub payment_confirm_source: Option, } #[derive(Debug, Clone, Serialize, Deserialize)] @@ -185,6 +187,7 @@ pub enum PaymentIntentUpdate { statement_descriptor_suffix: Option, order_details: Option>, metadata: Option, + payment_confirm_source: Option, }, PaymentAttemptAndAttemptCountUpdate { active_attempt_id: String, @@ -229,6 +232,7 @@ pub struct PaymentIntentUpdateInternal { // Denotes the action(approve or reject) taken by merchant in case of manual review. // Manual review can occur when the transaction is marked as risky by the frm_processor, payment processor or when there is underpayment/over payment incase of crypto payment pub merchant_decision: Option, + pub payment_confirm_source: Option, } impl PaymentIntentUpdate { @@ -278,6 +282,7 @@ impl From for PaymentIntentUpdateInternal { statement_descriptor_suffix, order_details, metadata, + payment_confirm_source, } => Self { amount: Some(amount), currency: Some(currency), @@ -295,6 +300,7 @@ impl From for PaymentIntentUpdateInternal { statement_descriptor_suffix, order_details, metadata, + payment_confirm_source, ..Default::default() }, PaymentIntentUpdate::MetadataUpdate { metadata } => Self { diff --git a/crates/diesel_models/src/enums.rs b/crates/diesel_models/src/enums.rs index 69c5710e42..7a1c76ba41 100644 --- a/crates/diesel_models/src/enums.rs +++ b/crates/diesel_models/src/enums.rs @@ -10,9 +10,10 @@ pub mod diesel_exports { DbFutureUsage as FutureUsage, DbIntentStatus as IntentStatus, DbMandateStatus as MandateStatus, DbMandateType as MandateType, DbMerchantStorageScheme as MerchantStorageScheme, - DbPaymentMethodIssuerCode as PaymentMethodIssuerCode, DbPayoutStatus as PayoutStatus, - DbPayoutType as PayoutType, DbProcessTrackerStatus as ProcessTrackerStatus, - DbReconStatus as ReconStatus, DbRefundStatus as RefundStatus, DbRefundType as RefundType, + DbPaymentMethodIssuerCode as PaymentMethodIssuerCode, DbPaymentSource as PaymentSource, + DbPayoutStatus as PayoutStatus, DbPayoutType as PayoutType, + DbProcessTrackerStatus as ProcessTrackerStatus, DbReconStatus as ReconStatus, + DbRefundStatus as RefundStatus, DbRefundType as RefundType, }; } pub use common_enums::*; diff --git a/crates/diesel_models/src/payment_intent.rs b/crates/diesel_models/src/payment_intent.rs index 123d734d20..6468a8b1df 100644 --- a/crates/diesel_models/src/payment_intent.rs +++ b/crates/diesel_models/src/payment_intent.rs @@ -46,6 +46,7 @@ pub struct PaymentIntent { // Denotes the action(approve or reject) taken by merchant in case of manual review. // Manual review can occur when the transaction is marked as risky by the frm_processor, payment processor or when there is underpayment/over payment incase of crypto payment pub merchant_decision: Option, + pub payment_confirm_source: Option, } #[derive( @@ -96,6 +97,7 @@ pub struct PaymentIntentNew { pub attempt_count: i16, pub profile_id: Option, pub merchant_decision: Option, + pub payment_confirm_source: Option, } #[derive(Debug, Clone, Serialize, Deserialize)] @@ -139,6 +141,7 @@ pub enum PaymentIntentUpdate { statement_descriptor_suffix: Option, order_details: Option>, metadata: Option, + payment_confirm_source: Option, }, PaymentAttemptAndAttemptCountUpdate { active_attempt_id: String, @@ -184,6 +187,7 @@ pub struct PaymentIntentUpdateInternal { pub order_details: Option>, pub attempt_count: Option, merchant_decision: Option, + payment_confirm_source: Option, } impl PaymentIntentUpdate { @@ -233,6 +237,7 @@ impl From for PaymentIntentUpdateInternal { statement_descriptor_suffix, order_details, metadata, + payment_confirm_source, } => Self { amount: Some(amount), currency: Some(currency), @@ -250,6 +255,7 @@ impl From for PaymentIntentUpdateInternal { statement_descriptor_suffix, order_details, metadata, + payment_confirm_source, ..Default::default() }, PaymentIntentUpdate::MetadataUpdate { metadata } => Self { diff --git a/crates/diesel_models/src/schema.rs b/crates/diesel_models/src/schema.rs index 66bd11dccb..fe22efd744 100644 --- a/crates/diesel_models/src/schema.rs +++ b/crates/diesel_models/src/schema.rs @@ -597,6 +597,7 @@ diesel::table! { profile_id -> Nullable, #[max_length = 64] merchant_decision -> Nullable, + payment_confirm_source -> Nullable, } } diff --git a/crates/router/src/compatibility/stripe/payment_intents.rs b/crates/router/src/compatibility/stripe/payment_intents.rs index 0efb44d9d7..cf91f07a90 100644 --- a/crates/router/src/compatibility/stripe/payment_intents.rs +++ b/crates/router/src/compatibility/stripe/payment_intents.rs @@ -58,6 +58,7 @@ pub async fn payment_intents_create( req, api::AuthFlow::Merchant, payments::CallConnectorAction::Trigger, + api_types::HeaderPayload::default(), ) }, &auth::ApiKeyAuth, @@ -115,6 +116,7 @@ pub async fn payment_intents_retrieve( payload, auth_flow, payments::CallConnectorAction::Trigger, + api_types::HeaderPayload::default(), ) }, &*auth_type, @@ -176,6 +178,7 @@ pub async fn payment_intents_retrieve_with_gateway_creds( req, api::AuthFlow::Merchant, payments::CallConnectorAction::Trigger, + api_types::HeaderPayload::default(), ) }, &*auth_type, @@ -238,6 +241,7 @@ pub async fn payment_intents_update( req, auth_flow, payments::CallConnectorAction::Trigger, + api_types::HeaderPayload::default(), ) }, &*auth_type, @@ -302,6 +306,7 @@ pub async fn payment_intents_confirm( req, auth_flow, payments::CallConnectorAction::Trigger, + api_types::HeaderPayload::default(), ) }, &*auth_type, @@ -356,6 +361,7 @@ pub async fn payment_intents_capture( payload, api::AuthFlow::Merchant, payments::CallConnectorAction::Trigger, + api_types::HeaderPayload::default(), ) }, &auth::ApiKeyAuth, @@ -414,6 +420,7 @@ pub async fn payment_intents_cancel( req, auth_flow, payments::CallConnectorAction::Trigger, + api_types::HeaderPayload::default(), ) }, &*auth_type, diff --git a/crates/router/src/compatibility/stripe/setup_intents.rs b/crates/router/src/compatibility/stripe/setup_intents.rs index a5d24c1581..3de871274e 100644 --- a/crates/router/src/compatibility/stripe/setup_intents.rs +++ b/crates/router/src/compatibility/stripe/setup_intents.rs @@ -62,6 +62,7 @@ pub async fn setup_intents_create( req, api::AuthFlow::Merchant, payments::CallConnectorAction::Trigger, + api_types::HeaderPayload::default(), ) }, &auth::ApiKeyAuth, @@ -119,6 +120,7 @@ pub async fn setup_intents_retrieve( payload, auth_flow, payments::CallConnectorAction::Trigger, + api_types::HeaderPayload::default(), ) }, &*auth_type, @@ -182,6 +184,7 @@ pub async fn setup_intents_update( req, auth_flow, payments::CallConnectorAction::Trigger, + api_types::HeaderPayload::default(), ) }, &*auth_type, @@ -246,6 +249,7 @@ pub async fn setup_intents_confirm( req, auth_flow, payments::CallConnectorAction::Trigger, + api_types::HeaderPayload::default(), ) }, &*auth_type, diff --git a/crates/router/src/core/payments.rs b/crates/router/src/core/payments.rs index 133df44701..1dcfd09b1d 100644 --- a/crates/router/src/core/payments.rs +++ b/crates/router/src/core/payments.rs @@ -9,6 +9,7 @@ pub mod types; use std::{fmt::Debug, marker::PhantomData, ops::Deref, time::Instant}; +use api_models::payments::HeaderPayload; use common_utils::{ext_traits::AsyncExt, pii}; use diesel_models::{ephemeral_key, fraud_check::FraudCheck}; use error_stack::{IntoReport, ResultExt}; @@ -45,6 +46,7 @@ use crate::{ workflows::payment_sync, }; +#[allow(clippy::too_many_arguments)] #[instrument(skip_all, fields(payment_id, merchant_id))] pub async fn payments_operation_core( state: &AppState, @@ -54,6 +56,7 @@ pub async fn payments_operation_core( req: Req, call_connector_action: CallConnectorAction, auth_flow: services::AuthFlow, + header_payload: HeaderPayload, ) -> RouterResult<(PaymentData, Req, Option, Option)> where F: Send + Clone + Sync, @@ -169,6 +172,7 @@ where updated_customer, &validate_result, schedule_time, + header_payload, ) .await?; @@ -227,6 +231,7 @@ where updated_customer, &key_store, None, + header_payload, ) .await?; } @@ -243,6 +248,7 @@ pub async fn payments_core( req: Req, auth_flow: services::AuthFlow, call_connector_action: CallConnectorAction, + header_payload: HeaderPayload, ) -> RouterResponse where F: Send + Clone + Sync, @@ -269,6 +275,7 @@ where req, call_connector_action, auth_flow, + header_payload, ) .await?; @@ -424,6 +431,7 @@ impl PaymentRedirectFlow for PaymentRedirectCompleteAuthorize { payment_confirm_req, services::api::AuthFlow::Merchant, connector_action, + HeaderPayload::default(), ) .await } @@ -518,6 +526,7 @@ impl PaymentRedirectFlow for PaymentRedirectSync { payment_sync_req, services::api::AuthFlow::Merchant, connector_action, + HeaderPayload::default(), ) .await } @@ -555,6 +564,7 @@ pub async fn call_connector_service( updated_customer: Option, validate_result: &operations::ValidateResult<'_>, schedule_time: Option, + header_payload: HeaderPayload, ) -> RouterResult> where F: Send + Clone + Sync, @@ -715,6 +725,7 @@ where updated_customer, key_store, None, + header_payload, ) .await?; diff --git a/crates/router/src/core/payments/helpers.rs b/crates/router/src/core/payments/helpers.rs index 83f33fb3e8..055e7f8f61 100644 --- a/crates/router/src/core/payments/helpers.rs +++ b/crates/router/src/core/payments/helpers.rs @@ -2245,6 +2245,7 @@ mod tests { attempt_count: 1, profile_id: None, merchant_decision: None, + payment_confirm_source: None, }; let req_cs = Some("1".to_string()); let merchant_fulfillment_time = Some(900); @@ -2291,6 +2292,7 @@ mod tests { attempt_count: 1, profile_id: None, merchant_decision: None, + payment_confirm_source: None, }; let req_cs = Some("1".to_string()); let merchant_fulfillment_time = Some(10); @@ -2337,6 +2339,7 @@ mod tests { attempt_count: 1, profile_id: None, merchant_decision: None, + payment_confirm_source: None, }; let req_cs = Some("1".to_string()); let merchant_fulfillment_time = Some(10); diff --git a/crates/router/src/core/payments/operations.rs b/crates/router/src/core/payments/operations.rs index 71fb6154ae..b0c7a5ae12 100644 --- a/crates/router/src/core/payments/operations.rs +++ b/crates/router/src/core/payments/operations.rs @@ -154,6 +154,7 @@ pub trait UpdateTracker: Send { updated_customer: Option, mechant_key_store: &domain::MerchantKeyStore, frm_suggestion: Option, + header_payload: api::HeaderPayload, ) -> RouterResult<(BoxedOperation<'b, F, Req>, D)> where F: 'b + Send; diff --git a/crates/router/src/core/payments/operations/payment_approve.rs b/crates/router/src/core/payments/operations/payment_approve.rs index 22fdfba9fa..804ef0f634 100644 --- a/crates/router/src/core/payments/operations/payment_approve.rs +++ b/crates/router/src/core/payments/operations/payment_approve.rs @@ -342,6 +342,7 @@ impl UpdateTracker, api::PaymentsRequest> for Paymen _updated_customer: Option, _merchant_key_store: &domain::MerchantKeyStore, _frm_suggestion: Option, + _header_payload: api::HeaderPayload, ) -> RouterResult<(BoxedOperation<'b, F, api::PaymentsRequest>, PaymentData)> where F: 'b + Send, diff --git a/crates/router/src/core/payments/operations/payment_cancel.rs b/crates/router/src/core/payments/operations/payment_cancel.rs index b219021c57..a657034c42 100644 --- a/crates/router/src/core/payments/operations/payment_cancel.rs +++ b/crates/router/src/core/payments/operations/payment_cancel.rs @@ -183,6 +183,7 @@ impl UpdateTracker, api::PaymentsCancelRequest> for _updated_customer: Option, _mechant_key_store: &domain::MerchantKeyStore, _frm_suggestion: Option, + _header_payload: api::HeaderPayload, ) -> RouterResult<( BoxedOperation<'b, F, api::PaymentsCancelRequest>, PaymentData, diff --git a/crates/router/src/core/payments/operations/payment_capture.rs b/crates/router/src/core/payments/operations/payment_capture.rs index 0ebc0668ee..3848199fba 100644 --- a/crates/router/src/core/payments/operations/payment_capture.rs +++ b/crates/router/src/core/payments/operations/payment_capture.rs @@ -253,6 +253,7 @@ impl UpdateTracker, api::PaymentsCaptureRe _updated_customer: Option, _mechant_key_store: &domain::MerchantKeyStore, _frm_suggestion: Option, + _header_payload: api::HeaderPayload, ) -> RouterResult<( BoxedOperation<'b, F, api::PaymentsCaptureRequest>, payments::PaymentData, diff --git a/crates/router/src/core/payments/operations/payment_complete_authorize.rs b/crates/router/src/core/payments/operations/payment_complete_authorize.rs index 045c286dba..5ad04c5df3 100644 --- a/crates/router/src/core/payments/operations/payment_complete_authorize.rs +++ b/crates/router/src/core/payments/operations/payment_complete_authorize.rs @@ -333,6 +333,7 @@ impl UpdateTracker, api::PaymentsRequest> for Comple _updated_customer: Option, _merchant_key_store: &domain::MerchantKeyStore, _frm_suggestion: Option, + _header_payload: api::HeaderPayload, ) -> RouterResult<(BoxedOperation<'b, F, api::PaymentsRequest>, PaymentData)> where F: 'b + Send, diff --git a/crates/router/src/core/payments/operations/payment_confirm.rs b/crates/router/src/core/payments/operations/payment_confirm.rs index ef67315201..796ce87488 100644 --- a/crates/router/src/core/payments/operations/payment_confirm.rs +++ b/crates/router/src/core/payments/operations/payment_confirm.rs @@ -447,6 +447,7 @@ impl UpdateTracker, api::PaymentsRequest> for Paymen updated_customer: Option, key_store: &domain::MerchantKeyStore, frm_suggestion: Option, + header_payload: api::HeaderPayload, ) -> RouterResult<(BoxedOperation<'b, F, api::PaymentsRequest>, PaymentData)> where F: 'b + Send, @@ -565,6 +566,7 @@ impl UpdateTracker, api::PaymentsRequest> for Paymen statement_descriptor_suffix, order_details, metadata, + payment_confirm_source: header_payload.payment_confirm_source, }, 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 6e33ba88ff..ba4a6eb5b3 100644 --- a/crates/router/src/core/payments/operations/payment_create.rs +++ b/crates/router/src/core/payments/operations/payment_create.rs @@ -366,6 +366,7 @@ impl UpdateTracker, api::PaymentsRequest> for Paymen _updated_customer: Option, _merchant_key_store: &domain::MerchantKeyStore, _frm_suggestion: Option, + _header_payload: api::HeaderPayload, ) -> RouterResult<(BoxedOperation<'b, F, api::PaymentsRequest>, PaymentData)> where F: 'b + Send, @@ -676,6 +677,7 @@ impl PaymentCreate { attempt_count: 1, profile_id, merchant_decision: None, + payment_confirm_source: None, }) } diff --git a/crates/router/src/core/payments/operations/payment_method_validate.rs b/crates/router/src/core/payments/operations/payment_method_validate.rs index 2f845a1341..8641d3b1f8 100644 --- a/crates/router/src/core/payments/operations/payment_method_validate.rs +++ b/crates/router/src/core/payments/operations/payment_method_validate.rs @@ -215,6 +215,7 @@ impl UpdateTracker, api::VerifyRequest> for PaymentM _updated_customer: Option, _mechant_key_store: &domain::MerchantKeyStore, _frm_suggestion: Option, + _header_payload: api::HeaderPayload, ) -> RouterResult<(BoxedOperation<'b, F, api::VerifyRequest>, PaymentData)> where F: 'b + Send, diff --git a/crates/router/src/core/payments/operations/payment_reject.rs b/crates/router/src/core/payments/operations/payment_reject.rs index e156f66d14..18a4df4fd6 100644 --- a/crates/router/src/core/payments/operations/payment_reject.rs +++ b/crates/router/src/core/payments/operations/payment_reject.rs @@ -169,6 +169,7 @@ impl UpdateTracker, PaymentsRejectRequest> for Payme _updated_customer: Option, _mechant_key_store: &domain::MerchantKeyStore, _should_decline_transaction: Option, + _header_payload: api::HeaderPayload, ) -> RouterResult<(BoxedOperation<'b, F, PaymentsRejectRequest>, PaymentData)> where F: 'b + Send, diff --git a/crates/router/src/core/payments/operations/payment_session.rs b/crates/router/src/core/payments/operations/payment_session.rs index 604f442b62..e432e0cdb6 100644 --- a/crates/router/src/core/payments/operations/payment_session.rs +++ b/crates/router/src/core/payments/operations/payment_session.rs @@ -205,6 +205,7 @@ impl UpdateTracker, api::PaymentsSessionRequest> for _updated_customer: Option, _mechant_key_store: &domain::MerchantKeyStore, _frm_suggestion: Option, + _header_payload: api::HeaderPayload, ) -> RouterResult<( BoxedOperation<'b, F, api::PaymentsSessionRequest>, PaymentData, diff --git a/crates/router/src/core/payments/operations/payment_start.rs b/crates/router/src/core/payments/operations/payment_start.rs index 8699257019..a93b9a4c4d 100644 --- a/crates/router/src/core/payments/operations/payment_start.rs +++ b/crates/router/src/core/payments/operations/payment_start.rs @@ -178,6 +178,7 @@ impl UpdateTracker, api::PaymentsStartRequest> for P _updated_customer: Option, _mechant_key_store: &domain::MerchantKeyStore, _frm_suggestion: Option, + _header_payload: api::HeaderPayload, ) -> RouterResult<( BoxedOperation<'b, F, api::PaymentsStartRequest>, PaymentData, diff --git a/crates/router/src/core/payments/operations/payment_status.rs b/crates/router/src/core/payments/operations/payment_status.rs index 324e82f797..0f34839962 100644 --- a/crates/router/src/core/payments/operations/payment_status.rs +++ b/crates/router/src/core/payments/operations/payment_status.rs @@ -124,6 +124,7 @@ impl UpdateTracker, api::PaymentsRequest> for Paymen _updated_customer: Option, _key_store: &domain::MerchantKeyStore, _frm_suggestion: Option, + _header_payload: api::HeaderPayload, ) -> RouterResult<(BoxedOperation<'b, F, api::PaymentsRequest>, PaymentData)> where F: 'b + Send, @@ -143,6 +144,7 @@ impl UpdateTracker, api::PaymentsRetrieveRequest> fo _updated_customer: Option, _key_store: &domain::MerchantKeyStore, _frm_suggestion: Option, + _header_payload: api::HeaderPayload, ) -> RouterResult<( BoxedOperation<'b, F, api::PaymentsRetrieveRequest>, PaymentData, diff --git a/crates/router/src/core/payments/operations/payment_update.rs b/crates/router/src/core/payments/operations/payment_update.rs index ce3b3eeb44..ec0c33e29e 100644 --- a/crates/router/src/core/payments/operations/payment_update.rs +++ b/crates/router/src/core/payments/operations/payment_update.rs @@ -427,6 +427,7 @@ impl UpdateTracker, api::PaymentsRequest> for Paymen _updated_customer: Option, _key_store: &domain::MerchantKeyStore, _frm_suggestion: Option, + _header_payload: api::HeaderPayload, ) -> RouterResult<(BoxedOperation<'b, F, api::PaymentsRequest>, PaymentData)> where F: 'b + Send, @@ -542,6 +543,7 @@ impl UpdateTracker, api::PaymentsRequest> for Paymen statement_descriptor_suffix, order_details, metadata, + payment_confirm_source: None, }, storage_scheme, ) diff --git a/crates/router/src/core/payments/transformers.rs b/crates/router/src/core/payments/transformers.rs index cde3a3d401..2362c6a297 100644 --- a/crates/router/src/core/payments/transformers.rs +++ b/crates/router/src/core/payments/transformers.rs @@ -417,7 +417,7 @@ where let payment_method_data_response = additional_payment_method_data.map(api::PaymentMethodDataResponse::from); - let headers = connector_http_status_code + let mut headers = connector_http_status_code .map(|status_code| { vec![( "connector_http_status_code".to_string(), @@ -425,6 +425,12 @@ where )] }) .unwrap_or(vec![]); + if let Some(payment_confirm_source) = payment_intent.payment_confirm_source { + headers.push(( + "payment_confirm_source".to_string(), + payment_confirm_source.to_string(), + )) + } let output = Ok(match payment_request { Some(_request) => { diff --git a/crates/router/src/core/webhooks.rs b/crates/router/src/core/webhooks.rs index 1ca68b4094..bc1fa3f914 100644 --- a/crates/router/src/core/webhooks.rs +++ b/crates/router/src/core/webhooks.rs @@ -1,6 +1,7 @@ pub mod types; pub mod utils; +use api_models::payments::HeaderPayload; use common_utils::errors::ReportSwitchExt; use error_stack::{report, IntoReport, ResultExt}; use masking::ExposeInterface; @@ -62,6 +63,7 @@ pub async fn payments_incoming_webhook_flow( }, services::AuthFlow::Merchant, consume_or_trigger_flow, + HeaderPayload::default(), ) .await; @@ -438,6 +440,7 @@ async fn bank_transfer_webhook_flow( request, services::api::AuthFlow::Merchant, payments::CallConnectorAction::Trigger, + HeaderPayload::default(), ) .await } else { diff --git a/crates/router/src/routes/payments.rs b/crates/router/src/routes/payments.rs index d550cfe17a..c0508ca927 100644 --- a/crates/router/src/routes/payments.rs +++ b/crates/router/src/routes/payments.rs @@ -1,6 +1,7 @@ pub mod helpers; use actix_web::{web, Responder}; +use api_models::payments::HeaderPayload; use error_stack::report; use router_env::{instrument, tracing, Flow}; @@ -20,6 +21,7 @@ use crate::{ types::{ api::{self as api_types, enums as api_enums, payments as payment_types}, domain, + transformers::ForeignTryFrom, }, }; @@ -105,6 +107,7 @@ pub async fn payments_create( state, auth.merchant_account, auth.key_store, + payment_types::HeaderPayload::default(), req, api::AuthFlow::Merchant, ) @@ -158,6 +161,7 @@ pub async fn payments_start( req, api::AuthFlow::Client, payments::CallConnectorAction::Trigger, + HeaderPayload::default(), ) }, &auth::MerchantIdAuth(merchant_id), @@ -221,6 +225,7 @@ pub async fn payments_retrieve( req, auth_flow, payments::CallConnectorAction::Trigger, + HeaderPayload::default(), ) }, &*auth_type, @@ -278,6 +283,7 @@ pub async fn payments_retrieve_with_gateway_creds( req, api::AuthFlow::Merchant, payments::CallConnectorAction::Trigger, + HeaderPayload::default(), ) }, &*auth_type, @@ -338,6 +344,7 @@ pub async fn payments_update( state, auth.merchant_account, auth.key_store, + payment_types::HeaderPayload::default(), req, auth_flow, ) @@ -387,12 +394,18 @@ pub async fn payments_confirm( let payment_id = path.into_inner(); payload.payment_id = Some(payment_types::PaymentIdType::PaymentIntentId(payment_id)); payload.confirm = Some(true); + let header_payload = match payment_types::HeaderPayload::foreign_try_from(req.headers()) { + Ok(headers) => headers, + Err(err) => { + return api::log_and_return_error_response(err); + } + }; + let (auth_type, auth_flow) = match auth::check_client_secret_and_get_auth(req.headers(), &payload) { Ok(auth) => auth, Err(e) => return api::log_and_return_error_response(e), }; - api::server_wrap( flow, state.get_ref(), @@ -404,6 +417,7 @@ pub async fn payments_confirm( state, auth.merchant_account, auth.key_store, + header_payload, req, auth_flow, ) @@ -459,6 +473,7 @@ pub async fn payments_capture( payload, api::AuthFlow::Merchant, payments::CallConnectorAction::Trigger, + HeaderPayload::default(), ) }, &auth::ApiKeyAuth, @@ -510,6 +525,7 @@ pub async fn payments_connector_session( payload, api::AuthFlow::Client, payments::CallConnectorAction::Trigger, + HeaderPayload::default(), ) }, &auth::PublishableKeyAuth, @@ -710,6 +726,7 @@ pub async fn payments_cancel( req, api::AuthFlow::Merchant, payments::CallConnectorAction::Trigger, + HeaderPayload::default(), ) }, &auth::ApiKeyAuth, @@ -811,6 +828,7 @@ async fn authorize_verify_select( state: &app::AppState, merchant_account: domain::MerchantAccount, key_store: domain::MerchantKeyStore, + header_payload: HeaderPayload, req: api_models::payments::PaymentsRequest, auth_flow: api::AuthFlow, ) -> app::core::errors::RouterResponse @@ -842,6 +860,7 @@ where req, auth_flow, payments::CallConnectorAction::Trigger, + header_payload, ) .await, @@ -854,6 +873,7 @@ where req, auth_flow, payments::CallConnectorAction::Trigger, + header_payload, ) .await } diff --git a/crates/router/src/services/authentication.rs b/crates/router/src/services/authentication.rs index 49bfecb69f..922d560a4a 100644 --- a/crates/router/src/services/authentication.rs +++ b/crates/router/src/services/authentication.rs @@ -1,5 +1,5 @@ use actix_web::http::header::HeaderMap; -use api_models::{payment_methods::PaymentMethodListRequest, payments::PaymentsRequest}; +use api_models::{payment_methods::PaymentMethodListRequest, payments}; use async_trait::async_trait; use common_utils::date_time; use error_stack::{report, IntoReport, ResultExt}; @@ -354,7 +354,7 @@ pub trait ClientSecretFetch { fn get_client_secret(&self) -> Option<&String>; } -impl ClientSecretFetch for PaymentsRequest { +impl ClientSecretFetch for payments::PaymentsRequest { fn get_client_secret(&self) -> Option<&String> { self.client_secret.as_ref() } @@ -498,13 +498,23 @@ where } pub fn get_api_key(headers: &HeaderMap) -> RouterResult<&str> { + get_header_value_by_key("api-key".into(), headers)?.get_required_value("api_key") +} + +pub fn get_header_value_by_key(key: String, headers: &HeaderMap) -> RouterResult> { headers - .get("api-key") - .get_required_value("api-key")? - .to_str() - .into_report() - .change_context(errors::ApiErrorResponse::InternalServerError) - .attach_printable("Failed to convert API key to string") + .get(&key) + .map(|source_str| { + source_str + .to_str() + .into_report() + .change_context(errors::ApiErrorResponse::InternalServerError) + .attach_printable(format!( + "Failed to convert header value to string for header key: {}", + key + )) + }) + .transpose() } pub fn get_jwt(headers: &HeaderMap) -> RouterResult<&str> { diff --git a/crates/router/src/types/api/payments.rs b/crates/router/src/types/api/payments.rs index 4cf0663858..f266e67af1 100644 --- a/crates/router/src/types/api/payments.rs +++ b/crates/router/src/types/api/payments.rs @@ -1,10 +1,10 @@ pub use api_models::payments::{ AcceptanceType, Address, AddressDetails, Amount, AuthenticationForStartResponse, Card, - CryptoData, CustomerAcceptance, MandateData, MandateTransactionType, MandateType, - MandateValidationFields, NextActionType, OnlineMandate, PayLaterData, PaymentIdType, - PaymentListConstraints, PaymentListFilterConstraints, PaymentListFilters, PaymentListResponse, - PaymentListResponseV2, PaymentMethodData, PaymentMethodDataResponse, PaymentOp, - PaymentRetrieveBody, PaymentRetrieveBodyWithCredentials, PaymentsApproveRequest, + CryptoData, CustomerAcceptance, HeaderPayload, MandateData, MandateTransactionType, + MandateType, MandateValidationFields, NextActionType, OnlineMandate, PayLaterData, + PaymentIdType, PaymentListConstraints, PaymentListFilterConstraints, PaymentListFilters, + PaymentListResponse, PaymentListResponseV2, PaymentMethodData, PaymentMethodDataResponse, + PaymentOp, PaymentRetrieveBody, PaymentRetrieveBodyWithCredentials, PaymentsApproveRequest, PaymentsCancelRequest, PaymentsCaptureRequest, PaymentsRedirectRequest, PaymentsRedirectionResponse, PaymentsRejectRequest, PaymentsRequest, PaymentsResponse, PaymentsResponseForm, PaymentsRetrieveRequest, PaymentsSessionRequest, PaymentsSessionResponse, diff --git a/crates/router/src/types/transformers.rs b/crates/router/src/types/transformers.rs index b04241f599..c2a10abd67 100644 --- a/crates/router/src/types/transformers.rs +++ b/crates/router/src/types/transformers.rs @@ -1,5 +1,11 @@ +// use actix_web::HttpMessage; +use actix_web::http::header::HeaderMap; use api_models::enums as api_enums; -use common_utils::{crypto::Encryptable, ext_traits::ValueExt, pii}; +use common_utils::{ + crypto::Encryptable, + ext_traits::{StringExt, ValueExt}, + pii, +}; use diesel_models::enums as storage_enums; use error_stack::ResultExt; use masking::{ExposeInterface, PeekInterface}; @@ -7,6 +13,7 @@ use masking::{ExposeInterface, PeekInterface}; use super::domain; use crate::{ core::errors, + services::authentication::get_header_value_by_key, types::{api as api_types, storage}, }; @@ -684,6 +691,30 @@ impl ForeignFrom for api_enums::PaymentMethod { } } +impl ForeignTryFrom<&HeaderMap> for api_models::payments::HeaderPayload { + type Error = error_stack::Report; + fn foreign_try_from(headers: &HeaderMap) -> Result { + let payment_confirm_source: Option = + get_header_value_by_key("payment_confirm_source".into(), headers)? + .map(|source| { + source + .to_owned() + .parse_enum("PaymentSource") + .change_context(errors::ApiErrorResponse::InvalidRequestData { + message: "Invalid data received in payment_confirm_source header" + .into(), + }) + .attach_printable( + "Failed while paring PaymentConfirmSource header value to enum", + ) + }) + .transpose()?; + Ok(Self { + payment_confirm_source, + }) + } +} + impl ForeignFrom<( Option<&storage::PaymentAttempt>, diff --git a/crates/router/src/workflows/payment_sync.rs b/crates/router/src/workflows/payment_sync.rs index dca2df35ba..4225f780b0 100644 --- a/crates/router/src/workflows/payment_sync.rs +++ b/crates/router/src/workflows/payment_sync.rs @@ -63,6 +63,7 @@ impl ProcessTrackerWorkflow for PaymentsSyncWorkflow { tracking_data.clone(), payment_flows::CallConnectorAction::Trigger, services::AuthFlow::Client, + api::HeaderPayload::default(), ) .await?; diff --git a/crates/router/tests/payments.rs b/crates/router/tests/payments.rs index 651949d9b2..f3e4ab1b15 100644 --- a/crates/router/tests/payments.rs +++ b/crates/router/tests/payments.rs @@ -369,6 +369,7 @@ async fn payments_create_core() { req, services::AuthFlow::Merchant, payments::CallConnectorAction::Trigger, + api::HeaderPayload::default(), ) .await .unwrap(); @@ -538,6 +539,7 @@ async fn payments_create_core_adyen_no_redirect() { req, services::AuthFlow::Merchant, payments::CallConnectorAction::Trigger, + api::HeaderPayload::default(), ) .await .unwrap(); diff --git a/crates/router/tests/payments2.rs b/crates/router/tests/payments2.rs index 2079a1cbe1..9d7af81022 100644 --- a/crates/router/tests/payments2.rs +++ b/crates/router/tests/payments2.rs @@ -129,6 +129,7 @@ async fn payments_create_core() { req, services::AuthFlow::Merchant, payments::CallConnectorAction::Trigger, + api::HeaderPayload::default(), ) .await .unwrap(); @@ -300,6 +301,7 @@ async fn payments_create_core_adyen_no_redirect() { req, services::AuthFlow::Merchant, payments::CallConnectorAction::Trigger, + api::HeaderPayload::default(), ) .await .unwrap(); diff --git a/crates/storage_impl/src/payments/payment_intent.rs b/crates/storage_impl/src/payments/payment_intent.rs index 5b10d3f0a5..8f20746ec0 100644 --- a/crates/storage_impl/src/payments/payment_intent.rs +++ b/crates/storage_impl/src/payments/payment_intent.rs @@ -88,6 +88,7 @@ impl PaymentIntentInterface for KVRouterStore { attempt_count: new.attempt_count, profile_id: new.profile_id.clone(), merchant_decision: new.merchant_decision.clone(), + payment_confirm_source: new.payment_confirm_source, }; match self @@ -751,6 +752,7 @@ impl PaymentIntentInterface for MockDb { attempt_count: new.attempt_count, profile_id: new.profile_id, merchant_decision: new.merchant_decision, + payment_confirm_source: new.payment_confirm_source, }; payment_intents.push(payment_intent.clone()); Ok(payment_intent) @@ -829,6 +831,7 @@ impl DataModelExt for PaymentIntentNew { attempt_count: self.attempt_count, profile_id: self.profile_id, merchant_decision: self.merchant_decision, + payment_confirm_source: self.payment_confirm_source, } } @@ -865,6 +868,7 @@ impl DataModelExt for PaymentIntentNew { attempt_count: storage_model.attempt_count, profile_id: storage_model.profile_id, merchant_decision: storage_model.merchant_decision, + payment_confirm_source: storage_model.payment_confirm_source, } } } @@ -906,6 +910,7 @@ impl DataModelExt for PaymentIntent { attempt_count: self.attempt_count, profile_id: self.profile_id, merchant_decision: self.merchant_decision, + payment_confirm_source: self.payment_confirm_source, } } @@ -943,6 +948,7 @@ impl DataModelExt for PaymentIntent { attempt_count: storage_model.attempt_count, profile_id: storage_model.profile_id, merchant_decision: storage_model.merchant_decision, + payment_confirm_source: storage_model.payment_confirm_source, } } } @@ -1003,6 +1009,7 @@ impl DataModelExt for PaymentIntentUpdate { statement_descriptor_suffix, order_details, metadata, + payment_confirm_source, } => DieselPaymentIntentUpdate::Update { amount, currency, @@ -1019,6 +1026,7 @@ impl DataModelExt for PaymentIntentUpdate { statement_descriptor_suffix, order_details, metadata, + payment_confirm_source, }, Self::PaymentAttemptAndAttemptCountUpdate { active_attempt_id, @@ -1102,6 +1110,7 @@ impl DataModelExt for PaymentIntentUpdate { statement_descriptor_suffix, order_details, metadata, + payment_confirm_source, } => Self::Update { amount, currency, @@ -1118,6 +1127,7 @@ impl DataModelExt for PaymentIntentUpdate { statement_descriptor_suffix, order_details, metadata, + payment_confirm_source, }, DieselPaymentIntentUpdate::PaymentAttemptAndAttemptCountUpdate { active_attempt_id, diff --git a/migrations/2023-09-08-134514_add_payment_confirm_source_in_payment_intent/down.sql b/migrations/2023-09-08-134514_add_payment_confirm_source_in_payment_intent/down.sql new file mode 100644 index 0000000000..98d6d1cdf0 --- /dev/null +++ b/migrations/2023-09-08-134514_add_payment_confirm_source_in_payment_intent/down.sql @@ -0,0 +1,5 @@ +-- This file should undo anything in `up.sql` +ALTER TABLE payment_intent +DROP COLUMN IF EXISTS payment_confirm_source; + +DROP TYPE "PaymentSource"; \ No newline at end of file diff --git a/migrations/2023-09-08-134514_add_payment_confirm_source_in_payment_intent/up.sql b/migrations/2023-09-08-134514_add_payment_confirm_source_in_payment_intent/up.sql new file mode 100644 index 0000000000..9c8a6f0fb1 --- /dev/null +++ b/migrations/2023-09-08-134514_add_payment_confirm_source_in_payment_intent/up.sql @@ -0,0 +1,10 @@ +-- Your SQL goes here +CREATE TYPE "PaymentSource" AS ENUM ( + 'merchant_server', + 'postman', + 'dashboard', + 'sdk' +); + +ALTER TABLE payment_intent +ADD COLUMN IF NOT EXISTS payment_confirm_source "PaymentSource"; \ No newline at end of file