From 6123823523847e709d9075c63a0a80a0c02216b7 Mon Sep 17 00:00:00 2001 From: Sahebjot singh Date: Sat, 24 Dec 2022 15:09:49 +0530 Subject: [PATCH] feat(connector): Cybersource Authorize (#154) Co-authored-by: Arun Raj M --- config/Development.toml | 5 +- config/config.example.toml | 6 +- config/docker_compose.toml | 5 +- crates/api_models/src/enums.rs | 1 + crates/router/src/configs/defaults.toml | 4 +- crates/router/src/configs/settings.rs | 1 + crates/router/src/connector.rs | 4 +- crates/router/src/connector/cybersource.rs | 303 +++++++++++++++++ .../src/connector/cybersource/transformers.rs | 316 ++++++++++++++++++ crates/router/src/core/payments.rs | 1 + .../payments/operations/payment_cancel.rs | 1 + .../payments/operations/payment_capture.rs | 1 + .../payments/operations/payment_confirm.rs | 1 + .../payments/operations/payment_create.rs | 1 + .../operations/payment_method_validate.rs | 1 + .../payments/operations/payment_session.rs | 2 + .../core/payments/operations/payment_start.rs | 2 + .../payments/operations/payment_status.rs | 1 + .../payments/operations/payment_update.rs | 1 + .../router/src/core/payments/transformers.rs | 1 + crates/router/src/types.rs | 2 + crates/router/src/types/api.rs | 1 + crates/router/tests/connectors/aci.rs | 1 + .../tests/connectors/authorizedotnet.rs | 1 + crates/router/tests/connectors/checkout.rs | 1 + crates/router/tests/connectors/utils.rs | 1 + 26 files changed, 658 insertions(+), 7 deletions(-) create mode 100644 crates/router/src/connector/cybersource.rs create mode 100644 crates/router/src/connector/cybersource/transformers.rs diff --git a/config/Development.toml b/config/Development.toml index bc2b912fa3..f14f230006 100644 --- a/config/Development.toml +++ b/config/Development.toml @@ -38,7 +38,7 @@ locker_decryption_key2 = "" [connectors.supported] wallets = ["klarna","braintree","applepay"] -cards = ["stripe","adyen","authorizedotnet","checkout","braintree","aci","shift4"] +cards = ["stripe","adyen","authorizedotnet","checkout","braintree","aci","shift4","cybersource"] [eph_key] validity = 1 @@ -67,6 +67,9 @@ base_url = "https://api-na.playground.klarna.com/" [connectors.applepay] base_url = "https://apple-pay-gateway.apple.com/" +[connectors.cybersource] +base_url = "https://apitest.cybersource.com/" + [connectors.shift4] base_url = "https://api.shift4.com/" diff --git a/config/config.example.toml b/config/config.example.toml index 6e9f4d152c..196155c818 100644 --- a/config/config.example.toml +++ b/config/config.example.toml @@ -119,14 +119,16 @@ base_url = "https://api-na.playground.klarna.com/" [connectors.applepay] base_url = "https://apple-pay-gateway.apple.com/" +[connectors.cybersource] +base_url = "https://apitest.cybersource.com/" + [connectors.shift4] base_url = "https://api.shift4.com/" # This data is used to call respective connectors for wallets and cards [connectors.supported] wallets = ["klarna","braintree","applepay"] -cards = ["stripe","adyen","authorizedotnet","checkout","braintree"] - +cards = ["stripe","adyen","authorizedotnet","checkout","braintree", "cybersource"] # Scheduler settings provides a point to modify the behaviour of scheduler flow. # It defines the the streams/queues name and configuration as well as event selection variables diff --git a/config/docker_compose.toml b/config/docker_compose.toml index 58a22c7c5c..265aefe903 100644 --- a/config/docker_compose.toml +++ b/config/docker_compose.toml @@ -74,9 +74,12 @@ base_url = "https://api-na.playground.klarna.com/" [connectors.applepay] base_url = "https://apple-pay-gateway.apple.com/" +[connectors.cybersource] +base_url = "https://apitest.cybersource.com/" + [connectors.shift4] base_url = "https://api.shift4.com/" [connectors.supported] wallets = ["klarna","braintree","applepay"] -cards = ["stripe","adyen","authorizedotnet","checkout","braintree","shift4"] +cards = ["stripe","adyen","authorizedotnet","checkout","braintree","shift4","cybersource"] diff --git a/crates/api_models/src/enums.rs b/crates/api_models/src/enums.rs index e37de90ba7..48c5a32a9d 100644 --- a/crates/api_models/src/enums.rs +++ b/crates/api_models/src/enums.rs @@ -491,6 +491,7 @@ pub enum Connector { Authorizedotnet, Braintree, Checkout, + Cybersource, #[default] Dummy, Klarna, diff --git a/crates/router/src/configs/defaults.toml b/crates/router/src/configs/defaults.toml index c6680ebaac..5ccfaefe39 100644 --- a/crates/router/src/configs/defaults.toml +++ b/crates/router/src/configs/defaults.toml @@ -57,5 +57,5 @@ num_partitions = 64 max_read_count = 100 [connectors.supported] -wallets = ["klarna", "braintree"] -cards = ["stripe", "adyen", "authorizedotnet", "checkout", "braintree"] +wallets = ["klarna","braintree"] +cards = ["stripe","adyen","authorizedotnet","checkout","braintree", "cybersource"] diff --git a/crates/router/src/configs/settings.rs b/crates/router/src/configs/settings.rs index e0cc902531..f5e8e74431 100644 --- a/crates/router/src/configs/settings.rs +++ b/crates/router/src/configs/settings.rs @@ -113,6 +113,7 @@ pub struct Connectors { pub braintree: ConnectorParams, pub checkout: ConnectorParams, pub klarna: ConnectorParams, + pub cybersource: ConnectorParams, pub shift4: ConnectorParams, pub stripe: ConnectorParams, pub supported: SupportedConnectors, diff --git a/crates/router/src/connector.rs b/crates/router/src/connector.rs index 9476a02669..9b598df8f3 100644 --- a/crates/router/src/connector.rs +++ b/crates/router/src/connector.rs @@ -4,6 +4,7 @@ pub mod applepay; pub mod authorizedotnet; pub mod braintree; pub mod checkout; +pub mod cybersource; pub mod klarna; pub mod stripe; @@ -11,5 +12,6 @@ pub mod shift4; pub use self::{ aci::Aci, adyen::Adyen, applepay::Applepay, authorizedotnet::Authorizedotnet, - braintree::Braintree, checkout::Checkout, klarna::Klarna, shift4::Shift4, stripe::Stripe, + braintree::Braintree, checkout::Checkout, cybersource::Cybersource, klarna::Klarna, + shift4::Shift4, stripe::Stripe, }; diff --git a/crates/router/src/connector/cybersource.rs b/crates/router/src/connector/cybersource.rs new file mode 100644 index 0000000000..6bdb1f22a6 --- /dev/null +++ b/crates/router/src/connector/cybersource.rs @@ -0,0 +1,303 @@ +mod transformers; + +use std::fmt::Debug; + +use base64; +use bytes::Bytes; +use error_stack::{IntoReport, ResultExt}; +use ring::{digest, hmac}; +use time::OffsetDateTime; +use transformers as cybersource; +use url::Url; + +use crate::{ + configs::settings, + consts, + core::errors::{self, CustomResult}, + headers, logger, services, + types::{self, api}, + utils::{self, BytesExt}, +}; + +#[derive(Debug, Clone)] +pub struct Cybersource; + +impl Cybersource { + pub fn generate_digest(&self, payload: &[u8]) -> String { + let payload_digest = digest::digest(&digest::SHA256, payload); + base64::encode(payload_digest) + } + + pub fn generate_signature( + &self, + auth: cybersource::CybersourceAuthType, + host: String, + resource: &str, + payload: &str, + date: OffsetDateTime, + ) -> CustomResult { + let cybersource::CybersourceAuthType { + api_key, + merchant_account, + api_secret, + } = auth; + + let headers_for_post_method = "host date (request-target) digest v-c-merchant-id"; + let signature_string = format!( + "host: {host}\n\ + date: {date}\n\ + (request-target): post {resource}\n\ + digest: SHA-256={}\n\ + v-c-merchant-id: {merchant_account}", + self.generate_digest(payload.as_bytes()) + ); + let key_value = base64::decode(api_secret) + .into_report() + .change_context(errors::ConnectorError::RequestEncodingFailed)?; + let key = hmac::Key::new(hmac::HMAC_SHA256, &key_value); + let signature_value = + base64::encode(hmac::sign(&key, signature_string.as_bytes()).as_ref()); + let signature_header = format!( + r#"keyid="{api_key}", algorithm="HmacSHA256", headers="{headers_for_post_method}", signature="{signature_value}""# + ); + + Ok(signature_header) + } +} + +impl api::ConnectorCommon for Cybersource { + fn id(&self) -> &'static str { + "cybersource" + } + + fn common_get_content_type(&self) -> &'static str { + "application/json" + } + + fn base_url<'a>(&self, connectors: &'a settings::Connectors) -> &'a str { + connectors.cybersource.base_url.as_ref() + } +} + +impl api::Payment for Cybersource {} +impl api::PaymentAuthorize for Cybersource {} +impl api::PaymentSync for Cybersource {} +impl api::PaymentVoid for Cybersource {} +impl api::PaymentCapture for Cybersource {} +impl api::PreVerify for Cybersource {} + +impl + services::ConnectorIntegration< + api::Verify, + types::VerifyRequestData, + types::PaymentsResponseData, + > for Cybersource +{ +} + +impl api::PaymentSession for Cybersource {} + +impl + services::ConnectorIntegration< + api::Session, + types::PaymentsSessionData, + types::PaymentsResponseData, + > for Cybersource +{ +} + +impl + services::ConnectorIntegration< + api::Capture, + types::PaymentsCaptureData, + types::PaymentsResponseData, + > for Cybersource +{ +} + +impl + services::ConnectorIntegration + for Cybersource +{ +} + +impl + services::ConnectorIntegration< + api::Authorize, + types::PaymentsAuthorizeData, + types::PaymentsResponseData, + > for Cybersource +{ + fn get_headers( + &self, + _req: &types::PaymentsAuthorizeRouterData, + ) -> CustomResult, errors::ConnectorError> { + let headers = vec![ + ( + headers::CONTENT_TYPE.to_string(), + types::PaymentsAuthorizeType::get_content_type(self).to_string(), + ), + ( + headers::ACCEPT.to_string(), + "application/hal+json;charset=utf-8".to_string(), + ), + ]; + Ok(headers) + } + + fn get_content_type(&self) -> &'static str { + "application/json;charset=utf-8" + } + + fn get_url( + &self, + _req: &types::PaymentsAuthorizeRouterData, + connectors: &settings::Connectors, + ) -> CustomResult { + Ok(format!( + "{}pts/v2/payments/", + api::ConnectorCommon::base_url(self, connectors) + )) + } + + fn build_request( + &self, + req: &types::PaymentsAuthorizeRouterData, + connectors: &settings::Connectors, + ) -> CustomResult, errors::ConnectorError> { + let date = OffsetDateTime::now_utc(); + + let cybersource_req = + utils::Encode::::convert_and_encode(req) + .change_context(errors::ConnectorError::RequestEncodingFailed)?; + let auth: cybersource::CybersourceAuthType = + cybersource::CybersourceAuthType::try_from(&req.connector_auth_type)?; + let merchant_account = auth.merchant_account.clone(); + + let cybersource_host = Url::parse(connectors.cybersource.base_url.as_str()) + .into_report() + .change_context(errors::ConnectorError::RequestEncodingFailed)?; + match cybersource_host.host_str() { + Some(host) => { + let signature = self.generate_signature( + auth, + host.to_string(), + "/pts/v2/payments/", + &cybersource_req, + date, + )?; + let headers = vec![ + ( + "Digest".to_string(), + format!( + "SHA-256={}", + self.generate_digest(cybersource_req.as_bytes()) + ), + ), + ("v-c-merchant-id".to_string(), merchant_account), + ("Date".to_string(), date.to_string()), + ("Host".to_string(), host.to_string()), + ("Signature".to_string(), signature), + ]; + let request = services::RequestBuilder::new() + .method(services::Method::Post) + .url(&types::PaymentsAuthorizeType::get_url( + self, req, connectors, + )?) + .headers(headers) + .headers(types::PaymentsAuthorizeType::get_headers(self, req)?) + .body(Some(cybersource_req)) + .build(); + + Ok(Some(request)) + } + None => Err(errors::ConnectorError::RequestEncodingFailed.into()), + } + } + + fn handle_response( + &self, + data: &types::PaymentsAuthorizeRouterData, + res: types::Response, + ) -> CustomResult { + let response: cybersource::CybersourcePaymentsResponse = res + .response + .parse_struct("Cybersource PaymentResponse") + .change_context(errors::ConnectorError::ResponseDeserializationFailed)?; + logger::debug!(cybersourcepayments_create_response=?response); + types::ResponseRouterData { + response, + data: data.clone(), + http_code: res.status_code, + } + .try_into() + .change_context(errors::ConnectorError::ResponseHandlingFailed) + } + + fn get_error_response( + &self, + res: Bytes, + ) -> CustomResult { + let response: cybersource::ErrorResponse = res + .parse_struct("Cybersource ErrorResponse") + .change_context(errors::ConnectorError::ResponseDeserializationFailed)?; + Ok(types::ErrorResponse { + code: consts::NO_ERROR_CODE.to_string(), + message: response + .message + .unwrap_or_else(|| consts::NO_ERROR_MESSAGE.to_string()), + reason: None, + }) + } +} + +impl + services::ConnectorIntegration< + api::Void, + types::PaymentsCancelData, + types::PaymentsResponseData, + > for Cybersource +{ +} + +impl api::Refund for Cybersource {} +impl api::RefundExecute for Cybersource {} +impl api::RefundSync for Cybersource {} + +#[allow(dead_code)] +impl services::ConnectorIntegration + for Cybersource +{ +} + +#[allow(dead_code)] +impl services::ConnectorIntegration + for Cybersource +{ +} + +#[async_trait::async_trait] +impl api::IncomingWebhook for Cybersource { + fn get_webhook_object_reference_id( + &self, + _body: &[u8], + ) -> CustomResult { + Err(errors::ConnectorError::NotImplemented("cybersource".to_string()).into()) + } + + fn get_webhook_event_type( + &self, + _body: &[u8], + ) -> CustomResult { + Err(errors::ConnectorError::NotImplemented("cybersource".to_string()).into()) + } + + fn get_webhook_resource_object( + &self, + _body: &[u8], + ) -> CustomResult { + Err(errors::ConnectorError::NotImplemented("cybersource".to_string()).into()) + } +} + +impl services::ConnectorRedirectResponse for Cybersource {} diff --git a/crates/router/src/connector/cybersource/transformers.rs b/crates/router/src/connector/cybersource/transformers.rs new file mode 100644 index 0000000000..7e1565e521 --- /dev/null +++ b/crates/router/src/connector/cybersource/transformers.rs @@ -0,0 +1,316 @@ +use api_models::payments; +use common_utils::pii; +use masking::Secret; +use serde::{Deserialize, Serialize}; + +use crate::{ + core::errors, + pii::PeekInterface, + types::{self, api, storage::enums}, +}; + +#[derive(Default, Debug, Serialize, Eq, PartialEq)] +#[serde(rename_all = "camelCase")] +pub struct CybersourcePaymentsRequest { + processing_information: ProcessingInformation, + payment_information: PaymentInformation, + order_information: OrderInformationWithBill, +} + +#[derive(Default, Debug, Serialize, Eq, PartialEq)] +pub struct ProcessingInformation { + capture: bool, +} + +#[derive(Default, Debug, Serialize, Eq, PartialEq)] +pub struct PaymentInformation { + card: Card, +} + +#[derive(Default, Debug, Serialize, Eq, PartialEq)] +#[serde(rename_all = "camelCase")] +pub struct Card { + number: String, + expiration_month: String, + expiration_year: String, + security_code: String, +} + +#[derive(Default, Debug, Serialize, Eq, PartialEq)] +#[serde(rename_all = "camelCase")] +pub struct OrderInformationWithBill { + amount_details: Amount, + bill_to: BillTo, +} + +#[derive(Default, Debug, Serialize, Eq, PartialEq)] +#[serde(rename_all = "camelCase")] +pub struct OrderInformation { + amount_details: Amount, +} + +#[derive(Default, Debug, Serialize, Eq, PartialEq)] +#[serde(rename_all = "camelCase")] +pub struct Amount { + total_amount: String, + currency: String, +} + +#[derive(Default, Debug, Serialize, Eq, PartialEq)] +#[serde(rename_all = "camelCase")] +pub struct BillTo { + first_name: Secret, + last_name: Secret, + address1: Secret, + locality: String, + administrative_area: Secret, + postal_code: Secret, + country: String, + email: Secret, + phone_number: Secret, +} + +// for cybersource each item in Billing is mandatory +fn build_bill_to( + address_details: payments::Address, + email: Secret, + phone_number: Secret, +) -> Option { + if let Some(api_models::payments::AddressDetails { + first_name: Some(f_name), + last_name: Some(last_name), + line1: Some(address1), + city: Some(city), + line2: Some(administrative_area), + zip: Some(postal_code), + country: Some(country), + .. + }) = address_details.address + { + Some(BillTo { + first_name: f_name, + last_name, + address1, + locality: city, + administrative_area, + postal_code, + country, + email, + phone_number, + }) + } else { + None + } +} + +impl TryFrom<&types::PaymentsAuthorizeRouterData> for CybersourcePaymentsRequest { + type Error = error_stack::Report; + fn try_from(item: &types::PaymentsAuthorizeRouterData) -> Result { + match item.request.payment_method_data { + api::PaymentMethod::Card(ref ccard) => { + let address = item + .address + .billing + .clone() + .ok_or(errors::ConnectorError::RequestEncodingFailed)?; + let phone = address + .clone() + .phone + .ok_or(errors::ConnectorError::RequestEncodingFailed)?; + let phone_number = phone + .number + .ok_or(errors::ConnectorError::RequestEncodingFailed)?; + let country_code = phone + .country_code + .ok_or(errors::ConnectorError::RequestEncodingFailed)?; + let number_with_code = + Secret::new(format!("{}{}", country_code, phone_number.peek())); + let email = item + .request + .email + .clone() + .ok_or(errors::ConnectorError::RequestEncodingFailed)?; + let bill_to = build_bill_to(address, email, number_with_code) + .ok_or(errors::ConnectorError::RequestEncodingFailed)?; + + let order_information = OrderInformationWithBill { + amount_details: Amount { + total_amount: item.request.amount.to_string(), + currency: item.request.currency.to_string().to_uppercase(), + }, + bill_to, + }; + + let payment_information = PaymentInformation { + card: Card { + number: ccard.card_number.peek().clone(), + expiration_month: ccard.card_exp_month.peek().clone(), + expiration_year: ccard.card_exp_year.peek().clone(), + security_code: ccard.card_cvc.peek().clone(), + }, + }; + + let processing_information = ProcessingInformation { + capture: matches!( + item.request.capture_method, + Some(enums::CaptureMethod::Automatic) | None + ), + }; + + Ok(Self { + processing_information, + payment_information, + order_information, + }) + } + _ => Err(errors::ConnectorError::NotImplemented("Payment methods".to_string()).into()), + } + } +} + +pub struct CybersourceAuthType { + pub(super) api_key: String, + pub(super) merchant_account: String, + pub(super) api_secret: String, +} + +impl TryFrom<&types::ConnectorAuthType> for CybersourceAuthType { + type Error = error_stack::Report; + fn try_from(auth_type: &types::ConnectorAuthType) -> Result { + if let types::ConnectorAuthType::SignatureKey { + api_key, + key1, + api_secret, + } = auth_type + { + Ok(Self { + api_key: api_key.to_string(), + merchant_account: key1.to_string(), + api_secret: api_secret.to_string(), + }) + } else { + Err(errors::ConnectorError::FailedToObtainAuthType)? + } + } +} +#[derive(Debug, Default, Clone, Deserialize, Eq, PartialEq)] +#[serde(rename_all = "UPPERCASE")] +pub enum CybersourcePaymentStatus { + Authorized, + Succeeded, + Failed, + #[default] + Processing, +} + +impl From for enums::AttemptStatus { + fn from(item: CybersourcePaymentStatus) -> Self { + match item { + CybersourcePaymentStatus::Authorized => Self::Authorized, + CybersourcePaymentStatus::Succeeded => Self::Charged, + CybersourcePaymentStatus::Failed => Self::Failure, + CybersourcePaymentStatus::Processing => Self::Authorizing, + } + } +} + +#[derive(Default, Debug, Clone, Deserialize, Eq, PartialEq)] +pub struct CybersourcePaymentsResponse { + id: String, + status: CybersourcePaymentStatus, +} + +impl TryFrom> + for types::PaymentsAuthorizeRouterData +{ + type Error = error_stack::Report; + fn try_from( + item: types::PaymentsResponseRouterData, + ) -> Result { + Ok(Self { + status: item.response.status.into(), + response: Ok(types::PaymentsResponseData::TransactionResponse { + resource_id: types::ResponseId::ConnectorTransactionId(item.response.id), + redirection_data: None, + redirect: false, + mandate_reference: None, + }), + ..item.data + }) + } +} + +#[derive(Debug, Default, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct ErrorResponse { + pub error_information: Option, + pub status: String, + pub message: Option, +} + +#[derive(Debug, Default, Deserialize)] +pub struct ErrorInformation { + pub message: String, + pub reason: String, +} + +#[derive(Default, Debug, Serialize)] +pub struct CybersourceRefundRequest { + order_information: OrderInformation, +} + +impl TryFrom<&types::RefundsRouterData> for CybersourceRefundRequest { + type Error = error_stack::Report; + fn try_from(item: &types::RefundsRouterData) -> Result { + Ok(Self { + order_information: OrderInformation { + amount_details: Amount { + total_amount: item.request.amount.to_string(), + currency: item.request.currency.to_string(), + }, + }, + }) + } +} + +#[allow(dead_code)] +#[derive(Debug, Default, Deserialize, Clone)] +pub enum RefundStatus { + Succeeded, + Failed, + #[default] + Processing, +} + +impl From for enums::RefundStatus { + fn from(item: RefundStatus) -> Self { + match item { + self::RefundStatus::Succeeded => Self::Success, + self::RefundStatus::Failed => Self::Failure, + self::RefundStatus::Processing => Self::Pending, + } + } +} + +#[derive(Default, Debug, Clone, Deserialize)] +pub struct CybersourceRefundResponse { + pub id: String, + pub status: RefundStatus, +} + +impl TryFrom> + for types::RefundsRouterData +{ + type Error = error_stack::Report; + fn try_from( + item: types::RefundsResponseRouterData, + ) -> Result { + Ok(Self { + response: Ok(types::RefundsResponseData { + connector_refund_id: item.response.id, + refund_status: enums::RefundStatus::from(item.response.status), + }), + ..item.data + }) + } +} diff --git a/crates/router/src/core/payments.rs b/crates/router/src/core/payments.rs index b10478a1ad..8ca9322c75 100644 --- a/crates/router/src/core/payments.rs +++ b/crates/router/src/core/payments.rs @@ -469,6 +469,7 @@ where pub refunds: Vec, pub sessions_token: Vec, pub card_cvc: Option>, + pub email: Option>, } #[derive(Debug)] diff --git a/crates/router/src/core/payments/operations/payment_cancel.rs b/crates/router/src/core/payments/operations/payment_cancel.rs index 5647f3dc5d..6377231744 100644 --- a/crates/router/src/core/payments/operations/payment_cancel.rs +++ b/crates/router/src/core/payments/operations/payment_cancel.rs @@ -113,6 +113,7 @@ impl GetTracker, api::PaymentsCancelRequest> payment_attempt, currency, amount, + email: None, mandate_id: None, setup_mandate: None, token: None, diff --git a/crates/router/src/core/payments/operations/payment_capture.rs b/crates/router/src/core/payments/operations/payment_capture.rs index 81620fb9e7..356ff837ea 100644 --- a/crates/router/src/core/payments/operations/payment_capture.rs +++ b/crates/router/src/core/payments/operations/payment_capture.rs @@ -125,6 +125,7 @@ impl GetTracker, api::PaymentsCaptu currency, force_sync: None, amount, + email: None, mandate_id: None, setup_mandate: None, token: None, diff --git a/crates/router/src/core/payments/operations/payment_confirm.rs b/crates/router/src/core/payments/operations/payment_confirm.rs index 93cd443a71..e92bd7cafe 100644 --- a/crates/router/src/core/payments/operations/payment_confirm.rs +++ b/crates/router/src/core/payments/operations/payment_confirm.rs @@ -142,6 +142,7 @@ impl GetTracker, api::PaymentsRequest> for Pa currency, connector_response, amount, + email: request.email.clone(), mandate_id: None, setup_mandate, token, diff --git a/crates/router/src/core/payments/operations/payment_create.rs b/crates/router/src/core/payments/operations/payment_create.rs index 33f1df2a88..357416fe2b 100644 --- a/crates/router/src/core/payments/operations/payment_create.rs +++ b/crates/router/src/core/payments/operations/payment_create.rs @@ -214,6 +214,7 @@ impl GetTracker, api::PaymentsRequest> for Pa payment_attempt, currency, amount, + email: request.email.clone(), mandate_id, setup_mandate, token, diff --git a/crates/router/src/core/payments/operations/payment_method_validate.rs b/crates/router/src/core/payments/operations/payment_method_validate.rs index bc3aa5e89b..5e9b510ca3 100644 --- a/crates/router/src/core/payments/operations/payment_method_validate.rs +++ b/crates/router/src/core/payments/operations/payment_method_validate.rs @@ -136,6 +136,7 @@ impl GetTracker, api::VerifyRequest> for Paym /// currency and amount are irrelevant in this scenario currency: storage_enums::Currency::default(), amount: api::Amount::Zero, + email: None, mandate_id: None, setup_mandate: request.mandate_data.clone(), token: request.payment_token.clone(), diff --git a/crates/router/src/core/payments/operations/payment_session.rs b/crates/router/src/core/payments/operations/payment_session.rs index 185889feaa..9f940cf396 100644 --- a/crates/router/src/core/payments/operations/payment_session.rs +++ b/crates/router/src/core/payments/operations/payment_session.rs @@ -13,6 +13,7 @@ use crate::{ payments::{self, helpers, operations, PaymentData}, }, db::StorageInterface, + pii, pii::Secret, routes::AppState, types::{ @@ -131,6 +132,7 @@ impl GetTracker, api::PaymentsSessionRequest> payment_attempt, currency, amount, + email: None::>, mandate_id: None, token: None, setup_mandate: None, diff --git a/crates/router/src/core/payments/operations/payment_start.rs b/crates/router/src/core/payments/operations/payment_start.rs index cf847caae4..8f086e6695 100644 --- a/crates/router/src/core/payments/operations/payment_start.rs +++ b/crates/router/src/core/payments/operations/payment_start.rs @@ -12,6 +12,7 @@ use crate::{ payments::{helpers, operations, CustomerDetails, PaymentAddress, PaymentData}, }, db::StorageInterface, + pii, pii::Secret, routes::AppState, types::{ @@ -128,6 +129,7 @@ impl GetTracker, api::PaymentsStartRequest> f payment_intent, currency, amount, + email: None::>, mandate_id: None, connector_response, setup_mandate: None, diff --git a/crates/router/src/core/payments/operations/payment_status.rs b/crates/router/src/core/payments/operations/payment_status.rs index 693a1f4ed8..4cf7606604 100644 --- a/crates/router/src/core/payments/operations/payment_status.rs +++ b/crates/router/src/core/payments/operations/payment_status.rs @@ -275,6 +275,7 @@ async fn get_tracker_for_sync< connector_response, currency, amount, + email: None, mandate_id: None, setup_mandate: None, token: None, diff --git a/crates/router/src/core/payments/operations/payment_update.rs b/crates/router/src/core/payments/operations/payment_update.rs index a9d1560b81..e9347e1aae 100644 --- a/crates/router/src/core/payments/operations/payment_update.rs +++ b/crates/router/src/core/payments/operations/payment_update.rs @@ -164,6 +164,7 @@ impl GetTracker, api::PaymentsRequest> for Pa payment_attempt, currency, amount, + email: request.email.clone(), mandate_id, token, setup_mandate, diff --git a/crates/router/src/core/payments/transformers.rs b/crates/router/src/core/payments/transformers.rs index 7e96342450..36df6cdbc3 100644 --- a/crates/router/src/core/payments/transformers.rs +++ b/crates/router/src/core/payments/transformers.rs @@ -433,6 +433,7 @@ impl TryFrom> for types::PaymentsAuthorizeData { amount: payment_data.amount.into(), currency: payment_data.currency, browser_info, + email: payment_data.email, order_details, }) } diff --git a/crates/router/src/types.rs b/crates/router/src/types.rs index 088e009038..7ab6221cf3 100644 --- a/crates/router/src/types.rs +++ b/crates/router/src/types.rs @@ -13,6 +13,7 @@ pub mod transformers; use std::marker::PhantomData; pub use api_models::enums::Connector; +use common_utils::pii::Email; use error_stack::{IntoReport, ResultExt}; use self::{api::payments, storage::enums as storage_enums}; @@ -90,6 +91,7 @@ pub struct RouterData { pub struct PaymentsAuthorizeData { pub payment_method_data: payments::PaymentMethod, pub amount: i64, + pub email: Option>, pub currency: storage_enums::Currency, pub confirm: bool, pub statement_descriptor_suffix: Option, diff --git a/crates/router/src/types/api.rs b/crates/router/src/types/api.rs index ff73559ad9..e34849fabe 100644 --- a/crates/router/src/types/api.rs +++ b/crates/router/src/types/api.rs @@ -146,6 +146,7 @@ impl ConnectorData { "braintree" => Ok(Box::new(&connector::Braintree)), "klarna" => Ok(Box::new(&connector::Klarna)), "applepay" => Ok(Box::new(&connector::Applepay)), + "cybersource" => Ok(Box::new(&connector::Cybersource)), "shift4" => Ok(Box::new(&connector::Shift4)), _ => Err(report!(errors::UnexpectedError) .attach_printable(format!("invalid connector name: {connector_name}"))) diff --git a/crates/router/tests/connectors/aci.rs b/crates/router/tests/connectors/aci.rs index 630107deab..3d417b15d5 100644 --- a/crates/router/tests/connectors/aci.rs +++ b/crates/router/tests/connectors/aci.rs @@ -48,6 +48,7 @@ fn construct_payment_router_data() -> types::PaymentsAuthorizeRouterData { capture_method: None, browser_info: None, order_details: None, + email: None, }, response: Err(types::ErrorResponse::default()), payment_method_id: None, diff --git a/crates/router/tests/connectors/authorizedotnet.rs b/crates/router/tests/connectors/authorizedotnet.rs index 6b9fe6b67c..45b8b18e3d 100644 --- a/crates/router/tests/connectors/authorizedotnet.rs +++ b/crates/router/tests/connectors/authorizedotnet.rs @@ -48,6 +48,7 @@ fn construct_payment_router_data() -> types::PaymentsAuthorizeRouterData { capture_method: None, browser_info: None, order_details: None, + email: None, }, payment_method_id: None, response: Err(types::ErrorResponse::default()), diff --git a/crates/router/tests/connectors/checkout.rs b/crates/router/tests/connectors/checkout.rs index cefd2e1d8a..578a7fab93 100644 --- a/crates/router/tests/connectors/checkout.rs +++ b/crates/router/tests/connectors/checkout.rs @@ -45,6 +45,7 @@ fn construct_payment_router_data() -> types::PaymentsAuthorizeRouterData { capture_method: None, browser_info: None, order_details: None, + email: None, }, response: Err(types::ErrorResponse::default()), payment_method_id: None, diff --git a/crates/router/tests/connectors/utils.rs b/crates/router/tests/connectors/utils.rs index 51fdfc8710..8997da2b5a 100644 --- a/crates/router/tests/connectors/utils.rs +++ b/crates/router/tests/connectors/utils.rs @@ -134,6 +134,7 @@ impl Default for PaymentAuthorizeType { setup_mandate_details: None, browser_info: None, order_details: None, + email: None, }; Self(data) }