mirror of
https://github.com/juspay/hyperswitch.git
synced 2025-10-29 00:49:42 +08:00
feat(connector): [Noon] Add Card Payments, Capture, Void and Refund (#1207)
This commit is contained in:
@ -616,7 +616,7 @@ pub enum Connector {
|
||||
Multisafepay,
|
||||
Nexinets,
|
||||
Nmi,
|
||||
// Noon, added as template code for future usage
|
||||
Noon,
|
||||
Nuvei,
|
||||
// Payeezy, As psync and rsync are not supported by this connector, it is added as template code for future usage
|
||||
Paypal,
|
||||
@ -698,7 +698,7 @@ pub enum RoutableConnectors {
|
||||
Multisafepay,
|
||||
Nexinets,
|
||||
Nmi,
|
||||
// Noon, added as template code for future usage
|
||||
Noon,
|
||||
Nuvei,
|
||||
Opennode,
|
||||
// Payeezy, As psync and rsync are not supported by this connector, it is added as template code for future usage
|
||||
|
||||
@ -2,12 +2,18 @@ mod transformers;
|
||||
|
||||
use std::fmt::Debug;
|
||||
|
||||
use base64::Engine;
|
||||
use error_stack::{IntoReport, ResultExt};
|
||||
use transformers as noon;
|
||||
|
||||
use super::utils::PaymentsSyncRequestData;
|
||||
use crate::{
|
||||
configs::settings,
|
||||
core::errors::{self, CustomResult},
|
||||
consts,
|
||||
core::{
|
||||
errors::{self, CustomResult},
|
||||
payments,
|
||||
},
|
||||
headers,
|
||||
services::{self, ConnectorIntegration},
|
||||
types::{
|
||||
@ -80,9 +86,16 @@ impl ConnectorCommon for Noon {
|
||||
&self,
|
||||
auth_type: &types::ConnectorAuthType,
|
||||
) -> CustomResult<Vec<(String, String)>, errors::ConnectorError> {
|
||||
let auth = noon::NoonAuthType::try_from(auth_type)
|
||||
.change_context(errors::ConnectorError::FailedToObtainAuthType)?;
|
||||
Ok(vec![(headers::AUTHORIZATION.to_string(), auth.api_key)])
|
||||
let auth = noon::NoonAuthType::try_from(auth_type)?;
|
||||
|
||||
let encoded_api_key = consts::BASE64_ENGINE.encode(format!(
|
||||
"{}.{}:{}",
|
||||
auth.business_identifier, auth.application_identifier, auth.api_key
|
||||
));
|
||||
Ok(vec![(
|
||||
headers::AUTHORIZATION.to_string(),
|
||||
format!("Key_Test {encoded_api_key}"),
|
||||
)])
|
||||
}
|
||||
|
||||
fn build_error_response(
|
||||
@ -96,9 +109,9 @@ impl ConnectorCommon for Noon {
|
||||
|
||||
Ok(ErrorResponse {
|
||||
status_code: res.status_code,
|
||||
code: response.code,
|
||||
code: response.result_code.to_string(),
|
||||
message: response.message,
|
||||
reason: response.reason,
|
||||
reason: Some(response.class_description),
|
||||
})
|
||||
}
|
||||
}
|
||||
@ -137,9 +150,9 @@ impl ConnectorIntegration<api::Authorize, types::PaymentsAuthorizeData, types::P
|
||||
fn get_url(
|
||||
&self,
|
||||
_req: &types::PaymentsAuthorizeRouterData,
|
||||
_connectors: &settings::Connectors,
|
||||
connectors: &settings::Connectors,
|
||||
) -> CustomResult<String, errors::ConnectorError> {
|
||||
Err(errors::ConnectorError::NotImplemented("get_url method".to_string()).into())
|
||||
Ok(format!("{}payment/v1/order", self.base_url(connectors)))
|
||||
}
|
||||
|
||||
fn get_request_body(
|
||||
@ -187,7 +200,6 @@ impl ConnectorIntegration<api::Authorize, types::PaymentsAuthorizeData, types::P
|
||||
data: data.clone(),
|
||||
http_code: res.status_code,
|
||||
})
|
||||
.change_context(errors::ConnectorError::ResponseHandlingFailed)
|
||||
}
|
||||
|
||||
fn get_error_response(
|
||||
@ -215,10 +227,14 @@ impl ConnectorIntegration<api::PSync, types::PaymentsSyncData, types::PaymentsRe
|
||||
|
||||
fn get_url(
|
||||
&self,
|
||||
_req: &types::PaymentsSyncRouterData,
|
||||
_connectors: &settings::Connectors,
|
||||
req: &types::PaymentsSyncRouterData,
|
||||
connectors: &settings::Connectors,
|
||||
) -> CustomResult<String, errors::ConnectorError> {
|
||||
Err(errors::ConnectorError::NotImplemented("get_url method".to_string()).into())
|
||||
let connector_transaction_id = req.request.get_connector_transaction_id()?;
|
||||
Ok(format!(
|
||||
"{}payment/v1/order/{connector_transaction_id}",
|
||||
self.base_url(connectors)
|
||||
))
|
||||
}
|
||||
|
||||
fn build_request(
|
||||
@ -250,7 +266,6 @@ impl ConnectorIntegration<api::PSync, types::PaymentsSyncData, types::PaymentsRe
|
||||
data: data.clone(),
|
||||
http_code: res.status_code,
|
||||
})
|
||||
.change_context(errors::ConnectorError::ResponseHandlingFailed)
|
||||
}
|
||||
|
||||
fn get_error_response(
|
||||
@ -279,16 +294,20 @@ impl ConnectorIntegration<api::Capture, types::PaymentsCaptureData, types::Payme
|
||||
fn get_url(
|
||||
&self,
|
||||
_req: &types::PaymentsCaptureRouterData,
|
||||
_connectors: &settings::Connectors,
|
||||
connectors: &settings::Connectors,
|
||||
) -> CustomResult<String, errors::ConnectorError> {
|
||||
Err(errors::ConnectorError::NotImplemented("get_url method".to_string()).into())
|
||||
Ok(format!("{}payment/v1/order", self.base_url(connectors)))
|
||||
}
|
||||
|
||||
fn get_request_body(
|
||||
&self,
|
||||
_req: &types::PaymentsCaptureRouterData,
|
||||
req: &types::PaymentsCaptureRouterData,
|
||||
) -> CustomResult<Option<String>, errors::ConnectorError> {
|
||||
Err(errors::ConnectorError::NotImplemented("get_request_body method".to_string()).into())
|
||||
let req_obj = noon::NoonPaymentsActionRequest::try_from(req)?;
|
||||
let noon_req =
|
||||
utils::Encode::<noon::NoonPaymentsRequest>::encode_to_string_of_json(&req_obj)
|
||||
.change_context(errors::ConnectorError::RequestEncodingFailed)?;
|
||||
Ok(Some(noon_req))
|
||||
}
|
||||
|
||||
fn build_request(
|
||||
@ -323,7 +342,6 @@ impl ConnectorIntegration<api::Capture, types::PaymentsCaptureData, types::Payme
|
||||
data: data.clone(),
|
||||
http_code: res.status_code,
|
||||
})
|
||||
.change_context(errors::ConnectorError::ResponseHandlingFailed)
|
||||
}
|
||||
|
||||
fn get_error_response(
|
||||
@ -337,6 +355,75 @@ impl ConnectorIntegration<api::Capture, types::PaymentsCaptureData, types::Payme
|
||||
impl ConnectorIntegration<api::Void, types::PaymentsCancelData, types::PaymentsResponseData>
|
||||
for Noon
|
||||
{
|
||||
fn get_headers(
|
||||
&self,
|
||||
req: &types::PaymentsCancelRouterData,
|
||||
connectors: &settings::Connectors,
|
||||
) -> CustomResult<Vec<(String, 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!("{}payment/v1/order", self.base_url(connectors)))
|
||||
}
|
||||
fn get_request_body(
|
||||
&self,
|
||||
req: &types::PaymentsCancelRouterData,
|
||||
) -> CustomResult<Option<String>, errors::ConnectorError> {
|
||||
let connector_req = noon::NoonPaymentsCancelRequest::try_from(req)?;
|
||||
let noon_req = utils::Encode::<noon::NoonPaymentsCancelRequest>::encode_to_string_of_json(
|
||||
&connector_req,
|
||||
)
|
||||
.change_context(errors::ConnectorError::RequestEncodingFailed)?;
|
||||
Ok(Some(noon_req))
|
||||
}
|
||||
|
||||
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)?)
|
||||
.body(types::PaymentsVoidType::get_request_body(self, req)?)
|
||||
.build(),
|
||||
))
|
||||
}
|
||||
|
||||
fn handle_response(
|
||||
&self,
|
||||
data: &types::PaymentsCancelRouterData,
|
||||
res: Response,
|
||||
) -> CustomResult<types::PaymentsCancelRouterData, errors::ConnectorError> {
|
||||
let response: noon::NoonPaymentsResponse = res
|
||||
.response
|
||||
.parse_struct("Noon PaymentsCancelResponse")
|
||||
.change_context(errors::ConnectorError::ResponseDeserializationFailed)?;
|
||||
types::RouterData::try_from(types::ResponseRouterData {
|
||||
response,
|
||||
data: data.clone(),
|
||||
http_code: res.status_code,
|
||||
})
|
||||
}
|
||||
|
||||
fn get_error_response(
|
||||
&self,
|
||||
res: Response,
|
||||
) -> CustomResult<ErrorResponse, errors::ConnectorError> {
|
||||
self.build_error_response(res)
|
||||
}
|
||||
}
|
||||
|
||||
impl ConnectorIntegration<api::Execute, types::RefundsData, types::RefundsResponseData> for Noon {
|
||||
@ -355,18 +442,19 @@ impl ConnectorIntegration<api::Execute, types::RefundsData, types::RefundsRespon
|
||||
fn get_url(
|
||||
&self,
|
||||
_req: &types::RefundsRouterData<api::Execute>,
|
||||
_connectors: &settings::Connectors,
|
||||
connectors: &settings::Connectors,
|
||||
) -> CustomResult<String, errors::ConnectorError> {
|
||||
Err(errors::ConnectorError::NotImplemented("get_url method".to_string()).into())
|
||||
Ok(format!("{}payment/v1/order", self.base_url(connectors)))
|
||||
}
|
||||
|
||||
fn get_request_body(
|
||||
&self,
|
||||
req: &types::RefundsRouterData<api::Execute>,
|
||||
) -> CustomResult<Option<String>, errors::ConnectorError> {
|
||||
let req_obj = noon::NoonRefundRequest::try_from(req)?;
|
||||
let noon_req = utils::Encode::<noon::NoonRefundRequest>::encode_to_string_of_json(&req_obj)
|
||||
.change_context(errors::ConnectorError::RequestEncodingFailed)?;
|
||||
let req_obj = noon::NoonPaymentsActionRequest::try_from(req)?;
|
||||
let noon_req =
|
||||
utils::Encode::<noon::NoonPaymentsActionRequest>::encode_to_string_of_json(&req_obj)
|
||||
.change_context(errors::ConnectorError::RequestEncodingFailed)?;
|
||||
Ok(Some(noon_req))
|
||||
}
|
||||
|
||||
@ -401,7 +489,6 @@ impl ConnectorIntegration<api::Execute, types::RefundsData, types::RefundsRespon
|
||||
data: data.clone(),
|
||||
http_code: res.status_code,
|
||||
})
|
||||
.change_context(errors::ConnectorError::ResponseHandlingFailed)
|
||||
}
|
||||
|
||||
fn get_error_response(
|
||||
@ -427,10 +514,14 @@ impl ConnectorIntegration<api::RSync, types::RefundsData, types::RefundsResponse
|
||||
|
||||
fn get_url(
|
||||
&self,
|
||||
_req: &types::RefundSyncRouterData,
|
||||
_connectors: &settings::Connectors,
|
||||
req: &types::RefundSyncRouterData,
|
||||
connectors: &settings::Connectors,
|
||||
) -> CustomResult<String, errors::ConnectorError> {
|
||||
Err(errors::ConnectorError::NotImplemented("get_url method".to_string()).into())
|
||||
Ok(format!(
|
||||
"{}payment/v1/order/{}",
|
||||
self.base_url(connectors),
|
||||
req.request.connector_transaction_id
|
||||
))
|
||||
}
|
||||
|
||||
fn build_request(
|
||||
@ -454,10 +545,11 @@ impl ConnectorIntegration<api::RSync, types::RefundsData, types::RefundsResponse
|
||||
data: &types::RefundSyncRouterData,
|
||||
res: Response,
|
||||
) -> CustomResult<types::RefundSyncRouterData, errors::ConnectorError> {
|
||||
let response: noon::RefundResponse =
|
||||
res.response
|
||||
.parse_struct("noon RefundSyncResponse")
|
||||
.change_context(errors::ConnectorError::ResponseDeserializationFailed)?;
|
||||
let response: noon::RefundSyncResponse = res
|
||||
.response
|
||||
.parse_struct("noon RefundSyncResponse")
|
||||
.change_context(errors::ConnectorError::ResponseDeserializationFailed)?;
|
||||
|
||||
types::RouterData::try_from(types::ResponseRouterData {
|
||||
response,
|
||||
data: data.clone(),
|
||||
@ -474,6 +566,17 @@ impl ConnectorIntegration<api::RSync, types::RefundsData, types::RefundsResponse
|
||||
}
|
||||
}
|
||||
|
||||
impl services::ConnectorRedirectResponse for Noon {
|
||||
fn get_flow_type(
|
||||
&self,
|
||||
_query_params: &str,
|
||||
_json_payload: Option<serde_json::Value>,
|
||||
_action: services::PaymentAction,
|
||||
) -> CustomResult<payments::CallConnectorAction, errors::ConnectorError> {
|
||||
Ok(payments::CallConnectorAction::Trigger)
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait::async_trait]
|
||||
impl api::IncomingWebhook for Noon {
|
||||
fn get_webhook_object_reference_id(
|
||||
|
||||
@ -2,94 +2,192 @@ use masking::Secret;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::{
|
||||
connector::utils::PaymentsAuthorizeRequestData,
|
||||
connector::utils::{
|
||||
self as conn_utils, PaymentsAuthorizeRequestData, RefundsRequestData, RouterData,
|
||||
},
|
||||
core::errors,
|
||||
services,
|
||||
types::{self, api, storage::enums},
|
||||
};
|
||||
|
||||
//TODO: Fill the struct with respective fields
|
||||
#[derive(Default, Debug, Serialize, Eq, PartialEq)]
|
||||
pub struct NoonPaymentsRequest {
|
||||
amount: i64,
|
||||
card: NoonCard,
|
||||
#[derive(Debug, Serialize)]
|
||||
#[serde(rename_all = "UPPERCASE")]
|
||||
pub enum NoonChannels {
|
||||
Web,
|
||||
}
|
||||
|
||||
#[derive(Default, Debug, Serialize, Eq, PartialEq)]
|
||||
#[derive(Debug, Serialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct NoonOrder {
|
||||
amount: String,
|
||||
currency: storage_models::enums::Currency,
|
||||
channel: NoonChannels,
|
||||
category: String,
|
||||
//Short description of the order.
|
||||
name: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize)]
|
||||
#[serde(rename_all = "UPPERCASE")]
|
||||
pub enum NoonPaymentActions {
|
||||
Authorize,
|
||||
Sale,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct NoonConfiguration {
|
||||
payment_action: NoonPaymentActions,
|
||||
return_url: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct NoonCard {
|
||||
name: Secret<String>,
|
||||
number: cards::CardNumber,
|
||||
name_on_card: Secret<String>,
|
||||
number_plain: cards::CardNumber,
|
||||
expiry_month: Secret<String>,
|
||||
expiry_year: Secret<String>,
|
||||
cvc: Secret<String>,
|
||||
complete: bool,
|
||||
cvv: Secret<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize)]
|
||||
#[serde(tag = "type", content = "data")]
|
||||
pub enum NoonPaymentData {
|
||||
Card(NoonCard),
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize)]
|
||||
#[serde(rename_all = "UPPERCASE")]
|
||||
pub enum NoonApiOperations {
|
||||
Initiate,
|
||||
Capture,
|
||||
Reverse,
|
||||
Refund,
|
||||
}
|
||||
#[derive(Debug, Serialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct NoonPaymentsRequest {
|
||||
api_operation: NoonApiOperations,
|
||||
order: NoonOrder,
|
||||
configuration: NoonConfiguration,
|
||||
payment_data: NoonPaymentData,
|
||||
}
|
||||
|
||||
impl TryFrom<&types::PaymentsAuthorizeRouterData> for NoonPaymentsRequest {
|
||||
type Error = error_stack::Report<errors::ConnectorError>;
|
||||
fn try_from(item: &types::PaymentsAuthorizeRouterData) -> Result<Self, Self::Error> {
|
||||
match item.request.payment_method_data.clone() {
|
||||
api::PaymentMethodData::Card(req_card) => {
|
||||
let card = NoonCard {
|
||||
name: req_card.card_holder_name,
|
||||
number: req_card.card_number,
|
||||
expiry_month: req_card.card_exp_month,
|
||||
expiry_year: req_card.card_exp_year,
|
||||
cvc: req_card.card_cvc,
|
||||
complete: item.request.is_auto_capture()?,
|
||||
};
|
||||
Ok(Self {
|
||||
amount: item.request.amount,
|
||||
card,
|
||||
})
|
||||
}
|
||||
_ => Err(errors::ConnectorError::NotImplemented("Payment methods".to_string()).into()),
|
||||
}
|
||||
let payment_data = match item.request.payment_method_data.clone() {
|
||||
api::PaymentMethodData::Card(req_card) => Ok(NoonPaymentData::Card(NoonCard {
|
||||
name_on_card: req_card.card_holder_name,
|
||||
number_plain: req_card.card_number,
|
||||
expiry_month: req_card.card_exp_month,
|
||||
expiry_year: req_card.card_exp_year,
|
||||
cvv: req_card.card_cvc,
|
||||
})),
|
||||
_ => Err(errors::ConnectorError::NotImplemented(
|
||||
"Payment methods".to_string(),
|
||||
)),
|
||||
}?;
|
||||
|
||||
let order = NoonOrder {
|
||||
amount: conn_utils::to_currency_base_unit(item.request.amount, item.request.currency)?,
|
||||
currency: item.request.currency,
|
||||
channel: NoonChannels::Web,
|
||||
category: "pay".to_string(),
|
||||
name: item.get_description()?,
|
||||
};
|
||||
let payment_action = if item.request.is_auto_capture()? {
|
||||
NoonPaymentActions::Sale
|
||||
} else {
|
||||
NoonPaymentActions::Authorize
|
||||
};
|
||||
Ok(Self {
|
||||
api_operation: NoonApiOperations::Initiate,
|
||||
order,
|
||||
configuration: NoonConfiguration {
|
||||
payment_action,
|
||||
return_url: item.request.router_return_url.clone(),
|
||||
},
|
||||
payment_data,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
//TODO: Fill the struct with respective fields
|
||||
// Auth Struct
|
||||
pub struct NoonAuthType {
|
||||
pub(super) api_key: String,
|
||||
pub(super) application_identifier: String,
|
||||
pub(super) business_identifier: String,
|
||||
}
|
||||
|
||||
impl TryFrom<&types::ConnectorAuthType> for NoonAuthType {
|
||||
type Error = error_stack::Report<errors::ConnectorError>;
|
||||
fn try_from(auth_type: &types::ConnectorAuthType) -> Result<Self, Self::Error> {
|
||||
match auth_type {
|
||||
types::ConnectorAuthType::HeaderKey { api_key } => Ok(Self {
|
||||
types::ConnectorAuthType::SignatureKey {
|
||||
api_key,
|
||||
key1,
|
||||
api_secret,
|
||||
} => Ok(Self {
|
||||
api_key: api_key.to_string(),
|
||||
application_identifier: api_secret.to_string(),
|
||||
business_identifier: key1.to_string(),
|
||||
}),
|
||||
_ => Err(errors::ConnectorError::FailedToObtainAuthType.into()),
|
||||
}
|
||||
}
|
||||
}
|
||||
// PaymentsResponse
|
||||
//TODO: Append the remaining status flags
|
||||
#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq)]
|
||||
#[serde(rename_all = "lowercase")]
|
||||
#[derive(Default, Debug, Deserialize)]
|
||||
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
|
||||
pub enum NoonPaymentStatus {
|
||||
Succeeded,
|
||||
Authorized,
|
||||
Captured,
|
||||
PartiallyCaptured,
|
||||
Reversed,
|
||||
#[serde(rename = "3DS_ENROLL_INITIATED")]
|
||||
ThreeDsEnrollInitiated,
|
||||
Failed,
|
||||
#[default]
|
||||
Processing,
|
||||
Pending,
|
||||
}
|
||||
|
||||
impl From<NoonPaymentStatus> for enums::AttemptStatus {
|
||||
fn from(item: NoonPaymentStatus) -> Self {
|
||||
match item {
|
||||
NoonPaymentStatus::Succeeded => Self::Charged,
|
||||
NoonPaymentStatus::Authorized => Self::Authorized,
|
||||
NoonPaymentStatus::Captured | NoonPaymentStatus::PartiallyCaptured => Self::Charged,
|
||||
NoonPaymentStatus::Reversed => Self::Voided,
|
||||
NoonPaymentStatus::ThreeDsEnrollInitiated => Self::AuthenticationPending,
|
||||
NoonPaymentStatus::Failed => Self::Failure,
|
||||
NoonPaymentStatus::Processing => Self::Authorizing,
|
||||
NoonPaymentStatus::Pending => Self::Pending,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//TODO: Fill the struct with respective fields
|
||||
#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq)]
|
||||
pub struct NoonPaymentsResponse {
|
||||
#[derive(Default, Debug, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct NoonPaymentsOrderResponse {
|
||||
status: NoonPaymentStatus,
|
||||
id: String,
|
||||
id: u64,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct NoonCheckoutData {
|
||||
post_url: url::Url,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct NoonPaymentsResponseResult {
|
||||
order: NoonPaymentsOrderResponse,
|
||||
checkout_data: Option<NoonCheckoutData>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
pub struct NoonPaymentsResponse {
|
||||
result: NoonPaymentsResponseResult,
|
||||
}
|
||||
|
||||
impl<F, T>
|
||||
@ -100,11 +198,20 @@ impl<F, T>
|
||||
fn try_from(
|
||||
item: types::ResponseRouterData<F, NoonPaymentsResponse, T, types::PaymentsResponseData>,
|
||||
) -> Result<Self, Self::Error> {
|
||||
let redirection_data = item.response.result.checkout_data.map(|redirection_data| {
|
||||
services::RedirectForm::Form {
|
||||
endpoint: redirection_data.post_url.to_string(),
|
||||
method: services::Method::Post,
|
||||
form_fields: std::collections::HashMap::new(),
|
||||
}
|
||||
});
|
||||
Ok(Self {
|
||||
status: enums::AttemptStatus::from(item.response.status),
|
||||
status: enums::AttemptStatus::from(item.response.result.order.status),
|
||||
response: Ok(types::PaymentsResponseData::TransactionResponse {
|
||||
resource_id: types::ResponseId::ConnectorTransactionId(item.response.id),
|
||||
redirection_data: None,
|
||||
resource_id: types::ResponseId::ConnectorTransactionId(
|
||||
item.response.result.order.id.to_string(),
|
||||
),
|
||||
redirection_data,
|
||||
mandate_reference: None,
|
||||
connector_metadata: None,
|
||||
network_txn_id: None,
|
||||
@ -114,52 +221,125 @@ impl<F, T>
|
||||
}
|
||||
}
|
||||
|
||||
//TODO: Fill the struct with respective fields
|
||||
// REFUND :
|
||||
// Type definition for RefundRequest
|
||||
#[derive(Default, Debug, Serialize)]
|
||||
pub struct NoonRefundRequest {
|
||||
pub amount: i64,
|
||||
#[derive(Debug, Serialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct NoonActionTransaction {
|
||||
amount: String,
|
||||
currency: storage_models::enums::Currency,
|
||||
}
|
||||
|
||||
impl<F> TryFrom<&types::RefundsRouterData<F>> for NoonRefundRequest {
|
||||
#[derive(Debug, Serialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct NoonActionOrder {
|
||||
id: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct NoonPaymentsActionRequest {
|
||||
api_operation: NoonApiOperations,
|
||||
order: NoonActionOrder,
|
||||
transaction: NoonActionTransaction,
|
||||
}
|
||||
|
||||
impl TryFrom<&types::PaymentsCaptureRouterData> for NoonPaymentsActionRequest {
|
||||
type Error = error_stack::Report<errors::ConnectorError>;
|
||||
fn try_from(item: &types::RefundsRouterData<F>) -> Result<Self, Self::Error> {
|
||||
fn try_from(item: &types::PaymentsCaptureRouterData) -> Result<Self, Self::Error> {
|
||||
let order = NoonActionOrder {
|
||||
id: item.request.connector_transaction_id.clone(),
|
||||
};
|
||||
let transaction = NoonActionTransaction {
|
||||
amount: conn_utils::to_currency_base_unit(
|
||||
item.request.amount_to_capture,
|
||||
item.request.currency,
|
||||
)?,
|
||||
currency: item.request.currency,
|
||||
};
|
||||
Ok(Self {
|
||||
amount: item.request.amount,
|
||||
api_operation: NoonApiOperations::Capture,
|
||||
order,
|
||||
transaction,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// Type definition for Refund Response
|
||||
#[derive(Debug, Serialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct NoonPaymentsCancelRequest {
|
||||
api_operation: NoonApiOperations,
|
||||
order: NoonActionOrder,
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
#[derive(Debug, Serialize, Default, Deserialize, Clone)]
|
||||
impl TryFrom<&types::PaymentsCancelRouterData> for NoonPaymentsCancelRequest {
|
||||
type Error = error_stack::Report<errors::ConnectorError>;
|
||||
fn try_from(item: &types::PaymentsCancelRouterData) -> Result<Self, Self::Error> {
|
||||
let order = NoonActionOrder {
|
||||
id: item.request.connector_transaction_id.clone(),
|
||||
};
|
||||
Ok(Self {
|
||||
api_operation: NoonApiOperations::Reverse,
|
||||
order,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<F> TryFrom<&types::RefundsRouterData<F>> for NoonPaymentsActionRequest {
|
||||
type Error = error_stack::Report<errors::ConnectorError>;
|
||||
fn try_from(item: &types::RefundsRouterData<F>) -> Result<Self, Self::Error> {
|
||||
let order = NoonActionOrder {
|
||||
id: item.request.connector_transaction_id.clone(),
|
||||
};
|
||||
let transaction = NoonActionTransaction {
|
||||
amount: conn_utils::to_currency_base_unit(
|
||||
item.request.refund_amount,
|
||||
item.request.currency,
|
||||
)?,
|
||||
currency: item.request.currency,
|
||||
};
|
||||
Ok(Self {
|
||||
api_operation: NoonApiOperations::Refund,
|
||||
order,
|
||||
transaction,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, Deserialize, Clone)]
|
||||
#[serde(rename_all = "UPPERCASE")]
|
||||
pub enum RefundStatus {
|
||||
Succeeded,
|
||||
Success,
|
||||
Failed,
|
||||
#[default]
|
||||
Processing,
|
||||
Pending,
|
||||
}
|
||||
|
||||
impl From<RefundStatus> for enums::RefundStatus {
|
||||
fn from(item: RefundStatus) -> Self {
|
||||
match item {
|
||||
RefundStatus::Succeeded => Self::Success,
|
||||
RefundStatus::Success => Self::Success,
|
||||
RefundStatus::Failed => Self::Failure,
|
||||
RefundStatus::Processing => Self::Pending,
|
||||
//TODO: Review mapping
|
||||
RefundStatus::Pending => Self::Pending,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//TODO: Fill the struct with respective fields
|
||||
#[derive(Default, Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct RefundResponse {
|
||||
#[derive(Default, Debug, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct NoonPaymentsTransactionResponse {
|
||||
id: String,
|
||||
status: RefundStatus,
|
||||
}
|
||||
|
||||
#[derive(Default, Debug, Deserialize)]
|
||||
pub struct NoonRefundResponseResult {
|
||||
transaction: NoonPaymentsTransactionResponse,
|
||||
}
|
||||
|
||||
#[derive(Default, Debug, Deserialize)]
|
||||
pub struct RefundResponse {
|
||||
result: NoonRefundResponseResult,
|
||||
}
|
||||
|
||||
impl TryFrom<types::RefundsResponseRouterData<api::Execute, RefundResponse>>
|
||||
for types::RefundsRouterData<api::Execute>
|
||||
{
|
||||
@ -169,36 +349,60 @@ impl TryFrom<types::RefundsResponseRouterData<api::Execute, RefundResponse>>
|
||||
) -> Result<Self, Self::Error> {
|
||||
Ok(Self {
|
||||
response: Ok(types::RefundsResponseData {
|
||||
connector_refund_id: item.response.id.to_string(),
|
||||
refund_status: enums::RefundStatus::from(item.response.status),
|
||||
connector_refund_id: item.response.result.transaction.id,
|
||||
refund_status: enums::RefundStatus::from(item.response.result.transaction.status),
|
||||
}),
|
||||
..item.data
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<types::RefundsResponseRouterData<api::RSync, RefundResponse>>
|
||||
#[derive(Default, Debug, Deserialize)]
|
||||
pub struct NoonRefundResponseTransactions {
|
||||
id: String,
|
||||
status: RefundStatus,
|
||||
}
|
||||
|
||||
#[derive(Default, Debug, Deserialize)]
|
||||
pub struct NoonRefundSyncResponseResult {
|
||||
transactions: Vec<NoonRefundResponseTransactions>,
|
||||
}
|
||||
|
||||
#[derive(Default, Debug, Deserialize)]
|
||||
pub struct RefundSyncResponse {
|
||||
result: NoonRefundSyncResponseResult,
|
||||
}
|
||||
|
||||
impl TryFrom<types::RefundsResponseRouterData<api::RSync, RefundSyncResponse>>
|
||||
for types::RefundsRouterData<api::RSync>
|
||||
{
|
||||
type Error = error_stack::Report<errors::ConnectorError>;
|
||||
fn try_from(
|
||||
item: types::RefundsResponseRouterData<api::RSync, RefundResponse>,
|
||||
item: types::RefundsResponseRouterData<api::RSync, RefundSyncResponse>,
|
||||
) -> Result<Self, Self::Error> {
|
||||
let connector_refund_id = item.data.request.get_connector_refund_id()?;
|
||||
let noon_transaction: &NoonRefundResponseTransactions = item
|
||||
.response
|
||||
.result
|
||||
.transactions
|
||||
.iter()
|
||||
.find(|transaction| transaction.id == connector_refund_id)
|
||||
.ok_or(errors::ConnectorError::ResponseHandlingFailed)?;
|
||||
|
||||
Ok(Self {
|
||||
response: Ok(types::RefundsResponseData {
|
||||
connector_refund_id: item.response.id.to_string(),
|
||||
refund_status: enums::RefundStatus::from(item.response.status),
|
||||
connector_refund_id: noon_transaction.id.to_owned(),
|
||||
refund_status: enums::RefundStatus::from(noon_transaction.status.to_owned()),
|
||||
}),
|
||||
..item.data
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
//TODO: Fill the struct with respective fields
|
||||
#[derive(Default, Debug, Serialize, Deserialize, PartialEq)]
|
||||
#[derive(Debug, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct NoonErrorResponse {
|
||||
pub status_code: u16,
|
||||
pub code: String,
|
||||
pub result_code: u32,
|
||||
pub message: String,
|
||||
pub reason: Option<String>,
|
||||
pub class_description: String,
|
||||
}
|
||||
|
||||
@ -261,7 +261,6 @@ default_imp_for_connector_redirect_response!(
|
||||
connector::Multisafepay,
|
||||
connector::Nexinets,
|
||||
connector::Nmi,
|
||||
connector::Noon,
|
||||
connector::Opennode,
|
||||
connector::Payeezy,
|
||||
connector::Payu,
|
||||
|
||||
@ -226,7 +226,7 @@ impl ConnectorData {
|
||||
enums::Connector::Klarna => Ok(Box::new(&connector::Klarna)),
|
||||
enums::Connector::Mollie => Ok(Box::new(&connector::Mollie)),
|
||||
enums::Connector::Nmi => Ok(Box::new(&connector::Nmi)),
|
||||
// "noon" => Ok(Box::new(&connector::Noon)), added as template code for future usage
|
||||
enums::Connector::Noon => Ok(Box::new(&connector::Noon)),
|
||||
enums::Connector::Nuvei => Ok(Box::new(&connector::Nuvei)),
|
||||
enums::Connector::Opennode => Ok(Box::new(&connector::Opennode)),
|
||||
// "payeezy" => Ok(Box::new(&connector::Payeezy)), As psync and rsync are not supported by this connector, it is added as template code for future usage
|
||||
|
||||
@ -25,7 +25,7 @@ pub(crate) struct ConnectorAuthentication {
|
||||
pub mollie: Option<HeaderKey>,
|
||||
pub multisafepay: Option<HeaderKey>,
|
||||
pub nexinets: Option<HeaderKey>,
|
||||
pub noon: Option<HeaderKey>,
|
||||
pub noon: Option<SignatureKey>,
|
||||
pub nmi: Option<HeaderKey>,
|
||||
pub nuvei: Option<SignatureKey>,
|
||||
pub opennode: Option<HeaderKey>,
|
||||
|
||||
@ -1,7 +1,10 @@
|
||||
use std::str::FromStr;
|
||||
|
||||
use masking::Secret;
|
||||
use router::types::{self, api, storage::enums};
|
||||
use router::types::{
|
||||
self, api,
|
||||
storage::{self, enums},
|
||||
};
|
||||
|
||||
use crate::{
|
||||
connector_auth,
|
||||
@ -16,7 +19,7 @@ impl utils::Connector for NoonTest {
|
||||
use router::connector::Noon;
|
||||
types::api::ConnectorData {
|
||||
connector: Box::new(&Noon),
|
||||
connector_name: types::Connector::DummyConnector1,
|
||||
connector_name: types::Connector::Noon,
|
||||
get_token: types::api::GetToken::Connector,
|
||||
}
|
||||
}
|
||||
@ -41,7 +44,10 @@ fn get_default_payment_info() -> Option<utils::PaymentInfo> {
|
||||
}
|
||||
|
||||
fn payment_method_details() -> Option<types::PaymentsAuthorizeData> {
|
||||
None
|
||||
Some(types::PaymentsAuthorizeData {
|
||||
currency: storage::enums::Currency::AED,
|
||||
..utils::PaymentAuthorizeType::default().0
|
||||
})
|
||||
}
|
||||
|
||||
// Cards Positive Tests
|
||||
|
||||
@ -112,4 +112,6 @@ api_secret = "secrect"
|
||||
api_key = "API Key"
|
||||
|
||||
[noon]
|
||||
api_key = "API Key"
|
||||
api_key = "Application API KEY"
|
||||
api_secret = "Application Identifier"
|
||||
key1 = "Business Identifier"
|
||||
|
||||
Reference in New Issue
Block a user