mirror of
https://github.com/juspay/hyperswitch.git
synced 2025-11-01 11:06:50 +08:00
feat(connector): [HELCIM] Implement Cards for Helcim (#2210)
This commit is contained in:
@ -9,7 +9,8 @@ use serde::{Deserialize, Serialize};
|
||||
use crate::{
|
||||
connector::utils::{
|
||||
self, AddressDetailsData, BankDirectDebitBillingData, BrowserInformationData,
|
||||
ConnectorCustomerData, PaymentsAuthorizeRequestData, RouterData, SetupMandateRequestData,
|
||||
ConnectorCustomerData, PaymentsAuthorizeRequestData, PaymentsSetupMandateRequestData,
|
||||
RouterData,
|
||||
},
|
||||
core::errors,
|
||||
types::{
|
||||
|
||||
@ -2,12 +2,15 @@ pub mod transformers;
|
||||
|
||||
use std::fmt::Debug;
|
||||
|
||||
use diesel_models::enums;
|
||||
use error_stack::{IntoReport, ResultExt};
|
||||
use masking::ExposeInterface;
|
||||
use transformers as helcim;
|
||||
|
||||
use super::utils::{to_connector_meta, PaymentsAuthorizeRequestData};
|
||||
use crate::{
|
||||
configs::settings,
|
||||
consts::NO_ERROR_CODE,
|
||||
core::errors::{self, CustomResult},
|
||||
headers,
|
||||
services::{
|
||||
@ -39,6 +42,16 @@ impl api::RefundExecute for Helcim {}
|
||||
impl api::RefundSync for Helcim {}
|
||||
impl api::PaymentToken for Helcim {}
|
||||
|
||||
impl Helcim {
|
||||
pub fn connector_transaction_id(
|
||||
&self,
|
||||
connector_meta: &Option<serde_json::Value>,
|
||||
) -> CustomResult<Option<String>, errors::ConnectorError> {
|
||||
let meta: helcim::HelcimMetaData = to_connector_meta(connector_meta.clone())?;
|
||||
Ok(Some(meta.preauth_transaction_id.to_string()))
|
||||
}
|
||||
}
|
||||
|
||||
impl
|
||||
ConnectorIntegration<
|
||||
api::PaymentMethodToken,
|
||||
@ -60,12 +73,19 @@ where
|
||||
) -> 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(),
|
||||
self.get_content_type().to_string().into(),
|
||||
)];
|
||||
let mut api_key = self.get_auth_header(&req.connector_auth_type)?;
|
||||
|
||||
//Helcim requires an Idempotency Key of length 25. We prefix every ID by "HS_".
|
||||
const ID_LENGTH: usize = 22;
|
||||
let mut idempotency_key = vec![(
|
||||
headers::IDEMPOTENCY_KEY.to_string(),
|
||||
utils::generate_id(ID_LENGTH, "HS").into_masked(),
|
||||
)];
|
||||
|
||||
header.append(&mut api_key);
|
||||
header.append(&mut idempotency_key);
|
||||
Ok(header)
|
||||
}
|
||||
}
|
||||
@ -75,6 +95,10 @@ impl ConnectorCommon for Helcim {
|
||||
"helcim"
|
||||
}
|
||||
|
||||
fn get_currency_unit(&self) -> api::CurrencyUnit {
|
||||
api::CurrencyUnit::Base
|
||||
}
|
||||
|
||||
fn common_get_content_type(&self) -> &'static str {
|
||||
"application/json"
|
||||
}
|
||||
@ -90,7 +114,7 @@ impl ConnectorCommon for Helcim {
|
||||
let auth = helcim::HelcimAuthType::try_from(auth_type)
|
||||
.change_context(errors::ConnectorError::FailedToObtainAuthType)?;
|
||||
Ok(vec![(
|
||||
headers::AUTHORIZATION.to_string(),
|
||||
headers::API_TOKEN.to_string(),
|
||||
auth.api_key.expose().into_masked(),
|
||||
)])
|
||||
}
|
||||
@ -103,18 +127,33 @@ impl ConnectorCommon for Helcim {
|
||||
.response
|
||||
.parse_struct("HelcimErrorResponse")
|
||||
.change_context(errors::ConnectorError::ResponseDeserializationFailed)?;
|
||||
let error_string = match response.errors {
|
||||
transformers::HelcimErrorTypes::StringType(error) => error,
|
||||
transformers::HelcimErrorTypes::JsonType(error) => error.to_string(),
|
||||
};
|
||||
|
||||
Ok(ErrorResponse {
|
||||
status_code: res.status_code,
|
||||
code: response.code,
|
||||
message: response.message,
|
||||
reason: response.reason,
|
||||
code: NO_ERROR_CODE.to_owned(),
|
||||
message: error_string.clone(),
|
||||
reason: Some(error_string),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl ConnectorValidation for Helcim {
|
||||
//TODO: implement functions when support enabled
|
||||
fn validate_capture_method(
|
||||
&self,
|
||||
capture_method: Option<enums::CaptureMethod>,
|
||||
) -> 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_supported_error_report(capture_method, self.id()),
|
||||
),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ConnectorIntegration<api::Session, types::PaymentsSessionData, types::PaymentsResponseData>
|
||||
@ -135,6 +174,70 @@ impl
|
||||
types::PaymentsResponseData,
|
||||
> for Helcim
|
||||
{
|
||||
fn get_headers(
|
||||
&self,
|
||||
req: &types::SetupMandateRouterData,
|
||||
connectors: &settings::Connectors,
|
||||
) -> CustomResult<Vec<(String, request::Maskable<String>)>, errors::ConnectorError> {
|
||||
self.build_headers(req, connectors)
|
||||
}
|
||||
|
||||
fn get_url(
|
||||
&self,
|
||||
_req: &types::SetupMandateRouterData,
|
||||
connectors: &settings::Connectors,
|
||||
) -> CustomResult<String, errors::ConnectorError> {
|
||||
Ok(format!("{}v2/payment/verify", self.base_url(connectors)))
|
||||
}
|
||||
fn get_request_body(
|
||||
&self,
|
||||
req: &types::SetupMandateRouterData,
|
||||
) -> CustomResult<Option<types::RequestBody>, errors::ConnectorError> {
|
||||
let connector_req = helcim::HelcimVerifyRequest::try_from(req)?;
|
||||
|
||||
let helcim_req = types::RequestBody::log_and_get_request_body(
|
||||
&connector_req,
|
||||
utils::Encode::<helcim::HelcimVerifyRequest>::encode_to_string_of_json,
|
||||
)
|
||||
.change_context(errors::ConnectorError::RequestEncodingFailed)?;
|
||||
Ok(Some(helcim_req))
|
||||
}
|
||||
fn build_request(
|
||||
&self,
|
||||
req: &types::SetupMandateRouterData,
|
||||
connectors: &settings::Connectors,
|
||||
) -> CustomResult<Option<services::Request>, errors::ConnectorError> {
|
||||
Ok(Some(
|
||||
services::RequestBuilder::new()
|
||||
.method(services::Method::Post)
|
||||
.url(&types::SetupMandateType::get_url(self, req, connectors)?)
|
||||
.attach_default_headers()
|
||||
.headers(types::SetupMandateType::get_headers(self, req, connectors)?)
|
||||
.body(types::SetupMandateType::get_request_body(self, req)?)
|
||||
.build(),
|
||||
))
|
||||
}
|
||||
fn handle_response(
|
||||
&self,
|
||||
data: &types::SetupMandateRouterData,
|
||||
res: Response,
|
||||
) -> CustomResult<types::SetupMandateRouterData, errors::ConnectorError> {
|
||||
let response: helcim::HelcimPaymentsResponse = res
|
||||
.response
|
||||
.parse_struct("Helcim PaymentsAuthorizeResponse")
|
||||
.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::Authorize, types::PaymentsAuthorizeData, types::PaymentsResponseData>
|
||||
@ -154,17 +257,26 @@ impl ConnectorIntegration<api::Authorize, types::PaymentsAuthorizeData, types::P
|
||||
|
||||
fn get_url(
|
||||
&self,
|
||||
_req: &types::PaymentsAuthorizeRouterData,
|
||||
_connectors: &settings::Connectors,
|
||||
req: &types::PaymentsAuthorizeRouterData,
|
||||
connectors: &settings::Connectors,
|
||||
) -> CustomResult<String, errors::ConnectorError> {
|
||||
Err(errors::ConnectorError::NotImplemented("get_url method".to_string()).into())
|
||||
if req.request.is_auto_capture()? {
|
||||
return Ok(format!("{}v2/payment/purchase", self.base_url(connectors)));
|
||||
}
|
||||
Ok(format!("{}v2/payment/preauth", self.base_url(connectors)))
|
||||
}
|
||||
|
||||
fn get_request_body(
|
||||
&self,
|
||||
req: &types::PaymentsAuthorizeRouterData,
|
||||
) -> CustomResult<Option<types::RequestBody>, errors::ConnectorError> {
|
||||
let req_obj = helcim::HelcimPaymentsRequest::try_from(req)?;
|
||||
let connector_router_data = helcim::HelcimRouterData::try_from((
|
||||
&self.get_currency_unit(),
|
||||
req.request.currency,
|
||||
req.request.amount,
|
||||
req,
|
||||
))?;
|
||||
let req_obj = helcim::HelcimPaymentsRequest::try_from(&connector_router_data)?;
|
||||
let helcim_req = types::RequestBody::log_and_get_request_body(
|
||||
&req_obj,
|
||||
utils::Encode::<helcim::HelcimPaymentsRequest>::encode_to_string_of_json,
|
||||
@ -223,9 +335,17 @@ impl ConnectorIntegration<api::PSync, types::PaymentsSyncData, types::PaymentsRe
|
||||
fn get_headers(
|
||||
&self,
|
||||
req: &types::PaymentsSyncRouterData,
|
||||
connectors: &settings::Connectors,
|
||||
_connectors: &settings::Connectors,
|
||||
) -> CustomResult<Vec<(String, request::Maskable<String>)>, errors::ConnectorError> {
|
||||
self.build_headers(req, connectors)
|
||||
let mut header = vec![(
|
||||
headers::CONTENT_TYPE.to_string(),
|
||||
types::PaymentsSyncType::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)
|
||||
}
|
||||
|
||||
fn get_content_type(&self) -> &'static str {
|
||||
@ -234,10 +354,19 @@ 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_payment_id = req
|
||||
.request
|
||||
.connector_transaction_id
|
||||
.get_connector_transaction_id()
|
||||
.change_context(errors::ConnectorError::MissingConnectorTransactionID)?;
|
||||
|
||||
Ok(format!(
|
||||
"{}v2/card-transactions/{connector_payment_id}",
|
||||
self.base_url(connectors)
|
||||
))
|
||||
}
|
||||
|
||||
fn build_request(
|
||||
@ -277,6 +406,12 @@ impl ConnectorIntegration<api::PSync, types::PaymentsSyncData, types::PaymentsRe
|
||||
) -> CustomResult<ErrorResponse, errors::ConnectorError> {
|
||||
self.build_error_response(res)
|
||||
}
|
||||
|
||||
// fn get_multiple_capture_sync_method(
|
||||
// &self,
|
||||
// ) -> CustomResult<services::CaptureSyncMethod, errors::ConnectorError> {
|
||||
// Ok(services::CaptureSyncMethod::Individual)
|
||||
// }
|
||||
}
|
||||
|
||||
impl ConnectorIntegration<api::Capture, types::PaymentsCaptureData, types::PaymentsResponseData>
|
||||
@ -297,16 +432,28 @@ 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!("{}v2/payment/capture", self.base_url(connectors)))
|
||||
}
|
||||
|
||||
fn get_request_body(
|
||||
&self,
|
||||
_req: &types::PaymentsCaptureRouterData,
|
||||
req: &types::PaymentsCaptureRouterData,
|
||||
) -> CustomResult<Option<types::RequestBody>, errors::ConnectorError> {
|
||||
Err(errors::ConnectorError::NotImplemented("get_request_body method".to_string()).into())
|
||||
let connector_router_data = helcim::HelcimRouterData::try_from((
|
||||
&self.get_currency_unit(),
|
||||
req.request.currency,
|
||||
req.request.amount_to_capture,
|
||||
req,
|
||||
))?;
|
||||
let connector_req = helcim::HelcimCaptureRequest::try_from(&connector_router_data)?;
|
||||
let helcim_req = types::RequestBody::log_and_get_request_body(
|
||||
&connector_req,
|
||||
utils::Encode::<helcim::HelcimCaptureRequest>::encode_to_string_of_json,
|
||||
)
|
||||
.change_context(errors::ConnectorError::RequestEncodingFailed)?;
|
||||
Ok(Some(helcim_req))
|
||||
}
|
||||
|
||||
fn build_request(
|
||||
@ -354,6 +501,78 @@ impl ConnectorIntegration<api::Capture, types::PaymentsCaptureData, types::Payme
|
||||
impl ConnectorIntegration<api::Void, types::PaymentsCancelData, types::PaymentsResponseData>
|
||||
for Helcim
|
||||
{
|
||||
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/payment/reverse", self.base_url(connectors)))
|
||||
}
|
||||
|
||||
fn get_request_body(
|
||||
&self,
|
||||
req: &types::PaymentsCancelRouterData,
|
||||
) -> CustomResult<Option<types::RequestBody>, errors::ConnectorError> {
|
||||
let req_obj = helcim::HelcimVoidRequest::try_from(req)?;
|
||||
let helcim_req = types::RequestBody::log_and_get_request_body(
|
||||
&req_obj,
|
||||
utils::Encode::<helcim::HelcimVoidRequest>::encode_to_string_of_json,
|
||||
)
|
||||
.change_context(errors::ConnectorError::RequestEncodingFailed)?;
|
||||
Ok(Some(helcim_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: helcim::HelcimPaymentsResponse = res
|
||||
.response
|
||||
.parse_struct("HelcimPaymentsResponse")
|
||||
.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: Response,
|
||||
) -> CustomResult<ErrorResponse, errors::ConnectorError> {
|
||||
self.build_error_response(res)
|
||||
}
|
||||
}
|
||||
|
||||
impl ConnectorIntegration<api::Execute, types::RefundsData, types::RefundsResponseData> for Helcim {
|
||||
@ -372,16 +591,22 @@ 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!("{}v2/payment/refund", self.base_url(connectors)))
|
||||
}
|
||||
|
||||
fn get_request_body(
|
||||
&self,
|
||||
req: &types::RefundsRouterData<api::Execute>,
|
||||
) -> CustomResult<Option<types::RequestBody>, errors::ConnectorError> {
|
||||
let req_obj = helcim::HelcimRefundRequest::try_from(req)?;
|
||||
let connector_router_data = helcim::HelcimRouterData::try_from((
|
||||
&self.get_currency_unit(),
|
||||
req.request.currency,
|
||||
req.request.refund_amount,
|
||||
req,
|
||||
))?;
|
||||
let req_obj = helcim::HelcimRefundRequest::try_from(&connector_router_data)?;
|
||||
let helcim_req = types::RequestBody::log_and_get_request_body(
|
||||
&req_obj,
|
||||
utils::Encode::<helcim::HelcimRefundRequest>::encode_to_string_of_json,
|
||||
@ -435,9 +660,17 @@ impl ConnectorIntegration<api::RSync, types::RefundsData, types::RefundsResponse
|
||||
fn get_headers(
|
||||
&self,
|
||||
req: &types::RefundSyncRouterData,
|
||||
connectors: &settings::Connectors,
|
||||
_connectors: &settings::Connectors,
|
||||
) -> CustomResult<Vec<(String, request::Maskable<String>)>, errors::ConnectorError> {
|
||||
self.build_headers(req, connectors)
|
||||
let mut header = vec![(
|
||||
headers::CONTENT_TYPE.to_string(),
|
||||
types::RefundSyncType::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)
|
||||
}
|
||||
|
||||
fn get_content_type(&self) -> &'static str {
|
||||
@ -446,10 +679,19 @@ 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())
|
||||
let connector_refund_id = req
|
||||
.request
|
||||
.connector_refund_id
|
||||
.clone()
|
||||
.ok_or(errors::ConnectorError::MissingConnectorRefundID)?;
|
||||
|
||||
Ok(format!(
|
||||
"{}v2/card-transactions/{connector_refund_id}",
|
||||
self.base_url(connectors)
|
||||
))
|
||||
}
|
||||
|
||||
fn build_request(
|
||||
|
||||
@ -1,53 +1,238 @@
|
||||
use common_utils::pii::{Email, IpAddress};
|
||||
use error_stack::{IntoReport, ResultExt};
|
||||
use masking::Secret;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::{
|
||||
connector::utils::PaymentsAuthorizeRequestData,
|
||||
connector::utils::{
|
||||
self, AddressDetailsData, BrowserInformationData, CardData, PaymentsAuthorizeRequestData,
|
||||
PaymentsCancelRequestData, PaymentsCaptureRequestData, PaymentsSetupMandateRequestData,
|
||||
RefundsRequestData, RouterData,
|
||||
},
|
||||
core::errors,
|
||||
types::{self, api, storage::enums},
|
||||
};
|
||||
|
||||
//TODO: Fill the struct with respective fields
|
||||
#[derive(Default, Debug, Serialize, Eq, PartialEq)]
|
||||
pub struct HelcimPaymentsRequest {
|
||||
amount: i64,
|
||||
card: HelcimCard,
|
||||
#[derive(Debug, Serialize)]
|
||||
pub struct HelcimRouterData<T> {
|
||||
pub amount: f64,
|
||||
pub router_data: T,
|
||||
}
|
||||
|
||||
#[derive(Default, Debug, Serialize, Eq, PartialEq)]
|
||||
pub struct HelcimCard {
|
||||
name: Secret<String>,
|
||||
number: cards::CardNumber,
|
||||
expiry_month: Secret<String>,
|
||||
expiry_year: Secret<String>,
|
||||
cvc: Secret<String>,
|
||||
complete: bool,
|
||||
}
|
||||
|
||||
impl TryFrom<&types::PaymentsAuthorizeRouterData> for HelcimPaymentsRequest {
|
||||
impl<T>
|
||||
TryFrom<(
|
||||
&types::api::CurrencyUnit,
|
||||
types::storage::enums::Currency,
|
||||
i64,
|
||||
T,
|
||||
)> for HelcimRouterData<T>
|
||||
{
|
||||
type Error = error_stack::Report<errors::ConnectorError>;
|
||||
fn try_from(item: &types::PaymentsAuthorizeRouterData) -> Result<Self, Self::Error> {
|
||||
fn try_from(
|
||||
(currency_unit, currency, amount, item): (
|
||||
&types::api::CurrencyUnit,
|
||||
types::storage::enums::Currency,
|
||||
i64,
|
||||
T,
|
||||
),
|
||||
) -> Result<Self, Self::Error> {
|
||||
let amount = utils::get_amount_as_f64(currency_unit, amount, currency)?;
|
||||
Ok(Self {
|
||||
amount,
|
||||
router_data: item,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct HelcimVerifyRequest {
|
||||
currency: enums::Currency,
|
||||
ip_address: Secret<String, IpAddress>,
|
||||
card_data: HelcimCard,
|
||||
billing_address: HelcimBillingAddress,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
ecommerce: Option<bool>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct HelcimPaymentsRequest {
|
||||
amount: f64,
|
||||
currency: enums::Currency,
|
||||
ip_address: Secret<String, IpAddress>,
|
||||
card_data: HelcimCard,
|
||||
billing_address: HelcimBillingAddress,
|
||||
//The ecommerce field is an optional field in Connector Helcim.
|
||||
//Setting the ecommerce field to true activates the Helcim Fraud Defender.
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
ecommerce: Option<bool>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct HelcimBillingAddress {
|
||||
name: Secret<String>,
|
||||
street1: Secret<String>,
|
||||
postal_code: Secret<String>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
street2: Option<Secret<String>>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
city: Option<String>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
email: Option<Email>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct HelcimCard {
|
||||
card_number: cards::CardNumber,
|
||||
card_expiry: Secret<String>,
|
||||
card_c_v_v: Secret<String>,
|
||||
}
|
||||
|
||||
impl TryFrom<(&types::SetupMandateRouterData, &api::Card)> for HelcimVerifyRequest {
|
||||
type Error = error_stack::Report<errors::ConnectorError>;
|
||||
fn try_from(value: (&types::SetupMandateRouterData, &api::Card)) -> Result<Self, Self::Error> {
|
||||
let (item, req_card) = value;
|
||||
let card_data = HelcimCard {
|
||||
card_expiry: req_card.get_card_expiry_month_year_2_digit_with_delimiter("".to_string()),
|
||||
card_number: req_card.card_number.clone(),
|
||||
card_c_v_v: req_card.card_cvc.clone(),
|
||||
};
|
||||
let req_address = item.get_billing_address()?.to_owned();
|
||||
|
||||
let billing_address = HelcimBillingAddress {
|
||||
name: req_address.get_full_name()?,
|
||||
street1: req_address.get_line1()?.to_owned(),
|
||||
postal_code: req_address.get_zip()?.to_owned(),
|
||||
street2: req_address.line2,
|
||||
city: req_address.city,
|
||||
email: item.request.email.clone(),
|
||||
};
|
||||
let ip_address = item.request.get_browser_info()?.get_ip_address()?;
|
||||
|
||||
Ok(Self {
|
||||
currency: item.request.currency,
|
||||
ip_address,
|
||||
card_data,
|
||||
billing_address,
|
||||
ecommerce: None,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<&types::SetupMandateRouterData> for HelcimVerifyRequest {
|
||||
type Error = error_stack::Report<errors::ConnectorError>;
|
||||
fn try_from(item: &types::SetupMandateRouterData) -> Result<Self, Self::Error> {
|
||||
match item.request.payment_method_data.clone() {
|
||||
api::PaymentMethodData::Card(req_card) => {
|
||||
let card = HelcimCard {
|
||||
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,
|
||||
})
|
||||
api::PaymentMethodData::Card(req_card) => Self::try_from((item, &req_card)),
|
||||
api_models::payments::PaymentMethodData::BankTransfer(_) => Err(
|
||||
errors::ConnectorError::NotImplemented("Payment Method".to_string()),
|
||||
)
|
||||
.into_report(),
|
||||
api_models::payments::PaymentMethodData::CardRedirect(_)
|
||||
| api_models::payments::PaymentMethodData::Wallet(_)
|
||||
| api_models::payments::PaymentMethodData::PayLater(_)
|
||||
| api_models::payments::PaymentMethodData::BankRedirect(_)
|
||||
| api_models::payments::PaymentMethodData::BankDebit(_)
|
||||
| api_models::payments::PaymentMethodData::Crypto(_)
|
||||
| api_models::payments::PaymentMethodData::MandatePayment
|
||||
| api_models::payments::PaymentMethodData::Reward
|
||||
| api_models::payments::PaymentMethodData::Upi(_)
|
||||
| api_models::payments::PaymentMethodData::Voucher(_)
|
||||
| api_models::payments::PaymentMethodData::GiftCard(_) => {
|
||||
Err(errors::ConnectorError::NotSupported {
|
||||
message: format!("{:?}", item.request.payment_method_data),
|
||||
connector: "Helcim",
|
||||
})?
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl
|
||||
TryFrom<(
|
||||
&HelcimRouterData<&types::PaymentsAuthorizeRouterData>,
|
||||
&api::Card,
|
||||
)> for HelcimPaymentsRequest
|
||||
{
|
||||
type Error = error_stack::Report<errors::ConnectorError>;
|
||||
fn try_from(
|
||||
value: (
|
||||
&HelcimRouterData<&types::PaymentsAuthorizeRouterData>,
|
||||
&api::Card,
|
||||
),
|
||||
) -> Result<Self, Self::Error> {
|
||||
let (item, req_card) = value;
|
||||
let card_data = HelcimCard {
|
||||
card_expiry: req_card.get_card_expiry_month_year_2_digit_with_delimiter("".to_string()),
|
||||
card_number: req_card.card_number.clone(),
|
||||
card_c_v_v: req_card.card_cvc.clone(),
|
||||
};
|
||||
let req_address = item
|
||||
.router_data
|
||||
.get_billing()?
|
||||
.to_owned()
|
||||
.address
|
||||
.ok_or_else(utils::missing_field_err("billing.address"))?;
|
||||
|
||||
let billing_address = HelcimBillingAddress {
|
||||
name: req_address.get_full_name()?,
|
||||
street1: req_address.get_line1()?.to_owned(),
|
||||
postal_code: req_address.get_zip()?.to_owned(),
|
||||
street2: req_address.line2,
|
||||
city: req_address.city,
|
||||
email: item.router_data.request.email.clone(),
|
||||
};
|
||||
|
||||
let ip_address = item
|
||||
.router_data
|
||||
.request
|
||||
.get_browser_info()?
|
||||
.get_ip_address()?;
|
||||
Ok(Self {
|
||||
amount: item.amount.to_owned(),
|
||||
currency: item.router_data.request.currency,
|
||||
ip_address,
|
||||
card_data,
|
||||
billing_address,
|
||||
ecommerce: None,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<&HelcimRouterData<&types::PaymentsAuthorizeRouterData>> for HelcimPaymentsRequest {
|
||||
type Error = error_stack::Report<errors::ConnectorError>;
|
||||
fn try_from(
|
||||
item: &HelcimRouterData<&types::PaymentsAuthorizeRouterData>,
|
||||
) -> Result<Self, Self::Error> {
|
||||
match item.router_data.request.payment_method_data.clone() {
|
||||
api::PaymentMethodData::Card(req_card) => Self::try_from((item, &req_card)),
|
||||
api_models::payments::PaymentMethodData::BankTransfer(_) => Err(
|
||||
errors::ConnectorError::NotImplemented("Payment Method".to_string()),
|
||||
)
|
||||
.into_report(),
|
||||
api_models::payments::PaymentMethodData::CardRedirect(_)
|
||||
| api_models::payments::PaymentMethodData::Wallet(_)
|
||||
| api_models::payments::PaymentMethodData::PayLater(_)
|
||||
| api_models::payments::PaymentMethodData::BankRedirect(_)
|
||||
| api_models::payments::PaymentMethodData::BankDebit(_)
|
||||
| api_models::payments::PaymentMethodData::Crypto(_)
|
||||
| api_models::payments::PaymentMethodData::MandatePayment
|
||||
| api_models::payments::PaymentMethodData::Reward
|
||||
| api_models::payments::PaymentMethodData::Upi(_)
|
||||
| api_models::payments::PaymentMethodData::Voucher(_)
|
||||
| api_models::payments::PaymentMethodData::GiftCard(_) => {
|
||||
Err(errors::ConnectorError::NotSupported {
|
||||
message: format!("{:?}", item.router_data.request.payment_method_data),
|
||||
connector: "Helcim",
|
||||
})?
|
||||
}
|
||||
_ => Err(errors::ConnectorError::NotImplemented("Payment methods".to_string()).into()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//TODO: Fill the struct with respective fields
|
||||
// Auth Struct
|
||||
pub struct HelcimAuthType {
|
||||
pub(super) api_key: Secret<String>,
|
||||
@ -65,100 +250,413 @@ impl TryFrom<&types::ConnectorAuthType> for HelcimAuthType {
|
||||
}
|
||||
}
|
||||
// PaymentsResponse
|
||||
//TODO: Append the remaining status flags
|
||||
#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq)]
|
||||
#[serde(rename_all = "lowercase")]
|
||||
#[derive(Debug, Deserialize)]
|
||||
#[serde(rename_all = "UPPERCASE")]
|
||||
pub enum HelcimPaymentStatus {
|
||||
Succeeded,
|
||||
Failed,
|
||||
#[default]
|
||||
Processing,
|
||||
Approved,
|
||||
Declined,
|
||||
}
|
||||
|
||||
impl From<HelcimPaymentStatus> for enums::AttemptStatus {
|
||||
fn from(item: HelcimPaymentStatus) -> Self {
|
||||
match item {
|
||||
HelcimPaymentStatus::Succeeded => Self::Charged,
|
||||
HelcimPaymentStatus::Failed => Self::Failure,
|
||||
HelcimPaymentStatus::Processing => Self::Authorizing,
|
||||
#[derive(Debug, Deserialize)]
|
||||
#[serde(rename_all = "lowercase")]
|
||||
pub enum HelcimTransactionType {
|
||||
Purchase,
|
||||
PreAuth,
|
||||
Capture,
|
||||
Verify,
|
||||
Reverse,
|
||||
}
|
||||
|
||||
impl From<HelcimPaymentsResponse> for enums::AttemptStatus {
|
||||
fn from(item: HelcimPaymentsResponse) -> Self {
|
||||
match item.transaction_type {
|
||||
HelcimTransactionType::Purchase | HelcimTransactionType::Verify => match item.status {
|
||||
HelcimPaymentStatus::Approved => Self::Charged,
|
||||
HelcimPaymentStatus::Declined => Self::Failure,
|
||||
},
|
||||
HelcimTransactionType::PreAuth => match item.status {
|
||||
HelcimPaymentStatus::Approved => Self::Authorized,
|
||||
HelcimPaymentStatus::Declined => Self::AuthorizationFailed,
|
||||
},
|
||||
HelcimTransactionType::Capture => match item.status {
|
||||
HelcimPaymentStatus::Approved => Self::Charged,
|
||||
HelcimPaymentStatus::Declined => Self::CaptureFailed,
|
||||
},
|
||||
HelcimTransactionType::Reverse => match item.status {
|
||||
HelcimPaymentStatus::Approved => Self::Voided,
|
||||
HelcimPaymentStatus::Declined => Self::VoidFailed,
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//TODO: Fill the struct with respective fields
|
||||
#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq)]
|
||||
#[derive(Debug, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct HelcimPaymentsResponse {
|
||||
status: HelcimPaymentStatus,
|
||||
id: String,
|
||||
transaction_id: u64,
|
||||
#[serde(rename = "type")]
|
||||
transaction_type: HelcimTransactionType,
|
||||
}
|
||||
|
||||
impl<F, T>
|
||||
TryFrom<types::ResponseRouterData<F, HelcimPaymentsResponse, T, types::PaymentsResponseData>>
|
||||
for types::RouterData<F, T, types::PaymentsResponseData>
|
||||
impl<F>
|
||||
TryFrom<
|
||||
types::ResponseRouterData<
|
||||
F,
|
||||
HelcimPaymentsResponse,
|
||||
types::SetupMandateRequestData,
|
||||
types::PaymentsResponseData,
|
||||
>,
|
||||
> for types::RouterData<F, types::SetupMandateRequestData, types::PaymentsResponseData>
|
||||
{
|
||||
type Error = error_stack::Report<errors::ConnectorError>;
|
||||
fn try_from(
|
||||
item: types::ResponseRouterData<F, HelcimPaymentsResponse, T, types::PaymentsResponseData>,
|
||||
item: types::ResponseRouterData<
|
||||
F,
|
||||
HelcimPaymentsResponse,
|
||||
types::SetupMandateRequestData,
|
||||
types::PaymentsResponseData,
|
||||
>,
|
||||
) -> Result<Self, Self::Error> {
|
||||
Ok(Self {
|
||||
status: enums::AttemptStatus::from(item.response.status),
|
||||
response: Ok(types::PaymentsResponseData::TransactionResponse {
|
||||
resource_id: types::ResponseId::ConnectorTransactionId(item.response.id),
|
||||
resource_id: types::ResponseId::ConnectorTransactionId(
|
||||
item.response.transaction_id.to_string(),
|
||||
),
|
||||
redirection_data: None,
|
||||
mandate_reference: None,
|
||||
connector_metadata: None,
|
||||
network_txn_id: None,
|
||||
connector_response_reference_id: None,
|
||||
}),
|
||||
status: enums::AttemptStatus::from(item.response),
|
||||
..item.data
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
//TODO: Fill the struct with respective fields
|
||||
// REFUND :
|
||||
// Type definition for RefundRequest
|
||||
#[derive(Default, Debug, Serialize)]
|
||||
pub struct HelcimRefundRequest {
|
||||
pub amount: i64,
|
||||
#[derive(Debug, Deserialize, Serialize)]
|
||||
pub struct HelcimMetaData {
|
||||
pub preauth_transaction_id: u64,
|
||||
}
|
||||
|
||||
impl<F> TryFrom<&types::RefundsRouterData<F>> for HelcimRefundRequest {
|
||||
impl<F>
|
||||
TryFrom<
|
||||
types::ResponseRouterData<
|
||||
F,
|
||||
HelcimPaymentsResponse,
|
||||
types::PaymentsAuthorizeData,
|
||||
types::PaymentsResponseData,
|
||||
>,
|
||||
> for types::RouterData<F, types::PaymentsAuthorizeData, types::PaymentsResponseData>
|
||||
{
|
||||
type Error = error_stack::Report<errors::ConnectorError>;
|
||||
fn try_from(item: &types::RefundsRouterData<F>) -> Result<Self, Self::Error> {
|
||||
fn try_from(
|
||||
item: types::ResponseRouterData<
|
||||
F,
|
||||
HelcimPaymentsResponse,
|
||||
types::PaymentsAuthorizeData,
|
||||
types::PaymentsResponseData,
|
||||
>,
|
||||
) -> Result<Self, Self::Error> {
|
||||
//PreAuth Transaction ID is stored in connector metadata
|
||||
//Initially resource_id is stored as NoResponseID for manual capture
|
||||
//After Capture Transaction is completed it is updated to store the Capture ID
|
||||
let resource_id = if item.data.request.is_auto_capture()? {
|
||||
types::ResponseId::ConnectorTransactionId(item.response.transaction_id.to_string())
|
||||
} else {
|
||||
types::ResponseId::NoResponseId
|
||||
};
|
||||
let connector_metadata = if !item.data.request.is_auto_capture()? {
|
||||
Some(serde_json::json!(HelcimMetaData {
|
||||
preauth_transaction_id: item.response.transaction_id,
|
||||
}))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
Ok(Self {
|
||||
amount: item.request.refund_amount,
|
||||
response: Ok(types::PaymentsResponseData::TransactionResponse {
|
||||
resource_id,
|
||||
redirection_data: None,
|
||||
mandate_reference: None,
|
||||
connector_metadata,
|
||||
network_txn_id: None,
|
||||
connector_response_reference_id: None,
|
||||
}),
|
||||
status: enums::AttemptStatus::from(item.response),
|
||||
..item.data
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// Type definition for Refund Response
|
||||
// impl utils::MultipleCaptureSyncResponse for HelcimPaymentsResponse {
|
||||
// fn get_connector_capture_id(&self) -> String {
|
||||
// self.transaction_id.to_string()
|
||||
// }
|
||||
|
||||
#[allow(dead_code)]
|
||||
#[derive(Debug, Serialize, Default, Deserialize, Clone)]
|
||||
pub enum RefundStatus {
|
||||
Succeeded,
|
||||
Failed,
|
||||
#[default]
|
||||
Processing,
|
||||
}
|
||||
// fn get_capture_attempt_status(&self) -> diesel_models::enums::AttemptStatus {
|
||||
// enums::AttemptStatus::from(self.to_owned())
|
||||
// }
|
||||
|
||||
impl From<RefundStatus> for enums::RefundStatus {
|
||||
fn from(item: RefundStatus) -> Self {
|
||||
match item {
|
||||
RefundStatus::Succeeded => Self::Success,
|
||||
RefundStatus::Failed => Self::Failure,
|
||||
RefundStatus::Processing => Self::Pending,
|
||||
//TODO: Review mapping
|
||||
// fn is_capture_response(&self) -> bool {
|
||||
// true
|
||||
// }
|
||||
|
||||
// fn get_amount_captured(&self) -> Option<i64> {
|
||||
// Some(self.amount)
|
||||
// }
|
||||
// fn get_connector_reference_id(&self) -> Option<String> {
|
||||
// None
|
||||
// }
|
||||
// }
|
||||
|
||||
impl<F>
|
||||
TryFrom<
|
||||
types::ResponseRouterData<
|
||||
F,
|
||||
HelcimPaymentsResponse,
|
||||
types::PaymentsSyncData,
|
||||
types::PaymentsResponseData,
|
||||
>,
|
||||
> for types::RouterData<F, types::PaymentsSyncData, types::PaymentsResponseData>
|
||||
{
|
||||
type Error = error_stack::Report<errors::ConnectorError>;
|
||||
fn try_from(
|
||||
item: types::ResponseRouterData<
|
||||
F,
|
||||
HelcimPaymentsResponse,
|
||||
types::PaymentsSyncData,
|
||||
types::PaymentsResponseData,
|
||||
>,
|
||||
) -> Result<Self, Self::Error> {
|
||||
match item.data.request.sync_type {
|
||||
types::SyncRequestType::SinglePaymentSync => Ok(Self {
|
||||
response: Ok(types::PaymentsResponseData::TransactionResponse {
|
||||
resource_id: types::ResponseId::ConnectorTransactionId(
|
||||
item.response.transaction_id.to_string(),
|
||||
),
|
||||
redirection_data: None,
|
||||
mandate_reference: None,
|
||||
connector_metadata: None,
|
||||
network_txn_id: None,
|
||||
connector_response_reference_id: None,
|
||||
}),
|
||||
status: enums::AttemptStatus::from(item.response),
|
||||
..item.data
|
||||
}),
|
||||
types::SyncRequestType::MultipleCaptureSync(_) => {
|
||||
Err(errors::ConnectorError::NotImplemented(
|
||||
"manual multiple capture sync".to_string(),
|
||||
)
|
||||
.into())
|
||||
// let capture_sync_response_list =
|
||||
// utils::construct_captures_response_hashmap(vec![item.response]);
|
||||
// Ok(Self {
|
||||
// response: Ok(types::PaymentsResponseData::MultipleCaptureResponse {
|
||||
// capture_sync_response_list,
|
||||
// }),
|
||||
// ..item.data
|
||||
// })
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//TODO: Fill the struct with respective fields
|
||||
#[derive(Default, Debug, Clone, Serialize, Deserialize)]
|
||||
#[derive(Debug, Serialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct HelcimCaptureRequest {
|
||||
pre_auth_transaction_id: u64,
|
||||
amount: f64,
|
||||
ip_address: Secret<String, IpAddress>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
ecommerce: Option<bool>,
|
||||
}
|
||||
|
||||
impl TryFrom<&HelcimRouterData<&types::PaymentsCaptureRouterData>> for HelcimCaptureRequest {
|
||||
type Error = error_stack::Report<errors::ConnectorError>;
|
||||
fn try_from(
|
||||
item: &HelcimRouterData<&types::PaymentsCaptureRouterData>,
|
||||
) -> Result<Self, Self::Error> {
|
||||
let ip_address = item
|
||||
.router_data
|
||||
.request
|
||||
.get_browser_info()?
|
||||
.get_ip_address()?;
|
||||
Ok(Self {
|
||||
pre_auth_transaction_id: item
|
||||
.router_data
|
||||
.request
|
||||
.connector_transaction_id
|
||||
.parse::<u64>()
|
||||
.into_report()
|
||||
.change_context(errors::ConnectorError::RequestEncodingFailed)?,
|
||||
amount: item.amount,
|
||||
ip_address,
|
||||
ecommerce: None,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<F>
|
||||
TryFrom<
|
||||
types::ResponseRouterData<
|
||||
F,
|
||||
HelcimPaymentsResponse,
|
||||
types::PaymentsCaptureData,
|
||||
types::PaymentsResponseData,
|
||||
>,
|
||||
> for types::RouterData<F, types::PaymentsCaptureData, types::PaymentsResponseData>
|
||||
{
|
||||
type Error = error_stack::Report<errors::ConnectorError>;
|
||||
fn try_from(
|
||||
item: types::ResponseRouterData<
|
||||
F,
|
||||
HelcimPaymentsResponse,
|
||||
types::PaymentsCaptureData,
|
||||
types::PaymentsResponseData,
|
||||
>,
|
||||
) -> Result<Self, Self::Error> {
|
||||
Ok(Self {
|
||||
response: Ok(types::PaymentsResponseData::TransactionResponse {
|
||||
resource_id: types::ResponseId::ConnectorTransactionId(
|
||||
item.response.transaction_id.to_string(),
|
||||
),
|
||||
redirection_data: None,
|
||||
mandate_reference: None,
|
||||
connector_metadata: None,
|
||||
network_txn_id: None,
|
||||
connector_response_reference_id: None,
|
||||
}),
|
||||
status: enums::AttemptStatus::from(item.response),
|
||||
..item.data
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct HelcimVoidRequest {
|
||||
card_transaction_id: u64,
|
||||
ip_address: Secret<String, IpAddress>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
ecommerce: Option<bool>,
|
||||
}
|
||||
|
||||
impl TryFrom<&types::PaymentsCancelRouterData> for HelcimVoidRequest {
|
||||
type Error = error_stack::Report<errors::ConnectorError>;
|
||||
fn try_from(item: &types::PaymentsCancelRouterData) -> Result<Self, Self::Error> {
|
||||
let ip_address = item.request.get_browser_info()?.get_ip_address()?;
|
||||
Ok(Self {
|
||||
card_transaction_id: item
|
||||
.request
|
||||
.connector_transaction_id
|
||||
.parse::<u64>()
|
||||
.into_report()
|
||||
.change_context(errors::ConnectorError::RequestEncodingFailed)?,
|
||||
ip_address,
|
||||
ecommerce: None,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<F>
|
||||
TryFrom<
|
||||
types::ResponseRouterData<
|
||||
F,
|
||||
HelcimPaymentsResponse,
|
||||
types::PaymentsCancelData,
|
||||
types::PaymentsResponseData,
|
||||
>,
|
||||
> for types::RouterData<F, types::PaymentsCancelData, types::PaymentsResponseData>
|
||||
{
|
||||
type Error = error_stack::Report<errors::ConnectorError>;
|
||||
fn try_from(
|
||||
item: types::ResponseRouterData<
|
||||
F,
|
||||
HelcimPaymentsResponse,
|
||||
types::PaymentsCancelData,
|
||||
types::PaymentsResponseData,
|
||||
>,
|
||||
) -> Result<Self, Self::Error> {
|
||||
Ok(Self {
|
||||
response: Ok(types::PaymentsResponseData::TransactionResponse {
|
||||
resource_id: types::ResponseId::ConnectorTransactionId(
|
||||
item.response.transaction_id.to_string(),
|
||||
),
|
||||
redirection_data: None,
|
||||
mandate_reference: None,
|
||||
connector_metadata: None,
|
||||
network_txn_id: None,
|
||||
connector_response_reference_id: None,
|
||||
}),
|
||||
status: enums::AttemptStatus::from(item.response),
|
||||
..item.data
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// REFUND :
|
||||
// Type definition for RefundRequest
|
||||
#[derive(Debug, Serialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct HelcimRefundRequest {
|
||||
amount: f64,
|
||||
original_transaction_id: u64,
|
||||
ip_address: Secret<String, IpAddress>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
ecommerce: Option<bool>,
|
||||
}
|
||||
|
||||
impl<F> TryFrom<&HelcimRouterData<&types::RefundsRouterData<F>>> for HelcimRefundRequest {
|
||||
type Error = error_stack::Report<errors::ConnectorError>;
|
||||
fn try_from(
|
||||
item: &HelcimRouterData<&types::RefundsRouterData<F>>,
|
||||
) -> Result<Self, Self::Error> {
|
||||
let original_transaction_id = item
|
||||
.router_data
|
||||
.request
|
||||
.connector_transaction_id
|
||||
.parse::<u64>()
|
||||
.into_report()
|
||||
.change_context(errors::ConnectorError::RequestEncodingFailed)?;
|
||||
|
||||
let ip_address = item
|
||||
.router_data
|
||||
.request
|
||||
.get_browser_info()?
|
||||
.get_ip_address()?;
|
||||
Ok(Self {
|
||||
amount: item.amount,
|
||||
original_transaction_id,
|
||||
ip_address,
|
||||
ecommerce: None,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
#[serde(rename_all = "lowercase")]
|
||||
pub enum HelcimRefundTransactionType {
|
||||
Refund,
|
||||
}
|
||||
#[derive(Debug, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct RefundResponse {
|
||||
id: String,
|
||||
status: RefundStatus,
|
||||
status: HelcimPaymentStatus,
|
||||
transaction_id: u64,
|
||||
#[serde(rename = "type")]
|
||||
transaction_type: HelcimRefundTransactionType,
|
||||
}
|
||||
|
||||
impl From<RefundResponse> for enums::RefundStatus {
|
||||
fn from(item: RefundResponse) -> Self {
|
||||
match item.transaction_type {
|
||||
HelcimRefundTransactionType::Refund => match item.status {
|
||||
HelcimPaymentStatus::Approved => Self::Success,
|
||||
HelcimPaymentStatus::Declined => Self::Failure,
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<types::RefundsResponseRouterData<api::Execute, RefundResponse>>
|
||||
@ -170,8 +668,8 @@ 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.transaction_id.to_string(),
|
||||
refund_status: enums::RefundStatus::from(item.response),
|
||||
}),
|
||||
..item.data
|
||||
})
|
||||
@ -187,19 +685,22 @@ impl TryFrom<types::RefundsResponseRouterData<api::RSync, 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.transaction_id.to_string(),
|
||||
refund_status: enums::RefundStatus::from(item.response),
|
||||
}),
|
||||
..item.data
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
//TODO: Fill the struct with respective fields
|
||||
#[derive(Default, Debug, Serialize, Deserialize, PartialEq)]
|
||||
pub struct HelcimErrorResponse {
|
||||
pub status_code: u16,
|
||||
pub code: String,
|
||||
pub message: String,
|
||||
pub reason: Option<String>,
|
||||
#[derive(Debug, strum::Display, Deserialize)]
|
||||
#[serde(untagged)]
|
||||
pub enum HelcimErrorTypes {
|
||||
StringType(String),
|
||||
JsonType(serde_json::Value),
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
pub struct HelcimErrorResponse {
|
||||
pub errors: HelcimErrorTypes,
|
||||
}
|
||||
|
||||
@ -248,19 +248,25 @@ impl PaymentsPreProcessingData for types::PaymentsPreProcessingData {
|
||||
|
||||
pub trait PaymentsCaptureRequestData {
|
||||
fn is_multiple_capture(&self) -> bool;
|
||||
fn get_browser_info(&self) -> Result<types::BrowserInformation, Error>;
|
||||
}
|
||||
|
||||
impl PaymentsCaptureRequestData for types::PaymentsCaptureData {
|
||||
fn is_multiple_capture(&self) -> bool {
|
||||
self.multiple_capture_data.is_some()
|
||||
}
|
||||
fn get_browser_info(&self) -> Result<types::BrowserInformation, Error> {
|
||||
self.browser_info
|
||||
.clone()
|
||||
.ok_or_else(missing_field_err("browser_info"))
|
||||
}
|
||||
}
|
||||
|
||||
pub trait SetupMandateRequestData {
|
||||
pub trait PaymentsSetupMandateRequestData {
|
||||
fn get_browser_info(&self) -> Result<types::BrowserInformation, Error>;
|
||||
}
|
||||
|
||||
impl SetupMandateRequestData for types::SetupMandateRequestData {
|
||||
impl PaymentsSetupMandateRequestData for types::SetupMandateRequestData {
|
||||
fn get_browser_info(&self) -> Result<types::BrowserInformation, Error> {
|
||||
self.browser_info
|
||||
.clone()
|
||||
@ -511,6 +517,7 @@ pub trait PaymentsCancelRequestData {
|
||||
fn get_amount(&self) -> Result<i64, Error>;
|
||||
fn get_currency(&self) -> Result<diesel_models::enums::Currency, Error>;
|
||||
fn get_cancellation_reason(&self) -> Result<String, Error>;
|
||||
fn get_browser_info(&self) -> Result<types::BrowserInformation, Error>;
|
||||
}
|
||||
|
||||
impl PaymentsCancelRequestData for PaymentsCancelData {
|
||||
@ -525,11 +532,17 @@ impl PaymentsCancelRequestData for PaymentsCancelData {
|
||||
.clone()
|
||||
.ok_or_else(missing_field_err("cancellation_reason"))
|
||||
}
|
||||
fn get_browser_info(&self) -> Result<types::BrowserInformation, Error> {
|
||||
self.browser_info
|
||||
.clone()
|
||||
.ok_or_else(missing_field_err("browser_info"))
|
||||
}
|
||||
}
|
||||
|
||||
pub trait RefundsRequestData {
|
||||
fn get_connector_refund_id(&self) -> Result<String, Error>;
|
||||
fn get_webhook_url(&self) -> Result<String, Error>;
|
||||
fn get_browser_info(&self) -> Result<types::BrowserInformation, Error>;
|
||||
}
|
||||
|
||||
impl RefundsRequestData for types::RefundsData {
|
||||
@ -545,6 +558,11 @@ impl RefundsRequestData for types::RefundsData {
|
||||
.clone()
|
||||
.ok_or_else(missing_field_err("webhook_url"))
|
||||
}
|
||||
fn get_browser_info(&self) -> Result<types::BrowserInformation, Error> {
|
||||
self.browser_info
|
||||
.clone()
|
||||
.ok_or_else(missing_field_err("browser_info"))
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, serde::Serialize)]
|
||||
|
||||
@ -1365,6 +1365,10 @@ pub(crate) fn validate_auth_and_metadata_type(
|
||||
gocardless::transformers::GocardlessAuthType::try_from(val)?;
|
||||
Ok(())
|
||||
}
|
||||
api_enums::Connector::Helcim => {
|
||||
helcim::transformers::HelcimAuthType::try_from(val)?;
|
||||
Ok(())
|
||||
}
|
||||
api_enums::Connector::Iatapay => {
|
||||
iatapay::transformers::IatapayAuthType::try_from(val)?;
|
||||
Ok(())
|
||||
|
||||
@ -427,7 +427,6 @@ default_imp_for_connector_request_id!(
|
||||
connector::Globalpay,
|
||||
connector::Globepay,
|
||||
connector::Gocardless,
|
||||
connector::Helcim,
|
||||
connector::Iatapay,
|
||||
connector::Klarna,
|
||||
connector::Mollie,
|
||||
|
||||
@ -9,7 +9,7 @@ use router_env::{instrument, tracing};
|
||||
use super::{flows::Feature, PaymentData};
|
||||
use crate::{
|
||||
configs::settings::{ConnectorRequestReferenceIdConfig, Server},
|
||||
connector::Nexinets,
|
||||
connector::{Helcim, Nexinets},
|
||||
core::{
|
||||
errors::{self, RouterResponse, RouterResult},
|
||||
payments::{self, helpers},
|
||||
@ -1059,6 +1059,21 @@ impl<F: Clone> TryFrom<PaymentAdditionalData<'_, F>> for types::PaymentsSyncData
|
||||
}
|
||||
}
|
||||
|
||||
impl api::ConnectorTransactionId for Helcim {
|
||||
fn connector_transaction_id(
|
||||
&self,
|
||||
payment_attempt: storage::PaymentAttempt,
|
||||
) -> Result<Option<String>, errors::ApiErrorResponse> {
|
||||
if payment_attempt.connector_transaction_id.is_none() {
|
||||
let metadata =
|
||||
Self::connector_transaction_id(self, &payment_attempt.connector_metadata);
|
||||
metadata.map_err(|_| errors::ApiErrorResponse::ResourceIdNotFound)
|
||||
} else {
|
||||
Ok(payment_attempt.connector_transaction_id)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl api::ConnectorTransactionId for Nexinets {
|
||||
fn connector_transaction_id(
|
||||
&self,
|
||||
@ -1083,6 +1098,16 @@ impl<F: Clone> TryFrom<PaymentAdditionalData<'_, F>> for types::PaymentsCaptureD
|
||||
.payment_attempt
|
||||
.amount_to_capture
|
||||
.map_or(payment_data.amount.into(), |capture_amount| capture_amount);
|
||||
let browser_info: Option<types::BrowserInformation> = payment_data
|
||||
.payment_attempt
|
||||
.browser_info
|
||||
.clone()
|
||||
.map(|b| b.parse_value("BrowserInformation"))
|
||||
.transpose()
|
||||
.change_context(errors::ApiErrorResponse::InvalidDataValue {
|
||||
field_name: "browser_info",
|
||||
})?;
|
||||
|
||||
Ok(Self {
|
||||
amount_to_capture,
|
||||
currency: payment_data.currency,
|
||||
@ -1102,6 +1127,7 @@ impl<F: Clone> TryFrom<PaymentAdditionalData<'_, F>> for types::PaymentsCaptureD
|
||||
}),
|
||||
None => None,
|
||||
},
|
||||
browser_info,
|
||||
})
|
||||
}
|
||||
}
|
||||
@ -1116,6 +1142,15 @@ impl<F: Clone> TryFrom<PaymentAdditionalData<'_, F>> for types::PaymentsCancelDa
|
||||
&additional_data.connector_name,
|
||||
api::GetToken::Connector,
|
||||
)?;
|
||||
let browser_info: Option<types::BrowserInformation> = payment_data
|
||||
.payment_attempt
|
||||
.browser_info
|
||||
.clone()
|
||||
.map(|b| b.parse_value("BrowserInformation"))
|
||||
.transpose()
|
||||
.change_context(errors::ApiErrorResponse::InvalidDataValue {
|
||||
field_name: "browser_info",
|
||||
})?;
|
||||
Ok(Self {
|
||||
amount: Some(payment_data.amount.into()),
|
||||
currency: Some(payment_data.currency),
|
||||
@ -1125,6 +1160,7 @@ impl<F: Clone> TryFrom<PaymentAdditionalData<'_, F>> for types::PaymentsCancelDa
|
||||
.ok_or(errors::ApiErrorResponse::ResourceIdNotFound)?,
|
||||
cancellation_reason: payment_data.payment_attempt.cancellation_reason,
|
||||
connector_meta: payment_data.payment_attempt.connector_metadata,
|
||||
browser_info,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@ -269,6 +269,15 @@ pub async fn construct_refund_router_data<'a, F>(
|
||||
None
|
||||
};
|
||||
|
||||
let browser_info: Option<types::BrowserInformation> = payment_attempt
|
||||
.browser_info
|
||||
.clone()
|
||||
.map(|b| b.parse_value("BrowserInformation"))
|
||||
.transpose()
|
||||
.change_context(errors::ApiErrorResponse::InvalidDataValue {
|
||||
field_name: "browser_info",
|
||||
})?;
|
||||
|
||||
let router_data = types::RouterData {
|
||||
flow: PhantomData,
|
||||
merchant_id: merchant_account.merchant_id.clone(),
|
||||
@ -297,6 +306,7 @@ pub async fn construct_refund_router_data<'a, F>(
|
||||
connector_metadata: payment_attempt.connector_metadata.clone(),
|
||||
reason: refund.refund_reason.clone(),
|
||||
connector_refund_id: refund.connector_refund_id.clone(),
|
||||
browser_info,
|
||||
},
|
||||
|
||||
response: Ok(types::RefundsResponseData {
|
||||
|
||||
@ -47,9 +47,11 @@ pub mod headers {
|
||||
pub const API_KEY: &str = "API-KEY";
|
||||
pub const APIKEY: &str = "apikey";
|
||||
pub const X_CC_API_KEY: &str = "X-CC-Api-Key";
|
||||
pub const API_TOKEN: &str = "Api-Token";
|
||||
pub const AUTHORIZATION: &str = "Authorization";
|
||||
pub const CONTENT_TYPE: &str = "Content-Type";
|
||||
pub const DATE: &str = "Date";
|
||||
pub const IDEMPOTENCY_KEY: &str = "Idempotency-Key";
|
||||
pub const NONCE: &str = "nonce";
|
||||
pub const TIMESTAMP: &str = "Timestamp";
|
||||
pub const TOKEN: &str = "token";
|
||||
|
||||
@ -390,6 +390,7 @@ pub struct PaymentsCaptureData {
|
||||
pub payment_amount: i64,
|
||||
pub multiple_capture_data: Option<MultipleCaptureRequestData>,
|
||||
pub connector_meta: Option<serde_json::Value>,
|
||||
pub browser_info: Option<BrowserInformation>,
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
@ -492,6 +493,7 @@ pub struct PaymentsCancelData {
|
||||
pub connector_transaction_id: String,
|
||||
pub cancellation_reason: Option<String>,
|
||||
pub connector_meta: Option<serde_json::Value>,
|
||||
pub browser_info: Option<BrowserInformation>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, Clone)]
|
||||
@ -711,6 +713,7 @@ pub struct RefundsData {
|
||||
pub refund_amount: i64,
|
||||
/// Arbitrary metadata required for refund
|
||||
pub connector_metadata: Option<serde_json::Value>,
|
||||
pub browser_info: Option<BrowserInformation>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Default, serde::Serialize, serde::Deserialize)]
|
||||
|
||||
@ -330,7 +330,7 @@ impl ConnectorData {
|
||||
enums::Connector::Globalpay => Ok(Box::new(&connector::Globalpay)),
|
||||
enums::Connector::Globepay => Ok(Box::new(&connector::Globepay)),
|
||||
enums::Connector::Gocardless => Ok(Box::new(&connector::Gocardless)),
|
||||
//enums::Connector::Helcim => Ok(Box::new(&connector::Helcim)), , it is added as template code for future usage
|
||||
enums::Connector::Helcim => Ok(Box::new(&connector::Helcim)),
|
||||
enums::Connector::Iatapay => Ok(Box::new(&connector::Iatapay)),
|
||||
enums::Connector::Klarna => Ok(Box::new(&connector::Klarna)),
|
||||
enums::Connector::Mollie => Ok(Box::new(&connector::Mollie)),
|
||||
|
||||
@ -126,6 +126,7 @@ fn construct_refund_router_data<F>() -> types::RefundsRouterData<F> {
|
||||
connector_metadata: None,
|
||||
reason: None,
|
||||
connector_refund_id: None,
|
||||
browser_info: None,
|
||||
},
|
||||
payment_method_id: None,
|
||||
response: Err(types::ErrorResponse::default()),
|
||||
|
||||
@ -388,6 +388,7 @@ pub trait ConnectorActions: Connector {
|
||||
connector_metadata: None,
|
||||
reason: None,
|
||||
connector_refund_id: Some(refund_id),
|
||||
browser_info: None,
|
||||
}),
|
||||
payment_info,
|
||||
);
|
||||
@ -955,6 +956,7 @@ impl Default for PaymentRefundType {
|
||||
connector_metadata: None,
|
||||
reason: Some("Customer returned product".to_string()),
|
||||
connector_refund_id: None,
|
||||
browser_info: None,
|
||||
};
|
||||
Self(data)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user