diff --git a/config/config.example.toml b/config/config.example.toml index 84f423714a..040e027dcf 100644 --- a/config/config.example.toml +++ b/config/config.example.toml @@ -274,6 +274,7 @@ razorpay.base_url = "https://api.razorpay.com/" recurly.base_url = "https://v3.recurly.com" redsys.base_url = "https://sis-t.redsys.es:25443" riskified.base_url = "https://sandbox.riskified.com/api" +santander.base_url = "https://pix.santander.com.br/api/v1/sandbox/" shift4.base_url = "https://api.shift4.com/" signifyd.base_url = "https://api.signifyd.com/" square.base_url = "https://connect.squareupsandbox.com/" diff --git a/config/deployments/integration_test.toml b/config/deployments/integration_test.toml index 492de9d9a8..513cb58717 100644 --- a/config/deployments/integration_test.toml +++ b/config/deployments/integration_test.toml @@ -110,6 +110,7 @@ rapyd.base_url = "https://sandboxapi.rapyd.net" razorpay.base_url = "https://api.razorpay.com/" recurly.base_url = "https://v3.recurly.com" redsys.base_url = "https://sis-t.redsys.es:25443" +santander.base_url = "https://pix.santander.com.br/api/v1/sandbox/" shift4.base_url = "https://api.shift4.com/" signifyd.base_url = "https://api.signifyd.com/" riskified.base_url = "https://sandbox.riskified.com/api" diff --git a/config/deployments/production.toml b/config/deployments/production.toml index 368e388db6..4b990ad046 100644 --- a/config/deployments/production.toml +++ b/config/deployments/production.toml @@ -115,6 +115,7 @@ razorpay.base_url = "https://api.razorpay.com/" recurly.base_url = "https://v3.recurly.com" redsys.base_url = "https://sis.redsys.es" riskified.base_url = "https://wh.riskified.com/api/" +santander.base_url = "https://trust-pix.santander.com.br/" shift4.base_url = "https://api.shift4.com/" signifyd.base_url = "https://api.signifyd.com/" square.base_url = "https://connect.squareup.com/" diff --git a/config/deployments/sandbox.toml b/config/deployments/sandbox.toml index bb8e48868f..d00b12c751 100644 --- a/config/deployments/sandbox.toml +++ b/config/deployments/sandbox.toml @@ -115,6 +115,7 @@ razorpay.base_url = "https://api.razorpay.com/" recurly.base_url = "https://v3.recurly.com" redsys.base_url = "https://sis-t.redsys.es:25443" riskified.base_url = "https://sandbox.riskified.com/api" +santander.base_url = "https://pix.santander.com.br/api/v1/sandbox/" shift4.base_url = "https://api.shift4.com/" signifyd.base_url = "https://api.signifyd.com/" square.base_url = "https://connect.squareupsandbox.com/" diff --git a/config/development.toml b/config/development.toml index 49228eb25d..95484d6b01 100644 --- a/config/development.toml +++ b/config/development.toml @@ -165,6 +165,7 @@ cards = [ "powertranz", "prophetpay", "redsys", + "santander", "shift4", "square", "stax", @@ -306,6 +307,7 @@ razorpay.base_url = "https://api.razorpay.com/" recurly.base_url = "https://v3.recurly.com" redsys.base_url = "https://sis-t.redsys.es:25443" riskified.base_url = "https://sandbox.riskified.com/api" +santander.base_url = "https://pix.santander.com.br/api/v1/sandbox/" shift4.base_url = "https://api.shift4.com/" signifyd.base_url = "https://api.signifyd.com/" square.base_url = "https://connect.squareupsandbox.com/" diff --git a/config/docker_compose.toml b/config/docker_compose.toml index 06211cd2db..b029fb9987 100644 --- a/config/docker_compose.toml +++ b/config/docker_compose.toml @@ -200,6 +200,7 @@ razorpay.base_url = "https://api.razorpay.com/" recurly.base_url = "https://v3.recurly.com" redsys.base_url = "https://sis-t.redsys.es:25443" riskified.base_url = "https://sandbox.riskified.com/api" +santander.base_url = "https://pix.santander.com.br/api/v1/sandbox/" shift4.base_url = "https://api.shift4.com/" signifyd.base_url = "https://api.signifyd.com/" square.base_url = "https://connect.squareupsandbox.com/" @@ -310,6 +311,7 @@ cards = [ "powertranz", "prophetpay", "redsys", + "santander", "shift4", "square", "stax", diff --git a/crates/common_enums/src/connector_enums.rs b/crates/common_enums/src/connector_enums.rs index ea7f7e740a..0e358e030c 100644 --- a/crates/common_enums/src/connector_enums.rs +++ b/crates/common_enums/src/connector_enums.rs @@ -131,6 +131,7 @@ pub enum RoutableConnectors { Recurly, Redsys, Riskified, + // Santander, Shift4, Signifyd, Square, @@ -290,6 +291,7 @@ pub enum Connector { Razorpay, Recurly, Redsys, + // Santander, Shift4, Square, Stax, @@ -460,6 +462,7 @@ impl Connector { | Self::Rapyd | Self::Recurly | Self::Redsys + // | Self::Santander | Self::Shift4 | Self::Square | Self::Stax @@ -620,6 +623,7 @@ impl From for Connector { RoutableConnectors::Recurly => Self::Recurly, RoutableConnectors::Redsys => Self::Redsys, RoutableConnectors::Riskified => Self::Riskified, + // RoutableConnectors::Santander => Self::Santander, RoutableConnectors::Shift4 => Self::Shift4, RoutableConnectors::Signifyd => Self::Signifyd, RoutableConnectors::Square => Self::Square, @@ -735,6 +739,7 @@ impl TryFrom for RoutableConnectors { Connector::Rapyd => Ok(Self::Rapyd), Connector::Razorpay => Ok(Self::Razorpay), Connector::Riskified => Ok(Self::Riskified), + // Connector::Santander => Ok(Self::Santander), Connector::Shift4 => Ok(Self::Shift4), Connector::Signifyd => Ok(Self::Signifyd), Connector::Square => Ok(Self::Square), diff --git a/crates/connector_configs/src/connector.rs b/crates/connector_configs/src/connector.rs index 9d48cbb6da..2c0bd6fd8f 100644 --- a/crates/connector_configs/src/connector.rs +++ b/crates/connector_configs/src/connector.rs @@ -255,6 +255,7 @@ pub struct ConnectorConfig { pub riskified: Option, pub rapyd: Option, pub redsys: Option, + pub santander: Option, pub shift4: Option, pub stripe: Option, #[cfg(feature = "payouts")] diff --git a/crates/hyperswitch_connectors/src/connectors.rs b/crates/hyperswitch_connectors/src/connectors.rs index 15dc705db5..7395ab45d3 100644 --- a/crates/hyperswitch_connectors/src/connectors.rs +++ b/crates/hyperswitch_connectors/src/connectors.rs @@ -80,6 +80,7 @@ pub mod razorpay; pub mod recurly; pub mod redsys; pub mod riskified; +pub mod santander; pub mod shift4; pub mod signifyd; pub mod square; @@ -126,11 +127,11 @@ pub use self::{ nuvei::Nuvei, opayo::Opayo, opennode::Opennode, paybox::Paybox, payeezy::Payeezy, payme::Payme, payone::Payone, paypal::Paypal, paystack::Paystack, payu::Payu, placetopay::Placetopay, plaid::Plaid, powertranz::Powertranz, prophetpay::Prophetpay, rapyd::Rapyd, razorpay::Razorpay, - recurly::Recurly, redsys::Redsys, riskified::Riskified, shift4::Shift4, signifyd::Signifyd, - square::Square, stax::Stax, stripe::Stripe, stripebilling::Stripebilling, taxjar::Taxjar, - threedsecureio::Threedsecureio, thunes::Thunes, tokenio::Tokenio, trustpay::Trustpay, - tsys::Tsys, unified_authentication_service::UnifiedAuthenticationService, vgs::Vgs, volt::Volt, - wellsfargo::Wellsfargo, wellsfargopayout::Wellsfargopayout, wise::Wise, worldline::Worldline, - worldpay::Worldpay, worldpayvantiv::Worldpayvantiv, worldpayxml::Worldpayxml, xendit::Xendit, - zen::Zen, zsl::Zsl, + recurly::Recurly, redsys::Redsys, riskified::Riskified, santander::Santander, shift4::Shift4, + signifyd::Signifyd, square::Square, stax::Stax, stripe::Stripe, stripebilling::Stripebilling, + taxjar::Taxjar, threedsecureio::Threedsecureio, thunes::Thunes, tokenio::Tokenio, + trustpay::Trustpay, tsys::Tsys, unified_authentication_service::UnifiedAuthenticationService, + vgs::Vgs, volt::Volt, wellsfargo::Wellsfargo, wellsfargopayout::Wellsfargopayout, wise::Wise, + worldline::Worldline, worldpay::Worldpay, worldpayvantiv::Worldpayvantiv, + worldpayxml::Worldpayxml, xendit::Xendit, zen::Zen, zsl::Zsl, }; diff --git a/crates/hyperswitch_connectors/src/connectors/santander.rs b/crates/hyperswitch_connectors/src/connectors/santander.rs new file mode 100644 index 0000000000..6320d1ae9b --- /dev/null +++ b/crates/hyperswitch_connectors/src/connectors/santander.rs @@ -0,0 +1,574 @@ +pub mod transformers; + +use common_utils::{ + errors::CustomResult, + ext_traits::BytesExt, + request::{Method, Request, RequestBuilder, RequestContent}, + types::{AmountConvertor, StringMajorUnit, StringMajorUnitForConnector}, +}; +use error_stack::{report, ResultExt}; +use hyperswitch_domain_models::{ + router_data::{AccessToken, ConnectorAuthType, ErrorResponse, RouterData}, + router_flow_types::{ + access_token_auth::AccessTokenAuth, + payments::{Authorize, Capture, PSync, PaymentMethodToken, Session, SetupMandate, Void}, + refunds::{Execute, RSync}, + }, + router_request_types::{ + AccessTokenRequestData, PaymentMethodTokenizationData, PaymentsAuthorizeData, + PaymentsCancelData, PaymentsCaptureData, PaymentsSessionData, PaymentsSyncData, + RefundsData, SetupMandateRequestData, + }, + router_response_types::{PaymentsResponseData, RefundsResponseData}, + types::{ + PaymentsAuthorizeRouterData, PaymentsCaptureRouterData, PaymentsSyncRouterData, + RefundSyncRouterData, RefundsRouterData, + }, +}; +use hyperswitch_interfaces::{ + api::{ + self, ConnectorCommon, ConnectorCommonExt, ConnectorIntegration, ConnectorSpecifications, + ConnectorValidation, + }, + configs::Connectors, + errors, + events::connector_api_logs::ConnectorEvent, + types::{self, Response}, + webhooks, +}; +use masking::{ExposeInterface, Mask}; +use transformers as santander; + +use crate::{constants::headers, types::ResponseRouterData, utils}; + +#[derive(Clone)] +pub struct Santander { + amount_converter: &'static (dyn AmountConvertor + Sync), +} + +impl Santander { + pub fn new() -> &'static Self { + &Self { + amount_converter: &StringMajorUnitForConnector, + } + } +} + +impl api::Payment for Santander {} +impl api::PaymentSession for Santander {} +impl api::ConnectorAccessToken for Santander {} +impl api::MandateSetup for Santander {} +impl api::PaymentAuthorize for Santander {} +impl api::PaymentSync for Santander {} +impl api::PaymentCapture for Santander {} +impl api::PaymentVoid for Santander {} +impl api::Refund for Santander {} +impl api::RefundExecute for Santander {} +impl api::RefundSync for Santander {} +impl api::PaymentToken for Santander {} + +impl ConnectorIntegration + for Santander +{ + // Not Implemented (R) +} + +impl ConnectorCommonExt for Santander +where + Self: ConnectorIntegration, +{ + fn build_headers( + &self, + req: &RouterData, + _connectors: &Connectors, + ) -> CustomResult)>, errors::ConnectorError> { + let mut header = vec![( + headers::CONTENT_TYPE.to_string(), + self.get_content_type().to_string().into(), + )]; + let mut api_key = self.get_auth_header(&req.connector_auth_type)?; + header.append(&mut api_key); + Ok(header) + } +} + +impl ConnectorCommon for Santander { + fn id(&self) -> &'static str { + "santander" + } + + fn get_currency_unit(&self) -> api::CurrencyUnit { + api::CurrencyUnit::Minor + // TODO! Check connector documentation, on which unit they are processing the currency. + // If the connector accepts amount in lower unit ( i.e cents for USD) then return api::CurrencyUnit::Minor, + // if connector accepts amount in base unit (i.e dollars for USD) then return api::CurrencyUnit::Base + } + + fn common_get_content_type(&self) -> &'static str { + "application/json" + } + + fn base_url<'a>(&self, connectors: &'a Connectors) -> &'a str { + connectors.santander.base_url.as_ref() + } + + fn get_auth_header( + &self, + auth_type: &ConnectorAuthType, + ) -> CustomResult)>, errors::ConnectorError> { + let auth = santander::SantanderAuthType::try_from(auth_type) + .change_context(errors::ConnectorError::FailedToObtainAuthType)?; + Ok(vec![( + headers::AUTHORIZATION.to_string(), + auth.api_key.expose().into_masked(), + )]) + } + + fn build_error_response( + &self, + res: Response, + event_builder: Option<&mut ConnectorEvent>, + ) -> CustomResult { + let response: santander::SantanderErrorResponse = res + .response + .parse_struct("SantanderErrorResponse") + .change_context(errors::ConnectorError::ResponseDeserializationFailed)?; + + event_builder.map(|i| i.set_response_body(&response)); + router_env::logger::info!(connector_response=?response); + + Ok(ErrorResponse { + status_code: res.status_code, + code: response.code, + message: response.message, + reason: response.reason, + attempt_status: None, + connector_transaction_id: None, + network_advice_code: None, + network_decline_code: None, + network_error_message: None, + }) + } +} + +impl ConnectorValidation for Santander { + //TODO: implement functions when support enabled +} + +impl ConnectorIntegration for Santander { + //TODO: implement sessions flow +} + +impl ConnectorIntegration for Santander {} + +impl ConnectorIntegration + for Santander +{ +} + +impl ConnectorIntegration for Santander { + fn get_headers( + &self, + req: &PaymentsAuthorizeRouterData, + connectors: &Connectors, + ) -> CustomResult)>, errors::ConnectorError> { + self.build_headers(req, connectors) + } + + fn get_content_type(&self) -> &'static str { + self.common_get_content_type() + } + + fn get_url( + &self, + _req: &PaymentsAuthorizeRouterData, + _connectors: &Connectors, + ) -> CustomResult { + Err(errors::ConnectorError::NotImplemented("get_url method".to_string()).into()) + } + + fn get_request_body( + &self, + req: &PaymentsAuthorizeRouterData, + _connectors: &Connectors, + ) -> CustomResult { + let amount = utils::convert_amount( + self.amount_converter, + req.request.minor_amount, + req.request.currency, + )?; + + let connector_router_data = santander::SantanderRouterData::from((amount, req)); + let connector_req = santander::SantanderPaymentsRequest::try_from(&connector_router_data)?; + Ok(RequestContent::Json(Box::new(connector_req))) + } + + fn build_request( + &self, + req: &PaymentsAuthorizeRouterData, + connectors: &Connectors, + ) -> CustomResult, errors::ConnectorError> { + Ok(Some( + RequestBuilder::new() + .method(Method::Post) + .url(&types::PaymentsAuthorizeType::get_url( + self, req, connectors, + )?) + .attach_default_headers() + .headers(types::PaymentsAuthorizeType::get_headers( + self, req, connectors, + )?) + .set_body(types::PaymentsAuthorizeType::get_request_body( + self, req, connectors, + )?) + .build(), + )) + } + + fn handle_response( + &self, + data: &PaymentsAuthorizeRouterData, + event_builder: Option<&mut ConnectorEvent>, + res: Response, + ) -> CustomResult { + let response: santander::SantanderPaymentsResponse = res + .response + .parse_struct("Santander PaymentsAuthorizeResponse") + .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) + } +} + +impl ConnectorIntegration for Santander { + fn get_headers( + &self, + req: &PaymentsSyncRouterData, + connectors: &Connectors, + ) -> CustomResult)>, errors::ConnectorError> { + self.build_headers(req, connectors) + } + + fn get_content_type(&self) -> &'static str { + self.common_get_content_type() + } + + fn get_url( + &self, + _req: &PaymentsSyncRouterData, + _connectors: &Connectors, + ) -> CustomResult { + Err(errors::ConnectorError::NotImplemented("get_url method".to_string()).into()) + } + + fn build_request( + &self, + req: &PaymentsSyncRouterData, + connectors: &Connectors, + ) -> CustomResult, errors::ConnectorError> { + Ok(Some( + RequestBuilder::new() + .method(Method::Get) + .url(&types::PaymentsSyncType::get_url(self, req, connectors)?) + .attach_default_headers() + .headers(types::PaymentsSyncType::get_headers(self, req, connectors)?) + .build(), + )) + } + + fn handle_response( + &self, + data: &PaymentsSyncRouterData, + event_builder: Option<&mut ConnectorEvent>, + res: Response, + ) -> CustomResult { + let response: santander::SantanderPaymentsResponse = res + .response + .parse_struct("santander PaymentsSyncResponse") + .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) + } +} + +impl ConnectorIntegration for Santander { + fn get_headers( + &self, + req: &PaymentsCaptureRouterData, + connectors: &Connectors, + ) -> CustomResult)>, errors::ConnectorError> { + self.build_headers(req, connectors) + } + + fn get_content_type(&self) -> &'static str { + self.common_get_content_type() + } + + fn get_url( + &self, + _req: &PaymentsCaptureRouterData, + _connectors: &Connectors, + ) -> CustomResult { + Err(errors::ConnectorError::NotImplemented("get_url method".to_string()).into()) + } + + fn get_request_body( + &self, + _req: &PaymentsCaptureRouterData, + _connectors: &Connectors, + ) -> CustomResult { + Err(errors::ConnectorError::NotImplemented("get_request_body method".to_string()).into()) + } + + fn build_request( + &self, + req: &PaymentsCaptureRouterData, + connectors: &Connectors, + ) -> CustomResult, errors::ConnectorError> { + Ok(Some( + RequestBuilder::new() + .method(Method::Post) + .url(&types::PaymentsCaptureType::get_url(self, req, connectors)?) + .attach_default_headers() + .headers(types::PaymentsCaptureType::get_headers( + self, req, connectors, + )?) + .set_body(types::PaymentsCaptureType::get_request_body( + self, req, connectors, + )?) + .build(), + )) + } + + fn handle_response( + &self, + data: &PaymentsCaptureRouterData, + event_builder: Option<&mut ConnectorEvent>, + res: Response, + ) -> CustomResult { + let response: santander::SantanderPaymentsResponse = res + .response + .parse_struct("Santander PaymentsCaptureResponse") + .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) + } +} + +impl ConnectorIntegration for Santander {} + +impl ConnectorIntegration for Santander { + fn get_headers( + &self, + req: &RefundsRouterData, + connectors: &Connectors, + ) -> CustomResult)>, errors::ConnectorError> { + self.build_headers(req, connectors) + } + + fn get_content_type(&self) -> &'static str { + self.common_get_content_type() + } + + fn get_url( + &self, + _req: &RefundsRouterData, + _connectors: &Connectors, + ) -> CustomResult { + Err(errors::ConnectorError::NotImplemented("get_url method".to_string()).into()) + } + + fn get_request_body( + &self, + req: &RefundsRouterData, + _connectors: &Connectors, + ) -> CustomResult { + let refund_amount = utils::convert_amount( + self.amount_converter, + req.request.minor_refund_amount, + req.request.currency, + )?; + + let connector_router_data = santander::SantanderRouterData::from((refund_amount, req)); + let connector_req = santander::SantanderRefundRequest::try_from(&connector_router_data)?; + Ok(RequestContent::Json(Box::new(connector_req))) + } + + fn build_request( + &self, + req: &RefundsRouterData, + connectors: &Connectors, + ) -> CustomResult, errors::ConnectorError> { + let request = RequestBuilder::new() + .method(Method::Post) + .url(&types::RefundExecuteType::get_url(self, req, connectors)?) + .attach_default_headers() + .headers(types::RefundExecuteType::get_headers( + self, req, connectors, + )?) + .set_body(types::RefundExecuteType::get_request_body( + self, req, connectors, + )?) + .build(); + Ok(Some(request)) + } + + fn handle_response( + &self, + data: &RefundsRouterData, + event_builder: Option<&mut ConnectorEvent>, + res: Response, + ) -> CustomResult, errors::ConnectorError> { + let response: santander::RefundResponse = res + .response + .parse_struct("santander RefundResponse") + .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) + } +} + +impl ConnectorIntegration for Santander { + fn get_headers( + &self, + req: &RefundSyncRouterData, + connectors: &Connectors, + ) -> CustomResult)>, errors::ConnectorError> { + self.build_headers(req, connectors) + } + + fn get_content_type(&self) -> &'static str { + self.common_get_content_type() + } + + fn get_url( + &self, + _req: &RefundSyncRouterData, + _connectors: &Connectors, + ) -> CustomResult { + Err(errors::ConnectorError::NotImplemented("get_url method".to_string()).into()) + } + + fn build_request( + &self, + req: &RefundSyncRouterData, + connectors: &Connectors, + ) -> CustomResult, errors::ConnectorError> { + Ok(Some( + RequestBuilder::new() + .method(Method::Get) + .url(&types::RefundSyncType::get_url(self, req, connectors)?) + .attach_default_headers() + .headers(types::RefundSyncType::get_headers(self, req, connectors)?) + .set_body(types::RefundSyncType::get_request_body( + self, req, connectors, + )?) + .build(), + )) + } + + fn handle_response( + &self, + data: &RefundSyncRouterData, + event_builder: Option<&mut ConnectorEvent>, + res: Response, + ) -> CustomResult { + let response: santander::RefundResponse = res + .response + .parse_struct("santander RefundSyncResponse") + .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 Santander { + fn get_webhook_object_reference_id( + &self, + _request: &webhooks::IncomingWebhookRequestDetails<'_>, + ) -> CustomResult { + Err(report!(errors::ConnectorError::WebhooksNotImplemented)) + } + + fn get_webhook_event_type( + &self, + _request: &webhooks::IncomingWebhookRequestDetails<'_>, + ) -> CustomResult { + Err(report!(errors::ConnectorError::WebhooksNotImplemented)) + } + + fn get_webhook_resource_object( + &self, + _request: &webhooks::IncomingWebhookRequestDetails<'_>, + ) -> CustomResult, errors::ConnectorError> { + Err(report!(errors::ConnectorError::WebhooksNotImplemented)) + } +} + +impl ConnectorSpecifications for Santander {} diff --git a/crates/hyperswitch_connectors/src/connectors/santander/transformers.rs b/crates/hyperswitch_connectors/src/connectors/santander/transformers.rs new file mode 100644 index 0000000000..2a8831625b --- /dev/null +++ b/crates/hyperswitch_connectors/src/connectors/santander/transformers.rs @@ -0,0 +1,228 @@ +use common_enums::enums; +use common_utils::types::StringMajorUnit; +use hyperswitch_domain_models::{ + payment_method_data::PaymentMethodData, + router_data::{ConnectorAuthType, RouterData}, + router_flow_types::refunds::{Execute, RSync}, + router_request_types::ResponseId, + router_response_types::{PaymentsResponseData, RefundsResponseData}, + types::{PaymentsAuthorizeRouterData, RefundsRouterData}, +}; +use hyperswitch_interfaces::errors; +use masking::Secret; +use serde::{Deserialize, Serialize}; + +use crate::{ + types::{RefundsResponseRouterData, ResponseRouterData}, + utils::PaymentsAuthorizeRequestData, +}; + +//TODO: Fill the struct with respective fields +pub struct SantanderRouterData { + pub amount: StringMajorUnit, // The type of amount that a connector accepts, for example, String, i64, f64, etc. + pub router_data: T, +} + +impl From<(StringMajorUnit, T)> for SantanderRouterData { + fn from((amount, item): (StringMajorUnit, T)) -> Self { + //Todo : use utils to convert the amount to the type of amount that a connector accepts + Self { + amount, + router_data: item, + } + } +} + +//TODO: Fill the struct with respective fields +#[derive(Default, Debug, Serialize, PartialEq)] +pub struct SantanderPaymentsRequest { + amount: StringMajorUnit, + card: SantanderCard, +} + +#[derive(Default, Debug, Serialize, Eq, PartialEq)] +pub struct SantanderCard { + number: cards::CardNumber, + expiry_month: Secret, + expiry_year: Secret, + cvc: Secret, + complete: bool, +} + +impl TryFrom<&SantanderRouterData<&PaymentsAuthorizeRouterData>> for SantanderPaymentsRequest { + type Error = error_stack::Report; + fn try_from( + item: &SantanderRouterData<&PaymentsAuthorizeRouterData>, + ) -> Result { + match item.router_data.request.payment_method_data.clone() { + PaymentMethodData::Card(req_card) => { + let card = SantanderCard { + number: req_card.card_number, + expiry_month: req_card.card_exp_month, + expiry_year: req_card.card_exp_year, + cvc: req_card.card_cvc, + complete: item.router_data.request.is_auto_capture()?, + }; + Ok(Self { + amount: item.amount.clone(), + card, + }) + } + _ => Err(errors::ConnectorError::NotImplemented("Payment method".to_string()).into()), + } + } +} + +//TODO: Fill the struct with respective fields +// Auth Struct +pub struct SantanderAuthType { + pub(super) api_key: Secret, +} + +impl TryFrom<&ConnectorAuthType> for SantanderAuthType { + type Error = error_stack::Report; + fn try_from(auth_type: &ConnectorAuthType) -> Result { + match auth_type { + ConnectorAuthType::HeaderKey { api_key } => Ok(Self { + api_key: api_key.to_owned(), + }), + _ => Err(errors::ConnectorError::FailedToObtainAuthType.into()), + } + } +} +// PaymentsResponse +//TODO: Append the remaining status flags +#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq)] +#[serde(rename_all = "lowercase")] +pub enum SantanderPaymentStatus { + Succeeded, + Failed, + #[default] + Processing, +} + +impl From for common_enums::AttemptStatus { + fn from(item: SantanderPaymentStatus) -> Self { + match item { + SantanderPaymentStatus::Succeeded => Self::Charged, + SantanderPaymentStatus::Failed => Self::Failure, + SantanderPaymentStatus::Processing => Self::Authorizing, + } + } +} + +//TODO: Fill the struct with respective fields +#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq)] +pub struct SantanderPaymentsResponse { + status: SantanderPaymentStatus, + id: String, +} + +impl TryFrom> + for RouterData +{ + type Error = error_stack::Report; + fn try_from( + item: ResponseRouterData, + ) -> Result { + Ok(Self { + status: common_enums::AttemptStatus::from(item.response.status), + response: Ok(PaymentsResponseData::TransactionResponse { + resource_id: ResponseId::ConnectorTransactionId(item.response.id), + redirection_data: Box::new(None), + mandate_reference: Box::new(None), + connector_metadata: None, + network_txn_id: None, + connector_response_reference_id: None, + incremental_authorization_allowed: None, + charges: None, + }), + ..item.data + }) + } +} + +//TODO: Fill the struct with respective fields +// REFUND : +// Type definition for RefundRequest +#[derive(Default, Debug, Serialize)] +pub struct SantanderRefundRequest { + pub amount: StringMajorUnit, +} + +impl TryFrom<&SantanderRouterData<&RefundsRouterData>> for SantanderRefundRequest { + type Error = error_stack::Report; + fn try_from(item: &SantanderRouterData<&RefundsRouterData>) -> Result { + Ok(Self { + amount: item.amount.to_owned(), + }) + } +} + +// Type definition for Refund Response + +#[allow(dead_code)] +#[derive(Debug, Serialize, Default, Deserialize, Clone)] +pub enum RefundStatus { + Succeeded, + Failed, + #[default] + Processing, +} + +impl From for enums::RefundStatus { + fn from(item: RefundStatus) -> Self { + match item { + RefundStatus::Succeeded => Self::Success, + RefundStatus::Failed => Self::Failure, + RefundStatus::Processing => Self::Pending, + //TODO: Review mapping + } + } +} + +//TODO: Fill the struct with respective fields +#[derive(Default, Debug, Clone, Serialize, Deserialize)] +pub struct RefundResponse { + id: String, + status: RefundStatus, +} + +impl TryFrom> for RefundsRouterData { + type Error = error_stack::Report; + fn try_from( + item: RefundsResponseRouterData, + ) -> Result { + Ok(Self { + response: Ok(RefundsResponseData { + connector_refund_id: item.response.id.to_string(), + refund_status: enums::RefundStatus::from(item.response.status), + }), + ..item.data + }) + } +} + +impl TryFrom> for RefundsRouterData { + type Error = error_stack::Report; + fn try_from( + item: RefundsResponseRouterData, + ) -> Result { + Ok(Self { + response: Ok(RefundsResponseData { + connector_refund_id: item.response.id.to_string(), + refund_status: enums::RefundStatus::from(item.response.status), + }), + ..item.data + }) + } +} + +//TODO: Fill the struct with respective fields +#[derive(Default, Debug, Serialize, Deserialize, PartialEq)] +pub struct SantanderErrorResponse { + pub status_code: u16, + pub code: String, + pub message: String, + pub reason: Option, +} diff --git a/crates/hyperswitch_connectors/src/default_implementations.rs b/crates/hyperswitch_connectors/src/default_implementations.rs index e78067e24e..b1c6007022 100644 --- a/crates/hyperswitch_connectors/src/default_implementations.rs +++ b/crates/hyperswitch_connectors/src/default_implementations.rs @@ -220,6 +220,7 @@ default_imp_for_authorize_session_token!( connectors::Recurly, connectors::Redsys, connectors::Riskified, + connectors::Santander, connectors::Shift4, connectors::Signifyd, connectors::Stax, @@ -341,6 +342,7 @@ default_imp_for_calculate_tax!( connectors::Recurly, connectors::Redsys, connectors::Riskified, + connectors::Santander, connectors::Shift4, connectors::Signifyd, connectors::Stax, @@ -429,6 +431,7 @@ default_imp_for_session_update!( connectors::Recurly, connectors::Redsys, connectors::Riskified, + connectors::Santander, connectors::Shift4, connectors::Signifyd, connectors::Stripe, @@ -550,6 +553,7 @@ default_imp_for_post_session_tokens!( connectors::Recurly, connectors::Redsys, connectors::Riskified, + connectors::Santander, connectors::Shift4, connectors::Signifyd, connectors::Square, @@ -669,6 +673,7 @@ default_imp_for_create_order!( connectors::Recurly, connectors::Redsys, connectors::Riskified, + connectors::Santander, connectors::Shift4, connectors::Signifyd, connectors::Square, @@ -791,6 +796,7 @@ default_imp_for_update_metadata!( connectors::Razorpay, connectors::Recurly, connectors::Redsys, + connectors::Santander, connectors::Shift4, connectors::Signifyd, connectors::Square, @@ -929,6 +935,7 @@ default_imp_for_complete_authorize!( connectors::Razorpay, connectors::Recurly, connectors::Riskified, + connectors::Santander, connectors::Signifyd, connectors::Stax, connectors::Square, @@ -1048,6 +1055,7 @@ default_imp_for_incremental_authorization!( connectors::Recurly, connectors::Redsys, connectors::Riskified, + connectors::Santander, connectors::Shift4, connectors::Signifyd, connectors::Stax, @@ -1167,6 +1175,7 @@ default_imp_for_create_customer!( connectors::Recurly, connectors::Redsys, connectors::Riskified, + connectors::Santander, connectors::Shift4, connectors::Signifyd, connectors::Square, @@ -1273,6 +1282,7 @@ default_imp_for_connector_redirect_response!( connectors::Recurly, connectors::Redsys, connectors::Riskified, + connectors::Santander, connectors::Shift4, connectors::Signifyd, connectors::Stax, @@ -1381,6 +1391,7 @@ default_imp_for_pre_processing_steps!( connectors::Razorpay, connectors::Recurly, connectors::Riskified, + connectors::Santander, connectors::Signifyd, connectors::Stax, connectors::Square, @@ -1500,6 +1511,7 @@ default_imp_for_post_processing_steps!( connectors::Recurly, connectors::Redsys, connectors::Riskified, + connectors::Santander, connectors::Shift4, connectors::Signifyd, connectors::Stax, @@ -1623,6 +1635,7 @@ default_imp_for_approve!( connectors::Recurly, connectors::Redsys, connectors::Riskified, + connectors::Santander, connectors::Shift4, connectors::Signifyd, connectors::Stax, @@ -1746,6 +1759,7 @@ default_imp_for_reject!( connectors::Recurly, connectors::Redsys, connectors::Riskified, + connectors::Santander, connectors::Shift4, connectors::Signifyd, connectors::Stax, @@ -1868,6 +1882,7 @@ default_imp_for_webhook_source_verification!( connectors::Recurly, connectors::Redsys, connectors::Riskified, + connectors::Santander, connectors::Shift4, connectors::Signifyd, connectors::Stax, @@ -1990,6 +2005,7 @@ default_imp_for_accept_dispute!( connectors::Recurly, connectors::Redsys, connectors::Riskified, + connectors::Santander, connectors::Shift4, connectors::Signifyd, connectors::Stax, @@ -2111,6 +2127,7 @@ default_imp_for_submit_evidence!( connectors::Recurly, connectors::Redsys, connectors::Riskified, + connectors::Santander, connectors::Shift4, connectors::Signifyd, connectors::Stax, @@ -2231,6 +2248,7 @@ default_imp_for_defend_dispute!( connectors::Recurly, connectors::Redsys, connectors::Riskified, + connectors::Santander, connectors::Shift4, connectors::Signifyd, connectors::Stax, @@ -2361,6 +2379,7 @@ default_imp_for_file_upload!( connectors::Recurly, connectors::Redsys, connectors::Riskified, + connectors::Santander, connectors::Shift4, connectors::Signifyd, connectors::Stax, @@ -2469,6 +2488,7 @@ default_imp_for_payouts!( connectors::Recurly, connectors::Redsys, connectors::Riskified, + connectors::Santander, connectors::Shift4, connectors::Signifyd, connectors::Square, @@ -2587,6 +2607,7 @@ default_imp_for_payouts_create!( connectors::Recurly, connectors::Redsys, connectors::Riskified, + connectors::Santander, connectors::Shift4, connectors::Signifyd, connectors::Stax, @@ -2708,6 +2729,7 @@ default_imp_for_payouts_retrieve!( connectors::Recurly, connectors::Redsys, connectors::Riskified, + connectors::Santander, connectors::Shift4, connectors::Signifyd, connectors::Stax, @@ -2830,6 +2852,7 @@ default_imp_for_payouts_eligibility!( connectors::Recurly, connectors::Redsys, connectors::Riskified, + connectors::Santander, connectors::Shift4, connectors::Signifyd, connectors::Stax, @@ -2947,6 +2970,7 @@ default_imp_for_payouts_fulfill!( connectors::Recurly, connectors::Redsys, connectors::Riskified, + connectors::Santander, connectors::Shift4, connectors::Signifyd, connectors::Stax, @@ -3067,6 +3091,7 @@ default_imp_for_payouts_cancel!( connectors::Recurly, connectors::Redsys, connectors::Riskified, + connectors::Santander, connectors::Shift4, connectors::Signifyd, connectors::Stax, @@ -3188,6 +3213,7 @@ default_imp_for_payouts_quote!( connectors::Recurly, connectors::Redsys, connectors::Riskified, + connectors::Santander, connectors::Shift4, connectors::Signifyd, connectors::Stax, @@ -3310,6 +3336,7 @@ default_imp_for_payouts_recipient!( connectors::Recurly, connectors::Redsys, connectors::Riskified, + connectors::Santander, connectors::Shift4, connectors::Signifyd, connectors::Stax, @@ -3432,6 +3459,7 @@ default_imp_for_payouts_recipient_account!( connectors::Recurly, connectors::Redsys, connectors::Riskified, + connectors::Santander, connectors::Shift4, connectors::Signifyd, connectors::Stax, @@ -3555,6 +3583,7 @@ default_imp_for_frm_sale!( connectors::Razorpay, connectors::Recurly, connectors::Redsys, + connectors::Santander, connectors::Shift4, connectors::Stax, connectors::Square, @@ -3678,6 +3707,7 @@ default_imp_for_frm_checkout!( connectors::Razorpay, connectors::Recurly, connectors::Redsys, + connectors::Santander, connectors::Shift4, connectors::Stax, connectors::Square, @@ -3801,6 +3831,7 @@ default_imp_for_frm_transaction!( connectors::Razorpay, connectors::Recurly, connectors::Redsys, + connectors::Santander, connectors::Shift4, connectors::Stax, connectors::Square, @@ -3924,6 +3955,7 @@ default_imp_for_frm_fulfillment!( connectors::Razorpay, connectors::Recurly, connectors::Redsys, + connectors::Santander, connectors::Shift4, connectors::Stax, connectors::Square, @@ -4047,6 +4079,7 @@ default_imp_for_frm_record_return!( connectors::Razorpay, connectors::Recurly, connectors::Redsys, + connectors::Santander, connectors::Shift4, connectors::Stripe, connectors::Stax, @@ -4165,6 +4198,7 @@ default_imp_for_revoking_mandates!( connectors::Recurly, connectors::Redsys, connectors::Riskified, + connectors::Santander, connectors::Shift4, connectors::Signifyd, connectors::Stax, @@ -4286,6 +4320,7 @@ default_imp_for_uas_pre_authentication!( connectors::Recurly, connectors::Redsys, connectors::Riskified, + connectors::Santander, connectors::Shift4, connectors::Signifyd, connectors::Stripe, @@ -4406,6 +4441,7 @@ default_imp_for_uas_post_authentication!( connectors::Recurly, connectors::Redsys, connectors::Riskified, + connectors::Santander, connectors::Shift4, connectors::Signifyd, connectors::Stripe, @@ -4527,6 +4563,7 @@ default_imp_for_uas_authentication_confirmation!( connectors::Recurly, connectors::Redsys, connectors::Riskified, + connectors::Santander, connectors::Shift4, connectors::Signifyd, connectors::Stax, @@ -4639,6 +4676,7 @@ default_imp_for_connector_request_id!( connectors::Recurly, connectors::Redsys, connectors::Riskified, + connectors::Santander, connectors::Shift4, connectors::Signifyd, connectors::Stax, @@ -4755,6 +4793,7 @@ default_imp_for_fraud_check!( connectors::Razorpay, connectors::Recurly, connectors::Redsys, + connectors::Santander, connectors::Shift4, connectors::Stax, connectors::Square, @@ -4899,6 +4938,7 @@ default_imp_for_connector_authentication!( connectors::Recurly, connectors::Redsys, connectors::Riskified, + connectors::Santander, connectors::Shift4, connectors::Signifyd, connectors::Stax, @@ -5018,6 +5058,7 @@ default_imp_for_uas_authentication!( connectors::Recurly, connectors::Redsys, connectors::Riskified, + connectors::Santander, connectors::Shift4, connectors::Signifyd, connectors::Stax, @@ -5132,6 +5173,7 @@ default_imp_for_revenue_recovery! { connectors::Recurly, connectors::Redsys, connectors::Riskified, + connectors::Santander, connectors::Shift4, connectors::Signifyd, connectors::Stax, @@ -5256,6 +5298,7 @@ default_imp_for_billing_connector_payment_sync!( connectors::Recurly, connectors::Redsys, connectors::Riskified, + connectors::Santander, connectors::Shift4, connectors::Signifyd, connectors::Stripe, @@ -5378,6 +5421,7 @@ default_imp_for_revenue_recovery_record_back!( connectors::Recurly, connectors::Redsys, connectors::Riskified, + connectors::Santander, connectors::Shift4, connectors::Signifyd, connectors::Stripe, @@ -5500,6 +5544,7 @@ default_imp_for_billing_connector_invoice_sync!( connectors::Recurly, connectors::Redsys, connectors::Riskified, + connectors::Santander, connectors::Signifyd, connectors::Shift4, connectors::Stax, @@ -5616,6 +5661,7 @@ default_imp_for_external_vault!( connectors::Recurly, connectors::Redsys, connectors::Riskified, + connectors::Santander, connectors::Signifyd, connectors::Shift4, connectors::Stax, @@ -5738,6 +5784,7 @@ default_imp_for_external_vault_insert!( connectors::Recurly, connectors::Redsys, connectors::Riskified, + connectors::Santander, connectors::Signifyd, connectors::Shift4, connectors::Stax, @@ -5860,6 +5907,7 @@ default_imp_for_external_vault_retrieve!( connectors::Recurly, connectors::Redsys, connectors::Riskified, + connectors::Santander, connectors::Signifyd, connectors::Shift4, connectors::Stax, @@ -5982,6 +6030,7 @@ default_imp_for_external_vault_delete!( connectors::Recurly, connectors::Redsys, connectors::Riskified, + connectors::Santander, connectors::Signifyd, connectors::Shift4, connectors::Stax, @@ -6103,6 +6152,7 @@ default_imp_for_external_vault_create!( connectors::Recurly, connectors::Redsys, connectors::Riskified, + connectors::Santander, connectors::Signifyd, connectors::Shift4, connectors::Stax, diff --git a/crates/hyperswitch_connectors/src/default_implementations_v2.rs b/crates/hyperswitch_connectors/src/default_implementations_v2.rs index 5786463770..329e2fbbb9 100644 --- a/crates/hyperswitch_connectors/src/default_implementations_v2.rs +++ b/crates/hyperswitch_connectors/src/default_implementations_v2.rs @@ -327,6 +327,7 @@ default_imp_for_new_connector_integration_payment!( connectors::Recurly, connectors::Redsys, connectors::Riskified, + connectors::Santander, connectors::Shift4, connectors::Signifyd, connectors::Stax, @@ -450,6 +451,7 @@ default_imp_for_new_connector_integration_refund!( connectors::Recurly, connectors::Redsys, connectors::Riskified, + connectors::Santander, connectors::Shift4, connectors::Signifyd, connectors::Stax, @@ -568,6 +570,7 @@ default_imp_for_new_connector_integration_connector_access_token!( connectors::Recurly, connectors::Redsys, connectors::Riskified, + connectors::Santander, connectors::Shift4, connectors::Signifyd, connectors::Stax, @@ -691,6 +694,7 @@ default_imp_for_new_connector_integration_accept_dispute!( connectors::Recurly, connectors::Redsys, connectors::Riskified, + connectors::Santander, connectors::Shift4, connectors::Signifyd, connectors::Stax, @@ -813,6 +817,7 @@ default_imp_for_new_connector_integration_submit_evidence!( connectors::Recurly, connectors::Redsys, connectors::Riskified, + connectors::Santander, connectors::Shift4, connectors::Signifyd, connectors::Stax, @@ -936,6 +941,7 @@ default_imp_for_new_connector_integration_defend_dispute!( connectors::Recurly, connectors::Redsys, connectors::Riskified, + connectors::Santander, connectors::Shift4, connectors::Signifyd, connectors::Stax, @@ -1069,6 +1075,7 @@ default_imp_for_new_connector_integration_file_upload!( connectors::Recurly, connectors::Redsys, connectors::Riskified, + connectors::Santander, connectors::Shift4, connectors::Signifyd, connectors::Stax, @@ -1194,6 +1201,7 @@ default_imp_for_new_connector_integration_payouts_create!( connectors::Recurly, connectors::Redsys, connectors::Riskified, + connectors::Santander, connectors::Shift4, connectors::Signifyd, connectors::Stax, @@ -1319,6 +1327,7 @@ default_imp_for_new_connector_integration_payouts_eligibility!( connectors::Recurly, connectors::Redsys, connectors::Riskified, + connectors::Santander, connectors::Shift4, connectors::Signifyd, connectors::Stax, @@ -1444,6 +1453,7 @@ default_imp_for_new_connector_integration_payouts_fulfill!( connectors::Recurly, connectors::Redsys, connectors::Riskified, + connectors::Santander, connectors::Shift4, connectors::Signifyd, connectors::Stax, @@ -1569,6 +1579,7 @@ default_imp_for_new_connector_integration_payouts_cancel!( connectors::Recurly, connectors::Redsys, connectors::Riskified, + connectors::Santander, connectors::Shift4, connectors::Signifyd, connectors::Stax, @@ -1694,6 +1705,7 @@ default_imp_for_new_connector_integration_payouts_quote!( connectors::Recurly, connectors::Redsys, connectors::Riskified, + connectors::Santander, connectors::Shift4, connectors::Signifyd, connectors::Stax, @@ -1819,6 +1831,7 @@ default_imp_for_new_connector_integration_payouts_recipient!( connectors::Recurly, connectors::Redsys, connectors::Riskified, + connectors::Santander, connectors::Shift4, connectors::Signifyd, connectors::Stax, @@ -1944,6 +1957,7 @@ default_imp_for_new_connector_integration_payouts_sync!( connectors::Recurly, connectors::Redsys, connectors::Riskified, + connectors::Santander, connectors::Shift4, connectors::Signifyd, connectors::Stax, @@ -2069,6 +2083,7 @@ default_imp_for_new_connector_integration_payouts_recipient_account!( connectors::Recurly, connectors::Redsys, connectors::Riskified, + connectors::Santander, connectors::Shift4, connectors::Signifyd, connectors::Stax, @@ -2192,6 +2207,7 @@ default_imp_for_new_connector_integration_webhook_source_verification!( connectors::Recurly, connectors::Redsys, connectors::Riskified, + connectors::Santander, connectors::Shift4, connectors::Signifyd, connectors::Stax, @@ -2317,6 +2333,7 @@ default_imp_for_new_connector_integration_frm_sale!( connectors::Recurly, connectors::Redsys, connectors::Riskified, + connectors::Santander, connectors::Shift4, connectors::Signifyd, connectors::Stax, @@ -2442,6 +2459,7 @@ default_imp_for_new_connector_integration_frm_checkout!( connectors::Recurly, connectors::Redsys, connectors::Riskified, + connectors::Santander, connectors::Shift4, connectors::Signifyd, connectors::Stax, @@ -2567,6 +2585,7 @@ default_imp_for_new_connector_integration_frm_transaction!( connectors::Recurly, connectors::Redsys, connectors::Riskified, + connectors::Santander, connectors::Shift4, connectors::Signifyd, connectors::Stax, @@ -2692,6 +2711,7 @@ default_imp_for_new_connector_integration_frm_fulfillment!( connectors::Recurly, connectors::Redsys, connectors::Riskified, + connectors::Santander, connectors::Shift4, connectors::Signifyd, connectors::Stax, @@ -2817,6 +2837,7 @@ default_imp_for_new_connector_integration_frm_record_return!( connectors::Recurly, connectors::Redsys, connectors::Riskified, + connectors::Santander, connectors::Shift4, connectors::Signifyd, connectors::Stax, @@ -2938,6 +2959,7 @@ default_imp_for_new_connector_integration_revoking_mandates!( connectors::Recurly, connectors::Redsys, connectors::Riskified, + connectors::Santander, connectors::Shift4, connectors::Signifyd, connectors::Stax, @@ -3031,6 +3053,7 @@ default_imp_for_new_connector_integration_frm!( connectors::Recurly, connectors::Redsys, connectors::Riskified, + connectors::Santander, connectors::Shift4, connectors::Signifyd, connectors::Stax, @@ -3151,6 +3174,7 @@ default_imp_for_new_connector_integration_connector_authentication!( connectors::Redsys, connectors::Riskified, connectors::Recurly, + connectors::Santander, connectors::Shift4, connectors::Signifyd, connectors::Stax, @@ -3263,6 +3287,7 @@ default_imp_for_new_connector_integration_revenue_recovery!( connectors::Razorpay, connectors::Redsys, connectors::Riskified, + connectors::Santander, connectors::Shift4, connectors::Signifyd, connectors::Stax, @@ -3409,6 +3434,7 @@ default_imp_for_new_connector_integration_external_vault!( connectors::Recurly, connectors::Redsys, connectors::Riskified, + connectors::Santander, connectors::Shift4, connectors::Signifyd, connectors::Stax, diff --git a/crates/hyperswitch_domain_models/src/configs.rs b/crates/hyperswitch_domain_models/src/configs.rs index 4c170d5c50..e9f13a1de8 100644 --- a/crates/hyperswitch_domain_models/src/configs.rs +++ b/crates/hyperswitch_domain_models/src/configs.rs @@ -95,6 +95,7 @@ pub struct Connectors { pub recurly: ConnectorParams, pub redsys: ConnectorParams, pub riskified: ConnectorParams, + pub santander: ConnectorParams, pub shift4: ConnectorParams, pub signifyd: ConnectorParams, pub square: ConnectorParams, diff --git a/crates/router/src/connector.rs b/crates/router/src/connector.rs index 4a6ae9b668..ccf2e35fde 100644 --- a/crates/router/src/connector.rs +++ b/crates/router/src/connector.rs @@ -30,9 +30,9 @@ pub use hyperswitch_connectors::connectors::{ paystack, paystack::Paystack, payu, payu::Payu, placetopay, placetopay::Placetopay, plaid, plaid::Plaid, powertranz, powertranz::Powertranz, prophetpay, prophetpay::Prophetpay, rapyd, rapyd::Rapyd, razorpay, razorpay::Razorpay, recurly, recurly::Recurly, redsys, redsys::Redsys, - riskified, riskified::Riskified, shift4, shift4::Shift4, signifyd, signifyd::Signifyd, square, - square::Square, stax, stax::Stax, stripe, stripe::Stripe, stripebilling, - stripebilling::Stripebilling, taxjar, taxjar::Taxjar, threedsecureio, + riskified, riskified::Riskified, santander, santander::Santander, shift4, shift4::Shift4, + signifyd, signifyd::Signifyd, square, square::Square, stax, stax::Stax, stripe, stripe::Stripe, + stripebilling, stripebilling::Stripebilling, taxjar, taxjar::Taxjar, threedsecureio, threedsecureio::Threedsecureio, thunes, thunes::Thunes, tokenio, tokenio::Tokenio, trustpay, trustpay::Trustpay, tsys, tsys::Tsys, unified_authentication_service, unified_authentication_service::UnifiedAuthenticationService, vgs, vgs::Vgs, volt, volt::Volt, diff --git a/crates/router/src/core/admin.rs b/crates/router/src/core/admin.rs index ae4da756d7..07bae476d2 100644 --- a/crates/router/src/core/admin.rs +++ b/crates/router/src/core/admin.rs @@ -1702,6 +1702,10 @@ impl ConnectorAuthTypeAndMetadataValidation<'_> { redsys::transformers::RedsysAuthType::try_from(self.auth_type)?; Ok(()) } + // api_enums::Connector::Santander => { + // santander::transformers::SantanderAuthType::try_from(self.auth_type)?; + // Ok(()) + // }, api_enums::Connector::Shift4 => { shift4::transformers::Shift4AuthType::try_from(self.auth_type)?; Ok(()) diff --git a/crates/router/src/types/api.rs b/crates/router/src/types/api.rs index ce12ba9844..186d9235c4 100644 --- a/crates/router/src/types/api.rs +++ b/crates/router/src/types/api.rs @@ -554,6 +554,9 @@ impl ConnectorData { enums::Connector::Redsys => { Ok(ConnectorEnum::Old(Box::new(connector::Redsys::new()))) } + // enums::Connector::Santander => { + // Ok(ConnectorEnum::Old(Box::new(connector::Santander))) + // } enums::Connector::Shift4 => { Ok(ConnectorEnum::Old(Box::new(connector::Shift4::new()))) } diff --git a/crates/router/src/types/transformers.rs b/crates/router/src/types/transformers.rs index 76b2ae6a5d..2e64b10c28 100644 --- a/crates/router/src/types/transformers.rs +++ b/crates/router/src/types/transformers.rs @@ -308,6 +308,7 @@ impl ForeignTryFrom for common_enums::RoutableConnectors { api_enums::Connector::Razorpay => Self::Razorpay, api_enums::Connector::Recurly => Self::Recurly, api_enums::Connector::Redsys => Self::Redsys, + // api_enums::Connector::Santander => Self::Santander, api_enums::Connector::Shift4 => Self::Shift4, api_enums::Connector::Signifyd => { Err(common_utils::errors::ValidationError::InvalidValue { diff --git a/crates/router/tests/connectors/main.rs b/crates/router/tests/connectors/main.rs index 1d499d5829..fbceba0864 100644 --- a/crates/router/tests/connectors/main.rs +++ b/crates/router/tests/connectors/main.rs @@ -85,6 +85,7 @@ mod prophetpay; mod rapyd; mod razorpay; mod redsys; +mod santander; mod shift4; mod square; mod stax; diff --git a/crates/router/tests/connectors/sample_auth.toml b/crates/router/tests/connectors/sample_auth.toml index f64811d105..00e0820288 100644 --- a/crates/router/tests/connectors/sample_auth.toml +++ b/crates/router/tests/connectors/sample_auth.toml @@ -339,4 +339,8 @@ api_key= "API Key" [barclaycard] api_key = "MyApiKey" key1 = "Merchant id" -api_secret = "Secret key" \ No newline at end of file +api_secret = "Secret key" + +[santander] +api_key="Client ID" +key1 ="Client Secret" \ No newline at end of file diff --git a/crates/router/tests/connectors/santander.rs b/crates/router/tests/connectors/santander.rs new file mode 100644 index 0000000000..5cf2d603f3 --- /dev/null +++ b/crates/router/tests/connectors/santander.rs @@ -0,0 +1,420 @@ +use masking::Secret; +use router::types::{self, api, domain, storage::enums}; +use test_utils::connector_auth; + +use crate::utils::{self, ConnectorActions}; + +#[derive(Clone, Copy)] +struct SantanderTest; +impl ConnectorActions for SantanderTest {} +impl utils::Connector for SantanderTest { + fn get_data(&self) -> api::ConnectorData { + use router::connector::Santander; + utils::construct_connector_data_old( + Box::new(Santander::new()), + types::Connector::DummyConnector1, + api::GetToken::Connector, + None, + ) + } + + fn get_auth_token(&self) -> types::ConnectorAuthType { + utils::to_connector_auth_type( + connector_auth::ConnectorAuthentication::new() + .santander + .expect("Missing connector authentication configuration") + .into(), + ) + } + + fn get_name(&self) -> String { + "santander".to_string() + } +} + +static CONNECTOR: SantanderTest = SantanderTest {}; + +fn get_default_payment_info() -> Option { + None +} + +fn payment_method_details() -> Option { + None +} + +// Cards Positive Tests +// Creates a payment using the manual capture flow (Non 3DS). +#[actix_web::test] +async fn should_only_authorize_payment() { + let response = CONNECTOR + .authorize_payment(payment_method_details(), get_default_payment_info()) + .await + .expect("Authorize payment response"); + assert_eq!(response.status, enums::AttemptStatus::Authorized); +} + +// Captures a payment using the manual capture flow (Non 3DS). +#[actix_web::test] +async fn should_capture_authorized_payment() { + let response = CONNECTOR + .authorize_and_capture_payment(payment_method_details(), None, get_default_payment_info()) + .await + .expect("Capture payment response"); + assert_eq!(response.status, enums::AttemptStatus::Charged); +} + +// Partially captures a payment using the manual capture flow (Non 3DS). +#[actix_web::test] +async fn should_partially_capture_authorized_payment() { + let response = CONNECTOR + .authorize_and_capture_payment( + payment_method_details(), + Some(types::PaymentsCaptureData { + amount_to_capture: 50, + ..utils::PaymentCaptureType::default().0 + }), + get_default_payment_info(), + ) + .await + .expect("Capture payment response"); + assert_eq!(response.status, enums::AttemptStatus::Charged); +} + +// Synchronizes a payment using the manual capture flow (Non 3DS). +#[actix_web::test] +async fn should_sync_authorized_payment() { + let authorize_response = CONNECTOR + .authorize_payment(payment_method_details(), get_default_payment_info()) + .await + .expect("Authorize payment response"); + let txn_id = utils::get_connector_transaction_id(authorize_response.response); + let response = CONNECTOR + .psync_retry_till_status_matches( + enums::AttemptStatus::Authorized, + Some(types::PaymentsSyncData { + connector_transaction_id: types::ResponseId::ConnectorTransactionId( + txn_id.unwrap(), + ), + ..Default::default() + }), + get_default_payment_info(), + ) + .await + .expect("PSync response"); + assert_eq!(response.status, enums::AttemptStatus::Authorized,); +} + +// Voids a payment using the manual capture flow (Non 3DS). +#[actix_web::test] +async fn should_void_authorized_payment() { + let response = CONNECTOR + .authorize_and_void_payment( + payment_method_details(), + Some(types::PaymentsCancelData { + connector_transaction_id: String::from(""), + cancellation_reason: Some("requested_by_customer".to_string()), + ..Default::default() + }), + get_default_payment_info(), + ) + .await + .expect("Void payment response"); + assert_eq!(response.status, enums::AttemptStatus::Voided); +} + +// Refunds a payment using the manual capture flow (Non 3DS). +#[actix_web::test] +async fn should_refund_manually_captured_payment() { + let response = CONNECTOR + .capture_payment_and_refund( + payment_method_details(), + None, + None, + get_default_payment_info(), + ) + .await + .unwrap(); + assert_eq!( + response.response.unwrap().refund_status, + enums::RefundStatus::Success, + ); +} + +// Partially refunds a payment using the manual capture flow (Non 3DS). +#[actix_web::test] +async fn should_partially_refund_manually_captured_payment() { + let response = CONNECTOR + .capture_payment_and_refund( + payment_method_details(), + None, + Some(types::RefundsData { + refund_amount: 50, + ..utils::PaymentRefundType::default().0 + }), + get_default_payment_info(), + ) + .await + .unwrap(); + assert_eq!( + response.response.unwrap().refund_status, + enums::RefundStatus::Success, + ); +} + +// Synchronizes a refund using the manual capture flow (Non 3DS). +#[actix_web::test] +async fn should_sync_manually_captured_refund() { + let refund_response = CONNECTOR + .capture_payment_and_refund( + payment_method_details(), + None, + None, + get_default_payment_info(), + ) + .await + .unwrap(); + let response = CONNECTOR + .rsync_retry_till_status_matches( + enums::RefundStatus::Success, + refund_response.response.unwrap().connector_refund_id, + None, + get_default_payment_info(), + ) + .await + .unwrap(); + assert_eq!( + response.response.unwrap().refund_status, + enums::RefundStatus::Success, + ); +} + +// Creates a payment using the automatic capture flow (Non 3DS). +#[actix_web::test] +async fn should_make_payment() { + let authorize_response = CONNECTOR + .make_payment(payment_method_details(), get_default_payment_info()) + .await + .unwrap(); + assert_eq!(authorize_response.status, enums::AttemptStatus::Charged); +} + +// Synchronizes a payment using the automatic capture flow (Non 3DS). +#[actix_web::test] +async fn should_sync_auto_captured_payment() { + let authorize_response = CONNECTOR + .make_payment(payment_method_details(), get_default_payment_info()) + .await + .unwrap(); + assert_eq!(authorize_response.status, enums::AttemptStatus::Charged); + let txn_id = utils::get_connector_transaction_id(authorize_response.response); + assert_ne!(txn_id, None, "Empty connector transaction id"); + let response = CONNECTOR + .psync_retry_till_status_matches( + enums::AttemptStatus::Charged, + Some(types::PaymentsSyncData { + connector_transaction_id: types::ResponseId::ConnectorTransactionId( + txn_id.unwrap(), + ), + capture_method: Some(enums::CaptureMethod::Automatic), + ..Default::default() + }), + get_default_payment_info(), + ) + .await + .unwrap(); + assert_eq!(response.status, enums::AttemptStatus::Charged,); +} + +// Refunds a payment using the automatic capture flow (Non 3DS). +#[actix_web::test] +async fn should_refund_auto_captured_payment() { + let response = CONNECTOR + .make_payment_and_refund(payment_method_details(), None, get_default_payment_info()) + .await + .unwrap(); + assert_eq!( + response.response.unwrap().refund_status, + enums::RefundStatus::Success, + ); +} + +// Partially refunds a payment using the automatic capture flow (Non 3DS). +#[actix_web::test] +async fn should_partially_refund_succeeded_payment() { + let refund_response = CONNECTOR + .make_payment_and_refund( + payment_method_details(), + Some(types::RefundsData { + refund_amount: 50, + ..utils::PaymentRefundType::default().0 + }), + get_default_payment_info(), + ) + .await + .unwrap(); + assert_eq!( + refund_response.response.unwrap().refund_status, + enums::RefundStatus::Success, + ); +} + +// Creates multiple refunds against a payment using the automatic capture flow (Non 3DS). +#[actix_web::test] +async fn should_refund_succeeded_payment_multiple_times() { + CONNECTOR + .make_payment_and_multiple_refund( + payment_method_details(), + Some(types::RefundsData { + refund_amount: 50, + ..utils::PaymentRefundType::default().0 + }), + get_default_payment_info(), + ) + .await; +} + +// Synchronizes a refund using the automatic capture flow (Non 3DS). +#[actix_web::test] +async fn should_sync_refund() { + let refund_response = CONNECTOR + .make_payment_and_refund(payment_method_details(), None, get_default_payment_info()) + .await + .unwrap(); + let response = CONNECTOR + .rsync_retry_till_status_matches( + enums::RefundStatus::Success, + refund_response.response.unwrap().connector_refund_id, + None, + get_default_payment_info(), + ) + .await + .unwrap(); + assert_eq!( + response.response.unwrap().refund_status, + enums::RefundStatus::Success, + ); +} + +// Cards Negative scenarios +// Creates a payment with incorrect CVC. +#[actix_web::test] +async fn should_fail_payment_for_incorrect_cvc() { + let response = CONNECTOR + .make_payment( + Some(types::PaymentsAuthorizeData { + payment_method_data: domain::PaymentMethodData::Card(domain::Card { + card_cvc: Secret::new("12345".to_string()), + ..utils::CCardType::default().0 + }), + ..utils::PaymentAuthorizeType::default().0 + }), + get_default_payment_info(), + ) + .await + .unwrap(); + assert_eq!( + response.response.unwrap_err().message, + "Your card's security code is invalid.".to_string(), + ); +} + +// Creates a payment with incorrect expiry month. +#[actix_web::test] +async fn should_fail_payment_for_invalid_exp_month() { + let response = CONNECTOR + .make_payment( + Some(types::PaymentsAuthorizeData { + payment_method_data: domain::PaymentMethodData::Card(domain::Card { + card_exp_month: Secret::new("20".to_string()), + ..utils::CCardType::default().0 + }), + ..utils::PaymentAuthorizeType::default().0 + }), + get_default_payment_info(), + ) + .await + .unwrap(); + assert_eq!( + response.response.unwrap_err().message, + "Your card's expiration month is invalid.".to_string(), + ); +} + +// Creates a payment with incorrect expiry year. +#[actix_web::test] +async fn should_fail_payment_for_incorrect_expiry_year() { + let response = CONNECTOR + .make_payment( + Some(types::PaymentsAuthorizeData { + payment_method_data: domain::PaymentMethodData::Card(domain::Card { + card_exp_year: Secret::new("2000".to_string()), + ..utils::CCardType::default().0 + }), + ..utils::PaymentAuthorizeType::default().0 + }), + get_default_payment_info(), + ) + .await + .unwrap(); + assert_eq!( + response.response.unwrap_err().message, + "Your card's expiration year is invalid.".to_string(), + ); +} + +// Voids a payment using automatic capture flow (Non 3DS). +#[actix_web::test] +async fn should_fail_void_payment_for_auto_capture() { + let authorize_response = CONNECTOR + .make_payment(payment_method_details(), get_default_payment_info()) + .await + .unwrap(); + assert_eq!(authorize_response.status, enums::AttemptStatus::Charged); + let txn_id = utils::get_connector_transaction_id(authorize_response.response); + assert_ne!(txn_id, None, "Empty connector transaction id"); + let void_response = CONNECTOR + .void_payment(txn_id.unwrap(), None, get_default_payment_info()) + .await + .unwrap(); + assert_eq!( + void_response.response.unwrap_err().message, + "You cannot cancel this PaymentIntent because it has a status of succeeded." + ); +} + +// Captures a payment using invalid connector payment id. +#[actix_web::test] +async fn should_fail_capture_for_invalid_payment() { + let capture_response = CONNECTOR + .capture_payment("123456789".to_string(), None, get_default_payment_info()) + .await + .unwrap(); + assert_eq!( + capture_response.response.unwrap_err().message, + String::from("No such payment_intent: '123456789'") + ); +} + +// Refunds a payment with refund amount higher than payment amount. +#[actix_web::test] +async fn should_fail_for_refund_amount_higher_than_payment_amount() { + let response = CONNECTOR + .make_payment_and_refund( + payment_method_details(), + Some(types::RefundsData { + refund_amount: 150, + ..utils::PaymentRefundType::default().0 + }), + get_default_payment_info(), + ) + .await + .unwrap(); + assert_eq!( + response.response.unwrap_err().message, + "Refund amount (₹1.50) is greater than charge amount (₹1.00)", + ); +} + +// Connector dependent test cases goes here + +// [#478]: add unit tests for non 3DS, wallets & webhooks in connector tests diff --git a/crates/test_utils/src/connector_auth.rs b/crates/test_utils/src/connector_auth.rs index 7e5616e6a2..751065bafc 100644 --- a/crates/test_utils/src/connector_auth.rs +++ b/crates/test_utils/src/connector_auth.rs @@ -91,6 +91,7 @@ pub struct ConnectorAuthentication { pub razorpay: Option, pub recurly: Option, pub redsys: Option, + pub santander: Option, pub shift4: Option, pub square: Option, pub stax: Option, diff --git a/loadtest/config/development.toml b/loadtest/config/development.toml index 99655bacaf..5a59523eeb 100644 --- a/loadtest/config/development.toml +++ b/loadtest/config/development.toml @@ -167,6 +167,7 @@ razorpay.base_url = "https://api.razorpay.com/" recurly.base_url = "https://v3.recurly.com" redsys.base_url = "https://sis-t.redsys.es:25443" riskified.base_url = "https://sandbox.riskified.com/api" +santander.base_url = "https://pix.santander.com.br/api/v1/sandbox/" shift4.base_url = "https://api.shift4.com/" signifyd.base_url = "https://api.signifyd.com/" square.base_url = "https://connect.squareupsandbox.com/" @@ -276,6 +277,7 @@ cards = [ "powertranz", "prophetpay", "redsys", + "santander", "shift4", "square", "stax", diff --git a/scripts/add_connector.sh b/scripts/add_connector.sh index 8199bcc501..52227c6274 100755 --- a/scripts/add_connector.sh +++ b/scripts/add_connector.sh @@ -6,7 +6,7 @@ function find_prev_connector() { git checkout $self cp $self $self.tmp # Add new connector to existing list and sort it - connectors=(aci adyen adyenplatform airwallex amazonpay applepay archipel authorizedotnet bambora bamboraapac bankofamerica barclaycard billwerk bitpay bluesnap boku braintree cashtocode chargebee checkout coinbase cryptopay ctp_visa cybersource datatrans deutschebank digitalvirgo dlocal dummyconnector ebanx elavon facilitapay fiserv fiservemea fiuu forte getnet globalpay globepay gocardless gpayments helcim hipay hyperswitch_vault iatapay inespay itaubank jpmorgan juspaythreedsserver klarna mifinity mollie moneris multisafepay netcetera nexinets nexixpay nomupay noon nordea novalnet nuvei opayo opennode paybox payeezy payme payone paypal paystack payu placetopay plaid powertranz prophetpay rapyd razorpay recurly redsys shift4 square stax stripe stripebilling taxjar threedsecureio thunes tokenio trustpay tsys unified_authentication_service vgs volt wellsfargo wellsfargopayout wise worldline worldpay worldpayvantiv worldpayxml xendit zsl "$1") + connectors=(aci adyen adyenplatform airwallex amazonpay applepay archipel authorizedotnet bambora bamboraapac bankofamerica barclaycard billwerk bitpay bluesnap boku braintree cashtocode chargebee checkout coinbase cryptopay ctp_visa cybersource datatrans deutschebank digitalvirgo dlocal dummyconnector ebanx elavon facilitapay fiserv fiservemea fiuu forte getnet globalpay globepay gocardless gpayments helcim hipay hyperswitch_vault iatapay inespay itaubank jpmorgan juspaythreedsserver klarna mifinity mollie moneris multisafepay netcetera nexinets nexixpay nomupay noon nordea novalnet nuvei opayo opennode paybox payeezy payme payone paypal paystack payu placetopay plaid powertranz prophetpay rapyd razorpay recurly redsys santander shift4 square stax stripe stripebilling taxjar threedsecureio thunes tokenio trustpay tsys unified_authentication_service vgs volt wellsfargo wellsfargopayout wise worldline worldpay worldpayvantiv worldpayxml xendit zsl "$1") IFS=$'\n' sorted=($(sort <<<"${connectors[*]}")); unset IFS res="$(echo ${sorted[@]})" sed -i'' -e "s/^ connectors=.*/ connectors=($res \"\$1\")/" $self.tmp