diff --git a/api-reference/openapi_spec.json b/api-reference/openapi_spec.json index 57153fd233..48e376791b 100644 --- a/api-reference/openapi_spec.json +++ b/api-reference/openapi_spec.json @@ -18291,6 +18291,12 @@ ], "nullable": true }, + "request_extended_authorization": { + "type": "boolean", + "description": "Optional boolean value to extent authorization period of this payment\n\ncapture method must be manual or manual_multiple", + "default": false, + "nullable": true + }, "merchant_order_reference_id": { "type": "string", "description": "Merchant's identifier for the payment/invoice. This will be sent to the connector\nif the connector provides support to accept multiple reference ids.\nIn case the connector supports only one reference id, Hyperswitch's Payment ID will be sent as reference.", @@ -18684,6 +18690,12 @@ ], "nullable": true }, + "request_extended_authorization": { + "type": "boolean", + "description": "Optional boolean value to extent authorization period of this payment\n\ncapture method must be manual or manual_multiple", + "default": false, + "nullable": true + }, "merchant_order_reference_id": { "type": "string", "description": "Merchant's identifier for the payment/invoice. This will be sent to the connector\nif the connector provides support to accept multiple reference ids.\nIn case the connector supports only one reference id, Hyperswitch's Payment ID will be sent as reference.", @@ -19231,6 +19243,17 @@ "description": "You can specify up to 50 keys, with key names up to 40 characters long and values up to 500 characters long. FRM Metadata is useful for storing additional, structured information on an object related to FRM.", "nullable": true }, + "extended_authorization_applied": { + "type": "boolean", + "description": "flag that indicates if extended authorization is applied on this payment or not", + "nullable": true + }, + "capture_before": { + "type": "string", + "format": "date-time", + "description": "date and time after which this payment cannot be captured", + "nullable": true + }, "merchant_order_reference_id": { "type": "string", "description": "Merchant's identifier for the payment/invoice. This will be sent to the connector\nif the connector provides support to accept multiple reference ids.\nIn case the connector supports only one reference id, Hyperswitch's Payment ID will be sent as reference.", @@ -19909,6 +19932,12 @@ ], "nullable": true }, + "request_extended_authorization": { + "type": "boolean", + "description": "Optional boolean value to extent authorization period of this payment\n\ncapture method must be manual or manual_multiple", + "default": false, + "nullable": true + }, "merchant_order_reference_id": { "type": "string", "description": "Merchant's identifier for the payment/invoice. This will be sent to the connector\nif the connector provides support to accept multiple reference ids.\nIn case the connector supports only one reference id, Hyperswitch's Payment ID will be sent as reference.", @@ -20482,6 +20511,17 @@ "description": "You can specify up to 50 keys, with key names up to 40 characters long and values up to 500 characters long. FRM Metadata is useful for storing additional, structured information on an object related to FRM.", "nullable": true }, + "extended_authorization_applied": { + "type": "boolean", + "description": "flag that indicates if extended authorization is applied on this payment or not", + "nullable": true + }, + "capture_before": { + "type": "string", + "format": "date-time", + "description": "date and time after which this payment cannot be captured", + "nullable": true + }, "merchant_order_reference_id": { "type": "string", "description": "Merchant's identifier for the payment/invoice. This will be sent to the connector\nif the connector provides support to accept multiple reference ids.\nIn case the connector supports only one reference id, Hyperswitch's Payment ID will be sent as reference.", @@ -20973,6 +21013,12 @@ ], "nullable": true }, + "request_extended_authorization": { + "type": "boolean", + "description": "Optional boolean value to extent authorization period of this payment\n\ncapture method must be manual or manual_multiple", + "default": false, + "nullable": true + }, "merchant_order_reference_id": { "type": "string", "description": "Merchant's identifier for the payment/invoice. This will be sent to the connector\nif the connector provides support to accept multiple reference ids.\nIn case the connector supports only one reference id, Hyperswitch's Payment ID will be sent as reference.", @@ -22978,6 +23024,11 @@ "nullable": true, "minimum": 0 }, + "always_request_extended_authorization": { + "type": "boolean", + "description": "Bool indicating if extended authentication must be requested for all payments", + "nullable": true + }, "is_click_to_pay_enabled": { "type": "boolean", "description": "Indicates if click to pay is enabled or not." @@ -23217,6 +23268,11 @@ "description": "Maximum number of auto retries allowed for a payment", "nullable": true }, + "always_request_extended_authorization": { + "type": "boolean", + "description": "Bool indicating if extended authentication must be requested for all payments", + "nullable": true + }, "is_click_to_pay_enabled": { "type": "boolean", "description": "Indicates if click to pay is enabled or not.", diff --git a/crates/api_models/src/admin.rs b/crates/api_models/src/admin.rs index 7a0ece8520..77488262c7 100644 --- a/crates/api_models/src/admin.rs +++ b/crates/api_models/src/admin.rs @@ -8,7 +8,10 @@ use common_utils::{ id_type, link_utils, pii, }; #[cfg(feature = "v1")] -use common_utils::{crypto::OptionalEncryptableName, ext_traits::ValueExt}; +use common_utils::{ + crypto::OptionalEncryptableName, ext_traits::ValueExt, + types::AlwaysRequestExtendedAuthorization, +}; #[cfg(feature = "v2")] use masking::ExposeInterface; use masking::{PeekInterface, Secret}; @@ -1897,6 +1900,10 @@ pub struct ProfileCreate { /// Maximum number of auto retries allowed for a payment pub max_auto_retries_enabled: Option, + /// Bool indicating if extended authentication must be requested for all payments + #[schema(value_type = Option)] + pub always_request_extended_authorization: Option, + /// Indicates if click to pay is enabled or not. #[serde(default)] pub is_click_to_pay_enabled: bool, @@ -2152,6 +2159,10 @@ pub struct ProfileResponse { /// Maximum number of auto retries allowed for a payment pub max_auto_retries_enabled: Option, + /// Bool indicating if extended authentication must be requested for all payments + #[schema(value_type = Option)] + pub always_request_extended_authorization: Option, + /// Indicates if click to pay is enabled or not. #[schema(default = false, example = false)] pub is_click_to_pay_enabled: bool, diff --git a/crates/api_models/src/lib.rs b/crates/api_models/src/lib.rs index 60d1d11f27..5ec80d2b53 100644 --- a/crates/api_models/src/lib.rs +++ b/crates/api_models/src/lib.rs @@ -41,3 +41,12 @@ pub mod verifications; pub mod verify_connector; pub mod webhook_events; pub mod webhooks; + +pub trait ValidateFieldAndGet { + fn validate_field_and_get( + &self, + request: &Request, + ) -> common_utils::errors::CustomResult + where + Self: Sized; +} diff --git a/crates/api_models/src/payments.rs b/crates/api_models/src/payments.rs index bf7dd37739..7b1ba943ee 100644 --- a/crates/api_models/src/payments.rs +++ b/crates/api_models/src/payments.rs @@ -4,6 +4,7 @@ use std::{ num::NonZeroI64, }; pub mod additional_info; +pub mod trait_impls; use cards::CardNumber; #[cfg(feature = "v2")] use common_enums::enums::PaymentConnectorTransmission; @@ -18,7 +19,10 @@ use common_utils::{ hashing::HashedString, id_type, pii::{self, Email}, - types::{MinorUnit, StringMajorUnit}, + types::{ + ExtendedAuthorizationAppliedBool, MinorUnit, RequestExtendedAuthorizationBool, + StringMajorUnit, + }, }; use error_stack::ResultExt; use masking::{PeekInterface, Secret, WithType}; @@ -37,7 +41,7 @@ use crate::{ admin::{self, MerchantConnectorInfo}, disputes, enums as api_enums, mandates::RecurringDetails, - refunds, + refunds, ValidateFieldAndGet, }; #[derive(Clone, Copy, Debug, Eq, PartialEq)] @@ -1042,6 +1046,12 @@ pub struct PaymentsRequest { #[schema(value_type = Option)] pub split_payments: Option, + /// Optional boolean value to extent authorization period of this payment + /// + /// capture method must be manual or manual_multiple + #[schema(value_type = Option, default = false)] + pub request_extended_authorization: Option, + /// Merchant's identifier for the payment/invoice. This will be sent to the connector /// if the connector provides support to accept multiple reference ids. /// In case the connector supports only one reference id, Hyperswitch's Payment ID will be sent as reference. @@ -1101,6 +1111,18 @@ impl PaymentsRequest { .or(self.customer.as_ref().map(|customer| &customer.id)) } + pub fn validate_and_get_request_extended_authorization( + &self, + ) -> common_utils::errors::CustomResult, ValidationError> + { + self.request_extended_authorization + .as_ref() + .map(|request_extended_authorization| { + request_extended_authorization.validate_field_and_get(self) + }) + .transpose() + } + /// Checks if the customer details are passed in both places /// If they are passed in both places, check for both the values to be equal /// Or else, return the field which has inconsistent data @@ -4842,6 +4864,14 @@ pub struct PaymentsResponse { #[schema(value_type = Option, example = r#"{ "fulfillment_method" : "deliver", "coverage_request" : "fraud" }"#)] pub frm_metadata: Option, + /// flag that indicates if extended authorization is applied on this payment or not + #[schema(value_type = Option)] + pub extended_authorization_applied: Option, + + /// date and time after which this payment cannot be captured + #[serde(default, with = "common_utils::custom_serde::iso8601::option")] + pub capture_before: Option, + /// Merchant's identifier for the payment/invoice. This will be sent to the connector /// if the connector provides support to accept multiple reference ids. /// In case the connector supports only one reference id, Hyperswitch's Payment ID will be sent as reference. diff --git a/crates/api_models/src/payments/trait_impls.rs b/crates/api_models/src/payments/trait_impls.rs new file mode 100644 index 0000000000..5df07a24a9 --- /dev/null +++ b/crates/api_models/src/payments/trait_impls.rs @@ -0,0 +1,29 @@ +#[cfg(feature = "v1")] +use common_enums::enums; +#[cfg(feature = "v1")] +use common_utils::errors; + +#[cfg(feature = "v1")] +use crate::payments; + +#[cfg(feature = "v1")] +impl crate::ValidateFieldAndGet + for common_utils::types::RequestExtendedAuthorizationBool +{ + fn validate_field_and_get( + &self, + request: &payments::PaymentsRequest, + ) -> errors::CustomResult + where + Self: Sized, + { + match request.capture_method{ + Some(enums::CaptureMethod::Automatic) + | Some(enums::CaptureMethod::Scheduled) + | Some(enums::CaptureMethod::SequentialAutomatic) + | None => Err(error_stack::report!(errors::ValidationError::InvalidValue { message: "request_extended_authorization must be sent only if capture method is manual or manual_multiple".to_string() })), + Some(enums::CaptureMethod::Manual) + | Some(enums::CaptureMethod::ManualMultiple) => Ok(*self) + } + } +} diff --git a/crates/common_utils/src/types.rs b/crates/common_utils/src/types.rs index 0377a3e818..9a105933e1 100644 --- a/crates/common_utils/src/types.rs +++ b/crates/common_utils/src/types.rs @@ -6,6 +6,9 @@ pub mod authentication; /// Enum for Theme Lineage pub mod theme; +/// types that are wrappers around primitive types +pub mod primitive_wrappers; + use std::{ borrow::Cow, fmt::Display, @@ -26,6 +29,10 @@ use diesel::{ AsExpression, FromSqlRow, Queryable, }; use error_stack::{report, ResultExt}; +pub use primitive_wrappers::bool_wrappers::{ + AlwaysRequestExtendedAuthorization, ExtendedAuthorizationAppliedBool, + RequestExtendedAuthorizationBool, +}; use rust_decimal::{ prelude::{FromPrimitive, ToPrimitive}, Decimal, diff --git a/crates/common_utils/src/types/primitive_wrappers.rs b/crates/common_utils/src/types/primitive_wrappers.rs new file mode 100644 index 0000000000..850f6a7cb5 --- /dev/null +++ b/crates/common_utils/src/types/primitive_wrappers.rs @@ -0,0 +1,107 @@ +pub(crate) mod bool_wrappers { + use serde::{Deserialize, Serialize}; + + /// Bool that represents if Extended Authorization is Applied or not + #[derive( + Clone, Copy, Debug, Eq, PartialEq, Serialize, Deserialize, diesel::expression::AsExpression, + )] + #[diesel(sql_type = diesel::sql_types::Bool)] + pub struct ExtendedAuthorizationAppliedBool(bool); + impl From for ExtendedAuthorizationAppliedBool { + fn from(value: bool) -> Self { + Self(value) + } + } + impl diesel::serialize::ToSql for ExtendedAuthorizationAppliedBool + where + DB: diesel::backend::Backend, + bool: diesel::serialize::ToSql, + { + fn to_sql<'b>( + &'b self, + out: &mut diesel::serialize::Output<'b, '_, DB>, + ) -> diesel::serialize::Result { + self.0.to_sql(out) + } + } + impl diesel::deserialize::FromSql + for ExtendedAuthorizationAppliedBool + where + DB: diesel::backend::Backend, + bool: diesel::deserialize::FromSql, + { + fn from_sql(value: DB::RawValue<'_>) -> diesel::deserialize::Result { + bool::from_sql(value).map(Self) + } + } + + /// Bool that represents if Extended Authorization is Requested or not + #[derive( + Clone, Copy, Debug, Eq, PartialEq, Serialize, Deserialize, diesel::expression::AsExpression, + )] + #[diesel(sql_type = diesel::sql_types::Bool)] + pub struct RequestExtendedAuthorizationBool(bool); + impl From for RequestExtendedAuthorizationBool { + fn from(value: bool) -> Self { + Self(value) + } + } + impl RequestExtendedAuthorizationBool { + /// returns the inner bool value + pub fn is_true(&self) -> bool { + self.0 + } + } + impl diesel::serialize::ToSql for RequestExtendedAuthorizationBool + where + DB: diesel::backend::Backend, + bool: diesel::serialize::ToSql, + { + fn to_sql<'b>( + &'b self, + out: &mut diesel::serialize::Output<'b, '_, DB>, + ) -> diesel::serialize::Result { + self.0.to_sql(out) + } + } + impl diesel::deserialize::FromSql + for RequestExtendedAuthorizationBool + where + DB: diesel::backend::Backend, + bool: diesel::deserialize::FromSql, + { + fn from_sql(value: DB::RawValue<'_>) -> diesel::deserialize::Result { + bool::from_sql(value).map(Self) + } + } + + /// Bool that represents if Extended Authorization is always Requested or not + #[derive( + Clone, Copy, Debug, Eq, PartialEq, diesel::expression::AsExpression, Serialize, Deserialize, + )] + #[diesel(sql_type = diesel::sql_types::Bool)] + pub struct AlwaysRequestExtendedAuthorization(bool); + impl diesel::serialize::ToSql + for AlwaysRequestExtendedAuthorization + where + DB: diesel::backend::Backend, + bool: diesel::serialize::ToSql, + { + fn to_sql<'b>( + &'b self, + out: &mut diesel::serialize::Output<'b, '_, DB>, + ) -> diesel::serialize::Result { + self.0.to_sql(out) + } + } + impl diesel::deserialize::FromSql + for AlwaysRequestExtendedAuthorization + where + DB: diesel::backend::Backend, + bool: diesel::deserialize::FromSql, + { + fn from_sql(value: DB::RawValue<'_>) -> diesel::deserialize::Result { + bool::from_sql(value).map(Self) + } + } +} diff --git a/crates/diesel_models/src/business_profile.rs b/crates/diesel_models/src/business_profile.rs index 70f969bd2a..92da197845 100644 --- a/crates/diesel_models/src/business_profile.rs +++ b/crates/diesel_models/src/business_profile.rs @@ -1,7 +1,7 @@ use std::collections::{HashMap, HashSet}; use common_enums::{AuthenticationConnectors, UIWidgetFormLayout}; -use common_utils::{encryption::Encryption, pii}; +use common_utils::{encryption::Encryption, pii, types::AlwaysRequestExtendedAuthorization}; use diesel::{AsChangeset, Identifiable, Insertable, Queryable, Selectable}; use masking::Secret; @@ -57,6 +57,7 @@ pub struct Profile { pub is_network_tokenization_enabled: bool, pub is_auto_retries_enabled: Option, pub max_auto_retries_enabled: Option, + pub always_request_extended_authorization: Option, pub is_click_to_pay_enabled: bool, pub authentication_product_ids: Option, @@ -146,6 +147,7 @@ pub struct ProfileUpdateInternal { pub is_network_tokenization_enabled: Option, pub is_auto_retries_enabled: Option, pub max_auto_retries_enabled: Option, + pub always_request_extended_authorization: Option, pub is_click_to_pay_enabled: Option, pub authentication_product_ids: Option, @@ -188,6 +190,7 @@ impl ProfileUpdateInternal { is_network_tokenization_enabled, is_auto_retries_enabled, max_auto_retries_enabled, + always_request_extended_authorization, is_click_to_pay_enabled, authentication_product_ids, } = self; @@ -249,6 +252,8 @@ impl ProfileUpdateInternal { .unwrap_or(source.is_network_tokenization_enabled), is_auto_retries_enabled: is_auto_retries_enabled.or(source.is_auto_retries_enabled), max_auto_retries_enabled: max_auto_retries_enabled.or(source.max_auto_retries_enabled), + always_request_extended_authorization: always_request_extended_authorization + .or(source.always_request_extended_authorization), is_click_to_pay_enabled: is_click_to_pay_enabled .unwrap_or(source.is_click_to_pay_enabled), authentication_product_ids: authentication_product_ids @@ -307,6 +312,7 @@ pub struct Profile { pub is_network_tokenization_enabled: bool, pub is_auto_retries_enabled: Option, pub max_auto_retries_enabled: Option, + pub always_request_extended_authorization: Option, pub is_click_to_pay_enabled: bool, pub authentication_product_ids: Option, @@ -527,6 +533,7 @@ impl ProfileUpdateInternal { .unwrap_or(source.is_network_tokenization_enabled), is_auto_retries_enabled: is_auto_retries_enabled.or(source.is_auto_retries_enabled), max_auto_retries_enabled: max_auto_retries_enabled.or(source.max_auto_retries_enabled), + always_request_extended_authorization: None, is_click_to_pay_enabled: is_click_to_pay_enabled .unwrap_or(source.is_click_to_pay_enabled), authentication_product_ids: authentication_product_ids diff --git a/crates/diesel_models/src/payment_attempt.rs b/crates/diesel_models/src/payment_attempt.rs index 2391422ee0..8cf6b604b9 100644 --- a/crates/diesel_models/src/payment_attempt.rs +++ b/crates/diesel_models/src/payment_attempt.rs @@ -1,6 +1,9 @@ use common_utils::{ id_type, pii, - types::{ConnectorTransactionId, ConnectorTransactionIdTrait, MinorUnit}, + types::{ + ConnectorTransactionId, ConnectorTransactionIdTrait, ExtendedAuthorizationAppliedBool, + MinorUnit, RequestExtendedAuthorizationBool, + }, }; use diesel::{AsChangeset, Identifiable, Insertable, Queryable, Selectable}; use serde::{Deserialize, Serialize}; @@ -93,6 +96,9 @@ pub struct PaymentAttempt { pub id: id_type::GlobalAttemptId, pub shipping_cost: Option, pub order_tax_amount: Option, + pub request_extended_authorization: Option, + pub extended_authorization_applied: Option, + pub capture_before: Option, pub card_discovery: Option, pub charges: Option, } @@ -174,6 +180,9 @@ pub struct PaymentAttempt { /// INFO: This field is deprecated and replaced by processor_transaction_data pub connector_transaction_data: Option, pub connector_mandate_detail: Option, + pub request_extended_authorization: Option, + pub extended_authorization_applied: Option, + pub capture_before: Option, pub processor_transaction_data: Option, pub card_discovery: Option, pub charges: Option, @@ -303,6 +312,9 @@ pub struct PaymentAttemptNew { pub id: id_type::GlobalAttemptId, pub connector_token_details: Option, pub card_discovery: Option, + pub request_extended_authorization: Option, + pub extended_authorization_applied: Option, + pub capture_before: Option, pub charges: Option, } @@ -376,6 +388,10 @@ pub struct PaymentAttemptNew { pub shipping_cost: Option, pub order_tax_amount: Option, pub connector_mandate_detail: Option, + pub request_extended_authorization: Option, + pub extended_authorization_applied: Option, + #[serde(default, with = "common_utils::custom_serde::iso8601::option")] + pub capture_before: Option, pub card_discovery: Option, } diff --git a/crates/diesel_models/src/payment_intent.rs b/crates/diesel_models/src/payment_intent.rs index 57233306c2..f10616ceb1 100644 --- a/crates/diesel_models/src/payment_intent.rs +++ b/crates/diesel_models/src/payment_intent.rs @@ -1,5 +1,9 @@ use common_enums::{PaymentMethodType, RequestIncrementalAuthorization}; -use common_utils::{encryption::Encryption, pii, types::MinorUnit}; +use common_utils::{ + encryption::Encryption, + pii, + types::{MinorUnit, RequestExtendedAuthorizationBool}, +}; use diesel::{AsChangeset, Identifiable, Insertable, Queryable, Selectable}; use serde::{Deserialize, Serialize}; use time::PrimitiveDateTime; @@ -72,6 +76,7 @@ pub struct PaymentIntent { pub routing_algorithm_id: Option, pub payment_link_config: Option, pub id: common_utils::id_type::GlobalPaymentId, + pub request_extended_authorization: Option, pub psd2_sca_exemption_type: Option, pub split_payments: Option, pub platform_merchant_id: Option, @@ -139,6 +144,7 @@ pub struct PaymentIntent { pub organization_id: common_utils::id_type::OrganizationId, pub tax_details: Option, pub skip_external_tax_calculation: Option, + pub request_extended_authorization: Option, pub psd2_sca_exemption_type: Option, pub split_payments: Option, pub platform_merchant_id: Option, @@ -372,6 +378,7 @@ pub struct PaymentIntentNew { pub organization_id: common_utils::id_type::OrganizationId, pub tax_details: Option, pub skip_external_tax_calculation: Option, + pub request_extended_authorization: Option, pub psd2_sca_exemption_type: Option, pub platform_merchant_id: Option, pub split_payments: Option, diff --git a/crates/diesel_models/src/schema.rs b/crates/diesel_models/src/schema.rs index 2e8219c5ab..cfc0f28e4d 100644 --- a/crates/diesel_models/src/schema.rs +++ b/crates/diesel_models/src/schema.rs @@ -216,6 +216,7 @@ diesel::table! { is_network_tokenization_enabled -> Bool, is_auto_retries_enabled -> Nullable, max_auto_retries_enabled -> Nullable, + always_request_extended_authorization -> Nullable, is_click_to_pay_enabled -> Bool, authentication_product_ids -> Nullable, } @@ -910,6 +911,9 @@ diesel::table! { #[max_length = 512] connector_transaction_data -> Nullable, connector_mandate_detail -> Nullable, + request_extended_authorization -> Nullable, + extended_authorization_applied -> Nullable, + capture_before -> Nullable, processor_transaction_data -> Nullable, card_discovery -> Nullable, charges -> Nullable, @@ -993,6 +997,7 @@ diesel::table! { organization_id -> Varchar, tax_details -> Nullable, skip_external_tax_calculation -> Nullable, + request_extended_authorization -> Nullable, psd2_sca_exemption_type -> Nullable, split_payments -> Nullable, #[max_length = 64] diff --git a/crates/diesel_models/src/schema_v2.rs b/crates/diesel_models/src/schema_v2.rs index ab2195526f..c4db38c0ee 100644 --- a/crates/diesel_models/src/schema_v2.rs +++ b/crates/diesel_models/src/schema_v2.rs @@ -224,6 +224,7 @@ diesel::table! { is_network_tokenization_enabled -> Bool, is_auto_retries_enabled -> Nullable, max_auto_retries_enabled -> Nullable, + always_request_extended_authorization -> Nullable, is_click_to_pay_enabled -> Bool, authentication_product_ids -> Nullable, three_ds_decision_manager_config -> Nullable, @@ -877,6 +878,9 @@ diesel::table! { id -> Varchar, shipping_cost -> Nullable, order_tax_amount -> Nullable, + request_extended_authorization -> Nullable, + extended_authorization_applied -> Nullable, + capture_before -> Nullable, card_discovery -> Nullable, charges -> Nullable, } @@ -952,6 +956,7 @@ diesel::table! { payment_link_config -> Nullable, #[max_length = 64] id -> Varchar, + request_extended_authorization -> Nullable, psd2_sca_exemption_type -> Nullable, split_payments -> Nullable, #[max_length = 64] diff --git a/crates/diesel_models/src/user/sample_data.rs b/crates/diesel_models/src/user/sample_data.rs index 2708542408..d6c8d5616f 100644 --- a/crates/diesel_models/src/user/sample_data.rs +++ b/crates/diesel_models/src/user/sample_data.rs @@ -2,7 +2,10 @@ use common_enums::{ AttemptStatus, AuthenticationType, CaptureMethod, Currency, PaymentExperience, PaymentMethod, PaymentMethodType, }; -use common_utils::types::{ConnectorTransactionId, MinorUnit}; +use common_utils::types::{ + ConnectorTransactionId, ExtendedAuthorizationAppliedBool, MinorUnit, + RequestExtendedAuthorizationBool, +}; use serde::{Deserialize, Serialize}; use time::PrimitiveDateTime; @@ -203,6 +206,9 @@ pub struct PaymentAttemptBatchNew { pub order_tax_amount: Option, pub processor_transaction_data: Option, pub connector_mandate_detail: Option, + pub request_extended_authorization: Option, + pub extended_authorization_applied: Option, + pub capture_before: Option, pub card_discovery: Option, } @@ -282,6 +288,9 @@ impl PaymentAttemptBatchNew { shipping_cost: self.shipping_cost, order_tax_amount: self.order_tax_amount, connector_mandate_detail: self.connector_mandate_detail, + request_extended_authorization: self.request_extended_authorization, + extended_authorization_applied: self.extended_authorization_applied, + capture_before: self.capture_before, card_discovery: self.card_discovery, } } diff --git a/crates/hyperswitch_domain_models/src/business_profile.rs b/crates/hyperswitch_domain_models/src/business_profile.rs index b2385efb6d..4a6a84124a 100644 --- a/crates/hyperswitch_domain_models/src/business_profile.rs +++ b/crates/hyperswitch_domain_models/src/business_profile.rs @@ -4,7 +4,7 @@ use common_utils::{ encryption::Encryption, errors::{CustomResult, ValidationError}, pii, type_name, - types::keymanager, + types::{keymanager, AlwaysRequestExtendedAuthorization}, }; use diesel_models::business_profile::{ AuthenticationConnectorDetails, BusinessPaymentLinkConfig, BusinessPayoutLinkConfig, @@ -58,6 +58,7 @@ pub struct Profile { pub is_network_tokenization_enabled: bool, pub is_auto_retries_enabled: bool, pub max_auto_retries_enabled: Option, + pub always_request_extended_authorization: Option, pub is_click_to_pay_enabled: bool, pub authentication_product_ids: Option, @@ -101,6 +102,7 @@ pub struct ProfileSetter { pub is_network_tokenization_enabled: bool, pub is_auto_retries_enabled: bool, pub max_auto_retries_enabled: Option, + pub always_request_extended_authorization: Option, pub is_click_to_pay_enabled: bool, pub authentication_product_ids: Option, @@ -151,6 +153,7 @@ impl From for Profile { is_network_tokenization_enabled: value.is_network_tokenization_enabled, is_auto_retries_enabled: value.is_auto_retries_enabled, max_auto_retries_enabled: value.max_auto_retries_enabled, + always_request_extended_authorization: value.always_request_extended_authorization, is_click_to_pay_enabled: value.is_click_to_pay_enabled, authentication_product_ids: value.authentication_product_ids, } @@ -306,6 +309,7 @@ impl From for ProfileUpdateInternal { is_network_tokenization_enabled, is_auto_retries_enabled, max_auto_retries_enabled, + always_request_extended_authorization: None, is_click_to_pay_enabled, authentication_product_ids, } @@ -347,6 +351,7 @@ impl From for ProfileUpdateInternal { is_network_tokenization_enabled: None, is_auto_retries_enabled: None, max_auto_retries_enabled: None, + always_request_extended_authorization: None, is_click_to_pay_enabled: None, authentication_product_ids: None, }, @@ -386,6 +391,7 @@ impl From for ProfileUpdateInternal { is_network_tokenization_enabled: None, is_auto_retries_enabled: None, max_auto_retries_enabled: None, + always_request_extended_authorization: None, is_click_to_pay_enabled: None, authentication_product_ids: None, }, @@ -425,6 +431,7 @@ impl From for ProfileUpdateInternal { is_network_tokenization_enabled: None, is_auto_retries_enabled: None, max_auto_retries_enabled: None, + always_request_extended_authorization: None, is_click_to_pay_enabled: None, authentication_product_ids: None, }, @@ -464,6 +471,7 @@ impl From for ProfileUpdateInternal { is_network_tokenization_enabled: None, is_auto_retries_enabled: None, max_auto_retries_enabled: None, + always_request_extended_authorization: None, is_click_to_pay_enabled: None, authentication_product_ids: None, }, @@ -503,6 +511,7 @@ impl From for ProfileUpdateInternal { is_network_tokenization_enabled: Some(is_network_tokenization_enabled), is_auto_retries_enabled: None, max_auto_retries_enabled: None, + always_request_extended_authorization: None, is_click_to_pay_enabled: None, authentication_product_ids: None, }, @@ -561,6 +570,7 @@ impl super::behaviour::Conversion for Profile { is_network_tokenization_enabled: self.is_network_tokenization_enabled, is_auto_retries_enabled: Some(self.is_auto_retries_enabled), max_auto_retries_enabled: self.max_auto_retries_enabled, + always_request_extended_authorization: self.always_request_extended_authorization, is_click_to_pay_enabled: self.is_click_to_pay_enabled, authentication_product_ids: self.authentication_product_ids, }) @@ -631,6 +641,7 @@ impl super::behaviour::Conversion for Profile { is_network_tokenization_enabled: item.is_network_tokenization_enabled, is_auto_retries_enabled: item.is_auto_retries_enabled.unwrap_or(false), max_auto_retries_enabled: item.max_auto_retries_enabled, + always_request_extended_authorization: item.always_request_extended_authorization, is_click_to_pay_enabled: item.is_click_to_pay_enabled, authentication_product_ids: item.authentication_product_ids, }) @@ -1343,6 +1354,7 @@ impl super::behaviour::Conversion for Profile { is_network_tokenization_enabled: self.is_network_tokenization_enabled, is_auto_retries_enabled: None, max_auto_retries_enabled: None, + always_request_extended_authorization: None, is_click_to_pay_enabled: self.is_click_to_pay_enabled, authentication_product_ids: self.authentication_product_ids, three_ds_decision_manager_config: self.three_ds_decision_manager_config, diff --git a/crates/hyperswitch_domain_models/src/payments.rs b/crates/hyperswitch_domain_models/src/payments.rs index 2457448f21..0b98885fda 100644 --- a/crates/hyperswitch_domain_models/src/payments.rs +++ b/crates/hyperswitch_domain_models/src/payments.rs @@ -11,7 +11,7 @@ use common_utils::{ encryption::Encryption, errors::CustomResult, id_type, pii, - types::{keymanager::ToEncryptable, MinorUnit}, + types::{keymanager::ToEncryptable, MinorUnit, RequestExtendedAuthorizationBool}, }; use diesel_models::payment_intent::TaxDetails; #[cfg(feature = "v2")] @@ -108,6 +108,7 @@ pub struct PaymentIntent { pub organization_id: id_type::OrganizationId, pub tax_details: Option, pub skip_external_tax_calculation: Option, + pub request_extended_authorization: Option, pub psd2_sca_exemption_type: Option, pub platform_merchant_id: Option, } diff --git a/crates/hyperswitch_domain_models/src/payments/payment_attempt.rs b/crates/hyperswitch_domain_models/src/payments/payment_attempt.rs index af462443cd..f557e7c907 100644 --- a/crates/hyperswitch_domain_models/src/payments/payment_attempt.rs +++ b/crates/hyperswitch_domain_models/src/payments/payment_attempt.rs @@ -11,7 +11,8 @@ use common_utils::{ id_type, pii, types::{ keymanager::{self, KeyManagerState}, - ConnectorTransactionId, ConnectorTransactionIdTrait, MinorUnit, + ConnectorTransactionId, ConnectorTransactionIdTrait, ExtendedAuthorizationAppliedBool, + MinorUnit, RequestExtendedAuthorizationBool, }, }; use diesel_models::{ @@ -614,6 +615,9 @@ pub struct PaymentAttempt { pub profile_id: id_type::ProfileId, pub organization_id: id_type::OrganizationId, pub connector_mandate_detail: Option, + pub request_extended_authorization: Option, + pub extended_authorization_applied: Option, + pub capture_before: Option, pub card_discovery: Option, pub charges: Option, } @@ -861,6 +865,9 @@ pub struct PaymentAttemptNew { pub profile_id: id_type::ProfileId, pub organization_id: id_type::OrganizationId, pub connector_mandate_detail: Option, + pub request_extended_authorization: Option, + pub extended_authorization_applied: Option, + pub capture_before: Option, pub card_discovery: Option, } @@ -1585,6 +1592,9 @@ impl behaviour::Conversion for PaymentAttempt { order_tax_amount: self.net_amount.get_order_tax_amount(), shipping_cost: self.net_amount.get_shipping_cost(), connector_mandate_detail: self.connector_mandate_detail, + request_extended_authorization: self.request_extended_authorization, + extended_authorization_applied: self.extended_authorization_applied, + capture_before: self.capture_before, processor_transaction_data, card_discovery: self.card_discovery, charges: self.charges, @@ -1671,6 +1681,9 @@ impl behaviour::Conversion for PaymentAttempt { profile_id: storage_model.profile_id, organization_id: storage_model.organization_id, connector_mandate_detail: storage_model.connector_mandate_detail, + request_extended_authorization: storage_model.request_extended_authorization, + extended_authorization_applied: storage_model.extended_authorization_applied, + capture_before: storage_model.capture_before, card_discovery: storage_model.card_discovery, charges: storage_model.charges, }) @@ -1754,6 +1767,9 @@ impl behaviour::Conversion for PaymentAttempt { order_tax_amount: self.net_amount.get_order_tax_amount(), shipping_cost: self.net_amount.get_shipping_cost(), connector_mandate_detail: self.connector_mandate_detail, + request_extended_authorization: self.request_extended_authorization, + extended_authorization_applied: self.extended_authorization_applied, + capture_before: self.capture_before, card_discovery: self.card_discovery, }) } @@ -1900,6 +1916,9 @@ impl behaviour::Conversion for PaymentAttempt { connector_payment_data, connector_token_details, card_discovery, + request_extended_authorization: None, + extended_authorization_applied: None, + capture_before: None, charges, }) } @@ -2023,9 +2042,54 @@ impl behaviour::Conversion for PaymentAttempt { async fn construct_new(self) -> CustomResult { use common_utils::encryption::Encryption; + let Self { + payment_id, + merchant_id, + status, + error, + amount_details, + authentication_type, + created_at, + modified_at, + last_synced, + cancellation_reason, + browser_info, + payment_token, + connector_metadata, + payment_experience, + payment_method_data, + routing_result, + preprocessing_step_id, + multiple_capture_count, + connector_response_reference_id, + updated_by, + redirection_data, + encoded_data, + merchant_connector_id, + external_three_ds_authentication_attempted, + authentication_connector, + authentication_id, + fingerprint_id, + client_source, + client_version, + customer_acceptance, + profile_id, + organization_id, + payment_method_type, + connector_payment_id, + payment_method_subtype, + authentication_applied, + external_reference_id, + id, + payment_method_id, + payment_method_billing_address, + connector, + connector_token_details, + card_discovery, + charges, + } = self; - let card_network = self - .payment_method_data + let card_network = payment_method_data .as_ref() .and_then(|data| data.peek().as_object()) .and_then(|card| card.get("card")) @@ -2034,70 +2098,70 @@ impl behaviour::Conversion for PaymentAttempt { .and_then(|network| network.as_str()) .map(|network| network.to_string()); - let error_details = self.error; + let error_details = error; Ok(DieselPaymentAttemptNew { - payment_id: self.payment_id, - merchant_id: self.merchant_id, - status: self.status, + payment_id, + merchant_id, + status, error_message: error_details .as_ref() .map(|details| details.message.clone()), - surcharge_amount: self.amount_details.surcharge_amount, - tax_on_surcharge: self.amount_details.tax_on_surcharge, - payment_method_id: self.payment_method_id, - authentication_type: self.authentication_type, - created_at: self.created_at, - modified_at: self.modified_at, - last_synced: self.last_synced, - cancellation_reason: self.cancellation_reason, - browser_info: self.browser_info, - payment_token: self.payment_token, + surcharge_amount: amount_details.surcharge_amount, + tax_on_surcharge: amount_details.tax_on_surcharge, + payment_method_id, + authentication_type, + created_at, + modified_at, + last_synced, + cancellation_reason, + browser_info, + payment_token, error_code: error_details.as_ref().map(|details| details.code.clone()), - connector_metadata: self.connector_metadata, - payment_experience: self.payment_experience, - payment_method_data: self.payment_method_data, - preprocessing_step_id: self.preprocessing_step_id, + connector_metadata, + payment_experience, + payment_method_data, + preprocessing_step_id, error_reason: error_details .as_ref() .and_then(|details| details.reason.clone()), - connector_response_reference_id: self.connector_response_reference_id, - multiple_capture_count: self.multiple_capture_count, - amount_capturable: self.amount_details.amount_capturable, - updated_by: self.updated_by, - merchant_connector_id: self.merchant_connector_id, - redirection_data: self.redirection_data.map(From::from), - encoded_data: self.encoded_data, + connector_response_reference_id, + multiple_capture_count, + amount_capturable: amount_details.amount_capturable, + updated_by, + merchant_connector_id, + redirection_data: redirection_data.map(From::from), + encoded_data, unified_code: error_details .as_ref() .and_then(|details| details.unified_code.clone()), unified_message: error_details .as_ref() .and_then(|details| details.unified_message.clone()), - net_amount: self.amount_details.net_amount, - external_three_ds_authentication_attempted: self - .external_three_ds_authentication_attempted, - authentication_connector: self.authentication_connector, - authentication_id: self.authentication_id, - fingerprint_id: self.fingerprint_id, - client_source: self.client_source, - client_version: self.client_version, - customer_acceptance: self.customer_acceptance, - profile_id: self.profile_id, - organization_id: self.organization_id, + net_amount: amount_details.net_amount, + external_three_ds_authentication_attempted, + authentication_connector, + authentication_id, + fingerprint_id, + client_source, + client_version, + customer_acceptance, + profile_id, + organization_id, card_network, - order_tax_amount: self.amount_details.order_tax_amount, - shipping_cost: self.amount_details.shipping_cost, - amount_to_capture: self.amount_details.amount_to_capture, - payment_method_billing_address: self - .payment_method_billing_address - .map(Encryption::from), - payment_method_subtype: self.payment_method_subtype, - payment_method_type_v2: self.payment_method_type, - id: self.id, - connector_token_details: self.connector_token_details, - card_discovery: self.card_discovery, - charges: self.charges, + order_tax_amount: amount_details.order_tax_amount, + shipping_cost: amount_details.shipping_cost, + amount_to_capture: amount_details.amount_to_capture, + payment_method_billing_address: payment_method_billing_address.map(Encryption::from), + payment_method_subtype, + payment_method_type_v2: payment_method_type, + id, + charges, + connector_token_details, + card_discovery, + extended_authorization_applied: None, + request_extended_authorization: None, + capture_before: None, }) } } diff --git a/crates/hyperswitch_domain_models/src/payments/payment_intent.rs b/crates/hyperswitch_domain_models/src/payments/payment_intent.rs index b2335550cf..f82eb782c5 100644 --- a/crates/hyperswitch_domain_models/src/payments/payment_intent.rs +++ b/crates/hyperswitch_domain_models/src/payments/payment_intent.rs @@ -1406,6 +1406,7 @@ impl behaviour::Conversion for PaymentIntent { payment_link_config, routing_algorithm_id, psd2_sca_exemption_type: None, + request_extended_authorization: None, platform_merchant_id, split_payments: None, }) @@ -1672,6 +1673,7 @@ impl behaviour::Conversion for PaymentIntent { shipping_cost: self.shipping_cost, tax_details: self.tax_details, skip_external_tax_calculation: self.skip_external_tax_calculation, + request_extended_authorization: self.request_extended_authorization, psd2_sca_exemption_type: self.psd2_sca_exemption_type, platform_merchant_id: self.platform_merchant_id, }) @@ -1761,6 +1763,7 @@ impl behaviour::Conversion for PaymentIntent { is_payment_processor_token_flow: storage_model.is_payment_processor_token_flow, organization_id: storage_model.organization_id, skip_external_tax_calculation: storage_model.skip_external_tax_calculation, + request_extended_authorization: storage_model.request_extended_authorization, psd2_sca_exemption_type: storage_model.psd2_sca_exemption_type, platform_merchant_id: storage_model.platform_merchant_id, }) @@ -1826,6 +1829,7 @@ impl behaviour::Conversion for PaymentIntent { shipping_cost: self.shipping_cost, tax_details: self.tax_details, skip_external_tax_calculation: self.skip_external_tax_calculation, + request_extended_authorization: self.request_extended_authorization, psd2_sca_exemption_type: self.psd2_sca_exemption_type, platform_merchant_id: self.platform_merchant_id, }) diff --git a/crates/router/src/core/admin.rs b/crates/router/src/core/admin.rs index cdca5883f4..601fbd1623 100644 --- a/crates/router/src/core/admin.rs +++ b/crates/router/src/core/admin.rs @@ -3710,6 +3710,7 @@ impl ProfileCreateBridge for api::ProfileCreate { is_network_tokenization_enabled: self.is_network_tokenization_enabled, is_auto_retries_enabled: self.is_auto_retries_enabled.unwrap_or_default(), max_auto_retries_enabled: self.max_auto_retries_enabled.map(i16::from), + always_request_extended_authorization: self.always_request_extended_authorization, is_click_to_pay_enabled: self.is_click_to_pay_enabled, authentication_product_ids: self.authentication_product_ids, })) diff --git a/crates/router/src/core/payments/helpers.rs b/crates/router/src/core/payments/helpers.rs index 39bbef1f3d..6702b94c43 100644 --- a/crates/router/src/core/payments/helpers.rs +++ b/crates/router/src/core/payments/helpers.rs @@ -3613,6 +3613,7 @@ mod tests { shipping_cost: None, tax_details: None, skip_external_tax_calculation: None, + request_extended_authorization: None, psd2_sca_exemption_type: None, platform_merchant_id: None, }; @@ -3684,6 +3685,7 @@ mod tests { shipping_cost: None, tax_details: None, skip_external_tax_calculation: None, + request_extended_authorization: None, psd2_sca_exemption_type: None, platform_merchant_id: None, }; @@ -3753,6 +3755,7 @@ mod tests { shipping_cost: None, tax_details: None, skip_external_tax_calculation: None, + request_extended_authorization: None, psd2_sca_exemption_type: None, platform_merchant_id: None, }; @@ -4286,6 +4289,9 @@ impl AttemptType { organization_id: old_payment_attempt.organization_id, profile_id: old_payment_attempt.profile_id, connector_mandate_detail: None, + request_extended_authorization: None, + extended_authorization_applied: None, + capture_before: None, card_discovery: None, } } diff --git a/crates/router/src/core/payments/operations/payment_create.rs b/crates/router/src/core/payments/operations/payment_create.rs index 1fd9f2404f..1d3a56162e 100644 --- a/crates/router/src/core/payments/operations/payment_create.rs +++ b/crates/router/src/core/payments/operations/payment_create.rs @@ -1305,6 +1305,9 @@ impl PaymentCreate { organization_id: organization_id.clone(), profile_id, connector_mandate_detail: None, + request_extended_authorization: None, + extended_authorization_applied: None, + capture_before: None, card_discovery: None, }, additional_pm_data, @@ -1513,6 +1516,7 @@ impl PaymentCreate { shipping_cost: request.shipping_cost, tax_details, skip_external_tax_calculation, + request_extended_authorization: None, psd2_sca_exemption_type: request.psd2_sca_exemption_type, platform_merchant_id: platform_merchant_account .map(|platform_merchant_account| platform_merchant_account.get_id().to_owned()), diff --git a/crates/router/src/core/payments/retry.rs b/crates/router/src/core/payments/retry.rs index 1a0a7ad9e5..d113cfd55d 100644 --- a/crates/router/src/core/payments/retry.rs +++ b/crates/router/src/core/payments/retry.rs @@ -653,6 +653,9 @@ pub fn make_new_payment_attempt( fingerprint_id: Default::default(), customer_acceptance: Default::default(), connector_mandate_detail: Default::default(), + request_extended_authorization: Default::default(), + extended_authorization_applied: Default::default(), + capture_before: Default::default(), card_discovery: old_payment_attempt.card_discovery, } } diff --git a/crates/router/src/core/payments/transformers.rs b/crates/router/src/core/payments/transformers.rs index 49ff8eb35d..5f39ad7135 100644 --- a/crates/router/src/core/payments/transformers.rs +++ b/crates/router/src/core/payments/transformers.rs @@ -2508,6 +2508,8 @@ where order_tax_amount, connector_mandate_id, shipping_cost: payment_intent.shipping_cost, + capture_before: payment_attempt.capture_before, + extended_authorization_applied: payment_attempt.extended_authorization_applied, card_discovery: payment_attempt.card_discovery, }; @@ -2762,6 +2764,8 @@ impl ForeignFrom<(storage::PaymentIntent, storage::PaymentAttempt)> for api::Pay updated: None, split_payments: None, frm_metadata: None, + capture_before: pa.capture_before, + extended_authorization_applied: pa.extended_authorization_applied, order_tax_amount: None, connector_mandate_id:None, shipping_cost: None, diff --git a/crates/router/src/types/api/admin.rs b/crates/router/src/types/api/admin.rs index 28533e33e1..289b56a780 100644 --- a/crates/router/src/types/api/admin.rs +++ b/crates/router/src/types/api/admin.rs @@ -176,6 +176,7 @@ impl ForeignTryFrom for ProfileResponse { is_network_tokenization_enabled: item.is_network_tokenization_enabled, is_auto_retries_enabled: item.is_auto_retries_enabled, max_auto_retries_enabled: item.max_auto_retries_enabled, + always_request_extended_authorization: item.always_request_extended_authorization, is_click_to_pay_enabled: item.is_click_to_pay_enabled, authentication_product_ids: item.authentication_product_ids, }) @@ -375,6 +376,7 @@ pub async fn create_profile_from_merchant_account( is_network_tokenization_enabled: request.is_network_tokenization_enabled, is_auto_retries_enabled: request.is_auto_retries_enabled.unwrap_or_default(), max_auto_retries_enabled: request.max_auto_retries_enabled.map(i16::from), + always_request_extended_authorization: request.always_request_extended_authorization, is_click_to_pay_enabled: request.is_click_to_pay_enabled, authentication_product_ids: request.authentication_product_ids, })) diff --git a/crates/router/src/types/storage/payment_attempt.rs b/crates/router/src/types/storage/payment_attempt.rs index c82a921fe1..ce6c1e92bd 100644 --- a/crates/router/src/types/storage/payment_attempt.rs +++ b/crates/router/src/types/storage/payment_attempt.rs @@ -218,6 +218,9 @@ mod tests { profile_id: common_utils::generate_profile_id_of_default_length(), organization_id: Default::default(), connector_mandate_detail: Default::default(), + request_extended_authorization: Default::default(), + extended_authorization_applied: Default::default(), + capture_before: Default::default(), card_discovery: Default::default(), }; @@ -302,6 +305,9 @@ mod tests { profile_id: common_utils::generate_profile_id_of_default_length(), organization_id: Default::default(), connector_mandate_detail: Default::default(), + request_extended_authorization: Default::default(), + extended_authorization_applied: Default::default(), + capture_before: Default::default(), card_discovery: Default::default(), }; let store = state @@ -399,6 +405,9 @@ mod tests { profile_id: common_utils::generate_profile_id_of_default_length(), organization_id: Default::default(), connector_mandate_detail: Default::default(), + request_extended_authorization: Default::default(), + extended_authorization_applied: Default::default(), + capture_before: Default::default(), card_discovery: Default::default(), }; let store = state diff --git a/crates/router/src/utils/user/sample_data.rs b/crates/router/src/utils/user/sample_data.rs index e189deb948..8217f9e34c 100644 --- a/crates/router/src/utils/user/sample_data.rs +++ b/crates/router/src/utils/user/sample_data.rs @@ -274,6 +274,7 @@ pub async fn generate_sample_data( shipping_cost: None, tax_details: None, skip_external_tax_calculation: None, + request_extended_authorization: None, psd2_sca_exemption_type: None, platform_merchant_id: None, }; @@ -361,6 +362,9 @@ pub async fn generate_sample_data( order_tax_amount: None, processor_transaction_data, connector_mandate_detail: None, + request_extended_authorization: None, + extended_authorization_applied: None, + capture_before: None, card_discovery: None, }; diff --git a/crates/router/tests/payments.rs b/crates/router/tests/payments.rs index 82871fcfe2..94dcba750a 100644 --- a/crates/router/tests/payments.rs +++ b/crates/router/tests/payments.rs @@ -448,6 +448,8 @@ async fn payments_create_core() { split_payments: None, frm_metadata: None, merchant_order_reference_id: None, + capture_before: None, + extended_authorization_applied: None, order_tax_amount: None, connector_mandate_id: None, shipping_cost: None, @@ -713,6 +715,8 @@ async fn payments_create_core_adyen_no_redirect() { split_payments: None, frm_metadata: None, merchant_order_reference_id: None, + capture_before: None, + extended_authorization_applied: None, order_tax_amount: None, connector_mandate_id: None, shipping_cost: None, diff --git a/crates/router/tests/payments2.rs b/crates/router/tests/payments2.rs index 63df5a2dec..66cd41dbc7 100644 --- a/crates/router/tests/payments2.rs +++ b/crates/router/tests/payments2.rs @@ -209,6 +209,8 @@ async fn payments_create_core() { split_payments: None, frm_metadata: None, merchant_order_reference_id: None, + capture_before: None, + extended_authorization_applied: None, order_tax_amount: None, connector_mandate_id: None, shipping_cost: None, @@ -483,6 +485,8 @@ async fn payments_create_core_adyen_no_redirect() { split_payments: None, frm_metadata: None, merchant_order_reference_id: None, + capture_before: None, + extended_authorization_applied: None, order_tax_amount: None, connector_mandate_id: None, shipping_cost: None, diff --git a/crates/storage_impl/src/mock_db/payment_attempt.rs b/crates/storage_impl/src/mock_db/payment_attempt.rs index c9d250d306..a44624203b 100644 --- a/crates/storage_impl/src/mock_db/payment_attempt.rs +++ b/crates/storage_impl/src/mock_db/payment_attempt.rs @@ -208,6 +208,9 @@ impl PaymentAttemptInterface for MockDb { organization_id: payment_attempt.organization_id, profile_id: payment_attempt.profile_id, connector_mandate_detail: payment_attempt.connector_mandate_detail, + request_extended_authorization: payment_attempt.request_extended_authorization, + extended_authorization_applied: payment_attempt.extended_authorization_applied, + capture_before: payment_attempt.capture_before, card_discovery: payment_attempt.card_discovery, charges: None, }; diff --git a/crates/storage_impl/src/payments/payment_attempt.rs b/crates/storage_impl/src/payments/payment_attempt.rs index 7fb1d3ab80..98569ea69a 100644 --- a/crates/storage_impl/src/payments/payment_attempt.rs +++ b/crates/storage_impl/src/payments/payment_attempt.rs @@ -603,6 +603,9 @@ impl PaymentAttemptInterface for KVRouterStore { organization_id: payment_attempt.organization_id.clone(), profile_id: payment_attempt.profile_id.clone(), connector_mandate_detail: payment_attempt.connector_mandate_detail.clone(), + request_extended_authorization: payment_attempt.request_extended_authorization, + extended_authorization_applied: payment_attempt.extended_authorization_applied, + capture_before: payment_attempt.capture_before, card_discovery: payment_attempt.card_discovery, charges: None, }; @@ -1572,6 +1575,9 @@ impl DataModelExt for PaymentAttempt { shipping_cost: self.net_amount.get_shipping_cost(), order_tax_amount: self.net_amount.get_order_tax_amount(), connector_mandate_detail: self.connector_mandate_detail, + request_extended_authorization: self.request_extended_authorization, + extended_authorization_applied: self.extended_authorization_applied, + capture_before: self.capture_before, processor_transaction_data, card_discovery: self.card_discovery, charges: self.charges, @@ -1653,6 +1659,9 @@ impl DataModelExt for PaymentAttempt { organization_id: storage_model.organization_id, profile_id: storage_model.profile_id, connector_mandate_detail: storage_model.connector_mandate_detail, + request_extended_authorization: storage_model.request_extended_authorization, + extended_authorization_applied: storage_model.extended_authorization_applied, + capture_before: storage_model.capture_before, card_discovery: storage_model.card_discovery, charges: storage_model.charges, } @@ -1737,6 +1746,9 @@ impl DataModelExt for PaymentAttemptNew { shipping_cost: self.net_amount.get_shipping_cost(), order_tax_amount: self.net_amount.get_order_tax_amount(), connector_mandate_detail: self.connector_mandate_detail, + request_extended_authorization: self.request_extended_authorization, + extended_authorization_applied: self.extended_authorization_applied, + capture_before: self.capture_before, card_discovery: self.card_discovery, } } @@ -1809,6 +1821,9 @@ impl DataModelExt for PaymentAttemptNew { organization_id: storage_model.organization_id, profile_id: storage_model.profile_id, connector_mandate_detail: storage_model.connector_mandate_detail, + request_extended_authorization: storage_model.request_extended_authorization, + extended_authorization_applied: storage_model.extended_authorization_applied, + capture_before: storage_model.capture_before, card_discovery: storage_model.card_discovery, } } diff --git a/migrations/2024-11-13-090548_add-extended-authorization-related-fields/down.sql b/migrations/2024-11-13-090548_add-extended-authorization-related-fields/down.sql new file mode 100644 index 0000000000..a5e982f8ac --- /dev/null +++ b/migrations/2024-11-13-090548_add-extended-authorization-related-fields/down.sql @@ -0,0 +1,16 @@ +-- Remove the 'request_extended_authorization' column from the 'payment_intent' table +ALTER TABLE payment_intent +DROP COLUMN request_extended_authorization; + +-- Remove the 'request_extended_authorization' and 'extended_authorization_applied' columns from the 'payment_attempt' table +ALTER TABLE payment_attempt +DROP COLUMN request_extended_authorization, +DROP COLUMN extended_authorization_applied; + +-- Remove the 'capture_before' column from the 'payment_attempt' table +ALTER TABLE payment_attempt +DROP COLUMN capture_before; + +-- Remove the 'always_request_extended_authorization' column from the 'business_profile' table +ALTER TABLE business_profile +DROP COLUMN always_request_extended_authorization; \ No newline at end of file diff --git a/migrations/2024-11-13-090548_add-extended-authorization-related-fields/up.sql b/migrations/2024-11-13-090548_add-extended-authorization-related-fields/up.sql new file mode 100644 index 0000000000..ab5eac1941 --- /dev/null +++ b/migrations/2024-11-13-090548_add-extended-authorization-related-fields/up.sql @@ -0,0 +1,21 @@ +-- stores the flag send by the merchant during payments-create call +ALTER TABLE payment_intent +ADD COLUMN request_extended_authorization boolean; + + +ALTER TABLE payment_attempt +-- stores the flag sent to the connector +ADD COLUMN request_extended_authorization boolean; + +ALTER TABLE payment_attempt +-- Set to true if extended authentication request was successfully processed by the connector +ADD COLUMN extended_authorization_applied boolean; + + +ALTER TABLE payment_attempt +-- stores the flag sent to the connector +ADD COLUMN capture_before timestamp; + +ALTER TABLE business_profile +-- merchant can configure the default value for request_extended_authorization here +ADD COLUMN always_request_extended_authorization boolean;