mirror of
https://github.com/juspay/hyperswitch.git
synced 2025-10-29 17:19:15 +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,
|
res: Bytes,
|
||||||
) -> CustomResult<ErrorResponse, errors::ConnectorError> {
|
) -> CustomResult<ErrorResponse, errors::ConnectorError> {
|
||||||
let response: braintree::ErrorResponse = res
|
let response: braintree::ErrorResponse = res
|
||||||
.parse_struct("Error Response")
|
.parse_struct("Braintree Error Response")
|
||||||
.change_context(errors::ConnectorError::ResponseDeserializationFailed)?;
|
.change_context(errors::ConnectorError::ResponseDeserializationFailed)?;
|
||||||
|
|
||||||
Ok(ErrorResponse {
|
Ok(ErrorResponse {
|
||||||
@ -271,7 +271,7 @@ impl
|
|||||||
logger::debug!(payment_sync_response=?res);
|
logger::debug!(payment_sync_response=?res);
|
||||||
let response: braintree::BraintreePaymentsResponse = res
|
let response: braintree::BraintreePaymentsResponse = res
|
||||||
.response
|
.response
|
||||||
.parse_struct("braintree PaymentsResponse")
|
.parse_struct("Braintree PaymentsResponse")
|
||||||
.change_context(errors::ConnectorError::ResponseDeserializationFailed)?;
|
.change_context(errors::ConnectorError::ResponseDeserializationFailed)?;
|
||||||
types::RouterData::try_from(types::ResponseRouterData {
|
types::RouterData::try_from(types::ResponseRouterData {
|
||||||
response,
|
response,
|
||||||
@ -356,7 +356,7 @@ impl
|
|||||||
) -> CustomResult<types::PaymentsAuthorizeRouterData, errors::ConnectorError> {
|
) -> CustomResult<types::PaymentsAuthorizeRouterData, errors::ConnectorError> {
|
||||||
let response: braintree::BraintreePaymentsResponse = res
|
let response: braintree::BraintreePaymentsResponse = res
|
||||||
.response
|
.response
|
||||||
.parse_struct("Braintree Payments Response")
|
.parse_struct("Braintree PaymentsResponse")
|
||||||
.change_context(errors::ConnectorError::ResponseDeserializationFailed)?;
|
.change_context(errors::ConnectorError::ResponseDeserializationFailed)?;
|
||||||
logger::debug!(braintreepayments_create_response=?response);
|
logger::debug!(braintreepayments_create_response=?response);
|
||||||
types::ResponseRouterData {
|
types::ResponseRouterData {
|
||||||
@ -375,7 +375,7 @@ impl
|
|||||||
logger::debug!(braintreepayments_create_response=?res);
|
logger::debug!(braintreepayments_create_response=?res);
|
||||||
|
|
||||||
let response: braintree::ErrorResponse = res
|
let response: braintree::ErrorResponse = res
|
||||||
.parse_struct("ErrorResponse")
|
.parse_struct("Braintree ErrorResponse")
|
||||||
.change_context(errors::ConnectorError::ResponseDeserializationFailed)?;
|
.change_context(errors::ConnectorError::ResponseDeserializationFailed)?;
|
||||||
Ok(ErrorResponse {
|
Ok(ErrorResponse {
|
||||||
code: consts::NO_ERROR_CODE.to_string(),
|
code: consts::NO_ERROR_CODE.to_string(),
|
||||||
@ -394,50 +394,94 @@ impl
|
|||||||
{
|
{
|
||||||
fn get_headers(
|
fn get_headers(
|
||||||
&self,
|
&self,
|
||||||
_req: &types::PaymentsCancelRouterData,
|
req: &types::PaymentsCancelRouterData,
|
||||||
) -> CustomResult<Vec<(String, String)>, errors::ConnectorError> {
|
) -> 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 {
|
fn get_content_type(&self) -> &'static str {
|
||||||
""
|
"application/json"
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_url(
|
fn get_url(
|
||||||
&self,
|
&self,
|
||||||
_req: &types::PaymentsCancelRouterData,
|
req: &types::PaymentsCancelRouterData,
|
||||||
_connectors: Connectors,
|
connectors: Connectors,
|
||||||
) -> CustomResult<String, errors::ConnectorError> {
|
) -> 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(
|
fn get_request_body(
|
||||||
&self,
|
&self,
|
||||||
_req: &types::PaymentsCancelRouterData,
|
_req: &types::PaymentsCancelRouterData,
|
||||||
) -> CustomResult<Option<String>, errors::ConnectorError> {
|
) -> CustomResult<Option<String>, errors::ConnectorError> {
|
||||||
Err(errors::ConnectorError::NotImplemented("braintree".to_string()).into())
|
Ok(None)
|
||||||
}
|
|
||||||
fn build_request(
|
|
||||||
&self,
|
|
||||||
_req: &types::PaymentsCancelRouterData,
|
|
||||||
_connectors: Connectors,
|
|
||||||
) -> CustomResult<Option<services::Request>, errors::ConnectorError> {
|
|
||||||
Err(errors::ConnectorError::NotImplemented("braintree".to_string()).into())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn handle_response(
|
fn handle_response(
|
||||||
&self,
|
&self,
|
||||||
_data: &types::PaymentsCancelRouterData,
|
data: &types::PaymentsCancelRouterData,
|
||||||
_res: Response,
|
res: Response,
|
||||||
) -> CustomResult<types::PaymentsCancelRouterData, errors::ConnectorError> {
|
) -> CustomResult<types::PaymentsCancelRouterData, errors::ConnectorError> {
|
||||||
Err(errors::ConnectorError::NotImplemented("braintree".to_string()).into())
|
logger::debug!(payment_sync_response=?res);
|
||||||
}
|
let response: braintree::BraintreePaymentsResponse = res
|
||||||
|
.response
|
||||||
fn get_error_response(
|
.parse_struct("Braintree PaymentsVoidResponse")
|
||||||
&self,
|
.change_context(errors::ConnectorError::ResponseDeserializationFailed)?;
|
||||||
_res: Bytes,
|
types::RouterData::try_from(types::ResponseRouterData {
|
||||||
) -> CustomResult<ErrorResponse, errors::ConnectorError> {
|
response,
|
||||||
Err(errors::ConnectorError::NotImplemented("braintree".to_string()).into())
|
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 {
|
fn get_content_type(&self) -> &'static str {
|
||||||
""
|
"application/json"
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_url(
|
fn get_url(
|
||||||
&self,
|
&self,
|
||||||
_req: &types::RefundsRouterData<api::Execute>,
|
req: &types::RefundsRouterData<api::Execute>,
|
||||||
_connectors: Connectors,
|
connectors: Connectors,
|
||||||
) -> CustomResult<String, errors::ConnectorError> {
|
) -> 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(
|
fn get_request_body(
|
||||||
&self,
|
&self,
|
||||||
req: &types::RefundsRouterData<api::Execute>,
|
req: &types::RefundsRouterData<api::Execute>,
|
||||||
) -> CustomResult<Option<String>, errors::ConnectorError> {
|
) -> CustomResult<Option<String>, errors::ConnectorError> {
|
||||||
let braintree_req = utils::Encode::<braintree::RefundRequest>::convert_and_url_encode(req)
|
let braintree_req =
|
||||||
.change_context(errors::ConnectorError::RequestEncodingFailed)?;
|
utils::Encode::<braintree::BraintreeRefundRequest>::convert_and_url_encode(req)
|
||||||
|
.change_context(errors::ConnectorError::RequestEncodingFailed)?;
|
||||||
Ok(Some(braintree_req))
|
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);
|
logger::debug!(target: "router::connector::braintree", response=?res);
|
||||||
let response: braintree::RefundResponse = res
|
let response: braintree::RefundResponse = res
|
||||||
.response
|
.response
|
||||||
.parse_struct("braintree RefundResponse")
|
.parse_struct("Braintree RefundResponse")
|
||||||
.change_context(errors::ConnectorError::RequestEncodingFailed)?;
|
.change_context(errors::ConnectorError::RequestEncodingFailed)?;
|
||||||
types::ResponseRouterData {
|
types::ResponseRouterData {
|
||||||
response,
|
response,
|
||||||
@ -584,7 +637,7 @@ impl services::ConnectorIntegration<api::RSync, types::RefundsData, types::Refun
|
|||||||
logger::debug!(target: "router::connector::braintree", response=?res);
|
logger::debug!(target: "router::connector::braintree", response=?res);
|
||||||
let response: braintree::RefundResponse = res
|
let response: braintree::RefundResponse = res
|
||||||
.response
|
.response
|
||||||
.parse_struct("braintree RefundResponse")
|
.parse_struct("Braintree RefundResponse")
|
||||||
.change_context(errors::ConnectorError::ResponseDeserializationFailed)?;
|
.change_context(errors::ConnectorError::ResponseDeserializationFailed)?;
|
||||||
types::ResponseRouterData {
|
types::ResponseRouterData {
|
||||||
response,
|
response,
|
||||||
|
|||||||
@ -44,11 +44,15 @@ impl TryFrom<&types::PaymentsAuthorizeRouterData> for BraintreePaymentsRequest {
|
|||||||
fn try_from(item: &types::PaymentsAuthorizeRouterData) -> Result<Self, Self::Error> {
|
fn try_from(item: &types::PaymentsAuthorizeRouterData) -> Result<Self, Self::Error> {
|
||||||
match item.request.payment_method_data {
|
match item.request.payment_method_data {
|
||||||
api::PaymentMethod::Card(ref ccard) => {
|
api::PaymentMethod::Card(ref ccard) => {
|
||||||
|
let submit_for_settlement = matches!(
|
||||||
|
item.request.capture_method,
|
||||||
|
Some(enums::CaptureMethod::Automatic) | None
|
||||||
|
);
|
||||||
let braintree_payment_request = TransactionBody {
|
let braintree_payment_request = TransactionBody {
|
||||||
amount: item.request.amount.to_string(),
|
amount: item.request.amount.to_string(),
|
||||||
device_data: DeviceData {},
|
device_data: DeviceData {},
|
||||||
options: PaymentOptions {
|
options: PaymentOptions {
|
||||||
submit_for_settlement: true,
|
submit_for_settlement,
|
||||||
},
|
},
|
||||||
credit_card: Card {
|
credit_card: Card {
|
||||||
number: ccard.card_number.peek().clone(),
|
number: ccard.card_number.peek().clone(),
|
||||||
@ -62,7 +66,9 @@ impl TryFrom<&types::PaymentsAuthorizeRouterData> for BraintreePaymentsRequest {
|
|||||||
transaction: braintree_payment_request,
|
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> {
|
) -> Result<Self, Self::Error> {
|
||||||
Ok(types::RouterData {
|
Ok(types::RouterData {
|
||||||
|
status: enums::AttemptStatus::from(item.response.transaction.status),
|
||||||
response: Ok(types::PaymentsResponseData::TransactionResponse {
|
response: Ok(types::PaymentsResponseData::TransactionResponse {
|
||||||
resource_id: types::ResponseId::ConnectorTransactionId(
|
resource_id: types::ResponseId::ConnectorTransactionId(
|
||||||
item.response.transaction.id,
|
item.response.transaction.id,
|
||||||
@ -223,12 +230,21 @@ pub struct ApiErrorResponse {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Default, Debug, Clone, Serialize)]
|
#[derive(Default, Debug, Clone, Serialize)]
|
||||||
pub struct RefundRequest {}
|
pub struct BraintreeRefundRequest {
|
||||||
|
transaction: Amount,
|
||||||
|
}
|
||||||
|
|
||||||
impl<F> TryFrom<&types::RefundsRouterData<F>> for RefundRequest {
|
#[derive(Default, Debug, Serialize, Clone)]
|
||||||
type Error = error_stack::Report<errors::ParsingError>;
|
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> {
|
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
|
.await
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Method::Put => client.put(url).add_headers(headers).send().await,
|
||||||
Method::Delete => client.delete(url).add_headers(headers).send().await,
|
Method::Delete => client.delete(url).add_headers(headers).send().await,
|
||||||
}
|
}
|
||||||
.into_report()
|
.into_report()
|
||||||
|
|||||||
@ -20,6 +20,7 @@ pub(crate) type Headers = Vec<(String, String)>;
|
|||||||
pub enum Method {
|
pub enum Method {
|
||||||
Get,
|
Get,
|
||||||
Post,
|
Post,
|
||||||
|
Put,
|
||||||
Delete,
|
Delete,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -39,6 +39,7 @@ case "$connector" in
|
|||||||
authorizedotnet) required_connector="authorizedotnet";;
|
authorizedotnet) required_connector="authorizedotnet";;
|
||||||
aci) required_connector="aci";;
|
aci) required_connector="aci";;
|
||||||
adyen) required_connector="adyen";;
|
adyen) required_connector="adyen";;
|
||||||
|
braintree) required_connector="braintree";;
|
||||||
*) echo "This connector is not supported" 1>&2;exit 1;;
|
*) echo "This connector is not supported" 1>&2;exit 1;;
|
||||||
esac
|
esac
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user