diff --git a/api-reference/v1/openapi_spec_v1.json b/api-reference/v1/openapi_spec_v1.json index ee3ac6ea3a..3b7d2f8a41 100644 --- a/api-reference/v1/openapi_spec_v1.json +++ b/api-reference/v1/openapi_spec_v1.json @@ -12131,6 +12131,7 @@ "stripebilling", "taxjar", "threedsecureio", + "tokenex", "tokenio", "trustpay", "trustpayments", diff --git a/api-reference/v2/openapi_spec_v2.json b/api-reference/v2/openapi_spec_v2.json index 8c768053eb..32fc9db02c 100644 --- a/api-reference/v2/openapi_spec_v2.json +++ b/api-reference/v2/openapi_spec_v2.json @@ -8733,6 +8733,7 @@ "stripebilling", "taxjar", "threedsecureio", + "tokenex", "tokenio", "trustpay", "trustpayments", diff --git a/crates/api_models/src/enums.rs b/crates/api_models/src/enums.rs index 52f2b3c2e0..0b4d53c185 100644 --- a/crates/api_models/src/enums.rs +++ b/crates/api_models/src/enums.rs @@ -177,6 +177,7 @@ pub enum BillingConnectors { pub enum VaultConnectors { Vgs, HyperswitchVault, + Tokenex, } impl From for Connector { @@ -184,6 +185,7 @@ impl From for Connector { match value { VaultConnectors::Vgs => Self::Vgs, VaultConnectors::HyperswitchVault => Self::HyperswitchVault, + VaultConnectors::Tokenex => Self::Tokenex, } } } diff --git a/crates/common_enums/src/connector_enums.rs b/crates/common_enums/src/connector_enums.rs index 6f429829f5..cf2e9f1c79 100644 --- a/crates/common_enums/src/connector_enums.rs +++ b/crates/common_enums/src/connector_enums.rs @@ -336,7 +336,7 @@ pub enum Connector { Threedsecureio, // Tokenio, //Thunes, - // Tokenex, added as template code for future usage + Tokenex, Tokenio, Trustpay, Trustpayments, @@ -548,6 +548,7 @@ impl Connector { | Self::Cardinal | Self::CtpVisa | Self::Noon + | Self::Tokenex | Self::Tokenio | Self::Stripe | Self::Datatrans @@ -870,7 +871,8 @@ impl TryFrom for RoutableConnectors { | Connector::Threedsecureio | Connector::Vgs | Connector::CtpVisa - | Connector::Cardinal => Err("Invalid conversion. Not a routable connector"), + | Connector::Cardinal + | Connector::Tokenex => Err("Invalid conversion. Not a routable connector"), } } } diff --git a/crates/connector_configs/src/connector.rs b/crates/connector_configs/src/connector.rs index 9dc646f36d..3c86878215 100644 --- a/crates/connector_configs/src/connector.rs +++ b/crates/connector_configs/src/connector.rs @@ -535,6 +535,7 @@ impl ConnectorConfig { Connector::Stax => Ok(connector_data.stax), Connector::Stripe => Ok(connector_data.stripe), Connector::Stripebilling => Ok(connector_data.stripebilling), + Connector::Tokenex => Ok(connector_data.tokenex), Connector::Tokenio => Ok(connector_data.tokenio), Connector::Trustpay => Ok(connector_data.trustpay), Connector::Trustpayments => Ok(connector_data.trustpayments), diff --git a/crates/connector_configs/toml/development.toml b/crates/connector_configs/toml/development.toml index e27c801faf..7be63721c9 100644 --- a/crates/connector_configs/toml/development.toml +++ b/crates/connector_configs/toml/development.toml @@ -7087,8 +7087,9 @@ required=false type="Text" [tokenex] -[tokenex.connector_auth.HeaderKey] +[tokenex.connector_auth.BodyKey] api_key = "API Key" +key1 = "TokenEx ID" [gigadat] [gigadat.connector_auth.HeaderKey] diff --git a/crates/hyperswitch_connectors/src/connectors/tokenex.rs b/crates/hyperswitch_connectors/src/connectors/tokenex.rs index f4477cca6a..3dbe640beb 100644 --- a/crates/hyperswitch_connectors/src/connectors/tokenex.rs +++ b/crates/hyperswitch_connectors/src/connectors/tokenex.rs @@ -7,29 +7,26 @@ use common_utils::{ errors::CustomResult, ext_traits::BytesExt, request::{Method, Request, RequestBuilder, RequestContent}, - types::{AmountConvertor, StringMinorUnit, StringMinorUnitForConnector}, }; use error_stack::{report, ResultExt}; use hyperswitch_domain_models::{ - payment_method_data::PaymentMethodData, router_data::{AccessToken, ConnectorAuthType, ErrorResponse, RouterData}, router_flow_types::{ access_token_auth::AccessTokenAuth, payments::{Authorize, Capture, PSync, PaymentMethodToken, Session, SetupMandate, Void}, refunds::{Execute, RSync}, + ExternalVaultInsertFlow, ExternalVaultRetrieveFlow, }, router_request_types::{ AccessTokenRequestData, PaymentMethodTokenizationData, PaymentsAuthorizeData, PaymentsCancelData, PaymentsCaptureData, PaymentsSessionData, PaymentsSyncData, - RefundsData, SetupMandateRequestData, + RefundsData, SetupMandateRequestData, VaultRequestData, }, router_response_types::{ ConnectorInfo, PaymentsResponseData, RefundsResponseData, SupportedPaymentMethods, + VaultResponseData, }, - types::{ - PaymentsAuthorizeRouterData, PaymentsCaptureRouterData, PaymentsSyncRouterData, - RefundSyncRouterData, RefundsRouterData, - }, + types::VaultRouterData, }; use hyperswitch_interfaces::{ api::{ @@ -45,20 +42,10 @@ use hyperswitch_interfaces::{ use masking::{ExposeInterface, Mask}; use transformers as tokenex; -use crate::{constants::headers, types::ResponseRouterData, utils}; +use crate::{constants::headers, types::ResponseRouterData}; #[derive(Clone)] -pub struct Tokenex { - amount_converter: &'static (dyn AmountConvertor + Sync), -} - -impl Tokenex { - pub fn new() -> &'static Self { - &Self { - amount_converter: &StringMinorUnitForConnector, - } - } -} +pub struct Tokenex; impl api::Payment for Tokenex {} impl api::PaymentSession for Tokenex {} @@ -72,6 +59,9 @@ impl api::Refund for Tokenex {} impl api::RefundExecute for Tokenex {} impl api::RefundSync for Tokenex {} impl api::PaymentToken for Tokenex {} +impl api::ExternalVaultInsert for Tokenex {} +impl api::ExternalVault for Tokenex {} +impl api::ExternalVaultRetrieve for Tokenex {} impl ConnectorIntegration for Tokenex @@ -79,6 +69,13 @@ impl ConnectorIntegration ConnectorCommonExt for Tokenex where Self: ConnectorIntegration, @@ -88,12 +85,26 @@ where 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); + let auth = tokenex::TokenexAuthType::try_from(&req.connector_auth_type) + .change_context(errors::ConnectorError::FailedToObtainAuthType)?; + let header = vec![ + ( + headers::CONTENT_TYPE.to_string(), + self.get_content_type().to_string().into(), + ), + ( + auth_headers::TOKENEX_ID.to_string(), + auth.tokenex_id.expose().into_masked(), + ), + ( + auth_headers::TOKENEX_API_KEY.to_string(), + auth.api_key.expose().into_masked(), + ), + ( + auth_headers::TOKENEX_SCHEME.to_string(), + auth_headers::TOKENEX_SCHEME_VALUE.to_string().into(), + ), + ]; Ok(header) } } @@ -155,31 +166,7 @@ impl ConnectorCommon for Tokenex { } } -impl ConnectorValidation for Tokenex { - fn validate_mandate_payment( - &self, - _pm_type: Option, - pm_data: PaymentMethodData, - ) -> CustomResult<(), errors::ConnectorError> { - match pm_data { - PaymentMethodData::Card(_) => Err(errors::ConnectorError::NotImplemented( - "validate_mandate_payment does not support cards".to_string(), - ) - .into()), - _ => Ok(()), - } - } - - fn validate_psync_reference_id( - &self, - _data: &PaymentsSyncData, - _is_three_ds: bool, - _status: enums::AttemptStatus, - _connector_meta_data: Option, - ) -> CustomResult<(), errors::ConnectorError> { - Ok(()) - } -} +impl ConnectorValidation for Tokenex {} impl ConnectorIntegration for Tokenex { //TODO: implement sessions flow @@ -189,301 +176,77 @@ impl ConnectorIntegration impl ConnectorIntegration for Tokenex {} -impl ConnectorIntegration for Tokenex { - fn get_headers( - &self, - req: &PaymentsAuthorizeRouterData, - connectors: &Connectors, - ) -> CustomResult)>, errors::ConnectorError> { - self.build_headers(req, connectors) - } +impl ConnectorIntegration for Tokenex {} - fn get_content_type(&self) -> &'static str { - self.common_get_content_type() - } +impl ConnectorIntegration for Tokenex {} - 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 = tokenex::TokenexRouterData::from((amount, req)); - let connector_req = tokenex::TokenexPaymentsRequest::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: tokenex::TokenexPaymentsResponse = res - .response - .parse_struct("Tokenex 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 Tokenex { - 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: tokenex::TokenexPaymentsResponse = res - .response - .parse_struct("tokenex 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 Tokenex { - 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: tokenex::TokenexPaymentsResponse = res - .response - .parse_struct("Tokenex 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 Tokenex {} impl ConnectorIntegration for Tokenex {} -impl ConnectorIntegration for Tokenex { +impl ConnectorIntegration for Tokenex {} + +impl ConnectorIntegration for Tokenex {} + +impl ConnectorIntegration + for Tokenex +{ + fn get_url( + &self, + _req: &VaultRouterData, + connectors: &Connectors, + ) -> CustomResult { + Ok(format!("{}/v2/Pci/Tokenize", self.base_url(connectors))) + } + fn get_headers( &self, - req: &RefundsRouterData, + req: &VaultRouterData, 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, + req: &VaultRouterData, _connectors: &Connectors, ) -> CustomResult { - let refund_amount = utils::convert_amount( - self.amount_converter, - req.request.minor_refund_amount, - req.request.currency, - )?; - - let connector_router_data = tokenex::TokenexRouterData::from((refund_amount, req)); - let connector_req = tokenex::TokenexRefundRequest::try_from(&connector_router_data)?; + let connector_req = tokenex::TokenexInsertRequest::try_from(req)?; Ok(RequestContent::Json(Box::new(connector_req))) } fn build_request( &self, - req: &RefundsRouterData, + req: &VaultRouterData, 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)) + Ok(Some( + RequestBuilder::new() + .method(Method::Post) + .url(&types::ExternalVaultInsertType::get_url( + self, req, connectors, + )?) + .attach_default_headers() + .headers(types::ExternalVaultInsertType::get_headers( + self, req, connectors, + )?) + .set_body(types::ExternalVaultInsertType::get_request_body( + self, req, connectors, + )?) + .build(), + )) } fn handle_response( &self, - data: &RefundsRouterData, + data: &VaultRouterData, event_builder: Option<&mut ConnectorEvent>, res: Response, - ) -> CustomResult, errors::ConnectorError> { - let response: tokenex::RefundResponse = res + ) -> CustomResult, errors::ConnectorError> { + let response: tokenex::TokenexInsertResponse = res .response - .parse_struct("tokenex RefundResponse") + .parse_struct("TokenexInsertResponse") .change_context(errors::ConnectorError::ResponseDeserializationFailed)?; event_builder.map(|i| i.set_response_body(&response)); router_env::logger::info!(connector_response=?response); @@ -503,39 +266,53 @@ impl ConnectorIntegration for Tokenex } } -impl ConnectorIntegration for Tokenex { +impl ConnectorIntegration + for Tokenex +{ + fn get_url( + &self, + _req: &VaultRouterData, + connectors: &Connectors, + ) -> CustomResult { + Ok(format!( + "{}/v2/Pci/DetokenizeWithCvv", + self.base_url(connectors) + )) + } + fn get_headers( &self, - req: &RefundSyncRouterData, + req: &VaultRouterData, 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( + fn get_request_body( &self, - _req: &RefundSyncRouterData, + req: &VaultRouterData, _connectors: &Connectors, - ) -> CustomResult { - Err(errors::ConnectorError::NotImplemented("get_url method".to_string()).into()) + ) -> CustomResult { + let connector_req = tokenex::TokenexRetrieveRequest::try_from(req)?; + Ok(RequestContent::Json(Box::new(connector_req))) } fn build_request( &self, - req: &RefundSyncRouterData, + req: &VaultRouterData, connectors: &Connectors, ) -> CustomResult, errors::ConnectorError> { Ok(Some( RequestBuilder::new() - .method(Method::Get) - .url(&types::RefundSyncType::get_url(self, req, connectors)?) + .method(Method::Post) + .url(&types::ExternalVaultRetrieveType::get_url( + self, req, connectors, + )?) .attach_default_headers() - .headers(types::RefundSyncType::get_headers(self, req, connectors)?) - .set_body(types::RefundSyncType::get_request_body( + .headers(types::ExternalVaultRetrieveType::get_headers( + self, req, connectors, + )?) + .set_body(types::ExternalVaultRetrieveType::get_request_body( self, req, connectors, )?) .build(), @@ -544,13 +321,13 @@ impl ConnectorIntegration for Tokenex { fn handle_response( &self, - data: &RefundSyncRouterData, + data: &VaultRouterData, event_builder: Option<&mut ConnectorEvent>, res: Response, - ) -> CustomResult { - let response: tokenex::RefundResponse = res + ) -> CustomResult, errors::ConnectorError> { + let response: tokenex::TokenexRetrieveResponse = res .response - .parse_struct("tokenex RefundSyncResponse") + .parse_struct("TokenexRetrieveResponse") .change_context(errors::ConnectorError::ResponseDeserializationFailed)?; event_builder.map(|i| i.set_response_body(&response)); router_env::logger::info!(connector_response=?response); diff --git a/crates/hyperswitch_connectors/src/connectors/tokenex/transformers.rs b/crates/hyperswitch_connectors/src/connectors/tokenex/transformers.rs index 163fa16fa2..9d7082746b 100644 --- a/crates/hyperswitch_connectors/src/connectors/tokenex/transformers.rs +++ b/crates/hyperswitch_connectors/src/connectors/tokenex/transformers.rs @@ -1,20 +1,22 @@ -use common_enums::enums; -use common_utils::types::StringMinorUnit; +use common_utils::{ + ext_traits::{Encode, StringExt}, + types::StringMinorUnit, +}; +use error_stack::ResultExt; 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}, + router_flow_types::{ExternalVaultInsertFlow, ExternalVaultRetrieveFlow}, + router_request_types::VaultRequestData, + router_response_types::VaultResponseData, + types::VaultRouterData, + vault::PaymentMethodVaultingData, }; use hyperswitch_interfaces::errors; -use masking::Secret; +use masking::{ExposeInterface, Secret}; use serde::{Deserialize, Serialize}; -use crate::types::{RefundsResponseRouterData, ResponseRouterData}; +use crate::types::ResponseRouterData; -//TODO: Fill the struct with respective fields pub struct TokenexRouterData { pub amount: StringMinorUnit, // The type of amount that a connector accepts, for example, String, i64, f64, etc. pub router_data: T, @@ -30,33 +32,27 @@ impl From<(StringMinorUnit, T)> for TokenexRouterData { } } -//TODO: Fill the struct with respective fields #[derive(Default, Debug, Serialize, PartialEq)] -pub struct TokenexPaymentsRequest { - amount: StringMinorUnit, - card: TokenexCard, +pub struct TokenexInsertRequest { + data: Secret, } -#[derive(Default, Debug, Serialize, Eq, PartialEq)] -pub struct TokenexCard { - number: cards::CardNumber, - expiry_month: Secret, - expiry_year: Secret, - cvc: Secret, - complete: bool, -} - -impl TryFrom<&TokenexRouterData<&PaymentsAuthorizeRouterData>> for TokenexPaymentsRequest { +impl TryFrom<&VaultRouterData> for TokenexInsertRequest { type Error = error_stack::Report; - fn try_from( - item: &TokenexRouterData<&PaymentsAuthorizeRouterData>, - ) -> Result { - match item.router_data.request.payment_method_data.clone() { - PaymentMethodData::Card(_) => Err(errors::ConnectorError::NotImplemented( - "Card payment method not implemented".to_string(), + fn try_from(item: &VaultRouterData) -> Result { + match item.request.payment_method_vaulting_data.clone() { + Some(PaymentMethodVaultingData::Card(req_card)) => { + let stringified_card = req_card + .encode_to_string_of_json() + .change_context(errors::ConnectorError::RequestEncodingFailed)?; + Ok(Self { + data: Secret::new(stringified_card), + }) + } + _ => Err(errors::ConnectorError::NotImplemented( + "Payment method apart from card".to_string(), ) .into()), - _ => Err(errors::ConnectorError::NotImplemented("Payment method".to_string()).into()), } } } @@ -65,148 +61,126 @@ impl TryFrom<&TokenexRouterData<&PaymentsAuthorizeRouterData>> for TokenexPaymen // Auth Struct pub struct TokenexAuthType { pub(super) api_key: Secret, + pub(super) tokenex_id: Secret, } impl TryFrom<&ConnectorAuthType> for TokenexAuthType { type Error = error_stack::Report; fn try_from(auth_type: &ConnectorAuthType) -> Result { match auth_type { - ConnectorAuthType::HeaderKey { api_key } => Ok(Self { + ConnectorAuthType::BodyKey { api_key, key1 } => Ok(Self { api_key: api_key.to_owned(), + tokenex_id: key1.to_owned(), }), _ => Err(errors::ConnectorError::FailedToObtainAuthType.into()), } } } -// PaymentsResponse -//TODO: Append the remaining status flags -#[derive(Debug, Clone, Copy, Default, Serialize, Deserialize, PartialEq)] -#[serde(rename_all = "lowercase")] -pub enum TokenexPaymentStatus { - Succeeded, - Failed, - #[default] - Processing, -} -impl From for common_enums::AttemptStatus { - fn from(item: TokenexPaymentStatus) -> Self { - match item { - TokenexPaymentStatus::Succeeded => Self::Charged, - TokenexPaymentStatus::Failed => Self::Failure, - TokenexPaymentStatus::Processing => Self::Authorizing, - } - } -} - -//TODO: Fill the struct with respective fields #[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq)] -pub struct TokenexPaymentsResponse { - status: TokenexPaymentStatus, - id: String, +pub struct TokenexInsertResponse { + token: String, + first_six: String, + success: bool, } - -impl TryFrom> - for RouterData +impl + TryFrom< + ResponseRouterData< + ExternalVaultInsertFlow, + TokenexInsertResponse, + VaultRequestData, + VaultResponseData, + >, + > for RouterData { type Error = error_stack::Report; fn try_from( - item: ResponseRouterData, + item: ResponseRouterData< + ExternalVaultInsertFlow, + TokenexInsertResponse, + VaultRequestData, + VaultResponseData, + >, ) -> Result { + let resp = item.response; + 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, + status: common_enums::AttemptStatus::Started, + response: Ok(VaultResponseData::ExternalVaultInsertResponse { + connector_vault_id: resp.token.clone(), + //fingerprint is not provided by tokenex, using token as fingerprint + fingerprint_id: resp.token.clone(), }), ..item.data }) } } -//TODO: Fill the struct with respective fields -// REFUND : -// Type definition for RefundRequest -#[derive(Default, Debug, Serialize)] -pub struct TokenexRefundRequest { - pub amount: StringMinorUnit, +#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq)] +pub struct TokenexRetrieveRequest { + token: Secret, //Currently only card number is tokenized. Data can be stringified and can be tokenized + cache_cvv: bool, } -impl TryFrom<&TokenexRouterData<&RefundsRouterData>> for TokenexRefundRequest { +#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq)] +pub struct TokenexRetrieveResponse { + value: Secret, + success: bool, +} + +impl TryFrom<&VaultRouterData> for TokenexRetrieveRequest { type Error = error_stack::Report; - fn try_from(item: &TokenexRouterData<&RefundsRouterData>) -> Result { + fn try_from(item: &VaultRouterData) -> Result { + let connector_vault_id = item.request.connector_vault_id.as_ref().ok_or( + errors::ConnectorError::MissingRequiredField { + field_name: "connector_vault_id", + }, + )?; Ok(Self { - amount: item.amount.to_owned(), + token: Secret::new(connector_vault_id.clone()), + cache_cvv: false, //since cvv is not stored at tokenex }) } } -// Type definition for Refund Response - -#[allow(dead_code)] -#[derive(Debug, Copy, 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 { +impl + TryFrom< + ResponseRouterData< + ExternalVaultRetrieveFlow, + TokenexRetrieveResponse, + VaultRequestData, + VaultResponseData, + >, + > for RouterData +{ type Error = error_stack::Report; fn try_from( - item: RefundsResponseRouterData, + item: ResponseRouterData< + ExternalVaultRetrieveFlow, + TokenexRetrieveResponse, + VaultRequestData, + VaultResponseData, + >, ) -> Result { + let resp = item.response; + + let card_detail: api_models::payment_methods::CardDetail = resp + .value + .clone() + .expose() + .parse_struct("CardDetail") + .change_context(errors::ConnectorError::ParsingFailed)?; + Ok(Self { - response: Ok(RefundsResponseData { - connector_refund_id: item.response.id.to_string(), - refund_status: enums::RefundStatus::from(item.response.status), + status: common_enums::AttemptStatus::Started, + response: Ok(VaultResponseData::ExternalVaultRetrieveResponse { + vault_data: PaymentMethodVaultingData::Card(card_detail), }), ..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 TokenexErrorResponse { pub status_code: u16, diff --git a/crates/hyperswitch_connectors/src/default_implementations.rs b/crates/hyperswitch_connectors/src/default_implementations.rs index 057aca866f..ad6a276b67 100644 --- a/crates/hyperswitch_connectors/src/default_implementations.rs +++ b/crates/hyperswitch_connectors/src/default_implementations.rs @@ -7765,7 +7765,6 @@ default_imp_for_external_vault!( connectors::Taxjar, connectors::Threedsecureio, connectors::Thunes, - connectors::Tokenex, connectors::Tokenio, connectors::Trustpay, connectors::Trustpayments, @@ -7911,7 +7910,6 @@ default_imp_for_external_vault_insert!( connectors::Taxjar, connectors::Threedsecureio, connectors::Thunes, - connectors::Tokenex, connectors::Tokenio, connectors::Trustpay, connectors::Trustpayments, @@ -8202,7 +8200,6 @@ default_imp_for_external_vault_retrieve!( connectors::Taxjar, connectors::Threedsecureio, connectors::Thunes, - connectors::Tokenex, connectors::Tokenio, connectors::Trustpay, connectors::Trustpayments, diff --git a/crates/router/src/core/connector_validation.rs b/crates/router/src/core/connector_validation.rs index 29e13cfd37..a4bc945b93 100644 --- a/crates/router/src/core/connector_validation.rs +++ b/crates/router/src/core/connector_validation.rs @@ -492,6 +492,10 @@ impl ConnectorAuthTypeAndMetadataValidation<'_> { trustpayments::transformers::TrustpaymentsAuthType::try_from(self.auth_type)?; Ok(()) } + api_enums::Connector::Tokenex => { + tokenex::transformers::TokenexAuthType::try_from(self.auth_type)?; + Ok(()) + } api_enums::Connector::Tokenio => { tokenio::transformers::TokenioAuthType::try_from(self.auth_type)?; Ok(()) diff --git a/crates/router/src/core/unified_connector_service.rs b/crates/router/src/core/unified_connector_service.rs index 32953cd5ea..3792cfa280 100644 --- a/crates/router/src/core/unified_connector_service.rs +++ b/crates/router/src/core/unified_connector_service.rs @@ -550,7 +550,7 @@ pub fn build_unified_connector_service_external_vault_proxy_metadata( } )) } - api_enums::VaultConnectors::HyperswitchVault => None, + api_enums::VaultConnectors::HyperswitchVault | api_enums::VaultConnectors::Tokenex => None, }; match unified_service_vault_metdata { diff --git a/crates/router/src/types/api/connector_mapping.rs b/crates/router/src/types/api/connector_mapping.rs index f8ee7842fd..d0b2d5560c 100644 --- a/crates/router/src/types/api/connector_mapping.rs +++ b/crates/router/src/types/api/connector_mapping.rs @@ -432,6 +432,7 @@ impl ConnectorData { Ok(ConnectorEnum::Old(Box::new(connector::Paystack::new()))) } // enums::Connector::Thunes => Ok(ConnectorEnum::Old(Box::new(connector::Thunes))), + enums::Connector::Tokenex => Ok(ConnectorEnum::Old(Box::new(&connector::Tokenex))), enums::Connector::Tokenio => { Ok(ConnectorEnum::Old(Box::new(connector::Tokenio::new()))) } diff --git a/crates/router/src/types/api/feature_matrix.rs b/crates/router/src/types/api/feature_matrix.rs index bad9c07fe2..9f514337a6 100644 --- a/crates/router/src/types/api/feature_matrix.rs +++ b/crates/router/src/types/api/feature_matrix.rs @@ -353,6 +353,7 @@ impl FeatureMatrixConnectorData { Ok(ConnectorEnum::Old(Box::new(connector::Paystack::new()))) } // enums::Connector::Thunes => Ok(ConnectorEnum::Old(Box::new(connector::Thunes))), + enums::Connector::Tokenex => Ok(ConnectorEnum::Old(Box::new(&connector::Tokenex))), enums::Connector::Tokenio => { Ok(ConnectorEnum::Old(Box::new(connector::Tokenio::new()))) } diff --git a/crates/router/src/types/connector_transformers.rs b/crates/router/src/types/connector_transformers.rs index ee9ec93c5a..ae8dfa2672 100644 --- a/crates/router/src/types/connector_transformers.rs +++ b/crates/router/src/types/connector_transformers.rs @@ -145,6 +145,11 @@ impl ForeignTryFrom for common_enums::RoutableConnectors { api_enums::Connector::Stripe => Self::Stripe, api_enums::Connector::Stripebilling => Self::Stripebilling, // api_enums::Connector::Thunes => Self::Thunes, + api_enums::Connector::Tokenex => { + Err(common_utils::errors::ValidationError::InvalidValue { + message: "Tokenex is not a routable connector".to_string(), + })? + } api_enums::Connector::Tokenio => Self::Tokenio, api_enums::Connector::Trustpay => Self::Trustpay, api_enums::Connector::Trustpayments => Self::Trustpayments, diff --git a/crates/router/tests/connectors/tokenex.rs b/crates/router/tests/connectors/tokenex.rs index fbcbe13019..72fe2daacc 100644 --- a/crates/router/tests/connectors/tokenex.rs +++ b/crates/router/tests/connectors/tokenex.rs @@ -12,8 +12,8 @@ impl utils::Connector for TokenexTest { fn get_data(&self) -> api::ConnectorData { use router::connector::Tokenex; utils::construct_connector_data_old( - Box::new(Tokenex::new()), - types::Connector::Plaid, + Box::new(&Tokenex), + types::Connector::Tokenex, api::GetToken::Connector, None, ) diff --git a/crates/test_utils/src/connector_auth.rs b/crates/test_utils/src/connector_auth.rs index d58a1d8388..e44d56a57d 100644 --- a/crates/test_utils/src/connector_auth.rs +++ b/crates/test_utils/src/connector_auth.rs @@ -121,7 +121,7 @@ pub struct ConnectorAuthentication { pub taxjar: Option, pub threedsecureio: Option, pub thunes: Option, - pub tokenex: Option, + pub tokenex: Option, pub tokenio: Option, pub stripe_au: Option, pub stripe_uk: Option,