pub mod transformers; use error_stack::{report, ResultExt}; use masking::ExposeInterface; use common_utils::{ types::{AmountConvertor, StringMinorUnit, StringMinorUnitForConnector} }; use super::utils::{self as connector_utils}; use crate::{ events::connector_api_logs::ConnectorEvent, configs::settings, utils::BytesExt, core::{ errors::{self, CustomResult}, }, headers, services::{self, ConnectorIntegration, ConnectorValidation, request::{self, Mask}}, types::{ self, api::{self, ConnectorCommon, ConnectorCommonExt}, ErrorResponse, Response, RequestContent } }; use transformers as {{project-name | downcase}}; #[derive(Clone)] pub struct {{project-name | downcase | pascal_case}} { amount_converter: &'static (dyn AmountConvertor + Sync) } impl {{project-name | downcase | pascal_case}} { pub fn new() -> &'static Self { &Self { amount_converter: &StringMinorUnitForConnector } } } impl api::Payment for {{project-name | downcase | pascal_case}} {} impl api::PaymentSession for {{project-name | downcase | pascal_case}} {} impl api::ConnectorAccessToken for {{project-name | downcase | pascal_case}} {} impl api::MandateSetup for {{project-name | downcase | pascal_case}} {} impl api::PaymentAuthorize for {{project-name | downcase | pascal_case}} {} impl api::PaymentSync for {{project-name | downcase | pascal_case}} {} impl api::PaymentCapture for {{project-name | downcase | pascal_case}} {} impl api::PaymentVoid for {{project-name | downcase | pascal_case}} {} impl api::Refund for {{project-name | downcase | pascal_case}} {} impl api::RefundExecute for {{project-name | downcase | pascal_case}} {} impl api::RefundSync for {{project-name | downcase | pascal_case}} {} impl api::PaymentToken for {{project-name | downcase | pascal_case}} {} impl ConnectorIntegration< api::PaymentMethodToken, types::PaymentMethodTokenizationData, types::PaymentsResponseData, > for {{project-name | downcase | pascal_case}} { // Not Implemented (R) } impl ConnectorCommonExt for {{project-name | downcase | pascal_case}} where Self: ConnectorIntegration,{ fn build_headers( &self, req: &types::RouterData, _connectors: &settings::Connectors, ) -> CustomResult)>, errors::ConnectorError> { let mut header = vec![( headers::CONTENT_TYPE.to_string(), self.get_content_type().to_string().into(), )]; let mut api_key = self.get_auth_header(&req.connector_auth_type)?; header.append(&mut api_key); Ok(header) } } impl ConnectorCommon for {{project-name | downcase | pascal_case}} { fn id(&self) -> &'static str { "{{project-name | downcase}}" } fn get_currency_unit(&self) -> api::CurrencyUnit { todo!() // TODO! Check connector documentation, on which unit they are processing the currency. // If the connector accepts amount in lower unit ( i.e cents for USD) then return api::CurrencyUnit::Minor, // if connector accepts amount in base unit (i.e dollars for USD) then return api::CurrencyUnit::Base } fn common_get_content_type(&self) -> &'static str { "application/json" } fn base_url<'a>(&self, connectors: &'a settings::Connectors) -> &'a str { connectors.{{project-name}}.base_url.as_ref() } fn get_auth_header(&self, auth_type:&types::ConnectorAuthType)-> CustomResult)>,errors::ConnectorError> { let auth = {{project-name | downcase}}::{{project-name | downcase | pascal_case}}AuthType::try_from(auth_type) .change_context(errors::ConnectorError::FailedToObtainAuthType)?; Ok(vec![(headers::AUTHORIZATION.to_string(), auth.api_key.expose().into_masked())]) } fn build_error_response( &self, res: Response, event_builder: Option<&mut ConnectorEvent>, ) -> CustomResult { let response: {{project-name | downcase}}::{{project-name | downcase | pascal_case}}ErrorResponse = res .response .parse_struct("{{project-name | downcase | pascal_case}}ErrorResponse") .change_context(errors::ConnectorError::ResponseDeserializationFailed)?; event_builder.map(|i| i.set_response_body(&response)); router_env::logger::info!(connector_response=?response); Ok(ErrorResponse { status_code: res.status_code, code: response.code, message: response.message, reason: response.reason, attempt_status: None, connector_transaction_id: None, }) } } impl ConnectorValidation for {{project-name | downcase | pascal_case}} { //TODO: implement functions when support enabled } impl ConnectorIntegration< api::Session, types::PaymentsSessionData, types::PaymentsResponseData, > for {{project-name | downcase | pascal_case}} { //TODO: implement sessions flow } impl ConnectorIntegration for {{project-name | downcase | pascal_case}} { } impl ConnectorIntegration< api::SetupMandate, types::SetupMandateRequestData, types::PaymentsResponseData, > for {{project-name | downcase | pascal_case}} { } impl ConnectorIntegration< api::Authorize, types::PaymentsAuthorizeData, types::PaymentsResponseData, > for {{project-name | downcase | pascal_case}} { fn get_headers(&self, req: &types::PaymentsAuthorizeRouterData, connectors: &settings::Connectors,) -> CustomResult)>,errors::ConnectorError> { self.build_headers(req, connectors) } fn get_content_type(&self) -> &'static str { self.common_get_content_type() } fn get_url(&self, _req: &types::PaymentsAuthorizeRouterData, _connectors: &settings::Connectors,) -> CustomResult { Err(errors::ConnectorError::NotImplemented("get_url method".to_string()).into()) } fn get_request_body(&self, req: &types::PaymentsAuthorizeRouterData, _connectors: &settings::Connectors,) -> CustomResult { let amount = connector_utils::convert_amount( self.amount_converter, req.request.minor_amount, req.request.currency, )?; let connector_router_data = {{project-name | downcase}}::{{project-name | downcase | pascal_case}}RouterData::from(( amount, req, )); let connector_req = {{project-name | downcase}}::{{project-name | downcase | pascal_case}}PaymentsRequest::try_from(&connector_router_data)?; Ok(RequestContent::Json(Box::new(connector_req))) } fn build_request( &self, req: &types::PaymentsAuthorizeRouterData, connectors: &settings::Connectors, ) -> CustomResult, errors::ConnectorError> { Ok(Some( services::RequestBuilder::new() .method(services::Method::Post) .url(&types::PaymentsAuthorizeType::get_url( self, req, connectors, )?) .attach_default_headers() .headers(types::PaymentsAuthorizeType::get_headers( self, req, connectors, )?) .set_body(types::PaymentsAuthorizeType::get_request_body(self, req, connectors)?) .build(), )) } fn handle_response( &self, data: &types::PaymentsAuthorizeRouterData, event_builder: Option<&mut ConnectorEvent>, res: Response, ) -> CustomResult { let response: {{project-name | downcase}}::{{project-name | downcase | pascal_case}}PaymentsResponse = res.response.parse_struct("{{project-name | downcase | pascal_case}} PaymentsAuthorizeResponse").change_context(errors::ConnectorError::ResponseDeserializationFailed)?; event_builder.map(|i| i.set_response_body(&response)); router_env::logger::info!(connector_response=?response); types::RouterData::try_from(types::ResponseRouterData { response, data: data.clone(), http_code: res.status_code, }) } fn get_error_response(&self, res: Response, event_builder: Option<&mut ConnectorEvent>) -> CustomResult { self.build_error_response(res, event_builder) } } impl ConnectorIntegration for {{project-name | downcase | pascal_case}} { fn get_headers( &self, req: &types::PaymentsSyncRouterData, connectors: &settings::Connectors, ) -> CustomResult)>, errors::ConnectorError> { self.build_headers(req, connectors) } fn get_content_type(&self) -> &'static str { self.common_get_content_type() } fn get_url( &self, _req: &types::PaymentsSyncRouterData, _connectors: &settings::Connectors, ) -> CustomResult { Err(errors::ConnectorError::NotImplemented("get_url method".to_string()).into()) } fn build_request( &self, req: &types::PaymentsSyncRouterData, connectors: &settings::Connectors, ) -> CustomResult, errors::ConnectorError> { Ok(Some( services::RequestBuilder::new() .method(services::Method::Get) .url(&types::PaymentsSyncType::get_url(self, req, connectors)?) .attach_default_headers() .headers(types::PaymentsSyncType::get_headers(self, req, connectors)?) .build(), )) } fn handle_response( &self, data: &types::PaymentsSyncRouterData, event_builder: Option<&mut ConnectorEvent>, res: Response, ) -> CustomResult { let response: {{project-name | downcase}}:: {{project-name | downcase | pascal_case}}PaymentsResponse = res .response .parse_struct("{{project-name | downcase}} PaymentsSyncResponse") .change_context(errors::ConnectorError::ResponseDeserializationFailed)?; event_builder.map(|i| i.set_response_body(&response)); router_env::logger::info!(connector_response=?response); types::RouterData::try_from(types::ResponseRouterData { response, data: data.clone(), http_code: res.status_code, }) } fn get_error_response( &self, res: Response, event_builder: Option<&mut ConnectorEvent> ) -> CustomResult { self.build_error_response(res, event_builder) } } impl ConnectorIntegration< api::Capture, types::PaymentsCaptureData, types::PaymentsResponseData, > for {{project-name | downcase | pascal_case}} { fn get_headers( &self, req: &types::PaymentsCaptureRouterData, connectors: &settings::Connectors, ) -> CustomResult)>, errors::ConnectorError> { self.build_headers(req, connectors) } fn get_content_type(&self) -> &'static str { self.common_get_content_type() } fn get_url( &self, _req: &types::PaymentsCaptureRouterData, _connectors: &settings::Connectors, ) -> CustomResult { Err(errors::ConnectorError::NotImplemented("get_url method".to_string()).into()) } fn get_request_body( &self, _req: &types::PaymentsCaptureRouterData, _connectors: &settings::Connectors, ) -> CustomResult { Err(errors::ConnectorError::NotImplemented("get_request_body method".to_string()).into()) } fn build_request( &self, req: &types::PaymentsCaptureRouterData, connectors: &settings::Connectors, ) -> CustomResult, errors::ConnectorError> { Ok(Some( services::RequestBuilder::new() .method(services::Method::Post) .url(&types::PaymentsCaptureType::get_url(self, req, connectors)?) .attach_default_headers() .headers(types::PaymentsCaptureType::get_headers( self, req, connectors, )?) .set_body(types::PaymentsCaptureType::get_request_body(self, req, connectors)?) .build(), )) } fn handle_response( &self, data: &types::PaymentsCaptureRouterData, event_builder: Option<&mut ConnectorEvent>, res: Response, ) -> CustomResult { let response: {{project-name | downcase }}::{{project-name | downcase | pascal_case}}PaymentsResponse = res .response .parse_struct("{{project-name | downcase | pascal_case}} PaymentsCaptureResponse") .change_context(errors::ConnectorError::ResponseDeserializationFailed)?; event_builder.map(|i| i.set_response_body(&response)); router_env::logger::info!(connector_response=?response); types::RouterData::try_from(types::ResponseRouterData { response, data: data.clone(), http_code: res.status_code, }) } fn get_error_response( &self, res: Response, event_builder: Option<&mut ConnectorEvent> ) -> CustomResult { self.build_error_response(res, event_builder) } } impl ConnectorIntegration< api::Void, types::PaymentsCancelData, types::PaymentsResponseData, > for {{project-name | downcase | pascal_case}} {} impl ConnectorIntegration< api::Execute, types::RefundsData, types::RefundsResponseData, > for {{project-name | downcase | pascal_case}} { fn get_headers(&self, req: &types::RefundsRouterData, connectors: &settings::Connectors,) -> CustomResult)>,errors::ConnectorError> { self.build_headers(req, connectors) } fn get_content_type(&self) -> &'static str { self.common_get_content_type() } fn get_url(&self, _req: &types::RefundsRouterData, _connectors: &settings::Connectors,) -> CustomResult { Err(errors::ConnectorError::NotImplemented("get_url method".to_string()).into()) } fn get_request_body(&self, req: &types::RefundsRouterData, _connectors: &settings::Connectors,) -> CustomResult { let refund_amount = connector_utils::convert_amount( self.amount_converter, req.request.minor_refund_amount, req.request.currency, )?; let connector_router_data = {{project-name | downcase}}::{{project-name | downcase | pascal_case}}RouterData::from(( refund_amount, req, )); let connector_req = {{project-name | downcase}}::{{project-name | downcase | pascal_case}}RefundRequest::try_from(&connector_router_data)?; Ok(RequestContent::Json(Box::new(connector_req))) } fn build_request(&self, req: &types::RefundsRouterData, connectors: &settings::Connectors,) -> CustomResult,errors::ConnectorError> { let request = services::RequestBuilder::new() .method(services::Method::Post) .url(&types::RefundExecuteType::get_url(self, req, connectors)?) .attach_default_headers() .headers(types::RefundExecuteType::get_headers(self, req, connectors)?) .set_body(types::RefundExecuteType::get_request_body(self, req, connectors)?) .build(); Ok(Some(request)) } fn handle_response( &self, data: &types::RefundsRouterData, event_builder: Option<&mut ConnectorEvent>, res: Response, ) -> CustomResult,errors::ConnectorError> { let response: {{project-name| downcase}}::RefundResponse = res.response.parse_struct("{{project-name | downcase}} RefundResponse").change_context(errors::ConnectorError::ResponseDeserializationFailed)?; event_builder.map(|i| i.set_response_body(&response)); router_env::logger::info!(connector_response=?response); types::RouterData::try_from(types::ResponseRouterData { response, data: data.clone(), http_code: res.status_code, }) } fn get_error_response(&self, res: Response, event_builder: Option<&mut ConnectorEvent>) -> CustomResult { self.build_error_response(res, event_builder) } } impl ConnectorIntegration for {{project-name | downcase | pascal_case}} { fn get_headers(&self, req: &types::RefundSyncRouterData,connectors: &settings::Connectors,) -> CustomResult)>,errors::ConnectorError> { self.build_headers(req, connectors) } fn get_content_type(&self) -> &'static str { self.common_get_content_type() } fn get_url(&self, _req: &types::RefundSyncRouterData,_connectors: &settings::Connectors,) -> CustomResult { Err(errors::ConnectorError::NotImplemented("get_url method".to_string()).into()) } fn build_request( &self, req: &types::RefundSyncRouterData, connectors: &settings::Connectors, ) -> CustomResult, errors::ConnectorError> { Ok(Some( services::RequestBuilder::new() .method(services::Method::Get) .url(&types::RefundSyncType::get_url(self, req, connectors)?) .attach_default_headers() .headers(types::RefundSyncType::get_headers(self, req, connectors)?) .set_body(types::RefundSyncType::get_request_body(self, req, connectors)?) .build(), )) } fn handle_response( &self, data: &types::RefundSyncRouterData, event_builder: Option<&mut ConnectorEvent>, res: Response, ) -> CustomResult { let response: {{project-name | downcase}}::RefundResponse = res.response.parse_struct("{{project-name | downcase}} RefundSyncResponse").change_context(errors::ConnectorError::ResponseDeserializationFailed)?; event_builder.map(|i| i.set_response_body(&response)); router_env::logger::info!(connector_response=?response); types::RouterData::try_from(types::ResponseRouterData { response, data: data.clone(), http_code: res.status_code, }) } fn get_error_response(&self, res: Response, event_builder: Option<&mut ConnectorEvent>) -> CustomResult { self.build_error_response(res, event_builder) } } #[async_trait::async_trait] impl api::IncomingWebhook for {{project-name | downcase | pascal_case}} { fn get_webhook_object_reference_id( &self, _request: &api::IncomingWebhookRequestDetails<'_>, ) -> CustomResult { Err(report!(errors::ConnectorError::WebhooksNotImplemented)) } fn get_webhook_event_type( &self, _request: &api::IncomingWebhookRequestDetails<'_>, ) -> CustomResult { Err(report!(errors::ConnectorError::WebhooksNotImplemented)) } fn get_webhook_resource_object( &self, _request: &api::IncomingWebhookRequestDetails<'_>, ) -> CustomResult, errors::ConnectorError> { Err(report!(errors::ConnectorError::WebhooksNotImplemented)) } }