diff --git a/api-reference/v1/openapi_spec_v1.json b/api-reference/v1/openapi_spec_v1.json index eb14c88272..8b38587077 100644 --- a/api-reference/v1/openapi_spec_v1.json +++ b/api-reference/v1/openapi_spec_v1.json @@ -10939,6 +10939,16 @@ "type": "string", "description": "The device model of the client", "nullable": true + }, + "accept_language": { + "type": "string", + "description": "Accept-language of the browser", + "nullable": true + }, + "referer": { + "type": "string", + "description": "Identifier of the source that initiated the request.", + "nullable": true } } }, @@ -26637,6 +26647,14 @@ "type": "string", "description": "Identifier for payout method", "nullable": true + }, + "browser_info": { + "allOf": [ + { + "$ref": "#/components/schemas/BrowserInformation" + } + ], + "nullable": true } } }, @@ -26647,6 +26665,7 @@ "adyenplatform", "cybersource", "ebanx", + "gigadat", "nomupay", "nuvei", "payone", @@ -27614,6 +27633,14 @@ "type": "string", "description": "Identifier for payout method", "nullable": true + }, + "browser_info": { + "allOf": [ + { + "$ref": "#/components/schemas/BrowserInformation" + } + ], + "nullable": true } } }, @@ -27838,6 +27865,14 @@ "type": "string", "description": "Identifier for payout method", "nullable": true + }, + "browser_info": { + "allOf": [ + { + "$ref": "#/components/schemas/BrowserInformation" + } + ], + "nullable": true } } }, diff --git a/api-reference/v2/openapi_spec_v2.json b/api-reference/v2/openapi_spec_v2.json index 67350969a6..b38b5139be 100644 --- a/api-reference/v2/openapi_spec_v2.json +++ b/api-reference/v2/openapi_spec_v2.json @@ -20741,6 +20741,7 @@ "adyenplatform", "cybersource", "ebanx", + "gigadat", "nomupay", "nuvei", "payone", @@ -21041,6 +21042,14 @@ "type": "string", "description": "Identifier for payout method", "nullable": true + }, + "browser_info": { + "allOf": [ + { + "$ref": "#/components/schemas/BrowserInformation" + } + ], + "nullable": true } }, "additionalProperties": false diff --git a/crates/api_models/src/enums.rs b/crates/api_models/src/enums.rs index edc6bf7875..de46df2373 100644 --- a/crates/api_models/src/enums.rs +++ b/crates/api_models/src/enums.rs @@ -48,6 +48,7 @@ pub enum PayoutConnectors { Adyenplatform, Cybersource, Ebanx, + Gigadat, Nomupay, Nuvei, Payone, @@ -75,6 +76,7 @@ impl From for RoutableConnectors { PayoutConnectors::Adyenplatform => Self::Adyenplatform, PayoutConnectors::Cybersource => Self::Cybersource, PayoutConnectors::Ebanx => Self::Ebanx, + PayoutConnectors::Gigadat => Self::Gigadat, PayoutConnectors::Nomupay => Self::Nomupay, PayoutConnectors::Nuvei => Self::Nuvei, PayoutConnectors::Payone => Self::Payone, @@ -93,6 +95,7 @@ impl From for Connector { PayoutConnectors::Adyenplatform => Self::Adyenplatform, PayoutConnectors::Cybersource => Self::Cybersource, PayoutConnectors::Ebanx => Self::Ebanx, + PayoutConnectors::Gigadat => Self::Gigadat, PayoutConnectors::Nomupay => Self::Nomupay, PayoutConnectors::Nuvei => Self::Nuvei, PayoutConnectors::Payone => Self::Payone, diff --git a/crates/api_models/src/payments.rs b/crates/api_models/src/payments.rs index 55e13f5b09..5f38ee86f9 100644 --- a/crates/api_models/src/payments.rs +++ b/crates/api_models/src/payments.rs @@ -1597,6 +1597,12 @@ pub struct BrowserInformation { /// The device model of the client pub device_model: Option, + + /// Accept-language of the browser + pub accept_language: Option, + + /// Identifier of the source that initiated the request. + pub referer: Option, } impl RequestSurchargeDetails { diff --git a/crates/api_models/src/payouts.rs b/crates/api_models/src/payouts.rs index a6fdaa2b6e..d61fc5bbc0 100644 --- a/crates/api_models/src/payouts.rs +++ b/crates/api_models/src/payouts.rs @@ -1,6 +1,8 @@ use std::collections::HashMap; use cards::CardNumber; +#[cfg(feature = "v2")] +use common_utils::types::BrowserInformation; use common_utils::{ consts::default_payouts_list_limit, crypto, id_type, link_utils, payout_method_utils, @@ -9,6 +11,8 @@ use common_utils::{ types::{UnifiedCode, UnifiedMessage}, }; use masking::Secret; +#[cfg(feature = "v1")] +use payments::BrowserInformation; use router_derive::FlatStruct; use serde::{Deserialize, Serialize}; use time::PrimitiveDateTime; @@ -191,6 +195,10 @@ pub struct PayoutCreateRequest { /// Identifier for payout method pub payout_method_id: Option, + + /// Additional details required by 3DS 2.0 + #[schema(value_type = Option)] + pub browser_info: Option, } impl PayoutCreateRequest { diff --git a/crates/connector_configs/src/connector.rs b/crates/connector_configs/src/connector.rs index a0d7e90306..e69ba486c3 100644 --- a/crates/connector_configs/src/connector.rs +++ b/crates/connector_configs/src/connector.rs @@ -285,6 +285,8 @@ pub struct ConnectorConfig { pub forte: Option, pub getnet: Option, pub gigadat: Option, + #[cfg(feature = "payouts")] + pub gigadat_payout: Option, pub globalpay: Option, pub globepay: Option, pub gocardless: Option, @@ -399,6 +401,7 @@ impl ConnectorConfig { PayoutConnectors::Adyenplatform => Ok(connector_data.adyenplatform_payout), PayoutConnectors::Cybersource => Ok(connector_data.cybersource_payout), PayoutConnectors::Ebanx => Ok(connector_data.ebanx_payout), + PayoutConnectors::Gigadat => Ok(connector_data.gigadat_payout), PayoutConnectors::Nomupay => Ok(connector_data.nomupay_payout), PayoutConnectors::Nuvei => Ok(connector_data.nuvei_payout), PayoutConnectors::Payone => Ok(connector_data.payone_payout), diff --git a/crates/connector_configs/toml/development.toml b/crates/connector_configs/toml/development.toml index 11f17b6988..1a1b9b1efb 100644 --- a/crates/connector_configs/toml/development.toml +++ b/crates/connector_configs/toml/development.toml @@ -7221,6 +7221,20 @@ placeholder = "Enter site where transaction is initiated" required = true type = "Text" +[gigadat_payout] +[gigadat_payout.connector_auth.SignatureKey] +api_key = "Access Token" +api_secret = "Security Token" +key1 = "Campaign ID" +[[gigadat_payout.bank_redirect]] +payment_method_type = "interac" +[gigadat_payout.metadata.site] +name = "site" +label = "Site where transaction is initiated" +placeholder = "Enter site where transaction is initiated" +required = true +type = "Text" + [finix] [finix.connector_auth.HeaderKey] api_key = "API Key" diff --git a/crates/connector_configs/toml/production.toml b/crates/connector_configs/toml/production.toml index cc39c1fbc4..a65dcde1e2 100644 --- a/crates/connector_configs/toml/production.toml +++ b/crates/connector_configs/toml/production.toml @@ -5959,6 +5959,20 @@ placeholder = "Enter site where transaction is initiated" required = true type = "Text" +[gigadat_payout] +[gigadat_payout.connector_auth.SignatureKey] +api_key = "Access Token" +api_secret = "Security Token" +key1 = "Campaign ID" +[[gigadat_payout.bank_redirect]] +payment_method_type = "interac" +[gigadat_payout.metadata.site] +name = "site" +label = "Site where transaction is initiated" +placeholder = "Enter site where transaction is initiated" +required = true +type = "Text" + [finix] [finix.connector_auth.HeaderKey] api_key = "API Key" diff --git a/crates/connector_configs/toml/sandbox.toml b/crates/connector_configs/toml/sandbox.toml index 0bc4aa79ed..124286a88a 100644 --- a/crates/connector_configs/toml/sandbox.toml +++ b/crates/connector_configs/toml/sandbox.toml @@ -7200,6 +7200,19 @@ placeholder = "Enter site where transaction is initiated" required = true type = "Text" +[gigadat_payout] +[gigadat_payout.connector_auth.SignatureKey] +api_key = "Access Token" +api_secret = "Security Token" +key1 = "Campaign ID" +[[gigadat_payout.bank_redirect]] +payment_method_type = "interac" +[gigadat_payout.metadata.site] +name = "site" +label = "Site where transaction is initiated" +placeholder = "Enter site where transaction is initiated" +required = true +type = "Text" [finix] [finix.connector_auth.HeaderKey] diff --git a/crates/hyperswitch_connectors/src/connectors/gigadat.rs b/crates/hyperswitch_connectors/src/connectors/gigadat.rs index 973e1ae6f3..33910dce4b 100644 --- a/crates/hyperswitch_connectors/src/connectors/gigadat.rs +++ b/crates/hyperswitch_connectors/src/connectors/gigadat.rs @@ -32,6 +32,13 @@ use hyperswitch_domain_models::{ RefundsRouterData, }, }; +#[cfg(feature = "payouts")] +use hyperswitch_domain_models::{ + router_flow_types::{PoCreate, PoFulfill, PoQuote}, + types::{PayoutsData, PayoutsResponseData, PayoutsRouterData}, +}; +#[cfg(feature = "payouts")] +use hyperswitch_interfaces::types::{PayoutCreateType, PayoutFulfillType, PayoutQuoteType}; use hyperswitch_interfaces::{ api::{ self, ConnectorCommon, ConnectorCommonExt, ConnectorIntegration, ConnectorSpecifications, @@ -45,6 +52,8 @@ use hyperswitch_interfaces::{ }; use lazy_static::lazy_static; use masking::{Mask, PeekInterface}; +#[cfg(feature = "payouts")] +use router_env::{instrument, tracing}; use transformers as gigadat; use uuid::Uuid; @@ -75,6 +84,12 @@ impl api::Refund for Gigadat {} impl api::RefundExecute for Gigadat {} impl api::RefundSync for Gigadat {} impl api::PaymentToken for Gigadat {} +#[cfg(feature = "payouts")] +impl api::PayoutQuote for Gigadat {} +#[cfg(feature = "payouts")] +impl api::PayoutCreate for Gigadat {} +#[cfg(feature = "payouts")] +impl api::PayoutFulfill for Gigadat {} impl ConnectorIntegration for Gigadat @@ -578,6 +593,245 @@ impl ConnectorIntegration for Gigadat { //Gigadat does not support Refund Sync } +#[cfg(feature = "payouts")] +impl ConnectorIntegration for Gigadat { + fn get_headers( + &self, + req: &PayoutsRouterData, + connectors: &Connectors, + ) -> CustomResult)>, errors::ConnectorError> { + self.build_headers(req, connectors) + } + + fn get_url( + &self, + req: &PayoutsRouterData, + connectors: &Connectors, + ) -> CustomResult { + let auth = gigadat::GigadatAuthType::try_from(&req.connector_auth_type) + .change_context(errors::ConnectorError::FailedToObtainAuthType)?; + Ok(format!( + "{}api/payment-token/{}", + self.base_url(connectors), + auth.campaign_id.peek() + )) + } + + fn get_request_body( + &self, + req: &PayoutsRouterData, + _connectors: &Connectors, + ) -> CustomResult { + let amount = utils::convert_amount( + self.amount_converter, + req.request.minor_amount, + req.request.destination_currency, + )?; + + let connector_router_data = gigadat::GigadatRouterData::from((amount, req)); + let connector_req = gigadat::GigadatPayoutQuoteRequest::try_from(&connector_router_data)?; + Ok(RequestContent::Json(Box::new(connector_req))) + } + + fn build_request( + &self, + req: &PayoutsRouterData, + connectors: &Connectors, + ) -> CustomResult, errors::ConnectorError> { + let request = RequestBuilder::new() + .method(Method::Post) + .url(&PayoutQuoteType::get_url(self, req, connectors)?) + .attach_default_headers() + .headers(PayoutQuoteType::get_headers(self, req, connectors)?) + .set_body(PayoutQuoteType::get_request_body(self, req, connectors)?) + .build(); + + Ok(Some(request)) + } + + #[instrument(skip_all)] + fn handle_response( + &self, + data: &PayoutsRouterData, + event_builder: Option<&mut ConnectorEvent>, + res: Response, + ) -> CustomResult, errors::ConnectorError> { + let response: gigadat::GigadatPayoutResponse = res + .response + .parse_struct("GigadatPayoutResponse") + .change_context(errors::ConnectorError::ResponseDeserializationFailed)?; + + event_builder.map(|i| i.set_response_body(&response)); + router_env::logger::info!(connector_response=?response); + + RouterData::try_from(ResponseRouterData { + response, + data: data.clone(), + http_code: res.status_code, + }) + } + + fn get_error_response( + &self, + res: Response, + event_builder: Option<&mut ConnectorEvent>, + ) -> CustomResult { + self.build_error_response(res, event_builder) + } +} + +#[async_trait::async_trait] +#[cfg(feature = "payouts")] +impl ConnectorIntegration for Gigadat { + fn get_headers( + &self, + req: &PayoutsRouterData, + connectors: &Connectors, + ) -> CustomResult)>, errors::ConnectorError> { + self.build_headers(req, connectors) + } + + fn get_url( + &self, + req: &PayoutsRouterData, + connectors: &Connectors, + ) -> CustomResult { + let transfer_id = req.request.connector_payout_id.to_owned().ok_or( + errors::ConnectorError::MissingRequiredField { + field_name: "transfer_id", + }, + )?; + Ok(format!( + "{}webflow?transaction={}", + self.base_url(connectors), + transfer_id, + )) + } + + fn build_request( + &self, + req: &PayoutsRouterData, + connectors: &Connectors, + ) -> CustomResult, errors::ConnectorError> { + let request = RequestBuilder::new() + .method(Method::Post) + .url(&PayoutCreateType::get_url(self, req, connectors)?) + .attach_default_headers() + .headers(PayoutCreateType::get_headers(self, req, connectors)?) + .build(); + + Ok(Some(request)) + } + + #[instrument(skip_all)] + fn handle_response( + &self, + data: &PayoutsRouterData, + _event_builder: Option<&mut ConnectorEvent>, + res: Response, + ) -> CustomResult, errors::ConnectorError> { + router_env::logger::debug!("Expected zero bytes response, skipped parsing of the response"); + + let status = if res.status_code == 200 { + enums::PayoutStatus::RequiresFulfillment + } else { + enums::PayoutStatus::Failed + }; + Ok(PayoutsRouterData { + response: Ok(PayoutsResponseData { + status: Some(status), + connector_payout_id: None, + payout_eligible: None, + should_add_next_step_to_process_tracker: false, + error_code: None, + error_message: None, + }), + ..data.clone() + }) + } + + fn get_error_response( + &self, + res: Response, + event_builder: Option<&mut ConnectorEvent>, + ) -> CustomResult { + self.build_error_response(res, event_builder) + } +} + +#[cfg(feature = "payouts")] +impl ConnectorIntegration for Gigadat { + fn get_headers( + &self, + req: &PayoutsRouterData, + connectors: &Connectors, + ) -> CustomResult)>, errors::ConnectorError> { + self.build_headers(req, connectors) + } + + fn get_url( + &self, + req: &PayoutsRouterData, + connectors: &Connectors, + ) -> CustomResult { + let transfer_id = req.request.connector_payout_id.to_owned().ok_or( + errors::ConnectorError::MissingRequiredField { + field_name: "transfer_id", + }, + )?; + Ok(format!( + "{}webflow?transaction={}", + self.base_url(connectors), + transfer_id, + )) + } + + fn build_request( + &self, + req: &PayoutsRouterData, + connectors: &Connectors, + ) -> CustomResult, errors::ConnectorError> { + let request = RequestBuilder::new() + .method(Method::Get) + .url(&PayoutFulfillType::get_url(self, req, connectors)?) + .attach_default_headers() + .headers(PayoutFulfillType::get_headers(self, req, connectors)?) + .build(); + + Ok(Some(request)) + } + + #[instrument(skip_all)] + fn handle_response( + &self, + data: &PayoutsRouterData, + event_builder: Option<&mut ConnectorEvent>, + res: Response, + ) -> CustomResult, errors::ConnectorError> { + let response: gigadat::GigadatPayoutResponse = res + .response + .parse_struct("GigadatPayoutResponse") + .change_context(errors::ConnectorError::ResponseDeserializationFailed)?; + + event_builder.map(|i| i.set_response_body(&response)); + router_env::logger::info!(connector_response=?response); + + RouterData::try_from(ResponseRouterData { + response, + data: data.clone(), + http_code: res.status_code, + }) + } + + fn get_error_response( + &self, + res: Response, + event_builder: Option<&mut ConnectorEvent>, + ) -> CustomResult { + self.build_error_response(res, event_builder) + } +} + #[async_trait::async_trait] impl webhooks::IncomingWebhook for Gigadat { fn get_webhook_object_reference_id( diff --git a/crates/hyperswitch_connectors/src/connectors/gigadat/transformers.rs b/crates/hyperswitch_connectors/src/connectors/gigadat/transformers.rs index 060628f7a4..a4bfc9c01a 100644 --- a/crates/hyperswitch_connectors/src/connectors/gigadat/transformers.rs +++ b/crates/hyperswitch_connectors/src/connectors/gigadat/transformers.rs @@ -14,10 +14,17 @@ use hyperswitch_domain_models::{ router_response_types::{PaymentsResponseData, RedirectForm, RefundsResponseData}, types::{PaymentsAuthorizeRouterData, RefundsRouterData}, }; +#[cfg(feature = "payouts")] +use hyperswitch_domain_models::{ + router_flow_types::PoQuote, router_response_types::PayoutsResponseData, + types::PayoutsRouterData, +}; use hyperswitch_interfaces::errors; use masking::{PeekInterface, Secret}; use serde::{Deserialize, Serialize}; +#[cfg(feature = "payouts")] +use crate::{types::PayoutsResponseRouterData, utils::PayoutsData as _}; use crate::{ types::{RefundsResponseRouterData, ResponseRouterData}, utils::{self, BrowserInformationData, PaymentsAuthorizeRequestData, RouterData as _}, @@ -56,7 +63,7 @@ impl TryFrom<&Option> for GigadatConnectorMetadataObject } // CPI (Combined Pay-in) Request Structure for Gigadat -#[derive(Debug, Serialize, PartialEq)] +#[derive(Debug, Serialize)] #[serde(rename_all = "camelCase")] pub struct GigadatCpiRequest { pub user_id: id_type::CustomerId, @@ -73,10 +80,11 @@ pub struct GigadatCpiRequest { pub mobile: Secret, } -#[derive(Debug, Serialize, Deserialize, PartialEq)] +#[derive(Debug, Serialize, Deserialize)] #[serde(rename_all = "UPPERCASE")] pub enum GidadatTransactionType { Cpi, + Eto, } impl TryFrom<&GigadatRouterData<&PaymentsAuthorizeRouterData>> for GigadatCpiRequest { @@ -153,19 +161,19 @@ impl TryFrom<&ConnectorAuthType> for GigadatAuthType { } } -#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] +#[derive(Debug, Clone, Serialize, Deserialize)] pub struct GigadatPaymentResponse { pub token: Secret, pub data: GigadatPaymentData, } -#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] +#[derive(Debug, Clone, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct GigadatPaymentData { pub transaction_id: String, } -#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] +#[derive(Debug, Clone, Serialize, Deserialize)] #[serde(rename_all = "SCREAMING_SNAKE_CASE")] pub enum GigadatPaymentStatus { StatusInited, @@ -194,7 +202,7 @@ impl From for enums::AttemptStatus { } } -#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] +#[derive(Debug, Clone, Serialize, Deserialize)] pub struct GigadatTransactionStatusResponse { pub status: GigadatPaymentStatus, } @@ -310,18 +318,107 @@ impl TryFrom> for RefundsRout } } -#[derive(Default, Debug, Serialize, Deserialize, PartialEq)] +#[derive(Debug, Serialize)] +#[serde(rename_all = "camelCase")] +pub struct GigadatPayoutQuoteRequest { + pub amount: FloatMajorUnit, + pub campaign: Secret, + pub currency: Currency, + pub email: Email, + pub mobile: Secret, + pub name: Secret, + pub site: String, + pub transaction_id: String, + #[serde(rename = "type")] + pub transaction_type: GidadatTransactionType, + pub user_id: id_type::CustomerId, + pub user_ip: Secret, +} + +// Payouts fulfill request transform +#[cfg(feature = "payouts")] +impl TryFrom<&GigadatRouterData<&PayoutsRouterData>> for GigadatPayoutQuoteRequest { + type Error = error_stack::Report; + fn try_from( + item: &GigadatRouterData<&PayoutsRouterData>, + ) -> Result { + let metadata: GigadatConnectorMetadataObject = + utils::to_connector_meta_from_secret(item.router_data.connector_meta_data.clone()) + .change_context(errors::ConnectorError::InvalidConnectorConfig { + config: "merchant_connector_account.metadata", + })?; + + let router_data = item.router_data; + let name = router_data.get_billing_full_name()?; + let email = router_data.get_billing_email()?; + let mobile = router_data.get_billing_phone_number()?; + let currency = item.router_data.request.destination_currency; + + let user_ip = router_data.request.get_browser_info()?.get_ip_address()?; + let auth_type = GigadatAuthType::try_from(&item.router_data.connector_auth_type)?; + + Ok(Self { + user_id: router_data.get_customer_id()?, + site: metadata.site, + user_ip, + currency, + amount: item.amount, + transaction_id: router_data.connector_request_reference_id.clone(), + transaction_type: GidadatTransactionType::Eto, + name, + email, + mobile, + campaign: auth_type.campaign_id, + }) + } +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct GigadatPayoutResponse { + pub token: Secret, + pub data: GigadatPayoutData, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct GigadatPayoutData { + pub transaction_id: String, + #[serde(rename = "type")] + pub transaction_type: String, +} + +#[cfg(feature = "payouts")] +impl TryFrom> for PayoutsRouterData { + type Error = error_stack::Report; + fn try_from( + item: PayoutsResponseRouterData, + ) -> Result { + Ok(Self { + response: Ok(PayoutsResponseData { + status: None, + connector_payout_id: Some(item.response.data.transaction_id), + payout_eligible: None, + should_add_next_step_to_process_tracker: false, + error_code: None, + error_message: None, + }), + ..item.data + }) + } +} + +#[derive(Default, Debug, Serialize, Deserialize)] pub struct GigadatErrorResponse { pub err: String, } -#[derive(Default, Debug, Serialize, Deserialize, PartialEq)] +#[derive(Default, Debug, Serialize, Deserialize)] pub struct GigadatRefundErrorResponse { pub error: Vec, pub message: String, } -#[derive(Default, Debug, Serialize, Deserialize, PartialEq)] +#[derive(Default, Debug, Serialize, Deserialize)] pub struct Error { pub code: Option, pub detail: String, diff --git a/crates/hyperswitch_connectors/src/default_implementations.rs b/crates/hyperswitch_connectors/src/default_implementations.rs index 31b474cd3c..b3eb01ba92 100644 --- a/crates/hyperswitch_connectors/src/default_implementations.rs +++ b/crates/hyperswitch_connectors/src/default_implementations.rs @@ -3992,7 +3992,6 @@ default_imp_for_payouts_create!( connectors::Flexiti, connectors::Forte, connectors::Getnet, - connectors::Gigadat, connectors::Globalpay, connectors::Globepay, connectors::Gocardless, @@ -4433,7 +4432,6 @@ default_imp_for_payouts_fulfill!( connectors::Flexiti, connectors::Forte, connectors::Getnet, - connectors::Gigadat, connectors::Globalpay, connectors::Globepay, connectors::Gocardless, @@ -4724,7 +4722,6 @@ default_imp_for_payouts_quote!( connectors::Flexiti, connectors::Forte, connectors::Getnet, - connectors::Gigadat, connectors::Globalpay, connectors::Globepay, connectors::Gocardless, diff --git a/crates/hyperswitch_connectors/src/utils.rs b/crates/hyperswitch_connectors/src/utils.rs index 182c606720..fd3e68f801 100644 --- a/crates/hyperswitch_connectors/src/utils.rs +++ b/crates/hyperswitch_connectors/src/utils.rs @@ -6399,6 +6399,7 @@ pub trait PayoutsData { fn get_vendor_details(&self) -> Result; fn get_payout_type(&self) -> Result; fn get_webhook_url(&self) -> Result; + fn get_browser_info(&self) -> Result; } #[cfg(feature = "payouts")] @@ -6430,6 +6431,11 @@ impl PayoutsData for hyperswitch_domain_models::router_request_types::PayoutsDat .to_owned() .ok_or_else(missing_field_err("webhook_url")) } + fn get_browser_info(&self) -> Result { + self.browser_info + .clone() + .ok_or_else(missing_field_err("browser_info")) + } } pub trait RevokeMandateRequestData { fn get_connector_mandate_id(&self) -> Result; diff --git a/crates/hyperswitch_domain_models/src/router_request_types.rs b/crates/hyperswitch_domain_models/src/router_request_types.rs index e035af3b07..415b97d1af 100644 --- a/crates/hyperswitch_domain_models/src/router_request_types.rs +++ b/crates/hyperswitch_domain_models/src/router_request_types.rs @@ -939,6 +939,29 @@ impl From for BrowserInformation { } } +#[cfg(feature = "v1")] +impl From for BrowserInformation { + fn from(value: api_models::payments::BrowserInformation) -> Self { + Self { + color_depth: value.color_depth, + java_enabled: value.java_enabled, + java_script_enabled: value.java_script_enabled, + language: value.language, + screen_height: value.screen_height, + screen_width: value.screen_width, + time_zone: value.time_zone, + ip_address: value.ip_address, + accept_header: value.accept_header, + user_agent: value.user_agent, + os_type: value.os_type, + os_version: value.os_version, + device_model: value.device_model, + accept_language: value.accept_language, + referer: value.referer, + } + } +} + #[derive(Debug, Clone, Default, Serialize)] pub enum ResponseId { ConnectorTransactionId(String), @@ -1316,6 +1339,7 @@ pub struct PayoutsData { pub priority: Option, pub connector_transfer_method_id: Option, pub webhook_url: Option, + pub browser_info: Option, } #[derive(Debug, Default, Clone)] diff --git a/crates/router/src/core/payouts.rs b/crates/router/src/core/payouts.rs index c132ec8d12..90ba7f161b 100644 --- a/crates/router/src/core/payouts.rs +++ b/crates/router/src/core/payouts.rs @@ -79,6 +79,7 @@ pub struct PayoutData { pub current_locale: String, pub payment_method: Option, pub connector_transfer_method_id: Option, + pub browser_info: Option, } // ********************************************** CORE FLOWS ********************************************** @@ -533,12 +534,12 @@ pub async fn payouts_retrieve_core( ) .await?; - complete_payout_retrieve( + Box::pin(complete_payout_retrieve( &state, &merchant_context, connector_call_type, &mut payout_data, - ) + )) .await?; } @@ -2874,6 +2875,7 @@ pub async fn payout_create_db_entries( current_locale: locale.to_string(), payment_method, connector_transfer_method_id: None, + browser_info: req.browser_info.clone().map(Into::into), }) } @@ -2906,6 +2908,12 @@ pub async fn make_payout_data( payouts::PayoutRequest::PayoutRetrieveRequest(r) => r.payout_id.clone(), }; + let browser_info = match req { + payouts::PayoutRequest::PayoutActionRequest(_) => None, + payouts::PayoutRequest::PayoutCreateRequest(r) => r.browser_info.clone().map(Into::into), + payouts::PayoutRequest::PayoutRetrieveRequest(_) => None, + }; + let payouts = db .find_payout_by_merchant_id_payout_id( merchant_id, @@ -3100,6 +3108,7 @@ pub async fn make_payout_data( current_locale: locale.to_string(), payment_method, connector_transfer_method_id: None, + browser_info, }) } diff --git a/crates/router/src/core/utils.rs b/crates/router/src/core/utils.rs index 30bc466063..367973ddbd 100644 --- a/crates/router/src/core/utils.rs +++ b/crates/router/src/core/utils.rs @@ -152,6 +152,8 @@ pub async fn construct_payout_router_data<'a, F>( let connector_transfer_method_id = payout_helpers::should_create_connector_transfer_method(&*payout_data, connector_data)?; + let browser_info = payout_data.browser_info.to_owned(); + let router_data = types::RouterData { flow: PhantomData, merchant_id: merchant_context.get_merchant_account().get_id().to_owned(), @@ -197,6 +199,7 @@ pub async fn construct_payout_router_data<'a, F>( }), connector_transfer_method_id, webhook_url: Some(webhook_url), + browser_info, }, response: Ok(types::PayoutsResponseData::default()), access_token: None, diff --git a/crates/router/tests/connectors/utils.rs b/crates/router/tests/connectors/utils.rs index 7537b9117b..8d7493d0c4 100644 --- a/crates/router/tests/connectors/utils.rs +++ b/crates/router/tests/connectors/utils.rs @@ -478,6 +478,7 @@ pub trait ConnectorActions: Connector { priority: None, connector_transfer_method_id: None, webhook_url: None, + browser_info: None, }, payment_info, )