diff --git a/crates/router/src/connector/adyen.rs b/crates/router/src/connector/adyen.rs index 82d586c0f6..b4c3c6f28b 100644 --- a/crates/router/src/connector/adyen.rs +++ b/crates/router/src/connector/adyen.rs @@ -304,24 +304,11 @@ impl data: &types::PaymentsRouterData, res: Response, ) -> CustomResult { - let response = match data.payment_method { - types::storage::enums::PaymentMethodType::Wallet => { - let response: adyen::AdyenWalletResponse = res - .response - .parse_struct("AdyenWalletResponse") - .change_context(errors::ConnectorError::ResponseDeserializationFailed)?; + let response: adyen::AdyenPaymentResponse = res + .response + .parse_struct("AdyenPaymentResponse") + .change_context(errors::ConnectorError::ResponseDeserializationFailed)?; - adyen::AdyenPaymentResponse::AdyenWalletResponse(response) - } - _ => { - let response: adyen::AdyenResponse = res - .response - .parse_struct("AdyenResponse") - .change_context(errors::ConnectorError::ResponseDeserializationFailed)?; - - adyen::AdyenPaymentResponse::AdyenResponse(response) - } - }; types::RouterData::try_from(types::ResponseRouterData { response, data: data.clone(), diff --git a/crates/router/src/connector/adyen/transformers.rs b/crates/router/src/connector/adyen/transformers.rs index 81bfb2807f..d76081635c 100644 --- a/crates/router/src/connector/adyen/transformers.rs +++ b/crates/router/src/connector/adyen/transformers.rs @@ -1,3 +1,5 @@ +use std::{collections::HashMap, str::FromStr}; + use error_stack::{IntoReport, ResultExt}; use reqwest::Url; use serde::{Deserialize, Serialize}; @@ -29,7 +31,7 @@ pub enum AdyenRecurringModel { UnscheduledCardOnFile, } -#[derive(Debug, Serialize, Deserialize)] +#[derive(Debug, Serialize)] #[serde(rename_all = "camelCase")] pub struct AdyenPaymentRequest { amount: Amount, @@ -37,11 +39,25 @@ pub struct AdyenPaymentRequest { payment_method: AdyenPaymentMethod, reference: String, return_url: String, + browser_info: Option, shopper_interaction: AdyenShopperInteraction, #[serde(skip_serializing_if = "Option::is_none")] recurring_processing_model: Option, } +#[derive(Debug, Serialize)] +#[serde(rename_all = "camelCase")] +struct AdyenBrowserInfo { + user_agent: String, + accept_header: String, + language: String, + color_depth: u8, + screen_height: u32, + screen_width: u32, + time_zone_offset: i32, + java_enabled: bool, +} + #[derive(Debug, Serialize, Deserialize, Eq, PartialEq)] pub struct AdyenRedirectRequest { pub details: AdyenRedirectRequestTypes, @@ -78,7 +94,7 @@ pub struct AdyenThreeDS { #[serde(untagged)] pub enum AdyenPaymentResponse { AdyenResponse(AdyenResponse), - AdyenWalletResponse(AdyenWalletResponse), + AdyenRedirectResponse(AdyenWalletResponse), } #[derive(Debug, Clone, Serialize, Deserialize)] @@ -109,6 +125,7 @@ pub struct AdyenWalletAction { method: String, #[serde(rename = "type")] type_of_response: String, + data: HashMap, } #[derive(Default, Debug, Clone, Serialize, Deserialize)] @@ -194,6 +211,27 @@ impl TryFrom<&types::ConnectorAuthType> for AdyenAuthType { } } } + +impl TryFrom<&types::BrowserInformation> for AdyenBrowserInfo { + type Error = error_stack::Report; + fn try_from(item: &types::BrowserInformation) -> Result { + Ok(Self { + accept_header: item.accept_header.clone().ok_or( + errors::ConnectorError::MissingRequiredField { + field_name: "accept_header".to_string(), + }, + )?, + language: item.language.clone(), + screen_height: item.screen_height, + screen_width: item.screen_width, + color_depth: item.color_depth, + user_agent: item.user_agent.clone(), + time_zone_offset: item.time_zone, + java_enabled: item.java_enabled, + }) + } +} + // Payment Request Transform impl TryFrom<&types::PaymentsRouterData> for AdyenPaymentRequest { type Error = error_stack::Report; @@ -253,14 +291,29 @@ impl TryFrom<&types::PaymentsRouterData> for AdyenPaymentRequest { }), }?; + let browser_info = if matches!(item.auth_type, enums::AuthenticationType::ThreeDs) { + item.request + .browser_info + .clone() + .map(|d| AdyenBrowserInfo::try_from(&d)) + .transpose()? + } else { + None + }; + Ok(AdyenPaymentRequest { amount, merchant_account: auth_type.merchant_account, payment_method, reference, - return_url: "juspay.io".to_string(), + return_url: item.orca_return_url.clone().ok_or( + errors::ConnectorError::MissingRequiredField { + field_name: "orca_return_url".into(), + }, + )?, shopper_interaction, recurring_processing_model, + browser_info, }) } } @@ -379,12 +432,10 @@ pub fn get_wallet_response( let redirection_data = services::RedirectForm { url: redirection_url_response.to_string(), - method: services::Method::Get, - form_fields: std::collections::HashMap::from_iter( - redirection_url_response - .query_pairs() - .map(|(k, v)| (k.to_string(), v.to_string())), - ), + method: services::Method::from_str(&response.action.method) + .into_report() + .change_context(errors::ParsingError)?, + form_fields: response.action.data, }; let payments_response_data = types::PaymentsResponseData { @@ -405,7 +456,7 @@ impl ) -> Result { let (status, error, payment_response_data) = match item.response { AdyenPaymentResponse::AdyenResponse(response) => get_adyen_response(response)?, - AdyenPaymentResponse::AdyenWalletResponse(response) => get_wallet_response(response)?, + AdyenPaymentResponse::AdyenRedirectResponse(response) => get_wallet_response(response)?, }; Ok(types::RouterData { diff --git a/crates/router/src/core/payments/operations/payment_create.rs b/crates/router/src/core/payments/operations/payment_create.rs index 9e1a744055..1fbb61654a 100644 --- a/crates/router/src/core/payments/operations/payment_create.rs +++ b/crates/router/src/core/payments/operations/payment_create.rs @@ -26,7 +26,7 @@ use crate::{ enums::{self, IntentStatus}, }, }, - utils::OptionExt, + utils::{self, OptionExt}, }; #[derive(Debug, Clone, Copy, PaymentOperation)] #[operation(ops = "all", flow = "authorize")] @@ -70,6 +70,15 @@ impl GetTracker, api::PaymentsRequest> for Pa let billing_address = helpers::get_address_for_payment_request(db, request.billing.as_ref(), None).await?; + let browser_info = request + .browser_info + .clone() + .map(|x| utils::Encode::::encode_to_value(&x)) + .transpose() + .change_context(errors::ApiErrorResponse::InvalidDataValue { + field_name: "browser_info", + })?; + payment_attempt = match db .insert_payment_attempt(Self::make_payment_attempt( &payment_id, @@ -78,6 +87,7 @@ impl GetTracker, api::PaymentsRequest> for Pa money, payment_method_type, request, + browser_info, )) .await { @@ -287,6 +297,7 @@ impl PaymentCreate { money: (i32, enums::Currency), payment_method: Option, request: &api::PaymentsRequest, + browser_info: Option, ) -> storage::PaymentAttemptNew { let created_at @ modified_at @ last_synced = Some(crate::utils::date_time::now()); let status = @@ -308,6 +319,7 @@ impl PaymentCreate { modified_at, last_synced, authentication_type: request.authentication_type, + browser_info, ..storage::PaymentAttemptNew::default() } } diff --git a/crates/router/src/core/payments/transformers.rs b/crates/router/src/core/payments/transformers.rs index a09d474a3a..dc78f6dccb 100644 --- a/crates/router/src/core/payments/transformers.rs +++ b/crates/router/src/core/payments/transformers.rs @@ -255,6 +255,14 @@ impl TryFrom> for types::PaymentsRequestData { type Error = error_stack::Report; fn try_from(payment_data: PaymentData) -> Result { + let browser_info: Option = payment_data + .payment_attempt + .browser_info + .map(|b| b.parse_value("BrowserInformation")) + .transpose() + .change_context(errors::ApiErrorResponse::InvalidDataValue { + field_name: "browser_info", + })?; Ok(Self { payment_method_data: { let payment_method_type = payment_data @@ -276,6 +284,7 @@ impl TryFrom> for types::PaymentsRequestData { confirm: payment_data.payment_attempt.confirm, statement_descriptor_suffix: payment_data.payment_intent.statement_descriptor_suffix, capture_method: payment_data.payment_attempt.capture_method, + browser_info, }) } } diff --git a/crates/router/src/db/payment_attempt.rs b/crates/router/src/db/payment_attempt.rs index e7682cebfe..1c367303f0 100644 --- a/crates/router/src/db/payment_attempt.rs +++ b/crates/router/src/db/payment_attempt.rs @@ -200,6 +200,7 @@ mod storage { amount_to_capture: payment_attempt.amount_to_capture, cancellation_reason: payment_attempt.cancellation_reason.clone(), mandate_id: payment_attempt.mandate_id.clone(), + browser_info: payment_attempt.browser_info.clone(), }; // TODO: Add a proper error for serialization failure let redis_value = serde_json::to_string(&created_attempt) diff --git a/crates/router/src/schema.rs b/crates/router/src/schema.rs index d4d7b89595..7dd78457a8 100644 --- a/crates/router/src/schema.rs +++ b/crates/router/src/schema.rs @@ -203,6 +203,7 @@ diesel::table! { cancellation_reason -> Nullable, amount_to_capture -> Nullable, mandate_id -> Nullable, + browser_info -> Nullable, } } diff --git a/crates/router/src/services/api/request.rs b/crates/router/src/services/api/request.rs index 1c5c2f7a10..8f455a8736 100644 --- a/crates/router/src/services/api/request.rs +++ b/crates/router/src/services/api/request.rs @@ -12,7 +12,9 @@ use crate::{ pub(crate) type Headers = Vec<(String, String)>; -#[derive(Clone, Copy, Debug, Eq, PartialEq, Deserialize, Serialize, strum::Display)] +#[derive( + Clone, Copy, Debug, Eq, PartialEq, Deserialize, Serialize, strum::Display, strum::EnumString, +)] #[serde(rename_all = "UPPERCASE")] #[strum(serialize_all = "UPPERCASE")] pub enum Method { diff --git a/crates/router/src/types.rs b/crates/router/src/types.rs index 8677347dab..981954086c 100644 --- a/crates/router/src/types.rs +++ b/crates/router/src/types.rs @@ -75,6 +75,7 @@ pub struct PaymentsRequestData { pub mandate_id: Option, pub off_session: Option, pub setup_mandate_details: Option, + pub browser_info: Option, } #[derive(Debug, Clone)] @@ -112,7 +113,7 @@ pub struct RefundsRequestData { } #[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] -pub struct DeviceInformation { +pub struct BrowserInformation { pub color_depth: u8, pub java_enabled: bool, pub java_script_enabled: bool, diff --git a/crates/router/src/types/api/payments.rs b/crates/router/src/types/api/payments.rs index bdd79cd641..300871a1e1 100644 --- a/crates/router/src/types/api/payments.rs +++ b/crates/router/src/types/api/payments.rs @@ -50,6 +50,7 @@ pub struct PaymentsRequest { pub payment_token: Option, pub shipping: Option
, pub billing: Option
, + pub browser_info: Option, pub statement_descriptor_name: Option, pub statement_descriptor_suffix: Option, pub metadata: Option, diff --git a/crates/router/src/types/storage/payment_attempt.rs b/crates/router/src/types/storage/payment_attempt.rs index 5d04890829..3d14de11ed 100644 --- a/crates/router/src/types/storage/payment_attempt.rs +++ b/crates/router/src/types/storage/payment_attempt.rs @@ -35,6 +35,7 @@ pub struct PaymentAttempt { pub cancellation_reason: Option, pub amount_to_capture: Option, pub mandate_id: Option, + pub browser_info: Option, } #[derive(Clone, Debug, Default, Insertable, router_derive::DebugAsDisplay)] @@ -68,6 +69,7 @@ pub struct PaymentAttemptNew { pub cancellation_reason: Option, pub amount_to_capture: Option, pub mandate_id: Option, + pub browser_info: Option, } #[derive(Clone, Debug)] diff --git a/crates/router/tests/connectors/aci.rs b/crates/router/tests/connectors/aci.rs index e88fe2caec..3245a453c3 100644 --- a/crates/router/tests/connectors/aci.rs +++ b/crates/router/tests/connectors/aci.rs @@ -46,6 +46,7 @@ fn construct_payment_router_data() -> types::PaymentsRouterData { off_session: None, setup_mandate_details: None, capture_method: None, + browser_info: None, }, response: None, payment_method_id: None, diff --git a/crates/router/tests/connectors/authorizedotnet.rs b/crates/router/tests/connectors/authorizedotnet.rs index defe0e28ba..d3ad85344d 100644 --- a/crates/router/tests/connectors/authorizedotnet.rs +++ b/crates/router/tests/connectors/authorizedotnet.rs @@ -46,6 +46,7 @@ fn construct_payment_router_data() -> types::PaymentsRouterData { off_session: None, setup_mandate_details: None, capture_method: None, + browser_info: None, }, payment_method_id: None, response: None, diff --git a/crates/router/tests/connectors/checkout.rs b/crates/router/tests/connectors/checkout.rs index e09ae1f838..3d95d5babd 100644 --- a/crates/router/tests/connectors/checkout.rs +++ b/crates/router/tests/connectors/checkout.rs @@ -42,6 +42,7 @@ fn construct_payment_router_data() -> types::PaymentsRouterData { off_session: None, setup_mandate_details: None, capture_method: None, + browser_info: None, }, response: None, payment_method_id: None, diff --git a/crates/router/tests/payments.rs b/crates/router/tests/payments.rs index 6ee96ecb41..acbfd3ee70 100644 --- a/crates/router/tests/payments.rs +++ b/crates/router/tests/payments.rs @@ -326,6 +326,7 @@ async fn payments_create_core() { mandate_id: None, off_session: None, client_secret: None, + browser_info: None, }; let expected_response = api::PaymentsResponse { @@ -484,6 +485,7 @@ async fn payments_create_core_adyen_no_redirect() { mandate_id: None, off_session: None, client_secret: None, + browser_info: None, }; let expected_response = services::BachResponse::Json(api::PaymentsResponse { diff --git a/crates/router/tests/payments2.rs b/crates/router/tests/payments2.rs index 27bff1b357..09aac122fd 100644 --- a/crates/router/tests/payments2.rs +++ b/crates/router/tests/payments2.rs @@ -91,6 +91,7 @@ async fn payments_create_core() { mandate_id: None, off_session: None, client_secret: None, + browser_info: None, }; let expected_response = api::PaymentsResponse { @@ -252,6 +253,7 @@ async fn payments_create_core_adyen_no_redirect() { off_session: None, mandate_id: None, client_secret: None, + browser_info: None, }; let expected_response = services::BachResponse::Json(api::PaymentsResponse { diff --git a/migrations/2022-11-24-095709_add_browser_info_to_payment_attempt/down.sql b/migrations/2022-11-24-095709_add_browser_info_to_payment_attempt/down.sql new file mode 100644 index 0000000000..b172b43b73 --- /dev/null +++ b/migrations/2022-11-24-095709_add_browser_info_to_payment_attempt/down.sql @@ -0,0 +1,2 @@ +ALTER TABLE payment_attempt +DROP COLUMN browser_info; diff --git a/migrations/2022-11-24-095709_add_browser_info_to_payment_attempt/up.sql b/migrations/2022-11-24-095709_add_browser_info_to_payment_attempt/up.sql new file mode 100644 index 0000000000..a072f6cb3b --- /dev/null +++ b/migrations/2022-11-24-095709_add_browser_info_to_payment_attempt/up.sql @@ -0,0 +1,2 @@ +ALTER TABLE payment_attempt +ADD COLUMN browser_info JSONB DEFAULT '{}'::JSONB;