feat(connector): [HELCIM] Implement Cards for Helcim (#2210)

This commit is contained in:
DEEPANSHU BANSAL
2023-10-13 18:14:12 +05:30
committed by GitHub
parent be80a14c5d
commit b5feab61d9
17 changed files with 1208 additions and 133 deletions

View File

@ -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::{

View File

@ -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(

View File

@ -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,
}

View File

@ -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)]

View File

@ -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(())

View File

@ -427,7 +427,6 @@ default_imp_for_connector_request_id!(
connector::Globalpay,
connector::Globepay,
connector::Gocardless,
connector::Helcim,
connector::Iatapay,
connector::Klarna,
connector::Mollie,

View File

@ -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,
})
}
}

View File

@ -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 {

View File

@ -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";

View File

@ -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)]

View File

@ -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)),

View File

@ -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()),

View File

@ -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)
}