mirror of
https://github.com/juspay/hyperswitch.git
synced 2025-11-02 21:07:58 +08:00
Co-authored-by: hyperswitch-bot[bot] <148525504+hyperswitch-bot[bot]@users.noreply.github.com> Co-authored-by: Sanchith Hegde <22217505+SanchithHegde@users.noreply.github.com>
941 lines
31 KiB
Rust
941 lines
31 KiB
Rust
pub mod transformers;
|
|
|
|
use std::fmt::Debug;
|
|
|
|
use api_models::enums;
|
|
use base64::Engine;
|
|
use common_utils::{ext_traits::ByteSliceExt, request::RequestContent};
|
|
use error_stack::ResultExt;
|
|
use masking::PeekInterface;
|
|
use transformers as square;
|
|
|
|
use super::utils::{self as super_utils, RefundsRequestData};
|
|
use crate::{
|
|
configs::settings,
|
|
consts,
|
|
core::errors::{self, CustomResult},
|
|
events::connector_api_logs::ConnectorEvent,
|
|
headers,
|
|
services::{
|
|
self,
|
|
request::{self, Mask},
|
|
ConnectorIntegration, ConnectorValidation,
|
|
},
|
|
types::{
|
|
self,
|
|
api::{self, ConnectorCommon, ConnectorCommonExt},
|
|
ErrorResponse, Response,
|
|
},
|
|
utils::BytesExt,
|
|
};
|
|
|
|
#[derive(Debug, Clone)]
|
|
pub struct Square;
|
|
|
|
impl api::Payment for Square {}
|
|
impl api::PaymentSession for Square {}
|
|
impl api::ConnectorAccessToken for Square {}
|
|
impl api::MandateSetup for Square {}
|
|
impl api::PaymentAuthorize for Square {}
|
|
impl api::PaymentAuthorizeSessionToken for Square {}
|
|
impl api::PaymentSync for Square {}
|
|
impl api::PaymentCapture for Square {}
|
|
impl api::PaymentVoid for Square {}
|
|
impl api::Refund for Square {}
|
|
impl api::RefundExecute for Square {}
|
|
impl api::RefundSync for Square {}
|
|
impl api::PaymentToken for Square {}
|
|
|
|
impl<Flow, Request, Response> ConnectorCommonExt<Flow, Request, Response> for Square
|
|
where
|
|
Self: ConnectorIntegration<Flow, Request, Response>,
|
|
{
|
|
fn build_headers(
|
|
&self,
|
|
req: &types::RouterData<Flow, Request, Response>,
|
|
_connectors: &settings::Connectors,
|
|
) -> CustomResult<Vec<(String, request::Maskable<String>)>, errors::ConnectorError> {
|
|
let mut header = vec![(
|
|
headers::CONTENT_TYPE.to_string(),
|
|
types::PaymentsAuthorizeType::get_content_type(self)
|
|
.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 Square {
|
|
fn id(&self) -> &'static str {
|
|
"square"
|
|
}
|
|
|
|
fn common_get_content_type(&self) -> &'static str {
|
|
"application/json"
|
|
}
|
|
|
|
fn base_url<'a>(&self, connectors: &'a settings::Connectors) -> &'a str {
|
|
connectors.square.base_url.as_ref()
|
|
}
|
|
|
|
fn get_auth_header(
|
|
&self,
|
|
auth_type: &types::ConnectorAuthType,
|
|
) -> CustomResult<Vec<(String, request::Maskable<String>)>, errors::ConnectorError> {
|
|
let auth = square::SquareAuthType::try_from(auth_type)
|
|
.change_context(errors::ConnectorError::FailedToObtainAuthType)?;
|
|
Ok(vec![(
|
|
headers::AUTHORIZATION.to_string(),
|
|
format!("Bearer {}", auth.api_key.peek()).into_masked(),
|
|
)])
|
|
}
|
|
|
|
fn build_error_response(
|
|
&self,
|
|
res: Response,
|
|
event_builder: Option<&mut ConnectorEvent>,
|
|
) -> CustomResult<ErrorResponse, errors::ConnectorError> {
|
|
let response: square::SquareErrorResponse = res
|
|
.response
|
|
.parse_struct("SquareErrorResponse")
|
|
.change_context(errors::ConnectorError::ResponseDeserializationFailed)?;
|
|
|
|
event_builder.map(|i| i.set_error_response_body(&response));
|
|
router_env::logger::info!(connector_response=?response);
|
|
|
|
let mut reason_list = Vec::new();
|
|
for error_iter in response.errors.iter() {
|
|
if let Some(error) = error_iter.detail.clone() {
|
|
reason_list.push(error)
|
|
}
|
|
}
|
|
let reason = reason_list.join(" ");
|
|
|
|
Ok(ErrorResponse {
|
|
status_code: res.status_code,
|
|
code: response
|
|
.errors
|
|
.first()
|
|
.and_then(|error| error.code.clone())
|
|
.unwrap_or(consts::NO_ERROR_CODE.to_string()),
|
|
message: response
|
|
.errors
|
|
.first()
|
|
.and_then(|error| error.category.clone())
|
|
.unwrap_or(consts::NO_ERROR_MESSAGE.to_string()),
|
|
reason: Some(reason),
|
|
attempt_status: None,
|
|
connector_transaction_id: None,
|
|
})
|
|
}
|
|
}
|
|
|
|
impl ConnectorValidation for Square {
|
|
fn validate_capture_method(
|
|
&self,
|
|
capture_method: Option<enums::CaptureMethod>,
|
|
_pmt: Option<enums::PaymentMethodType>,
|
|
) -> CustomResult<(), errors::ConnectorError> {
|
|
let capture_method = capture_method.unwrap_or_default();
|
|
match capture_method {
|
|
enums::CaptureMethod::Automatic | enums::CaptureMethod::Manual => Ok(()),
|
|
enums::CaptureMethod::ManualMultiple | enums::CaptureMethod::Scheduled => Err(
|
|
super::utils::construct_not_implemented_error_report(capture_method, self.id()),
|
|
),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl ConnectorIntegration<api::Session, types::PaymentsSessionData, types::PaymentsResponseData>
|
|
for Square
|
|
{
|
|
//TODO: implement sessions flow
|
|
}
|
|
|
|
impl ConnectorIntegration<api::AccessTokenAuth, types::AccessTokenRequestData, types::AccessToken>
|
|
for Square
|
|
{
|
|
}
|
|
|
|
impl
|
|
ConnectorIntegration<
|
|
api::SetupMandate,
|
|
types::SetupMandateRequestData,
|
|
types::PaymentsResponseData,
|
|
> for Square
|
|
{
|
|
fn build_request(
|
|
&self,
|
|
_req: &types::RouterData<
|
|
api::SetupMandate,
|
|
types::SetupMandateRequestData,
|
|
types::PaymentsResponseData,
|
|
>,
|
|
_connectors: &settings::Connectors,
|
|
) -> CustomResult<Option<services::Request>, errors::ConnectorError> {
|
|
Err(
|
|
errors::ConnectorError::NotImplemented("Setup Mandate flow for Square".to_string())
|
|
.into(),
|
|
)
|
|
}
|
|
}
|
|
|
|
#[async_trait::async_trait]
|
|
impl
|
|
ConnectorIntegration<
|
|
api::PaymentMethodToken,
|
|
types::PaymentMethodTokenizationData,
|
|
types::PaymentsResponseData,
|
|
> for Square
|
|
{
|
|
fn get_headers(
|
|
&self,
|
|
_req: &types::TokenizationRouterData,
|
|
_connectors: &settings::Connectors,
|
|
) -> CustomResult<Vec<(String, request::Maskable<String>)>, errors::ConnectorError> {
|
|
Ok(vec![(
|
|
headers::CONTENT_TYPE.to_string(),
|
|
types::TokenizationType::get_content_type(self)
|
|
.to_string()
|
|
.into(),
|
|
)])
|
|
}
|
|
|
|
fn get_content_type(&self) -> &'static str {
|
|
self.common_get_content_type()
|
|
}
|
|
|
|
fn get_url(
|
|
&self,
|
|
_req: &types::TokenizationRouterData,
|
|
connectors: &settings::Connectors,
|
|
) -> CustomResult<String, errors::ConnectorError> {
|
|
Ok(format!(
|
|
"{}v2/card-nonce",
|
|
connectors
|
|
.square
|
|
.secondary_base_url
|
|
.clone()
|
|
.ok_or(errors::ConnectorError::FailedToObtainIntegrationUrl)?,
|
|
))
|
|
}
|
|
|
|
fn get_request_body(
|
|
&self,
|
|
req: &types::TokenizationRouterData,
|
|
_connectors: &settings::Connectors,
|
|
) -> CustomResult<RequestContent, errors::ConnectorError> {
|
|
let connector_req = square::SquareTokenRequest::try_from(req)?;
|
|
|
|
Ok(RequestContent::Json(Box::new(connector_req)))
|
|
}
|
|
|
|
fn build_request(
|
|
&self,
|
|
req: &types::TokenizationRouterData,
|
|
connectors: &settings::Connectors,
|
|
) -> CustomResult<Option<services::Request>, errors::ConnectorError> {
|
|
Ok(Some(
|
|
services::RequestBuilder::new()
|
|
.method(services::Method::Post)
|
|
.url(&types::TokenizationType::get_url(self, req, connectors)?)
|
|
.attach_default_headers()
|
|
.headers(types::TokenizationType::get_headers(self, req, connectors)?)
|
|
.set_body(types::TokenizationType::get_request_body(
|
|
self, req, connectors,
|
|
)?)
|
|
.build(),
|
|
))
|
|
}
|
|
|
|
fn handle_response(
|
|
&self,
|
|
data: &types::TokenizationRouterData,
|
|
event_builder: Option<&mut ConnectorEvent>,
|
|
res: Response,
|
|
) -> CustomResult<types::TokenizationRouterData, errors::ConnectorError>
|
|
where
|
|
types::PaymentsResponseData: Clone,
|
|
{
|
|
let response: square::SquareTokenResponse = res
|
|
.response
|
|
.parse_struct("SquareTokenResponse")
|
|
.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<ErrorResponse, errors::ConnectorError> {
|
|
self.build_error_response(res, event_builder)
|
|
}
|
|
}
|
|
|
|
impl
|
|
ConnectorIntegration<
|
|
api::AuthorizeSessionToken,
|
|
types::AuthorizeSessionTokenData,
|
|
types::PaymentsResponseData,
|
|
> for Square
|
|
{
|
|
fn get_headers(
|
|
&self,
|
|
_req: &types::PaymentsAuthorizeSessionTokenRouterData,
|
|
_connectors: &settings::Connectors,
|
|
) -> CustomResult<Vec<(String, request::Maskable<String>)>, errors::ConnectorError> {
|
|
Ok(vec![(
|
|
headers::CONTENT_TYPE.to_string(),
|
|
types::PaymentsAuthorizeType::get_content_type(self)
|
|
.to_string()
|
|
.into(),
|
|
)])
|
|
}
|
|
|
|
fn get_content_type(&self) -> &'static str {
|
|
self.common_get_content_type()
|
|
}
|
|
|
|
fn get_url(
|
|
&self,
|
|
req: &types::PaymentsAuthorizeSessionTokenRouterData,
|
|
connectors: &settings::Connectors,
|
|
) -> CustomResult<String, errors::ConnectorError> {
|
|
let auth = square::SquareAuthType::try_from(&req.connector_auth_type)
|
|
.change_context(errors::ConnectorError::FailedToObtainAuthType)?;
|
|
|
|
Ok(format!(
|
|
"{}payments/hydrate?applicationId={}",
|
|
connectors
|
|
.square
|
|
.secondary_base_url
|
|
.clone()
|
|
.ok_or(errors::ConnectorError::FailedToObtainIntegrationUrl)?,
|
|
auth.key1.peek()
|
|
))
|
|
}
|
|
|
|
fn build_request(
|
|
&self,
|
|
req: &types::PaymentsAuthorizeSessionTokenRouterData,
|
|
connectors: &settings::Connectors,
|
|
) -> CustomResult<Option<services::Request>, errors::ConnectorError> {
|
|
Ok(Some(
|
|
services::RequestBuilder::new()
|
|
.method(services::Method::Get)
|
|
.url(&types::PaymentsPreAuthorizeType::get_url(
|
|
self, req, connectors,
|
|
)?)
|
|
.attach_default_headers()
|
|
.headers(types::PaymentsPreAuthorizeType::get_headers(
|
|
self, req, connectors,
|
|
)?)
|
|
.build(),
|
|
))
|
|
}
|
|
|
|
fn handle_response(
|
|
&self,
|
|
data: &types::PaymentsAuthorizeSessionTokenRouterData,
|
|
event_builder: Option<&mut ConnectorEvent>,
|
|
res: Response,
|
|
) -> CustomResult<types::PaymentsAuthorizeSessionTokenRouterData, errors::ConnectorError> {
|
|
let response: square::SquareSessionResponse = res
|
|
.response
|
|
.parse_struct("SquareSessionResponse")
|
|
.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<ErrorResponse, errors::ConnectorError> {
|
|
self.build_error_response(res, event_builder)
|
|
}
|
|
}
|
|
|
|
impl ConnectorIntegration<api::Authorize, types::PaymentsAuthorizeData, types::PaymentsResponseData>
|
|
for Square
|
|
{
|
|
fn get_headers(
|
|
&self,
|
|
req: &types::PaymentsAuthorizeRouterData,
|
|
connectors: &settings::Connectors,
|
|
) -> CustomResult<Vec<(String, request::Maskable<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::PaymentsAuthorizeRouterData,
|
|
connectors: &settings::Connectors,
|
|
) -> CustomResult<String, errors::ConnectorError> {
|
|
Ok(format!("{}v2/payments", self.base_url(connectors)))
|
|
}
|
|
|
|
fn get_request_body(
|
|
&self,
|
|
req: &types::PaymentsAuthorizeRouterData,
|
|
_connectors: &settings::Connectors,
|
|
) -> CustomResult<RequestContent, errors::ConnectorError> {
|
|
let connector_req = square::SquarePaymentsRequest::try_from(req)?;
|
|
|
|
Ok(RequestContent::Json(Box::new(connector_req)))
|
|
}
|
|
|
|
fn build_request(
|
|
&self,
|
|
req: &types::PaymentsAuthorizeRouterData,
|
|
connectors: &settings::Connectors,
|
|
) -> CustomResult<Option<services::Request>, 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<types::PaymentsAuthorizeRouterData, errors::ConnectorError> {
|
|
let response: square::SquarePaymentsResponse = res
|
|
.response
|
|
.parse_struct("SquarePaymentsAuthorizeResponse")
|
|
.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<ErrorResponse, errors::ConnectorError> {
|
|
self.build_error_response(res, event_builder)
|
|
}
|
|
}
|
|
|
|
impl ConnectorIntegration<api::PSync, types::PaymentsSyncData, types::PaymentsResponseData>
|
|
for Square
|
|
{
|
|
fn get_headers(
|
|
&self,
|
|
req: &types::PaymentsSyncRouterData,
|
|
connectors: &settings::Connectors,
|
|
) -> CustomResult<Vec<(String, request::Maskable<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::PaymentsSyncRouterData,
|
|
connectors: &settings::Connectors,
|
|
) -> CustomResult<String, errors::ConnectorError> {
|
|
let connector_payment_id = req
|
|
.request
|
|
.connector_transaction_id
|
|
.get_connector_transaction_id()
|
|
.change_context(errors::ConnectorError::MissingConnectorTransactionID)?;
|
|
|
|
Ok(format!(
|
|
"{}v2/payments/{connector_payment_id}",
|
|
self.base_url(connectors),
|
|
))
|
|
}
|
|
|
|
fn build_request(
|
|
&self,
|
|
req: &types::PaymentsSyncRouterData,
|
|
connectors: &settings::Connectors,
|
|
) -> CustomResult<Option<services::Request>, 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<types::PaymentsSyncRouterData, errors::ConnectorError> {
|
|
let response: square::SquarePaymentsResponse = res
|
|
.response
|
|
.parse_struct("SquarePaymentsSyncResponse")
|
|
.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<ErrorResponse, errors::ConnectorError> {
|
|
self.build_error_response(res, event_builder)
|
|
}
|
|
}
|
|
|
|
impl ConnectorIntegration<api::Capture, types::PaymentsCaptureData, types::PaymentsResponseData>
|
|
for Square
|
|
{
|
|
fn get_headers(
|
|
&self,
|
|
req: &types::PaymentsCaptureRouterData,
|
|
connectors: &settings::Connectors,
|
|
) -> CustomResult<Vec<(String, request::Maskable<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::PaymentsCaptureRouterData,
|
|
connectors: &settings::Connectors,
|
|
) -> CustomResult<String, errors::ConnectorError> {
|
|
Ok(format!(
|
|
"{}v2/payments/{}/complete",
|
|
self.base_url(connectors),
|
|
req.request.connector_transaction_id,
|
|
))
|
|
}
|
|
|
|
fn build_request(
|
|
&self,
|
|
req: &types::PaymentsCaptureRouterData,
|
|
connectors: &settings::Connectors,
|
|
) -> CustomResult<Option<services::Request>, errors::ConnectorError> {
|
|
if req.request.amount_to_capture != req.request.payment_amount {
|
|
Err(errors::ConnectorError::NotSupported {
|
|
message: "Partial Capture".to_string(),
|
|
connector: "Square",
|
|
})?
|
|
}
|
|
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,
|
|
)?)
|
|
.build(),
|
|
))
|
|
}
|
|
|
|
fn handle_response(
|
|
&self,
|
|
data: &types::PaymentsCaptureRouterData,
|
|
event_builder: Option<&mut ConnectorEvent>,
|
|
res: Response,
|
|
) -> CustomResult<types::PaymentsCaptureRouterData, errors::ConnectorError> {
|
|
let response: square::SquarePaymentsResponse = res
|
|
.response
|
|
.parse_struct("SquarePaymentsCaptureResponse")
|
|
.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<ErrorResponse, errors::ConnectorError> {
|
|
self.build_error_response(res, event_builder)
|
|
}
|
|
}
|
|
|
|
impl ConnectorIntegration<api::Void, types::PaymentsCancelData, types::PaymentsResponseData>
|
|
for Square
|
|
{
|
|
fn get_headers(
|
|
&self,
|
|
req: &types::PaymentsCancelRouterData,
|
|
connectors: &settings::Connectors,
|
|
) -> CustomResult<Vec<(String, request::Maskable<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> {
|
|
Ok(format!(
|
|
"{}v2/payments/{}/cancel",
|
|
self.base_url(connectors),
|
|
req.request.connector_transaction_id,
|
|
))
|
|
}
|
|
|
|
fn build_request(
|
|
&self,
|
|
req: &types::PaymentsCancelRouterData,
|
|
connectors: &settings::Connectors,
|
|
) -> CustomResult<Option<services::Request>, errors::ConnectorError> {
|
|
Ok(Some(
|
|
services::RequestBuilder::new()
|
|
.method(services::Method::Post)
|
|
.url(&types::PaymentsVoidType::get_url(self, req, connectors)?)
|
|
.attach_default_headers()
|
|
.headers(types::PaymentsVoidType::get_headers(self, req, connectors)?)
|
|
.build(),
|
|
))
|
|
}
|
|
|
|
fn handle_response(
|
|
&self,
|
|
data: &types::PaymentsCancelRouterData,
|
|
event_builder: Option<&mut ConnectorEvent>,
|
|
res: Response,
|
|
) -> CustomResult<types::PaymentsCancelRouterData, errors::ConnectorError> {
|
|
let response: square::SquarePaymentsResponse = res
|
|
.response
|
|
.parse_struct("SquarePaymentsVoidResponse")
|
|
.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<ErrorResponse, errors::ConnectorError> {
|
|
self.build_error_response(res, event_builder)
|
|
}
|
|
}
|
|
|
|
impl ConnectorIntegration<api::Execute, types::RefundsData, types::RefundsResponseData> for Square {
|
|
fn get_headers(
|
|
&self,
|
|
req: &types::RefundsRouterData<api::Execute>,
|
|
connectors: &settings::Connectors,
|
|
) -> CustomResult<Vec<(String, request::Maskable<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::RefundsRouterData<api::Execute>,
|
|
connectors: &settings::Connectors,
|
|
) -> CustomResult<String, errors::ConnectorError> {
|
|
Ok(format!("{}v2/refunds", self.base_url(connectors),))
|
|
}
|
|
|
|
fn get_request_body(
|
|
&self,
|
|
req: &types::RefundsRouterData<api::Execute>,
|
|
_connectors: &settings::Connectors,
|
|
) -> CustomResult<RequestContent, errors::ConnectorError> {
|
|
let connector_req = square::SquareRefundRequest::try_from(req)?;
|
|
Ok(RequestContent::Json(Box::new(connector_req)))
|
|
}
|
|
|
|
fn build_request(
|
|
&self,
|
|
req: &types::RefundsRouterData<api::Execute>,
|
|
connectors: &settings::Connectors,
|
|
) -> CustomResult<Option<services::Request>, 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<api::Execute>,
|
|
event_builder: Option<&mut ConnectorEvent>,
|
|
res: Response,
|
|
) -> CustomResult<types::RefundsRouterData<api::Execute>, errors::ConnectorError> {
|
|
let response: square::RefundResponse = res
|
|
.response
|
|
.parse_struct("SquareRefundResponse")
|
|
.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<ErrorResponse, errors::ConnectorError> {
|
|
self.build_error_response(res, event_builder)
|
|
}
|
|
}
|
|
|
|
impl ConnectorIntegration<api::RSync, types::RefundsData, types::RefundsResponseData> for Square {
|
|
fn get_headers(
|
|
&self,
|
|
req: &types::RefundSyncRouterData,
|
|
connectors: &settings::Connectors,
|
|
) -> CustomResult<Vec<(String, request::Maskable<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::RefundSyncRouterData,
|
|
connectors: &settings::Connectors,
|
|
) -> CustomResult<String, errors::ConnectorError> {
|
|
Ok(format!(
|
|
"{}v2/refunds/{}",
|
|
self.base_url(connectors),
|
|
req.request.get_connector_refund_id()?,
|
|
))
|
|
}
|
|
|
|
fn build_request(
|
|
&self,
|
|
req: &types::RefundSyncRouterData,
|
|
connectors: &settings::Connectors,
|
|
) -> CustomResult<Option<services::Request>, 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)?)
|
|
.build(),
|
|
))
|
|
}
|
|
|
|
fn handle_response(
|
|
&self,
|
|
data: &types::RefundSyncRouterData,
|
|
event_builder: Option<&mut ConnectorEvent>,
|
|
res: Response,
|
|
) -> CustomResult<types::RefundSyncRouterData, errors::ConnectorError> {
|
|
let response: square::RefundResponse = res
|
|
.response
|
|
.parse_struct("SquareRefundSyncResponse")
|
|
.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<ErrorResponse, errors::ConnectorError> {
|
|
self.build_error_response(res, event_builder)
|
|
}
|
|
}
|
|
|
|
#[async_trait::async_trait]
|
|
impl api::IncomingWebhook for Square {
|
|
fn get_webhook_source_verification_algorithm(
|
|
&self,
|
|
_request: &api::IncomingWebhookRequestDetails<'_>,
|
|
) -> CustomResult<Box<dyn common_utils::crypto::VerifySignature + Send>, errors::ConnectorError>
|
|
{
|
|
Ok(Box::new(common_utils::crypto::HmacSha256))
|
|
}
|
|
|
|
fn get_webhook_source_verification_signature(
|
|
&self,
|
|
request: &api::IncomingWebhookRequestDetails<'_>,
|
|
_connector_webhook_secrets: &api_models::webhooks::ConnectorWebhookSecrets,
|
|
) -> CustomResult<Vec<u8>, errors::ConnectorError> {
|
|
let encoded_signature =
|
|
super_utils::get_header_key_value("x-square-hmacsha256-signature", request.headers)?;
|
|
let signature = consts::BASE64_ENGINE
|
|
.decode(encoded_signature)
|
|
.change_context(errors::ConnectorError::WebhookSignatureNotFound)?;
|
|
Ok(signature)
|
|
}
|
|
|
|
fn get_webhook_source_verification_message(
|
|
&self,
|
|
request: &api::IncomingWebhookRequestDetails<'_>,
|
|
_merchant_id: &common_utils::id_type::MerchantId,
|
|
_connector_webhook_secrets: &api_models::webhooks::ConnectorWebhookSecrets,
|
|
) -> CustomResult<Vec<u8>, errors::ConnectorError> {
|
|
let header_value = request
|
|
.headers
|
|
.get(actix_web::http::header::HOST)
|
|
.ok_or(errors::ConnectorError::WebhookSourceVerificationFailed)?;
|
|
let authority = header_value
|
|
.to_str()
|
|
.change_context(errors::ConnectorError::WebhookSourceVerificationFailed)?;
|
|
|
|
Ok(format!(
|
|
"https://{}{}{}",
|
|
authority,
|
|
request.uri,
|
|
String::from_utf8_lossy(request.body)
|
|
)
|
|
.into_bytes())
|
|
}
|
|
|
|
fn get_webhook_object_reference_id(
|
|
&self,
|
|
request: &api::IncomingWebhookRequestDetails<'_>,
|
|
) -> CustomResult<api::webhooks::ObjectReferenceId, errors::ConnectorError> {
|
|
let webhook_body: square::SquareWebhookBody = request
|
|
.body
|
|
.parse_struct("SquareWebhookBody")
|
|
.change_context(errors::ConnectorError::WebhookReferenceIdNotFound)?;
|
|
|
|
match webhook_body.data.object {
|
|
square::SquareWebhookObject::Payment(_) => {
|
|
Ok(api_models::webhooks::ObjectReferenceId::PaymentId(
|
|
api_models::payments::PaymentIdType::ConnectorTransactionId(
|
|
webhook_body.data.id,
|
|
),
|
|
))
|
|
}
|
|
square::SquareWebhookObject::Refund(_) => {
|
|
Ok(api_models::webhooks::ObjectReferenceId::RefundId(
|
|
api_models::webhooks::RefundIdType::ConnectorRefundId(webhook_body.data.id),
|
|
))
|
|
}
|
|
}
|
|
}
|
|
|
|
fn get_webhook_event_type(
|
|
&self,
|
|
request: &api::IncomingWebhookRequestDetails<'_>,
|
|
) -> CustomResult<api::IncomingWebhookEvent, errors::ConnectorError> {
|
|
let details: square::SquareWebhookBody = request
|
|
.body
|
|
.parse_struct("SquareWebhookEventType")
|
|
.change_context(errors::ConnectorError::WebhookEventTypeNotFound)?;
|
|
Ok(api::IncomingWebhookEvent::from(details.data.object))
|
|
}
|
|
|
|
fn get_webhook_resource_object(
|
|
&self,
|
|
request: &api::IncomingWebhookRequestDetails<'_>,
|
|
) -> CustomResult<Box<dyn masking::ErasedMaskSerialize>, errors::ConnectorError> {
|
|
let details: square::SquareWebhookBody =
|
|
request
|
|
.body
|
|
.parse_struct("SquareWebhookObject")
|
|
.change_context(errors::ConnectorError::WebhookEventTypeNotFound)?;
|
|
Ok(match details.data.object {
|
|
square::SquareWebhookObject::Payment(square_payments_response_details) => {
|
|
Box::new(square_payments_response_details)
|
|
}
|
|
square::SquareWebhookObject::Refund(square_refund_response_details) => {
|
|
Box::new(square_refund_response_details)
|
|
}
|
|
})
|
|
}
|
|
}
|