diff --git a/config/config.example.toml b/config/config.example.toml index d01d67cc68..b9ac37705a 100644 --- a/config/config.example.toml +++ b/config/config.example.toml @@ -252,6 +252,7 @@ trustpay.base_url_bank_redirects = "https://aapi.trustpay.eu/" tsys.base_url = "https://stagegw.transnox.com/" volt.base_url = "https://api.sandbox.volt.io/" wellsfargo.base_url = "https://apitest.cybersource.com/" +wellsfargopayout.base_url = "https://api-sandbox.wellsfargo.com/" wise.base_url = "https://api.sandbox.transferwise.tech/" worldline.base_url = "https://eu.sandbox.api-ingenico.com/" worldpay.base_url = "https://try.access.worldpay.com/" diff --git a/config/deployments/integration_test.toml b/config/deployments/integration_test.toml index 16b715b7e7..32af0a86b1 100644 --- a/config/deployments/integration_test.toml +++ b/config/deployments/integration_test.toml @@ -90,6 +90,7 @@ trustpay.base_url_bank_redirects = "https://aapi.trustpay.eu/" tsys.base_url = "https://stagegw.transnox.com/" volt.base_url = "https://api.sandbox.volt.io/" wellsfargo.base_url = "https://apitest.cybersource.com/" +wellsfargopayout.base_url = "https://api-sandbox.wellsfargo.com/" wise.base_url = "https://api.sandbox.transferwise.tech/" worldline.base_url = "https://eu.sandbox.api-ingenico.com/" worldpay.base_url = "https://try.access.worldpay.com/" diff --git a/config/deployments/production.toml b/config/deployments/production.toml index 6bf81d3242..48414e7618 100644 --- a/config/deployments/production.toml +++ b/config/deployments/production.toml @@ -94,6 +94,7 @@ trustpay.base_url_bank_redirects = "https://aapi.trustpay.eu/" tsys.base_url = "https://gateway.transit-pass.com/" volt.base_url = "https://api.volt.io/" wellsfargo.base_url = "https://api.cybersource.com/" +wellsfargopayout.base_url = "https://api.wellsfargo.com/" wise.base_url = "https://api.sandbox.transferwise.tech/" worldline.base_url = "https://eu.sandbox.api-ingenico.com/" worldpay.base_url = "https://try.access.worldpay.com/" diff --git a/config/deployments/sandbox.toml b/config/deployments/sandbox.toml index e1e346a825..9450548bcf 100644 --- a/config/deployments/sandbox.toml +++ b/config/deployments/sandbox.toml @@ -94,6 +94,7 @@ trustpay.base_url_bank_redirects = "https://aapi.trustpay.eu/" tsys.base_url = "https://stagegw.transnox.com/" volt.base_url = "https://api.sandbox.volt.io/" wellsfargo.base_url = "https://apitest.cybersource.com/" +wellsfargopayout.base_url = "https://api-sandbox.wellsfargo.com/" wise.base_url = "https://api.sandbox.transferwise.tech/" worldline.base_url = "https://eu.sandbox.api-ingenico.com/" worldpay.base_url = "https://try.access.worldpay.com/" diff --git a/config/development.toml b/config/development.toml index 1b82a36dc0..45c5b1a2a2 100644 --- a/config/development.toml +++ b/config/development.toml @@ -154,6 +154,7 @@ cards = [ "tsys", "volt", "wellsfargo", + "wellsfargopayout", "wise", "worldline", "worldpay", @@ -255,6 +256,7 @@ trustpay.base_url = "https://test-tpgw.trustpay.eu/" tsys.base_url = "https://stagegw.transnox.com/" volt.base_url = "https://api.sandbox.volt.io/" wellsfargo.base_url = "https://apitest.cybersource.com/" +wellsfargopayout.base_url = "https://api-sandbox.wellsfargo.com/" trustpay.base_url_bank_redirects = "https://aapi.trustpay.eu/" zen.base_url = "https://api.zen-test.com/" zen.secondary_base_url = "https://secure.zen-test.com/" diff --git a/config/docker_compose.toml b/config/docker_compose.toml index a523715919..fb8de66729 100644 --- a/config/docker_compose.toml +++ b/config/docker_compose.toml @@ -181,6 +181,7 @@ trustpay.base_url_bank_redirects = "https://aapi.trustpay.eu/" tsys.base_url = "https://stagegw.transnox.com/" volt.base_url = "https://api.sandbox.volt.io/" wellsfargo.base_url = "https://apitest.cybersource.com/" +wellsfargopayout.base_url = "https://api-sandbox.wellsfargo.com/" wise.base_url = "https://api.sandbox.transferwise.tech/" worldline.base_url = "https://eu.sandbox.api-ingenico.com/" worldpay.base_url = "https://try.access.worldpay.com/" @@ -254,6 +255,7 @@ cards = [ "tsys", "volt", "wellsfargo", + "wellsfargopayout", "wise", "worldline", "worldpay", diff --git a/crates/api_models/src/enums.rs b/crates/api_models/src/enums.rs index eed29b1aa0..cf5585b4dc 100644 --- a/crates/api_models/src/enums.rs +++ b/crates/api_models/src/enums.rs @@ -132,10 +132,10 @@ pub enum Connector { Stripe, Threedsecureio, Trustpay, - // Tsys, Tsys, Volt, Wellsfargo, + // Wellsfargopayout, Wise, Worldline, Worldpay, @@ -256,6 +256,7 @@ impl Connector { | Self::Tsys | Self::Volt | Self::Wellsfargo + // | Self::Wellsfargopayout | Self::Wise | Self::Worldline | Self::Worldpay diff --git a/crates/common_enums/src/enums.rs b/crates/common_enums/src/enums.rs index 7f8f878dc9..a76a0203c1 100644 --- a/crates/common_enums/src/enums.rs +++ b/crates/common_enums/src/enums.rs @@ -249,6 +249,7 @@ pub enum RoutableConnectors { Tsys, Volt, Wellsfargo, + // Wellsfargopayout, Wise, Worldline, Worldpay, diff --git a/crates/hyperswitch_interfaces/src/configs.rs b/crates/hyperswitch_interfaces/src/configs.rs index cb3fa0313c..60dc93d057 100644 --- a/crates/hyperswitch_interfaces/src/configs.rs +++ b/crates/hyperswitch_interfaces/src/configs.rs @@ -79,6 +79,7 @@ pub struct Connectors { pub tsys: ConnectorParams, pub volt: ConnectorParams, pub wellsfargo: ConnectorParams, + pub wellsfargopayout: ConnectorParams, pub wise: ConnectorParams, pub worldline: ConnectorParams, pub worldpay: ConnectorParams, diff --git a/crates/router/src/connector.rs b/crates/router/src/connector.rs index f97d428c17..5ceb8845b7 100644 --- a/crates/router/src/connector.rs +++ b/crates/router/src/connector.rs @@ -60,6 +60,7 @@ pub mod tsys; pub mod utils; pub mod volt; pub mod wellsfargo; +pub mod wellsfargopayout; pub mod wise; pub mod worldline; pub mod worldpay; @@ -87,6 +88,6 @@ pub use self::{ powertranz::Powertranz, prophetpay::Prophetpay, rapyd::Rapyd, razorpay::Razorpay, riskified::Riskified, shift4::Shift4, signifyd::Signifyd, square::Square, stripe::Stripe, threedsecureio::Threedsecureio, trustpay::Trustpay, tsys::Tsys, volt::Volt, - wellsfargo::Wellsfargo, wise::Wise, worldline::Worldline, worldpay::Worldpay, zen::Zen, - zsl::Zsl, + wellsfargo::Wellsfargo, wellsfargopayout::Wellsfargopayout, wise::Wise, worldline::Worldline, + worldpay::Worldpay, zen::Zen, zsl::Zsl, }; diff --git a/crates/router/src/connector/wellsfargopayout.rs b/crates/router/src/connector/wellsfargopayout.rs new file mode 100644 index 0000000000..51b303494b --- /dev/null +++ b/crates/router/src/connector/wellsfargopayout.rs @@ -0,0 +1,583 @@ +pub mod transformers; + +use common_utils::types::{AmountConvertor, StringMinorUnit, StringMinorUnitForConnector}; +use error_stack::{report, ResultExt}; +use masking::ExposeInterface; +use transformers as wellsfargopayout; + +use super::utils::{self as connector_utils}; +use crate::{ + configs::settings, + core::errors::{self, CustomResult}, + events::connector_api_logs::ConnectorEvent, + headers, + services::{ + self, + request::{self, Mask}, + ConnectorIntegration, ConnectorValidation, + }, + types::{ + self, + api::{self, ConnectorCommon, ConnectorCommonExt}, + ErrorResponse, RequestContent, Response, + }, + utils::BytesExt, +}; + +#[derive(Clone)] +pub struct Wellsfargopayout { + amount_converter: &'static (dyn AmountConvertor + Sync), +} + +impl Wellsfargopayout { + pub fn new() -> &'static Self { + &Self { + amount_converter: &StringMinorUnitForConnector, + } + } +} + +impl api::Payment for Wellsfargopayout {} +impl api::PaymentSession for Wellsfargopayout {} +impl api::ConnectorAccessToken for Wellsfargopayout {} +impl api::MandateSetup for Wellsfargopayout {} +impl api::PaymentAuthorize for Wellsfargopayout {} +impl api::PaymentSync for Wellsfargopayout {} +impl api::PaymentCapture for Wellsfargopayout {} +impl api::PaymentVoid for Wellsfargopayout {} +impl api::Refund for Wellsfargopayout {} +impl api::RefundExecute for Wellsfargopayout {} +impl api::RefundSync for Wellsfargopayout {} +impl api::PaymentToken for Wellsfargopayout {} + +impl + ConnectorIntegration< + api::PaymentMethodToken, + types::PaymentMethodTokenizationData, + types::PaymentsResponseData, + > for Wellsfargopayout +{ + // Not Implemented (R) +} + +impl ConnectorCommonExt for Wellsfargopayout +where + Self: ConnectorIntegration, +{ + fn build_headers( + &self, + req: &types::RouterData, + _connectors: &settings::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 Wellsfargopayout { + fn id(&self) -> &'static str { + "wellsfargopayout" + } + + fn get_currency_unit(&self) -> api::CurrencyUnit { + api::CurrencyUnit::Minor + // todo!() + // 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 settings::Connectors) -> &'a str { + connectors.wellsfargopayout.base_url.as_ref() + } + + fn get_auth_header( + &self, + auth_type: &types::ConnectorAuthType, + ) -> CustomResult)>, errors::ConnectorError> { + let auth = wellsfargopayout::WellsfargopayoutAuthType::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: wellsfargopayout::WellsfargopayoutErrorResponse = res + .response + .parse_struct("WellsfargopayoutErrorResponse") + .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, + }) + } +} + +impl ConnectorValidation for Wellsfargopayout { + //TODO: implement functions when support enabled +} + +impl ConnectorIntegration + for Wellsfargopayout +{ + //TODO: implement sessions flow +} + +impl ConnectorIntegration + for Wellsfargopayout +{ +} + +impl + ConnectorIntegration< + api::SetupMandate, + types::SetupMandateRequestData, + types::PaymentsResponseData, + > for Wellsfargopayout +{ +} + +impl ConnectorIntegration + for Wellsfargopayout +{ + fn get_headers( + &self, + req: &types::PaymentsAuthorizeRouterData, + connectors: &settings::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: &types::PaymentsAuthorizeRouterData, + _connectors: &settings::Connectors, + ) -> CustomResult { + Err(errors::ConnectorError::NotImplemented("get_url method".to_string()).into()) + } + + fn get_request_body( + &self, + req: &types::PaymentsAuthorizeRouterData, + _connectors: &settings::Connectors, + ) -> CustomResult { + let amount = connector_utils::convert_amount( + self.amount_converter, + req.request.minor_amount, + req.request.currency, + )?; + + let connector_router_data = + wellsfargopayout::WellsfargopayoutRouterData::from((amount, req)); + let connector_req = + wellsfargopayout::WellsfargopayoutPaymentsRequest::try_from(&connector_router_data)?; + Ok(RequestContent::Json(Box::new(connector_req))) + } + + fn build_request( + &self, + req: &types::PaymentsAuthorizeRouterData, + connectors: &settings::Connectors, + ) -> CustomResult, errors::ConnectorError> { + Ok(Some( + services::RequestBuilder::new() + .method(services::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: &types::PaymentsAuthorizeRouterData, + event_builder: Option<&mut ConnectorEvent>, + res: Response, + ) -> CustomResult { + let response: wellsfargopayout::WellsfargopayoutPaymentsResponse = res + .response + .parse_struct("Wellsfargopayout PaymentsAuthorizeResponse") + .change_context(errors::ConnectorError::ResponseDeserializationFailed)?; + event_builder.map(|i| i.set_response_body(&response)); + router_env::logger::info!(connector_response=?response); + types::RouterData::try_from(types::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 Wellsfargopayout +{ + fn get_headers( + &self, + req: &types::PaymentsSyncRouterData, + connectors: &settings::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: &types::PaymentsSyncRouterData, + _connectors: &settings::Connectors, + ) -> CustomResult { + Err(errors::ConnectorError::NotImplemented("get_url method".to_string()).into()) + } + + fn build_request( + &self, + req: &types::PaymentsSyncRouterData, + connectors: &settings::Connectors, + ) -> CustomResult, errors::ConnectorError> { + Ok(Some( + services::RequestBuilder::new() + .method(services::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: &types::PaymentsSyncRouterData, + event_builder: Option<&mut ConnectorEvent>, + res: Response, + ) -> CustomResult { + let response: wellsfargopayout::WellsfargopayoutPaymentsResponse = res + .response + .parse_struct("wellsfargopayout PaymentsSyncResponse") + .change_context(errors::ConnectorError::ResponseDeserializationFailed)?; + event_builder.map(|i| i.set_response_body(&response)); + router_env::logger::info!(connector_response=?response); + types::RouterData::try_from(types::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 Wellsfargopayout +{ + fn get_headers( + &self, + req: &types::PaymentsCaptureRouterData, + connectors: &settings::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: &types::PaymentsCaptureRouterData, + _connectors: &settings::Connectors, + ) -> CustomResult { + Err(errors::ConnectorError::NotImplemented("get_url method".to_string()).into()) + } + + fn get_request_body( + &self, + _req: &types::PaymentsCaptureRouterData, + _connectors: &settings::Connectors, + ) -> CustomResult { + Err(errors::ConnectorError::NotImplemented("get_request_body method".to_string()).into()) + } + + fn build_request( + &self, + req: &types::PaymentsCaptureRouterData, + connectors: &settings::Connectors, + ) -> CustomResult, errors::ConnectorError> { + Ok(Some( + services::RequestBuilder::new() + .method(services::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: &types::PaymentsCaptureRouterData, + event_builder: Option<&mut ConnectorEvent>, + res: Response, + ) -> CustomResult { + let response: wellsfargopayout::WellsfargopayoutPaymentsResponse = res + .response + .parse_struct("Wellsfargopayout PaymentsCaptureResponse") + .change_context(errors::ConnectorError::ResponseDeserializationFailed)?; + event_builder.map(|i| i.set_response_body(&response)); + router_env::logger::info!(connector_response=?response); + types::RouterData::try_from(types::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 Wellsfargopayout +{ +} + +impl ConnectorIntegration + for Wellsfargopayout +{ + fn get_headers( + &self, + req: &types::RefundsRouterData, + connectors: &settings::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: &types::RefundsRouterData, + _connectors: &settings::Connectors, + ) -> CustomResult { + Err(errors::ConnectorError::NotImplemented("get_url method".to_string()).into()) + } + + fn get_request_body( + &self, + req: &types::RefundsRouterData, + _connectors: &settings::Connectors, + ) -> CustomResult { + let refund_amount = connector_utils::convert_amount( + self.amount_converter, + req.request.minor_refund_amount, + req.request.currency, + )?; + + let connector_router_data = + wellsfargopayout::WellsfargopayoutRouterData::from((refund_amount, req)); + let connector_req = + wellsfargopayout::WellsfargopayoutRefundRequest::try_from(&connector_router_data)?; + Ok(RequestContent::Json(Box::new(connector_req))) + } + + fn build_request( + &self, + req: &types::RefundsRouterData, + connectors: &settings::Connectors, + ) -> CustomResult, errors::ConnectorError> { + let request = services::RequestBuilder::new() + .method(services::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: &types::RefundsRouterData, + event_builder: Option<&mut ConnectorEvent>, + res: Response, + ) -> CustomResult, errors::ConnectorError> { + let response: wellsfargopayout::RefundResponse = res + .response + .parse_struct("wellsfargopayout RefundResponse") + .change_context(errors::ConnectorError::ResponseDeserializationFailed)?; + event_builder.map(|i| i.set_response_body(&response)); + router_env::logger::info!(connector_response=?response); + types::RouterData::try_from(types::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 Wellsfargopayout +{ + fn get_headers( + &self, + req: &types::RefundSyncRouterData, + connectors: &settings::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: &types::RefundSyncRouterData, + _connectors: &settings::Connectors, + ) -> CustomResult { + Err(errors::ConnectorError::NotImplemented("get_url method".to_string()).into()) + } + + fn build_request( + &self, + req: &types::RefundSyncRouterData, + connectors: &settings::Connectors, + ) -> CustomResult, errors::ConnectorError> { + Ok(Some( + services::RequestBuilder::new() + .method(services::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: &types::RefundSyncRouterData, + event_builder: Option<&mut ConnectorEvent>, + res: Response, + ) -> CustomResult { + let response: wellsfargopayout::RefundResponse = res + .response + .parse_struct("wellsfargopayout RefundSyncResponse") + .change_context(errors::ConnectorError::ResponseDeserializationFailed)?; + event_builder.map(|i| i.set_response_body(&response)); + router_env::logger::info!(connector_response=?response); + types::RouterData::try_from(types::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 api::IncomingWebhook for Wellsfargopayout { + fn get_webhook_object_reference_id( + &self, + _request: &api::IncomingWebhookRequestDetails<'_>, + ) -> CustomResult { + Err(report!(errors::ConnectorError::WebhooksNotImplemented)) + } + + fn get_webhook_event_type( + &self, + _request: &api::IncomingWebhookRequestDetails<'_>, + ) -> CustomResult { + Err(report!(errors::ConnectorError::WebhooksNotImplemented)) + } + + fn get_webhook_resource_object( + &self, + _request: &api::IncomingWebhookRequestDetails<'_>, + ) -> CustomResult, errors::ConnectorError> { + Err(report!(errors::ConnectorError::WebhooksNotImplemented)) + } +} diff --git a/crates/router/src/connector/wellsfargopayout/transformers.rs b/crates/router/src/connector/wellsfargopayout/transformers.rs new file mode 100644 index 0000000000..e335c3cf59 --- /dev/null +++ b/crates/router/src/connector/wellsfargopayout/transformers.rs @@ -0,0 +1,241 @@ +use common_utils::types::StringMinorUnit; +use masking::Secret; +use serde::{Deserialize, Serialize}; + +use crate::{ + connector::utils::PaymentsAuthorizeRequestData, + core::errors, + types::{self, api, domain, storage::enums}, +}; + +//TODO: Fill the struct with respective fields +pub struct WellsfargopayoutRouterData { + pub amount: StringMinorUnit, // The type of amount that a connector accepts, for example, String, i64, f64, etc. + pub router_data: T, +} + +impl From<(StringMinorUnit, T)> for WellsfargopayoutRouterData { + fn from((amount, item): (StringMinorUnit, 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 WellsfargopayoutPaymentsRequest { + amount: StringMinorUnit, + card: WellsfargopayoutCard, +} + +#[derive(Default, Debug, Serialize, Eq, PartialEq)] +pub struct WellsfargopayoutCard { + number: cards::CardNumber, + expiry_month: Secret, + expiry_year: Secret, + cvc: Secret, + complete: bool, +} + +impl TryFrom<&WellsfargopayoutRouterData<&types::PaymentsAuthorizeRouterData>> + for WellsfargopayoutPaymentsRequest +{ + type Error = error_stack::Report; + fn try_from( + item: &WellsfargopayoutRouterData<&types::PaymentsAuthorizeRouterData>, + ) -> Result { + match item.router_data.request.payment_method_data.clone() { + domain::PaymentMethodData::Card(req_card) => { + let card = WellsfargopayoutCard { + 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 methods".to_string()).into()), + } + } +} + +//TODO: Fill the struct with respective fields +// Auth Struct +pub struct WellsfargopayoutAuthType { + pub(super) api_key: Secret, +} + +impl TryFrom<&types::ConnectorAuthType> for WellsfargopayoutAuthType { + type Error = error_stack::Report; + fn try_from(auth_type: &types::ConnectorAuthType) -> Result { + match auth_type { + types::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 WellsfargopayoutPaymentStatus { + Succeeded, + Failed, + #[default] + Processing, +} + +impl From for enums::AttemptStatus { + fn from(item: WellsfargopayoutPaymentStatus) -> Self { + match item { + WellsfargopayoutPaymentStatus::Succeeded => Self::Charged, + WellsfargopayoutPaymentStatus::Failed => Self::Failure, + WellsfargopayoutPaymentStatus::Processing => Self::Authorizing, + } + } +} + +//TODO: Fill the struct with respective fields +#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq)] +pub struct WellsfargopayoutPaymentsResponse { + status: WellsfargopayoutPaymentStatus, + id: String, +} + +impl + TryFrom< + types::ResponseRouterData< + F, + WellsfargopayoutPaymentsResponse, + T, + types::PaymentsResponseData, + >, + > for types::RouterData +{ + type Error = error_stack::Report; + fn try_from( + item: types::ResponseRouterData< + F, + WellsfargopayoutPaymentsResponse, + T, + types::PaymentsResponseData, + >, + ) -> Result { + Ok(Self { + status: enums::AttemptStatus::from(item.response.status), + response: Ok(types::PaymentsResponseData::TransactionResponse { + resource_id: types::ResponseId::ConnectorTransactionId(item.response.id), + redirection_data: None, + mandate_reference: None, + connector_metadata: None, + network_txn_id: None, + connector_response_reference_id: None, + incremental_authorization_allowed: None, + charge_id: None, + }), + ..item.data + }) + } +} + +//TODO: Fill the struct with respective fields +// REFUND : +// Type definition for RefundRequest +#[derive(Default, Debug, Serialize)] +pub struct WellsfargopayoutRefundRequest { + pub amount: StringMinorUnit, +} + +impl TryFrom<&WellsfargopayoutRouterData<&types::RefundsRouterData>> + for WellsfargopayoutRefundRequest +{ + type Error = error_stack::Report; + fn try_from( + item: &WellsfargopayoutRouterData<&types::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 types::RefundsRouterData +{ + type Error = error_stack::Report; + fn try_from( + item: types::RefundsResponseRouterData, + ) -> Result { + Ok(Self { + response: Ok(types::RefundsResponseData { + connector_refund_id: item.response.id.to_string(), + refund_status: enums::RefundStatus::from(item.response.status), + }), + ..item.data + }) + } +} + +impl TryFrom> + for types::RefundsRouterData +{ + type Error = error_stack::Report; + fn try_from( + item: types::RefundsResponseRouterData, + ) -> Result { + Ok(Self { + response: Ok(types::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 WellsfargopayoutErrorResponse { + pub status_code: u16, + pub code: String, + pub message: String, + pub reason: Option, +} diff --git a/crates/router/src/core/payments/connector_integration_v2_impls.rs b/crates/router/src/core/payments/connector_integration_v2_impls.rs index 92ec7c0094..fc530d192e 100644 --- a/crates/router/src/core/payments/connector_integration_v2_impls.rs +++ b/crates/router/src/core/payments/connector_integration_v2_impls.rs @@ -692,6 +692,7 @@ default_imp_for_new_connector_integration_payment!( connector::Tsys, connector::Volt, connector::Wellsfargo, + connector::Wellsfargopayout, connector::Wise, connector::Worldline, connector::Worldpay, diff --git a/crates/router/src/core/payments/flows.rs b/crates/router/src/core/payments/flows.rs index 893cc957bb..5a346d2368 100644 --- a/crates/router/src/core/payments/flows.rs +++ b/crates/router/src/core/payments/flows.rs @@ -232,6 +232,7 @@ default_imp_for_complete_authorize!( connector::Volt, connector::Wise, connector::Wellsfargo, + connector::Wellsfargopayout, connector::Worldline, connector::Worldpay, connector::Zen, @@ -322,6 +323,7 @@ default_imp_for_webhook_source_verification!( connector::Tsys, connector::Volt, connector::Wellsfargo, + connector::Wellsfargopayout, connector::Wise, connector::Worldline, connector::Worldpay, @@ -414,6 +416,7 @@ default_imp_for_create_customer!( connector::Tsys, connector::Volt, connector::Wellsfargo, + connector::Wellsfargopayout, connector::Wise, connector::Worldline, connector::Worldpay, @@ -496,6 +499,7 @@ default_imp_for_connector_redirect_response!( connector::Tsys, connector::Volt, connector::Wellsfargo, + connector::Wellsfargopayout, connector::Wise, connector::Worldline, connector::Worldpay, @@ -577,6 +581,7 @@ default_imp_for_connector_request_id!( connector::Tsys, connector::Volt, connector::Wellsfargo, + connector::Wellsfargopayout, connector::Wise, connector::Worldline, connector::Worldpay, @@ -672,6 +677,7 @@ default_imp_for_accept_dispute!( connector::Tsys, connector::Volt, connector::Wellsfargo, + connector::Wellsfargopayout, connector::Wise, connector::Worldline, connector::Worldpay, @@ -786,6 +792,7 @@ default_imp_for_file_upload!( connector::Volt, connector::Opennode, connector::Wellsfargo, + connector::Wellsfargopayout, connector::Wise, connector::Worldline, connector::Worldpay, @@ -878,6 +885,7 @@ default_imp_for_submit_evidence!( connector::Volt, connector::Opennode, connector::Wellsfargo, + connector::Wellsfargopayout, connector::Wise, connector::Worldline, connector::Worldpay, @@ -971,6 +979,7 @@ default_imp_for_defend_dispute!( connector::Volt, connector::Opennode, connector::Wellsfargo, + connector::Wellsfargopayout, connector::Wise, connector::Worldline, connector::Worldpay, @@ -1069,6 +1078,7 @@ default_imp_for_pre_processing_steps!( connector::Tsys, connector::Volt, connector::Wellsfargo, + connector::Wellsfargopayout, connector::Wise, connector::Worldline, connector::Worldpay, @@ -1146,6 +1156,7 @@ default_imp_for_post_processing_steps!( connector::Tsys, connector::Volt, connector::Wellsfargo, + connector::Wellsfargopayout, connector::Wise, connector::Worldline, connector::Worldpay, @@ -1223,6 +1234,7 @@ default_imp_for_payouts!( connector::Tsys, connector::Volt, connector::Wellsfargo, + connector::Wellsfargopayout, connector::Worldline, connector::Worldpay, connector::Zen, @@ -1313,6 +1325,7 @@ default_imp_for_payouts_create!( connector::Tsys, connector::Volt, connector::Wellsfargo, + connector::Wellsfargopayout, connector::Worldline, connector::Worldpay, connector::Zen, @@ -1406,6 +1419,7 @@ default_imp_for_payouts_retrieve!( connector::Tsys, connector::Volt, connector::Wellsfargo, + connector::Wellsfargopayout, connector::Wise, connector::Worldline, connector::Worldpay, @@ -1502,6 +1516,7 @@ default_imp_for_payouts_eligibility!( connector::Tsys, connector::Volt, connector::Wellsfargo, + connector::Wellsfargopayout, connector::Worldline, connector::Worldpay, connector::Zen, @@ -1589,6 +1604,7 @@ default_imp_for_payouts_fulfill!( connector::Tsys, connector::Volt, connector::Wellsfargo, + connector::Wellsfargopayout, connector::Worldline, connector::Worldpay, connector::Zen, @@ -1680,6 +1696,7 @@ default_imp_for_payouts_cancel!( connector::Tsys, connector::Volt, connector::Wellsfargo, + connector::Wellsfargopayout, connector::Worldline, connector::Worldpay, connector::Zen, @@ -1773,6 +1790,7 @@ default_imp_for_payouts_quote!( connector::Tsys, connector::Volt, connector::Wellsfargo, + connector::Wellsfargopayout, connector::Worldline, connector::Worldpay, connector::Zen, @@ -1865,6 +1883,7 @@ default_imp_for_payouts_recipient!( connector::Tsys, connector::Volt, connector::Wellsfargo, + connector::Wellsfargopayout, connector::Worldline, connector::Worldpay, connector::Zen, @@ -1961,6 +1980,7 @@ default_imp_for_payouts_recipient_account!( connector::Tsys, connector::Volt, connector::Wellsfargo, + connector::Wellsfargopayout, connector::Wise, connector::Worldline, connector::Worldpay, @@ -2055,6 +2075,7 @@ default_imp_for_approve!( connector::Tsys, connector::Volt, connector::Wellsfargo, + connector::Wellsfargopayout, connector::Wise, connector::Worldline, connector::Worldpay, @@ -2149,6 +2170,7 @@ default_imp_for_reject!( connector::Tsys, connector::Volt, connector::Wellsfargo, + connector::Wellsfargopayout, connector::Wise, connector::Worldline, connector::Worldpay, @@ -2230,6 +2252,7 @@ default_imp_for_fraud_check!( connector::Tsys, connector::Volt, connector::Wellsfargo, + connector::Wellsfargopayout, connector::Wise, connector::Worldline, connector::Worldpay, @@ -2324,6 +2347,7 @@ default_imp_for_frm_sale!( connector::Tsys, connector::Volt, connector::Wellsfargo, + connector::Wellsfargopayout, connector::Wise, connector::Worldline, connector::Worldpay, @@ -2418,6 +2442,7 @@ default_imp_for_frm_checkout!( connector::Tsys, connector::Volt, connector::Wellsfargo, + connector::Wellsfargopayout, connector::Wise, connector::Worldline, connector::Worldpay, @@ -2512,6 +2537,7 @@ default_imp_for_frm_transaction!( connector::Tsys, connector::Volt, connector::Wellsfargo, + connector::Wellsfargopayout, connector::Wise, connector::Worldline, connector::Worldpay, @@ -2606,6 +2632,7 @@ default_imp_for_frm_fulfillment!( connector::Tsys, connector::Volt, connector::Wellsfargo, + connector::Wellsfargopayout, connector::Wise, connector::Worldline, connector::Worldpay, @@ -2700,6 +2727,7 @@ default_imp_for_frm_record_return!( connector::Tsys, connector::Volt, connector::Wellsfargo, + connector::Wellsfargopayout, connector::Wise, connector::Worldline, connector::Worldpay, @@ -2792,6 +2820,7 @@ default_imp_for_incremental_authorization!( connector::Trustpay, connector::Tsys, connector::Volt, + connector::Wellsfargopayout, connector::Wise, connector::Worldline, connector::Worldpay, @@ -3036,6 +3065,7 @@ default_imp_for_connector_authentication!( connector::Tsys, connector::Volt, connector::Wellsfargo, + connector::Wellsfargopayout, connector::Wise, connector::Worldline, connector::Worldpay, @@ -3125,6 +3155,7 @@ default_imp_for_authorize_session_token!( connector::Tsys, connector::Volt, connector::Wellsfargo, + connector::Wellsfargopayout, connector::Wise, connector::Worldline, connector::Worldpay, diff --git a/crates/router/src/types/api.rs b/crates/router/src/types/api.rs index fa04ae2ec1..7384b67367 100644 --- a/crates/router/src/types/api.rs +++ b/crates/router/src/types/api.rs @@ -471,6 +471,9 @@ impl ConnectorData { enums::Connector::Wellsfargo => { Ok(ConnectorEnum::Old(Box::new(&connector::Wellsfargo))) } + // enums::Connector::Wellsfargopayout => { + // Ok(Box::new(connector::Wellsfargopayout::new())) + // } enums::Connector::Zen => Ok(ConnectorEnum::Old(Box::new(&connector::Zen))), enums::Connector::Zsl => Ok(ConnectorEnum::Old(Box::new(&connector::Zsl))), enums::Connector::Plaid => { diff --git a/crates/router/src/types/transformers.rs b/crates/router/src/types/transformers.rs index fe65e84885..a6bc5175b4 100644 --- a/crates/router/src/types/transformers.rs +++ b/crates/router/src/types/transformers.rs @@ -299,6 +299,7 @@ impl ForeignTryFrom for common_enums::RoutableConnectors { api_enums::Connector::Tsys => Self::Tsys, api_enums::Connector::Volt => Self::Volt, api_enums::Connector::Wellsfargo => Self::Wellsfargo, + // api_enums::Connector::Wellsfargopayout => Self::Wellsfargopayout, api_enums::Connector::Wise => Self::Wise, api_enums::Connector::Worldline => Self::Worldline, api_enums::Connector::Worldpay => Self::Worldpay, diff --git a/crates/router/tests/connectors/main.rs b/crates/router/tests/connectors/main.rs index a701f11748..4c220a0a34 100644 --- a/crates/router/tests/connectors/main.rs +++ b/crates/router/tests/connectors/main.rs @@ -73,6 +73,7 @@ mod tsys; mod utils; mod volt; mod wellsfargo; +// mod wellsfargopayout; #[cfg(feature = "payouts")] mod wise; mod worldline; diff --git a/crates/router/tests/connectors/sample_auth.toml b/crates/router/tests/connectors/sample_auth.toml index 278bffa6ae..d3accafdbc 100644 --- a/crates/router/tests/connectors/sample_auth.toml +++ b/crates/router/tests/connectors/sample_auth.toml @@ -257,3 +257,8 @@ api_secret = "Secret key" [paybox] api_key="API Key" + +[wellsfargopayout] +api_key = "Consumer Key" +key1 = "Gateway Entity Id" +api_secret = "Consumer Secret" \ No newline at end of file diff --git a/crates/router/tests/connectors/wellsfargopayout.rs b/crates/router/tests/connectors/wellsfargopayout.rs new file mode 100644 index 0000000000..7612abb650 --- /dev/null +++ b/crates/router/tests/connectors/wellsfargopayout.rs @@ -0,0 +1,420 @@ +use masking::Secret; +use router::types::{self, api, storage::enums}; +use test_utils::connector_auth; + +use crate::utils::{self, ConnectorActions}; + +#[derive(Clone, Copy)] +struct WellsfargopayoutTest; +impl ConnectorActions for WellsfargopayoutTest {} +impl utils::Connector for WellsfargopayoutTest { + fn get_data(&self) -> api::ConnectorData { + use router::connector::Wellsfargopayout; + api::ConnectorData { + connector: Box::new(Wellsfargopayout::new()), + connector_name: types::Connector::Wellsfargopayout, + get_token: types::api::GetToken::Connector, + merchant_connector_id: None, + } + } + + fn get_auth_token(&self) -> types::ConnectorAuthType { + utils::to_connector_auth_type( + connector_auth::ConnectorAuthentication::new() + .wellsfargopayout + .expect("Missing connector authentication configuration") + .into(), + ) + } + + fn get_name(&self) -> String { + "wellsfargopayout".to_string() + } +} + +static CONNECTOR: WellsfargopayoutTest = WellsfargopayoutTest {}; + +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: types::api::PaymentMethodData::Card(api::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: api::PaymentMethodData::Card(api::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: api::PaymentMethodData::Card(api::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 154e4e28fd..671f9dae5c 100644 --- a/crates/test_utils/src/connector_auth.rs +++ b/crates/test_utils/src/connector_auth.rs @@ -77,6 +77,7 @@ pub struct ConnectorAuthentication { pub tsys: Option, pub volt: Option, pub wellsfargo: Option, + // pub wellsfargopayout: Option, pub wise: Option, pub worldpay: Option, pub worldline: Option, diff --git a/loadtest/config/development.toml b/loadtest/config/development.toml index 3fa9bb01c4..237c773e85 100644 --- a/loadtest/config/development.toml +++ b/loadtest/config/development.toml @@ -146,6 +146,7 @@ trustpay.base_url_bank_redirects = "https://aapi.trustpay.eu/" tsys.base_url = "https://stagegw.transnox.com/" volt.base_url = "https://api.sandbox.volt.io/" wellsfargo.base_url = "https://apitest.cybersource.com/" +wellsfargopayout.base_url = "https://api-sandbox.wellsfargo.com/" worldline.base_url = "https://eu.sandbox.api-ingenico.com/" worldpay.base_url = "https://try.access.worldpay.com/" wise.base_url = "https://api.sandbox.transferwise.tech/" @@ -219,6 +220,7 @@ cards = [ "tsys", "volt", "wellsfargo", + "wellsfargopayout", "wise", "worldline", "worldpay", diff --git a/scripts/add_connector.sh b/scripts/add_connector.sh index 21740df39f..5fec357f15 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 applepay authorizedotnet bambora bamboraapac bankofamerica billwerk bitpay bluesnap boku braintree cashtocode checkout coinbase cryptopay cybersource datatrans dlocal dummyconnector ebanx fiserv forte globalpay globepay gocardless gpayments helcim iatapay itaubank klarna mifinity mollie multisafepay netcetera nexinets noon nuvei opayo opennode paybox payeezy payme payone paypal payu placetopay plaid powertranz prophetpay rapyd razorpay shift4 square stax stripe threedsecureio trustpay tsys volt wellsfargo wise worldline worldpay zsl "$1") + connectors=(aci adyen adyenplatform airwallex applepay authorizedotnet bambora bamboraapac bankofamerica billwerk bitpay bluesnap boku braintree cashtocode checkout coinbase cryptopay cybersource datatrans dlocal dummyconnector ebanx fiserv forte globalpay globepay gocardless gpayments helcim iatapay itaubank klarna mifinity mollie multisafepay netcetera nexinets noon nuvei opayo opennode paybox payeezy payme payone paypal payu placetopay plaid powertranz prophetpay rapyd razorpay shift4 square stax stripe threedsecureio trustpay tsys volt wellsfargo wellsfargopayout wise worldline worldpay zsl "$1") IFS=$'\n' sorted=($(sort <<<"${connectors[*]}")); unset IFS res=`echo ${sorted[@]}` sed -i'' -e "s/^ connectors=.*/ connectors=($res \"\$1\")/" $self.tmp