mirror of
				https://github.com/juspay/hyperswitch.git
				synced 2025-11-01 02:57:02 +08:00 
			
		
		
		
	feat(connector): integrate payments void, sync and refund flows for Braintree (#73)
This commit is contained in:
		| @ -246,7 +246,7 @@ impl | ||||
|         res: Bytes, | ||||
|     ) -> CustomResult<ErrorResponse, errors::ConnectorError> { | ||||
|         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<types::PaymentsAuthorizeRouterData, errors::ConnectorError> { | ||||
|         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<Vec<(String, String)>, 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<String, errors::ConnectorError> { | ||||
|         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<Option<services::Request>, 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<ErrorResponse, errors::ConnectorError> { | ||||
|         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<Option<String>, errors::ConnectorError> { | ||||
|         Err(errors::ConnectorError::NotImplemented("braintree".to_string()).into()) | ||||
|     } | ||||
|     fn build_request( | ||||
|         &self, | ||||
|         _req: &types::PaymentsCancelRouterData, | ||||
|         _connectors: Connectors, | ||||
|     ) -> CustomResult<Option<services::Request>, 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<types::PaymentsCancelRouterData, errors::ConnectorError> { | ||||
|         Err(errors::ConnectorError::NotImplemented("braintree".to_string()).into()) | ||||
|     } | ||||
|  | ||||
|     fn get_error_response( | ||||
|         &self, | ||||
|         _res: Bytes, | ||||
|     ) -> CustomResult<ErrorResponse, errors::ConnectorError> { | ||||
|         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<api::Execute, types::RefundsData, types::Ref | ||||
|     } | ||||
|  | ||||
|     fn get_content_type(&self) -> &'static str { | ||||
|         "" | ||||
|         "application/json" | ||||
|     } | ||||
|  | ||||
|     fn get_url( | ||||
|         &self, | ||||
|         _req: &types::RefundsRouterData<api::Execute>, | ||||
|         _connectors: Connectors, | ||||
|         req: &types::RefundsRouterData<api::Execute>, | ||||
|         connectors: Connectors, | ||||
|     ) -> CustomResult<String, errors::ConnectorError> { | ||||
|         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<api::Execute>, | ||||
|     ) -> CustomResult<Option<String>, errors::ConnectorError> { | ||||
|         let braintree_req = utils::Encode::<braintree::RefundRequest>::convert_and_url_encode(req) | ||||
|             .change_context(errors::ConnectorError::RequestEncodingFailed)?; | ||||
|         let braintree_req = | ||||
|             utils::Encode::<braintree::BraintreeRefundRequest>::convert_and_url_encode(req) | ||||
|                 .change_context(errors::ConnectorError::RequestEncodingFailed)?; | ||||
|         Ok(Some(braintree_req)) | ||||
|     } | ||||
|  | ||||
| @ -509,7 +562,7 @@ impl services::ConnectorIntegration<api::Execute, types::RefundsData, types::Ref | ||||
|         logger::debug!(target: "router::connector::braintree", response=?res); | ||||
|         let response: braintree::RefundResponse = res | ||||
|             .response | ||||
|             .parse_struct("braintree RefundResponse") | ||||
|             .parse_struct("Braintree RefundResponse") | ||||
|             .change_context(errors::ConnectorError::RequestEncodingFailed)?; | ||||
|         types::ResponseRouterData { | ||||
|             response, | ||||
| @ -584,7 +637,7 @@ impl services::ConnectorIntegration<api::RSync, types::RefundsData, types::Refun | ||||
|         logger::debug!(target: "router::connector::braintree", response=?res); | ||||
|         let response: braintree::RefundResponse = res | ||||
|             .response | ||||
|             .parse_struct("braintree RefundResponse") | ||||
|             .parse_struct("Braintree RefundResponse") | ||||
|             .change_context(errors::ConnectorError::ResponseDeserializationFailed)?; | ||||
|         types::ResponseRouterData { | ||||
|             response, | ||||
|  | ||||
| @ -44,11 +44,15 @@ impl TryFrom<&types::PaymentsAuthorizeRouterData> for BraintreePaymentsRequest { | ||||
|     fn try_from(item: &types::PaymentsAuthorizeRouterData) -> Result<Self, Self::Error> { | ||||
|         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<F, T> | ||||
|         >, | ||||
|     ) -> Result<Self, Self::Error> { | ||||
|         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<F> TryFrom<&types::RefundsRouterData<F>> for RefundRequest { | ||||
|     type Error = error_stack::Report<errors::ParsingError>; | ||||
| #[derive(Default, Debug, Serialize, Clone)] | ||||
| pub struct Amount { | ||||
|     amount: Option<String>, | ||||
| } | ||||
|  | ||||
| impl<F> TryFrom<&types::RefundsRouterData<F>> for BraintreeRefundRequest { | ||||
|     type Error = error_stack::Report<errors::ConnectorError>; | ||||
|     fn try_from(_item: &types::RefundsRouterData<F>) -> Result<Self, Self::Error> { | ||||
|         Ok(RefundRequest {}) | ||||
|         Ok(BraintreeRefundRequest { | ||||
|             transaction: Amount { amount: None }, | ||||
|         }) | ||||
|     } | ||||
| } | ||||
|  | ||||
|  | ||||
| @ -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() | ||||
|  | ||||
| @ -20,6 +20,7 @@ pub(crate) type Headers = Vec<(String, String)>; | ||||
| pub enum Method { | ||||
|     Get, | ||||
|     Post, | ||||
|     Put, | ||||
|     Delete, | ||||
| } | ||||
|  | ||||
|  | ||||
		Reference in New Issue
	
	Block a user
	 Sahebjot singh
					Sahebjot singh