mirror of
https://github.com/juspay/hyperswitch.git
synced 2026-03-13 09:02:06 +08:00
refactor(core): move authentication data fields to authentication table (#4093)
Co-authored-by: hyperswitch-bot[bot] <148525504+hyperswitch-bot[bot]@users.noreply.github.com> Co-authored-by: sai-harsha-vardhan <harsha111hero@gmail.com> Co-authored-by: Sai Harsha Vardhan <56996463+sai-harsha-vardhan@users.noreply.github.com>
This commit is contained in:
869
Cargo.lock
generated
869
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
@@ -2307,7 +2307,7 @@ pub enum ThreeDsMethodData {
|
||||
/// Whether ThreeDS method data submission is required
|
||||
three_ds_method_data_submission: bool,
|
||||
/// ThreeDS method data
|
||||
three_ds_method_data: String,
|
||||
three_ds_method_data: Option<String>,
|
||||
/// ThreeDS method url
|
||||
three_ds_method_url: Option<String>,
|
||||
},
|
||||
@@ -3637,40 +3637,12 @@ pub struct SdkInformation {
|
||||
pub sdk_max_timeout: u8,
|
||||
}
|
||||
|
||||
#[derive(Clone, Default, Debug, serde::Serialize, serde::Deserialize, Eq, PartialEq, ToSchema)]
|
||||
pub enum TransactionStatus {
|
||||
/// Authentication/ Account Verification Successful
|
||||
#[serde(rename = "Y")]
|
||||
Success,
|
||||
/// Not Authenticated /Account Not Verified; Transaction denied
|
||||
#[default]
|
||||
#[serde(rename = "N")]
|
||||
Failure,
|
||||
/// Authentication/ Account Verification Could Not Be Performed; Technical or other problem, as indicated in Authentication Response(ARes) or Result Request (RReq)
|
||||
#[serde(rename = "U")]
|
||||
VerificationNotPerformed,
|
||||
/// Attempts Processing Performed; Not Authenticated/Verified , but a proof of attempted authentication/verification is provided
|
||||
#[serde(rename = "A")]
|
||||
NotVerified,
|
||||
/// Authentication/ Account Verification Rejected; Issuer is rejecting authentication/verification and request that authorisation not be attempted.
|
||||
#[serde(rename = "R")]
|
||||
Rejected,
|
||||
/// Challenge Required; Additional authentication is required using the Challenge Request (CReq) / Challenge Response (CRes)
|
||||
#[serde(rename = "C")]
|
||||
ChallengeRequired,
|
||||
/// Challenge Required; Decoupled Authentication confirmed.
|
||||
#[serde(rename = "D")]
|
||||
ChallengeRequiredDecoupledAuthentication,
|
||||
/// Informational Only; 3DS Requestor challenge preference acknowledged.
|
||||
#[serde(rename = "I")]
|
||||
InformationOnly,
|
||||
}
|
||||
|
||||
#[derive(Debug, serde::Serialize, serde::Deserialize, Clone, ToSchema)]
|
||||
pub struct PaymentsExternalAuthenticationResponse {
|
||||
/// Indicates the trans status
|
||||
#[serde(rename = "trans_status")]
|
||||
pub transaction_status: TransactionStatus,
|
||||
#[schema(value_type = TransactionStatus)]
|
||||
pub transaction_status: common_enums::TransactionStatus,
|
||||
/// Access Server URL to be used for challenge submission
|
||||
pub acs_url: Option<String>,
|
||||
/// Challenge request which should be sent to acs_url
|
||||
|
||||
@@ -2363,6 +2363,47 @@ pub enum RoleScope {
|
||||
Organization,
|
||||
}
|
||||
|
||||
#[derive(
|
||||
Clone,
|
||||
Default,
|
||||
Debug,
|
||||
serde::Serialize,
|
||||
serde::Deserialize,
|
||||
Eq,
|
||||
PartialEq,
|
||||
ToSchema,
|
||||
strum::Display,
|
||||
strum::EnumString,
|
||||
)]
|
||||
#[router_derive::diesel_enum(storage_type = "text")]
|
||||
pub enum TransactionStatus {
|
||||
/// Authentication/ Account Verification Successful
|
||||
#[serde(rename = "Y")]
|
||||
Success,
|
||||
/// Not Authenticated /Account Not Verified; Transaction denied
|
||||
#[default]
|
||||
#[serde(rename = "N")]
|
||||
Failure,
|
||||
/// Authentication/ Account Verification Could Not Be Performed; Technical or other problem, as indicated in Authentication Response(ARes) or Result Request (RReq)
|
||||
#[serde(rename = "U")]
|
||||
VerificationNotPerformed,
|
||||
/// Attempts Processing Performed; Not Authenticated/Verified , but a proof of attempted authentication/verification is provided
|
||||
#[serde(rename = "A")]
|
||||
NotVerified,
|
||||
/// Authentication/ Account Verification Rejected; Issuer is rejecting authentication/verification and request that authorisation not be attempted.
|
||||
#[serde(rename = "R")]
|
||||
Rejected,
|
||||
/// Challenge Required; Additional authentication is required using the Challenge Request (CReq) / Challenge Response (CRes)
|
||||
#[serde(rename = "C")]
|
||||
ChallengeRequired,
|
||||
/// Challenge Required; Decoupled Authentication confirmed.
|
||||
#[serde(rename = "D")]
|
||||
ChallengeRequiredDecoupledAuthentication,
|
||||
/// Informational Only; 3DS Requestor challenge preference acknowledged.
|
||||
#[serde(rename = "I")]
|
||||
InformationOnly,
|
||||
}
|
||||
|
||||
#[derive(
|
||||
Clone,
|
||||
Copy,
|
||||
|
||||
@@ -38,6 +38,7 @@ strum = { version = "0.24.1", features = ["derive"] }
|
||||
thiserror = "1.0.40"
|
||||
time = { version = "0.3.21", features = ["serde", "serde-well-known", "std"] }
|
||||
tokio = { version = "1.36.0", features = ["macros", "rt-multi-thread"], optional = true }
|
||||
semver = { version = "1.0.22", features = ["serde"] }
|
||||
uuid = { version = "1.7.0", features = ["v7"] }
|
||||
|
||||
# First party crates
|
||||
|
||||
@@ -1,12 +1,21 @@
|
||||
//! Types that can be used in other crates
|
||||
use std::{fmt::Display, str::FromStr};
|
||||
|
||||
use diesel::{
|
||||
backend::Backend,
|
||||
deserialize::FromSql,
|
||||
serialize::{Output, ToSql},
|
||||
sql_types::Jsonb,
|
||||
AsExpression, FromSqlRow,
|
||||
};
|
||||
use error_stack::{IntoReport, ResultExt};
|
||||
use semver::Version;
|
||||
use serde::{de::Visitor, Deserialize, Deserializer};
|
||||
|
||||
use crate::{
|
||||
consts,
|
||||
errors::{CustomResult, PercentageError},
|
||||
errors::{CustomResult, ParsingError, PercentageError},
|
||||
};
|
||||
|
||||
/// Represents Percentage Value between 0 and 100 both inclusive
|
||||
#[derive(Clone, Default, Debug, PartialEq, serde::Serialize)]
|
||||
pub struct Percentage<const PRECISION: u8> {
|
||||
@@ -149,3 +158,56 @@ pub enum Surcharge {
|
||||
/// Surcharge percentage
|
||||
Rate(Percentage<{ consts::SURCHARGE_PERCENTAGE_PRECISION_LENGTH }>),
|
||||
}
|
||||
|
||||
/// This struct lets us represent a semantic version type
|
||||
#[derive(Debug, Clone, PartialEq, Eq, FromSqlRow, AsExpression)]
|
||||
#[diesel(sql_type = Jsonb)]
|
||||
#[derive(serde::Serialize, serde::Deserialize)]
|
||||
pub struct SemanticVersion(#[serde(with = "Version")] Version);
|
||||
|
||||
impl SemanticVersion {
|
||||
/// returns major version number
|
||||
pub fn get_major(&self) -> u64 {
|
||||
self.0.major
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for SemanticVersion {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "{}", self.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl FromStr for SemanticVersion {
|
||||
type Err = error_stack::Report<ParsingError>;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
Ok(Self(Version::from_str(s).into_report().change_context(
|
||||
ParsingError::StructParseFailure("SemanticVersion"),
|
||||
)?))
|
||||
}
|
||||
}
|
||||
|
||||
impl<DB: Backend> FromSql<Jsonb, DB> for SemanticVersion
|
||||
where
|
||||
serde_json::Value: FromSql<Jsonb, DB>,
|
||||
{
|
||||
fn from_sql(bytes: DB::RawValue<'_>) -> diesel::deserialize::Result<Self> {
|
||||
let value = <serde_json::Value as FromSql<Jsonb, DB>>::from_sql(bytes)?;
|
||||
Ok(serde_json::from_value(value)?)
|
||||
}
|
||||
}
|
||||
|
||||
impl ToSql<Jsonb, diesel::pg::Pg> for SemanticVersion
|
||||
where
|
||||
serde_json::Value: ToSql<Jsonb, diesel::pg::Pg>,
|
||||
{
|
||||
fn to_sql<'b>(&'b self, out: &mut Output<'b, '_, diesel::pg::Pg>) -> diesel::serialize::Result {
|
||||
let value = serde_json::to_value(self)?;
|
||||
|
||||
// the function `reborrow` only works in case of `Pg` backend. But, in case of other backends
|
||||
// please refer to the diesel migration blog:
|
||||
// https://github.com/Diesel-rs/Diesel/blob/master/guide_drafts/migration_guide.md#changed-tosql-implementations
|
||||
<serde_json::Value as ToSql<Jsonb, diesel::pg::Pg>>::to_sql(&value, &mut out.reborrow())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,6 +23,31 @@ pub struct Authentication {
|
||||
pub error_message: Option<String>,
|
||||
pub error_code: Option<String>,
|
||||
pub connector_metadata: Option<serde_json::Value>,
|
||||
pub maximum_supported_version: Option<common_utils::types::SemanticVersion>,
|
||||
pub threeds_server_transaction_id: Option<String>,
|
||||
pub cavv: Option<String>,
|
||||
pub authentication_flow_type: Option<String>,
|
||||
pub message_version: Option<common_utils::types::SemanticVersion>,
|
||||
pub eci: Option<String>,
|
||||
pub trans_status: Option<common_enums::TransactionStatus>,
|
||||
pub acquirer_bin: Option<String>,
|
||||
pub acquirer_merchant_id: Option<String>,
|
||||
pub three_ds_method_data: Option<String>,
|
||||
pub three_ds_method_url: Option<String>,
|
||||
pub acs_url: Option<String>,
|
||||
pub challenge_request: Option<String>,
|
||||
pub acs_reference_number: Option<String>,
|
||||
pub acs_trans_id: Option<String>,
|
||||
pub three_ds_server_trans_id: Option<String>,
|
||||
pub acs_signed_content: Option<String>,
|
||||
}
|
||||
|
||||
impl Authentication {
|
||||
pub fn is_separate_authn_required(&self) -> bool {
|
||||
self.maximum_supported_version
|
||||
.as_ref()
|
||||
.is_some_and(|version| version.get_major() == 2)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize, Insertable)]
|
||||
@@ -32,7 +57,7 @@ pub struct AuthenticationNew {
|
||||
pub merchant_id: String,
|
||||
pub authentication_connector: String,
|
||||
pub connector_authentication_id: Option<String>,
|
||||
pub authentication_data: Option<serde_json::Value>,
|
||||
// pub authentication_data: Option<serde_json::Value>,
|
||||
pub payment_method_id: String,
|
||||
pub authentication_type: Option<common_enums::DecoupledAuthenticationType>,
|
||||
pub authentication_status: common_enums::AuthenticationStatus,
|
||||
@@ -40,21 +65,56 @@ pub struct AuthenticationNew {
|
||||
pub error_message: Option<String>,
|
||||
pub error_code: Option<String>,
|
||||
pub connector_metadata: Option<serde_json::Value>,
|
||||
pub maximum_supported_version: Option<common_utils::types::SemanticVersion>,
|
||||
pub threeds_server_transaction_id: Option<String>,
|
||||
pub cavv: Option<String>,
|
||||
pub authentication_flow_type: Option<String>,
|
||||
pub message_version: Option<common_utils::types::SemanticVersion>,
|
||||
pub eci: Option<String>,
|
||||
pub trans_status: Option<common_enums::TransactionStatus>,
|
||||
pub acquirer_bin: Option<String>,
|
||||
pub acquirer_merchant_id: Option<String>,
|
||||
pub three_ds_method_data: Option<String>,
|
||||
pub three_ds_method_url: Option<String>,
|
||||
pub acs_url: Option<String>,
|
||||
pub challenge_request: Option<String>,
|
||||
pub acs_reference_number: Option<String>,
|
||||
pub acs_trans_id: Option<String>,
|
||||
pub three_dsserver_trans_id: Option<String>,
|
||||
pub acs_signed_content: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum AuthenticationUpdate {
|
||||
AuthenticationDataUpdate {
|
||||
authentication_data: Option<serde_json::Value>,
|
||||
connector_authentication_id: Option<String>,
|
||||
payment_method_id: Option<String>,
|
||||
authentication_type: Option<common_enums::DecoupledAuthenticationType>,
|
||||
authentication_status: Option<common_enums::AuthenticationStatus>,
|
||||
authentication_lifecycle_status: Option<common_enums::AuthenticationLifecycleStatus>,
|
||||
PreAuthenticationUpdate {
|
||||
threeds_server_transaction_id: String,
|
||||
maximum_supported_3ds_version: common_utils::types::SemanticVersion,
|
||||
connector_authentication_id: String,
|
||||
three_ds_method_data: String,
|
||||
three_ds_method_url: Option<String>,
|
||||
message_version: common_utils::types::SemanticVersion,
|
||||
connector_metadata: Option<serde_json::Value>,
|
||||
authentication_status: common_enums::AuthenticationStatus,
|
||||
payment_method_id: Option<String>,
|
||||
acquirer_bin: Option<String>,
|
||||
acquirer_merchant_id: Option<String>,
|
||||
},
|
||||
PostAuthorizationUpdate {
|
||||
authentication_lifecycle_status: common_enums::AuthenticationLifecycleStatus,
|
||||
AuthenticationUpdate {
|
||||
authentication_value: Option<String>,
|
||||
trans_status: common_enums::TransactionStatus,
|
||||
authentication_type: common_enums::DecoupledAuthenticationType,
|
||||
acs_url: Option<String>,
|
||||
challenge_request: Option<String>,
|
||||
acs_reference_number: Option<String>,
|
||||
acs_trans_id: Option<String>,
|
||||
acs_signed_content: Option<String>,
|
||||
authentication_status: common_enums::AuthenticationStatus,
|
||||
},
|
||||
PostAuthenticationUpdate {
|
||||
trans_status: common_enums::TransactionStatus,
|
||||
authentication_value: Option<String>,
|
||||
eci: Option<String>,
|
||||
authentication_status: common_enums::AuthenticationStatus,
|
||||
},
|
||||
ErrorUpdate {
|
||||
error_message: Option<String>,
|
||||
@@ -62,13 +122,16 @@ pub enum AuthenticationUpdate {
|
||||
authentication_status: common_enums::AuthenticationStatus,
|
||||
connector_authentication_id: Option<String>,
|
||||
},
|
||||
PostAuthorizationUpdate {
|
||||
authentication_lifecycle_status: common_enums::AuthenticationLifecycleStatus,
|
||||
},
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Eq, PartialEq, AsChangeset, Serialize, Deserialize)]
|
||||
#[diesel(table_name = authentication)]
|
||||
pub struct AuthenticationUpdateInternal {
|
||||
pub connector_authentication_id: Option<String>,
|
||||
pub authentication_data: Option<serde_json::Value>,
|
||||
// pub authentication_data: Option<serde_json::Value>,
|
||||
pub payment_method_id: Option<String>,
|
||||
pub authentication_type: Option<common_enums::DecoupledAuthenticationType>,
|
||||
pub authentication_status: Option<common_enums::AuthenticationStatus>,
|
||||
@@ -77,13 +140,62 @@ pub struct AuthenticationUpdateInternal {
|
||||
pub error_message: Option<String>,
|
||||
pub error_code: Option<String>,
|
||||
pub connector_metadata: Option<serde_json::Value>,
|
||||
pub maximum_supported_version: Option<common_utils::types::SemanticVersion>,
|
||||
pub threeds_server_transaction_id: Option<String>,
|
||||
pub cavv: Option<String>,
|
||||
pub authentication_flow_type: Option<String>,
|
||||
pub message_version: Option<common_utils::types::SemanticVersion>,
|
||||
pub eci: Option<String>,
|
||||
pub trans_status: Option<common_enums::TransactionStatus>,
|
||||
pub acquirer_bin: Option<String>,
|
||||
pub acquirer_merchant_id: Option<String>,
|
||||
pub three_ds_method_data: Option<String>,
|
||||
pub three_ds_method_url: Option<String>,
|
||||
pub acs_url: Option<String>,
|
||||
pub challenge_request: Option<String>,
|
||||
pub acs_reference_number: Option<String>,
|
||||
pub acs_trans_id: Option<String>,
|
||||
pub three_dsserver_trans_id: Option<String>,
|
||||
pub acs_signed_content: Option<String>,
|
||||
}
|
||||
|
||||
impl Default for AuthenticationUpdateInternal {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
connector_authentication_id: Default::default(),
|
||||
payment_method_id: Default::default(),
|
||||
authentication_type: Default::default(),
|
||||
authentication_status: Default::default(),
|
||||
authentication_lifecycle_status: Default::default(),
|
||||
modified_at: common_utils::date_time::now(),
|
||||
error_message: Default::default(),
|
||||
error_code: Default::default(),
|
||||
connector_metadata: Default::default(),
|
||||
maximum_supported_version: Default::default(),
|
||||
threeds_server_transaction_id: Default::default(),
|
||||
cavv: Default::default(),
|
||||
authentication_flow_type: Default::default(),
|
||||
message_version: Default::default(),
|
||||
eci: Default::default(),
|
||||
trans_status: Default::default(),
|
||||
acquirer_bin: Default::default(),
|
||||
acquirer_merchant_id: Default::default(),
|
||||
three_ds_method_data: Default::default(),
|
||||
three_ds_method_url: Default::default(),
|
||||
acs_url: Default::default(),
|
||||
challenge_request: Default::default(),
|
||||
acs_reference_number: Default::default(),
|
||||
acs_trans_id: Default::default(),
|
||||
three_dsserver_trans_id: Default::default(),
|
||||
acs_signed_content: Default::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl AuthenticationUpdateInternal {
|
||||
pub fn apply_changeset(self, source: Authentication) -> Authentication {
|
||||
let Self {
|
||||
connector_authentication_id,
|
||||
authentication_data,
|
||||
payment_method_id,
|
||||
authentication_type,
|
||||
authentication_status,
|
||||
@@ -92,11 +204,27 @@ impl AuthenticationUpdateInternal {
|
||||
error_code,
|
||||
error_message,
|
||||
connector_metadata,
|
||||
maximum_supported_version,
|
||||
threeds_server_transaction_id,
|
||||
cavv,
|
||||
authentication_flow_type,
|
||||
message_version,
|
||||
eci,
|
||||
trans_status,
|
||||
acquirer_bin,
|
||||
acquirer_merchant_id,
|
||||
three_ds_method_data,
|
||||
three_ds_method_url,
|
||||
acs_url,
|
||||
challenge_request,
|
||||
acs_reference_number,
|
||||
acs_trans_id,
|
||||
three_dsserver_trans_id,
|
||||
acs_signed_content,
|
||||
} = self;
|
||||
Authentication {
|
||||
connector_authentication_id: connector_authentication_id
|
||||
.or(source.connector_authentication_id),
|
||||
authentication_data: authentication_data.or(source.authentication_data),
|
||||
payment_method_id: payment_method_id.unwrap_or(source.payment_method_id),
|
||||
authentication_type: authentication_type.or(source.authentication_type),
|
||||
authentication_status: authentication_status.unwrap_or(source.authentication_status),
|
||||
@@ -106,6 +234,25 @@ impl AuthenticationUpdateInternal {
|
||||
error_code: error_code.or(source.error_code),
|
||||
error_message: error_message.or(source.error_message),
|
||||
connector_metadata: connector_metadata.or(source.connector_metadata),
|
||||
maximum_supported_version: maximum_supported_version
|
||||
.or(source.maximum_supported_version),
|
||||
threeds_server_transaction_id: threeds_server_transaction_id
|
||||
.or(source.threeds_server_transaction_id),
|
||||
cavv: cavv.or(source.cavv),
|
||||
authentication_flow_type: authentication_flow_type.or(source.authentication_flow_type),
|
||||
message_version: message_version.or(source.message_version),
|
||||
eci: eci.or(source.eci),
|
||||
trans_status: trans_status.or(source.trans_status),
|
||||
acquirer_bin: acquirer_bin.or(source.acquirer_bin),
|
||||
acquirer_merchant_id: acquirer_merchant_id.or(source.acquirer_merchant_id),
|
||||
three_ds_method_data: three_ds_method_data.or(source.three_ds_method_data),
|
||||
three_ds_method_url: three_ds_method_url.or(source.three_ds_method_url),
|
||||
acs_url: acs_url.or(source.acs_url),
|
||||
challenge_request: challenge_request.or(source.challenge_request),
|
||||
acs_reference_number: acs_reference_number.or(source.acs_reference_number),
|
||||
acs_trans_id: acs_trans_id.or(source.acs_trans_id),
|
||||
three_ds_server_trans_id: three_dsserver_trans_id.or(source.three_ds_server_trans_id),
|
||||
acs_signed_content: acs_signed_content.or(source.acs_signed_content),
|
||||
..source
|
||||
}
|
||||
}
|
||||
@@ -114,26 +261,6 @@ impl AuthenticationUpdateInternal {
|
||||
impl From<AuthenticationUpdate> for AuthenticationUpdateInternal {
|
||||
fn from(auth_update: AuthenticationUpdate) -> Self {
|
||||
match auth_update {
|
||||
AuthenticationUpdate::AuthenticationDataUpdate {
|
||||
authentication_data,
|
||||
connector_authentication_id,
|
||||
authentication_type,
|
||||
authentication_status,
|
||||
payment_method_id,
|
||||
authentication_lifecycle_status,
|
||||
connector_metadata,
|
||||
} => Self {
|
||||
authentication_data,
|
||||
connector_authentication_id,
|
||||
authentication_type,
|
||||
authentication_status,
|
||||
authentication_lifecycle_status,
|
||||
modified_at: common_utils::date_time::now(),
|
||||
payment_method_id,
|
||||
error_message: None,
|
||||
error_code: None,
|
||||
connector_metadata,
|
||||
},
|
||||
AuthenticationUpdate::ErrorUpdate {
|
||||
error_message,
|
||||
error_code,
|
||||
@@ -143,19 +270,20 @@ impl From<AuthenticationUpdate> for AuthenticationUpdateInternal {
|
||||
error_code,
|
||||
error_message,
|
||||
authentication_status: Some(authentication_status),
|
||||
authentication_data: None,
|
||||
|
||||
connector_authentication_id,
|
||||
authentication_type: None,
|
||||
authentication_lifecycle_status: None,
|
||||
modified_at: common_utils::date_time::now(),
|
||||
payment_method_id: None,
|
||||
connector_metadata: None,
|
||||
..Default::default()
|
||||
},
|
||||
AuthenticationUpdate::PostAuthorizationUpdate {
|
||||
authentication_lifecycle_status,
|
||||
} => Self {
|
||||
connector_authentication_id: None,
|
||||
authentication_data: None,
|
||||
|
||||
payment_method_id: None,
|
||||
authentication_type: None,
|
||||
authentication_status: None,
|
||||
@@ -164,6 +292,67 @@ impl From<AuthenticationUpdate> for AuthenticationUpdateInternal {
|
||||
error_message: None,
|
||||
error_code: None,
|
||||
connector_metadata: None,
|
||||
..Default::default()
|
||||
},
|
||||
AuthenticationUpdate::PreAuthenticationUpdate {
|
||||
threeds_server_transaction_id,
|
||||
maximum_supported_3ds_version,
|
||||
connector_authentication_id,
|
||||
three_ds_method_data,
|
||||
three_ds_method_url,
|
||||
message_version,
|
||||
connector_metadata,
|
||||
authentication_status,
|
||||
payment_method_id,
|
||||
acquirer_bin,
|
||||
acquirer_merchant_id,
|
||||
} => Self {
|
||||
threeds_server_transaction_id: Some(threeds_server_transaction_id),
|
||||
maximum_supported_version: Some(maximum_supported_3ds_version),
|
||||
connector_authentication_id: Some(connector_authentication_id),
|
||||
three_ds_method_data: Some(three_ds_method_data),
|
||||
three_ds_method_url,
|
||||
message_version: Some(message_version),
|
||||
connector_metadata,
|
||||
authentication_status: Some(authentication_status),
|
||||
payment_method_id,
|
||||
acquirer_bin,
|
||||
acquirer_merchant_id,
|
||||
..Default::default()
|
||||
},
|
||||
AuthenticationUpdate::AuthenticationUpdate {
|
||||
authentication_value,
|
||||
trans_status,
|
||||
authentication_type,
|
||||
acs_url,
|
||||
challenge_request,
|
||||
acs_reference_number,
|
||||
acs_trans_id,
|
||||
acs_signed_content,
|
||||
authentication_status,
|
||||
} => Self {
|
||||
cavv: authentication_value,
|
||||
trans_status: Some(trans_status),
|
||||
authentication_type: Some(authentication_type),
|
||||
acs_url,
|
||||
challenge_request,
|
||||
acs_reference_number,
|
||||
acs_trans_id,
|
||||
acs_signed_content,
|
||||
authentication_status: Some(authentication_status),
|
||||
..Default::default()
|
||||
},
|
||||
AuthenticationUpdate::PostAuthenticationUpdate {
|
||||
trans_status,
|
||||
authentication_value,
|
||||
eci,
|
||||
authentication_status,
|
||||
} => Self {
|
||||
trans_status: Some(trans_status),
|
||||
cavv: authentication_value,
|
||||
eci,
|
||||
authentication_status: Some(authentication_status),
|
||||
..Default::default()
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -87,6 +87,30 @@ diesel::table! {
|
||||
#[max_length = 64]
|
||||
error_code -> Nullable<Varchar>,
|
||||
connector_metadata -> Nullable<Jsonb>,
|
||||
maximum_supported_version -> Nullable<Jsonb>,
|
||||
#[max_length = 64]
|
||||
threeds_server_transaction_id -> Nullable<Varchar>,
|
||||
#[max_length = 64]
|
||||
cavv -> Nullable<Varchar>,
|
||||
#[max_length = 64]
|
||||
authentication_flow_type -> Nullable<Varchar>,
|
||||
message_version -> Nullable<Jsonb>,
|
||||
#[max_length = 64]
|
||||
eci -> Nullable<Varchar>,
|
||||
#[max_length = 64]
|
||||
trans_status -> Nullable<Varchar>,
|
||||
#[max_length = 64]
|
||||
acquirer_bin -> Nullable<Varchar>,
|
||||
#[max_length = 64]
|
||||
acquirer_merchant_id -> Nullable<Varchar>,
|
||||
three_ds_method_data -> Nullable<Varchar>,
|
||||
three_ds_method_url -> Nullable<Varchar>,
|
||||
acs_url -> Nullable<Varchar>,
|
||||
challenge_request -> Nullable<Varchar>,
|
||||
acs_reference_number -> Nullable<Varchar>,
|
||||
acs_trans_id -> Nullable<Varchar>,
|
||||
three_dsserver_trans_id -> Nullable<Varchar>,
|
||||
acs_signed_content -> Nullable<Varchar>,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -384,7 +384,7 @@ Never share your secret api keys. Keep them guarded and secure.
|
||||
api_models::payments::SdkInformation,
|
||||
api_models::payments::DeviceChannel,
|
||||
api_models::payments::ThreeDsCompletionIndicator,
|
||||
api_models::payments::TransactionStatus,
|
||||
api_models::enums::TransactionStatus,
|
||||
api_models::payments::BrowserInformation,
|
||||
api_models::payments::PaymentCreatePaymentLinkConfig,
|
||||
api_models::payments::ThreeDsData,
|
||||
|
||||
@@ -391,8 +391,8 @@ impl TryFrom<&CheckoutRouterData<&types::PaymentsAuthorizeRouterData>> for Payme
|
||||
enums::AuthenticationType::ThreeDs => CheckoutThreeDS {
|
||||
enabled: true,
|
||||
force_3ds: true,
|
||||
eci: authentication_data.and_then(|auth| auth.eci.clone()),
|
||||
cryptogram: authentication_data.and_then(|auth| auth.cavv.clone()),
|
||||
eci: authentication_data.map(|auth| auth.eci.clone()),
|
||||
cryptogram: authentication_data.map(|auth| auth.cavv.clone()),
|
||||
xid: authentication_data.map(|auth| auth.threeds_server_transaction_id.clone()),
|
||||
version: authentication_data.map(|auth| auth.message_version.clone()),
|
||||
},
|
||||
|
||||
@@ -457,11 +457,7 @@ impl
|
||||
_connectors: &settings::Connectors,
|
||||
) -> CustomResult<RequestContent, errors::ConnectorError> {
|
||||
let req_obj = threedsecureio::ThreedsecureioPostAuthenticationRequest {
|
||||
three_ds_server_trans_id: req
|
||||
.request
|
||||
.authentication_data
|
||||
.threeds_server_transaction_id
|
||||
.clone(),
|
||||
three_ds_server_trans_id: req.request.threeds_server_transaction_id.clone(),
|
||||
};
|
||||
Ok(RequestContent::Json(Box::new(req_obj)))
|
||||
}
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
use std::str::FromStr;
|
||||
|
||||
use api_models::payments::{DeviceChannel, ThreeDsCompletionIndicator};
|
||||
use base64::Engine;
|
||||
use common_utils::date_time;
|
||||
use error_stack::{report, IntoReport, ResultExt};
|
||||
use error_stack::{IntoReport, ResultExt};
|
||||
use iso_currency::Currency;
|
||||
use isocountry;
|
||||
use masking::{ExposeInterface, Secret};
|
||||
@@ -16,7 +18,6 @@ use crate::{
|
||||
self,
|
||||
api::{self, MessageCategory},
|
||||
authentication::ChallengeParams,
|
||||
transformers::ForeignTryFrom,
|
||||
},
|
||||
utils::OptionExt,
|
||||
};
|
||||
@@ -100,13 +101,18 @@ impl
|
||||
threeds_server_transaction_id: pre_authn_response
|
||||
.threeds_server_trans_id
|
||||
.clone(),
|
||||
maximum_supported_3ds_version: ForeignTryFrom::foreign_try_from(
|
||||
pre_authn_response.acs_end_protocol_version.clone(),
|
||||
)?,
|
||||
maximum_supported_3ds_version:
|
||||
common_utils::types::SemanticVersion::from_str(
|
||||
&pre_authn_response.acs_end_protocol_version,
|
||||
)
|
||||
.change_context(errors::ConnectorError::ParsingFailed)?,
|
||||
connector_authentication_id: pre_authn_response.threeds_server_trans_id,
|
||||
three_ds_method_data: three_ds_method_data_base64,
|
||||
three_ds_method_url: pre_authn_response.threeds_method_url,
|
||||
message_version: pre_authn_response.acs_end_protocol_version.clone(),
|
||||
message_version: common_utils::types::SemanticVersion::from_str(
|
||||
&pre_authn_response.acs_end_protocol_version,
|
||||
)
|
||||
.change_context(errors::ConnectorError::ParsingFailed)?,
|
||||
connector_metadata: Some(connector_metadata),
|
||||
},
|
||||
)
|
||||
@@ -307,7 +313,7 @@ impl TryFrom<&ThreedsecureioRouterData<&types::authentication::ConnectorAuthenti
|
||||
.parse_value("ThreeDSecureIoMetaData")
|
||||
.change_context(errors::ConnectorError::RequestEncodingFailed)?;
|
||||
|
||||
let authentication_data = &request.authentication_data.0;
|
||||
let pre_authentication_data = &request.pre_authentication_data;
|
||||
let sdk_information = match request.device_channel {
|
||||
DeviceChannel::App => Some(item.router_data.request.sdk_information.clone().ok_or(
|
||||
errors::ConnectorError::MissingRequiredField {
|
||||
@@ -316,21 +322,24 @@ impl TryFrom<&ThreedsecureioRouterData<&types::authentication::ConnectorAuthenti
|
||||
)?),
|
||||
DeviceChannel::Browser => None,
|
||||
};
|
||||
let acquirer_details = authentication_data
|
||||
.acquirer_details
|
||||
let (acquirer_bin, acquirer_merchant_id) = pre_authentication_data
|
||||
.acquirer_bin
|
||||
.clone()
|
||||
.zip(pre_authentication_data.acquirer_merchant_id.clone())
|
||||
.get_required_value("acquirer_details")
|
||||
.change_context(errors::ConnectorError::MissingRequiredField {
|
||||
field_name: "acquirer_details",
|
||||
})?;
|
||||
let meta: ThreeDSecureIoConnectorMetaData =
|
||||
to_connector_meta(request.authentication_data.1.connector_metadata.clone())?;
|
||||
to_connector_meta(request.pre_authentication_data.connector_metadata.clone())?;
|
||||
Ok(Self {
|
||||
ds_start_protocol_version: meta.ds_start_protocol_version.clone(),
|
||||
ds_end_protocol_version: meta.ds_end_protocol_version.clone(),
|
||||
acs_start_protocol_version: meta.acs_start_protocol_version.clone(),
|
||||
acs_end_protocol_version: meta.acs_end_protocol_version.clone(),
|
||||
three_dsserver_trans_id: authentication_data.threeds_server_transaction_id.clone(),
|
||||
three_dsserver_trans_id: pre_authentication_data
|
||||
.threeds_server_transaction_id
|
||||
.clone(),
|
||||
acct_number: card_details.card_number.clone(),
|
||||
notification_url: request
|
||||
.return_url
|
||||
@@ -342,8 +351,8 @@ impl TryFrom<&ThreedsecureioRouterData<&types::authentication::ConnectorAuthenti
|
||||
request.threeds_method_comp_ind.clone(),
|
||||
),
|
||||
three_dsrequestor_url: request.three_ds_requestor_url.clone(),
|
||||
acquirer_bin: acquirer_details.acquirer_bin,
|
||||
acquirer_merchant_id: acquirer_details.acquirer_merchant_id,
|
||||
acquirer_bin,
|
||||
acquirer_merchant_id,
|
||||
card_expiry_date: card_details.get_expiry_date_as_yymm()?.expose(),
|
||||
bill_addr_city: billing_address
|
||||
.city
|
||||
@@ -410,7 +419,7 @@ impl TryFrom<&ThreedsecureioRouterData<&types::authentication::ConnectorAuthenti
|
||||
merchant_country_code: connector_meta_data.merchant_country_code,
|
||||
merchant_name: connector_meta_data.merchant_name,
|
||||
message_type: "AReq".to_string(),
|
||||
message_version: authentication_data.message_version.clone(),
|
||||
message_version: pre_authentication_data.message_version.clone(),
|
||||
purchase_amount: item.amount.clone(),
|
||||
purchase_currency: purchase_currency.numeric().to_string(),
|
||||
trans_type: "01".to_string(),
|
||||
@@ -644,7 +653,7 @@ impl From<ThreeDsCompletionIndicator> for ThreeDSecureIoThreeDsCompletionIndicat
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ThreedsecureioTransStatus> for api_models::payments::TransactionStatus {
|
||||
impl From<ThreedsecureioTransStatus> for common_enums::TransactionStatus {
|
||||
fn from(value: ThreedsecureioTransStatus) -> Self {
|
||||
match value {
|
||||
ThreedsecureioTransStatus::Y => Self::Success,
|
||||
@@ -707,41 +716,3 @@ impl TryFrom<&ThreedsecureioRouterData<&types::authentication::PreAuthNRouterDat
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl ForeignTryFrom<String> for (i64, i64, i64) {
|
||||
type Error = error_stack::Report<errors::ConnectorError>;
|
||||
fn foreign_try_from(value: String) -> Result<Self, Self::Error> {
|
||||
let mut split_version = value.split('.');
|
||||
let version_string = {
|
||||
let major_version = split_version.next().ok_or(report!(
|
||||
errors::ConnectorError::ResponseDeserializationFailed
|
||||
))?;
|
||||
let minor_version = split_version.next().ok_or(report!(
|
||||
errors::ConnectorError::ResponseDeserializationFailed
|
||||
))?;
|
||||
let patch_version = split_version.next().ok_or(report!(
|
||||
errors::ConnectorError::ResponseDeserializationFailed
|
||||
))?;
|
||||
(major_version, minor_version, patch_version)
|
||||
};
|
||||
let int_representation = {
|
||||
let major_version = version_string
|
||||
.0
|
||||
.parse()
|
||||
.into_report()
|
||||
.change_context(errors::ConnectorError::ResponseDeserializationFailed)?;
|
||||
let minor_version = version_string
|
||||
.1
|
||||
.parse()
|
||||
.into_report()
|
||||
.change_context(errors::ConnectorError::ResponseDeserializationFailed)?;
|
||||
let patch_version = version_string
|
||||
.2
|
||||
.parse()
|
||||
.into_report()
|
||||
.change_context(errors::ConnectorError::ResponseDeserializationFailed)?;
|
||||
(major_version, minor_version, patch_version)
|
||||
};
|
||||
Ok(int_representation)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,7 +32,7 @@ pub async fn perform_authentication(
|
||||
currency: Option<Currency>,
|
||||
message_category: api::authentication::MessageCategory,
|
||||
device_channel: payments::DeviceChannel,
|
||||
authentication_data: (types::AuthenticationData, storage::Authentication),
|
||||
authentication_data: storage::Authentication,
|
||||
return_url: Option<String>,
|
||||
sdk_information: Option<payments::SdkInformation>,
|
||||
threeds_method_comp_ind: api_models::payments::ThreeDsCompletionIndicator,
|
||||
@@ -59,8 +59,7 @@ pub async fn perform_authentication(
|
||||
)?;
|
||||
let response =
|
||||
utils::do_auth_connector_call(state, authentication_connector.clone(), router_data).await?;
|
||||
let (_authentication, _authentication_data) =
|
||||
utils::update_trackers(state, response.clone(), authentication_data.1, None, None).await?;
|
||||
utils::update_trackers(state, response.clone(), authentication_data, None, None).await?;
|
||||
let authentication_response =
|
||||
response
|
||||
.response
|
||||
@@ -115,42 +114,37 @@ pub async fn perform_post_authentication<F: Clone + Send>(
|
||||
match authentication_flow_input {
|
||||
types::PostAuthenthenticationFlowInput::PaymentAuthNFlow {
|
||||
payment_data,
|
||||
authentication_data: (authentication, authentication_data),
|
||||
authentication,
|
||||
should_continue_confirm_transaction,
|
||||
} => {
|
||||
// let (auth, authentication_data) = authentication;
|
||||
let updated_authentication =
|
||||
let authentication_status =
|
||||
if !authentication.authentication_status.is_terminal_status() {
|
||||
let router_data = transformers::construct_post_authentication_router_data(
|
||||
authentication_connector.clone(),
|
||||
business_profile,
|
||||
merchant_connector_account,
|
||||
authentication_data,
|
||||
&authentication,
|
||||
)?;
|
||||
let router_data =
|
||||
utils::do_auth_connector_call(state, authentication_connector, router_data)
|
||||
.await?;
|
||||
let (updated_authentication, updated_authentication_data) =
|
||||
utils::update_trackers(
|
||||
state,
|
||||
router_data,
|
||||
authentication,
|
||||
payment_data.token.clone(),
|
||||
None,
|
||||
)
|
||||
.await?;
|
||||
payment_data.authentication = Some((
|
||||
updated_authentication.clone(),
|
||||
updated_authentication_data.unwrap_or_default(),
|
||||
));
|
||||
updated_authentication
|
||||
let updated_authentication = utils::update_trackers(
|
||||
state,
|
||||
router_data,
|
||||
authentication,
|
||||
payment_data.token.clone(),
|
||||
None,
|
||||
)
|
||||
.await?;
|
||||
let authentication_status = updated_authentication.authentication_status;
|
||||
payment_data.authentication = Some(updated_authentication);
|
||||
authentication_status
|
||||
} else {
|
||||
authentication
|
||||
authentication.authentication_status
|
||||
};
|
||||
//If authentication is not successful, skip the payment connector flows and mark the payment as failure
|
||||
if !(updated_authentication.authentication_status
|
||||
== api_models::enums::AuthenticationStatus::Success)
|
||||
{
|
||||
if !(authentication_status == api_models::enums::AuthenticationStatus::Success) {
|
||||
*should_continue_confirm_transaction = false;
|
||||
}
|
||||
}
|
||||
@@ -198,7 +192,7 @@ pub async fn perform_pre_authentication<F: Clone + Send>(
|
||||
.parse_value("AcquirerDetails")
|
||||
.change_context(ApiErrorResponse::PreconditionFailed { message: "acquirer_bin and acquirer_merchant_id not found in Payment Connector's Metadata".to_string()})?;
|
||||
|
||||
let (authentication, authentication_data) = utils::update_trackers(
|
||||
let authentication = utils::update_trackers(
|
||||
state,
|
||||
router_data,
|
||||
authentication,
|
||||
@@ -206,15 +200,12 @@ pub async fn perform_pre_authentication<F: Clone + Send>(
|
||||
Some(acquirer_details),
|
||||
)
|
||||
.await?;
|
||||
if authentication_data
|
||||
.as_ref()
|
||||
.is_some_and(|authentication_data| authentication_data.is_separate_authn_required())
|
||||
if authentication.is_separate_authn_required()
|
||||
|| authentication.authentication_status.is_failed()
|
||||
{
|
||||
*should_continue_confirm_transaction = false;
|
||||
}
|
||||
payment_data.authentication =
|
||||
Some((authentication, authentication_data.unwrap_or_default()));
|
||||
payment_data.authentication = Some(authentication);
|
||||
}
|
||||
types::PreAuthenthenticationFlowInput::PaymentMethodAuthNFlow {
|
||||
card_number: _,
|
||||
|
||||
@@ -10,7 +10,10 @@ use crate::{
|
||||
errors::{self, RouterResult},
|
||||
payments::helpers as payments_helpers,
|
||||
},
|
||||
types::{self, storage, transformers::ForeignFrom},
|
||||
types::{
|
||||
self, storage,
|
||||
transformers::{ForeignFrom, ForeignTryFrom},
|
||||
},
|
||||
utils::ext_traits::OptionExt,
|
||||
};
|
||||
|
||||
@@ -35,7 +38,7 @@ pub fn construct_authentication_router_data(
|
||||
device_channel: payments::DeviceChannel,
|
||||
business_profile: storage::BusinessProfile,
|
||||
merchant_connector_account: payments_helpers::MerchantConnectorAccountType,
|
||||
authentication_data: (super::types::AuthenticationData, storage::Authentication),
|
||||
authentication_data: storage::Authentication,
|
||||
return_url: Option<String>,
|
||||
sdk_information: Option<api_models::payments::SdkInformation>,
|
||||
threeds_method_comp_ind: api_models::payments::ThreeDsCompletionIndicator,
|
||||
@@ -61,7 +64,9 @@ pub fn construct_authentication_router_data(
|
||||
currency,
|
||||
message_category,
|
||||
device_channel,
|
||||
authentication_data,
|
||||
pre_authentication_data: super::types::PreAuthenticationData::foreign_try_from(
|
||||
&authentication_data,
|
||||
)?,
|
||||
return_url,
|
||||
sdk_information,
|
||||
email,
|
||||
@@ -82,10 +87,15 @@ pub fn construct_post_authentication_router_data(
|
||||
authentication_connector: String,
|
||||
business_profile: storage::BusinessProfile,
|
||||
merchant_connector_account: payments_helpers::MerchantConnectorAccountType,
|
||||
authentication_data: super::types::AuthenticationData,
|
||||
authentication_data: &storage::Authentication,
|
||||
) -> RouterResult<types::authentication::ConnectorPostAuthenticationRouterData> {
|
||||
let threeds_server_transaction_id = authentication_data
|
||||
.threeds_server_transaction_id
|
||||
.clone()
|
||||
.get_required_value("threeds_server_transaction_id")
|
||||
.change_context(errors::ApiErrorResponse::InternalServerError)?;
|
||||
let router_request = types::authentication::ConnectorPostAuthenticationRequestData {
|
||||
authentication_data,
|
||||
threeds_server_transaction_id,
|
||||
};
|
||||
construct_router_data(
|
||||
authentication_connector,
|
||||
@@ -174,17 +184,17 @@ pub fn construct_router_data<F: Clone, Req, Res>(
|
||||
})
|
||||
}
|
||||
|
||||
impl ForeignFrom<payments::TransactionStatus> for common_enums::AuthenticationStatus {
|
||||
fn foreign_from(trans_status: payments::TransactionStatus) -> Self {
|
||||
impl ForeignFrom<common_enums::TransactionStatus> for common_enums::AuthenticationStatus {
|
||||
fn foreign_from(trans_status: common_enums::TransactionStatus) -> Self {
|
||||
match trans_status {
|
||||
api_models::payments::TransactionStatus::Success => Self::Success,
|
||||
api_models::payments::TransactionStatus::Failure
|
||||
| api_models::payments::TransactionStatus::Rejected
|
||||
| api_models::payments::TransactionStatus::VerificationNotPerformed
|
||||
| api_models::payments::TransactionStatus::NotVerified => Self::Failed,
|
||||
api_models::payments::TransactionStatus::ChallengeRequired
|
||||
| api_models::payments::TransactionStatus::ChallengeRequiredDecoupledAuthentication
|
||||
| api_models::payments::TransactionStatus::InformationOnly => Self::Pending,
|
||||
common_enums::TransactionStatus::Success => Self::Success,
|
||||
common_enums::TransactionStatus::Failure
|
||||
| common_enums::TransactionStatus::Rejected
|
||||
| common_enums::TransactionStatus::VerificationNotPerformed
|
||||
| common_enums::TransactionStatus::NotVerified => Self::Failed,
|
||||
common_enums::TransactionStatus::ChallengeRequired
|
||||
| common_enums::TransactionStatus::ChallengeRequiredDecoupledAuthentication
|
||||
| common_enums::TransactionStatus::InformationOnly => Self::Pending,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
use cards::CardNumber;
|
||||
use error_stack::{Report, ResultExt};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::{
|
||||
core::payments,
|
||||
types::{authentication::AuthNFlowType, storage},
|
||||
core::{errors, payments},
|
||||
types::{storage, transformers::ForeignTryFrom},
|
||||
utils::OptionExt,
|
||||
};
|
||||
pub enum PreAuthenthenticationFlowInput<'a, F: Clone> {
|
||||
PaymentAuthNFlow {
|
||||
@@ -20,7 +22,7 @@ pub enum PreAuthenthenticationFlowInput<'a, F: Clone> {
|
||||
pub enum PostAuthenthenticationFlowInput<'a, F: Clone> {
|
||||
PaymentAuthNFlow {
|
||||
payment_data: &'a mut payments::PaymentData<F>,
|
||||
authentication_data: (storage::Authentication, AuthenticationData),
|
||||
authentication: storage::Authentication,
|
||||
should_continue_confirm_transaction: &'a mut bool,
|
||||
},
|
||||
PaymentMethodAuthNFlow {
|
||||
@@ -28,22 +30,36 @@ pub enum PostAuthenthenticationFlowInput<'a, F: Clone> {
|
||||
},
|
||||
}
|
||||
|
||||
#[derive(Clone, Default, Debug, Serialize, Deserialize)]
|
||||
pub struct AuthenticationData {
|
||||
pub maximum_supported_version: (i64, i64, i64),
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct PreAuthenticationData {
|
||||
pub threeds_server_transaction_id: String,
|
||||
pub cavv: Option<String>,
|
||||
pub authn_flow_type: Option<AuthNFlowType>,
|
||||
pub three_ds_method_data: ThreeDsMethodData,
|
||||
pub message_version: String,
|
||||
pub eci: Option<String>,
|
||||
pub trans_status: api_models::payments::TransactionStatus,
|
||||
pub acquirer_details: Option<AcquirerDetails>,
|
||||
pub acquirer_bin: Option<String>,
|
||||
pub acquirer_merchant_id: Option<String>,
|
||||
pub connector_metadata: Option<serde_json::Value>,
|
||||
}
|
||||
|
||||
impl AuthenticationData {
|
||||
pub fn is_separate_authn_required(&self) -> bool {
|
||||
self.maximum_supported_version.0 == 2
|
||||
impl ForeignTryFrom<&storage::Authentication> for PreAuthenticationData {
|
||||
type Error = Report<errors::ApiErrorResponse>;
|
||||
|
||||
fn foreign_try_from(authentication: &storage::Authentication) -> Result<Self, Self::Error> {
|
||||
let error_message = errors::ApiErrorResponse::UnprocessableEntity { message: "Pre Authentication must be completed successfully before Authentication can be performed".to_string() };
|
||||
let threeds_server_transaction_id = authentication
|
||||
.threeds_server_transaction_id
|
||||
.clone()
|
||||
.get_required_value("threeds_server_transaction_id")
|
||||
.change_context(error_message)?;
|
||||
let message_version = authentication
|
||||
.message_version
|
||||
.as_ref()
|
||||
.get_required_value("message_version")?;
|
||||
Ok(Self {
|
||||
threeds_server_transaction_id,
|
||||
message_version: message_version.to_string(),
|
||||
acquirer_bin: authentication.acquirer_bin.clone(),
|
||||
acquirer_merchant_id: authentication.acquirer_merchant_id.clone(),
|
||||
connector_metadata: authentication.connector_metadata.clone(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,12 +1,9 @@
|
||||
use common_enums::DecoupledAuthenticationType;
|
||||
use common_utils::ext_traits::{Encode, ValueExt};
|
||||
use error_stack::ResultExt;
|
||||
|
||||
use super::types::{AuthenticationData, ThreeDsMethodData};
|
||||
use crate::{
|
||||
consts,
|
||||
core::{
|
||||
errors::{ApiErrorResponse, ConnectorErrorExt, StorageErrorExt},
|
||||
errors::{self, ConnectorErrorExt, StorageErrorExt},
|
||||
payments,
|
||||
},
|
||||
errors::RouterResult,
|
||||
@@ -14,12 +11,11 @@ use crate::{
|
||||
services::{self, execute_connector_processing_step},
|
||||
types::{
|
||||
api::{self, ConnectorCallType},
|
||||
authentication::{AuthNFlowType, AuthenticationResponseData},
|
||||
authentication::AuthenticationResponseData,
|
||||
storage,
|
||||
transformers::ForeignFrom,
|
||||
RouterData,
|
||||
},
|
||||
utils::OptionExt,
|
||||
};
|
||||
|
||||
pub fn get_connector_name_if_separate_authn_supported(
|
||||
@@ -56,19 +52,8 @@ pub async fn update_trackers<F: Clone, Req>(
|
||||
authentication: storage::Authentication,
|
||||
token: Option<String>,
|
||||
acquirer_details: Option<super::types::AcquirerDetails>,
|
||||
) -> RouterResult<(storage::Authentication, Option<AuthenticationData>)> {
|
||||
let authentication_data_option = authentication
|
||||
.authentication_data
|
||||
.as_ref()
|
||||
.map(|authentication_data| {
|
||||
authentication_data
|
||||
.to_owned()
|
||||
.parse_value::<AuthenticationData>("AuthenticationData")
|
||||
.change_context(ApiErrorResponse::InternalServerError)
|
||||
})
|
||||
.transpose()?;
|
||||
|
||||
let (authentication_update, updated_authentication_data) = match router_data.response {
|
||||
) -> RouterResult<storage::Authentication> {
|
||||
let authentication_update = match router_data.response {
|
||||
Ok(response) => match response {
|
||||
AuthenticationResponseData::PreAuthNResponse {
|
||||
threeds_server_transaction_id,
|
||||
@@ -78,132 +63,70 @@ pub async fn update_trackers<F: Clone, Req>(
|
||||
three_ds_method_url,
|
||||
message_version,
|
||||
connector_metadata,
|
||||
} => {
|
||||
let three_ds_method_data = ThreeDsMethodData {
|
||||
three_ds_method_data,
|
||||
three_ds_method_data_submission: three_ds_method_url.is_some(),
|
||||
three_ds_method_url,
|
||||
};
|
||||
let authentication_data = AuthenticationData {
|
||||
maximum_supported_version: maximum_supported_3ds_version,
|
||||
threeds_server_transaction_id,
|
||||
three_ds_method_data,
|
||||
message_version,
|
||||
acquirer_details,
|
||||
..Default::default()
|
||||
};
|
||||
(
|
||||
storage::AuthenticationUpdate::AuthenticationDataUpdate {
|
||||
authentication_data: Some(
|
||||
Encode::encode_to_value(&authentication_data)
|
||||
.change_context(ApiErrorResponse::InternalServerError)?,
|
||||
),
|
||||
connector_authentication_id: Some(connector_authentication_id),
|
||||
payment_method_id: token.map(|token| format!("eph_{}", token)),
|
||||
authentication_type: None,
|
||||
authentication_status: Some(common_enums::AuthenticationStatus::Started),
|
||||
authentication_lifecycle_status: None,
|
||||
connector_metadata,
|
||||
},
|
||||
Some(authentication_data),
|
||||
)
|
||||
}
|
||||
} => storage::AuthenticationUpdate::PreAuthenticationUpdate {
|
||||
threeds_server_transaction_id,
|
||||
maximum_supported_3ds_version,
|
||||
connector_authentication_id,
|
||||
three_ds_method_data,
|
||||
three_ds_method_url,
|
||||
message_version,
|
||||
connector_metadata,
|
||||
authentication_status: common_enums::AuthenticationStatus::Pending,
|
||||
payment_method_id: token.map(|token| format!("eph_{}", token)),
|
||||
acquirer_bin: acquirer_details
|
||||
.as_ref()
|
||||
.map(|acquirer_details| acquirer_details.acquirer_bin.clone()),
|
||||
acquirer_merchant_id: acquirer_details
|
||||
.map(|acquirer_details| acquirer_details.acquirer_merchant_id),
|
||||
},
|
||||
AuthenticationResponseData::AuthNResponse {
|
||||
authn_flow_type,
|
||||
authentication_value: cavv,
|
||||
authentication_value,
|
||||
trans_status,
|
||||
} => {
|
||||
let authentication_data = authentication_data_option
|
||||
.get_required_value("authentication_data")
|
||||
.attach_printable(
|
||||
"AuthenticationData is required to make Authentication call",
|
||||
)?;
|
||||
let authentication_data = AuthenticationData {
|
||||
authn_flow_type: Some(authn_flow_type.clone()),
|
||||
cavv,
|
||||
trans_status: trans_status.clone(),
|
||||
..authentication_data
|
||||
};
|
||||
(
|
||||
storage::AuthenticationUpdate::AuthenticationDataUpdate {
|
||||
authentication_data: Some(
|
||||
Encode::encode_to_value(&authentication_data)
|
||||
.change_context(ApiErrorResponse::InternalServerError)?,
|
||||
),
|
||||
connector_authentication_id: None,
|
||||
payment_method_id: None,
|
||||
authentication_type: Some(match authn_flow_type {
|
||||
AuthNFlowType::Challenge { .. } => {
|
||||
DecoupledAuthenticationType::Challenge
|
||||
}
|
||||
AuthNFlowType::Frictionless => {
|
||||
DecoupledAuthenticationType::Frictionless
|
||||
}
|
||||
}),
|
||||
authentication_status: Some(
|
||||
common_enums::AuthenticationStatus::foreign_from(trans_status),
|
||||
),
|
||||
authentication_lifecycle_status: None,
|
||||
connector_metadata: None,
|
||||
},
|
||||
Some(authentication_data),
|
||||
)
|
||||
let authentication_status =
|
||||
common_enums::AuthenticationStatus::foreign_from(trans_status.clone());
|
||||
storage::AuthenticationUpdate::AuthenticationUpdate {
|
||||
authentication_value,
|
||||
trans_status,
|
||||
acs_url: authn_flow_type.get_acs_url(),
|
||||
challenge_request: authn_flow_type.get_challenge_request(),
|
||||
acs_reference_number: authn_flow_type.get_acs_reference_number(),
|
||||
acs_trans_id: authn_flow_type.get_acs_trans_id(),
|
||||
acs_signed_content: authn_flow_type.get_acs_signed_content(),
|
||||
authentication_type: authn_flow_type.get_decoupled_authentication_type(),
|
||||
authentication_status,
|
||||
}
|
||||
}
|
||||
AuthenticationResponseData::PostAuthNResponse {
|
||||
trans_status,
|
||||
authentication_value,
|
||||
eci,
|
||||
} => {
|
||||
let authentication_data = authentication_data_option
|
||||
.get_required_value("authentication_data")
|
||||
.attach_printable(
|
||||
"AuthenticationData is required to make Post Authentication call",
|
||||
)?;
|
||||
let authentication_data = AuthenticationData {
|
||||
cavv: authentication_value,
|
||||
eci,
|
||||
trans_status: trans_status.clone(),
|
||||
..authentication_data
|
||||
};
|
||||
(
|
||||
storage::AuthenticationUpdate::AuthenticationDataUpdate {
|
||||
authentication_data: Some(
|
||||
Encode::encode_to_value(&authentication_data)
|
||||
.change_context(ApiErrorResponse::InternalServerError)?,
|
||||
),
|
||||
connector_authentication_id: None,
|
||||
payment_method_id: None,
|
||||
authentication_type: None,
|
||||
authentication_status: Some(
|
||||
common_enums::AuthenticationStatus::foreign_from(trans_status),
|
||||
),
|
||||
authentication_lifecycle_status: None,
|
||||
connector_metadata: None,
|
||||
},
|
||||
Some(authentication_data),
|
||||
)
|
||||
}
|
||||
},
|
||||
Err(error) => (
|
||||
storage::AuthenticationUpdate::ErrorUpdate {
|
||||
connector_authentication_id: error.connector_transaction_id,
|
||||
authentication_status: common_enums::AuthenticationStatus::Failed,
|
||||
error_message: Some(error.message),
|
||||
error_code: Some(error.code),
|
||||
} => storage::AuthenticationUpdate::PostAuthenticationUpdate {
|
||||
authentication_status: common_enums::AuthenticationStatus::foreign_from(
|
||||
trans_status.clone(),
|
||||
),
|
||||
trans_status,
|
||||
authentication_value,
|
||||
eci,
|
||||
},
|
||||
authentication_data_option,
|
||||
),
|
||||
},
|
||||
Err(error) => storage::AuthenticationUpdate::ErrorUpdate {
|
||||
connector_authentication_id: error.connector_transaction_id,
|
||||
authentication_status: common_enums::AuthenticationStatus::Failed,
|
||||
error_message: Some(error.message),
|
||||
error_code: Some(error.code),
|
||||
},
|
||||
};
|
||||
let authentication_result = state
|
||||
state
|
||||
.store
|
||||
.update_authentication_by_merchant_id_authentication_id(
|
||||
authentication,
|
||||
authentication_update,
|
||||
)
|
||||
.await
|
||||
.change_context(ApiErrorResponse::InternalServerError)
|
||||
.attach_printable("Error while updating authentication");
|
||||
authentication_result.map(|authentication| (authentication, updated_authentication_data))
|
||||
.change_context(errors::ApiErrorResponse::InternalServerError)
|
||||
.attach_printable("Error while updating authentication")
|
||||
}
|
||||
|
||||
impl ForeignFrom<common_enums::AuthenticationStatus> for common_enums::AttemptStatus {
|
||||
@@ -229,7 +152,6 @@ pub async fn create_new_authentication(
|
||||
merchant_id,
|
||||
authentication_connector,
|
||||
connector_authentication_id: None,
|
||||
authentication_data: None,
|
||||
payment_method_id: "".into(),
|
||||
authentication_type: None,
|
||||
authentication_status: common_enums::AuthenticationStatus::Started,
|
||||
@@ -237,12 +159,29 @@ pub async fn create_new_authentication(
|
||||
error_message: None,
|
||||
error_code: None,
|
||||
connector_metadata: None,
|
||||
maximum_supported_version: None,
|
||||
threeds_server_transaction_id: None,
|
||||
cavv: None,
|
||||
authentication_flow_type: None,
|
||||
message_version: None,
|
||||
eci: None,
|
||||
trans_status: None,
|
||||
acquirer_bin: None,
|
||||
acquirer_merchant_id: None,
|
||||
three_ds_method_data: None,
|
||||
three_ds_method_url: None,
|
||||
acs_url: None,
|
||||
challenge_request: None,
|
||||
acs_reference_number: None,
|
||||
acs_trans_id: None,
|
||||
three_dsserver_trans_id: None,
|
||||
acs_signed_content: None,
|
||||
};
|
||||
state
|
||||
.store
|
||||
.insert_authentication(new_authorization)
|
||||
.await
|
||||
.to_duplicate_response(ApiErrorResponse::GenericDuplicateError {
|
||||
.to_duplicate_response(errors::ApiErrorResponse::GenericDuplicateError {
|
||||
message: format!(
|
||||
"Authentication with authentication_id {} already exists",
|
||||
authentication_id
|
||||
|
||||
@@ -45,8 +45,7 @@ use self::{
|
||||
routing::{self as self_routing, SessionFlowRoutingInput},
|
||||
};
|
||||
use super::{
|
||||
authentication::types::AuthenticationData, errors::StorageErrorExt,
|
||||
payment_methods::surcharge_decision_configs, routing::TransactionData,
|
||||
errors::StorageErrorExt, payment_methods::surcharge_decision_configs, routing::TransactionData,
|
||||
};
|
||||
#[cfg(feature = "frm")]
|
||||
use crate::core::fraud_check as frm_core;
|
||||
@@ -2220,7 +2219,7 @@ where
|
||||
pub payment_link_data: Option<api_models::payments::PaymentLinkResponse>,
|
||||
pub incremental_authorization_details: Option<IncrementalAuthorizationDetails>,
|
||||
pub authorizations: Vec<diesel_models::authorization::Authorization>,
|
||||
pub authentication: Option<(storage::Authentication, AuthenticationData)>,
|
||||
pub authentication: Option<storage::Authentication>,
|
||||
pub frm_metadata: Option<serde_json::Value>,
|
||||
}
|
||||
|
||||
@@ -3383,12 +3382,6 @@ pub async fn payment_external_authentication(
|
||||
.await
|
||||
.to_not_found_response(errors::ApiErrorResponse::InternalServerError)
|
||||
.attach_printable("Error while fetching authentication record")?;
|
||||
let authentication_data: AuthenticationData = authentication
|
||||
.authentication_data
|
||||
.clone()
|
||||
.parse_value("authentication data")
|
||||
.change_context(errors::ApiErrorResponse::InternalServerError)
|
||||
.attach_printable("Error while parsing authentication_data")?;
|
||||
let payment_method_details = helpers::get_payment_method_details_from_payment_token(
|
||||
&state,
|
||||
&payment_attempt,
|
||||
@@ -3446,7 +3439,7 @@ pub async fn payment_external_authentication(
|
||||
Some(currency),
|
||||
authentication::MessageCategory::Payment,
|
||||
req.device_channel,
|
||||
(authentication_data, authentication),
|
||||
authentication,
|
||||
return_url,
|
||||
req.sdk_information,
|
||||
req.threeds_method_comp_ind,
|
||||
|
||||
@@ -12,7 +12,7 @@ use tracing_futures::Instrument;
|
||||
use super::{BoxedOperation, Domain, GetTracker, Operation, UpdateTracker, ValidateRequest};
|
||||
use crate::{
|
||||
core::{
|
||||
authentication::{self, types},
|
||||
authentication,
|
||||
blocklist::utils as blocklist_utils,
|
||||
errors::{self, CustomResult, RouterResult, StorageErrorExt},
|
||||
payment_methods::PaymentMethodRetrieve,
|
||||
@@ -554,27 +554,18 @@ impl<F: Send + Clone, Ctx: PaymentMethodRetrieve>
|
||||
.payment_method_data
|
||||
.apply_additional_payment_data(additional_payment_data)
|
||||
});
|
||||
let authentication = match payment_attempt.authentication_id.clone() {
|
||||
Some(authentication_id) => {
|
||||
let authentication = state
|
||||
.store
|
||||
.find_authentication_by_merchant_id_authentication_id(
|
||||
merchant_id.to_string(),
|
||||
authentication_id.clone(),
|
||||
)
|
||||
.await
|
||||
.to_not_found_response(errors::ApiErrorResponse::InternalServerError)
|
||||
.attach_printable_lazy(|| format!("Error while fetching authentication record with authentication_id {authentication_id}"))?;
|
||||
let authentication_data: authentication::types::AuthenticationData = authentication
|
||||
.authentication_data
|
||||
.clone()
|
||||
.parse_value("authentication data")
|
||||
.change_context(errors::ApiErrorResponse::InternalServerError)
|
||||
.attach_printable("Error while parsing authentication_data")?;
|
||||
Some((authentication, authentication_data))
|
||||
}
|
||||
None => None,
|
||||
};
|
||||
let authentication = payment_attempt.authentication_id.as_ref().async_map(|authentication_id| async move {
|
||||
state
|
||||
.store
|
||||
.find_authentication_by_merchant_id_authentication_id(
|
||||
merchant_id.to_string(),
|
||||
authentication_id.clone(),
|
||||
)
|
||||
.await
|
||||
.to_not_found_response(errors::ApiErrorResponse::InternalServerError)
|
||||
.attach_printable_lazy(|| format!("Error while fetching authentication record with authentication_id {authentication_id}"))
|
||||
}).await
|
||||
.transpose()?;
|
||||
|
||||
payment_attempt.payment_method_billing_address_id = payment_method_billing
|
||||
.as_ref()
|
||||
@@ -806,7 +797,7 @@ impl<F: Clone + Send, Ctx: PaymentMethodRetrieve> Domain<F, api::PaymentsRequest
|
||||
None,
|
||||
)
|
||||
.await?;
|
||||
if let Some(authentication_data) = authentication {
|
||||
if let Some(authentication) = authentication {
|
||||
// call post authn service
|
||||
authentication::perform_post_authentication(
|
||||
state,
|
||||
@@ -815,7 +806,7 @@ impl<F: Clone + Send, Ctx: PaymentMethodRetrieve> Domain<F, api::PaymentsRequest
|
||||
authentication_connector_mca,
|
||||
authentication::types::PostAuthenthenticationFlowInput::PaymentAuthNFlow {
|
||||
payment_data,
|
||||
authentication_data,
|
||||
authentication,
|
||||
should_continue_confirm_transaction,
|
||||
},
|
||||
)
|
||||
@@ -924,10 +915,7 @@ impl<F: Clone, Ctx: PaymentMethodRetrieve>
|
||||
};
|
||||
|
||||
let status_handler_for_authentication_results =
|
||||
|(authentication, authentication_data): &(
|
||||
storage::Authentication,
|
||||
types::AuthenticationData,
|
||||
)| {
|
||||
|authentication: &storage::Authentication| {
|
||||
if authentication.authentication_status.is_failed() {
|
||||
(
|
||||
storage_enums::IntentStatus::Failed,
|
||||
@@ -937,7 +925,7 @@ impl<F: Clone, Ctx: PaymentMethodRetrieve>
|
||||
Some(Some("external authentication failure".to_string())),
|
||||
),
|
||||
)
|
||||
} else if authentication_data.is_separate_authn_required() {
|
||||
} else if authentication.is_separate_authn_required() {
|
||||
(
|
||||
storage_enums::IntentStatus::RequiresCustomerAction,
|
||||
storage_enums::AttemptStatus::AuthenticationPending,
|
||||
@@ -1038,8 +1026,8 @@ impl<F: Clone, Ctx: PaymentMethodRetrieve>
|
||||
authentication_connector,
|
||||
authentication_id,
|
||||
) = match payment_data.authentication.as_ref() {
|
||||
Some((authentication, authentication_data)) => (
|
||||
Some(authentication_data.is_separate_authn_required()),
|
||||
Some(authentication) => (
|
||||
Some(authentication.is_separate_authn_required()),
|
||||
Some(authentication.authentication_connector.clone()),
|
||||
Some(authentication.authentication_id.clone()),
|
||||
),
|
||||
|
||||
@@ -793,7 +793,7 @@ async fn payment_response_update_tracker<F: Clone, T: types::Capturable>(
|
||||
payment_data.payment_attempt = payment_attempt;
|
||||
|
||||
payment_data.authentication = match payment_data.authentication {
|
||||
Some((authentication, authentication_data)) => {
|
||||
Some(authentication) => {
|
||||
let authentication_update = storage::AuthenticationUpdate::PostAuthorizationUpdate {
|
||||
authentication_lifecycle_status:
|
||||
storage::enums::AuthenticationLifecycleStatus::Used,
|
||||
@@ -806,7 +806,7 @@ async fn payment_response_update_tracker<F: Clone, T: types::Capturable>(
|
||||
)
|
||||
.await
|
||||
.to_not_found_response(errors::ApiErrorResponse::PaymentNotFound)?;
|
||||
Some((updated_authentication, authentication_data))
|
||||
Some(updated_authentication)
|
||||
}
|
||||
None => None,
|
||||
};
|
||||
|
||||
@@ -2,7 +2,7 @@ use std::marker::PhantomData;
|
||||
|
||||
use api_models::enums::FrmSuggestion;
|
||||
use async_trait::async_trait;
|
||||
use common_utils::ext_traits::{AsyncExt, ValueExt};
|
||||
use common_utils::ext_traits::AsyncExt;
|
||||
use error_stack::ResultExt;
|
||||
use router_derive::PaymentOperation;
|
||||
use router_env::{instrument, tracing};
|
||||
@@ -10,7 +10,6 @@ use router_env::{instrument, tracing};
|
||||
use super::{BoxedOperation, Domain, GetTracker, Operation, UpdateTracker, ValidateRequest};
|
||||
use crate::{
|
||||
core::{
|
||||
authentication,
|
||||
errors::{self, CustomResult, RouterResult, StorageErrorExt},
|
||||
payment_methods::PaymentMethodRetrieve,
|
||||
payments::{
|
||||
@@ -400,33 +399,17 @@ async fn get_tracker_for_sync<
|
||||
.to_not_found_response(errors::ApiErrorResponse::BusinessProfileNotFound {
|
||||
id: profile_id.to_string(),
|
||||
})?;
|
||||
|
||||
let authentication = match payment_attempt.authentication_id.clone() {
|
||||
Some(authentication_id) => {
|
||||
let authentication = db
|
||||
.find_authentication_by_merchant_id_authentication_id(
|
||||
merchant_account.merchant_id.to_string(),
|
||||
let merchant_id = payment_intent.merchant_id.clone();
|
||||
let authentication = payment_attempt.authentication_id.clone().async_map(|authentication_id| async move {
|
||||
db.find_authentication_by_merchant_id_authentication_id(
|
||||
merchant_id,
|
||||
authentication_id.clone(),
|
||||
)
|
||||
.await
|
||||
.to_not_found_response(errors::ApiErrorResponse::InternalServerError)
|
||||
.attach_printable_lazy(|| format!("Error while fetching authentication record with authentication_id {authentication_id}"))?;
|
||||
let authentication_data: authentication::types::AuthenticationData = authentication
|
||||
.authentication_data
|
||||
.clone()
|
||||
.map(|authentication_data_value| {
|
||||
authentication_data_value
|
||||
.parse_value("AuthenticationData")
|
||||
.change_context(errors::ApiErrorResponse::InternalServerError)
|
||||
.attach_printable("Error while parsing authentication_data")
|
||||
})
|
||||
.transpose()?
|
||||
.unwrap_or_default();
|
||||
|
||||
Some((authentication, authentication_data))
|
||||
}
|
||||
None => None,
|
||||
};
|
||||
.attach_printable_lazy(|| format!("Error while fetching authentication record with authentication_id {authentication_id}"))
|
||||
}).await
|
||||
.transpose()?;
|
||||
|
||||
let payment_data = PaymentData {
|
||||
flow: PhantomData,
|
||||
|
||||
@@ -8,7 +8,7 @@ use error_stack::{report, IntoReport, ResultExt};
|
||||
use masking::Maskable;
|
||||
use router_env::{instrument, tracing};
|
||||
|
||||
use super::{flows::Feature, PaymentData};
|
||||
use super::{flows::Feature, types::AuthenticationData, PaymentData};
|
||||
use crate::{
|
||||
configs::settings::{ConnectorRequestReferenceIdConfig, Server},
|
||||
connector::{Helcim, Nexinets},
|
||||
@@ -567,35 +567,29 @@ where
|
||||
}
|
||||
}))
|
||||
.or(match payment_data.authentication.as_ref(){
|
||||
Some((_authentication, authentication_data)) => {
|
||||
if payment_intent.status == common_enums::IntentStatus::RequiresCustomerAction && authentication_data.cavv.is_none() && authentication_data.is_separate_authn_required(){
|
||||
Some(authentication) => {
|
||||
if payment_intent.status == common_enums::IntentStatus::RequiresCustomerAction && authentication.cavv.is_none() && authentication.is_separate_authn_required(){
|
||||
// if preAuthn and separate authentication needed.
|
||||
let payment_connector_name = payment_attempt.connector
|
||||
.as_ref()
|
||||
.get_required_value("connector")?;
|
||||
Some(api_models::payments::NextActionData::ThreeDsInvoke {
|
||||
three_ds_data: api_models::payments::ThreeDsData {
|
||||
three_ds_authentication_url: helpers::create_authentication_url(
|
||||
&server.base_url,
|
||||
&payment_attempt,
|
||||
),
|
||||
three_ds_authentication_url: helpers::create_authentication_url(&server.base_url, &payment_attempt),
|
||||
three_ds_authorize_url: helpers::create_authorize_url(
|
||||
&server.base_url,
|
||||
&payment_attempt,
|
||||
payment_connector_name,
|
||||
),
|
||||
three_ds_method_details: authentication_data.three_ds_method_data.three_ds_method_url.as_ref().map(|three_ds_method_url|{
|
||||
three_ds_method_details: authentication.three_ds_method_url.as_ref().zip(authentication.three_ds_method_data.as_ref()).map(|(three_ds_method_url,three_ds_method_data )|{
|
||||
api_models::payments::ThreeDsMethodData::AcsThreeDsMethodData {
|
||||
three_ds_method_data_submission: true,
|
||||
three_ds_method_data: authentication_data
|
||||
.three_ds_method_data
|
||||
.three_ds_method_data
|
||||
.clone(),
|
||||
three_ds_method_data: Some(three_ds_method_data.clone()),
|
||||
three_ds_method_url: Some(three_ds_method_url.to_owned()),
|
||||
}
|
||||
}).unwrap_or(api_models::payments::ThreeDsMethodData::AcsThreeDsMethodData {
|
||||
three_ds_method_data_submission: false,
|
||||
three_ds_method_data: "".into(),
|
||||
three_ds_method_data: None,
|
||||
three_ds_method_url: None,
|
||||
}),
|
||||
},
|
||||
@@ -1212,7 +1206,11 @@ impl<F: Clone> TryFrom<PaymentAdditionalData<'_, F>> for types::PaymentsAuthoriz
|
||||
| Some(RequestIncrementalAuthorization::Default)
|
||||
),
|
||||
metadata: additional_data.payment_data.payment_intent.metadata,
|
||||
authentication_data: payment_data.authentication.map(|auth| auth.1),
|
||||
authentication_data: payment_data
|
||||
.authentication
|
||||
.as_ref()
|
||||
.map(AuthenticationData::foreign_try_from)
|
||||
.transpose()?,
|
||||
customer_acceptance: payment_data.customer_acceptance,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -1,7 +1,12 @@
|
||||
use std::{collections::HashMap, num::TryFromIntError};
|
||||
|
||||
use api_models::{payment_methods::SurchargeDetailsResponse, payments::RequestSurchargeDetails};
|
||||
use common_utils::{consts, errors::CustomResult, ext_traits::Encode, types as common_types};
|
||||
use common_utils::{
|
||||
consts,
|
||||
errors::CustomResult,
|
||||
ext_traits::{Encode, OptionExt},
|
||||
types as common_types,
|
||||
};
|
||||
use data_models::payments::payment_attempt::PaymentAttempt;
|
||||
use diesel_models::business_profile::BusinessProfile;
|
||||
use error_stack::{IntoReport, ResultExt};
|
||||
@@ -371,3 +376,53 @@ impl SurchargeMetadata {
|
||||
.await
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct AuthenticationData {
|
||||
pub eci: String,
|
||||
pub cavv: String,
|
||||
pub threeds_server_transaction_id: String,
|
||||
pub message_version: String,
|
||||
}
|
||||
|
||||
impl ForeignTryFrom<&storage::Authentication> for AuthenticationData {
|
||||
type Error = error_stack::Report<errors::ApiErrorResponse>;
|
||||
fn foreign_try_from(authentication: &storage::Authentication) -> Result<Self, Self::Error> {
|
||||
if authentication.authentication_status == common_enums::AuthenticationStatus::Success {
|
||||
let threeds_server_transaction_id = authentication
|
||||
.threeds_server_transaction_id
|
||||
.clone()
|
||||
.get_required_value("threeds_server_transaction_id")
|
||||
.change_context(errors::ApiErrorResponse::InternalServerError)
|
||||
.attach_printable("threeds_server_transaction_id must not be null when authentication_status is success")?;
|
||||
let message_version = authentication
|
||||
.message_version
|
||||
.clone()
|
||||
.get_required_value("message_version")
|
||||
.change_context(errors::ApiErrorResponse::InternalServerError)
|
||||
.attach_printable(
|
||||
"message_version must not be null when authentication_status is success",
|
||||
)?;
|
||||
let cavv = authentication
|
||||
.cavv
|
||||
.clone()
|
||||
.get_required_value("cavv")
|
||||
.change_context(errors::ApiErrorResponse::InternalServerError)
|
||||
.attach_printable("cavv must not be null when authentication_status is success")?;
|
||||
let eci = authentication
|
||||
.eci
|
||||
.clone()
|
||||
.get_required_value("eci")
|
||||
.change_context(errors::ApiErrorResponse::InternalServerError)
|
||||
.attach_printable("eci must not be null when authentication_status is success")?;
|
||||
Ok(Self {
|
||||
eci,
|
||||
cavv,
|
||||
threeds_server_transaction_id,
|
||||
message_version: message_version.to_string(),
|
||||
})
|
||||
} else {
|
||||
Err(errors::ApiErrorResponse::PaymentAuthenticationFailed { data: None }.into())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -103,13 +103,30 @@ impl AuthenticationInterface for MockDb {
|
||||
authentication_status: authentication.authentication_status,
|
||||
authentication_connector: authentication.authentication_connector,
|
||||
connector_authentication_id: authentication.connector_authentication_id,
|
||||
authentication_data: authentication.authentication_data,
|
||||
authentication_data: None,
|
||||
payment_method_id: authentication.payment_method_id,
|
||||
authentication_type: authentication.authentication_type,
|
||||
authentication_lifecycle_status: authentication.authentication_lifecycle_status,
|
||||
error_code: authentication.error_code,
|
||||
error_message: authentication.error_message,
|
||||
connector_metadata: authentication.connector_metadata,
|
||||
maximum_supported_version: authentication.maximum_supported_version,
|
||||
threeds_server_transaction_id: authentication.threeds_server_transaction_id,
|
||||
cavv: authentication.cavv,
|
||||
authentication_flow_type: authentication.authentication_flow_type,
|
||||
message_version: authentication.message_version,
|
||||
eci: authentication.eci,
|
||||
trans_status: authentication.trans_status,
|
||||
acquirer_bin: authentication.acquirer_bin,
|
||||
acquirer_merchant_id: authentication.acquirer_merchant_id,
|
||||
three_ds_method_data: authentication.three_ds_method_data,
|
||||
three_ds_method_url: authentication.three_ds_method_url,
|
||||
acs_url: authentication.acs_url,
|
||||
challenge_request: authentication.challenge_request,
|
||||
acs_reference_number: authentication.acs_reference_number,
|
||||
acs_trans_id: authentication.acs_trans_id,
|
||||
three_ds_server_trans_id: authentication.three_dsserver_trans_id,
|
||||
acs_signed_content: authentication.acs_signed_content,
|
||||
};
|
||||
authentications.push(authentication.clone());
|
||||
Ok(authentication)
|
||||
|
||||
@@ -36,15 +36,13 @@ pub use crate::core::payments::{payment_address::PaymentAddress, CustomerDetails
|
||||
use crate::core::utils::IRRELEVANT_CONNECTOR_REQUEST_REFERENCE_ID_IN_DISPUTE_FLOW;
|
||||
use crate::{
|
||||
core::{
|
||||
authentication as authentication_core,
|
||||
errors::{self, RouterResult},
|
||||
payments::{types, PaymentData, RecurringMandatePaymentData},
|
||||
},
|
||||
services,
|
||||
types::transformers::ForeignFrom,
|
||||
types::{transformers::ForeignFrom, types::AuthenticationData},
|
||||
utils::OptionExt,
|
||||
};
|
||||
|
||||
pub type PaymentsAuthorizeRouterData =
|
||||
RouterData<api::Authorize, PaymentsAuthorizeData, PaymentsResponseData>;
|
||||
pub type PaymentsPreProcessingRouterData =
|
||||
@@ -425,7 +423,7 @@ pub struct PaymentsAuthorizeData {
|
||||
pub customer_id: Option<String>,
|
||||
pub request_incremental_authorization: bool,
|
||||
pub metadata: Option<pii::SecretSerdeValue>,
|
||||
pub authentication_data: Option<authentication_core::types::AuthenticationData>,
|
||||
pub authentication_data: Option<AuthenticationData>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Default)]
|
||||
|
||||
@@ -25,7 +25,7 @@ pub struct AcquirerDetails {
|
||||
|
||||
#[derive(Clone, serde::Deserialize, Debug, serde::Serialize)]
|
||||
pub struct AuthenticationResponse {
|
||||
pub trans_status: api_models::payments::TransactionStatus,
|
||||
pub trans_status: common_enums::TransactionStatus,
|
||||
pub acs_url: Option<url::Url>,
|
||||
pub challenge_request: Option<String>,
|
||||
pub acs_reference_number: Option<String>,
|
||||
|
||||
@@ -5,7 +5,7 @@ use serde::{Deserialize, Serialize};
|
||||
|
||||
use super::{
|
||||
api::{self, authentication},
|
||||
storage, BrowserInformation, RouterData,
|
||||
BrowserInformation, RouterData,
|
||||
};
|
||||
use crate::services;
|
||||
|
||||
@@ -13,20 +13,20 @@ use crate::services;
|
||||
pub enum AuthenticationResponseData {
|
||||
PreAuthNResponse {
|
||||
threeds_server_transaction_id: String,
|
||||
maximum_supported_3ds_version: (i64, i64, i64),
|
||||
maximum_supported_3ds_version: common_utils::types::SemanticVersion,
|
||||
connector_authentication_id: String,
|
||||
three_ds_method_data: String,
|
||||
three_ds_method_url: Option<String>,
|
||||
message_version: String,
|
||||
message_version: common_utils::types::SemanticVersion,
|
||||
connector_metadata: Option<serde_json::Value>,
|
||||
},
|
||||
AuthNResponse {
|
||||
authn_flow_type: AuthNFlowType,
|
||||
authentication_value: Option<String>,
|
||||
trans_status: api_models::payments::TransactionStatus,
|
||||
trans_status: common_enums::TransactionStatus,
|
||||
},
|
||||
PostAuthNResponse {
|
||||
trans_status: api_models::payments::TransactionStatus,
|
||||
trans_status: common_enums::TransactionStatus,
|
||||
authentication_value: Option<String>,
|
||||
eci: Option<String>,
|
||||
},
|
||||
@@ -48,6 +48,50 @@ pub enum AuthNFlowType {
|
||||
Frictionless,
|
||||
}
|
||||
|
||||
impl AuthNFlowType {
|
||||
pub fn get_acs_url(&self) -> Option<String> {
|
||||
if let Self::Challenge(challenge_params) = self {
|
||||
challenge_params.acs_url.as_ref().map(ToString::to_string)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
pub fn get_challenge_request(&self) -> Option<String> {
|
||||
if let Self::Challenge(challenge_params) = self {
|
||||
challenge_params.challenge_request.clone()
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
pub fn get_acs_reference_number(&self) -> Option<String> {
|
||||
if let Self::Challenge(challenge_params) = self {
|
||||
challenge_params.acs_reference_number.clone()
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
pub fn get_acs_trans_id(&self) -> Option<String> {
|
||||
if let Self::Challenge(challenge_params) = self {
|
||||
challenge_params.acs_trans_id.clone()
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
pub fn get_acs_signed_content(&self) -> Option<String> {
|
||||
if let Self::Challenge(challenge_params) = self {
|
||||
challenge_params.acs_signed_content.clone()
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
pub fn get_decoupled_authentication_type(&self) -> common_enums::DecoupledAuthenticationType {
|
||||
match self {
|
||||
Self::Challenge(_) => common_enums::DecoupledAuthenticationType::Challenge,
|
||||
Self::Frictionless => common_enums::DecoupledAuthenticationType::Frictionless,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Default, Debug)]
|
||||
pub struct PreAuthNRequestData {
|
||||
// card number
|
||||
@@ -65,10 +109,7 @@ pub struct ConnectorAuthenticationRequestData {
|
||||
pub currency: Option<common_enums::Currency>,
|
||||
pub message_category: authentication::MessageCategory,
|
||||
pub device_channel: api_models::payments::DeviceChannel,
|
||||
pub authentication_data: (
|
||||
crate::core::authentication::types::AuthenticationData,
|
||||
storage::Authentication,
|
||||
),
|
||||
pub pre_authentication_data: crate::core::authentication::types::PreAuthenticationData,
|
||||
pub return_url: Option<String>,
|
||||
pub sdk_information: Option<api_models::payments::SdkInformation>,
|
||||
pub email: Option<Email>,
|
||||
@@ -78,7 +119,7 @@ pub struct ConnectorAuthenticationRequestData {
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct ConnectorPostAuthenticationRequestData {
|
||||
pub authentication_data: crate::core::authentication::types::AuthenticationData,
|
||||
pub threeds_server_transaction_id: String,
|
||||
}
|
||||
|
||||
pub type PreAuthNRouterData =
|
||||
|
||||
@@ -14,7 +14,7 @@ use masking::{ExposeInterface, PeekInterface};
|
||||
|
||||
use super::domain;
|
||||
use crate::{
|
||||
core::{authentication::types::AuthenticationData, errors},
|
||||
core::errors,
|
||||
services::authentication::get_header_value_by_key,
|
||||
types::{
|
||||
api::{self as api_types, routing as routing_types},
|
||||
@@ -745,31 +745,20 @@ impl ForeignFrom<storage::Authorization> for payments::IncrementalAuthorizationR
|
||||
}
|
||||
}
|
||||
|
||||
impl ForeignFrom<&(storage::Authentication, AuthenticationData)>
|
||||
for payments::ExternalAuthenticationDetailsResponse
|
||||
{
|
||||
fn foreign_from(authn_data: &(storage::Authentication, AuthenticationData)) -> Self {
|
||||
let (ds_transaction_id, version) = if authn_data.0.authentication_data.is_some() {
|
||||
(
|
||||
Some(authn_data.1.threeds_server_transaction_id.clone()),
|
||||
Some(format!(
|
||||
"{}.{}.{}",
|
||||
authn_data.1.maximum_supported_version.0,
|
||||
authn_data.1.maximum_supported_version.1,
|
||||
authn_data.1.maximum_supported_version.2
|
||||
)),
|
||||
)
|
||||
} else {
|
||||
(None, None)
|
||||
};
|
||||
impl ForeignFrom<&storage::Authentication> for payments::ExternalAuthenticationDetailsResponse {
|
||||
fn foreign_from(authn_data: &storage::Authentication) -> Self {
|
||||
let version = authn_data
|
||||
.maximum_supported_version
|
||||
.as_ref()
|
||||
.map(|version| version.to_string());
|
||||
Self {
|
||||
authentication_flow: authn_data.0.authentication_type,
|
||||
electronic_commerce_indicator: authn_data.1.eci.clone(),
|
||||
status: authn_data.0.authentication_status,
|
||||
ds_transaction_id,
|
||||
authentication_flow: authn_data.authentication_type,
|
||||
electronic_commerce_indicator: authn_data.eci.clone(),
|
||||
status: authn_data.authentication_status,
|
||||
ds_transaction_id: authn_data.threeds_server_transaction_id.clone(),
|
||||
version,
|
||||
error_code: authn_data.0.error_code.clone(),
|
||||
error_message: authn_data.0.error_message.clone(),
|
||||
error_code: authn_data.error_code.clone(),
|
||||
error_message: authn_data.error_message.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
-- This file should undo anything in `up.sql`
|
||||
ALTER TABLE authentication
|
||||
DROP COLUMN IF EXISTS maximum_supported_version ,
|
||||
DROP COLUMN IF EXISTS threeds_server_transaction_id ,
|
||||
DROP COLUMN IF EXISTS cavv ,
|
||||
DROP COLUMN IF EXISTS authentication_flow_type ,
|
||||
DROP COLUMN IF EXISTS message_version ,
|
||||
DROP COLUMN IF EXISTS eci ,
|
||||
DROP COLUMN IF EXISTS trans_status ,
|
||||
DROP COLUMN IF EXISTS acquirer_bin ,
|
||||
DROP COLUMN IF EXISTS acquirer_merchant_id ,
|
||||
DROP COLUMN IF EXISTS three_ds_method_data ,
|
||||
DROP COLUMN IF EXISTS three_ds_method_url ,
|
||||
DROP COLUMN IF EXISTS acs_url ,
|
||||
DROP COLUMN IF EXISTS challenge_request ,
|
||||
DROP COLUMN IF EXISTS acs_reference_number ,
|
||||
DROP COLUMN IF EXISTS acs_trans_id ,
|
||||
DROP COLUMN IF EXISTS three_dsserver_trans_id ,
|
||||
DROP COLUMN IF EXISTS acs_signed_content;
|
||||
@@ -0,0 +1,20 @@
|
||||
-- Your SQL goes here
|
||||
ALTER TABLE authentication
|
||||
ADD COLUMN IF NOT EXISTS maximum_supported_version JSONB,
|
||||
ADD COLUMN IF NOT EXISTS threeds_server_transaction_id VARCHAR(64),
|
||||
ADD COLUMN IF NOT EXISTS cavv VARCHAR(64),
|
||||
ADD COLUMN IF NOT EXISTS authentication_flow_type VARCHAR(64),
|
||||
ADD COLUMN IF NOT EXISTS message_version JSONB,
|
||||
ADD COLUMN IF NOT EXISTS eci VARCHAR(64),
|
||||
ADD COLUMN IF NOT EXISTS trans_status VARCHAR(64),
|
||||
ADD COLUMN IF NOT EXISTS acquirer_bin VARCHAR(64),
|
||||
ADD COLUMN IF NOT EXISTS acquirer_merchant_id VARCHAR(64),
|
||||
ADD COLUMN IF NOT EXISTS three_ds_method_data VARCHAR,
|
||||
ADD COLUMN IF NOT EXISTS three_ds_method_url VARCHAR,
|
||||
ADD COLUMN IF NOT EXISTS acs_url VARCHAR,
|
||||
ADD COLUMN IF NOT EXISTS challenge_request VARCHAR,
|
||||
ADD COLUMN IF NOT EXISTS acs_reference_number VARCHAR,
|
||||
ADD COLUMN IF NOT EXISTS acs_trans_id VARCHAR,
|
||||
ADD COLUMN IF NOT EXISTS three_dsserver_trans_id VARCHAR,
|
||||
ADD COLUMN IF NOT EXISTS acs_signed_content VARCHAR,
|
||||
ADD COLUMN IF NOT EXISTS connector_metadata JSONB;
|
||||
@@ -16872,7 +16872,6 @@
|
||||
"type": "object",
|
||||
"required": [
|
||||
"three_ds_method_data_submission",
|
||||
"three_ds_method_data",
|
||||
"three_ds_method_key"
|
||||
],
|
||||
"properties": {
|
||||
@@ -16882,7 +16881,8 @@
|
||||
},
|
||||
"three_ds_method_data": {
|
||||
"type": "string",
|
||||
"description": "ThreeDS method data"
|
||||
"description": "ThreeDS method data",
|
||||
"nullable": true
|
||||
},
|
||||
"three_ds_method_url": {
|
||||
"type": "string",
|
||||
|
||||
Reference in New Issue
Block a user