mirror of
				https://github.com/juspay/hyperswitch.git
				synced 2025-10-31 18:17:13 +08:00 
			
		
		
		
	feat(connector): add authorize, capture, void, psync, refund, rsync for Forte connector (#955)
Co-authored-by: Arjun Karthik <m.arjunkarthik@gmail.com> Co-authored-by: Arun Raj M <jarnura47@gmail.com>
This commit is contained in:
		| @ -186,7 +186,7 @@ impl TryFrom<&types::PaymentsAuthorizeRouterData> for AciPaymentsRequest { | ||||
|             } | ||||
|             api::PaymentMethodData::Crypto(_) | api::PaymentMethodData::BankDebit(_) => { | ||||
|                 Err(errors::ConnectorError::NotSupported { | ||||
|                     payment_method: format!("{:?}", item.payment_method), | ||||
|                     message: format!("{:?}", item.payment_method), | ||||
|                     connector: "Aci", | ||||
|                     payment_experience: api_models::enums::PaymentExperience::RedirectToUrl | ||||
|                         .to_string(), | ||||
|  | ||||
| @ -348,7 +348,7 @@ impl TryFrom<&api_enums::BankNames> for OnlineBankingCzechRepublicBanks { | ||||
|             api::enums::BankNames::CeskaSporitelna => Ok(Self::CS), | ||||
|             api::enums::BankNames::PlatnoscOnlineKartaPlatnicza => Ok(Self::C), | ||||
|             _ => Err(errors::ConnectorError::NotSupported { | ||||
|                 payment_method: String::from("BankRedirect"), | ||||
|                 message: String::from("BankRedirect"), | ||||
|                 connector: "Adyen", | ||||
|                 payment_experience: api_enums::PaymentExperience::RedirectToUrl.to_string(), | ||||
|             })?, | ||||
| @ -429,7 +429,7 @@ impl TryFrom<&api_enums::BankNames> for OnlineBankingPolandBanks { | ||||
|             api_models::enums::BankNames::VeloBank => Ok(Self::VeloBank), | ||||
|             api_models::enums::BankNames::ETransferPocztowy24 => Ok(Self::ETransferPocztowy24), | ||||
|             _ => Err(errors::ConnectorError::NotSupported { | ||||
|                 payment_method: String::from("BankRedirect"), | ||||
|                 message: String::from("BankRedirect"), | ||||
|                 connector: "Adyen", | ||||
|                 payment_experience: api_enums::PaymentExperience::RedirectToUrl.to_string(), | ||||
|             })?, | ||||
| @ -465,7 +465,7 @@ impl TryFrom<&api_enums::BankNames> for OnlineBankingSlovakiaBanks { | ||||
|             api::enums::BankNames::TatraPay => Ok(Self::Tatra), | ||||
|             api::enums::BankNames::Viamo => Ok(Self::Viamo), | ||||
|             _ => Err(errors::ConnectorError::NotSupported { | ||||
|                 payment_method: String::from("BankRedirect"), | ||||
|                 message: String::from("BankRedirect"), | ||||
|                 connector: "Adyen", | ||||
|                 payment_experience: api_enums::PaymentExperience::RedirectToUrl.to_string(), | ||||
|             })?, | ||||
| @ -697,7 +697,7 @@ impl<'a> TryFrom<&api_enums::BankNames> for AdyenTestBankNames<'a> { | ||||
|                 Self("4a0a975b-0594-4b40-9068-39f77b3a91f9") | ||||
|             } | ||||
|             _ => Err(errors::ConnectorError::NotSupported { | ||||
|                 payment_method: String::from("BankRedirect"), | ||||
|                 message: String::from("BankRedirect"), | ||||
|                 connector: "Adyen", | ||||
|                 payment_experience: api_enums::PaymentExperience::RedirectToUrl.to_string(), | ||||
|             })?, | ||||
| @ -743,7 +743,7 @@ impl<'a> TryFrom<&types::PaymentsAuthorizeRouterData> for AdyenPaymentRequest<'a | ||||
|                     AdyenPaymentRequest::try_from((item, bank_redirect)) | ||||
|                 } | ||||
|                 _ => Err(errors::ConnectorError::NotSupported { | ||||
|                     payment_method: format!("{:?}", item.request.payment_method_type), | ||||
|                     message: format!("{:?}", item.request.payment_method_type), | ||||
|                     connector: "Adyen", | ||||
|                     payment_experience: api_models::enums::PaymentExperience::RedirectToUrl | ||||
|                         .to_string(), | ||||
| @ -1130,7 +1130,7 @@ impl<'a> TryFrom<(&types::PaymentsAuthorizeRouterData, MandateReferenceId)> | ||||
|                         Ok(AdyenPaymentMethod::AdyenCard(Box::new(adyen_card))) | ||||
|                     } | ||||
|                     _ => Err(errors::ConnectorError::NotSupported { | ||||
|                         payment_method: format!("mandate_{:?}", item.payment_method), | ||||
|                         message: format!("mandate_{:?}", item.payment_method), | ||||
|                         connector: "Adyen", | ||||
|                         payment_experience: api_models::enums::PaymentExperience::RedirectToUrl | ||||
|                             .to_string(), | ||||
|  | ||||
| @ -104,7 +104,7 @@ fn get_pm_and_subsequent_auth_detail( | ||||
|                     Ok((payment_details, processing_options, subseuent_auth_info)) | ||||
|                 } | ||||
|                 _ => Err(errors::ConnectorError::NotSupported { | ||||
|                     payment_method: format!("{:?}", item.request.payment_method_data), | ||||
|                     message: format!("{:?}", item.request.payment_method_data), | ||||
|                     connector: "AuthorizeDotNet", | ||||
|                     payment_experience: api_models::enums::PaymentExperience::RedirectToUrl | ||||
|                         .to_string(), | ||||
| @ -131,7 +131,7 @@ fn get_pm_and_subsequent_auth_detail( | ||||
|             } | ||||
|             api::PaymentMethodData::Crypto(_) | api::PaymentMethodData::BankDebit(_) => { | ||||
|                 Err(errors::ConnectorError::NotSupported { | ||||
|                     payment_method: format!("{:?}", item.request.payment_method_data), | ||||
|                     message: format!("{:?}", item.request.payment_method_data), | ||||
|                     connector: "AuthorizeDotNet", | ||||
|                     payment_experience: api_models::enums::PaymentExperience::RedirectToUrl | ||||
|                         .to_string(), | ||||
|  | ||||
| @ -2,11 +2,14 @@ mod transformers; | ||||
|  | ||||
| use std::fmt::Debug; | ||||
|  | ||||
| use base64::Engine; | ||||
| use error_stack::{IntoReport, ResultExt}; | ||||
| use transformers as forte; | ||||
|  | ||||
| use crate::{ | ||||
|     configs::settings, | ||||
|     connector::utils::{PaymentsSyncRequestData, RefundsRequestData}, | ||||
|     consts, | ||||
|     core::errors::{self, CustomResult}, | ||||
|     headers, | ||||
|     services::{self, ConnectorIntegration}, | ||||
| @ -42,6 +45,7 @@ impl | ||||
|     > for Forte | ||||
| { | ||||
| } | ||||
| pub const AUTH_ORG_ID_HEADER: &str = "X-Forte-Auth-Organization-Id"; | ||||
|  | ||||
| impl<Flow, Request, Response> ConnectorCommonExt<Flow, Request, Response> for Forte | ||||
| where | ||||
| @ -52,13 +56,10 @@ where | ||||
|         req: &types::RouterData<Flow, Request, Response>, | ||||
|         _connectors: &settings::Connectors, | ||||
|     ) -> CustomResult<Vec<(String, String)>, errors::ConnectorError> { | ||||
|         let mut header = vec![( | ||||
|             headers::CONTENT_TYPE.to_string(), | ||||
|             types::PaymentsAuthorizeType::get_content_type(self).to_string(), | ||||
|         )]; | ||||
|         let mut api_key = self.get_auth_header(&req.connector_auth_type)?; | ||||
|         header.append(&mut api_key); | ||||
|         Ok(header) | ||||
|         let content_type = ConnectorCommon::common_get_content_type(self); | ||||
|         let mut common_headers = self.get_auth_header(&req.connector_auth_type)?; | ||||
|         common_headers.push((headers::CONTENT_TYPE.to_string(), content_type.to_string())); | ||||
|         Ok(common_headers) | ||||
|     } | ||||
| } | ||||
|  | ||||
| @ -79,25 +80,34 @@ impl ConnectorCommon for Forte { | ||||
|         &self, | ||||
|         auth_type: &types::ConnectorAuthType, | ||||
|     ) -> CustomResult<Vec<(String, String)>, errors::ConnectorError> { | ||||
|         let auth = forte::ForteAuthType::try_from(auth_type) | ||||
|         let auth: forte::ForteAuthType = auth_type | ||||
|             .try_into() | ||||
|             .change_context(errors::ConnectorError::FailedToObtainAuthType)?; | ||||
|         Ok(vec![(headers::AUTHORIZATION.to_string(), auth.api_key)]) | ||||
|         let raw_basic_token = format!("{}:{}", auth.api_access_id, auth.api_secret_key); | ||||
|         let basic_token = format!("Basic {}", consts::BASE64_ENGINE.encode(raw_basic_token)); | ||||
|         Ok(vec![ | ||||
|             (headers::AUTHORIZATION.to_string(), basic_token), | ||||
|             (AUTH_ORG_ID_HEADER.to_string(), auth.organization_id), | ||||
|         ]) | ||||
|     } | ||||
|  | ||||
|     fn build_error_response( | ||||
|         &self, | ||||
|         res: Response, | ||||
|     ) -> CustomResult<ErrorResponse, errors::ConnectorError> { | ||||
|         let response: forte::ForteErrorResponse = | ||||
|             res.response | ||||
|                 .parse_struct("ForteErrorResponse") | ||||
|                 .change_context(errors::ConnectorError::ResponseDeserializationFailed)?; | ||||
|  | ||||
|         let response: forte::ForteErrorResponse = res | ||||
|             .response | ||||
|             .parse_struct("Forte ErrorResponse") | ||||
|             .change_context(errors::ConnectorError::ResponseDeserializationFailed)?; | ||||
|         let message = response.response.response_desc; | ||||
|         let code = response | ||||
|             .response | ||||
|             .response_code | ||||
|             .unwrap_or_else(|| consts::NO_ERROR_CODE.to_string()); | ||||
|         Ok(ErrorResponse { | ||||
|             status_code: res.status_code, | ||||
|             code: response.code, | ||||
|             message: response.message, | ||||
|             reason: response.reason, | ||||
|             code, | ||||
|             message, | ||||
|             reason: None, | ||||
|         }) | ||||
|     } | ||||
| } | ||||
| @ -134,19 +144,25 @@ impl ConnectorIntegration<api::Authorize, types::PaymentsAuthorizeData, types::P | ||||
|  | ||||
|     fn get_url( | ||||
|         &self, | ||||
|         _req: &types::PaymentsAuthorizeRouterData, | ||||
|         _connectors: &settings::Connectors, | ||||
|         req: &types::PaymentsAuthorizeRouterData, | ||||
|         connectors: &settings::Connectors, | ||||
|     ) -> CustomResult<String, errors::ConnectorError> { | ||||
|         Err(errors::ConnectorError::NotImplemented("get_url method".to_string()).into()) | ||||
|         let auth: forte::ForteAuthType = forte::ForteAuthType::try_from(&req.connector_auth_type)?; | ||||
|         Ok(format!( | ||||
|             "{}/organizations/{}/locations/{}/transactions", | ||||
|             self.base_url(connectors), | ||||
|             auth.organization_id, | ||||
|             auth.location_id | ||||
|         )) | ||||
|     } | ||||
|  | ||||
|     fn get_request_body( | ||||
|         &self, | ||||
|         req: &types::PaymentsAuthorizeRouterData, | ||||
|     ) -> CustomResult<Option<String>, errors::ConnectorError> { | ||||
|         let req_obj = forte::FortePaymentsRequest::try_from(req)?; | ||||
|         let connector_req = forte::FortePaymentsRequest::try_from(req)?; | ||||
|         let forte_req = | ||||
|             utils::Encode::<forte::FortePaymentsRequest>::encode_to_string_of_json(&req_obj) | ||||
|             utils::Encode::<forte::FortePaymentsRequest>::encode_to_string_of_json(&connector_req) | ||||
|                 .change_context(errors::ConnectorError::RequestEncodingFailed)?; | ||||
|         Ok(Some(forte_req)) | ||||
|     } | ||||
| @ -178,14 +194,13 @@ impl ConnectorIntegration<api::Authorize, types::PaymentsAuthorizeData, types::P | ||||
|     ) -> CustomResult<types::PaymentsAuthorizeRouterData, errors::ConnectorError> { | ||||
|         let response: forte::FortePaymentsResponse = res | ||||
|             .response | ||||
|             .parse_struct("Forte PaymentsAuthorizeResponse") | ||||
|             .parse_struct("Forte AuthorizeResponse") | ||||
|             .change_context(errors::ConnectorError::ResponseDeserializationFailed)?; | ||||
|         types::RouterData::try_from(types::ResponseRouterData { | ||||
|             response, | ||||
|             data: data.clone(), | ||||
|             http_code: res.status_code, | ||||
|         }) | ||||
|         .change_context(errors::ConnectorError::ResponseHandlingFailed) | ||||
|     } | ||||
|  | ||||
|     fn get_error_response( | ||||
| @ -213,10 +228,19 @@ impl ConnectorIntegration<api::PSync, types::PaymentsSyncData, types::PaymentsRe | ||||
|  | ||||
|     fn get_url( | ||||
|         &self, | ||||
|         _req: &types::PaymentsSyncRouterData, | ||||
|         _connectors: &settings::Connectors, | ||||
|         req: &types::PaymentsSyncRouterData, | ||||
|         connectors: &settings::Connectors, | ||||
|     ) -> CustomResult<String, errors::ConnectorError> { | ||||
|         Err(errors::ConnectorError::NotImplemented("get_url method".to_string()).into()) | ||||
|         let auth: forte::ForteAuthType = forte::ForteAuthType::try_from(&req.connector_auth_type)?; | ||||
|         let txn_id = PaymentsSyncRequestData::get_connector_transaction_id(&req.request) | ||||
|             .change_context(errors::ConnectorError::MissingConnectorTransactionID)?; | ||||
|         Ok(format!( | ||||
|             "{}/organizations/{}/locations/{}/transactions/{}", | ||||
|             self.base_url(connectors), | ||||
|             auth.organization_id, | ||||
|             auth.location_id, | ||||
|             txn_id | ||||
|         )) | ||||
|     } | ||||
|  | ||||
|     fn build_request( | ||||
| @ -233,13 +257,12 @@ impl ConnectorIntegration<api::PSync, types::PaymentsSyncData, types::PaymentsRe | ||||
|                 .build(), | ||||
|         )) | ||||
|     } | ||||
|  | ||||
|     fn handle_response( | ||||
|         &self, | ||||
|         data: &types::PaymentsSyncRouterData, | ||||
|         res: Response, | ||||
|     ) -> CustomResult<types::PaymentsSyncRouterData, errors::ConnectorError> { | ||||
|         let response: forte::FortePaymentsResponse = res | ||||
|         let response: forte::FortePaymentsSyncResponse = res | ||||
|             .response | ||||
|             .parse_struct("forte PaymentsSyncResponse") | ||||
|             .change_context(errors::ConnectorError::ResponseDeserializationFailed)?; | ||||
| @ -248,7 +271,6 @@ impl ConnectorIntegration<api::PSync, types::PaymentsSyncData, types::PaymentsRe | ||||
|             data: data.clone(), | ||||
|             http_code: res.status_code, | ||||
|         }) | ||||
|         .change_context(errors::ConnectorError::ResponseHandlingFailed) | ||||
|     } | ||||
|  | ||||
|     fn get_error_response( | ||||
| @ -276,17 +298,27 @@ impl ConnectorIntegration<api::Capture, types::PaymentsCaptureData, types::Payme | ||||
|  | ||||
|     fn get_url( | ||||
|         &self, | ||||
|         _req: &types::PaymentsCaptureRouterData, | ||||
|         _connectors: &settings::Connectors, | ||||
|         req: &types::PaymentsCaptureRouterData, | ||||
|         connectors: &settings::Connectors, | ||||
|     ) -> CustomResult<String, errors::ConnectorError> { | ||||
|         Err(errors::ConnectorError::NotImplemented("get_url method".to_string()).into()) | ||||
|         let auth: forte::ForteAuthType = forte::ForteAuthType::try_from(&req.connector_auth_type)?; | ||||
|         Ok(format!( | ||||
|             "{}/organizations/{}/locations/{}/transactions", | ||||
|             self.base_url(connectors), | ||||
|             auth.organization_id, | ||||
|             auth.location_id | ||||
|         )) | ||||
|     } | ||||
|  | ||||
|     fn get_request_body( | ||||
|         &self, | ||||
|         _req: &types::PaymentsCaptureRouterData, | ||||
|         req: &types::PaymentsCaptureRouterData, | ||||
|     ) -> CustomResult<Option<String>, errors::ConnectorError> { | ||||
|         Err(errors::ConnectorError::NotImplemented("get_request_body method".to_string()).into()) | ||||
|         let connector_req = forte::ForteCaptureRequest::try_from(req)?; | ||||
|         let forte_req = | ||||
|             utils::Encode::<forte::ForteCaptureRequest>::encode_to_string_of_json(&connector_req) | ||||
|                 .change_context(errors::ConnectorError::RequestEncodingFailed)?; | ||||
|         Ok(Some(forte_req)) | ||||
|     } | ||||
|  | ||||
|     fn build_request( | ||||
| @ -296,12 +328,13 @@ impl ConnectorIntegration<api::Capture, types::PaymentsCaptureData, types::Payme | ||||
|     ) -> CustomResult<Option<services::Request>, errors::ConnectorError> { | ||||
|         Ok(Some( | ||||
|             services::RequestBuilder::new() | ||||
|                 .method(services::Method::Post) | ||||
|                 .method(services::Method::Put) | ||||
|                 .url(&types::PaymentsCaptureType::get_url(self, req, connectors)?) | ||||
|                 .attach_default_headers() | ||||
|                 .headers(types::PaymentsCaptureType::get_headers( | ||||
|                     self, req, connectors, | ||||
|                 )?) | ||||
|                 .body(types::PaymentsCaptureType::get_request_body(self, req)?) | ||||
|                 .build(), | ||||
|         )) | ||||
|     } | ||||
| @ -311,7 +344,7 @@ impl ConnectorIntegration<api::Capture, types::PaymentsCaptureData, types::Payme | ||||
|         data: &types::PaymentsCaptureRouterData, | ||||
|         res: Response, | ||||
|     ) -> CustomResult<types::PaymentsCaptureRouterData, errors::ConnectorError> { | ||||
|         let response: forte::FortePaymentsResponse = res | ||||
|         let response: forte::ForteCaptureResponse = res | ||||
|             .response | ||||
|             .parse_struct("Forte PaymentsCaptureResponse") | ||||
|             .change_context(errors::ConnectorError::ResponseDeserializationFailed)?; | ||||
| @ -320,7 +353,6 @@ impl ConnectorIntegration<api::Capture, types::PaymentsCaptureData, types::Payme | ||||
|             data: data.clone(), | ||||
|             http_code: res.status_code, | ||||
|         }) | ||||
|         .change_context(errors::ConnectorError::ResponseHandlingFailed) | ||||
|     } | ||||
|  | ||||
|     fn get_error_response( | ||||
| @ -330,10 +362,84 @@ impl ConnectorIntegration<api::Capture, types::PaymentsCaptureData, types::Payme | ||||
|         self.build_error_response(res) | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl ConnectorIntegration<api::Void, types::PaymentsCancelData, types::PaymentsResponseData> | ||||
|     for Forte | ||||
| { | ||||
|     fn get_headers( | ||||
|         &self, | ||||
|         req: &types::PaymentsCancelRouterData, | ||||
|         connectors: &settings::Connectors, | ||||
|     ) -> CustomResult<Vec<(String, String)>, 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::PaymentsCancelRouterData, | ||||
|         connectors: &settings::Connectors, | ||||
|     ) -> CustomResult<String, errors::ConnectorError> { | ||||
|         let auth: forte::ForteAuthType = forte::ForteAuthType::try_from(&req.connector_auth_type)?; | ||||
|         Ok(format!( | ||||
|             "{}/organizations/{}/locations/{}/transactions/{}", | ||||
|             self.base_url(connectors), | ||||
|             auth.organization_id, | ||||
|             auth.location_id, | ||||
|             req.request.connector_transaction_id | ||||
|         )) | ||||
|     } | ||||
|     fn get_request_body( | ||||
|         &self, | ||||
|         req: &types::PaymentsCancelRouterData, | ||||
|     ) -> CustomResult<Option<String>, errors::ConnectorError> { | ||||
|         let connector_req = forte::ForteCancelRequest::try_from(req)?; | ||||
|         let forte_req = | ||||
|             utils::Encode::<forte::ForteCancelRequest>::encode_to_string_of_json(&connector_req) | ||||
|                 .change_context(errors::ConnectorError::RequestEncodingFailed)?; | ||||
|         Ok(Some(forte_req)) | ||||
|     } | ||||
|  | ||||
|     fn build_request( | ||||
|         &self, | ||||
|         req: &types::PaymentsCancelRouterData, | ||||
|         connectors: &settings::Connectors, | ||||
|     ) -> CustomResult<Option<services::Request>, errors::ConnectorError> { | ||||
|         Ok(Some( | ||||
|             services::RequestBuilder::new() | ||||
|                 .method(services::Method::Put) | ||||
|                 .url(&types::PaymentsVoidType::get_url(self, req, connectors)?) | ||||
|                 .attach_default_headers() | ||||
|                 .headers(types::PaymentsVoidType::get_headers(self, req, connectors)?) | ||||
|                 .body(types::PaymentsVoidType::get_request_body(self, req)?) | ||||
|                 .build(), | ||||
|         )) | ||||
|     } | ||||
|  | ||||
|     fn handle_response( | ||||
|         &self, | ||||
|         data: &types::PaymentsCancelRouterData, | ||||
|         res: Response, | ||||
|     ) -> CustomResult<types::PaymentsCancelRouterData, errors::ConnectorError> { | ||||
|         let response: forte::ForteCancelResponse = res | ||||
|             .response | ||||
|             .parse_struct("forte CancelResponse") | ||||
|             .change_context(errors::ConnectorError::ResponseDeserializationFailed)?; | ||||
|         types::RouterData::try_from(types::ResponseRouterData { | ||||
|             response, | ||||
|             data: data.clone(), | ||||
|             http_code: res.status_code, | ||||
|         }) | ||||
|     } | ||||
|  | ||||
|     fn get_error_response( | ||||
|         &self, | ||||
|         res: Response, | ||||
|     ) -> CustomResult<ErrorResponse, errors::ConnectorError> { | ||||
|         self.build_error_response(res) | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl ConnectorIntegration<api::Execute, types::RefundsData, types::RefundsResponseData> for Forte { | ||||
| @ -351,19 +457,25 @@ impl ConnectorIntegration<api::Execute, types::RefundsData, types::RefundsRespon | ||||
|  | ||||
|     fn get_url( | ||||
|         &self, | ||||
|         _req: &types::RefundsRouterData<api::Execute>, | ||||
|         _connectors: &settings::Connectors, | ||||
|         req: &types::RefundsRouterData<api::Execute>, | ||||
|         connectors: &settings::Connectors, | ||||
|     ) -> CustomResult<String, errors::ConnectorError> { | ||||
|         Err(errors::ConnectorError::NotImplemented("get_url method".to_string()).into()) | ||||
|         let auth: forte::ForteAuthType = forte::ForteAuthType::try_from(&req.connector_auth_type)?; | ||||
|         Ok(format!( | ||||
|             "{}/organizations/{}/locations/{}/transactions", | ||||
|             self.base_url(connectors), | ||||
|             auth.organization_id, | ||||
|             auth.location_id | ||||
|         )) | ||||
|     } | ||||
|  | ||||
|     fn get_request_body( | ||||
|         &self, | ||||
|         req: &types::RefundsRouterData<api::Execute>, | ||||
|     ) -> CustomResult<Option<String>, errors::ConnectorError> { | ||||
|         let req_obj = forte::ForteRefundRequest::try_from(req)?; | ||||
|         let connector_req = forte::ForteRefundRequest::try_from(req)?; | ||||
|         let forte_req = | ||||
|             utils::Encode::<forte::ForteRefundRequest>::encode_to_string_of_json(&req_obj) | ||||
|             utils::Encode::<forte::ForteRefundRequest>::encode_to_string_of_json(&connector_req) | ||||
|                 .change_context(errors::ConnectorError::RequestEncodingFailed)?; | ||||
|         Ok(Some(forte_req)) | ||||
|     } | ||||
| @ -399,7 +511,6 @@ impl ConnectorIntegration<api::Execute, types::RefundsData, types::RefundsRespon | ||||
|             data: data.clone(), | ||||
|             http_code: res.status_code, | ||||
|         }) | ||||
|         .change_context(errors::ConnectorError::ResponseHandlingFailed) | ||||
|     } | ||||
|  | ||||
|     fn get_error_response( | ||||
| @ -425,10 +536,17 @@ impl ConnectorIntegration<api::RSync, types::RefundsData, types::RefundsResponse | ||||
|  | ||||
|     fn get_url( | ||||
|         &self, | ||||
|         _req: &types::RefundSyncRouterData, | ||||
|         _connectors: &settings::Connectors, | ||||
|         req: &types::RefundSyncRouterData, | ||||
|         connectors: &settings::Connectors, | ||||
|     ) -> CustomResult<String, errors::ConnectorError> { | ||||
|         Err(errors::ConnectorError::NotImplemented("get_url method".to_string()).into()) | ||||
|         let auth: forte::ForteAuthType = forte::ForteAuthType::try_from(&req.connector_auth_type)?; | ||||
|         Ok(format!( | ||||
|             "{}/organizations/{}/locations/{}/transactions/{}", | ||||
|             self.base_url(connectors), | ||||
|             auth.organization_id, | ||||
|             auth.location_id, | ||||
|             req.request.get_connector_refund_id()? | ||||
|         )) | ||||
|     } | ||||
|  | ||||
|     fn build_request( | ||||
| @ -442,7 +560,6 @@ impl ConnectorIntegration<api::RSync, types::RefundsData, types::RefundsResponse | ||||
|                 .url(&types::RefundSyncType::get_url(self, req, connectors)?) | ||||
|                 .attach_default_headers() | ||||
|                 .headers(types::RefundSyncType::get_headers(self, req, connectors)?) | ||||
|                 .body(types::RefundSyncType::get_request_body(self, req)?) | ||||
|                 .build(), | ||||
|         )) | ||||
|     } | ||||
| @ -452,7 +569,7 @@ impl ConnectorIntegration<api::RSync, types::RefundsData, types::RefundsResponse | ||||
|         data: &types::RefundSyncRouterData, | ||||
|         res: Response, | ||||
|     ) -> CustomResult<types::RefundSyncRouterData, errors::ConnectorError> { | ||||
|         let response: forte::RefundResponse = res | ||||
|         let response: forte::RefundSyncResponse = res | ||||
|             .response | ||||
|             .parse_struct("forte RefundSyncResponse") | ||||
|             .change_context(errors::ConnectorError::ResponseDeserializationFailed)?; | ||||
| @ -461,7 +578,6 @@ impl ConnectorIntegration<api::RSync, types::RefundsData, types::RefundsResponse | ||||
|             data: data.clone(), | ||||
|             http_code: res.status_code, | ||||
|         }) | ||||
|         .change_context(errors::ConnectorError::ResponseHandlingFailed) | ||||
|     } | ||||
|  | ||||
|     fn get_error_response( | ||||
|  | ||||
| @ -2,90 +2,242 @@ use masking::Secret; | ||||
| use serde::{Deserialize, Serialize}; | ||||
|  | ||||
| use crate::{ | ||||
|     connector::utils::PaymentsAuthorizeRequestData, | ||||
|     connector::utils::{ | ||||
|         self, AddressDetailsData, CardData, PaymentsAuthorizeRequestData, RouterData, | ||||
|     }, | ||||
|     core::errors, | ||||
|     types::{self, api, storage::enums}, | ||||
|     pii::{self}, | ||||
|     types::{self, api, storage::enums, transformers::ForeignFrom}, | ||||
| }; | ||||
|  | ||||
| #[derive(Default, Debug, Serialize, Eq, PartialEq)] | ||||
| #[derive(Debug, Serialize)] | ||||
| pub struct FortePaymentsRequest { | ||||
|     amount: i64, | ||||
|     card: ForteCard, | ||||
|     action: ForteAction, | ||||
|     authorization_amount: f64, | ||||
|     billing_address: BillingAddress, | ||||
|     card: Card, | ||||
| } | ||||
| #[derive(Debug, Serialize, Deserialize)] | ||||
| pub struct BillingAddress { | ||||
|     first_name: Secret<String>, | ||||
|     last_name: Secret<String>, | ||||
| } | ||||
|  | ||||
| #[derive(Default, Debug, Serialize, Eq, PartialEq)] | ||||
| pub struct ForteCard { | ||||
|     name: Secret<String>, | ||||
|     number: Secret<String, common_utils::pii::CardNumber>, | ||||
|     expiry_month: Secret<String>, | ||||
|     expiry_year: Secret<String>, | ||||
|     cvc: Secret<String>, | ||||
|     complete: bool, | ||||
| #[derive(Debug, Serialize)] | ||||
| pub struct Card { | ||||
|     card_type: ForteCardType, | ||||
|     name_on_card: Secret<String>, | ||||
|     account_number: Secret<String, pii::CardNumber>, | ||||
|     expire_month: Secret<String>, | ||||
|     expire_year: Secret<String>, | ||||
|     card_verification_value: Secret<String>, | ||||
| } | ||||
|  | ||||
| #[derive(Debug, Serialize, Deserialize)] | ||||
| #[serde(rename_all = "snake_case")] | ||||
| pub enum ForteCardType { | ||||
|     Visa, | ||||
|     MasterCard, | ||||
|     Amex, | ||||
|     Discover, | ||||
|     DinersClub, | ||||
|     Jcb, | ||||
| } | ||||
|  | ||||
| impl TryFrom<utils::CardIssuer> for ForteCardType { | ||||
|     type Error = error_stack::Report<errors::ConnectorError>; | ||||
|     fn try_from(issuer: utils::CardIssuer) -> Result<Self, Self::Error> { | ||||
|         match issuer { | ||||
|             utils::CardIssuer::AmericanExpress => Ok(Self::Amex), | ||||
|             utils::CardIssuer::Master => Ok(Self::MasterCard), | ||||
|             utils::CardIssuer::Discover => Ok(Self::Discover), | ||||
|             utils::CardIssuer::Visa => Ok(Self::Visa), | ||||
|             utils::CardIssuer::DinersClub => Ok(Self::DinersClub), | ||||
|             utils::CardIssuer::JCB => Ok(Self::Jcb), | ||||
|             _ => Err(errors::ConnectorError::NotSupported { | ||||
|                 message: issuer.to_string(), | ||||
|                 connector: "Forte", | ||||
|                 payment_experience: api::enums::PaymentExperience::RedirectToUrl.to_string(), | ||||
|             } | ||||
|             .into()), | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl TryFrom<&types::PaymentsAuthorizeRouterData> for FortePaymentsRequest { | ||||
|     type Error = error_stack::Report<errors::ConnectorError>; | ||||
|     fn try_from(item: &types::PaymentsAuthorizeRouterData) -> Result<Self, Self::Error> { | ||||
|         match item.request.payment_method_data.clone() { | ||||
|             api::PaymentMethodData::Card(req_card) => { | ||||
|                 let card = ForteCard { | ||||
|                     name: req_card.card_holder_name, | ||||
|                     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.request.is_auto_capture()?, | ||||
|         if item.request.currency != enums::Currency::USD { | ||||
|             Err(errors::ConnectorError::NotSupported { | ||||
|                 message: item.request.currency.to_string(), | ||||
|                 connector: "Forte", | ||||
|                 payment_experience: api::enums::PaymentExperience::RedirectToUrl.to_string(), | ||||
|             })? | ||||
|         } | ||||
|         match item.request.payment_method_data { | ||||
|             api_models::payments::PaymentMethodData::Card(ref ccard) => { | ||||
|                 let action = match item.request.is_auto_capture()? { | ||||
|                     true => ForteAction::Sale, | ||||
|                     false => ForteAction::Authorize, | ||||
|                 }; | ||||
|                 let card_type = ForteCardType::try_from(ccard.get_card_issuer()?)?; | ||||
|                 let address = item.get_billing_address()?; | ||||
|                 let card = Card { | ||||
|                     card_type, | ||||
|                     name_on_card: ccard.card_holder_name.clone(), | ||||
|                     account_number: ccard.card_number.clone(), | ||||
|                     expire_month: ccard.card_exp_month.clone(), | ||||
|                     expire_year: ccard.card_exp_year.clone(), | ||||
|                     card_verification_value: ccard.card_cvc.clone(), | ||||
|                 }; | ||||
|                 let billing_address = BillingAddress { | ||||
|                     first_name: address.get_first_name()?.to_owned(), | ||||
|                     last_name: address.get_last_name()?.to_owned(), | ||||
|                 }; | ||||
|                 let authorization_amount = | ||||
|                     utils::to_currency_base_unit_asf64(item.request.amount, item.request.currency)?; | ||||
|                 Ok(Self { | ||||
|                     amount: item.request.amount, | ||||
|                     action, | ||||
|                     authorization_amount, | ||||
|                     billing_address, | ||||
|                     card, | ||||
|                 }) | ||||
|             } | ||||
|             _ => Err(errors::ConnectorError::NotImplemented("Payment methods".to_string()).into()), | ||||
|             _ => Err(errors::ConnectorError::NotImplemented( | ||||
|                 "Payment Method".to_string(), | ||||
|             ))?, | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| // Auth Struct | ||||
| pub struct ForteAuthType { | ||||
|     pub(super) api_key: String, | ||||
|     pub(super) api_access_id: String, | ||||
|     pub(super) organization_id: String, | ||||
|     pub(super) location_id: String, | ||||
|     pub(super) api_secret_key: String, | ||||
| } | ||||
|  | ||||
| impl TryFrom<&types::ConnectorAuthType> for ForteAuthType { | ||||
|     type Error = error_stack::Report<errors::ConnectorError>; | ||||
|     fn try_from(auth_type: &types::ConnectorAuthType) -> Result<Self, Self::Error> { | ||||
|         match auth_type { | ||||
|             types::ConnectorAuthType::HeaderKey { api_key } => Ok(Self { | ||||
|                 api_key: api_key.to_string(), | ||||
|             types::ConnectorAuthType::MultiAuthKey { | ||||
|                 api_key, | ||||
|                 key1, | ||||
|                 api_secret, | ||||
|                 key2, | ||||
|             } => Ok(Self { | ||||
|                 api_access_id: api_key.to_string(), | ||||
|                 organization_id: format!("org_{}", key1), | ||||
|                 location_id: format!("loc_{}", key2), | ||||
|                 api_secret_key: api_secret.to_string(), | ||||
|             }), | ||||
|             _ => Err(errors::ConnectorError::FailedToObtainAuthType.into()), | ||||
|             _ => Err(errors::ConnectorError::FailedToObtainAuthType)?, | ||||
|         } | ||||
|     } | ||||
| } | ||||
| // PaymentsResponse | ||||
| #[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq)] | ||||
| #[derive(Debug, Deserialize)] | ||||
| #[serde(rename_all = "lowercase")] | ||||
| pub enum FortePaymentStatus { | ||||
|     Succeeded, | ||||
|     Complete, | ||||
|     Failed, | ||||
|     #[default] | ||||
|     Processing, | ||||
|     Authorized, | ||||
|     Ready, | ||||
|     Voided, | ||||
|     Settled, | ||||
| } | ||||
|  | ||||
| impl From<FortePaymentStatus> for enums::AttemptStatus { | ||||
|     fn from(item: FortePaymentStatus) -> Self { | ||||
|         match item { | ||||
|             FortePaymentStatus::Succeeded => Self::Charged, | ||||
|             FortePaymentStatus::Complete | FortePaymentStatus::Settled => Self::Charged, | ||||
|             FortePaymentStatus::Failed => Self::Failure, | ||||
|             FortePaymentStatus::Processing => Self::Authorizing, | ||||
|             FortePaymentStatus::Ready => Self::Pending, | ||||
|             FortePaymentStatus::Authorized => Self::Authorized, | ||||
|             FortePaymentStatus::Voided => Self::Voided, | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| #[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq)] | ||||
| impl ForeignFrom<(ForteResponseCode, ForteAction)> for enums::AttemptStatus { | ||||
|     fn foreign_from((response_code, action): (ForteResponseCode, ForteAction)) -> Self { | ||||
|         match response_code { | ||||
|             ForteResponseCode::A01 => match action { | ||||
|                 ForteAction::Authorize => Self::Authorized, | ||||
|                 ForteAction::Sale => Self::Pending, | ||||
|             }, | ||||
|             ForteResponseCode::A05 | ForteResponseCode::A06 => Self::Authorizing, | ||||
|             _ => Self::Failure, | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| #[derive(Debug, Deserialize)] | ||||
| pub struct CardResponse { | ||||
|     pub name_on_card: Secret<String>, | ||||
|     pub last_4_account_number: String, | ||||
|     pub masked_account_number: String, | ||||
|     pub card_type: String, | ||||
| } | ||||
|  | ||||
| #[derive(Debug, Deserialize)] | ||||
| pub enum ForteResponseCode { | ||||
|     A01, | ||||
|     A05, | ||||
|     A06, | ||||
|     U13, | ||||
|     U14, | ||||
|     U18, | ||||
|     U20, | ||||
| } | ||||
|  | ||||
| impl From<ForteResponseCode> for enums::AttemptStatus { | ||||
|     fn from(item: ForteResponseCode) -> Self { | ||||
|         match item { | ||||
|             ForteResponseCode::A01 | ForteResponseCode::A05 | ForteResponseCode::A06 => { | ||||
|                 Self::Pending | ||||
|             } | ||||
|             _ => Self::Failure, | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| #[derive(Debug, Deserialize)] | ||||
| pub struct ResponseStatus { | ||||
|     pub environment: String, | ||||
|     pub response_type: String, | ||||
|     pub response_code: ForteResponseCode, | ||||
|     pub response_desc: String, | ||||
|     pub authorization_code: String, | ||||
|     pub avs_result: Option<String>, | ||||
|     pub cvv_result: Option<String>, | ||||
| } | ||||
|  | ||||
| #[derive(Debug, Serialize, Deserialize)] | ||||
| #[serde(rename_all = "lowercase")] | ||||
| pub enum ForteAction { | ||||
|     Sale, | ||||
|     Authorize, | ||||
| } | ||||
|  | ||||
| #[derive(Debug, Deserialize)] | ||||
| pub struct FortePaymentsResponse { | ||||
|     status: FortePaymentStatus, | ||||
|     id: String, | ||||
|     pub transaction_id: String, | ||||
|     pub location_id: String, | ||||
|     pub action: ForteAction, | ||||
|     pub authorization_amount: Option<f64>, | ||||
|     pub authorization_code: String, | ||||
|     pub entered_by: String, | ||||
|     pub billing_address: Option<BillingAddress>, | ||||
|     pub card: Option<CardResponse>, | ||||
|     pub response: ResponseStatus, | ||||
| } | ||||
|  | ||||
| #[derive(Debug, Serialize, Deserialize)] | ||||
| pub struct ForteMeta { | ||||
|     pub auth_id: String, | ||||
| } | ||||
|  | ||||
| impl<F, T> | ||||
| @ -96,13 +248,197 @@ impl<F, T> | ||||
|     fn try_from( | ||||
|         item: types::ResponseRouterData<F, FortePaymentsResponse, T, types::PaymentsResponseData>, | ||||
|     ) -> Result<Self, Self::Error> { | ||||
|         let response_code = item.response.response.response_code; | ||||
|         let action = item.response.action; | ||||
|         let transaction_id = &item.response.transaction_id; | ||||
|         Ok(Self { | ||||
|             status: enums::AttemptStatus::foreign_from((response_code, action)), | ||||
|             response: Ok(types::PaymentsResponseData::TransactionResponse { | ||||
|                 resource_id: types::ResponseId::ConnectorTransactionId(transaction_id.to_string()), | ||||
|                 redirection_data: None, | ||||
|                 mandate_reference: None, | ||||
|                 connector_metadata: Some(serde_json::json!(ForteMeta { | ||||
|                     auth_id: item.response.authorization_code, | ||||
|                 })), | ||||
|                 network_txn_id: None, | ||||
|             }), | ||||
|             ..item.data | ||||
|         }) | ||||
|     } | ||||
| } | ||||
|  | ||||
| //PsyncResponse | ||||
|  | ||||
| #[derive(Debug, Deserialize)] | ||||
| pub struct FortePaymentsSyncResponse { | ||||
|     pub transaction_id: String, | ||||
|     pub location_id: String, | ||||
|     pub status: FortePaymentStatus, | ||||
|     pub action: ForteAction, | ||||
|     pub authorization_amount: Option<f64>, | ||||
|     pub authorization_code: String, | ||||
|     pub entered_by: String, | ||||
|     pub billing_address: Option<BillingAddress>, | ||||
|     pub card: Option<CardResponse>, | ||||
|     pub response: ResponseStatus, | ||||
| } | ||||
|  | ||||
| impl<F, T> | ||||
|     TryFrom<types::ResponseRouterData<F, FortePaymentsSyncResponse, T, types::PaymentsResponseData>> | ||||
|     for types::RouterData<F, T, types::PaymentsResponseData> | ||||
| { | ||||
|     type Error = error_stack::Report<errors::ConnectorError>; | ||||
|     fn try_from( | ||||
|         item: types::ResponseRouterData< | ||||
|             F, | ||||
|             FortePaymentsSyncResponse, | ||||
|             T, | ||||
|             types::PaymentsResponseData, | ||||
|         >, | ||||
|     ) -> Result<Self, Self::Error> { | ||||
|         let transaction_id = &item.response.transaction_id; | ||||
|         Ok(Self { | ||||
|             status: enums::AttemptStatus::from(item.response.status), | ||||
|             response: Ok(types::PaymentsResponseData::TransactionResponse { | ||||
|                 resource_id: types::ResponseId::ConnectorTransactionId(item.response.id), | ||||
|                 resource_id: types::ResponseId::ConnectorTransactionId(transaction_id.to_string()), | ||||
|                 redirection_data: None, | ||||
|                 mandate_reference: None, | ||||
|                 connector_metadata: None, | ||||
|                 connector_metadata: Some(serde_json::json!(ForteMeta { | ||||
|                     auth_id: item.response.authorization_code, | ||||
|                 })), | ||||
|                 network_txn_id: None, | ||||
|             }), | ||||
|             ..item.data | ||||
|         }) | ||||
|     } | ||||
| } | ||||
|  | ||||
| // Capture | ||||
|  | ||||
| #[derive(Debug, Serialize)] | ||||
| pub struct ForteCaptureRequest { | ||||
|     action: String, | ||||
|     transaction_id: String, | ||||
|     authorization_code: String, | ||||
| } | ||||
|  | ||||
| impl TryFrom<&types::PaymentsCaptureRouterData> for ForteCaptureRequest { | ||||
|     type Error = error_stack::Report<errors::ConnectorError>; | ||||
|     fn try_from(item: &types::PaymentsCaptureRouterData) -> Result<Self, Self::Error> { | ||||
|         let trn_id = item.request.connector_transaction_id.clone(); | ||||
|         let connector_auth_id: ForteMeta = | ||||
|             utils::to_connector_meta(item.request.connector_meta.clone())?; | ||||
|         let auth_code = connector_auth_id.auth_id; | ||||
|         Ok(Self { | ||||
|             action: "capture".to_string(), | ||||
|             transaction_id: trn_id, | ||||
|             authorization_code: auth_code, | ||||
|         }) | ||||
|     } | ||||
| } | ||||
|  | ||||
| #[derive(Debug, Deserialize)] | ||||
| pub struct CaptureResponseStatus { | ||||
|     pub environment: String, | ||||
|     pub response_type: String, | ||||
|     pub response_code: ForteResponseCode, | ||||
|     pub response_desc: String, | ||||
|     pub authorization_code: String, | ||||
| } | ||||
| // Capture Response | ||||
| #[derive(Debug, Deserialize)] | ||||
| pub struct ForteCaptureResponse { | ||||
|     pub transaction_id: String, | ||||
|     pub original_transaction_id: String, | ||||
|     pub entered_by: String, | ||||
|     pub authorization_code: String, | ||||
|     pub response: CaptureResponseStatus, | ||||
| } | ||||
|  | ||||
| impl TryFrom<types::PaymentsCaptureResponseRouterData<ForteCaptureResponse>> | ||||
|     for types::PaymentsCaptureRouterData | ||||
| { | ||||
|     type Error = error_stack::Report<errors::ConnectorError>; | ||||
|     fn try_from( | ||||
|         item: types::PaymentsCaptureResponseRouterData<ForteCaptureResponse>, | ||||
|     ) -> Result<Self, Self::Error> { | ||||
|         Ok(Self { | ||||
|             status: enums::AttemptStatus::from(item.response.response.response_code), | ||||
|             response: Ok(types::PaymentsResponseData::TransactionResponse { | ||||
|                 resource_id: types::ResponseId::ConnectorTransactionId( | ||||
|                     item.response.transaction_id, | ||||
|                 ), | ||||
|                 redirection_data: None, | ||||
|                 mandate_reference: None, | ||||
|                 connector_metadata: Some(serde_json::json!(ForteMeta { | ||||
|                     auth_id: item.response.authorization_code, | ||||
|                 })), | ||||
|                 network_txn_id: None, | ||||
|             }), | ||||
|             amount_captured: None, | ||||
|             ..item.data | ||||
|         }) | ||||
|     } | ||||
| } | ||||
|  | ||||
| //Cancel | ||||
|  | ||||
| #[derive(Debug, Serialize)] | ||||
| pub struct ForteCancelRequest { | ||||
|     action: String, | ||||
|     authorization_code: String, | ||||
| } | ||||
|  | ||||
| impl TryFrom<&types::PaymentsCancelRouterData> for ForteCancelRequest { | ||||
|     type Error = error_stack::Report<errors::ConnectorError>; | ||||
|     fn try_from(item: &types::PaymentsCancelRouterData) -> Result<Self, Self::Error> { | ||||
|         let action = "void".to_string(); | ||||
|         let connector_auth_id: ForteMeta = | ||||
|             utils::to_connector_meta(item.request.connector_meta.clone())?; | ||||
|         let authorization_code = connector_auth_id.auth_id; | ||||
|         Ok(Self { | ||||
|             action, | ||||
|             authorization_code, | ||||
|         }) | ||||
|     } | ||||
| } | ||||
|  | ||||
| #[derive(Debug, Deserialize)] | ||||
| pub struct CancelResponseStatus { | ||||
|     pub response_type: String, | ||||
|     pub response_code: ForteResponseCode, | ||||
|     pub response_desc: String, | ||||
|     pub authorization_code: String, | ||||
| } | ||||
|  | ||||
| #[derive(Debug, Deserialize)] | ||||
| pub struct ForteCancelResponse { | ||||
|     pub transaction_id: String, | ||||
|     pub location_id: String, | ||||
|     pub action: String, | ||||
|     pub authorization_code: String, | ||||
|     pub entered_by: String, | ||||
|     pub response: CancelResponseStatus, | ||||
| } | ||||
|  | ||||
| impl<F, T> | ||||
|     TryFrom<types::ResponseRouterData<F, ForteCancelResponse, T, types::PaymentsResponseData>> | ||||
|     for types::RouterData<F, T, types::PaymentsResponseData> | ||||
| { | ||||
|     type Error = error_stack::Report<errors::ConnectorError>; | ||||
|     fn try_from( | ||||
|         item: types::ResponseRouterData<F, ForteCancelResponse, T, types::PaymentsResponseData>, | ||||
|     ) -> Result<Self, Self::Error> { | ||||
|         let transaction_id = &item.response.transaction_id; | ||||
|         Ok(Self { | ||||
|             status: enums::AttemptStatus::from(item.response.response.response_code), | ||||
|             response: Ok(types::PaymentsResponseData::TransactionResponse { | ||||
|                 resource_id: types::ResponseId::ConnectorTransactionId(transaction_id.to_string()), | ||||
|                 redirection_data: None, | ||||
|                 mandate_reference: None, | ||||
|                 connector_metadata: Some(serde_json::json!(ForteMeta { | ||||
|                     auth_id: item.response.authorization_code, | ||||
|                 })), | ||||
|                 network_txn_id: None, | ||||
|             }), | ||||
|             ..item.data | ||||
| @ -111,46 +447,68 @@ impl<F, T> | ||||
| } | ||||
|  | ||||
| // REFUND : | ||||
| // Type definition for RefundRequest | ||||
| #[derive(Default, Debug, Serialize)] | ||||
| pub struct ForteRefundRequest { | ||||
|     pub amount: i64, | ||||
|     action: String, | ||||
|     authorization_amount: f64, | ||||
|     original_transaction_id: String, | ||||
|     authorization_code: String, | ||||
| } | ||||
|  | ||||
| impl<F> TryFrom<&types::RefundsRouterData<F>> for ForteRefundRequest { | ||||
|     type Error = error_stack::Report<errors::ConnectorError>; | ||||
|     fn try_from(item: &types::RefundsRouterData<F>) -> Result<Self, Self::Error> { | ||||
|         let trn_id = item.request.connector_transaction_id.clone(); | ||||
|         let connector_auth_id: ForteMeta = | ||||
|             utils::to_connector_meta(item.request.connector_metadata.clone())?; | ||||
|         let auth_code = connector_auth_id.auth_id; | ||||
|         let authorization_amount = | ||||
|             utils::to_currency_base_unit_asf64(item.request.amount, item.request.currency)?; | ||||
|         Ok(Self { | ||||
|             amount: item.request.amount, | ||||
|             action: "reverse".to_string(), | ||||
|             authorization_amount, | ||||
|             original_transaction_id: trn_id, | ||||
|             authorization_code: auth_code, | ||||
|         }) | ||||
|     } | ||||
| } | ||||
|  | ||||
| // Type definition for Refund Response | ||||
|  | ||||
| #[allow(dead_code)] | ||||
| #[derive(Debug, Serialize, Default, Deserialize, Clone)] | ||||
| #[derive(Debug, Deserialize)] | ||||
| #[serde(rename_all = "lowercase")] | ||||
| pub enum RefundStatus { | ||||
|     Succeeded, | ||||
|     Complete, | ||||
|     Ready, | ||||
|     Failed, | ||||
|     #[default] | ||||
|     Processing, | ||||
| } | ||||
|  | ||||
| impl From<RefundStatus> for enums::RefundStatus { | ||||
|     fn from(item: RefundStatus) -> Self { | ||||
|         match item { | ||||
|             RefundStatus::Succeeded => Self::Success, | ||||
|             RefundStatus::Complete => Self::Success, | ||||
|             RefundStatus::Ready => Self::Pending, | ||||
|             RefundStatus::Failed => Self::Failure, | ||||
|             RefundStatus::Processing => Self::Pending, | ||||
|         } | ||||
|     } | ||||
| } | ||||
| impl From<ForteResponseCode> for enums::RefundStatus { | ||||
|     fn from(item: ForteResponseCode) -> Self { | ||||
|         match item { | ||||
|             ForteResponseCode::A01 | ForteResponseCode::A05 | ForteResponseCode::A06 => { | ||||
|                 Self::Pending | ||||
|             } | ||||
|             _ => Self::Failure, | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| #[derive(Default, Debug, Clone, Serialize, Deserialize)] | ||||
| #[derive(Debug, Deserialize)] | ||||
| pub struct RefundResponse { | ||||
|     id: String, | ||||
|     status: RefundStatus, | ||||
|     pub transaction_id: String, | ||||
|     pub original_transaction_id: String, | ||||
|     pub action: String, | ||||
|     pub authorization_amount: Option<f64>, | ||||
|     pub authorization_code: String, | ||||
|     pub response: ResponseStatus, | ||||
| } | ||||
|  | ||||
| impl TryFrom<types::RefundsResponseRouterData<api::Execute, RefundResponse>> | ||||
| @ -162,24 +520,30 @@ impl TryFrom<types::RefundsResponseRouterData<api::Execute, RefundResponse>> | ||||
|     ) -> Result<Self, Self::Error> { | ||||
|         Ok(Self { | ||||
|             response: Ok(types::RefundsResponseData { | ||||
|                 connector_refund_id: item.response.id.to_string(), | ||||
|                 refund_status: enums::RefundStatus::from(item.response.status), | ||||
|                 connector_refund_id: item.response.transaction_id, | ||||
|                 refund_status: enums::RefundStatus::from(item.response.response.response_code), | ||||
|             }), | ||||
|             ..item.data | ||||
|         }) | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl TryFrom<types::RefundsResponseRouterData<api::RSync, RefundResponse>> | ||||
| #[derive(Debug, Deserialize)] | ||||
| pub struct RefundSyncResponse { | ||||
|     status: RefundStatus, | ||||
|     transaction_id: String, | ||||
| } | ||||
|  | ||||
| impl TryFrom<types::RefundsResponseRouterData<api::RSync, RefundSyncResponse>> | ||||
|     for types::RefundsRouterData<api::RSync> | ||||
| { | ||||
|     type Error = error_stack::Report<errors::ConnectorError>; | ||||
|     fn try_from( | ||||
|         item: types::RefundsResponseRouterData<api::RSync, RefundResponse>, | ||||
|         item: types::RefundsResponseRouterData<api::RSync, RefundSyncResponse>, | ||||
|     ) -> Result<Self, Self::Error> { | ||||
|         Ok(Self { | ||||
|             response: Ok(types::RefundsResponseData { | ||||
|                 connector_refund_id: item.response.id.to_string(), | ||||
|                 connector_refund_id: item.response.transaction_id, | ||||
|                 refund_status: enums::RefundStatus::from(item.response.status), | ||||
|             }), | ||||
|             ..item.data | ||||
| @ -187,10 +551,15 @@ impl TryFrom<types::RefundsResponseRouterData<api::RSync, RefundResponse>> | ||||
|     } | ||||
| } | ||||
|  | ||||
| #[derive(Default, Debug, Serialize, Deserialize, PartialEq)] | ||||
| pub struct ForteErrorResponse { | ||||
|     pub status_code: u16, | ||||
|     pub code: String, | ||||
|     pub message: String, | ||||
|     pub reason: Option<String>, | ||||
| #[derive(Debug, Deserialize)] | ||||
| pub struct ErrorResponseStatus { | ||||
|     pub environment: String, | ||||
|     pub response_type: Option<String>, | ||||
|     pub response_code: Option<String>, | ||||
|     pub response_desc: String, | ||||
| } | ||||
|  | ||||
| #[derive(Debug, Deserialize)] | ||||
| pub struct ForteErrorResponse { | ||||
|     pub response: ErrorResponseStatus, | ||||
| } | ||||
|  | ||||
| @ -261,7 +261,7 @@ impl | ||||
|                     token | ||||
|                 )), | ||||
|                 _ => Err(error_stack::report!(errors::ConnectorError::NotSupported { | ||||
|                     payment_method: payment_method_type.to_string(), | ||||
|                     message: payment_method_type.to_string(), | ||||
|                     connector: "klarna", | ||||
|                     payment_experience: payment_experience.to_string() | ||||
|                 })), | ||||
|  | ||||
| @ -194,14 +194,21 @@ pub struct MultisafepayPaymentsRequest { | ||||
|     pub var3: Option<String>, | ||||
| } | ||||
|  | ||||
| impl From<utils::CardIssuer> for Gateway { | ||||
|     fn from(issuer: utils::CardIssuer) -> Self { | ||||
| impl TryFrom<utils::CardIssuer> for Gateway { | ||||
|     type Error = error_stack::Report<errors::ConnectorError>; | ||||
|     fn try_from(issuer: utils::CardIssuer) -> Result<Self, Self::Error> { | ||||
|         match issuer { | ||||
|             utils::CardIssuer::AmericanExpress => Self::Amex, | ||||
|             utils::CardIssuer::Master => Self::MasterCard, | ||||
|             utils::CardIssuer::Maestro => Self::Maestro, | ||||
|             utils::CardIssuer::Visa => Self::Visa, | ||||
|             utils::CardIssuer::Discover => Self::Discover, | ||||
|             utils::CardIssuer::AmericanExpress => Ok(Self::Amex), | ||||
|             utils::CardIssuer::Master => Ok(Self::MasterCard), | ||||
|             utils::CardIssuer::Maestro => Ok(Self::Maestro), | ||||
|             utils::CardIssuer::Discover => Ok(Self::Discover), | ||||
|             utils::CardIssuer::Visa => Ok(Self::Visa), | ||||
|             _ => Err(errors::ConnectorError::NotSupported { | ||||
|                 message: issuer.to_string(), | ||||
|                 connector: "Multisafe pay", | ||||
|                 payment_experience: api::enums::PaymentExperience::RedirectToUrl.to_string(), | ||||
|             } | ||||
|             .into()), | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @ -216,7 +223,7 @@ impl TryFrom<&types::PaymentsAuthorizeRouterData> for MultisafepayPaymentsReques | ||||
|         }; | ||||
|  | ||||
|         let gateway = match item.request.payment_method_data { | ||||
|             api::PaymentMethodData::Card(ref ccard) => Gateway::from(ccard.get_card_issuer()?), | ||||
|             api::PaymentMethodData::Card(ref ccard) => Gateway::try_from(ccard.get_card_issuer()?)?, | ||||
|             api::PaymentMethodData::PayLater( | ||||
|                 api_models::payments::PayLaterData::KlarnaRedirect { | ||||
|                     billing_email: _, | ||||
|  | ||||
| @ -539,7 +539,7 @@ impl<F> | ||||
|                 ) | ||||
|             } | ||||
|             _ => Err(errors::ConnectorError::NotSupported { | ||||
|                 payment_method: "Bank Redirect".to_string(), | ||||
|                 message: "Bank Redirect".to_string(), | ||||
|                 connector: "Nuvei", | ||||
|                 payment_experience: "Redirection".to_string(), | ||||
|             })?, | ||||
| @ -583,7 +583,7 @@ impl<F> | ||||
|                     item, | ||||
|                 )), | ||||
|                 _ => Err(errors::ConnectorError::NotSupported { | ||||
|                     payment_method: "Wallet".to_string(), | ||||
|                     message: "Wallet".to_string(), | ||||
|                     connector: "Nuvei", | ||||
|                     payment_experience: "RedirectToUrl".to_string(), | ||||
|                 } | ||||
| @ -611,7 +611,7 @@ impl<F> | ||||
|                     item, | ||||
|                 )), | ||||
|                 _ => Err(errors::ConnectorError::NotSupported { | ||||
|                     payment_method: "Bank Redirect".to_string(), | ||||
|                     message: "Bank Redirect".to_string(), | ||||
|                     connector: "Nuvei", | ||||
|                     payment_experience: "RedirectToUrl".to_string(), | ||||
|                 } | ||||
|  | ||||
| @ -38,7 +38,7 @@ impl TryFrom<utils::CardIssuer> for PayeezyCardType { | ||||
|             utils::CardIssuer::Discover => Ok(Self::Discover), | ||||
|             utils::CardIssuer::Visa => Ok(Self::Visa), | ||||
|             _ => Err(errors::ConnectorError::NotSupported { | ||||
|                 payment_method: api::enums::PaymentMethod::Card.to_string(), | ||||
|                 message: issuer.to_string(), | ||||
|                 connector: "Payeezy", | ||||
|                 payment_experience: api::enums::PaymentExperience::RedirectToUrl.to_string(), | ||||
|             } | ||||
|  | ||||
| @ -454,7 +454,7 @@ impl TryFrom<&api_models::enums::BankNames> for StripeBankNames { | ||||
|             api_models::enums::BankNames::VolkskreditbankAg => Self::VolkskreditbankAg, | ||||
|             api_models::enums::BankNames::VrBankBraunau => Self::VrBankBraunau, | ||||
|             _ => Err(errors::ConnectorError::NotSupported { | ||||
|                 payment_method: api_enums::PaymentMethod::BankRedirect.to_string(), | ||||
|                 message: api_enums::PaymentMethod::BankRedirect.to_string(), | ||||
|                 connector: "Stripe", | ||||
|                 payment_experience: api_enums::PaymentExperience::RedirectToUrl.to_string(), | ||||
|             })?, | ||||
| @ -497,14 +497,14 @@ fn infer_stripe_pay_later_type( | ||||
|                 Ok(StripePaymentMethodType::AfterpayClearpay) | ||||
|             } | ||||
|             _ => Err(errors::ConnectorError::NotSupported { | ||||
|                 payment_method: pm_type.to_string(), | ||||
|                 message: pm_type.to_string(), | ||||
|                 connector: "stripe", | ||||
|                 payment_experience: experience.to_string(), | ||||
|             }), | ||||
|         } | ||||
|     } else { | ||||
|         Err(errors::ConnectorError::NotSupported { | ||||
|             payment_method: pm_type.to_string(), | ||||
|             message: pm_type.to_string(), | ||||
|             connector: "stripe", | ||||
|             payment_experience: experience.to_string(), | ||||
|         }) | ||||
| @ -1865,7 +1865,7 @@ impl | ||||
|                 })) | ||||
|             } | ||||
|             api::PaymentMethodData::Crypto(_) => Err(errors::ConnectorError::NotSupported { | ||||
|                 payment_method: format!("{pm_type:?}"), | ||||
|                 message: format!("{pm_type:?}"), | ||||
|                 connector: "Stripe", | ||||
|                 payment_experience: api_models::enums::PaymentExperience::RedirectToUrl.to_string(), | ||||
|             })?, | ||||
|  | ||||
| @ -325,6 +325,14 @@ static CARD_REGEX: Lazy<HashMap<CardIssuer, Result<Regex, regex::Error>>> = Lazy | ||||
|         CardIssuer::Maestro, | ||||
|         Regex::new(r"^(5018|5020|5038|5893|6304|6759|6761|6762|6763)[0-9]{8,15}$"), | ||||
|     ); | ||||
|     map.insert( | ||||
|         CardIssuer::DinersClub, | ||||
|         Regex::new(r"^3(?:0[0-5]|[68][0-9])[0-9]{11}$"), | ||||
|     ); | ||||
|     map.insert( | ||||
|         CardIssuer::JCB, | ||||
|         Regex::new(r"^(3(?:088|096|112|158|337|5(?:2[89]|[3-8][0-9]))\d{12})$"), | ||||
|     ); | ||||
|     map | ||||
| }); | ||||
|  | ||||
| @ -335,6 +343,8 @@ pub enum CardIssuer { | ||||
|     Maestro, | ||||
|     Visa, | ||||
|     Discover, | ||||
|     DinersClub, | ||||
|     JCB, | ||||
| } | ||||
|  | ||||
| pub trait CardData { | ||||
| @ -630,6 +640,14 @@ pub fn to_currency_base_unit( | ||||
|     amount: i64, | ||||
|     currency: storage_models::enums::Currency, | ||||
| ) -> Result<String, error_stack::Report<errors::ConnectorError>> { | ||||
|     let amount_f64 = to_currency_base_unit_asf64(amount, currency)?; | ||||
|     Ok(format!("{amount_f64:.2}")) | ||||
| } | ||||
|  | ||||
| pub fn to_currency_base_unit_asf64( | ||||
|     amount: i64, | ||||
|     currency: storage_models::enums::Currency, | ||||
| ) -> Result<f64, error_stack::Report<errors::ConnectorError>> { | ||||
|     let amount_u32 = u32::try_from(amount) | ||||
|         .into_report() | ||||
|         .change_context(errors::ConnectorError::RequestEncodingFailed)?; | ||||
| @ -642,7 +660,7 @@ pub fn to_currency_base_unit( | ||||
|         | storage_models::enums::Currency::OMR => amount_f64 / 1000.00, | ||||
|         _ => amount_f64 / 100.00, | ||||
|     }; | ||||
|     Ok(format!("{amount:.2}")) | ||||
|     Ok(amount) | ||||
| } | ||||
|  | ||||
| pub fn str_to_f32<S>(value: &str, serializer: S) -> Result<S::Ok, S::Error> | ||||
|  | ||||
| @ -130,7 +130,7 @@ impl TryFrom<utils::CardIssuer> for Gateway { | ||||
|             utils::CardIssuer::Discover => Ok(Self::Discover), | ||||
|             utils::CardIssuer::Visa => Ok(Self::Visa), | ||||
|             _ => Err(errors::ConnectorError::NotSupported { | ||||
|                 payment_method: api_enums::PaymentMethod::Card.to_string(), | ||||
|                 message: issuer.to_string(), | ||||
|                 connector: "worldline", | ||||
|                 payment_experience: api_enums::PaymentExperience::RedirectToUrl.to_string(), | ||||
|             } | ||||
|  | ||||
| @ -253,9 +253,9 @@ pub enum ConnectorError { | ||||
|     FailedToObtainCertificateKey, | ||||
|     #[error("This step has not been implemented for: {0}")] | ||||
|     NotImplemented(String), | ||||
|     #[error("{payment_method} is not supported by {connector}")] | ||||
|     #[error("{message} is not supported by {connector}")] | ||||
|     NotSupported { | ||||
|         payment_method: String, | ||||
|         message: String, | ||||
|         connector: &'static str, | ||||
|         payment_experience: String, | ||||
|     }, | ||||
|  | ||||
| @ -107,8 +107,8 @@ impl ConnectorErrorExt for error_stack::Report<errors::ConnectorError> { | ||||
|                         "payment_method_data, payment_method_type and payment_experience does not match", | ||||
|                 } | ||||
|             }, | ||||
|             errors::ConnectorError::NotSupported { payment_method, connector, payment_experience } => { | ||||
|                 errors::ApiErrorResponse::NotSupported { message: format!("Payment method type {payment_method} is not supported by {connector} through payment experience {payment_experience}") } | ||||
|             errors::ConnectorError::NotSupported { message, connector, payment_experience } => { | ||||
|                 errors::ApiErrorResponse::NotSupported { message: format!("{message} is not supported by {connector} through payment experience {payment_experience}") } | ||||
|             }, | ||||
|             errors::ConnectorError::FlowNotSupported{ flow, connector } => { | ||||
|                 errors::ApiErrorResponse::FlowNotSupported { flow: flow.to_owned(), connector: connector.to_owned() } | ||||
|  | ||||
| @ -537,6 +537,12 @@ pub enum ConnectorAuthType { | ||||
|         key1: String, | ||||
|         api_secret: String, | ||||
|     }, | ||||
|     MultiAuthKey { | ||||
|         api_key: String, | ||||
|         key1: String, | ||||
|         api_secret: String, | ||||
|         key2: String, | ||||
|     }, | ||||
|     #[default] | ||||
|     NoKey, | ||||
| } | ||||
|  | ||||
| @ -204,7 +204,7 @@ impl ConnectorData { | ||||
|             "cybersource" => Ok(Box::new(&connector::Cybersource)), | ||||
|             "dlocal" => Ok(Box::new(&connector::Dlocal)), | ||||
|             "fiserv" => Ok(Box::new(&connector::Fiserv)), | ||||
|             // "forte" => Ok(Box::new(&connector::Forte)), | ||||
|             "forte" => Ok(Box::new(&connector::Forte)), | ||||
|             "globalpay" => Ok(Box::new(&connector::Globalpay)), | ||||
|             "klarna" => Ok(Box::new(&connector::Klarna)), | ||||
|             "mollie" => Ok(Box::new(&connector::Mollie)), | ||||
|  | ||||
		Reference in New Issue
	
	Block a user
	 Prasunna Soppa
					Prasunna Soppa