mirror of
https://github.com/juspay/hyperswitch.git
synced 2025-10-31 01:57:45 +08:00
feat(connector): add psync for authorizedotnet (#7)
This commit is contained in:
@ -60,7 +60,6 @@ impl
|
|||||||
// Not Implemented (R)
|
// Not Implemented (R)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(dead_code)]
|
|
||||||
type PSync = dyn services::ConnectorIntegration<
|
type PSync = dyn services::ConnectorIntegration<
|
||||||
api::PSync,
|
api::PSync,
|
||||||
types::PaymentsRequestSyncData,
|
types::PaymentsRequestSyncData,
|
||||||
@ -73,7 +72,109 @@ impl
|
|||||||
types::PaymentsResponseData,
|
types::PaymentsResponseData,
|
||||||
> for Authorizedotnet
|
> for Authorizedotnet
|
||||||
{
|
{
|
||||||
// Not Implemented (R)
|
fn get_headers(
|
||||||
|
&self,
|
||||||
|
_req: &types::PaymentsRouterSyncData,
|
||||||
|
) -> CustomResult<Vec<(String, String)>, errors::ConnectorError> {
|
||||||
|
// This connector does not require an auth header, the authentication details are sent in the request body
|
||||||
|
Ok(vec![
|
||||||
|
(
|
||||||
|
headers::CONTENT_TYPE.to_string(),
|
||||||
|
PSync::get_content_type(self).to_string(),
|
||||||
|
),
|
||||||
|
(headers::X_ROUTER.to_string(), "test".to_string()),
|
||||||
|
])
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_content_type(&self) -> &'static str {
|
||||||
|
"application/json"
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_url(
|
||||||
|
&self,
|
||||||
|
_req: &types::PaymentsRouterSyncData,
|
||||||
|
connectors: Connectors,
|
||||||
|
) -> CustomResult<String, errors::ConnectorError> {
|
||||||
|
Ok(self.base_url(connectors))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_request_body(
|
||||||
|
&self,
|
||||||
|
req: &types::PaymentsRouterSyncData,
|
||||||
|
) -> CustomResult<Option<String>, errors::ConnectorError> {
|
||||||
|
let sync_request =
|
||||||
|
utils::Encode::<authorizedotnet::AuthorizedotnetCreateSyncRequest>::convert_and_encode(
|
||||||
|
req,
|
||||||
|
)
|
||||||
|
.change_context(errors::ConnectorError::RequestEncodingFailed)?;
|
||||||
|
Ok(Some(sync_request))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn build_request(
|
||||||
|
&self,
|
||||||
|
req: &types::PaymentsRouterSyncData,
|
||||||
|
connectors: Connectors,
|
||||||
|
) -> CustomResult<Option<services::Request>, errors::ConnectorError> {
|
||||||
|
let request = services::RequestBuilder::new()
|
||||||
|
.method(services::Method::Post)
|
||||||
|
.url(&PSync::get_url(self, req, connectors)?)
|
||||||
|
.headers(PSync::get_headers(self, req)?)
|
||||||
|
.body(PSync::get_request_body(self, req)?)
|
||||||
|
.build();
|
||||||
|
Ok(Some(request))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn handle_response(
|
||||||
|
&self,
|
||||||
|
data: &types::PaymentsRouterSyncData,
|
||||||
|
res: Response,
|
||||||
|
) -> CustomResult<types::PaymentsRouterSyncData, errors::ConnectorError> {
|
||||||
|
use bytes::Buf;
|
||||||
|
|
||||||
|
// Handle the case where response bytes contains U+FEFF (BOM) character sent by connector
|
||||||
|
let encoding = encoding_rs::UTF_8;
|
||||||
|
let intermediate_response = encoding.decode_with_bom_removal(res.response.chunk());
|
||||||
|
let intermediate_response =
|
||||||
|
bytes::Bytes::copy_from_slice(intermediate_response.0.as_bytes());
|
||||||
|
|
||||||
|
let response: authorizedotnet::AuthorizedotnetSyncResponse = intermediate_response
|
||||||
|
.parse_struct("AuthorizedotnetSyncResponse")
|
||||||
|
.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)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_error_response(
|
||||||
|
&self,
|
||||||
|
res: Bytes,
|
||||||
|
) -> CustomResult<ErrorResponse, errors::ConnectorError> {
|
||||||
|
let response: authorizedotnet::AuthorizedotnetPaymentsResponse = res
|
||||||
|
.parse_struct("AuthorizedotnetPaymentsResponse")
|
||||||
|
.change_context(errors::ConnectorError::ResponseDeserializationFailed)?;
|
||||||
|
|
||||||
|
let error = response
|
||||||
|
.transaction_response
|
||||||
|
.errors
|
||||||
|
.and_then(|errors| {
|
||||||
|
errors.first().map(|error| types::ErrorResponse {
|
||||||
|
code: error.error_code.clone(),
|
||||||
|
message: error.error_text.clone(),
|
||||||
|
reason: None,
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.unwrap_or_else(|| types::ErrorResponse {
|
||||||
|
code: consts::NO_ERROR_CODE.to_string(),
|
||||||
|
message: consts::NO_ERROR_MESSAGE.to_string(),
|
||||||
|
reason: None,
|
||||||
|
});
|
||||||
|
|
||||||
|
Ok(error)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
type Authorize = dyn services::ConnectorIntegration<
|
type Authorize = dyn services::ConnectorIntegration<
|
||||||
@ -487,7 +588,9 @@ impl
|
|||||||
req: &types::RefundsRouterData<api::RSync>,
|
req: &types::RefundsRouterData<api::RSync>,
|
||||||
) -> CustomResult<Option<String>, errors::ConnectorError> {
|
) -> CustomResult<Option<String>, errors::ConnectorError> {
|
||||||
let sync_request =
|
let sync_request =
|
||||||
utils::Encode::<authorizedotnet::CreateSyncRequest>::convert_and_encode(req)
|
utils::Encode::<authorizedotnet::AuthorizedotnetCreateSyncRequest>::convert_and_encode(
|
||||||
|
req,
|
||||||
|
)
|
||||||
.change_context(errors::ConnectorError::RequestEncodingFailed)?;
|
.change_context(errors::ConnectorError::RequestEncodingFailed)?;
|
||||||
Ok(Some(sync_request))
|
Ok(Some(sync_request))
|
||||||
}
|
}
|
||||||
@ -519,8 +622,8 @@ impl
|
|||||||
let intermediate_response =
|
let intermediate_response =
|
||||||
bytes::Bytes::copy_from_slice(intermediate_response.0.as_bytes());
|
bytes::Bytes::copy_from_slice(intermediate_response.0.as_bytes());
|
||||||
|
|
||||||
let response: authorizedotnet::SyncResponse = intermediate_response
|
let response: authorizedotnet::AuthorizedotnetSyncResponse = intermediate_response
|
||||||
.parse_struct("SyncResponse")
|
.parse_struct("AuthorizedotnetSyncResponse")
|
||||||
.change_context(errors::ConnectorError::ResponseDeserializationFailed)?;
|
.change_context(errors::ConnectorError::ResponseDeserializationFailed)?;
|
||||||
|
|
||||||
types::RouterData::try_from(types::ResponseRouterData {
|
types::RouterData::try_from(types::ResponseRouterData {
|
||||||
|
|||||||
@ -427,11 +427,11 @@ pub struct TransactionDetails {
|
|||||||
}
|
}
|
||||||
#[derive(Serialize)]
|
#[derive(Serialize)]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
pub struct CreateSyncRequest {
|
pub struct AuthorizedotnetCreateSyncRequest {
|
||||||
get_transaction_details_request: TransactionDetails,
|
get_transaction_details_request: TransactionDetails,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<F> TryFrom<&types::RefundsRouterData<F>> for CreateSyncRequest {
|
impl<F> TryFrom<&types::RefundsRouterData<F>> for AuthorizedotnetCreateSyncRequest {
|
||||||
type Error = error_stack::Report<errors::ConnectorError>;
|
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> {
|
||||||
@ -441,7 +441,27 @@ impl<F> TryFrom<&types::RefundsRouterData<F>> for CreateSyncRequest {
|
|||||||
.map(|refund_response_data| refund_response_data.connector_refund_id.clone());
|
.map(|refund_response_data| refund_response_data.connector_refund_id.clone());
|
||||||
let merchant_authentication = MerchantAuthentication::try_from(&item.connector_auth_type)?;
|
let merchant_authentication = MerchantAuthentication::try_from(&item.connector_auth_type)?;
|
||||||
|
|
||||||
let payload = CreateSyncRequest {
|
let payload = AuthorizedotnetCreateSyncRequest {
|
||||||
|
get_transaction_details_request: TransactionDetails {
|
||||||
|
merchant_authentication,
|
||||||
|
transaction_id,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
Ok(payload)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TryFrom<&types::PaymentsRouterSyncData> for AuthorizedotnetCreateSyncRequest {
|
||||||
|
type Error = error_stack::Report<errors::ConnectorError>;
|
||||||
|
|
||||||
|
fn try_from(item: &types::PaymentsRouterSyncData) -> Result<Self, Self::Error> {
|
||||||
|
let transaction_id = item
|
||||||
|
.response
|
||||||
|
.as_ref()
|
||||||
|
.map(|payment_response_data| payment_response_data.connector_transaction_id.clone());
|
||||||
|
let merchant_authentication = MerchantAuthentication::try_from(&item.connector_auth_type)?;
|
||||||
|
|
||||||
|
let payload = AuthorizedotnetCreateSyncRequest {
|
||||||
get_transaction_details_request: TransactionDetails {
|
get_transaction_details_request: TransactionDetails {
|
||||||
merchant_authentication,
|
merchant_authentication,
|
||||||
transaction_id,
|
transaction_id,
|
||||||
@ -456,6 +476,13 @@ impl<F> TryFrom<&types::RefundsRouterData<F>> for CreateSyncRequest {
|
|||||||
pub enum SyncStatus {
|
pub enum SyncStatus {
|
||||||
RefundSettledSuccessfully,
|
RefundSettledSuccessfully,
|
||||||
RefundPendingSettlement,
|
RefundPendingSettlement,
|
||||||
|
AuthorizedPendingCapture,
|
||||||
|
CapturedPendingSettlement,
|
||||||
|
SettledSuccessfully,
|
||||||
|
Declined,
|
||||||
|
Voided,
|
||||||
|
CouldNotVoid,
|
||||||
|
GeneralError,
|
||||||
}
|
}
|
||||||
#[derive(Debug, Deserialize)]
|
#[derive(Debug, Deserialize)]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
@ -466,7 +493,7 @@ pub struct SyncTransactionResponse {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Deserialize)]
|
#[derive(Debug, Deserialize)]
|
||||||
pub struct SyncResponse {
|
pub struct AuthorizedotnetSyncResponse {
|
||||||
transaction: SyncTransactionResponse,
|
transaction: SyncTransactionResponse,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -475,17 +502,33 @@ impl From<SyncStatus> for enums::RefundStatus {
|
|||||||
match transaction_status {
|
match transaction_status {
|
||||||
SyncStatus::RefundSettledSuccessfully => enums::RefundStatus::Success,
|
SyncStatus::RefundSettledSuccessfully => enums::RefundStatus::Success,
|
||||||
SyncStatus::RefundPendingSettlement => enums::RefundStatus::Pending,
|
SyncStatus::RefundPendingSettlement => enums::RefundStatus::Pending,
|
||||||
|
_ => enums::RefundStatus::Failure,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TryFrom<types::RefundsResponseRouterData<api::RSync, SyncResponse>>
|
impl From<SyncStatus> for enums::AttemptStatus {
|
||||||
|
fn from(transaction_status: SyncStatus) -> Self {
|
||||||
|
match transaction_status {
|
||||||
|
SyncStatus::SettledSuccessfully | SyncStatus::CapturedPendingSettlement => {
|
||||||
|
enums::AttemptStatus::Charged
|
||||||
|
}
|
||||||
|
SyncStatus::Declined => enums::AttemptStatus::AuthenticationFailed,
|
||||||
|
SyncStatus::Voided => enums::AttemptStatus::Voided,
|
||||||
|
SyncStatus::CouldNotVoid => enums::AttemptStatus::VoidFailed,
|
||||||
|
SyncStatus::GeneralError => enums::AttemptStatus::Failure,
|
||||||
|
_ => enums::AttemptStatus::Pending,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TryFrom<types::RefundsResponseRouterData<api::RSync, AuthorizedotnetSyncResponse>>
|
||||||
for types::RefundsRouterData<api::RSync>
|
for types::RefundsRouterData<api::RSync>
|
||||||
{
|
{
|
||||||
type Error = error_stack::Report<errors::ConnectorError>;
|
type Error = error_stack::Report<errors::ConnectorError>;
|
||||||
|
|
||||||
fn try_from(
|
fn try_from(
|
||||||
item: types::RefundsResponseRouterData<api::RSync, SyncResponse>,
|
item: types::RefundsResponseRouterData<api::RSync, AuthorizedotnetSyncResponse>,
|
||||||
) -> Result<Self, Self::Error> {
|
) -> Result<Self, Self::Error> {
|
||||||
let refund_status = enums::RefundStatus::from(item.response.transaction.transaction_status);
|
let refund_status = enums::RefundStatus::from(item.response.transaction.transaction_status);
|
||||||
Ok(types::RouterData {
|
Ok(types::RouterData {
|
||||||
@ -498,6 +541,35 @@ impl TryFrom<types::RefundsResponseRouterData<api::RSync, SyncResponse>>
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<F, Req>
|
||||||
|
TryFrom<
|
||||||
|
types::ResponseRouterData<F, AuthorizedotnetSyncResponse, Req, types::PaymentsResponseData>,
|
||||||
|
> for types::RouterData<F, Req, types::PaymentsResponseData>
|
||||||
|
{
|
||||||
|
type Error = error_stack::Report<errors::ConnectorError>;
|
||||||
|
|
||||||
|
fn try_from(
|
||||||
|
item: types::ResponseRouterData<
|
||||||
|
F,
|
||||||
|
AuthorizedotnetSyncResponse,
|
||||||
|
Req,
|
||||||
|
types::PaymentsResponseData,
|
||||||
|
>,
|
||||||
|
) -> Result<Self, Self::Error> {
|
||||||
|
let payment_status =
|
||||||
|
enums::AttemptStatus::from(item.response.transaction.transaction_status);
|
||||||
|
Ok(types::RouterData {
|
||||||
|
response: Some(types::PaymentsResponseData {
|
||||||
|
connector_transaction_id: item.response.transaction.transaction_id,
|
||||||
|
redirection_data: None,
|
||||||
|
redirect: false,
|
||||||
|
}),
|
||||||
|
status: payment_status,
|
||||||
|
..item.data
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Default, Eq, PartialEq, Deserialize)]
|
#[derive(Debug, Default, Eq, PartialEq, Deserialize)]
|
||||||
pub struct ErrorDetails {
|
pub struct ErrorDetails {
|
||||||
pub code: Option<String>,
|
pub code: Option<String>,
|
||||||
|
|||||||
Reference in New Issue
Block a user