From adc5f89da48bcd11dfe8f19b58903738fc471570 Mon Sep 17 00:00:00 2001 From: DEEPANSHU BANSAL Date: Wed, 20 Sep 2023 21:04:50 +0530 Subject: [PATCH 01/20] feat(connector): [HELCIM] Activate Helcim --- crates/api_models/src/enums.rs | 4 ++-- crates/router/src/core/admin.rs | 4 ++++ crates/router/src/types/api.rs | 2 +- 3 files changed, 7 insertions(+), 3 deletions(-) diff --git a/crates/api_models/src/enums.rs b/crates/api_models/src/enums.rs index 33cde866eed1..348354b62394 100644 --- a/crates/api_models/src/enums.rs +++ b/crates/api_models/src/enums.rs @@ -91,7 +91,7 @@ pub enum Connector { Globalpay, Globepay, Gocardless, - //Helcim, added as template code for future usage, + Helcim, Iatapay, Klarna, Mollie, @@ -208,7 +208,7 @@ pub enum RoutableConnectors { Globalpay, Globepay, Gocardless, - //Helcim, added as template code for future usage, + Helcim, Iatapay, Klarna, Mollie, diff --git a/crates/router/src/core/admin.rs b/crates/router/src/core/admin.rs index db1af73e622e..75f874261a4a 100644 --- a/crates/router/src/core/admin.rs +++ b/crates/router/src/core/admin.rs @@ -1306,6 +1306,10 @@ pub(crate) fn validate_auth_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(()) diff --git a/crates/router/src/types/api.rs b/crates/router/src/types/api.rs index 27d86db831fe..d6dd056af957 100644 --- a/crates/router/src/types/api.rs +++ b/crates/router/src/types/api.rs @@ -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)), From a5e825b2d5fefd1a70c485e837ef1d6bd985d092 Mon Sep 17 00:00:00 2001 From: DEEPANSHU BANSAL Date: Thu, 21 Sep 2023 00:04:33 +0530 Subject: [PATCH 02/20] feat(connector): [HELCIM] Add Authorize Flow --- crates/router/src/connector/helcim.rs | 12 ++- .../src/connector/helcim/transformers.rs | 95 ++++++++++++------- crates/router/src/lib.rs | 2 + 3 files changed, 73 insertions(+), 36 deletions(-) diff --git a/crates/router/src/connector/helcim.rs b/crates/router/src/connector/helcim.rs index e5444b53be0f..772b99fca823 100644 --- a/crates/router/src/connector/helcim.rs +++ b/crates/router/src/connector/helcim.rs @@ -65,7 +65,13 @@ where .into(), )]; let mut api_key = self.get_auth_header(&req.connector_auth_type)?; + + let mut idempotency_key = vec![( + headers::IDEMPOTENCY_KEY.to_string(), + format!("{}_", req.payment_id).into_masked(), + )]; header.append(&mut api_key); + header.append(&mut idempotency_key); Ok(header) } } @@ -90,7 +96,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(), )]) } @@ -151,9 +157,9 @@ impl ConnectorIntegration CustomResult { - Err(errors::ConnectorError::NotImplemented("get_url method".to_string()).into()) + Ok(format!("{}v2/payment/purchase", self.base_url(connectors))) } fn get_request_body( diff --git a/crates/router/src/connector/helcim/transformers.rs b/crates/router/src/connector/helcim/transformers.rs index 20a8bef08a2d..7ee308eac86c 100644 --- a/crates/router/src/connector/helcim/transformers.rs +++ b/crates/router/src/connector/helcim/transformers.rs @@ -2,26 +2,37 @@ use masking::Secret; use serde::{Deserialize, Serialize}; use crate::{ - connector::utils::PaymentsAuthorizeRequestData, + connector::utils::CardData, core::errors, types::{self, api, storage::enums}, }; //TODO: Fill the struct with respective fields #[derive(Default, Debug, Serialize, Eq, PartialEq)] +#[serde(rename_all = "camelCase")] pub struct HelcimPaymentsRequest { amount: i64, - card: HelcimCard, + currency: enums::Currency, + ip_address: Secret, + // ip_address: Secret, + card_data: HelcimCard, + billing_address: HelcimBillingAddress, } #[derive(Default, Debug, Serialize, Eq, PartialEq)] -pub struct HelcimCard { +#[serde(rename_all = "camelCase")] +pub struct HelcimBillingAddress { name: Secret, - number: cards::CardNumber, - expiry_month: Secret, - expiry_year: Secret, - cvc: Secret, - complete: bool, + street1: String, + postal_code: String, +} + +#[derive(Default, Debug, Serialize, Eq, PartialEq)] +#[serde(rename_all = "camelCase")] +pub struct HelcimCard { + card_number: cards::CardNumber, + card_expiry: Secret, + card_c_v_v: Secret, } impl TryFrom<&types::PaymentsAuthorizeRouterData> for HelcimPaymentsRequest { @@ -29,17 +40,24 @@ impl TryFrom<&types::PaymentsAuthorizeRouterData> for HelcimPaymentsRequest { fn try_from(item: &types::PaymentsAuthorizeRouterData) -> Result { match item.request.payment_method_data.clone() { api::PaymentMethodData::Card(req_card) => { - let card = HelcimCard { + 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, + card_c_v_v: req_card.card_cvc, + }; + // let ip_address = item.request.get_browser_info()?.get_ip_address()?; + let billing_address = HelcimBillingAddress { 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()?, + street1: "Jump Street 21".to_string(), + postal_code: "H0H0H0".to_string(), }; Ok(Self { amount: item.request.amount, - card, + currency: item.request.currency, + ip_address: Secret::new("127.0.0.1".to_string()), + card_data, + billing_address, }) } _ => Err(errors::ConnectorError::NotImplemented("Payment methods".to_string()).into()), @@ -66,44 +84,55 @@ 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, Clone, Serialize, Deserialize, PartialEq)] +#[serde(rename_all = "UPPERCASE")] pub enum HelcimPaymentStatus { - Succeeded, - Failed, - #[default] - Processing, + Approved, + Declined, } impl From for enums::AttemptStatus { fn from(item: HelcimPaymentStatus) -> Self { match item { - HelcimPaymentStatus::Succeeded => Self::Charged, - HelcimPaymentStatus::Failed => Self::Failure, - HelcimPaymentStatus::Processing => Self::Authorizing, + HelcimPaymentStatus::Approved => Self::Charged, + HelcimPaymentStatus::Declined => Self::Failure, } } } //TODO: Fill the struct with respective fields -#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq)] +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] +#[serde(rename_all = "camelCase")] pub struct HelcimPaymentsResponse { status: HelcimPaymentStatus, - id: String, -} - -impl - TryFrom> - for types::RouterData + transaction_id: u64, +} + +impl + TryFrom< + types::ResponseRouterData< + F, + HelcimPaymentsResponse, + types::PaymentsAuthorizeData, + types::PaymentsResponseData, + >, + > for types::RouterData { type Error = error_stack::Report; fn try_from( - item: types::ResponseRouterData, + item: types::ResponseRouterData< + F, + HelcimPaymentsResponse, + types::PaymentsAuthorizeData, + types::PaymentsResponseData, + >, ) -> Result { 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, diff --git a/crates/router/src/lib.rs b/crates/router/src/lib.rs index 738646b2964b..0312b0df3ee4 100644 --- a/crates/router/src/lib.rs +++ b/crates/router/src/lib.rs @@ -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"; From 177e3470ac8d22265070ee0c40a32b5c43de60b5 Mon Sep 17 00:00:00 2001 From: DEEPANSHU BANSAL Date: Thu, 21 Sep 2023 02:16:23 +0530 Subject: [PATCH 03/20] feat(connector): [HELCIM] Add Capture Flow --- crates/router/src/connector/helcim.rs | 65 +++++++-- .../src/connector/helcim/transformers.rs | 134 +++++++++++++++++- 2 files changed, 181 insertions(+), 18 deletions(-) diff --git a/crates/router/src/connector/helcim.rs b/crates/router/src/connector/helcim.rs index 772b99fca823..a67ad38b7b19 100644 --- a/crates/router/src/connector/helcim.rs +++ b/crates/router/src/connector/helcim.rs @@ -2,10 +2,12 @@ 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::PaymentsAuthorizeRequestData; use crate::{ configs::settings, core::errors::{self, CustomResult}, @@ -120,7 +122,20 @@ impl ConnectorCommon for Helcim { } impl ConnectorValidation for Helcim { - //TODO: implement functions when support enabled + fn validate_capture_method( + &self, + capture_method: Option, + ) -> CustomResult<(), errors::ConnectorError> { + let capture_method = capture_method.unwrap_or_default(); + match capture_method { + enums::CaptureMethod::Automatic + | enums::CaptureMethod::Manual + | enums::CaptureMethod::ManualMultiple => Ok(()), + enums::CaptureMethod::Scheduled => Err( + super::utils::construct_not_supported_error_report(capture_method, self.id()), + ), + } + } } impl ConnectorIntegration @@ -156,10 +171,13 @@ impl ConnectorIntegration CustomResult { - Ok(format!("{}v2/payment/purchase", self.base_url(connectors))) + 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( @@ -225,9 +243,17 @@ impl ConnectorIntegration CustomResult)>, errors::ConnectorError> { - self.build_headers(req, connectors) + let mut header = vec![( + headers::CONTENT_TYPE.to_string(), + types::PaymentsAuthorizeType::get_content_type(self) + .to_string() + .into(), + )]; + let mut api_key = self.get_auth_header(&req.connector_auth_type)?; + header.append(&mut api_key); + Ok(header) } fn get_content_type(&self) -> &'static str { @@ -236,10 +262,19 @@ impl ConnectorIntegration CustomResult { - 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( @@ -299,16 +334,22 @@ impl ConnectorIntegration CustomResult { - 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, errors::ConnectorError> { - Err(errors::ConnectorError::NotImplemented("get_request_body method".to_string()).into()) + let connector_req = helcim::HelcimCaptureRequest::try_from(req)?; + let helcim_req = types::RequestBody::log_and_get_request_body( + &connector_req, + utils::Encode::::encode_to_string_of_json, + ) + .change_context(errors::ConnectorError::RequestEncodingFailed)?; + Ok(Some(helcim_req)) } fn build_request( diff --git a/crates/router/src/connector/helcim/transformers.rs b/crates/router/src/connector/helcim/transformers.rs index 7ee308eac86c..c40af075af48 100644 --- a/crates/router/src/connector/helcim/transformers.rs +++ b/crates/router/src/connector/helcim/transformers.rs @@ -1,3 +1,4 @@ +use error_stack::{IntoReport, ResultExt}; use masking::Secret; use serde::{Deserialize, Serialize}; @@ -91,11 +92,34 @@ pub enum HelcimPaymentStatus { Declined, } -impl From for enums::AttemptStatus { - fn from(item: HelcimPaymentStatus) -> Self { - match item { - HelcimPaymentStatus::Approved => Self::Charged, - HelcimPaymentStatus::Declined => Self::Failure, +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] +#[serde(rename_all = "lowercase")] +pub enum HelcimTransactionType { + Purchase, + PreAuth, + Capture, + Verify, +} + +impl From for enums::AttemptStatus { + fn from(item: HelcimPaymentsResponse) -> Self { + match item.transaction_type { + HelcimTransactionType::Purchase => 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, //Is this the correct status PartialCharged + HelcimPaymentStatus::Declined => Self::CaptureFailed, + }, + HelcimTransactionType::Verify => match item.status { + HelcimPaymentStatus::Approved => Self::AuthenticationSuccessful, + HelcimPaymentStatus::Declined => Self::AuthenticationFailed, + }, } } } @@ -106,6 +130,8 @@ impl From for enums::AttemptStatus { pub struct HelcimPaymentsResponse { status: HelcimPaymentStatus, transaction_id: u64, + #[serde(rename = "type")] + transaction_type: HelcimTransactionType, } impl @@ -128,7 +154,6 @@ impl >, ) -> Result { Ok(Self { - status: enums::AttemptStatus::from(item.response.status), response: Ok(types::PaymentsResponseData::TransactionResponse { resource_id: types::ResponseId::ConnectorTransactionId( item.response.transaction_id.to_string(), @@ -139,6 +164,103 @@ impl network_txn_id: None, connector_response_reference_id: None, }), + status: enums::AttemptStatus::from(item.response), + ..item.data + }) + } +} + +impl + TryFrom< + types::ResponseRouterData< + F, + HelcimPaymentsResponse, + types::PaymentsSyncData, + types::PaymentsResponseData, + >, + > for types::RouterData +{ + type Error = error_stack::Report; + fn try_from( + item: types::ResponseRouterData< + F, + HelcimPaymentsResponse, + types::PaymentsSyncData, + types::PaymentsResponseData, + >, + ) -> Result { + 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 HelcimCaptureRequest { + pre_auth_transaction_id: u64, + amount: i64, + ip_address: Secret, +} + +impl TryFrom<&types::PaymentsCaptureRouterData> for HelcimCaptureRequest { + type Error = error_stack::Report; + fn try_from(item: &types::PaymentsCaptureRouterData) -> Result { + Ok(Self { + pre_auth_transaction_id: item + .request + .connector_transaction_id + .parse::() + .into_report() + .change_context(errors::ConnectorError::RequestEncodingFailed)?, + amount: item.request.amount_to_capture, + ip_address: Secret::new("127.0.0.1".to_string()), + }) + } +} + +impl + TryFrom< + types::ResponseRouterData< + F, + HelcimPaymentsResponse, + types::PaymentsCaptureData, + types::PaymentsResponseData, + >, + > for types::RouterData +{ + type Error = error_stack::Report; + fn try_from( + item: types::ResponseRouterData< + F, + HelcimPaymentsResponse, + types::PaymentsCaptureData, + types::PaymentsResponseData, + >, + ) -> Result { + 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 }) } From 497d1ad9451f28c183307dba02e20953dc2d5015 Mon Sep 17 00:00:00 2001 From: DEEPANSHU BANSAL Date: Thu, 21 Sep 2023 15:01:32 +0530 Subject: [PATCH 04/20] feat(connector): [HELCIM] Implement multiple captures --- crates/router/src/connector/helcim.rs | 6 ++ .../src/connector/helcim/transformers.rs | 62 ++++++++++++++----- crates/router/src/connector/utils.rs | 1 + 3 files changed, 55 insertions(+), 14 deletions(-) diff --git a/crates/router/src/connector/helcim.rs b/crates/router/src/connector/helcim.rs index a67ad38b7b19..7c4357755dda 100644 --- a/crates/router/src/connector/helcim.rs +++ b/crates/router/src/connector/helcim.rs @@ -314,6 +314,12 @@ impl ConnectorIntegration CustomResult { self.build_error_response(res) } + + fn get_multiple_capture_sync_method( + &self, + ) -> CustomResult { + Ok(services::CaptureSyncMethod::Individual) + } } impl ConnectorIntegration diff --git a/crates/router/src/connector/helcim/transformers.rs b/crates/router/src/connector/helcim/transformers.rs index c40af075af48..61ed74fbb70f 100644 --- a/crates/router/src/connector/helcim/transformers.rs +++ b/crates/router/src/connector/helcim/transformers.rs @@ -3,7 +3,7 @@ use masking::Secret; use serde::{Deserialize, Serialize}; use crate::{ - connector::utils::CardData, + connector::utils::{self, CardData}, core::errors, types::{self, api, storage::enums}, }; @@ -128,6 +128,7 @@ impl From for enums::AttemptStatus { #[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] #[serde(rename_all = "camelCase")] pub struct HelcimPaymentsResponse { + amount: i64, status: HelcimPaymentStatus, transaction_id: u64, #[serde(rename = "type")] @@ -170,6 +171,27 @@ impl } } +impl utils::MultipleCaptureSyncResponse for HelcimPaymentsResponse { + fn get_connector_capture_id(&self) -> String { + self.transaction_id.to_string() + } + + fn get_capture_attempt_status(&self) -> diesel_models::enums::AttemptStatus { + enums::AttemptStatus::from(self.to_owned()) + } + + fn is_capture_response(&self) -> bool { + true + } + + fn get_amount_captured(&self) -> Option { + Some(self.amount) + } + fn get_connector_reference_id(&self) -> Option { + None + } +} + impl TryFrom< types::ResponseRouterData< @@ -189,20 +211,32 @@ impl types::PaymentsResponseData, >, ) -> Result { - 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, + 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 }), - status: enums::AttemptStatus::from(item.response), - ..item.data - }) + types::SyncRequestType::MultipleCaptureSync(_) => { + 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 + }) + } + } } } diff --git a/crates/router/src/connector/utils.rs b/crates/router/src/connector/utils.rs index c9904b41e6ab..e9944d609306 100644 --- a/crates/router/src/connector/utils.rs +++ b/crates/router/src/connector/utils.rs @@ -279,6 +279,7 @@ impl PaymentsAuthorizeRequestData for types::PaymentsAuthorizeData { match self.capture_method { Some(diesel_models::enums::CaptureMethod::Automatic) | None => Ok(true), Some(diesel_models::enums::CaptureMethod::Manual) => Ok(false), + Some(diesel_models::enums::CaptureMethod::ManualMultiple) => Ok(false), Some(_) => Err(errors::ConnectorError::CaptureMethodNotSupported.into()), } } From 2aaaa7adbb240362ea63bf87602fec2bd7d9e144 Mon Sep 17 00:00:00 2001 From: DEEPANSHU BANSAL Date: Thu, 21 Sep 2023 19:45:12 +0530 Subject: [PATCH 05/20] feat(connector): [HELCIM] Implement Refunds and RSYNC --- crates/router/src/connector/helcim.rs | 59 +++++++--- .../src/connector/helcim/transformers.rs | 111 ++++++++++++------ crates/router/src/connector/utils.rs | 1 - openapi/openapi_spec.json | 1 + 4 files changed, 122 insertions(+), 50 deletions(-) diff --git a/crates/router/src/connector/helcim.rs b/crates/router/src/connector/helcim.rs index 7c4357755dda..40040b7d9ca4 100644 --- a/crates/router/src/connector/helcim.rs +++ b/crates/router/src/connector/helcim.rs @@ -315,11 +315,11 @@ impl ConnectorIntegration CustomResult { - Ok(services::CaptureSyncMethod::Individual) - } + // fn get_multiple_capture_sync_method( + // &self, + // ) -> CustomResult { + // Ok(services::CaptureSyncMethod::Individual) + // } } impl ConnectorIntegration @@ -409,9 +409,23 @@ impl ConnectorIntegration, - connectors: &settings::Connectors, + _connectors: &settings::Connectors, ) -> CustomResult)>, errors::ConnectorError> { - self.build_headers(req, connectors) + let mut header = vec![( + headers::CONTENT_TYPE.to_string(), + types::PaymentsAuthorizeType::get_content_type(self) + .to_string() + .into(), + )]; + let mut api_key = self.get_auth_header(&req.connector_auth_type)?; + + let mut idempotency_key = vec![( + headers::IDEMPOTENCY_KEY.to_string(), + format!("{}_", req.request.refund_id).into_masked(), + )]; + header.append(&mut api_key); + header.append(&mut idempotency_key); + Ok(header) } fn get_content_type(&self) -> &'static str { @@ -421,9 +435,9 @@ impl ConnectorIntegration, - _connectors: &settings::Connectors, + connectors: &settings::Connectors, ) -> CustomResult { - Err(errors::ConnectorError::NotImplemented("get_url method".to_string()).into()) + Ok(format!("{}v2/payment/refund", self.base_url(connectors))) } fn get_request_body( @@ -484,9 +498,17 @@ impl ConnectorIntegration CustomResult)>, errors::ConnectorError> { - self.build_headers(req, connectors) + let mut header = vec![( + headers::CONTENT_TYPE.to_string(), + types::PaymentsAuthorizeType::get_content_type(self) + .to_string() + .into(), + )]; + let mut api_key = self.get_auth_header(&req.connector_auth_type)?; + header.append(&mut api_key); + Ok(header) } fn get_content_type(&self) -> &'static str { @@ -495,10 +517,19 @@ impl ConnectorIntegration CustomResult { - 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( diff --git a/crates/router/src/connector/helcim/transformers.rs b/crates/router/src/connector/helcim/transformers.rs index 61ed74fbb70f..1d2ae33c6dd9 100644 --- a/crates/router/src/connector/helcim/transformers.rs +++ b/crates/router/src/connector/helcim/transformers.rs @@ -3,7 +3,7 @@ use masking::Secret; use serde::{Deserialize, Serialize}; use crate::{ - connector::utils::{self, CardData}, + connector::utils::{to_connector_meta, CardData}, core::errors, types::{self, api, storage::enums}, }; @@ -171,26 +171,31 @@ impl } } -impl utils::MultipleCaptureSyncResponse for HelcimPaymentsResponse { - fn get_connector_capture_id(&self) -> String { - self.transaction_id.to_string() - } +#[derive(Debug, Deserialize, Serialize)] +pub struct HelcimMetaData { + pub capture_id: u64, +} - fn get_capture_attempt_status(&self) -> diesel_models::enums::AttemptStatus { - enums::AttemptStatus::from(self.to_owned()) - } +// impl utils::MultipleCaptureSyncResponse for HelcimPaymentsResponse { +// fn get_connector_capture_id(&self) -> String { +// self.transaction_id.to_string() +// } - fn is_capture_response(&self) -> bool { - true - } +// fn get_capture_attempt_status(&self) -> diesel_models::enums::AttemptStatus { +// enums::AttemptStatus::from(self.to_owned()) +// } - fn get_amount_captured(&self) -> Option { - Some(self.amount) - } - fn get_connector_reference_id(&self) -> Option { - None - } -} +// fn is_capture_response(&self) -> bool { +// true +// } + +// fn get_amount_captured(&self) -> Option { +// Some(self.amount) +// } +// fn get_connector_reference_id(&self) -> Option { +// None +// } +// } impl TryFrom< @@ -227,14 +232,18 @@ impl ..item.data }), types::SyncRequestType::MultipleCaptureSync(_) => { - 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 - }) + 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 + // }) } } } @@ -283,6 +292,9 @@ impl types::PaymentsResponseData, >, ) -> Result { + let connector_metadata = Some(serde_json::json!(HelcimMetaData { + capture_id: item.response.transaction_id, + })); Ok(Self { response: Ok(types::PaymentsResponseData::TransactionResponse { resource_id: types::ResponseId::ConnectorTransactionId( @@ -290,7 +302,7 @@ impl ), redirection_data: None, mandate_reference: None, - connector_metadata: None, + connector_metadata, network_txn_id: None, connector_response_reference_id: None, }), @@ -304,15 +316,23 @@ impl // REFUND : // Type definition for RefundRequest #[derive(Default, Debug, Serialize)] +#[serde(rename_all = "camelCase")] pub struct HelcimRefundRequest { - pub amount: i64, + amount: i64, + original_transaction_id: u64, + ip_address: Secret, } impl TryFrom<&types::RefundsRouterData> for HelcimRefundRequest { type Error = error_stack::Report; fn try_from(item: &types::RefundsRouterData) -> Result { + let helcim_meta_data: HelcimMetaData = + to_connector_meta(item.request.connector_metadata.clone())?; + let original_transaction_id = helcim_meta_data.capture_id; Ok(Self { amount: item.request.refund_amount, + original_transaction_id, + ip_address: Secret::new("127.0.0.1".to_string()), }) } } @@ -339,11 +359,32 @@ impl From for enums::RefundStatus { } } +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] +#[serde(rename_all = "lowercase")] +pub enum HelcimRefundTransactionType { + Refund, +} + //TODO: Fill the struct with respective fields -#[derive(Default, Debug, Clone, Serialize, Deserialize)] +#[derive(Debug, Clone, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] pub struct RefundResponse { - id: String, - status: RefundStatus, + amount: i64, + status: HelcimPaymentStatus, + transaction_id: u64, + #[serde(rename = "type")] + transaction_type: HelcimRefundTransactionType, +} + +impl From 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> @@ -355,8 +396,8 @@ impl TryFrom> ) -> Result { 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 }) @@ -372,8 +413,8 @@ impl TryFrom> ) -> Result { 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 }) diff --git a/crates/router/src/connector/utils.rs b/crates/router/src/connector/utils.rs index e9944d609306..c9904b41e6ab 100644 --- a/crates/router/src/connector/utils.rs +++ b/crates/router/src/connector/utils.rs @@ -279,7 +279,6 @@ impl PaymentsAuthorizeRequestData for types::PaymentsAuthorizeData { match self.capture_method { Some(diesel_models::enums::CaptureMethod::Automatic) | None => Ok(true), Some(diesel_models::enums::CaptureMethod::Manual) => Ok(false), - Some(diesel_models::enums::CaptureMethod::ManualMultiple) => Ok(false), Some(_) => Err(errors::ConnectorError::CaptureMethodNotSupported.into()), } } diff --git a/openapi/openapi_spec.json b/openapi/openapi_spec.json index 33f86dab7b70..a0fad40a09c3 100644 --- a/openapi/openapi_spec.json +++ b/openapi/openapi_spec.json @@ -3855,6 +3855,7 @@ "globalpay", "globepay", "gocardless", + "helcim", "iatapay", "klarna", "mollie", From 36baf93c016a517b60abb9044086e1ad769fa25e Mon Sep 17 00:00:00 2001 From: DEEPANSHU BANSAL Date: Fri, 22 Sep 2023 15:25:19 +0530 Subject: [PATCH 06/20] feat(connector): [HELCIM] Add currency base unit --- crates/router/src/connector/helcim.rs | 28 ++++- .../src/connector/helcim/transformers.rs | 118 ++++++++++-------- 2 files changed, 94 insertions(+), 52 deletions(-) diff --git a/crates/router/src/connector/helcim.rs b/crates/router/src/connector/helcim.rs index 40040b7d9ca4..b666d5259956 100644 --- a/crates/router/src/connector/helcim.rs +++ b/crates/router/src/connector/helcim.rs @@ -83,6 +83,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" } @@ -184,7 +188,13 @@ impl ConnectorIntegration CustomResult, 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::::encode_to_string_of_json, @@ -349,7 +359,13 @@ impl ConnectorIntegration CustomResult, errors::ConnectorError> { - let connector_req = helcim::HelcimCaptureRequest::try_from(req)?; + 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::::encode_to_string_of_json, @@ -444,7 +460,13 @@ impl ConnectorIntegration, ) -> CustomResult, 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::::encode_to_string_of_json, diff --git a/crates/router/src/connector/helcim/transformers.rs b/crates/router/src/connector/helcim/transformers.rs index 1d2ae33c6dd9..438a70a02e77 100644 --- a/crates/router/src/connector/helcim/transformers.rs +++ b/crates/router/src/connector/helcim/transformers.rs @@ -3,24 +3,56 @@ use masking::Secret; use serde::{Deserialize, Serialize}; use crate::{ - connector::utils::{to_connector_meta, CardData}, + connector::utils::{self, to_connector_meta, CardData}, core::errors, types::{self, api, storage::enums}, }; +#[derive(Debug, Serialize)] +pub struct HelcimRouterData { + pub amount: f64, + pub router_data: T, +} + +impl + TryFrom<( + &types::api::CurrencyUnit, + types::storage::enums::Currency, + i64, + T, + )> for HelcimRouterData +{ + type Error = error_stack::Report; + fn try_from( + (currency_unit, currency, amount, item): ( + &types::api::CurrencyUnit, + types::storage::enums::Currency, + i64, + T, + ), + ) -> Result { + let amount = utils::get_amount_as_f64(currency_unit, amount, currency)?; + Ok(Self { + amount, + router_data: item, + }) + } +} + //TODO: Fill the struct with respective fields -#[derive(Default, Debug, Serialize, Eq, PartialEq)] +#[derive(Debug, Serialize)] #[serde(rename_all = "camelCase")] pub struct HelcimPaymentsRequest { - amount: i64, + amount: f64, currency: enums::Currency, ip_address: Secret, // ip_address: Secret, card_data: HelcimCard, billing_address: HelcimBillingAddress, + ecommerce: Option, } -#[derive(Default, Debug, Serialize, Eq, PartialEq)] +#[derive(Debug, Serialize)] #[serde(rename_all = "camelCase")] pub struct HelcimBillingAddress { name: Secret, @@ -28,7 +60,7 @@ pub struct HelcimBillingAddress { postal_code: String, } -#[derive(Default, Debug, Serialize, Eq, PartialEq)] +#[derive(Debug, Serialize)] #[serde(rename_all = "camelCase")] pub struct HelcimCard { card_number: cards::CardNumber, @@ -36,10 +68,12 @@ pub struct HelcimCard { card_c_v_v: Secret, } -impl TryFrom<&types::PaymentsAuthorizeRouterData> for HelcimPaymentsRequest { +impl TryFrom<&HelcimRouterData<&types::PaymentsAuthorizeRouterData>> for HelcimPaymentsRequest { type Error = error_stack::Report; - fn try_from(item: &types::PaymentsAuthorizeRouterData) -> Result { - match item.request.payment_method_data.clone() { + fn try_from( + item: &HelcimRouterData<&types::PaymentsAuthorizeRouterData>, + ) -> Result { + match item.router_data.request.payment_method_data.clone() { api::PaymentMethodData::Card(req_card) => { let card_data = HelcimCard { card_expiry: req_card @@ -54,11 +88,12 @@ impl TryFrom<&types::PaymentsAuthorizeRouterData> for HelcimPaymentsRequest { postal_code: "H0H0H0".to_string(), }; Ok(Self { - amount: item.request.amount, - currency: item.request.currency, + amount: item.amount.to_owned(), + currency: item.router_data.request.currency, ip_address: Secret::new("127.0.0.1".to_string()), card_data, billing_address, + ecommerce: None, }) } _ => Err(errors::ConnectorError::NotImplemented("Payment methods".to_string()).into()), @@ -85,14 +120,14 @@ impl TryFrom<&types::ConnectorAuthType> for HelcimAuthType { } // PaymentsResponse //TODO: Append the remaining status flags -#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] +#[derive(Debug, Deserialize)] #[serde(rename_all = "UPPERCASE")] pub enum HelcimPaymentStatus { Approved, Declined, } -#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] +#[derive(Debug, Deserialize)] #[serde(rename_all = "lowercase")] pub enum HelcimTransactionType { Purchase, @@ -125,10 +160,9 @@ impl From for enums::AttemptStatus { } //TODO: Fill the struct with respective fields -#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] +#[derive(Debug, Deserialize)] #[serde(rename_all = "camelCase")] pub struct HelcimPaymentsResponse { - amount: i64, status: HelcimPaymentStatus, transaction_id: u64, #[serde(rename = "type")] @@ -253,22 +287,27 @@ impl #[serde(rename_all = "camelCase")] pub struct HelcimCaptureRequest { pre_auth_transaction_id: u64, - amount: i64, + amount: f64, ip_address: Secret, + ecommerce: Option, } -impl TryFrom<&types::PaymentsCaptureRouterData> for HelcimCaptureRequest { +impl TryFrom<&HelcimRouterData<&types::PaymentsCaptureRouterData>> for HelcimCaptureRequest { type Error = error_stack::Report; - fn try_from(item: &types::PaymentsCaptureRouterData) -> Result { + fn try_from( + item: &HelcimRouterData<&types::PaymentsCaptureRouterData>, + ) -> Result { Ok(Self { pre_auth_transaction_id: item + .router_data .request .connector_transaction_id .parse::() .into_report() .change_context(errors::ConnectorError::RequestEncodingFailed)?, - amount: item.request.amount_to_capture, + amount: item.amount, ip_address: Secret::new("127.0.0.1".to_string()), + ecommerce: None, }) } } @@ -315,61 +354,42 @@ impl //TODO: Fill the struct with respective fields // REFUND : // Type definition for RefundRequest -#[derive(Default, Debug, Serialize)] +#[derive(Debug, Serialize)] #[serde(rename_all = "camelCase")] pub struct HelcimRefundRequest { - amount: i64, + amount: f64, original_transaction_id: u64, ip_address: Secret, + ecommerce: Option, } -impl TryFrom<&types::RefundsRouterData> for HelcimRefundRequest { +impl TryFrom<&HelcimRouterData<&types::RefundsRouterData>> for HelcimRefundRequest { type Error = error_stack::Report; - fn try_from(item: &types::RefundsRouterData) -> Result { + fn try_from( + item: &HelcimRouterData<&types::RefundsRouterData>, + ) -> Result { let helcim_meta_data: HelcimMetaData = - to_connector_meta(item.request.connector_metadata.clone())?; + to_connector_meta(item.router_data.request.connector_metadata.clone())?; let original_transaction_id = helcim_meta_data.capture_id; Ok(Self { - amount: item.request.refund_amount, + amount: item.amount, original_transaction_id, ip_address: Secret::new("127.0.0.1".to_string()), + ecommerce: None, }) } } -// Type definition for Refund Response - -#[allow(dead_code)] -#[derive(Debug, Serialize, Default, Deserialize, Clone)] -pub enum RefundStatus { - Succeeded, - Failed, - #[default] - Processing, -} - -impl From 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 - } - } -} - -#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] +#[derive(Debug, Deserialize)] #[serde(rename_all = "lowercase")] pub enum HelcimRefundTransactionType { Refund, } //TODO: Fill the struct with respective fields -#[derive(Debug, Clone, Serialize, Deserialize)] +#[derive(Debug, Deserialize)] #[serde(rename_all = "camelCase")] pub struct RefundResponse { - amount: i64, status: HelcimPaymentStatus, transaction_id: u64, #[serde(rename = "type")] From c70e83f0e6712077f675e0b74ce9d72eb465ecba Mon Sep 17 00:00:00 2001 From: DEEPANSHU BANSAL Date: Fri, 22 Sep 2023 16:25:37 +0530 Subject: [PATCH 07/20] feat(connector): [HELCIM] Softcode User IP --- .../src/connector/helcim/transformers.rs | 35 ++++++++++++++----- crates/router/src/connector/utils.rs | 12 +++++++ .../router/src/core/payments/transformers.rs | 11 ++++++ crates/router/src/core/utils.rs | 10 ++++++ crates/router/src/types.rs | 2 ++ crates/router/tests/connectors/aci.rs | 1 + crates/router/tests/connectors/utils.rs | 2 ++ 7 files changed, 64 insertions(+), 9 deletions(-) diff --git a/crates/router/src/connector/helcim/transformers.rs b/crates/router/src/connector/helcim/transformers.rs index 438a70a02e77..f904f0ff3fa6 100644 --- a/crates/router/src/connector/helcim/transformers.rs +++ b/crates/router/src/connector/helcim/transformers.rs @@ -1,9 +1,13 @@ +use common_utils::pii::IpAddress; use error_stack::{IntoReport, ResultExt}; use masking::Secret; use serde::{Deserialize, Serialize}; use crate::{ - connector::utils::{self, to_connector_meta, CardData}, + connector::utils::{ + self, to_connector_meta, BrowserInformationData, CardData, PaymentsAuthorizeRequestData, + PaymentsCaptureRequestData, RefundsRequestData, + }, core::errors, types::{self, api, storage::enums}, }; @@ -45,8 +49,7 @@ impl pub struct HelcimPaymentsRequest { amount: f64, currency: enums::Currency, - ip_address: Secret, - // ip_address: Secret, + ip_address: Secret, card_data: HelcimCard, billing_address: HelcimBillingAddress, ecommerce: Option, @@ -81,16 +84,20 @@ impl TryFrom<&HelcimRouterData<&types::PaymentsAuthorizeRouterData>> for HelcimP card_number: req_card.card_number, card_c_v_v: req_card.card_cvc, }; - // let ip_address = item.request.get_browser_info()?.get_ip_address()?; let billing_address = HelcimBillingAddress { name: req_card.card_holder_name, street1: "Jump Street 21".to_string(), postal_code: "H0H0H0".to_string(), }; + 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: Secret::new("127.0.0.1".to_string()), + ip_address, card_data, billing_address, ecommerce: None, @@ -288,7 +295,7 @@ impl pub struct HelcimCaptureRequest { pre_auth_transaction_id: u64, amount: f64, - ip_address: Secret, + ip_address: Secret, ecommerce: Option, } @@ -297,6 +304,11 @@ impl TryFrom<&HelcimRouterData<&types::PaymentsCaptureRouterData>> for HelcimCap fn try_from( item: &HelcimRouterData<&types::PaymentsCaptureRouterData>, ) -> Result { + let ip_address = item + .router_data + .request + .get_browser_info()? + .get_ip_address()?; Ok(Self { pre_auth_transaction_id: item .router_data @@ -306,7 +318,7 @@ impl TryFrom<&HelcimRouterData<&types::PaymentsCaptureRouterData>> for HelcimCap .into_report() .change_context(errors::ConnectorError::RequestEncodingFailed)?, amount: item.amount, - ip_address: Secret::new("127.0.0.1".to_string()), + ip_address, ecommerce: None, }) } @@ -359,7 +371,7 @@ impl pub struct HelcimRefundRequest { amount: f64, original_transaction_id: u64, - ip_address: Secret, + ip_address: Secret, ecommerce: Option, } @@ -371,10 +383,15 @@ impl TryFrom<&HelcimRouterData<&types::RefundsRouterData>> for HelcimRefun let helcim_meta_data: HelcimMetaData = to_connector_meta(item.router_data.request.connector_metadata.clone())?; let original_transaction_id = helcim_meta_data.capture_id; + let ip_address = item + .router_data + .request + .get_browser_info()? + .get_ip_address()?; Ok(Self { amount: item.amount, original_transaction_id, - ip_address: Secret::new("127.0.0.1".to_string()), + ip_address, ecommerce: None, }) } diff --git a/crates/router/src/connector/utils.rs b/crates/router/src/connector/utils.rs index c9904b41e6ab..85627bea1c7d 100644 --- a/crates/router/src/connector/utils.rs +++ b/crates/router/src/connector/utils.rs @@ -248,12 +248,18 @@ impl PaymentsPreProcessingData for types::PaymentsPreProcessingData { pub trait PaymentsCaptureRequestData { fn is_multiple_capture(&self) -> bool; + fn get_browser_info(&self) -> Result; } impl PaymentsCaptureRequestData for types::PaymentsCaptureData { fn is_multiple_capture(&self) -> bool { self.multiple_capture_data.is_some() } + fn get_browser_info(&self) -> Result { + self.browser_info + .clone() + .ok_or_else(missing_field_err("browser_info")) + } } pub trait PaymentsAuthorizeRequestData { @@ -511,6 +517,7 @@ impl PaymentsCancelRequestData for PaymentsCancelData { pub trait RefundsRequestData { fn get_connector_refund_id(&self) -> Result; fn get_webhook_url(&self) -> Result; + fn get_browser_info(&self) -> Result; } impl RefundsRequestData for types::RefundsData { @@ -526,6 +533,11 @@ impl RefundsRequestData for types::RefundsData { .clone() .ok_or_else(missing_field_err("webhook_url")) } + fn get_browser_info(&self) -> Result { + self.browser_info + .clone() + .ok_or_else(missing_field_err("browser_info")) + } } #[derive(Clone, Debug, serde::Serialize)] diff --git a/crates/router/src/core/payments/transformers.rs b/crates/router/src/core/payments/transformers.rs index 9723b4c92f94..8d3a2aaaaf07 100644 --- a/crates/router/src/core/payments/transformers.rs +++ b/crates/router/src/core/payments/transformers.rs @@ -1048,6 +1048,16 @@ impl TryFrom> for types::PaymentsCaptureD .payment_attempt .amount_to_capture .map_or(payment_data.amount.into(), |capture_amount| capture_amount); + let browser_info: Option = 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, @@ -1067,6 +1077,7 @@ impl TryFrom> for types::PaymentsCaptureD }), None => None, }, + browser_info, }) } } diff --git a/crates/router/src/core/utils.rs b/crates/router/src/core/utils.rs index 14a3b777149b..9b7d57d27f7a 100644 --- a/crates/router/src/core/utils.rs +++ b/crates/router/src/core/utils.rs @@ -270,6 +270,15 @@ pub async fn construct_refund_router_data<'a, F>( None }; + let browser_info: Option = 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(), @@ -298,6 +307,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 { diff --git a/crates/router/src/types.rs b/crates/router/src/types.rs index 4fd3ffee0376..76ed89341654 100644 --- a/crates/router/src/types.rs +++ b/crates/router/src/types.rs @@ -383,6 +383,7 @@ pub struct PaymentsCaptureData { pub payment_amount: i64, pub multiple_capture_data: Option, pub connector_meta: Option, + pub browser_info: Option, } #[allow(dead_code)] @@ -702,6 +703,7 @@ pub struct RefundsData { pub refund_amount: i64, /// Arbitrary metadata required for refund pub connector_metadata: Option, + pub browser_info: Option, } #[derive(Clone, Debug, Default, serde::Serialize, serde::Deserialize)] diff --git a/crates/router/tests/connectors/aci.rs b/crates/router/tests/connectors/aci.rs index 8823c12b79b2..9741b992da68 100644 --- a/crates/router/tests/connectors/aci.rs +++ b/crates/router/tests/connectors/aci.rs @@ -123,6 +123,7 @@ fn construct_refund_router_data() -> types::RefundsRouterData { connector_metadata: None, reason: None, connector_refund_id: None, + browser_info: None, }, payment_method_id: None, response: Err(types::ErrorResponse::default()), diff --git a/crates/router/tests/connectors/utils.rs b/crates/router/tests/connectors/utils.rs index 8a043261ed5f..4f070e49722a 100644 --- a/crates/router/tests/connectors/utils.rs +++ b/crates/router/tests/connectors/utils.rs @@ -388,6 +388,7 @@ pub trait ConnectorActions: Connector { connector_metadata: None, reason: None, connector_refund_id: Some(refund_id), + browser_info: None, }), payment_info, ); @@ -957,6 +958,7 @@ impl Default for PaymentRefundType { connector_metadata: None, reason: Some("Customer returned product".to_string()), connector_refund_id: None, + browser_info: None, }; Self(data) } From fa6ba4ccfda206875cd00a9b2f98b206e1595400 Mon Sep 17 00:00:00 2001 From: DEEPANSHU BANSAL Date: Mon, 25 Sep 2023 18:16:16 +0530 Subject: [PATCH 08/20] feat(connector): [HELCIM] Add optional billing address fields --- crates/common_enums/src/transformers.rs | 255 ++++++++++++++++++ .../src/connector/helcim/transformers.rs | 34 ++- 2 files changed, 281 insertions(+), 8 deletions(-) diff --git a/crates/common_enums/src/transformers.rs b/crates/common_enums/src/transformers.rs index 8f967ddf9406..73f736cdeefa 100644 --- a/crates/common_enums/src/transformers.rs +++ b/crates/common_enums/src/transformers.rs @@ -10,6 +10,261 @@ impl Display for NumericCountryCodeParseError { } } +impl CountryAlpha2 { + pub const fn from_alpha2_to_alpha3(code: Self) -> CountryAlpha3 { + match code { + Self::AF => CountryAlpha3::AFG, + Self::AX => CountryAlpha3::ALA, + Self::AL => CountryAlpha3::ALB, + Self::DZ => CountryAlpha3::DZA, + Self::AS => CountryAlpha3::ASM, + Self::AD => CountryAlpha3::AND, + Self::AO => CountryAlpha3::AGO, + Self::AI => CountryAlpha3::AIA, + Self::AQ => CountryAlpha3::ATA, + Self::AG => CountryAlpha3::ATG, + Self::AR => CountryAlpha3::ARG, + Self::AM => CountryAlpha3::ARM, + Self::AW => CountryAlpha3::ABW, + Self::AU => CountryAlpha3::AUS, + Self::AT => CountryAlpha3::AUT, + Self::AZ => CountryAlpha3::AZE, + Self::BS => CountryAlpha3::BHS, + Self::BH => CountryAlpha3::BHR, + Self::BD => CountryAlpha3::BGD, + Self::BB => CountryAlpha3::BRB, + Self::BY => CountryAlpha3::BLR, + Self::BE => CountryAlpha3::BEL, + Self::BZ => CountryAlpha3::BLZ, + Self::BJ => CountryAlpha3::BEN, + Self::BM => CountryAlpha3::BMU, + Self::BT => CountryAlpha3::BTN, + Self::BO => CountryAlpha3::BOL, + Self::BQ => CountryAlpha3::BES, + Self::BA => CountryAlpha3::BIH, + Self::BW => CountryAlpha3::BWA, + Self::BV => CountryAlpha3::BVT, + Self::BR => CountryAlpha3::BRA, + Self::IO => CountryAlpha3::IOT, + Self::BN => CountryAlpha3::BRN, + Self::BG => CountryAlpha3::BGR, + Self::BF => CountryAlpha3::BFA, + Self::BI => CountryAlpha3::BDI, + Self::CV => CountryAlpha3::CPV, + Self::KH => CountryAlpha3::KHM, + Self::CM => CountryAlpha3::CMR, + Self::CA => CountryAlpha3::CAN, + Self::KY => CountryAlpha3::CYM, + Self::CF => CountryAlpha3::CAF, + Self::TD => CountryAlpha3::TCD, + Self::CL => CountryAlpha3::CHL, + Self::CN => CountryAlpha3::CHN, + Self::CX => CountryAlpha3::CXR, + Self::CC => CountryAlpha3::CCK, + Self::CO => CountryAlpha3::COL, + Self::KM => CountryAlpha3::COM, + Self::CG => CountryAlpha3::COG, + Self::CD => CountryAlpha3::COD, + Self::CK => CountryAlpha3::COK, + Self::CR => CountryAlpha3::CRI, + Self::CI => CountryAlpha3::CIV, + Self::HR => CountryAlpha3::HRV, + Self::CU => CountryAlpha3::CUB, + Self::CW => CountryAlpha3::CUW, + Self::CY => CountryAlpha3::CYP, + Self::CZ => CountryAlpha3::CZE, + Self::DK => CountryAlpha3::DNK, + Self::DJ => CountryAlpha3::DJI, + Self::DM => CountryAlpha3::DMA, + Self::DO => CountryAlpha3::DOM, + Self::EC => CountryAlpha3::ECU, + Self::EG => CountryAlpha3::EGY, + Self::SV => CountryAlpha3::SLV, + Self::GQ => CountryAlpha3::GNQ, + Self::ER => CountryAlpha3::ERI, + Self::EE => CountryAlpha3::EST, + Self::ET => CountryAlpha3::ETH, + Self::FK => CountryAlpha3::FLK, + Self::FO => CountryAlpha3::FRO, + Self::FJ => CountryAlpha3::FJI, + Self::FI => CountryAlpha3::FIN, + Self::FR => CountryAlpha3::FRA, + Self::GF => CountryAlpha3::GUF, + Self::PF => CountryAlpha3::PYF, + Self::TF => CountryAlpha3::ATF, + Self::GA => CountryAlpha3::GAB, + Self::GM => CountryAlpha3::GMB, + Self::GE => CountryAlpha3::GEO, + Self::DE => CountryAlpha3::DEU, + Self::GH => CountryAlpha3::GHA, + Self::GI => CountryAlpha3::GIB, + Self::GR => CountryAlpha3::GRC, + Self::GL => CountryAlpha3::GRL, + Self::GD => CountryAlpha3::GRD, + Self::GP => CountryAlpha3::GLP, + Self::GU => CountryAlpha3::GUM, + Self::GT => CountryAlpha3::GTM, + Self::GG => CountryAlpha3::GGY, + Self::GN => CountryAlpha3::GIN, + Self::GW => CountryAlpha3::GNB, + Self::GY => CountryAlpha3::GUY, + Self::HT => CountryAlpha3::HTI, + Self::HM => CountryAlpha3::HMD, + Self::VA => CountryAlpha3::VAT, + Self::HN => CountryAlpha3::HND, + Self::HK => CountryAlpha3::HKG, + Self::HU => CountryAlpha3::HUN, + Self::IS => CountryAlpha3::ISL, + Self::IN => CountryAlpha3::IND, + Self::ID => CountryAlpha3::IDN, + Self::IR => CountryAlpha3::IRN, + Self::IQ => CountryAlpha3::IRQ, + Self::IE => CountryAlpha3::IRL, + Self::IM => CountryAlpha3::IMN, + Self::IL => CountryAlpha3::ISR, + Self::IT => CountryAlpha3::ITA, + Self::JM => CountryAlpha3::JAM, + Self::JP => CountryAlpha3::JPN, + Self::JE => CountryAlpha3::JEY, + Self::JO => CountryAlpha3::JOR, + Self::KZ => CountryAlpha3::KAZ, + Self::KE => CountryAlpha3::KEN, + Self::KI => CountryAlpha3::KIR, + Self::KP => CountryAlpha3::PRK, + Self::KR => CountryAlpha3::KOR, + Self::KW => CountryAlpha3::KWT, + Self::KG => CountryAlpha3::KGZ, + Self::LA => CountryAlpha3::LAO, + Self::LV => CountryAlpha3::LVA, + Self::LB => CountryAlpha3::LBN, + Self::LS => CountryAlpha3::LSO, + Self::LR => CountryAlpha3::LBR, + Self::LY => CountryAlpha3::LBY, + Self::LI => CountryAlpha3::LIE, + Self::LT => CountryAlpha3::LTU, + Self::LU => CountryAlpha3::LUX, + Self::MO => CountryAlpha3::MAC, + Self::MK => CountryAlpha3::MKD, + Self::MG => CountryAlpha3::MDG, + Self::MW => CountryAlpha3::MWI, + Self::MY => CountryAlpha3::MYS, + Self::MV => CountryAlpha3::MDV, + Self::ML => CountryAlpha3::MLI, + Self::MT => CountryAlpha3::MLT, + Self::MH => CountryAlpha3::MHL, + Self::MQ => CountryAlpha3::MTQ, + Self::MR => CountryAlpha3::MRT, + Self::MU => CountryAlpha3::MUS, + Self::YT => CountryAlpha3::MYT, + Self::MX => CountryAlpha3::MEX, + Self::FM => CountryAlpha3::FSM, + Self::MD => CountryAlpha3::MDA, + Self::MC => CountryAlpha3::MCO, + Self::MN => CountryAlpha3::MNG, + Self::ME => CountryAlpha3::MNE, + Self::MS => CountryAlpha3::MSR, + Self::MA => CountryAlpha3::MAR, + Self::MZ => CountryAlpha3::MOZ, + Self::MM => CountryAlpha3::MMR, + Self::NA => CountryAlpha3::NAM, + Self::NR => CountryAlpha3::NRU, + Self::NP => CountryAlpha3::NPL, + Self::NL => CountryAlpha3::NLD, + Self::NC => CountryAlpha3::NCL, + Self::NZ => CountryAlpha3::NZL, + Self::NI => CountryAlpha3::NIC, + Self::NE => CountryAlpha3::NER, + Self::NG => CountryAlpha3::NGA, + Self::NU => CountryAlpha3::NIU, + Self::NF => CountryAlpha3::NFK, + Self::MP => CountryAlpha3::MNP, + Self::NO => CountryAlpha3::NOR, + Self::OM => CountryAlpha3::OMN, + Self::PK => CountryAlpha3::PAK, + Self::PW => CountryAlpha3::PLW, + Self::PS => CountryAlpha3::PSE, + Self::PA => CountryAlpha3::PAN, + Self::PG => CountryAlpha3::PNG, + Self::PY => CountryAlpha3::PRY, + Self::PE => CountryAlpha3::PER, + Self::PH => CountryAlpha3::PHL, + Self::PN => CountryAlpha3::PCN, + Self::PL => CountryAlpha3::POL, + Self::PT => CountryAlpha3::PRT, + Self::PR => CountryAlpha3::PRI, + Self::QA => CountryAlpha3::QAT, + Self::RE => CountryAlpha3::REU, + Self::RO => CountryAlpha3::ROU, + Self::RU => CountryAlpha3::RUS, + Self::RW => CountryAlpha3::RWA, + Self::BL => CountryAlpha3::BLM, + Self::SH => CountryAlpha3::SHN, + Self::KN => CountryAlpha3::KNA, + Self::LC => CountryAlpha3::LCA, + Self::MF => CountryAlpha3::MAF, + Self::PM => CountryAlpha3::SPM, + Self::VC => CountryAlpha3::VCT, + Self::WS => CountryAlpha3::WSM, + Self::SM => CountryAlpha3::SMR, + Self::ST => CountryAlpha3::STP, + Self::SA => CountryAlpha3::SAU, + Self::SN => CountryAlpha3::SEN, + Self::RS => CountryAlpha3::SRB, + Self::SC => CountryAlpha3::SYC, + Self::SL => CountryAlpha3::SLE, + Self::SG => CountryAlpha3::SGP, + Self::SX => CountryAlpha3::SXM, + Self::SK => CountryAlpha3::SVK, + Self::SI => CountryAlpha3::SVN, + Self::SB => CountryAlpha3::SLB, + Self::SO => CountryAlpha3::SOM, + Self::ZA => CountryAlpha3::ZAF, + Self::GS => CountryAlpha3::SGS, + Self::SS => CountryAlpha3::SSD, + Self::ES => CountryAlpha3::ESP, + Self::LK => CountryAlpha3::LKA, + Self::SD => CountryAlpha3::SDN, + Self::SR => CountryAlpha3::SUR, + Self::SJ => CountryAlpha3::SJM, + Self::SZ => CountryAlpha3::SWZ, + Self::SE => CountryAlpha3::SWE, + Self::CH => CountryAlpha3::CHE, + Self::SY => CountryAlpha3::SYR, + Self::TW => CountryAlpha3::TWN, + Self::TJ => CountryAlpha3::TJK, + Self::TZ => CountryAlpha3::TZA, + Self::TH => CountryAlpha3::THA, + Self::TL => CountryAlpha3::TLS, + Self::TG => CountryAlpha3::TGO, + Self::TK => CountryAlpha3::TKL, + Self::TO => CountryAlpha3::TON, + Self::TT => CountryAlpha3::TTO, + Self::TN => CountryAlpha3::TUN, + Self::TR => CountryAlpha3::TUR, + Self::TM => CountryAlpha3::TKM, + Self::TC => CountryAlpha3::TCA, + Self::TV => CountryAlpha3::TUV, + Self::UG => CountryAlpha3::UGA, + Self::UA => CountryAlpha3::UKR, + Self::AE => CountryAlpha3::ARE, + Self::GB => CountryAlpha3::GBR, + Self::US => CountryAlpha3::USA, + Self::UM => CountryAlpha3::UMI, + Self::UY => CountryAlpha3::URY, + Self::UZ => CountryAlpha3::UZB, + Self::VU => CountryAlpha3::VUT, + Self::VE => CountryAlpha3::VEN, + Self::VN => CountryAlpha3::VNM, + Self::VG => CountryAlpha3::VGB, + Self::VI => CountryAlpha3::VIR, + Self::WF => CountryAlpha3::WLF, + Self::EH => CountryAlpha3::ESH, + Self::YE => CountryAlpha3::YEM, + Self::ZM => CountryAlpha3::ZMB, + Self::ZW => CountryAlpha3::ZWE, + } + } +} impl Country { pub const fn from_alpha2(code: CountryAlpha2) -> Self { match code { diff --git a/crates/router/src/connector/helcim/transformers.rs b/crates/router/src/connector/helcim/transformers.rs index f904f0ff3fa6..853892b8cee4 100644 --- a/crates/router/src/connector/helcim/transformers.rs +++ b/crates/router/src/connector/helcim/transformers.rs @@ -1,12 +1,12 @@ -use common_utils::pii::IpAddress; +use common_utils::pii::{Email, IpAddress}; use error_stack::{IntoReport, ResultExt}; use masking::Secret; use serde::{Deserialize, Serialize}; use crate::{ connector::utils::{ - self, to_connector_meta, BrowserInformationData, CardData, PaymentsAuthorizeRequestData, - PaymentsCaptureRequestData, RefundsRequestData, + self, to_connector_meta, AddressDetailsData, BrowserInformationData, CardData, + PaymentsAuthorizeRequestData, PaymentsCaptureRequestData, RefundsRequestData, RouterData, }, core::errors, types::{self, api, storage::enums}, @@ -52,6 +52,7 @@ pub struct HelcimPaymentsRequest { ip_address: Secret, card_data: HelcimCard, billing_address: HelcimBillingAddress, + #[serde(skip_serializing_if = "Option::is_none")] ecommerce: Option, } @@ -59,8 +60,14 @@ pub struct HelcimPaymentsRequest { #[serde(rename_all = "camelCase")] pub struct HelcimBillingAddress { name: Secret, - street1: String, - postal_code: String, + street1: Secret, + postal_code: Secret, + #[serde(skip_serializing_if = "Option::is_none")] + street2: Option>, + #[serde(skip_serializing_if = "Option::is_none")] + city: Option, + #[serde(skip_serializing_if = "Option::is_none")] + email: Option, } #[derive(Debug, Serialize)] @@ -84,11 +91,22 @@ impl TryFrom<&HelcimRouterData<&types::PaymentsAuthorizeRouterData>> for HelcimP card_number: req_card.card_number, card_c_v_v: req_card.card_cvc, }; + 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_card.card_holder_name, - street1: "Jump Street 21".to_string(), - postal_code: "H0H0H0".to_string(), + 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 From 04b1258dd3358f616d1058bb6bdbbd5a295499c0 Mon Sep 17 00:00:00 2001 From: DEEPANSHU BANSAL Date: Mon, 25 Sep 2023 19:48:49 +0530 Subject: [PATCH 09/20] feat(connector): [HELCIM] Implement Void Flow --- crates/router/src/connector/helcim.rs | 92 ++++++++++++++++++- .../src/connector/helcim/transformers.rs | 69 +++++++++++++- crates/router/src/connector/utils.rs | 6 ++ .../router/src/core/payments/transformers.rs | 10 ++ crates/router/src/types.rs | 1 + 5 files changed, 174 insertions(+), 4 deletions(-) diff --git a/crates/router/src/connector/helcim.rs b/crates/router/src/connector/helcim.rs index b666d5259956..6acc70154d76 100644 --- a/crates/router/src/connector/helcim.rs +++ b/crates/router/src/connector/helcim.rs @@ -257,7 +257,7 @@ impl ConnectorIntegration CustomResult)>, errors::ConnectorError> { let mut header = vec![( headers::CONTENT_TYPE.to_string(), - types::PaymentsAuthorizeType::get_content_type(self) + types::PaymentsSyncType::get_content_type(self) .to_string() .into(), )]; @@ -419,6 +419,92 @@ impl ConnectorIntegration for Helcim { + fn get_headers( + &self, + req: &types::PaymentsCancelRouterData, + _connectors: &settings::Connectors, + ) -> CustomResult)>, errors::ConnectorError> { + let mut header = vec![( + headers::CONTENT_TYPE.to_string(), + types::PaymentsVoidType::get_content_type(self) + .to_string() + .into(), + )]; + let mut api_key = self.get_auth_header(&req.connector_auth_type)?; + + let mut idempotency_key = vec![( + headers::IDEMPOTENCY_KEY.to_string(), + format!("{}V", req.payment_id).into_masked(), + )]; + header.append(&mut api_key); + header.append(&mut idempotency_key); + Ok(header) + } + + fn get_content_type(&self) -> &'static str { + self.common_get_content_type() + } + + fn get_url( + &self, + _req: &types::PaymentsCancelRouterData, + connectors: &settings::Connectors, + ) -> CustomResult { + Ok(format!("{}v2/payment/reverse", self.base_url(connectors))) + } + + fn get_request_body( + &self, + req: &types::PaymentsCancelRouterData, + ) -> CustomResult, errors::ConnectorError> { + let req_obj = helcim::HelcimVoidRequest::try_from(req)?; + let helcim_req = types::RequestBody::log_and_get_request_body( + &req_obj, + utils::Encode::::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, 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 { + 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 { + self.build_error_response(res) + } } impl ConnectorIntegration for Helcim { @@ -429,7 +515,7 @@ impl ConnectorIntegration CustomResult)>, errors::ConnectorError> { let mut header = vec![( headers::CONTENT_TYPE.to_string(), - types::PaymentsAuthorizeType::get_content_type(self) + types::RefundExecuteType::get_content_type(self) .to_string() .into(), )]; @@ -524,7 +610,7 @@ impl ConnectorIntegration CustomResult)>, errors::ConnectorError> { let mut header = vec![( headers::CONTENT_TYPE.to_string(), - types::PaymentsAuthorizeType::get_content_type(self) + types::RefundSyncType::get_content_type(self) .to_string() .into(), )]; diff --git a/crates/router/src/connector/helcim/transformers.rs b/crates/router/src/connector/helcim/transformers.rs index 853892b8cee4..15fd225553e2 100644 --- a/crates/router/src/connector/helcim/transformers.rs +++ b/crates/router/src/connector/helcim/transformers.rs @@ -6,7 +6,8 @@ use serde::{Deserialize, Serialize}; use crate::{ connector::utils::{ self, to_connector_meta, AddressDetailsData, BrowserInformationData, CardData, - PaymentsAuthorizeRequestData, PaymentsCaptureRequestData, RefundsRequestData, RouterData, + PaymentsAuthorizeRequestData, PaymentsCancelRequestData, PaymentsCaptureRequestData, + RefundsRequestData, RouterData, }, core::errors, types::{self, api, storage::enums}, @@ -159,6 +160,7 @@ pub enum HelcimTransactionType { PreAuth, Capture, Verify, + Reverse, } impl From for enums::AttemptStatus { @@ -180,6 +182,10 @@ impl From for enums::AttemptStatus { HelcimPaymentStatus::Approved => Self::AuthenticationSuccessful, HelcimPaymentStatus::Declined => Self::AuthenticationFailed, }, + HelcimTransactionType::Reverse => match item.status { + HelcimPaymentStatus::Approved => Self::Voided, + HelcimPaymentStatus::Declined => Self::VoidFailed, + }, } } } @@ -381,6 +387,67 @@ impl } } +#[derive(Debug, Serialize)] +#[serde(rename_all = "camelCase")] +pub struct HelcimVoidRequest { + card_transaction_id: u64, + ip_address: Secret, + ecommerce: Option, +} + +impl TryFrom<&types::PaymentsCancelRouterData> for HelcimVoidRequest { + type Error = error_stack::Report; + fn try_from(item: &types::PaymentsCancelRouterData) -> Result { + let ip_address = item.request.get_browser_info()?.get_ip_address()?; + Ok(Self { + card_transaction_id: item + .request + .connector_transaction_id + .parse::() + .into_report() + .change_context(errors::ConnectorError::RequestEncodingFailed)?, + ip_address, + ecommerce: None, + }) + } +} + +impl + TryFrom< + types::ResponseRouterData< + F, + HelcimPaymentsResponse, + types::PaymentsCancelData, + types::PaymentsResponseData, + >, + > for types::RouterData +{ + type Error = error_stack::Report; + fn try_from( + item: types::ResponseRouterData< + F, + HelcimPaymentsResponse, + types::PaymentsCancelData, + types::PaymentsResponseData, + >, + ) -> Result { + 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 + }) + } +} + //TODO: Fill the struct with respective fields // REFUND : // Type definition for RefundRequest diff --git a/crates/router/src/connector/utils.rs b/crates/router/src/connector/utils.rs index 85627bea1c7d..2325c2356144 100644 --- a/crates/router/src/connector/utils.rs +++ b/crates/router/src/connector/utils.rs @@ -498,6 +498,7 @@ pub trait PaymentsCancelRequestData { fn get_amount(&self) -> Result; fn get_currency(&self) -> Result; fn get_cancellation_reason(&self) -> Result; + fn get_browser_info(&self) -> Result; } impl PaymentsCancelRequestData for PaymentsCancelData { @@ -512,6 +513,11 @@ impl PaymentsCancelRequestData for PaymentsCancelData { .clone() .ok_or_else(missing_field_err("cancellation_reason")) } + fn get_browser_info(&self) -> Result { + self.browser_info + .clone() + .ok_or_else(missing_field_err("browser_info")) + } } pub trait RefundsRequestData { diff --git a/crates/router/src/core/payments/transformers.rs b/crates/router/src/core/payments/transformers.rs index 8d3a2aaaaf07..7b6ca0b49b8c 100644 --- a/crates/router/src/core/payments/transformers.rs +++ b/crates/router/src/core/payments/transformers.rs @@ -1092,6 +1092,15 @@ impl TryFrom> for types::PaymentsCancelDa &additional_data.connector_name, api::GetToken::Connector, )?; + let browser_info: Option = 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), @@ -1101,6 +1110,7 @@ impl TryFrom> 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, }) } } diff --git a/crates/router/src/types.rs b/crates/router/src/types.rs index 76ed89341654..a8f2240e7e12 100644 --- a/crates/router/src/types.rs +++ b/crates/router/src/types.rs @@ -485,6 +485,7 @@ pub struct PaymentsCancelData { pub connector_transaction_id: String, pub cancellation_reason: Option, pub connector_meta: Option, + pub browser_info: Option, } #[derive(Debug, Default, Clone)] From c0b9c75fd140fe1dc6a92bff60093bfbcbad74d3 Mon Sep 17 00:00:00 2001 From: DEEPANSHU BANSAL Date: Wed, 27 Sep 2023 13:45:08 +0530 Subject: [PATCH 10/20] feat(connector): [HELCIM] Implement Verify Flow --- crates/router/src/connector/helcim.rs | 66 ++++++++++++ .../src/connector/helcim/transformers.rs | 100 ++++++++++++++++-- 2 files changed, 160 insertions(+), 6 deletions(-) diff --git a/crates/router/src/connector/helcim.rs b/crates/router/src/connector/helcim.rs index 6acc70154d76..01fe5646009a 100644 --- a/crates/router/src/connector/helcim.rs +++ b/crates/router/src/connector/helcim.rs @@ -156,6 +156,72 @@ impl ConnectorIntegration for Helcim { + fn get_headers( + &self, + req: &types::VerifyRouterData, + connectors: &settings::Connectors, + ) -> CustomResult)>, errors::ConnectorError> { + self.build_headers(req, connectors) + } + + fn get_url( + &self, + _req: &types::VerifyRouterData, + connectors: &settings::Connectors, + ) -> CustomResult { + Ok(format!("{}v2/payment/verify", self.base_url(connectors))) + } + fn get_request_body( + &self, + req: &types::VerifyRouterData, + ) -> CustomResult, errors::ConnectorError> { + let connector_req = helcim::HelcimVerifyRequest::try_from(req)?; + + let helcim_req = types::RequestBody::log_and_get_request_body( + &connector_req, + utils::Encode::::encode_to_string_of_json, + ) + .change_context(errors::ConnectorError::RequestEncodingFailed)?; + Ok(Some(helcim_req)) + } + fn build_request( + &self, + req: &types::VerifyRouterData, + connectors: &settings::Connectors, + ) -> CustomResult, errors::ConnectorError> { + Ok(Some( + services::RequestBuilder::new() + .method(services::Method::Post) + .url(&types::PaymentsVerifyType::get_url(self, req, connectors)?) + .attach_default_headers() + .headers(types::PaymentsVerifyType::get_headers( + self, req, connectors, + )?) + .body(types::PaymentsVerifyType::get_request_body(self, req)?) + .build(), + )) + } + fn handle_response( + &self, + data: &types::VerifyRouterData, + res: Response, + ) -> CustomResult { + 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 { + self.build_error_response(res) + } } impl ConnectorIntegration diff --git a/crates/router/src/connector/helcim/transformers.rs b/crates/router/src/connector/helcim/transformers.rs index 15fd225553e2..e2559de12c95 100644 --- a/crates/router/src/connector/helcim/transformers.rs +++ b/crates/router/src/connector/helcim/transformers.rs @@ -44,6 +44,17 @@ impl } } +#[derive(Debug, Serialize)] +#[serde(rename_all = "camelCase")] +pub struct HelcimVerifyRequest { + currency: enums::Currency, + ip_address: Secret, + card_data: HelcimCard, + billing_address: HelcimBillingAddress, + #[serde(skip_serializing_if = "Option::is_none")] + ecommerce: Option, +} + //TODO: Fill the struct with respective fields #[derive(Debug, Serialize)] #[serde(rename_all = "camelCase")] @@ -79,6 +90,51 @@ pub struct HelcimCard { card_c_v_v: Secret, } +impl TryFrom<&types::VerifyRouterData> for HelcimVerifyRequest { + type Error = error_stack::Report; + fn try_from(item: &types::VerifyRouterData) -> Result { + match item.request.payment_method_data.clone() { + api::PaymentMethodData::Card(req_card) => { + 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, + card_c_v_v: req_card.card_cvc, + }; + let req_address = item + .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.request.email.clone(), + }; + let ip_address = item + .request + .browser_info + .clone() + .ok_or_else(utils::missing_field_err("browser_info"))? + .get_ip_address()?; + + Ok(Self { + currency: item.request.currency, + ip_address, + card_data, + billing_address, + ecommerce: None, + }) + } + _ => Err(errors::ConnectorError::NotImplemented("Payment methods".to_string()).into()), + } + } +} + impl TryFrom<&HelcimRouterData<&types::PaymentsAuthorizeRouterData>> for HelcimPaymentsRequest { type Error = error_stack::Report; fn try_from( @@ -166,7 +222,7 @@ pub enum HelcimTransactionType { impl From for enums::AttemptStatus { fn from(item: HelcimPaymentsResponse) -> Self { match item.transaction_type { - HelcimTransactionType::Purchase => match item.status { + HelcimTransactionType::Purchase | HelcimTransactionType::Verify => match item.status { HelcimPaymentStatus::Approved => Self::Charged, HelcimPaymentStatus::Declined => Self::Failure, }, @@ -175,13 +231,9 @@ impl From for enums::AttemptStatus { HelcimPaymentStatus::Declined => Self::AuthorizationFailed, }, HelcimTransactionType::Capture => match item.status { - HelcimPaymentStatus::Approved => Self::Charged, //Is this the correct status PartialCharged + HelcimPaymentStatus::Approved => Self::Charged, HelcimPaymentStatus::Declined => Self::CaptureFailed, }, - HelcimTransactionType::Verify => match item.status { - HelcimPaymentStatus::Approved => Self::AuthenticationSuccessful, - HelcimPaymentStatus::Declined => Self::AuthenticationFailed, - }, HelcimTransactionType::Reverse => match item.status { HelcimPaymentStatus::Approved => Self::Voided, HelcimPaymentStatus::Declined => Self::VoidFailed, @@ -200,6 +252,42 @@ pub struct HelcimPaymentsResponse { transaction_type: HelcimTransactionType, } +impl + TryFrom< + types::ResponseRouterData< + F, + HelcimPaymentsResponse, + types::VerifyRequestData, + types::PaymentsResponseData, + >, + > for types::RouterData +{ + type Error = error_stack::Report; + fn try_from( + item: types::ResponseRouterData< + F, + HelcimPaymentsResponse, + types::VerifyRequestData, + types::PaymentsResponseData, + >, + ) -> Result { + 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 + }) + } +} + impl TryFrom< types::ResponseRouterData< From d814ebb83f752c0c9be3dc34581828fae8684c63 Mon Sep 17 00:00:00 2001 From: DEEPANSHU BANSAL Date: Wed, 27 Sep 2023 17:08:13 +0530 Subject: [PATCH 11/20] feat(connector): [HELCIM] Fix Refunds --- .../router/src/connector/helcim/transformers.rs | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/crates/router/src/connector/helcim/transformers.rs b/crates/router/src/connector/helcim/transformers.rs index e2559de12c95..cc48e10461bb 100644 --- a/crates/router/src/connector/helcim/transformers.rs +++ b/crates/router/src/connector/helcim/transformers.rs @@ -553,9 +553,18 @@ impl TryFrom<&HelcimRouterData<&types::RefundsRouterData>> for HelcimRefun fn try_from( item: &HelcimRouterData<&types::RefundsRouterData>, ) -> Result { - let helcim_meta_data: HelcimMetaData = - to_connector_meta(item.router_data.request.connector_metadata.clone())?; - let original_transaction_id = helcim_meta_data.capture_id; + let original_transaction_id = if item.router_data.request.connector_metadata.is_none() { + item.router_data + .request + .connector_transaction_id + .parse::() + .into_report() + .change_context(errors::ConnectorError::RequestEncodingFailed)? + } else { + let helcim_capture: HelcimMetaData = + to_connector_meta(item.router_data.request.connector_metadata.clone())?; + helcim_capture.capture_id + }; let ip_address = item .router_data .request From b46ccc8810664dbcc0db4d50a4a4d9713dd84ce2 Mon Sep 17 00:00:00 2001 From: DEEPANSHU BANSAL Date: Wed, 27 Sep 2023 20:30:12 +0530 Subject: [PATCH 12/20] feat(connector): [Helcim] Implement Error Handling --- crates/router/src/connector/helcim.rs | 6 +++--- crates/router/src/connector/helcim/transformers.rs | 5 +---- 2 files changed, 4 insertions(+), 7 deletions(-) diff --git a/crates/router/src/connector/helcim.rs b/crates/router/src/connector/helcim.rs index 01fe5646009a..52b98cd62d24 100644 --- a/crates/router/src/connector/helcim.rs +++ b/crates/router/src/connector/helcim.rs @@ -118,9 +118,9 @@ impl ConnectorCommon for Helcim { Ok(ErrorResponse { status_code: res.status_code, - code: response.code, - message: response.message, - reason: response.reason, + code: response.errors.clone(), + message: response.errors, + reason: None, }) } } diff --git a/crates/router/src/connector/helcim/transformers.rs b/crates/router/src/connector/helcim/transformers.rs index cc48e10461bb..3bc0cddc6513 100644 --- a/crates/router/src/connector/helcim/transformers.rs +++ b/crates/router/src/connector/helcim/transformers.rs @@ -643,8 +643,5 @@ impl TryFrom> //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, + pub errors: String, } From d6d1931f733ac8f5fdf623a80a17691d9983ce57 Mon Sep 17 00:00:00 2001 From: DEEPANSHU BANSAL Date: Tue, 3 Oct 2023 19:06:37 +0530 Subject: [PATCH 13/20] feat(connector): [HELCIM] Resolve PR comment IDEMPOTENCY KEY Generation --- connector-template/mod.rs | 2 +- crates/router/src/connector/helcim.rs | 45 ++++--------------- .../src/connector/helcim/transformers.rs | 3 ++ 3 files changed, 13 insertions(+), 37 deletions(-) diff --git a/connector-template/mod.rs b/connector-template/mod.rs index d9aa340b1dc8..da9133e093a6 100644 --- a/connector-template/mod.rs +++ b/connector-template/mod.rs @@ -57,7 +57,7 @@ where ) -> CustomResult)>, 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)?; header.append(&mut api_key); diff --git a/crates/router/src/connector/helcim.rs b/crates/router/src/connector/helcim.rs index 52b98cd62d24..ab0bd290dab8 100644 --- a/crates/router/src/connector/helcim.rs +++ b/crates/router/src/connector/helcim.rs @@ -62,16 +62,17 @@ where ) -> CustomResult)>, 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(), - format!("{}_", req.payment_id).into_masked(), + utils::generate_id(ID_LENGTH, "HS").into_masked(), )]; + header.append(&mut api_key); header.append(&mut idempotency_key); Ok(header) @@ -488,23 +489,9 @@ impl ConnectorIntegration CustomResult)>, errors::ConnectorError> { - let mut header = vec![( - headers::CONTENT_TYPE.to_string(), - types::PaymentsVoidType::get_content_type(self) - .to_string() - .into(), - )]; - let mut api_key = self.get_auth_header(&req.connector_auth_type)?; - - let mut idempotency_key = vec![( - headers::IDEMPOTENCY_KEY.to_string(), - format!("{}V", req.payment_id).into_masked(), - )]; - header.append(&mut api_key); - header.append(&mut idempotency_key); - Ok(header) + self.build_headers(req, connectors) } fn get_content_type(&self) -> &'static str { @@ -577,23 +564,9 @@ impl ConnectorIntegration, - _connectors: &settings::Connectors, + connectors: &settings::Connectors, ) -> CustomResult)>, errors::ConnectorError> { - let mut header = vec![( - headers::CONTENT_TYPE.to_string(), - types::RefundExecuteType::get_content_type(self) - .to_string() - .into(), - )]; - let mut api_key = self.get_auth_header(&req.connector_auth_type)?; - - let mut idempotency_key = vec![( - headers::IDEMPOTENCY_KEY.to_string(), - format!("{}_", req.request.refund_id).into_masked(), - )]; - header.append(&mut api_key); - header.append(&mut idempotency_key); - Ok(header) + self.build_headers(req, connectors) } fn get_content_type(&self) -> &'static str { diff --git a/crates/router/src/connector/helcim/transformers.rs b/crates/router/src/connector/helcim/transformers.rs index 3bc0cddc6513..be78c2c378d6 100644 --- a/crates/router/src/connector/helcim/transformers.rs +++ b/crates/router/src/connector/helcim/transformers.rs @@ -408,6 +408,7 @@ pub struct HelcimCaptureRequest { pre_auth_transaction_id: u64, amount: f64, ip_address: Secret, + #[serde(skip_serializing_if = "Option::is_none")] ecommerce: Option, } @@ -480,6 +481,7 @@ impl pub struct HelcimVoidRequest { card_transaction_id: u64, ip_address: Secret, + #[serde(skip_serializing_if = "Option::is_none")] ecommerce: Option, } @@ -545,6 +547,7 @@ pub struct HelcimRefundRequest { amount: f64, original_transaction_id: u64, ip_address: Secret, + #[serde(skip_serializing_if = "Option::is_none")] ecommerce: Option, } From 4868e19fd6a9629c6201ece596b5639adad3fc51 Mon Sep 17 00:00:00 2001 From: DEEPANSHU BANSAL Date: Wed, 4 Oct 2023 01:09:21 +0530 Subject: [PATCH 14/20] feat(connector): [HELCIM] Resolve PR Comments Part-2 --- crates/router/src/connector/helcim.rs | 10 +- .../src/connector/helcim/transformers.rs | 211 +++++++++++------- crates/router/src/connector/utils.rs | 12 + 3 files changed, 147 insertions(+), 86 deletions(-) diff --git a/crates/router/src/connector/helcim.rs b/crates/router/src/connector/helcim.rs index ab0bd290dab8..e4820e62d3df 100644 --- a/crates/router/src/connector/helcim.rs +++ b/crates/router/src/connector/helcim.rs @@ -120,8 +120,8 @@ impl ConnectorCommon for Helcim { Ok(ErrorResponse { status_code: res.status_code, code: response.errors.clone(), - message: response.errors, - reason: None, + message: response.errors.clone(), + reason: Some(response.errors), }) } } @@ -133,10 +133,8 @@ impl ConnectorValidation for Helcim { ) -> CustomResult<(), errors::ConnectorError> { let capture_method = capture_method.unwrap_or_default(); match capture_method { - enums::CaptureMethod::Automatic - | enums::CaptureMethod::Manual - | enums::CaptureMethod::ManualMultiple => Ok(()), - enums::CaptureMethod::Scheduled => Err( + 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()), ), } diff --git a/crates/router/src/connector/helcim/transformers.rs b/crates/router/src/connector/helcim/transformers.rs index be78c2c378d6..ab874753b792 100644 --- a/crates/router/src/connector/helcim/transformers.rs +++ b/crates/router/src/connector/helcim/transformers.rs @@ -7,7 +7,7 @@ use crate::{ connector::utils::{ self, to_connector_meta, AddressDetailsData, BrowserInformationData, CardData, PaymentsAuthorizeRequestData, PaymentsCancelRequestData, PaymentsCaptureRequestData, - RefundsRequestData, RouterData, + PaymentsVerifyRequestData, RefundsRequestData, RouterData, }, core::errors, types::{self, api, storage::enums}, @@ -55,7 +55,6 @@ pub struct HelcimVerifyRequest { ecommerce: Option, } -//TODO: Fill the struct with respective fields #[derive(Debug, Serialize)] #[serde(rename_all = "camelCase")] pub struct HelcimPaymentsRequest { @@ -64,6 +63,8 @@ pub struct HelcimPaymentsRequest { ip_address: Secret, 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, } @@ -90,51 +91,123 @@ pub struct HelcimCard { card_c_v_v: Secret, } +impl TryFrom<(&types::VerifyRouterData, &api::Card)> for HelcimVerifyRequest { + type Error = error_stack::Report; + fn try_from(value: (&types::VerifyRouterData, &api::Card)) -> Result { + 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()? + .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.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::VerifyRouterData> for HelcimVerifyRequest { type Error = error_stack::Report; fn try_from(item: &types::VerifyRouterData) -> Result { match item.request.payment_method_data.clone() { api::PaymentMethodData::Card(req_card) => { - 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, - card_c_v_v: req_card.card_cvc, - }; - let req_address = item - .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.request.email.clone(), - }; - let ip_address = item - .request - .browser_info - .clone() - .ok_or_else(utils::missing_field_err("browser_info"))? - .get_ip_address()?; - - Ok(Self { - currency: item.request.currency, - ip_address, - card_data, - billing_address, - ecommerce: None, - }) + 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", + })? } - _ => Err(errors::ConnectorError::NotImplemented("Payment methods".to_string()).into()), } } } +impl + TryFrom<( + &HelcimRouterData<&types::PaymentsAuthorizeRouterData>, + &api::Card, + )> for HelcimPaymentsRequest +{ + type Error = error_stack::Report; + fn try_from( + value: ( + &HelcimRouterData<&types::PaymentsAuthorizeRouterData>, + &api::Card, + ), + ) -> Result { + 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; fn try_from( @@ -142,48 +215,32 @@ impl TryFrom<&HelcimRouterData<&types::PaymentsAuthorizeRouterData>> for HelcimP ) -> Result { match item.router_data.request.payment_method_data.clone() { api::PaymentMethodData::Card(req_card) => { - 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, - card_c_v_v: req_card.card_cvc, - }; - 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, - }) + 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, @@ -201,7 +258,6 @@ impl TryFrom<&types::ConnectorAuthType> for HelcimAuthType { } } // PaymentsResponse -//TODO: Append the remaining status flags #[derive(Debug, Deserialize)] #[serde(rename_all = "UPPERCASE")] pub enum HelcimPaymentStatus { @@ -242,7 +298,6 @@ impl From for enums::AttemptStatus { } } -//TODO: Fill the struct with respective fields #[derive(Debug, Deserialize)] #[serde(rename_all = "camelCase")] pub struct HelcimPaymentsResponse { @@ -538,7 +593,6 @@ impl } } -//TODO: Fill the struct with respective fields // REFUND : // Type definition for RefundRequest #[derive(Debug, Serialize)] @@ -587,8 +641,6 @@ impl TryFrom<&HelcimRouterData<&types::RefundsRouterData>> for HelcimRefun pub enum HelcimRefundTransactionType { Refund, } - -//TODO: Fill the struct with respective fields #[derive(Debug, Deserialize)] #[serde(rename_all = "camelCase")] pub struct RefundResponse { @@ -643,7 +695,6 @@ impl TryFrom> } } -//TODO: Fill the struct with respective fields #[derive(Default, Debug, Serialize, Deserialize, PartialEq)] pub struct HelcimErrorResponse { pub errors: String, diff --git a/crates/router/src/connector/utils.rs b/crates/router/src/connector/utils.rs index 2325c2356144..113daf9bdba5 100644 --- a/crates/router/src/connector/utils.rs +++ b/crates/router/src/connector/utils.rs @@ -262,6 +262,18 @@ impl PaymentsCaptureRequestData for types::PaymentsCaptureData { } } +pub trait PaymentsVerifyRequestData { + fn get_browser_info(&self) -> Result; +} + +impl PaymentsVerifyRequestData for types::VerifyRequestData { + fn get_browser_info(&self) -> Result { + self.browser_info + .clone() + .ok_or_else(missing_field_err("browser_info")) + } +} + pub trait PaymentsAuthorizeRequestData { fn is_auto_capture(&self) -> Result; fn get_email(&self) -> Result; From 7db1683d9d45b6cec093dd54b72f1ca453ca9a40 Mon Sep 17 00:00:00 2001 From: DEEPANSHU BANSAL Date: Wed, 4 Oct 2023 14:59:43 +0530 Subject: [PATCH 15/20] feat(connector): [HELCIM] handle preauth transaction id --- .../src/connector/helcim/transformers.rs | 64 +++++++++---------- 1 file changed, 30 insertions(+), 34 deletions(-) diff --git a/crates/router/src/connector/helcim/transformers.rs b/crates/router/src/connector/helcim/transformers.rs index ab874753b792..e265e3ad54a7 100644 --- a/crates/router/src/connector/helcim/transformers.rs +++ b/crates/router/src/connector/helcim/transformers.rs @@ -5,9 +5,9 @@ use serde::{Deserialize, Serialize}; use crate::{ connector::utils::{ - self, to_connector_meta, AddressDetailsData, BrowserInformationData, CardData, - PaymentsAuthorizeRequestData, PaymentsCancelRequestData, PaymentsCaptureRequestData, - PaymentsVerifyRequestData, RefundsRequestData, RouterData, + self, AddressDetailsData, BrowserInformationData, CardData, PaymentsAuthorizeRequestData, + PaymentsCancelRequestData, PaymentsCaptureRequestData, PaymentsVerifyRequestData, + RefundsRequestData, RouterData, }, core::errors, types::{self, api, storage::enums}, @@ -130,9 +130,7 @@ impl TryFrom<&types::VerifyRouterData> for HelcimVerifyRequest { type Error = error_stack::Report; fn try_from(item: &types::VerifyRouterData) -> Result { match item.request.payment_method_data.clone() { - api::PaymentMethodData::Card(req_card) => { - Self::try_from((item, &req_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()), ) @@ -214,9 +212,7 @@ impl TryFrom<&HelcimRouterData<&types::PaymentsAuthorizeRouterData>> for HelcimP item: &HelcimRouterData<&types::PaymentsAuthorizeRouterData>, ) -> Result { match item.router_data.request.payment_method_data.clone() { - api::PaymentMethodData::Card(req_card) => { - Self::try_from((item, &req_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()), ) @@ -343,6 +339,11 @@ impl } } +#[derive(Debug, Deserialize, Serialize)] +pub struct HelcimMetaData { + pub preauth_transaction_id: u64, +} + impl TryFrom< types::ResponseRouterData< @@ -362,14 +363,21 @@ impl types::PaymentsResponseData, >, ) -> Result { + let resource_id = + types::ResponseId::ConnectorTransactionId(item.response.transaction_id.to_string()); + 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 { response: Ok(types::PaymentsResponseData::TransactionResponse { - resource_id: types::ResponseId::ConnectorTransactionId( - item.response.transaction_id.to_string(), - ), + resource_id, redirection_data: None, mandate_reference: None, - connector_metadata: None, + connector_metadata, network_txn_id: None, connector_response_reference_id: None, }), @@ -379,11 +387,6 @@ impl } } -#[derive(Debug, Deserialize, Serialize)] -pub struct HelcimMetaData { - pub capture_id: u64, -} - // impl utils::MultipleCaptureSyncResponse for HelcimPaymentsResponse { // fn get_connector_capture_id(&self) -> String { // self.transaction_id.to_string() @@ -511,9 +514,6 @@ impl types::PaymentsResponseData, >, ) -> Result { - let connector_metadata = Some(serde_json::json!(HelcimMetaData { - capture_id: item.response.transaction_id, - })); Ok(Self { response: Ok(types::PaymentsResponseData::TransactionResponse { resource_id: types::ResponseId::ConnectorTransactionId( @@ -521,7 +521,7 @@ impl ), redirection_data: None, mandate_reference: None, - connector_metadata, + connector_metadata: None, network_txn_id: None, connector_response_reference_id: None, }), @@ -610,18 +610,14 @@ impl TryFrom<&HelcimRouterData<&types::RefundsRouterData>> for HelcimRefun fn try_from( item: &HelcimRouterData<&types::RefundsRouterData>, ) -> Result { - let original_transaction_id = if item.router_data.request.connector_metadata.is_none() { - item.router_data - .request - .connector_transaction_id - .parse::() - .into_report() - .change_context(errors::ConnectorError::RequestEncodingFailed)? - } else { - let helcim_capture: HelcimMetaData = - to_connector_meta(item.router_data.request.connector_metadata.clone())?; - helcim_capture.capture_id - }; + let original_transaction_id = item + .router_data + .request + .connector_transaction_id + .parse::() + .into_report() + .change_context(errors::ConnectorError::RequestEncodingFailed)?; + let ip_address = item .router_data .request From 27bcfc4f9b31373e7517ea014154985dfb4f87e3 Mon Sep 17 00:00:00 2001 From: DEEPANSHU BANSAL Date: Wed, 4 Oct 2023 17:37:16 +0530 Subject: [PATCH 16/20] feat(connector): [HELCIM] Improve error handling --- crates/router/src/connector/helcim.rs | 10 +++++++--- crates/router/src/connector/helcim/transformers.rs | 11 +++++++++-- 2 files changed, 16 insertions(+), 5 deletions(-) diff --git a/crates/router/src/connector/helcim.rs b/crates/router/src/connector/helcim.rs index e4820e62d3df..e6a63666c62f 100644 --- a/crates/router/src/connector/helcim.rs +++ b/crates/router/src/connector/helcim.rs @@ -116,12 +116,16 @@ 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.errors.clone(), - message: response.errors.clone(), - reason: Some(response.errors), + code: error_string.clone(), + message: error_string.clone(), + reason: Some(error_string), }) } } diff --git a/crates/router/src/connector/helcim/transformers.rs b/crates/router/src/connector/helcim/transformers.rs index e265e3ad54a7..d08b2c18ef7b 100644 --- a/crates/router/src/connector/helcim/transformers.rs +++ b/crates/router/src/connector/helcim/transformers.rs @@ -691,7 +691,14 @@ impl TryFrom> } } -#[derive(Default, Debug, Serialize, Deserialize, PartialEq)] +#[derive(Debug, strum::Display, Deserialize)] +#[serde(untagged)] +pub enum HelcimErrorTypes { + StringType(String), + JsonType(serde_json::Value), +} + +#[derive(Debug, Deserialize)] pub struct HelcimErrorResponse { - pub errors: String, + pub errors: HelcimErrorTypes, } From 789d8aafcab434e0091ec1e1ccfe67f893cc39be Mon Sep 17 00:00:00 2001 From: DEEPANSHU BANSAL Date: Wed, 4 Oct 2023 17:43:37 +0530 Subject: [PATCH 17/20] feat(connector): [HELCIM] Improve error handling --- crates/router/src/connector/helcim.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/crates/router/src/connector/helcim.rs b/crates/router/src/connector/helcim.rs index e6a63666c62f..6073013e4529 100644 --- a/crates/router/src/connector/helcim.rs +++ b/crates/router/src/connector/helcim.rs @@ -10,6 +10,7 @@ use transformers as helcim; use super::utils::PaymentsAuthorizeRequestData; use crate::{ configs::settings, + consts::NO_ERROR_CODE, core::errors::{self, CustomResult}, headers, services::{ @@ -123,7 +124,7 @@ impl ConnectorCommon for Helcim { Ok(ErrorResponse { status_code: res.status_code, - code: error_string.clone(), + code: NO_ERROR_CODE.to_owned(), message: error_string.clone(), reason: Some(error_string), }) From f6c78d9bf75e2e13c499c0e5504af09e5e8b6437 Mon Sep 17 00:00:00 2001 From: DEEPANSHU BANSAL Date: Mon, 9 Oct 2023 16:17:58 +0530 Subject: [PATCH 18/20] feat(connector): [HELCIM] Resolve PR Comments --- crates/router/src/connector/helcim/transformers.rs | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/crates/router/src/connector/helcim/transformers.rs b/crates/router/src/connector/helcim/transformers.rs index d08b2c18ef7b..ead4f8b63028 100644 --- a/crates/router/src/connector/helcim/transformers.rs +++ b/crates/router/src/connector/helcim/transformers.rs @@ -100,11 +100,7 @@ impl TryFrom<(&types::VerifyRouterData, &api::Card)> for HelcimVerifyRequest { card_number: req_card.card_number.clone(), card_c_v_v: req_card.card_cvc.clone(), }; - let req_address = item - .get_billing()? - .to_owned() - .address - .ok_or_else(utils::missing_field_err("billing.address"))?; + let req_address = item.get_billing_address()?.to_owned(); let billing_address = HelcimBillingAddress { name: req_address.get_full_name()?, From 498f4a5b1c2469614ed676f9eaf6c95261dac6c8 Mon Sep 17 00:00:00 2001 From: DEEPANSHU BANSAL Date: Tue, 10 Oct 2023 15:25:54 +0530 Subject: [PATCH 19/20] feat(connector): [HELCIM] Put resource-id as NoResponseID during Manual Capture --- crates/router/src/connector/helcim.rs | 12 +++++++++++- .../router/src/connector/helcim/transformers.rs | 10 ++++++++-- crates/router/src/core/payments/flows.rs | 1 - crates/router/src/core/payments/transformers.rs | 17 ++++++++++++++++- 4 files changed, 35 insertions(+), 5 deletions(-) diff --git a/crates/router/src/connector/helcim.rs b/crates/router/src/connector/helcim.rs index 6073013e4529..eb2549601987 100644 --- a/crates/router/src/connector/helcim.rs +++ b/crates/router/src/connector/helcim.rs @@ -7,7 +7,7 @@ use error_stack::{IntoReport, ResultExt}; use masking::ExposeInterface; use transformers as helcim; -use super::utils::PaymentsAuthorizeRequestData; +use super::utils::{to_connector_meta, PaymentsAuthorizeRequestData}; use crate::{ configs::settings, consts::NO_ERROR_CODE, @@ -42,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, + ) -> CustomResult, errors::ConnectorError> { + let meta: helcim::HelcimMetaData = to_connector_meta(connector_meta.clone())?; + Ok(Some(meta.preauth_transaction_id.to_string())) + } +} + impl ConnectorIntegration< api::PaymentMethodToken, diff --git a/crates/router/src/connector/helcim/transformers.rs b/crates/router/src/connector/helcim/transformers.rs index ead4f8b63028..22e0a41fbf85 100644 --- a/crates/router/src/connector/helcim/transformers.rs +++ b/crates/router/src/connector/helcim/transformers.rs @@ -359,8 +359,14 @@ impl types::PaymentsResponseData, >, ) -> Result { - let resource_id = - types::ResponseId::ConnectorTransactionId(item.response.transaction_id.to_string()); + //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, diff --git a/crates/router/src/core/payments/flows.rs b/crates/router/src/core/payments/flows.rs index e7c19ac2a69f..a0bf6c411cd7 100644 --- a/crates/router/src/core/payments/flows.rs +++ b/crates/router/src/core/payments/flows.rs @@ -423,7 +423,6 @@ default_imp_for_connector_request_id!( connector::Globalpay, connector::Globepay, connector::Gocardless, - connector::Helcim, connector::Iatapay, connector::Klarna, connector::Mollie, diff --git a/crates/router/src/core/payments/transformers.rs b/crates/router/src/core/payments/transformers.rs index 7b6ca0b49b8c..fdc29b1d801b 100644 --- a/crates/router/src/core/payments/transformers.rs +++ b/crates/router/src/core/payments/transformers.rs @@ -10,7 +10,7 @@ use router_env::{instrument, tracing}; use super::{flows::Feature, PaymentAddress, PaymentData}; use crate::{ configs::settings::{ConnectorRequestReferenceIdConfig, Server}, - connector::Nexinets, + connector::{Helcim, Nexinets}, core::{ errors::{self, RouterResponse, RouterResult}, payments::{self, helpers}, @@ -1024,6 +1024,21 @@ impl TryFrom> for types::PaymentsSyncData } } +impl api::ConnectorTransactionId for Helcim { + fn connector_transaction_id( + &self, + payment_attempt: storage::PaymentAttempt, + ) -> Result, 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, From 2e573849c7d3f2d24baad26f46844947a48f8df3 Mon Sep 17 00:00:00 2001 From: DEEPANSHU BANSAL Date: Tue, 10 Oct 2023 16:06:48 +0530 Subject: [PATCH 20/20] feat(connector): [HELCIM] Rename verify to setupmandate --- .../src/connector/gocardless/transformers.rs | 3 ++- crates/router/src/connector/helcim.rs | 20 +++++++++---------- .../src/connector/helcim/transformers.rs | 16 +++++++-------- crates/router/src/connector/utils.rs | 16 ++------------- 4 files changed, 21 insertions(+), 34 deletions(-) diff --git a/crates/router/src/connector/gocardless/transformers.rs b/crates/router/src/connector/gocardless/transformers.rs index 8187c5ccc49d..d7120c5983ff 100644 --- a/crates/router/src/connector/gocardless/transformers.rs +++ b/crates/router/src/connector/gocardless/transformers.rs @@ -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::{ diff --git a/crates/router/src/connector/helcim.rs b/crates/router/src/connector/helcim.rs index 4e1b2429ebb3..87fcfdd36d88 100644 --- a/crates/router/src/connector/helcim.rs +++ b/crates/router/src/connector/helcim.rs @@ -176,7 +176,7 @@ impl { fn get_headers( &self, - req: &types::VerifyRouterData, + req: &types::SetupMandateRouterData, connectors: &settings::Connectors, ) -> CustomResult)>, errors::ConnectorError> { self.build_headers(req, connectors) @@ -184,14 +184,14 @@ impl fn get_url( &self, - _req: &types::VerifyRouterData, + _req: &types::SetupMandateRouterData, connectors: &settings::Connectors, ) -> CustomResult { Ok(format!("{}v2/payment/verify", self.base_url(connectors))) } fn get_request_body( &self, - req: &types::VerifyRouterData, + req: &types::SetupMandateRouterData, ) -> CustomResult, errors::ConnectorError> { let connector_req = helcim::HelcimVerifyRequest::try_from(req)?; @@ -204,26 +204,24 @@ impl } fn build_request( &self, - req: &types::VerifyRouterData, + req: &types::SetupMandateRouterData, connectors: &settings::Connectors, ) -> CustomResult, errors::ConnectorError> { Ok(Some( services::RequestBuilder::new() .method(services::Method::Post) - .url(&types::PaymentsVerifyType::get_url(self, req, connectors)?) + .url(&types::SetupMandateType::get_url(self, req, connectors)?) .attach_default_headers() - .headers(types::PaymentsVerifyType::get_headers( - self, req, connectors, - )?) - .body(types::PaymentsVerifyType::get_request_body(self, req)?) + .headers(types::SetupMandateType::get_headers(self, req, connectors)?) + .body(types::SetupMandateType::get_request_body(self, req)?) .build(), )) } fn handle_response( &self, - data: &types::VerifyRouterData, + data: &types::SetupMandateRouterData, res: Response, - ) -> CustomResult { + ) -> CustomResult { let response: helcim::HelcimPaymentsResponse = res .response .parse_struct("Helcim PaymentsAuthorizeResponse") diff --git a/crates/router/src/connector/helcim/transformers.rs b/crates/router/src/connector/helcim/transformers.rs index 22e0a41fbf85..9510ff6e67ad 100644 --- a/crates/router/src/connector/helcim/transformers.rs +++ b/crates/router/src/connector/helcim/transformers.rs @@ -6,7 +6,7 @@ use serde::{Deserialize, Serialize}; use crate::{ connector::utils::{ self, AddressDetailsData, BrowserInformationData, CardData, PaymentsAuthorizeRequestData, - PaymentsCancelRequestData, PaymentsCaptureRequestData, PaymentsVerifyRequestData, + PaymentsCancelRequestData, PaymentsCaptureRequestData, PaymentsSetupMandateRequestData, RefundsRequestData, RouterData, }, core::errors, @@ -91,9 +91,9 @@ pub struct HelcimCard { card_c_v_v: Secret, } -impl TryFrom<(&types::VerifyRouterData, &api::Card)> for HelcimVerifyRequest { +impl TryFrom<(&types::SetupMandateRouterData, &api::Card)> for HelcimVerifyRequest { type Error = error_stack::Report; - fn try_from(value: (&types::VerifyRouterData, &api::Card)) -> Result { + fn try_from(value: (&types::SetupMandateRouterData, &api::Card)) -> Result { let (item, req_card) = value; let card_data = HelcimCard { card_expiry: req_card.get_card_expiry_month_year_2_digit_with_delimiter("".to_string()), @@ -122,9 +122,9 @@ impl TryFrom<(&types::VerifyRouterData, &api::Card)> for HelcimVerifyRequest { } } -impl TryFrom<&types::VerifyRouterData> for HelcimVerifyRequest { +impl TryFrom<&types::SetupMandateRouterData> for HelcimVerifyRequest { type Error = error_stack::Report; - fn try_from(item: &types::VerifyRouterData) -> Result { + fn try_from(item: &types::SetupMandateRouterData) -> Result { match item.request.payment_method_data.clone() { api::PaymentMethodData::Card(req_card) => Self::try_from((item, &req_card)), api_models::payments::PaymentMethodData::BankTransfer(_) => Err( @@ -304,17 +304,17 @@ impl types::ResponseRouterData< F, HelcimPaymentsResponse, - types::VerifyRequestData, + types::SetupMandateRequestData, types::PaymentsResponseData, >, - > for types::RouterData + > for types::RouterData { type Error = error_stack::Report; fn try_from( item: types::ResponseRouterData< F, HelcimPaymentsResponse, - types::VerifyRequestData, + types::SetupMandateRequestData, types::PaymentsResponseData, >, ) -> Result { diff --git a/crates/router/src/connector/utils.rs b/crates/router/src/connector/utils.rs index 240fd2b190d8..c27c859c9998 100644 --- a/crates/router/src/connector/utils.rs +++ b/crates/router/src/connector/utils.rs @@ -262,23 +262,11 @@ impl PaymentsCaptureRequestData for types::PaymentsCaptureData { } } -pub trait PaymentsVerifyRequestData { +pub trait PaymentsSetupMandateRequestData { fn get_browser_info(&self) -> Result; } -impl PaymentsVerifyRequestData for types::VerifyRequestData { - fn get_browser_info(&self) -> Result { - self.browser_info - .clone() - .ok_or_else(missing_field_err("browser_info")) - } -} - -pub trait SetupMandateRequestData { - fn get_browser_info(&self) -> Result; -} - -impl SetupMandateRequestData for types::SetupMandateRequestData { +impl PaymentsSetupMandateRequestData for types::SetupMandateRequestData { fn get_browser_info(&self) -> Result { self.browser_info .clone()