diff --git a/crates/router/src/connector/braintree.rs b/crates/router/src/connector/braintree.rs index 24bf1ef15a..dc4eaccc31 100644 --- a/crates/router/src/connector/braintree.rs +++ b/crates/router/src/connector/braintree.rs @@ -246,7 +246,7 @@ impl res: Bytes, ) -> CustomResult { let response: braintree::ErrorResponse = res - .parse_struct("Error Response") + .parse_struct("Braintree Error Response") .change_context(errors::ConnectorError::ResponseDeserializationFailed)?; Ok(ErrorResponse { @@ -271,7 +271,7 @@ impl logger::debug!(payment_sync_response=?res); let response: braintree::BraintreePaymentsResponse = res .response - .parse_struct("braintree PaymentsResponse") + .parse_struct("Braintree PaymentsResponse") .change_context(errors::ConnectorError::ResponseDeserializationFailed)?; types::RouterData::try_from(types::ResponseRouterData { response, @@ -356,7 +356,7 @@ impl ) -> CustomResult { let response: braintree::BraintreePaymentsResponse = res .response - .parse_struct("Braintree Payments Response") + .parse_struct("Braintree PaymentsResponse") .change_context(errors::ConnectorError::ResponseDeserializationFailed)?; logger::debug!(braintreepayments_create_response=?response); types::ResponseRouterData { @@ -375,7 +375,7 @@ impl logger::debug!(braintreepayments_create_response=?res); let response: braintree::ErrorResponse = res - .parse_struct("ErrorResponse") + .parse_struct("Braintree ErrorResponse") .change_context(errors::ConnectorError::ResponseDeserializationFailed)?; Ok(ErrorResponse { code: consts::NO_ERROR_CODE.to_string(), @@ -394,50 +394,94 @@ impl { fn get_headers( &self, - _req: &types::PaymentsCancelRouterData, + req: &types::PaymentsCancelRouterData, ) -> CustomResult, errors::ConnectorError> { - Err(errors::ConnectorError::NotImplemented("braintree".to_string()).into()) + let mut headers = vec![ + ( + headers::CONTENT_TYPE.to_string(), + types::PaymentsAuthorizeType::get_content_type(self).to_string(), + ), + (headers::X_ROUTER.to_string(), "test".to_string()), + (headers::X_API_VERSION.to_string(), "6".to_string()), + (headers::ACCEPT.to_string(), "application/json".to_string()), + ]; + let mut api_key = self.get_auth_header(&req.connector_auth_type)?; + headers.append(&mut api_key); + Ok(headers) } fn get_content_type(&self) -> &'static str { - "" + "application/json" } fn get_url( &self, - _req: &types::PaymentsCancelRouterData, - _connectors: Connectors, + req: &types::PaymentsCancelRouterData, + connectors: Connectors, ) -> CustomResult { - Err(errors::ConnectorError::NotImplemented("braintree".to_string()).into()) + let auth_type = braintree::BraintreeAuthType::try_from(&req.connector_auth_type) + .change_context(errors::ConnectorError::FailedToObtainAuthType)?; + Ok(format!( + "{}merchants/{}/transactions/{}/void", + self.base_url(connectors), + auth_type.merchant_account, + req.request.connector_transaction_id + )) + } + + fn build_request( + &self, + req: &types::PaymentsCancelRouterData, + connectors: Connectors, + ) -> CustomResult, errors::ConnectorError> { + Ok(Some( + services::RequestBuilder::new() + .method(services::Method::Put) + .url(&types::PaymentsVoidType::get_url(self, req, connectors)?) + .headers(types::PaymentsVoidType::get_headers(self, req)?) + .body(types::PaymentsVoidType::get_request_body(self, req)?) + .build(), + )) + } + + fn get_error_response( + &self, + res: Bytes, + ) -> CustomResult { + let response: braintree::ErrorResponse = res + .parse_struct("Braintree ErrorResponse") + .change_context(errors::ConnectorError::ResponseDeserializationFailed)?; + + Ok(ErrorResponse { + code: consts::NO_ERROR_CODE.to_string(), + message: response.api_error_response.message, + reason: None, + }) } fn get_request_body( &self, _req: &types::PaymentsCancelRouterData, ) -> CustomResult, errors::ConnectorError> { - Err(errors::ConnectorError::NotImplemented("braintree".to_string()).into()) - } - fn build_request( - &self, - _req: &types::PaymentsCancelRouterData, - _connectors: Connectors, - ) -> CustomResult, errors::ConnectorError> { - Err(errors::ConnectorError::NotImplemented("braintree".to_string()).into()) + Ok(None) } fn handle_response( &self, - _data: &types::PaymentsCancelRouterData, - _res: Response, + data: &types::PaymentsCancelRouterData, + res: Response, ) -> CustomResult { - Err(errors::ConnectorError::NotImplemented("braintree".to_string()).into()) - } - - fn get_error_response( - &self, - _res: Bytes, - ) -> CustomResult { - Err(errors::ConnectorError::NotImplemented("braintree".to_string()).into()) + logger::debug!(payment_sync_response=?res); + let response: braintree::BraintreePaymentsResponse = res + .response + .parse_struct("Braintree PaymentsVoidResponse") + .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) } } @@ -467,23 +511,32 @@ impl services::ConnectorIntegration &'static str { - "" + "application/json" } fn get_url( &self, - _req: &types::RefundsRouterData, - _connectors: Connectors, + req: &types::RefundsRouterData, + connectors: Connectors, ) -> CustomResult { - Err(errors::ConnectorError::NotImplemented("braintree".to_string()).into()) + let auth_type = BraintreeAuthType::try_from(&req.connector_auth_type) + .change_context(errors::ConnectorError::FailedToObtainAuthType)?; + let connector_payment_id = req.request.connector_transaction_id.clone(); + Ok(format!( + "{}merchants/{}/transactions/{}", + self.base_url(connectors), + auth_type.merchant_account, + connector_payment_id + )) } fn get_request_body( &self, req: &types::RefundsRouterData, ) -> CustomResult, errors::ConnectorError> { - let braintree_req = utils::Encode::::convert_and_url_encode(req) - .change_context(errors::ConnectorError::RequestEncodingFailed)?; + let braintree_req = + utils::Encode::::convert_and_url_encode(req) + .change_context(errors::ConnectorError::RequestEncodingFailed)?; Ok(Some(braintree_req)) } @@ -509,7 +562,7 @@ impl services::ConnectorIntegration for BraintreePaymentsRequest { fn try_from(item: &types::PaymentsAuthorizeRouterData) -> Result { match item.request.payment_method_data { api::PaymentMethod::Card(ref ccard) => { + let submit_for_settlement = matches!( + item.request.capture_method, + Some(enums::CaptureMethod::Automatic) | None + ); let braintree_payment_request = TransactionBody { amount: item.request.amount.to_string(), device_data: DeviceData {}, options: PaymentOptions { - submit_for_settlement: true, + submit_for_settlement, }, credit_card: Card { number: ccard.card_number.peek().clone(), @@ -62,7 +66,9 @@ impl TryFrom<&types::PaymentsAuthorizeRouterData> for BraintreePaymentsRequest { transaction: braintree_payment_request, }) } - _ => Err(errors::ConnectorError::RequestEncodingFailed.into()), + _ => Err( + errors::ConnectorError::NotImplemented("Current Payment Method".to_string()).into(), + ), } } } @@ -140,6 +146,7 @@ impl >, ) -> Result { Ok(types::RouterData { + status: enums::AttemptStatus::from(item.response.transaction.status), response: Ok(types::PaymentsResponseData::TransactionResponse { resource_id: types::ResponseId::ConnectorTransactionId( item.response.transaction.id, @@ -223,12 +230,21 @@ pub struct ApiErrorResponse { } #[derive(Default, Debug, Clone, Serialize)] -pub struct RefundRequest {} +pub struct BraintreeRefundRequest { + transaction: Amount, +} -impl TryFrom<&types::RefundsRouterData> for RefundRequest { - type Error = error_stack::Report; +#[derive(Default, Debug, Serialize, Clone)] +pub struct Amount { + amount: Option, +} + +impl TryFrom<&types::RefundsRouterData> for BraintreeRefundRequest { + type Error = error_stack::Report; fn try_from(_item: &types::RefundsRouterData) -> Result { - Ok(RefundRequest {}) + Ok(BraintreeRefundRequest { + transaction: Amount { amount: None }, + }) } } diff --git a/crates/router/src/services/api.rs b/crates/router/src/services/api.rs index 1ef2da29c3..c1f632e404 100644 --- a/crates/router/src/services/api.rs +++ b/crates/router/src/services/api.rs @@ -229,6 +229,7 @@ async fn send_request( .await } + Method::Put => client.put(url).add_headers(headers).send().await, Method::Delete => client.delete(url).add_headers(headers).send().await, } .into_report() diff --git a/crates/router/src/services/api/request.rs b/crates/router/src/services/api/request.rs index 8f455a8736..04cb0413c4 100644 --- a/crates/router/src/services/api/request.rs +++ b/crates/router/src/services/api/request.rs @@ -20,6 +20,7 @@ pub(crate) type Headers = Vec<(String, String)>; pub enum Method { Get, Post, + Put, Delete, } diff --git a/scripts/create_connector_account.sh b/scripts/create_connector_account.sh index b3a2eebab9..4815197eba 100644 --- a/scripts/create_connector_account.sh +++ b/scripts/create_connector_account.sh @@ -39,6 +39,7 @@ case "$connector" in authorizedotnet) required_connector="authorizedotnet";; aci) required_connector="aci";; adyen) required_connector="adyen";; + braintree) required_connector="braintree";; *) echo "This connector is not supported" 1>&2;exit 1;; esac