From 8d337bf34f1cc02a7821318eada125ed1709203f Mon Sep 17 00:00:00 2001 From: github-actions <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 6 May 2024 00:14:50 +0000 Subject: [PATCH 01/34] chore(version): 2024.05.06.0 --- CHANGELOG.md | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8e961ee0b79..6f4da0ae086 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,30 @@ All notable changes to HyperSwitch will be documented here. - - - +## 2024.05.06.0 + +### Features + +- **core:** Add profile level config to toggle extended card bin ([#4445](https://github.com/juspay/hyperswitch/pull/4445)) ([`0304e8e`](https://github.com/juspay/hyperswitch/commit/0304e8e76a8ca1f602305991c4129107b20d148e)) +- **euclid_wasm:** Add configs for new payout connectors ([#4528](https://github.com/juspay/hyperswitch/pull/4528)) ([`9f41919`](https://github.com/juspay/hyperswitch/commit/9f41919094638baf9ea405a5acb89d69ecf1e2b7)) + +### Bug Fixes + +- **connector:** [BAMBORA] Restrict Card Expiry Year to 2 Digits and pass Amount in Decimal Format ([#4536](https://github.com/juspay/hyperswitch/pull/4536)) ([`d5d9006`](https://github.com/juspay/hyperswitch/commit/d5d9006fbd8e32f822f1e84d486b8a4483164baa)) +- **users:** Revert add password validations ([#4542](https://github.com/juspay/hyperswitch/pull/4542)) ([`bcce8b0`](https://github.com/juspay/hyperswitch/commit/bcce8b0489aad8455748e0945127f0a7447e8fb1)) + +### Refactors + +- **connector:** [NMI] Change fields for external auth due to API contract changes ([#4531](https://github.com/juspay/hyperswitch/pull/4531)) ([`7417250`](https://github.com/juspay/hyperswitch/commit/74172509e3b9c0af04bb2fe8a5192ab7f7fd37b5)) + +### Documentation + +- **cypress:** Update cypress docs ([#4505](https://github.com/juspay/hyperswitch/pull/4505)) ([`17b369c`](https://github.com/juspay/hyperswitch/commit/17b369cfabc42d4d06f65b92e967057dba348731)) + +**Full Changelog:** [`2024.05.03.1...2024.05.06.0`](https://github.com/juspay/hyperswitch/compare/2024.05.03.1...2024.05.06.0) + +- - - + ## 2024.05.03.1 ### Bug Fixes From 1335554f5193f05ba512d75a8eb9bb8047a65466 Mon Sep 17 00:00:00 2001 From: Mani Chandra <84711804+ThisIsMani@users.noreply.github.com> Date: Mon, 6 May 2024 12:39:36 +0530 Subject: [PATCH 02/34] feat: Add decision starter API for email flows (#4533) Co-authored-by: hyperswitch-bot[bot] <148525504+hyperswitch-bot[bot]@users.noreply.github.com> --- .github/CODEOWNERS | 2 + crates/api_models/src/events/user.rs | 8 +-- crates/api_models/src/user.rs | 5 ++ crates/router/src/core/user.rs | 51 +++++++++++++------ crates/router/src/routes/app.rs | 1 + crates/router/src/routes/lock_utils.rs | 3 +- crates/router/src/routes/user.rs | 19 +++++++ crates/router/src/services/email/types.rs | 44 ++++++++++++---- .../src/types/domain/user/decision_manager.rs | 21 +++++++- crates/router_env/src/logger/types.rs | 2 + 10 files changed, 127 insertions(+), 29 deletions(-) diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 0eb3d95bfc6..8f1326625cf 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -58,7 +58,9 @@ crates/router/src/core/payments/routing.rs @juspay/hyperswitch-routing crates/api_models/src/connector_onboarding.rs @juspay/hyperswitch-dashboard crates/api_models/src/user @juspay/hyperswitch-dashboard crates/api_models/src/user.rs @juspay/hyperswitch-dashboard +crates/api_models/src/events/user.rs @juspay/hyperswitch-dashboard crates/api_models/src/user_role.rs @juspay/hyperswitch-dashboard +crates/api_models/src/events/user_role.rs @juspay/hyperswitch-dashboard crates/api_models/src/verify_connector.rs @juspay/hyperswitch-dashboard crates/api_models/src/connector_onboarding.rs @juspay/hyperswitch-dashboard crates/diesel_models/src/query/dashboard_metadata.rs @juspay/hyperswitch-dashboard diff --git a/crates/api_models/src/events/user.rs b/crates/api_models/src/events/user.rs index 594b60b5816..14676656d0f 100644 --- a/crates/api_models/src/events/user.rs +++ b/crates/api_models/src/events/user.rs @@ -15,8 +15,8 @@ use crate::user::{ GetUserDetailsResponse, GetUserRoleDetailsRequest, GetUserRoleDetailsResponse, InviteUserRequest, ListUsersResponse, ReInviteUserRequest, ResetPasswordRequest, SendVerifyEmailRequest, SignInResponse, SignInWithTokenResponse, SignUpRequest, - SignUpWithMerchantIdRequest, SwitchMerchantIdRequest, UpdateUserAccountDetailsRequest, - UserMerchantCreate, VerifyEmailRequest, + SignUpWithMerchantIdRequest, SwitchMerchantIdRequest, TokenResponse, + UpdateUserAccountDetailsRequest, UserFromEmailRequest, UserMerchantCreate, VerifyEmailRequest, }; impl ApiEventMetric for DashboardEntryResponse { @@ -64,7 +64,9 @@ common_utils::impl_misc_api_event_type!( GetUserDetailsResponse, SignInWithTokenResponse, GetUserRoleDetailsRequest, - GetUserRoleDetailsResponse + GetUserRoleDetailsResponse, + TokenResponse, + UserFromEmailRequest ); #[cfg(feature = "dummy_connector")] diff --git a/crates/api_models/src/user.rs b/crates/api_models/src/user.rs index b4d53a92c1a..a6d5cf36358 100644 --- a/crates/api_models/src/user.rs +++ b/crates/api_models/src/user.rs @@ -231,3 +231,8 @@ pub enum SignInWithTokenResponse { Token(TokenResponse), SignInResponse(SignInResponse), } + +#[derive(Debug, serde::Deserialize, serde::Serialize)] +pub struct UserFromEmailRequest { + pub token: Secret, +} diff --git a/crates/router/src/core/user.rs b/crates/router/src/core/user.rs index 0ae1b162e0e..58a6452c9bb 100644 --- a/crates/router/src/core/user.rs +++ b/crates/router/src/core/user.rs @@ -183,21 +183,7 @@ pub async fn signin_token_only_flow( let next_flow = domain::NextFlow::from_origin(domain::Origin::SignIn, user_from_db.clone(), &state).await?; - let token = match next_flow.get_flow() { - domain::UserFlow::SPTFlow(spt_flow) => spt_flow.generate_spt(&state, &next_flow).await, - domain::UserFlow::JWTFlow(jwt_flow) => { - #[cfg(feature = "email")] - { - user_from_db.get_verification_days_left(&state)?; - } - - let user_role = user_from_db - .get_preferred_or_active_user_role_from_db(&state) - .await - .to_not_found_response(UserErrors::InternalServerError)?; - jwt_flow.generate_jwt(&state, &next_flow, &user_role).await - } - }?; + let token = next_flow.get_token(&state).await?; let response = user_api::SignInWithTokenResponse::Token(user_api::TokenResponse { token: token.clone(), @@ -1323,3 +1309,38 @@ pub async fn update_user_details( Ok(ApplicationResponse::StatusOk) } + +#[cfg(feature = "email")] +pub async fn user_from_email( + state: AppState, + req: user_api::UserFromEmailRequest, +) -> UserResponse { + let token = req.token.expose(); + let email_token = auth::decode_jwt::(&token, &state) + .await + .change_context(UserErrors::LinkInvalid)?; + + auth::blacklist::check_email_token_in_blacklist(&state, &token).await?; + + let user_from_db: domain::UserFromStorage = state + .store + .find_user_by_email( + &email_token + .get_email() + .change_context(UserErrors::InternalServerError)?, + ) + .await + .change_context(UserErrors::InternalServerError)? + .into(); + + let next_flow = + domain::NextFlow::from_origin(email_token.get_flow(), user_from_db.clone(), &state).await?; + + let token = next_flow.get_token(&state).await?; + + let response = user_api::TokenResponse { + token: token.clone(), + token_type: next_flow.get_flow().into(), + }; + auth::cookies::set_cookie_response(response, token) +} diff --git a/crates/router/src/routes/app.rs b/crates/router/src/routes/app.rs index 19a632bf895..0f3673ee206 100644 --- a/crates/router/src/routes/app.rs +++ b/crates/router/src/routes/app.rs @@ -1201,6 +1201,7 @@ impl User { #[cfg(feature = "email")] { route = route + .service(web::resource("/from_email").route(web::post().to(user_from_email))) .service( web::resource("/connect_account").route(web::post().to(user_connect_account)), ) diff --git a/crates/router/src/routes/lock_utils.rs b/crates/router/src/routes/lock_utils.rs index faef85f3901..4d1121a8eab 100644 --- a/crates/router/src/routes/lock_utils.rs +++ b/crates/router/src/routes/lock_utils.rs @@ -219,7 +219,8 @@ impl From for ApiIdentifier { | Flow::DeleteUserRole | Flow::TransferOrgOwnership | Flow::CreateRole - | Flow::UpdateRole => Self::UserRole, + | Flow::UpdateRole + | Flow::UserFromEmail => Self::UserRole, Flow::GetActionUrl | Flow::SyncOnboardingStatus | Flow::ResetTrackingId => { Self::ConnectorOnboarding diff --git a/crates/router/src/routes/user.rs b/crates/router/src/routes/user.rs index 4d841913bc0..ede6edbceb8 100644 --- a/crates/router/src/routes/user.rs +++ b/crates/router/src/routes/user.rs @@ -512,3 +512,22 @@ pub async fn update_user_account_details( )) .await } + +#[cfg(feature = "email")] +pub async fn user_from_email( + state: web::Data, + req: HttpRequest, + json_payload: web::Json, +) -> HttpResponse { + let flow = Flow::UserFromEmail; + Box::pin(api::server_wrap( + flow, + state.clone(), + &req, + json_payload.into_inner(), + |state, _: (), req_body, _| user_core::user_from_email(state, req_body), + &auth::NoAuth, + api_locking::LockAction::NotApplicable, + )) + .await +} diff --git a/crates/router/src/services/email/types.rs b/crates/router/src/services/email/types.rs index 323f98a56df..ee51d976b40 100644 --- a/crates/router/src/services/email/types.rs +++ b/crates/router/src/services/email/types.rs @@ -151,6 +151,7 @@ Email : {user_email} pub struct EmailToken { email: String, merchant_id: Option, + flow: domain::Origin, exp: u64, } @@ -158,6 +159,7 @@ impl EmailToken { pub async fn new_token( email: domain::UserEmail, merchant_id: Option, + flow: domain::Origin, settings: &configs::Settings, ) -> CustomResult { let expiration_duration = std::time::Duration::from_secs(consts::EMAIL_TOKEN_TIME_IN_SECS); @@ -165,6 +167,7 @@ impl EmailToken { let token_payload = Self { email: email.get_secret().expose(), merchant_id, + flow, exp, }; jwt::generate_jwt(&token_payload, settings).await @@ -177,6 +180,10 @@ impl EmailToken { pub fn get_merchant_id(&self) -> Option<&str> { self.merchant_id.as_deref() } + + pub fn get_flow(&self) -> domain::Origin { + self.flow.clone() + } } pub fn get_link_with_token( @@ -197,9 +204,14 @@ pub struct VerifyEmail { #[async_trait::async_trait] impl EmailData for VerifyEmail { async fn get_email_data(&self) -> CustomResult { - let token = EmailToken::new_token(self.recipient_email.clone(), None, &self.settings) - .await - .change_context(EmailError::TokenGenerationFailure)?; + let token = EmailToken::new_token( + self.recipient_email.clone(), + None, + domain::Origin::VerifyEmail, + &self.settings, + ) + .await + .change_context(EmailError::TokenGenerationFailure)?; let verify_email_link = get_link_with_token(&self.settings.email.base_url, token, "verify_email"); @@ -226,9 +238,14 @@ pub struct ResetPassword { #[async_trait::async_trait] impl EmailData for ResetPassword { async fn get_email_data(&self) -> CustomResult { - let token = EmailToken::new_token(self.recipient_email.clone(), None, &self.settings) - .await - .change_context(EmailError::TokenGenerationFailure)?; + let token = EmailToken::new_token( + self.recipient_email.clone(), + None, + domain::Origin::ResetPassword, + &self.settings, + ) + .await + .change_context(EmailError::TokenGenerationFailure)?; let reset_password_link = get_link_with_token(&self.settings.email.base_url, token, "set_password"); @@ -256,9 +273,14 @@ pub struct MagicLink { #[async_trait::async_trait] impl EmailData for MagicLink { async fn get_email_data(&self) -> CustomResult { - let token = EmailToken::new_token(self.recipient_email.clone(), None, &self.settings) - .await - .change_context(EmailError::TokenGenerationFailure)?; + let token = EmailToken::new_token( + self.recipient_email.clone(), + None, + domain::Origin::MagicLink, + &self.settings, + ) + .await + .change_context(EmailError::TokenGenerationFailure)?; let magic_link_login = get_link_with_token(&self.settings.email.base_url, token, "verify_email"); @@ -276,6 +298,7 @@ impl EmailData for MagicLink { } } +// TODO: Deprecate this and use InviteRegisteredUser for new invites pub struct InviteUser { pub recipient_email: domain::UserEmail, pub user_name: domain::UserName, @@ -290,6 +313,7 @@ impl EmailData for InviteUser { let token = EmailToken::new_token( self.recipient_email.clone(), Some(self.merchant_id.clone()), + domain::Origin::ResetPassword, &self.settings, ) .await @@ -310,6 +334,7 @@ impl EmailData for InviteUser { }) } } + pub struct InviteRegisteredUser { pub recipient_email: domain::UserEmail, pub user_name: domain::UserName, @@ -324,6 +349,7 @@ impl EmailData for InviteRegisteredUser { let token = EmailToken::new_token( self.recipient_email.clone(), Some(self.merchant_id.clone()), + domain::Origin::AcceptInvitationFromEmail, &self.settings, ) .await diff --git a/crates/router/src/types/domain/user/decision_manager.rs b/crates/router/src/types/domain/user/decision_manager.rs index b5aff779106..e460bc74649 100644 --- a/crates/router/src/types/domain/user/decision_manager.rs +++ b/crates/router/src/types/domain/user/decision_manager.rs @@ -4,7 +4,7 @@ use masking::Secret; use super::UserFromStorage; use crate::{ - core::errors::{UserErrors, UserResult}, + core::errors::{StorageErrorExt, UserErrors, UserResult}, routes::AppState, services::authentication as auth, }; @@ -225,6 +225,25 @@ impl NextFlow { pub fn get_flow(&self) -> UserFlow { self.next_flow } + + pub async fn get_token(&self, state: &AppState) -> UserResult> { + match self.next_flow { + UserFlow::SPTFlow(spt_flow) => spt_flow.generate_spt(state, self).await, + UserFlow::JWTFlow(jwt_flow) => { + #[cfg(feature = "email")] + { + self.user.get_verification_days_left(state)?; + } + + let user_role = self + .user + .get_preferred_or_active_user_role_from_db(state) + .await + .to_not_found_response(UserErrors::InternalServerError)?; + jwt_flow.generate_jwt(state, self, &user_role).await + } + } + } } impl From for TokenPurpose { diff --git a/crates/router_env/src/logger/types.rs b/crates/router_env/src/logger/types.rs index 26cb619d74c..78f570f647e 100644 --- a/crates/router_env/src/logger/types.rs +++ b/crates/router_env/src/logger/types.rs @@ -394,6 +394,8 @@ pub enum Flow { CreateRole, /// Update Role UpdateRole, + /// User email flow start + UserFromEmail, /// List initial webhook delivery attempts WebhookEventInitialDeliveryAttemptList, /// List delivery attempts for a webhook event From b878677f1572dceb9cd1983c2fd0b3b05ed8a573 Mon Sep 17 00:00:00 2001 From: Swangi Kumari <85639103+swangi-kumari@users.noreply.github.com> Date: Mon, 6 May 2024 13:09:18 +0530 Subject: [PATCH 03/34] refactor(paylater): use payment_method_data.billing fields instead of payment_method_data (#4333) Co-authored-by: hyperswitch-bot[bot] <148525504+hyperswitch-bot[bot]@users.noreply.github.com> --- crates/api_models/src/payments.rs | 30 +++++++------- .../connector/multisafepay/transformers.rs | 16 +++----- .../src/connector/stripe/transformers.rs | 40 +------------------ crates/router/src/types/domain/payments.rs | 32 ++++----------- openapi/openapi_spec.json | 24 +++++------ .../Payments - Confirm/request.json | 13 ++++++ .../Payments - Create/request.json | 3 +- .../stripe.postman_collection.json | 4 +- 8 files changed, 58 insertions(+), 104 deletions(-) diff --git a/crates/api_models/src/payments.rs b/crates/api_models/src/payments.rs index b39355d2c99..819e67a73bb 100644 --- a/crates/api_models/src/payments.rs +++ b/crates/api_models/src/payments.rs @@ -1063,11 +1063,11 @@ pub enum PayLaterData { /// For KlarnaRedirect as PayLater Option KlarnaRedirect { /// The billing email - #[schema(value_type = String)] - billing_email: Email, + #[schema(value_type = Option)] + billing_email: Option, // The billing country code - #[schema(value_type = CountryAlpha2, example = "US")] - billing_country: api_enums::CountryAlpha2, + #[schema(value_type = Option, example = "US")] + billing_country: Option, }, /// For Klarna Sdk as PayLater Option KlarnaSdk { @@ -1079,11 +1079,11 @@ pub enum PayLaterData { /// For AfterpayClearpay redirect as PayLater Option AfterpayClearpayRedirect { /// The billing email - #[schema(value_type = String)] - billing_email: Email, + #[schema(value_type = Option)] + billing_email: Option, /// The billing name - #[schema(value_type = String)] - billing_name: Secret, + #[schema(value_type = Option)] + billing_name: Option>, }, /// For PayBright Redirect as PayLater Option PayBrightRedirect {}, @@ -1102,13 +1102,13 @@ impl GetAddressFromPaymentMethodData for PayLaterData { billing_country, } => { let address_details = AddressDetails { - country: Some(*billing_country), + country: *billing_country, ..AddressDetails::default() }; Some(Address { address: Some(address_details), - email: Some(billing_email.clone()), + email: billing_email.clone(), phone: None, }) } @@ -1117,13 +1117,13 @@ impl GetAddressFromPaymentMethodData for PayLaterData { billing_name, } => { let address_details = AddressDetails { - first_name: Some(billing_name.clone()), + first_name: billing_name.clone(), ..AddressDetails::default() }; Some(Address { address: Some(address_details), - email: Some(billing_email.clone()), + email: billing_email.clone(), phone: None, }) } @@ -1444,7 +1444,7 @@ impl GetAddressFromPaymentMethodData for PaymentMethodData { Self::Card(card_data) => card_data.get_billing_address(), Self::CardRedirect(_) => None, Self::Wallet(wallet_data) => wallet_data.get_billing_address(), - Self::PayLater(_) => None, + Self::PayLater(pay_later) => pay_later.get_billing_address(), Self::BankRedirect(_) => None, Self::BankDebit(_) => None, Self::BankTransfer(_) => None, @@ -4871,8 +4871,8 @@ mod billing_from_payment_method_data { let klarna_paylater_payment_method_data = PaymentMethodData::PayLater(PayLaterData::KlarnaRedirect { - billing_email: test_email.clone(), - billing_country: TEST_COUNTRY, + billing_email: Some(test_email.clone()), + billing_country: Some(TEST_COUNTRY), }); let billing_address = klarna_paylater_payment_method_data diff --git a/crates/router/src/connector/multisafepay/transformers.rs b/crates/router/src/connector/multisafepay/transformers.rs index 926781e39e9..850bd058e41 100644 --- a/crates/router/src/connector/multisafepay/transformers.rs +++ b/crates/router/src/connector/multisafepay/transformers.rs @@ -349,10 +349,9 @@ impl TryFrom<&MultisafepayRouterData<&types::PaymentsAuthorizeRouterData>> utils::get_unimplemented_payment_method_error_message("multisafepay"), ))?, }), - domain::PaymentMethodData::PayLater(domain::PayLaterData::KlarnaRedirect { - billing_email: _, - billing_country: _, - }) => Some(Gateway::Klarna), + domain::PaymentMethodData::PayLater(domain::PayLaterData::KlarnaRedirect {}) => { + Some(Gateway::Klarna) + } domain::PaymentMethodData::MandatePayment => None, domain::PaymentMethodData::CardRedirect(_) | domain::PaymentMethodData::PayLater(_) @@ -484,15 +483,12 @@ impl TryFrom<&MultisafepayRouterData<&types::PaymentsAuthorizeRouterData>> domain::PaymentMethodData::PayLater(ref paylater) => { Some(GatewayInfo::PayLater(PayLaterInfo { email: Some(match paylater { - domain::PayLaterData::KlarnaRedirect { billing_email, .. } => { - billing_email.clone() + domain::PayLaterData::KlarnaRedirect {} => { + item.router_data.get_billing_email()? } domain::PayLaterData::KlarnaSdk { token: _ } | domain::PayLaterData::AffirmRedirect {} - | domain::PayLaterData::AfterpayClearpayRedirect { - billing_email: _, - billing_name: _, - } + | domain::PayLaterData::AfterpayClearpayRedirect {} | domain::PayLaterData::PayBrightRedirect {} | domain::PayLaterData::WalleyRedirect {} | domain::PayLaterData::AlmaRedirect {} diff --git a/crates/router/src/connector/stripe/transformers.rs b/crates/router/src/connector/stripe/transformers.rs index 46c47a51f32..6932f6b7495 100644 --- a/crates/router/src/connector/stripe/transformers.rs +++ b/crates/router/src/connector/stripe/transformers.rs @@ -1057,44 +1057,6 @@ impl From<&domain::BankDebitData> for StripePaymentMethodType { } } -impl TryFrom<(&domain::payments::PayLaterData, StripePaymentMethodType)> for StripeBillingAddress { - type Error = errors::ConnectorError; - - fn try_from( - (pay_later_data, pm_type): (&domain::payments::PayLaterData, StripePaymentMethodType), - ) -> Result { - match (pay_later_data, pm_type) { - ( - domain::payments::PayLaterData::KlarnaRedirect { - billing_email, - billing_country, - }, - StripePaymentMethodType::Klarna, - ) => Ok(Self { - email: Some(billing_email.to_owned()), - country: Some(billing_country.to_owned()), - ..Self::default() - }), - ( - domain::payments::PayLaterData::AffirmRedirect {}, - StripePaymentMethodType::Affirm, - ) => Ok(Self::default()), - ( - domain::payments::PayLaterData::AfterpayClearpayRedirect { - billing_email, - billing_name, - }, - StripePaymentMethodType::AfterpayClearpay, - ) => Ok(Self { - email: Some(billing_email.to_owned()), - name: Some(billing_name.to_owned()), - ..Self::default() - }), - _ => Err(errors::ConnectorError::MismatchedPaymentData), - } - } -} - impl From<&domain::BankDebitBilling> for StripeBillingAddress { fn from(item: &domain::BankDebitBilling) -> Self { Self { @@ -1318,7 +1280,7 @@ fn create_stripe_payment_method( } domain::PaymentMethodData::PayLater(pay_later_data) => { let stripe_pm_type = StripePaymentMethodType::try_from(pay_later_data)?; - let billing_address = StripeBillingAddress::try_from((pay_later_data, stripe_pm_type))?; + Ok(( StripePaymentMethodData::PayLater(StripePayLaterData { payment_method_data_type: stripe_pm_type, diff --git a/crates/router/src/types/domain/payments.rs b/crates/router/src/types/domain/payments.rs index fe34f2092b9..58f2fa98eef 100644 --- a/crates/router/src/types/domain/payments.rs +++ b/crates/router/src/types/domain/payments.rs @@ -66,18 +66,10 @@ pub enum CardRedirectData { #[derive(Eq, PartialEq, Clone, Debug, serde::Deserialize, serde::Serialize)] pub enum PayLaterData { - KlarnaRedirect { - billing_email: Email, - billing_country: common_enums::CountryAlpha2, - }, - KlarnaSdk { - token: String, - }, + KlarnaRedirect {}, + KlarnaSdk { token: String }, AffirmRedirect {}, - AfterpayClearpayRedirect { - billing_email: Email, - billing_name: Secret, - }, + AfterpayClearpayRedirect {}, PayBrightRedirect {}, WalleyRedirect {}, AlmaRedirect {}, @@ -713,22 +705,12 @@ impl From for ApplePayWalletData { impl From for PayLaterData { fn from(value: api_models::payments::PayLaterData) -> Self { match value { - api_models::payments::PayLaterData::KlarnaRedirect { - billing_email, - billing_country, - } => Self::KlarnaRedirect { - billing_email, - billing_country, - }, + api_models::payments::PayLaterData::KlarnaRedirect { .. } => Self::KlarnaRedirect {}, api_models::payments::PayLaterData::KlarnaSdk { token } => Self::KlarnaSdk { token }, api_models::payments::PayLaterData::AffirmRedirect {} => Self::AffirmRedirect {}, - api_models::payments::PayLaterData::AfterpayClearpayRedirect { - billing_email, - billing_name, - } => Self::AfterpayClearpayRedirect { - billing_email, - billing_name, - }, + api_models::payments::PayLaterData::AfterpayClearpayRedirect { .. } => { + Self::AfterpayClearpayRedirect {} + } api_models::payments::PayLaterData::PayBrightRedirect {} => Self::PayBrightRedirect {}, api_models::payments::PayLaterData::WalleyRedirect {} => Self::WalleyRedirect {}, api_models::payments::PayLaterData::AlmaRedirect {} => Self::AlmaRedirect {}, diff --git a/openapi/openapi_spec.json b/openapi/openapi_spec.json index d278c50a388..586fd1c1115 100644 --- a/openapi/openapi_spec.json +++ b/openapi/openapi_spec.json @@ -11951,17 +11951,19 @@ "klarna_redirect": { "type": "object", "description": "For KlarnaRedirect as PayLater Option", - "required": [ - "billing_email", - "billing_country" - ], "properties": { "billing_email": { "type": "string", - "description": "The billing email" + "description": "The billing email", + "nullable": true }, "billing_country": { - "$ref": "#/components/schemas/CountryAlpha2" + "allOf": [ + { + "$ref": "#/components/schemas/CountryAlpha2" + } + ], + "nullable": true } } } @@ -12009,18 +12011,16 @@ "afterpay_clearpay_redirect": { "type": "object", "description": "For AfterpayClearpay redirect as PayLater Option", - "required": [ - "billing_email", - "billing_name" - ], "properties": { "billing_email": { "type": "string", - "description": "The billing email" + "description": "The billing email", + "nullable": true }, "billing_name": { "type": "string", - "description": "The billing name" + "description": "The billing name", + "nullable": true } } } diff --git a/postman/collection-dir/stripe/Flow Testcases/Happy Cases/Scenario12-BNPL-klarna/Payments - Confirm/request.json b/postman/collection-dir/stripe/Flow Testcases/Happy Cases/Scenario12-BNPL-klarna/Payments - Confirm/request.json index 9b19b521cad..bd39f2f528f 100644 --- a/postman/collection-dir/stripe/Flow Testcases/Happy Cases/Scenario12-BNPL-klarna/Payments - Confirm/request.json +++ b/postman/collection-dir/stripe/Flow Testcases/Happy Cases/Scenario12-BNPL-klarna/Payments - Confirm/request.json @@ -50,6 +50,19 @@ } } }, + "billing": { + "address": { + "line1": "1467", + "line2": "Harrison Street", + "line3": "Harrison Street", + "city": "San Fransico", + "state": "California", + "zip": "94122", + "country": "US", + "first_name": "sundari" + }, + "email": "narayan@example.com" + }, "client_secret": "{{client_secret}}" } }, diff --git a/postman/collection-dir/stripe/Flow Testcases/Happy Cases/Scenario13-BNPL-afterpay/Payments - Create/request.json b/postman/collection-dir/stripe/Flow Testcases/Happy Cases/Scenario13-BNPL-afterpay/Payments - Create/request.json index dcbeac44d9e..273ed5ee3c6 100644 --- a/postman/collection-dir/stripe/Flow Testcases/Happy Cases/Scenario13-BNPL-afterpay/Payments - Create/request.json +++ b/postman/collection-dir/stripe/Flow Testcases/Happy Cases/Scenario13-BNPL-afterpay/Payments - Create/request.json @@ -42,7 +42,8 @@ "first_name": "John", "last_name": "Doe", "country": "SE" - } + }, + "email": "narayan@example.com" }, "browser_info": { "user_agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.110 Safari/537.36", diff --git a/postman/collection-json/stripe.postman_collection.json b/postman/collection-json/stripe.postman_collection.json index 2a18984e3f1..c981df1a4c4 100644 --- a/postman/collection-json/stripe.postman_collection.json +++ b/postman/collection-json/stripe.postman_collection.json @@ -13426,7 +13426,7 @@ "language": "json" } }, - "raw": "{\"payment_method\":\"pay_later\",\"payment_method_type\":\"klarna\",\"payment_experience\":\"redirect_to_url\",\"payment_method_data\":{\"pay_later\":{\"klarna_redirect\":{\"issuer_name\":\"stripe\",\"billing_email\":\"arjun.karthik@juspay.in\",\"billing_country\":\"US\"}}},\"client_secret\":\"{{client_secret}}\"}" + "raw": "{\"payment_method\":\"pay_later\",\"payment_method_type\":\"klarna\",\"payment_experience\":\"redirect_to_url\",\"payment_method_data\":{\"pay_later\":{\"klarna_redirect\":{\"issuer_name\":\"stripe\",\"billing_email\":\"arjun.karthik@juspay.in\",\"billing_country\":\"US\"}}},\"billing\": {\"address\": {\"line1\": \"1467\",\"line2\": \"Harrison Street\",\"line3\": \"Harrison Street\",\"city\": \"San Fransico\",\"state\": \"California\",\"zip\": \"94122\",\"country\": \"US\",\"first_name\": \"sundari\"},\"email\": \"narayan@example.com\"},\"client_secret\":\"{{client_secret}}\"}" }, "url": { "raw": "{{baseUrl}}/payments/:id/confirm", @@ -13677,7 +13677,7 @@ "language": "json" } }, - "raw": "{\"amount\":7000,\"currency\":\"USD\",\"confirm\":false,\"capture_method\":\"automatic\",\"capture_on\":\"2022-09-10T10:11:12Z\",\"customer_id\":\"StripeCustomer\",\"email\":\"abcdef123@gmail.com\",\"name\":\"John Doe\",\"phone\":\"999999999\",\"phone_country_code\":\"+65\",\"description\":\"Its my first payment request\",\"authentication_type\":\"three_ds\",\"return_url\":\"https://duck.com\",\"billing\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"first_name\":\"John\",\"last_name\":\"Doe\",\"country\":\"SE\"}},\"browser_info\":{\"user_agent\":\"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.110 Safari/537.36\",\"accept_header\":\"text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8\",\"language\":\"nl-NL\",\"color_depth\":24,\"screen_height\":723,\"screen_width\":1536,\"time_zone\":0,\"java_enabled\":true,\"java_script_enabled\":true,\"ip_address\":\"127.0.0.1\"},\"shipping\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"SE\",\"first_name\":\"John\",\"last_name\":\"Doe\"}},\"statement_descriptor_name\":\"joseph\",\"statement_descriptor_suffix\":\"JS\",\"metadata\":{\"order_details\":{\"product_name\":\"Socks\",\"amount\":7000,\"quantity\":1}},\"routing\":{\"type\":\"single\",\"data\":\"stripe\"}}" + "raw": "{\"amount\":7000,\"currency\":\"USD\",\"confirm\":false,\"capture_method\":\"automatic\",\"capture_on\":\"2022-09-10T10:11:12Z\",\"customer_id\":\"StripeCustomer\",\"email\":\"abcdef123@gmail.com\",\"name\":\"John Doe\",\"phone\":\"999999999\",\"phone_country_code\":\"+65\",\"description\":\"Its my first payment request\",\"authentication_type\":\"three_ds\",\"return_url\":\"https://duck.com\",\"billing\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"first_name\":\"John\",\"last_name\":\"Doe\",\"country\":\"SE\"},\"email\": \"narayan@example.com\"},\"browser_info\":{\"user_agent\":\"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.110 Safari/537.36\",\"accept_header\":\"text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8\",\"language\":\"nl-NL\",\"color_depth\":24,\"screen_height\":723,\"screen_width\":1536,\"time_zone\":0,\"java_enabled\":true,\"java_script_enabled\":true,\"ip_address\":\"127.0.0.1\"},\"shipping\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"SE\",\"first_name\":\"John\",\"last_name\":\"Doe\"}},\"statement_descriptor_name\":\"joseph\",\"statement_descriptor_suffix\":\"JS\",\"metadata\":{\"order_details\":{\"product_name\":\"Socks\",\"amount\":7000,\"quantity\":1}},\"routing\":{\"type\":\"single\",\"data\":\"stripe\"}}" }, "url": { "raw": "{{baseUrl}}/payments", From 89e5884f9eb341026b09a0d1ab8b836de5ba0c19 Mon Sep 17 00:00:00 2001 From: Sampras Lopes Date: Mon, 6 May 2024 16:34:04 +0530 Subject: [PATCH 04/34] feat(clickhouse): Init Clickhouse container on startup (#4365) Co-authored-by: Vrishab Srivatsa <136090360+vsrivatsa-juspay@users.noreply.github.com> --- .../docs/clickhouse/scripts/api_events.sql | 186 ++++++++++++++---- .../clickhouse/scripts/connector_events.sql | 138 +++++++++---- .../docs/clickhouse/scripts/disputes.sql | 54 +++-- .../scripts/outgoing_webhook_events.sql | 178 +++++++++++------ .../clickhouse/scripts/payment_attempts.sql | 63 +++--- .../clickhouse/scripts/payment_intents.sql | 34 ++-- .../docs/clickhouse/scripts/payouts.sql | 24 +-- .../docs/clickhouse/scripts/refunds.sql | 29 ++- docker-compose-development.yml | 26 +++ docker-compose.yml | 7 +- 10 files changed, 501 insertions(+), 238 deletions(-) diff --git a/crates/analytics/docs/clickhouse/scripts/api_events.sql b/crates/analytics/docs/clickhouse/scripts/api_events.sql index e466fc56cb6..36ad10db953 100644 --- a/crates/analytics/docs/clickhouse/scripts/api_events.sql +++ b/crates/analytics/docs/clickhouse/scripts/api_events.sql @@ -23,17 +23,16 @@ CREATE TABLE api_events_queue ( `ip_addr` String, `hs_latency` Nullable(UInt128), `http_method` LowCardinality(String), - `url_path` String, + `url_path` Nullable(String), `dispute_id` Nullable(String) ) ENGINE = Kafka SETTINGS kafka_broker_list = 'kafka0:29092', kafka_topic_list = 'hyperswitch-api-log-events', -kafka_group_name = 'hyper-c1', +kafka_group_name = 'hyper', kafka_format = 'JSONEachRow', kafka_handle_error_mode = 'stream'; - -CREATE TABLE api_events_dist ( - `merchant_id` String, +CREATE TABLE api_events ( + `merchant_id` LowCardinality(String), `payment_id` Nullable(String), `refund_id` Nullable(String), `payment_method_id` Nullable(String), @@ -51,27 +50,85 @@ CREATE TABLE api_events_dist ( `error` Nullable(String), `authentication_data` Nullable(String), `status_code` UInt32, - `created_at_timestamp` DateTime64(3), + `created_at` DateTime64(3), + `inserted_at` DateTime DEFAULT now() CODEC(T64, LZ4), `latency` UInt128, `user_agent` String, `ip_addr` String, `hs_latency` Nullable(UInt128), `http_method` LowCardinality(String), - `url_path` String, + `url_path` Nullable(String), `dispute_id` Nullable(String), + `masked_response` Nullable(String), INDEX flowIndex flow_type TYPE bloom_filter GRANULARITY 1, INDEX apiIndex api_flow TYPE bloom_filter GRANULARITY 1, INDEX statusIndex status_code TYPE bloom_filter GRANULARITY 1 +) ENGINE = MergeTree PARTITION BY toStartOfDay(created_at) +ORDER BY + ( + created_at, + merchant_id, + flow_type, + status_code, + api_flow + ) TTL inserted_at + toIntervalMonth(18) SETTINGS index_granularity = 8192; + +CREATE TABLE api_events_audit ( + `merchant_id` LowCardinality(String), + `payment_id` String, + `refund_id` Nullable(String), + `payment_method_id` Nullable(String), + `payment_method` Nullable(String), + `payment_method_type` Nullable(String), + `customer_id` Nullable(String), + `user_id` Nullable(String), + `connector` Nullable(String), + `request_id` String, + `flow_type` LowCardinality(String), + `api_flow` LowCardinality(String), + `api_auth_type` LowCardinality(String), + `request` String, + `response` Nullable(String), + `error` Nullable(String), + `authentication_data` Nullable(String), + `status_code` UInt32, + `created_at` DateTime64(3), + `inserted_at` DateTime DEFAULT now() CODEC(T64, LZ4), + `latency` UInt128, + `user_agent` String, + `ip_addr` String, + `hs_latency` Nullable(UInt128), + `http_method` LowCardinality(Nullable(String)), + `url_path` Nullable(String), + `dispute_id` Nullable(String), + `masked_response` Nullable(String) +) ENGINE = MergeTree PARTITION BY merchant_id +ORDER BY + (merchant_id, payment_id) TTL inserted_at + toIntervalMonth(18) SETTINGS index_granularity = 8192; + +CREATE MATERIALIZED VIEW api_events_parse_errors ( + `topic` String, + `partition` Int64, + `offset` Int64, + `raw` String, + `error` String ) ENGINE = MergeTree -PARTITION BY toStartOfDay(created_at) ORDER BY - (created_at, merchant_id, flow_type, status_code, api_flow) -TTL created_at + toIntervalMonth(6) -; + (topic, partition, offset) SETTINGS index_granularity = 8192 AS +SELECT + _topic AS topic, + _partition AS partition, + _offset AS offset, + _raw_message AS raw, + _error AS error +FROM + api_events_queue +WHERE + length(_error) > 0; -CREATE MATERIALIZED VIEW api_events_mv TO api_events_dist ( +CREATE MATERIALIZED VIEW api_events_audit_mv TO api_events_audit ( `merchant_id` String, - `payment_id` Nullable(String), + `payment_id` String, `refund_id` Nullable(String), `payment_method_id` Nullable(String), `payment_method` Nullable(String), @@ -88,14 +145,16 @@ CREATE MATERIALIZED VIEW api_events_mv TO api_events_dist ( `error` Nullable(String), `authentication_data` Nullable(String), `status_code` UInt32, - `created_at_timestamp` DateTime64(3), + `created_at` DateTime64(3), + `inserted_at` DateTime DEFAULT now() CODEC(T64, LZ4), `latency` UInt128, `user_agent` String, `ip_addr` String, `hs_latency` Nullable(UInt128), - `http_method` LowCardinality(String), - `url_path` String, - `dispute_id` Nullable(String) + `http_method` LowCardinality(Nullable(String)), + `url_path` Nullable(String), + `dispute_id` Nullable(String), + `masked_response` Nullable(String) ) AS SELECT merchant_id, @@ -116,37 +175,82 @@ SELECT error, authentication_data, status_code, - created_at_timestamp, - now() as inserted_at, + created_at_timestamp AS created_at, + now() AS inserted_at, latency, user_agent, ip_addr, hs_latency, http_method, url_path, - dispute_id + dispute_id, + response AS masked_response FROM api_events_queue -where length(_error) = 0; - +WHERE + (length(_error) = 0) + AND (payment_id IS NOT NULL); -CREATE MATERIALIZED VIEW api_events_parse_errors -( - `topic` String, - `partition` Int64, - `offset` Int64, - `raw` String, - `error` String -) -ENGINE = MergeTree -ORDER BY (topic, partition, offset) -SETTINGS index_granularity = 8192 AS +CREATE MATERIALIZED VIEW api_events_mv TO api_events ( + `merchant_id` String, + `payment_id` Nullable(String), + `refund_id` Nullable(String), + `payment_method_id` Nullable(String), + `payment_method` Nullable(String), + `payment_method_type` Nullable(String), + `customer_id` Nullable(String), + `user_id` Nullable(String), + `connector` Nullable(String), + `request_id` String, + `flow_type` LowCardinality(String), + `api_flow` LowCardinality(String), + `api_auth_type` LowCardinality(String), + `request` String, + `response` Nullable(String), + `error` Nullable(String), + `authentication_data` Nullable(String), + `status_code` UInt32, + `created_at` DateTime64(3), + `inserted_at` DateTime DEFAULT now() CODEC(T64, LZ4), + `latency` UInt128, + `user_agent` String, + `ip_addr` String, + `hs_latency` Nullable(UInt128), + `http_method` LowCardinality(Nullable(String)), + `url_path` Nullable(String), + `dispute_id` Nullable(String), + `masked_response` Nullable(String) +) AS SELECT - _topic AS topic, - _partition AS partition, - _offset AS offset, - _raw_message AS raw, - _error AS error -FROM api_events_queue -WHERE length(_error) > 0 -; + merchant_id, + payment_id, + refund_id, + payment_method_id, + payment_method, + payment_method_type, + customer_id, + user_id, + connector, + request_id, + flow_type, + api_flow, + api_auth_type, + request, + response, + error, + authentication_data, + status_code, + created_at_timestamp AS created_at, + now() AS inserted_at, + latency, + user_agent, + ip_addr, + hs_latency, + http_method, + url_path, + dispute_id, + response AS masked_response +FROM + api_events_queue +WHERE + length(_error) = 0; \ No newline at end of file diff --git a/crates/analytics/docs/clickhouse/scripts/connector_events.sql b/crates/analytics/docs/clickhouse/scripts/connector_events.sql index 47bbd7aec00..61a036bccd5 100644 --- a/crates/analytics/docs/clickhouse/scripts/connector_events.sql +++ b/crates/analytics/docs/clickhouse/scripts/connector_events.sql @@ -5,24 +5,42 @@ CREATE TABLE connector_events_queue ( `request_id` String, `flow` LowCardinality(String), `request` String, - `response` Nullable(String), `masked_response` Nullable(String), `error` Nullable(String), `status_code` UInt32, `created_at` DateTime64(3), `latency` UInt128, `method` LowCardinality(String), - `refund_id` Nullable(String), - `dispute_id` Nullable(String) + `dispute_id` Nullable(String), + `refund_id` Nullable(String) ) ENGINE = Kafka SETTINGS kafka_broker_list = 'kafka0:29092', -kafka_topic_list = 'hyperswitch-connector-api-events', -kafka_group_name = 'hyper-c1', +kafka_topic_list = 'hyperswitch-outgoing-connector-events', +kafka_group_name = 'hyper', kafka_format = 'JSONEachRow', kafka_handle_error_mode = 'stream'; +CREATE MATERIALIZED VIEW connector_events_parse_errors ( + `topic` String, + `partition` Int64, + `offset` Int64, + `raw` String, + `error` String +) ENGINE = MergeTree +ORDER BY + (topic, partition, offset) SETTINGS index_granularity = 8192 AS +SELECT + _topic AS topic, + _partition AS partition, + _offset AS offset, + _raw_message AS raw, + _error AS error +FROM + connector_events_queue +WHERE + length(_error) > 0; -CREATE TABLE connector_events_dist ( - `merchant_id` String, +CREATE TABLE connector_events ( + `merchant_id` LowCardinality(String), `payment_id` Nullable(String), `connector_name` LowCardinality(String), `request_id` String, @@ -36,19 +54,46 @@ CREATE TABLE connector_events_dist ( `inserted_at` DateTime DEFAULT now() CODEC(T64, LZ4), `latency` UInt128, `method` LowCardinality(String), + `dispute_id` Nullable(String), `refund_id` Nullable(String), + INDEX flowIndex flow TYPE bloom_filter GRANULARITY 1, + INDEX connectorIndex connector_name TYPE bloom_filter GRANULARITY 1, + INDEX statusIndex status_code TYPE bloom_filter GRANULARITY 1 +) ENGINE = MergeTree PARTITION BY toStartOfDay(created_at) +ORDER BY + ( + created_at, + merchant_id, + connector_name, + flow, + status_code + ) TTL inserted_at + toIntervalMonth(18) SETTINGS index_granularity = 8192; + +CREATE TABLE connector_events_audit ( + `merchant_id` LowCardinality(String), + `payment_id` String, + `connector_name` LowCardinality(String), + `request_id` String, + `flow` LowCardinality(String), + `request` String, + `response` Nullable(String), + `masked_response` Nullable(String), + `error` Nullable(String), + `status_code` UInt32, + `created_at` DateTime64(3), + `inserted_at` DateTime DEFAULT now() CODEC(T64, LZ4), + `latency` UInt128, + `method` LowCardinality(String), `dispute_id` Nullable(String), + `refund_id` Nullable(String), INDEX flowIndex flow TYPE bloom_filter GRANULARITY 1, INDEX connectorIndex connector_name TYPE bloom_filter GRANULARITY 1, INDEX statusIndex status_code TYPE bloom_filter GRANULARITY 1 -) ENGINE = MergeTree -PARTITION BY toStartOfDay(created_at) +) ENGINE = MergeTree PARTITION BY merchant_id ORDER BY - (created_at, merchant_id, connector_name, flow) -TTL inserted_at + toIntervalMonth(6) -; + (merchant_id, payment_id) TTL inserted_at + toIntervalMonth(18) SETTINGS index_granularity = 8192; -CREATE MATERIALIZED VIEW connector_events_mv TO connector_events_dist ( +CREATE MATERIALIZED VIEW connector_events_audit_mv TO connector_events_audit ( `merchant_id` String, `payment_id` Nullable(String), `connector_name` LowCardinality(String), @@ -60,6 +105,7 @@ CREATE MATERIALIZED VIEW connector_events_mv TO connector_events_dist ( `error` Nullable(String), `status_code` UInt32, `created_at` DateTime64(3), + `inserted_at` DateTime DEFAULT now() CODEC(T64, LZ4), `latency` UInt128, `method` LowCardinality(String), `refund_id` Nullable(String), @@ -72,38 +118,58 @@ SELECT request_id, flow, request, - response, + masked_response AS response, masked_response, error, status_code, created_at, - now() as inserted_at, + now64() AS inserted_at, latency, method, refund_id, dispute_id FROM connector_events_queue -where length(_error) = 0; +WHERE + (length(_error) = 0) + AND (payment_id IS NOT NULL); - -CREATE MATERIALIZED VIEW connector_events_parse_errors -( - `topic` String, - `partition` Int64, - `offset` Int64, - `raw` String, - `error` String -) -ENGINE = MergeTree -ORDER BY (topic, partition, offset) -SETTINGS index_granularity = 8192 AS +CREATE MATERIALIZED VIEW connector_events_mv TO connector_events ( + `merchant_id` String, + `payment_id` Nullable(String), + `connector_name` LowCardinality(String), + `request_id` String, + `flow` LowCardinality(String), + `request` String, + `response` Nullable(String), + `masked_response` Nullable(String), + `error` Nullable(String), + `status_code` UInt32, + `created_at` DateTime64(3), + `inserted_at` DateTime DEFAULT now() CODEC(T64, LZ4), + `latency` UInt128, + `method` LowCardinality(String), + `refund_id` Nullable(String), + `dispute_id` Nullable(String) +) AS SELECT - _topic AS topic, - _partition AS partition, - _offset AS offset, - _raw_message AS raw, - _error AS error -FROM connector_events_queue -WHERE length(_error) > 0 -; + merchant_id, + payment_id, + connector_name, + request_id, + flow, + request, + masked_response AS response, + masked_response, + error, + status_code, + created_at, + now64() AS inserted_at, + latency, + method, + refund_id, + dispute_id +FROM + connector_events_queue +WHERE + length(_error) = 0; \ No newline at end of file diff --git a/crates/analytics/docs/clickhouse/scripts/disputes.sql b/crates/analytics/docs/clickhouse/scripts/disputes.sql index 3f700bc06d3..bb7472a4d54 100644 --- a/crates/analytics/docs/clickhouse/scripts/disputes.sql +++ b/crates/analytics/docs/clickhouse/scripts/disputes.sql @@ -1,6 +1,6 @@ CREATE TABLE dispute_queue ( `dispute_id` String, - `amount` String, + `dispute_amount` UInt32, `currency` String, `dispute_stage` LowCardinality(String), `dispute_status` LowCardinality(String), @@ -23,20 +23,19 @@ CREATE TABLE dispute_queue ( `sign_flag` Int8 ) ENGINE = Kafka SETTINGS kafka_broker_list = 'kafka0:29092', kafka_topic_list = 'hyperswitch-dispute-events', -kafka_group_name = 'hyper-c1', +kafka_group_name = 'hyper', kafka_format = 'JSONEachRow', kafka_handle_error_mode = 'stream'; - CREATE TABLE dispute ( `dispute_id` String, - `amount` String, + `dispute_amount` UInt32, `currency` String, `dispute_stage` LowCardinality(String), `dispute_status` LowCardinality(String), `payment_id` String, `attempt_id` String, - `merchant_id` String, + `merchant_id` LowCardinality(String), `connector_status` String, `connector_dispute_id` String, `connector_reason` Nullable(String), @@ -47,26 +46,21 @@ CREATE TABLE dispute ( `created_at` DateTime DEFAULT now() CODEC(T64, LZ4), `modified_at` DateTime DEFAULT now() CODEC(T64, LZ4), `connector` LowCardinality(String), - `evidence` String DEFAULT '{}' CODEC(T64, LZ4), + `evidence` String DEFAULT '{}', `profile_id` Nullable(String), `merchant_connector_id` Nullable(String), `inserted_at` DateTime DEFAULT now() CODEC(T64, LZ4), - `sign_flag` Int8 + `sign_flag` Int8, INDEX connectorIndex connector TYPE bloom_filter GRANULARITY 1, INDEX disputeStatusIndex dispute_status TYPE bloom_filter GRANULARITY 1, INDEX disputeStageIndex dispute_stage TYPE bloom_filter GRANULARITY 1 -) ENGINE = CollapsingMergeTree( - sign_flag -) -PARTITION BY toStartOfDay(created_at) +) ENGINE = CollapsingMergeTree(sign_flag) PARTITION BY toStartOfDay(created_at) ORDER BY - (created_at, merchant_id, dispute_id) -TTL created_at + toIntervalMonth(6) -; + (created_at, merchant_id, dispute_id) TTL inserted_at + toIntervalMonth(18) SETTINGS index_granularity = 8192; -CREATE MATERIALIZED VIEW kafka_parse_dispute TO dispute ( +CREATE MATERIALIZED VIEW dispute_mv TO dispute ( `dispute_id` String, - `amount` String, + `dispute_amount` UInt32, `currency` String, `dispute_stage` LowCardinality(String), `dispute_status` LowCardinality(String), @@ -91,7 +85,7 @@ CREATE MATERIALIZED VIEW kafka_parse_dispute TO dispute ( ) AS SELECT dispute_id, - amount, + dispute_amount, currency, dispute_stage, dispute_status, @@ -111,7 +105,29 @@ SELECT evidence, profile_id, merchant_connector_id, - now() as inserted_at, + now() AS inserted_at, sign_flag FROM - dispute_queue; + dispute_queue +WHERE + length(_error) = 0; + +CREATE MATERIALIZED VIEW dispute_parse_errors ( + `topic` String, + `partition` Int64, + `offset` Int64, + `raw` String, + `error` String +) ENGINE = MergeTree +ORDER BY + (topic, partition, offset) SETTINGS index_granularity = 8192 AS +SELECT + _topic AS topic, + _partition AS partition, + _offset AS offset, + _raw_message AS raw, + _error AS error +FROM + dispute_queue +WHERE + length(_error) > 0; \ No newline at end of file diff --git a/crates/analytics/docs/clickhouse/scripts/outgoing_webhook_events.sql b/crates/analytics/docs/clickhouse/scripts/outgoing_webhook_events.sql index 5d2d3fac74a..30f3f293bfa 100644 --- a/crates/analytics/docs/clickhouse/scripts/outgoing_webhook_events.sql +++ b/crates/analytics/docs/clickhouse/scripts/outgoing_webhook_events.sql @@ -1,58 +1,75 @@ -CREATE TABLE - outgoing_webhook_events_queue ( - `merchant_id` String, - `event_id` Nullable(String), - `event_type` LowCardinality(String), - `outgoing_webhook_event_type` LowCardinality(String), - `payment_id` Nullable(String), - `refund_id` Nullable(String), - `attempt_id` Nullable(String), - `dispute_id` Nullable(String), - `payment_method_id` Nullable(String), - `mandate_id` Nullable(String), - `content` Nullable(String), - `is_error` Bool, - `error` Nullable(String), - `created_at_timestamp` DateTime64(3), - `initial_attempt_id` Nullable(String) - ) ENGINE = Kafka SETTINGS kafka_broker_list = 'kafka0:29092', - kafka_topic_list = 'hyperswitch-outgoing-webhook-events', - kafka_group_name = 'hyper-c1', - kafka_format = 'JSONEachRow', - kafka_handle_error_mode = 'stream'; +CREATE TABLE outgoing_webhook_events_queue ( + `merchant_id` String, + `event_id` String, + `event_type` LowCardinality(String), + `outgoing_webhook_event_type` LowCardinality(String), + `payment_id` Nullable(String), + `refund_id` Nullable(String), + `attempt_id` Nullable(String), + `dispute_id` Nullable(String), + `payment_method_id` Nullable(String), + `mandate_id` Nullable(String), + `content` Nullable(String), + `is_error` Bool, + `error` Nullable(String), + `created_at_timestamp` DateTime64(3) +) ENGINE = Kafka SETTINGS kafka_broker_list = 'kafka0:29092', +kafka_topic_list = 'hyperswitch-outgoing-webhook-events', +kafka_group_name = 'hyper', +kafka_format = 'JSONEachRow', +kafka_handle_error_mode = 'stream'; -CREATE TABLE - outgoing_webhook_events_cluster ( - `merchant_id` String, - `event_id` String, - `event_type` LowCardinality(String), - `outgoing_webhook_event_type` LowCardinality(String), - `payment_id` Nullable(String), - `refund_id` Nullable(String), - `attempt_id` Nullable(String), - `dispute_id` Nullable(String), - `payment_method_id` Nullable(String), - `mandate_id` Nullable(String), - `content` Nullable(String), - `is_error` Bool, - `error` Nullable(String), - `created_at_timestamp` DateTime64(3), - `inserted_at` DateTime DEFAULT now() CODEC(T64, LZ4), - `initial_attempt_id` Nullable(String), - INDEX eventIndex event_type TYPE bloom_filter GRANULARITY 1, - INDEX webhookeventIndex outgoing_webhook_event_type TYPE bloom_filter GRANULARITY 1 - ) ENGINE = MergeTree PARTITION BY toStartOfDay(created_at_timestamp) -ORDER BY ( - created_at_timestamp, +CREATE TABLE outgoing_webhook_events ( + `merchant_id` LowCardinality(String), + `event_id` String, + `event_type` LowCardinality(String), + `outgoing_webhook_event_type` LowCardinality(String), + `payment_id` Nullable(String), + `refund_id` Nullable(String), + `attempt_id` Nullable(String), + `dispute_id` Nullable(String), + `payment_method_id` Nullable(String), + `mandate_id` Nullable(String), + `content` Nullable(String), + `is_error` Bool, + `error` Nullable(String), + `created_at` DateTime64(3), + `inserted_at` DateTime DEFAULT now() CODEC(T64, LZ4), + INDEX eventIndex event_type TYPE bloom_filter GRANULARITY 1, + INDEX webhookeventIndex outgoing_webhook_event_type TYPE bloom_filter GRANULARITY 1 +) ENGINE = MergeTree PARTITION BY toStartOfDay(created_at) +ORDER BY + ( + created_at, merchant_id, event_id, event_type, outgoing_webhook_event_type - ) TTL inserted_at + toIntervalMonth(6); + ) TTL inserted_at + toIntervalMonth(18) SETTINGS index_granularity = 8192; -CREATE MATERIALIZED VIEW outgoing_webhook_events_mv TO outgoing_webhook_events_cluster ( +CREATE TABLE outgoing_webhook_events_audit ( + `merchant_id` LowCardinality(String), + `event_id` String, + `event_type` LowCardinality(String), + `outgoing_webhook_event_type` LowCardinality(String), + `payment_id` String, + `refund_id` Nullable(String), + `attempt_id` Nullable(String), + `dispute_id` Nullable(String), + `payment_method_id` Nullable(String), + `mandate_id` Nullable(String), + `content` Nullable(String), + `is_error` Bool, + `error` Nullable(String), + `created_at` DateTime64(3), + `inserted_at` DateTime DEFAULT now() CODEC(T64, LZ4) +) ENGINE = MergeTree PARTITION BY merchant_id +ORDER BY + (merchant_id, payment_id) TTL inserted_at + toIntervalMonth(18) SETTINGS index_granularity = 8192; + +CREATE MATERIALIZED VIEW outgoing_webhook_events_mv TO outgoing_webhook_events ( `merchant_id` String, - `event_id` Nullable(String), + `event_id` String, `event_type` LowCardinality(String), `outgoing_webhook_event_type` LowCardinality(String), `payment_id` Nullable(String), @@ -64,9 +81,46 @@ CREATE MATERIALIZED VIEW outgoing_webhook_events_mv TO outgoing_webhook_events_c `content` Nullable(String), `is_error` Bool, `error` Nullable(String), - `created_at_timestamp` DateTime64(3), - `inserted_at` DateTime DEFAULT now() CODEC(T64, LZ4), - `initial_attempt_id` Nullable(String) + `created_at` DateTime64(3), + `inserted_at` DateTime DEFAULT now() CODEC(T64, LZ4) +) AS +SELECT + merchant_id, + event_id, + event_type, + outgoing_webhook_event_type, + payment_id, + refund_id, + attempt_id, + dispute_id, + payment_method_id, + mandate_id, + content, + is_error, + error, + created_at_timestamp AS created_at, + now() AS inserted_at +FROM + outgoing_webhook_events_queue +WHERE + length(_error) = 0; + +CREATE MATERIALIZED VIEW outgoing_webhook_events_audit_mv TO outgoing_webhook_events_audit ( + `merchant_id` String, + `event_id` String, + `event_type` LowCardinality(String), + `outgoing_webhook_event_type` LowCardinality(String), + `payment_id` String, + `refund_id` Nullable(String), + `attempt_id` Nullable(String), + `dispute_id` Nullable(String), + `payment_method_id` Nullable(String), + `mandate_id` Nullable(String), + `content` Nullable(String), + `is_error` Bool, + `error` Nullable(String), + `created_at` DateTime64(3), + `inserted_at` DateTime DEFAULT now() CODEC(T64, LZ4) ) AS SELECT merchant_id, @@ -82,12 +136,13 @@ SELECT content, is_error, error, - created_at_timestamp, - now() AS inserted_at, - initial_attempt_id + created_at_timestamp AS created_at, + now() AS inserted_at FROM outgoing_webhook_events_queue -where length(_error) = 0; +WHERE + (length(_error) = 0) + AND (payment_id IS NOT NULL); CREATE MATERIALIZED VIEW outgoing_webhook_parse_errors ( `topic` String, @@ -96,18 +151,15 @@ CREATE MATERIALIZED VIEW outgoing_webhook_parse_errors ( `raw` String, `error` String ) ENGINE = MergeTree -ORDER BY ( - topic, partition, - offset - ) SETTINGS index_granularity = 8192 AS +ORDER BY + (topic, partition, offset) SETTINGS index_granularity = 8192 AS SELECT _topic AS topic, _partition AS partition, - _offset AS -offset -, + _offset AS offset, _raw_message AS raw, _error AS error FROM outgoing_webhook_events_queue -WHERE length(_error) > 0; \ No newline at end of file +WHERE + length(_error) > 0; \ No newline at end of file diff --git a/crates/analytics/docs/clickhouse/scripts/payment_attempts.sql b/crates/analytics/docs/clickhouse/scripts/payment_attempts.sql index 8b7715044c1..e5c52ccace8 100644 --- a/crates/analytics/docs/clickhouse/scripts/payment_attempts.sql +++ b/crates/analytics/docs/clickhouse/scripts/payment_attempts.sql @@ -1,4 +1,4 @@ -CREATE TABLE payment_attempts_queue ( +CREATE TABLE payment_attempt_queue ( `payment_id` String, `merchant_id` String, `attempt_id` String, @@ -32,22 +32,22 @@ CREATE TABLE payment_attempts_queue ( `payment_method_data` Nullable(String), `error_reason` Nullable(String), `multiple_capture_count` Nullable(Int16), - `amount_capturable` Nullable(UInt64) , - `merchant_connector_id` Nullable(String), - `net_amount` Nullable(UInt64) , - `unified_code` Nullable(String), - `unified_message` Nullable(String), - `mandate_data` Nullable(String), + `amount_capturable` Nullable(UInt64), + `merchant_connector_id` Nullable(String), + `net_amount` Nullable(UInt64), + `unified_code` Nullable(String), + `unified_message` Nullable(String), + `mandate_data` Nullable(String), `sign_flag` Int8 ) ENGINE = Kafka SETTINGS kafka_broker_list = 'kafka0:29092', kafka_topic_list = 'hyperswitch-payment-attempt-events', -kafka_group_name = 'hyper-c1', +kafka_group_name = 'hyper', kafka_format = 'JSONEachRow', kafka_handle_error_mode = 'stream'; -CREATE TABLE payment_attempt_dist ( +CREATE TABLE payment_attempts ( `payment_id` String, - `merchant_id` String, + `merchant_id` LowCardinality(String), `attempt_id` String, `status` LowCardinality(String), `amount` Nullable(UInt32), @@ -79,12 +79,12 @@ CREATE TABLE payment_attempt_dist ( `payment_method_data` Nullable(String), `error_reason` Nullable(String), `multiple_capture_count` Nullable(Int16), - `amount_capturable` Nullable(UInt64) , - `merchant_connector_id` Nullable(String), - `net_amount` Nullable(UInt64) , - `unified_code` Nullable(String), - `unified_message` Nullable(String), - `mandate_data` Nullable(String), + `amount_capturable` Nullable(UInt64), + `merchant_connector_id` Nullable(String), + `net_amount` Nullable(UInt64), + `unified_code` Nullable(String), + `unified_message` Nullable(String), + `mandate_data` Nullable(String), `inserted_at` DateTime DEFAULT now() CODEC(T64, LZ4), `sign_flag` Int8, INDEX connectorIndex connector TYPE bloom_filter GRANULARITY 1, @@ -92,17 +92,11 @@ CREATE TABLE payment_attempt_dist ( INDEX authenticationTypeIndex authentication_type TYPE bloom_filter GRANULARITY 1, INDEX currencyIndex currency TYPE bloom_filter GRANULARITY 1, INDEX statusIndex status TYPE bloom_filter GRANULARITY 1 -) ENGINE = CollapsingMergeTree( - sign_flag -) -PARTITION BY toStartOfDay(created_at) +) ENGINE = CollapsingMergeTree(sign_flag) PARTITION BY toStartOfDay(created_at) ORDER BY - (created_at, merchant_id, attempt_id) -TTL created_at + toIntervalMonth(6) -; + (created_at, merchant_id, attempt_id) TTL created_at + toIntervalMonth(18) SETTINGS index_granularity = 8192; - -CREATE MATERIALIZED VIEW kafka_parse_pa TO payment_attempt_dist ( +CREATE MATERIALIZED VIEW payment_attempt_mv TO payment_attempts ( `payment_id` String, `merchant_id` String, `attempt_id` String, @@ -136,12 +130,12 @@ CREATE MATERIALIZED VIEW kafka_parse_pa TO payment_attempt_dist ( `payment_method_data` Nullable(String), `error_reason` Nullable(String), `multiple_capture_count` Nullable(Int16), - `amount_capturable` Nullable(UInt64) , - `merchant_connector_id` Nullable(String), - `net_amount` Nullable(UInt64) , - `unified_code` Nullable(String), - `unified_message` Nullable(String), - `mandate_data` Nullable(String), + `amount_capturable` Nullable(UInt64), + `merchant_connector_id` Nullable(String), + `net_amount` Nullable(UInt64), + `unified_code` Nullable(String), + `unified_message` Nullable(String), + `mandate_data` Nullable(String), `inserted_at` DateTime64(3), `sign_flag` Int8 ) AS @@ -185,8 +179,9 @@ SELECT unified_code, unified_message, mandate_data, - now() as inserted_at, + now() AS inserted_at, sign_flag FROM - payment_attempts_queue; - + payment_attempt_queue +WHERE + length(_error) = 0; \ No newline at end of file diff --git a/crates/analytics/docs/clickhouse/scripts/payment_intents.sql b/crates/analytics/docs/clickhouse/scripts/payment_intents.sql index 8cd487f364b..6889a1ff79d 100644 --- a/crates/analytics/docs/clickhouse/scripts/payment_intents.sql +++ b/crates/analytics/docs/clickhouse/scripts/payment_intents.sql @@ -1,4 +1,5 @@ -CREATE TABLE payment_intents_queue ( +CREATE TABLE payment_intents_queue +( `payment_id` String, `merchant_id` String, `status` LowCardinality(String), @@ -15,22 +16,23 @@ CREATE TABLE payment_intents_queue ( `off_session` Nullable(Bool), `client_secret` Nullable(String), `active_attempt_id` String, - `business_country` String, + `business_country` LowCardinality(String), `business_label` String, + `attempt_count` UInt8, `modified_at` DateTime CODEC(T64, LZ4), `created_at` DateTime CODEC(T64, LZ4), `last_synced` Nullable(DateTime) CODEC(T64, LZ4), `sign_flag` Int8 ) ENGINE = Kafka SETTINGS kafka_broker_list = 'kafka0:29092', kafka_topic_list = 'hyperswitch-payment-intent-events', -kafka_group_name = 'hyper-c1', +kafka_group_name = 'hyper', kafka_format = 'JSONEachRow', kafka_handle_error_mode = 'stream'; - -CREATE TABLE payment_intents_dist ( +CREATE TABLE payment_intents +( `payment_id` String, - `merchant_id` String, + `merchant_id` LowCardinality(String), `status` LowCardinality(String), `amount` UInt32, `currency` LowCardinality(Nullable(String)), @@ -47,6 +49,7 @@ CREATE TABLE payment_intents_dist ( `active_attempt_id` String, `business_country` LowCardinality(String), `business_label` String, + `attempt_count` UInt8, `modified_at` DateTime DEFAULT now() CODEC(T64, LZ4), `created_at` DateTime DEFAULT now() CODEC(T64, LZ4), `last_synced` Nullable(DateTime) CODEC(T64, LZ4), @@ -55,16 +58,15 @@ CREATE TABLE payment_intents_dist ( INDEX connectorIndex connector_id TYPE bloom_filter GRANULARITY 1, INDEX currencyIndex currency TYPE bloom_filter GRANULARITY 1, INDEX statusIndex status TYPE bloom_filter GRANULARITY 1 -) ENGINE = CollapsingMergeTree( - sign_flag ) +ENGINE = CollapsingMergeTree(sign_flag) PARTITION BY toStartOfDay(created_at) -ORDER BY - (created_at, merchant_id, payment_id) -TTL created_at + toIntervalMonth(6) -; +ORDER BY (created_at, merchant_id, payment_id) +TTL created_at + toIntervalMonth(18) +SETTINGS index_granularity = 8192; -CREATE MATERIALIZED VIEW kafka_parse_payment_intent TO payment_intents_dist ( +CREATE MATERIALIZED VIEW payment_intents_mv TO payment_intents +( `payment_id` String, `merchant_id` String, `status` LowCardinality(String), @@ -83,6 +85,7 @@ CREATE MATERIALIZED VIEW kafka_parse_payment_intent TO payment_intents_dist ( `active_attempt_id` String, `business_country` LowCardinality(String), `business_label` String, + `attempt_count` UInt8, `modified_at` DateTime64(3), `created_at` DateTime64(3), `last_synced` Nullable(DateTime64(3)), @@ -108,9 +111,10 @@ SELECT active_attempt_id, business_country, business_label, + attempt_count, modified_at, created_at, last_synced, - now() as inserted_at, + now() AS inserted_at, sign_flag -FROM payment_intents_queue; +FROM payment_intents_queue; \ No newline at end of file diff --git a/crates/analytics/docs/clickhouse/scripts/payouts.sql b/crates/analytics/docs/clickhouse/scripts/payouts.sql index 68d109f7b9b..b7426c2983a 100644 --- a/crates/analytics/docs/clickhouse/scripts/payouts.sql +++ b/crates/analytics/docs/clickhouse/scripts/payouts.sql @@ -25,13 +25,13 @@ CREATE TABLE payout_queue ( `is_eligible` Nullable(Bool), `error_message` Nullable(String), `error_code` Nullable(String), - `business_country` Nullable(LowCardinality(String)), + `business_country` LowCardinality(Nullable(String)), `business_label` Nullable(String), `merchant_connector_id` Nullable(String), `sign_flag` Int8 ) ENGINE = Kafka SETTINGS kafka_broker_list = 'kafka0:29092', kafka_topic_list = 'hyperswitch-payout-events', -kafka_group_name = 'hyper-c1', +kafka_group_name = 'hyper', kafka_format = 'JSONEachRow', kafka_handle_error_mode = 'stream'; @@ -42,7 +42,7 @@ CREATE TABLE payout ( `customer_id` String, `address_id` String, `profile_id` String, - `payout_method_id` String, + `payout_method_id` Nullable(String), `payout_type` LowCardinality(String), `amount` UInt64, `destination_currency` LowCardinality(String), @@ -62,7 +62,7 @@ CREATE TABLE payout ( `is_eligible` Nullable(Bool), `error_message` Nullable(String), `error_code` Nullable(String), - `business_country` Nullable(LowCardinality(String)), + `business_country` LowCardinality(Nullable(String)), `business_label` Nullable(String), `merchant_connector_id` Nullable(String), `inserted_at` DateTime DEFAULT now() CODEC(T64, LZ4), @@ -75,16 +75,16 @@ CREATE TABLE payout ( INDEX businessCountryIndex business_country TYPE bloom_filter GRANULARITY 1 ) ENGINE = CollapsingMergeTree(sign_flag) PARTITION BY toStartOfDay(created_at) ORDER BY - (created_at, merchant_id, payout_id) TTL created_at + toIntervalMonth(6); + (created_at, merchant_id, payout_id) TTL created_at + toIntervalMonth(6) SETTINGS index_granularity = 8192; -CREATE MATERIALIZED VIEW kafka_parse_payout TO payout ( +CREATE MATERIALIZED VIEW payout_mv TO payout ( `payout_id` String, `payout_attempt_id` String, `merchant_id` String, `customer_id` String, `address_id` String, `profile_id` String, - `payout_method_id` String, + `payout_method_id` Nullable(String), `payout_type` LowCardinality(String), `amount` UInt64, `destination_currency` LowCardinality(String), @@ -95,8 +95,8 @@ CREATE MATERIALIZED VIEW kafka_parse_payout TO payout ( `return_url` Nullable(String), `entity_type` LowCardinality(String), `metadata` Nullable(String), - `created_at` DateTime64(3), - `last_modified_at` DateTime64(3), + `created_at` DateTime DEFAULT now() CODEC(T64, LZ4), + `last_modified_at` DateTime DEFAULT now() CODEC(T64, LZ4), `attempt_count` UInt16, `status` LowCardinality(String), `connector` Nullable(String), @@ -104,11 +104,11 @@ CREATE MATERIALIZED VIEW kafka_parse_payout TO payout ( `is_eligible` Nullable(Bool), `error_message` Nullable(String), `error_code` Nullable(String), - `business_country` Nullable(LowCardinality(String)), + `business_country` LowCardinality(Nullable(String)), `business_label` Nullable(String), `merchant_connector_id` Nullable(String), - `inserted_at` DateTime64(3), - `sign_flag` Int8 + `inserted_at` DateTime DEFAULT now() CODEC(T64, LZ4), + `sign_flag` Int8, ) AS SELECT payout_id, diff --git a/crates/analytics/docs/clickhouse/scripts/refunds.sql b/crates/analytics/docs/clickhouse/scripts/refunds.sql index a131270c132..74c069db853 100644 --- a/crates/analytics/docs/clickhouse/scripts/refunds.sql +++ b/crates/analytics/docs/clickhouse/scripts/refunds.sql @@ -19,21 +19,20 @@ CREATE TABLE refund_queue ( `description` Nullable(String), `refund_reason` Nullable(String), `refund_error_code` Nullable(String), - `created_at` DateTime CODEC(T64, LZ4), - `modified_at` DateTime CODEC(T64, LZ4), + `created_at` DateTime, + `modified_at` DateTime, `sign_flag` Int8 ) ENGINE = Kafka SETTINGS kafka_broker_list = 'kafka0:29092', kafka_topic_list = 'hyperswitch-refund-events', -kafka_group_name = 'hyper-c1', +kafka_group_name = 'hyper', kafka_format = 'JSONEachRow', kafka_handle_error_mode = 'stream'; - -CREATE TABLE refund_dist ( +CREATE TABLE refunds ( `internal_reference_id` String, `refund_id` String, `payment_id` String, - `merchant_id` String, + `merchant_id` LowCardinality(String), `connector_transaction_id` String, `connector` LowCardinality(Nullable(String)), `connector_refund_id` Nullable(String), @@ -58,16 +57,11 @@ CREATE TABLE refund_dist ( INDEX refundTypeIndex refund_type TYPE bloom_filter GRANULARITY 1, INDEX currencyIndex currency TYPE bloom_filter GRANULARITY 1, INDEX statusIndex refund_status TYPE bloom_filter GRANULARITY 1 -) ENGINE = CollapsingMergeTree( - sign_flag -) -PARTITION BY toStartOfDay(created_at) +) ENGINE = CollapsingMergeTree(sign_flag) PARTITION BY toStartOfDay(created_at) ORDER BY - (created_at, merchant_id, refund_id) -TTL created_at + toIntervalMonth(6) -; + (created_at, merchant_id, refund_id) TTL created_at + toIntervalMonth(18) SETTINGS index_granularity = 8192; -CREATE MATERIALIZED VIEW kafka_parse_refund TO refund_dist ( +CREATE MATERIALIZED VIEW refund_mv TO refunds ( `internal_reference_id` String, `refund_id` String, `payment_id` String, @@ -116,6 +110,9 @@ SELECT refund_error_code, created_at, modified_at, - now() as inserted_at, + now() AS inserted_at, sign_flag -FROM refund_queue; +FROM + refund_queue +WHERE + length(_error) = 0; \ No newline at end of file diff --git a/docker-compose-development.yml b/docker-compose-development.yml index 665bdf2f05d..f2ba6d3eda4 100644 --- a/docker-compose-development.yml +++ b/docker-compose-development.yml @@ -295,3 +295,29 @@ services: - "8001:8001" volumes: - redisinsight_store:/db + + hyperswitch-control-center: + image: juspaydotin/hyperswitch-control-center:latest + networks: + - router_net + ports: + - "9000:9000" + environment: + apiBaseUrl: http://localhost:8080 + sdkBaseUrl: http://localhost:9050/HyperLoader.js + + hyperswitch-web-sdk: + build: + dockerfile_inline: | + FROM node:lts + RUN git clone https://github.com/juspay/hyperswitch-web.git --depth 1 + WORKDIR hyperswitch-web + RUN npm i --force + command: bash -c 'npm run re:build && npx run webpack serve --config webpack.dev.js --host 0.0.0.0' + ports: + - "9050:9050" + environment: + sdkEnv: local + envSdkUrl: http://localhost:9050 + envBackendUrl: http://localhost:8080 + envLoggingUrl: http://localhost:8207 diff --git a/docker-compose.yml b/docker-compose.yml index 040832f8e27..7d7c7c27405 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -3,6 +3,7 @@ version: "3.8" volumes: pg_data: redisinsight_store: + ckh_data: networks: router_net: @@ -321,12 +322,14 @@ services: KAFKA_CLUSTERS_0_JMXPORT: 9997 clickhouse-server: - image: clickhouse/clickhouse-server:23.5 + image: clickhouse/clickhouse-server:24.3 networks: - router_net ports: - "9000" - "8123:8123" + volumes: + - ./crates/analytics/docs/clickhouse/scripts:/docker-entrypoint-initdb.d profiles: - analytics ulimits: @@ -363,4 +366,4 @@ services: environment: OPENSEARCH_HOSTS: '["https://opensearch:9200"]' networks: - - router_net \ No newline at end of file + - router_net From 575fac6f3ef94ef1856a77d778f822c0e97b0e9c Mon Sep 17 00:00:00 2001 From: Sarthak Soni <76486416+Sarthak1799@users.noreply.github.com> Date: Mon, 6 May 2024 16:48:04 +0530 Subject: [PATCH 05/34] feat(payment_methods): Filter payment methods based on pm client secret (#4249) Co-authored-by: hyperswitch-bot[bot] <148525504+hyperswitch-bot[bot]@users.noreply.github.com> --- config/config.example.toml | 3 + config/deployments/integration_test.toml | 3 + config/deployments/production.toml | 3 + config/deployments/sandbox.toml | 3 + config/development.toml | 3 + config/docker_compose.toml | 3 + crates/diesel_models/src/payment_method.rs | 20 ++++ .../src/configs/secrets_transformers.rs | 1 + crates/router/src/configs/settings.rs | 6 ++ .../router/src/core/payment_methods/cards.rs | 96 +++++++++++++++++-- .../src/core/payment_methods/transformers.rs | 2 +- 11 files changed, 132 insertions(+), 11 deletions(-) diff --git a/config/config.example.toml b/config/config.example.toml index b2e1fe3c458..b5585a84b32 100644 --- a/config/config.example.toml +++ b/config/config.example.toml @@ -613,3 +613,6 @@ payment_attempts = "hyperswitch-payment-attempt-events" payment_intents = "hyperswitch-payment-intent-events" refunds = "hyperswitch-refund-events" disputes = "hyperswitch-dispute-events" + +[saved_payment_methods] +sdk_eligible_payment_methods = ["card"] \ No newline at end of file diff --git a/config/deployments/integration_test.toml b/config/deployments/integration_test.toml index 8edfef7485e..fe47b10b543 100644 --- a/config/deployments/integration_test.toml +++ b/config/deployments/integration_test.toml @@ -319,3 +319,6 @@ connectors_with_webhook_source_verification_call = "paypal" # List of co [unmasked_headers] keys = "user-agent" + +[saved_payment_methods] +sdk_eligible_payment_methods = ["card"] \ No newline at end of file diff --git a/config/deployments/production.toml b/config/deployments/production.toml index 12724485ea1..3135b3d5682 100644 --- a/config/deployments/production.toml +++ b/config/deployments/production.toml @@ -330,3 +330,6 @@ connectors_with_webhook_source_verification_call = "paypal" # List of connec [unmasked_headers] keys = "user-agent" + +[saved_payment_methods] +sdk_eligible_payment_methods = ["card"] \ No newline at end of file diff --git a/config/deployments/sandbox.toml b/config/deployments/sandbox.toml index 6c830c389a6..921301ebd6f 100644 --- a/config/deployments/sandbox.toml +++ b/config/deployments/sandbox.toml @@ -334,3 +334,6 @@ connectors_with_webhook_source_verification_call = "paypal" # List of con [unmasked_headers] keys = "user-agent" + +[saved_payment_methods] +sdk_eligible_payment_methods = ["card"] \ No newline at end of file diff --git a/config/development.toml b/config/development.toml index 92e13f24fec..db7f1ed93f4 100644 --- a/config/development.toml +++ b/config/development.toml @@ -615,3 +615,6 @@ payment_attempts = "hyperswitch-payment-attempt-events" payment_intents = "hyperswitch-payment-intent-events" refunds = "hyperswitch-refund-events" disputes = "hyperswitch-dispute-events" + +[saved_payment_methods] +sdk_eligible_payment_methods = ["card"] diff --git a/config/docker_compose.toml b/config/docker_compose.toml index d99ab473501..702d134f2d5 100644 --- a/config/docker_compose.toml +++ b/config/docker_compose.toml @@ -474,3 +474,6 @@ payment_attempts = "hyperswitch-payment-attempt-events" payment_intents = "hyperswitch-payment-intent-events" refunds = "hyperswitch-refund-events" disputes = "hyperswitch-dispute-events" + +[saved_payment_methods] +sdk_eligible_payment_methods = ["card"] \ No newline at end of file diff --git a/crates/diesel_models/src/payment_method.rs b/crates/diesel_models/src/payment_method.rs index e2807f3b5ea..262848e1d55 100644 --- a/crates/diesel_models/src/payment_method.rs +++ b/crates/diesel_models/src/payment_method.rs @@ -143,6 +143,8 @@ pub enum PaymentMethodUpdate { status: Option, locker_id: Option, payment_method: Option, + payment_method_type: Option, + payment_method_issuer: Option, }, ConnectorMandateDetailsUpdate { connector_mandate_details: Option, @@ -162,6 +164,8 @@ pub struct PaymentMethodUpdateInternal { locker_id: Option, payment_method: Option, connector_mandate_details: Option, + payment_method_type: Option, + payment_method_issuer: Option, } impl PaymentMethodUpdateInternal { @@ -208,6 +212,8 @@ impl From for PaymentMethodUpdateInternal { locker_id: None, payment_method: None, connector_mandate_details: None, + payment_method_issuer: None, + payment_method_type: None, }, PaymentMethodUpdate::PaymentMethodDataUpdate { payment_method_data, @@ -220,6 +226,8 @@ impl From for PaymentMethodUpdateInternal { locker_id: None, payment_method: None, connector_mandate_details: None, + payment_method_issuer: None, + payment_method_type: None, }, PaymentMethodUpdate::LastUsedUpdate { last_used_at } => Self { metadata: None, @@ -230,6 +238,8 @@ impl From for PaymentMethodUpdateInternal { locker_id: None, payment_method: None, connector_mandate_details: None, + payment_method_issuer: None, + payment_method_type: None, }, PaymentMethodUpdate::NetworkTransactionIdAndStatusUpdate { network_transaction_id, @@ -243,6 +253,8 @@ impl From for PaymentMethodUpdateInternal { locker_id: None, payment_method: None, connector_mandate_details: None, + payment_method_issuer: None, + payment_method_type: None, }, PaymentMethodUpdate::StatusUpdate { status } => Self { metadata: None, @@ -253,12 +265,16 @@ impl From for PaymentMethodUpdateInternal { locker_id: None, payment_method: None, connector_mandate_details: None, + payment_method_issuer: None, + payment_method_type: None, }, PaymentMethodUpdate::AdditionalDataUpdate { payment_method_data, status, locker_id, payment_method, + payment_method_type, + payment_method_issuer, } => Self { metadata: None, payment_method_data, @@ -268,6 +284,8 @@ impl From for PaymentMethodUpdateInternal { locker_id, payment_method, connector_mandate_details: None, + payment_method_issuer, + payment_method_type, }, PaymentMethodUpdate::ConnectorMandateDetailsUpdate { connector_mandate_details, @@ -280,6 +298,8 @@ impl From for PaymentMethodUpdateInternal { payment_method: None, connector_mandate_details, network_transaction_id: None, + payment_method_issuer: None, + payment_method_type: None, }, } } diff --git a/crates/router/src/configs/secrets_transformers.rs b/crates/router/src/configs/secrets_transformers.rs index 537f6666c5d..423f8e03d23 100644 --- a/crates/router/src/configs/secrets_transformers.rs +++ b/crates/router/src/configs/secrets_transformers.rs @@ -365,5 +365,6 @@ pub(crate) async fn fetch_raw_secrets( connector_onboarding, cors: conf.cors, unmasked_headers: conf.unmasked_headers, + saved_payment_methods: conf.saved_payment_methods, } } diff --git a/crates/router/src/configs/settings.rs b/crates/router/src/configs/settings.rs index 01b05c60bd9..12ce9c39d6f 100644 --- a/crates/router/src/configs/settings.rs +++ b/crates/router/src/configs/settings.rs @@ -119,6 +119,7 @@ pub struct Settings { #[cfg(feature = "olap")] pub connector_onboarding: SecretStateContainer, pub unmasked_headers: UnmaskedHeaders, + pub saved_payment_methods: EligiblePaymentMethods, } #[derive(Debug, Deserialize, Clone, Default)] @@ -165,6 +166,11 @@ pub struct PaymentMethodAuth { pub pm_auth_key: Secret, } +#[derive(Debug, Deserialize, Clone, Default)] +pub struct EligiblePaymentMethods { + pub sdk_eligible_payment_methods: HashSet, +} + #[derive(Debug, Deserialize, Clone, Default)] pub struct DefaultExchangeRates { pub base_currency: String, diff --git a/crates/router/src/core/payment_methods/cards.rs b/crates/router/src/core/payment_methods/cards.rs index 27d126bce90..d3f6aeea24b 100644 --- a/crates/router/src/core/payment_methods/cards.rs +++ b/crates/router/src/core/payment_methods/cards.rs @@ -441,6 +441,8 @@ pub async fn add_payment_method_data( status: Some(enums::PaymentMethodStatus::Active), locker_id: Some(locker_id), payment_method: req.payment_method, + payment_method_issuer: req.payment_method_issuer, + payment_method_type: req.payment_method_type, }; db.update_payment_method( @@ -555,7 +557,7 @@ pub async fn add_payment_method( match duplication_check { Some(duplication_check) => match duplication_check { payment_methods::DataDuplicationCheck::Duplicated => { - get_or_insert_payment_method( + let existing_pm = get_or_insert_payment_method( db, req.clone(), &mut resp, @@ -564,6 +566,8 @@ pub async fn add_payment_method( key_store, ) .await?; + + resp.client_secret = existing_pm.client_secret; } payment_methods::DataDuplicationCheck::MetaDataChanged => { if let Some(card) = req.card.clone() { @@ -577,6 +581,8 @@ pub async fn add_payment_method( ) .await?; + let client_secret = existing_pm.client_secret.clone(); + delete_card_from_locker( &state, &customer_id, @@ -653,6 +659,8 @@ pub async fn add_payment_method( .await .change_context(errors::ApiErrorResponse::InternalServerError) .attach_printable("Failed to add payment method in db")?; + + resp.client_secret = client_secret; } } }, @@ -667,7 +675,7 @@ pub async fn add_payment_method( None }; resp.payment_method_id = generate_id(consts::ID_LENGTH, "pm"); - insert_payment_method( + let pm = insert_payment_method( db, &resp, req, @@ -682,6 +690,8 @@ pub async fn add_payment_method( merchant_account.storage_scheme, ) .await?; + + resp.client_secret = pm.client_secret; } } @@ -744,6 +754,18 @@ pub async fn update_customer_payment_method( .await .to_not_found_response(errors::ApiErrorResponse::PaymentMethodNotFound)?; + if pm.status == enums::PaymentMethodStatus::AwaitingData { + return Err(report!(errors::ApiErrorResponse::NotSupported { + message: "Payment method is awaiting data so it cannot be updated".into() + })); + } + + if pm.payment_method_data.is_none() { + return Err(report!(errors::ApiErrorResponse::GenericNotFoundError { + message: "payment_method_data not found".to_string() + })); + } + // Fetch the existing payment method data from db let existing_card_data = decrypt::( pm.payment_method_data.clone(), @@ -812,7 +834,7 @@ pub async fn update_customer_payment_method( wallet: req.wallet, metadata: req.metadata, customer_id: Some(pm.customer_id.clone()), - client_secret: None, + client_secret: pm.client_secret.clone(), payment_method_data: None, card_network: req .card_network @@ -901,7 +923,7 @@ pub async fn update_customer_payment_method( installment_payment_enabled: false, payment_experience: Some(vec![api_models::enums::PaymentExperience::RedirectToUrl]), last_used_at: Some(common_utils::date_time::now()), - client_secret: None, + client_secret: pm.client_secret.clone(), } }; @@ -1691,12 +1713,21 @@ pub async fn list_payment_methods( let db = &*state.store; let pm_config_mapping = &state.conf.pm_filters; - let payment_intent = helpers::verify_payment_intent_time_and_client_secret( - db, - &merchant_account, - req.client_secret.clone(), - ) - .await?; + let payment_intent = if let Some(cs) = &req.client_secret { + if cs.starts_with("pm_") { + validate_payment_method_and_client_secret(cs, db, &merchant_account).await?; + None + } else { + helpers::verify_payment_intent_time_and_client_secret( + db, + &merchant_account, + req.client_secret.clone(), + ) + .await? + } + } else { + None + }; let shipping_address = payment_intent .as_ref() @@ -1839,6 +1870,7 @@ pub async fn list_payment_methods( pm_config_mapping, &state.conf.mandates.supported_payment_methods, &state.conf.mandates.update_mandate_supported, + &state.conf.saved_payment_methods, ) .await?; } @@ -2535,6 +2567,34 @@ pub async fn list_payment_methods( )) } +async fn validate_payment_method_and_client_secret( + cs: &String, + db: &dyn db::StorageInterface, + merchant_account: &domain::MerchantAccount, +) -> Result<(), error_stack::Report> { + let pm_vec = cs.split("_secret").collect::>(); + let pm_id = pm_vec + .first() + .ok_or(errors::ApiErrorResponse::MissingRequiredField { + field_name: "client_secret", + })?; + + let payment_method = db + .find_payment_method(pm_id, merchant_account.storage_scheme) + .await + .change_context(errors::ApiErrorResponse::PaymentMethodNotFound) + .attach_printable("Unable to find payment method")?; + + let client_secret_expired = + authenticate_pm_client_secret_and_check_expiry(cs, &payment_method)?; + if client_secret_expired { + return Err::<(), error_stack::Report>( + (errors::ApiErrorResponse::ClientSecretExpired).into(), + ); + } + Ok(()) +} + pub async fn call_surcharge_decision_management( state: routes::AppState, merchant_account: &domain::MerchantAccount, @@ -2644,6 +2704,7 @@ pub async fn filter_payment_methods( config: &settings::ConnectorFilters, supported_payment_methods_for_mandate: &settings::SupportedPaymentMethodsForMandate, supported_payment_methods_for_update_mandate: &settings::SupportedPaymentMethodsForMandate, + saved_payment_methods: &settings::EligiblePaymentMethods, ) -> errors::CustomResult<(), errors::ApiErrorResponse> { for payment_method in payment_methods.into_iter() { let parse_result = serde_json::from_value::(payment_method); @@ -2761,6 +2822,20 @@ pub async fn filter_payment_methods( }) .unwrap_or(true); + let filter9 = req + .client_secret + .as_ref() + .map(|cs| { + if cs.starts_with("pm_") { + saved_payment_methods + .sdk_eligible_payment_methods + .contains(payment_method.to_string().as_str()) + } else { + true + } + }) + .unwrap_or(true); + let connector = connector.clone(); let response_pm_type = ResponsePaymentMethodIntermediate::new( @@ -2777,6 +2852,7 @@ pub async fn filter_payment_methods( && filter6 && filter7 && filter8 + && filter9 { resp.push(response_pm_type); } diff --git a/crates/router/src/core/payment_methods/transformers.rs b/crates/router/src/core/payment_methods/transformers.rs index 749508c9740..9c53642f4d7 100644 --- a/crates/router/src/core/payment_methods/transformers.rs +++ b/crates/router/src/core/payment_methods/transformers.rs @@ -374,7 +374,7 @@ pub fn mk_add_card_response_hs( installment_payment_enabled: false, // #[#256] payment_experience: Some(vec![api_models::enums::PaymentExperience::RedirectToUrl]), last_used_at: Some(common_utils::date_time::now()), // [#256] - client_secret: None, + client_secret: req.client_secret, } } From b1cfef257a54b3ebe4f48e56a48a932e2c758dc7 Mon Sep 17 00:00:00 2001 From: Prasad Bhalerao <67261499+prasad89@users.noreply.github.com> Date: Mon, 6 May 2024 17:19:45 +0530 Subject: [PATCH 06/34] build(docker): add web client and control center services to docker compose setup (#4197) --- docker-compose.yml | 33 +++++++++++++++++++++++++++++++++ docker/web.Dockerfile | 15 +++++++++++++++ 2 files changed, 48 insertions(+) create mode 100644 docker/web.Dockerfile diff --git a/docker-compose.yml b/docker-compose.yml index 7d7c7c27405..e6b9c4cec4b 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -123,6 +123,39 @@ services: labels: logs: "promtail" +### Web Client + hyperswitch-web: + ports: + - "9050:9050" + - "9060:9060" + - "5252:5252" + build: + context: ./docker + dockerfile: web.Dockerfile + environment: + - HYPERSWITCH_PUBLISHABLE_KEY=$HYPERSWITCH_PUBLISHABLE_KEY + - HYPERSWITCH_SECRET_KEY=$HYPERSWITCH_SECRET_KEY + - HYPERSWITCH_SERVER_URL=${HYPERSWITCH_SERVER_URL:-http://hyperswitch-server:8080} + - HYPERSWITCH_CLIENT_URL=${HYPERSWITCH_CLIENT_URL:-http://localhost:9050} + - SELF_SERVER_URL=${SELF_SERVER_URL:-http://localhost:5252} + - SDK_ENV=${SDK_ENV:-local} + - ENV_SDK_URL=${ENV_SDK_URL:-http://localhost:9050} + - ENV_BACKEND_URL=${ENV_BACKEND_URL:-http://localhost:8080} + - ENV_LOGGING_URL=${ENV_LOGGING_URL:-http://localhost:3100} + labels: + logs: "promtail" + + ### Control Center + hyperswitch-control-center: + image: juspaydotin/hyperswitch-control-center:latest + ports: + - "9000:9000" + environment: + - apiBaseUrl=http://localhost:8080 + - sdkBaseUrl=http://localhost:9050/HyperLoader.js + labels: + logs: "promtail" + ### Clustered Redis setup redis-cluster: image: redis:7 diff --git a/docker/web.Dockerfile b/docker/web.Dockerfile new file mode 100644 index 00000000000..532ce28fb7c --- /dev/null +++ b/docker/web.Dockerfile @@ -0,0 +1,15 @@ +FROM node:lts + +RUN npm install concurrently -g + +WORKDIR /hyperswitch-web + +RUN git clone https://github.com/juspay/hyperswitch-web --depth 1 . + +RUN npm install + +EXPOSE 9050 +EXPOSE 5252 +EXPOSE 9060 + +CMD concurrently "npm run re:build && npm run start" "npm run start:playground" \ No newline at end of file From a23a365cdf3fc2a24f4e2a08996a5683dc4da89a Mon Sep 17 00:00:00 2001 From: Shanks Date: Mon, 6 May 2024 18:38:44 +0530 Subject: [PATCH 07/34] feat(constraint_graph): make the constraint graph framework generic and move it into a separate crate (#3071) --- .github/CODEOWNERS | 1 + Cargo.lock | 53 +- crates/euclid/Cargo.toml | 1 + crates/euclid/src/dssa/analyzer.rs | 37 +- crates/euclid/src/dssa/graph.rs | 1475 ++++++----------- crates/euclid/src/dssa/truth.rs | 41 +- crates/euclid/src/dssa/types.rs | 5 +- crates/euclid/src/lib.rs | 1 - crates/euclid/src/utils.rs | 3 - crates/euclid_macros/src/inner/knowledge.rs | 38 +- crates/euclid_wasm/Cargo.toml | 1 + crates/euclid_wasm/src/lib.rs | 31 +- .../hyperswitch_constraint_graph/Cargo.toml | 16 + .../src/builder.rs | 283 ++++ .../src}/dense_map.rs | 18 + .../hyperswitch_constraint_graph/src/error.rs | 77 + .../hyperswitch_constraint_graph/src/graph.rs | 587 +++++++ .../hyperswitch_constraint_graph/src/lib.rs | 13 + .../hyperswitch_constraint_graph/src/types.rs | 249 +++ crates/kgraph_utils/Cargo.toml | 1 + crates/kgraph_utils/benches/evaluation.rs | 12 +- crates/kgraph_utils/src/error.rs | 4 +- crates/kgraph_utils/src/mca.rs | 220 +-- crates/router/Cargo.toml | 1 + crates/router/src/core/payments/routing.rs | 18 +- 25 files changed, 2048 insertions(+), 1138 deletions(-) delete mode 100644 crates/euclid/src/utils.rs create mode 100644 crates/hyperswitch_constraint_graph/Cargo.toml create mode 100644 crates/hyperswitch_constraint_graph/src/builder.rs rename crates/{euclid/src/utils => hyperswitch_constraint_graph/src}/dense_map.rs (92%) create mode 100644 crates/hyperswitch_constraint_graph/src/error.rs create mode 100644 crates/hyperswitch_constraint_graph/src/graph.rs create mode 100644 crates/hyperswitch_constraint_graph/src/lib.rs create mode 100644 crates/hyperswitch_constraint_graph/src/types.rs diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 8f1326625cf..933fe96526c 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -45,6 +45,7 @@ crates/router/src/compatibility/ @juspay/hyperswitch-compatibility crates/router/src/core/ @juspay/hyperswitch-core crates/api_models/src/routing.rs @juspay/hyperswitch-routing +crates/hyperswitch_constraint_graph @juspay/hyperswitch-routing crates/euclid @juspay/hyperswitch-routing crates/euclid_macros @juspay/hyperswitch-routing crates/euclid_wasm @juspay/hyperswitch-routing diff --git a/Cargo.lock b/Cargo.lock index 00c491d7101..7fce1e7f538 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2677,6 +2677,15 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" +[[package]] +name = "erased-serde" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c138974f9d5e7fe373eb04df7cae98833802ae4b11c24ac7039a21d5af4b26c" +dependencies = [ + "serde", +] + [[package]] name = "erased-serde" version = "0.4.4" @@ -2733,10 +2742,11 @@ version = "0.1.0" dependencies = [ "common_enums", "criterion", - "erased-serde", + "erased-serde 0.4.4", "euclid_macros", "frunk", "frunk_core", + "hyperswitch_constraint_graph", "nom", "once_cell", "rustc-hash", @@ -2768,6 +2778,7 @@ dependencies = [ "currency_conversion", "euclid", "getrandom", + "hyperswitch_constraint_graph", "kgraph_utils", "once_cell", "ron-parser", @@ -3602,6 +3613,18 @@ dependencies = [ "tokio 1.37.0", ] +[[package]] +name = "hyperswitch_constraint_graph" +version = "0.1.0" +dependencies = [ + "erased-serde 0.3.31", + "rustc-hash", + "serde", + "serde_json", + "strum 0.25.0", + "thiserror", +] + [[package]] name = "hyperswitch_domain_models" version = "0.1.0" @@ -3911,6 +3934,7 @@ dependencies = [ "common_enums", "criterion", "euclid", + "hyperswitch_constraint_graph", "masking", "serde", "serde_json", @@ -4067,7 +4091,7 @@ version = "0.1.0" dependencies = [ "bytes 1.6.0", "diesel", - "erased-serde", + "erased-serde 0.4.4", "serde", "serde_json", "subtle", @@ -5599,7 +5623,7 @@ dependencies = [ "digest", "dyn-clone", "encoding_rs", - "erased-serde", + "erased-serde 0.4.4", "error-stack", "euclid", "events", @@ -5608,6 +5632,7 @@ dependencies = [ "hex", "http 0.2.12", "hyper 0.14.28", + "hyperswitch_constraint_graph", "hyperswitch_domain_models", "hyperswitch_interfaces", "image", @@ -6698,6 +6723,15 @@ dependencies = [ "strum_macros 0.24.3", ] +[[package]] +name = "strum" +version = "0.25.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "290d54ea6f91c969195bdbcd7442c8c2a2ba87da8bf60a7ee86a235d4bc1e125" +dependencies = [ + "strum_macros 0.25.3", +] + [[package]] name = "strum" version = "0.26.2" @@ -6720,6 +6754,19 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "strum_macros" +version = "0.25.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23dc1fa9ac9c169a78ba62f0b841814b7abae11bdd047b9c58f893439e309ea0" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "rustversion", + "syn 2.0.57", +] + [[package]] name = "strum_macros" version = "0.26.2" diff --git a/crates/euclid/Cargo.toml b/crates/euclid/Cargo.toml index 7de27645523..3341746ab74 100644 --- a/crates/euclid/Cargo.toml +++ b/crates/euclid/Cargo.toml @@ -21,6 +21,7 @@ utoipa = { version = "4.2.0", features = ["preserve_order", "preserve_path_order # First party dependencies common_enums = { version = "0.1.0", path = "../common_enums" } +hyperswitch_constraint_graph = { version = "0.1.0", path = "../hyperswitch_constraint_graph" } euclid_macros = { version = "0.1.0", path = "../euclid_macros" } [features] diff --git a/crates/euclid/src/dssa/analyzer.rs b/crates/euclid/src/dssa/analyzer.rs index 4c615e78495..dc1da99a832 100644 --- a/crates/euclid/src/dssa/analyzer.rs +++ b/crates/euclid/src/dssa/analyzer.rs @@ -4,11 +4,15 @@ //! in the Euclid Rule DSL. These include standard control flow analyses like testing //! conflicting assertions, to Domain Specific Analyses making use of the //! [`Knowledge Graph Framework`](crate::dssa::graph). +use hyperswitch_constraint_graph::{ConstraintGraph, Memoization}; use rustc_hash::{FxHashMap, FxHashSet}; -use super::{graph::Memoization, types::EuclidAnalysable}; use crate::{ - dssa::{graph, state_machine, truth, types}, + dssa::{ + graph::CgraphExt, + state_machine, truth, + types::{self, EuclidAnalysable}, + }, frontend::{ ast, dir::{self, EuclidDirFilter}, @@ -203,12 +207,12 @@ fn perform_condition_analyses( fn perform_context_analyses( context: &types::ConjunctiveContext<'_>, - knowledge_graph: &graph::KnowledgeGraph<'_>, + knowledge_graph: &ConstraintGraph<'_, dir::DirValue>, ) -> Result<(), types::AnalysisError> { perform_condition_analyses(context)?; let mut memo = Memoization::new(); knowledge_graph - .perform_context_analysis(context, &mut memo) + .perform_context_analysis(context, &mut memo, None) .map_err(|err| types::AnalysisError { error_type: types::AnalysisErrorType::GraphAnalysis(err, memo), metadata: Default::default(), @@ -218,7 +222,7 @@ fn perform_context_analyses( pub fn analyze( program: ast::Program, - knowledge_graph: Option<&graph::KnowledgeGraph<'_>>, + knowledge_graph: Option<&ConstraintGraph<'_, dir::DirValue>>, ) -> Result, types::AnalysisError> { let dir_program = ast::lowering::lower_program(program)?; @@ -241,9 +245,14 @@ mod tests { use std::{ops::Deref, sync::Weak}; use euclid_macros::knowledge; + use hyperswitch_constraint_graph as cgraph; use super::*; - use crate::{dirval, types::DummyOutput}; + use crate::{ + dirval, + dssa::graph::{self, euclid_graph_prelude}, + types::DummyOutput, + }; #[test] fn test_conflicting_assertion_detection() { @@ -368,7 +377,7 @@ mod tests { #[test] fn test_negation_graph_analysis() { - let graph = knowledge! {crate + let graph = knowledge! { CaptureMethod(Automatic) ->> PaymentMethod(Card); }; @@ -410,18 +419,18 @@ mod tests { .deref() .clone() { - graph::AnalysisTrace::Value { predecessors, .. } => { - let _value = graph::NodeValue::Value(dir::DirValue::PaymentMethod( + cgraph::AnalysisTrace::Value { predecessors, .. } => { + let _value = cgraph::NodeValue::Value(dir::DirValue::PaymentMethod( dir::enums::PaymentMethod::Card, )); - let _relation = graph::Relation::Positive; + let _relation = cgraph::Relation::Positive; predecessors } _ => panic!("Expected Negation Trace for payment method = card"), }; let pred = match predecessor { - Some(graph::ValueTracePredecessor::Mandatory(predecessor)) => predecessor, + Some(cgraph::error::ValueTracePredecessor::Mandatory(predecessor)) => predecessor, _ => panic!("No predecessor found"), }; assert_eq!( @@ -433,11 +442,11 @@ mod tests { *Weak::upgrade(&pred) .expect("Expected Arc not found") .deref(), - graph::AnalysisTrace::Value { - value: graph::NodeValue::Value(dir::DirValue::CaptureMethod( + cgraph::AnalysisTrace::Value { + value: cgraph::NodeValue::Value(dir::DirValue::CaptureMethod( dir::enums::CaptureMethod::Automatic )), - relation: graph::Relation::Positive, + relation: cgraph::Relation::Positive, info: None, metadata: None, predecessors: None, diff --git a/crates/euclid/src/dssa/graph.rs b/crates/euclid/src/dssa/graph.rs index cb72cca0448..526248a3817 100644 --- a/crates/euclid/src/dssa/graph.rs +++ b/crates/euclid/src/dssa/graph.rs @@ -1,272 +1,53 @@ -use std::{ - fmt::Debug, - hash::Hash, - ops::{Deref, DerefMut}, - sync::{Arc, Weak}, -}; +use std::{fmt::Debug, sync::Weak}; -use erased_serde::{self, Serialize as ErasedSerialize}; +use hyperswitch_constraint_graph as cgraph; use rustc_hash::{FxHashMap, FxHashSet}; -use serde::Serialize; use crate::{ dssa::types, frontend::dir, types::{DataType, Metadata}, - utils, }; -#[derive(Debug, Clone, Copy, Serialize, PartialEq, Eq, Hash, strum::Display)] -pub enum Strength { - Weak, - Normal, - Strong, -} - -#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, strum::Display, Serialize)] -#[serde(rename_all = "snake_case")] -pub enum Relation { - Positive, - Negative, -} - -impl From for bool { - fn from(value: Relation) -> Self { - matches!(value, Relation::Positive) - } -} - -#[derive(Debug, Clone, Copy, Serialize, PartialEq, Eq, Hash)] -pub struct NodeId(usize); - -impl utils::EntityId for NodeId { - #[inline] - fn get_id(&self) -> usize { - self.0 - } - - #[inline] - fn with_id(id: usize) -> Self { - Self(id) - } -} - -#[derive(Debug, Clone, PartialEq, Eq, Hash)] -pub struct DomainInfo<'a> { - pub domain_identifier: DomainIdentifier<'a>, - pub domain_description: String, -} - -#[derive(Debug, Clone, PartialEq, Eq, Hash)] -pub struct DomainIdentifier<'a>(&'a str); - -impl<'a> DomainIdentifier<'a> { - pub fn new(domain_identifier: &'a str) -> Self { - Self(domain_identifier) - } -} - -#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] -pub struct DomainId(usize); - -impl utils::EntityId for DomainId { - #[inline] - fn get_id(&self) -> usize { - self.0 - } - - #[inline] - fn with_id(id: usize) -> Self { - Self(id) - } -} - -#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] -pub struct EdgeId(usize); +pub mod euclid_graph_prelude { + pub use hyperswitch_constraint_graph as cgraph; + pub use rustc_hash::{FxHashMap, FxHashSet}; -impl utils::EntityId for EdgeId { - #[inline] - fn get_id(&self) -> usize { - self.0 - } - - #[inline] - fn with_id(id: usize) -> Self { - Self(id) - } -} - -#[derive(Debug, Clone, Serialize)] -pub struct Memoization(FxHashMap<(NodeId, Relation, Strength), Result<(), Arc>>); - -impl Memoization { - pub fn new() -> Self { - Self(FxHashMap::default()) - } -} - -impl Default for Memoization { - #[inline] - fn default() -> Self { - Self::new() - } -} - -impl Deref for Memoization { - type Target = FxHashMap<(NodeId, Relation, Strength), Result<(), Arc>>; - fn deref(&self) -> &Self::Target { - &self.0 - } -} -impl DerefMut for Memoization { - fn deref_mut(&mut self) -> &mut Self::Target { - &mut self.0 - } -} -#[derive(Debug, Clone)] -pub struct Edge { - pub strength: Strength, - pub relation: Relation, - pub pred: NodeId, - pub succ: NodeId, + pub use crate::{ + dssa::graph::*, + frontend::dir::{enums::*, DirKey, DirKeyKind, DirValue}, + types::*, + }; } -#[derive(Debug)] -pub struct Node { - pub node_type: NodeType, - pub preds: Vec, - pub succs: Vec, - pub domain_ids: Vec, -} +impl cgraph::KeyNode for dir::DirKey {} -impl Node { - fn new(node_type: NodeType, domain_ids: Vec) -> Self { - Self { - node_type, - preds: Vec::new(), - succs: Vec::new(), - domain_ids, - } - } -} - -pub trait KgraphMetadata: ErasedSerialize + std::any::Any + Sync + Send + Debug {} -erased_serde::serialize_trait_object!(KgraphMetadata); - -impl KgraphMetadata for M where M: ErasedSerialize + std::any::Any + Sync + Send + Debug {} - -#[derive(Debug)] -pub struct KnowledgeGraph<'a> { - domain: utils::DenseMap>, - nodes: utils::DenseMap, - edges: utils::DenseMap, - value_map: FxHashMap, - node_info: utils::DenseMap>, - node_metadata: utils::DenseMap>>, -} - -pub struct KnowledgeGraphBuilder<'a> { - domain: utils::DenseMap>, - nodes: utils::DenseMap, - edges: utils::DenseMap, - domain_identifier_map: FxHashMap, DomainId>, - value_map: FxHashMap, - edges_map: FxHashMap<(NodeId, NodeId), EdgeId>, - node_info: utils::DenseMap>, - node_metadata: utils::DenseMap>>, -} +impl cgraph::ValueNode for dir::DirValue { + type Key = dir::DirKey; -impl<'a> Default for KnowledgeGraphBuilder<'a> { - #[inline] - fn default() -> Self { - Self::new() + fn get_key(&self) -> Self::Key { + Self::get_key(self) } } -#[derive(Debug, PartialEq, Eq)] -pub enum NodeType { - AllAggregator, - AnyAggregator, - InAggregator(FxHashSet), - Value(NodeValue), -} - -#[derive(Debug, Clone, PartialEq, Eq, Hash, serde::Serialize)] -#[serde(tag = "type", content = "value", rename_all = "snake_case")] -pub enum NodeValue { - Key(dir::DirKey), - Value(dir::DirValue), -} - -impl From for NodeValue { - fn from(value: dir::DirValue) -> Self { - Self::Value(value) - } -} - -impl From for NodeValue { - fn from(key: dir::DirKey) -> Self { - Self::Key(key) - } -} - -#[derive(Debug, Clone, serde::Serialize)] -#[serde(tag = "type", content = "predecessor", rename_all = "snake_case")] -pub enum ValueTracePredecessor { - Mandatory(Box>), - OneOf(Vec>), -} - -#[derive(Debug, Clone, serde::Serialize)] -#[serde(tag = "type", content = "trace", rename_all = "snake_case")] -pub enum AnalysisTrace { - Value { - value: NodeValue, - relation: Relation, - predecessors: Option, - info: Option<&'static str>, - metadata: Option>, - }, - - AllAggregation { - unsatisfied: Vec>, - info: Option<&'static str>, - metadata: Option>, - }, - - AnyAggregation { - unsatisfied: Vec>, - info: Option<&'static str>, - metadata: Option>, - }, - - InAggregation { - expected: Vec, - found: Option, - relation: Relation, - info: Option<&'static str>, - metadata: Option>, - }, -} - #[derive(Debug, Clone, serde::Serialize)] #[serde(tag = "type", content = "details", rename_all = "snake_case")] -pub enum AnalysisError { - Graph(GraphError), +pub enum AnalysisError { + Graph(cgraph::GraphError), AssertionTrace { - trace: Weak, + trace: Weak>, metadata: Metadata, }, NegationTrace { - trace: Weak, + trace: Weak>, metadata: Vec, }, } -impl AnalysisError { - fn assertion_from_graph_error(metadata: &Metadata, graph_error: GraphError) -> Self { +impl AnalysisError { + fn assertion_from_graph_error(metadata: &Metadata, graph_error: cgraph::GraphError) -> Self { match graph_error { - GraphError::AnalysisError(trace) => Self::AssertionTrace { + cgraph::GraphError::AnalysisError(trace) => Self::AssertionTrace { trace, metadata: metadata.clone(), }, @@ -275,9 +56,12 @@ impl AnalysisError { } } - fn negation_from_graph_error(metadata: Vec<&Metadata>, graph_error: GraphError) -> Self { + fn negation_from_graph_error( + metadata: Vec<&Metadata>, + graph_error: cgraph::GraphError, + ) -> Self { match graph_error { - GraphError::AnalysisError(trace) => Self::NegationTrace { + cgraph::GraphError::AnalysisError(trace) => Self::NegationTrace { trace, metadata: metadata.iter().map(|m| (*m).clone()).collect(), }, @@ -287,56 +71,6 @@ impl AnalysisError { } } -#[derive(Debug, Clone, serde::Serialize, thiserror::Error)] -#[serde(tag = "type", content = "info", rename_all = "snake_case")] -pub enum GraphError { - #[error("An edge was not found in the graph")] - EdgeNotFound, - #[error("Attempted to create a conflicting edge between two nodes")] - ConflictingEdgeCreated, - #[error("Cycle detected in graph")] - CycleDetected, - #[error("Domain wasn't found in the Graph")] - DomainNotFound, - #[error("Malformed Graph: {reason}")] - MalformedGraph { reason: String }, - #[error("A node was not found in the graph")] - NodeNotFound, - #[error("A value node was not found: {0:#?}")] - ValueNodeNotFound(dir::DirValue), - #[error("No values provided for an 'in' aggregator node")] - NoInAggregatorValues, - #[error("Error during analysis: {0:#?}")] - AnalysisError(Weak), -} - -impl GraphError { - fn get_analysis_trace(self) -> Result, Self> { - match self { - Self::AnalysisError(trace) => Ok(trace), - _ => Err(self), - } - } -} - -impl PartialEq for NodeValue { - fn eq(&self, other: &dir::DirValue) -> bool { - match self { - Self::Key(dir_key) => *dir_key == other.get_key(), - Self::Value(dir_value) if dir_value.get_key() == other.get_key() => { - if let (Some(left), Some(right)) = - (dir_value.get_num_value(), other.get_num_value()) - { - left.fits(&right) - } else { - dir::DirValue::check_equality(dir_value, other) - } - } - Self::Value(_) => false, - } - } -} - pub struct AnalysisContext { keywise_values: FxHashMap>, } @@ -355,33 +89,6 @@ impl AnalysisContext { Self { keywise_values } } - fn check_presence(&self, value: &NodeValue, weak: bool) -> bool { - match value { - NodeValue::Key(k) => self.keywise_values.contains_key(k) || weak, - NodeValue::Value(val) => { - let key = val.get_key(); - let value_set = if let Some(set) = self.keywise_values.get(&key) { - set - } else { - return weak; - }; - - match key.kind.get_type() { - DataType::EnumVariant | DataType::StrValue | DataType::MetadataValue => { - value_set.contains(val) - } - DataType::Number => val.get_num_value().map_or(false, |num_val| { - value_set.iter().any(|ctx_val| { - ctx_val - .get_num_value() - .map_or(false, |ctx_num_val| num_val.fits(&ctx_num_val)) - }) - }), - } - } - } - } - pub fn insert(&mut self, value: dir::DirValue) { self.keywise_values .entry(value.get_key()) @@ -400,477 +107,153 @@ impl AnalysisContext { } } -impl<'a> KnowledgeGraphBuilder<'a> { - pub fn new() -> Self { - Self { - domain: utils::DenseMap::new(), - nodes: utils::DenseMap::new(), - edges: utils::DenseMap::new(), - domain_identifier_map: FxHashMap::default(), - value_map: FxHashMap::default(), - edges_map: FxHashMap::default(), - node_info: utils::DenseMap::new(), - node_metadata: utils::DenseMap::new(), - } - } - - pub fn build(self) -> KnowledgeGraph<'a> { - KnowledgeGraph { - domain: self.domain, - nodes: self.nodes, - edges: self.edges, - value_map: self.value_map, - node_info: self.node_info, - node_metadata: self.node_metadata, - } - } - - pub fn make_domain( - &mut self, - domain_identifier: DomainIdentifier<'a>, - domain_description: String, - ) -> Result { - Ok(self - .domain_identifier_map - .clone() - .get(&domain_identifier) - .map_or_else( - || { - let domain_id = self.domain.push(DomainInfo { - domain_identifier: domain_identifier.clone(), - domain_description, - }); - self.domain_identifier_map - .insert(domain_identifier.clone(), domain_id); - domain_id - }, - |domain_id| *domain_id, - )) - } - - pub fn make_value_node( - &mut self, - value: NodeValue, - info: Option<&'static str>, - domain_identifiers: Vec>, - metadata: Option, - ) -> Result { - match self.value_map.get(&value).copied() { - Some(node_id) => Ok(node_id), - None => { - let mut domain_ids: Vec = Vec::new(); - domain_identifiers - .iter() - .try_for_each(|ident| { - self.domain_identifier_map - .get(ident) - .map(|id| domain_ids.push(*id)) - }) - .ok_or(GraphError::DomainNotFound)?; - - let node_id = self - .nodes - .push(Node::new(NodeType::Value(value.clone()), domain_ids)); - let _node_info_id = self.node_info.push(info); - - let _node_metadata_id = self - .node_metadata - .push(metadata.map(|meta| -> Arc { Arc::new(meta) })); - - self.value_map.insert(value, node_id); - Ok(node_id) - } - } - } - - pub fn make_edge( - &mut self, - pred_id: NodeId, - succ_id: NodeId, - strength: Strength, - relation: Relation, - ) -> Result { - self.ensure_node_exists(pred_id)?; - self.ensure_node_exists(succ_id)?; - self.edges_map - .get(&(pred_id, succ_id)) - .copied() - .and_then(|edge_id| self.edges.get(edge_id).cloned().map(|edge| (edge_id, edge))) - .map_or_else( - || { - let edge_id = self.edges.push(Edge { - strength, - relation, - pred: pred_id, - succ: succ_id, - }); - self.edges_map.insert((pred_id, succ_id), edge_id); - - let pred = self - .nodes - .get_mut(pred_id) - .ok_or(GraphError::NodeNotFound)?; - pred.succs.push(edge_id); - - let succ = self - .nodes - .get_mut(succ_id) - .ok_or(GraphError::NodeNotFound)?; - succ.preds.push(edge_id); - - Ok(edge_id) - }, - |(edge_id, edge)| { - if edge.strength == strength && edge.relation == relation { - Ok(edge_id) - } else { - Err(GraphError::ConflictingEdgeCreated) - } - }, - ) - } - - pub fn make_all_aggregator( - &mut self, - nodes: &[(NodeId, Relation, Strength)], - info: Option<&'static str>, - metadata: Option, - domain: Vec>, - ) -> Result { - nodes - .iter() - .try_for_each(|(node_id, _, _)| self.ensure_node_exists(*node_id))?; +impl cgraph::CheckingContext for AnalysisContext { + type Value = dir::DirValue; - let mut domain_ids: Vec = Vec::new(); - domain - .iter() - .try_for_each(|ident| { - self.domain_identifier_map - .get(ident) - .map(|id| domain_ids.push(*id)) - }) - .ok_or(GraphError::DomainNotFound)?; - - let aggregator_id = self - .nodes - .push(Node::new(NodeType::AllAggregator, domain_ids)); - let _aggregator_info_id = self.node_info.push(info); - - let _node_metadata_id = self - .node_metadata - .push(metadata.map(|meta| -> Arc { Arc::new(meta) })); + fn from_node_values(vals: impl IntoIterator) -> Self + where + L: Into, + { + let mut keywise_values: FxHashMap> = + FxHashMap::default(); - for (node_id, relation, strength) in nodes { - self.make_edge(*node_id, aggregator_id, *strength, *relation)?; + for dir_val in vals.into_iter().map(L::into) { + let key = dir_val.get_key(); + let set = keywise_values.entry(key).or_default(); + set.insert(dir_val); } - Ok(aggregator_id) + Self { keywise_values } } - pub fn make_any_aggregator( - &mut self, - nodes: &[(NodeId, Relation)], - info: Option<&'static str>, - metadata: Option, - domain: Vec>, - ) -> Result { - nodes - .iter() - .try_for_each(|(node_id, _)| self.ensure_node_exists(*node_id))?; - - let mut domain_ids: Vec = Vec::new(); - domain - .iter() - .try_for_each(|ident| { - self.domain_identifier_map - .get(ident) - .map(|id| domain_ids.push(*id)) - }) - .ok_or(GraphError::DomainNotFound)?; - - let aggregator_id = self - .nodes - .push(Node::new(NodeType::AnyAggregator, domain_ids)); - let _aggregator_info_id = self.node_info.push(info); - - let _node_metadata_id = self - .node_metadata - .push(metadata.map(|meta| -> Arc { Arc::new(meta) })); - - for (node_id, relation) in nodes { - self.make_edge(*node_id, aggregator_id, Strength::Strong, *relation)?; - } + fn check_presence( + &self, + value: &cgraph::NodeValue, + strength: cgraph::Strength, + ) -> bool { + match value { + cgraph::NodeValue::Key(k) => { + self.keywise_values.contains_key(k) || matches!(strength, cgraph::Strength::Weak) + } - Ok(aggregator_id) - } + cgraph::NodeValue::Value(val) => { + let key = val.get_key(); + let value_set = if let Some(set) = self.keywise_values.get(&key) { + set + } else { + return matches!(strength, cgraph::Strength::Weak); + }; - pub fn make_in_aggregator( - &mut self, - values: Vec, - info: Option<&'static str>, - metadata: Option, - domain: Vec>, - ) -> Result { - let key = values - .first() - .ok_or(GraphError::NoInAggregatorValues)? - .get_key(); - - for val in &values { - if val.get_key() != key { - Err(GraphError::MalformedGraph { - reason: "Values for 'In' aggregator not of same key".to_string(), - })?; + match key.kind.get_type() { + DataType::EnumVariant | DataType::StrValue | DataType::MetadataValue => { + value_set.contains(val) + } + DataType::Number => val.get_num_value().map_or(false, |num_val| { + value_set.iter().any(|ctx_val| { + ctx_val + .get_num_value() + .map_or(false, |ctx_num_val| num_val.fits(&ctx_num_val)) + }) + }), + } } } - - let mut domain_ids: Vec = Vec::new(); - domain - .iter() - .try_for_each(|ident| { - self.domain_identifier_map - .get(ident) - .map(|id| domain_ids.push(*id)) - }) - .ok_or(GraphError::DomainNotFound)?; - - let node_id = self.nodes.push(Node::new( - NodeType::InAggregator(FxHashSet::from_iter(values)), - domain_ids, - )); - let _aggregator_info_id = self.node_info.push(info); - - let _node_metadata_id = self - .node_metadata - .push(metadata.map(|meta| -> Arc { Arc::new(meta) })); - - Ok(node_id) } - fn ensure_node_exists(&self, id: NodeId) -> Result<(), GraphError> { - if self.nodes.contains_key(id) { - Ok(()) - } else { - Err(GraphError::NodeNotFound) - } + fn get_values_by_key( + &self, + key: &::Key, + ) -> Option> { + self.keywise_values + .get(key) + .map(|set| set.iter().cloned().collect()) } } -impl<'a> KnowledgeGraph<'a> { - fn check_node( +pub trait CgraphExt { + fn key_analysis( &self, + key: dir::DirKey, ctx: &AnalysisContext, - node_id: NodeId, - relation: Relation, - strength: Strength, - memo: &mut Memoization, - ) -> Result<(), GraphError> { - let node = self.nodes.get(node_id).ok_or(GraphError::NodeNotFound)?; - if let Some(already_memo) = memo.get(&(node_id, relation, strength)) { - already_memo - .clone() - .map_err(|err| GraphError::AnalysisError(Arc::downgrade(&err))) - } else { - match &node.node_type { - NodeType::AllAggregator => { - let mut unsatisfied = Vec::>::new(); - - for edge_id in node.preds.iter().copied() { - let edge = self.edges.get(edge_id).ok_or(GraphError::EdgeNotFound)?; - - if let Err(e) = - self.check_node(ctx, edge.pred, edge.relation, edge.strength, memo) - { - unsatisfied.push(e.get_analysis_trace()?); - } - } - - if !unsatisfied.is_empty() { - let err = Arc::new(AnalysisTrace::AllAggregation { - unsatisfied, - info: self.node_info.get(node_id).cloned().flatten(), - metadata: self.node_metadata.get(node_id).cloned().flatten(), - }); - - memo.insert((node_id, relation, strength), Err(Arc::clone(&err))); - Err(GraphError::AnalysisError(Arc::downgrade(&err))) - } else { - memo.insert((node_id, relation, strength), Ok(())); - Ok(()) - } - } - - NodeType::AnyAggregator => { - let mut unsatisfied = Vec::>::new(); - let mut matched_one = false; - - for edge_id in node.preds.iter().copied() { - let edge = self.edges.get(edge_id).ok_or(GraphError::EdgeNotFound)?; - - if let Err(e) = - self.check_node(ctx, edge.pred, edge.relation, edge.strength, memo) - { - unsatisfied.push(e.get_analysis_trace()?); - } else { - matched_one = true; - } - } - - if matched_one || node.preds.is_empty() { - memo.insert((node_id, relation, strength), Ok(())); - Ok(()) - } else { - let err = Arc::new(AnalysisTrace::AnyAggregation { - unsatisfied: unsatisfied.clone(), - info: self.node_info.get(node_id).cloned().flatten(), - metadata: self.node_metadata.get(node_id).cloned().flatten(), - }); - - memo.insert((node_id, relation, strength), Err(Arc::clone(&err))); - Err(GraphError::AnalysisError(Arc::downgrade(&err))) - } - } + memo: &mut cgraph::Memoization, + cycle_map: &mut cgraph::CycleCheck, + domains: Option<&[&str]>, + ) -> Result<(), cgraph::GraphError>; - NodeType::InAggregator(expected) => { - let the_key = expected - .iter() - .next() - .ok_or_else(|| GraphError::MalformedGraph { - reason: - "An OnlyIn aggregator node must have at least one expected value" - .to_string(), - })? - .get_key(); - - let ctx_vals = if let Some(vals) = ctx.keywise_values.get(&the_key) { - vals - } else { - return if let Strength::Weak = strength { - memo.insert((node_id, relation, strength), Ok(())); - Ok(()) - } else { - let err = Arc::new(AnalysisTrace::InAggregation { - expected: expected.iter().cloned().collect(), - found: None, - relation, - info: self.node_info.get(node_id).cloned().flatten(), - metadata: self.node_metadata.get(node_id).cloned().flatten(), - }); - - memo.insert((node_id, relation, strength), Err(Arc::clone(&err))); - Err(GraphError::AnalysisError(Arc::downgrade(&err))) - }; - }; - - let relation_bool: bool = relation.into(); - for ctx_value in ctx_vals { - if expected.contains(ctx_value) != relation_bool { - let err = Arc::new(AnalysisTrace::InAggregation { - expected: expected.iter().cloned().collect(), - found: Some(ctx_value.clone()), - relation, - info: self.node_info.get(node_id).cloned().flatten(), - metadata: self.node_metadata.get(node_id).cloned().flatten(), - }); - - memo.insert((node_id, relation, strength), Err(Arc::clone(&err))); - Err(GraphError::AnalysisError(Arc::downgrade(&err)))?; - } - } + fn value_analysis( + &self, + val: dir::DirValue, + ctx: &AnalysisContext, + memo: &mut cgraph::Memoization, + cycle_map: &mut cgraph::CycleCheck, + domains: Option<&[&str]>, + ) -> Result<(), cgraph::GraphError>; - memo.insert((node_id, relation, strength), Ok(())); - Ok(()) - } + fn check_value_validity( + &self, + val: dir::DirValue, + analysis_ctx: &AnalysisContext, + memo: &mut cgraph::Memoization, + cycle_map: &mut cgraph::CycleCheck, + domains: Option<&[&str]>, + ) -> Result>; - NodeType::Value(val) => { - let in_context = ctx.check_presence(val, matches!(strength, Strength::Weak)); - let relation_bool: bool = relation.into(); - - if in_context != relation_bool { - let err = Arc::new(AnalysisTrace::Value { - value: val.clone(), - relation, - predecessors: None, - info: self.node_info.get(node_id).cloned().flatten(), - metadata: self.node_metadata.get(node_id).cloned().flatten(), - }); - - memo.insert((node_id, relation, strength), Err(Arc::clone(&err))); - Err(GraphError::AnalysisError(Arc::downgrade(&err)))?; - } + fn key_value_analysis( + &self, + val: dir::DirValue, + ctx: &AnalysisContext, + memo: &mut cgraph::Memoization, + cycle_map: &mut cgraph::CycleCheck, + domains: Option<&[&str]>, + ) -> Result<(), cgraph::GraphError>; - if !relation_bool { - memo.insert((node_id, relation, strength), Ok(())); - return Ok(()); - } + fn assertion_analysis( + &self, + positive_ctx: &[(&dir::DirValue, &Metadata)], + analysis_ctx: &AnalysisContext, + memo: &mut cgraph::Memoization, + cycle_map: &mut cgraph::CycleCheck, + domains: Option<&[&str]>, + ) -> Result<(), AnalysisError>; - let mut errors = Vec::>::new(); - let mut matched_one = false; - - for edge_id in node.preds.iter().copied() { - let edge = self.edges.get(edge_id).ok_or(GraphError::EdgeNotFound)?; - let result = - self.check_node(ctx, edge.pred, edge.relation, edge.strength, memo); - - match (edge.strength, result) { - (Strength::Strong, Err(trace)) => { - let err = Arc::new(AnalysisTrace::Value { - value: val.clone(), - relation, - info: self.node_info.get(node_id).cloned().flatten(), - metadata: self.node_metadata.get(node_id).cloned().flatten(), - predecessors: Some(ValueTracePredecessor::Mandatory(Box::new( - trace.get_analysis_trace()?, - ))), - }); - memo.insert((node_id, relation, strength), Err(Arc::clone(&err))); - Err(GraphError::AnalysisError(Arc::downgrade(&err)))?; - } - - (Strength::Strong, Ok(_)) => { - matched_one = true; - } - - (Strength::Normal | Strength::Weak, Err(trace)) => { - errors.push(trace.get_analysis_trace()?); - } - - (Strength::Normal | Strength::Weak, Ok(_)) => { - matched_one = true; - } - } - } + fn negation_analysis( + &self, + negative_ctx: &[(&[dir::DirValue], &Metadata)], + analysis_ctx: &mut AnalysisContext, + memo: &mut cgraph::Memoization, + cycle_map: &mut cgraph::CycleCheck, + domains: Option<&[&str]>, + ) -> Result<(), AnalysisError>; - if matched_one || node.preds.is_empty() { - memo.insert((node_id, relation, strength), Ok(())); - Ok(()) - } else { - let err = Arc::new(AnalysisTrace::Value { - value: val.clone(), - relation, - info: self.node_info.get(node_id).cloned().flatten(), - metadata: self.node_metadata.get(node_id).cloned().flatten(), - predecessors: Some(ValueTracePredecessor::OneOf(errors.clone())), - }); - - memo.insert((node_id, relation, strength), Err(Arc::clone(&err))); - Err(GraphError::AnalysisError(Arc::downgrade(&err))) - } - } - } - } - } + fn perform_context_analysis( + &self, + ctx: &types::ConjunctiveContext<'_>, + memo: &mut cgraph::Memoization, + domains: Option<&[&str]>, + ) -> Result<(), AnalysisError>; +} +impl CgraphExt for cgraph::ConstraintGraph<'_, dir::DirValue> { fn key_analysis( &self, key: dir::DirKey, ctx: &AnalysisContext, - memo: &mut Memoization, - ) -> Result<(), GraphError> { + memo: &mut cgraph::Memoization, + cycle_map: &mut cgraph::CycleCheck, + domains: Option<&[&str]>, + ) -> Result<(), cgraph::GraphError> { self.value_map - .get(&NodeValue::Key(key)) + .get(&cgraph::NodeValue::Key(key)) .map_or(Ok(()), |node_id| { - self.check_node(ctx, *node_id, Relation::Positive, Strength::Strong, memo) + self.check_node( + ctx, + *node_id, + cgraph::Relation::Positive, + cgraph::Strength::Strong, + memo, + cycle_map, + domains, + ) }) } @@ -878,22 +261,34 @@ impl<'a> KnowledgeGraph<'a> { &self, val: dir::DirValue, ctx: &AnalysisContext, - memo: &mut Memoization, - ) -> Result<(), GraphError> { + memo: &mut cgraph::Memoization, + cycle_map: &mut cgraph::CycleCheck, + domains: Option<&[&str]>, + ) -> Result<(), cgraph::GraphError> { self.value_map - .get(&NodeValue::Value(val)) + .get(&cgraph::NodeValue::Value(val)) .map_or(Ok(()), |node_id| { - self.check_node(ctx, *node_id, Relation::Positive, Strength::Strong, memo) + self.check_node( + ctx, + *node_id, + cgraph::Relation::Positive, + cgraph::Strength::Strong, + memo, + cycle_map, + domains, + ) }) } - pub fn check_value_validity( + fn check_value_validity( &self, val: dir::DirValue, analysis_ctx: &AnalysisContext, - memo: &mut Memoization, - ) -> Result { - let maybe_node_id = self.value_map.get(&NodeValue::Value(val)); + memo: &mut cgraph::Memoization, + cycle_map: &mut cgraph::CycleCheck, + domains: Option<&[&str]>, + ) -> Result> { + let maybe_node_id = self.value_map.get(&cgraph::NodeValue::Value(val)); let node_id = if let Some(nid) = maybe_node_id { nid @@ -904,9 +299,11 @@ impl<'a> KnowledgeGraph<'a> { let result = self.check_node( analysis_ctx, *node_id, - Relation::Positive, - Strength::Weak, + cgraph::Relation::Positive, + cgraph::Strength::Weak, memo, + cycle_map, + domains, ); match result { @@ -918,24 +315,28 @@ impl<'a> KnowledgeGraph<'a> { } } - pub fn key_value_analysis( + fn key_value_analysis( &self, val: dir::DirValue, ctx: &AnalysisContext, - memo: &mut Memoization, - ) -> Result<(), GraphError> { - self.key_analysis(val.get_key(), ctx, memo) - .and_then(|_| self.value_analysis(val, ctx, memo)) + memo: &mut cgraph::Memoization, + cycle_map: &mut cgraph::CycleCheck, + domains: Option<&[&str]>, + ) -> Result<(), cgraph::GraphError> { + self.key_analysis(val.get_key(), ctx, memo, cycle_map, domains) + .and_then(|_| self.value_analysis(val, ctx, memo, cycle_map, domains)) } fn assertion_analysis( &self, positive_ctx: &[(&dir::DirValue, &Metadata)], analysis_ctx: &AnalysisContext, - memo: &mut Memoization, - ) -> Result<(), AnalysisError> { + memo: &mut cgraph::Memoization, + cycle_map: &mut cgraph::CycleCheck, + domains: Option<&[&str]>, + ) -> Result<(), AnalysisError> { positive_ctx.iter().try_for_each(|(value, metadata)| { - self.key_value_analysis((*value).clone(), analysis_ctx, memo) + self.key_value_analysis((*value).clone(), analysis_ctx, memo, cycle_map, domains) .map_err(|e| AnalysisError::assertion_from_graph_error(metadata, e)) }) } @@ -944,8 +345,10 @@ impl<'a> KnowledgeGraph<'a> { &self, negative_ctx: &[(&[dir::DirValue], &Metadata)], analysis_ctx: &mut AnalysisContext, - memo: &mut Memoization, - ) -> Result<(), AnalysisError> { + memo: &mut cgraph::Memoization, + cycle_map: &mut cgraph::CycleCheck, + domains: Option<&[&str]>, + ) -> Result<(), AnalysisError> { let mut keywise_metadata: FxHashMap> = FxHashMap::default(); let mut keywise_negation: FxHashMap> = FxHashMap::default(); @@ -974,7 +377,7 @@ impl<'a> KnowledgeGraph<'a> { let all_metadata = keywise_metadata.remove(&key).unwrap_or_default(); let first_metadata = all_metadata.first().cloned().cloned().unwrap_or_default(); - self.key_analysis(key.clone(), analysis_ctx, memo) + self.key_analysis(key.clone(), analysis_ctx, memo, cycle_map, domains) .map_err(|e| AnalysisError::assertion_from_graph_error(&first_metadata, e))?; let mut value_set = if let Some(set) = key.kind.get_value_set() { @@ -987,7 +390,7 @@ impl<'a> KnowledgeGraph<'a> { for value in value_set { analysis_ctx.insert(value.clone()); - self.value_analysis(value.clone(), analysis_ctx, memo) + self.value_analysis(value.clone(), analysis_ctx, memo, cycle_map, domains) .map_err(|e| { AnalysisError::negation_from_graph_error(all_metadata.clone(), e) })?; @@ -998,11 +401,12 @@ impl<'a> KnowledgeGraph<'a> { Ok(()) } - pub fn perform_context_analysis( + fn perform_context_analysis( &self, ctx: &types::ConjunctiveContext<'_>, - memo: &mut Memoization, - ) -> Result<(), AnalysisError> { + memo: &mut cgraph::Memoization, + domains: Option<&[&str]>, + ) -> Result<(), AnalysisError> { let mut analysis_ctx = AnalysisContext::from_dir_values( ctx.iter() .filter_map(|ctx_val| ctx_val.value.get_assertion().cloned()), @@ -1017,7 +421,13 @@ impl<'a> KnowledgeGraph<'a> { .map(|val| (val, ctx_val.metadata)) }) .collect::>(); - self.assertion_analysis(&positive_ctx, &analysis_ctx, memo)?; + self.assertion_analysis( + &positive_ctx, + &analysis_ctx, + memo, + &mut cgraph::CycleCheck::new(), + domains, + )?; let negative_ctx = ctx .iter() @@ -1028,127 +438,38 @@ impl<'a> KnowledgeGraph<'a> { .map(|vals| (vals, ctx_val.metadata)) }) .collect::>(); - self.negation_analysis(&negative_ctx, &mut analysis_ctx, memo)?; + self.negation_analysis( + &negative_ctx, + &mut analysis_ctx, + memo, + &mut cgraph::CycleCheck::new(), + domains, + )?; Ok(()) } - - pub fn combine<'b>(g1: &'b Self, g2: &'b Self) -> Result { - let mut node_builder = KnowledgeGraphBuilder::new(); - let mut g1_old2new_id = utils::DenseMap::::new(); - let mut g2_old2new_id = utils::DenseMap::::new(); - let mut g1_old2new_domain_id = utils::DenseMap::::new(); - let mut g2_old2new_domain_id = utils::DenseMap::::new(); - - let add_domain = |node_builder: &mut KnowledgeGraphBuilder<'a>, - domain: DomainInfo<'a>| - -> Result { - node_builder.make_domain(domain.domain_identifier, domain.domain_description) - }; - - let add_node = |node_builder: &mut KnowledgeGraphBuilder<'a>, - node: &Node, - domains: Vec>| - -> Result { - match &node.node_type { - NodeType::Value(node_value) => { - node_builder.make_value_node(node_value.clone(), None, domains, None::<()>) - } - - NodeType::AllAggregator => { - Ok(node_builder.make_all_aggregator(&[], None, None::<()>, domains)?) - } - - NodeType::AnyAggregator => { - Ok(node_builder.make_any_aggregator(&[], None, None::<()>, Vec::new())?) - } - - NodeType::InAggregator(expected) => Ok(node_builder.make_in_aggregator( - expected.iter().cloned().collect(), - None, - None::<()>, - Vec::new(), - )?), - } - }; - - for (_old_domain_id, domain) in g1.domain.iter() { - let new_domain_id = add_domain(&mut node_builder, domain.clone())?; - g1_old2new_domain_id.push(new_domain_id); - } - - for (_old_domain_id, domain) in g2.domain.iter() { - let new_domain_id = add_domain(&mut node_builder, domain.clone())?; - g2_old2new_domain_id.push(new_domain_id); - } - - for (_old_node_id, node) in g1.nodes.iter() { - let mut domain_identifiers: Vec> = Vec::new(); - for domain_id in &node.domain_ids { - match g1.domain.get(*domain_id) { - Some(domain) => domain_identifiers.push(domain.domain_identifier.clone()), - None => return Err(GraphError::DomainNotFound), - } - } - let new_node_id = add_node(&mut node_builder, node, domain_identifiers.clone())?; - g1_old2new_id.push(new_node_id); - } - - for (_old_node_id, node) in g2.nodes.iter() { - let mut domain_identifiers: Vec> = Vec::new(); - for domain_id in &node.domain_ids { - match g2.domain.get(*domain_id) { - Some(domain) => domain_identifiers.push(domain.domain_identifier.clone()), - None => return Err(GraphError::DomainNotFound), - } - } - let new_node_id = add_node(&mut node_builder, node, domain_identifiers.clone())?; - g2_old2new_id.push(new_node_id); - } - - for edge in g1.edges.values() { - let new_pred_id = g1_old2new_id - .get(edge.pred) - .ok_or(GraphError::NodeNotFound)?; - let new_succ_id = g1_old2new_id - .get(edge.succ) - .ok_or(GraphError::NodeNotFound)?; - - node_builder.make_edge(*new_pred_id, *new_succ_id, edge.strength, edge.relation)?; - } - - for edge in g2.edges.values() { - let new_pred_id = g2_old2new_id - .get(edge.pred) - .ok_or(GraphError::NodeNotFound)?; - let new_succ_id = g2_old2new_id - .get(edge.succ) - .ok_or(GraphError::NodeNotFound)?; - - node_builder.make_edge(*new_pred_id, *new_succ_id, edge.strength, edge.relation)?; - } - - Ok(node_builder.build()) - } } #[cfg(test)] mod test { #![allow(clippy::unwrap_used, clippy::expect_used, clippy::panic)] + use std::ops::Deref; + use euclid_macros::knowledge; + use hyperswitch_constraint_graph::CycleCheck; use super::*; use crate::{dirval, frontend::dir::enums}; #[test] fn test_strong_positive_relation_success() { - let graph = knowledge! {crate + let graph = knowledge! { PaymentMethod(Card) ->> CaptureMethod(Automatic); PaymentMethod(not Wallet) & PaymentMethod(not PayLater) -> CaptureMethod(Automatic); }; - let memo = &mut Memoization::new(); + let memo = &mut cgraph::Memoization::new(); let result = graph.key_value_analysis( dirval!(CaptureMethod = Automatic), &AnalysisContext::from_dir_values([ @@ -1156,6 +477,8 @@ mod test { dirval!(PaymentMethod = Card), ]), memo, + &mut CycleCheck::new(), + None, ); assert!(result.is_ok()); @@ -1163,15 +486,17 @@ mod test { #[test] fn test_strong_positive_relation_failure() { - let graph = knowledge! {crate + let graph = knowledge! { PaymentMethod(Card) ->> CaptureMethod(Automatic); PaymentMethod(not Wallet) -> CaptureMethod(Automatic); }; - let memo = &mut Memoization::new(); + let memo = &mut cgraph::Memoization::new(); let result = graph.key_value_analysis( dirval!(CaptureMethod = Automatic), &AnalysisContext::from_dir_values([dirval!(CaptureMethod = Automatic)]), memo, + &mut CycleCheck::new(), + None, ); assert!(result.is_err()); @@ -1179,11 +504,11 @@ mod test { #[test] fn test_strong_negative_relation_success() { - let graph = knowledge! {crate + let graph = knowledge! { PaymentMethod(Card) -> CaptureMethod(Automatic); PaymentMethod(not Wallet) ->> CaptureMethod(Automatic); }; - let memo = &mut Memoization::new(); + let memo = &mut cgraph::Memoization::new(); let result = graph.key_value_analysis( dirval!(CaptureMethod = Automatic), &AnalysisContext::from_dir_values([ @@ -1191,6 +516,8 @@ mod test { dirval!(PaymentMethod = Card), ]), memo, + &mut CycleCheck::new(), + None, ); assert!(result.is_ok()); @@ -1198,11 +525,11 @@ mod test { #[test] fn test_strong_negative_relation_failure() { - let graph = knowledge! {crate + let graph = knowledge! { PaymentMethod(Card) -> CaptureMethod(Automatic); PaymentMethod(not Wallet) ->> CaptureMethod(Automatic); }; - let memo = &mut Memoization::new(); + let memo = &mut cgraph::Memoization::new(); let result = graph.key_value_analysis( dirval!(CaptureMethod = Automatic), &AnalysisContext::from_dir_values([ @@ -1210,6 +537,8 @@ mod test { dirval!(PaymentMethod = Wallet), ]), memo, + &mut CycleCheck::new(), + None, ); assert!(result.is_err()); @@ -1217,11 +546,11 @@ mod test { #[test] fn test_normal_one_of_failure() { - let graph = knowledge! {crate + let graph = knowledge! { PaymentMethod(Card) -> CaptureMethod(Automatic); PaymentMethod(Wallet) -> CaptureMethod(Automatic); }; - let memo = &mut Memoization::new(); + let memo = &mut cgraph::Memoization::new(); let result = graph.key_value_analysis( dirval!(CaptureMethod = Automatic), &AnalysisContext::from_dir_values([ @@ -1229,12 +558,14 @@ mod test { dirval!(PaymentMethod = PayLater), ]), memo, + &mut CycleCheck::new(), + None, ); assert!(matches!( *Weak::upgrade(&result.unwrap_err().get_analysis_trace().unwrap()) .expect("Expected Arc"), - AnalysisTrace::Value { - predecessors: Some(ValueTracePredecessor::OneOf(_)), + cgraph::AnalysisTrace::Value { + predecessors: Some(cgraph::error::ValueTracePredecessor::OneOf(_)), .. } )); @@ -1242,10 +573,10 @@ mod test { #[test] fn test_all_aggregator_success() { - let graph = knowledge! {crate + let graph = knowledge! { PaymentMethod(Card) & PaymentMethod(not Wallet) -> CaptureMethod(Automatic); }; - let memo = &mut Memoization::new(); + let memo = &mut cgraph::Memoization::new(); let result = graph.key_value_analysis( dirval!(CaptureMethod = Automatic), &AnalysisContext::from_dir_values([ @@ -1253,6 +584,8 @@ mod test { dirval!(CaptureMethod = Automatic), ]), memo, + &mut CycleCheck::new(), + None, ); assert!(result.is_ok()); @@ -1260,10 +593,10 @@ mod test { #[test] fn test_all_aggregator_failure() { - let graph = knowledge! {crate + let graph = knowledge! { PaymentMethod(Card) & PaymentMethod(not Wallet) -> CaptureMethod(Automatic); }; - let memo = &mut Memoization::new(); + let memo = &mut cgraph::Memoization::new(); let result = graph.key_value_analysis( dirval!(CaptureMethod = Automatic), &AnalysisContext::from_dir_values([ @@ -1271,6 +604,8 @@ mod test { dirval!(PaymentMethod = PayLater), ]), memo, + &mut CycleCheck::new(), + None, ); assert!(result.is_err()); @@ -1278,10 +613,10 @@ mod test { #[test] fn test_all_aggregator_mandatory_failure() { - let graph = knowledge! {crate + let graph = knowledge! { PaymentMethod(Card) & PaymentMethod(not Wallet) ->> CaptureMethod(Automatic); }; - let mut memo = Memoization::new(); + let mut memo = cgraph::Memoization::new(); let result = graph.key_value_analysis( dirval!(CaptureMethod = Automatic), &AnalysisContext::from_dir_values([ @@ -1289,13 +624,15 @@ mod test { dirval!(PaymentMethod = PayLater), ]), &mut memo, + &mut CycleCheck::new(), + None, ); assert!(matches!( *Weak::upgrade(&result.unwrap_err().get_analysis_trace().unwrap()) .expect("Expected Arc"), - AnalysisTrace::Value { - predecessors: Some(ValueTracePredecessor::Mandatory(_)), + cgraph::AnalysisTrace::Value { + predecessors: Some(cgraph::error::ValueTracePredecessor::Mandatory(_)), .. } )); @@ -1303,10 +640,10 @@ mod test { #[test] fn test_in_aggregator_success() { - let graph = knowledge! {crate + let graph = knowledge! { PaymentMethod(in [Card, Wallet]) -> CaptureMethod(Automatic); }; - let memo = &mut Memoization::new(); + let memo = &mut cgraph::Memoization::new(); let result = graph.key_value_analysis( dirval!(CaptureMethod = Automatic), &AnalysisContext::from_dir_values([ @@ -1315,6 +652,8 @@ mod test { dirval!(PaymentMethod = Wallet), ]), memo, + &mut CycleCheck::new(), + None, ); assert!(result.is_ok()); @@ -1322,10 +661,10 @@ mod test { #[test] fn test_in_aggregator_failure() { - let graph = knowledge! {crate + let graph = knowledge! { PaymentMethod(in [Card, Wallet]) -> CaptureMethod(Automatic); }; - let memo = &mut Memoization::new(); + let memo = &mut cgraph::Memoization::new(); let result = graph.key_value_analysis( dirval!(CaptureMethod = Automatic), &AnalysisContext::from_dir_values([ @@ -1335,6 +674,8 @@ mod test { dirval!(PaymentMethod = PayLater), ]), memo, + &mut CycleCheck::new(), + None, ); assert!(result.is_err()); @@ -1342,10 +683,10 @@ mod test { #[test] fn test_not_in_aggregator_success() { - let graph = knowledge! {crate + let graph = knowledge! { PaymentMethod(not in [Card, Wallet]) ->> CaptureMethod(Automatic); }; - let memo = &mut Memoization::new(); + let memo = &mut cgraph::Memoization::new(); let result = graph.key_value_analysis( dirval!(CaptureMethod = Automatic), &AnalysisContext::from_dir_values([ @@ -1354,6 +695,8 @@ mod test { dirval!(PaymentMethod = BankRedirect), ]), memo, + &mut CycleCheck::new(), + None, ); assert!(result.is_ok()); @@ -1361,10 +704,10 @@ mod test { #[test] fn test_not_in_aggregator_failure() { - let graph = knowledge! {crate + let graph = knowledge! { PaymentMethod(not in [Card, Wallet]) ->> CaptureMethod(Automatic); }; - let memo = &mut Memoization::new(); + let memo = &mut cgraph::Memoization::new(); let result = graph.key_value_analysis( dirval!(CaptureMethod = Automatic), &AnalysisContext::from_dir_values([ @@ -1374,6 +717,8 @@ mod test { dirval!(PaymentMethod = Card), ]), memo, + &mut CycleCheck::new(), + None, ); assert!(result.is_err()); @@ -1381,10 +726,10 @@ mod test { #[test] fn test_in_aggregator_failure_trace() { - let graph = knowledge! {crate + let graph = knowledge! { PaymentMethod(in [Card, Wallet]) ->> CaptureMethod(Automatic); }; - let memo = &mut Memoization::new(); + let memo = &mut cgraph::Memoization::new(); let result = graph.key_value_analysis( dirval!(CaptureMethod = Automatic), &AnalysisContext::from_dir_values([ @@ -1394,10 +739,12 @@ mod test { dirval!(PaymentMethod = PayLater), ]), memo, + &mut CycleCheck::new(), + None, ); - if let AnalysisTrace::Value { - predecessors: Some(ValueTracePredecessor::Mandatory(agg_error)), + if let cgraph::AnalysisTrace::Value { + predecessors: Some(cgraph::error::ValueTracePredecessor::Mandatory(agg_error)), .. } = Weak::upgrade(&result.unwrap_err().get_analysis_trace().unwrap()) .expect("Expected arc") @@ -1405,7 +752,7 @@ mod test { { assert!(matches!( *Weak::upgrade(agg_error.deref()).expect("Expected Arc"), - AnalysisTrace::InAggregation { + cgraph::AnalysisTrace::InAggregation { found: Some(dir::DirValue::PaymentMethod(enums::PaymentMethod::PayLater)), .. } @@ -1416,43 +763,43 @@ mod test { } #[test] - fn _test_memoization_in_kgraph() { - let mut builder = KnowledgeGraphBuilder::new(); + fn test_memoization_in_kgraph() { + let mut builder = cgraph::ConstraintGraphBuilder::new(); let _node_1 = builder.make_value_node( - NodeValue::Value(dir::DirValue::PaymentMethod(enums::PaymentMethod::Wallet)), + cgraph::NodeValue::Value(dir::DirValue::PaymentMethod(enums::PaymentMethod::Wallet)), None, - Vec::new(), None::<()>, ); let _node_2 = builder.make_value_node( - NodeValue::Value(dir::DirValue::BillingCountry(enums::BillingCountry::India)), + cgraph::NodeValue::Value(dir::DirValue::BillingCountry(enums::BillingCountry::India)), None, - Vec::new(), None::<()>, ); let _node_3 = builder.make_value_node( - NodeValue::Value(dir::DirValue::BusinessCountry( + cgraph::NodeValue::Value(dir::DirValue::BusinessCountry( enums::BusinessCountry::UnitedStatesOfAmerica, )), None, - Vec::new(), None::<()>, ); - let mut memo = Memoization::new(); + let mut memo = cgraph::Memoization::new(); + let mut cycle_map = CycleCheck::new(); let _edge_1 = builder .make_edge( - _node_1.expect("node1 constructtion failed"), - _node_2.clone().expect("node2 construction failed"), - Strength::Strong, - Relation::Positive, + _node_1, + _node_2, + cgraph::Strength::Strong, + cgraph::Relation::Positive, + None::, ) .expect("Failed to make an edge"); let _edge_2 = builder .make_edge( - _node_2.expect("node2 construction failed"), - _node_3.clone().expect("node3 construction failed"), - Strength::Strong, - Relation::Positive, + _node_2, + _node_3, + cgraph::Strength::Strong, + cgraph::Relation::Positive, + None::, ) .expect("Failed to an edge"); let graph = builder.build(); @@ -1464,15 +811,263 @@ mod test { dirval!(BusinessCountry = UnitedStatesOfAmerica), ]), &mut memo, + &mut cycle_map, + None, ); let _answer = memo - .0 .get(&( - _node_3.expect("node3 construction failed"), - Relation::Positive, - Strength::Strong, + _node_3, + cgraph::Relation::Positive, + cgraph::Strength::Strong, )) .expect("Memoization not workng"); matches!(_answer, Ok(())); } + + #[test] + fn test_cycle_resolution_in_graph() { + let mut builder = cgraph::ConstraintGraphBuilder::new(); + let _node_1 = builder.make_value_node( + cgraph::NodeValue::Value(dir::DirValue::PaymentMethod(enums::PaymentMethod::Wallet)), + None, + None::<()>, + ); + let _node_2 = builder.make_value_node( + cgraph::NodeValue::Value(dir::DirValue::PaymentMethod(enums::PaymentMethod::Card)), + None, + None::<()>, + ); + let mut memo = cgraph::Memoization::new(); + let mut cycle_map = cgraph::CycleCheck::new(); + let _edge_1 = builder + .make_edge( + _node_1, + _node_2, + cgraph::Strength::Weak, + cgraph::Relation::Positive, + None::, + ) + .expect("Failed to make an edge"); + let _edge_2 = builder + .make_edge( + _node_2, + _node_1, + cgraph::Strength::Weak, + cgraph::Relation::Positive, + None::, + ) + .expect("Failed to an edge"); + let graph = builder.build(); + let _result = graph.key_value_analysis( + dirval!(PaymentMethod = Wallet), + &AnalysisContext::from_dir_values([ + dirval!(PaymentMethod = Wallet), + dirval!(PaymentMethod = Card), + ]), + &mut memo, + &mut cycle_map, + None, + ); + + assert!(_result.is_ok()); + } + + #[test] + fn test_cycle_resolution_in_graph1() { + let mut builder = cgraph::ConstraintGraphBuilder::new(); + let _node_1 = builder.make_value_node( + cgraph::NodeValue::Value(dir::DirValue::CaptureMethod( + enums::CaptureMethod::Automatic, + )), + None, + None::<()>, + ); + + let _node_2 = builder.make_value_node( + cgraph::NodeValue::Value(dir::DirValue::PaymentMethod(enums::PaymentMethod::Card)), + None, + None::<()>, + ); + let _node_3 = builder.make_value_node( + cgraph::NodeValue::Value(dir::DirValue::PaymentMethod(enums::PaymentMethod::Wallet)), + None, + None::<()>, + ); + let mut memo = cgraph::Memoization::new(); + let mut cycle_map = cgraph::CycleCheck::new(); + + let _edge_1 = builder + .make_edge( + _node_1, + _node_2, + cgraph::Strength::Weak, + cgraph::Relation::Positive, + None::, + ) + .expect("Failed to make an edge"); + let _edge_2 = builder + .make_edge( + _node_1, + _node_3, + cgraph::Strength::Weak, + cgraph::Relation::Positive, + None::, + ) + .expect("Failed to make an edge"); + let _edge_3 = builder + .make_edge( + _node_2, + _node_1, + cgraph::Strength::Weak, + cgraph::Relation::Positive, + None::, + ) + .expect("Failed to make an edge"); + let _edge_4 = builder + .make_edge( + _node_3, + _node_1, + cgraph::Strength::Strong, + cgraph::Relation::Positive, + None::, + ) + .expect("Failed to make an edge"); + + let graph = builder.build(); + let _result = graph.key_value_analysis( + dirval!(CaptureMethod = Automatic), + &AnalysisContext::from_dir_values([ + dirval!(PaymentMethod = Card), + dirval!(PaymentMethod = Wallet), + dirval!(CaptureMethod = Automatic), + ]), + &mut memo, + &mut cycle_map, + None, + ); + + assert!(_result.is_ok()); + } + + #[test] + fn test_cycle_resolution_in_graph2() { + let mut builder = cgraph::ConstraintGraphBuilder::new(); + let _node_0 = builder.make_value_node( + cgraph::NodeValue::Value(dir::DirValue::BillingCountry( + enums::BillingCountry::Afghanistan, + )), + None, + None::<()>, + ); + + let _node_1 = builder.make_value_node( + cgraph::NodeValue::Value(dir::DirValue::CaptureMethod( + enums::CaptureMethod::Automatic, + )), + None, + None::<()>, + ); + + let _node_2 = builder.make_value_node( + cgraph::NodeValue::Value(dir::DirValue::PaymentMethod(enums::PaymentMethod::Card)), + None, + None::<()>, + ); + let _node_3 = builder.make_value_node( + cgraph::NodeValue::Value(dir::DirValue::PaymentMethod(enums::PaymentMethod::Wallet)), + None, + None::<()>, + ); + + let _node_4 = builder.make_value_node( + cgraph::NodeValue::Value(dir::DirValue::PaymentCurrency(enums::PaymentCurrency::USD)), + None, + None::<()>, + ); + + let mut memo = cgraph::Memoization::new(); + let mut cycle_map = cgraph::CycleCheck::new(); + + let _edge_1 = builder + .make_edge( + _node_0, + _node_1, + cgraph::Strength::Weak, + cgraph::Relation::Positive, + None::, + ) + .expect("Failed to make an edge"); + let _edge_2 = builder + .make_edge( + _node_1, + _node_2, + cgraph::Strength::Normal, + cgraph::Relation::Positive, + None::, + ) + .expect("Failed to make an edge"); + let _edge_3 = builder + .make_edge( + _node_1, + _node_3, + cgraph::Strength::Weak, + cgraph::Relation::Positive, + None::, + ) + .expect("Failed to make an edge"); + let _edge_4 = builder + .make_edge( + _node_3, + _node_4, + cgraph::Strength::Normal, + cgraph::Relation::Positive, + None::, + ) + .expect("Failed to make an edge"); + let _edge_5 = builder + .make_edge( + _node_2, + _node_4, + cgraph::Strength::Normal, + cgraph::Relation::Positive, + None::, + ) + .expect("Failed to make an edge"); + + let _edge_6 = builder + .make_edge( + _node_4, + _node_1, + cgraph::Strength::Normal, + cgraph::Relation::Positive, + None::, + ) + .expect("Failed to make an edge"); + let _edge_7 = builder + .make_edge( + _node_4, + _node_0, + cgraph::Strength::Normal, + cgraph::Relation::Positive, + None::, + ) + .expect("Failed to make an edge"); + + let graph = builder.build(); + let _result = graph.key_value_analysis( + dirval!(BillingCountry = Afghanistan), + &AnalysisContext::from_dir_values([ + dirval!(PaymentCurrency = USD), + dirval!(PaymentMethod = Card), + dirval!(PaymentMethod = Wallet), + dirval!(CaptureMethod = Automatic), + dirval!(BillingCountry = Afghanistan), + ]), + &mut memo, + &mut cycle_map, + None, + ); + + assert!(_result.is_ok()); + } } diff --git a/crates/euclid/src/dssa/truth.rs b/crates/euclid/src/dssa/truth.rs index 17e6e728e68..388180eed5b 100644 --- a/crates/euclid/src/dssa/truth.rs +++ b/crates/euclid/src/dssa/truth.rs @@ -1,29 +1,30 @@ use euclid_macros::knowledge; use once_cell::sync::Lazy; -use crate::dssa::graph; +use crate::{dssa::graph::euclid_graph_prelude, frontend::dir}; -pub static ANALYSIS_GRAPH: Lazy> = Lazy::new(|| { - knowledge! {crate - // Payment Method should be `Card` for a CardType to be present - PaymentMethod(Card) ->> CardType(any); +pub static ANALYSIS_GRAPH: Lazy> = + Lazy::new(|| { + knowledge! { + // Payment Method should be `Card` for a CardType to be present + PaymentMethod(Card) ->> CardType(any); - // Payment Method should be `PayLater` for a PayLaterType to be present - PaymentMethod(PayLater) ->> PayLaterType(any); + // Payment Method should be `PayLater` for a PayLaterType to be present + PaymentMethod(PayLater) ->> PayLaterType(any); - // Payment Method should be `Wallet` for a WalletType to be present - PaymentMethod(Wallet) ->> WalletType(any); + // Payment Method should be `Wallet` for a WalletType to be present + PaymentMethod(Wallet) ->> WalletType(any); - // Payment Method should be `BankRedirect` for a BankRedirectType to - // be present - PaymentMethod(BankRedirect) ->> BankRedirectType(any); + // Payment Method should be `BankRedirect` for a BankRedirectType to + // be present + PaymentMethod(BankRedirect) ->> BankRedirectType(any); - // Payment Method should be `BankTransfer` for a BankTransferType to - // be present - PaymentMethod(BankTransfer) ->> BankTransferType(any); + // Payment Method should be `BankTransfer` for a BankTransferType to + // be present + PaymentMethod(BankTransfer) ->> BankTransferType(any); - // Payment Method should be `GiftCard` for a GiftCardType to - // be present - PaymentMethod(GiftCard) ->> GiftCardType(any); - } -}); + // Payment Method should be `GiftCard` for a GiftCardType to + // be present + PaymentMethod(GiftCard) ->> GiftCardType(any); + } + }); diff --git a/crates/euclid/src/dssa/types.rs b/crates/euclid/src/dssa/types.rs index 4070e0825ef..df54de2dd99 100644 --- a/crates/euclid/src/dssa/types.rs +++ b/crates/euclid/src/dssa/types.rs @@ -140,7 +140,10 @@ pub enum AnalysisErrorType { negation_metadata: Metadata, }, #[error("Graph analysis error: {0:#?}")] - GraphAnalysis(graph::AnalysisError, graph::Memoization), + GraphAnalysis( + graph::AnalysisError, + hyperswitch_constraint_graph::Memoization, + ), #[error("State machine error")] StateMachine(dssa::state_machine::StateMachineError), #[error("Unsupported program key '{0}'")] diff --git a/crates/euclid/src/lib.rs b/crates/euclid/src/lib.rs index d64297437ae..261b3dc02d6 100644 --- a/crates/euclid/src/lib.rs +++ b/crates/euclid/src/lib.rs @@ -4,4 +4,3 @@ pub mod dssa; pub mod enums; pub mod frontend; pub mod types; -pub mod utils; diff --git a/crates/euclid/src/utils.rs b/crates/euclid/src/utils.rs deleted file mode 100644 index e8cb7901f0d..00000000000 --- a/crates/euclid/src/utils.rs +++ /dev/null @@ -1,3 +0,0 @@ -pub mod dense_map; - -pub use dense_map::{DenseMap, EntityId}; diff --git a/crates/euclid_macros/src/inner/knowledge.rs b/crates/euclid_macros/src/inner/knowledge.rs index 9f33a6871c5..a9c453b42c6 100644 --- a/crates/euclid_macros/src/inner/knowledge.rs +++ b/crates/euclid_macros/src/inner/knowledge.rs @@ -329,19 +329,17 @@ impl ToString for Scope { #[derive(Clone)] struct Program { rules: Vec>, - scope: Scope, } impl Parse for Program { fn parse(input: syn::parse::ParseStream<'_>) -> syn::Result { - let scope: Scope = input.parse()?; let mut rules: Vec> = Vec::new(); while !input.is_empty() { rules.push(Rc::new(input.parse::()?)); } - Ok(Self { rules, scope }) + Ok(Self { rules }) } } @@ -502,12 +500,12 @@ impl GenContext { let key = format_ident!("{}", &atom.key); let the_value = match &atom.value { ValueType::Any => quote! { - NodeValue::Key(DirKey::new(DirKeyKind::#key,None)) + cgraph::NodeValue::Key(DirKey::new(DirKeyKind::#key,None)) }, ValueType::EnumVariant(variant) => { let variant = format_ident!("{}", variant); quote! { - NodeValue::Value(DirValue::#key(#key::#variant)) + cgraph::NodeValue::Value(DirValue::#key(#key::#variant)) } } ValueType::Number { number, comparison } => { @@ -530,7 +528,7 @@ impl GenContext { }; quote! { - NodeValue::Value(DirValue::#key(NumValue { + cgraph::NodeValue::Value(DirValue::#key(NumValue { number: #number, refinement: #comp_type, })) @@ -539,7 +537,7 @@ impl GenContext { }; let compiled = quote! { - let #identifier = graph.make_value_node(#the_value, None, Vec::new(), None::<()>).expect("NodeId derivation failed"); + let #identifier = graph.make_value_node(#the_value, None, None::<()>); }; tokens.extend(compiled); @@ -581,7 +579,6 @@ impl GenContext { Vec::from_iter([#(#values_tokens),*]), None, None::<()>, - Vec::new(), ).expect("Failed to make In aggregator"); }; @@ -606,7 +603,7 @@ impl GenContext { for (from_node, relation) in &node_details { let relation = format_ident!("{}", relation.to_string()); tokens.extend(quote! { - graph.make_edge(#from_node, #rhs_ident, Strength::#strength, Relation::#relation) + graph.make_edge(#from_node, #rhs_ident, cgraph::Strength::#strength, cgraph::Relation::#relation, None::) .expect("Failed to make edge"); }); } @@ -614,16 +611,18 @@ impl GenContext { let mut all_agg_nodes: Vec = Vec::with_capacity(node_details.len()); for (from_node, relation) in &node_details { let relation = format_ident!("{}", relation.to_string()); - all_agg_nodes.push(quote! { (#from_node, Relation::#relation, Strength::Strong) }); + all_agg_nodes.push( + quote! { (#from_node, cgraph::Relation::#relation, cgraph::Strength::Strong) }, + ); } let strength = format_ident!("{}", rule.strength.to_string()); let (agg_node_ident, _) = self.next_node_ident(); tokens.extend(quote! { - let #agg_node_ident = graph.make_all_aggregator(&[#(#all_agg_nodes),*], None, None::<()>, Vec::new()) + let #agg_node_ident = graph.make_all_aggregator(&[#(#all_agg_nodes),*], None, None::<()>, None) .expect("Failed to make all aggregator node"); - graph.make_edge(#agg_node_ident, #rhs_ident, Strength::#strength, Relation::Positive) + graph.make_edge(#agg_node_ident, #rhs_ident, cgraph::Strength::#strength, cgraph::Relation::Positive, None::) .expect("Failed to create all aggregator edge"); }); @@ -638,21 +637,10 @@ impl GenContext { self.compile_rule(rule, &mut tokens)?; } - let scope = match &program.scope { - Scope::Crate => quote! { crate }, - Scope::Extern => quote! { euclid }, - }; - let compiled = quote! {{ - use #scope::{ - dssa::graph::*, - types::*, - frontend::dir::{*, enums::*}, - }; - - use rustc_hash::{FxHashMap, FxHashSet}; + use euclid_graph_prelude::*; - let mut graph = KnowledgeGraphBuilder::new(); + let mut graph = cgraph::ConstraintGraphBuilder::new(); #tokens diff --git a/crates/euclid_wasm/Cargo.toml b/crates/euclid_wasm/Cargo.toml index 6f5d3ec9cc3..293940f27c4 100644 --- a/crates/euclid_wasm/Cargo.toml +++ b/crates/euclid_wasm/Cargo.toml @@ -23,6 +23,7 @@ payouts = ["api_models/payouts", "euclid/payouts"] [dependencies] api_models = { version = "0.1.0", path = "../api_models", package = "api_models" } +hyperswitch_constraint_graph = { version = "0.1.0", path = "../hyperswitch_constraint_graph" } currency_conversion = { version = "0.1.0", path = "../currency_conversion" } connector_configs = { version = "0.1.0", path = "../connector_configs" } euclid = { version = "0.1.0", path = "../euclid", features = [] } diff --git a/crates/euclid_wasm/src/lib.rs b/crates/euclid_wasm/src/lib.rs index 36af0dc2d23..4920243bcc0 100644 --- a/crates/euclid_wasm/src/lib.rs +++ b/crates/euclid_wasm/src/lib.rs @@ -20,11 +20,7 @@ use currency_conversion::{ }; use euclid::{ backend::{inputs, interpreter::InterpreterBackend, EuclidBackend}, - dssa::{ - self, analyzer, - graph::{self, Memoization}, - state_machine, truth, - }, + dssa::{self, analyzer, graph::CgraphExt, state_machine, truth}, frontend::{ ast, dir::{self, enums as dir_enums, EuclidDirFilter}, @@ -38,7 +34,7 @@ use crate::utils::JsResultExt; type JsResult = Result; struct SeedData<'a> { - kgraph: graph::KnowledgeGraph<'a>, + cgraph: hyperswitch_constraint_graph::ConstraintGraph<'a, dir::DirValue>, connectors: Vec, } @@ -98,11 +94,12 @@ pub fn seed_knowledge_graph(mcas: JsValue) -> JsResult { let mca_graph = kgraph_utils::mca::make_mca_graph(mcas).err_to_js()?; let analysis_graph = - graph::KnowledgeGraph::combine(&mca_graph, &truth::ANALYSIS_GRAPH).err_to_js()?; + hyperswitch_constraint_graph::ConstraintGraph::combine(&mca_graph, &truth::ANALYSIS_GRAPH) + .err_to_js()?; SEED_DATA .set(SeedData { - kgraph: analysis_graph, + cgraph: analysis_graph, connectors, }) .map_err(|_| "Knowledge Graph has been already seeded".to_string()) @@ -138,8 +135,12 @@ pub fn get_valid_connectors_for_rule(rule: JsValue) -> JsResult { // Standalone conjunctive context analysis to ensure the context itself is valid before // checking it against merchant's connectors seed_data - .kgraph - .perform_context_analysis(ctx, &mut Memoization::new()) + .cgraph + .perform_context_analysis( + ctx, + &mut hyperswitch_constraint_graph::Memoization::new(), + None, + ) .err_to_js()?; // Update conjunctive context and run analysis on all of merchant's connectors. @@ -150,9 +151,11 @@ pub fn get_valid_connectors_for_rule(rule: JsValue) -> JsResult { let ctx_val = dssa::types::ContextValue::assertion(choice, &dummy_meta); ctx.push(ctx_val); - let analysis_result = seed_data - .kgraph - .perform_context_analysis(ctx, &mut Memoization::new()); + let analysis_result = seed_data.cgraph.perform_context_analysis( + ctx, + &mut hyperswitch_constraint_graph::Memoization::new(), + None, + ); if analysis_result.is_err() { invalid_connectors.insert(conn.clone()); } @@ -171,7 +174,7 @@ pub fn get_valid_connectors_for_rule(rule: JsValue) -> JsResult { #[wasm_bindgen(js_name = analyzeProgram)] pub fn analyze_program(js_program: JsValue) -> JsResult { let program: ast::Program = serde_wasm_bindgen::from_value(js_program)?; - analyzer::analyze(program, SEED_DATA.get().map(|sd| &sd.kgraph)).err_to_js()?; + analyzer::analyze(program, SEED_DATA.get().map(|sd| &sd.cgraph)).err_to_js()?; Ok(JsValue::NULL) } diff --git a/crates/hyperswitch_constraint_graph/Cargo.toml b/crates/hyperswitch_constraint_graph/Cargo.toml new file mode 100644 index 00000000000..425855a05b0 --- /dev/null +++ b/crates/hyperswitch_constraint_graph/Cargo.toml @@ -0,0 +1,16 @@ +[package] +name = "hyperswitch_constraint_graph" +description = "Constraint Graph Framework for modeling Domain-Specific Constraints" +version = "0.1.0" +edition.workspace = true +rust-version.workspace = true + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +erased-serde = "0.3.28" +rustc-hash = "1.1.0" +serde = { version = "1.0.163", features = ["derive", "rc"] } +serde_json = "1.0.96" +strum = { version = "0.25", features = ["derive"] } +thiserror = "1.0.43" diff --git a/crates/hyperswitch_constraint_graph/src/builder.rs b/crates/hyperswitch_constraint_graph/src/builder.rs new file mode 100644 index 00000000000..c1343eff885 --- /dev/null +++ b/crates/hyperswitch_constraint_graph/src/builder.rs @@ -0,0 +1,283 @@ +use std::sync::Arc; + +use rustc_hash::{FxHashMap, FxHashSet}; + +use crate::{ + dense_map::DenseMap, + error::GraphError, + graph::ConstraintGraph, + types::{ + DomainId, DomainIdentifier, DomainInfo, Edge, EdgeId, Metadata, Node, NodeId, NodeType, + NodeValue, Relation, Strength, ValueNode, + }, +}; + +pub enum DomainIdOrIdentifier<'a> { + DomainId(DomainId), + DomainIdentifier(DomainIdentifier<'a>), +} + +impl<'a> From<&'a str> for DomainIdOrIdentifier<'a> { + fn from(value: &'a str) -> Self { + Self::DomainIdentifier(DomainIdentifier::new(value)) + } +} + +impl From for DomainIdOrIdentifier<'_> { + fn from(value: DomainId) -> Self { + Self::DomainId(value) + } +} + +pub struct ConstraintGraphBuilder<'a, V: ValueNode> { + domain: DenseMap>, + nodes: DenseMap>, + edges: DenseMap, + domain_identifier_map: FxHashMap, DomainId>, + value_map: FxHashMap, NodeId>, + edges_map: FxHashMap<(NodeId, NodeId, Option), EdgeId>, + node_info: DenseMap>, + node_metadata: DenseMap>>, +} + +#[allow(clippy::new_without_default)] +impl<'a, V> ConstraintGraphBuilder<'a, V> +where + V: ValueNode, +{ + pub fn new() -> Self { + Self { + domain: DenseMap::new(), + nodes: DenseMap::new(), + edges: DenseMap::new(), + domain_identifier_map: FxHashMap::default(), + value_map: FxHashMap::default(), + edges_map: FxHashMap::default(), + node_info: DenseMap::new(), + node_metadata: DenseMap::new(), + } + } + + pub fn build(self) -> ConstraintGraph<'a, V> { + ConstraintGraph { + domain: self.domain, + domain_identifier_map: self.domain_identifier_map, + nodes: self.nodes, + edges: self.edges, + value_map: self.value_map, + node_info: self.node_info, + node_metadata: self.node_metadata, + } + } + + fn retrieve_domain_from_identifier( + &self, + domain_ident: DomainIdentifier<'_>, + ) -> Result> { + self.domain_identifier_map + .get(&domain_ident) + .copied() + .ok_or(GraphError::DomainNotFound) + } + + pub fn make_domain( + &mut self, + domain_identifier: &'a str, + domain_description: &str, + ) -> Result> { + let domain_identifier = DomainIdentifier::new(domain_identifier); + Ok(self + .domain_identifier_map + .clone() + .get(&domain_identifier) + .map_or_else( + || { + let domain_id = self.domain.push(DomainInfo { + domain_identifier, + domain_description: domain_description.to_string(), + }); + self.domain_identifier_map + .insert(domain_identifier, domain_id); + domain_id + }, + |domain_id| *domain_id, + )) + } + + pub fn make_value_node( + &mut self, + value: NodeValue, + info: Option<&'static str>, + metadata: Option, + ) -> NodeId { + self.value_map.get(&value).copied().unwrap_or_else(|| { + let node_id = self.nodes.push(Node::new(NodeType::Value(value.clone()))); + let _node_info_id = self.node_info.push(info); + + let _node_metadata_id = self + .node_metadata + .push(metadata.map(|meta| -> Arc { Arc::new(meta) })); + + self.value_map.insert(value, node_id); + node_id + }) + } + + pub fn make_edge<'short, T: Into>>( + &mut self, + pred_id: NodeId, + succ_id: NodeId, + strength: Strength, + relation: Relation, + domain: Option, + ) -> Result> { + self.ensure_node_exists(pred_id)?; + self.ensure_node_exists(succ_id)?; + let domain_id = domain + .map(|d| match d.into() { + DomainIdOrIdentifier::DomainIdentifier(ident) => { + self.retrieve_domain_from_identifier(ident) + } + DomainIdOrIdentifier::DomainId(domain_id) => { + self.ensure_domain_exists(domain_id).map(|_| domain_id) + } + }) + .transpose()?; + self.edges_map + .get(&(pred_id, succ_id, domain_id)) + .copied() + .and_then(|edge_id| self.edges.get(edge_id).cloned().map(|edge| (edge_id, edge))) + .map_or_else( + || { + let edge_id = self.edges.push(Edge { + strength, + relation, + pred: pred_id, + succ: succ_id, + domain: domain_id, + }); + self.edges_map + .insert((pred_id, succ_id, domain_id), edge_id); + + let pred = self + .nodes + .get_mut(pred_id) + .ok_or(GraphError::NodeNotFound)?; + pred.succs.push(edge_id); + + let succ = self + .nodes + .get_mut(succ_id) + .ok_or(GraphError::NodeNotFound)?; + succ.preds.push(edge_id); + + Ok(edge_id) + }, + |(edge_id, edge)| { + if edge.strength == strength && edge.relation == relation { + Ok(edge_id) + } else { + Err(GraphError::ConflictingEdgeCreated) + } + }, + ) + } + + pub fn make_all_aggregator( + &mut self, + nodes: &[(NodeId, Relation, Strength)], + info: Option<&'static str>, + metadata: Option, + domain: Option<&str>, + ) -> Result> { + nodes + .iter() + .try_for_each(|(node_id, _, _)| self.ensure_node_exists(*node_id))?; + + let aggregator_id = self.nodes.push(Node::new(NodeType::AllAggregator)); + let _aggregator_info_id = self.node_info.push(info); + + let _node_metadata_id = self + .node_metadata + .push(metadata.map(|meta| -> Arc { Arc::new(meta) })); + + for (node_id, relation, strength) in nodes { + self.make_edge(*node_id, aggregator_id, *strength, *relation, domain)?; + } + + Ok(aggregator_id) + } + + pub fn make_any_aggregator( + &mut self, + nodes: &[(NodeId, Relation, Strength)], + info: Option<&'static str>, + metadata: Option, + domain: Option<&str>, + ) -> Result> { + nodes + .iter() + .try_for_each(|(node_id, _, _)| self.ensure_node_exists(*node_id))?; + + let aggregator_id = self.nodes.push(Node::new(NodeType::AnyAggregator)); + let _aggregator_info_id = self.node_info.push(info); + + let _node_metadata_id = self + .node_metadata + .push(metadata.map(|meta| -> Arc { Arc::new(meta) })); + + for (node_id, relation, strength) in nodes { + self.make_edge(*node_id, aggregator_id, *strength, *relation, domain)?; + } + + Ok(aggregator_id) + } + + pub fn make_in_aggregator( + &mut self, + values: Vec, + info: Option<&'static str>, + metadata: Option, + ) -> Result> { + let key = values + .first() + .ok_or(GraphError::NoInAggregatorValues)? + .get_key(); + + for val in &values { + if val.get_key() != key { + Err(GraphError::MalformedGraph { + reason: "Values for 'In' aggregator not of same key".to_string(), + })?; + } + } + let node_id = self + .nodes + .push(Node::new(NodeType::InAggregator(FxHashSet::from_iter( + values, + )))); + let _aggregator_info_id = self.node_info.push(info); + + let _node_metadata_id = self + .node_metadata + .push(metadata.map(|meta| -> Arc { Arc::new(meta) })); + + Ok(node_id) + } + + fn ensure_node_exists(&self, id: NodeId) -> Result<(), GraphError> { + if self.nodes.contains_key(id) { + Ok(()) + } else { + Err(GraphError::NodeNotFound) + } + } + + fn ensure_domain_exists(&self, id: DomainId) -> Result<(), GraphError> { + if self.domain.contains_key(id) { + Ok(()) + } else { + Err(GraphError::DomainNotFound) + } + } +} diff --git a/crates/euclid/src/utils/dense_map.rs b/crates/hyperswitch_constraint_graph/src/dense_map.rs similarity index 92% rename from crates/euclid/src/utils/dense_map.rs rename to crates/hyperswitch_constraint_graph/src/dense_map.rs index 8bd4487c77b..682833d65e6 100644 --- a/crates/euclid/src/utils/dense_map.rs +++ b/crates/hyperswitch_constraint_graph/src/dense_map.rs @@ -5,6 +5,24 @@ pub trait EntityId { fn with_id(id: usize) -> Self; } +macro_rules! impl_entity { + ($name:ident) => { + impl $crate::dense_map::EntityId for $name { + #[inline] + fn get_id(&self) -> usize { + self.0 + } + + #[inline] + fn with_id(id: usize) -> Self { + Self(id) + } + } + }; +} + +pub(crate) use impl_entity; + pub struct DenseMap { data: Vec, _marker: PhantomData, diff --git a/crates/hyperswitch_constraint_graph/src/error.rs b/crates/hyperswitch_constraint_graph/src/error.rs new file mode 100644 index 00000000000..cd2269de264 --- /dev/null +++ b/crates/hyperswitch_constraint_graph/src/error.rs @@ -0,0 +1,77 @@ +use std::sync::{Arc, Weak}; + +use crate::types::{Metadata, NodeValue, Relation, RelationResolution, ValueNode}; + +#[derive(Debug, Clone, serde::Serialize)] +#[serde(tag = "type", content = "predecessor", rename_all = "snake_case")] +pub enum ValueTracePredecessor { + Mandatory(Box>>), + OneOf(Vec>>), +} + +#[derive(Debug, Clone, serde::Serialize)] +#[serde(tag = "type", content = "trace", rename_all = "snake_case")] +pub enum AnalysisTrace { + Value { + value: NodeValue, + relation: Relation, + predecessors: Option>, + info: Option<&'static str>, + metadata: Option>, + }, + + AllAggregation { + unsatisfied: Vec>>, + info: Option<&'static str>, + metadata: Option>, + }, + + AnyAggregation { + unsatisfied: Vec>>, + info: Option<&'static str>, + metadata: Option>, + }, + + InAggregation { + expected: Vec, + found: Option, + relation: Relation, + info: Option<&'static str>, + metadata: Option>, + }, + Contradiction { + relation: RelationResolution, + }, +} + +#[derive(Debug, Clone, serde::Serialize, thiserror::Error)] +#[serde(tag = "type", content = "info", rename_all = "snake_case")] +pub enum GraphError { + #[error("An edge was not found in the graph")] + EdgeNotFound, + #[error("Attempted to create a conflicting edge between two nodes")] + ConflictingEdgeCreated, + #[error("Cycle detected in graph")] + CycleDetected, + #[error("Domain wasn't found in the Graph")] + DomainNotFound, + #[error("Malformed Graph: {reason}")] + MalformedGraph { reason: String }, + #[error("A node was not found in the graph")] + NodeNotFound, + #[error("A value node was not found: {0:#?}")] + ValueNodeNotFound(V), + #[error("No values provided for an 'in' aggregator node")] + NoInAggregatorValues, + #[error("Error during analysis: {0:#?}")] + AnalysisError(Weak>), +} + +impl GraphError { + pub fn get_analysis_trace(self) -> Result>, Self> { + match self { + Self::AnalysisError(trace) => Ok(trace), + _ => Err(self), + } + } +} diff --git a/crates/hyperswitch_constraint_graph/src/graph.rs b/crates/hyperswitch_constraint_graph/src/graph.rs new file mode 100644 index 00000000000..d0a98e19520 --- /dev/null +++ b/crates/hyperswitch_constraint_graph/src/graph.rs @@ -0,0 +1,587 @@ +use std::sync::{Arc, Weak}; + +use rustc_hash::{FxHashMap, FxHashSet}; + +use crate::{ + builder, + dense_map::DenseMap, + error::{self, AnalysisTrace, GraphError}, + types::{ + CheckingContext, CycleCheck, DomainId, DomainIdentifier, DomainInfo, Edge, EdgeId, + Memoization, Metadata, Node, NodeId, NodeType, NodeValue, Relation, RelationResolution, + Strength, ValueNode, + }, +}; + +struct CheckNodeContext<'a, V: ValueNode, C: CheckingContext> { + ctx: &'a C, + node: &'a Node, + node_id: NodeId, + relation: Relation, + strength: Strength, + memo: &'a mut Memoization, + cycle_map: &'a mut CycleCheck, + domains: Option<&'a [DomainId]>, +} + +pub struct ConstraintGraph<'a, V: ValueNode> { + pub domain: DenseMap>, + pub domain_identifier_map: FxHashMap, DomainId>, + pub nodes: DenseMap>, + pub edges: DenseMap, + pub value_map: FxHashMap, NodeId>, + pub node_info: DenseMap>, + pub node_metadata: DenseMap>>, +} + +impl<'a, V> ConstraintGraph<'a, V> +where + V: ValueNode, +{ + fn get_predecessor_edges_by_domain( + &self, + node_id: NodeId, + domains: Option<&[DomainId]>, + ) -> Result, GraphError> { + let node = self.nodes.get(node_id).ok_or(GraphError::NodeNotFound)?; + let mut final_list = Vec::new(); + for &pred in &node.preds { + let edge = self.edges.get(pred).ok_or(GraphError::EdgeNotFound)?; + if let Some((domain_id, domains)) = edge.domain.zip(domains) { + if domains.contains(&domain_id) { + final_list.push(edge); + } + } else if edge.domain.is_none() { + final_list.push(edge); + } + } + + Ok(final_list) + } + + #[allow(clippy::too_many_arguments)] + pub fn check_node( + &self, + ctx: &C, + node_id: NodeId, + relation: Relation, + strength: Strength, + memo: &mut Memoization, + cycle_map: &mut CycleCheck, + domains: Option<&[&str]>, + ) -> Result<(), GraphError> + where + C: CheckingContext, + { + let domains = domains + .map(|domain_idents| { + domain_idents + .iter() + .map(|domain_ident| { + self.domain_identifier_map + .get(&DomainIdentifier::new(domain_ident)) + .copied() + .ok_or(GraphError::DomainNotFound) + }) + .collect::, _>>() + }) + .transpose()?; + + self.check_node_inner( + ctx, + node_id, + relation, + strength, + memo, + cycle_map, + domains.as_deref(), + ) + } + + #[allow(clippy::too_many_arguments)] + pub fn check_node_inner( + &self, + ctx: &C, + node_id: NodeId, + relation: Relation, + strength: Strength, + memo: &mut Memoization, + cycle_map: &mut CycleCheck, + domains: Option<&[DomainId]>, + ) -> Result<(), GraphError> + where + C: CheckingContext, + { + let node = self.nodes.get(node_id).ok_or(GraphError::NodeNotFound)?; + + if let Some(already_memo) = memo.get(&(node_id, relation, strength)) { + already_memo + .clone() + .map_err(|err| GraphError::AnalysisError(Arc::downgrade(&err))) + } else if let Some((initial_strength, initial_relation)) = cycle_map.get(&node_id).cloned() + { + let strength_relation = Strength::get_resolved_strength(initial_strength, strength); + let relation_resolve = + RelationResolution::get_resolved_relation(initial_relation, relation.into()); + cycle_map.entry(node_id).and_modify(|value| { + value.0 = strength_relation; + value.1 = relation_resolve + }); + Ok(()) + } else { + let check_node_context = CheckNodeContext { + node, + node_id, + relation, + strength, + memo, + cycle_map, + ctx, + domains, + }; + match &node.node_type { + NodeType::AllAggregator => self.validate_all_aggregator(check_node_context), + + NodeType::AnyAggregator => self.validate_any_aggregator(check_node_context), + + NodeType::InAggregator(expected) => { + self.validate_in_aggregator(check_node_context, expected) + } + NodeType::Value(val) => self.validate_value_node(check_node_context, val), + } + } + } + + fn validate_all_aggregator( + &self, + vald: CheckNodeContext<'_, V, C>, + ) -> Result<(), GraphError> + where + C: CheckingContext, + { + let mut unsatisfied = Vec::>>::new(); + + for edge in self.get_predecessor_edges_by_domain(vald.node_id, vald.domains)? { + vald.cycle_map + .insert(vald.node_id, (vald.strength, vald.relation.into())); + if let Err(e) = self.check_node_inner( + vald.ctx, + edge.pred, + edge.relation, + edge.strength, + vald.memo, + vald.cycle_map, + vald.domains, + ) { + unsatisfied.push(e.get_analysis_trace()?); + } + if let Some((_resolved_strength, resolved_relation)) = + vald.cycle_map.remove(&vald.node_id) + { + if resolved_relation == RelationResolution::Contradiction { + let err = Arc::new(AnalysisTrace::Contradiction { + relation: resolved_relation, + }); + vald.memo.insert( + (vald.node_id, vald.relation, vald.strength), + Err(Arc::clone(&err)), + ); + return Err(GraphError::AnalysisError(Arc::downgrade(&err))); + } + } + } + + if !unsatisfied.is_empty() { + let err = Arc::new(AnalysisTrace::AllAggregation { + unsatisfied, + info: self.node_info.get(vald.node_id).cloned().flatten(), + metadata: self.node_metadata.get(vald.node_id).cloned().flatten(), + }); + + vald.memo.insert( + (vald.node_id, vald.relation, vald.strength), + Err(Arc::clone(&err)), + ); + Err(GraphError::AnalysisError(Arc::downgrade(&err))) + } else { + vald.memo + .insert((vald.node_id, vald.relation, vald.strength), Ok(())); + Ok(()) + } + } + + fn validate_any_aggregator( + &self, + vald: CheckNodeContext<'_, V, C>, + ) -> Result<(), GraphError> + where + C: CheckingContext, + { + let mut unsatisfied = Vec::>>::new(); + let mut matched_one = false; + + for edge in self.get_predecessor_edges_by_domain(vald.node_id, vald.domains)? { + vald.cycle_map + .insert(vald.node_id, (vald.strength, vald.relation.into())); + if let Err(e) = self.check_node_inner( + vald.ctx, + edge.pred, + edge.relation, + edge.strength, + vald.memo, + vald.cycle_map, + vald.domains, + ) { + unsatisfied.push(e.get_analysis_trace()?); + } else { + matched_one = true; + } + if let Some((_resolved_strength, resolved_relation)) = + vald.cycle_map.remove(&vald.node_id) + { + if resolved_relation == RelationResolution::Contradiction { + let err = Arc::new(AnalysisTrace::Contradiction { + relation: resolved_relation, + }); + vald.memo.insert( + (vald.node_id, vald.relation, vald.strength), + Err(Arc::clone(&err)), + ); + + return Err(GraphError::AnalysisError(Arc::downgrade(&err))); + } + } + } + + if matched_one || vald.node.preds.is_empty() { + vald.memo + .insert((vald.node_id, vald.relation, vald.strength), Ok(())); + Ok(()) + } else { + let err = Arc::new(AnalysisTrace::AnyAggregation { + unsatisfied: unsatisfied.clone(), + info: self.node_info.get(vald.node_id).cloned().flatten(), + metadata: self.node_metadata.get(vald.node_id).cloned().flatten(), + }); + + vald.memo.insert( + (vald.node_id, vald.relation, vald.strength), + Err(Arc::clone(&err)), + ); + Err(GraphError::AnalysisError(Arc::downgrade(&err))) + } + } + + fn validate_in_aggregator( + &self, + vald: CheckNodeContext<'_, V, C>, + expected: &FxHashSet, + ) -> Result<(), GraphError> + where + C: CheckingContext, + { + let the_key = expected + .iter() + .next() + .ok_or_else(|| GraphError::MalformedGraph { + reason: "An OnlyIn aggregator node must have at least one expected value" + .to_string(), + })? + .get_key(); + + let ctx_vals = if let Some(vals) = vald.ctx.get_values_by_key(&the_key) { + vals + } else { + return if let Strength::Weak = vald.strength { + vald.memo + .insert((vald.node_id, vald.relation, vald.strength), Ok(())); + Ok(()) + } else { + let err = Arc::new(AnalysisTrace::InAggregation { + expected: expected.iter().cloned().collect(), + found: None, + relation: vald.relation, + info: self.node_info.get(vald.node_id).cloned().flatten(), + metadata: self.node_metadata.get(vald.node_id).cloned().flatten(), + }); + + vald.memo.insert( + (vald.node_id, vald.relation, vald.strength), + Err(Arc::clone(&err)), + ); + Err(GraphError::AnalysisError(Arc::downgrade(&err))) + }; + }; + + let relation_bool: bool = vald.relation.into(); + for ctx_value in ctx_vals { + if expected.contains(&ctx_value) != relation_bool { + let err = Arc::new(AnalysisTrace::InAggregation { + expected: expected.iter().cloned().collect(), + found: Some(ctx_value.clone()), + relation: vald.relation, + info: self.node_info.get(vald.node_id).cloned().flatten(), + metadata: self.node_metadata.get(vald.node_id).cloned().flatten(), + }); + + vald.memo.insert( + (vald.node_id, vald.relation, vald.strength), + Err(Arc::clone(&err)), + ); + Err(GraphError::AnalysisError(Arc::downgrade(&err)))?; + } + } + + vald.memo + .insert((vald.node_id, vald.relation, vald.strength), Ok(())); + Ok(()) + } + + fn validate_value_node( + &self, + vald: CheckNodeContext<'_, V, C>, + val: &NodeValue, + ) -> Result<(), GraphError> + where + C: CheckingContext, + { + let mut errors = Vec::>>::new(); + let mut matched_one = false; + + self.context_analysis( + vald.node_id, + vald.relation, + vald.strength, + vald.ctx, + val, + vald.memo, + )?; + + for edge in self.get_predecessor_edges_by_domain(vald.node_id, vald.domains)? { + vald.cycle_map + .insert(vald.node_id, (vald.strength, vald.relation.into())); + let result = self.check_node_inner( + vald.ctx, + edge.pred, + edge.relation, + edge.strength, + vald.memo, + vald.cycle_map, + vald.domains, + ); + + if let Some((resolved_strength, resolved_relation)) = + vald.cycle_map.remove(&vald.node_id) + { + if resolved_relation == RelationResolution::Contradiction { + let err = Arc::new(AnalysisTrace::Contradiction { + relation: resolved_relation, + }); + vald.memo.insert( + (vald.node_id, vald.relation, vald.strength), + Err(Arc::clone(&err)), + ); + return Err(GraphError::AnalysisError(Arc::downgrade(&err))); + } else if resolved_strength != vald.strength { + self.context_analysis( + vald.node_id, + vald.relation, + resolved_strength, + vald.ctx, + val, + vald.memo, + )? + } + } + match (edge.strength, result) { + (Strength::Strong, Err(trace)) => { + let err = Arc::new(AnalysisTrace::Value { + value: val.clone(), + relation: vald.relation, + info: self.node_info.get(vald.node_id).cloned().flatten(), + metadata: self.node_metadata.get(vald.node_id).cloned().flatten(), + predecessors: Some(error::ValueTracePredecessor::Mandatory(Box::new( + trace.get_analysis_trace()?, + ))), + }); + vald.memo.insert( + (vald.node_id, vald.relation, vald.strength), + Err(Arc::clone(&err)), + ); + Err(GraphError::AnalysisError(Arc::downgrade(&err)))?; + } + + (Strength::Strong, Ok(_)) => { + matched_one = true; + } + + (Strength::Normal | Strength::Weak, Err(trace)) => { + errors.push(trace.get_analysis_trace()?); + } + + (Strength::Normal | Strength::Weak, Ok(_)) => { + matched_one = true; + } + } + } + + if matched_one || vald.node.preds.is_empty() { + vald.memo + .insert((vald.node_id, vald.relation, vald.strength), Ok(())); + Ok(()) + } else { + let err = Arc::new(AnalysisTrace::Value { + value: val.clone(), + relation: vald.relation, + info: self.node_info.get(vald.node_id).cloned().flatten(), + metadata: self.node_metadata.get(vald.node_id).cloned().flatten(), + predecessors: Some(error::ValueTracePredecessor::OneOf(errors.clone())), + }); + + vald.memo.insert( + (vald.node_id, vald.relation, vald.strength), + Err(Arc::clone(&err)), + ); + Err(GraphError::AnalysisError(Arc::downgrade(&err))) + } + } + + fn context_analysis( + &self, + node_id: NodeId, + relation: Relation, + strength: Strength, + ctx: &C, + val: &NodeValue, + memo: &mut Memoization, + ) -> Result<(), GraphError> + where + C: CheckingContext, + { + let in_context = ctx.check_presence(val, strength); + let relation_bool: bool = relation.into(); + if in_context != relation_bool { + let err = Arc::new(AnalysisTrace::Value { + value: val.clone(), + relation, + predecessors: None, + info: self.node_info.get(node_id).cloned().flatten(), + metadata: self.node_metadata.get(node_id).cloned().flatten(), + }); + memo.insert((node_id, relation, strength), Err(Arc::clone(&err))); + Err(GraphError::AnalysisError(Arc::downgrade(&err)))?; + } + if !relation_bool { + memo.insert((node_id, relation, strength), Ok(())); + return Ok(()); + } + Ok(()) + } + + pub fn combine<'b>(g1: &'b Self, g2: &'b Self) -> Result> { + let mut node_builder = builder::ConstraintGraphBuilder::new(); + let mut g1_old2new_id = DenseMap::::new(); + let mut g2_old2new_id = DenseMap::::new(); + let mut g1_old2new_domain_id = DenseMap::::new(); + let mut g2_old2new_domain_id = DenseMap::::new(); + + let add_domain = |node_builder: &mut builder::ConstraintGraphBuilder<'a, V>, + domain: DomainInfo<'a>| + -> Result> { + node_builder.make_domain( + domain.domain_identifier.into_inner(), + &domain.domain_description, + ) + }; + + let add_node = |node_builder: &mut builder::ConstraintGraphBuilder<'a, V>, + node: &Node| + -> Result> { + match &node.node_type { + NodeType::Value(node_value) => { + Ok(node_builder.make_value_node(node_value.clone(), None, None::<()>)) + } + + NodeType::AllAggregator => { + Ok(node_builder.make_all_aggregator(&[], None, None::<()>, None)?) + } + + NodeType::AnyAggregator => { + Ok(node_builder.make_any_aggregator(&[], None, None::<()>, None)?) + } + + NodeType::InAggregator(expected) => Ok(node_builder.make_in_aggregator( + expected.iter().cloned().collect(), + None, + None::<()>, + )?), + } + }; + + for (_old_domain_id, domain) in g1.domain.iter() { + let new_domain_id = add_domain(&mut node_builder, domain.clone())?; + g1_old2new_domain_id.push(new_domain_id); + } + + for (_old_domain_id, domain) in g2.domain.iter() { + let new_domain_id = add_domain(&mut node_builder, domain.clone())?; + g2_old2new_domain_id.push(new_domain_id); + } + + for (_old_node_id, node) in g1.nodes.iter() { + let new_node_id = add_node(&mut node_builder, node)?; + g1_old2new_id.push(new_node_id); + } + + for (_old_node_id, node) in g2.nodes.iter() { + let new_node_id = add_node(&mut node_builder, node)?; + g2_old2new_id.push(new_node_id); + } + + for edge in g1.edges.values() { + let new_pred_id = g1_old2new_id + .get(edge.pred) + .ok_or(GraphError::NodeNotFound)?; + let new_succ_id = g1_old2new_id + .get(edge.succ) + .ok_or(GraphError::NodeNotFound)?; + let domain_ident = edge + .domain + .map(|domain_id| g1.domain.get(domain_id).ok_or(GraphError::DomainNotFound)) + .transpose()? + .map(|domain| domain.domain_identifier); + + node_builder.make_edge( + *new_pred_id, + *new_succ_id, + edge.strength, + edge.relation, + domain_ident.as_deref(), + )?; + } + + for edge in g2.edges.values() { + let new_pred_id = g2_old2new_id + .get(edge.pred) + .ok_or(GraphError::NodeNotFound)?; + let new_succ_id = g2_old2new_id + .get(edge.succ) + .ok_or(GraphError::NodeNotFound)?; + let domain_ident = edge + .domain + .map(|domain_id| g2.domain.get(domain_id).ok_or(GraphError::DomainNotFound)) + .transpose()? + .map(|domain| domain.domain_identifier); + + node_builder.make_edge( + *new_pred_id, + *new_succ_id, + edge.strength, + edge.relation, + domain_ident.as_deref(), + )?; + } + + Ok(node_builder.build()) + } +} diff --git a/crates/hyperswitch_constraint_graph/src/lib.rs b/crates/hyperswitch_constraint_graph/src/lib.rs new file mode 100644 index 00000000000..ade9a64272f --- /dev/null +++ b/crates/hyperswitch_constraint_graph/src/lib.rs @@ -0,0 +1,13 @@ +pub mod builder; +mod dense_map; +pub mod error; +pub mod graph; +pub mod types; + +pub use builder::ConstraintGraphBuilder; +pub use error::{AnalysisTrace, GraphError}; +pub use graph::ConstraintGraph; +pub use types::{ + CheckingContext, CycleCheck, DomainId, DomainIdentifier, Edge, EdgeId, KeyNode, Memoization, + Node, NodeId, NodeValue, Relation, Strength, ValueNode, +}; diff --git a/crates/hyperswitch_constraint_graph/src/types.rs b/crates/hyperswitch_constraint_graph/src/types.rs new file mode 100644 index 00000000000..d1d14bd7e5c --- /dev/null +++ b/crates/hyperswitch_constraint_graph/src/types.rs @@ -0,0 +1,249 @@ +use std::{ + any::Any, + fmt, hash, + ops::{Deref, DerefMut}, + sync::Arc, +}; + +use rustc_hash::{FxHashMap, FxHashSet}; + +use crate::{dense_map::impl_entity, error::AnalysisTrace}; + +pub trait KeyNode: fmt::Debug + Clone + hash::Hash + serde::Serialize + PartialEq + Eq {} + +pub trait ValueNode: fmt::Debug + Clone + hash::Hash + serde::Serialize + PartialEq + Eq { + type Key: KeyNode; + + fn get_key(&self) -> Self::Key; +} + +#[derive(Debug, Clone, Copy, serde::Serialize, PartialEq, Eq, Hash)] +#[serde(transparent)] +pub struct NodeId(usize); + +impl_entity!(NodeId); + +#[derive(Debug)] +pub struct Node { + pub node_type: NodeType, + pub preds: Vec, + pub succs: Vec, +} + +impl Node { + pub(crate) fn new(node_type: NodeType) -> Self { + Self { + node_type, + preds: Vec::new(), + succs: Vec::new(), + } + } +} + +#[derive(Debug, PartialEq, Eq)] +pub enum NodeType { + AllAggregator, + AnyAggregator, + InAggregator(FxHashSet), + Value(NodeValue), +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash, serde::Serialize)] +#[serde(tag = "type", content = "value", rename_all = "snake_case")] +pub enum NodeValue { + Key(::Key), + Value(V), +} + +impl From for NodeValue { + fn from(value: V) -> Self { + Self::Value(value) + } +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +pub struct EdgeId(usize); + +impl_entity!(EdgeId); + +#[derive( + Debug, Clone, Copy, serde::Serialize, PartialEq, Eq, Hash, strum::Display, PartialOrd, Ord, +)] +pub enum Strength { + Weak, + Normal, + Strong, +} + +impl Strength { + pub fn get_resolved_strength(prev_strength: Self, curr_strength: Self) -> Self { + std::cmp::max(prev_strength, curr_strength) + } +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, strum::Display, serde::Serialize)] +#[serde(rename_all = "snake_case")] +pub enum Relation { + Positive, + Negative, +} + +impl From for bool { + fn from(value: Relation) -> Self { + matches!(value, Relation::Positive) + } +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, strum::Display, serde::Serialize)] +pub enum RelationResolution { + Positive, + Negative, + Contradiction, +} + +impl From for RelationResolution { + fn from(value: Relation) -> Self { + match value { + Relation::Positive => Self::Positive, + Relation::Negative => Self::Negative, + } + } +} + +impl RelationResolution { + pub fn get_resolved_relation(prev_relation: Self, curr_relation: Self) -> Self { + if prev_relation != curr_relation { + Self::Contradiction + } else { + curr_relation + } + } +} + +#[derive(Debug, Clone)] +pub struct Edge { + pub strength: Strength, + pub relation: Relation, + pub pred: NodeId, + pub succ: NodeId, + pub domain: Option, +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +pub struct DomainId(usize); + +impl_entity!(DomainId); + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +pub struct DomainIdentifier<'a>(&'a str); + +impl<'a> DomainIdentifier<'a> { + pub fn new(identifier: &'a str) -> Self { + Self(identifier) + } + + pub fn into_inner(&self) -> &'a str { + self.0 + } +} + +impl<'a> From<&'a str> for DomainIdentifier<'a> { + fn from(value: &'a str) -> Self { + Self(value) + } +} + +impl<'a> Deref for DomainIdentifier<'a> { + type Target = str; + + fn deref(&self) -> &'a Self::Target { + self.0 + } +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct DomainInfo<'a> { + pub domain_identifier: DomainIdentifier<'a>, + pub domain_description: String, +} + +pub trait CheckingContext { + type Value: ValueNode; + + fn from_node_values(vals: impl IntoIterator) -> Self + where + L: Into; + + fn check_presence(&self, value: &NodeValue, strength: Strength) -> bool; + + fn get_values_by_key( + &self, + expected: &::Key, + ) -> Option>; +} + +#[derive(Debug, Clone, serde::Serialize)] +pub struct Memoization( + #[allow(clippy::type_complexity)] + FxHashMap<(NodeId, Relation, Strength), Result<(), Arc>>>, +); + +impl Memoization { + pub fn new() -> Self { + Self(FxHashMap::default()) + } +} + +impl Default for Memoization { + #[inline] + fn default() -> Self { + Self::new() + } +} + +impl Deref for Memoization { + type Target = FxHashMap<(NodeId, Relation, Strength), Result<(), Arc>>>; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl DerefMut for Memoization { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.0 + } +} + +#[derive(Debug, Clone)] +pub struct CycleCheck(FxHashMap); +impl CycleCheck { + pub fn new() -> Self { + Self(FxHashMap::default()) + } +} + +impl Default for CycleCheck { + #[inline] + fn default() -> Self { + Self::new() + } +} + +impl Deref for CycleCheck { + type Target = FxHashMap; + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl DerefMut for CycleCheck { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.0 + } +} + +pub trait Metadata: erased_serde::Serialize + Any + Send + Sync + fmt::Debug {} +erased_serde::serialize_trait_object!(Metadata); + +impl Metadata for M where M: erased_serde::Serialize + Any + Send + Sync + fmt::Debug {} diff --git a/crates/kgraph_utils/Cargo.toml b/crates/kgraph_utils/Cargo.toml index 4ad5ef04f42..86de6002c32 100644 --- a/crates/kgraph_utils/Cargo.toml +++ b/crates/kgraph_utils/Cargo.toml @@ -13,6 +13,7 @@ connector_choice_mca_id = ["api_models/connector_choice_mca_id", "euclid/connect [dependencies] api_models = { version = "0.1.0", path = "../api_models", package = "api_models" } common_enums = { version = "0.1.0", path = "../common_enums" } +hyperswitch_constraint_graph = { version = "0.1.0", path = "../hyperswitch_constraint_graph" } euclid = { version = "0.1.0", path = "../euclid" } masking = { version = "0.1.0", path = "../masking/" } diff --git a/crates/kgraph_utils/benches/evaluation.rs b/crates/kgraph_utils/benches/evaluation.rs index 6105dc85d7e..9921ee7af35 100644 --- a/crates/kgraph_utils/benches/evaluation.rs +++ b/crates/kgraph_utils/benches/evaluation.rs @@ -8,13 +8,17 @@ use api_models::{ use criterion::{black_box, criterion_group, criterion_main, Criterion}; use euclid::{ dirval, - dssa::graph::{self, Memoization}, + dssa::graph::{self, CgraphExt}, frontend::dir, types::{NumValue, NumValueRefinement}, }; +use hyperswitch_constraint_graph::{CycleCheck, Memoization}; use kgraph_utils::{error::KgraphError, transformers::IntoDirValue}; -fn build_test_data<'a>(total_enabled: usize, total_pm_types: usize) -> graph::KnowledgeGraph<'a> { +fn build_test_data<'a>( + total_enabled: usize, + total_pm_types: usize, +) -> hyperswitch_constraint_graph::ConstraintGraph<'a, dir::DirValue> { use api_models::{admin::*, payment_methods::*}; let mut pms_enabled: Vec = Vec::new(); @@ -88,6 +92,8 @@ fn evaluation(c: &mut Criterion) { dirval!(PaymentAmount = 100), ]), &mut Memoization::new(), + &mut CycleCheck::new(), + None, ); }); }); @@ -105,6 +111,8 @@ fn evaluation(c: &mut Criterion) { dirval!(PaymentAmount = 100), ]), &mut Memoization::new(), + &mut CycleCheck::new(), + None, ); }); }); diff --git a/crates/kgraph_utils/src/error.rs b/crates/kgraph_utils/src/error.rs index 5a16c6375b0..95450fbe350 100644 --- a/crates/kgraph_utils/src/error.rs +++ b/crates/kgraph_utils/src/error.rs @@ -1,4 +1,4 @@ -use euclid::dssa::{graph::GraphError, types::AnalysisErrorType}; +use euclid::{dssa::types::AnalysisErrorType, frontend::dir}; #[derive(Debug, thiserror::Error, serde::Serialize)] #[serde(tag = "type", content = "info", rename_all = "snake_case")] @@ -6,7 +6,7 @@ pub enum KgraphError { #[error("Invalid connector name encountered: '{0}'")] InvalidConnectorName(String), #[error("There was an error constructing the graph: {0}")] - GraphConstructionError(GraphError), + GraphConstructionError(hyperswitch_constraint_graph::GraphError), #[error("There was an error constructing the context")] ContextConstructionError(AnalysisErrorType), #[error("there was an unprecedented indexing error")] diff --git a/crates/kgraph_utils/src/mca.rs b/crates/kgraph_utils/src/mca.rs index 8542437a5a6..14a88dd1c6e 100644 --- a/crates/kgraph_utils/src/mca.rs +++ b/crates/kgraph_utils/src/mca.rs @@ -4,42 +4,39 @@ use api_models::{ admin as admin_api, enums as api_enums, payment_methods::RequestPaymentMethodTypes, }; use euclid::{ - dssa::graph::{self, DomainIdentifier}, frontend::{ast, dir}, types::{NumValue, NumValueRefinement}, }; +use hyperswitch_constraint_graph as cgraph; use crate::{error::KgraphError, transformers::IntoDirValue}; pub const DOMAIN_IDENTIFIER: &str = "payment_methods_enabled_for_merchantconnectoraccount"; fn compile_request_pm_types( - builder: &mut graph::KnowledgeGraphBuilder<'_>, + builder: &mut cgraph::ConstraintGraphBuilder<'_, dir::DirValue>, pm_types: RequestPaymentMethodTypes, pm: api_enums::PaymentMethod, -) -> Result { - let mut agg_nodes: Vec<(graph::NodeId, graph::Relation, graph::Strength)> = Vec::new(); +) -> Result { + let mut agg_nodes: Vec<(cgraph::NodeId, cgraph::Relation, cgraph::Strength)> = Vec::new(); let pmt_info = "PaymentMethodType"; - let pmt_id = builder - .make_value_node( - (pm_types.payment_method_type, pm) - .into_dir_value() - .map(Into::into)?, - Some(pmt_info), - vec![DomainIdentifier::new(DOMAIN_IDENTIFIER)], - None::<()>, - ) - .map_err(KgraphError::GraphConstructionError)?; + let pmt_id = builder.make_value_node( + (pm_types.payment_method_type, pm) + .into_dir_value() + .map(Into::into)?, + Some(pmt_info), + None::<()>, + ); agg_nodes.push(( pmt_id, - graph::Relation::Positive, + cgraph::Relation::Positive, match pm_types.payment_method_type { api_enums::PaymentMethodType::Credit | api_enums::PaymentMethodType::Debit => { - graph::Strength::Weak + cgraph::Strength::Weak } - _ => graph::Strength::Strong, + _ => cgraph::Strength::Strong, }, )); @@ -52,13 +49,13 @@ fn compile_request_pm_types( let card_network_info = "Card Networks"; let card_network_id = builder - .make_in_aggregator(dir_vals, Some(card_network_info), None::<()>, Vec::new()) + .make_in_aggregator(dir_vals, Some(card_network_info), None::<()>) .map_err(KgraphError::GraphConstructionError)?; agg_nodes.push(( card_network_id, - graph::Relation::Positive, - graph::Strength::Weak, + cgraph::Relation::Positive, + cgraph::Strength::Weak, )); } } @@ -71,7 +68,7 @@ fn compile_request_pm_types( .map(IntoDirValue::into_dir_value) .collect::>() .ok()?, - graph::Relation::Positive, + cgraph::Relation::Positive, )), admin_api::AcceptedCurrencies::DisableOnly(curr) if !curr.is_empty() => Some(( @@ -79,7 +76,7 @@ fn compile_request_pm_types( .map(IntoDirValue::into_dir_value) .collect::>() .ok()?, - graph::Relation::Negative, + cgraph::Relation::Negative, )), _ => None, @@ -88,15 +85,10 @@ fn compile_request_pm_types( if let Some((currencies, relation)) = currencies_data { let accepted_currencies_info = "Accepted Currencies"; let accepted_currencies_id = builder - .make_in_aggregator( - currencies, - Some(accepted_currencies_info), - None::<()>, - Vec::new(), - ) + .make_in_aggregator(currencies, Some(accepted_currencies_info), None::<()>) .map_err(KgraphError::GraphConstructionError)?; - agg_nodes.push((accepted_currencies_id, relation, graph::Strength::Strong)); + agg_nodes.push((accepted_currencies_id, relation, cgraph::Strength::Strong)); } let mut amount_nodes = Vec::with_capacity(2); @@ -108,14 +100,11 @@ fn compile_request_pm_types( }; let min_amt_info = "Minimum Amount"; - let min_amt_id = builder - .make_value_node( - dir::DirValue::PaymentAmount(num_val).into(), - Some(min_amt_info), - vec![DomainIdentifier::new(DOMAIN_IDENTIFIER)], - None::<()>, - ) - .map_err(KgraphError::GraphConstructionError)?; + let min_amt_id = builder.make_value_node( + dir::DirValue::PaymentAmount(num_val).into(), + Some(min_amt_info), + None::<()>, + ); amount_nodes.push(min_amt_id); } @@ -127,14 +116,11 @@ fn compile_request_pm_types( }; let max_amt_info = "Maximum Amount"; - let max_amt_id = builder - .make_value_node( - dir::DirValue::PaymentAmount(num_val).into(), - Some(max_amt_info), - vec![DomainIdentifier::new(DOMAIN_IDENTIFIER)], - None::<()>, - ) - .map_err(KgraphError::GraphConstructionError)?; + let max_amt_id = builder.make_value_node( + dir::DirValue::PaymentAmount(num_val).into(), + Some(max_amt_info), + None::<()>, + ); amount_nodes.push(max_amt_id); } @@ -145,14 +131,11 @@ fn compile_request_pm_types( refinement: None, }; - let zero_amt_id = builder - .make_value_node( - dir::DirValue::PaymentAmount(zero_num_val).into(), - Some("zero_amount"), - vec![DomainIdentifier::new(DOMAIN_IDENTIFIER)], - None::<()>, - ) - .map_err(KgraphError::GraphConstructionError)?; + let zero_amt_id = builder.make_value_node( + dir::DirValue::PaymentAmount(zero_num_val).into(), + Some("zero_amount"), + None::<()>, + ); let or_node_neighbor_id = if amount_nodes.len() == 1 { amount_nodes @@ -163,7 +146,13 @@ fn compile_request_pm_types( let nodes = amount_nodes .iter() .copied() - .map(|node_id| (node_id, graph::Relation::Positive, graph::Strength::Strong)) + .map(|node_id| { + ( + node_id, + cgraph::Relation::Positive, + cgraph::Strength::Strong, + ) + }) .collect::>(); builder @@ -171,7 +160,7 @@ fn compile_request_pm_types( &nodes, Some("amount_constraint_aggregator"), None::<()>, - vec![DomainIdentifier::new(DOMAIN_IDENTIFIER)], + None, ) .map_err(KgraphError::GraphConstructionError)? }; @@ -179,37 +168,40 @@ fn compile_request_pm_types( let any_aggregator = builder .make_any_aggregator( &[ - (zero_amt_id, graph::Relation::Positive), - (or_node_neighbor_id, graph::Relation::Positive), + ( + zero_amt_id, + cgraph::Relation::Positive, + cgraph::Strength::Strong, + ), + ( + or_node_neighbor_id, + cgraph::Relation::Positive, + cgraph::Strength::Strong, + ), ], Some("zero_plus_limits_amount_aggregator"), None::<()>, - vec![DomainIdentifier::new(DOMAIN_IDENTIFIER)], + None, ) .map_err(KgraphError::GraphConstructionError)?; agg_nodes.push(( any_aggregator, - graph::Relation::Positive, - graph::Strength::Strong, + cgraph::Relation::Positive, + cgraph::Strength::Strong, )); } let pmt_all_aggregator_info = "All Aggregator for PaymentMethodType"; builder - .make_all_aggregator( - &agg_nodes, - Some(pmt_all_aggregator_info), - None::<()>, - Vec::new(), - ) + .make_all_aggregator(&agg_nodes, Some(pmt_all_aggregator_info), None::<()>, None) .map_err(KgraphError::GraphConstructionError) } fn compile_payment_method_enabled( - builder: &mut graph::KnowledgeGraphBuilder<'_>, + builder: &mut cgraph::ConstraintGraphBuilder<'_, dir::DirValue>, enabled: admin_api::PaymentMethodsEnabled, -) -> Result, KgraphError> { +) -> Result, KgraphError> { let agg_id = if !enabled .payment_method_types .as_ref() @@ -217,48 +209,44 @@ fn compile_payment_method_enabled( .unwrap_or(true) { let pm_info = "PaymentMethod"; - let pm_id = builder - .make_value_node( - enabled.payment_method.into_dir_value().map(Into::into)?, - Some(pm_info), - vec![DomainIdentifier::new(DOMAIN_IDENTIFIER)], - None::<()>, - ) - .map_err(KgraphError::GraphConstructionError)?; + let pm_id = builder.make_value_node( + enabled.payment_method.into_dir_value().map(Into::into)?, + Some(pm_info), + None::<()>, + ); - let mut agg_nodes: Vec<(graph::NodeId, graph::Relation)> = Vec::new(); + let mut agg_nodes: Vec<(cgraph::NodeId, cgraph::Relation, cgraph::Strength)> = Vec::new(); if let Some(pm_types) = enabled.payment_method_types { for pm_type in pm_types { let node_id = compile_request_pm_types(builder, pm_type, enabled.payment_method)?; - agg_nodes.push((node_id, graph::Relation::Positive)); + agg_nodes.push(( + node_id, + cgraph::Relation::Positive, + cgraph::Strength::Strong, + )); } } let any_aggregator_info = "Any aggregation for PaymentMethodsType"; let pm_type_agg_id = builder - .make_any_aggregator( - &agg_nodes, - Some(any_aggregator_info), - None::<()>, - Vec::new(), - ) + .make_any_aggregator(&agg_nodes, Some(any_aggregator_info), None::<()>, None) .map_err(KgraphError::GraphConstructionError)?; let all_aggregator_info = "All aggregation for PaymentMethod"; let enabled_pm_agg_id = builder .make_all_aggregator( &[ - (pm_id, graph::Relation::Positive, graph::Strength::Strong), + (pm_id, cgraph::Relation::Positive, cgraph::Strength::Strong), ( pm_type_agg_id, - graph::Relation::Positive, - graph::Strength::Strong, + cgraph::Relation::Positive, + cgraph::Strength::Strong, ), ], Some(all_aggregator_info), None::<()>, - Vec::new(), + None, ) .map_err(KgraphError::GraphConstructionError)?; @@ -271,26 +259,30 @@ fn compile_payment_method_enabled( } fn compile_merchant_connector_graph( - builder: &mut graph::KnowledgeGraphBuilder<'_>, + builder: &mut cgraph::ConstraintGraphBuilder<'_, dir::DirValue>, mca: admin_api::MerchantConnectorResponse, ) -> Result<(), KgraphError> { let connector = common_enums::RoutableConnectors::from_str(&mca.connector_name) .map_err(|_| KgraphError::InvalidConnectorName(mca.connector_name.clone()))?; - let mut agg_nodes: Vec<(graph::NodeId, graph::Relation)> = Vec::new(); + let mut agg_nodes: Vec<(cgraph::NodeId, cgraph::Relation, cgraph::Strength)> = Vec::new(); if let Some(pms_enabled) = mca.payment_methods_enabled { for pm_enabled in pms_enabled { let maybe_pm_enabled_id = compile_payment_method_enabled(builder, pm_enabled)?; if let Some(pm_enabled_id) = maybe_pm_enabled_id { - agg_nodes.push((pm_enabled_id, graph::Relation::Positive)); + agg_nodes.push(( + pm_enabled_id, + cgraph::Relation::Positive, + cgraph::Strength::Strong, + )); } } } let aggregator_info = "Available Payment methods for connector"; let pms_enabled_agg_id = builder - .make_any_aggregator(&agg_nodes, Some(aggregator_info), None::<()>, Vec::new()) + .make_any_aggregator(&agg_nodes, Some(aggregator_info), None::<()>, None) .map_err(KgraphError::GraphConstructionError)?; let connector_dir_val = dir::DirValue::Connector(Box::new(ast::ConnectorChoice { @@ -300,21 +292,16 @@ fn compile_merchant_connector_graph( })); let connector_info = "Connector"; - let connector_node_id = builder - .make_value_node( - connector_dir_val.into(), - Some(connector_info), - vec![DomainIdentifier::new(DOMAIN_IDENTIFIER)], - None::<()>, - ) - .map_err(KgraphError::GraphConstructionError)?; + let connector_node_id = + builder.make_value_node(connector_dir_val.into(), Some(connector_info), None::<()>); builder .make_edge( pms_enabled_agg_id, connector_node_id, - graph::Strength::Normal, - graph::Relation::Positive, + cgraph::Strength::Normal, + cgraph::Relation::Positive, + None::, ) .map_err(KgraphError::GraphConstructionError)?; @@ -323,11 +310,11 @@ fn compile_merchant_connector_graph( pub fn make_mca_graph<'a>( accts: Vec, -) -> Result, KgraphError> { - let mut builder = graph::KnowledgeGraphBuilder::new(); +) -> Result, KgraphError> { + let mut builder = cgraph::ConstraintGraphBuilder::new(); let _domain = builder.make_domain( - DomainIdentifier::new(DOMAIN_IDENTIFIER), - "Payment methods enabled for MerchantConnectorAccount".to_string(), + DOMAIN_IDENTIFIER, + "Payment methods enabled for MerchantConnectorAccount", ); for acct in accts { compile_merchant_connector_graph(&mut builder, acct)?; @@ -343,12 +330,13 @@ mod tests { use api_models::enums as api_enums; use euclid::{ dirval, - dssa::graph::{AnalysisContext, Memoization}, + dssa::graph::{AnalysisContext, CgraphExt}, }; + use hyperswitch_constraint_graph::{ConstraintGraph, CycleCheck, Memoization}; use super::*; - fn build_test_data<'a>() -> graph::KnowledgeGraph<'a> { + fn build_test_data<'a>() -> ConstraintGraph<'a, dir::DirValue> { use api_models::{admin::*, payment_methods::*}; let stripe_account = MerchantConnectorResponse { @@ -428,6 +416,8 @@ mod tests { dirval!(PaymentAmount = 100), ]), &mut Memoization::new(), + &mut CycleCheck::new(), + None, ); assert!(result.is_ok()); @@ -448,6 +438,8 @@ mod tests { dirval!(PaymentAmount = 100), ]), &mut Memoization::new(), + &mut CycleCheck::new(), + None, ); assert!(result.is_ok()); @@ -468,6 +460,8 @@ mod tests { dirval!(PaymentAmount = 100), ]), &mut Memoization::new(), + &mut CycleCheck::new(), + None, ); assert!(result.is_err()); @@ -488,6 +482,8 @@ mod tests { dirval!(PaymentAmount = 7), ]), &mut Memoization::new(), + &mut CycleCheck::new(), + None, ); assert!(result.is_err()); @@ -507,6 +503,8 @@ mod tests { dirval!(PaymentAmount = 7), ]), &mut Memoization::new(), + &mut CycleCheck::new(), + None, ); //println!("{:#?}", result); @@ -529,6 +527,8 @@ mod tests { dirval!(PaymentAmount = 100), ]), &mut Memoization::new(), + &mut CycleCheck::new(), + None, ); //println!("{:#?}", result); @@ -725,6 +725,8 @@ mod tests { dirval!(Connector = Stripe), &context, &mut Memoization::new(), + &mut CycleCheck::new(), + None, ); assert!(result.is_ok(), "stripe validation failed"); @@ -733,6 +735,8 @@ mod tests { dirval!(Connector = Bluesnap), &context, &mut Memoization::new(), + &mut CycleCheck::new(), + None, ); assert!(result.is_err(), "bluesnap validation failed"); } diff --git a/crates/router/Cargo.toml b/crates/router/Cargo.toml index f8e2cfad126..7ff47b927d8 100644 --- a/crates/router/Cargo.toml +++ b/crates/router/Cargo.toml @@ -103,6 +103,7 @@ analytics = { version = "0.1.0", path = "../analytics", optional = true } cards = { version = "0.1.0", path = "../cards" } common_enums = { version = "0.1.0", path = "../common_enums" } common_utils = { version = "0.1.0", path = "../common_utils", features = ["signals", "async_ext", "logs"] } +hyperswitch_constraint_graph = { version = "0.1.0", path = "../hyperswitch_constraint_graph" } currency_conversion = { version = "0.1.0", path = "../currency_conversion" } hyperswitch_domain_models = { version = "0.1.0", path = "../hyperswitch_domain_models", default-features = false } diesel_models = { version = "0.1.0", path = "../diesel_models", features = ["kv_store"] } diff --git a/crates/router/src/core/payments/routing.rs b/crates/router/src/core/payments/routing.rs index ff7303c900d..6967c977753 100644 --- a/crates/router/src/core/payments/routing.rs +++ b/crates/router/src/core/payments/routing.rs @@ -17,9 +17,9 @@ use diesel_models::enums as storage_enums; use error_stack::ResultExt; use euclid::{ backend::{self, inputs as dsl_inputs, EuclidBackend}, - dssa::graph::{self as euclid_graph, Memoization}, + dssa::graph::{self as euclid_graph, CgraphExt}, enums as euclid_enums, - frontend::ast, + frontend::{ast, dir as euclid_dir}, }; use kgraph_utils::{ mca as mca_graph, @@ -82,7 +82,9 @@ pub struct SessionRoutingPmTypeInput<'a> { profile_id: Option, } static ROUTING_CACHE: StaticCache = StaticCache::new(); -static KGRAPH_CACHE: StaticCache> = StaticCache::new(); +static KGRAPH_CACHE: StaticCache< + hyperswitch_constraint_graph::ConstraintGraph<'_, euclid_dir::DirValue>, +> = StaticCache::new(); type RoutingResult = oss_errors::CustomResult; @@ -542,7 +544,7 @@ pub async fn get_merchant_kgraph<'a>( merchant_last_modified: i64, #[cfg(feature = "business_profile_routing")] profile_id: Option, transaction_type: &api_enums::TransactionType, -) -> RoutingResult>> { +) -> RoutingResult>> { let merchant_id = &key_store.merchant_id; #[cfg(feature = "business_profile_routing")] @@ -690,7 +692,13 @@ async fn perform_kgraph_filtering( .into_dir_value() .change_context(errors::RoutingError::KgraphAnalysisError)?; let kgraph_eligible = cached_kgraph - .check_value_validity(dir_val, &context, &mut Memoization::new()) + .check_value_validity( + dir_val, + &context, + &mut hyperswitch_constraint_graph::Memoization::new(), + &mut hyperswitch_constraint_graph::CycleCheck::new(), + None, + ) .change_context(errors::RoutingError::KgraphAnalysisError)?; let filter_eligible = From 25cd6854f9d9162915e56213b6989eeb1d178b49 Mon Sep 17 00:00:00 2001 From: github-actions <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 7 May 2024 00:12:12 +0000 Subject: [PATCH 08/34] chore(postman): update Postman collection files --- postman/collection-json/stripe.postman_collection.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/postman/collection-json/stripe.postman_collection.json b/postman/collection-json/stripe.postman_collection.json index c981df1a4c4..d4ed3bd3ae0 100644 --- a/postman/collection-json/stripe.postman_collection.json +++ b/postman/collection-json/stripe.postman_collection.json @@ -13426,7 +13426,7 @@ "language": "json" } }, - "raw": "{\"payment_method\":\"pay_later\",\"payment_method_type\":\"klarna\",\"payment_experience\":\"redirect_to_url\",\"payment_method_data\":{\"pay_later\":{\"klarna_redirect\":{\"issuer_name\":\"stripe\",\"billing_email\":\"arjun.karthik@juspay.in\",\"billing_country\":\"US\"}}},\"billing\": {\"address\": {\"line1\": \"1467\",\"line2\": \"Harrison Street\",\"line3\": \"Harrison Street\",\"city\": \"San Fransico\",\"state\": \"California\",\"zip\": \"94122\",\"country\": \"US\",\"first_name\": \"sundari\"},\"email\": \"narayan@example.com\"},\"client_secret\":\"{{client_secret}}\"}" + "raw": "{\"payment_method\":\"pay_later\",\"payment_method_type\":\"klarna\",\"payment_experience\":\"redirect_to_url\",\"payment_method_data\":{\"pay_later\":{\"klarna_redirect\":{\"issuer_name\":\"stripe\",\"billing_email\":\"arjun.karthik@juspay.in\",\"billing_country\":\"US\"}}},\"billing\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"sundari\"},\"email\":\"narayan@example.com\"},\"client_secret\":\"{{client_secret}}\"}" }, "url": { "raw": "{{baseUrl}}/payments/:id/confirm", @@ -13677,7 +13677,7 @@ "language": "json" } }, - "raw": "{\"amount\":7000,\"currency\":\"USD\",\"confirm\":false,\"capture_method\":\"automatic\",\"capture_on\":\"2022-09-10T10:11:12Z\",\"customer_id\":\"StripeCustomer\",\"email\":\"abcdef123@gmail.com\",\"name\":\"John Doe\",\"phone\":\"999999999\",\"phone_country_code\":\"+65\",\"description\":\"Its my first payment request\",\"authentication_type\":\"three_ds\",\"return_url\":\"https://duck.com\",\"billing\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"first_name\":\"John\",\"last_name\":\"Doe\",\"country\":\"SE\"},\"email\": \"narayan@example.com\"},\"browser_info\":{\"user_agent\":\"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.110 Safari/537.36\",\"accept_header\":\"text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8\",\"language\":\"nl-NL\",\"color_depth\":24,\"screen_height\":723,\"screen_width\":1536,\"time_zone\":0,\"java_enabled\":true,\"java_script_enabled\":true,\"ip_address\":\"127.0.0.1\"},\"shipping\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"SE\",\"first_name\":\"John\",\"last_name\":\"Doe\"}},\"statement_descriptor_name\":\"joseph\",\"statement_descriptor_suffix\":\"JS\",\"metadata\":{\"order_details\":{\"product_name\":\"Socks\",\"amount\":7000,\"quantity\":1}},\"routing\":{\"type\":\"single\",\"data\":\"stripe\"}}" + "raw": "{\"amount\":7000,\"currency\":\"USD\",\"confirm\":false,\"capture_method\":\"automatic\",\"capture_on\":\"2022-09-10T10:11:12Z\",\"customer_id\":\"StripeCustomer\",\"email\":\"abcdef123@gmail.com\",\"name\":\"John Doe\",\"phone\":\"999999999\",\"phone_country_code\":\"+65\",\"description\":\"Its my first payment request\",\"authentication_type\":\"three_ds\",\"return_url\":\"https://duck.com\",\"billing\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"first_name\":\"John\",\"last_name\":\"Doe\",\"country\":\"SE\"},\"email\":\"narayan@example.com\"},\"browser_info\":{\"user_agent\":\"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.110 Safari/537.36\",\"accept_header\":\"text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8\",\"language\":\"nl-NL\",\"color_depth\":24,\"screen_height\":723,\"screen_width\":1536,\"time_zone\":0,\"java_enabled\":true,\"java_script_enabled\":true,\"ip_address\":\"127.0.0.1\"},\"shipping\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"SE\",\"first_name\":\"John\",\"last_name\":\"Doe\"}},\"statement_descriptor_name\":\"joseph\",\"statement_descriptor_suffix\":\"JS\",\"metadata\":{\"order_details\":{\"product_name\":\"Socks\",\"amount\":7000,\"quantity\":1}},\"routing\":{\"type\":\"single\",\"data\":\"stripe\"}}" }, "url": { "raw": "{{baseUrl}}/payments", From 76b76eccc6e8e6b9f095c55430a024e1b576bde5 Mon Sep 17 00:00:00 2001 From: github-actions <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 7 May 2024 00:14:14 +0000 Subject: [PATCH 09/34] chore(version): 2024.05.07.0 --- CHANGELOG.md | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6f4da0ae086..461484deb4f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,31 @@ All notable changes to HyperSwitch will be documented here. - - - +## 2024.05.07.0 + +### Features + +- **clickhouse:** Init Clickhouse container on startup ([#4365](https://github.com/juspay/hyperswitch/pull/4365)) ([`89e5884`](https://github.com/juspay/hyperswitch/commit/89e5884f9eb341026b09a0d1ab8b836de5ba0c19)) +- **constraint_graph:** Make the constraint graph framework generic and move it into a separate crate ([#3071](https://github.com/juspay/hyperswitch/pull/3071)) ([`a23a365`](https://github.com/juspay/hyperswitch/commit/a23a365cdf3fc2a24f4e2a08996a5683dc4da89a)) +- **payment_methods:** Filter payment methods based on pm client secret ([#4249](https://github.com/juspay/hyperswitch/pull/4249)) ([`575fac6`](https://github.com/juspay/hyperswitch/commit/575fac6f3ef94ef1856a77d778f822c0e97b0e9c)) +- Add decision starter API for email flows ([#4533](https://github.com/juspay/hyperswitch/pull/4533)) ([`1335554`](https://github.com/juspay/hyperswitch/commit/1335554f5193f05ba512d75a8eb9bb8047a65466)) + +### Refactors + +- **paylater:** Use payment_method_data.billing fields instead of payment_method_data ([#4333](https://github.com/juspay/hyperswitch/pull/4333)) ([`b878677`](https://github.com/juspay/hyperswitch/commit/b878677f1572dceb9cd1983c2fd0b3b05ed8a573)) + +### Miscellaneous Tasks + +- **postman:** Update Postman collection files ([`25cd685`](https://github.com/juspay/hyperswitch/commit/25cd6854f9d9162915e56213b6989eeb1d178b49)) + +### Build System / Dependencies + +- **docker:** Add web client and control center services to docker compose setup ([#4197](https://github.com/juspay/hyperswitch/pull/4197)) ([`b1cfef2`](https://github.com/juspay/hyperswitch/commit/b1cfef257a54b3ebe4f48e56a48a932e2c758dc7)) + +**Full Changelog:** [`2024.05.06.0...2024.05.07.0`](https://github.com/juspay/hyperswitch/compare/2024.05.06.0...2024.05.07.0) + +- - - + ## 2024.05.06.0 ### Features From df2c2ca22dc4cea986cbbf30850311d3e85000c5 Mon Sep 17 00:00:00 2001 From: chikke srujan <121822803+srujanchikke@users.noreply.github.com> Date: Tue, 7 May 2024 11:28:04 +0530 Subject: [PATCH 10/34] feat(FRM): add missing fields in Signifyd payment request (#4554) Co-authored-by: hyperswitch-bot[bot] <148525504+hyperswitch-bot[bot]@users.noreply.github.com> --- crates/api_models/src/payments.rs | 4 + crates/router/src/connector/signifyd.rs | 7 +- .../connector/signifyd/transformers/api.rs | 114 ++++++++++++++++-- .../core/fraud_check/flows/checkout_flow.rs | 16 ++- .../src/core/fraud_check/flows/sale_flow.rs | 17 ++- .../fraud_check/flows/transaction_flow.rs | 2 +- .../fraud_check/operation/fraud_check_post.rs | 4 + .../router/src/core/payments/transformers.rs | 1 + crates/router/src/types/fraud_check.rs | 2 + crates/router/tests/connectors/payme.rs | 4 + crates/router/tests/connectors/zen.rs | 4 + openapi/openapi_spec.json | 10 ++ 12 files changed, 168 insertions(+), 17 deletions(-) diff --git a/crates/api_models/src/payments.rs b/crates/api_models/src/payments.rs index 819e67a73bb..8270ddaf3fe 100644 --- a/crates/api_models/src/payments.rs +++ b/crates/api_models/src/payments.rs @@ -3776,6 +3776,8 @@ pub struct OrderDetailsWithAmount { pub product_id: Option, /// Category of the product that is being purchased pub category: Option, + /// Sub category of the product that is being purchased + pub sub_category: Option, /// Brand of the product that is being purchased pub brand: Option, /// Type of the product that is being purchased @@ -3810,6 +3812,8 @@ pub struct OrderDetails { pub product_id: Option, /// Category of the product that is being purchased pub category: Option, + /// Sub category of the product that is being purchased + pub sub_category: Option, /// Brand of the product that is being purchased pub brand: Option, /// Type of the product that is being purchased diff --git a/crates/router/src/connector/signifyd.rs b/crates/router/src/connector/signifyd.rs index c28fbc0550b..550e1ae30a8 100644 --- a/crates/router/src/connector/signifyd.rs +++ b/crates/router/src/connector/signifyd.rs @@ -1,6 +1,7 @@ pub mod transformers; use std::fmt::Debug; +use base64::Engine; #[cfg(feature = "frm")] use common_utils::request::RequestContent; use error_stack::{report, ResultExt}; @@ -9,6 +10,7 @@ use transformers as signifyd; use crate::{ configs::settings, + consts, core::errors::{self, CustomResult}, headers, services::{self, request, ConnectorIntegration, ConnectorValidation}, @@ -65,7 +67,10 @@ impl ConnectorCommon for Signifyd { ) -> CustomResult)>, errors::ConnectorError> { let auth = signifyd::SignifydAuthType::try_from(auth_type) .change_context(errors::ConnectorError::FailedToObtainAuthType)?; - let auth_api_key = format!("Basic {}", auth.api_key.peek()); + let auth_api_key = format!( + "Basic {}", + consts::BASE64_ENGINE.encode(auth.api_key.peek()) + ); Ok(vec![( headers::AUTHORIZATION.to_string(), diff --git a/crates/router/src/connector/signifyd/transformers/api.rs b/crates/router/src/connector/signifyd/transformers/api.rs index 684c789be6b..b56c7f7011e 100644 --- a/crates/router/src/connector/signifyd/transformers/api.rs +++ b/crates/router/src/connector/signifyd/transformers/api.rs @@ -1,6 +1,6 @@ use bigdecimal::ToPrimitive; -use common_utils::pii::Email; -use error_stack; +use common_utils::{ext_traits::ValueExt, pii::Email}; +use error_stack::{self, ResultExt}; use masking::Secret; use serde::{Deserialize, Serialize}; use time::PrimitiveDateTime; @@ -35,10 +35,14 @@ pub struct Purchase { total_price: i64, products: Vec, shipments: Shipments, + currency: Option, + total_shipping_cost: Option, + confirmation_email: Option, + confirmation_phone: Option>, } #[derive(Debug, Serialize, Eq, PartialEq, Deserialize, Clone)] -#[serde(rename_all = "SCREAMING_SNAKE_CASE")] +#[serde(rename_all(serialize = "SCREAMING_SNAKE_CASE", deserialize = "snake_case"))] pub enum OrderChannel { Web, Phone, @@ -51,17 +55,36 @@ pub enum OrderChannel { Mit, } +#[derive(Debug, Serialize, Eq, PartialEq, Deserialize, Clone)] +#[serde(rename_all(serialize = "SCREAMING_SNAKE_CASE", deserialize = "snake_case"))] +pub enum FulfillmentMethod { + Delivery, + CounterPickup, + CubsidePickup, + LockerPickup, + StandardShipping, + ExpeditedShipping, + GasPickup, + ScheduledDelivery, +} + #[derive(Debug, Serialize, Eq, PartialEq, Clone)] #[serde(rename_all = "camelCase")] pub struct Products { item_name: String, item_price: i64, item_quantity: i32, + item_id: Option, + item_category: Option, + item_sub_category: Option, + item_is_digital: Option, } #[derive(Debug, Serialize, Eq, PartialEq, Clone)] +#[serde(rename_all = "camelCase")] pub struct Shipments { destination: Destination, + fulfillment_method: Option, } #[derive(Debug, Deserialize, Serialize, Eq, PartialEq, Clone)] @@ -84,12 +107,32 @@ pub struct Address { country_code: common_enums::CountryAlpha2, } +#[derive(Debug, Serialize, Eq, PartialEq, Deserialize, Clone)] +#[serde(rename_all(serialize = "SCREAMING_SNAKE_CASE", deserialize = "snake_case"))] +pub enum CoverageRequests { + Fraud, // use when you need a financial guarantee for Payment Fraud. + Inr, // use when you need a financial guarantee for Item Not Received. + Snad, // use when you need a financial guarantee for fraud alleging items are Significantly Not As Described. + All, // use when you need a financial guarantee on all chargebacks. + None, // use when you do not need a financial guarantee. Suggested actions in decision.checkpointAction are recommendations. +} + #[derive(Debug, Serialize, Eq, PartialEq)] #[serde(rename_all = "camelCase")] pub struct SignifydPaymentsSaleRequest { order_id: String, purchase: Purchase, decision_delivery: DecisionDelivery, + coverage_requests: Option, +} + +#[derive(Debug, Serialize, Eq, PartialEq, Deserialize, Clone)] +#[serde(rename_all(serialize = "camelCase", deserialize = "snake_case"))] +pub struct SignifydFrmMetadata { + pub total_shipping_cost: Option, + pub fulfillment_method: Option, + pub coverage_request: Option, + pub order_channel: OrderChannel, } impl TryFrom<&frm_types::FrmSaleRouterData> for SignifydPaymentsSaleRequest { @@ -103,9 +146,25 @@ impl TryFrom<&frm_types::FrmSaleRouterData> for SignifydPaymentsSaleRequest { item_name: order_detail.product_name.clone(), item_price: order_detail.amount, item_quantity: i32::from(order_detail.quantity), + item_id: order_detail.product_id.clone(), + item_category: order_detail.category.clone(), + item_sub_category: order_detail.sub_category.clone(), + item_is_digital: order_detail + .product_type + .as_ref() + .map(|product| (product == &api_models::payments::ProductType::Digital)), }) .collect::>(); + let metadata: SignifydFrmMetadata = item + .frm_metadata + .clone() + .ok_or(errors::ConnectorError::MissingRequiredField { + field_name: "frm_metadata", + })? + .parse_value("Signifyd Frm Metadata") + .change_context(errors::ConnectorError::RequestEncodingFailed)?; let ship_address = item.get_shipping_address()?; + let billing_address = item.get_billing()?; let street_addr = ship_address.get_line1()?; let city_addr = ship_address.get_city()?; let zip_code_addr = ship_address.get_zip()?; @@ -128,19 +187,30 @@ impl TryFrom<&frm_types::FrmSaleRouterData> for SignifydPaymentsSaleRequest { }; let created_at = common_utils::date_time::now(); - let order_channel = OrderChannel::Web; - let shipments = Shipments { destination }; + let order_channel = metadata.order_channel; + let shipments = Shipments { + destination, + fulfillment_method: metadata.fulfillment_method, + }; let purchase = Purchase { created_at, order_channel, total_price: item.request.amount, products, shipments, + currency: item.request.currency, + total_shipping_cost: metadata.total_shipping_cost, + confirmation_email: item.request.email.clone(), + confirmation_phone: billing_address + .clone() + .phone + .and_then(|phone_data| phone_data.number), }; Ok(Self { order_id: item.attempt_id.clone(), purchase, - decision_delivery: DecisionDelivery::Sync, + decision_delivery: DecisionDelivery::Sync, // Specify SYNC if you require the Response to contain a decision field. If you have registered for a webhook associated with this checkpoint, then the webhook will also be sent when SYNC is specified. If ASYNC_ONLY is specified, then the decision field in the response will be null, and you will require a Webhook integration to receive Signifyd's final decision + coverage_requests: metadata.coverage_request, }) } } @@ -297,6 +367,7 @@ pub struct SignifydPaymentsCheckoutRequest { checkout_id: String, order_id: String, purchase: Purchase, + coverage_requests: Option, } impl TryFrom<&frm_types::FrmCheckoutRouterData> for SignifydPaymentsCheckoutRequest { @@ -310,8 +381,23 @@ impl TryFrom<&frm_types::FrmCheckoutRouterData> for SignifydPaymentsCheckoutRequ item_name: order_detail.product_name.clone(), item_price: order_detail.amount, item_quantity: i32::from(order_detail.quantity), + item_id: order_detail.product_id.clone(), + item_category: order_detail.category.clone(), + item_sub_category: order_detail.sub_category.clone(), + item_is_digital: order_detail + .product_type + .as_ref() + .map(|product| (product == &api_models::payments::ProductType::Digital)), }) .collect::>(); + let metadata: SignifydFrmMetadata = item + .frm_metadata + .clone() + .ok_or(errors::ConnectorError::MissingRequiredField { + field_name: "frm_metadata", + })? + .parse_value("Signifyd Frm Metadata") + .change_context(errors::ConnectorError::RequestEncodingFailed)?; let ship_address = item.get_shipping_address()?; let street_addr = ship_address.get_line1()?; let city_addr = ship_address.get_city()?; @@ -319,6 +405,7 @@ impl TryFrom<&frm_types::FrmCheckoutRouterData> for SignifydPaymentsCheckoutRequ let country_code_addr = ship_address.get_country()?; let _first_name_addr = ship_address.get_first_name()?; let _last_name_addr = ship_address.get_last_name()?; + let billing_address = item.get_billing()?; let address: Address = Address { street_address: street_addr.clone(), unit: None, @@ -334,19 +421,30 @@ impl TryFrom<&frm_types::FrmCheckoutRouterData> for SignifydPaymentsCheckoutRequ address, }; let created_at = common_utils::date_time::now(); - let order_channel = OrderChannel::Web; - let shipments: Shipments = Shipments { destination }; + let order_channel = metadata.order_channel; + let shipments: Shipments = Shipments { + destination, + fulfillment_method: metadata.fulfillment_method, + }; let purchase = Purchase { created_at, order_channel, total_price: item.request.amount, products, shipments, + currency: item.request.currency, + total_shipping_cost: metadata.total_shipping_cost, + confirmation_email: item.request.email.clone(), + confirmation_phone: billing_address + .clone() + .phone + .and_then(|phone_data| phone_data.number), }; Ok(Self { checkout_id: item.payment_id.clone(), order_id: item.attempt_id.clone(), purchase, + coverage_requests: metadata.coverage_request, }) } } diff --git a/crates/router/src/core/fraud_check/flows/checkout_flow.rs b/crates/router/src/core/fraud_check/flows/checkout_flow.rs index 58e82ec21c7..d0e5376b2ca 100644 --- a/crates/router/src/core/fraud_check/flows/checkout_flow.rs +++ b/crates/router/src/core/fraud_check/flows/checkout_flow.rs @@ -86,11 +86,17 @@ impl ConstructFlowSpecificData Domain for FraudCheckPost { request: FrmRequest::Sale(FraudCheckSaleData { amount: router_data.request.amount, order_details: router_data.request.order_details, + currency: router_data.request.currency, + email: router_data.request.email, }), response: FrmResponse::Sale(router_data.response), })) @@ -318,6 +320,8 @@ impl Domain for FraudCheckPost { request: FrmRequest::Sale(FraudCheckSaleData { amount: router_data.request.amount, order_details: router_data.request.order_details, + currency: router_data.request.currency, + email: router_data.request.email, }), response: FrmResponse::Sale(router_data.response), }) diff --git a/crates/router/src/core/payments/transformers.rs b/crates/router/src/core/payments/transformers.rs index d459f632d8f..56874e5a9fc 100644 --- a/crates/router/src/core/payments/transformers.rs +++ b/crates/router/src/core/payments/transformers.rs @@ -1014,6 +1014,7 @@ pub fn change_order_details_to_new_type( requires_shipping: order_details.requires_shipping, product_id: order_details.product_id, category: order_details.category, + sub_category: order_details.sub_category, brand: order_details.brand, product_type: order_details.product_type, }]) diff --git a/crates/router/src/types/fraud_check.rs b/crates/router/src/types/fraud_check.rs index b6f1d10888d..12ab7e4a067 100644 --- a/crates/router/src/types/fraud_check.rs +++ b/crates/router/src/types/fraud_check.rs @@ -17,6 +17,8 @@ pub type FrmSaleType = pub struct FraudCheckSaleData { pub amount: i64, pub order_details: Option>, + pub currency: Option, + pub email: Option, } #[derive(Debug, Clone)] pub struct FrmRouterData { diff --git a/crates/router/tests/connectors/payme.rs b/crates/router/tests/connectors/payme.rs index 5e9531a4f7a..a1682001488 100644 --- a/crates/router/tests/connectors/payme.rs +++ b/crates/router/tests/connectors/payme.rs @@ -84,6 +84,7 @@ fn payment_method_details() -> Option { requires_shipping: None, product_id: None, category: None, + sub_category: None, brand: None, product_type: None, }]), @@ -383,6 +384,7 @@ async fn should_fail_payment_for_incorrect_cvc() { requires_shipping: None, product_id: None, category: None, + sub_category: None, brand: None, product_type: None, }]), @@ -421,6 +423,7 @@ async fn should_fail_payment_for_invalid_exp_month() { requires_shipping: None, product_id: None, category: None, + sub_category: None, brand: None, product_type: None, }]), @@ -459,6 +462,7 @@ async fn should_fail_payment_for_incorrect_expiry_year() { requires_shipping: None, product_id: None, category: None, + sub_category: None, brand: None, product_type: None, }]), diff --git a/crates/router/tests/connectors/zen.rs b/crates/router/tests/connectors/zen.rs index 95017e7da0e..e076aee42c4 100644 --- a/crates/router/tests/connectors/zen.rs +++ b/crates/router/tests/connectors/zen.rs @@ -322,6 +322,7 @@ async fn should_fail_payment_for_incorrect_card_number() { requires_shipping: None, product_id: None, category: None, + sub_category: None, brand: None, product_type: None, }]), @@ -363,6 +364,7 @@ async fn should_fail_payment_for_incorrect_cvc() { requires_shipping: None, product_id: None, category: None, + sub_category: None, brand: None, product_type: None, }]), @@ -404,6 +406,7 @@ async fn should_fail_payment_for_invalid_exp_month() { requires_shipping: None, product_id: None, category: None, + sub_category: None, brand: None, product_type: None, }]), @@ -445,6 +448,7 @@ async fn should_fail_payment_for_incorrect_expiry_year() { requires_shipping: None, product_id: None, category: None, + sub_category: None, brand: None, product_type: None, }]), diff --git a/openapi/openapi_spec.json b/openapi/openapi_spec.json index 586fd1c1115..70e4715183f 100644 --- a/openapi/openapi_spec.json +++ b/openapi/openapi_spec.json @@ -11658,6 +11658,11 @@ "description": "Category of the product that is being purchased", "nullable": true }, + "sub_category": { + "type": "string", + "description": "Sub category of the product that is being purchased", + "nullable": true + }, "brand": { "type": "string", "description": "Brand of the product that is being purchased", @@ -11718,6 +11723,11 @@ "description": "Category of the product that is being purchased", "nullable": true }, + "sub_category": { + "type": "string", + "description": "Sub category of the product that is being purchased", + "nullable": true + }, "brand": { "type": "string", "description": "Brand of the product that is being purchased", From a72f040d9281744bceb928ef2e8d3a26783aae9e Mon Sep 17 00:00:00 2001 From: Sakil Mostak <73734619+Sakilmostak@users.noreply.github.com> Date: Tue, 7 May 2024 12:48:22 +0530 Subject: [PATCH 11/34] feat(connector): [Cybersource] Add payout flows for Card (#4511) Co-authored-by: hyperswitch-bot[bot] <148525504+hyperswitch-bot[bot]@users.noreply.github.com> --- crates/api_models/src/enums.rs | 4 + crates/connector_configs/src/connector.rs | 3 + crates/router/src/connector/cybersource.rs | 122 +++++++++ .../src/connector/cybersource/transformers.rs | 234 +++++++++++++++++- crates/router/src/connector/utils.rs | 76 +++++- crates/router/src/core/payments/flows.rs | 2 - openapi/openapi_spec.json | 3 +- 7 files changed, 434 insertions(+), 10 deletions(-) diff --git a/crates/api_models/src/enums.rs b/crates/api_models/src/enums.rs index 02fac0fbfa5..b48e6bce573 100644 --- a/crates/api_models/src/enums.rs +++ b/crates/api_models/src/enums.rs @@ -348,6 +348,7 @@ pub enum PayoutConnectors { Wise, Paypal, Ebanx, + Cybersource, } #[cfg(feature = "payouts")] @@ -359,6 +360,7 @@ impl From for RoutableConnectors { PayoutConnectors::Wise => Self::Wise, PayoutConnectors::Paypal => Self::Paypal, PayoutConnectors::Ebanx => Self::Ebanx, + PayoutConnectors::Cybersource => Self::Cybersource, } } } @@ -372,6 +374,7 @@ impl From for Connector { PayoutConnectors::Wise => Self::Wise, PayoutConnectors::Paypal => Self::Paypal, PayoutConnectors::Ebanx => Self::Ebanx, + PayoutConnectors::Cybersource => Self::Cybersource, } } } @@ -386,6 +389,7 @@ impl TryFrom for PayoutConnectors { Connector::Wise => Ok(Self::Wise), Connector::Paypal => Ok(Self::Paypal), Connector::Ebanx => Ok(Self::Ebanx), + Connector::Cybersource => Ok(Self::Cybersource), _ => Err(format!("Invalid payout connector {}", value)), } } diff --git a/crates/connector_configs/src/connector.rs b/crates/connector_configs/src/connector.rs index b9fbed28158..691d52b4fbb 100644 --- a/crates/connector_configs/src/connector.rs +++ b/crates/connector_configs/src/connector.rs @@ -133,6 +133,8 @@ pub struct ConnectorConfig { pub coinbase: Option, pub cryptopay: Option, pub cybersource: Option, + #[cfg(feature = "payouts")] + pub cybersource_payout: Option, pub iatapay: Option, pub opennode: Option, pub bambora: Option, @@ -223,6 +225,7 @@ impl ConnectorConfig { PayoutConnectors::Wise => Ok(connector_data.wise_payout), PayoutConnectors::Paypal => Ok(connector_data.paypal_payout), PayoutConnectors::Ebanx => Ok(connector_data.ebanx_payout), + PayoutConnectors::Cybersource => Ok(connector_data.cybersource_payout), } } diff --git a/crates/router/src/connector/cybersource.rs b/crates/router/src/connector/cybersource.rs index 39079fe9f99..d93a9405ae3 100644 --- a/crates/router/src/connector/cybersource.rs +++ b/crates/router/src/connector/cybersource.rs @@ -304,6 +304,10 @@ impl api::PaymentsPreProcessing for Cybersource {} impl api::PaymentsCompleteAuthorize for Cybersource {} impl api::ConnectorMandateRevoke for Cybersource {} +impl api::Payouts for Cybersource {} +#[cfg(feature = "payouts")] +impl api::PayoutFulfill for Cybersource {} + impl ConnectorIntegration< api::PaymentMethodToken, @@ -965,6 +969,124 @@ impl ConnectorIntegration + for Cybersource +{ + fn get_url( + &self, + _req: &types::PayoutsRouterData, + connectors: &settings::Connectors, + ) -> CustomResult { + Ok(format!("{}pts/v2/payouts", self.base_url(connectors))) + } + + fn get_headers( + &self, + req: &types::PayoutsRouterData, + connectors: &settings::Connectors, + ) -> CustomResult)>, errors::ConnectorError> { + self.build_headers(req, connectors) + } + + fn get_request_body( + &self, + req: &types::PayoutsRouterData, + _connectors: &settings::Connectors, + ) -> CustomResult { + let connector_router_data = cybersource::CybersourceRouterData::try_from(( + &self.get_currency_unit(), + req.request.destination_currency, + req.request.amount, + req, + ))?; + let connector_req = + cybersource::CybersourcePayoutFulfillRequest::try_from(&connector_router_data)?; + Ok(RequestContent::Json(Box::new(connector_req))) + } + + fn build_request( + &self, + req: &types::PayoutsRouterData, + connectors: &settings::Connectors, + ) -> CustomResult, errors::ConnectorError> { + let request = services::RequestBuilder::new() + .method(services::Method::Post) + .url(&types::PayoutFulfillType::get_url(self, req, connectors)?) + .attach_default_headers() + .headers(types::PayoutFulfillType::get_headers( + self, req, connectors, + )?) + .set_body(types::PayoutFulfillType::get_request_body( + self, req, connectors, + )?) + .build(); + + Ok(Some(request)) + } + + fn handle_response( + &self, + data: &types::PayoutsRouterData, + event_builder: Option<&mut ConnectorEvent>, + res: types::Response, + ) -> CustomResult, errors::ConnectorError> { + let response: cybersource::CybersourceFulfillResponse = res + .response + .parse_struct("CybersourceFulfillResponse") + .change_context(errors::ConnectorError::ResponseDeserializationFailed)?; + + event_builder.map(|i| i.set_response_body(&response)); + router_env::logger::info!(connector_response=?response); + + types::RouterData::try_from(types::ResponseRouterData { + response, + data: data.clone(), + http_code: res.status_code, + }) + } + + fn get_error_response( + &self, + res: types::Response, + event_builder: Option<&mut ConnectorEvent>, + ) -> CustomResult { + self.build_error_response(res, event_builder) + } + + fn get_5xx_error_response( + &self, + res: types::Response, + event_builder: Option<&mut ConnectorEvent>, + ) -> CustomResult { + let response: cybersource::CybersourceServerErrorResponse = res + .response + .parse_struct("CybersourceServerErrorResponse") + .change_context(errors::ConnectorError::ResponseDeserializationFailed)?; + + event_builder.map(|event| event.set_response_body(&response)); + router_env::logger::info!(error_response=?response); + + let attempt_status = match response.reason { + Some(reason) => match reason { + transformers::Reason::SystemError => Some(enums::AttemptStatus::Failure), + transformers::Reason::ServerTimeout | transformers::Reason::ServiceTimeout => None, + }, + None => None, + }; + Ok(types::ErrorResponse { + status_code: res.status_code, + reason: response.status.clone(), + code: response.status.unwrap_or(consts::NO_ERROR_CODE.to_string()), + message: response + .message + .unwrap_or(consts::NO_ERROR_MESSAGE.to_string()), + attempt_status, + connector_transaction_id: None, + }) + } +} + impl ConnectorIntegration< api::CompleteAuthorize, diff --git a/crates/router/src/connector/cybersource/transformers.rs b/crates/router/src/connector/cybersource/transformers.rs index 4c8e06b28ef..8251de8ca31 100644 --- a/crates/router/src/connector/cybersource/transformers.rs +++ b/crates/router/src/connector/cybersource/transformers.rs @@ -1,4 +1,9 @@ use api_models::payments; +#[cfg(feature = "payouts")] +use api_models::{ + payments::{AddressDetails, PhoneDetails}, + payouts::PayoutMethodData, +}; use base64::Engine; use common_enums::FutureUsage; use common_utils::{ext_traits::ValueExt, pii}; @@ -111,7 +116,7 @@ impl TryFrom<&types::SetupMandateRouterData> for CybersourceZeroMandateRequest { number: ccard.card_number, expiration_month: ccard.card_exp_month, expiration_year: ccard.card_exp_year, - security_code: ccard.card_cvc, + security_code: Some(ccard.card_cvc), card_type, }, }), @@ -404,7 +409,7 @@ pub struct Card { number: cards::CardNumber, expiration_month: Secret, expiration_year: Secret, - security_code: Secret, + security_code: Option>, #[serde(rename = "type")] card_type: Option, } @@ -849,7 +854,7 @@ impl number: ccard.card_number, expiration_month: ccard.card_exp_month, expiration_year: ccard.card_exp_year, - security_code: ccard.card_cvc, + security_code: Some(ccard.card_cvc), card_type: card_type.clone(), }, }); @@ -900,7 +905,7 @@ impl number: ccard.card_number, expiration_month: ccard.card_exp_month, expiration_year: ccard.card_exp_year, - security_code: ccard.card_cvc, + security_code: Some(ccard.card_cvc), card_type, }, }); @@ -1278,7 +1283,7 @@ impl TryFrom<&CybersourceRouterData<&types::PaymentsAuthorizeRouterData>> number: ccard.card_number, expiration_month: ccard.card_exp_month, expiration_year: ccard.card_exp_year, - security_code: ccard.card_cvc, + security_code: Some(ccard.card_cvc), card_type, }, }); @@ -1982,7 +1987,7 @@ impl TryFrom<&CybersourceRouterData<&types::PaymentsPreProcessingRouterData>> number: ccard.card_number, expiration_month: ccard.card_exp_month, expiration_year: ccard.card_exp_year, - security_code: ccard.card_cvc, + security_code: Some(ccard.card_cvc), card_type, }, })) @@ -2775,6 +2780,223 @@ impl TryFrom, + last_name: Secret, + address1: Secret, + locality: String, + administrative_area: Secret, + postal_code: Secret, + country: api_enums::CountryAlpha2, + phone_number: Option>, +} + +#[cfg(feature = "payouts")] +#[derive(Debug, Serialize)] +#[serde(rename_all = "camelCase")] +pub struct CybersourceSenderInfo { + reference_number: String, + account: CybersourceAccountInfo, +} + +#[cfg(feature = "payouts")] +#[derive(Debug, Serialize)] +#[serde(rename_all = "camelCase")] +pub struct CybersourceAccountInfo { + funds_source: CybersourcePayoutFundSourceType, +} + +#[cfg(feature = "payouts")] +#[derive(Debug, Serialize)] +pub enum CybersourcePayoutFundSourceType { + #[serde(rename = "05")] + Disbursement, +} + +#[cfg(feature = "payouts")] +#[derive(Debug, Serialize)] +#[serde(rename_all = "camelCase")] +pub struct CybersourceProcessingInfo { + business_application_id: CybersourcePayoutBusinessType, +} + +#[cfg(feature = "payouts")] +#[derive(Debug, Serialize)] +pub enum CybersourcePayoutBusinessType { + #[serde(rename = "PP")] + PersonToPerson, + #[serde(rename = "AA")] + AccountToAccount, +} + +#[cfg(feature = "payouts")] +impl TryFrom<&CybersourceRouterData<&types::PayoutsRouterData>> + for CybersourcePayoutFulfillRequest +{ + type Error = error_stack::Report; + fn try_from( + item: &CybersourceRouterData<&types::PayoutsRouterData>, + ) -> Result { + match item.router_data.request.payout_type { + enums::PayoutType::Card => { + let client_reference_information = ClientReferenceInformation { + code: Some(item.router_data.request.payout_id.clone()), + }; + + let order_information = OrderInformation { + amount_details: Amount { + total_amount: item.amount.to_owned(), + currency: item.router_data.request.destination_currency, + }, + }; + + let billing_address = item.router_data.get_billing_address()?; + let phone_address = item.router_data.get_billing_phone()?; + let recipient_information = + CybersourceRecipientInfo::try_from((billing_address, phone_address))?; + + let sender_information = CybersourceSenderInfo { + reference_number: item.router_data.request.payout_id.clone(), + account: CybersourceAccountInfo { + funds_source: CybersourcePayoutFundSourceType::Disbursement, + }, + }; + + let processing_information = CybersourceProcessingInfo { + business_application_id: CybersourcePayoutBusinessType::PersonToPerson, // this means sender and receiver are different + }; + + let payout_method_data = item.router_data.get_payout_method_data()?; + let payment_information = PaymentInformation::try_from(payout_method_data)?; + + Ok(Self { + client_reference_information, + order_information, + recipient_information, + sender_information, + processing_information, + payment_information, + }) + } + enums::PayoutType::Bank | enums::PayoutType::Wallet => { + Err(errors::ConnectorError::NotSupported { + message: "PayoutType is not supported".to_string(), + connector: "Cybersource", + })? + } + } + } +} + +#[cfg(feature = "payouts")] +impl TryFrom<(&AddressDetails, &PhoneDetails)> for CybersourceRecipientInfo { + type Error = error_stack::Report; + fn try_from(item: (&AddressDetails, &PhoneDetails)) -> Result { + let (billing_address, phone_address) = item; + Ok(Self { + first_name: billing_address.get_first_name()?.to_owned(), + last_name: billing_address.get_last_name()?.to_owned(), + address1: billing_address.get_line1()?.to_owned(), + locality: billing_address.get_city()?.to_owned(), + administrative_area: billing_address.get_state()?.to_owned(), + postal_code: billing_address.get_zip()?.to_owned(), + country: billing_address.get_country()?.to_owned(), + phone_number: phone_address.number.clone(), + }) + } +} + +#[cfg(feature = "payouts")] +impl TryFrom for PaymentInformation { + type Error = error_stack::Report; + fn try_from(item: PayoutMethodData) -> Result { + match item { + PayoutMethodData::Card(card_details) => { + let card_issuer = card_details.get_card_issuer().ok(); + let card_type = card_issuer.map(String::from); + let card = Card { + number: card_details.card_number, + expiration_month: card_details.expiry_month, + expiration_year: card_details.expiry_year, + security_code: None, + card_type, + }; + Ok(Self::Cards(CardPaymentInformation { card })) + } + PayoutMethodData::Bank(_) | PayoutMethodData::Wallet(_) => { + Err(errors::ConnectorError::NotSupported { + message: "PayoutMethod is not supported".to_string(), + connector: "Cybersource", + })? + } + } + } +} + +#[cfg(feature = "payouts")] +#[derive(Debug, Deserialize, Serialize)] +#[serde(rename_all = "camelCase")] +pub struct CybersourceFulfillResponse { + id: String, + status: CybersourcePayoutStatus, +} + +#[cfg(feature = "payouts")] +#[derive(Debug, Deserialize, Serialize)] +#[serde(rename_all = "SCREAMING_SNAKE_CASE")] +pub enum CybersourcePayoutStatus { + Accepted, + Declined, + InvalidRequest, +} + +#[cfg(feature = "payouts")] +impl ForeignFrom for enums::PayoutStatus { + fn foreign_from(status: CybersourcePayoutStatus) -> Self { + match status { + CybersourcePayoutStatus::Accepted => Self::Success, + CybersourcePayoutStatus::Declined | CybersourcePayoutStatus::InvalidRequest => { + Self::Failed + } + } + } +} + +#[cfg(feature = "payouts")] +impl TryFrom> + for types::PayoutsRouterData +{ + type Error = error_stack::Report; + fn try_from( + item: types::PayoutsResponseRouterData, + ) -> Result { + Ok(Self { + response: Ok(types::PayoutsResponseData { + status: Some(enums::PayoutStatus::foreign_from(item.response.status)), + connector_payout_id: item.response.id, + payout_eligible: None, + should_add_next_step_to_process_tracker: false, + }), + ..item.data + }) + } +} + #[derive(Debug, Deserialize, Serialize)] #[serde(rename_all = "camelCase")] pub struct CybersourceStandardErrorResponse { diff --git a/crates/router/src/connector/utils.rs b/crates/router/src/connector/utils.rs index 2cb3aa436e4..bb52d4885fb 100644 --- a/crates/router/src/connector/utils.rs +++ b/crates/router/src/connector/utils.rs @@ -1,7 +1,7 @@ use std::collections::HashMap; #[cfg(feature = "payouts")] -use api_models::payouts::PayoutVendorAccountDetails; +use api_models::payouts::{self, PayoutVendorAccountDetails}; use api_models::{ enums::{CanadaStatesAbbreviation, UsStatesAbbreviation}, payments::{self, OrderDetailsWithAmount}, @@ -1078,6 +1078,80 @@ pub trait CardData { fn get_expiry_year_as_i32(&self) -> Result, Error>; } +#[cfg(feature = "payouts")] +impl CardData for payouts::Card { + fn get_card_expiry_year_2_digit(&self) -> Result, errors::ConnectorError> { + let binding = self.expiry_year.clone(); + let year = binding.peek(); + Ok(Secret::new( + year.get(year.len() - 2..) + .ok_or(errors::ConnectorError::RequestEncodingFailed)? + .to_string(), + )) + } + fn get_card_issuer(&self) -> Result { + get_card_issuer(self.card_number.peek()) + } + fn get_card_expiry_month_year_2_digit_with_delimiter( + &self, + delimiter: String, + ) -> Result, errors::ConnectorError> { + let year = self.get_card_expiry_year_2_digit()?; + Ok(Secret::new(format!( + "{}{}{}", + self.expiry_month.peek(), + delimiter, + year.peek() + ))) + } + fn get_expiry_date_as_yyyymm(&self, delimiter: &str) -> Secret { + let year = self.get_expiry_year_4_digit(); + Secret::new(format!( + "{}{}{}", + year.peek(), + delimiter, + self.expiry_month.peek() + )) + } + fn get_expiry_date_as_mmyyyy(&self, delimiter: &str) -> Secret { + let year = self.get_expiry_year_4_digit(); + Secret::new(format!( + "{}{}{}", + self.expiry_month.peek(), + delimiter, + year.peek() + )) + } + fn get_expiry_year_4_digit(&self) -> Secret { + let mut year = self.expiry_year.peek().clone(); + if year.len() == 2 { + year = format!("20{}", year); + } + Secret::new(year) + } + fn get_expiry_date_as_yymm(&self) -> Result, errors::ConnectorError> { + let year = self.get_card_expiry_year_2_digit()?.expose(); + let month = self.expiry_month.clone().expose(); + Ok(Secret::new(format!("{year}{month}"))) + } + fn get_expiry_month_as_i8(&self) -> Result, Error> { + self.expiry_month + .peek() + .clone() + .parse::() + .change_context(errors::ConnectorError::ResponseDeserializationFailed) + .map(Secret::new) + } + fn get_expiry_year_as_i32(&self) -> Result, Error> { + self.expiry_year + .peek() + .clone() + .parse::() + .change_context(errors::ConnectorError::ResponseDeserializationFailed) + .map(Secret::new) + } +} + impl CardData for domain::Card { fn get_card_expiry_year_2_digit(&self) -> Result, errors::ConnectorError> { let binding = self.card_exp_year.clone(); diff --git a/crates/router/src/core/payments/flows.rs b/crates/router/src/core/payments/flows.rs index c7ec86ac5e6..0687f5986ba 100644 --- a/crates/router/src/core/payments/flows.rs +++ b/crates/router/src/core/payments/flows.rs @@ -975,7 +975,6 @@ default_imp_for_payouts!( connector::Cashtocode, connector::Checkout, connector::Cryptopay, - connector::Cybersource, connector::Coinbase, connector::Dlocal, connector::Fiserv, @@ -1232,7 +1231,6 @@ default_imp_for_payouts_fulfill!( connector::Cashtocode, connector::Checkout, connector::Cryptopay, - connector::Cybersource, connector::Coinbase, connector::Dlocal, connector::Fiserv, diff --git a/openapi/openapi_spec.json b/openapi/openapi_spec.json index 70e4715183f..c45a4aaaa10 100644 --- a/openapi/openapi_spec.json +++ b/openapi/openapi_spec.json @@ -15721,7 +15721,8 @@ "stripe", "wise", "paypal", - "ebanx" + "ebanx", + "cybersource" ] }, "PayoutCreateRequest": { From e769abe501470185fcca29e0abede0654579da06 Mon Sep 17 00:00:00 2001 From: Shankar Singh C <83439957+ShankarSinghC@users.noreply.github.com> Date: Tue, 7 May 2024 12:50:59 +0530 Subject: [PATCH 12/34] feat(router): add an api to enable `connector_agnostic_mit` feature (#4480) Co-authored-by: hyperswitch-bot[bot] <148525504+hyperswitch-bot[bot]@users.noreply.github.com> --- crates/api_models/src/admin.rs | 7 ++++ crates/diesel_models/src/business_profile.rs | 15 +++++++ crates/diesel_models/src/schema.rs | 1 + crates/router/src/core/admin.rs | 41 +++++++++++++++++++ crates/router/src/routes/admin.rs | 26 ++++++++++++ crates/router/src/routes/app.rs | 4 ++ crates/router/src/routes/lock_utils.rs | 3 +- crates/router/src/types/api/admin.rs | 1 + crates/router_env/src/logger/types.rs | 2 + .../down.sql | 3 ++ .../up.sql | 3 ++ 11 files changed, 105 insertions(+), 1 deletion(-) create mode 100644 migrations/2024-04-24-111807_add-is-connector_agnostic_mit/down.sql create mode 100644 migrations/2024-04-24-111807_add-is-connector_agnostic_mit/up.sql diff --git a/crates/api_models/src/admin.rs b/crates/api_models/src/admin.rs index fde693a3700..75bd2a31d8e 100644 --- a/crates/api_models/src/admin.rs +++ b/crates/api_models/src/admin.rs @@ -1100,6 +1100,13 @@ pub struct ExtendedCardInfoChoice { impl common_utils::events::ApiEventMetric for ExtendedCardInfoChoice {} +#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, PartialEq, Eq)] +pub struct ConnectorAgnosticMitChoice { + pub enabled: bool, +} + +impl common_utils::events::ApiEventMetric for ConnectorAgnosticMitChoice {} + #[derive(Debug, Clone, serde::Serialize, serde::Deserialize, ToSchema)] pub struct ExtendedCardInfoConfig { /// Merchant public key diff --git a/crates/diesel_models/src/business_profile.rs b/crates/diesel_models/src/business_profile.rs index 90a9dcbb7d8..1131e9ad674 100644 --- a/crates/diesel_models/src/business_profile.rs +++ b/crates/diesel_models/src/business_profile.rs @@ -37,6 +37,7 @@ pub struct BusinessProfile { pub authentication_connector_details: Option, pub is_extended_card_info_enabled: Option, pub extended_card_info_config: Option, + pub is_connector_agnostic_mit_enabled: Option, } #[derive(Clone, Debug, Insertable, router_derive::DebugAsDisplay)] @@ -65,6 +66,7 @@ pub struct BusinessProfileNew { pub authentication_connector_details: Option, pub is_extended_card_info_enabled: Option, pub extended_card_info_config: Option, + pub is_connector_agnostic_mit_enabled: Option, } #[derive(Clone, Debug, Default, AsChangeset, router_derive::DebugAsDisplay)] @@ -90,6 +92,7 @@ pub struct BusinessProfileUpdateInternal { pub authentication_connector_details: Option, pub is_extended_card_info_enabled: Option, pub extended_card_info_config: Option, + pub is_connector_agnostic_mit_enabled: Option, } #[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] @@ -117,6 +120,9 @@ pub enum BusinessProfileUpdate { ExtendedCardInfoUpdate { is_extended_card_info_enabled: Option, }, + ConnectorAgnosticMitUpdate { + is_connector_agnostic_mit_enabled: Option, + }, } impl From for BusinessProfileUpdateInternal { @@ -168,6 +174,12 @@ impl From for BusinessProfileUpdateInternal { is_extended_card_info_enabled, ..Default::default() }, + BusinessProfileUpdate::ConnectorAgnosticMitUpdate { + is_connector_agnostic_mit_enabled, + } => Self { + is_connector_agnostic_mit_enabled, + ..Default::default() + }, } } } @@ -195,6 +207,7 @@ impl From for BusinessProfile { payment_link_config: new.payment_link_config, session_expiry: new.session_expiry, authentication_connector_details: new.authentication_connector_details, + is_connector_agnostic_mit_enabled: new.is_connector_agnostic_mit_enabled, is_extended_card_info_enabled: new.is_extended_card_info_enabled, extended_card_info_config: new.extended_card_info_config, } @@ -223,6 +236,7 @@ impl BusinessProfileUpdate { authentication_connector_details, is_extended_card_info_enabled, extended_card_info_config, + is_connector_agnostic_mit_enabled, } = self.into(); BusinessProfile { profile_name: profile_name.unwrap_or(source.profile_name), @@ -245,6 +259,7 @@ impl BusinessProfileUpdate { session_expiry, authentication_connector_details, is_extended_card_info_enabled, + is_connector_agnostic_mit_enabled, extended_card_info_config, ..source } diff --git a/crates/diesel_models/src/schema.rs b/crates/diesel_models/src/schema.rs index f5dc5abe1e6..2e28bc5d30f 100644 --- a/crates/diesel_models/src/schema.rs +++ b/crates/diesel_models/src/schema.rs @@ -194,6 +194,7 @@ diesel::table! { authentication_connector_details -> Nullable, is_extended_card_info_enabled -> Nullable, extended_card_info_config -> Nullable, + is_connector_agnostic_mit_enabled -> Nullable, } } diff --git a/crates/router/src/core/admin.rs b/crates/router/src/core/admin.rs index 7d2fabc3f37..0b8d52332d9 100644 --- a/crates/router/src/core/admin.rs +++ b/crates/router/src/core/admin.rs @@ -1739,6 +1739,47 @@ pub async fn extended_card_info_toggle( Ok(service_api::ApplicationResponse::Json(ext_card_info_choice)) } +pub async fn connector_agnostic_mit_toggle( + state: AppState, + merchant_id: &str, + profile_id: &str, + connector_agnostic_mit_choice: admin_types::ConnectorAgnosticMitChoice, +) -> RouterResponse { + let db = state.store.as_ref(); + + let business_profile = db + .find_business_profile_by_profile_id(profile_id) + .await + .to_not_found_response(errors::ApiErrorResponse::BusinessProfileNotFound { + id: profile_id.to_string(), + })?; + + if business_profile.merchant_id != merchant_id { + Err(errors::ApiErrorResponse::AccessForbidden { + resource: profile_id.to_string(), + })? + } + + if business_profile.is_connector_agnostic_mit_enabled + != Some(connector_agnostic_mit_choice.enabled) + { + let business_profile_update = + storage::business_profile::BusinessProfileUpdate::ConnectorAgnosticMitUpdate { + is_connector_agnostic_mit_enabled: Some(connector_agnostic_mit_choice.enabled), + }; + + db.update_business_profile_by_profile_id(business_profile, business_profile_update) + .await + .to_not_found_response(errors::ApiErrorResponse::BusinessProfileNotFound { + id: profile_id.to_owned(), + })?; + } + + Ok(service_api::ApplicationResponse::Json( + connector_agnostic_mit_choice, + )) +} + pub(crate) fn validate_auth_and_metadata_type( connector_name: api_models::enums::Connector, val: &types::ConnectorAuthType, diff --git a/crates/router/src/routes/admin.rs b/crates/router/src/routes/admin.rs index 63d9f840a00..d9cadf002c1 100644 --- a/crates/router/src/routes/admin.rs +++ b/crates/router/src/routes/admin.rs @@ -589,6 +589,32 @@ pub async fn business_profiles_list( ) .await } + +#[instrument(skip_all, fields(flow = ?Flow::ToggleConnectorAgnosticMit))] +pub async fn toggle_connector_agnostic_mit( + state: web::Data, + req: HttpRequest, + path: web::Path<(String, String)>, + json_payload: web::Json, +) -> HttpResponse { + let flow = Flow::ToggleConnectorAgnosticMit; + let (merchant_id, profile_id) = path.into_inner(); + + Box::pin(api::server_wrap( + flow, + state, + &req, + json_payload.into_inner(), + |state, _, req, _| connector_agnostic_mit_toggle(state, &merchant_id, &profile_id, req), + auth::auth_type( + &auth::ApiKeyAuth, + &auth::JWTAuth(Permission::RoutingWrite), + req.headers(), + ), + api_locking::LockAction::NotApplicable, + )) + .await +} /// Merchant Account - KV Status /// /// Toggle KV mode for the Merchant Account diff --git a/crates/router/src/routes/app.rs b/crates/router/src/routes/app.rs index 0f3673ee206..6a8a4ee5e03 100644 --- a/crates/router/src/routes/app.rs +++ b/crates/router/src/routes/app.rs @@ -1131,6 +1131,10 @@ impl BusinessProfile { .service( web::resource("/toggle_extended_card_info") .route(web::post().to(toggle_extended_card_info)), + ) + .service( + web::resource("/toggle_connector_agnostic_mit") + .route(web::post().to(toggle_connector_agnostic_mit)), ), ) } diff --git a/crates/router/src/routes/lock_utils.rs b/crates/router/src/routes/lock_utils.rs index 4d1121a8eab..bf91f8055d1 100644 --- a/crates/router/src/routes/lock_utils.rs +++ b/crates/router/src/routes/lock_utils.rs @@ -168,7 +168,8 @@ impl From for ApiIdentifier { | Flow::BusinessProfileRetrieve | Flow::BusinessProfileDelete | Flow::BusinessProfileList - | Flow::ToggleExtendedCardInfo => Self::Business, + | Flow::ToggleExtendedCardInfo + | Flow::ToggleConnectorAgnosticMit => Self::Business, Flow::PaymentLinkRetrieve | Flow::PaymentLinkInitiate diff --git a/crates/router/src/types/api/admin.rs b/crates/router/src/types/api/admin.rs index 77fe8371817..793d042ea49 100644 --- a/crates/router/src/types/api/admin.rs +++ b/crates/router/src/types/api/admin.rs @@ -175,6 +175,7 @@ impl ForeignTryFrom<(domain::MerchantAccount, BusinessProfileCreate)> .change_context(errors::ApiErrorResponse::InvalidDataValue { field_name: "authentication_connector_details", })?, + is_connector_agnostic_mit_enabled: None, is_extended_card_info_enabled: None, extended_card_info_config: None, }) diff --git a/crates/router_env/src/logger/types.rs b/crates/router_env/src/logger/types.rs index 78f570f647e..e8ffe0685b2 100644 --- a/crates/router_env/src/logger/types.rs +++ b/crates/router_env/src/logger/types.rs @@ -406,6 +406,8 @@ pub enum Flow { RetrievePollStatus, /// Toggles the extended card info feature in profile level ToggleExtendedCardInfo, + /// Toggles the extended card info feature in profile level + ToggleConnectorAgnosticMit, /// Get the extended card info associated to a payment_id GetExtendedCardInfo, } diff --git a/migrations/2024-04-24-111807_add-is-connector_agnostic_mit/down.sql b/migrations/2024-04-24-111807_add-is-connector_agnostic_mit/down.sql new file mode 100644 index 00000000000..80672e85a26 --- /dev/null +++ b/migrations/2024-04-24-111807_add-is-connector_agnostic_mit/down.sql @@ -0,0 +1,3 @@ +-- This file should undo anything in `up.sql` + +ALTER TABLE business_profile DROP COLUMN IF EXISTS is_connector_agnostic_mit_enabled; \ No newline at end of file diff --git a/migrations/2024-04-24-111807_add-is-connector_agnostic_mit/up.sql b/migrations/2024-04-24-111807_add-is-connector_agnostic_mit/up.sql new file mode 100644 index 00000000000..beee3325617 --- /dev/null +++ b/migrations/2024-04-24-111807_add-is-connector_agnostic_mit/up.sql @@ -0,0 +1,3 @@ +-- Your SQL goes here + +ALTER TABLE business_profile ADD COLUMN IF NOT EXISTS is_connector_agnostic_mit_enabled BOOLEAN DEFAULT FALSE; \ No newline at end of file From f63a97024c755fd30a3403e2146812fe4edb8067 Mon Sep 17 00:00:00 2001 From: Sarthak Soni <76486416+Sarthak1799@users.noreply.github.com> Date: Tue, 7 May 2024 12:59:28 +0530 Subject: [PATCH 13/34] fix(payment_methods): fix deserialization errors for `sdk_eligible_payment_methods` (#4565) --- config/config.example.toml | 2 +- config/deployments/integration_test.toml | 2 +- config/deployments/production.toml | 2 +- config/deployments/sandbox.toml | 2 +- config/development.toml | 2 +- config/docker_compose.toml | 2 +- crates/router/src/configs/settings.rs | 2 ++ 7 files changed, 8 insertions(+), 6 deletions(-) diff --git a/config/config.example.toml b/config/config.example.toml index b5585a84b32..f0dad483c65 100644 --- a/config/config.example.toml +++ b/config/config.example.toml @@ -615,4 +615,4 @@ refunds = "hyperswitch-refund-events" disputes = "hyperswitch-dispute-events" [saved_payment_methods] -sdk_eligible_payment_methods = ["card"] \ No newline at end of file +sdk_eligible_payment_methods = "card" \ No newline at end of file diff --git a/config/deployments/integration_test.toml b/config/deployments/integration_test.toml index fe47b10b543..faf3bb7f466 100644 --- a/config/deployments/integration_test.toml +++ b/config/deployments/integration_test.toml @@ -321,4 +321,4 @@ connectors_with_webhook_source_verification_call = "paypal" # List of co keys = "user-agent" [saved_payment_methods] -sdk_eligible_payment_methods = ["card"] \ No newline at end of file +sdk_eligible_payment_methods = "card" \ No newline at end of file diff --git a/config/deployments/production.toml b/config/deployments/production.toml index 3135b3d5682..f564b976bc0 100644 --- a/config/deployments/production.toml +++ b/config/deployments/production.toml @@ -332,4 +332,4 @@ connectors_with_webhook_source_verification_call = "paypal" # List of connec keys = "user-agent" [saved_payment_methods] -sdk_eligible_payment_methods = ["card"] \ No newline at end of file +sdk_eligible_payment_methods = "card" \ No newline at end of file diff --git a/config/deployments/sandbox.toml b/config/deployments/sandbox.toml index 921301ebd6f..35c8a2ba3d6 100644 --- a/config/deployments/sandbox.toml +++ b/config/deployments/sandbox.toml @@ -336,4 +336,4 @@ connectors_with_webhook_source_verification_call = "paypal" # List of con keys = "user-agent" [saved_payment_methods] -sdk_eligible_payment_methods = ["card"] \ No newline at end of file +sdk_eligible_payment_methods = "card" \ No newline at end of file diff --git a/config/development.toml b/config/development.toml index db7f1ed93f4..3f05872bc6a 100644 --- a/config/development.toml +++ b/config/development.toml @@ -617,4 +617,4 @@ refunds = "hyperswitch-refund-events" disputes = "hyperswitch-dispute-events" [saved_payment_methods] -sdk_eligible_payment_methods = ["card"] +sdk_eligible_payment_methods = "card" diff --git a/config/docker_compose.toml b/config/docker_compose.toml index 702d134f2d5..9fd22f46707 100644 --- a/config/docker_compose.toml +++ b/config/docker_compose.toml @@ -476,4 +476,4 @@ refunds = "hyperswitch-refund-events" disputes = "hyperswitch-dispute-events" [saved_payment_methods] -sdk_eligible_payment_methods = ["card"] \ No newline at end of file +sdk_eligible_payment_methods = "card" \ No newline at end of file diff --git a/crates/router/src/configs/settings.rs b/crates/router/src/configs/settings.rs index 12ce9c39d6f..adc09fb55af 100644 --- a/crates/router/src/configs/settings.rs +++ b/crates/router/src/configs/settings.rs @@ -167,7 +167,9 @@ pub struct PaymentMethodAuth { } #[derive(Debug, Deserialize, Clone, Default)] +#[serde(default)] pub struct EligiblePaymentMethods { + #[serde(deserialize_with = "deserialize_hashset")] pub sdk_eligible_payment_methods: HashSet, } From 71a070e26989f080031d92a88aa0143836d1ea7b Mon Sep 17 00:00:00 2001 From: Hrithikesh <61539176+hrithikesh026@users.noreply.github.com> Date: Tue, 7 May 2024 13:38:53 +0530 Subject: [PATCH 14/34] refactor(core): refactor authentication core to fetch authentication only within it (#4138) Co-authored-by: hyperswitch-bot[bot] <148525504+hyperswitch-bot[bot]@users.noreply.github.com> --- Cargo.lock | 4 +- crates/api_models/src/enums.rs | 62 +--- crates/diesel_models/src/authentication.rs | 3 - crates/router/src/core/authentication.rs | 265 +++++------------- .../router/src/core/authentication/utils.rs | 98 +++++-- crates/router/src/core/payments/helpers.rs | 98 +++++++ .../payments/operations/payment_confirm.rs | 177 +++++------- crates/router/src/types/api/authentication.rs | 24 +- 8 files changed, 336 insertions(+), 395 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 7fce1e7f538..84fdee38021 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -7040,9 +7040,9 @@ dependencies = [ [[package]] name = "time" -version = "0.3.35" +version = "0.3.36" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef89ece63debf11bc32d1ed8d078ac870cbeb44da02afb02a9ff135ae7ca0582" +checksum = "5dfd88e563464686c916c7e46e623e520ddc6d79fa6641390f2e3fa86e83e885" dependencies = [ "deranged", "itoa", diff --git a/crates/api_models/src/enums.rs b/crates/api_models/src/enums.rs index b48e6bce573..aed9915366f 100644 --- a/crates/api_models/src/enums.rs +++ b/crates/api_models/src/enums.rs @@ -177,8 +177,8 @@ impl Connector { matches!(self, Self::Checkout) } pub fn is_separate_authentication_supported(&self) -> bool { - #[cfg(feature = "dummy_connector")] match self { + #[cfg(feature = "dummy_connector")] Self::DummyConnector1 | Self::DummyConnector2 | Self::DummyConnector3 @@ -243,66 +243,6 @@ impl Connector { | Self::Stripe => false, Self::Checkout | Self::Nmi => true, } - #[cfg(not(feature = "dummy_connector"))] - match self { - Self::Aci - | Self::Adyen - | Self::Airwallex - | Self::Authorizedotnet - | Self::Bambora - | Self::Bankofamerica - | Self::Billwerk - | Self::Bitpay - | Self::Bluesnap - | Self::Boku - | Self::Braintree - | Self::Cashtocode - | Self::Coinbase - | Self::Cryptopay - | Self::Dlocal - | Self::Ebanx - | Self::Fiserv - | Self::Forte - | Self::Globalpay - | Self::Globepay - | Self::Gocardless - | Self::Helcim - | Self::Iatapay - | Self::Klarna - | Self::Mollie - | Self::Multisafepay - | Self::Nexinets - | Self::Nmi - | Self::Nuvei - | Self::Opennode - | Self::Payme - | Self::Paypal - | Self::Payu - | Self::Placetopay - | Self::Powertranz - | Self::Prophetpay - | Self::Rapyd - | Self::Shift4 - | Self::Square - | Self::Stax - | Self::Trustpay - | Self::Tsys - | Self::Volt - | Self::Wise - | Self::Worldline - | Self::Worldpay - | Self::Zen - | Self::Zsl - | Self::Signifyd - | Self::Plaid - | Self::Riskified - | Self::Threedsecureio - | Self::Cybersource - | Self::Noon - | Self::Netcetera - | Self::Stripe => false, - Self::Checkout => true, - } } } diff --git a/crates/diesel_models/src/authentication.rs b/crates/diesel_models/src/authentication.rs index 61f64dbaa3d..8840d287e54 100644 --- a/crates/diesel_models/src/authentication.rs +++ b/crates/diesel_models/src/authentication.rs @@ -101,7 +101,6 @@ pub enum AuthenticationUpdate { message_version: common_utils::types::SemanticVersion, connector_metadata: Option, authentication_status: common_enums::AuthenticationStatus, - payment_method_id: Option, acquirer_bin: Option, acquirer_merchant_id: Option, }, @@ -309,7 +308,6 @@ impl From for AuthenticationUpdateInternal { message_version, connector_metadata, authentication_status, - payment_method_id, acquirer_bin, acquirer_merchant_id, } => Self { @@ -321,7 +319,6 @@ impl From for AuthenticationUpdateInternal { message_version: Some(message_version), connector_metadata, authentication_status: Some(authentication_status), - payment_method_id, acquirer_bin, acquirer_merchant_id, ..Default::default() diff --git a/crates/router/src/core/authentication.rs b/crates/router/src/core/authentication.rs index f9161f2ccc6..36ee3c20416 100644 --- a/crates/router/src/core/authentication.rs +++ b/crates/router/src/core/authentication.rs @@ -5,19 +5,16 @@ pub mod types; use api_models::payments; use common_enums::Currency; -use common_utils::{ - errors::CustomResult, - ext_traits::{Encode, StringExt, ValueExt}, -}; -use error_stack::{report, ResultExt}; -use masking::{ExposeInterface, PeekInterface}; +use common_utils::errors::CustomResult; +use error_stack::ResultExt; +use masking::ExposeInterface; -use super::errors; +use super::errors::{self, StorageErrorExt}; use crate::{ core::{errors::ApiErrorResponse, payments as payments_core}, routes::AppState, - types::{self as core_types, api, authentication::AuthenticationResponseData, storage}, - utils::{check_if_pull_mechanism_for_external_3ds_enabled_from_connector_metadata, OptionExt}, + types::{self as core_types, api, domain, storage}, + utils::check_if_pull_mechanism_for_external_3ds_enabled_from_connector_metadata, }; #[allow(clippy::too_many_arguments)] @@ -64,133 +61,76 @@ pub async fn perform_authentication( )?; let response = utils::do_auth_connector_call(state, authentication_connector.clone(), router_data).await?; - utils::update_trackers(state, response.clone(), authentication_data, None, None).await?; - let authentication_response = - response - .response - .map_err(|err| ApiErrorResponse::ExternalConnectorError { - code: err.code, - message: err.message, - connector: authentication_connector, - status_code: err.status_code, - reason: err.reason, - })?; - match authentication_response { - AuthenticationResponseData::AuthNResponse { - authn_flow_type, - trans_status, - .. - } => Ok(match authn_flow_type { - core_types::authentication::AuthNFlowType::Challenge(challenge_params) => { - core_types::api::AuthenticationResponse { - trans_status, - acs_url: challenge_params.acs_url, - challenge_request: challenge_params.challenge_request, - acs_reference_number: challenge_params.acs_reference_number, - acs_trans_id: challenge_params.acs_trans_id, - three_dsserver_trans_id: challenge_params.three_dsserver_trans_id, - acs_signed_content: challenge_params.acs_signed_content, - } - } - core_types::authentication::AuthNFlowType::Frictionless => { - core_types::api::AuthenticationResponse { - trans_status, - acs_url: None, - challenge_request: None, - acs_reference_number: None, - acs_trans_id: None, - three_dsserver_trans_id: None, - acs_signed_content: None, - } - } - }), - _ => Err(report!(errors::ApiErrorResponse::InternalServerError)) - .attach_printable("unexpected response in authentication flow")?, - } + let authentication = + utils::update_trackers(state, response.clone(), authentication_data, None).await?; + response + .response + .map_err(|err| ApiErrorResponse::ExternalConnectorError { + code: err.code, + message: err.message, + connector: authentication_connector, + status_code: err.status_code, + reason: err.reason, + })?; + core_types::api::authentication::AuthenticationResponse::try_from(authentication) } -pub async fn perform_post_authentication( +pub async fn perform_post_authentication( state: &AppState, - authentication_connector: String, + key_store: &domain::MerchantKeyStore, business_profile: core_types::storage::BusinessProfile, - merchant_connector_account: payments_core::helpers::MerchantConnectorAccountType, - authentication_flow_input: types::PostAuthenthenticationFlowInput<'_, F>, -) -> CustomResult<(), ApiErrorResponse> { - match authentication_flow_input { - types::PostAuthenthenticationFlowInput::PaymentAuthNFlow { - payment_data, - authentication, - should_continue_confirm_transaction, - } => { - let is_pull_mechanism_enabled = - check_if_pull_mechanism_for_external_3ds_enabled_from_connector_metadata( - merchant_connector_account - .get_metadata() - .map(|metadata| metadata.expose()), - ); - let authentication_status = - if !authentication.authentication_status.is_terminal_status() - && is_pull_mechanism_enabled - { - let router_data = transformers::construct_post_authentication_router_data( - authentication_connector.clone(), - business_profile.clone(), - merchant_connector_account, - &authentication, - )?; - let router_data = - utils::do_auth_connector_call(state, authentication_connector, router_data) - .await?; - let updated_authentication = utils::update_trackers( - state, - router_data, - authentication.clone(), - payment_data.token.clone(), - None, - ) - .await?; - let authentication_status = updated_authentication.authentication_status; - payment_data.authentication = Some(updated_authentication); - authentication_status - } else { - authentication.authentication_status - }; - //If authentication is not successful, skip the payment connector flows and mark the payment as failure - if !(authentication_status == api_models::enums::AuthenticationStatus::Success) { - *should_continue_confirm_transaction = false; - } - } - types::PostAuthenthenticationFlowInput::PaymentMethodAuthNFlow { other_fields: _ } => { - // todo!("Payment method post authN operation"); - } - } - Ok(()) -} - -fn get_payment_id_from_pre_authentication_flow_input( - pre_authentication_flow_input: &types::PreAuthenthenticationFlowInput<'_, F>, -) -> Option { - match pre_authentication_flow_input { - types::PreAuthenthenticationFlowInput::PaymentAuthNFlow { payment_data, .. } => { - Some(payment_data.payment_intent.payment_id.clone()) - } - _ => None, + authentication_id: String, +) -> CustomResult { + let (authentication_connector, three_ds_connector_account) = + utils::get_authentication_connector_data(state, key_store, &business_profile).await?; + let is_pull_mechanism_enabled = + check_if_pull_mechanism_for_external_3ds_enabled_from_connector_metadata( + three_ds_connector_account + .get_metadata() + .map(|metadata| metadata.expose()), + ); + let authentication = state + .store + .find_authentication_by_merchant_id_authentication_id( + business_profile.merchant_id.clone(), + authentication_id.clone(), + ) + .await + .to_not_found_response(errors::ApiErrorResponse::InternalServerError) + .attach_printable_lazy(|| format!("Error while fetching authentication record with authentication_id {authentication_id}"))?; + if !authentication.authentication_status.is_terminal_status() && is_pull_mechanism_enabled { + let router_data = transformers::construct_post_authentication_router_data( + authentication_connector.to_string(), + business_profile, + three_ds_connector_account, + &authentication, + )?; + let router_data = + utils::do_auth_connector_call(state, authentication_connector.to_string(), router_data) + .await?; + utils::update_trackers(state, router_data, authentication, None).await + } else { + Ok(authentication) } } -pub async fn perform_pre_authentication( +pub async fn perform_pre_authentication( state: &AppState, - authentication_connector_name: String, - authentication_flow_input: types::PreAuthenthenticationFlowInput<'_, F>, + key_store: &domain::MerchantKeyStore, + card_number: cards::CardNumber, + token: String, business_profile: &core_types::storage::BusinessProfile, - three_ds_connector_account: payments_core::helpers::MerchantConnectorAccountType, - payment_connector_account: payments_core::helpers::MerchantConnectorAccountType, -) -> CustomResult<(), ApiErrorResponse> { - let payment_id = get_payment_id_from_pre_authentication_flow_input(&authentication_flow_input); + acquirer_details: Option, + payment_id: Option, +) -> CustomResult { + let (authentication_connector, three_ds_connector_account) = + utils::get_authentication_connector_data(state, key_store, business_profile).await?; + let authentication_connector_name = authentication_connector.to_string(); let authentication = utils::create_new_authentication( state, business_profile.merchant_id.clone(), authentication_connector_name.clone(), + token, business_profile.profile_id.clone(), payment_id, three_ds_connector_account @@ -199,74 +139,15 @@ pub async fn perform_pre_authentication( .attach_printable("Error while finding mca_id from merchant_connector_account")?, ) .await?; - match authentication_flow_input { - types::PreAuthenthenticationFlowInput::PaymentAuthNFlow { - payment_data, - should_continue_confirm_transaction, - card_number, - } => { - let router_data = transformers::construct_pre_authentication_router_data( - authentication_connector_name.clone(), - card_number, - &three_ds_connector_account, - business_profile.merchant_id.clone(), - )?; - let router_data = utils::do_auth_connector_call( - state, - authentication_connector_name.clone(), - router_data, - ) - .await?; - let acquirer_details: types::AcquirerDetails = payment_connector_account - .get_metadata() - .get_required_value("merchant_connector_account.metadata")? - .peek() - .clone() - .parse_value("AcquirerDetails") - .change_context(ApiErrorResponse::PreconditionFailed { message: "acquirer_bin and acquirer_merchant_id not found in Payment Connector's Metadata".to_string()})?; - let authentication = utils::update_trackers( - state, - router_data, - authentication, - payment_data.token.clone(), - Some(acquirer_details), - ) - .await?; - if authentication.is_separate_authn_required() - || authentication.authentication_status.is_failed() - { - *should_continue_confirm_transaction = false; - // If flow is going through external authentication, set the poll_config in payment_data which can be fetched while sending next_action block in confirm response - let default_poll_config = core_types::PollConfig::default(); - let default_config_str = default_poll_config - .encode_to_string_of_json() - .change_context(errors::ApiErrorResponse::InternalServerError) - .attach_printable("Error while stringifying default poll config")?; - let poll_config = state - .store - .find_config_by_key_unwrap_or( - &core_types::PollConfig::get_poll_config_key(authentication_connector_name), - Some(default_config_str), - ) - .await - .change_context(errors::ApiErrorResponse::InternalServerError) - .attach_printable("The poll config was not found in the DB")?; - let poll_config: core_types::PollConfig = poll_config - .config - .parse_struct("PollConfig") - .change_context(errors::ApiErrorResponse::InternalServerError) - .attach_printable("Error while parsing PollConfig")?; - payment_data.poll_config = Some(poll_config) - } - payment_data.authentication = Some(authentication); - } - types::PreAuthenthenticationFlowInput::PaymentMethodAuthNFlow { - card_number: _, - other_fields: _, - } => { - // todo!("Payment method authN operation"); - } - }; - Ok(()) + let router_data = transformers::construct_pre_authentication_router_data( + authentication_connector_name.clone(), + card_number, + &three_ds_connector_account, + business_profile.merchant_id.clone(), + )?; + let router_data = + utils::do_auth_connector_call(state, authentication_connector_name, router_data).await?; + + utils::update_trackers(state, router_data, authentication, acquirer_details).await } diff --git a/crates/router/src/core/authentication/utils.rs b/crates/router/src/core/authentication/utils.rs index e9c0b3ffc68..bece5459236 100644 --- a/crates/router/src/core/authentication/utils.rs +++ b/crates/router/src/core/authentication/utils.rs @@ -1,3 +1,4 @@ +use common_utils::ext_traits::ValueExt; use error_stack::ResultExt; use crate::{ @@ -10,39 +11,39 @@ use crate::{ routes::AppState, services::{self, execute_connector_processing_step}, types::{ - api::{self, ConnectorCallType}, - authentication::AuthenticationResponseData, - storage, - transformers::ForeignFrom, - RouterData, + api, authentication::AuthenticationResponseData, domain, storage, + transformers::ForeignFrom, RouterData, }, + utils::OptionExt, }; -pub fn get_connector_name_if_separate_authn_supported( - connector_call_type: &ConnectorCallType, -) -> Option { +pub fn get_connector_data_if_separate_authn_supported( + connector_call_type: &api::ConnectorCallType, +) -> Option { match connector_call_type { - ConnectorCallType::PreDetermined(connector_data) => { + api::ConnectorCallType::PreDetermined(connector_data) => { if connector_data .connector_name .is_separate_authentication_supported() { - Some(connector_data.connector_name.to_string()) + Some(connector_data.clone()) } else { None } } - ConnectorCallType::Retryable(connectors) => connectors.first().and_then(|connector_data| { - if connector_data - .connector_name - .is_separate_authentication_supported() - { - Some(connector_data.connector_name.to_string()) - } else { - None - } - }), - ConnectorCallType::SessionMultiple(_) => None, + api::ConnectorCallType::Retryable(connectors) => { + connectors.first().and_then(|connector_data| { + if connector_data + .connector_name + .is_separate_authentication_supported() + { + Some(connector_data.clone()) + } else { + None + } + }) + } + api::ConnectorCallType::SessionMultiple(_) => None, } } @@ -50,7 +51,6 @@ pub async fn update_trackers( state: &AppState, router_data: RouterData, authentication: storage::Authentication, - token: Option, acquirer_details: Option, ) -> RouterResult { let authentication_update = match router_data.response { @@ -72,7 +72,6 @@ pub async fn update_trackers( message_version, connector_metadata, authentication_status: common_enums::AuthenticationStatus::Pending, - payment_method_id: token.map(|token| format!("eph_{}", token)), acquirer_bin: acquirer_details .as_ref() .map(|acquirer_details| acquirer_details.acquirer_bin.clone()), @@ -144,6 +143,7 @@ pub async fn create_new_authentication( state: &AppState, merchant_id: String, authentication_connector: String, + token: String, profile_id: String, payment_id: Option, merchant_connector_id: String, @@ -155,7 +155,7 @@ pub async fn create_new_authentication( merchant_id, authentication_connector, connector_authentication_id: None, - payment_method_id: "".into(), + payment_method_id: format!("eph_{}", token), authentication_type: None, authentication_status: common_enums::AuthenticationStatus::Started, authentication_lifecycle_status: common_enums::AuthenticationLifecycleStatus::Unused, @@ -221,3 +221,53 @@ where .to_payment_failed_response()?; Ok(router_data) } + +pub async fn get_authentication_connector_data( + state: &AppState, + key_store: &domain::MerchantKeyStore, + business_profile: &storage::BusinessProfile, +) -> RouterResult<( + api_models::enums::AuthenticationConnectors, + payments::helpers::MerchantConnectorAccountType, +)> { + let authentication_details: api_models::admin::AuthenticationConnectorDetails = + business_profile + .authentication_connector_details + .clone() + .get_required_value("authentication_details") + .change_context(errors::ApiErrorResponse::UnprocessableEntity { + message: "authentication_connector_details is not available in business profile" + .into(), + }) + .attach_printable("authentication_connector_details not configured by the merchant")? + .parse_value("AuthenticationConnectorDetails") + .change_context(errors::ApiErrorResponse::InternalServerError) + .attach_printable( + "Error while parsing authentication_connector_details from business_profile", + )?; + let authentication_connector = authentication_details + .authentication_connectors + .first() + .ok_or(errors::ApiErrorResponse::UnprocessableEntity { + message: format!( + "No authentication_connector found for profile_id {}", + business_profile.profile_id + ), + }) + .attach_printable( + "No authentication_connector found from merchant_account.authentication_details", + )? + .to_owned(); + let profile_id = &business_profile.profile_id; + let authentication_connector_mca = payments::helpers::get_merchant_connector_account( + state, + &business_profile.merchant_id, + None, + key_store, + profile_id, + authentication_connector.to_string().as_str(), + None, + ) + .await?; + Ok((authentication_connector, authentication_connector_mca)) +} diff --git a/crates/router/src/core/payments/helpers.rs b/crates/router/src/core/payments/helpers.rs index d2a2896de8c..f17a7075d43 100644 --- a/crates/router/src/core/payments/helpers.rs +++ b/crates/router/src/core/payments/helpers.rs @@ -36,6 +36,7 @@ use crate::{ connector, consts::{self, BASE64_ENGINE}, core::{ + authentication, errors::{self, CustomResult, RouterResult, StorageErrorExt}, mandate::helpers::MandateGenericData, payment_methods::{cards, vault, PaymentMethodRetrieve}, @@ -4356,6 +4357,103 @@ pub fn validate_mandate_data_and_future_usage( } } +pub enum PaymentExternalAuthenticationFlow { + PreAuthenticationFlow { + acquirer_details: authentication::types::AcquirerDetails, + card_number: ::cards::CardNumber, + token: String, + }, + PostAuthenticationFlow { + authentication_id: String, + }, +} + +pub async fn get_payment_external_authentication_flow_during_confirm( + state: &AppState, + key_store: &domain::MerchantKeyStore, + business_profile: &storage::BusinessProfile, + payment_data: &mut PaymentData, + connector_call_type: &api::ConnectorCallType, +) -> RouterResult> { + let authentication_id = payment_data.payment_attempt.authentication_id.clone(); + let is_authentication_type_3ds = payment_data.payment_attempt.authentication_type + == Some(common_enums::AuthenticationType::ThreeDs); + let separate_authentication_requested = payment_data + .payment_intent + .request_external_three_ds_authentication + .unwrap_or(false); + let separate_three_ds_authentication_attempted = payment_data + .payment_attempt + .external_three_ds_authentication_attempted + .unwrap_or(false); + let connector_supports_separate_authn = + authentication::utils::get_connector_data_if_separate_authn_supported(connector_call_type); + logger::info!("is_pre_authn_call {:?}", authentication_id.is_none()); + logger::info!( + "separate_authentication_requested {:?}", + separate_authentication_requested + ); + logger::info!( + "payment connector supports external authentication: {:?}", + connector_supports_separate_authn.is_some() + ); + let card_number = payment_data.payment_method_data.as_ref().and_then(|pmd| { + if let api_models::payments::PaymentMethodData::Card(card) = pmd { + Some(card.card_number.clone()) + } else { + None + } + }); + Ok(if separate_three_ds_authentication_attempted { + authentication_id.map(|authentication_id| { + PaymentExternalAuthenticationFlow::PostAuthenticationFlow { authentication_id } + }) + } else if separate_authentication_requested && is_authentication_type_3ds { + if let Some((connector_data, card_number)) = + connector_supports_separate_authn.zip(card_number) + { + let token = payment_data + .token + .clone() + .get_required_value("token") + .change_context(errors::ApiErrorResponse::InternalServerError) + .attach_printable( + "payment_data.token should not be None while making pre authentication call", + )?; + let payment_connector_mca = get_merchant_connector_account( + state, + &business_profile.merchant_id, + None, + key_store, + &business_profile.profile_id, + connector_data.connector_name.to_string().as_str(), + connector_data.merchant_connector_id.as_ref(), + ) + .await?; + let acquirer_details: authentication::types::AcquirerDetails = payment_connector_mca + .get_metadata() + .get_required_value("merchant_connector_account.metadata")? + .peek() + .clone() + .parse_value("AcquirerDetails") + .change_context(errors::ApiErrorResponse::PreconditionFailed { + message: + "acquirer_bin and acquirer_merchant_id not found in Payment Connector's Metadata" + .to_string(), + })?; + Some(PaymentExternalAuthenticationFlow::PreAuthenticationFlow { + card_number, + token, + acquirer_details, + }) + } else { + None + } + } else { + None + }) +} + pub fn get_redis_key_for_extended_card_info(merchant_id: &str, payment_id: &str) -> String { format!("{merchant_id}_{payment_id}_extended_card_info") } diff --git a/crates/router/src/core/payments/operations/payment_confirm.rs b/crates/router/src/core/payments/operations/payment_confirm.rs index 89c996236a3..3d4f2a3cad8 100644 --- a/crates/router/src/core/payments/operations/payment_confirm.rs +++ b/crates/router/src/core/payments/operations/payment_confirm.rs @@ -2,7 +2,7 @@ use std::marker::PhantomData; use api_models::{admin::ExtendedCardInfoConfig, enums::FrmSuggestion, payments::ExtendedCardInfo}; use async_trait::async_trait; -use common_utils::ext_traits::{AsyncExt, Encode, ValueExt}; +use common_utils::ext_traits::{AsyncExt, Encode, StringExt, ValueExt}; use error_stack::{report, ResultExt}; use futures::FutureExt; use masking::{ExposeInterface, PeekInterface}; @@ -28,6 +28,7 @@ use crate::{ routes::{app::ReqState, AppState}, services, types::{ + self, api::{self, ConnectorCallType, PaymentIdTypeExt}, domain, storage::{self, enums as storage_enums}, @@ -587,18 +588,6 @@ impl .map(|(payment_method_data, additional_payment_data)| { payment_method_data.apply_additional_payment_data(additional_payment_data) }); - let authentication = payment_attempt.authentication_id.as_ref().async_map(|authentication_id| async move { - state - .store - .find_authentication_by_merchant_id_authentication_id( - merchant_id.to_string(), - authentication_id.clone(), - ) - .await - .to_not_found_response(errors::ApiErrorResponse::InternalServerError) - .attach_printable_lazy(|| format!("Error while fetching authentication record with authentication_id {authentication_id}")) - }).await - .transpose()?; payment_attempt.payment_method_billing_address_id = payment_method_billing .as_ref() @@ -644,7 +633,7 @@ impl incremental_authorization_details: None, authorizations: vec![], frm_metadata: request.frm_metadata.clone(), - authentication, + authentication: None, recurring_details, poll_config: None, }; @@ -785,117 +774,81 @@ impl Domain CustomResult<(), errors::ApiErrorResponse> { - // if authentication has already happened, then payment_data.authentication will be Some. - // We should do post authn call to fetch the authentication data from 3ds connector - let authentication = payment_data.authentication.clone(); - let is_authentication_type_3ds = payment_data.payment_attempt.authentication_type - == Some(common_enums::AuthenticationType::ThreeDs); - let separate_authentication_requested = payment_data - .payment_intent - .request_external_three_ds_authentication - .unwrap_or(false); - let connector_supports_separate_authn = - authentication::utils::get_connector_name_if_separate_authn_supported( - connector_call_type, - ); - logger::info!("is_pre_authn_call {:?}", authentication.is_none()); - logger::info!( - "separate_authentication_requested {:?}", - separate_authentication_requested - ); - if let Some(payment_connector) = match connector_supports_separate_authn { - Some(payment_connector) - if is_authentication_type_3ds && separate_authentication_requested => - { - Some(payment_connector) - } - _ => None, - } { - let authentication_details: api_models::admin::AuthenticationConnectorDetails = - business_profile - .authentication_connector_details - .clone() - .get_required_value("authentication_details") - .attach_printable("authentication_details not configured by the merchant")? - .parse_value("AuthenticationDetails") - .change_context(errors::ApiErrorResponse::UnprocessableEntity { - message: "Invalid data format found for authentication_details".into(), - }) - .attach_printable( - "Error while parsing authentication_details from merchant_account", - )?; - let authentication_connector_name = authentication_details - .authentication_connectors - .first() - .ok_or(errors::ApiErrorResponse::UnprocessableEntity { message: format!("No authentication_connector found for profile_id {}", business_profile.profile_id) }) - - .attach_printable("No authentication_connector found from merchant_account.authentication_details")? - .to_string(); - let profile_id = &business_profile.profile_id; - let authentication_connector_mca = helpers::get_merchant_connector_account( + let external_authentication_flow = + helpers::get_payment_external_authentication_flow_during_confirm( state, - &business_profile.merchant_id, - None, key_store, - profile_id, - &authentication_connector_name, - None, + business_profile, + payment_data, + connector_call_type, ) .await?; - if let Some(authentication) = authentication { - // call post authn service - authentication::perform_post_authentication( + payment_data.authentication = match external_authentication_flow { + Some(helpers::PaymentExternalAuthenticationFlow::PreAuthenticationFlow { + acquirer_details, + card_number, + token, + }) => { + let authentication = authentication::perform_pre_authentication( state, - authentication_connector_name.clone(), - business_profile.clone(), - authentication_connector_mca, - authentication::types::PostAuthenthenticationFlowInput::PaymentAuthNFlow { - payment_data, - authentication, - should_continue_confirm_transaction, - }, + key_store, + card_number, + token, + business_profile, + Some(acquirer_details), + Some(payment_data.payment_attempt.payment_id.clone()), ) .await?; - } else { - let payment_connector_mca = helpers::get_merchant_connector_account( + if authentication.is_separate_authn_required() + || authentication.authentication_status.is_failed() + { + *should_continue_confirm_transaction = false; + let default_poll_config = types::PollConfig::default(); + let default_config_str = default_poll_config + .encode_to_string_of_json() + .change_context(errors::ApiErrorResponse::InternalServerError) + .attach_printable("Error while stringifying default poll config")?; + let poll_config = state + .store + .find_config_by_key_unwrap_or( + &types::PollConfig::get_poll_config_key( + authentication.authentication_connector.clone(), + ), + Some(default_config_str), + ) + .await + .change_context(errors::ApiErrorResponse::InternalServerError) + .attach_printable("The poll config was not found in the DB")?; + let poll_config: types::PollConfig = poll_config + .config + .parse_struct("PollConfig") + .change_context(errors::ApiErrorResponse::InternalServerError) + .attach_printable("Error while parsing PollConfig")?; + payment_data.poll_config = Some(poll_config) + } + Some(authentication) + } + Some(helpers::PaymentExternalAuthenticationFlow::PostAuthenticationFlow { + authentication_id, + }) => { + let authentication = authentication::perform_post_authentication( state, - &business_profile.merchant_id, - None, key_store, - profile_id, - &payment_connector, - None, + business_profile.clone(), + authentication_id.clone(), ) .await?; - // call pre authn service - let card_number = payment_data.payment_method_data.as_ref().and_then(|pmd| { - if let api_models::payments::PaymentMethodData::Card(card) = pmd { - Some(card.card_number.clone()) - } else { - None - } - }); - // External 3DS authentication is applicable only for cards - if let Some(card_number) = card_number { - authentication::perform_pre_authentication( - state, - authentication_connector_name, - authentication::types::PreAuthenthenticationFlowInput::PaymentAuthNFlow { - payment_data, - should_continue_confirm_transaction, - card_number, - }, - business_profile, - authentication_connector_mca, - payment_connector_mca, - ) - .await?; + //If authentication is not successful, skip the payment connector flows and mark the payment as failure + if authentication.authentication_status + != api_models::enums::AuthenticationStatus::Success + { + *should_continue_confirm_transaction = false; } + Some(authentication) } - Ok(()) - } else { - Ok(()) - } + None => None, + }; + Ok(()) } #[instrument(skip_all)] diff --git a/crates/router/src/types/api/authentication.rs b/crates/router/src/types/api/authentication.rs index dddb9c79879..92bcd1ae73b 100644 --- a/crates/router/src/types/api/authentication.rs +++ b/crates/router/src/types/api/authentication.rs @@ -15,7 +15,7 @@ pub struct Authentication; #[derive(Debug, Clone)] pub struct PostAuthentication; -use crate::{connector, services, types}; +use crate::{connector, services, types, types::storage}; #[derive(Clone, serde::Deserialize, Debug, serde::Serialize)] pub struct AcquirerDetails { @@ -34,6 +34,28 @@ pub struct AuthenticationResponse { pub acs_signed_content: Option, } +impl TryFrom for AuthenticationResponse { + type Error = error_stack::Report; + fn try_from(authentication: storage::Authentication) -> Result { + let trans_status = authentication.trans_status.ok_or(errors::ApiErrorResponse::InternalServerError).attach_printable("trans_status must be populated in authentication table authentication call is successful")?; + let acs_url = authentication + .acs_url + .map(|url| url::Url::from_str(&url)) + .transpose() + .change_context(errors::ApiErrorResponse::InternalServerError) + .attach_printable("not a valid URL")?; + Ok(Self { + trans_status, + acs_url, + challenge_request: authentication.challenge_request, + acs_reference_number: authentication.acs_reference_number, + acs_trans_id: authentication.acs_trans_id, + three_dsserver_trans_id: authentication.three_ds_server_trans_id, + acs_signed_content: authentication.acs_signed_content, + }) + } +} + #[derive(Clone, serde::Deserialize, Debug, serde::Serialize)] pub struct PostAuthenticationResponse { pub trans_status: String, From 5ec00d96de49ae0e0f76c5b19e22db11e7db6dd2 Mon Sep 17 00:00:00 2001 From: Mani Chandra <84711804+ThisIsMani@users.noreply.github.com> Date: Tue, 7 May 2024 14:57:05 +0530 Subject: [PATCH 15/34] feat(users): Create Token only support for pre-login user flow APIs (#4558) --- crates/api_models/src/events/user.rs | 11 +- crates/api_models/src/user.rs | 4 +- crates/api_models/src/user_role.rs | 1 + crates/router/src/core/user.rs | 169 +++++++++++++++++- crates/router/src/core/user_role.rs | 58 +++++- crates/router/src/routes/user.rs | 86 ++++++--- crates/router/src/routes/user_role.rs | 15 +- .../src/types/domain/user/decision_manager.rs | 40 ++++- 8 files changed, 343 insertions(+), 41 deletions(-) diff --git a/crates/api_models/src/events/user.rs b/crates/api_models/src/events/user.rs index 14676656d0f..b29b28f0136 100644 --- a/crates/api_models/src/events/user.rs +++ b/crates/api_models/src/events/user.rs @@ -14,8 +14,8 @@ use crate::user::{ CreateInternalUserRequest, DashboardEntryResponse, ForgotPasswordRequest, GetUserDetailsResponse, GetUserRoleDetailsRequest, GetUserRoleDetailsResponse, InviteUserRequest, ListUsersResponse, ReInviteUserRequest, ResetPasswordRequest, - SendVerifyEmailRequest, SignInResponse, SignInWithTokenResponse, SignUpRequest, - SignUpWithMerchantIdRequest, SwitchMerchantIdRequest, TokenResponse, + SendVerifyEmailRequest, SignInResponse, SignUpRequest, SignUpWithMerchantIdRequest, + SwitchMerchantIdRequest, TokenOrPayloadResponse, TokenResponse, UpdateUserAccountDetailsRequest, UserFromEmailRequest, UserMerchantCreate, VerifyEmailRequest, }; @@ -38,6 +38,12 @@ impl ApiEventMetric for VerifyTokenResponse { } } +impl ApiEventMetric for TokenOrPayloadResponse { + fn get_api_event_type(&self) -> Option { + Some(ApiEventsType::Miscellaneous) + } +} + common_utils::impl_misc_api_event_type!( SignUpRequest, SignUpWithMerchantIdRequest, @@ -62,7 +68,6 @@ common_utils::impl_misc_api_event_type!( SignInResponse, UpdateUserAccountDetailsRequest, GetUserDetailsResponse, - SignInWithTokenResponse, GetUserRoleDetailsRequest, GetUserRoleDetailsResponse, TokenResponse, diff --git a/crates/api_models/src/user.rs b/crates/api_models/src/user.rs index a6d5cf36358..fe4e37f8a8c 100644 --- a/crates/api_models/src/user.rs +++ b/crates/api_models/src/user.rs @@ -227,9 +227,9 @@ pub struct TokenResponse { #[derive(Debug, serde::Serialize)] #[serde(untagged)] -pub enum SignInWithTokenResponse { +pub enum TokenOrPayloadResponse { Token(TokenResponse), - SignInResponse(SignInResponse), + Payload(T), } #[derive(Debug, serde::Deserialize, serde::Serialize)] diff --git a/crates/api_models/src/user_role.rs b/crates/api_models/src/user_role.rs index 25c1c5ecd40..08d63d9097f 100644 --- a/crates/api_models/src/user_role.rs +++ b/crates/api_models/src/user_role.rs @@ -99,6 +99,7 @@ pub enum UserStatus { #[derive(Debug, serde::Deserialize, serde::Serialize)] pub struct AcceptInvitationRequest { pub merchant_ids: Vec, + // TODO: Remove this once the token only api is being used pub need_dashboard_entry_response: Option, } diff --git a/crates/router/src/core/user.rs b/crates/router/src/core/user.rs index 58a6452c9bb..6ad0afa9104 100644 --- a/crates/router/src/core/user.rs +++ b/crates/router/src/core/user.rs @@ -94,7 +94,7 @@ pub async fn get_user_details( pub async fn signup( state: AppState, request: user_api::SignUpRequest, -) -> UserResponse { +) -> UserResponse> { let new_user = domain::NewUser::try_from(request)?; new_user .get_new_merchant() @@ -117,13 +117,48 @@ pub async fn signup( let response = utils::user::get_dashboard_entry_response(&state, user_from_db, user_role, token.clone())?; + auth::cookies::set_cookie_response(user_api::TokenOrPayloadResponse::Payload(response), token) +} + +pub async fn signup_token_only_flow( + state: AppState, + request: user_api::SignUpRequest, +) -> UserResponse> { + let new_user = domain::NewUser::try_from(request)?; + new_user + .get_new_merchant() + .get_new_organization() + .insert_org_in_db(state.clone()) + .await?; + let user_from_db = new_user + .insert_user_and_merchant_in_db(state.clone()) + .await?; + let user_role = new_user + .insert_user_role_in_db( + state.clone(), + consts::user_role::ROLE_ID_ORGANIZATION_ADMIN.to_string(), + UserStatus::Active, + ) + .await?; + + let next_flow = + domain::NextFlow::from_origin(domain::Origin::SignUp, user_from_db.clone(), &state).await?; + + let token = next_flow + .get_token_with_user_role(&state, &user_role) + .await?; + + let response = user_api::TokenOrPayloadResponse::Token(user_api::TokenResponse { + token: token.clone(), + token_type: next_flow.get_flow().into(), + }); auth::cookies::set_cookie_response(response, token) } pub async fn signin( state: AppState, request: user_api::SignInRequest, -) -> UserResponse { +) -> UserResponse> { let user_from_db: domain::UserFromStorage = state .store .find_user_by_email(&request.email) @@ -161,16 +196,13 @@ pub async fn signin( let response = signin_strategy.get_signin_response(&state).await?; let token = utils::user::get_token_from_signin_response(&response); - auth::cookies::set_cookie_response( - user_api::SignInWithTokenResponse::SignInResponse(response), - token, - ) + auth::cookies::set_cookie_response(user_api::TokenOrPayloadResponse::Payload(response), token) } pub async fn signin_token_only_flow( state: AppState, request: user_api::SignInRequest, -) -> UserResponse { +) -> UserResponse> { let user_from_db: domain::UserFromStorage = state .store .find_user_by_email(&request.email) @@ -185,7 +217,7 @@ pub async fn signin_token_only_flow( let token = next_flow.get_token(&state).await?; - let response = user_api::SignInWithTokenResponse::Token(user_api::TokenResponse { + let response = user_api::TokenOrPayloadResponse::Token(user_api::TokenResponse { token: token.clone(), token_type: next_flow.get_flow().into(), }); @@ -820,6 +852,73 @@ pub async fn accept_invite_from_email( auth::cookies::set_cookie_response(response, token) } +#[cfg(feature = "email")] +pub async fn accept_invite_from_email_token_only_flow( + state: AppState, + user_token: auth::UserFromSinglePurposeToken, + request: user_api::AcceptInviteFromEmailRequest, +) -> UserResponse> { + let token = request.token.expose(); + + let email_token = auth::decode_jwt::(&token, &state) + .await + .change_context(UserErrors::LinkInvalid)?; + + auth::blacklist::check_email_token_in_blacklist(&state, &token).await?; + + let user_from_db: domain::UserFromStorage = state + .store + .find_user_by_email( + &email_token + .get_email() + .change_context(UserErrors::InternalServerError)?, + ) + .await + .change_context(UserErrors::InternalServerError)? + .into(); + + if user_from_db.get_user_id() != user_token.user_id { + return Err(UserErrors::LinkInvalid.into()); + } + + let merchant_id = email_token + .get_merchant_id() + .ok_or(UserErrors::LinkInvalid)?; + + let user_role = state + .store + .update_user_role_by_user_id_merchant_id( + user_from_db.get_user_id(), + merchant_id, + UserRoleUpdate::UpdateStatus { + status: UserStatus::Active, + modified_by: user_from_db.get_user_id().to_string(), + }, + ) + .await + .change_context(UserErrors::InternalServerError)?; + + let _ = auth::blacklist::insert_email_token_in_blacklist(&state, &token) + .await + .map_err(|e| logger::error!(?e)); + + let current_flow = domain::CurrentFlow::new( + user_token.origin, + domain::SPTFlow::AcceptInvitationFromEmail.into(), + )?; + let next_flow = current_flow.next(user_from_db.clone(), &state).await?; + + let token = next_flow + .get_token_with_user_role(&state, &user_role) + .await?; + + let response = user_api::TokenOrPayloadResponse::Token(user_api::TokenResponse { + token: token.clone(), + token_type: next_flow.get_flow().into(), + }); + auth::cookies::set_cookie_response(response, token) +} + pub async fn create_internal_user( state: AppState, request: user_api::CreateInternalUserRequest, @@ -1196,6 +1295,60 @@ pub async fn verify_email( auth::cookies::set_cookie_response(response, token) } +#[cfg(feature = "email")] +pub async fn verify_email_token_only_flow( + state: AppState, + user_token: auth::UserFromSinglePurposeToken, + req: user_api::VerifyEmailRequest, +) -> UserResponse> { + let token = req.token.clone().expose(); + let email_token = auth::decode_jwt::(&token, &state) + .await + .change_context(UserErrors::LinkInvalid)?; + + auth::blacklist::check_email_token_in_blacklist(&state, &token).await?; + + let user_from_email = state + .store + .find_user_by_email( + &email_token + .get_email() + .change_context(UserErrors::InternalServerError)?, + ) + .await + .change_context(UserErrors::InternalServerError)?; + + if user_from_email.user_id != user_token.user_id { + return Err(UserErrors::LinkInvalid.into()); + } + + let user_from_db: domain::UserFromStorage = state + .store + .update_user_by_user_id( + user_from_email.user_id.as_str(), + storage_user::UserUpdate::VerifyUser, + ) + .await + .change_context(UserErrors::InternalServerError)? + .into(); + + let _ = auth::blacklist::insert_email_token_in_blacklist(&state, &token) + .await + .map_err(|e| logger::error!(?e)); + + let current_flow = + domain::CurrentFlow::new(user_token.origin, domain::SPTFlow::VerifyEmail.into())?; + let next_flow = current_flow.next(user_from_db, &state).await?; + let token = next_flow.get_token(&state).await?; + + let response = user_api::TokenOrPayloadResponse::Token(user_api::TokenResponse { + token: token.clone(), + token_type: next_flow.get_flow().into(), + }); + + auth::cookies::set_cookie_response(response, token) +} + #[cfg(feature = "email")] pub async fn send_verification_mail( state: AppState, diff --git a/crates/router/src/core/user_role.rs b/crates/router/src/core/user_role.rs index 3f913b88a3f..ae20ec51ad4 100644 --- a/crates/router/src/core/user_role.rs +++ b/crates/router/src/core/user_role.rs @@ -172,8 +172,7 @@ pub async fn accept_invitation( state: AppState, user_token: auth::UserFromSinglePurposeToken, req: user_role_api::AcceptInvitationRequest, - _req_state: ReqState, -) -> UserResponse { +) -> UserResponse> { let user_role = futures::future::join_all(req.merchant_ids.iter().map(|merchant_id| async { state .store @@ -215,12 +214,65 @@ pub async fn accept_invitation( user_role, token.clone(), )?; - return auth::cookies::set_cookie_response(response, token); + return auth::cookies::set_cookie_response( + user_api::TokenOrPayloadResponse::Payload(response), + token, + ); } Ok(ApplicationResponse::StatusOk) } +pub async fn accept_invitation_token_only_flow( + state: AppState, + user_token: auth::UserFromSinglePurposeToken, + req: user_role_api::AcceptInvitationRequest, +) -> UserResponse> { + let user_role = futures::future::join_all(req.merchant_ids.iter().map(|merchant_id| async { + state + .store + .update_user_role_by_user_id_merchant_id( + user_token.user_id.as_str(), + merchant_id, + UserRoleUpdate::UpdateStatus { + status: UserStatus::Active, + modified_by: user_token.user_id.clone(), + }, + ) + .await + .map_err(|e| { + logger::error!("Error while accepting invitation {}", e); + }) + .ok() + })) + .await + .into_iter() + .reduce(Option::or) + .flatten() + .ok_or(UserErrors::MerchantIdNotFound)?; + + let user_from_db: domain::UserFromStorage = state + .store + .find_user_by_id(user_token.user_id.as_str()) + .await + .change_context(UserErrors::InternalServerError)? + .into(); + + let current_flow = + domain::CurrentFlow::new(user_token.origin, domain::SPTFlow::MerchantSelect.into())?; + let next_flow = current_flow.next(user_from_db.clone(), &state).await?; + + let token = next_flow + .get_token_with_user_role(&state, &user_role) + .await?; + + let response = user_api::TokenOrPayloadResponse::Token(user_api::TokenResponse { + token: token.clone(), + token_type: next_flow.get_flow().into(), + }); + auth::cookies::set_cookie_response(response, token) +} + pub async fn delete_user_role( state: AppState, user_from_token: auth::UserFromToken, diff --git a/crates/router/src/routes/user.rs b/crates/router/src/routes/user.rs index ede6edbceb8..a3f18168501 100644 --- a/crates/router/src/routes/user.rs +++ b/crates/router/src/routes/user.rs @@ -57,15 +57,23 @@ pub async fn user_signup( state: web::Data, http_req: HttpRequest, json_payload: web::Json, + query: web::Query, ) -> HttpResponse { let flow = Flow::UserSignUp; let req_payload = json_payload.into_inner(); + let is_token_only = query.into_inner().token_only; Box::pin(api::server_wrap( flow.clone(), state, &http_req, req_payload.clone(), - |state, _, req_body, _| user_core::signup(state, req_body), + |state, _, req_body, _| async move { + if let Some(true) = is_token_only { + user_core::signup_token_only_flow(state, req_body).await + } else { + user_core::signup(state, req_body).await + } + }, &auth::NoAuth, api_locking::LockAction::NotApplicable, )) @@ -428,18 +436,37 @@ pub async fn accept_invite_from_email( state: web::Data, req: HttpRequest, payload: web::Json, + query: web::Query, ) -> HttpResponse { let flow = Flow::AcceptInviteFromEmail; - Box::pin(api::server_wrap( - flow, - state.clone(), - &req, - payload.into_inner(), - |state, _, request_payload, _| user_core::accept_invite_from_email(state, request_payload), - &auth::NoAuth, - api_locking::LockAction::NotApplicable, - )) - .await + let is_token_only = query.into_inner().token_only; + if let Some(true) = is_token_only { + Box::pin(api::server_wrap( + flow.clone(), + state, + &req, + payload.into_inner(), + |state, user, req_payload, _| { + user_core::accept_invite_from_email_token_only_flow(state, user, req_payload) + }, + &auth::SinglePurposeJWTAuth(common_enums::TokenPurpose::AcceptInvitationFromEmail), + api_locking::LockAction::NotApplicable, + )) + .await + } else { + Box::pin(api::server_wrap( + flow, + state.clone(), + &req, + payload.into_inner(), + |state, _, request_payload, _| { + user_core::accept_invite_from_email(state, request_payload) + }, + &auth::NoAuth, + api_locking::LockAction::NotApplicable, + )) + .await + } } #[cfg(feature = "email")] @@ -447,18 +474,35 @@ pub async fn verify_email( state: web::Data, http_req: HttpRequest, json_payload: web::Json, + query: web::Query, ) -> HttpResponse { let flow = Flow::VerifyEmail; - Box::pin(api::server_wrap( - flow.clone(), - state, - &http_req, - json_payload.into_inner(), - |state, _, req_payload, _| user_core::verify_email(state, req_payload), - &auth::NoAuth, - api_locking::LockAction::NotApplicable, - )) - .await + let is_token_only = query.into_inner().token_only; + if let Some(true) = is_token_only { + Box::pin(api::server_wrap( + flow.clone(), + state, + &http_req, + json_payload.into_inner(), + |state, user, req_payload, _| { + user_core::verify_email_token_only_flow(state, user, req_payload) + }, + &auth::SinglePurposeJWTAuth(common_enums::TokenPurpose::VerifyEmail), + api_locking::LockAction::NotApplicable, + )) + .await + } else { + Box::pin(api::server_wrap( + flow.clone(), + state, + &http_req, + json_payload.into_inner(), + |state, _, req_payload, _| user_core::verify_email(state, req_payload), + &auth::NoAuth, + api_locking::LockAction::NotApplicable, + )) + .await + } } #[cfg(feature = "email")] diff --git a/crates/router/src/routes/user_role.rs b/crates/router/src/routes/user_role.rs index 59c07b79855..d406783cc71 100644 --- a/crates/router/src/routes/user_role.rs +++ b/crates/router/src/routes/user_role.rs @@ -1,5 +1,8 @@ use actix_web::{web, HttpRequest, HttpResponse}; -use api_models::user_role::{self as user_role_api, role as role_api}; +use api_models::{ + user as user_api, + user_role::{self as user_role_api, role as role_api}, +}; use common_enums::TokenPurpose; use router_env::Flow; @@ -206,15 +209,23 @@ pub async fn accept_invitation( state: web::Data, req: HttpRequest, json_payload: web::Json, + query: web::Query, ) -> HttpResponse { let flow = Flow::AcceptInvitation; let payload = json_payload.into_inner(); + let is_token_only = query.into_inner().token_only; Box::pin(api::server_wrap( flow, state.clone(), &req, payload, - user_role_core::accept_invitation, + |state, user, req_body, _| async move { + if let Some(true) = is_token_only { + user_role_core::accept_invitation_token_only_flow(state, user, req_body).await + } else { + user_role_core::accept_invitation(state, user, req_body).await + } + }, &auth::SinglePurposeJWTAuth(TokenPurpose::AcceptInvite), api_locking::LockAction::NotApplicable, )) diff --git a/crates/router/src/types/domain/user/decision_manager.rs b/crates/router/src/types/domain/user/decision_manager.rs index e460bc74649..616c595b244 100644 --- a/crates/router/src/types/domain/user/decision_manager.rs +++ b/crates/router/src/types/domain/user/decision_manager.rs @@ -7,6 +7,7 @@ use crate::{ core::errors::{StorageErrorExt, UserErrors, UserResult}, routes::AppState, services::authentication as auth, + utils, }; #[derive(Eq, PartialEq, Clone, Copy)] @@ -150,8 +151,9 @@ const VERIFY_EMAIL_FLOW: [UserFlow; 5] = [ UserFlow::JWTFlow(JWTFlow::UserInfo), ]; -const ACCEPT_INVITATION_FROM_EMAIL_FLOW: [UserFlow; 4] = [ +const ACCEPT_INVITATION_FROM_EMAIL_FLOW: [UserFlow; 5] = [ UserFlow::SPTFlow(SPTFlow::TOTP), + UserFlow::SPTFlow(SPTFlow::VerifyEmail), UserFlow::SPTFlow(SPTFlow::AcceptInvitationFromEmail), UserFlow::SPTFlow(SPTFlow::ForceSetPassword), UserFlow::JWTFlow(JWTFlow::UserInfo), @@ -234,16 +236,38 @@ impl NextFlow { { self.user.get_verification_days_left(state)?; } - let user_role = self .user .get_preferred_or_active_user_role_from_db(state) .await .to_not_found_response(UserErrors::InternalServerError)?; + utils::user_role::set_role_permissions_in_cache_by_user_role(state, &user_role) + .await; + jwt_flow.generate_jwt(state, self, &user_role).await } } } + + pub async fn get_token_with_user_role( + &self, + state: &AppState, + user_role: &UserRole, + ) -> UserResult> { + match self.next_flow { + UserFlow::SPTFlow(spt_flow) => spt_flow.generate_spt(state, self).await, + UserFlow::JWTFlow(jwt_flow) => { + #[cfg(feature = "email")] + { + self.user.get_verification_days_left(state)?; + } + utils::user_role::set_role_permissions_in_cache_by_user_role(state, user_role) + .await; + + jwt_flow.generate_jwt(state, self, user_role).await + } + } + } } impl From for TokenPurpose { @@ -274,3 +298,15 @@ impl From for TokenPurpose { } } } + +impl From for UserFlow { + fn from(value: SPTFlow) -> Self { + Self::SPTFlow(value) + } +} + +impl From for UserFlow { + fn from(value: JWTFlow) -> Self { + Self::JWTFlow(value) + } +} From 99bbc3982fa30f6ffd43334b1fa5da963975fe93 Mon Sep 17 00:00:00 2001 From: Shankar Singh C <83439957+ShankarSinghC@users.noreply.github.com> Date: Tue, 7 May 2024 15:21:20 +0530 Subject: [PATCH 16/34] refactor: remove `configs/pg_agnostic_mit` api as it will not be used (#4486) Co-authored-by: hyperswitch-bot[bot] <148525504+hyperswitch-bot[bot]@users.noreply.github.com> --- crates/api_models/src/routing.rs | 7 --- crates/router/src/core/payments.rs | 28 ++++------- crates/router/src/core/payments/operations.rs | 1 + .../payments/operations/payment_response.rs | 36 +++++--------- .../router/src/core/payments/tokenization.rs | 18 +------ crates/router/src/core/routing.rs | 32 +------------ crates/router/src/core/routing/helpers.rs | 47 ------------------- crates/router/src/routes/app.rs | 4 -- crates/router/src/routes/lock_utils.rs | 1 - crates/router/src/routes/routing.rs | 35 -------------- crates/router/src/types/api/routing.rs | 6 +-- crates/router_env/src/logger/types.rs | 2 - 12 files changed, 29 insertions(+), 188 deletions(-) diff --git a/crates/api_models/src/routing.rs b/crates/api_models/src/routing.rs index 723e6eccc36..f3d966f3d9d 100644 --- a/crates/api_models/src/routing.rs +++ b/crates/api_models/src/routing.rs @@ -299,13 +299,6 @@ impl From for ast::ConnectorChoice { } } -#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, PartialEq, Eq)] -pub struct DetailedConnectorChoice { - pub enabled: bool, -} - -impl common_utils::events::ApiEventMetric for DetailedConnectorChoice {} - #[derive(Debug, Copy, Clone, serde::Serialize, serde::Deserialize, strum::Display, ToSchema)] #[serde(rename_all = "snake_case")] #[strum(serialize_all = "snake_case")] diff --git a/crates/router/src/core/payments.rs b/crates/router/src/core/payments.rs index 815c604a0e3..07bceefb0f9 100644 --- a/crates/router/src/core/payments.rs +++ b/crates/router/src/core/payments.rs @@ -314,6 +314,7 @@ where &merchant_account, &key_store, &mut payment_data, + &business_profile, ) .await?; @@ -415,6 +416,7 @@ where &merchant_account, &key_store, &mut payment_data, + &business_profile, ) .await?; @@ -3320,6 +3322,7 @@ where routing_data, connector_data, mandate_type, + business_profile.is_connector_agnostic_mit_enabled, ) .await; } @@ -3377,6 +3380,7 @@ where routing_data, connector_data, mandate_type, + business_profile.is_connector_agnostic_mit_enabled, ) .await; } @@ -3400,6 +3404,7 @@ pub async fn decide_multiplex_connector_for_normal_or_recurring_payment, mandate_type: Option, + is_connector_agnostic_mit_enabled: Option, ) -> RouterResult { match ( payment_data.payment_intent.setup_future_usage, @@ -3442,22 +3447,6 @@ pub async fn decide_multiplex_connector_for_normal_or_recurring_payment, connector: enums::Connector, payment_method_info: &storage::PaymentMethod, ) -> bool { @@ -3597,7 +3586,7 @@ pub fn is_network_transaction_id_flow( .network_transaction_id_supported_connectors .connector_list; - pg_agnostic == "true" + is_connector_agnostic_mit_enabled == Some(true) && payment_method_info.payment_method == Some(storage_enums::PaymentMethod::Card) && ntid_supported_connectors.contains(&connector) && payment_method_info.network_transaction_id.is_some() @@ -3827,6 +3816,7 @@ where routing_data, connector_data, mandate_type, + business_profile.is_connector_agnostic_mit_enabled, ) .await } diff --git a/crates/router/src/core/payments/operations.rs b/crates/router/src/core/payments/operations.rs index c214decb0b4..da9a2108691 100644 --- a/crates/router/src/core/payments/operations.rs +++ b/crates/router/src/core/payments/operations.rs @@ -241,6 +241,7 @@ pub trait PostUpdateTracker: Send { _merchant_account: &domain::MerchantAccount, _key_store: &domain::MerchantKeyStore, _payment_data: &mut PaymentData, + _business_profile: &storage::business_profile::BusinessProfile, ) -> CustomResult<(), errors::ApiErrorResponse> where F: 'b + Clone + Send + Sync, diff --git a/crates/router/src/core/payments/operations/payment_response.rs b/crates/router/src/core/payments/operations/payment_response.rs index f4408749fd3..b40ee8e5c9c 100644 --- a/crates/router/src/core/payments/operations/payment_response.rs +++ b/crates/router/src/core/payments/operations/payment_response.rs @@ -89,13 +89,13 @@ impl PostUpdateTracker, types::PaymentsAuthor merchant_account: &domain::MerchantAccount, key_store: &domain::MerchantKeyStore, payment_data: &mut PaymentData, + business_profile: &storage::business_profile::BusinessProfile, ) -> CustomResult<(), errors::ApiErrorResponse> where F: 'b + Clone + Send + Sync, { let customer_id = payment_data.payment_intent.customer_id.clone(); let save_payment_data = tokenization::SavePaymentMethodData::from(resp); - let profile_id = payment_data.payment_intent.profile_id.clone(); let connector_name = payment_data .payment_attempt @@ -125,8 +125,8 @@ impl PostUpdateTracker, types::PaymentsAuthor key_store, Some(resp.request.amount), Some(resp.request.currency), - profile_id, billing_name.clone(), + business_profile, )); let is_connector_mandate = resp.request.customer_acceptance.is_some() @@ -169,11 +169,12 @@ impl PostUpdateTracker, types::PaymentsAuthor let key_store = key_store.clone(); let state = state.clone(); let customer_id = payment_data.payment_intent.customer_id.clone(); - let profile_id = payment_data.payment_intent.profile_id.clone(); let merchant_connector_id = payment_data.payment_attempt.merchant_connector_id.clone(); let payment_attempt = payment_data.payment_attempt.clone(); + let business_profile = business_profile.clone(); + let amount = resp.request.amount; let currency = resp.request.currency; let payment_method_type = resp.request.payment_method_type; @@ -195,8 +196,8 @@ impl PostUpdateTracker, types::PaymentsAuthor &key_store, Some(amount), Some(currency), - profile_id, billing_name, + &business_profile, )) .await; @@ -388,6 +389,7 @@ impl PostUpdateTracker, types::PaymentsSyncData> for merchant_account: &domain::MerchantAccount, _key_store: &domain::MerchantKeyStore, payment_data: &mut PaymentData, + business_profile: &storage::business_profile::BusinessProfile, ) -> CustomResult<(), errors::ApiErrorResponse> where F: 'b + Clone + Send + Sync, @@ -398,6 +400,7 @@ impl PostUpdateTracker, types::PaymentsSyncData> for resp.status, resp.response.clone(), merchant_account.storage_scheme, + business_profile.is_connector_agnostic_mit_enabled, ) .await?; Ok(()) @@ -587,6 +590,7 @@ impl PostUpdateTracker, types::SetupMandateRequestDa merchant_account: &domain::MerchantAccount, key_store: &domain::MerchantKeyStore, payment_data: &mut PaymentData, + business_profile: &storage::business_profile::BusinessProfile, ) -> CustomResult<(), errors::ApiErrorResponse> where F: 'b + Clone + Send + Sync, @@ -598,7 +602,6 @@ impl PostUpdateTracker, types::SetupMandateRequestDa .and_then(|address| address.get_optional_full_name()); let save_payment_data = tokenization::SavePaymentMethodData::from(resp); let customer_id = payment_data.payment_intent.customer_id.clone(); - let profile_id = payment_data.payment_intent.profile_id.clone(); let connector_name = payment_data .payment_attempt .connector @@ -622,8 +625,8 @@ impl PostUpdateTracker, types::SetupMandateRequestDa key_store, resp.request.amount, Some(resp.request.currency), - profile_id, billing_name, + business_profile, )) .await?; @@ -674,6 +677,7 @@ impl PostUpdateTracker, types::CompleteAuthorizeData merchant_account: &domain::MerchantAccount, _key_store: &domain::MerchantKeyStore, payment_data: &mut PaymentData, + business_profile: &storage::business_profile::BusinessProfile, ) -> CustomResult<(), errors::ApiErrorResponse> where F: 'b + Clone + Send + Sync, @@ -684,6 +688,7 @@ impl PostUpdateTracker, types::CompleteAuthorizeData resp.status, resp.response.clone(), merchant_account.storage_scheme, + business_profile.is_connector_agnostic_mit_enabled, ) .await?; Ok(()) @@ -1224,6 +1229,7 @@ async fn update_payment_method_status_and_ntid( attempt_status: common_enums::AttemptStatus, payment_response: Result, storage_scheme: enums::MerchantStorageScheme, + is_connector_agnostic_mit_enabled: Option, ) -> RouterResult<()> { if let Some(id) = &payment_data.payment_attempt.payment_method_id { let pm = state @@ -1244,23 +1250,7 @@ async fn update_payment_method_status_and_ntid( let network_transaction_id = if let Some(network_transaction_id) = pm_resp_network_transaction_id { - let profile_id = payment_data - .payment_intent - .profile_id - .as_ref() - .ok_or(errors::ApiErrorResponse::ResourceIdNotFound)?; - - let pg_agnostic = state - .store - .find_config_by_key_unwrap_or( - &format!("pg_agnostic_mandate_{}", profile_id), - Some("false".to_string()), - ) - .await - .change_context(errors::ApiErrorResponse::InternalServerError) - .attach_printable("The pg_agnostic config was not found in the DB")?; - - if &pg_agnostic.config == "true" + if is_connector_agnostic_mit_enabled == Some(true) && payment_data.payment_intent.setup_future_usage == Some(diesel_models::enums::FutureUsage::OffSession) { diff --git a/crates/router/src/core/payments/tokenization.rs b/crates/router/src/core/payments/tokenization.rs index 304e6edd62e..958c3f4362f 100644 --- a/crates/router/src/core/payments/tokenization.rs +++ b/crates/router/src/core/payments/tokenization.rs @@ -64,8 +64,8 @@ pub async fn save_payment_method( key_store: &domain::MerchantKeyStore, amount: Option, currency: Option, - profile_id: Option, billing_name: Option>, + business_profile: &storage::business_profile::BusinessProfile, ) -> RouterResult<(Option, Option)> where FData: mandate::MandateBehaviour + Clone, @@ -91,21 +91,7 @@ where let network_transaction_id = if let Some(network_transaction_id) = network_transaction_id { - let profile_id = profile_id - .as_ref() - .ok_or(errors::ApiErrorResponse::ResourceIdNotFound)?; - - let pg_agnostic = state - .store - .find_config_by_key_unwrap_or( - &format!("pg_agnostic_mandate_{}", profile_id), - Some("false".to_string()), - ) - .await - .change_context(errors::ApiErrorResponse::InternalServerError) - .attach_printable("The pg_agnostic config was not found in the DB")?; - - if &pg_agnostic.config == "true" + if business_profile.is_connector_agnostic_mit_enabled == Some(true) && save_payment_method_data.request.get_setup_future_usage() == Some(storage_enums::FutureUsage::OffSession) { diff --git a/crates/router/src/core/routing.rs b/crates/router/src/core/routing.rs index bec90c51e9c..31825617398 100644 --- a/crates/router/src/core/routing.rs +++ b/crates/router/src/core/routing.rs @@ -9,6 +9,7 @@ use api_models::{ }; #[cfg(not(feature = "business_profile_routing"))] use common_utils::ext_traits::{Encode, StringExt}; +#[cfg(not(feature = "business_profile_routing"))] use diesel_models::configs; #[cfg(feature = "business_profile_routing")] use diesel_models::routing_algorithm::RoutingAlgorithm; @@ -806,37 +807,6 @@ pub async fn retrieve_linked_routing_config( } } -pub async fn upsert_connector_agnostic_mandate_config( - state: AppState, - business_profile_id: &str, - mandate_config: routing_types::DetailedConnectorChoice, -) -> RouterResponse { - let key = helpers::get_pg_agnostic_mandate_config_key(business_profile_id); - - let mandate_config_str = mandate_config.enabled.to_string(); - - let find_config = state - .store - .find_config_by_key_unwrap_or(&key, Some(mandate_config_str.clone())) - .await - .change_context(errors::ApiErrorResponse::InternalServerError) - .attach_printable("error saving pg agnostic mandate config to db")?; - - if find_config.config != mandate_config_str { - let config_update = configs::ConfigUpdate::Update { - config: Some(mandate_config_str), - }; - state - .store - .update_config_by_key(&key, config_update) - .await - .change_context(errors::ApiErrorResponse::InternalServerError) - .attach_printable("error saving pg agnostic mandate config to db")?; - } - - Ok(service_api::ApplicationResponse::Json(mandate_config)) -} - pub async fn retrieve_default_routing_config_for_profiles( state: AppState, merchant_account: domain::MerchantAccount, diff --git a/crates/router/src/core/routing/helpers.rs b/crates/router/src/core/routing/helpers.rs index fb86ba5a87b..3702498767a 100644 --- a/crates/router/src/core/routing/helpers.rs +++ b/crates/router/src/core/routing/helpers.rs @@ -272,47 +272,6 @@ pub async fn update_business_profile_active_algorithm_ref( Ok(()) } -pub async fn get_merchant_connector_agnostic_mandate_config( - db: &dyn StorageInterface, - business_profile_id: &str, -) -> RouterResult> { - let key = get_pg_agnostic_mandate_config_key(business_profile_id); - let maybe_config = db.find_config_by_key(&key).await; - - match maybe_config { - Ok(config) => config - .config - .parse_struct("Vec") - .change_context(errors::ApiErrorResponse::InternalServerError) - .attach_printable("pg agnostic mandate config has invalid structure"), - - Err(e) if e.current_context().is_db_not_found() => { - let new_mandate_config: Vec = Vec::new(); - - let serialized = new_mandate_config - .encode_to_string_of_json() - .change_context(errors::ApiErrorResponse::InternalServerError) - .attach_printable("error serializing newly created pg agnostic mandate config")?; - - let new_config = configs::ConfigNew { - key, - config: serialized, - }; - - db.insert_config(new_config) - .await - .change_context(errors::ApiErrorResponse::InternalServerError) - .attach_printable("error inserting new pg agnostic mandate config in db")?; - - Ok(new_mandate_config) - } - - Err(e) => Err(e) - .change_context(errors::ApiErrorResponse::InternalServerError) - .attach_printable("error fetching pg agnostic mandate config for merchant from db"), - } -} - pub async fn validate_connectors_in_routing_config( db: &dyn StorageInterface, key_store: &domain::MerchantKeyStore, @@ -441,12 +400,6 @@ pub fn get_routing_dictionary_key(merchant_id: &str) -> String { format!("routing_dict_{merchant_id}") } -/// Provides the identifier for the specific merchant's agnostic_mandate_config -#[inline(always)] -pub fn get_pg_agnostic_mandate_config_key(business_profile_id: &str) -> String { - format!("pg_agnostic_mandate_{business_profile_id}") -} - /// Provides the identifier for the specific merchant's default_config #[inline(always)] pub fn get_default_config_key( diff --git a/crates/router/src/routes/app.rs b/crates/router/src/routes/app.rs index 6a8a4ee5e03..0ee44429756 100644 --- a/crates/router/src/routes/app.rs +++ b/crates/router/src/routes/app.rs @@ -466,10 +466,6 @@ impl Routing { ) })), ) - .service( - web::resource("/business_profile/{business_profile_id}/configs/pg_agnostic_mit") - .route(web::post().to(cloud_routing::upsert_connector_agnostic_mandate_config)), - ) .service( web::resource("/default") .route(web::get().to(|state, req| { diff --git a/crates/router/src/routes/lock_utils.rs b/crates/router/src/routes/lock_utils.rs index bf91f8055d1..1e301b069ae 100644 --- a/crates/router/src/routes/lock_utils.rs +++ b/crates/router/src/routes/lock_utils.rs @@ -231,7 +231,6 @@ impl From for ApiIdentifier { | Flow::ReconTokenRequest | Flow::ReconServiceRequest | Flow::ReconVerifyToken => Self::Recon, - Flow::CreateConnectorAgnosticMandateConfig => Self::Routing, Flow::RetrievePollStatus => Self::Poll, } diff --git a/crates/router/src/routes/routing.rs b/crates/router/src/routes/routing.rs index 8438424546c..556e91d8694 100644 --- a/crates/router/src/routes/routing.rs +++ b/crates/router/src/routes/routing.rs @@ -566,41 +566,6 @@ pub async fn routing_retrieve_linked_config( } } -#[cfg(feature = "olap")] -#[instrument(skip_all)] -pub async fn upsert_connector_agnostic_mandate_config( - state: web::Data, - req: HttpRequest, - json_payload: web::Json, - path: web::Path, -) -> impl Responder { - use crate::services::authentication::AuthenticationData; - - let flow = Flow::CreateConnectorAgnosticMandateConfig; - let business_profile_id = path.into_inner(); - - Box::pin(oss_api::server_wrap( - flow, - state, - &req, - json_payload.into_inner(), - |state, _auth: AuthenticationData, mandate_config, _| { - Box::pin(routing::upsert_connector_agnostic_mandate_config( - state, - &business_profile_id, - mandate_config, - )) - }, - auth::auth_type( - &auth::ApiKeyAuth, - &auth::JWTAuth(Permission::RoutingWrite), - req.headers(), - ), - api_locking::LockAction::NotApplicable, - )) - .await -} - #[cfg(feature = "olap")] #[instrument(skip_all)] pub async fn routing_retrieve_default_config_for_profiles( diff --git a/crates/router/src/types/api/routing.rs b/crates/router/src/types/api/routing.rs index faafac76e3d..4f982d543df 100644 --- a/crates/router/src/types/api/routing.rs +++ b/crates/router/src/types/api/routing.rs @@ -3,9 +3,9 @@ pub use api_models::routing::RoutableChoiceKind; pub use api_models::{ enums as api_enums, routing::{ - ConnectorVolumeSplit, DetailedConnectorChoice, RoutableConnectorChoice, RoutingAlgorithm, - RoutingAlgorithmKind, RoutingAlgorithmRef, RoutingConfigRequest, RoutingDictionary, - RoutingDictionaryRecord, StraightThroughAlgorithm, + ConnectorVolumeSplit, RoutableConnectorChoice, RoutingAlgorithm, RoutingAlgorithmKind, + RoutingAlgorithmRef, RoutingConfigRequest, RoutingDictionary, RoutingDictionaryRecord, + StraightThroughAlgorithm, }, }; diff --git a/crates/router_env/src/logger/types.rs b/crates/router_env/src/logger/types.rs index e8ffe0685b2..14b235eadb2 100644 --- a/crates/router_env/src/logger/types.rs +++ b/crates/router_env/src/logger/types.rs @@ -210,8 +210,6 @@ pub enum Flow { RoutingRetrieveConfig, /// Routing retrieve active config RoutingRetrieveActiveConfig, - /// Update connector agnostic mandate config - CreateConnectorAgnosticMandateConfig, /// Routing retrieve default config RoutingRetrieveDefaultConfig, /// Routing retrieve dictionary From 2216a88d25c42ede9862f6d036e7b0586a2e7c28 Mon Sep 17 00:00:00 2001 From: Chethan Rao <70657455+Chethan-rao@users.noreply.github.com> Date: Tue, 7 May 2024 16:05:32 +0530 Subject: [PATCH 17/34] chore: address Rust 1.78 clippy lints (#4545) Co-authored-by: hyperswitch-bot[bot] <148525504+hyperswitch-bot[bot]@users.noreply.github.com> --- crates/analytics/src/clickhouse.rs | 4 +- crates/analytics/src/lib.rs | 10 +- crates/analytics/src/payments/accumulator.rs | 10 +- crates/analytics/src/query.rs | 16 +-- crates/analytics/src/refunds.rs | 1 - crates/analytics/src/sdk_events.rs | 5 - crates/analytics/src/sqlx.rs | 4 +- crates/analytics/src/types.rs | 5 - crates/api_models/src/admin.rs | 6 +- crates/api_models/src/api_keys.rs | 4 +- crates/api_models/src/payments.rs | 18 ++- crates/api_models/src/routing.rs | 10 +- .../api_models/src/user/dashboard_metadata.rs | 2 +- crates/common_utils/src/crypto.rs | 20 ++-- crates/common_utils/src/ext_traits.rs | 4 +- crates/common_utils/src/pii.rs | 12 +- crates/common_utils/src/static_cache.rs | 2 + crates/connector_configs/src/transformer.rs | 33 +++--- crates/diesel_models/src/encryption.rs | 4 +- .../src/query/payment_attempt.rs | 6 +- .../diesel_models/src/query/payout_attempt.rs | 10 +- crates/drainer/src/handler.rs | 2 +- crates/drainer/src/lib.rs | 2 +- crates/euclid/src/dssa/graph.rs | 6 +- crates/euclid/src/frontend/ast/parser.rs | 6 +- crates/euclid_macros/src/inner/knowledge.rs | 42 ++++--- crates/euclid_wasm/src/lib.rs | 4 +- .../hyperswitch_domain_models/src/mandates.rs | 3 +- crates/masking/src/diesel.rs | 4 +- crates/openapi/src/openapi.rs | 2 + crates/pm_auth/src/types.rs | 6 +- crates/router/src/analytics.rs | 4 +- crates/router/src/bin/scheduler.rs | 4 +- .../stripe/payment_intents/types.rs | 4 +- crates/router/src/configs/settings.rs | 26 ++--- .../router/src/connector/aci/transformers.rs | 11 +- .../src/connector/adyen/transformers.rs | 8 +- crates/router/src/connector/airwallex.rs | 18 +-- .../src/connector/airwallex/transformers.rs | 13 +-- .../router/src/connector/authorizedotnet.rs | 3 +- .../connector/authorizedotnet/transformers.rs | 16 +-- .../src/connector/bambora/transformers.rs | 16 +-- crates/router/src/connector/bankofamerica.rs | 3 +- .../connector/bankofamerica/transformers.rs | 18 +-- .../src/connector/billwerk/transformers.rs | 16 +-- .../src/connector/bitpay/transformers.rs | 15 +-- .../src/connector/bluesnap/transformers.rs | 64 ++++------ .../router/src/connector/boku/transformers.rs | 2 +- crates/router/src/connector/braintree.rs | 6 +- .../braintree_graphql_transformers.rs | 18 +-- .../src/connector/braintree/transformers.rs | 4 +- .../src/connector/cashtocode/transformers.rs | 4 +- .../src/connector/checkout/transformers.rs | 16 +-- crates/router/src/connector/coinbase.rs | 3 +- .../src/connector/cryptopay/transformers.rs | 11 +- crates/router/src/connector/cybersource.rs | 12 +- .../src/connector/cybersource/transformers.rs | 24 +--- crates/router/src/connector/dlocal.rs | 3 +- .../src/connector/dlocal/transformers.rs | 13 +-- .../src/connector/fiserv/transformers.rs | 13 +-- .../src/connector/globalpay/transformers.rs | 6 +- .../src/connector/gocardless/transformers.rs | 20 +--- .../src/connector/helcim/transformers.rs | 22 +--- .../src/connector/iatapay/transformers.rs | 20 +--- .../src/connector/klarna/transformers.rs | 11 +- .../connector/multisafepay/transformers.rs | 21 +--- .../src/connector/netcetera/transformers.rs | 11 +- crates/router/src/connector/nexinets.rs | 2 +- .../router/src/connector/nmi/transformers.rs | 17 +-- crates/router/src/connector/nuvei.rs | 18 +-- .../src/connector/opennode/transformers.rs | 13 +-- .../src/connector/payeezy/transformers.rs | 13 +-- .../src/connector/payme/transformers.rs | 110 ++++++++---------- crates/router/src/connector/paypal.rs | 9 +- .../src/connector/paypal/transformers.rs | 27 ++--- .../src/connector/placetopay/transformers.rs | 11 +- .../src/connector/prophetpay/transformers.rs | 18 +-- crates/router/src/connector/rapyd.rs | 2 +- .../src/connector/rapyd/transformers.rs | 16 +-- crates/router/src/connector/shift4.rs | 4 +- .../src/connector/shift4/transformers.rs | 6 +- crates/router/src/connector/signifyd.rs | 2 +- .../src/connector/square/transformers.rs | 7 +- crates/router/src/connector/stax.rs | 32 +++-- .../router/src/connector/stax/transformers.rs | 16 +-- crates/router/src/connector/stripe.rs | 2 +- .../src/connector/stripe/transformers.rs | 32 +++-- .../connector/threedsecureio/transformers.rs | 15 +-- .../src/connector/trustpay/transformers.rs | 13 +-- .../router/src/connector/tsys/transformers.rs | 7 +- crates/router/src/connector/utils.rs | 75 ++++++------ .../router/src/connector/volt/transformers.rs | 11 +- .../src/connector/worldline/transformers.rs | 24 +--- crates/router/src/connector/worldpay.rs | 4 +- .../src/connector/worldpay/transformers.rs | 2 +- .../router/src/connector/zen/transformers.rs | 16 +-- .../router/src/connector/zsl/transformers.rs | 11 +- crates/router/src/core/admin.rs | 20 ++-- crates/router/src/core/api_keys.rs | 4 +- crates/router/src/core/authentication.rs | 22 ++-- .../src/core/authentication/transformers.rs | 8 +- crates/router/src/core/customers.rs | 2 +- crates/router/src/core/errors.rs | 2 +- crates/router/src/core/files.rs | 8 +- crates/router/src/core/fraud_check.rs | 6 +- crates/router/src/core/mandate.rs | 6 +- crates/router/src/core/payment_link.rs | 4 +- .../router/src/core/payment_methods/cards.rs | 31 +++-- .../src/core/payment_methods/transformers.rs | 2 +- crates/router/src/core/payments.rs | 82 ++++++------- .../router/src/core/payments/access_token.rs | 2 +- .../src/core/payments/flows/authorize_flow.rs | 13 +-- .../payments/flows/complete_authorize_flow.rs | 15 ++- crates/router/src/core/payments/helpers.rs | 2 +- .../payments/operations/payment_approve.rs | 5 +- .../payments/operations/payment_cancel.rs | 4 +- .../payments/operations/payment_create.rs | 5 +- .../payments/operations/payment_response.rs | 26 ++--- .../payments/operations/payment_status.rs | 2 +- .../payments/operations/payment_update.rs | 6 +- .../router/src/core/payments/tokenization.rs | 33 +++--- .../router/src/core/payments/transformers.rs | 4 +- crates/router/src/core/payouts/helpers.rs | 6 +- crates/router/src/core/payouts/retry.rs | 2 +- crates/router/src/core/pm_auth.rs | 31 +++-- .../src/core/user/dashboard_metadata.rs | 8 +- crates/router/src/core/utils.rs | 12 +- crates/router/src/core/verification.rs | 4 +- crates/router/src/core/webhooks.rs | 26 ++--- crates/router/src/core/webhooks/utils.rs | 4 +- crates/router/src/db.rs | 2 +- crates/router/src/db/business_profile.rs | 19 ++- crates/router/src/db/cache.rs | 2 +- crates/router/src/db/dispute.rs | 8 +- crates/router/src/db/user_role.rs | 4 +- crates/router/src/lib.rs | 2 +- crates/router/src/routes/api_keys.rs | 2 +- crates/router/src/routes/metrics/request.rs | 8 +- crates/router/src/routes/payments.rs | 16 ++- crates/router/src/routes/recon.rs | 3 +- crates/router/src/routes/user.rs | 7 +- crates/router/src/routes/webhooks.rs | 16 --- crates/router/src/services/api.rs | 13 +-- crates/router/src/services/api/client.rs | 2 +- crates/router/src/services/authentication.rs | 4 +- crates/router/src/services/pm_auth.rs | 14 +-- crates/router/src/types/api/customers.rs | 6 +- crates/router/src/types/api/payments.rs | 14 --- .../router/src/types/api/verify_connector.rs | 10 +- crates/router/src/types/domain/customer.rs | 4 +- .../domain/merchant_connector_account.rs | 2 +- crates/router/src/types/transformers.rs | 89 +++++++------- crates/router/src/utils.rs | 10 +- crates/router/src/utils/currency.rs | 4 +- crates/router/src/utils/user.rs | 4 +- crates/router/tests/connectors/aci.rs | 4 +- crates/router/tests/connectors/adyen.rs | 2 +- crates/router/tests/connectors/airwallex.rs | 16 +-- .../tests/connectors/authorizedotnet.rs | 66 ++++------- crates/router/tests/connectors/bambora.rs | 14 +-- .../router/tests/connectors/bankofamerica.rs | 10 +- crates/router/tests/connectors/billwerk.rs | 4 +- crates/router/tests/connectors/bitpay.rs | 12 +- crates/router/tests/connectors/bluesnap.rs | 10 +- crates/router/tests/connectors/boku.rs | 10 +- crates/router/tests/connectors/cashtocode.rs | 2 +- crates/router/tests/connectors/checkout.rs | 10 +- crates/router/tests/connectors/coinbase.rs | 16 +-- crates/router/tests/connectors/cryptopay.rs | 12 +- crates/router/tests/connectors/cybersource.rs | 21 ++-- crates/router/tests/connectors/dlocal.rs | 18 +-- .../router/tests/connectors/dummyconnector.rs | 12 +- crates/router/tests/connectors/ebanx.rs | 4 +- crates/router/tests/connectors/fiserv.rs | 16 +-- crates/router/tests/connectors/forte.rs | 20 ++-- crates/router/tests/connectors/globalpay.rs | 18 +-- crates/router/tests/connectors/globepay.rs | 10 +- crates/router/tests/connectors/gocardless.rs | 10 +- crates/router/tests/connectors/helcim.rs | 10 +- crates/router/tests/connectors/iatapay.rs | 8 +- crates/router/tests/connectors/main.rs | 1 + crates/router/tests/connectors/mollie.rs | 1 + .../router/tests/connectors/multisafepay.rs | 12 +- crates/router/tests/connectors/netcetera.rs | 4 +- crates/router/tests/connectors/nexinets.rs | 12 +- crates/router/tests/connectors/nmi.rs | 74 ++++++------ crates/router/tests/connectors/noon.rs | 11 +- crates/router/tests/connectors/nuvei.rs | 13 +-- crates/router/tests/connectors/opayo.rs | 12 +- crates/router/tests/connectors/opennode.rs | 14 +-- crates/router/tests/connectors/payme.rs | 18 +-- crates/router/tests/connectors/paypal.rs | 12 +- crates/router/tests/connectors/payu.rs | 16 +-- crates/router/tests/connectors/placetopay.rs | 10 +- crates/router/tests/connectors/powertranz.rs | 10 +- crates/router/tests/connectors/prophetpay.rs | 10 +- crates/router/tests/connectors/rapyd.rs | 10 +- crates/router/tests/connectors/shift4.rs | 12 +- crates/router/tests/connectors/square.rs | 18 ++- crates/router/tests/connectors/stax.rs | 12 +- crates/router/tests/connectors/stripe.rs | 14 +-- crates/router/tests/connectors/trustpay.rs | 14 +-- crates/router/tests/connectors/tsys.rs | 12 +- crates/router/tests/connectors/utils.rs | 12 +- crates/router/tests/connectors/volt.rs | 10 +- crates/router/tests/connectors/wise.rs | 36 +++--- crates/router/tests/connectors/worldline.rs | 6 +- crates/router/tests/connectors/worldpay.rs | 6 +- crates/router/tests/connectors/zen.rs | 12 +- crates/router/tests/payments.rs | 6 +- crates/router/tests/payments2.rs | 8 +- .../src/macros/api_error/helpers.rs | 6 +- crates/router_derive/src/macros/helpers.rs | 2 +- .../router_derive/src/macros/try_get_enum.rs | 2 +- crates/router_env/src/logger/setup.rs | 2 +- crates/scheduler/src/producer.rs | 7 +- crates/scheduler/src/utils.rs | 4 +- crates/storage_impl/src/errors.rs | 18 ++- crates/storage_impl/src/lib.rs | 2 +- crates/storage_impl/src/mock_db.rs | 6 +- .../src/payments/payment_intent.rs | 14 +-- crates/test_utils/src/newman_runner.rs | 4 +- 222 files changed, 1138 insertions(+), 1631 deletions(-) diff --git a/crates/analytics/src/clickhouse.rs b/crates/analytics/src/clickhouse.rs index 4d01c20972c..8f0d00cd799 100644 --- a/crates/analytics/src/clickhouse.rs +++ b/crates/analytics/src/clickhouse.rs @@ -453,7 +453,7 @@ where |(order_column, order)| format!( " order by {} {}", order_column.to_owned(), - order.to_string() + order ) ), alias.map_or_else(|| "".to_owned(), |alias| format!(" as {}", alias)) @@ -476,7 +476,7 @@ where |(order_column, order)| format!( " order by {} {}", order_column.to_owned(), - order.to_string() + order ) ), alias.map_or_else(|| "".to_owned(), |alias| format!(" as {}", alias)) diff --git a/crates/analytics/src/lib.rs b/crates/analytics/src/lib.rs index 2a7075a0f29..ae10fc9889e 100644 --- a/crates/analytics/src/lib.rs +++ b/crates/analytics/src/lib.rs @@ -78,14 +78,16 @@ impl Default for AnalyticsProvider { } } -impl ToString for AnalyticsProvider { - fn to_string(&self) -> String { - String::from(match self { +impl std::fmt::Display for AnalyticsProvider { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let analytics_provider = match self { Self::Clickhouse(_) => "Clickhouse", Self::Sqlx(_) => "Sqlx", Self::CombinedCkh(_, _) => "CombinedCkh", Self::CombinedSqlx(_, _) => "CombinedSqlx", - }) + }; + + write!(f, "{}", analytics_provider) } } diff --git a/crates/analytics/src/payments/accumulator.rs b/crates/analytics/src/payments/accumulator.rs index c340f2888f8..efc8aaf6983 100644 --- a/crates/analytics/src/payments/accumulator.rs +++ b/crates/analytics/src/payments/accumulator.rs @@ -153,10 +153,7 @@ impl PaymentMetricAccumulator for SumAccumulator { fn add_metrics_bucket(&mut self, metrics: &PaymentMetricRow) { self.total = match ( self.total, - metrics - .total - .as_ref() - .and_then(bigdecimal::ToPrimitive::to_i64), + metrics.total.as_ref().and_then(ToPrimitive::to_i64), ) { (None, None) => None, (None, i @ Some(_)) | (i @ Some(_), None) => i, @@ -173,10 +170,7 @@ impl PaymentMetricAccumulator for AverageAccumulator { type MetricOutput = Option; fn add_metrics_bucket(&mut self, metrics: &PaymentMetricRow) { - let total = metrics - .total - .as_ref() - .and_then(bigdecimal::ToPrimitive::to_u32); + let total = metrics.total.as_ref().and_then(ToPrimitive::to_u32); let count = metrics.count.and_then(|total| u32::try_from(total).ok()); match (total, count) { diff --git a/crates/analytics/src/query.rs b/crates/analytics/src/query.rs index d5296152076..525bc0f8467 100644 --- a/crates/analytics/src/query.rs +++ b/crates/analytics/src/query.rs @@ -286,12 +286,12 @@ pub enum Order { Descending, } -impl ToString for Order { - fn to_string(&self) -> String { - String::from(match self { - Self::Ascending => "asc", - Self::Descending => "desc", - }) +impl std::fmt::Display for Order { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Self::Ascending => write!(f, "asc"), + Self::Descending => write!(f, "desc"), + } } } @@ -709,12 +709,12 @@ where Ok(query) } - pub async fn execute_query( + pub async fn execute_query( &mut self, store: &P, ) -> CustomResult, QueryExecutionError>, QueryBuildingError> where - P: LoadRow, + P: LoadRow + AnalyticsDataSource, Aggregate<&'static str>: ToSql, Window<&'static str>: ToSql, { diff --git a/crates/analytics/src/refunds.rs b/crates/analytics/src/refunds.rs index 53481e23281..590dc148ebf 100644 --- a/crates/analytics/src/refunds.rs +++ b/crates/analytics/src/refunds.rs @@ -6,5 +6,4 @@ pub mod metrics; pub mod types; pub use accumulator::{RefundMetricAccumulator, RefundMetricsAccumulator}; -pub trait RefundAnalytics: metrics::RefundMetricAnalytics {} pub use self::core::{get_filters, get_metrics}; diff --git a/crates/analytics/src/sdk_events.rs b/crates/analytics/src/sdk_events.rs index fe8af7cfe2d..a02de4cbbee 100644 --- a/crates/analytics/src/sdk_events.rs +++ b/crates/analytics/src/sdk_events.rs @@ -5,10 +5,5 @@ pub mod filters; pub mod metrics; pub mod types; pub use accumulator::{SdkEventMetricAccumulator, SdkEventMetricsAccumulator}; -pub trait SDKEventAnalytics: events::SdkEventsFilterAnalytics {} -pub trait SdkEventAnalytics: - metrics::SdkEventMetricAnalytics + filters::SdkEventFilterAnalytics -{ -} pub use self::core::{get_filters, get_metrics, sdk_events_core}; diff --git a/crates/analytics/src/sqlx.rs b/crates/analytics/src/sqlx.rs index 86782c5f750..133e959d63b 100644 --- a/crates/analytics/src/sqlx.rs +++ b/crates/analytics/src/sqlx.rs @@ -593,7 +593,7 @@ where |(order_column, order)| format!( " order by {} {}", order_column.to_owned(), - order.to_string() + order ) ), alias.map_or_else(|| "".to_owned(), |alias| format!(" as {}", alias)) @@ -616,7 +616,7 @@ where |(order_column, order)| format!( " order by {} {}", order_column.to_owned(), - order.to_string() + order ) ), alias.map_or_else(|| "".to_owned(), |alias| format!(" as {}", alias)) diff --git a/crates/analytics/src/types.rs b/crates/analytics/src/types.rs index 356d11bb77d..d0c1d74bc10 100644 --- a/crates/analytics/src/types.rs +++ b/crates/analytics/src/types.rs @@ -63,11 +63,6 @@ where } } -// Analytics Framework - -pub trait RefundAnalytics {} -pub trait SdkEventAnalytics {} - #[async_trait::async_trait] pub trait AnalyticsDataSource where diff --git a/crates/api_models/src/admin.rs b/crates/api_models/src/admin.rs index 75bd2a31d8e..e845317b693 100644 --- a/crates/api_models/src/admin.rs +++ b/crates/api_models/src/admin.rs @@ -456,7 +456,7 @@ pub struct MerchantConnectorCreate { pub disabled: Option, /// Contains the frm configs for the merchant connector - #[schema(example = json!(common_utils::consts::FRM_CONFIGS_EG))] + #[schema(example = json!(consts::FRM_CONFIGS_EG))] pub frm_configs: Option>, /// The business country to which the connector account is attached. To be deprecated soon. Use the 'profile_id' instead @@ -609,7 +609,7 @@ pub struct MerchantConnectorResponse { pub disabled: Option, /// Contains the frm configs for the merchant connector - #[schema(example = json!(common_utils::consts::FRM_CONFIGS_EG))] + #[schema(example = json!(consts::FRM_CONFIGS_EG))] pub frm_configs: Option>, /// The business country to which the connector account is attached. To be deprecated soon. Use the 'profile_id' instead @@ -702,7 +702,7 @@ pub struct MerchantConnectorUpdate { pub disabled: Option, /// Contains the frm configs for the merchant connector - #[schema(example = json!(common_utils::consts::FRM_CONFIGS_EG))] + #[schema(example = json!(consts::FRM_CONFIGS_EG))] pub frm_configs: Option>, pub pm_auth_config: Option, diff --git a/crates/api_models/src/api_keys.rs b/crates/api_models/src/api_keys.rs index 805c5616c2a..801bbc63f5f 100644 --- a/crates/api_models/src/api_keys.rs +++ b/crates/api_models/src/api_keys.rs @@ -270,7 +270,7 @@ mod api_key_expiration_tests { let date = time::Date::from_calendar_date(2022, time::Month::September, 10).unwrap(); let time = time::Time::from_hms(11, 12, 13).unwrap(); assert_eq!( - serde_json::to_string(&ApiKeyExpiration::DateTime(time::PrimitiveDateTime::new( + serde_json::to_string(&ApiKeyExpiration::DateTime(PrimitiveDateTime::new( date, time ))) .unwrap(), @@ -289,7 +289,7 @@ mod api_key_expiration_tests { let time = time::Time::from_hms(11, 12, 13).unwrap(); assert_eq!( serde_json::from_str::(r#""2022-09-10T11:12:13.000Z""#).unwrap(), - ApiKeyExpiration::DateTime(time::PrimitiveDateTime::new(date, time)) + ApiKeyExpiration::DateTime(PrimitiveDateTime::new(date, time)) ); } diff --git a/crates/api_models/src/payments.rs b/crates/api_models/src/payments.rs index 8270ddaf3fe..0e46eb19931 100644 --- a/crates/api_models/src/payments.rs +++ b/crates/api_models/src/payments.rs @@ -682,7 +682,7 @@ impl PaymentsRequest { .as_ref() .map(|od| { od.iter() - .map(|order| order.encode_to_value().map(masking::Secret::new)) + .map(|order| order.encode_to_value().map(Secret::new)) .collect::, _>>() }) .transpose() @@ -1304,9 +1304,7 @@ mod payment_method_data_serde { match deserialize_to_inner { __Inner::OptionalPaymentMethod(value) => { let parsed_value = serde_json::from_value::<__InnerPaymentMethodData>(value) - .map_err(|serde_json_error| { - serde::de::Error::custom(serde_json_error.to_string()) - })?; + .map_err(|serde_json_error| de::Error::custom(serde_json_error.to_string()))?; let payment_method_data = if let Some(payment_method_data_value) = parsed_value.payment_method_data @@ -1321,14 +1319,12 @@ mod payment_method_data_serde { payment_method_data_value, ) .map_err(|serde_json_error| { - serde::de::Error::custom(serde_json_error.to_string()) + de::Error::custom(serde_json_error.to_string()) })?, ) } } else { - Err(serde::de::Error::custom( - "Expected a map for payment_method_data", - ))? + Err(de::Error::custom("Expected a map for payment_method_data"))? } } else { None @@ -1342,7 +1338,7 @@ mod payment_method_data_serde { __Inner::RewardString(inner_string) => { let payment_method_data = match inner_string.as_str() { "reward" => PaymentMethodData::Reward, - _ => Err(serde::de::Error::custom("Invalid Variant"))?, + _ => Err(de::Error::custom("Invalid Variant"))?, }; Ok(Some(PaymentMethodDataRequest { @@ -2686,8 +2682,8 @@ pub enum PaymentIdType { PreprocessingId(String), } -impl std::fmt::Display for PaymentIdType { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { +impl fmt::Display for PaymentIdType { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { Self::PaymentIntentId(payment_id) => { write!(f, "payment_intent_id = \"{payment_id}\"") diff --git a/crates/api_models/src/routing.rs b/crates/api_models/src/routing.rs index f3d966f3d9d..a7c4ed24475 100644 --- a/crates/api_models/src/routing.rs +++ b/crates/api_models/src/routing.rs @@ -203,8 +203,8 @@ pub struct RoutableConnectorChoice { pub sub_label: Option, } -impl ToString for RoutableConnectorChoice { - fn to_string(&self) -> String { +impl std::fmt::Display for RoutableConnectorChoice { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { #[cfg(feature = "connector_choice_mca_id")] let base = self.connector.to_string(); @@ -219,7 +219,7 @@ impl ToString for RoutableConnectorChoice { sub_base }; - base + write!(f, "{}", base) } } @@ -329,7 +329,7 @@ pub enum RoutingAlgorithm { Priority(Vec), VolumeSplit(Vec), #[schema(value_type=ProgramConnectorSelection)] - Advanced(euclid::frontend::ast::Program), + Advanced(ast::Program), } #[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] @@ -338,7 +338,7 @@ pub enum RoutingAlgorithmSerde { Single(Box), Priority(Vec), VolumeSplit(Vec), - Advanced(euclid::frontend::ast::Program), + Advanced(ast::Program), } impl TryFrom for RoutingAlgorithm { diff --git a/crates/api_models/src/user/dashboard_metadata.rs b/crates/api_models/src/user/dashboard_metadata.rs index b547a61d450..d8a437e31a6 100644 --- a/crates/api_models/src/user/dashboard_metadata.rs +++ b/crates/api_models/src/user/dashboard_metadata.rs @@ -33,7 +33,7 @@ pub enum SetMetaDataRequest { pub struct ProductionAgreementRequest { pub version: String, #[serde(skip_deserializing)] - pub ip_address: Option>, + pub ip_address: Option>, } #[derive(Debug, serde::Deserialize, serde::Serialize)] diff --git a/crates/common_utils/src/crypto.rs b/crates/common_utils/src/crypto.rs index 46904535f01..7901dec6db2 100644 --- a/crates/common_utils/src/crypto.rs +++ b/crates/common_utils/src/crypto.rs @@ -38,14 +38,14 @@ impl NonceSequence { } /// Returns the current nonce value as bytes. - fn current(&self) -> [u8; ring::aead::NONCE_LEN] { - let mut nonce = [0_u8; ring::aead::NONCE_LEN]; + fn current(&self) -> [u8; aead::NONCE_LEN] { + let mut nonce = [0_u8; aead::NONCE_LEN]; nonce.copy_from_slice(&self.0.to_be_bytes()[Self::SEQUENCE_NUMBER_START_INDEX..]); nonce } /// Constructs a nonce sequence from bytes - fn from_bytes(bytes: [u8; ring::aead::NONCE_LEN]) -> Self { + fn from_bytes(bytes: [u8; aead::NONCE_LEN]) -> Self { let mut sequence_number = [0_u8; 128 / 8]; sequence_number[Self::SEQUENCE_NUMBER_START_INDEX..].copy_from_slice(&bytes); let sequence_number = u128::from_be_bytes(sequence_number); @@ -53,16 +53,16 @@ impl NonceSequence { } } -impl ring::aead::NonceSequence for NonceSequence { - fn advance(&mut self) -> Result { - let mut nonce = [0_u8; ring::aead::NONCE_LEN]; +impl aead::NonceSequence for NonceSequence { + fn advance(&mut self) -> Result { + let mut nonce = [0_u8; aead::NONCE_LEN]; nonce.copy_from_slice(&self.0.to_be_bytes()[Self::SEQUENCE_NUMBER_START_INDEX..]); // Increment sequence number self.0 = self.0.wrapping_add(1); // Return previous sequence number as bytes - Ok(ring::aead::Nonce::assume_unique_for_key(nonce)) + Ok(aead::Nonce::assume_unique_for_key(nonce)) } } @@ -275,8 +275,8 @@ impl DecodeMessage for GcmAes256 { .change_context(errors::CryptoError::DecodingFailed)?; let nonce_sequence = NonceSequence::from_bytes( - <[u8; ring::aead::NONCE_LEN]>::try_from( - msg.get(..ring::aead::NONCE_LEN) + <[u8; aead::NONCE_LEN]>::try_from( + msg.get(..aead::NONCE_LEN) .ok_or(errors::CryptoError::DecodingFailed) .attach_printable("Failed to read the nonce form the encrypted ciphertext")?, ) @@ -288,7 +288,7 @@ impl DecodeMessage for GcmAes256 { let output = binding.as_mut_slice(); let result = key - .open_within(aead::Aad::empty(), output, ring::aead::NONCE_LEN..) + .open_within(aead::Aad::empty(), output, aead::NONCE_LEN..) .change_context(errors::CryptoError::DecodingFailed)?; Ok(result.to_vec()) diff --git a/crates/common_utils/src/ext_traits.rs b/crates/common_utils/src/ext_traits.rs index f71e098a99c..5ad53ec8533 100644 --- a/crates/common_utils/src/ext_traits.rs +++ b/crates/common_utils/src/ext_traits.rs @@ -472,13 +472,13 @@ pub trait XmlExt { /// /// Deserialize an XML string into the specified type ``. /// - fn parse_xml(self) -> Result + fn parse_xml(self) -> Result where T: serde::de::DeserializeOwned; } impl XmlExt for &str { - fn parse_xml(self) -> Result + fn parse_xml(self) -> Result where T: serde::de::DeserializeOwned, { diff --git a/crates/common_utils/src/pii.rs b/crates/common_utils/src/pii.rs index c1f9d716e4b..9c70c572fed 100644 --- a/crates/common_utils/src/pii.rs +++ b/crates/common_utils/src/pii.rs @@ -38,7 +38,7 @@ pub struct PhoneNumber(Secret); impl Strategy for PhoneNumberStrategy where - T: AsRef + std::fmt::Debug, + T: AsRef + fmt::Debug, { fn fmt(val: &T, f: &mut fmt::Formatter<'_>) -> fmt::Result { let val_str: &str = val.as_ref(); @@ -85,7 +85,7 @@ impl ops::DerefMut for PhoneNumber { } } -impl Queryable for PhoneNumber +impl Queryable for PhoneNumber where DB: Backend, Self: FromSql, @@ -210,7 +210,7 @@ pub enum EmailStrategy {} impl Strategy for EmailStrategy where - T: AsRef + std::fmt::Debug, + T: AsRef + fmt::Debug, { fn fmt(val: &T, f: &mut fmt::Formatter<'_>) -> fmt::Result { let val_str: &str = val.as_ref(); @@ -224,7 +224,7 @@ where #[derive( serde::Serialize, serde::Deserialize, Debug, Clone, PartialEq, Eq, Default, AsExpression, )] -#[diesel(sql_type = diesel::sql_types::Text)] +#[diesel(sql_type = sql_types::Text)] #[serde(try_from = "String")] pub struct Email(Secret); @@ -262,7 +262,7 @@ impl ops::DerefMut for Email { } } -impl Queryable for Email +impl Queryable for Email where DB: Backend, Self: FromSql, @@ -353,7 +353,7 @@ pub enum UpiVpaMaskingStrategy {} impl Strategy for UpiVpaMaskingStrategy where - T: AsRef + std::fmt::Debug, + T: AsRef + fmt::Debug, { fn fmt(val: &T, f: &mut fmt::Formatter<'_>) -> fmt::Result { let vpa_str: &str = val.as_ref(); diff --git a/crates/common_utils/src/static_cache.rs b/crates/common_utils/src/static_cache.rs index ca608fa9a3b..37705c892c5 100644 --- a/crates/common_utils/src/static_cache.rs +++ b/crates/common_utils/src/static_cache.rs @@ -26,6 +26,8 @@ impl StaticCache where T: Send, { + // Cannot have default impl as it cannot be called during instantiation of static item + #[allow(clippy::new_without_default)] pub const fn new() -> Self { Self { data: Lazy::new(|| RwLock::new(FxHashMap::default())), diff --git a/crates/connector_configs/src/transformer.rs b/crates/connector_configs/src/transformer.rs index 203e48ac507..f89c2e5921b 100644 --- a/crates/connector_configs/src/transformer.rs +++ b/crates/connector_configs/src/transformer.rs @@ -98,12 +98,11 @@ impl DashboardRequestPayload { if let Some(payment_methods_enabled) = request.payment_methods_enabled.clone() { for payload in payment_methods_enabled { match payload.payment_method { - api_models::enums::PaymentMethod::Card => { + PaymentMethod::Card => { if let Some(card_provider) = payload.card_provider { - let payment_type = api_models::enums::PaymentMethodType::from_str( - &payload.payment_method_type, - ) - .map_err(|_| "Invalid key received".to_string()); + let payment_type = + PaymentMethodType::from_str(&payload.payment_method_type) + .map_err(|_| "Invalid key received".to_string()); if let Ok(payment_type) = payment_type { for method in card_provider { @@ -124,17 +123,17 @@ impl DashboardRequestPayload { } } - api_models::enums::PaymentMethod::Wallet - | api_models::enums::PaymentMethod::BankRedirect - | api_models::enums::PaymentMethod::PayLater - | api_models::enums::PaymentMethod::BankTransfer - | api_models::enums::PaymentMethod::Crypto - | api_models::enums::PaymentMethod::BankDebit - | api_models::enums::PaymentMethod::Reward - | api_models::enums::PaymentMethod::Upi - | api_models::enums::PaymentMethod::Voucher - | api_models::enums::PaymentMethod::GiftCard - | api_models::enums::PaymentMethod::CardRedirect => { + PaymentMethod::Wallet + | PaymentMethod::BankRedirect + | PaymentMethod::PayLater + | PaymentMethod::BankTransfer + | PaymentMethod::Crypto + | PaymentMethod::BankDebit + | PaymentMethod::Reward + | PaymentMethod::Upi + | PaymentMethod::Voucher + | PaymentMethod::GiftCard + | PaymentMethod::CardRedirect => { if let Some(provider) = payload.provider { let val = Self::transform_payment_method( request.connector, @@ -154,7 +153,7 @@ impl DashboardRequestPayload { } if !card_payment_method_types.is_empty() { let card = PaymentMethodsEnabled { - payment_method: api_models::enums::PaymentMethod::Card, + payment_method: PaymentMethod::Card, payment_method_types: Some(card_payment_method_types), }; payment_method_enabled.push(card); diff --git a/crates/diesel_models/src/encryption.rs b/crates/diesel_models/src/encryption.rs index 375259491c9..6c6063c1604 100644 --- a/crates/diesel_models/src/encryption.rs +++ b/crates/diesel_models/src/encryption.rs @@ -8,7 +8,7 @@ use diesel::{ use masking::Secret; #[derive(Debug, AsExpression, Clone, serde::Serialize, serde::Deserialize, Eq, PartialEq)] -#[diesel(sql_type = diesel::sql_types::Binary)] +#[diesel(sql_type = sql_types::Binary)] #[repr(transparent)] pub struct Encryption { inner: Secret, EncryptionStrategy>, @@ -41,7 +41,7 @@ where DB: Backend, Secret, EncryptionStrategy>: FromSql, { - fn from_sql(bytes: DB::RawValue<'_>) -> diesel::deserialize::Result { + fn from_sql(bytes: DB::RawValue<'_>) -> deserialize::Result { , EncryptionStrategy>>::from_sql(bytes).map(Self::new) } } diff --git a/crates/diesel_models/src/query/payment_attempt.rs b/crates/diesel_models/src/query/payment_attempt.rs index f23c2318899..4e5773800aa 100644 --- a/crates/diesel_models/src/query/payment_attempt.rs +++ b/crates/diesel_models/src/query/payment_attempt.rs @@ -10,7 +10,7 @@ use error_stack::{report, ResultExt}; use super::generics; use crate::{ enums::{self, IntentStatus}, - errors::{self, DatabaseError}, + errors::DatabaseError, payment_attempt::{ PaymentAttempt, PaymentAttemptNew, PaymentAttemptUpdate, PaymentAttemptUpdateInternal, }, @@ -246,7 +246,7 @@ impl PaymentAttempt { .distinct() .get_results_async::>(conn) .await - .change_context(errors::DatabaseError::Others) + .change_context(DatabaseError::Others) .attach_printable("Error filtering records by connector")? .into_iter() .flatten() @@ -350,7 +350,7 @@ impl PaymentAttempt { db_metrics::DatabaseOperation::Filter, ) .await - .change_context(errors::DatabaseError::Others) + .change_context(DatabaseError::Others) .attach_printable("Error filtering count of payments") } } diff --git a/crates/diesel_models/src/query/payout_attempt.rs b/crates/diesel_models/src/query/payout_attempt.rs index ac34ee695ac..4f95877bd7c 100644 --- a/crates/diesel_models/src/query/payout_attempt.rs +++ b/crates/diesel_models/src/query/payout_attempt.rs @@ -11,7 +11,7 @@ use error_stack::{report, ResultExt}; use super::generics; use crate::{ enums, - errors::{self, DatabaseError}, + errors::DatabaseError, payout_attempt::{ PayoutAttempt, PayoutAttemptNew, PayoutAttemptUpdate, PayoutAttemptUpdateInternal, }, @@ -46,7 +46,7 @@ impl PayoutAttempt { .await { Err(error) => match error.current_context() { - errors::DatabaseError::NoFieldsToUpdate => Ok(self), + DatabaseError::NoFieldsToUpdate => Ok(self), _ => Err(error), }, result => result, @@ -98,7 +98,7 @@ impl PayoutAttempt { .first() .cloned() .ok_or_else(|| { - report!(errors::DatabaseError::NotFound).attach_printable("Error while updating payout") + report!(DatabaseError::NotFound).attach_printable("Error while updating payout") }) } @@ -119,7 +119,7 @@ impl PayoutAttempt { .first() .cloned() .ok_or_else(|| { - report!(errors::DatabaseError::NotFound).attach_printable("Error while updating payout") + report!(DatabaseError::NotFound).attach_printable("Error while updating payout") }) } @@ -161,7 +161,7 @@ impl PayoutAttempt { .distinct() .get_results_async::>(conn) .await - .change_context(errors::DatabaseError::Others) + .change_context(DatabaseError::Others) .attach_printable("Error filtering records by connector")? .into_iter() .flatten() diff --git a/crates/drainer/src/handler.rs b/crates/drainer/src/handler.rs index 47b60db80d5..35ad467d5fe 100644 --- a/crates/drainer/src/handler.rs +++ b/crates/drainer/src/handler.rs @@ -95,7 +95,7 @@ impl Handler { while let Some(_c) = rx.recv().await { logger::info!("Awaiting shutdown!"); metrics::SHUTDOWN_SIGNAL_RECEIVED.add(&metrics::CONTEXT, 1, &[]); - let shutdown_started = tokio::time::Instant::now(); + let shutdown_started = time::Instant::now(); rx.close(); //Check until the active tasks are zero. This does not include the tasks in the stream. diff --git a/crates/drainer/src/lib.rs b/crates/drainer/src/lib.rs index 56f2db0907e..cb2b55d202f 100644 --- a/crates/drainer/src/lib.rs +++ b/crates/drainer/src/lib.rs @@ -24,7 +24,7 @@ use router_env::{ }; use tokio::sync::mpsc; -pub(crate) type Settings = crate::settings::Settings; +pub(crate) type Settings = settings::Settings; use crate::{ connection::pg_connection, services::Store, settings::DrainerSettings, types::StreamData, diff --git a/crates/euclid/src/dssa/graph.rs b/crates/euclid/src/dssa/graph.rs index 526248a3817..0ffafe4d48b 100644 --- a/crates/euclid/src/dssa/graph.rs +++ b/crates/euclid/src/dssa/graph.rs @@ -838,7 +838,7 @@ mod test { None::<()>, ); let mut memo = cgraph::Memoization::new(); - let mut cycle_map = cgraph::CycleCheck::new(); + let mut cycle_map = CycleCheck::new(); let _edge_1 = builder .make_edge( _node_1, @@ -894,7 +894,7 @@ mod test { None::<()>, ); let mut memo = cgraph::Memoization::new(); - let mut cycle_map = cgraph::CycleCheck::new(); + let mut cycle_map = CycleCheck::new(); let _edge_1 = builder .make_edge( @@ -986,7 +986,7 @@ mod test { ); let mut memo = cgraph::Memoization::new(); - let mut cycle_map = cgraph::CycleCheck::new(); + let mut cycle_map = CycleCheck::new(); let _edge_1 = builder .make_edge( diff --git a/crates/euclid/src/frontend/ast/parser.rs b/crates/euclid/src/frontend/ast/parser.rs index 8b2f717a868..cbfc21c96a7 100644 --- a/crates/euclid/src/frontend/ast/parser.rs +++ b/crates/euclid/src/frontend/ast/parser.rs @@ -3,7 +3,7 @@ use nom::{ }; use crate::{frontend::ast, types::DummyOutput}; -pub type ParseResult = nom::IResult>; +pub type ParseResult = nom::IResult>; pub enum EuclidError { InvalidPercentage(String), @@ -50,9 +50,9 @@ impl EuclidParsable for DummyOutput { )(input) } } -pub fn skip_ws<'a, F: 'a, O>(inner: F) -> impl FnMut(&'a str) -> ParseResult<&str, O> +pub fn skip_ws<'a, F, O>(inner: F) -> impl FnMut(&'a str) -> ParseResult<&str, O> where - F: FnMut(&'a str) -> ParseResult<&str, O>, + F: FnMut(&'a str) -> ParseResult<&str, O> + 'a, { sequence::preceded(pchar::multispace0, inner) } diff --git a/crates/euclid_macros/src/inner/knowledge.rs b/crates/euclid_macros/src/inner/knowledge.rs index a9c453b42c6..baef55338f0 100644 --- a/crates/euclid_macros/src/inner/knowledge.rs +++ b/crates/euclid_macros/src/inner/knowledge.rs @@ -1,4 +1,8 @@ -use std::{hash::Hash, rc::Rc}; +use std::{ + fmt::{Display, Formatter}, + hash::Hash, + rc::Rc, +}; use proc_macro2::{Span, TokenStream}; use quote::{format_ident, quote}; @@ -24,15 +28,16 @@ enum Comparison { LessThanEqual, } -impl ToString for Comparison { - fn to_string(&self) -> String { - match self { - Self::LessThan => "< ".to_string(), - Self::Equal => String::new(), - Self::GreaterThanEqual => ">= ".to_string(), - Self::LessThanEqual => "<= ".to_string(), - Self::GreaterThan => "> ".to_string(), - } +impl Display for Comparison { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + let symbol = match self { + Self::LessThan => "< ", + Self::Equal => return Ok(()), + Self::GreaterThanEqual => ">= ", + Self::LessThanEqual => "<= ", + Self::GreaterThan => "> ", + }; + write!(f, "{}", symbol) } } @@ -69,7 +74,7 @@ impl ValueType { Self::Any => format!("{key}(any)"), Self::EnumVariant(s) => format!("{key}({s})"), Self::Number { number, comparison } => { - format!("{}({}{})", key, comparison.to_string(), number) + format!("{}({}{})", key, comparison, number) } } } @@ -104,9 +109,9 @@ struct Atom { value: ValueType, } -impl ToString for Atom { - fn to_string(&self) -> String { - self.value.to_string(&self.key) +impl Display for Atom { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + write!(f, "{}", self.value.to_string(&self.key)) } } @@ -317,15 +322,14 @@ impl Parse for Scope { } } -impl ToString for Scope { - fn to_string(&self) -> String { +impl Display for Scope { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { match self { - Self::Crate => "crate".to_string(), - Self::Extern => "euclid".to_string(), + Self::Crate => write!(f, "crate"), + Self::Extern => write!(f, "euclid"), } } } - #[derive(Clone)] struct Program { rules: Vec>, diff --git a/crates/euclid_wasm/src/lib.rs b/crates/euclid_wasm/src/lib.rs index 4920243bcc0..3668130608e 100644 --- a/crates/euclid_wasm/src/lib.rs +++ b/crates/euclid_wasm/src/lib.rs @@ -193,9 +193,7 @@ pub fn run_program(program: JsValue, input: JsValue) -> JsResult { #[wasm_bindgen(js_name = getAllConnectors)] pub fn get_all_connectors() -> JsResult { - Ok(serde_wasm_bindgen::to_value( - common_enums::RoutableConnectors::VARIANTS, - )?) + Ok(serde_wasm_bindgen::to_value(RoutableConnectors::VARIANTS)?) } #[wasm_bindgen(js_name = getAllKeys)] diff --git a/crates/hyperswitch_domain_models/src/mandates.rs b/crates/hyperswitch_domain_models/src/mandates.rs index 912ddc67058..180f733cfeb 100644 --- a/crates/hyperswitch_domain_models/src/mandates.rs +++ b/crates/hyperswitch_domain_models/src/mandates.rs @@ -169,8 +169,7 @@ impl CustomerAcceptance { } pub fn get_accepted_at(&self) -> PrimitiveDateTime { - self.accepted_at - .unwrap_or_else(common_utils::date_time::now) + self.accepted_at.unwrap_or_else(date_time::now) } } diff --git a/crates/masking/src/diesel.rs b/crates/masking/src/diesel.rs index f3576298bdb..ea60a861c9d 100644 --- a/crates/masking/src/diesel.rs +++ b/crates/masking/src/diesel.rs @@ -52,7 +52,7 @@ where S: FromSql, I: Strategy, { - fn from_sql(bytes: DB::RawValue<'_>) -> diesel::deserialize::Result { + fn from_sql(bytes: DB::RawValue<'_>) -> deserialize::Result { S::from_sql(bytes).map(|raw| raw.into()) } } @@ -122,7 +122,7 @@ where S: FromSql + ZeroizableSecret, I: Strategy, { - fn from_sql(bytes: DB::RawValue<'_>) -> diesel::deserialize::Result { + fn from_sql(bytes: DB::RawValue<'_>) -> deserialize::Result { S::from_sql(bytes).map(|raw| raw.into()) } } diff --git a/crates/openapi/src/openapi.rs b/crates/openapi/src/openapi.rs index 1e384408397..7abbc3b77e4 100644 --- a/crates/openapi/src/openapi.rs +++ b/crates/openapi/src/openapi.rs @@ -514,6 +514,8 @@ Never share your secret api keys. Keep them guarded and secure. )), modifiers(&SecurityAddon) )] +// Bypass clippy lint for not being constructed +#[allow(dead_code)] pub struct ApiDoc; struct SecurityAddon; diff --git a/crates/pm_auth/src/types.rs b/crates/pm_auth/src/types.rs index 51fd796072b..d5c66b9c64a 100644 --- a/crates/pm_auth/src/types.rs +++ b/crates/pm_auth/src/types.rs @@ -110,12 +110,12 @@ pub type BankDetailsRouterData = PaymentAuthRouterData< >; pub type PaymentAuthLinkTokenType = - dyn self::api::ConnectorIntegration; + dyn api::ConnectorIntegration; pub type PaymentAuthExchangeTokenType = - dyn self::api::ConnectorIntegration; + dyn api::ConnectorIntegration; -pub type PaymentAuthBankAccountDetailsType = dyn self::api::ConnectorIntegration< +pub type PaymentAuthBankAccountDetailsType = dyn api::ConnectorIntegration< BankAccountCredentials, BankAccountCredentialsRequest, BankAccountCredentialsResponse, diff --git a/crates/router/src/analytics.rs b/crates/router/src/analytics.rs index d509cf03d3c..8eb94a29124 100644 --- a/crates/router/src/analytics.rs +++ b/crates/router/src/analytics.rs @@ -114,7 +114,7 @@ pub mod routes { pub async fn get_info( state: web::Data, req: actix_web::HttpRequest, - domain: actix_web::web::Path, + domain: web::Path, ) -> impl Responder { let flow = AnalyticsFlow::GetInfo; Box::pin(api::server_wrap( @@ -631,7 +631,7 @@ pub mod routes { state: web::Data, req: actix_web::HttpRequest, json_payload: web::Json, - index: actix_web::web::Path, + index: web::Path, ) -> impl Responder { let flow = AnalyticsFlow::GetSearchResults; let indexed_req = GetSearchRequestWithIndex { diff --git a/crates/router/src/bin/scheduler.rs b/crates/router/src/bin/scheduler.rs index 47f41d8700f..5341a1b09c1 100644 --- a/crates/router/src/bin/scheduler.rs +++ b/crates/router/src/bin/scheduler.rs @@ -40,7 +40,7 @@ async fn main() -> CustomResult<(), ProcessTrackerError> { conf.proxy.clone(), services::proxy_bypass_urls(&conf.locker), ) - .change_context(errors::ProcessTrackerError::ConfigurationError)?, + .change_context(ProcessTrackerError::ConfigurationError)?, ); // channel for listening to redis disconnect events let (redis_shutdown_signal_tx, redis_shutdown_signal_rx) = oneshot::channel(); @@ -320,7 +320,7 @@ async fn start_scheduler( .conf .scheduler .clone() - .ok_or(errors::ProcessTrackerError::ConfigurationError)?; + .ok_or(ProcessTrackerError::ConfigurationError)?; scheduler::start_process_tracker( state, scheduler_flow, diff --git a/crates/router/src/compatibility/stripe/payment_intents/types.rs b/crates/router/src/compatibility/stripe/payment_intents/types.rs index eed80a1128e..3d5264ddbed 100644 --- a/crates/router/src/compatibility/stripe/payment_intents/types.rs +++ b/crates/router/src/compatibility/stripe/payment_intents/types.rs @@ -649,7 +649,7 @@ fn from_timestamp_to_datetime( } })?; - Ok(Some(time::PrimitiveDateTime::new(time.date(), time.time()))) + Ok(Some(PrimitiveDateTime::new(time.date(), time.time()))) } else { Ok(None) } @@ -744,7 +744,7 @@ impl ForeignTryFrom<(Option, Option)> for Option Some(api_models::payments::MandateType::MultiUse(Some( + None => Some(payments::MandateType::MultiUse(Some( payments::MandateAmountData { amount: mandate.amount.unwrap_or_default(), currency, diff --git a/crates/router/src/configs/settings.rs b/crates/router/src/configs/settings.rs index adc09fb55af..5b55ca8b0e3 100644 --- a/crates/router/src/configs/settings.rs +++ b/crates/router/src/configs/settings.rs @@ -200,7 +200,7 @@ pub struct ApplepayMerchantConfigs { #[derive(Debug, Deserialize, Clone, Default)] pub struct MultipleApiVersionSupportedConnectors { #[serde(deserialize_with = "deserialize_hashset")] - pub supported_connectors: HashSet, + pub supported_connectors: HashSet, } #[derive(Debug, Deserialize, Clone, Default)] @@ -214,10 +214,10 @@ pub struct TempLockerEnableConfig(pub HashMap, + pub connector_list: HashSet, #[cfg(feature = "payouts")] #[serde(deserialize_with = "deserialize_hashset")] - pub payout_connector_list: HashSet, + pub payout_connector_list: HashSet, } #[cfg(feature = "dummy_connector")] @@ -263,7 +263,7 @@ pub struct Mandates { #[derive(Debug, Deserialize, Clone, Default)] pub struct NetworkTransactionIdSupportedConnectors { #[serde(deserialize_with = "deserialize_hashset")] - pub connector_list: HashSet, + pub connector_list: HashSet, } #[derive(Debug, Deserialize, Clone)] @@ -279,7 +279,7 @@ pub struct SupportedPaymentMethodTypesForMandate( #[derive(Debug, Deserialize, Clone)] pub struct SupportedConnectorsForMandate { #[serde(deserialize_with = "deserialize_hashset")] - pub connector_list: HashSet, + pub connector_list: HashSet, } #[derive(Debug, Deserialize, Clone, Default)] @@ -322,9 +322,7 @@ pub enum PaymentMethodTypeTokenFilter { } #[derive(Debug, Deserialize, Clone, Default)] -pub struct BankRedirectConfig( - pub HashMap, -); +pub struct BankRedirectConfig(pub HashMap); #[derive(Debug, Deserialize, Clone)] pub struct ConnectorBankNames(pub HashMap); @@ -345,17 +343,17 @@ pub struct PaymentMethodFilters(pub HashMap>, + pub currency: Option>, #[serde(deserialize_with = "deserialize_optional_hashset")] - pub country: Option>, + pub country: Option>, pub not_available_flows: Option, } @@ -628,13 +626,13 @@ pub struct ApiKeys { #[derive(Debug, Deserialize, Clone, Default)] pub struct DelayedSessionConfig { #[serde(deserialize_with = "deserialize_hashset")] - pub connectors_with_delayed_session_response: HashSet, + pub connectors_with_delayed_session_response: HashSet, } #[derive(Debug, Deserialize, Clone, Default)] pub struct WebhookSourceVerificationCall { #[serde(deserialize_with = "deserialize_hashset")] - pub connectors_with_webhook_source_verification_call: HashSet, + pub connectors_with_webhook_source_verification_call: HashSet, } #[derive(Debug, Deserialize, Clone, Default)] diff --git a/crates/router/src/connector/aci/transformers.rs b/crates/router/src/connector/aci/transformers.rs index 23288e3819d..9353b848bdb 100644 --- a/crates/router/src/connector/aci/transformers.rs +++ b/crates/router/src/connector/aci/transformers.rs @@ -22,20 +22,13 @@ pub struct AciRouterData { router_data: T, } -impl - TryFrom<( - &types::api::CurrencyUnit, - types::storage::enums::Currency, - i64, - T, - )> for AciRouterData -{ +impl TryFrom<(&types::api::CurrencyUnit, enums::Currency, i64, T)> for AciRouterData { type Error = error_stack::Report; fn try_from( (currency_unit, currency, amount, item): ( &types::api::CurrencyUnit, - types::storage::enums::Currency, + enums::Currency, i64, T, ), diff --git a/crates/router/src/connector/adyen/transformers.rs b/crates/router/src/connector/adyen/transformers.rs index 6dfe899f2d1..77abaacf40b 100644 --- a/crates/router/src/connector/adyen/transformers.rs +++ b/crates/router/src/connector/adyen/transformers.rs @@ -1720,7 +1720,7 @@ fn get_amount_data(item: &AdyenRouterData<&types::PaymentsAuthorizeRouterData>) } fn get_address_info( - address: Option<&api_models::payments::Address>, + address: Option<&payments::Address>, ) -> Option>> { address.and_then(|add| { add.address.as_ref().map( @@ -1783,7 +1783,7 @@ fn get_telephone_number(item: &types::PaymentsAuthorizeRouterData) -> Option) -> Option { +fn get_shopper_name(address: Option<&payments::Address>) -> Option { let billing = address.and_then(|billing| billing.address.as_ref()); Some(ShopperName { first_name: billing.and_then(|a| a.first_name.clone()), @@ -1791,9 +1791,7 @@ fn get_shopper_name(address: Option<&api_models::payments::Address>) -> Option, -) -> Option { +fn get_country_code(address: Option<&payments::Address>) -> Option { address.and_then(|billing| billing.address.as_ref().and_then(|address| address.country)) } diff --git a/crates/router/src/connector/airwallex.rs b/crates/router/src/connector/airwallex.rs index 91a27fc3df0..bdd842bf958 100644 --- a/crates/router/src/connector/airwallex.rs +++ b/crates/router/src/connector/airwallex.rs @@ -223,7 +223,7 @@ impl ConnectorIntegration { pub router_data: T, } -impl - TryFrom<( - &types::api::CurrencyUnit, - types::storage::enums::Currency, - i64, - T, - )> for AirwallexRouterData -{ +impl TryFrom<(&api::CurrencyUnit, enums::Currency, i64, T)> for AirwallexRouterData { type Error = error_stack::Report; fn try_from( (currency_unit, currency, amount, router_data): ( - &types::api::CurrencyUnit, - types::storage::enums::Currency, + &api::CurrencyUnit, + enums::Currency, i64, T, ), diff --git a/crates/router/src/connector/authorizedotnet.rs b/crates/router/src/connector/authorizedotnet.rs index 7f9cdc376e1..2ca78e630fa 100644 --- a/crates/router/src/connector/authorizedotnet.rs +++ b/crates/router/src/connector/authorizedotnet.rs @@ -39,8 +39,7 @@ where &self, _req: &types::RouterData, _connectors: &settings::Connectors, - ) -> CustomResult)>, errors::ConnectorError> - { + ) -> CustomResult)>, errors::ConnectorError> { Ok(vec![( headers::CONTENT_TYPE.to_string(), self.get_content_type().to_string().into(), diff --git a/crates/router/src/connector/authorizedotnet/transformers.rs b/crates/router/src/connector/authorizedotnet/transformers.rs index a072fcf3214..ca8b13298b0 100644 --- a/crates/router/src/connector/authorizedotnet/transformers.rs +++ b/crates/router/src/connector/authorizedotnet/transformers.rs @@ -46,22 +46,10 @@ pub struct AuthorizedotnetRouterData { pub router_data: T, } -impl - TryFrom<( - &types::api::CurrencyUnit, - types::storage::enums::Currency, - i64, - T, - )> for AuthorizedotnetRouterData -{ +impl TryFrom<(&api::CurrencyUnit, enums::Currency, i64, T)> for AuthorizedotnetRouterData { type Error = error_stack::Report; fn try_from( - (currency_unit, currency, amount, item): ( - &types::api::CurrencyUnit, - types::storage::enums::Currency, - i64, - T, - ), + (currency_unit, currency, amount, item): (&api::CurrencyUnit, enums::Currency, i64, T), ) -> Result { let amount = utils::get_amount_as_f64(currency_unit, amount, currency)?; Ok(Self { diff --git a/crates/router/src/connector/bambora/transformers.rs b/crates/router/src/connector/bambora/transformers.rs index dbb655c6ca0..77c91af709d 100644 --- a/crates/router/src/connector/bambora/transformers.rs +++ b/crates/router/src/connector/bambora/transformers.rs @@ -20,22 +20,10 @@ pub struct BamboraRouterData { pub router_data: T, } -impl - TryFrom<( - &types::api::CurrencyUnit, - types::storage::enums::Currency, - i64, - T, - )> for BamboraRouterData -{ +impl TryFrom<(&api::CurrencyUnit, enums::Currency, i64, T)> for BamboraRouterData { type Error = error_stack::Report; fn try_from( - (currency_unit, currency, amount, item): ( - &types::api::CurrencyUnit, - types::storage::enums::Currency, - i64, - T, - ), + (currency_unit, currency, amount, item): (&api::CurrencyUnit, enums::Currency, i64, T), ) -> Result { let amount = crate::connector::utils::get_amount_as_f64(currency_unit, amount, currency)?; Ok(Self { diff --git a/crates/router/src/connector/bankofamerica.rs b/crates/router/src/connector/bankofamerica.rs index 0c01facf386..e5ddd3743b9 100644 --- a/crates/router/src/connector/bankofamerica.rs +++ b/crates/router/src/connector/bankofamerica.rs @@ -120,8 +120,7 @@ where &self, req: &types::RouterData, connectors: &settings::Connectors, - ) -> CustomResult)>, errors::ConnectorError> - { + ) -> CustomResult)>, errors::ConnectorError> { let date = OffsetDateTime::now_utc(); let boa_req = self.get_request_body(req, connectors)?; let http_method = self.get_http_method(); diff --git a/crates/router/src/connector/bankofamerica/transformers.rs b/crates/router/src/connector/bankofamerica/transformers.rs index efece7f61fb..2d7b2307039 100644 --- a/crates/router/src/connector/bankofamerica/transformers.rs +++ b/crates/router/src/connector/bankofamerica/transformers.rs @@ -58,22 +58,10 @@ pub struct BankOfAmericaRouterData { pub router_data: T, } -impl - TryFrom<( - &types::api::CurrencyUnit, - types::storage::enums::Currency, - i64, - T, - )> for BankOfAmericaRouterData -{ +impl TryFrom<(&api::CurrencyUnit, enums::Currency, i64, T)> for BankOfAmericaRouterData { type Error = error_stack::Report; fn try_from( - (currency_unit, currency, amount, item): ( - &types::api::CurrencyUnit, - types::storage::enums::Currency, - i64, - T, - ), + (currency_unit, currency, amount, item): (&api::CurrencyUnit, enums::Currency, i64, T), ) -> Result { let amount = utils::get_amount_as_string(currency_unit, amount, currency)?; Ok(Self { @@ -602,7 +590,7 @@ impl merchant_intitiated_transaction: Some(MerchantInitiatedTransaction { reason: None, original_authorized_amount: Some(utils::get_amount_as_string( - &types::api::CurrencyUnit::Base, + &api::CurrencyUnit::Base, original_amount, original_currency, )?), diff --git a/crates/router/src/connector/billwerk/transformers.rs b/crates/router/src/connector/billwerk/transformers.rs index 4e91a2c9ffb..3e5e53286e3 100644 --- a/crates/router/src/connector/billwerk/transformers.rs +++ b/crates/router/src/connector/billwerk/transformers.rs @@ -14,22 +14,10 @@ pub struct BillwerkRouterData { pub router_data: T, } -impl - TryFrom<( - &types::api::CurrencyUnit, - types::storage::enums::Currency, - i64, - T, - )> for BillwerkRouterData -{ +impl TryFrom<(&api::CurrencyUnit, enums::Currency, i64, T)> for BillwerkRouterData { type Error = error_stack::Report; fn try_from( - (_currency_unit, _currency, amount, item): ( - &types::api::CurrencyUnit, - types::storage::enums::Currency, - i64, - T, - ), + (_currency_unit, _currency, amount, item): (&api::CurrencyUnit, enums::Currency, i64, T), ) -> Result { Ok(Self { amount, diff --git a/crates/router/src/connector/bitpay/transformers.rs b/crates/router/src/connector/bitpay/transformers.rs index 74fa0b5c595..a70c4ba3ac2 100644 --- a/crates/router/src/connector/bitpay/transformers.rs +++ b/crates/router/src/connector/bitpay/transformers.rs @@ -15,20 +15,13 @@ pub struct BitpayRouterData { pub router_data: T, } -impl - TryFrom<( - &types::api::CurrencyUnit, - types::storage::enums::Currency, - i64, - T, - )> for BitpayRouterData -{ +impl TryFrom<(&api::CurrencyUnit, enums::Currency, i64, T)> for BitpayRouterData { type Error = error_stack::Report; fn try_from( (_currency_unit, _currency, amount, router_data): ( - &types::api::CurrencyUnit, - types::storage::enums::Currency, + &api::CurrencyUnit, + enums::Currency, i64, T, ), @@ -81,7 +74,7 @@ impl TryFrom<&ConnectorAuthType> for BitpayAuthType { type Error = error_stack::Report; fn try_from(auth_type: &ConnectorAuthType) -> Result { match auth_type { - types::ConnectorAuthType::HeaderKey { api_key } => Ok(Self { + ConnectorAuthType::HeaderKey { api_key } => Ok(Self { api_key: api_key.to_owned(), }), _ => Err(errors::ConnectorError::FailedToObtainAuthType.into()), diff --git a/crates/router/src/connector/bluesnap/transformers.rs b/crates/router/src/connector/bluesnap/transformers.rs index 2a572a2231a..0630fef2748 100644 --- a/crates/router/src/connector/bluesnap/transformers.rs +++ b/crates/router/src/connector/bluesnap/transformers.rs @@ -36,22 +36,10 @@ pub struct BluesnapRouterData { pub router_data: T, } -impl - TryFrom<( - &types::api::CurrencyUnit, - types::storage::enums::Currency, - i64, - T, - )> for BluesnapRouterData -{ +impl TryFrom<(&api::CurrencyUnit, enums::Currency, i64, T)> for BluesnapRouterData { type Error = error_stack::Report; fn try_from( - (currency_unit, currency, amount, item): ( - &types::api::CurrencyUnit, - types::storage::enums::Currency, - i64, - T, - ), + (currency_unit, currency, amount, item): (&api::CurrencyUnit, enums::Currency, i64, T), ) -> Result { let amount = utils::get_amount_as_string(currency_unit, amount, currency)?; Ok(Self { @@ -150,7 +138,7 @@ pub struct BluesnapGooglePayObject { #[derive(Debug, Serialize)] #[serde(rename_all = "camelCase")] pub struct BluesnapApplePayObject { - token: api_models::payments::ApplePayWalletData, + token: payments::ApplePayWalletData, } #[derive(Debug, Serialize)] @@ -447,23 +435,19 @@ impl TryFrom<&types::PaymentsSessionRouterData> for BluesnapCreateWalletToken { let apple_pay_metadata = item.get_connector_meta()?.expose(); let applepay_metadata = apple_pay_metadata .clone() - .parse_value::( + .parse_value::( "ApplepayCombinedSessionTokenData", ) .map(|combined_metadata| { - api_models::payments::ApplepaySessionTokenMetadata::ApplePayCombined( + payments::ApplepaySessionTokenMetadata::ApplePayCombined( combined_metadata.apple_pay_combined, ) }) .or_else(|_| { apple_pay_metadata - .parse_value::( - "ApplepaySessionTokenData", - ) + .parse_value::("ApplepaySessionTokenData") .map(|old_metadata| { - api_models::payments::ApplepaySessionTokenMetadata::ApplePay( - old_metadata.apple_pay, - ) + payments::ApplepaySessionTokenMetadata::ApplePay(old_metadata.apple_pay) }) }) .change_context(errors::ConnectorError::ParsingFailed)?; @@ -500,31 +484,26 @@ impl TryFrom( + .parse_value::( "ApplepayCombinedSessionTokenData", ) .map(|combined_metadata| { - api_models::payments::ApplepaySessionTokenMetadata::ApplePayCombined( + payments::ApplepaySessionTokenMetadata::ApplePayCombined( combined_metadata.apple_pay_combined, ) }) .or_else(|_| { metadata - .parse_value::( - "ApplepaySessionTokenData", - ) + .parse_value::("ApplepaySessionTokenData") .map(|old_metadata| { - api_models::payments::ApplepaySessionTokenMetadata::ApplePay( - old_metadata.apple_pay, - ) + payments::ApplepaySessionTokenMetadata::ApplePay(old_metadata.apple_pay) }) }) .change_context(errors::ConnectorError::ParsingFailed)?; @@ -543,16 +522,15 @@ impl TryFrom Ok(hosted_value .redirect_url - .map(|url| services::RedirectForm::from((url, services::Method::Get)))), + .map(|url| RedirectForm::from((url, services::Method::Get)))), None => Err(errors::ConnectorError::MissingConnectorRedirectionPayload { field_name: "redirect_url", }), diff --git a/crates/router/src/connector/braintree.rs b/crates/router/src/connector/braintree.rs index 1b55f12cab1..f7e142a9af9 100644 --- a/crates/router/src/connector/braintree.rs +++ b/crates/router/src/connector/braintree.rs @@ -1467,10 +1467,8 @@ impl api::IncomingWebhook for Braintree { match response.dispute { Some(dispute_data) => { - let currency = diesel_models::enums::Currency::from_str( - dispute_data.currency_iso_code.as_str(), - ) - .change_context(errors::ConnectorError::WebhookBodyDecodingFailed)?; + let currency = enums::Currency::from_str(dispute_data.currency_iso_code.as_str()) + .change_context(errors::ConnectorError::WebhookBodyDecodingFailed)?; Ok(api::disputes::DisputePayload { amount: connector_utils::to_currency_lower_unit( dispute_data.amount_disputed.to_string(), diff --git a/crates/router/src/connector/braintree/braintree_graphql_transformers.rs b/crates/router/src/connector/braintree/braintree_graphql_transformers.rs index 480867c6cbd..bd860e72900 100644 --- a/crates/router/src/connector/braintree/braintree_graphql_transformers.rs +++ b/crates/router/src/connector/braintree/braintree_graphql_transformers.rs @@ -27,22 +27,10 @@ pub struct BraintreeRouterData { pub router_data: T, } -impl - TryFrom<( - &types::api::CurrencyUnit, - types::storage::enums::Currency, - i64, - T, - )> for BraintreeRouterData -{ +impl TryFrom<(&api::CurrencyUnit, enums::Currency, i64, T)> for BraintreeRouterData { type Error = error_stack::Report; fn try_from( - (currency_unit, currency, amount, item): ( - &types::api::CurrencyUnit, - types::storage::enums::Currency, - i64, - T, - ), + (currency_unit, currency, amount, item): (&api::CurrencyUnit, enums::Currency, i64, T), ) -> Result { let amount = utils::get_amount_as_string(currency_unit, amount, currency)?; Ok(Self { @@ -80,7 +68,7 @@ pub enum BraintreePaymentsRequest { #[derive(Debug, Deserialize)] pub struct BraintreeMeta { merchant_account_id: Secret, - merchant_config_currency: types::storage::enums::Currency, + merchant_config_currency: enums::Currency, } impl TryFrom<&Option> for BraintreeMeta { diff --git a/crates/router/src/connector/braintree/transformers.rs b/crates/router/src/connector/braintree/transformers.rs index ef111a2588a..e278ff40b43 100644 --- a/crates/router/src/connector/braintree/transformers.rs +++ b/crates/router/src/connector/braintree/transformers.rs @@ -21,7 +21,7 @@ pub struct PaymentOptions { #[derive(Debug, Deserialize)] pub struct BraintreeMeta { merchant_account_id: Option>, - merchant_config_currency: Option, + merchant_config_currency: Option, } #[derive(Debug, Serialize, Eq, PartialEq)] @@ -304,7 +304,7 @@ impl ) -> Result { Ok(Self { response: Ok(types::PaymentsResponseData::SessionResponse { - session_token: types::api::SessionToken::Paypal(Box::new( + session_token: api::SessionToken::Paypal(Box::new( payments::PaypalSessionTokenResponse { session_token: item.response.client_token.value.expose(), }, diff --git a/crates/router/src/connector/cashtocode/transformers.rs b/crates/router/src/connector/cashtocode/transformers.rs index 525b19df001..f8c275df33b 100644 --- a/crates/router/src/connector/cashtocode/transformers.rs +++ b/crates/router/src/connector/cashtocode/transformers.rs @@ -203,13 +203,13 @@ fn get_redirect_form_data( enums::PaymentMethodType::ClassicReward => Ok(services::RedirectForm::Form { //redirect form is manually constructed because the connector for this pm type expects query params in the url endpoint: response_data.pay_url.to_string(), - method: services::Method::Post, + method: Method::Post, form_fields: Default::default(), }), enums::PaymentMethodType::Evoucher => Ok(services::RedirectForm::from(( //here the pay url gets parsed, and query params are sent as formfields as the connector expects response_data.pay_url, - services::Method::Get, + Method::Get, ))), _ => Err(errors::ConnectorError::NotImplemented( utils::get_unimplemented_payment_method_error_message("CashToCode"), diff --git a/crates/router/src/connector/checkout/transformers.rs b/crates/router/src/connector/checkout/transformers.rs index 3fff6d940d6..cc937c0ef60 100644 --- a/crates/router/src/connector/checkout/transformers.rs +++ b/crates/router/src/connector/checkout/transformers.rs @@ -23,22 +23,10 @@ pub struct CheckoutRouterData { pub router_data: T, } -impl - TryFrom<( - &types::api::CurrencyUnit, - types::storage::enums::Currency, - i64, - T, - )> for CheckoutRouterData -{ +impl TryFrom<(&api::CurrencyUnit, enums::Currency, i64, T)> for CheckoutRouterData { type Error = error_stack::Report; fn try_from( - (_currency_unit, _currency, amount, item): ( - &types::api::CurrencyUnit, - types::storage::enums::Currency, - i64, - T, - ), + (_currency_unit, _currency, amount, item): (&api::CurrencyUnit, enums::Currency, i64, T), ) -> Result { Ok(Self { amount, diff --git a/crates/router/src/connector/coinbase.rs b/crates/router/src/connector/coinbase.rs index 8bfbe8b695a..497cf81918e 100644 --- a/crates/router/src/connector/coinbase.rs +++ b/crates/router/src/connector/coinbase.rs @@ -52,8 +52,7 @@ where &self, req: &types::RouterData, _connectors: &settings::Connectors, - ) -> CustomResult)>, errors::ConnectorError> - { + ) -> CustomResult)>, errors::ConnectorError> { let mut header = vec![ ( headers::CONTENT_TYPE.to_string(), diff --git a/crates/router/src/connector/cryptopay/transformers.rs b/crates/router/src/connector/cryptopay/transformers.rs index b8f398a0d96..baea07faa60 100644 --- a/crates/router/src/connector/cryptopay/transformers.rs +++ b/crates/router/src/connector/cryptopay/transformers.rs @@ -19,19 +19,12 @@ pub struct CryptopayRouterData { pub router_data: T, } -impl - TryFrom<( - &types::api::CurrencyUnit, - types::storage::enums::Currency, - i64, - T, - )> for CryptopayRouterData -{ +impl TryFrom<(&types::api::CurrencyUnit, enums::Currency, i64, T)> for CryptopayRouterData { type Error = error_stack::Report; fn try_from( (currency_unit, currency, amount, item): ( &types::api::CurrencyUnit, - types::storage::enums::Currency, + enums::Currency, i64, T, ), diff --git a/crates/router/src/connector/cybersource.rs b/crates/router/src/connector/cybersource.rs index d93a9405ae3..1a85498ad06 100644 --- a/crates/router/src/connector/cybersource.rs +++ b/crates/router/src/connector/cybersource.rs @@ -233,8 +233,7 @@ where &self, req: &types::RouterData, connectors: &settings::Connectors, - ) -> CustomResult)>, errors::ConnectorError> - { + ) -> CustomResult)>, errors::ConnectorError> { let date = OffsetDateTime::now_utc(); let cybersource_req = self.get_request_body(req, connectors)?; let auth = cybersource::CybersourceAuthType::try_from(&req.connector_auth_type)?; @@ -740,8 +739,7 @@ impl ConnectorIntegration CustomResult)>, errors::ConnectorError> - { + ) -> CustomResult)>, errors::ConnectorError> { self.build_headers(req, connectors) } @@ -837,12 +835,12 @@ impl ConnectorIntegration CustomResult { Ok(format!( "{}pts/v2/payments/", - api::ConnectorCommon::base_url(self, connectors) + ConnectorCommon::base_url(self, connectors) )) } fn get_request_body( diff --git a/crates/router/src/connector/cybersource/transformers.rs b/crates/router/src/connector/cybersource/transformers.rs index 8251de8ca31..545ee672ed9 100644 --- a/crates/router/src/connector/cybersource/transformers.rs +++ b/crates/router/src/connector/cybersource/transformers.rs @@ -38,22 +38,10 @@ pub struct CybersourceRouterData { pub router_data: T, } -impl - TryFrom<( - &types::api::CurrencyUnit, - types::storage::enums::Currency, - i64, - T, - )> for CybersourceRouterData -{ +impl TryFrom<(&api::CurrencyUnit, enums::Currency, i64, T)> for CybersourceRouterData { type Error = error_stack::Report; fn try_from( - (currency_unit, currency, amount, item): ( - &types::api::CurrencyUnit, - types::storage::enums::Currency, - i64, - T, - ), + (currency_unit, currency, amount, item): (&api::CurrencyUnit, enums::Currency, i64, T), ) -> Result { // This conversion function is used at different places in the file, if updating this, keep a check for those let amount = utils::get_amount_as_string(currency_unit, amount, currency)?; @@ -570,7 +558,7 @@ impl .clone() .and_then(|mandate_id| mandate_id.mandate_reference_id) { - Some(api_models::payments::MandateReferenceId::ConnectorMandateId(_)) => { + Some(payments::MandateReferenceId::ConnectorMandateId(_)) => { let original_amount = item .router_data .get_recurring_mandate_payment_data()? @@ -587,7 +575,7 @@ impl merchant_intitiated_transaction: Some(MerchantInitiatedTransaction { reason: None, original_authorized_amount: Some(utils::get_amount_as_string( - &types::api::CurrencyUnit::Base, + &api::CurrencyUnit::Base, original_amount, original_currency, )?), @@ -596,9 +584,7 @@ impl }), ) } - Some(api_models::payments::MandateReferenceId::NetworkMandateId( - network_transaction_id, - )) => { + Some(payments::MandateReferenceId::NetworkMandateId(network_transaction_id)) => { let (original_amount, original_currency) = match network .clone() .map(|network| network.to_lowercase()) diff --git a/crates/router/src/connector/dlocal.rs b/crates/router/src/connector/dlocal.rs index c92c6b8b854..dc99619e9da 100644 --- a/crates/router/src/connector/dlocal.rs +++ b/crates/router/src/connector/dlocal.rs @@ -56,8 +56,7 @@ where &self, req: &types::RouterData, connectors: &settings::Connectors, - ) -> CustomResult)>, errors::ConnectorError> - { + ) -> CustomResult)>, errors::ConnectorError> { let dlocal_req = self.get_request_body(req, connectors)?; let date = date_time::date_as_yyyymmddthhmmssmmmz() diff --git a/crates/router/src/connector/dlocal/transformers.rs b/crates/router/src/connector/dlocal/transformers.rs index 14337ca0765..c073dc03cc9 100644 --- a/crates/router/src/connector/dlocal/transformers.rs +++ b/crates/router/src/connector/dlocal/transformers.rs @@ -57,20 +57,13 @@ pub struct DlocalRouterData { pub router_data: T, } -impl - TryFrom<( - &types::api::CurrencyUnit, - types::storage::enums::Currency, - i64, - T, - )> for DlocalRouterData -{ +impl TryFrom<(&api::CurrencyUnit, enums::Currency, i64, T)> for DlocalRouterData { type Error = error_stack::Report; fn try_from( (_currency_unit, _currency, amount, router_data): ( - &types::api::CurrencyUnit, - types::storage::enums::Currency, + &api::CurrencyUnit, + enums::Currency, i64, T, ), diff --git a/crates/router/src/connector/fiserv/transformers.rs b/crates/router/src/connector/fiserv/transformers.rs index fcb808c6700..3d40876c353 100644 --- a/crates/router/src/connector/fiserv/transformers.rs +++ b/crates/router/src/connector/fiserv/transformers.rs @@ -18,20 +18,13 @@ pub struct FiservRouterData { pub router_data: T, } -impl - TryFrom<( - &types::api::CurrencyUnit, - types::storage::enums::Currency, - i64, - T, - )> for FiservRouterData -{ +impl TryFrom<(&api::CurrencyUnit, enums::Currency, i64, T)> for FiservRouterData { type Error = error_stack::Report; fn try_from( (currency_unit, currency, amount, router_data): ( - &types::api::CurrencyUnit, - types::storage::enums::Currency, + &api::CurrencyUnit, + enums::Currency, i64, T, ), diff --git a/crates/router/src/connector/globalpay/transformers.rs b/crates/router/src/connector/globalpay/transformers.rs index cfb822715bf..478da2cf902 100644 --- a/crates/router/src/connector/globalpay/transformers.rs +++ b/crates/router/src/connector/globalpay/transformers.rs @@ -269,7 +269,7 @@ impl }) .transpose()?; let redirection_data = - redirect_url.map(|url| services::RedirectForm::from((url, services::Method::Get))); + redirect_url.map(|url| RedirectForm::from((url, services::Method::Get))); Ok(Self { status, response: get_payment_response(status, item.response, redirection_data), @@ -434,8 +434,8 @@ fn get_mandate_details(item: &types::PaymentsAuthorizeRouterData) -> Result requests::Sequence::Subsequent, - false => requests::Sequence::First, + true => Sequence::Subsequent, + false => Sequence::First, }), }), connector_mandate_id, diff --git a/crates/router/src/connector/gocardless/transformers.rs b/crates/router/src/connector/gocardless/transformers.rs index 59177307f22..e05f1b469ac 100644 --- a/crates/router/src/connector/gocardless/transformers.rs +++ b/crates/router/src/connector/gocardless/transformers.rs @@ -24,22 +24,10 @@ pub struct GocardlessRouterData { pub router_data: T, } -impl - TryFrom<( - &types::api::CurrencyUnit, - types::storage::enums::Currency, - i64, - T, - )> for GocardlessRouterData -{ +impl TryFrom<(&api::CurrencyUnit, enums::Currency, i64, T)> for GocardlessRouterData { type Error = error_stack::Report; fn try_from( - (_currency_unit, _currency, amount, item): ( - &types::api::CurrencyUnit, - types::storage::enums::Currency, - i64, - T, - ), + (_currency_unit, _currency, amount, item): (&api::CurrencyUnit, enums::Currency, i64, T), ) -> Result { Ok(Self { amount, @@ -736,7 +724,7 @@ impl Ok(Self { status: enums::AttemptStatus::from(item.response.payments.status), response: Ok(types::PaymentsResponseData::TransactionResponse { - resource_id: types::ResponseId::ConnectorTransactionId(item.response.payments.id), + resource_id: ResponseId::ConnectorTransactionId(item.response.payments.id), redirection_data: None, mandate_reference: Some(mandate_reference), connector_metadata: None, @@ -771,7 +759,7 @@ impl Ok(Self { status: enums::AttemptStatus::from(item.response.payments.status), response: Ok(types::PaymentsResponseData::TransactionResponse { - resource_id: types::ResponseId::ConnectorTransactionId(item.response.payments.id), + resource_id: ResponseId::ConnectorTransactionId(item.response.payments.id), redirection_data: None, mandate_reference: None, connector_metadata: None, diff --git a/crates/router/src/connector/helcim/transformers.rs b/crates/router/src/connector/helcim/transformers.rs index 2dc44c8a19b..655d8a8663b 100644 --- a/crates/router/src/connector/helcim/transformers.rs +++ b/crates/router/src/connector/helcim/transformers.rs @@ -19,22 +19,10 @@ pub struct HelcimRouterData { pub router_data: T, } -impl - TryFrom<( - &types::api::CurrencyUnit, - types::storage::enums::Currency, - i64, - T, - )> for HelcimRouterData -{ +impl TryFrom<(&api::CurrencyUnit, 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, - ), + (currency_unit, currency, amount, item): (&api::CurrencyUnit, enums::Currency, i64, T), ) -> Result { let amount = utils::get_amount_as_f64(currency_unit, amount, currency)?; Ok(Self { @@ -45,9 +33,9 @@ impl } pub fn check_currency( - currency: types::storage::enums::Currency, -) -> Result { - if currency == types::storage::enums::Currency::USD { + currency: enums::Currency, +) -> Result { + if currency == enums::Currency::USD { Ok(currency) } else { Err(errors::ConnectorError::NotSupported { diff --git a/crates/router/src/connector/iatapay/transformers.rs b/crates/router/src/connector/iatapay/transformers.rs index 8e45b96e69c..bce25a76429 100644 --- a/crates/router/src/connector/iatapay/transformers.rs +++ b/crates/router/src/connector/iatapay/transformers.rs @@ -37,22 +37,10 @@ pub struct IatapayRouterData { amount: f64, router_data: T, } -impl - TryFrom<( - &types::api::CurrencyUnit, - types::storage::enums::Currency, - i64, - T, - )> for IatapayRouterData -{ +impl TryFrom<(&api::CurrencyUnit, enums::Currency, i64, T)> for IatapayRouterData { type Error = error_stack::Report; fn try_from( - (currency_unit, currency, amount, item): ( - &types::api::CurrencyUnit, - types::storage::enums::Currency, - i64, - T, - ), + (currency_unit, currency, amount, item): (&api::CurrencyUnit, enums::Currency, i64, T), ) -> Result { Ok(Self { amount: connector_util::get_amount_as_f64(currency_unit, amount, currency)?, @@ -114,7 +102,7 @@ impl TryFrom< &IatapayRouterData< &types::RouterData< - types::api::payments::Authorize, + api::payments::Authorize, PaymentsAuthorizeData, types::PaymentsResponseData, >, @@ -126,7 +114,7 @@ impl fn try_from( item: &IatapayRouterData< &types::RouterData< - types::api::payments::Authorize, + api::payments::Authorize, PaymentsAuthorizeData, types::PaymentsResponseData, >, diff --git a/crates/router/src/connector/klarna/transformers.rs b/crates/router/src/connector/klarna/transformers.rs index 29dccfbf32b..db77b9c30cd 100644 --- a/crates/router/src/connector/klarna/transformers.rs +++ b/crates/router/src/connector/klarna/transformers.rs @@ -14,20 +14,13 @@ pub struct KlarnaRouterData { router_data: T, } -impl - TryFrom<( - &types::api::CurrencyUnit, - types::storage::enums::Currency, - i64, - T, - )> for KlarnaRouterData -{ +impl TryFrom<(&types::api::CurrencyUnit, enums::Currency, i64, T)> for KlarnaRouterData { type Error = error_stack::Report; fn try_from( (_currency_unit, _currency, amount, router_data): ( &types::api::CurrencyUnit, - types::storage::enums::Currency, + enums::Currency, i64, T, ), diff --git a/crates/router/src/connector/multisafepay/transformers.rs b/crates/router/src/connector/multisafepay/transformers.rs index 850bd058e41..f993928d94f 100644 --- a/crates/router/src/connector/multisafepay/transformers.rs +++ b/crates/router/src/connector/multisafepay/transformers.rs @@ -20,23 +20,11 @@ pub struct MultisafepayRouterData { router_data: T, } -impl - TryFrom<( - &types::api::CurrencyUnit, - types::storage::enums::Currency, - i64, - T, - )> for MultisafepayRouterData -{ +impl TryFrom<(&api::CurrencyUnit, enums::Currency, i64, T)> for MultisafepayRouterData { type Error = error_stack::Report; fn try_from( - (_currency_unit, _currency, amount, item): ( - &types::api::CurrencyUnit, - types::storage::enums::Currency, - i64, - T, - ), + (_currency_unit, _currency, amount, item): (&api::CurrencyUnit, enums::Currency, i64, T), ) -> Result { Ok(Self { amount, @@ -677,9 +665,8 @@ impl MultisafepayPaymentStatus::Declined }; - let status = enums::AttemptStatus::from( - payment_response.data.status.unwrap_or(default_status), - ); + let status = + AttemptStatus::from(payment_response.data.status.unwrap_or(default_status)); Ok(Self { status, diff --git a/crates/router/src/connector/netcetera/transformers.rs b/crates/router/src/connector/netcetera/transformers.rs index 7c95578bfd6..4bf61ce1ca4 100644 --- a/crates/router/src/connector/netcetera/transformers.rs +++ b/crates/router/src/connector/netcetera/transformers.rs @@ -16,18 +16,13 @@ pub struct NetceteraRouterData { pub router_data: T, } -impl - TryFrom<( - &types::api::CurrencyUnit, - types::storage::enums::Currency, - i64, - T, - )> for NetceteraRouterData +impl TryFrom<(&api::CurrencyUnit, types::storage::enums::Currency, i64, T)> + for NetceteraRouterData { type Error = error_stack::Report; fn try_from( (_currency_unit, _currency, amount, item): ( - &types::api::CurrencyUnit, + &api::CurrencyUnit, types::storage::enums::Currency, i64, T, diff --git a/crates/router/src/connector/nexinets.rs b/crates/router/src/connector/nexinets.rs index 1370fcf11cd..88aa149a267 100644 --- a/crates/router/src/connector/nexinets.rs +++ b/crates/router/src/connector/nexinets.rs @@ -120,7 +120,7 @@ impl ConnectorCommon for Nexinets { if !field.is_empty() { msg.push_str(format!("{} : {}", field, error.message).as_str()); } else { - msg = error.message.to_owned(); + error.message.clone_into(&mut msg) } if message.is_empty() { message.push_str(&msg); diff --git a/crates/router/src/connector/nmi/transformers.rs b/crates/router/src/connector/nmi/transformers.rs index ea61782d498..fdc65963fe8 100644 --- a/crates/router/src/connector/nmi/transformers.rs +++ b/crates/router/src/connector/nmi/transformers.rs @@ -42,11 +42,11 @@ impl TryFrom<&ConnectorAuthType> for NmiAuthType { type Error = Error; fn try_from(auth_type: &ConnectorAuthType) -> Result { match auth_type { - types::ConnectorAuthType::HeaderKey { api_key } => Ok(Self { + ConnectorAuthType::HeaderKey { api_key } => Ok(Self { api_key: api_key.to_owned(), public_key: None, }), - types::ConnectorAuthType::BodyKey { api_key, key1 } => Ok(Self { + ConnectorAuthType::BodyKey { api_key, key1 } => Ok(Self { api_key: api_key.to_owned(), public_key: Some(key1.to_owned()), }), @@ -61,20 +61,13 @@ pub struct NmiRouterData { pub router_data: T, } -impl - TryFrom<( - &types::api::CurrencyUnit, - types::storage::enums::Currency, - i64, - T, - )> for NmiRouterData -{ +impl TryFrom<(&api::CurrencyUnit, enums::Currency, i64, T)> for NmiRouterData { type Error = Report; fn try_from( (_currency_unit, currency, amount, router_data): ( - &types::api::CurrencyUnit, - types::storage::enums::Currency, + &api::CurrencyUnit, + enums::Currency, i64, T, ), diff --git a/crates/router/src/connector/nuvei.rs b/crates/router/src/connector/nuvei.rs index aa4157a68d8..d08820266c3 100644 --- a/crates/router/src/connector/nuvei.rs +++ b/crates/router/src/connector/nuvei.rs @@ -161,7 +161,7 @@ impl ) -> CustomResult { Ok(format!( "{}ppp/api/v1/payment.do", - api::ConnectorCommon::base_url(self, connectors) + ConnectorCommon::base_url(self, connectors) )) } fn get_request_body( @@ -248,7 +248,7 @@ impl ConnectorIntegration CustomResult { Ok(format!( "{}ppp/api/v1/voidTransaction.do", - api::ConnectorCommon::base_url(self, connectors) + ConnectorCommon::base_url(self, connectors) )) } @@ -334,7 +334,7 @@ impl ConnectorIntegration CustomResult { Ok(format!( "{}ppp/api/v1/getPaymentStatus.do", - api::ConnectorCommon::base_url(self, connectors) + ConnectorCommon::base_url(self, connectors) )) } @@ -417,7 +417,7 @@ impl ConnectorIntegration CustomResult { Ok(format!( "{}ppp/api/v1/settleTransaction.do", - api::ConnectorCommon::base_url(self, connectors) + ConnectorCommon::base_url(self, connectors) )) } @@ -509,7 +509,7 @@ impl ConnectorIntegration CustomResult { Ok(format!( "{}ppp/api/v1/payment.do", - api::ConnectorCommon::base_url(self, connectors) + ConnectorCommon::base_url(self, connectors) )) } @@ -671,7 +671,7 @@ impl ) -> CustomResult { Ok(format!( "{}ppp/api/v1/getSessionToken.do", - api::ConnectorCommon::base_url(self, connectors) + ConnectorCommon::base_url(self, connectors) )) } @@ -756,7 +756,7 @@ impl ConnectorIntegration CustomResult { Ok(format!( "{}ppp/api/v1/initPayment.do", - api::ConnectorCommon::base_url(self, connectors) + ConnectorCommon::base_url(self, connectors) )) } @@ -839,7 +839,7 @@ impl ConnectorIntegration CustomResult { Ok(format!( "{}ppp/api/v1/refundTransaction.do", - api::ConnectorCommon::base_url(self, connectors) + ConnectorCommon::base_url(self, connectors) )) } @@ -954,7 +954,7 @@ impl api::IncomingWebhook for Nuvei { serde_urlencoded::from_str::(&request.query_params) .change_context(errors::ConnectorError::WebhookBodyDecodingFailed)?; Ok(api_models::webhooks::ObjectReferenceId::PaymentId( - types::api::PaymentIdType::ConnectorTransactionId(body.ppp_transaction_id), + api::PaymentIdType::ConnectorTransactionId(body.ppp_transaction_id), )) } diff --git a/crates/router/src/connector/opennode/transformers.rs b/crates/router/src/connector/opennode/transformers.rs index 3267caf60d6..09da5fc8f94 100644 --- a/crates/router/src/connector/opennode/transformers.rs +++ b/crates/router/src/connector/opennode/transformers.rs @@ -16,20 +16,13 @@ pub struct OpennodeRouterData { pub router_data: T, } -impl - TryFrom<( - &types::api::CurrencyUnit, - types::storage::enums::Currency, - i64, - T, - )> for OpennodeRouterData -{ +impl TryFrom<(&api::CurrencyUnit, enums::Currency, i64, T)> for OpennodeRouterData { type Error = error_stack::Report; fn try_from( (_currency_unit, _currency, amount, router_data): ( - &types::api::CurrencyUnit, - types::storage::enums::Currency, + &api::CurrencyUnit, + enums::Currency, i64, T, ), diff --git a/crates/router/src/connector/payeezy/transformers.rs b/crates/router/src/connector/payeezy/transformers.rs index 0ceed1390be..a8442f7ea8a 100644 --- a/crates/router/src/connector/payeezy/transformers.rs +++ b/crates/router/src/connector/payeezy/transformers.rs @@ -15,20 +15,13 @@ pub struct PayeezyRouterData { pub router_data: T, } -impl - TryFrom<( - &types::api::CurrencyUnit, - types::storage::enums::Currency, - i64, - T, - )> for PayeezyRouterData -{ +impl TryFrom<(&api::CurrencyUnit, enums::Currency, i64, T)> for PayeezyRouterData { type Error = error_stack::Report; fn try_from( (currency_unit, currency, amount, router_data): ( - &types::api::CurrencyUnit, - types::storage::enums::Currency, + &api::CurrencyUnit, + enums::Currency, i64, T, ), diff --git a/crates/router/src/connector/payme/transformers.rs b/crates/router/src/connector/payme/transformers.rs index 7f5e30402d6..0b3f4fb48ce 100644 --- a/crates/router/src/connector/payme/transformers.rs +++ b/crates/router/src/connector/payme/transformers.rs @@ -29,22 +29,10 @@ pub struct PaymeRouterData { pub router_data: T, } -impl - TryFrom<( - &types::api::CurrencyUnit, - types::storage::enums::Currency, - i64, - T, - )> for PaymeRouterData -{ +impl TryFrom<(&api::CurrencyUnit, enums::Currency, i64, T)> for PaymeRouterData { type Error = error_stack::Report; fn try_from( - (_currency_unit, _currency, amount, item): ( - &types::api::CurrencyUnit, - types::storage::enums::Currency, - i64, - T, - ), + (_currency_unit, _currency, amount, item): (&api::CurrencyUnit, enums::Currency, i64, T), ) -> Result { Ok(Self { amount, @@ -630,7 +618,7 @@ impl TryFrom<&types::PaymentsAuthorizeRouterData> for PayRequest { type Error = error_stack::Report; fn try_from(item: &types::PaymentsAuthorizeRouterData) -> Result { match item.request.payment_method_data.clone() { - domain::PaymentMethodData::Card(req_card) => { + PaymentMethodData::Card(req_card) => { let card = PaymeCard { credit_card_cvv: req_card.card_cvc.clone(), credit_card_exp: req_card @@ -652,23 +640,21 @@ impl TryFrom<&types::PaymentsAuthorizeRouterData> for PayRequest { language: LANGUAGE.to_string(), }) } - domain::PaymentMethodData::CardRedirect(_) - | domain::PaymentMethodData::Wallet(_) - | domain::PaymentMethodData::PayLater(_) - | domain::PaymentMethodData::BankRedirect(_) - | domain::PaymentMethodData::BankDebit(_) - | domain::PaymentMethodData::BankTransfer(_) - | domain::PaymentMethodData::Crypto(_) - | domain::PaymentMethodData::MandatePayment - | domain::PaymentMethodData::Reward - | domain::PaymentMethodData::Upi(_) - | domain::PaymentMethodData::Voucher(_) - | domain::PaymentMethodData::GiftCard(_) - | domain::PaymentMethodData::CardToken(_) => { - Err(errors::ConnectorError::NotImplemented( - utils::get_unimplemented_payment_method_error_message("payme"), - ))? - } + PaymentMethodData::CardRedirect(_) + | PaymentMethodData::Wallet(_) + | PaymentMethodData::PayLater(_) + | PaymentMethodData::BankRedirect(_) + | PaymentMethodData::BankDebit(_) + | PaymentMethodData::BankTransfer(_) + | PaymentMethodData::Crypto(_) + | PaymentMethodData::MandatePayment + | PaymentMethodData::Reward + | PaymentMethodData::Upi(_) + | PaymentMethodData::Voucher(_) + | PaymentMethodData::GiftCard(_) + | PaymentMethodData::CardToken(_) => Err(errors::ConnectorError::NotImplemented( + utils::get_unimplemented_payment_method_error_message("payme"), + ))?, } } } @@ -681,7 +667,7 @@ impl TryFrom<&types::PaymentsCompleteAuthorizeRouterData> for Pay3dsRequest { type Error = error_stack::Report; fn try_from(item: &types::PaymentsCompleteAuthorizeRouterData) -> Result { match item.request.payment_method_data.clone() { - Some(domain::PaymentMethodData::Card(_)) => { + Some(PaymentMethodData::Card(_)) => { let buyer_email = item.request.get_email()?; let buyer_name = item.get_billing_address()?.get_full_name()?; @@ -712,19 +698,19 @@ impl TryFrom<&types::PaymentsCompleteAuthorizeRouterData> for Pay3dsRequest { meta_data_jwt: Secret::new(jwt_data.meta_data), }) } - Some(domain::PaymentMethodData::CardRedirect(_)) - | Some(domain::PaymentMethodData::Wallet(_)) - | Some(domain::PaymentMethodData::PayLater(_)) - | Some(domain::PaymentMethodData::BankRedirect(_)) - | Some(domain::PaymentMethodData::BankDebit(_)) - | Some(domain::PaymentMethodData::BankTransfer(_)) - | Some(domain::PaymentMethodData::Crypto(_)) - | Some(domain::PaymentMethodData::MandatePayment) - | Some(domain::PaymentMethodData::Reward) - | Some(domain::PaymentMethodData::Upi(_)) - | Some(domain::PaymentMethodData::Voucher(_)) - | Some(domain::PaymentMethodData::GiftCard(_)) - | Some(domain::PaymentMethodData::CardToken(_)) + Some(PaymentMethodData::CardRedirect(_)) + | Some(PaymentMethodData::Wallet(_)) + | Some(PaymentMethodData::PayLater(_)) + | Some(PaymentMethodData::BankRedirect(_)) + | Some(PaymentMethodData::BankDebit(_)) + | Some(PaymentMethodData::BankTransfer(_)) + | Some(PaymentMethodData::Crypto(_)) + | Some(PaymentMethodData::MandatePayment) + | Some(PaymentMethodData::Reward) + | Some(PaymentMethodData::Upi(_)) + | Some(PaymentMethodData::Voucher(_)) + | Some(PaymentMethodData::GiftCard(_)) + | Some(PaymentMethodData::CardToken(_)) | None => { Err(errors::ConnectorError::NotImplemented("Tokenize Flow".to_string()).into()) } @@ -736,7 +722,7 @@ impl TryFrom<&types::TokenizationRouterData> for CaptureBuyerRequest { type Error = error_stack::Report; fn try_from(item: &types::TokenizationRouterData) -> Result { match item.request.payment_method_data.clone() { - domain::PaymentMethodData::Card(req_card) => { + PaymentMethodData::Card(req_card) => { let seller_payme_id = PaymeAuthType::try_from(&item.connector_auth_type)?.seller_payme_id; let card = PaymeCard { @@ -750,19 +736,19 @@ impl TryFrom<&types::TokenizationRouterData> for CaptureBuyerRequest { seller_payme_id, }) } - domain::PaymentMethodData::Wallet(_) - | domain::PaymentMethodData::CardRedirect(_) - | domain::PaymentMethodData::PayLater(_) - | domain::PaymentMethodData::BankRedirect(_) - | domain::PaymentMethodData::BankDebit(_) - | domain::PaymentMethodData::BankTransfer(_) - | domain::PaymentMethodData::Crypto(_) - | domain::PaymentMethodData::MandatePayment - | domain::PaymentMethodData::Reward - | domain::PaymentMethodData::Upi(_) - | domain::PaymentMethodData::Voucher(_) - | domain::PaymentMethodData::GiftCard(_) - | domain::PaymentMethodData::CardToken(_) => { + PaymentMethodData::Wallet(_) + | PaymentMethodData::CardRedirect(_) + | PaymentMethodData::PayLater(_) + | PaymentMethodData::BankRedirect(_) + | PaymentMethodData::BankDebit(_) + | PaymentMethodData::BankTransfer(_) + | PaymentMethodData::Crypto(_) + | PaymentMethodData::MandatePayment + | PaymentMethodData::Reward + | PaymentMethodData::Upi(_) + | PaymentMethodData::Voucher(_) + | PaymentMethodData::GiftCard(_) + | PaymentMethodData::CardToken(_) => { Err(errors::ConnectorError::NotImplemented("Tokenize Flow".to_string()).into()) } } @@ -1151,14 +1137,14 @@ impl fn get_services(item: &types::PaymentsPreProcessingRouterData) -> Option { match item.auth_type { - api_models::enums::AuthenticationType::ThreeDs => { + AuthenticationType::ThreeDs => { let settings = ThreeDsSettings { active: true }; Some(ThreeDs { name: ThreeDsType::ThreeDs, settings, }) } - api_models::enums::AuthenticationType::NoThreeDs => None, + AuthenticationType::NoThreeDs => None, } } diff --git a/crates/router/src/connector/paypal.rs b/crates/router/src/connector/paypal.rs index 63912391787..cf81fec6341 100644 --- a/crates/router/src/connector/paypal.rs +++ b/crates/router/src/connector/paypal.rs @@ -960,8 +960,7 @@ impl ConnectorIntegration CustomResult { let paypal_meta: PaypalMeta = to_connector_meta(req.request.connector_meta.clone())?; match req.payment_method { - diesel_models::enums::PaymentMethod::Wallet - | diesel_models::enums::PaymentMethod::BankRedirect => Ok(format!( + enums::PaymentMethod::Wallet | enums::PaymentMethod::BankRedirect => Ok(format!( "{}v2/checkout/orders/{}", self.base_url(connectors), req.request @@ -1633,9 +1632,9 @@ impl services::ConnectorRedirectResponse for Paypal { action: PaymentAction, ) -> CustomResult { match action { - services::PaymentAction::PSync - | services::PaymentAction::CompleteAuthorize - | services::PaymentAction::PaymentAuthenticateCompleteAuthorize => { + PaymentAction::PSync + | PaymentAction::CompleteAuthorize + | PaymentAction::PaymentAuthenticateCompleteAuthorize => { Ok(payments::CallConnectorAction::Trigger) } } diff --git a/crates/router/src/connector/paypal/transformers.rs b/crates/router/src/connector/paypal/transformers.rs index 21b0f04af86..338b8c5ba4e 100644 --- a/crates/router/src/connector/paypal/transformers.rs +++ b/crates/router/src/connector/paypal/transformers.rs @@ -29,18 +29,13 @@ pub struct PaypalRouterData { pub router_data: T, } -impl - TryFrom<( - &types::api::CurrencyUnit, - types::storage::enums::Currency, - i64, - T, - )> for PaypalRouterData +impl TryFrom<(&api::CurrencyUnit, types::storage::enums::Currency, i64, T)> + for PaypalRouterData { type Error = error_stack::Report; fn try_from( (currency_unit, currency, amount, item): ( - &types::api::CurrencyUnit, + &api::CurrencyUnit, types::storage::enums::Currency, i64, T, @@ -153,7 +148,7 @@ impl From<&PaypalRouterData<&types::PaymentsAuthorizeRouterData>> for ItemDetail pub struct Address { address_line_1: Option>, postal_code: Option>, - country_code: api_models::enums::CountryAlpha2, + country_code: enums::CountryAlpha2, admin_area_2: Option, } @@ -216,7 +211,7 @@ pub enum ThreeDsType { #[derive(Debug, Serialize)] pub struct RedirectRequest { name: Secret, - country_code: api_models::enums::CountryAlpha2, + country_code: enums::CountryAlpha2, experience_context: ContextStruct, } @@ -454,12 +449,12 @@ impl TryFrom<&PaypalRouterData<&types::PaymentsAuthorizeRouterData>> for PaypalP let expiry = Some(card.get_expiry_date_as_yyyymm("-")); let attributes = match item.router_data.auth_type { - api_models::enums::AuthenticationType::ThreeDs => Some(ThreeDsSetting { + enums::AuthenticationType::ThreeDs => Some(ThreeDsSetting { verification: ThreeDsMethod { method: ThreeDsType::ScaAlways, }, }), - api_models::enums::AuthenticationType::NoThreeDs => None, + enums::AuthenticationType::NoThreeDs => None, }; let payment_source = Some(PaymentSourceItem::Card(CardRequest { @@ -858,13 +853,13 @@ impl TryFrom<&ConnectorAuthType> for PaypalAuthType { type Error = error_stack::Report; fn try_from(auth_type: &ConnectorAuthType) -> Result { match auth_type { - types::ConnectorAuthType::BodyKey { api_key, key1 } => Ok(Self::AuthWithDetails( + ConnectorAuthType::BodyKey { api_key, key1 } => Ok(Self::AuthWithDetails( PaypalConnectorCredentials::StandardIntegration(StandardFlowCredentials { client_id: key1.to_owned(), client_secret: api_key.to_owned(), }), )), - types::ConnectorAuthType::SignatureKey { + ConnectorAuthType::SignatureKey { api_key, key1, api_secret, @@ -875,7 +870,7 @@ impl TryFrom<&ConnectorAuthType> for PaypalAuthType { payer_id: api_secret.to_owned(), }), )), - types::ConnectorAuthType::TemporaryAuth => Ok(Self::TemporaryAuth), + ConnectorAuthType::TemporaryAuth => Ok(Self::TemporaryAuth), _ => Err(errors::ConnectorError::FailedToObtainAuthType)?, } } @@ -1216,7 +1211,7 @@ fn get_redirect_url( let mut link: Option = None; for item2 in link_vec.iter() { if item2.rel == "payer-action" { - link = item2.href.clone(); + link.clone_from(&item2.href) } } Ok(link) diff --git a/crates/router/src/connector/placetopay/transformers.rs b/crates/router/src/connector/placetopay/transformers.rs index 6ad3d3c2039..e400dc9b399 100644 --- a/crates/router/src/connector/placetopay/transformers.rs +++ b/crates/router/src/connector/placetopay/transformers.rs @@ -20,18 +20,13 @@ pub struct PlacetopayRouterData { pub router_data: T, } -impl - TryFrom<( - &types::api::CurrencyUnit, - types::storage::enums::Currency, - i64, - T, - )> for PlacetopayRouterData +impl TryFrom<(&api::CurrencyUnit, types::storage::enums::Currency, i64, T)> + for PlacetopayRouterData { type Error = error_stack::Report; fn try_from( (_currency_unit, _currency, amount, item): ( - &types::api::CurrencyUnit, + &api::CurrencyUnit, types::storage::enums::Currency, i64, T, diff --git a/crates/router/src/connector/prophetpay/transformers.rs b/crates/router/src/connector/prophetpay/transformers.rs index 570bf8d2b3a..7a8cd6e14d0 100644 --- a/crates/router/src/connector/prophetpay/transformers.rs +++ b/crates/router/src/connector/prophetpay/transformers.rs @@ -19,22 +19,10 @@ pub struct ProphetpayRouterData { pub router_data: T, } -impl - TryFrom<( - &types::api::CurrencyUnit, - types::storage::enums::Currency, - i64, - T, - )> for ProphetpayRouterData -{ +impl TryFrom<(&api::CurrencyUnit, enums::Currency, i64, T)> for ProphetpayRouterData { type Error = error_stack::Report; fn try_from( - (currency_unit, currency, amount, item): ( - &types::api::CurrencyUnit, - types::storage::enums::Currency, - i64, - T, - ), + (currency_unit, currency, amount, item): (&api::CurrencyUnit, enums::Currency, i64, T), ) -> Result { let amount = utils::get_amount_as_f64(currency_unit, amount, currency)?; Ok(Self { @@ -229,7 +217,7 @@ fn get_redirect_url_form( mut redirect_url: Url, complete_auth_url: Option, ) -> CustomResult { - let mut form_fields = std::collections::HashMap::::new(); + let mut form_fields = HashMap::::new(); form_fields.insert( String::from("redirectUrl"), diff --git a/crates/router/src/connector/rapyd.rs b/crates/router/src/connector/rapyd.rs index c5f92c20f5c..5d4579fffa7 100644 --- a/crates/router/src/connector/rapyd.rs +++ b/crates/router/src/connector/rapyd.rs @@ -652,7 +652,7 @@ impl services::ConnectorIntegration &'static str { - api::ConnectorCommon::common_get_content_type(self) + ConnectorCommon::common_get_content_type(self) } fn get_url( diff --git a/crates/router/src/connector/rapyd/transformers.rs b/crates/router/src/connector/rapyd/transformers.rs index 04dc4496b62..22e1074802d 100644 --- a/crates/router/src/connector/rapyd/transformers.rs +++ b/crates/router/src/connector/rapyd/transformers.rs @@ -19,22 +19,10 @@ pub struct RapydRouterData { pub router_data: T, } -impl - TryFrom<( - &types::api::CurrencyUnit, - types::storage::enums::Currency, - i64, - T, - )> for RapydRouterData -{ +impl TryFrom<(&api::CurrencyUnit, enums::Currency, i64, T)> for RapydRouterData { type Error = error_stack::Report; fn try_from( - (_currency_unit, _currency, amount, item): ( - &types::api::CurrencyUnit, - types::storage::enums::Currency, - i64, - T, - ), + (_currency_unit, _currency, amount, item): (&api::CurrencyUnit, enums::Currency, i64, T), ) -> Result { Ok(Self { amount, diff --git a/crates/router/src/connector/shift4.rs b/crates/router/src/connector/shift4.rs index e11a79cb027..e7487e795b6 100644 --- a/crates/router/src/connector/shift4.rs +++ b/crates/router/src/connector/shift4.rs @@ -217,8 +217,8 @@ impl ConnectorIntegration CustomResult<(), errors::ConnectorError> { - if router_data.auth_type == diesel_models::enums::AuthenticationType::ThreeDs - && router_data.payment_method == diesel_models::enums::PaymentMethod::Card + if router_data.auth_type == enums::AuthenticationType::ThreeDs + && router_data.payment_method == enums::PaymentMethod::Card { let integ: Box< &(dyn ConnectorIntegration< diff --git a/crates/router/src/connector/shift4/transformers.rs b/crates/router/src/connector/shift4/transformers.rs index 92a72f42d1e..30f327c700e 100644 --- a/crates/router/src/connector/shift4/transformers.rs +++ b/crates/router/src/connector/shift4/transformers.rs @@ -747,9 +747,9 @@ impl TryFrom<&types::RefundsRouterData> for Shift4RefundRequest { impl From for enums::RefundStatus { fn from(item: Shift4RefundStatus) -> Self { match item { - self::Shift4RefundStatus::Successful => Self::Success, - self::Shift4RefundStatus::Failed => Self::Failure, - self::Shift4RefundStatus::Processing => Self::Pending, + Shift4RefundStatus::Successful => Self::Success, + Shift4RefundStatus::Failed => Self::Failure, + Shift4RefundStatus::Processing => Self::Pending, } } } diff --git a/crates/router/src/connector/signifyd.rs b/crates/router/src/connector/signifyd.rs index 550e1ae30a8..1c045b8da01 100644 --- a/crates/router/src/connector/signifyd.rs +++ b/crates/router/src/connector/signifyd.rs @@ -94,7 +94,7 @@ impl ConnectorCommon for Signifyd { Ok(ErrorResponse { status_code: res.status_code, - code: crate::consts::NO_ERROR_CODE.to_string(), + code: consts::NO_ERROR_CODE.to_string(), message: response.messages.join(" &"), reason: Some(response.errors.to_string()), attempt_status: None, diff --git a/crates/router/src/connector/square/transformers.rs b/crates/router/src/connector/square/transformers.rs index 318e33978cc..c980a4e7494 100644 --- a/crates/router/src/connector/square/transformers.rs +++ b/crates/router/src/connector/square/transformers.rs @@ -5,10 +5,7 @@ use serde::{Deserialize, Serialize}; use crate::{ connector::utils::{self, CardData, PaymentsAuthorizeRequestData, RouterData}, core::errors, - types::{ - self, api, domain, - storage::{self, enums}, - }, + types::{self, api, domain, storage::enums}, unimplemented_payment_method, }; @@ -199,7 +196,7 @@ impl item: types::ResponseRouterData, ) -> Result { Ok(Self { - status: storage::enums::AttemptStatus::Pending, + status: enums::AttemptStatus::Pending, session_token: Some(item.response.session_id.clone().expose()), response: Ok(types::PaymentsResponseData::SessionTokenResponse { session_token: item.response.session_id.expose(), diff --git a/crates/router/src/connector/stax.rs b/crates/router/src/connector/stax.rs index c923ca0577d..4604e9da872 100644 --- a/crates/router/src/connector/stax.rs +++ b/crates/router/src/connector/stax.rs @@ -869,29 +869,25 @@ impl api::IncomingWebhook for Stax { .change_context(errors::ConnectorError::WebhookReferenceIdNotFound)?; match webhook_body.transaction_type { - stax::StaxWebhookEventType::Refund => { - Ok(api_models::webhooks::ObjectReferenceId::RefundId( - api_models::webhooks::RefundIdType::ConnectorRefundId(webhook_body.id), - )) - } - stax::StaxWebhookEventType::Unknown => { + StaxWebhookEventType::Refund => Ok(api_models::webhooks::ObjectReferenceId::RefundId( + api_models::webhooks::RefundIdType::ConnectorRefundId(webhook_body.id), + )), + StaxWebhookEventType::Unknown => { Err(errors::ConnectorError::WebhookEventTypeNotFound.into()) } - stax::StaxWebhookEventType::PreAuth - | stax::StaxWebhookEventType::Capture - | stax::StaxWebhookEventType::Charge - | stax::StaxWebhookEventType::Void => { - Ok(api_models::webhooks::ObjectReferenceId::PaymentId( - api_models::payments::PaymentIdType::ConnectorTransactionId(match webhook_body - .transaction_type - { - stax::StaxWebhookEventType::Capture => webhook_body + StaxWebhookEventType::PreAuth + | StaxWebhookEventType::Capture + | StaxWebhookEventType::Charge + | StaxWebhookEventType::Void => Ok(api_models::webhooks::ObjectReferenceId::PaymentId( + api_models::payments::PaymentIdType::ConnectorTransactionId( + match webhook_body.transaction_type { + StaxWebhookEventType::Capture => webhook_body .auth_id .ok_or(errors::ConnectorError::WebhookReferenceIdNotFound)?, _ => webhook_body.id, - }), - )) - } + }, + ), + )), } } diff --git a/crates/router/src/connector/stax/transformers.rs b/crates/router/src/connector/stax/transformers.rs index 1d097c40e7f..d48a2b1e82b 100644 --- a/crates/router/src/connector/stax/transformers.rs +++ b/crates/router/src/connector/stax/transformers.rs @@ -18,22 +18,10 @@ pub struct StaxRouterData { pub router_data: T, } -impl - TryFrom<( - &types::api::CurrencyUnit, - types::storage::enums::Currency, - i64, - T, - )> for StaxRouterData -{ +impl TryFrom<(&api::CurrencyUnit, enums::Currency, i64, T)> for StaxRouterData { type Error = error_stack::Report; fn try_from( - (currency_unit, currency, amount, item): ( - &types::api::CurrencyUnit, - types::storage::enums::Currency, - i64, - T, - ), + (currency_unit, currency, amount, item): (&api::CurrencyUnit, enums::Currency, i64, T), ) -> Result { let amount = utils::get_amount_as_f64(currency_unit, amount, currency)?; Ok(Self { diff --git a/crates/router/src/connector/stripe.rs b/crates/router/src/connector/stripe.rs index 64789e176fc..724a0ea6f78 100644 --- a/crates/router/src/connector/stripe.rs +++ b/crates/router/src/connector/stripe.rs @@ -2274,7 +2274,7 @@ impl services::ConnectorRedirectResponse for Stripe { _query_params: &str, _json_payload: Option, action: services::PaymentAction, - ) -> CustomResult { + ) -> CustomResult { match action { services::PaymentAction::PSync | services::PaymentAction::CompleteAuthorize diff --git a/crates/router/src/connector/stripe/transformers.rs b/crates/router/src/connector/stripe/transformers.rs index 6932f6b7495..6b82c87b5d1 100644 --- a/crates/router/src/connector/stripe/transformers.rs +++ b/crates/router/src/connector/stripe/transformers.rs @@ -1107,9 +1107,7 @@ impl TryFrom<(&domain::BankRedirectData, Option)> for StripeBillingAddress }, )?; Self { - name: Some(connector_util::BankRedirectBillingData::get_billing_name( - &billing_data, - )?), + name: Some(BankRedirectBillingData::get_billing_name(&billing_data)?), ..Self::default() } }), @@ -2859,16 +2857,14 @@ impl Serialize for StripeNextActionResponse { { match *self { Self::CashappHandleRedirectOrDisplayQrCode(ref i) => { - serde::Serialize::serialize(i, serializer) - } - Self::RedirectToUrl(ref i) => serde::Serialize::serialize(i, serializer), - Self::AlipayHandleRedirect(ref i) => serde::Serialize::serialize(i, serializer), - Self::VerifyWithMicrodeposits(ref i) => serde::Serialize::serialize(i, serializer), - Self::WechatPayDisplayQrCode(ref i) => serde::Serialize::serialize(i, serializer), - Self::DisplayBankTransferInstructions(ref i) => { - serde::Serialize::serialize(i, serializer) + Serialize::serialize(i, serializer) } - Self::NoNextActionBody => serde::Serialize::serialize("NoNextActionBody", serializer), + Self::RedirectToUrl(ref i) => Serialize::serialize(i, serializer), + Self::AlipayHandleRedirect(ref i) => Serialize::serialize(i, serializer), + Self::VerifyWithMicrodeposits(ref i) => Serialize::serialize(i, serializer), + Self::WechatPayDisplayQrCode(ref i) => Serialize::serialize(i, serializer), + Self::DisplayBankTransferInstructions(ref i) => Serialize::serialize(i, serializer), + Self::NoNextActionBody => Serialize::serialize("NoNextActionBody", serializer), } } } @@ -2984,10 +2980,10 @@ pub enum RefundStatus { impl From for enums::RefundStatus { fn from(item: RefundStatus) -> Self { match item { - self::RefundStatus::Succeeded => Self::Success, - self::RefundStatus::Failed => Self::Failure, - self::RefundStatus::Pending => Self::Pending, - self::RefundStatus::RequiresAction => Self::ManualReview, + RefundStatus::Succeeded => Self::Success, + RefundStatus::Failed => Self::Failure, + RefundStatus::Pending => Self::Pending, + RefundStatus::RequiresAction => Self::ManualReview, } } } @@ -3382,12 +3378,12 @@ impl TryFrom { pub router_data: T, } -impl - TryFrom<( - &types::api::CurrencyUnit, - types::storage::enums::Currency, - i64, - T, - )> for ThreedsecureioRouterData +impl TryFrom<(&api::CurrencyUnit, types::storage::enums::Currency, i64, T)> + for ThreedsecureioRouterData { type Error = error_stack::Report; fn try_from( (_currency_unit, _currency, amount, item): ( - &types::api::CurrencyUnit, + &api::CurrencyUnit, types::storage::enums::Currency, i64, T, @@ -168,7 +163,7 @@ impl let creq_str = to_string(&creq) .change_context(errors::ConnectorError::ResponseDeserializationFailed) .attach_printable("error while constructing creq_str")?; - let creq_base64 = base64::Engine::encode(&BASE64_ENGINE, creq_str) + let creq_base64 = Engine::encode(&BASE64_ENGINE, creq_str) .trim_end_matches('=') .to_owned(); Ok( @@ -270,7 +265,7 @@ impl TryFrom<&ThreedsecureioRouterData<&types::authentication::ConnectorAuthenti .map(|currency| currency.to_string()) .ok_or(errors::ConnectorError::RequestEncodingFailed) .attach_printable("missing field currency")?; - let purchase_currency: Currency = iso_currency::Currency::from_code(¤cy) + let purchase_currency: Currency = Currency::from_code(¤cy) .ok_or(errors::ConnectorError::RequestEncodingFailed) .attach_printable("error while parsing Currency")?; let billing_address = request.billing_address.address.clone().ok_or( diff --git a/crates/router/src/connector/trustpay/transformers.rs b/crates/router/src/connector/trustpay/transformers.rs index c40435c01bb..5a318e5e440 100644 --- a/crates/router/src/connector/trustpay/transformers.rs +++ b/crates/router/src/connector/trustpay/transformers.rs @@ -28,19 +28,12 @@ pub struct TrustpayRouterData { pub router_data: T, } -impl - TryFrom<( - &types::api::CurrencyUnit, - types::storage::enums::Currency, - i64, - T, - )> for TrustpayRouterData -{ +impl TryFrom<(&types::api::CurrencyUnit, enums::Currency, i64, T)> for TrustpayRouterData { type Error = error_stack::Report; fn try_from( (currency_unit, currency, amount, item): ( &types::api::CurrencyUnit, - types::storage::enums::Currency, + enums::Currency, i64, T, ), @@ -1179,7 +1172,7 @@ impl let create_intent_response = item.response.init_result_data.to_owned(); let secrets = item.response.secrets.to_owned(); let instance_id = item.response.instance_id.to_owned(); - let pmt = utils::PaymentsPreProcessingData::get_payment_method_type(&item.data.request)?; + let pmt = PaymentsPreProcessingData::get_payment_method_type(&item.data.request)?; match (pmt, create_intent_response) { ( diff --git a/crates/router/src/connector/tsys/transformers.rs b/crates/router/src/connector/tsys/transformers.rs index 6ffe1f1ed7f..dfc7e61c000 100644 --- a/crates/router/src/connector/tsys/transformers.rs +++ b/crates/router/src/connector/tsys/transformers.rs @@ -5,10 +5,7 @@ use serde::{Deserialize, Serialize}; use crate::{ connector::utils::{self, CardData, PaymentsAuthorizeRequestData, RefundsRequestData}, core::errors, - types::{ - self, api, domain, - storage::{self, enums}, - }, + types::{self, api, domain, storage::enums}, }; #[derive(Debug, Serialize)] @@ -25,7 +22,7 @@ pub struct TsysPaymentAuthSaleRequest { transaction_key: Secret, card_data_source: String, transaction_amount: String, - currency_code: storage::enums::Currency, + currency_code: enums::Currency, card_number: cards::CardNumber, expiration_date: Secret, cvv2: Secret, diff --git a/crates/router/src/connector/utils.rs b/crates/router/src/connector/utils.rs index bb52d4885fb..5a5b1242611 100644 --- a/crates/router/src/connector/utils.rs +++ b/crates/router/src/connector/utils.rs @@ -363,10 +363,7 @@ impl RouterData for types::RouterData bool { - matches!( - self.auth_type, - diesel_models::enums::AuthenticationType::ThreeDs - ) + matches!(self.auth_type, enums::AuthenticationType::ThreeDs) } fn get_shipping_address(&self) -> Result<&api::AddressDetails, Error> { @@ -430,8 +427,8 @@ impl RouterData for types::RouterData Result; - fn get_payment_method_type(&self) -> Result; - fn get_currency(&self) -> Result; + fn get_payment_method_type(&self) -> Result; + fn get_currency(&self) -> Result; fn get_amount(&self) -> Result; fn is_auto_capture(&self) -> Result; fn get_order_details(&self) -> Result, Error>; @@ -445,12 +442,12 @@ impl PaymentsPreProcessingData for types::PaymentsPreProcessingData { fn get_email(&self) -> Result { self.email.clone().ok_or_else(missing_field_err("email")) } - fn get_payment_method_type(&self) -> Result { + fn get_payment_method_type(&self) -> Result { self.payment_method_type .to_owned() .ok_or_else(missing_field_err("payment_method_type")) } - fn get_currency(&self) -> Result { + fn get_currency(&self) -> Result { self.currency.ok_or_else(missing_field_err("currency")) } fn get_amount(&self) -> Result { @@ -458,8 +455,8 @@ impl PaymentsPreProcessingData for types::PaymentsPreProcessingData { } fn is_auto_capture(&self) -> Result { match self.capture_method { - Some(diesel_models::enums::CaptureMethod::Automatic) | None => Ok(true), - Some(diesel_models::enums::CaptureMethod::Manual) => Ok(false), + Some(enums::CaptureMethod::Automatic) | None => Ok(true), + Some(enums::CaptureMethod::Manual) => Ok(false), Some(_) => Err(errors::ConnectorError::CaptureMethodNotSupported.into()), } } @@ -551,7 +548,7 @@ pub trait PaymentsAuthorizeRequestData { fn get_router_return_url(&self) -> Result; fn is_wallet(&self) -> bool; fn is_card(&self) -> bool; - fn get_payment_method_type(&self) -> Result; + fn get_payment_method_type(&self) -> Result; fn get_connector_mandate_id(&self) -> Result; fn get_complete_authorize_url(&self) -> Result; fn get_ip_address_as_optional(&self) -> Option>; @@ -578,8 +575,8 @@ impl PaymentMethodTokenizationRequestData for types::PaymentMethodTokenizationDa impl PaymentsAuthorizeRequestData for types::PaymentsAuthorizeData { fn is_auto_capture(&self) -> Result { match self.capture_method { - Some(diesel_models::enums::CaptureMethod::Automatic) | None => Ok(true), - Some(diesel_models::enums::CaptureMethod::Manual) => Ok(false), + Some(enums::CaptureMethod::Automatic) | None => Ok(true), + Some(enums::CaptureMethod::Manual) => Ok(false), Some(_) => Err(errors::ConnectorError::CaptureMethodNotSupported.into()), } } @@ -619,9 +616,9 @@ impl PaymentsAuthorizeRequestData for types::PaymentsAuthorizeData { self.mandate_id .as_ref() .and_then(|mandate_ids| match &mandate_ids.mandate_reference_id { - Some(api_models::payments::MandateReferenceId::ConnectorMandateId( - connector_mandate_ids, - )) => connector_mandate_ids.connector_mandate_id.clone(), + Some(payments::MandateReferenceId::ConnectorMandateId(connector_mandate_ids)) => { + connector_mandate_ids.connector_mandate_id.clone() + } _ => None, }) } @@ -653,7 +650,7 @@ impl PaymentsAuthorizeRequestData for types::PaymentsAuthorizeData { matches!(self.payment_method_data, domain::PaymentMethodData::Card(_)) } - fn get_payment_method_type(&self) -> Result { + fn get_payment_method_type(&self) -> Result { self.payment_method_type .to_owned() .ok_or_else(missing_field_err("payment_method_type")) @@ -797,8 +794,8 @@ pub trait PaymentsCompleteAuthorizeRequestData { impl PaymentsCompleteAuthorizeRequestData for types::CompleteAuthorizeData { fn is_auto_capture(&self) -> Result { match self.capture_method { - Some(diesel_models::enums::CaptureMethod::Automatic) | None => Ok(true), - Some(diesel_models::enums::CaptureMethod::Manual) => Ok(false), + Some(enums::CaptureMethod::Automatic) | None => Ok(true), + Some(enums::CaptureMethod::Manual) => Ok(false), Some(_) => Err(errors::ConnectorError::CaptureMethodNotSupported.into()), } } @@ -831,8 +828,8 @@ pub trait PaymentsSyncRequestData { impl PaymentsSyncRequestData for types::PaymentsSyncData { fn is_auto_capture(&self) -> Result { match self.capture_method { - Some(diesel_models::enums::CaptureMethod::Automatic) | None => Ok(true), - Some(diesel_models::enums::CaptureMethod::Manual) => Ok(false), + Some(enums::CaptureMethod::Automatic) | None => Ok(true), + Some(enums::CaptureMethod::Manual) => Ok(false), Some(_) => Err(errors::ConnectorError::CaptureMethodNotSupported.into()), } } @@ -910,7 +907,7 @@ impl CustomerDetails for types::CustomerDetails { pub trait PaymentsCancelRequestData { fn get_amount(&self) -> Result; - fn get_currency(&self) -> Result; + fn get_currency(&self) -> Result; fn get_cancellation_reason(&self) -> Result; fn get_browser_info(&self) -> Result; } @@ -919,7 +916,7 @@ impl PaymentsCancelRequestData for PaymentsCancelData { fn get_amount(&self) -> Result { self.amount.ok_or_else(missing_field_err("amount")) } - fn get_currency(&self) -> Result { + fn get_currency(&self) -> Result { self.currency.ok_or_else(missing_field_err("currency")) } fn get_cancellation_reason(&self) -> Result { @@ -1540,7 +1537,7 @@ impl MandateData for payments::MandateAmountData { pub trait RecurringMandateData { fn get_original_payment_amount(&self) -> Result; - fn get_original_payment_currency(&self) -> Result; + fn get_original_payment_currency(&self) -> Result; } impl RecurringMandateData for RecurringMandatePaymentData { @@ -1548,7 +1545,7 @@ impl RecurringMandateData for RecurringMandatePaymentData { self.original_payment_authorized_amount .ok_or_else(missing_field_err("original_payment_authorized_amount")) } - fn get_original_payment_currency(&self) -> Result { + fn get_original_payment_currency(&self) -> Result { self.original_payment_authorized_currency .ok_or_else(missing_field_err("original_payment_authorized_currency")) } @@ -1558,7 +1555,7 @@ pub trait MandateReferenceData { fn get_connector_mandate_id(&self) -> Result; } -impl MandateReferenceData for api_models::payments::ConnectorMandateReferenceId { +impl MandateReferenceData for payments::ConnectorMandateReferenceId { fn get_connector_mandate_id(&self) -> Result { self.connector_mandate_id .clone() @@ -1645,7 +1642,7 @@ pub fn base64_decode(data: String) -> Result, Error> { pub fn to_currency_base_unit_from_optional_amount( amount: Option, - currency: diesel_models::enums::Currency, + currency: enums::Currency, ) -> Result> { match amount { Some(a) => to_currency_base_unit(a, currency), @@ -1657,25 +1654,25 @@ pub fn to_currency_base_unit_from_optional_amount( } pub fn get_amount_as_string( - currency_unit: &types::api::CurrencyUnit, + currency_unit: &api::CurrencyUnit, amount: i64, - currency: diesel_models::enums::Currency, + currency: enums::Currency, ) -> Result> { let amount = match currency_unit { - types::api::CurrencyUnit::Minor => amount.to_string(), - types::api::CurrencyUnit::Base => to_currency_base_unit(amount, currency)?, + api::CurrencyUnit::Minor => amount.to_string(), + api::CurrencyUnit::Base => to_currency_base_unit(amount, currency)?, }; Ok(amount) } pub fn get_amount_as_f64( - currency_unit: &types::api::CurrencyUnit, + currency_unit: &api::CurrencyUnit, amount: i64, - currency: diesel_models::enums::Currency, + currency: enums::Currency, ) -> Result> { let amount = match currency_unit { - types::api::CurrencyUnit::Base => to_currency_base_unit_asf64(amount, currency)?, - types::api::CurrencyUnit::Minor => u32::try_from(amount) + api::CurrencyUnit::Base => to_currency_base_unit_asf64(amount, currency)?, + api::CurrencyUnit::Minor => u32::try_from(amount) .change_context(errors::ConnectorError::ParsingFailed)? .into(), }; @@ -1684,7 +1681,7 @@ pub fn get_amount_as_f64( pub fn to_currency_base_unit( amount: i64, - currency: diesel_models::enums::Currency, + currency: enums::Currency, ) -> Result> { currency .to_currency_base_unit(amount) @@ -1693,7 +1690,7 @@ pub fn to_currency_base_unit( pub fn to_currency_lower_unit( amount: String, - currency: diesel_models::enums::Currency, + currency: enums::Currency, ) -> Result> { currency .to_currency_lower_unit(amount) @@ -1721,7 +1718,7 @@ pub fn construct_not_supported_error_report( pub fn to_currency_base_unit_with_zero_decimal_check( amount: i64, - currency: diesel_models::enums::Currency, + currency: enums::Currency, ) -> Result> { currency .to_currency_base_unit_with_zero_decimal_check(amount) @@ -1730,7 +1727,7 @@ pub fn to_currency_base_unit_with_zero_decimal_check( pub fn to_currency_base_unit_asf64( amount: i64, - currency: diesel_models::enums::Currency, + currency: enums::Currency, ) -> Result> { currency .to_currency_base_unit_asf64(amount) diff --git a/crates/router/src/connector/volt/transformers.rs b/crates/router/src/connector/volt/transformers.rs index d3913296c09..814eefaf5ff 100644 --- a/crates/router/src/connector/volt/transformers.rs +++ b/crates/router/src/connector/volt/transformers.rs @@ -18,18 +18,13 @@ pub struct VoltRouterData { pub router_data: T, } -impl - TryFrom<( - &types::api::CurrencyUnit, - types::storage::enums::Currency, - i64, - T, - )> for VoltRouterData +impl TryFrom<(&api::CurrencyUnit, types::storage::enums::Currency, i64, T)> + for VoltRouterData { type Error = error_stack::Report; fn try_from( (_currency_unit, _currency, amount, item): ( - &types::api::CurrencyUnit, + &api::CurrencyUnit, types::storage::enums::Currency, i64, T, diff --git a/crates/router/src/connector/worldline/transformers.rs b/crates/router/src/connector/worldline/transformers.rs index 3dba5ea24e7..795133f602f 100644 --- a/crates/router/src/connector/worldline/transformers.rs +++ b/crates/router/src/connector/worldline/transformers.rs @@ -190,22 +190,10 @@ pub struct WorldlineRouterData { amount: i64, router_data: T, } -impl - TryFrom<( - &types::api::CurrencyUnit, - types::storage::enums::Currency, - i64, - T, - )> for WorldlineRouterData -{ +impl TryFrom<(&api::CurrencyUnit, enums::Currency, i64, T)> for WorldlineRouterData { type Error = error_stack::Report; fn try_from( - (_currency_unit, _currency, amount, item): ( - &types::api::CurrencyUnit, - types::storage::enums::Currency, - i64, - T, - ), + (_currency_unit, _currency, amount, item): (&api::CurrencyUnit, enums::Currency, i64, T), ) -> Result { Ok(Self { amount, @@ -218,7 +206,7 @@ impl TryFrom< &WorldlineRouterData< &types::RouterData< - types::api::payments::Authorize, + api::payments::Authorize, PaymentsAuthorizeData, PaymentsResponseData, >, @@ -230,7 +218,7 @@ impl fn try_from( item: &WorldlineRouterData< &types::RouterData< - types::api::payments::Authorize, + api::payments::Authorize, PaymentsAuthorizeData, PaymentsResponseData, >, @@ -593,7 +581,7 @@ impl TryFrom TryFrom> })?, }, description: item.response.description, - response: Ok(types::PaymentsResponseData::TransactionResponse { + response: Ok(PaymentsResponseData::TransactionResponse { resource_id: types::ResponseId::try_from(item.response.links)?, redirection_data: None, mandate_reference: None, diff --git a/crates/router/src/connector/zen/transformers.rs b/crates/router/src/connector/zen/transformers.rs index ead716af721..67538b819d5 100644 --- a/crates/router/src/connector/zen/transformers.rs +++ b/crates/router/src/connector/zen/transformers.rs @@ -23,22 +23,10 @@ pub struct ZenRouterData { pub router_data: T, } -impl - TryFrom<( - &types::api::CurrencyUnit, - types::storage::enums::Currency, - i64, - T, - )> for ZenRouterData -{ +impl TryFrom<(&api::CurrencyUnit, enums::Currency, i64, T)> for ZenRouterData { type Error = error_stack::Report; fn try_from( - (currency_unit, currency, amount, item): ( - &types::api::CurrencyUnit, - types::storage::enums::Currency, - i64, - T, - ), + (currency_unit, currency, amount, item): (&api::CurrencyUnit, enums::Currency, i64, T), ) -> Result { let amount = utils::get_amount_as_string(currency_unit, amount, currency)?; Ok(Self { diff --git a/crates/router/src/connector/zsl/transformers.rs b/crates/router/src/connector/zsl/transformers.rs index 3ffd5b2ccef..58ff6ab2fab 100644 --- a/crates/router/src/connector/zsl/transformers.rs +++ b/crates/router/src/connector/zsl/transformers.rs @@ -27,19 +27,12 @@ pub struct ZslRouterData { pub router_data: T, } -impl - TryFrom<( - &types::api::CurrencyUnit, - types::storage::enums::Currency, - i64, - T, - )> for ZslRouterData -{ +impl TryFrom<(&types::api::CurrencyUnit, enums::Currency, i64, T)> for ZslRouterData { type Error = error_stack::Report; fn try_from( (currency_unit, currency, txn_amount, item): ( &types::api::CurrencyUnit, - types::storage::enums::Currency, + enums::Currency, i64, T, ), diff --git a/crates/router/src/core/admin.rs b/crates/router/src/core/admin.rs index 0b8d52332d9..ee06097a599 100644 --- a/crates/router/src/core/admin.rs +++ b/crates/router/src/core/admin.rs @@ -268,7 +268,7 @@ pub async fn create_merchant_account( .await .to_duplicate_response(errors::ApiErrorResponse::DuplicateMerchantAccount)?; - db.insert_config(diesel_models::configs::ConfigNew { + db.insert_config(configs::ConfigNew { key: format!("{}_requires_cvv", merchant_account.merchant_id), config: "true".to_string(), }) @@ -545,7 +545,7 @@ pub async fn merchant_account_update( let updated_merchant_account = storage::MerchantAccountUpdate::Update { merchant_name: req .merchant_name - .map(masking::Secret::new) + .map(Secret::new) .async_lift(|inner| domain_types::encrypt_optional(inner, key)) .await .change_context(errors::ApiErrorResponse::InternalServerError) @@ -554,11 +554,11 @@ pub async fn merchant_account_update( merchant_details: req .merchant_details .as_ref() - .map(utils::Encode::encode_to_value) + .map(Encode::encode_to_value) .transpose() .change_context(errors::ApiErrorResponse::InternalServerError) .attach_printable("Unable to convert merchant_details to a value")? - .map(masking::Secret::new) + .map(Secret::new) .async_lift(|inner| domain_types::encrypt_optional(inner, key)) .await .change_context(errors::ApiErrorResponse::InternalServerError) @@ -569,7 +569,7 @@ pub async fn merchant_account_update( webhook_details: req .webhook_details .as_ref() - .map(utils::Encode::encode_to_value) + .map(Encode::encode_to_value) .transpose() .change_context(errors::ApiErrorResponse::InternalServerError)?, @@ -941,8 +941,8 @@ pub async fn create_payment_connector( business_country: req.business_country, business_label: req.business_label.clone(), business_sub_label: req.business_sub_label.clone(), - created_at: common_utils::date_time::now(), - modified_at: common_utils::date_time::now(), + created_at: date_time::now(), + modified_at: date_time::now(), id: None, connector_webhook_details: match req.connector_webhook_details { Some(connector_webhook_details) => { @@ -951,7 +951,7 @@ pub async fn create_payment_connector( .change_context(errors::ApiErrorResponse::InternalServerError) .attach_printable(format!("Failed to serialize api_models::admin::MerchantConnectorWebhookDetails for Merchant: {}", merchant_id)) .map(Some)? - .map(masking::Secret::new) + .map(Secret::new) } None => None, }, @@ -1278,7 +1278,7 @@ pub async fn update_payment_connector( .encode_to_value() .change_context(errors::ApiErrorResponse::InternalServerError) .map(Some)? - .map(masking::Secret::new), + .map(Secret::new), None => None, }, applepay_verified_domains: None, @@ -1458,7 +1458,7 @@ pub fn get_frm_config_as_secret( config .encode_to_value() .change_context(errors::ApiErrorResponse::ConfigNotFound) - .map(masking::Secret::new) + .map(Secret::new) }) .collect::, _>>() .ok()?; diff --git a/crates/router/src/core/api_keys.rs b/crates/router/src/core/api_keys.rs index f75ed011688..7f2b343b968 100644 --- a/crates/router/src/core/api_keys.rs +++ b/crates/router/src/core/api_keys.rs @@ -184,7 +184,7 @@ pub async fn add_api_key_expiry_task( api_key: &ApiKey, expiry_reminder_days: Vec, ) -> Result<(), errors::ProcessTrackerError> { - let current_time = common_utils::date_time::now(); + let current_time = date_time::now(); let schedule_time = expiry_reminder_days .first() @@ -341,7 +341,7 @@ pub async fn update_api_key_expiry_task( api_key: &ApiKey, expiry_reminder_days: Vec, ) -> Result<(), errors::ProcessTrackerError> { - let current_time = common_utils::date_time::now(); + let current_time = date_time::now(); let schedule_time = expiry_reminder_days .first() diff --git a/crates/router/src/core/authentication.rs b/crates/router/src/core/authentication.rs index 36ee3c20416..e2f605304a7 100644 --- a/crates/router/src/core/authentication.rs +++ b/crates/router/src/core/authentication.rs @@ -9,7 +9,7 @@ use common_utils::errors::CustomResult; use error_stack::ResultExt; use masking::ExposeInterface; -use super::errors::{self, StorageErrorExt}; +use super::errors::StorageErrorExt; use crate::{ core::{errors::ApiErrorResponse, payments as payments_core}, routes::AppState, @@ -23,10 +23,10 @@ pub async fn perform_authentication( authentication_connector: String, payment_method_data: payments::PaymentMethodData, payment_method: common_enums::PaymentMethod, - billing_address: api_models::payments::Address, - shipping_address: Option, + billing_address: payments::Address, + shipping_address: Option, browser_details: Option, - business_profile: core_types::storage::BusinessProfile, + business_profile: storage::BusinessProfile, merchant_connector_account: payments_core::helpers::MerchantConnectorAccountType, amount: Option, currency: Option, @@ -35,10 +35,10 @@ pub async fn perform_authentication( authentication_data: storage::Authentication, return_url: Option, sdk_information: Option, - threeds_method_comp_ind: api_models::payments::ThreeDsCompletionIndicator, + threeds_method_comp_ind: payments::ThreeDsCompletionIndicator, email: Option, webhook_url: String, -) -> CustomResult { +) -> CustomResult { let router_data = transformers::construct_authentication_router_data( authentication_connector.clone(), payment_method_data, @@ -72,13 +72,13 @@ pub async fn perform_authentication( status_code: err.status_code, reason: err.reason, })?; - core_types::api::authentication::AuthenticationResponse::try_from(authentication) + api::authentication::AuthenticationResponse::try_from(authentication) } pub async fn perform_post_authentication( state: &AppState, key_store: &domain::MerchantKeyStore, - business_profile: core_types::storage::BusinessProfile, + business_profile: storage::BusinessProfile, authentication_id: String, ) -> CustomResult { let (authentication_connector, three_ds_connector_account) = @@ -96,7 +96,7 @@ pub async fn perform_post_authentication( authentication_id.clone(), ) .await - .to_not_found_response(errors::ApiErrorResponse::InternalServerError) + .to_not_found_response(ApiErrorResponse::InternalServerError) .attach_printable_lazy(|| format!("Error while fetching authentication record with authentication_id {authentication_id}"))?; if !authentication.authentication_status.is_terminal_status() && is_pull_mechanism_enabled { let router_data = transformers::construct_post_authentication_router_data( @@ -119,7 +119,7 @@ pub async fn perform_pre_authentication( key_store: &domain::MerchantKeyStore, card_number: cards::CardNumber, token: String, - business_profile: &core_types::storage::BusinessProfile, + business_profile: &storage::BusinessProfile, acquirer_details: Option, payment_id: Option, ) -> CustomResult { @@ -135,7 +135,7 @@ pub async fn perform_pre_authentication( payment_id, three_ds_connector_account .get_mca_id() - .ok_or(errors::ApiErrorResponse::InternalServerError) + .ok_or(ApiErrorResponse::InternalServerError) .attach_printable("Error while finding mca_id from merchant_connector_account")?, ) .await?; diff --git a/crates/router/src/core/authentication/transformers.rs b/crates/router/src/core/authentication/transformers.rs index d4cad2fbedb..9e00dfe70c9 100644 --- a/crates/router/src/core/authentication/transformers.rs +++ b/crates/router/src/core/authentication/transformers.rs @@ -29,8 +29,8 @@ pub fn construct_authentication_router_data( authentication_connector: String, payment_method_data: payments::PaymentMethodData, payment_method: PaymentMethod, - billing_address: api_models::payments::Address, - shipping_address: Option, + billing_address: payments::Address, + shipping_address: Option, browser_details: Option, amount: Option, currency: Option, @@ -40,8 +40,8 @@ pub fn construct_authentication_router_data( merchant_connector_account: payments_helpers::MerchantConnectorAccountType, authentication_data: storage::Authentication, return_url: Option, - sdk_information: Option, - threeds_method_comp_ind: api_models::payments::ThreeDsCompletionIndicator, + sdk_information: Option, + threeds_method_comp_ind: payments::ThreeDsCompletionIndicator, email: Option, webhook_url: String, ) -> RouterResult { diff --git a/crates/router/src/core/customers.rs b/crates/router/src/core/customers.rs index af97c500f3e..3b10b408f9e 100644 --- a/crates/router/src/core/customers.rs +++ b/crates/router/src/core/customers.rs @@ -37,7 +37,7 @@ pub async fn create_customer( let db = state.store.as_ref(); let customer_id = &customer_data.customer_id; let merchant_id = &merchant_account.merchant_id; - customer_data.merchant_id = merchant_id.to_owned(); + merchant_id.clone_into(&mut customer_data.merchant_id); // We first need to validate whether the customer with the given customer id already exists // this may seem like a redundant db call, as the insert_customer will anyway return this error diff --git a/crates/router/src/core/errors.rs b/crates/router/src/core/errors.rs index d80d86460b2..a70a97e7bf0 100644 --- a/crates/router/src/core/errors.rs +++ b/crates/router/src/core/errors.rs @@ -105,7 +105,7 @@ impl From for EncryptionError { pub fn http_not_implemented() -> actix_web::HttpResponse { ApiErrorResponse::NotImplemented { - message: api_error_response::NotImplementedMessage::Default, + message: NotImplementedMessage::Default, } .error_response() } diff --git a/crates/router/src/core/files.rs b/crates/router/src/core/files.rs index e2ce5b42c3f..5dedcb14863 100644 --- a/crates/router/src/core/files.rs +++ b/crates/router/src/core/files.rs @@ -7,7 +7,7 @@ use super::errors::{self, RouterResponse}; use crate::{ consts, routes::AppState, - services::{self, ApplicationResponse}, + services::ApplicationResponse, types::{api, domain}, }; @@ -72,9 +72,9 @@ pub async fn files_create_core( .attach_printable_lazy(|| { format!("Unable to update file_metadata with file_id: {}", file_id) })?; - Ok(services::api::ApplicationResponse::Json( - files::CreateFileResponse { file_id }, - )) + Ok(ApplicationResponse::Json(files::CreateFileResponse { + file_id, + })) } pub async fn files_delete_core( diff --git a/crates/router/src/core/fraud_check.rs b/crates/router/src/core/fraud_check.rs index 5e03033977e..e26d5d0b461 100644 --- a/crates/router/src/core/fraud_check.rs +++ b/crates/router/src/core/fraud_check.rs @@ -81,10 +81,10 @@ where ) .await?; - frm_data.payment_attempt.connector_transaction_id = payment_data + frm_data .payment_attempt .connector_transaction_id - .clone(); + .clone_from(&payment_data.payment_attempt.connector_transaction_id); let mut router_data = frm_data .construct_router_data( @@ -676,7 +676,7 @@ where if matches!(fraud_capture_method, Some(Some(CaptureMethod::Manual))) && matches!( payment_data.payment_attempt.status, - api_models::enums::AttemptStatus::Unresolved + AttemptStatus::Unresolved ) { if let Some(info) = frm_info { diff --git a/crates/router/src/core/mandate.rs b/crates/router/src/core/mandate.rs index 939367bd4db..7a1e294f9dc 100644 --- a/crates/router/src/core/mandate.rs +++ b/crates/router/src/core/mandate.rs @@ -512,11 +512,11 @@ impl ForeignFrom> pub trait MandateBehaviour { fn get_amount(&self) -> i64; fn get_setup_future_usage(&self) -> Option; - fn get_mandate_id(&self) -> Option<&api_models::payments::MandateIds>; - fn set_mandate_id(&mut self, new_mandate_id: Option); + fn get_mandate_id(&self) -> Option<&payments::MandateIds>; + fn set_mandate_id(&mut self, new_mandate_id: Option); fn get_payment_method_data(&self) -> domain::payments::PaymentMethodData; fn get_setup_mandate_details( &self, ) -> Option<&hyperswitch_domain_models::mandates::MandateData>; - fn get_customer_acceptance(&self) -> Option; + fn get_customer_acceptance(&self) -> Option; } diff --git a/crates/router/src/core/payment_link.rs b/crates/router/src/core/payment_link.rs index 3f9f5f017a7..11c53a77b1e 100644 --- a/crates/router/src/core/payment_link.rs +++ b/crates/router/src/core/payment_link.rs @@ -350,7 +350,9 @@ fn validate_order_details( order_details_amount_string.product_img_link = Some(DEFAULT_PRODUCT_IMG.to_string()) } else { - order_details_amount_string.product_img_link = order.product_img_link.clone() + order_details_amount_string + .product_img_link + .clone_from(&order.product_img_link) }; order_details_amount_string.amount = currency diff --git a/crates/router/src/core/payment_methods/cards.rs b/crates/router/src/core/payment_methods/cards.rs index d3f6aeea24b..9ae327269cf 100644 --- a/crates/router/src/core/payment_methods/cards.rs +++ b/crates/router/src/core/payment_methods/cards.rs @@ -115,10 +115,10 @@ pub async fn create_payment_method( payment_method_type: req.payment_method_type, payment_method_issuer: req.payment_method_issuer.clone(), scheme: req.card_network.clone(), - metadata: pm_metadata.map(masking::Secret::new), + metadata: pm_metadata.map(Secret::new), payment_method_data, connector_mandate_details, - customer_acceptance: customer_acceptance.map(masking::Secret::new), + customer_acceptance: customer_acceptance.map(Secret::new), client_secret: Some(client_secret), status: status.unwrap_or(enums::PaymentMethodStatus::Active), network_transaction_id: network_transaction_id.to_owned(), @@ -201,7 +201,7 @@ pub async fn get_or_insert_payment_method( .await; match &existing_pm_by_locker_id { - Ok(pm) => payment_method_id = pm.payment_method_id.clone(), + Ok(pm) => payment_method_id.clone_from(&pm.payment_method_id), Err(_) => payment_method_id = generate_id(consts::ID_LENGTH, "pm"), }; existing_pm_by_locker_id @@ -212,7 +212,7 @@ pub async fn get_or_insert_payment_method( existing_pm_by_pmid } }; - resp.payment_method_id = payment_method_id.to_owned(); + payment_method_id.clone_into(&mut resp.payment_method_id); match payment_method { Ok(pm) => Ok(pm), @@ -405,7 +405,7 @@ pub async fn add_payment_method_data( return Ok(services::ApplicationResponse::Json(pm_resp)); } else { let locker_id = pm_resp.payment_method_id.clone(); - pm_resp.payment_method_id = pm_id.clone(); + pm_resp.payment_method_id.clone_from(&pm_id); pm_resp.client_secret = Some(client_secret.clone()); let card_isin = card.card_number.clone().get_card_isin(); @@ -898,7 +898,9 @@ pub async fn update_customer_payment_method( payment_method_data: pm_data_encrypted, }; - add_card_resp.payment_method_id = pm.payment_method_id.clone(); + add_card_resp + .payment_method_id + .clone_from(&pm.payment_method_id); db.update_payment_method(pm, pm_update, merchant_account.storage_scheme) .await @@ -1191,7 +1193,7 @@ pub async fn add_card_hs( card_exp_year: card.card_exp_year.to_owned(), card_brand: card.card_network.as_ref().map(ToString::to_string), card_isin: None, - nick_name: card.nick_name.as_ref().map(masking::Secret::peek).cloned(), + nick_name: card.nick_name.as_ref().map(Secret::peek).cloned(), }, }); @@ -2912,7 +2914,7 @@ fn filter_pm_based_on_supported_payments_for_mandate( } fn filter_pm_based_on_config<'a>( - config: &'a crate::configs::settings::ConnectorFilters, + config: &'a settings::ConnectorFilters, connector: &'a str, payment_method_type: &'a api_enums::PaymentMethodType, payment_attempt: Option<&storage::PaymentAttempt>, @@ -3615,7 +3617,7 @@ pub async fn get_card_details_with_locker_fallback( Ok(if let Some(mut crd) = card_decrypted { if crd.saved_to_locker { - crd.scheme = pm.scheme.clone(); + crd.scheme.clone_from(&pm.scheme); Some(crd) } else { None @@ -3645,7 +3647,7 @@ pub async fn get_card_details_without_locker_fallback( }); Ok(if let Some(mut crd) = card_decrypted { - crd.scheme = pm.scheme.clone(); + crd.scheme.clone_from(&pm.scheme); crd } else { get_card_details_from_locker(state, pm).await? @@ -4005,10 +4007,7 @@ impl TempLockerCardSupport { metrics::TASKS_ADDED_COUNT.add( &metrics::CONTEXT, 1, - &[metrics::request::add_attributes( - "flow", - "DeleteTokenizeData", - )], + &[request::add_attributes("flow", "DeleteTokenizeData")], ); Ok(card) } @@ -4171,7 +4170,7 @@ pub async fn create_encrypted_payment_method_data( let pm_data_encrypted: Option = pm_data .as_ref() - .map(utils::Encode::encode_to_value) + .map(Encode::encode_to_value) .transpose() .change_context(errors::StorageError::SerializationFailed) .attach_printable("Unable to convert payment method data to a value") @@ -4179,7 +4178,7 @@ pub async fn create_encrypted_payment_method_data( logger::error!(err=?err); None }) - .map(masking::Secret::<_, masking::WithType>::new) + .map(Secret::<_, masking::WithType>::new) .async_lift(|inner| encrypt_optional(inner, key)) .await .change_context(errors::StorageError::EncryptionError) diff --git a/crates/router/src/core/payment_methods/transformers.rs b/crates/router/src/core/payment_methods/transformers.rs index 9c53642f4d7..009e9e54467 100644 --- a/crates/router/src/core/payment_methods/transformers.rs +++ b/crates/router/src/core/payment_methods/transformers.rs @@ -563,7 +563,7 @@ pub fn get_card_detail( card_token: None, card_fingerprint: None, card_holder_name: response.name_on_card, - nick_name: response.nick_name.map(masking::Secret::new), + nick_name: response.nick_name.map(Secret::new), card_isin: None, card_issuer: None, card_network: None, diff --git a/crates/router/src/core/payments.rs b/crates/router/src/core/payments.rs index 07bceefb0f9..0e10c2a5417 100644 --- a/crates/router/src/core/payments.rs +++ b/crates/router/src/core/payments.rs @@ -12,8 +12,10 @@ pub mod transformers; pub mod types; #[cfg(feature = "olap")] -use std::collections::{HashMap, HashSet}; -use std::{fmt::Debug, marker::PhantomData, ops::Deref, time::Instant, vec::IntoIter}; +use std::collections::HashMap; +use std::{ + collections::HashSet, fmt::Debug, marker::PhantomData, ops::Deref, time::Instant, vec::IntoIter, +}; #[cfg(feature = "olap")] use api_models::admin::MerchantConnectorInfo; @@ -119,7 +121,7 @@ where router_types::RouterData: Feature, // To construct connector flow specific api - dyn router_types::api::Connector: + dyn api::Connector: services::api::ConnectorIntegration, // To perform router related operation for PaymentResponse @@ -265,7 +267,7 @@ where _ => (), }; payment_data = match connector_details { - api::ConnectorCallType::PreDetermined(connector) => { + ConnectorCallType::PreDetermined(connector) => { let schedule_time = if should_add_task_to_process_tracker { payment_sync::get_sync_process_schedule_time( &*state.store, @@ -330,7 +332,7 @@ where .await? } - api::ConnectorCallType::Retryable(connectors) => { + ConnectorCallType::Retryable(connectors) => { let mut connectors = connectors.into_iter(); let connector_data = get_connector_data(&mut connectors)?; @@ -432,7 +434,7 @@ where .await? } - api::ConnectorCallType::SessionMultiple(connectors) => { + ConnectorCallType::SessionMultiple(connectors) => { let session_surcharge_details = call_surcharge_decision_management_for_session_flow( state, @@ -741,7 +743,7 @@ pub async fn payments_core( req: Req, auth_flow: services::AuthFlow, call_connector_action: CallConnectorAction, - eligible_connectors: Option>, + eligible_connectors: Option>, header_payload: HeaderPayload, ) -> RouterResponse where @@ -756,7 +758,7 @@ where Ctx: PaymentMethodRetrieve, // To construct connector flow specific api - dyn router_types::api::Connector: + dyn api::Connector: services::api::ConnectorIntegration, // To perform router related operation for PaymentResponse @@ -994,7 +996,7 @@ impl PaymentRedirectFlow for PaymentRedirectCom // If the status is requires customer action, then send the startpay url again // The redirection data must have been provided and updated by the connector let redirection_response = match payments_response.status { - api_models::enums::IntentStatus::RequiresCustomerAction => { + enums::IntentStatus::RequiresCustomerAction => { let startpay_url = payments_response .next_action .clone() @@ -1021,9 +1023,9 @@ impl PaymentRedirectFlow for PaymentRedirectCom }) } // If the status is terminal status, then redirect to merchant return url to provide status - api_models::enums::IntentStatus::Succeeded - | api_models::enums::IntentStatus::Failed - | api_models::enums::IntentStatus::Cancelled | api_models::enums::IntentStatus::RequiresCapture| api_models::enums::IntentStatus::Processing=> helpers::get_handle_response_url( + enums::IntentStatus::Succeeded + | enums::IntentStatus::Failed + | enums::IntentStatus::Cancelled | enums::IntentStatus::RequiresCapture| enums::IntentStatus::Processing=> helpers::get_handle_response_url( payment_id, &payment_flow_response.business_profile, payments_response, @@ -1293,9 +1295,8 @@ impl PaymentRedirectFlow for PaymentAuthenticat }?; // When intent status is RequiresCustomerAction, Set poll_id in redis to allow the fetch status of poll through retrieve_poll_status api from client if payments_response.status == common_enums::IntentStatus::RequiresCustomerAction { - let req_poll_id = - super::utils::get_external_authentication_request_poll_id(&payment_id); - let poll_id = super::utils::get_poll_id(merchant_id.clone(), req_poll_id.clone()); + let req_poll_id = utils::get_external_authentication_request_poll_id(&payment_id); + let poll_id = utils::get_poll_id(merchant_id.clone(), req_poll_id.clone()); let redis_conn = state .store .get_redis_conn() @@ -1797,7 +1798,7 @@ where merchant_connector_account.get_mca_id(), )?; - let connector_label = super::utils::get_connector_label( + let connector_label = utils::get_connector_label( payment_data.payment_intent.business_country, payment_data.payment_intent.business_label.as_ref(), payment_data.payment_attempt.business_sub_label.as_ref(), @@ -2087,13 +2088,11 @@ fn is_apple_pay_pre_decrypt_type_connector_tokenization( } fn decide_apple_pay_flow( - payment_method_type: &Option, + payment_method_type: &Option, merchant_connector_account: Option<&helpers::MerchantConnectorAccountType>, ) -> Option { payment_method_type.and_then(|pmt| match pmt { - api_models::enums::PaymentMethodType::ApplePay => { - check_apple_pay_metadata(merchant_connector_account) - } + enums::PaymentMethodType::ApplePay => check_apple_pay_metadata(merchant_connector_account), _ => None, }) } @@ -3032,7 +3031,7 @@ pub async fn get_connector_choice( business_profile: &storage::business_profile::BusinessProfile, key_store: &domain::MerchantKeyStore, payment_data: &mut PaymentData, - eligible_connectors: Option>, + eligible_connectors: Option>, mandate_type: Option, ) -> RouterResult> where @@ -3061,7 +3060,7 @@ where connectors, ) .await?; - api::ConnectorCallType::SessionMultiple(routing_output) + ConnectorCallType::SessionMultiple(routing_output) } api::ConnectorChoice::StraightThrough(straight_through) => { @@ -3112,7 +3111,7 @@ pub async fn connector_selection( key_store: &domain::MerchantKeyStore, payment_data: &mut PaymentData, request_straight_through: Option, - eligible_connectors: Option>, + eligible_connectors: Option>, mandate_type: Option, ) -> RouterResult where @@ -3188,7 +3187,7 @@ pub async fn decide_connector( payment_data: &mut PaymentData, request_straight_through: Option, routing_data: &mut storage::RoutingData, - eligible_connectors: Option>, + eligible_connectors: Option>, mandate_type: Option, ) -> RouterResult where @@ -3209,7 +3208,7 @@ where .attach_printable("Invalid connector name received in 'routed_through'")?; routing_data.routed_through = Some(connector_name.clone()); - return Ok(api::ConnectorCallType::PreDetermined(connector_data)); + return Ok(ConnectorCallType::PreDetermined(connector_data)); } if let Some(mandate_connector_details) = payment_data.mandate_connector.as_ref() { @@ -3228,10 +3227,11 @@ where routing_data.routed_through = Some(mandate_connector_details.connector.clone()); #[cfg(feature = "connector_choice_mca_id")] { - routing_data.merchant_connector_id = - mandate_connector_details.merchant_connector_id.clone(); + routing_data + .merchant_connector_id + .clone_from(&mandate_connector_details.merchant_connector_id); } - return Ok(api::ConnectorCallType::PreDetermined(connector_data)); + return Ok(ConnectorCallType::PreDetermined(connector_data)); } if let Some((pre_routing_results, storage_pm_type)) = routing_data @@ -3259,13 +3259,15 @@ where routing_data.routed_through = Some(choice.connector.to_string()); #[cfg(feature = "connector_choice_mca_id")] { - routing_data.merchant_connector_id = choice.merchant_connector_id.clone(); + routing_data + .merchant_connector_id + .clone_from(&choice.merchant_connector_id); } #[cfg(not(feature = "connector_choice_mca_id"))] { routing_data.business_sub_label = choice.sub_label.clone(); } - return Ok(api::ConnectorCallType::PreDetermined(connector_data)); + return Ok(ConnectorCallType::PreDetermined(connector_data)); } } @@ -3540,14 +3542,16 @@ pub async fn decide_multiplex_connector_for_normal_or_recurring_payment { let first_choice = connectors @@ -3570,7 +3574,7 @@ pub async fn decide_multiplex_connector_for_normal_or_recurring_payment( key_store: &domain::MerchantKeyStore, transaction_data: TransactionData<'_, F>, routing_data: &mut storage::RoutingData, - eligible_connectors: Option>, + eligible_connectors: Option>, mandate_type: Option, ) -> RouterResult where @@ -4015,7 +4019,7 @@ pub async fn payment_external_authentication( return_url, req.sdk_information, req.threeds_method_comp_ind, - optional_customer.and_then(|customer| customer.email.map(common_utils::pii::Email::from)), + optional_customer.and_then(|customer| customer.email.map(pii::Email::from)), webhook_url, )) .await?; diff --git a/crates/router/src/core/payments/access_token.rs b/crates/router/src/core/payments/access_token.rs index 1de9cb60ca0..f7fe5b7844f 100644 --- a/crates/router/src/core/payments/access_token.rs +++ b/crates/router/src/core/payments/access_token.rs @@ -35,7 +35,7 @@ pub fn update_router_data_with_access_token_result( if should_update_router_data { match add_access_token_result.access_token_result.as_ref() { Ok(access_token) => { - router_data.access_token = access_token.clone(); + router_data.access_token.clone_from(access_token); true } Err(connector_error) => { diff --git a/crates/router/src/core/payments/flows/authorize_flow.rs b/crates/router/src/core/payments/flows/authorize_flow.rs index 6b64dc04044..09b96284544 100644 --- a/crates/router/src/core/payments/flows/authorize_flow.rs +++ b/crates/router/src/core/payments/flows/authorize_flow.rs @@ -266,7 +266,7 @@ pub async fn authorize_preprocessing_steps( Err(types::ErrorResponse::default()); let preprocessing_router_data = - payments::helpers::router_data_type_conversion::<_, api::PreProcessing, _, _, _, _>( + helpers::router_data_type_conversion::<_, api::PreProcessing, _, _, _, _>( router_data.clone(), preprocessing_request_data, preprocessing_response_data, @@ -303,12 +303,11 @@ pub async fn authorize_preprocessing_steps( ], ); - let authorize_router_data = - payments::helpers::router_data_type_conversion::<_, F, _, _, _, _>( - resp.clone(), - router_data.request.to_owned(), - resp.response, - ); + let authorize_router_data = helpers::router_data_type_conversion::<_, F, _, _, _, _>( + resp.clone(), + router_data.request.to_owned(), + resp.response, + ); Ok(authorize_router_data) } else { diff --git a/crates/router/src/core/payments/flows/complete_authorize_flow.rs b/crates/router/src/core/payments/flows/complete_authorize_flow.rs index baf4190395f..667fa3feed0 100644 --- a/crates/router/src/core/payments/flows/complete_authorize_flow.rs +++ b/crates/router/src/core/payments/flows/complete_authorize_flow.rs @@ -172,7 +172,7 @@ pub async fn complete_authorize_preprocessing_steps( Err(types::ErrorResponse::default()); let preprocessing_router_data = - payments::helpers::router_data_type_conversion::<_, api::PreProcessing, _, _, _, _>( + helpers::router_data_type_conversion::<_, api::PreProcessing, _, _, _, _>( router_data.clone(), preprocessing_request_data, preprocessing_response_data, @@ -206,15 +206,14 @@ pub async fn complete_authorize_preprocessing_steps( connector_metadata, .. }) = &resp.response { - router_data_request.connector_meta = connector_metadata.to_owned(); + connector_metadata.clone_into(&mut router_data_request.connector_meta); }; - let authorize_router_data = - payments::helpers::router_data_type_conversion::<_, F, _, _, _, _>( - resp.clone(), - router_data_request, - resp.response, - ); + let authorize_router_data = helpers::router_data_type_conversion::<_, F, _, _, _, _>( + resp.clone(), + router_data_request, + resp.response, + ); Ok(authorize_router_data) } else { diff --git a/crates/router/src/core/payments/helpers.rs b/crates/router/src/core/payments/helpers.rs index f17a7075d43..e4c4540cacf 100644 --- a/crates/router/src/core/payments/helpers.rs +++ b/crates/router/src/core/payments/helpers.rs @@ -636,7 +636,7 @@ pub async fn get_token_for_recurring_mandate( merchant_connector_id: mandate.merchant_connector_id, }; - if let Some(diesel_models::enums::PaymentMethod::Card) = payment_method.payment_method { + if let Some(enums::PaymentMethod::Card) = payment_method.payment_method { if state.conf.locker.locker_enabled { let _ = cards::get_lookup_key_from_locker( state, diff --git a/crates/router/src/core/payments/operations/payment_approve.rs b/crates/router/src/core/payments/operations/payment_approve.rs index 87943b4cf2b..8dffd8eaec3 100644 --- a/crates/router/src/core/payments/operations/payment_approve.rs +++ b/crates/router/src/core/payments/operations/payment_approve.rs @@ -59,10 +59,7 @@ impl helpers::validate_payment_status_against_not_allowed_statuses( &payment_intent.status, - &[ - storage_enums::IntentStatus::Failed, - storage_enums::IntentStatus::Succeeded, - ], + &[IntentStatus::Failed, IntentStatus::Succeeded], "approve", )?; diff --git a/crates/router/src/core/payments/operations/payment_cancel.rs b/crates/router/src/core/payments/operations/payment_cancel.rs index 3e531280831..32035391fb7 100644 --- a/crates/router/src/core/payments/operations/payment_cancel.rs +++ b/crates/router/src/core/payments/operations/payment_cancel.rs @@ -112,7 +112,9 @@ impl let currency = payment_attempt.currency.get_required_value("currency")?; let amount = payment_attempt.get_total_amount().into(); - payment_attempt.cancellation_reason = request.cancellation_reason.clone(); + payment_attempt + .cancellation_reason + .clone_from(&request.cancellation_reason); let creds_identifier = request .merchant_connector_details diff --git a/crates/router/src/core/payments/operations/payment_create.rs b/crates/router/src/core/payments/operations/payment_create.rs index e770da70747..0d241202208 100644 --- a/crates/router/src/core/payments/operations/payment_create.rs +++ b/crates/router/src/core/payments/operations/payment_create.rs @@ -31,7 +31,6 @@ use crate::{ routes::{app::ReqState, AppState}, services, types::{ - self, api::{self, PaymentIdTypeExt}, domain, storage::{ @@ -949,7 +948,7 @@ impl PaymentCreate { #[allow(clippy::too_many_arguments)] async fn make_payment_intent( payment_id: &str, - merchant_account: &types::domain::MerchantAccount, + merchant_account: &domain::MerchantAccount, money: (api::Amount, enums::Currency), request: &api::PaymentsRequest, shipping_address_id: Option, @@ -971,7 +970,7 @@ impl PaymentCreate { request.confirm, ); let client_secret = - crate::utils::generate_id(consts::ID_LENGTH, format!("{payment_id}_secret").as_str()); + utils::generate_id(consts::ID_LENGTH, format!("{payment_id}_secret").as_str()); let (amount, currency) = (money.0, Some(money.1)); let order_details = request diff --git a/crates/router/src/core/payments/operations/payment_response.rs b/crates/router/src/core/payments/operations/payment_response.rs index b40ee8e5c9c..c3ef748c037 100644 --- a/crates/router/src/core/payments/operations/payment_response.rs +++ b/crates/router/src/core/payments/operations/payment_response.rs @@ -19,7 +19,6 @@ use crate::{ mandate, payment_methods::{self, PaymentMethodRetrieve}, payments::{ - self, helpers::{ self as payments_helpers, update_additional_payment_data_with_connector_response_pm_data, @@ -727,8 +726,8 @@ async fn payment_response_update_tracker( Some(multiple_capture_data) => { let capture_update = storage::CaptureUpdate::ErrorUpdate { status: match err.status_code { - 500..=511 => storage::enums::CaptureStatus::Pending, - _ => storage::enums::CaptureStatus::Failed, + 500..=511 => enums::CaptureStatus::Pending, + _ => enums::CaptureStatus::Failed, }, error_code: Some(err.code), error_message: Some(err.message), @@ -761,20 +760,20 @@ async fn payment_response_update_tracker( if flow_name == "PSync" { match err.status_code { // marking failure for 2xx because this is genuine payment failure - 200..=299 => storage::enums::AttemptStatus::Failure, + 200..=299 => enums::AttemptStatus::Failure, _ => router_data.status, } } else if flow_name == "Capture" { match err.status_code { - 500..=511 => storage::enums::AttemptStatus::Pending, + 500..=511 => enums::AttemptStatus::Pending, // don't update the status for 429 error status 429 => router_data.status, - _ => storage::enums::AttemptStatus::Failure, + _ => enums::AttemptStatus::Failure, } } else { match err.status_code { - 500..=511 => storage::enums::AttemptStatus::Pending, - _ => storage::enums::AttemptStatus::Failure, + 500..=511 => enums::AttemptStatus::Pending, + _ => enums::AttemptStatus::Failure, } } } @@ -896,8 +895,10 @@ async fn payment_response_update_tracker( }; if router_data.status == enums::AttemptStatus::Charged { - payment_data.payment_intent.fingerprint_id = - payment_data.payment_attempt.fingerprint_id.clone(); + payment_data + .payment_intent + .fingerprint_id + .clone_from(&payment_data.payment_attempt.fingerprint_id); metrics::SUCCESSFUL_PAYMENT.add(&metrics::CONTEXT, 1, &[]); } @@ -1075,8 +1076,7 @@ async fn payment_response_update_tracker( payment_data.authentication = match payment_data.authentication { Some(authentication) => { let authentication_update = storage::AuthenticationUpdate::PostAuthorizationUpdate { - authentication_lifecycle_status: - storage::enums::AuthenticationLifecycleStatus::Used, + authentication_lifecycle_status: enums::AuthenticationLifecycleStatus::Used, }; let updated_authentication = state .store @@ -1159,7 +1159,7 @@ async fn payment_response_update_tracker( }; if let Some(payment_method) = payment_data.payment_method_info.clone() { let connector_mandate_details = - payments::tokenization::update_connector_mandate_details_in_payment_method( + tokenization::update_connector_mandate_details_in_payment_method( payment_method.clone(), payment_method.payment_method_type, Some(payment_data.payment_attempt.amount), diff --git a/crates/router/src/core/payments/operations/payment_status.rs b/crates/router/src/core/payments/operations/payment_status.rs index 4c0359c28f0..06ad57346f5 100644 --- a/crates/router/src/core/payments/operations/payment_status.rs +++ b/crates/router/src/core/payments/operations/payment_status.rs @@ -289,7 +289,7 @@ async fn get_tracker_for_sync< ) .await?; - payment_attempt.encoded_data = request.param.clone(); + payment_attempt.encoded_data.clone_from(&request.param); let attempts = match request.expand_attempts { Some(true) => { diff --git a/crates/router/src/core/payments/operations/payment_update.rs b/crates/router/src/core/payments/operations/payment_update.rs index 0aaa6006052..de83b935ff0 100644 --- a/crates/router/src/core/payments/operations/payment_update.rs +++ b/crates/router/src/core/payments/operations/payment_update.rs @@ -727,8 +727,6 @@ impl .await .to_not_found_response(errors::ApiErrorResponse::PaymentNotFound)?; - payment_data.mandate_id = payment_data.mandate_id.clone(); - Ok(( payments::is_confirm(self, payment_data.confirm), payment_data, @@ -832,7 +830,9 @@ impl PaymentUpdate { payment_intent.business_country = request.business_country; - payment_intent.business_label = request.business_label.clone(); + payment_intent + .business_label + .clone_from(&request.business_label); request .description diff --git a/crates/router/src/core/payments/tokenization.rs b/crates/router/src/core/payments/tokenization.rs index 958c3f4362f..e7bb8f86d5d 100644 --- a/crates/router/src/core/payments/tokenization.rs +++ b/crates/router/src/core/payments/tokenization.rs @@ -205,9 +205,7 @@ where }; let pm_card_details = resp.card.as_ref().map(|card| { - api::payment_methods::PaymentMethodsData::Card(CardDetailsPaymentMethod::from( - card.clone(), - )) + PaymentMethodsData::Card(CardDetailsPaymentMethod::from(card.clone())) }); let pm_data_encrypted = @@ -243,7 +241,7 @@ where match &existing_pm_by_locker_id { Ok(pm) => { - payment_method_id = pm.payment_method_id.clone() + payment_method_id.clone_from(&pm.payment_method_id); } Err(_) => { payment_method_id = @@ -351,7 +349,8 @@ where match &existing_pm_by_locker_id { Ok(pm) => { - payment_method_id = pm.payment_method_id.clone() + payment_method_id + .clone_from(&pm.payment_method_id); } Err(_) => { payment_method_id = @@ -564,7 +563,7 @@ async fn skip_saving_card_in_locker( .customer_id .clone() .get_required_value("customer_id")?; - let payment_method_id = common_utils::generate_id(crate::consts::ID_LENGTH, "pm"); + let payment_method_id = common_utils::generate_id(consts::ID_LENGTH, "pm"); let last4_digits = payment_method_request .card @@ -616,7 +615,7 @@ async fn skip_saving_card_in_locker( Ok((pm_resp, None)) } None => { - let pm_id = common_utils::generate_id(crate::consts::ID_LENGTH, "pm"); + let pm_id = common_utils::generate_id(consts::ID_LENGTH, "pm"); let payment_method_response = api::PaymentMethodResponse { merchant_id: merchant_id.to_string(), customer_id: Some(customer_id), @@ -666,7 +665,7 @@ pub async fn save_in_locker( .change_context(errors::ApiErrorResponse::InternalServerError) .attach_printable("Add Card Failed"), None => { - let pm_id = common_utils::generate_id(crate::consts::ID_LENGTH, "pm"); + let pm_id = common_utils::generate_id(consts::ID_LENGTH, "pm"); let payment_method_response = api::PaymentMethodResponse { merchant_id: merchant_id.to_string(), customer_id: Some(customer_id), @@ -732,18 +731,12 @@ pub async fn add_payment_method_token( let pm_token_response_data: Result = Err(types::ErrorResponse::default()); - let mut pm_token_router_data = payments::helpers::router_data_type_conversion::< - _, - api::PaymentMethodToken, - _, - _, - _, - _, - >( - router_data.clone(), - pm_token_request_data, - pm_token_response_data, - ); + let mut pm_token_router_data = + helpers::router_data_type_conversion::<_, api::PaymentMethodToken, _, _, _, _>( + router_data.clone(), + pm_token_request_data, + pm_token_response_data, + ); connector_integration .execute_pretasks(&mut pm_token_router_data, state) diff --git a/crates/router/src/core/payments/transformers.rs b/crates/router/src/core/payments/transformers.rs index 56874e5a9fc..2b7ba070d2c 100644 --- a/crates/router/src/core/payments/transformers.rs +++ b/crates/router/src/core/payments/transformers.rs @@ -1547,8 +1547,8 @@ impl TryFrom for storage::CaptureUpdate { .. } => Ok(Self::ErrorUpdate { status: match status_code { - 500..=511 => storage::enums::CaptureStatus::Pending, - _ => storage::enums::CaptureStatus::Failed, + 500..=511 => enums::CaptureStatus::Pending, + _ => enums::CaptureStatus::Failed, }, error_code: Some(code), error_message: Some(message), diff --git a/crates/router/src/core/payouts/helpers.rs b/crates/router/src/core/payouts/helpers.rs index e521fac9a80..790cb25c479 100644 --- a/crates/router/src/core/payouts/helpers.rs +++ b/crates/router/src/core/payouts/helpers.rs @@ -403,7 +403,9 @@ pub async fn save_payout_data_to_locker( .await .flatten() .map(|card_info| { - payment_method.payment_method_issuer = card_info.card_issuer.clone(); + payment_method + .payment_method_issuer + .clone_from(&card_info.card_issuer); payment_method.card_network = card_info.card_network.clone().map(|cn| cn.to_string()); api::payment_methods::PaymentMethodsData::Card( @@ -641,7 +643,7 @@ pub async fn decide_payout_connector( request_straight_through: Option, routing_data: &mut storage::RoutingData, payout_data: &mut PayoutData, - eligible_connectors: Option>, + eligible_connectors: Option>, ) -> RouterResult { // 1. For existing attempts, use stored connector let payout_attempt = &payout_data.payout_attempt; diff --git a/crates/router/src/core/payouts/retry.rs b/crates/router/src/core/payouts/retry.rs index 6bbee9a4cec..a7dcfa864a0 100644 --- a/crates/router/src/core/payouts/retry.rs +++ b/crates/router/src/core/payouts/retry.rs @@ -114,7 +114,7 @@ pub async fn do_gsm_single_connector_actions( if let Ordering::Equal = gsm.cmp(&previous_gsm) { break; } - previous_gsm = gsm.clone(); + previous_gsm.clone_from(&gsm); match get_gsm_decision(gsm) { api_models::gsm::GsmDecision::Retry => { diff --git a/crates/router/src/core/pm_auth.rs b/crates/router/src/core/pm_auth.rs index 09ea935ea76..88138d7909f 100644 --- a/crates/router/src/core/pm_auth.rs +++ b/crates/router/src/core/pm_auth.rs @@ -64,7 +64,7 @@ pub async fn create_link_token( let redis_conn = db .get_redis_conn() - .change_context(errors::ApiErrorResponse::InternalServerError) + .change_context(ApiErrorResponse::InternalServerError) .attach_printable("Failed to get redis connection")?; let pm_auth_key = format!("pm_auth_{}", payload.payment_id); @@ -75,7 +75,7 @@ pub async fn create_link_token( "Vec", ) .await - .change_context(errors::ApiErrorResponse::InternalServerError) + .change_context(ApiErrorResponse::InternalServerError) .attach_printable("Failed to get payment method auth choices from redis")?; let selected_config = pm_auth_configs @@ -132,7 +132,7 @@ pub async fn create_link_token( &key_store, ) .await - .change_context(errors::ApiErrorResponse::MerchantConnectorAccountNotFound { + .change_context(ApiErrorResponse::MerchantConnectorAccountNotFound { id: merchant_account.merchant_id.clone(), })?; @@ -145,7 +145,7 @@ pub async fn create_link_token( request: pm_auth_types::LinkTokenRequest { client_name: "HyperSwitch".to_string(), country_codes: Some(vec![billing_country.ok_or( - errors::ApiErrorResponse::MissingRequiredField { + ApiErrorResponse::MissingRequiredField { field_name: "billing_country", }, )?]), @@ -216,8 +216,7 @@ pub async fn exchange_token_core( let connector_name = config.connector_name.as_str(); - let connector = - pm_auth_types::api::PaymentAuthConnectorData::get_connector_by_name(connector_name)?; + let connector = PaymentAuthConnectorData::get_connector_by_name(connector_name)?; let merchant_connector_account = state .store @@ -227,7 +226,7 @@ pub async fn exchange_token_core( &key_store, ) .await - .change_context(errors::ApiErrorResponse::MerchantConnectorAccountNotFound { + .change_context(ApiErrorResponse::MerchantConnectorAccountNotFound { id: merchant_account.merchant_id.clone(), })?; @@ -405,9 +404,7 @@ async fn store_bank_details_in_payment_methods( connector_details: vec![payment_methods::BankAccountConnectorDetails { connector: connector_name.to_string(), mca_id: mca_id.clone(), - access_token: payment_methods::BankAccountAccessCreds::AccessToken( - access_token.clone(), - ), + access_token: BankAccountAccessCreds::AccessToken(access_token.clone()), account_id: creds.account_id, }], }; @@ -612,7 +609,7 @@ async fn get_selected_config_from_redis( ) -> RouterResult { let redis_conn = db .get_redis_conn() - .change_context(errors::ApiErrorResponse::InternalServerError) + .change_context(ApiErrorResponse::InternalServerError) .attach_printable("Failed to get redis connection")?; let pm_auth_key = format!("pm_auth_{}", payload.payment_id); @@ -623,7 +620,7 @@ async fn get_selected_config_from_redis( "Vec", ) .await - .change_context(errors::ApiErrorResponse::GenericNotFoundError { + .change_context(ApiErrorResponse::GenericNotFoundError { message: "payment method auth connector name not found".to_string(), }) .attach_printable("Failed to get payment method auth choices from redis")?; @@ -651,14 +648,14 @@ pub async fn retrieve_payment_method_from_auth_service( ) -> RouterResult> { let db = state.store.as_ref(); - let connector = pm_auth_types::api::PaymentAuthConnectorData::get_connector_by_name( + let connector = PaymentAuthConnectorData::get_connector_by_name( auth_token.connector_details.connector.as_str(), )?; let merchant_account = db .find_merchant_account_by_merchant_id(&payment_intent.merchant_id, key_store) .await - .to_not_found_response(errors::ApiErrorResponse::MerchantAccountNotFound)?; + .to_not_found_response(ApiErrorResponse::MerchantAccountNotFound)?; let mca = db .find_by_merchant_connector_account_merchant_id_merchant_connector_id( @@ -667,7 +664,7 @@ pub async fn retrieve_payment_method_from_auth_service( key_store, ) .await - .to_not_found_response(errors::ApiErrorResponse::MerchantConnectorAccountNotFound { + .to_not_found_response(ApiErrorResponse::MerchantConnectorAccountNotFound { id: auth_token.connector_details.mca_id.clone(), }) .attach_printable( @@ -697,7 +694,7 @@ pub async fn retrieve_payment_method_from_auth_service( acc.payment_method_type == auth_token.payment_method_type && acc.payment_method == auth_token.payment_method }) - .ok_or(errors::ApiErrorResponse::InternalServerError) + .ok_or(ApiErrorResponse::InternalServerError) .attach_printable("Bank account details not found")?; let mut bank_type = None; @@ -720,7 +717,7 @@ pub async fn retrieve_payment_method_from_auth_service( let name = address .as_ref() .and_then(|addr| addr.first_name.clone().map(|name| name.into_inner())) - .ok_or(errors::ApiErrorResponse::GenericNotFoundError { + .ok_or(ApiErrorResponse::GenericNotFoundError { message: "billing_first_name not found".to_string(), }) .attach_printable("billing_first_name not found")?; diff --git a/crates/router/src/core/user/dashboard_metadata.rs b/crates/router/src/core/user/dashboard_metadata.rs index c3512c32f42..ee1693b0c9d 100644 --- a/crates/router/src/core/user/dashboard_metadata.rs +++ b/crates/router/src/core/user/dashboard_metadata.rs @@ -8,15 +8,15 @@ use masking::ExposeInterface; #[cfg(feature = "email")] use router_env::logger; +#[cfg(feature = "email")] +use crate::services::email::types as email_types; use crate::{ core::errors::{UserErrors, UserResponse, UserResult}, routes::{app::ReqState, AppState}, services::{authentication::UserFromToken, ApplicationResponse}, - types::domain::{user::dashboard_metadata as types, MerchantKeyStore}, + types::domain::{self, user::dashboard_metadata as types, MerchantKeyStore}, utils::user::dashboard_metadata as utils, }; -#[cfg(feature = "email")] -use crate::{services::email::types as email_types, types::domain}; pub async fn set_metadata( state: AppState, @@ -709,7 +709,7 @@ pub async fn get_merchant_connector_account_by_name( merchant_id: &str, connector_name: &str, key_store: &MerchantKeyStore, -) -> UserResult> { +) -> UserResult> { state .store .find_merchant_connector_account_by_merchant_id_connector_name( diff --git a/crates/router/src/core/utils.rs b/crates/router/src/core/utils.rs index 3923663f5d1..ac546ef5e49 100644 --- a/crates/router/src/core/utils.rs +++ b/crates/router/src/core/utils.rs @@ -564,7 +564,7 @@ pub async fn construct_accept_dispute_router_data<'a>( dispute_id: dispute.dispute_id.clone(), connector_dispute_id: dispute.connector_dispute_id.clone(), }, - response: Err(types::ErrorResponse::default()), + response: Err(ErrorResponse::default()), access_token: None, session_token: None, reference_id: None, @@ -654,7 +654,7 @@ pub async fn construct_submit_evidence_router_data<'a>( connector_meta_data: merchant_connector_account.get_metadata(), amount_captured: payment_intent.amount_captured, request: submit_evidence_request_data, - response: Err(types::ErrorResponse::default()), + response: Err(ErrorResponse::default()), access_token: None, session_token: None, reference_id: None, @@ -752,7 +752,7 @@ pub async fn construct_upload_file_router_data<'a>( file_type: create_file_request.file_type.clone(), file_size: create_file_request.file_size, }, - response: Err(types::ErrorResponse::default()), + response: Err(ErrorResponse::default()), access_token: None, session_token: None, reference_id: None, @@ -936,7 +936,7 @@ pub async fn construct_retrieve_file_router_data<'a>( .ok_or(errors::ApiErrorResponse::InternalServerError) .attach_printable("Missing provider file id")?, }, - response: Err(types::ErrorResponse::default()), + response: Err(ErrorResponse::default()), access_token: None, session_token: None, reference_id: None, @@ -1225,9 +1225,7 @@ pub fn get_incremental_authorization_allowed_value( incremental_authorization_allowed: Option, request_incremental_authorization: Option, ) -> Option { - if request_incremental_authorization - == Some(common_enums::RequestIncrementalAuthorization::False) - { + if request_incremental_authorization == Some(RequestIncrementalAuthorization::False) { Some(false) } else { incremental_authorization_allowed diff --git a/crates/router/src/core/verification.rs b/crates/router/src/core/verification.rs index 8782126b0b6..fe3d752497a 100644 --- a/crates/router/src/core/verification.rs +++ b/crates/router/src/core/verification.rs @@ -89,7 +89,7 @@ pub async fn get_verified_apple_domains_with_mid_mca_id( merchant_id: String, merchant_connector_id: String, ) -> CustomResult< - services::ApplicationResponse, + services::ApplicationResponse, api_error_response::ApiErrorResponse, > { let db = state.store.as_ref(); @@ -110,6 +110,6 @@ pub async fn get_verified_apple_domains_with_mid_mca_id( .unwrap_or_default(); Ok(services::api::ApplicationResponse::Json( - api_models::verifications::ApplepayVerifiedDomainsResponse { verified_domains }, + verifications::ApplepayVerifiedDomainsResponse { verified_domains }, )) } diff --git a/crates/router/src/core/webhooks.rs b/crates/router/src/core/webhooks.rs index 6647549e399..3c04fdb7e49 100644 --- a/crates/router/src/core/webhooks.rs +++ b/crates/router/src/core/webhooks.rs @@ -74,7 +74,7 @@ pub async fn payments_incoming_webhook_flow( payments::CallConnectorAction::Trigger }; let payments_response = match webhook_details.object_reference_id { - api_models::webhooks::ObjectReferenceId::PaymentId(id) => { + webhooks::ObjectReferenceId::PaymentId(id) => { let payment_id = get_payment_id( state.store.as_ref(), &id, @@ -84,7 +84,7 @@ pub async fn payments_incoming_webhook_flow( .await?; let lock_action = api_locking::LockAction::Hold { - input: super::api_locking::LockingInput { + input: api_locking::LockingInput { unique_locking_key: payment_id, api_identifier: lock_utils::ApiIdentifier::Payments, override_lock_retries: None, @@ -213,13 +213,13 @@ pub async fn refunds_incoming_webhook_flow( webhook_details: api::IncomingWebhookDetails, connector_name: &str, source_verified: bool, - event_type: api_models::webhooks::IncomingWebhookEvent, + event_type: webhooks::IncomingWebhookEvent, ) -> CustomResult { let db = &*state.store; //find refund by connector refund id let refund = match webhook_details.object_reference_id { - api_models::webhooks::ObjectReferenceId::RefundId(refund_id_type) => match refund_id_type { - api_models::webhooks::RefundIdType::RefundId(id) => db + webhooks::ObjectReferenceId::RefundId(refund_id_type) => match refund_id_type { + webhooks::RefundIdType::RefundId(id) => db .find_refund_by_merchant_id_refund_id( &merchant_account.merchant_id, &id, @@ -228,7 +228,7 @@ pub async fn refunds_incoming_webhook_flow( .await .change_context(errors::ApiErrorResponse::WebhookResourceNotFound) .attach_printable("Failed to fetch the refund")?, - api_models::webhooks::RefundIdType::ConnectorRefundId(id) => db + webhooks::RefundIdType::ConnectorRefundId(id) => db .find_refund_by_merchant_id_connector_refund_id_connector( &merchant_account.merchant_id, &id, @@ -305,7 +305,7 @@ pub async fn refunds_incoming_webhook_flow( pub async fn get_payment_attempt_from_object_reference_id( state: &AppState, - object_reference_id: api_models::webhooks::ObjectReferenceId, + object_reference_id: webhooks::ObjectReferenceId, merchant_account: &domain::MerchantAccount, ) -> CustomResult< hyperswitch_domain_models::payments::payment_attempt::PaymentAttempt, @@ -349,7 +349,7 @@ pub async fn get_or_update_dispute_object( dispute_details: api::disputes::DisputePayload, merchant_id: &str, payment_attempt: &hyperswitch_domain_models::payments::payment_attempt::PaymentAttempt, - event_type: api_models::webhooks::IncomingWebhookEvent, + event_type: webhooks::IncomingWebhookEvent, business_profile: &diesel_models::business_profile::BusinessProfile, connector_name: &str, ) -> CustomResult { @@ -425,7 +425,7 @@ pub async fn external_authentication_incoming_webhook_flow, connector: &(dyn api::Connector + Sync), object_ref_id: api::ObjectReferenceId, @@ -601,7 +601,7 @@ pub async fn mandates_incoming_webhook_flow( key_store: domain::MerchantKeyStore, webhook_details: api::IncomingWebhookDetails, source_verified: bool, - event_type: api_models::webhooks::IncomingWebhookEvent, + event_type: webhooks::IncomingWebhookEvent, ) -> CustomResult { if source_verified { let db = &*state.store; @@ -691,7 +691,7 @@ pub async fn disputes_incoming_webhook_flow( source_verified: bool, connector: &(dyn api::Connector + Sync), request_details: &api::IncomingWebhookRequestDetails<'_>, - event_type: api_models::webhooks::IncomingWebhookEvent, + event_type: webhooks::IncomingWebhookEvent, ) -> CustomResult { metrics::INCOMING_DISPUTE_WEBHOOK_METRIC.add(&metrics::CONTEXT, 1, &[]); if source_verified { @@ -1612,7 +1612,7 @@ pub async fn webhooks_core( outgoing_webhook, payment_response_hash_key ), - _ => get_outgoing_webhook_request_inner::( + _ => get_outgoing_webhook_request_inner::( outgoing_webhook, payment_response_hash_key, ), diff --git a/crates/router/src/core/webhooks/utils.rs b/crates/router/src/core/webhooks/utils.rs index 3bf8c7c7f21..ad1aafd3125 100644 --- a/crates/router/src/core/webhooks/utils.rs +++ b/crates/router/src/core/webhooks/utils.rs @@ -124,8 +124,8 @@ pub async fn construct_webhook_router_data<'a>( #[inline] pub(crate) fn get_idempotent_event_id( primary_object_id: &str, - event_type: crate::types::storage::enums::EventType, - delivery_attempt: crate::types::storage::enums::WebhookDeliveryAttempt, + event_type: types::storage::enums::EventType, + delivery_attempt: types::storage::enums::WebhookDeliveryAttempt, ) -> String { use crate::types::storage::enums::WebhookDeliveryAttempt; diff --git a/crates/router/src/db.rs b/crates/router/src/db.rs index 9b222486d18..ca9432fcba9 100644 --- a/crates/router/src/db.rs +++ b/crates/router/src/db.rs @@ -190,7 +190,7 @@ where let bytes = db.get_key(key).await?; bytes .parse_struct(type_name) - .change_context(redis_interface::errors::RedisError::JsonDeserializationFailed) + .change_context(RedisError::JsonDeserializationFailed) } dyn_clone::clone_trait_object!(StorageInterface); diff --git a/crates/router/src/db/business_profile.rs b/crates/router/src/db/business_profile.rs index 7b5ade8c965..5e7c9bd0b20 100644 --- a/crates/router/src/db/business_profile.rs +++ b/crates/router/src/db/business_profile.rs @@ -6,7 +6,7 @@ use crate::{ connection, core::errors::{self, CustomResult}, db::MockDb, - types::storage::{self, business_profile}, + types::storage::business_profile, }; #[async_trait::async_trait] @@ -65,7 +65,7 @@ impl BusinessProfileInterface for Store { profile_id: &str, ) -> CustomResult { let conn = connection::pg_connection_read(self).await?; - storage::business_profile::BusinessProfile::find_by_profile_id(&conn, profile_id) + business_profile::BusinessProfile::find_by_profile_id(&conn, profile_id) .await .map_err(|error| report!(errors::StorageError::from(error))) } @@ -77,7 +77,7 @@ impl BusinessProfileInterface for Store { merchant_id: &str, ) -> CustomResult { let conn = connection::pg_connection_read(self).await?; - storage::business_profile::BusinessProfile::find_by_profile_name_merchant_id( + business_profile::BusinessProfile::find_by_profile_name_merchant_id( &conn, profile_name, merchant_id, @@ -93,7 +93,7 @@ impl BusinessProfileInterface for Store { business_profile_update: business_profile::BusinessProfileUpdate, ) -> CustomResult { let conn = connection::pg_connection_write(self).await?; - storage::business_profile::BusinessProfile::update_by_profile_id( + business_profile::BusinessProfile::update_by_profile_id( current_state, &conn, business_profile_update, @@ -109,7 +109,7 @@ impl BusinessProfileInterface for Store { merchant_id: &str, ) -> CustomResult { let conn = connection::pg_connection_write(self).await?; - storage::business_profile::BusinessProfile::delete_by_profile_id_merchant_id( + business_profile::BusinessProfile::delete_by_profile_id_merchant_id( &conn, profile_id, merchant_id, @@ -124,12 +124,9 @@ impl BusinessProfileInterface for Store { merchant_id: &str, ) -> CustomResult, errors::StorageError> { let conn = connection::pg_connection_read(self).await?; - storage::business_profile::BusinessProfile::list_business_profile_by_merchant_id( - &conn, - merchant_id, - ) - .await - .map_err(|error| report!(errors::StorageError::from(error))) + business_profile::BusinessProfile::list_business_profile_by_merchant_id(&conn, merchant_id) + .await + .map_err(|error| report!(errors::StorageError::from(error))) } } diff --git a/crates/router/src/db/cache.rs b/crates/router/src/db/cache.rs index 7f2462b3fc8..d9db13dde80 100644 --- a/crates/router/src/db/cache.rs +++ b/crates/router/src/db/cache.rs @@ -43,7 +43,7 @@ where }; match redis_val { Err(err) => match err.current_context() { - errors::RedisError::NotFound | errors::RedisError::JsonDeserializationFailed => { + RedisError::NotFound | RedisError::JsonDeserializationFailed => { get_data_set_redis().await } _ => Err(err diff --git a/crates/router/src/db/dispute.rs b/crates/router/src/db/dispute.rs index 0fe5f3d7666..097ad9beab9 100644 --- a/crates/router/src/db/dispute.rs +++ b/crates/router/src/db/dispute.rs @@ -444,7 +444,7 @@ mod tests { #[tokio::test] async fn test_find_by_merchant_id_payment_id_connector_dispute_id() { #[allow(clippy::expect_used)] - let mockdb = MockDb::new(&redis_interface::RedisSettings::default()) + let mockdb = MockDb::new(&RedisSettings::default()) .await .expect("Failed to create Mock store"); @@ -487,7 +487,7 @@ mod tests { #[tokio::test] async fn test_find_dispute_by_merchant_id_dispute_id() { #[allow(clippy::expect_used)] - let mockdb = MockDb::new(&redis_interface::RedisSettings::default()) + let mockdb = MockDb::new(&RedisSettings::default()) .await .expect("Failed to create Mock store"); @@ -524,7 +524,7 @@ mod tests { #[tokio::test] async fn test_find_disputes_by_merchant_id() { #[allow(clippy::expect_used)] - let mockdb = MockDb::new(&redis_interface::RedisSettings::default()) + let mockdb = MockDb::new(&RedisSettings::default()) .await .expect("Failed to create Mock store"); @@ -578,7 +578,7 @@ mod tests { #[tokio::test] async fn test_find_disputes_by_merchant_id_payment_id() { #[allow(clippy::expect_used)] - let mockdb = MockDb::new(&redis_interface::RedisSettings::default()) + let mockdb = MockDb::new(&RedisSettings::default()) .await .expect("Failed to create Mock store"); diff --git a/crates/router/src/db/user_role.rs b/crates/router/src/db/user_role.rs index e31a2663354..fff82334be0 100644 --- a/crates/router/src/db/user_role.rs +++ b/crates/router/src/db/user_role.rs @@ -377,8 +377,8 @@ impl UserRoleInterface for MockDb { status, modified_by, } => { - user_role.status = status.to_owned(); - user_role.last_modified_by = modified_by.to_owned(); + status.clone_into(&mut user_role.status); + modified_by.clone_into(&mut user_role.last_modified_by); } } updated_user_roles.push(user_role.to_owned()); diff --git a/crates/router/src/lib.rs b/crates/router/src/lib.rs index fdabd07fa01..d11e5cf997a 100644 --- a/crates/router/src/lib.rs +++ b/crates/router/src/lib.rs @@ -188,7 +188,7 @@ pub async fn start_server(conf: settings::Settings) -> Applicatio errors::ApplicationError::ApiClientError(error.current_context().clone()) })?, ); - let state = Box::pin(routes::AppState::new(conf, tx, api_client)).await; + let state = Box::pin(AppState::new(conf, tx, api_client)).await; let request_body_limit = server.request_body_limit; let server = actix_web::HttpServer::new(move || mk_app(state.clone(), request_body_limit)) .bind((server.host.as_str(), server.port))? diff --git a/crates/router/src/routes/api_keys.rs b/crates/router/src/routes/api_keys.rs index 55959589152..f1170f0f0bb 100644 --- a/crates/router/src/routes/api_keys.rs +++ b/crates/router/src/routes/api_keys.rs @@ -131,7 +131,7 @@ pub async fn api_key_update( let (merchant_id, key_id) = path.into_inner(); let mut payload = json_payload.into_inner(); payload.key_id = key_id; - payload.merchant_id = merchant_id.clone(); + payload.merchant_id.clone_from(&merchant_id); api::server_wrap( flow, diff --git a/crates/router/src/routes/metrics/request.rs b/crates/router/src/routes/metrics/request.rs index 029c1c61f24..ef53ad83f2c 100644 --- a/crates/router/src/routes/metrics/request.rs +++ b/crates/router/src/routes/metrics/request.rs @@ -24,7 +24,7 @@ where #[inline] pub async fn record_operation_time( future: F, - metric: &once_cell::sync::Lazy>, + metric: &once_cell::sync::Lazy>, key_value: &[opentelemetry::KeyValue], ) -> R where @@ -35,11 +35,11 @@ where result } -pub fn add_attributes>( +pub fn add_attributes>( key: &'static str, value: T, -) -> router_env::opentelemetry::KeyValue { - router_env::opentelemetry::KeyValue::new(key, value) +) -> opentelemetry::KeyValue { + opentelemetry::KeyValue::new(key, value) } pub fn status_code_metrics(status_code: i64, flow: String, merchant_id: String) { diff --git a/crates/router/src/routes/payments.rs b/crates/router/src/routes/payments.rs index c9e1cf14ce3..70112e27037 100644 --- a/crates/router/src/routes/payments.rs +++ b/crates/router/src/routes/payments.rs @@ -131,7 +131,7 @@ pub async fn payments_create( req_state, auth.merchant_account, auth.key_store, - payment_types::HeaderPayload::default(), + HeaderPayload::default(), req, api::AuthFlow::Merchant, ) @@ -420,7 +420,7 @@ pub async fn payments_update( req_state, auth.merchant_account, auth.key_store, - payment_types::HeaderPayload::default(), + HeaderPayload::default(), req, auth_flow, ) @@ -471,7 +471,7 @@ pub async fn payments_confirm( tracing::Span::current().record("payment_id", &payment_id); payload.payment_id = Some(payment_types::PaymentIdType::PaymentIntentId(payment_id)); payload.confirm = Some(true); - let header_payload = match payment_types::HeaderPayload::foreign_try_from(req.headers()) { + let header_payload = match HeaderPayload::foreign_try_from(req.headers()) { Ok(headers) => headers, Err(err) => { return api::log_and_return_error_response(err); @@ -1019,7 +1019,7 @@ pub async fn payments_approve( api::AuthFlow::Merchant, payments::CallConnectorAction::Trigger, None, - payment_types::HeaderPayload::default(), + HeaderPayload::default(), ) }, match env::which() { @@ -1081,7 +1081,7 @@ pub async fn payments_reject( api::AuthFlow::Merchant, payments::CallConnectorAction::Trigger, None, - payment_types::HeaderPayload::default(), + HeaderPayload::default(), ) }, match env::which() { @@ -1107,7 +1107,7 @@ async fn authorize_verify_select( header_payload: HeaderPayload, req: api_models::payments::PaymentsRequest, auth_flow: api::AuthFlow, -) -> app::core::errors::RouterResponse +) -> errors::RouterResponse where Ctx: PaymentMethodRetrieve, Op: Sync @@ -1530,8 +1530,10 @@ impl GetLockingInput for payment_types::PaymentsCaptureRequest { } } +#[cfg(feature = "oltp")] struct FPaymentsApproveRequest<'a>(&'a payment_types::PaymentsApproveRequest); +#[cfg(feature = "oltp")] impl<'a> GetLockingInput for FPaymentsApproveRequest<'a> { fn get_locking_input(&self, flow: F) -> api_locking::LockAction where @@ -1548,8 +1550,10 @@ impl<'a> GetLockingInput for FPaymentsApproveRequest<'a> { } } +#[cfg(feature = "oltp")] struct FPaymentsRejectRequest<'a>(&'a payment_types::PaymentsRejectRequest); +#[cfg(feature = "oltp")] impl<'a> GetLockingInput for FPaymentsRejectRequest<'a> { fn get_locking_input(&self, flow: F) -> api_locking::LockAction where diff --git a/crates/router/src/routes/recon.rs b/crates/router/src/routes/recon.rs index 7845f52c924..fc0794ff9e0 100644 --- a/crates/router/src/routes/recon.rs +++ b/crates/router/src/routes/recon.rs @@ -1,6 +1,5 @@ use actix_web::{web, HttpRequest, HttpResponse}; use api_models::recon as recon_api; -use common_enums::ReconStatus; use error_stack::ResultExt; use masking::{ExposeInterface, PeekInterface, Secret}; use router_env::Flow; @@ -195,7 +194,7 @@ pub async fn recon_merchant_account_update( subject: "Approval of Recon Request - Access Granted to Recon Dashboard", }; - if req.recon_status == ReconStatus::Active { + if req.recon_status == enums::ReconStatus::Active { let _is_email_sent = state .email_client .compose_and_send_email( diff --git a/crates/router/src/routes/user.rs b/crates/router/src/routes/user.rs index a3f18168501..1ffb4dc8d1a 100644 --- a/crates/router/src/routes/user.rs +++ b/crates/router/src/routes/user.rs @@ -167,9 +167,10 @@ pub async fn set_dashboard_metadata( let flow = Flow::SetDashboardMetadata; let mut payload = json_payload.into_inner(); - if let Err(e) = common_utils::errors::ReportSwitchExt::<(), ApiErrorResponse>::switch( - set_ip_address_if_required(&mut payload, req.headers()), - ) { + if let Err(e) = ReportSwitchExt::<(), ApiErrorResponse>::switch(set_ip_address_if_required( + &mut payload, + req.headers(), + )) { return api::log_and_return_error_response(e); } diff --git a/crates/router/src/routes/webhooks.rs b/crates/router/src/routes/webhooks.rs index 073b1c57000..cdf680548a1 100644 --- a/crates/router/src/routes/webhooks.rs +++ b/crates/router/src/routes/webhooks.rs @@ -43,19 +43,3 @@ pub async fn receive_incoming_webhook( )) .await } - -#[derive(Debug)] -struct WebhookBytes(web::Bytes); - -impl serde::Serialize for WebhookBytes { - fn serialize(&self, serializer: S) -> Result { - let payload: serde_json::Value = serde_json::from_slice(&self.0).unwrap_or_default(); - payload.serialize(serializer) - } -} - -impl common_utils::events::ApiEventMetric for WebhookBytes { - fn get_api_event_type(&self) -> Option { - Some(common_utils::events::ApiEventsType::Miscellaneous) - } -} diff --git a/crates/router/src/services/api.rs b/crates/router/src/services/api.rs index 30c3ce164e2..cfe4fea11bb 100644 --- a/crates/router/src/services/api.rs +++ b/crates/router/src/services/api.rs @@ -273,7 +273,7 @@ pub enum CaptureSyncMethod { pub async fn execute_connector_processing_step< 'b, 'a, - T: 'static, + T, Req: Debug + Clone + 'static, Resp: Debug + Clone + 'static, >( @@ -284,7 +284,7 @@ pub async fn execute_connector_processing_step< connector_request: Option, ) -> CustomResult, errors::ConnectorError> where - T: Clone + Debug, + T: Clone + Debug + 'static, // BoxedConnectorIntegration: 'b, { // If needed add an error stack as follows @@ -651,7 +651,7 @@ pub async fn send_request( } .add_headers(headers) .timeout(Duration::from_secs( - option_timeout_secs.unwrap_or(crate::consts::REQUEST_TIME_OUT), + option_timeout_secs.unwrap_or(consts::REQUEST_TIME_OUT), )) }; @@ -920,7 +920,7 @@ pub enum RedirectForm { impl From<(url::Url, Method)> for RedirectForm { fn from((mut redirect_url, method): (url::Url, Method)) -> Self { - let form_fields = std::collections::HashMap::from_iter( + let form_fields = HashMap::from_iter( redirect_url .query_pairs() .map(|(key, value)| (key.to_string(), value.to_string())), @@ -1127,10 +1127,7 @@ where if unmasked_incoming_header_keys.contains(&key.as_str().to_lowercase()) { acc.insert(key.clone(), value.clone()); } else { - acc.insert( - key.clone(), - http::header::HeaderValue::from_static("**MASKED**"), - ); + acc.insert(key.clone(), HeaderValue::from_static("**MASKED**")); } acc }); diff --git a/crates/router/src/services/api/client.rs b/crates/router/src/services/api/client.rs index 09b4d25d0b6..ae45a2a7fce 100644 --- a/crates/router/src/services/api/client.rs +++ b/crates/router/src/services/api/client.rs @@ -27,7 +27,7 @@ fn get_client_builder( ) -> CustomResult { let mut client_builder = reqwest::Client::builder() .redirect(reqwest::redirect::Policy::none()) - .pool_idle_timeout(std::time::Duration::from_secs( + .pool_idle_timeout(Duration::from_secs( proxy_config .idle_pool_connection_timeout .unwrap_or_default(), diff --git a/crates/router/src/services/authentication.rs b/crates/router/src/services/authentication.rs index 5f15474115d..9aac92acd2a 100644 --- a/crates/router/src/services/authentication.rs +++ b/crates/router/src/services/authentication.rs @@ -876,13 +876,13 @@ impl ClientSecretFetch for api_models::cards_info::CardsInfoRequest { } } -impl ClientSecretFetch for api_models::payments::PaymentsRetrieveRequest { +impl ClientSecretFetch for payments::PaymentsRetrieveRequest { fn get_client_secret(&self) -> Option<&String> { self.client_secret.as_ref() } } -impl ClientSecretFetch for api_models::payments::RetrievePaymentLinkRequest { +impl ClientSecretFetch for payments::RetrievePaymentLinkRequest { fn get_client_secret(&self) -> Option<&String> { self.client_secret.as_ref() } diff --git a/crates/router/src/services/pm_auth.rs b/crates/router/src/services/pm_auth.rs index 91b752aaf13..a54ba881995 100644 --- a/crates/router/src/services/pm_auth.rs +++ b/crates/router/src/services/pm_auth.rs @@ -11,22 +11,16 @@ use crate::{ services::{self}, }; -pub async fn execute_connector_processing_step< - 'b, - 'a, - T: 'static, - Req: Clone + 'static, - Resp: Clone + 'static, ->( +pub async fn execute_connector_processing_step<'b, 'a, T, Req, Resp>( state: &'b AppState, connector_integration: BoxedConnectorIntegration<'a, T, Req, Resp>, req: &'b PaymentAuthRouterData, connector: &pm_auth_types::PaymentMethodAuthConnectors, ) -> errors::CustomResult, ConnectorError> where - T: Clone, - Req: Clone, - Resp: Clone, + T: Clone + 'static, + Req: Clone + 'static, + Resp: Clone + 'static, { let mut router_data = req.clone(); diff --git a/crates/router/src/types/api/customers.rs b/crates/router/src/types/api/customers.rs index 6f08a7fd7e4..e0ef95bcb0d 100644 --- a/crates/router/src/types/api/customers.rs +++ b/crates/router/src/types/api/customers.rs @@ -3,7 +3,7 @@ pub use api_models::customers::{CustomerDeleteResponse, CustomerId, CustomerRequ use serde::Serialize; use super::payments; -use crate::{core::errors::RouterResult, newtype, types::domain}; +use crate::{newtype, types::domain}; newtype!( pub CustomerResponse = customers::CustomerResponse, @@ -16,10 +16,6 @@ impl common_utils::events::ApiEventMetric for CustomerResponse { } } -pub(crate) trait CustomerRequestExt: Sized { - fn validate(self) -> RouterResult; -} - impl From<(domain::Customer, Option)> for CustomerResponse { fn from((cust, address): (domain::Customer, Option)) -> Self { customers::CustomerResponse { diff --git a/crates/router/src/types/api/payments.rs b/crates/router/src/types/api/payments.rs index e36c68c27d5..9f68cac98c9 100644 --- a/crates/router/src/types/api/payments.rs +++ b/crates/router/src/types/api/payments.rs @@ -21,20 +21,6 @@ use crate::{ types::{self, api as api_types}, }; -pub(crate) trait PaymentsRequestExt { - fn is_mandate(&self) -> Option; -} - -impl PaymentsRequestExt for PaymentsRequest { - fn is_mandate(&self) -> Option { - match (&self.mandate_data, &self.mandate_id) { - (None, None) => None, - (_, Some(_)) => Some(MandateTransactionType::RecurringMandateTransaction), - (Some(_), _) => Some(MandateTransactionType::NewMandateTransaction), - } - } -} - impl super::Router for PaymentsRequest {} // Core related api layer. diff --git a/crates/router/src/types/api/verify_connector.rs b/crates/router/src/types/api/verify_connector.rs index 39891a7075d..af7cb9160b2 100644 --- a/crates/router/src/types/api/verify_connector.rs +++ b/crates/router/src/types/api/verify_connector.rs @@ -14,7 +14,7 @@ use crate::{ #[derive(Clone, Debug)] pub struct VerifyConnectorData { - pub connector: &'static (dyn types::api::Connector + Sync), + pub connector: &'static (dyn api::Connector + Sync), pub connector_auth: types::ConnectorAuthType, pub card_details: domain::Card, } @@ -155,11 +155,11 @@ pub trait VerifyConnector { } async fn handle_payment_error_response( - connector: &(dyn types::api::Connector + Sync), + connector: &(dyn api::Connector + Sync), error_response: types::Response, ) -> errors::RouterResponse<()> where - dyn types::api::Connector + Sync: ConnectorIntegration, + dyn api::Connector + Sync: ConnectorIntegration, { let error = connector .get_error_response(error_response, None) @@ -171,11 +171,11 @@ pub trait VerifyConnector { } async fn handle_access_token_error_response( - connector: &(dyn types::api::Connector + Sync), + connector: &(dyn api::Connector + Sync), error_response: types::Response, ) -> errors::RouterResult> where - dyn types::api::Connector + Sync: ConnectorIntegration, + dyn api::Connector + Sync: ConnectorIntegration, { let error = connector .get_error_response(error_response, None) diff --git a/crates/router/src/types/domain/customer.rs b/crates/router/src/types/domain/customer.rs index de95251d9d4..139cd105779 100644 --- a/crates/router/src/types/domain/customer.rs +++ b/crates/router/src/types/domain/customer.rs @@ -148,14 +148,14 @@ impl From for CustomerUpdateInternal { }, CustomerUpdate::ConnectorCustomer { connector_customer } => Self { connector_customer, - modified_at: Some(common_utils::date_time::now()), + modified_at: Some(date_time::now()), ..Default::default() }, CustomerUpdate::UpdateDefaultPaymentMethod { default_payment_method_id, } => Self { default_payment_method_id, - modified_at: Some(common_utils::date_time::now()), + modified_at: Some(date_time::now()), ..Default::default() }, } diff --git a/crates/router/src/types/domain/merchant_connector_account.rs b/crates/router/src/types/domain/merchant_connector_account.rs index c84abbefc38..0e7f5b081c3 100644 --- a/crates/router/src/types/domain/merchant_connector_account.rs +++ b/crates/router/src/types/domain/merchant_connector_account.rs @@ -195,7 +195,7 @@ impl From for MerchantConnectorAccountUpdateInte metadata, frm_configs: None, frm_config: frm_configs, - modified_at: Some(common_utils::date_time::now()), + modified_at: Some(date_time::now()), connector_webhook_details, applepay_verified_domains, pm_auth_config, diff --git a/crates/router/src/types/transformers.rs b/crates/router/src/types/transformers.rs index ef12e32d57c..ce11251b83e 100644 --- a/crates/router/src/types/transformers.rs +++ b/crates/router/src/types/transformers.rs @@ -176,20 +176,18 @@ impl ForeignTryFrom for storage_enums::CaptureStat } } -impl ForeignFrom for storage_enums::MandateDataType { - fn foreign_from(from: api_models::payments::MandateType) -> Self { +impl ForeignFrom for storage_enums::MandateDataType { + fn foreign_from(from: payments::MandateType) -> Self { match from { - api_models::payments::MandateType::SingleUse(inner) => { - Self::SingleUse(inner.foreign_into()) - } - api_models::payments::MandateType::MultiUse(inner) => { + payments::MandateType::SingleUse(inner) => Self::SingleUse(inner.foreign_into()), + payments::MandateType::MultiUse(inner) => { Self::MultiUse(inner.map(ForeignInto::foreign_into)) } } } } -impl ForeignFrom for api_models::payments::MandateType { +impl ForeignFrom for payments::MandateType { fn foreign_from(from: storage_enums::MandateDataType) -> Self { match from { storage_enums::MandateDataType::SingleUse(inner) => { @@ -302,7 +300,7 @@ impl ForeignTryFrom for common_enums::RoutableConnectors { } } -impl ForeignFrom for api_models::payments::MandateAmountData { +impl ForeignFrom for payments::MandateAmountData { fn foreign_from(from: storage_enums::MandateAmountData) -> Self { Self { amount: from.amount, @@ -315,18 +313,16 @@ impl ForeignFrom for api_models::payments::Man } // TODO: remove foreign from since this conversion won't be needed in the router crate once data models is treated as a single & primary source of truth for structure information -impl ForeignFrom - for hyperswitch_domain_models::mandates::MandateData -{ - fn foreign_from(d: api_models::payments::MandateData) -> Self { +impl ForeignFrom for hyperswitch_domain_models::mandates::MandateData { + fn foreign_from(d: payments::MandateData) -> Self { Self { customer_acceptance: d.customer_acceptance.map(|d| { hyperswitch_domain_models::mandates::CustomerAcceptance { acceptance_type: match d.acceptance_type { - api_models::payments::AcceptanceType::Online => { + payments::AcceptanceType::Online => { hyperswitch_domain_models::mandates::AcceptanceType::Online } - api_models::payments::AcceptanceType::Offline => { + payments::AcceptanceType::Offline => { hyperswitch_domain_models::mandates::AcceptanceType::Offline } }, @@ -340,7 +336,7 @@ impl ForeignFrom } }), mandate_type: d.mandate_type.map(|d| match d { - api_models::payments::MandateType::MultiUse(Some(i)) => { + payments::MandateType::MultiUse(Some(i)) => { hyperswitch_domain_models::mandates::MandateDataType::MultiUse(Some( hyperswitch_domain_models::mandates::MandateAmountData { amount: i.amount, @@ -351,7 +347,7 @@ impl ForeignFrom }, )) } - api_models::payments::MandateType::SingleUse(i) => { + payments::MandateType::SingleUse(i) => { hyperswitch_domain_models::mandates::MandateDataType::SingleUse( hyperswitch_domain_models::mandates::MandateAmountData { amount: i.amount, @@ -362,7 +358,7 @@ impl ForeignFrom }, ) } - api_models::payments::MandateType::MultiUse(None) => { + payments::MandateType::MultiUse(None) => { hyperswitch_domain_models::mandates::MandateDataType::MultiUse(None) } }), @@ -371,8 +367,8 @@ impl ForeignFrom } } -impl ForeignFrom for storage_enums::MandateAmountData { - fn foreign_from(from: api_models::payments::MandateAmountData) -> Self { +impl ForeignFrom for storage_enums::MandateAmountData { + fn foreign_from(from: payments::MandateAmountData) -> Self { Self { amount: from.amount, currency: from.currency, @@ -503,26 +499,27 @@ impl ForeignFrom for api_enums::PaymentMethod { } } -impl ForeignTryFrom for api_enums::PaymentMethod { +impl ForeignTryFrom for api_enums::PaymentMethod { type Error = errors::ApiErrorResponse; fn foreign_try_from( - payment_method_data: api_models::payments::PaymentMethodData, + payment_method_data: payments::PaymentMethodData, ) -> Result { match payment_method_data { - api_models::payments::PaymentMethodData::Card(..) - | api_models::payments::PaymentMethodData::CardToken(..) => Ok(Self::Card), - api_models::payments::PaymentMethodData::Wallet(..) => Ok(Self::Wallet), - api_models::payments::PaymentMethodData::PayLater(..) => Ok(Self::PayLater), - api_models::payments::PaymentMethodData::BankRedirect(..) => Ok(Self::BankRedirect), - api_models::payments::PaymentMethodData::BankDebit(..) => Ok(Self::BankDebit), - api_models::payments::PaymentMethodData::BankTransfer(..) => Ok(Self::BankTransfer), - api_models::payments::PaymentMethodData::Crypto(..) => Ok(Self::Crypto), - api_models::payments::PaymentMethodData::Reward => Ok(Self::Reward), - api_models::payments::PaymentMethodData::Upi(..) => Ok(Self::Upi), - api_models::payments::PaymentMethodData::Voucher(..) => Ok(Self::Voucher), - api_models::payments::PaymentMethodData::GiftCard(..) => Ok(Self::GiftCard), - api_models::payments::PaymentMethodData::CardRedirect(..) => Ok(Self::CardRedirect), - api_models::payments::PaymentMethodData::MandatePayment => { + payments::PaymentMethodData::Card(..) | payments::PaymentMethodData::CardToken(..) => { + Ok(Self::Card) + } + payments::PaymentMethodData::Wallet(..) => Ok(Self::Wallet), + payments::PaymentMethodData::PayLater(..) => Ok(Self::PayLater), + payments::PaymentMethodData::BankRedirect(..) => Ok(Self::BankRedirect), + payments::PaymentMethodData::BankDebit(..) => Ok(Self::BankDebit), + payments::PaymentMethodData::BankTransfer(..) => Ok(Self::BankTransfer), + payments::PaymentMethodData::Crypto(..) => Ok(Self::Crypto), + payments::PaymentMethodData::Reward => Ok(Self::Reward), + payments::PaymentMethodData::Upi(..) => Ok(Self::Upi), + payments::PaymentMethodData::Voucher(..) => Ok(Self::Voucher), + payments::PaymentMethodData::GiftCard(..) => Ok(Self::GiftCard), + payments::PaymentMethodData::CardRedirect(..) => Ok(Self::CardRedirect), + payments::PaymentMethodData::MandatePayment => { Err(errors::ApiErrorResponse::InvalidRequestData { message: ("Mandate payments cannot have payment_method_data field".to_string()), }) @@ -902,7 +899,7 @@ impl TryFrom for api_models::admin::MerchantCo } } -impl ForeignFrom for api_models::payments::PaymentAttemptResponse { +impl ForeignFrom for payments::PaymentAttemptResponse { fn foreign_from(payment_attempt: storage::PaymentAttempt) -> Self { Self { attempt_id: payment_attempt.attempt_id, @@ -929,7 +926,7 @@ impl ForeignFrom for api_models::payments::PaymentAttem } } -impl ForeignFrom for api_models::payments::CaptureResponse { +impl ForeignFrom for payments::CaptureResponse { fn foreign_from(capture: storage::Capture) -> Self { Self { capture_id: capture.capture_id, @@ -1003,7 +1000,7 @@ impl ForeignFrom for api_enums::PaymentMethod { } } -impl ForeignTryFrom<&HeaderMap> for api_models::payments::HeaderPayload { +impl ForeignTryFrom<&HeaderMap> for payments::HeaderPayload { type Error = error_stack::Report; fn foreign_try_from(headers: &HeaderMap) -> Result { let payment_confirm_source: Option = @@ -1047,7 +1044,7 @@ impl Option<&domain::Address>, Option<&domain::Address>, Option<&domain::Customer>, - )> for api_models::payments::PaymentsRequest + )> for payments::PaymentsRequest { fn foreign_from( value: ( @@ -1072,17 +1069,11 @@ impl } } -impl - ForeignFrom<( - storage::PaymentLink, - api_models::payments::PaymentLinkStatus, - )> for api_models::payments::RetrievePaymentLinkResponse +impl ForeignFrom<(storage::PaymentLink, payments::PaymentLinkStatus)> + for payments::RetrievePaymentLinkResponse { fn foreign_from( - (payment_link_config, status): ( - storage::PaymentLink, - api_models::payments::PaymentLinkStatus, - ), + (payment_link_config, status): (storage::PaymentLink, payments::PaymentLinkStatus), ) -> Self { Self { payment_link_id: payment_link_config.payment_link_id, @@ -1171,7 +1162,7 @@ impl ForeignFrom for gsm_api_types::GsmResponse { } } -impl ForeignFrom<&domain::Customer> for api_models::payments::CustomerDetails { +impl ForeignFrom<&domain::Customer> for payments::CustomerDetails { fn foreign_from(customer: &domain::Customer) -> Self { Self { id: customer.customer_id.clone(), diff --git a/crates/router/src/utils.rs b/crates/router/src/utils.rs index 341c560e4d2..51854978b9e 100644 --- a/crates/router/src/utils.rs +++ b/crates/router/src/utils.rs @@ -99,7 +99,7 @@ pub mod error_parser { } pub fn custom_json_error_handler(err: JsonPayloadError, _req: &HttpRequest) -> Error { - actix_web::error::Error::from(CustomJsonError { err }) + Error::from(CustomJsonError { err }) } } @@ -565,14 +565,14 @@ pub fn add_connector_http_status_code_metrics(option_status_code: Option) { pub trait CustomerAddress { async fn get_address_update( &self, - address_details: api_models::payments::AddressDetails, + address_details: payments::AddressDetails, key: &[u8], storage_scheme: storage::enums::MerchantStorageScheme, ) -> CustomResult; async fn get_domain_address( &self, - address_details: api_models::payments::AddressDetails, + address_details: payments::AddressDetails, merchant_id: &str, customer_id: &str, key: &[u8], @@ -584,7 +584,7 @@ pub trait CustomerAddress { impl CustomerAddress for api_models::customers::CustomerRequest { async fn get_address_update( &self, - address_details: api_models::payments::AddressDetails, + address_details: payments::AddressDetails, key: &[u8], storage_scheme: storage::enums::MerchantStorageScheme, ) -> CustomResult { @@ -640,7 +640,7 @@ impl CustomerAddress for api_models::customers::CustomerRequest { async fn get_domain_address( &self, - address_details: api_models::payments::AddressDetails, + address_details: payments::AddressDetails, merchant_id: &str, customer_id: &str, key: &[u8], diff --git a/crates/router/src/utils/currency.rs b/crates/router/src/utils/currency.rs index 912f8fce94c..5cf68635c4e 100644 --- a/crates/router/src/utils/currency.rs +++ b/crates/router/src/utils/currency.rs @@ -525,10 +525,10 @@ pub async fn convert_currency( .await .change_context(ForexCacheError::ApiError)?; - let to_currency = api_models::enums::Currency::from_str(to_currency.as_str()) + let to_currency = enums::Currency::from_str(to_currency.as_str()) .change_context(ForexCacheError::CurrencyNotAcceptable)?; - let from_currency = api_models::enums::Currency::from_str(from_currency.as_str()) + let from_currency = enums::Currency::from_str(from_currency.as_str()) .change_context(ForexCacheError::CurrencyNotAcceptable)?; let converted_amount = diff --git a/crates/router/src/utils/user.rs b/crates/router/src/utils/user.rs index bde0fb72bd3..33e9aa6769c 100644 --- a/crates/router/src/utils/user.rs +++ b/crates/router/src/utils/user.rs @@ -11,7 +11,7 @@ use crate::{ routes::AppState, services::{ authentication::{AuthToken, UserFromToken}, - authorization::roles::{self, RoleInfo}, + authorization::roles::RoleInfo, }, types::domain::{self, MerchantAccount, UserFromStorage}, }; @@ -64,7 +64,7 @@ impl UserFromToken { } pub async fn get_role_info_from_db(&self, state: &AppState) -> UserResult { - roles::RoleInfo::from_role_id(state, &self.role_id, &self.merchant_id, &self.org_id) + RoleInfo::from_role_id(state, &self.role_id, &self.merchant_id, &self.org_id) .await .change_context(UserErrors::InternalServerError) } diff --git a/crates/router/tests/connectors/aci.rs b/crates/router/tests/connectors/aci.rs index 710d90a4ea3..2e5e8748f48 100644 --- a/crates/router/tests/connectors/aci.rs +++ b/crates/router/tests/connectors/aci.rs @@ -46,7 +46,7 @@ fn construct_payment_router_data() -> types::PaymentsAuthorizeRouterData { card_type: None, card_issuing_country: None, bank_code: None, - nick_name: Some(masking::Secret::new("nick_name".into())), + nick_name: Some(Secret::new("nick_name".into())), }), confirm: true, statement_descriptor_suffix: None, @@ -263,7 +263,7 @@ async fn payments_create_failure() { card_type: None, card_issuing_country: None, bank_code: None, - nick_name: Some(masking::Secret::new("nick_name".into())), + nick_name: Some(Secret::new("nick_name".into())), }); let response = services::api::execute_connector_processing_step( diff --git a/crates/router/tests/connectors/adyen.rs b/crates/router/tests/connectors/adyen.rs index 468834ca495..d19fff29003 100644 --- a/crates/router/tests/connectors/adyen.rs +++ b/crates/router/tests/connectors/adyen.rs @@ -151,7 +151,7 @@ impl AdyenTest { card_type: None, card_issuing_country: None, bank_code: None, - nick_name: Some(masking::Secret::new("nick_name".into())), + nick_name: Some(Secret::new("nick_name".into())), }), confirm: true, statement_descriptor_suffix: None, diff --git a/crates/router/tests/connectors/airwallex.rs b/crates/router/tests/connectors/airwallex.rs index 95b07162ac3..2ef61099ca7 100644 --- a/crates/router/tests/connectors/airwallex.rs +++ b/crates/router/tests/connectors/airwallex.rs @@ -70,7 +70,7 @@ fn get_default_payment_info() -> Option { } fn payment_method_details() -> Option { Some(types::PaymentsAuthorizeData { - payment_method_data: types::domain::PaymentMethodData::Card(domain::Card { + payment_method_data: domain::PaymentMethodData::Card(domain::Card { card_number: cards::CardNumber::from_str("4035501000000008").unwrap(), card_exp_month: Secret::new("02".to_string()), card_exp_year: Secret::new("2035".to_string()), @@ -80,7 +80,7 @@ fn payment_method_details() -> Option { card_type: None, card_issuing_country: None, bank_code: None, - nick_name: Some(masking::Secret::new("nick_name".into())), + nick_name: Some(Secret::new("nick_name".into())), }), capture_method: Some(diesel_models::enums::CaptureMethod::Manual), router_return_url: Some("https://google.com".to_string()), @@ -143,7 +143,7 @@ async fn should_sync_authorized_payment() { .psync_retry_till_status_matches( enums::AttemptStatus::Authorized, Some(types::PaymentsSyncData { - connector_transaction_id: router::types::ResponseId::ConnectorTransactionId( + connector_transaction_id: types::ResponseId::ConnectorTransactionId( txn_id.unwrap(), ), ..Default::default() @@ -272,7 +272,7 @@ async fn should_sync_auto_captured_payment() { .psync_retry_till_status_matches( enums::AttemptStatus::Charged, Some(types::PaymentsSyncData { - connector_transaction_id: router::types::ResponseId::ConnectorTransactionId( + connector_transaction_id: types::ResponseId::ConnectorTransactionId( txn_id.unwrap(), ), ..Default::default() @@ -370,7 +370,7 @@ async fn should_fail_payment_for_incorrect_card_number() { let response = CONNECTOR .make_payment( Some(types::PaymentsAuthorizeData { - payment_method_data: types::domain::PaymentMethodData::Card(domain::Card { + payment_method_data: domain::PaymentMethodData::Card(domain::Card { card_number: cards::CardNumber::from_str("1234567891011").unwrap(), ..utils::CCardType::default().0 }), @@ -393,7 +393,7 @@ async fn should_fail_payment_for_incorrect_cvc() { let response = CONNECTOR .make_payment( Some(types::PaymentsAuthorizeData { - payment_method_data: types::domain::PaymentMethodData::Card(domain::Card { + payment_method_data: domain::PaymentMethodData::Card(domain::Card { card_cvc: Secret::new("12345".to_string()), ..utils::CCardType::default().0 }), @@ -416,7 +416,7 @@ async fn should_fail_payment_for_invalid_exp_month() { let response = CONNECTOR .make_payment( Some(types::PaymentsAuthorizeData { - payment_method_data: types::domain::PaymentMethodData::Card(domain::Card { + payment_method_data: domain::PaymentMethodData::Card(domain::Card { card_exp_month: Secret::new("20".to_string()), ..utils::CCardType::default().0 }), @@ -439,7 +439,7 @@ async fn should_fail_payment_for_incorrect_expiry_year() { let response = CONNECTOR .make_payment( Some(types::PaymentsAuthorizeData { - payment_method_data: types::domain::PaymentMethodData::Card(domain::Card { + payment_method_data: domain::PaymentMethodData::Card(domain::Card { card_exp_year: Secret::new("2000".to_string()), ..utils::CCardType::default().0 }), diff --git a/crates/router/tests/connectors/authorizedotnet.rs b/crates/router/tests/connectors/authorizedotnet.rs index 2b9a79b0b7b..4c724f368d7 100644 --- a/crates/router/tests/connectors/authorizedotnet.rs +++ b/crates/router/tests/connectors/authorizedotnet.rs @@ -55,9 +55,7 @@ async fn should_only_authorize_payment() { .authorize_payment( Some(types::PaymentsAuthorizeData { amount: 300, - payment_method_data: types::domain::PaymentMethodData::Card( - get_payment_method_data(), - ), + payment_method_data: domain::PaymentMethodData::Card(get_payment_method_data()), capture_method: Some(diesel_models::enums::CaptureMethod::Manual), ..utils::PaymentAuthorizeType::default().0 }), @@ -72,7 +70,7 @@ async fn should_only_authorize_payment() { .psync_retry_till_status_matches( enums::AttemptStatus::Authorized, Some(types::PaymentsSyncData { - connector_transaction_id: router::types::ResponseId::ConnectorTransactionId(txn_id), + connector_transaction_id: types::ResponseId::ConnectorTransactionId(txn_id), encoded_data: None, capture_method: None, ..Default::default() @@ -92,9 +90,7 @@ async fn should_capture_authorized_payment() { .authorize_payment( Some(types::PaymentsAuthorizeData { amount: 301, - payment_method_data: types::domain::PaymentMethodData::Card( - get_payment_method_data(), - ), + payment_method_data: domain::PaymentMethodData::Card(get_payment_method_data()), capture_method: Some(diesel_models::enums::CaptureMethod::Manual), ..utils::PaymentAuthorizeType::default().0 }), @@ -109,9 +105,7 @@ async fn should_capture_authorized_payment() { .psync_retry_till_status_matches( enums::AttemptStatus::Authorized, Some(types::PaymentsSyncData { - connector_transaction_id: router::types::ResponseId::ConnectorTransactionId( - txn_id.clone(), - ), + connector_transaction_id: types::ResponseId::ConnectorTransactionId(txn_id.clone()), encoded_data: None, capture_method: None, ..Default::default() @@ -137,7 +131,7 @@ async fn should_capture_authorized_payment() { .psync_retry_till_status_matches( enums::AttemptStatus::CaptureInitiated, Some(types::PaymentsSyncData { - connector_transaction_id: router::types::ResponseId::ConnectorTransactionId(txn_id), + connector_transaction_id: types::ResponseId::ConnectorTransactionId(txn_id), encoded_data: None, capture_method: None, ..Default::default() @@ -156,9 +150,7 @@ async fn should_partially_capture_authorized_payment() { .authorize_payment( Some(types::PaymentsAuthorizeData { amount: 302, - payment_method_data: types::domain::PaymentMethodData::Card( - get_payment_method_data(), - ), + payment_method_data: domain::PaymentMethodData::Card(get_payment_method_data()), capture_method: Some(diesel_models::enums::CaptureMethod::Manual), ..utils::PaymentAuthorizeType::default().0 }), @@ -173,9 +165,7 @@ async fn should_partially_capture_authorized_payment() { .psync_retry_till_status_matches( enums::AttemptStatus::Authorized, Some(types::PaymentsSyncData { - connector_transaction_id: router::types::ResponseId::ConnectorTransactionId( - txn_id.clone(), - ), + connector_transaction_id: types::ResponseId::ConnectorTransactionId(txn_id.clone()), encoded_data: None, capture_method: None, ..Default::default() @@ -201,7 +191,7 @@ async fn should_partially_capture_authorized_payment() { .psync_retry_till_status_matches( enums::AttemptStatus::CaptureInitiated, Some(types::PaymentsSyncData { - connector_transaction_id: router::types::ResponseId::ConnectorTransactionId(txn_id), + connector_transaction_id: types::ResponseId::ConnectorTransactionId(txn_id), encoded_data: None, capture_method: None, ..Default::default() @@ -220,9 +210,7 @@ async fn should_sync_authorized_payment() { .authorize_payment( Some(types::PaymentsAuthorizeData { amount: 303, - payment_method_data: types::domain::PaymentMethodData::Card( - get_payment_method_data(), - ), + payment_method_data: domain::PaymentMethodData::Card(get_payment_method_data()), capture_method: Some(diesel_models::enums::CaptureMethod::Manual), ..utils::PaymentAuthorizeType::default().0 }), @@ -237,7 +225,7 @@ async fn should_sync_authorized_payment() { .psync_retry_till_status_matches( enums::AttemptStatus::Authorized, Some(types::PaymentsSyncData { - connector_transaction_id: router::types::ResponseId::ConnectorTransactionId(txn_id), + connector_transaction_id: types::ResponseId::ConnectorTransactionId(txn_id), encoded_data: None, capture_method: None, ..Default::default() @@ -256,9 +244,7 @@ async fn should_void_authorized_payment() { .authorize_payment( Some(types::PaymentsAuthorizeData { amount: 304, - payment_method_data: types::domain::PaymentMethodData::Card( - get_payment_method_data(), - ), + payment_method_data: domain::PaymentMethodData::Card(get_payment_method_data()), capture_method: Some(diesel_models::enums::CaptureMethod::Manual), ..utils::PaymentAuthorizeType::default().0 }), @@ -273,9 +259,7 @@ async fn should_void_authorized_payment() { .psync_retry_till_status_matches( enums::AttemptStatus::Authorized, Some(types::PaymentsSyncData { - connector_transaction_id: router::types::ResponseId::ConnectorTransactionId( - txn_id.clone(), - ), + connector_transaction_id: types::ResponseId::ConnectorTransactionId(txn_id.clone()), encoded_data: None, capture_method: None, ..Default::default() @@ -307,9 +291,7 @@ async fn should_make_payment() { .make_payment( Some(types::PaymentsAuthorizeData { amount: 310, - payment_method_data: types::domain::PaymentMethodData::Card( - get_payment_method_data(), - ), + payment_method_data: domain::PaymentMethodData::Card(get_payment_method_data()), capture_method: Some(diesel_models::enums::CaptureMethod::Manual), ..utils::PaymentAuthorizeType::default().0 }), @@ -323,9 +305,7 @@ async fn should_make_payment() { .psync_retry_till_status_matches( enums::AttemptStatus::CaptureInitiated, Some(types::PaymentsSyncData { - connector_transaction_id: router::types::ResponseId::ConnectorTransactionId( - txn_id.clone(), - ), + connector_transaction_id: types::ResponseId::ConnectorTransactionId(txn_id.clone()), encoded_data: None, capture_method: None, ..Default::default() @@ -347,9 +327,7 @@ async fn should_sync_auto_captured_payment() { .make_payment( Some(types::PaymentsAuthorizeData { amount: 311, - payment_method_data: types::domain::PaymentMethodData::Card( - get_payment_method_data(), - ), + payment_method_data: domain::PaymentMethodData::Card(get_payment_method_data()), capture_method: Some(diesel_models::enums::CaptureMethod::Manual), ..utils::PaymentAuthorizeType::default().0 }), @@ -364,7 +342,7 @@ async fn should_sync_auto_captured_payment() { .psync_retry_till_status_matches( enums::AttemptStatus::Pending, Some(types::PaymentsSyncData { - connector_transaction_id: router::types::ResponseId::ConnectorTransactionId( + connector_transaction_id: types::ResponseId::ConnectorTransactionId( txn_id.unwrap(), ), encoded_data: None, @@ -402,7 +380,7 @@ async fn should_fail_payment_for_empty_card_number() { let response = CONNECTOR .make_payment( Some(types::PaymentsAuthorizeData { - payment_method_data: types::domain::PaymentMethodData::Card(domain::Card { + payment_method_data: domain::PaymentMethodData::Card(domain::Card { card_number: cards::CardNumber::from_str("").unwrap(), ..utils::CCardType::default().0 }), @@ -425,7 +403,7 @@ async fn should_fail_payment_for_incorrect_cvc() { let response = CONNECTOR .make_payment( Some(types::PaymentsAuthorizeData { - payment_method_data: types::domain::PaymentMethodData::Card(domain::Card { + payment_method_data: domain::PaymentMethodData::Card(domain::Card { card_cvc: Secret::new("12345".to_string()), ..utils::CCardType::default().0 }), @@ -448,7 +426,7 @@ async fn should_fail_payment_for_invalid_exp_month() { let response = CONNECTOR .make_payment( Some(types::PaymentsAuthorizeData { - payment_method_data: types::domain::PaymentMethodData::Card(domain::Card { + payment_method_data: domain::PaymentMethodData::Card(domain::Card { card_exp_month: Secret::new("20".to_string()), ..utils::CCardType::default().0 }), @@ -470,7 +448,7 @@ async fn should_fail_payment_for_incorrect_expiry_year() { let response = CONNECTOR .make_payment( Some(types::PaymentsAuthorizeData { - payment_method_data: types::domain::PaymentMethodData::Card(domain::Card { + payment_method_data: domain::PaymentMethodData::Card(domain::Card { card_exp_year: Secret::new("2000".to_string()), ..utils::CCardType::default().0 }), @@ -493,9 +471,7 @@ async fn should_fail_void_payment_for_auto_capture() { .make_payment( Some(types::PaymentsAuthorizeData { amount: 307, - payment_method_data: types::domain::PaymentMethodData::Card( - get_payment_method_data(), - ), + payment_method_data: domain::PaymentMethodData::Card(get_payment_method_data()), capture_method: Some(diesel_models::enums::CaptureMethod::Manual), ..utils::PaymentAuthorizeType::default().0 }), diff --git a/crates/router/tests/connectors/bambora.rs b/crates/router/tests/connectors/bambora.rs index 5cd82d097aa..3115c1143d7 100644 --- a/crates/router/tests/connectors/bambora.rs +++ b/crates/router/tests/connectors/bambora.rs @@ -40,7 +40,7 @@ static CONNECTOR: BamboraTest = BamboraTest {}; fn get_default_payment_authorize_data() -> Option { Some(types::PaymentsAuthorizeData { - payment_method_data: types::domain::PaymentMethodData::Card(domain::Card { + payment_method_data: domain::PaymentMethodData::Card(domain::Card { card_number: cards::CardNumber::from_str("4030000010001234").unwrap(), card_exp_year: Secret::new("25".to_string()), card_cvc: Secret::new("123".to_string()), @@ -101,7 +101,7 @@ async fn should_sync_authorized_payment() { enums::AttemptStatus::Authorized, Some(types::PaymentsSyncData { mandate_id: None, - connector_transaction_id: router::types::ResponseId::ConnectorTransactionId( + connector_transaction_id: types::ResponseId::ConnectorTransactionId( txn_id.unwrap(), ), encoded_data: None, @@ -217,7 +217,7 @@ async fn should_sync_auto_captured_payment() { enums::AttemptStatus::Charged, Some(types::PaymentsSyncData { mandate_id: None, - connector_transaction_id: router::types::ResponseId::ConnectorTransactionId( + connector_transaction_id: types::ResponseId::ConnectorTransactionId( txn_id.unwrap(), ), encoded_data: None, @@ -296,7 +296,7 @@ async fn should_fail_payment_for_incorrect_card_number() { let response = CONNECTOR .make_payment( Some(types::PaymentsAuthorizeData { - payment_method_data: types::domain::PaymentMethodData::Card(domain::Card { + payment_method_data: domain::PaymentMethodData::Card(domain::Card { card_number: cards::CardNumber::from_str("1234567891011").unwrap(), card_exp_year: Secret::new("25".to_string()), ..utils::CCardType::default().0 @@ -319,7 +319,7 @@ async fn should_fail_payment_for_incorrect_cvc() { let response = CONNECTOR .make_payment( Some(types::PaymentsAuthorizeData { - payment_method_data: types::domain::PaymentMethodData::Card(domain::Card { + payment_method_data: domain::PaymentMethodData::Card(domain::Card { card_exp_year: Secret::new("25".to_string()), card_cvc: Secret::new("12345".to_string()), ..utils::CCardType::default().0 @@ -342,7 +342,7 @@ async fn should_fail_payment_for_invalid_exp_month() { let response = CONNECTOR .make_payment( Some(types::PaymentsAuthorizeData { - payment_method_data: types::domain::PaymentMethodData::Card(domain::Card { + payment_method_data: domain::PaymentMethodData::Card(domain::Card { card_exp_month: Secret::new("20".to_string()), card_number: cards::CardNumber::from_str("4030000010001234").unwrap(), card_exp_year: Secret::new("25".to_string()), @@ -367,7 +367,7 @@ async fn should_fail_payment_for_incorrect_expiry_year() { let response = CONNECTOR .make_payment( Some(types::PaymentsAuthorizeData { - payment_method_data: types::domain::PaymentMethodData::Card(domain::Card { + payment_method_data: domain::PaymentMethodData::Card(domain::Card { card_exp_year: Secret::new("2000".to_string()), card_number: cards::CardNumber::from_str("4030000010001234").unwrap(), card_cvc: Secret::new("123".to_string()), diff --git a/crates/router/tests/connectors/bankofamerica.rs b/crates/router/tests/connectors/bankofamerica.rs index ca54e9c97b9..77085216dd5 100644 --- a/crates/router/tests/connectors/bankofamerica.rs +++ b/crates/router/tests/connectors/bankofamerica.rs @@ -93,7 +93,7 @@ async fn should_sync_authorized_payment() { .psync_retry_till_status_matches( enums::AttemptStatus::Authorized, Some(types::PaymentsSyncData { - connector_transaction_id: router::types::ResponseId::ConnectorTransactionId( + connector_transaction_id: types::ResponseId::ConnectorTransactionId( txn_id.unwrap(), ), ..Default::default() @@ -213,7 +213,7 @@ async fn should_sync_auto_captured_payment() { .psync_retry_till_status_matches( enums::AttemptStatus::Charged, Some(types::PaymentsSyncData { - connector_transaction_id: router::types::ResponseId::ConnectorTransactionId( + connector_transaction_id: types::ResponseId::ConnectorTransactionId( txn_id.unwrap(), ), capture_method: Some(enums::CaptureMethod::Automatic), @@ -303,7 +303,7 @@ async fn should_fail_payment_for_incorrect_cvc() { let response = CONNECTOR .make_payment( Some(types::PaymentsAuthorizeData { - payment_method_data: types::domain::PaymentMethodData::Card(domain::Card { + payment_method_data: domain::PaymentMethodData::Card(domain::Card { card_cvc: Secret::new("12345".to_string()), ..utils::CCardType::default().0 }), @@ -325,7 +325,7 @@ async fn should_fail_payment_for_invalid_exp_month() { let response = CONNECTOR .make_payment( Some(types::PaymentsAuthorizeData { - payment_method_data: types::domain::PaymentMethodData::Card(domain::Card { + payment_method_data: domain::PaymentMethodData::Card(domain::Card { card_exp_month: Secret::new("20".to_string()), ..utils::CCardType::default().0 }), @@ -347,7 +347,7 @@ async fn should_fail_payment_for_incorrect_expiry_year() { let response = CONNECTOR .make_payment( Some(types::PaymentsAuthorizeData { - payment_method_data: types::domain::PaymentMethodData::Card(domain::Card { + payment_method_data: domain::PaymentMethodData::Card(domain::Card { card_exp_year: Secret::new("2000".to_string()), ..utils::CCardType::default().0 }), diff --git a/crates/router/tests/connectors/billwerk.rs b/crates/router/tests/connectors/billwerk.rs index 6795aa40115..4c992ef3d92 100644 --- a/crates/router/tests/connectors/billwerk.rs +++ b/crates/router/tests/connectors/billwerk.rs @@ -93,7 +93,7 @@ async fn should_sync_authorized_payment() { .psync_retry_till_status_matches( enums::AttemptStatus::Authorized, Some(types::PaymentsSyncData { - connector_transaction_id: router::types::ResponseId::ConnectorTransactionId( + connector_transaction_id: types::ResponseId::ConnectorTransactionId( txn_id.unwrap(), ), ..Default::default() @@ -213,7 +213,7 @@ async fn should_sync_auto_captured_payment() { .psync_retry_till_status_matches( enums::AttemptStatus::Charged, Some(types::PaymentsSyncData { - connector_transaction_id: router::types::ResponseId::ConnectorTransactionId( + connector_transaction_id: types::ResponseId::ConnectorTransactionId( txn_id.unwrap(), ), capture_method: Some(enums::CaptureMethod::Automatic), diff --git a/crates/router/tests/connectors/bitpay.rs b/crates/router/tests/connectors/bitpay.rs index df82775d6cf..85b388f2fdb 100644 --- a/crates/router/tests/connectors/bitpay.rs +++ b/crates/router/tests/connectors/bitpay.rs @@ -10,12 +10,12 @@ use crate::{ struct BitpayTest; impl ConnectorActions for BitpayTest {} impl utils::Connector for BitpayTest { - fn get_data(&self) -> types::api::ConnectorData { + fn get_data(&self) -> api::ConnectorData { use router::connector::Bitpay; - types::api::ConnectorData { + api::ConnectorData { connector: Box::new(&Bitpay), connector_name: types::Connector::Bitpay, - get_token: types::api::GetToken::Connector, + get_token: api::GetToken::Connector, merchant_connector_id: None, } } @@ -67,7 +67,7 @@ fn payment_method_details() -> Option { Some(types::PaymentsAuthorizeData { amount: 1, currency: enums::Currency::USD, - payment_method_data: types::domain::PaymentMethodData::Crypto(domain::CryptoData { + payment_method_data: domain::PaymentMethodData::Crypto(domain::CryptoData { pay_currency: None, }), confirm: true, @@ -126,7 +126,7 @@ async fn should_sync_authorized_payment() { .psync_retry_till_status_matches( enums::AttemptStatus::Authorized, Some(types::PaymentsSyncData { - connector_transaction_id: router::types::ResponseId::ConnectorTransactionId( + connector_transaction_id: types::ResponseId::ConnectorTransactionId( "NPf27TDfyU5mhcTCw2oaq4".to_string(), ), ..Default::default() @@ -145,7 +145,7 @@ async fn should_sync_expired_payment() { .psync_retry_till_status_matches( enums::AttemptStatus::Authorized, Some(types::PaymentsSyncData { - connector_transaction_id: router::types::ResponseId::ConnectorTransactionId( + connector_transaction_id: types::ResponseId::ConnectorTransactionId( "bUsFf4RjQEahjbjGcETRS".to_string(), ), ..Default::default() diff --git a/crates/router/tests/connectors/bluesnap.rs b/crates/router/tests/connectors/bluesnap.rs index 2e02f259341..3a654eda839 100644 --- a/crates/router/tests/connectors/bluesnap.rs +++ b/crates/router/tests/connectors/bluesnap.rs @@ -121,7 +121,7 @@ async fn should_sync_authorized_payment() { .psync_retry_till_status_matches( enums::AttemptStatus::Authorized, Some(types::PaymentsSyncData { - connector_transaction_id: router::types::ResponseId::ConnectorTransactionId( + connector_transaction_id: types::ResponseId::ConnectorTransactionId( txn_id.unwrap(), ), ..Default::default() @@ -261,7 +261,7 @@ async fn should_sync_auto_captured_payment() { .psync_retry_till_status_matches( enums::AttemptStatus::Charged, Some(types::PaymentsSyncData { - connector_transaction_id: router::types::ResponseId::ConnectorTransactionId( + connector_transaction_id: types::ResponseId::ConnectorTransactionId( txn_id.unwrap(), ), ..Default::default() @@ -401,7 +401,7 @@ async fn should_fail_payment_for_incorrect_cvc() { .make_payment( Some(types::PaymentsAuthorizeData { email: Some(Email::from_str("test@gmail.com").unwrap()), - payment_method_data: types::domain::PaymentMethodData::Card(domain::Card { + payment_method_data: domain::PaymentMethodData::Card(domain::Card { card_cvc: Secret::new("12345".to_string()), ..utils::CCardType::default().0 }), @@ -426,7 +426,7 @@ async fn should_fail_payment_for_invalid_exp_month() { .make_payment( Some(types::PaymentsAuthorizeData { email: Some(Email::from_str("test@gmail.com").unwrap()), - payment_method_data: types::domain::PaymentMethodData::Card(domain::Card { + payment_method_data: domain::PaymentMethodData::Card(domain::Card { card_exp_month: Secret::new("20".to_string()), ..utils::CCardType::default().0 }), @@ -451,7 +451,7 @@ async fn should_fail_payment_for_incorrect_expiry_year() { .make_payment( Some(types::PaymentsAuthorizeData { email: Some(Email::from_str("test@gmail.com").unwrap()), - payment_method_data: types::domain::PaymentMethodData::Card(domain::Card { + payment_method_data: domain::PaymentMethodData::Card(domain::Card { card_exp_year: Secret::new("2000".to_string()), ..utils::CCardType::default().0 }), diff --git a/crates/router/tests/connectors/boku.rs b/crates/router/tests/connectors/boku.rs index 2f496562e1d..f361a2365d5 100644 --- a/crates/router/tests/connectors/boku.rs +++ b/crates/router/tests/connectors/boku.rs @@ -92,7 +92,7 @@ async fn should_sync_authorized_payment() { .psync_retry_till_status_matches( enums::AttemptStatus::Authorized, Some(types::PaymentsSyncData { - connector_transaction_id: router::types::ResponseId::ConnectorTransactionId( + connector_transaction_id: types::ResponseId::ConnectorTransactionId( txn_id.unwrap(), ), ..Default::default() @@ -212,7 +212,7 @@ async fn should_sync_auto_captured_payment() { .psync_retry_till_status_matches( enums::AttemptStatus::Charged, Some(types::PaymentsSyncData { - connector_transaction_id: router::types::ResponseId::ConnectorTransactionId( + connector_transaction_id: types::ResponseId::ConnectorTransactionId( txn_id.unwrap(), ), capture_method: Some(enums::CaptureMethod::Automatic), @@ -302,7 +302,7 @@ async fn should_fail_payment_for_incorrect_cvc() { let response = CONNECTOR .make_payment( Some(types::PaymentsAuthorizeData { - payment_method_data: types::domain::PaymentMethodData::Card(domain::Card { + payment_method_data: domain::PaymentMethodData::Card(domain::Card { card_cvc: Secret::new("12345".to_string()), ..utils::CCardType::default().0 }), @@ -324,7 +324,7 @@ async fn should_fail_payment_for_invalid_exp_month() { let response = CONNECTOR .make_payment( Some(types::PaymentsAuthorizeData { - payment_method_data: types::domain::PaymentMethodData::Card(domain::Card { + payment_method_data: domain::PaymentMethodData::Card(domain::Card { card_exp_month: Secret::new("20".to_string()), ..utils::CCardType::default().0 }), @@ -346,7 +346,7 @@ async fn should_fail_payment_for_incorrect_expiry_year() { let response = CONNECTOR .make_payment( Some(types::PaymentsAuthorizeData { - payment_method_data: types::domain::PaymentMethodData::Card(domain::Card { + payment_method_data: domain::PaymentMethodData::Card(domain::Card { card_exp_year: Secret::new("2000".to_string()), ..utils::CCardType::default().0 }), diff --git a/crates/router/tests/connectors/cashtocode.rs b/crates/router/tests/connectors/cashtocode.rs index 616a080699b..025d9cdac64 100644 --- a/crates/router/tests/connectors/cashtocode.rs +++ b/crates/router/tests/connectors/cashtocode.rs @@ -39,7 +39,7 @@ static CONNECTOR: CashtocodeTest = CashtocodeTest {}; impl CashtocodeTest { fn get_payment_authorize_data( payment_method_type: Option, - payment_method_data: types::domain::PaymentMethodData, + payment_method_data: domain::PaymentMethodData, ) -> Option { Some(types::PaymentsAuthorizeData { amount: 1000, diff --git a/crates/router/tests/connectors/checkout.rs b/crates/router/tests/connectors/checkout.rs index 85b55afc8ea..886239878f2 100644 --- a/crates/router/tests/connectors/checkout.rs +++ b/crates/router/tests/connectors/checkout.rs @@ -97,7 +97,7 @@ async fn should_sync_authorized_payment() { .psync_retry_till_status_matches( enums::AttemptStatus::Authorized, Some(types::PaymentsSyncData { - connector_transaction_id: router::types::ResponseId::ConnectorTransactionId( + connector_transaction_id: types::ResponseId::ConnectorTransactionId( txn_id.unwrap(), ), ..Default::default() @@ -223,7 +223,7 @@ async fn should_sync_auto_captured_payment() { .psync_retry_till_status_matches( enums::AttemptStatus::Charged, Some(types::PaymentsSyncData { - connector_transaction_id: router::types::ResponseId::ConnectorTransactionId( + connector_transaction_id: types::ResponseId::ConnectorTransactionId( txn_id.unwrap(), ), capture_method: Some(enums::CaptureMethod::Automatic), @@ -320,7 +320,7 @@ async fn should_fail_payment_for_incorrect_cvc() { let response = CONNECTOR .make_payment( Some(types::PaymentsAuthorizeData { - payment_method_data: types::domain::PaymentMethodData::Card(domain::Card { + payment_method_data: domain::PaymentMethodData::Card(domain::Card { card_cvc: Secret::new("12345".to_string()), ..utils::CCardType::default().0 }), @@ -343,7 +343,7 @@ async fn should_fail_payment_for_invalid_exp_month() { let response = CONNECTOR .make_payment( Some(types::PaymentsAuthorizeData { - payment_method_data: types::domain::PaymentMethodData::Card(domain::Card { + payment_method_data: domain::PaymentMethodData::Card(domain::Card { card_exp_month: Secret::new("20".to_string()), ..utils::CCardType::default().0 }), @@ -366,7 +366,7 @@ async fn should_fail_payment_for_incorrect_expiry_year() { let response = CONNECTOR .make_payment( Some(types::PaymentsAuthorizeData { - payment_method_data: types::domain::PaymentMethodData::Card(domain::Card { + payment_method_data: domain::PaymentMethodData::Card(domain::Card { card_exp_year: Secret::new("2000".to_string()), ..utils::CCardType::default().0 }), diff --git a/crates/router/tests/connectors/coinbase.rs b/crates/router/tests/connectors/coinbase.rs index 0136284c826..0bcb3c705ab 100644 --- a/crates/router/tests/connectors/coinbase.rs +++ b/crates/router/tests/connectors/coinbase.rs @@ -11,12 +11,12 @@ use crate::{ struct CoinbaseTest; impl ConnectorActions for CoinbaseTest {} impl utils::Connector for CoinbaseTest { - fn get_data(&self) -> types::api::ConnectorData { + fn get_data(&self) -> api::ConnectorData { use router::connector::Coinbase; - types::api::ConnectorData { + api::ConnectorData { connector: Box::new(&Coinbase), connector_name: types::Connector::Coinbase, - get_token: types::api::GetToken::Connector, + get_token: api::GetToken::Connector, merchant_connector_id: None, } } @@ -69,7 +69,7 @@ fn payment_method_details() -> Option { Some(types::PaymentsAuthorizeData { amount: 1, currency: enums::Currency::USD, - payment_method_data: types::domain::PaymentMethodData::Crypto(domain::CryptoData { + payment_method_data: domain::PaymentMethodData::Crypto(domain::CryptoData { pay_currency: None, }), confirm: true, @@ -128,7 +128,7 @@ async fn should_sync_authorized_payment() { .psync_retry_till_status_matches( enums::AttemptStatus::Authorized, Some(types::PaymentsSyncData { - connector_transaction_id: router::types::ResponseId::ConnectorTransactionId( + connector_transaction_id: types::ResponseId::ConnectorTransactionId( "ADFY3789".to_string(), ), ..Default::default() @@ -147,7 +147,7 @@ async fn should_sync_unresolved_payment() { .psync_retry_till_status_matches( enums::AttemptStatus::Authorized, Some(types::PaymentsSyncData { - connector_transaction_id: router::types::ResponseId::ConnectorTransactionId( + connector_transaction_id: types::ResponseId::ConnectorTransactionId( "YJ6RFZXZ".to_string(), ), ..Default::default() @@ -166,7 +166,7 @@ async fn should_sync_expired_payment() { .psync_retry_till_status_matches( enums::AttemptStatus::Authorized, Some(types::PaymentsSyncData { - connector_transaction_id: router::types::ResponseId::ConnectorTransactionId( + connector_transaction_id: types::ResponseId::ConnectorTransactionId( "FZ89KDDB".to_string(), ), ..Default::default() @@ -185,7 +185,7 @@ async fn should_sync_cancelled_payment() { .psync_retry_till_status_matches( enums::AttemptStatus::Authorized, Some(types::PaymentsSyncData { - connector_transaction_id: router::types::ResponseId::ConnectorTransactionId( + connector_transaction_id: types::ResponseId::ConnectorTransactionId( "C35AAXKF".to_string(), ), ..Default::default() diff --git a/crates/router/tests/connectors/cryptopay.rs b/crates/router/tests/connectors/cryptopay.rs index 56c0e29f621..626c05fe114 100644 --- a/crates/router/tests/connectors/cryptopay.rs +++ b/crates/router/tests/connectors/cryptopay.rs @@ -10,12 +10,12 @@ use crate::{ struct CryptopayTest; impl ConnectorActions for CryptopayTest {} impl utils::Connector for CryptopayTest { - fn get_data(&self) -> types::api::ConnectorData { + fn get_data(&self) -> api::ConnectorData { use router::connector::Cryptopay; - types::api::ConnectorData { + api::ConnectorData { connector: Box::new(&Cryptopay), connector_name: types::Connector::Cryptopay, - get_token: types::api::GetToken::Connector, + get_token: api::GetToken::Connector, merchant_connector_id: None, } } @@ -68,7 +68,7 @@ fn payment_method_details() -> Option { Some(types::PaymentsAuthorizeData { amount: 1, currency: enums::Currency::USD, - payment_method_data: types::domain::PaymentMethodData::Crypto(domain::CryptoData { + payment_method_data: domain::PaymentMethodData::Crypto(domain::CryptoData { pay_currency: Some("XRP".to_string()), }), confirm: true, @@ -126,7 +126,7 @@ async fn should_sync_authorized_payment() { .psync_retry_till_status_matches( enums::AttemptStatus::Authorized, Some(types::PaymentsSyncData { - connector_transaction_id: router::types::ResponseId::ConnectorTransactionId( + connector_transaction_id: types::ResponseId::ConnectorTransactionId( "ea684036-2b54-44fa-bffe-8256650dce7c".to_string(), ), ..Default::default() @@ -145,7 +145,7 @@ async fn should_sync_unresolved_payment() { .psync_retry_till_status_matches( enums::AttemptStatus::Authorized, Some(types::PaymentsSyncData { - connector_transaction_id: router::types::ResponseId::ConnectorTransactionId( + connector_transaction_id: types::ResponseId::ConnectorTransactionId( "7993d4c2-efbc-4360-b8ce-d1e957e6f827".to_string(), ), ..Default::default() diff --git a/crates/router/tests/connectors/cybersource.rs b/crates/router/tests/connectors/cybersource.rs index 98ec44abd8a..700e9a2d946 100644 --- a/crates/router/tests/connectors/cybersource.rs +++ b/crates/router/tests/connectors/cybersource.rs @@ -2,10 +2,7 @@ use std::str::FromStr; use common_utils::pii::Email; use masking::Secret; -use router::types::{ - self, api, domain, - storage::{self, enums}, -}; +use router::types::{self, api, domain, storage::enums}; use crate::{ connector_auth, @@ -14,12 +11,12 @@ use crate::{ struct Cybersource; impl ConnectorActions for Cybersource {} impl utils::Connector for Cybersource { - fn get_data(&self) -> types::api::ConnectorData { + fn get_data(&self) -> api::ConnectorData { use router::connector::Cybersource; - types::api::ConnectorData { + api::ConnectorData { connector: Box::new(&Cybersource), connector_name: types::Connector::Cybersource, - get_token: types::api::GetToken::Connector, + get_token: api::GetToken::Connector, merchant_connector_id: None, } } @@ -64,7 +61,7 @@ fn get_default_payment_info() -> Option { } fn get_default_payment_authorize_data() -> Option { Some(types::PaymentsAuthorizeData { - currency: storage::enums::Currency::USD, + currency: enums::Currency::USD, email: Some(Email::from_str("abc@gmail.com").unwrap()), ..PaymentAuthorizeType::default().0 }) @@ -127,7 +124,7 @@ async fn should_sync_payment() { .psync_retry_till_status_matches( enums::AttemptStatus::Charged, Some(types::PaymentsSyncData { - connector_transaction_id: router::types::ResponseId::ConnectorTransactionId( + connector_transaction_id: types::ResponseId::ConnectorTransactionId( "6699597903496176903954".to_string(), ), ..Default::default() @@ -160,7 +157,7 @@ async fn should_fail_payment_for_invalid_exp_month() { let response = Cybersource {} .make_payment( Some(types::PaymentsAuthorizeData { - payment_method_data: types::domain::PaymentMethodData::Card(domain::Card { + payment_method_data: domain::PaymentMethodData::Card(domain::Card { card_exp_month: Secret::new("13".to_string()), ..utils::CCardType::default().0 }), @@ -185,7 +182,7 @@ async fn should_fail_payment_for_invalid_exp_year() { let response = Cybersource {} .make_payment( Some(types::PaymentsAuthorizeData { - payment_method_data: types::domain::PaymentMethodData::Card(domain::Card { + payment_method_data: domain::PaymentMethodData::Card(domain::Card { card_exp_year: Secret::new("2022".to_string()), ..utils::CCardType::default().0 }), @@ -203,7 +200,7 @@ async fn should_fail_payment_for_invalid_card_cvc() { let response = Cybersource {} .make_payment( Some(types::PaymentsAuthorizeData { - payment_method_data: types::domain::PaymentMethodData::Card(domain::Card { + payment_method_data: domain::PaymentMethodData::Card(domain::Card { card_cvc: Secret::new("2131233213".to_string()), ..utils::CCardType::default().0 }), diff --git a/crates/router/tests/connectors/dlocal.rs b/crates/router/tests/connectors/dlocal.rs index edb24646bcb..b715e20fec4 100644 --- a/crates/router/tests/connectors/dlocal.rs +++ b/crates/router/tests/connectors/dlocal.rs @@ -13,12 +13,12 @@ use crate::{ struct DlocalTest; impl ConnectorActions for DlocalTest {} impl utils::Connector for DlocalTest { - fn get_data(&self) -> types::api::ConnectorData { + fn get_data(&self) -> api::ConnectorData { use router::connector::Dlocal; - types::api::ConnectorData { + api::ConnectorData { connector: Box::new(&Dlocal), connector_name: types::Connector::Dlocal, - get_token: types::api::GetToken::Connector, + get_token: api::GetToken::Connector, merchant_connector_id: None, } } @@ -89,7 +89,7 @@ async fn should_sync_authorized_payment() { .psync_retry_till_status_matches( enums::AttemptStatus::Authorized, Some(types::PaymentsSyncData { - connector_transaction_id: router::types::ResponseId::ConnectorTransactionId( + connector_transaction_id: types::ResponseId::ConnectorTransactionId( txn_id.unwrap(), ), ..Default::default() @@ -200,7 +200,7 @@ async fn should_sync_auto_captured_payment() { .psync_retry_till_status_matches( enums::AttemptStatus::Charged, Some(types::PaymentsSyncData { - connector_transaction_id: router::types::ResponseId::ConnectorTransactionId( + connector_transaction_id: types::ResponseId::ConnectorTransactionId( txn_id.unwrap(), ), ..Default::default() @@ -289,7 +289,7 @@ async fn should_fail_payment_for_incorrect_card_number() { let response = CONNECTOR .make_payment( Some(types::PaymentsAuthorizeData { - payment_method_data: types::domain::PaymentMethodData::Card(domain::Card { + payment_method_data: domain::PaymentMethodData::Card(domain::Card { card_number: cards::CardNumber::from_str("1891011").unwrap(), ..utils::CCardType::default().0 }), @@ -310,7 +310,7 @@ async fn should_fail_payment_for_incorrect_cvc() { let response = CONNECTOR .make_payment( Some(types::PaymentsAuthorizeData { - payment_method_data: types::domain::PaymentMethodData::Card(domain::Card { + payment_method_data: domain::PaymentMethodData::Card(domain::Card { card_cvc: Secret::new("1ad2345".to_string()), ..utils::CCardType::default().0 }), @@ -331,7 +331,7 @@ async fn should_fail_payment_for_invalid_exp_month() { let response = CONNECTOR .make_payment( Some(types::PaymentsAuthorizeData { - payment_method_data: types::domain::PaymentMethodData::Card(domain::Card { + payment_method_data: domain::PaymentMethodData::Card(domain::Card { card_exp_month: Secret::new("201".to_string()), ..utils::CCardType::default().0 }), @@ -352,7 +352,7 @@ async fn should_fail_payment_for_incorrect_expiry_year() { let response = CONNECTOR .make_payment( Some(types::PaymentsAuthorizeData { - payment_method_data: types::domain::PaymentMethodData::Card(domain::Card { + payment_method_data: domain::PaymentMethodData::Card(domain::Card { card_exp_year: Secret::new("20001".to_string()), ..utils::CCardType::default().0 }), diff --git a/crates/router/tests/connectors/dummyconnector.rs b/crates/router/tests/connectors/dummyconnector.rs index 76f72b64907..e357eda94c4 100644 --- a/crates/router/tests/connectors/dummyconnector.rs +++ b/crates/router/tests/connectors/dummyconnector.rs @@ -95,7 +95,7 @@ async fn should_sync_authorized_payment() { .psync_retry_till_status_matches( enums::AttemptStatus::Authorized, Some(types::PaymentsSyncData { - connector_transaction_id: router::types::ResponseId::ConnectorTransactionId( + connector_transaction_id: types::ResponseId::ConnectorTransactionId( txn_id.unwrap(), ), ..Default::default() @@ -215,7 +215,7 @@ async fn should_sync_auto_captured_payment() { .psync_retry_till_status_matches( enums::AttemptStatus::Charged, Some(types::PaymentsSyncData { - connector_transaction_id: router::types::ResponseId::ConnectorTransactionId( + connector_transaction_id: types::ResponseId::ConnectorTransactionId( txn_id.unwrap(), ), capture_method: Some(enums::CaptureMethod::Automatic), @@ -305,7 +305,7 @@ async fn should_fail_payment_for_incorrect_card_number() { let response = CONNECTOR .make_payment( Some(types::PaymentsAuthorizeData { - payment_method_data: types::domain::PaymentMethodData::Card(domain::Card { + payment_method_data: domain::PaymentMethodData::Card(domain::Card { card_number: CardNumber::from_str("1234567891011").unwrap(), ..utils::CCardType::default().0 }), @@ -327,7 +327,7 @@ async fn should_fail_payment_for_incorrect_cvc() { let response = CONNECTOR .make_payment( Some(types::PaymentsAuthorizeData { - payment_method_data: types::domain::PaymentMethodData::Card(domain::Card { + payment_method_data: domain::PaymentMethodData::Card(domain::Card { card_cvc: Secret::new("12345".to_string()), ..utils::CCardType::default().0 }), @@ -349,7 +349,7 @@ async fn should_fail_payment_for_invalid_exp_month() { let response = CONNECTOR .make_payment( Some(types::PaymentsAuthorizeData { - payment_method_data: types::domain::PaymentMethodData::Card(domain::Card { + payment_method_data: domain::PaymentMethodData::Card(domain::Card { card_exp_month: Secret::new("20".to_string()), ..utils::CCardType::default().0 }), @@ -371,7 +371,7 @@ async fn should_fail_payment_for_incorrect_expiry_year() { let response = CONNECTOR .make_payment( Some(types::PaymentsAuthorizeData { - payment_method_data: types::domain::PaymentMethodData::Card(domain::Card { + payment_method_data: domain::PaymentMethodData::Card(domain::Card { card_exp_year: Secret::new("2000".to_string()), ..utils::CCardType::default().0 }), diff --git a/crates/router/tests/connectors/ebanx.rs b/crates/router/tests/connectors/ebanx.rs index 8571ed1e3f8..6f02c80ebde 100644 --- a/crates/router/tests/connectors/ebanx.rs +++ b/crates/router/tests/connectors/ebanx.rs @@ -92,7 +92,7 @@ async fn should_sync_authorized_payment() { .psync_retry_till_status_matches( enums::AttemptStatus::Authorized, Some(types::PaymentsSyncData { - connector_transaction_id: router::types::ResponseId::ConnectorTransactionId( + connector_transaction_id: types::ResponseId::ConnectorTransactionId( txn_id.unwrap(), ), ..Default::default() @@ -212,7 +212,7 @@ async fn should_sync_auto_captured_payment() { .psync_retry_till_status_matches( enums::AttemptStatus::Charged, Some(types::PaymentsSyncData { - connector_transaction_id: router::types::ResponseId::ConnectorTransactionId( + connector_transaction_id: types::ResponseId::ConnectorTransactionId( txn_id.unwrap(), ), capture_method: Some(enums::CaptureMethod::Automatic), diff --git a/crates/router/tests/connectors/fiserv.rs b/crates/router/tests/connectors/fiserv.rs index 050d6a54067..f95c0d276ec 100644 --- a/crates/router/tests/connectors/fiserv.rs +++ b/crates/router/tests/connectors/fiserv.rs @@ -42,7 +42,7 @@ impl utils::Connector for FiservTest { fn payment_method_details() -> Option { Some(types::PaymentsAuthorizeData { - payment_method_data: types::domain::PaymentMethodData::Card(domain::Card { + payment_method_data: domain::PaymentMethodData::Card(domain::Card { card_number: cards::CardNumber::from_str("4005550000000019").unwrap(), card_exp_month: Secret::new("02".to_string()), card_exp_year: Secret::new("2035".to_string()), @@ -52,7 +52,7 @@ fn payment_method_details() -> Option { card_type: None, card_issuing_country: None, bank_code: None, - nick_name: Some(masking::Secret::new("nick_name".into())), + nick_name: Some(Secret::new("nick_name".into())), }), capture_method: Some(diesel_models::enums::CaptureMethod::Manual), ..utils::PaymentAuthorizeType::default().0 @@ -123,7 +123,7 @@ async fn should_sync_authorized_payment() { .psync_retry_till_status_matches( enums::AttemptStatus::Authorized, Some(types::PaymentsSyncData { - connector_transaction_id: router::types::ResponseId::ConnectorTransactionId( + connector_transaction_id: types::ResponseId::ConnectorTransactionId( txn_id.unwrap(), ), ..Default::default() @@ -252,7 +252,7 @@ async fn should_sync_auto_captured_payment() { .psync_retry_till_status_matches( enums::AttemptStatus::Charged, Some(types::PaymentsSyncData { - connector_transaction_id: router::types::ResponseId::ConnectorTransactionId( + connector_transaction_id: types::ResponseId::ConnectorTransactionId( txn_id.unwrap(), ), ..Default::default() @@ -347,7 +347,7 @@ async fn should_fail_payment_for_incorrect_card_number() { let response = CONNECTOR .make_payment( Some(types::PaymentsAuthorizeData { - payment_method_data: types::domain::PaymentMethodData::Card(domain::Card { + payment_method_data: domain::PaymentMethodData::Card(domain::Card { card_number: cards::CardNumber::from_str("1234567891011").unwrap(), ..utils::CCardType::default().0 }), @@ -370,7 +370,7 @@ async fn should_fail_payment_for_incorrect_cvc() { let response = CONNECTOR .make_payment( Some(types::PaymentsAuthorizeData { - payment_method_data: types::domain::PaymentMethodData::Card(domain::Card { + payment_method_data: domain::PaymentMethodData::Card(domain::Card { card_cvc: Secret::new("12345".to_string()), ..utils::CCardType::default().0 }), @@ -393,7 +393,7 @@ async fn should_fail_payment_for_invalid_exp_month() { let response = CONNECTOR .make_payment( Some(types::PaymentsAuthorizeData { - payment_method_data: types::domain::PaymentMethodData::Card(domain::Card { + payment_method_data: domain::PaymentMethodData::Card(domain::Card { card_exp_month: Secret::new("20".to_string()), ..utils::CCardType::default().0 }), @@ -416,7 +416,7 @@ async fn should_fail_payment_for_incorrect_expiry_year() { let response = CONNECTOR .make_payment( Some(types::PaymentsAuthorizeData { - payment_method_data: types::domain::PaymentMethodData::Card(domain::Card { + payment_method_data: domain::PaymentMethodData::Card(domain::Card { card_exp_year: Secret::new("2000".to_string()), ..utils::CCardType::default().0 }), diff --git a/crates/router/tests/connectors/forte.rs b/crates/router/tests/connectors/forte.rs index c58cab38a22..1c99c2ff7b5 100644 --- a/crates/router/tests/connectors/forte.rs +++ b/crates/router/tests/connectors/forte.rs @@ -13,12 +13,12 @@ use crate::{ struct ForteTest; impl ConnectorActions for ForteTest {} impl utils::Connector for ForteTest { - fn get_data(&self) -> types::api::ConnectorData { + fn get_data(&self) -> api::ConnectorData { use router::connector::Forte; - types::api::ConnectorData { + api::ConnectorData { connector: Box::new(&Forte), connector_name: types::Connector::Forte, - get_token: types::api::GetToken::Connector, + get_token: api::GetToken::Connector, merchant_connector_id: None, } } @@ -41,7 +41,7 @@ static CONNECTOR: ForteTest = ForteTest {}; fn get_payment_data() -> Option { Some(types::PaymentsAuthorizeData { - payment_method_data: types::domain::PaymentMethodData::Card(domain::Card { + payment_method_data: domain::PaymentMethodData::Card(domain::Card { card_number: CardNumber::from_str("4111111111111111").unwrap(), ..utils::CCardType::default().0 }), @@ -148,7 +148,7 @@ async fn should_sync_authorized_payment() { .psync_retry_till_status_matches( enums::AttemptStatus::Authorized, Some(types::PaymentsSyncData { - connector_transaction_id: router::types::ResponseId::ConnectorTransactionId( + connector_transaction_id: types::ResponseId::ConnectorTransactionId( txn_id.unwrap(), ), encoded_data: None, @@ -319,7 +319,7 @@ async fn should_sync_auto_captured_payment() { .psync_retry_till_status_matches( enums::AttemptStatus::Charged, Some(types::PaymentsSyncData { - connector_transaction_id: router::types::ResponseId::ConnectorTransactionId( + connector_transaction_id: types::ResponseId::ConnectorTransactionId( txn_id.unwrap(), ), encoded_data: None, @@ -467,7 +467,7 @@ async fn should_fail_payment_for_incorrect_cvc() { let response = CONNECTOR .make_payment( Some(types::PaymentsAuthorizeData { - payment_method_data: types::domain::PaymentMethodData::Card(domain::Card { + payment_method_data: domain::PaymentMethodData::Card(domain::Card { card_cvc: Secret::new("12345".to_string()), ..utils::CCardType::default().0 }), @@ -489,7 +489,7 @@ async fn should_fail_payment_for_invalid_exp_month() { let response = CONNECTOR .make_payment( Some(types::PaymentsAuthorizeData { - payment_method_data: types::domain::PaymentMethodData::Card(domain::Card { + payment_method_data: domain::PaymentMethodData::Card(domain::Card { card_exp_month: Secret::new("20".to_string()), ..utils::CCardType::default().0 }), @@ -512,7 +512,7 @@ async fn should_fail_payment_for_incorrect_expiry_year() { let response = CONNECTOR .make_payment( Some(types::PaymentsAuthorizeData { - payment_method_data: types::domain::PaymentMethodData::Card(domain::Card { + payment_method_data: domain::PaymentMethodData::Card(domain::Card { card_exp_year: Secret::new("2000".to_string()), ..utils::CCardType::default().0 }), @@ -630,7 +630,7 @@ async fn should_fail_for_refund_amount_higher_than_payment_amount() { #[actix_web::test] async fn should_throw_not_implemented_for_unsupported_issuer() { let authorize_data = Some(types::PaymentsAuthorizeData { - payment_method_data: types::domain::PaymentMethodData::Card(domain::Card { + payment_method_data: domain::PaymentMethodData::Card(domain::Card { card_number: CardNumber::from_str("6759649826438453").unwrap(), ..utils::CCardType::default().0 }), diff --git a/crates/router/tests/connectors/globalpay.rs b/crates/router/tests/connectors/globalpay.rs index e2ca19869f5..8ca4d6f1ae3 100644 --- a/crates/router/tests/connectors/globalpay.rs +++ b/crates/router/tests/connectors/globalpay.rs @@ -13,12 +13,12 @@ struct Globalpay; impl ConnectorActions for Globalpay {} static CONNECTOR: Globalpay = Globalpay {}; impl Connector for Globalpay { - fn get_data(&self) -> types::api::ConnectorData { + fn get_data(&self) -> api::ConnectorData { use router::connector::Globalpay; - types::api::ConnectorData { + api::ConnectorData { connector: Box::new(&Globalpay), connector_name: types::Connector::Globalpay, - get_token: types::api::GetToken::Connector, + get_token: api::GetToken::Connector, merchant_connector_id: None, } } @@ -42,7 +42,7 @@ impl Connector for Globalpay { } fn get_access_token() -> Option { - match utils::Connector::get_auth_token(&CONNECTOR) { + match Connector::get_auth_token(&CONNECTOR) { ConnectorAuthType::BodyKey { api_key, key1: _ } => Some(AccessToken { token: api_key, expires: 18600, @@ -120,7 +120,7 @@ async fn should_sync_auto_captured_payment() { .psync_retry_till_status_matches( enums::AttemptStatus::Authorized, Some(types::PaymentsSyncData { - connector_transaction_id: router::types::ResponseId::ConnectorTransactionId( + connector_transaction_id: types::ResponseId::ConnectorTransactionId( txn_id.unwrap(), ), ..Default::default() @@ -137,7 +137,7 @@ async fn should_fail_payment_for_incorrect_cvc() { let response = CONNECTOR .make_payment( Some(types::PaymentsAuthorizeData { - payment_method_data: types::domain::PaymentMethodData::Card(domain::Card { + payment_method_data: domain::PaymentMethodData::Card(domain::Card { card_number: cards::CardNumber::from_str("4024007134364842").unwrap(), ..utils::CCardType::default().0 }), @@ -345,7 +345,7 @@ async fn should_fail_payment_for_incorrect_expiry_year() { let response = CONNECTOR .make_payment( Some(types::PaymentsAuthorizeData { - payment_method_data: types::domain::PaymentMethodData::Card(domain::Card { + payment_method_data: domain::PaymentMethodData::Card(domain::Card { card_exp_year: Secret::new("2000".to_string()), ..utils::CCardType::default().0 }), @@ -367,7 +367,7 @@ async fn should_fail_payment_for_invalid_exp_month() { let response = CONNECTOR .make_payment( Some(types::PaymentsAuthorizeData { - payment_method_data: types::domain::PaymentMethodData::Card(domain::Card { + payment_method_data: domain::PaymentMethodData::Card(domain::Card { card_exp_month: Secret::new("20".to_string()), ..utils::CCardType::default().0 }), @@ -410,7 +410,7 @@ async fn should_sync_authorized_payment() { .psync_retry_till_status_matches( enums::AttemptStatus::Authorized, Some(types::PaymentsSyncData { - connector_transaction_id: router::types::ResponseId::ConnectorTransactionId( + connector_transaction_id: types::ResponseId::ConnectorTransactionId( txn_id.unwrap(), ), ..Default::default() diff --git a/crates/router/tests/connectors/globepay.rs b/crates/router/tests/connectors/globepay.rs index 70167970210..34a2ecb4c88 100644 --- a/crates/router/tests/connectors/globepay.rs +++ b/crates/router/tests/connectors/globepay.rs @@ -94,7 +94,7 @@ async fn should_sync_authorized_payment() { .psync_retry_till_status_matches( enums::AttemptStatus::Authorized, Some(types::PaymentsSyncData { - connector_transaction_id: router::types::ResponseId::ConnectorTransactionId( + connector_transaction_id: types::ResponseId::ConnectorTransactionId( txn_id.unwrap(), ), ..Default::default() @@ -214,7 +214,7 @@ async fn should_sync_auto_captured_payment() { .psync_retry_till_status_matches( enums::AttemptStatus::Charged, Some(types::PaymentsSyncData { - connector_transaction_id: router::types::ResponseId::ConnectorTransactionId( + connector_transaction_id: types::ResponseId::ConnectorTransactionId( txn_id.unwrap(), ), capture_method: Some(enums::CaptureMethod::Automatic), @@ -304,7 +304,7 @@ async fn should_fail_payment_for_incorrect_cvc() { let response = CONNECTOR .make_payment( Some(types::PaymentsAuthorizeData { - payment_method_data: types::domain::PaymentMethodData::Card(domain::Card { + payment_method_data: domain::PaymentMethodData::Card(domain::Card { card_cvc: Secret::new("12345".to_string()), ..utils::CCardType::default().0 }), @@ -326,7 +326,7 @@ async fn should_fail_payment_for_invalid_exp_month() { let response = CONNECTOR .make_payment( Some(types::PaymentsAuthorizeData { - payment_method_data: types::domain::PaymentMethodData::Card(domain::Card { + payment_method_data: domain::PaymentMethodData::Card(domain::Card { card_exp_month: Secret::new("20".to_string()), ..utils::CCardType::default().0 }), @@ -348,7 +348,7 @@ async fn should_fail_payment_for_incorrect_expiry_year() { let response = CONNECTOR .make_payment( Some(types::PaymentsAuthorizeData { - payment_method_data: types::domain::PaymentMethodData::Card(domain::Card { + payment_method_data: domain::PaymentMethodData::Card(domain::Card { card_exp_year: Secret::new("2000".to_string()), ..utils::CCardType::default().0 }), diff --git a/crates/router/tests/connectors/gocardless.rs b/crates/router/tests/connectors/gocardless.rs index 0564ea7da6d..f7f2ed864bf 100644 --- a/crates/router/tests/connectors/gocardless.rs +++ b/crates/router/tests/connectors/gocardless.rs @@ -92,7 +92,7 @@ async fn should_sync_authorized_payment() { .psync_retry_till_status_matches( enums::AttemptStatus::Authorized, Some(types::PaymentsSyncData { - connector_transaction_id: router::types::ResponseId::ConnectorTransactionId( + connector_transaction_id: types::ResponseId::ConnectorTransactionId( txn_id.unwrap(), ), ..Default::default() @@ -212,7 +212,7 @@ async fn should_sync_auto_captured_payment() { .psync_retry_till_status_matches( enums::AttemptStatus::Charged, Some(types::PaymentsSyncData { - connector_transaction_id: router::types::ResponseId::ConnectorTransactionId( + connector_transaction_id: types::ResponseId::ConnectorTransactionId( txn_id.unwrap(), ), capture_method: Some(enums::CaptureMethod::Automatic), @@ -302,7 +302,7 @@ async fn should_fail_payment_for_incorrect_cvc() { let response = CONNECTOR .make_payment( Some(types::PaymentsAuthorizeData { - payment_method_data: types::domain::PaymentMethodData::Card(domain::Card { + payment_method_data: domain::PaymentMethodData::Card(domain::Card { card_cvc: Secret::new("12345".to_string()), ..utils::CCardType::default().0 }), @@ -324,7 +324,7 @@ async fn should_fail_payment_for_invalid_exp_month() { let response = CONNECTOR .make_payment( Some(types::PaymentsAuthorizeData { - payment_method_data: types::domain::PaymentMethodData::Card(domain::Card { + payment_method_data: domain::PaymentMethodData::Card(domain::Card { card_exp_month: Secret::new("20".to_string()), ..utils::CCardType::default().0 }), @@ -346,7 +346,7 @@ async fn should_fail_payment_for_incorrect_expiry_year() { let response = CONNECTOR .make_payment( Some(types::PaymentsAuthorizeData { - payment_method_data: types::domain::PaymentMethodData::Card(domain::Card { + payment_method_data: domain::PaymentMethodData::Card(domain::Card { card_exp_year: Secret::new("2000".to_string()), ..utils::CCardType::default().0 }), diff --git a/crates/router/tests/connectors/helcim.rs b/crates/router/tests/connectors/helcim.rs index dc3bb4d47e6..7bb4b9ab785 100644 --- a/crates/router/tests/connectors/helcim.rs +++ b/crates/router/tests/connectors/helcim.rs @@ -92,7 +92,7 @@ async fn should_sync_authorized_payment() { .psync_retry_till_status_matches( enums::AttemptStatus::Authorized, Some(types::PaymentsSyncData { - connector_transaction_id: router::types::ResponseId::ConnectorTransactionId( + connector_transaction_id: types::ResponseId::ConnectorTransactionId( txn_id.unwrap(), ), ..Default::default() @@ -212,7 +212,7 @@ async fn should_sync_auto_captured_payment() { .psync_retry_till_status_matches( enums::AttemptStatus::Charged, Some(types::PaymentsSyncData { - connector_transaction_id: router::types::ResponseId::ConnectorTransactionId( + connector_transaction_id: types::ResponseId::ConnectorTransactionId( txn_id.unwrap(), ), capture_method: Some(enums::CaptureMethod::Automatic), @@ -302,7 +302,7 @@ async fn should_fail_payment_for_incorrect_cvc() { let response = CONNECTOR .make_payment( Some(types::PaymentsAuthorizeData { - payment_method_data: types::domain::PaymentMethodData::Card(domain::Card { + payment_method_data: domain::PaymentMethodData::Card(domain::Card { card_cvc: Secret::new("12345".to_string()), ..utils::CCardType::default().0 }), @@ -324,7 +324,7 @@ async fn should_fail_payment_for_invalid_exp_month() { let response = CONNECTOR .make_payment( Some(types::PaymentsAuthorizeData { - payment_method_data: types::domain::PaymentMethodData::Card(domain::Card { + payment_method_data: domain::PaymentMethodData::Card(domain::Card { card_exp_month: Secret::new("20".to_string()), ..utils::CCardType::default().0 }), @@ -346,7 +346,7 @@ async fn should_fail_payment_for_incorrect_expiry_year() { let response = CONNECTOR .make_payment( Some(types::PaymentsAuthorizeData { - payment_method_data: types::domain::PaymentMethodData::Card(domain::Card { + payment_method_data: domain::PaymentMethodData::Card(domain::Card { card_exp_year: Secret::new("2000".to_string()), ..utils::CCardType::default().0 }), diff --git a/crates/router/tests/connectors/iatapay.rs b/crates/router/tests/connectors/iatapay.rs index d685756094e..dfb8e097be3 100644 --- a/crates/router/tests/connectors/iatapay.rs +++ b/crates/router/tests/connectors/iatapay.rs @@ -12,12 +12,12 @@ use crate::{ struct IatapayTest; impl ConnectorActions for IatapayTest {} impl Connector for IatapayTest { - fn get_data(&self) -> types::api::ConnectorData { + fn get_data(&self) -> api::ConnectorData { use router::connector::Iatapay; - types::api::ConnectorData { + api::ConnectorData { connector: Box::new(&Iatapay), connector_name: types::Connector::Iatapay, - get_token: types::api::GetToken::Connector, + get_token: api::GetToken::Connector, merchant_connector_id: None, } } @@ -155,7 +155,7 @@ async fn should_sync_payment() { .psync_retry_till_status_matches( enums::AttemptStatus::Charged, Some(types::PaymentsSyncData { - connector_transaction_id: router::types::ResponseId::ConnectorTransactionId( + connector_transaction_id: types::ResponseId::ConnectorTransactionId( "PE9OTYNP639XW".to_string(), ), ..Default::default() diff --git a/crates/router/tests/connectors/main.rs b/crates/router/tests/connectors/main.rs index eb1c00a227b..78e67b7670a 100644 --- a/crates/router/tests/connectors/main.rs +++ b/crates/router/tests/connectors/main.rs @@ -62,6 +62,7 @@ mod trustpay; mod tsys; mod utils; mod volt; +#[cfg(feature = "payouts")] mod wise; mod worldline; mod worldpay; diff --git a/crates/router/tests/connectors/mollie.rs b/crates/router/tests/connectors/mollie.rs index 14c77e2a9f0..be0b5d1c1d7 100644 --- a/crates/router/tests/connectors/mollie.rs +++ b/crates/router/tests/connectors/mollie.rs @@ -5,6 +5,7 @@ use crate::{ utils::{self, ConnectorActions}, }; +#[allow(dead_code)] #[derive(Clone, Copy)] struct MollieTest; impl ConnectorActions for MollieTest {} diff --git a/crates/router/tests/connectors/multisafepay.rs b/crates/router/tests/connectors/multisafepay.rs index f35fe05bf36..606b60b2490 100644 --- a/crates/router/tests/connectors/multisafepay.rs +++ b/crates/router/tests/connectors/multisafepay.rs @@ -59,7 +59,7 @@ fn get_default_payment_info() -> Option { )); Some(PaymentInfo { address, - ..utils::PaymentInfo::default() + ..PaymentInfo::default() }) } @@ -121,7 +121,7 @@ async fn should_sync_authorized_payment() { .psync_retry_till_status_matches( enums::AttemptStatus::Authorized, Some(types::PaymentsSyncData { - connector_transaction_id: router::types::ResponseId::ConnectorTransactionId( + connector_transaction_id: types::ResponseId::ConnectorTransactionId( txn_id.unwrap(), ), ..Default::default() @@ -237,7 +237,7 @@ async fn should_sync_auto_captured_payment() { .psync_retry_till_status_matches( enums::AttemptStatus::Charged, Some(types::PaymentsSyncData { - connector_transaction_id: router::types::ResponseId::ConnectorTransactionId( + connector_transaction_id: types::ResponseId::ConnectorTransactionId( txn_id.unwrap(), ), ..Default::default() @@ -331,7 +331,7 @@ async fn should_fail_payment_for_incorrect_cvc() { let response = CONNECTOR .make_payment( Some(types::PaymentsAuthorizeData { - payment_method_data: types::domain::PaymentMethodData::Card(domain::Card { + payment_method_data: domain::PaymentMethodData::Card(domain::Card { card_cvc: Secret::new("123498765".to_string()), ..utils::CCardType::default().0 }), @@ -350,7 +350,7 @@ async fn should_fail_payment_for_invalid_exp_month() { let response = CONNECTOR .make_payment( Some(types::PaymentsAuthorizeData { - payment_method_data: types::domain::PaymentMethodData::Card(domain::Card { + payment_method_data: domain::PaymentMethodData::Card(domain::Card { card_exp_month: Secret::new("20".to_string()), ..utils::CCardType::default().0 }), @@ -369,7 +369,7 @@ async fn should_fail_payment_for_incorrect_expiry_year() { let response = CONNECTOR .make_payment( Some(types::PaymentsAuthorizeData { - payment_method_data: types::domain::PaymentMethodData::Card(domain::Card { + payment_method_data: domain::PaymentMethodData::Card(domain::Card { card_exp_year: Secret::new("2000".to_string()), ..utils::CCardType::default().0 }), diff --git a/crates/router/tests/connectors/netcetera.rs b/crates/router/tests/connectors/netcetera.rs index f06e2a0f5d7..d7fceca9c5d 100644 --- a/crates/router/tests/connectors/netcetera.rs +++ b/crates/router/tests/connectors/netcetera.rs @@ -91,7 +91,7 @@ async fn should_sync_authorized_payment() { .psync_retry_till_status_matches( enums::AttemptStatus::Authorized, Some(types::PaymentsSyncData { - connector_transaction_id: router::types::ResponseId::ConnectorTransactionId( + connector_transaction_id: types::ResponseId::ConnectorTransactionId( txn_id.unwrap(), ), ..Default::default() @@ -211,7 +211,7 @@ async fn should_sync_auto_captured_payment() { .psync_retry_till_status_matches( enums::AttemptStatus::Charged, Some(types::PaymentsSyncData { - connector_transaction_id: router::types::ResponseId::ConnectorTransactionId( + connector_transaction_id: types::ResponseId::ConnectorTransactionId( txn_id.unwrap(), ), capture_method: Some(enums::CaptureMethod::Automatic), diff --git a/crates/router/tests/connectors/nexinets.rs b/crates/router/tests/connectors/nexinets.rs index 5948855ae8d..cdf94113c5a 100644 --- a/crates/router/tests/connectors/nexinets.rs +++ b/crates/router/tests/connectors/nexinets.rs @@ -41,7 +41,7 @@ impl utils::Connector for NexinetsTest { fn payment_method_details() -> Option { Some(PaymentsAuthorizeData { currency: diesel_models::enums::Currency::EUR, - payment_method_data: types::domain::PaymentMethodData::Card(domain::Card { + payment_method_data: domain::PaymentMethodData::Card(domain::Card { card_number: CardNumber::from_str("374111111111111").unwrap(), ..utils::CCardType::default().0 }), @@ -118,7 +118,7 @@ async fn should_sync_authorized_payment() { .psync_retry_till_status_matches( enums::AttemptStatus::Authorized, Some(types::PaymentsSyncData { - connector_transaction_id: router::types::ResponseId::ConnectorTransactionId(txn_id), + connector_transaction_id: types::ResponseId::ConnectorTransactionId(txn_id), encoded_data: None, capture_method: None, sync_type: types::SyncRequestType::SinglePaymentSync, @@ -341,7 +341,7 @@ async fn should_sync_auto_captured_payment() { .psync_retry_till_status_matches( enums::AttemptStatus::Charged, Some(types::PaymentsSyncData { - connector_transaction_id: router::types::ResponseId::ConnectorTransactionId(txn_id), + connector_transaction_id: types::ResponseId::ConnectorTransactionId(txn_id), capture_method: Some(enums::CaptureMethod::Automatic), connector_meta, ..Default::default() @@ -506,7 +506,7 @@ async fn should_fail_payment_for_incorrect_cvc() { let response = CONNECTOR .make_payment( Some(PaymentsAuthorizeData { - payment_method_data: types::domain::PaymentMethodData::Card(domain::Card { + payment_method_data: domain::PaymentMethodData::Card(domain::Card { card_cvc: Secret::new("12345".to_string()), ..utils::CCardType::default().0 }), @@ -528,7 +528,7 @@ async fn should_fail_payment_for_invalid_exp_month() { let response = CONNECTOR .make_payment( Some(PaymentsAuthorizeData { - payment_method_data: types::domain::PaymentMethodData::Card(domain::Card { + payment_method_data: domain::PaymentMethodData::Card(domain::Card { card_exp_month: Secret::new("20".to_string()), ..utils::CCardType::default().0 }), @@ -550,7 +550,7 @@ async fn should_fail_payment_for_incorrect_expiry_year() { let response = CONNECTOR .make_payment( Some(PaymentsAuthorizeData { - payment_method_data: types::domain::PaymentMethodData::Card(domain::Card { + payment_method_data: domain::PaymentMethodData::Card(domain::Card { card_exp_year: Secret::new("2000".to_string()), ..utils::CCardType::default().0 }), diff --git a/crates/router/tests/connectors/nmi.rs b/crates/router/tests/connectors/nmi.rs index 5b569fa497f..1d719e052a0 100644 --- a/crates/router/tests/connectors/nmi.rs +++ b/crates/router/tests/connectors/nmi.rs @@ -38,7 +38,7 @@ static CONNECTOR: NmiTest = NmiTest {}; fn get_payment_authorize_data() -> Option { Some(types::PaymentsAuthorizeData { - payment_method_data: types::domain::PaymentMethodData::Card(domain::Card { + payment_method_data: domain::PaymentMethodData::Card(domain::Card { card_number: cards::CardNumber::from_str("4111111111111111").unwrap(), ..utils::CCardType::default().0 }), @@ -60,10 +60,10 @@ async fn should_only_authorize_payment() { .psync_retry_till_status_matches( enums::AttemptStatus::Authorized, Some(types::PaymentsSyncData { - connector_transaction_id: router::types::ResponseId::ConnectorTransactionId( + connector_transaction_id: types::ResponseId::ConnectorTransactionId( transaction_id.clone(), ), - capture_method: Some(types::storage::enums::CaptureMethod::Manual), + capture_method: Some(enums::CaptureMethod::Manual), ..Default::default() }), None, @@ -87,10 +87,10 @@ async fn should_capture_authorized_payment() { .psync_retry_till_status_matches( enums::AttemptStatus::Authorized, Some(types::PaymentsSyncData { - connector_transaction_id: router::types::ResponseId::ConnectorTransactionId( + connector_transaction_id: types::ResponseId::ConnectorTransactionId( transaction_id.clone(), ), - capture_method: Some(types::storage::enums::CaptureMethod::Manual), + capture_method: Some(enums::CaptureMethod::Manual), ..Default::default() }), None, @@ -110,10 +110,10 @@ async fn should_capture_authorized_payment() { .psync_retry_till_status_matches( enums::AttemptStatus::Pending, Some(types::PaymentsSyncData { - connector_transaction_id: router::types::ResponseId::ConnectorTransactionId( + connector_transaction_id: types::ResponseId::ConnectorTransactionId( transaction_id.clone(), ), - capture_method: Some(types::storage::enums::CaptureMethod::Manual), + capture_method: Some(enums::CaptureMethod::Manual), ..Default::default() }), None, @@ -136,10 +136,10 @@ async fn should_partially_capture_authorized_payment() { .psync_retry_till_status_matches( enums::AttemptStatus::Authorized, Some(types::PaymentsSyncData { - connector_transaction_id: router::types::ResponseId::ConnectorTransactionId( + connector_transaction_id: types::ResponseId::ConnectorTransactionId( transaction_id.clone(), ), - capture_method: Some(types::storage::enums::CaptureMethod::Manual), + capture_method: Some(enums::CaptureMethod::Manual), ..Default::default() }), None, @@ -167,10 +167,10 @@ async fn should_partially_capture_authorized_payment() { .psync_retry_till_status_matches( enums::AttemptStatus::Pending, Some(types::PaymentsSyncData { - connector_transaction_id: router::types::ResponseId::ConnectorTransactionId( + connector_transaction_id: types::ResponseId::ConnectorTransactionId( transaction_id.clone(), ), - capture_method: Some(types::storage::enums::CaptureMethod::Manual), + capture_method: Some(enums::CaptureMethod::Manual), ..Default::default() }), None, @@ -194,10 +194,10 @@ async fn should_void_authorized_payment() { .psync_retry_till_status_matches( enums::AttemptStatus::Authorized, Some(types::PaymentsSyncData { - connector_transaction_id: router::types::ResponseId::ConnectorTransactionId( + connector_transaction_id: types::ResponseId::ConnectorTransactionId( transaction_id.clone(), ), - capture_method: Some(types::storage::enums::CaptureMethod::Manual), + capture_method: Some(enums::CaptureMethod::Manual), ..Default::default() }), None, @@ -223,10 +223,10 @@ async fn should_void_authorized_payment() { .psync_retry_till_status_matches( enums::AttemptStatus::Voided, Some(types::PaymentsSyncData { - connector_transaction_id: router::types::ResponseId::ConnectorTransactionId( + connector_transaction_id: types::ResponseId::ConnectorTransactionId( transaction_id.clone(), ), - capture_method: Some(types::storage::enums::CaptureMethod::Manual), + capture_method: Some(enums::CaptureMethod::Manual), ..Default::default() }), None, @@ -250,10 +250,10 @@ async fn should_refund_manually_captured_payment() { .psync_retry_till_status_matches( enums::AttemptStatus::Authorized, Some(types::PaymentsSyncData { - connector_transaction_id: router::types::ResponseId::ConnectorTransactionId( + connector_transaction_id: types::ResponseId::ConnectorTransactionId( transaction_id.clone(), ), - capture_method: Some(types::storage::enums::CaptureMethod::Manual), + capture_method: Some(enums::CaptureMethod::Manual), ..Default::default() }), None, @@ -273,10 +273,10 @@ async fn should_refund_manually_captured_payment() { .psync_retry_till_status_matches( enums::AttemptStatus::Pending, Some(types::PaymentsSyncData { - connector_transaction_id: router::types::ResponseId::ConnectorTransactionId( + connector_transaction_id: types::ResponseId::ConnectorTransactionId( transaction_id.clone(), ), - capture_method: Some(types::storage::enums::CaptureMethod::Manual), + capture_method: Some(enums::CaptureMethod::Manual), ..Default::default() }), None, @@ -319,10 +319,10 @@ async fn should_partially_refund_manually_captured_payment() { .psync_retry_till_status_matches( enums::AttemptStatus::Authorized, Some(types::PaymentsSyncData { - connector_transaction_id: router::types::ResponseId::ConnectorTransactionId( + connector_transaction_id: types::ResponseId::ConnectorTransactionId( transaction_id.clone(), ), - capture_method: Some(types::storage::enums::CaptureMethod::Manual), + capture_method: Some(enums::CaptureMethod::Manual), ..Default::default() }), None, @@ -349,10 +349,10 @@ async fn should_partially_refund_manually_captured_payment() { .psync_retry_till_status_matches( enums::AttemptStatus::Pending, Some(types::PaymentsSyncData { - connector_transaction_id: router::types::ResponseId::ConnectorTransactionId( + connector_transaction_id: types::ResponseId::ConnectorTransactionId( transaction_id.clone(), ), - capture_method: Some(types::storage::enums::CaptureMethod::Manual), + capture_method: Some(enums::CaptureMethod::Manual), ..Default::default() }), None, @@ -402,10 +402,10 @@ async fn should_make_payment() { .psync_retry_till_status_matches( enums::AttemptStatus::Pending, Some(types::PaymentsSyncData { - connector_transaction_id: router::types::ResponseId::ConnectorTransactionId( + connector_transaction_id: types::ResponseId::ConnectorTransactionId( transaction_id.clone(), ), - capture_method: Some(types::storage::enums::CaptureMethod::Automatic), + capture_method: Some(enums::CaptureMethod::Automatic), ..Default::default() }), None, @@ -429,10 +429,10 @@ async fn should_refund_auto_captured_payment() { .psync_retry_till_status_matches( enums::AttemptStatus::Pending, Some(types::PaymentsSyncData { - connector_transaction_id: router::types::ResponseId::ConnectorTransactionId( + connector_transaction_id: types::ResponseId::ConnectorTransactionId( transaction_id.clone(), ), - capture_method: Some(types::storage::enums::CaptureMethod::Automatic), + capture_method: Some(enums::CaptureMethod::Automatic), ..Default::default() }), None, @@ -475,10 +475,10 @@ async fn should_partially_refund_succeeded_payment() { .psync_retry_till_status_matches( enums::AttemptStatus::Pending, Some(types::PaymentsSyncData { - connector_transaction_id: router::types::ResponseId::ConnectorTransactionId( + connector_transaction_id: types::ResponseId::ConnectorTransactionId( transaction_id.clone(), ), - capture_method: Some(types::storage::enums::CaptureMethod::Automatic), + capture_method: Some(enums::CaptureMethod::Automatic), ..Default::default() }), None, @@ -528,10 +528,10 @@ async fn should_refund_succeeded_payment_multiple_times() { .psync_retry_till_status_matches( enums::AttemptStatus::Pending, Some(types::PaymentsSyncData { - connector_transaction_id: router::types::ResponseId::ConnectorTransactionId( + connector_transaction_id: types::ResponseId::ConnectorTransactionId( transaction_id.clone(), ), - capture_method: Some(types::storage::enums::CaptureMethod::Automatic), + capture_method: Some(enums::CaptureMethod::Automatic), ..Default::default() }), None, @@ -600,10 +600,10 @@ async fn should_fail_void_payment_for_auto_capture() { .psync_retry_till_status_matches( enums::AttemptStatus::Pending, Some(types::PaymentsSyncData { - connector_transaction_id: router::types::ResponseId::ConnectorTransactionId( + connector_transaction_id: types::ResponseId::ConnectorTransactionId( transaction_id.clone(), ), - capture_method: Some(types::storage::enums::CaptureMethod::Automatic), + capture_method: Some(enums::CaptureMethod::Automatic), ..Default::default() }), None, @@ -633,10 +633,10 @@ async fn should_fail_capture_for_invalid_payment() { .psync_retry_till_status_matches( enums::AttemptStatus::Authorized, Some(types::PaymentsSyncData { - connector_transaction_id: router::types::ResponseId::ConnectorTransactionId( + connector_transaction_id: types::ResponseId::ConnectorTransactionId( transaction_id.clone(), ), - capture_method: Some(types::storage::enums::CaptureMethod::Manual), + capture_method: Some(enums::CaptureMethod::Manual), ..Default::default() }), None, @@ -665,10 +665,10 @@ async fn should_fail_for_refund_amount_higher_than_payment_amount() { .psync_retry_till_status_matches( enums::AttemptStatus::Pending, Some(types::PaymentsSyncData { - connector_transaction_id: router::types::ResponseId::ConnectorTransactionId( + connector_transaction_id: types::ResponseId::ConnectorTransactionId( transaction_id.clone(), ), - capture_method: Some(types::storage::enums::CaptureMethod::Automatic), + capture_method: Some(enums::CaptureMethod::Automatic), ..Default::default() }), None, diff --git a/crates/router/tests/connectors/noon.rs b/crates/router/tests/connectors/noon.rs index 7e0bb954c6c..47e76d5ec76 100644 --- a/crates/router/tests/connectors/noon.rs +++ b/crates/router/tests/connectors/noon.rs @@ -1,10 +1,7 @@ use std::str::FromStr; use masking::Secret; -use router::types::{ - self, - storage::{self, enums}, -}; +use router::types::{self, storage::enums}; use crate::{ connector_auth, @@ -47,7 +44,7 @@ fn get_default_payment_info() -> Option { fn payment_method_details() -> Option { Some(types::PaymentsAuthorizeData { - currency: storage::enums::Currency::AED, + currency: enums::Currency::AED, ..utils::PaymentAuthorizeType::default().0 }) } @@ -102,7 +99,7 @@ async fn should_sync_authorized_payment() { .psync_retry_till_status_matches( enums::AttemptStatus::Authorized, Some(types::PaymentsSyncData { - connector_transaction_id: router::types::ResponseId::ConnectorTransactionId( + connector_transaction_id: types::ResponseId::ConnectorTransactionId( txn_id.unwrap(), ), ..Default::default() @@ -222,7 +219,7 @@ async fn should_sync_auto_captured_payment() { .psync_retry_till_status_matches( enums::AttemptStatus::Charged, Some(types::PaymentsSyncData { - connector_transaction_id: router::types::ResponseId::ConnectorTransactionId( + connector_transaction_id: types::ResponseId::ConnectorTransactionId( txn_id.unwrap(), ), capture_method: Some(enums::CaptureMethod::Automatic), diff --git a/crates/router/tests/connectors/nuvei.rs b/crates/router/tests/connectors/nuvei.rs index 04a83b3a606..55a8f234219 100644 --- a/crates/router/tests/connectors/nuvei.rs +++ b/crates/router/tests/connectors/nuvei.rs @@ -1,10 +1,7 @@ use std::str::FromStr; use masking::Secret; -use router::types::{ - self, - storage::{self, enums}, -}; +use router::types::{self, storage::enums}; use serde_json::json; use crate::{ @@ -102,7 +99,7 @@ async fn should_sync_authorized_payment() { .psync_retry_till_status_matches( enums::AttemptStatus::Authorized, Some(types::PaymentsSyncData { - connector_transaction_id: router::types::ResponseId::ConnectorTransactionId( + connector_transaction_id: types::ResponseId::ConnectorTransactionId( txn_id.unwrap(), ), connector_meta: Some(json!({ @@ -126,7 +123,7 @@ async fn should_void_authorized_payment() { Some(types::PaymentsCancelData { cancellation_reason: Some("requested_by_customer".to_string()), amount: Some(100), - currency: Some(storage::enums::Currency::USD), + currency: Some(enums::Currency::USD), ..Default::default() }), None, @@ -194,7 +191,7 @@ async fn should_sync_auto_captured_payment() { .psync_retry_till_status_matches( enums::AttemptStatus::Charged, Some(types::PaymentsSyncData { - connector_transaction_id: router::types::ResponseId::ConnectorTransactionId( + connector_transaction_id: types::ResponseId::ConnectorTransactionId( txn_id.unwrap(), ), connector_meta: Some(json!({ @@ -345,7 +342,7 @@ async fn should_fail_void_payment_for_auto_capture() { Some(types::PaymentsCancelData { cancellation_reason: Some("requested_by_customer".to_string()), amount: Some(100), - currency: Some(storage::enums::Currency::USD), + currency: Some(enums::Currency::USD), ..Default::default() }), None, diff --git a/crates/router/tests/connectors/opayo.rs b/crates/router/tests/connectors/opayo.rs index f4badf9b116..b383a2e1b0e 100644 --- a/crates/router/tests/connectors/opayo.rs +++ b/crates/router/tests/connectors/opayo.rs @@ -97,7 +97,7 @@ async fn should_sync_authorized_payment() { .psync_retry_till_status_matches( enums::AttemptStatus::Authorized, Some(types::PaymentsSyncData { - connector_transaction_id: router::types::ResponseId::ConnectorTransactionId( + connector_transaction_id: types::ResponseId::ConnectorTransactionId( txn_id.unwrap(), ), ..Default::default() @@ -217,7 +217,7 @@ async fn should_sync_auto_captured_payment() { .psync_retry_till_status_matches( enums::AttemptStatus::Charged, Some(types::PaymentsSyncData { - connector_transaction_id: router::types::ResponseId::ConnectorTransactionId( + connector_transaction_id: types::ResponseId::ConnectorTransactionId( txn_id.unwrap(), ), capture_method: Some(enums::CaptureMethod::Automatic), @@ -307,7 +307,7 @@ async fn should_fail_payment_for_incorrect_card_number() { let response = CONNECTOR .make_payment( Some(types::PaymentsAuthorizeData { - payment_method_data: types::domain::PaymentMethodData::Card(domain::Card { + payment_method_data: domain::PaymentMethodData::Card(domain::Card { card_number: cards::CardNumber::from_str("1234567891011").unwrap(), ..utils::CCardType::default().0 }), @@ -329,7 +329,7 @@ async fn should_fail_payment_for_incorrect_cvc() { let response = CONNECTOR .make_payment( Some(types::PaymentsAuthorizeData { - payment_method_data: types::domain::PaymentMethodData::Card(domain::Card { + payment_method_data: domain::PaymentMethodData::Card(domain::Card { card_cvc: Secret::new("12345".to_string()), ..utils::CCardType::default().0 }), @@ -351,7 +351,7 @@ async fn should_fail_payment_for_invalid_exp_month() { let response = CONNECTOR .make_payment( Some(types::PaymentsAuthorizeData { - payment_method_data: types::domain::PaymentMethodData::Card(domain::Card { + payment_method_data: domain::PaymentMethodData::Card(domain::Card { card_exp_month: Secret::new("20".to_string()), ..utils::CCardType::default().0 }), @@ -373,7 +373,7 @@ async fn should_fail_payment_for_incorrect_expiry_year() { let response = CONNECTOR .make_payment( Some(types::PaymentsAuthorizeData { - payment_method_data: types::domain::PaymentMethodData::Card(domain::Card { + payment_method_data: domain::PaymentMethodData::Card(domain::Card { card_exp_year: Secret::new("2000".to_string()), ..utils::CCardType::default().0 }), diff --git a/crates/router/tests/connectors/opennode.rs b/crates/router/tests/connectors/opennode.rs index e4d347c26d1..c91126953de 100644 --- a/crates/router/tests/connectors/opennode.rs +++ b/crates/router/tests/connectors/opennode.rs @@ -10,12 +10,12 @@ use crate::{ struct OpennodeTest; impl ConnectorActions for OpennodeTest {} impl utils::Connector for OpennodeTest { - fn get_data(&self) -> types::api::ConnectorData { + fn get_data(&self) -> api::ConnectorData { use router::connector::Opennode; - types::api::ConnectorData { + api::ConnectorData { connector: Box::new(&Opennode), connector_name: types::Connector::Opennode, - get_token: types::api::GetToken::Connector, + get_token: api::GetToken::Connector, merchant_connector_id: None, } } @@ -68,7 +68,7 @@ fn payment_method_details() -> Option { Some(types::PaymentsAuthorizeData { amount: 1, currency: enums::Currency::USD, - payment_method_data: types::domain::PaymentMethodData::Crypto(domain::CryptoData { + payment_method_data: domain::PaymentMethodData::Crypto(domain::CryptoData { pay_currency: None, }), confirm: true, @@ -127,7 +127,7 @@ async fn should_sync_authorized_payment() { .psync_retry_till_status_matches( enums::AttemptStatus::Authorized, Some(types::PaymentsSyncData { - connector_transaction_id: router::types::ResponseId::ConnectorTransactionId( + connector_transaction_id: types::ResponseId::ConnectorTransactionId( "5adebfb1-802e-432b-8b42-5db4b754b2eb".to_string(), ), ..Default::default() @@ -146,7 +146,7 @@ async fn should_sync_unresolved_payment() { .psync_retry_till_status_matches( enums::AttemptStatus::Authorized, Some(types::PaymentsSyncData { - connector_transaction_id: router::types::ResponseId::ConnectorTransactionId( + connector_transaction_id: types::ResponseId::ConnectorTransactionId( "4cf63e6b-5135-49cb-997f-6e0b30fecebc".to_string(), ), ..Default::default() @@ -165,7 +165,7 @@ async fn should_sync_expired_payment() { .psync_retry_till_status_matches( enums::AttemptStatus::Authorized, Some(types::PaymentsSyncData { - connector_transaction_id: router::types::ResponseId::ConnectorTransactionId( + connector_transaction_id: types::ResponseId::ConnectorTransactionId( "c36a097a-5091-4317-8749-80343a71c1c4".to_string(), ), ..Default::default() diff --git a/crates/router/tests/connectors/payme.rs b/crates/router/tests/connectors/payme.rs index a1682001488..234f3a0eeb8 100644 --- a/crates/router/tests/connectors/payme.rs +++ b/crates/router/tests/connectors/payme.rs @@ -91,7 +91,7 @@ fn payment_method_details() -> Option { router_return_url: Some("https://hyperswitch.io".to_string()), webhook_url: Some("https://hyperswitch.io".to_string()), email: Some(Email::from_str("test@gmail.com").unwrap()), - payment_method_data: types::domain::PaymentMethodData::Card(domain::Card { + payment_method_data: domain::PaymentMethodData::Card(domain::Card { card_number: cards::CardNumber::from_str("4111111111111111").unwrap(), card_cvc: Secret::new("123".to_string()), card_exp_month: Secret::new("10".to_string()), @@ -155,7 +155,7 @@ async fn should_sync_authorized_payment() { .psync_retry_till_status_matches( enums::AttemptStatus::Authorized, Some(types::PaymentsSyncData { - connector_transaction_id: router::types::ResponseId::ConnectorTransactionId( + connector_transaction_id: types::ResponseId::ConnectorTransactionId( txn_id.unwrap(), ), ..Default::default() @@ -280,7 +280,7 @@ async fn should_sync_auto_captured_payment() { .psync_retry_till_status_matches( enums::AttemptStatus::Charged, Some(types::PaymentsSyncData { - connector_transaction_id: router::types::ResponseId::ConnectorTransactionId( + connector_transaction_id: types::ResponseId::ConnectorTransactionId( txn_id.unwrap(), ), capture_method: Some(enums::CaptureMethod::Automatic), @@ -372,7 +372,7 @@ async fn should_fail_payment_for_incorrect_cvc() { Some(types::PaymentsAuthorizeData { amount: 100, currency: enums::Currency::ILS, - payment_method_data: types::domain::PaymentMethodData::Card(domain::Card { + payment_method_data: domain::PaymentMethodData::Card(domain::Card { card_cvc: Secret::new("12345".to_string()), ..utils::CCardType::default().0 }), @@ -391,7 +391,7 @@ async fn should_fail_payment_for_incorrect_cvc() { router_return_url: Some("https://hyperswitch.io".to_string()), webhook_url: Some("https://hyperswitch.io".to_string()), email: Some(Email::from_str("test@gmail.com").unwrap()), - ..utils::PaymentAuthorizeType::default().0 + ..PaymentAuthorizeType::default().0 }), get_default_payment_info(), ) @@ -411,7 +411,7 @@ async fn should_fail_payment_for_invalid_exp_month() { Some(types::PaymentsAuthorizeData { amount: 100, currency: enums::Currency::ILS, - payment_method_data: types::domain::PaymentMethodData::Card(domain::Card { + payment_method_data: domain::PaymentMethodData::Card(domain::Card { card_exp_month: Secret::new("20".to_string()), ..utils::CCardType::default().0 }), @@ -430,7 +430,7 @@ async fn should_fail_payment_for_invalid_exp_month() { router_return_url: Some("https://hyperswitch.io".to_string()), webhook_url: Some("https://hyperswitch.io".to_string()), email: Some(Email::from_str("test@gmail.com").unwrap()), - ..utils::PaymentAuthorizeType::default().0 + ..PaymentAuthorizeType::default().0 }), get_default_payment_info(), ) @@ -450,7 +450,7 @@ async fn should_fail_payment_for_incorrect_expiry_year() { Some(types::PaymentsAuthorizeData { amount: 100, currency: enums::Currency::ILS, - payment_method_data: types::domain::PaymentMethodData::Card(domain::Card { + payment_method_data: domain::PaymentMethodData::Card(domain::Card { card_exp_year: Secret::new("2012".to_string()), ..utils::CCardType::default().0 }), @@ -469,7 +469,7 @@ async fn should_fail_payment_for_incorrect_expiry_year() { router_return_url: Some("https://hyperswitch.io".to_string()), webhook_url: Some("https://hyperswitch.io".to_string()), email: Some(Email::from_str("test@gmail.com").unwrap()), - ..utils::PaymentAuthorizeType::default().0 + ..PaymentAuthorizeType::default().0 }), get_default_payment_info(), ) diff --git a/crates/router/tests/connectors/paypal.rs b/crates/router/tests/connectors/paypal.rs index bded135f45d..704aeca5936 100644 --- a/crates/router/tests/connectors/paypal.rs +++ b/crates/router/tests/connectors/paypal.rs @@ -56,7 +56,7 @@ fn get_default_payment_info() -> Option { fn get_payment_data() -> Option { Some(types::PaymentsAuthorizeData { - payment_method_data: types::domain::PaymentMethodData::Card(domain::Card { + payment_method_data: domain::PaymentMethodData::Card(domain::Card { card_number: cards::CardNumber::from_str("4000020000000000").unwrap(), ..utils::CCardType::default().0 }), @@ -136,7 +136,7 @@ async fn should_sync_authorized_payment() { enums::AttemptStatus::Authorized, Some(types::PaymentsSyncData { mandate_id: None, - connector_transaction_id: router::types::ResponseId::ConnectorTransactionId(txn_id), + connector_transaction_id: types::ResponseId::ConnectorTransactionId(txn_id), encoded_data: None, capture_method: None, sync_type: types::SyncRequestType::SinglePaymentSync, @@ -332,7 +332,7 @@ async fn should_sync_auto_captured_payment() { enums::AttemptStatus::Charged, Some(types::PaymentsSyncData { mandate_id: None, - connector_transaction_id: router::types::ResponseId::ConnectorTransactionId( + connector_transaction_id: types::ResponseId::ConnectorTransactionId( txn_id.unwrap(), ), encoded_data: None, @@ -450,7 +450,7 @@ async fn should_fail_payment_for_incorrect_cvc() { let response = CONNECTOR .make_payment( Some(types::PaymentsAuthorizeData { - payment_method_data: types::domain::PaymentMethodData::Card(domain::Card { + payment_method_data: domain::PaymentMethodData::Card(domain::Card { card_cvc: Secret::new("12345".to_string()), ..utils::CCardType::default().0 }), @@ -476,7 +476,7 @@ async fn should_fail_payment_for_invalid_exp_month() { let response = CONNECTOR .make_payment( Some(types::PaymentsAuthorizeData { - payment_method_data: types::domain::PaymentMethodData::Card(domain::Card { + payment_method_data: domain::PaymentMethodData::Card(domain::Card { card_exp_month: Secret::new("20".to_string()), ..utils::CCardType::default().0 }), @@ -502,7 +502,7 @@ async fn should_fail_payment_for_incorrect_expiry_year() { let response = CONNECTOR .make_payment( Some(types::PaymentsAuthorizeData { - payment_method_data: types::domain::PaymentMethodData::Card(domain::Card { + payment_method_data: domain::PaymentMethodData::Card(domain::Card { card_exp_year: Secret::new("2000".to_string()), ..utils::CCardType::default().0 }), diff --git a/crates/router/tests/connectors/payu.rs b/crates/router/tests/connectors/payu.rs index fc916e33677..2f6ebcefe6a 100644 --- a/crates/router/tests/connectors/payu.rs +++ b/crates/router/tests/connectors/payu.rs @@ -70,7 +70,7 @@ async fn should_authorize_card_payment() { .psync_retry_till_status_matches( enums::AttemptStatus::Authorized, Some(types::PaymentsSyncData { - connector_transaction_id: router::types::ResponseId::ConnectorTransactionId( + connector_transaction_id: types::ResponseId::ConnectorTransactionId( transaction_id.clone(), ), ..Default::default() @@ -115,7 +115,7 @@ async fn should_authorize_gpay_payment() { let sync_response = Payu {} .sync_payment( Some(types::PaymentsSyncData { - connector_transaction_id: router::types::ResponseId::ConnectorTransactionId( + connector_transaction_id: types::ResponseId::ConnectorTransactionId( transaction_id.clone(), ), ..Default::default() @@ -148,7 +148,7 @@ async fn should_capture_already_authorized_payment() { .psync_retry_till_status_matches( enums::AttemptStatus::Authorized, Some(types::PaymentsSyncData { - connector_transaction_id: router::types::ResponseId::ConnectorTransactionId( + connector_transaction_id: types::ResponseId::ConnectorTransactionId( transaction_id.clone(), ), ..Default::default() @@ -167,7 +167,7 @@ async fn should_capture_already_authorized_payment() { .psync_retry_till_status_matches( enums::AttemptStatus::Charged, Some(types::PaymentsSyncData { - connector_transaction_id: router::types::ResponseId::ConnectorTransactionId( + connector_transaction_id: types::ResponseId::ConnectorTransactionId( transaction_id, ), ..Default::default() @@ -203,7 +203,7 @@ async fn should_sync_payment() { .psync_retry_till_status_matches( enums::AttemptStatus::Authorized, Some(types::PaymentsSyncData { - connector_transaction_id: router::types::ResponseId::ConnectorTransactionId( + connector_transaction_id: types::ResponseId::ConnectorTransactionId( transaction_id, ), ..Default::default() @@ -246,7 +246,7 @@ async fn should_void_already_authorized_payment() { .psync_retry_till_status_matches( enums::AttemptStatus::Voided, Some(types::PaymentsSyncData { - connector_transaction_id: router::types::ResponseId::ConnectorTransactionId( + connector_transaction_id: types::ResponseId::ConnectorTransactionId( transaction_id, ), ..Default::default() @@ -280,7 +280,7 @@ async fn should_refund_succeeded_payment() { .psync_retry_till_status_matches( enums::AttemptStatus::Authorized, Some(types::PaymentsSyncData { - connector_transaction_id: router::types::ResponseId::ConnectorTransactionId( + connector_transaction_id: types::ResponseId::ConnectorTransactionId( transaction_id.clone(), ), ..Default::default() @@ -301,7 +301,7 @@ async fn should_refund_succeeded_payment() { .psync_retry_till_status_matches( enums::AttemptStatus::Charged, Some(types::PaymentsSyncData { - connector_transaction_id: router::types::ResponseId::ConnectorTransactionId( + connector_transaction_id: types::ResponseId::ConnectorTransactionId( transaction_id.clone(), ), ..Default::default() diff --git a/crates/router/tests/connectors/placetopay.rs b/crates/router/tests/connectors/placetopay.rs index 41675b9751b..d9b75cbe0e8 100644 --- a/crates/router/tests/connectors/placetopay.rs +++ b/crates/router/tests/connectors/placetopay.rs @@ -92,7 +92,7 @@ async fn should_sync_authorized_payment() { .psync_retry_till_status_matches( enums::AttemptStatus::Authorized, Some(types::PaymentsSyncData { - connector_transaction_id: router::types::ResponseId::ConnectorTransactionId( + connector_transaction_id: types::ResponseId::ConnectorTransactionId( txn_id.unwrap(), ), ..Default::default() @@ -212,7 +212,7 @@ async fn should_sync_auto_captured_payment() { .psync_retry_till_status_matches( enums::AttemptStatus::Charged, Some(types::PaymentsSyncData { - connector_transaction_id: router::types::ResponseId::ConnectorTransactionId( + connector_transaction_id: types::ResponseId::ConnectorTransactionId( txn_id.unwrap(), ), capture_method: Some(enums::CaptureMethod::Automatic), @@ -302,7 +302,7 @@ async fn should_fail_payment_for_incorrect_cvc() { let response = CONNECTOR .make_payment( Some(types::PaymentsAuthorizeData { - payment_method_data: types::domain::PaymentMethodData::Card(domain::Card { + payment_method_data: domain::PaymentMethodData::Card(domain::Card { card_cvc: Secret::new("12345".to_string()), ..utils::CCardType::default().0 }), @@ -324,7 +324,7 @@ async fn should_fail_payment_for_invalid_exp_month() { let response = CONNECTOR .make_payment( Some(types::PaymentsAuthorizeData { - payment_method_data: types::domain::PaymentMethodData::Card(domain::Card { + payment_method_data: domain::PaymentMethodData::Card(domain::Card { card_exp_month: Secret::new("20".to_string()), ..utils::CCardType::default().0 }), @@ -346,7 +346,7 @@ async fn should_fail_payment_for_incorrect_expiry_year() { let response = CONNECTOR .make_payment( Some(types::PaymentsAuthorizeData { - payment_method_data: types::domain::PaymentMethodData::Card(domain::Card { + payment_method_data: domain::PaymentMethodData::Card(domain::Card { card_exp_year: Secret::new("2000".to_string()), ..utils::CCardType::default().0 }), diff --git a/crates/router/tests/connectors/powertranz.rs b/crates/router/tests/connectors/powertranz.rs index 0701ec006d9..7f1e1937e05 100644 --- a/crates/router/tests/connectors/powertranz.rs +++ b/crates/router/tests/connectors/powertranz.rs @@ -96,7 +96,7 @@ async fn should_sync_authorized_payment() { .psync_retry_till_status_matches( enums::AttemptStatus::Authorized, Some(types::PaymentsSyncData { - connector_transaction_id: router::types::ResponseId::ConnectorTransactionId( + connector_transaction_id: types::ResponseId::ConnectorTransactionId( txn_id.unwrap(), ), ..Default::default() @@ -218,7 +218,7 @@ async fn should_sync_auto_captured_payment() { .psync_retry_till_status_matches( enums::AttemptStatus::Charged, Some(types::PaymentsSyncData { - connector_transaction_id: router::types::ResponseId::ConnectorTransactionId( + connector_transaction_id: types::ResponseId::ConnectorTransactionId( txn_id.unwrap(), ), capture_method: Some(enums::CaptureMethod::Automatic), @@ -310,7 +310,7 @@ async fn should_fail_payment_for_incorrect_cvc() { let response = CONNECTOR .make_payment( Some(types::PaymentsAuthorizeData { - payment_method_data: types::domain::PaymentMethodData::Card(domain::Card { + payment_method_data: domain::PaymentMethodData::Card(domain::Card { card_cvc: Secret::new("12345".to_string()), ..utils::CCardType::default().0 }), @@ -332,7 +332,7 @@ async fn should_fail_payment_for_invalid_exp_month() { let response = CONNECTOR .make_payment( Some(types::PaymentsAuthorizeData { - payment_method_data: types::domain::PaymentMethodData::Card(domain::Card { + payment_method_data: domain::PaymentMethodData::Card(domain::Card { card_exp_month: Secret::new("20".to_string()), ..utils::CCardType::default().0 }), @@ -354,7 +354,7 @@ async fn should_fail_payment_for_incorrect_expiry_year() { let response = CONNECTOR .make_payment( Some(types::PaymentsAuthorizeData { - payment_method_data: types::domain::PaymentMethodData::Card(domain::Card { + payment_method_data: domain::PaymentMethodData::Card(domain::Card { card_exp_year: Secret::new("2000".to_string()), ..utils::CCardType::default().0 }), diff --git a/crates/router/tests/connectors/prophetpay.rs b/crates/router/tests/connectors/prophetpay.rs index 9a0e2dbfa0e..5e326c0bcd3 100644 --- a/crates/router/tests/connectors/prophetpay.rs +++ b/crates/router/tests/connectors/prophetpay.rs @@ -92,7 +92,7 @@ async fn should_sync_authorized_payment() { .psync_retry_till_status_matches( enums::AttemptStatus::Authorized, Some(types::PaymentsSyncData { - connector_transaction_id: router::types::ResponseId::ConnectorTransactionId( + connector_transaction_id: types::ResponseId::ConnectorTransactionId( txn_id.unwrap(), ), ..Default::default() @@ -212,7 +212,7 @@ async fn should_sync_auto_captured_payment() { .psync_retry_till_status_matches( enums::AttemptStatus::Charged, Some(types::PaymentsSyncData { - connector_transaction_id: router::types::ResponseId::ConnectorTransactionId( + connector_transaction_id: types::ResponseId::ConnectorTransactionId( txn_id.unwrap(), ), capture_method: Some(enums::CaptureMethod::Automatic), @@ -302,7 +302,7 @@ async fn should_fail_payment_for_incorrect_cvc() { let response = CONNECTOR .make_payment( Some(types::PaymentsAuthorizeData { - payment_method_data: types::domain::PaymentMethodData::Card(domain::Card { + payment_method_data: domain::PaymentMethodData::Card(domain::Card { card_cvc: Secret::new("12345".to_string()), ..utils::CCardType::default().0 }), @@ -324,7 +324,7 @@ async fn should_fail_payment_for_invalid_exp_month() { let response = CONNECTOR .make_payment( Some(types::PaymentsAuthorizeData { - payment_method_data: types::domain::PaymentMethodData::Card(domain::Card { + payment_method_data: domain::PaymentMethodData::Card(domain::Card { card_exp_month: Secret::new("20".to_string()), ..utils::CCardType::default().0 }), @@ -346,7 +346,7 @@ async fn should_fail_payment_for_incorrect_expiry_year() { let response = CONNECTOR .make_payment( Some(types::PaymentsAuthorizeData { - payment_method_data: types::domain::PaymentMethodData::Card(domain::Card { + payment_method_data: domain::PaymentMethodData::Card(domain::Card { card_exp_year: Secret::new("2000".to_string()), ..utils::CCardType::default().0 }), diff --git a/crates/router/tests/connectors/rapyd.rs b/crates/router/tests/connectors/rapyd.rs index 61bc7ccdf15..4b5357c51ec 100644 --- a/crates/router/tests/connectors/rapyd.rs +++ b/crates/router/tests/connectors/rapyd.rs @@ -42,7 +42,7 @@ async fn should_only_authorize_payment() { let response = Rapyd {} .authorize_payment( Some(types::PaymentsAuthorizeData { - payment_method_data: types::domain::PaymentMethodData::Card(domain::Card { + payment_method_data: domain::PaymentMethodData::Card(domain::Card { card_number: cards::CardNumber::from_str("4111111111111111").unwrap(), card_exp_month: Secret::new("02".to_string()), card_exp_year: Secret::new("2024".to_string()), @@ -52,7 +52,7 @@ async fn should_only_authorize_payment() { card_type: None, card_issuing_country: None, bank_code: None, - nick_name: Some(masking::Secret::new("nick_name".into())), + nick_name: Some(Secret::new("nick_name".into())), }), capture_method: Some(diesel_models::enums::CaptureMethod::Manual), ..utils::PaymentAuthorizeType::default().0 @@ -69,7 +69,7 @@ async fn should_authorize_and_capture_payment() { let response = Rapyd {} .make_payment( Some(types::PaymentsAuthorizeData { - payment_method_data: types::domain::PaymentMethodData::Card(domain::Card { + payment_method_data: domain::PaymentMethodData::Card(domain::Card { card_number: cards::CardNumber::from_str("4111111111111111").unwrap(), card_exp_month: Secret::new("02".to_string()), card_exp_year: Secret::new("2024".to_string()), @@ -79,7 +79,7 @@ async fn should_authorize_and_capture_payment() { card_type: None, card_issuing_country: None, bank_code: None, - nick_name: Some(masking::Secret::new("nick_name".into())), + nick_name: Some(Secret::new("nick_name".into())), }), ..utils::PaymentAuthorizeType::default().0 }), @@ -157,7 +157,7 @@ async fn should_fail_payment_for_incorrect_card_number() { let response = Rapyd {} .make_payment( Some(types::PaymentsAuthorizeData { - payment_method_data: types::domain::PaymentMethodData::Card(domain::Card { + payment_method_data: domain::PaymentMethodData::Card(domain::Card { card_number: cards::CardNumber::from_str("0000000000000000").unwrap(), ..utils::CCardType::default().0 }), diff --git a/crates/router/tests/connectors/shift4.rs b/crates/router/tests/connectors/shift4.rs index 8f11fbfa1d8..632e5389351 100644 --- a/crates/router/tests/connectors/shift4.rs +++ b/crates/router/tests/connectors/shift4.rs @@ -90,7 +90,7 @@ async fn should_sync_authorized_payment() { .psync_retry_till_status_matches( enums::AttemptStatus::Authorized, Some(types::PaymentsSyncData { - connector_transaction_id: router::types::ResponseId::ConnectorTransactionId( + connector_transaction_id: types::ResponseId::ConnectorTransactionId( txn_id.unwrap(), ), ..Default::default() @@ -114,7 +114,7 @@ async fn should_sync_auto_captured_payment() { .psync_retry_till_status_matches( enums::AttemptStatus::Charged, Some(types::PaymentsSyncData { - connector_transaction_id: router::types::ResponseId::ConnectorTransactionId( + connector_transaction_id: types::ResponseId::ConnectorTransactionId( txn_id.unwrap(), ), ..Default::default() @@ -151,7 +151,7 @@ async fn should_fail_payment_for_incorrect_card_number() { let response = CONNECTOR .make_payment( Some(types::PaymentsAuthorizeData { - payment_method_data: types::domain::PaymentMethodData::Card(domain::Card { + payment_method_data: domain::PaymentMethodData::Card(domain::Card { card_number: cards::CardNumber::from_str("4024007134364842").unwrap(), ..utils::CCardType::default().0 }), @@ -173,7 +173,7 @@ async fn should_succeed_payment_for_incorrect_cvc() { let response = CONNECTOR .make_payment( Some(types::PaymentsAuthorizeData { - payment_method_data: types::domain::PaymentMethodData::Card(domain::Card { + payment_method_data: domain::PaymentMethodData::Card(domain::Card { card_cvc: Secret::new("asdasd".to_string()), //shift4 accept invalid CVV as it doesn't accept CVV ..utils::CCardType::default().0 }), @@ -192,7 +192,7 @@ async fn should_fail_payment_for_invalid_exp_month() { let response = CONNECTOR .make_payment( Some(types::PaymentsAuthorizeData { - payment_method_data: types::domain::PaymentMethodData::Card(domain::Card { + payment_method_data: domain::PaymentMethodData::Card(domain::Card { card_exp_month: Secret::new("20".to_string()), ..utils::CCardType::default().0 }), @@ -214,7 +214,7 @@ async fn should_fail_payment_for_incorrect_expiry_year() { let response = CONNECTOR .make_payment( Some(types::PaymentsAuthorizeData { - payment_method_data: types::domain::PaymentMethodData::Card(domain::Card { + payment_method_data: domain::PaymentMethodData::Card(domain::Card { card_exp_year: Secret::new("2000".to_string()), ..utils::CCardType::default().0 }), diff --git a/crates/router/tests/connectors/square.rs b/crates/router/tests/connectors/square.rs index 01d90c73c42..2dcd93cc105 100644 --- a/crates/router/tests/connectors/square.rs +++ b/crates/router/tests/connectors/square.rs @@ -1,11 +1,7 @@ use std::{str::FromStr, time::Duration}; use masking::Secret; -use router::types::{ - self, - storage::{self, enums}, - PaymentsResponseData, -}; +use router::types::{self, storage::enums, PaymentsResponseData}; use test_utils::connector_auth::ConnectorAuthentication; use crate::utils::{self, get_connector_transaction_id, Connector, ConnectorActions}; @@ -71,7 +67,7 @@ fn token_details() -> Option { }), browser_info: None, amount: None, - currency: storage::enums::Currency::USD, + currency: enums::Currency::USD, }) } @@ -147,7 +143,7 @@ async fn should_sync_authorized_payment() { .psync_retry_till_status_matches( enums::AttemptStatus::Authorized, Some(types::PaymentsSyncData { - connector_transaction_id: router::types::ResponseId::ConnectorTransactionId( + connector_transaction_id: types::ResponseId::ConnectorTransactionId( txn_id.unwrap(), ), ..Default::default() @@ -291,7 +287,7 @@ async fn should_sync_auto_captured_payment() { .psync_retry_till_status_matches( enums::AttemptStatus::Charged, Some(types::PaymentsSyncData { - connector_transaction_id: router::types::ResponseId::ConnectorTransactionId( + connector_transaction_id: types::ResponseId::ConnectorTransactionId( txn_id.unwrap(), ), capture_method: Some(enums::CaptureMethod::Automatic), @@ -444,7 +440,7 @@ async fn should_fail_payment_for_incorrect_cvc() { }), browser_info: None, amount: None, - currency: storage::enums::Currency::USD, + currency: enums::Currency::USD, }), get_default_payment_info(None), ) @@ -475,7 +471,7 @@ async fn should_fail_payment_for_invalid_exp_month() { }), browser_info: None, amount: None, - currency: storage::enums::Currency::USD, + currency: enums::Currency::USD, }), get_default_payment_info(None), ) @@ -506,7 +502,7 @@ async fn should_fail_payment_for_incorrect_expiry_year() { }), browser_info: None, amount: None, - currency: storage::enums::Currency::USD, + currency: enums::Currency::USD, }), get_default_payment_info(None), ) diff --git a/crates/router/tests/connectors/stax.rs b/crates/router/tests/connectors/stax.rs index d16f209a49a..421ad9524c3 100644 --- a/crates/router/tests/connectors/stax.rs +++ b/crates/router/tests/connectors/stax.rs @@ -63,7 +63,7 @@ fn customer_details() -> Option { fn token_details() -> Option { Some(types::PaymentMethodTokenizationData { - payment_method_data: types::domain::PaymentMethodData::Card(domain::Card { + payment_method_data: domain::PaymentMethodData::Card(domain::Card { card_number: cards::CardNumber::from_str("4111111111111111").unwrap(), card_exp_month: Secret::new("04".to_string()), card_exp_year: Secret::new("2027".to_string()), @@ -167,7 +167,7 @@ async fn should_sync_authorized_payment() { .psync_retry_till_status_matches( enums::AttemptStatus::Authorized, Some(types::PaymentsSyncData { - connector_transaction_id: router::types::ResponseId::ConnectorTransactionId( + connector_transaction_id: types::ResponseId::ConnectorTransactionId( txn_id.unwrap(), ), ..Default::default() @@ -344,7 +344,7 @@ async fn should_sync_auto_captured_payment() { .psync_retry_till_status_matches( enums::AttemptStatus::Charged, Some(types::PaymentsSyncData { - connector_transaction_id: router::types::ResponseId::ConnectorTransactionId( + connector_transaction_id: types::ResponseId::ConnectorTransactionId( txn_id.unwrap(), ), capture_method: Some(enums::CaptureMethod::Automatic), @@ -473,7 +473,7 @@ async fn should_fail_payment_for_incorrect_cvc() { let token_response = CONNECTOR .create_connector_pm_token( Some(types::PaymentMethodTokenizationData { - payment_method_data: types::domain::PaymentMethodData::Card(domain::Card { + payment_method_data: domain::PaymentMethodData::Card(domain::Card { card_number: cards::CardNumber::from_str("4111111111111111").unwrap(), card_exp_month: Secret::new("11".to_string()), card_exp_year: Secret::new("2027".to_string()), @@ -511,7 +511,7 @@ async fn should_fail_payment_for_invalid_exp_month() { let token_response = CONNECTOR .create_connector_pm_token( Some(types::PaymentMethodTokenizationData { - payment_method_data: types::domain::PaymentMethodData::Card(domain::Card { + payment_method_data: domain::PaymentMethodData::Card(domain::Card { card_number: cards::CardNumber::from_str("4111111111111111").unwrap(), card_exp_month: Secret::new("20".to_string()), card_exp_year: Secret::new("2027".to_string()), @@ -549,7 +549,7 @@ async fn should_fail_payment_for_incorrect_expiry_year() { let token_response = CONNECTOR .create_connector_pm_token( Some(types::PaymentMethodTokenizationData { - payment_method_data: types::domain::PaymentMethodData::Card(domain::Card { + payment_method_data: domain::PaymentMethodData::Card(domain::Card { card_number: cards::CardNumber::from_str("4111111111111111").unwrap(), card_exp_month: Secret::new("04".to_string()), card_exp_year: Secret::new("2000".to_string()), diff --git a/crates/router/tests/connectors/stripe.rs b/crates/router/tests/connectors/stripe.rs index 3dcaeba45b0..2d6b3e0cfd3 100644 --- a/crates/router/tests/connectors/stripe.rs +++ b/crates/router/tests/connectors/stripe.rs @@ -37,7 +37,7 @@ impl utils::Connector for Stripe { fn get_payment_authorize_data() -> Option { Some(types::PaymentsAuthorizeData { - payment_method_data: types::domain::PaymentMethodData::Card(domain::Card { + payment_method_data: domain::PaymentMethodData::Card(domain::Card { card_number: cards::CardNumber::from_str("4242424242424242").unwrap(), ..utils::CCardType::default().0 }), @@ -100,7 +100,7 @@ async fn should_sync_authorized_payment() { .psync_retry_till_status_matches( enums::AttemptStatus::Authorized, Some(types::PaymentsSyncData { - connector_transaction_id: router::types::ResponseId::ConnectorTransactionId( + connector_transaction_id: types::ResponseId::ConnectorTransactionId( txn_id.unwrap(), ), ..Default::default() @@ -124,7 +124,7 @@ async fn should_sync_payment() { .psync_retry_till_status_matches( enums::AttemptStatus::Charged, Some(types::PaymentsSyncData { - connector_transaction_id: router::types::ResponseId::ConnectorTransactionId( + connector_transaction_id: types::ResponseId::ConnectorTransactionId( txn_id.unwrap(), ), ..Default::default() @@ -158,7 +158,7 @@ async fn should_fail_payment_for_incorrect_card_number() { let response = Stripe {} .make_payment( Some(types::PaymentsAuthorizeData { - payment_method_data: types::domain::PaymentMethodData::Card(domain::Card { + payment_method_data: domain::PaymentMethodData::Card(domain::Card { card_number: cards::CardNumber::from_str("4024007134364842").unwrap(), ..utils::CCardType::default().0 }), @@ -180,7 +180,7 @@ async fn should_fail_payment_for_invalid_exp_month() { let response = Stripe {} .make_payment( Some(types::PaymentsAuthorizeData { - payment_method_data: types::domain::PaymentMethodData::Card(domain::Card { + payment_method_data: domain::PaymentMethodData::Card(domain::Card { card_exp_month: Secret::new("13".to_string()), ..utils::CCardType::default().0 }), @@ -202,7 +202,7 @@ async fn should_fail_payment_for_invalid_exp_year() { let response = Stripe {} .make_payment( Some(types::PaymentsAuthorizeData { - payment_method_data: types::domain::PaymentMethodData::Card(domain::Card { + payment_method_data: domain::PaymentMethodData::Card(domain::Card { card_exp_year: Secret::new("2022".to_string()), ..utils::CCardType::default().0 }), @@ -221,7 +221,7 @@ async fn should_fail_payment_for_invalid_card_cvc() { let response = Stripe {} .make_payment( Some(types::PaymentsAuthorizeData { - payment_method_data: types::domain::PaymentMethodData::Card(domain::Card { + payment_method_data: domain::PaymentMethodData::Card(domain::Card { card_cvc: Secret::new("12".to_string()), ..utils::CCardType::default().0 }), diff --git a/crates/router/tests/connectors/trustpay.rs b/crates/router/tests/connectors/trustpay.rs index 48cd165767c..fba612863b4 100644 --- a/crates/router/tests/connectors/trustpay.rs +++ b/crates/router/tests/connectors/trustpay.rs @@ -12,12 +12,12 @@ use crate::{ struct TrustpayTest; impl ConnectorActions for TrustpayTest {} impl utils::Connector for TrustpayTest { - fn get_data(&self) -> types::api::ConnectorData { + fn get_data(&self) -> api::ConnectorData { use router::connector::Trustpay; - types::api::ConnectorData { + api::ConnectorData { connector: Box::new(&Trustpay), connector_name: types::Connector::Trustpay, - get_token: types::api::GetToken::Connector, + get_token: api::GetToken::Connector, merchant_connector_id: None, } } @@ -53,7 +53,7 @@ fn get_default_browser_info() -> BrowserInformation { fn get_default_payment_authorize_data() -> Option { Some(types::PaymentsAuthorizeData { - payment_method_data: types::domain::PaymentMethodData::Card(domain::Card { + payment_method_data: domain::PaymentMethodData::Card(domain::Card { card_number: cards::CardNumber::from_str("4200000000000000").unwrap(), card_exp_year: Secret::new("25".to_string()), card_cvc: Secret::new("123".to_string()), @@ -122,7 +122,7 @@ async fn should_sync_auto_captured_payment() { .psync_retry_till_status_matches( enums::AttemptStatus::Charged, Some(types::PaymentsSyncData { - connector_transaction_id: router::types::ResponseId::ConnectorTransactionId( + connector_transaction_id: types::ResponseId::ConnectorTransactionId( txn_id.unwrap(), ), ..Default::default() @@ -182,7 +182,7 @@ async fn should_sync_refund() { #[actix_web::test] async fn should_fail_payment_for_incorrect_card_number() { let payment_authorize_data = types::PaymentsAuthorizeData { - payment_method_data: types::domain::PaymentMethodData::Card(domain::Card { + payment_method_data: domain::PaymentMethodData::Card(domain::Card { card_number: cards::CardNumber::from_str("1234567891011").unwrap(), card_exp_year: Secret::new("25".to_string()), card_cvc: Secret::new("123".to_string()), @@ -205,7 +205,7 @@ async fn should_fail_payment_for_incorrect_card_number() { #[actix_web::test] async fn should_fail_payment_for_incorrect_expiry_year() { let payment_authorize_data = Some(types::PaymentsAuthorizeData { - payment_method_data: types::domain::PaymentMethodData::Card(domain::Card { + payment_method_data: domain::PaymentMethodData::Card(domain::Card { card_number: cards::CardNumber::from_str("4200000000000000").unwrap(), card_exp_year: Secret::new("22".to_string()), card_cvc: Secret::new("123".to_string()), diff --git a/crates/router/tests/connectors/tsys.rs b/crates/router/tests/connectors/tsys.rs index d7a688bd128..ef4f576ad9e 100644 --- a/crates/router/tests/connectors/tsys.rs +++ b/crates/router/tests/connectors/tsys.rs @@ -46,7 +46,7 @@ fn get_default_payment_info() -> Option { fn payment_method_details(amount: i64) -> Option { Some(types::PaymentsAuthorizeData { amount, - payment_method_data: types::domain::PaymentMethodData::Card(domain::Card { + payment_method_data: domain::PaymentMethodData::Card(domain::Card { card_number: CardNumber::from_str("4111111111111111").unwrap(), ..utils::CCardType::default().0 }), @@ -108,7 +108,7 @@ async fn should_sync_authorized_payment() { .psync_retry_till_status_matches( enums::AttemptStatus::Authorized, Some(types::PaymentsSyncData { - connector_transaction_id: router::types::ResponseId::ConnectorTransactionId( + connector_transaction_id: types::ResponseId::ConnectorTransactionId( txn_id.unwrap(), ), ..Default::default() @@ -244,7 +244,7 @@ async fn should_sync_auto_captured_payment() { .psync_retry_till_status_matches( enums::AttemptStatus::Charged, Some(types::PaymentsSyncData { - connector_transaction_id: router::types::ResponseId::ConnectorTransactionId( + connector_transaction_id: types::ResponseId::ConnectorTransactionId( txn_id.unwrap(), ), capture_method: Some(enums::CaptureMethod::Automatic), @@ -346,7 +346,7 @@ async fn should_fail_payment_for_incorrect_cvc() { let response = CONNECTOR .make_payment( Some(types::PaymentsAuthorizeData { - payment_method_data: types::domain::PaymentMethodData::Card(domain::Card { + payment_method_data: domain::PaymentMethodData::Card(domain::Card { card_cvc: Secret::new("".to_string()), ..utils::CCardType::default().0 }), @@ -368,7 +368,7 @@ async fn should_fail_payment_for_invalid_exp_month() { let response = CONNECTOR .make_payment( Some(types::PaymentsAuthorizeData { - payment_method_data: types::domain::PaymentMethodData::Card(domain::Card { + payment_method_data: domain::PaymentMethodData::Card(domain::Card { card_exp_month: Secret::new("20".to_string()), ..utils::CCardType::default().0 }), @@ -390,7 +390,7 @@ async fn should_fail_payment_for_incorrect_expiry_year() { let response = CONNECTOR .make_payment( Some(types::PaymentsAuthorizeData { - payment_method_data: types::domain::PaymentMethodData::Card(domain::Card { + payment_method_data: domain::PaymentMethodData::Card(domain::Card { card_exp_year: Secret::new("abcd".to_string()), ..utils::CCardType::default().0 }), diff --git a/crates/router/tests/connectors/utils.rs b/crates/router/tests/connectors/utils.rs index 9678eca73f3..3196c4c763b 100644 --- a/crates/router/tests/connectors/utils.rs +++ b/crates/router/tests/connectors/utils.rs @@ -8,7 +8,7 @@ use masking::Secret; use router::core::utils as core_utils; use router::{ configs::settings::Settings, - core::{errors, errors::ConnectorError, payments}, + core::{errors::ConnectorError, payments}, db::StorageImpl, routes, services, types::{self, storage::enums, AccessToken, PaymentAddress, RouterData}, @@ -57,7 +57,7 @@ pub struct PaymentInfo { impl PaymentInfo { pub fn with_default_billing_name() -> Self { Self { - address: Some(types::PaymentAddress::new( + address: Some(PaymentAddress::new( None, None, Some(types::api::Address { @@ -215,7 +215,7 @@ pub trait ConnectorActions: Connector { } tokio::time::sleep(Duration::from_secs(self.get_request_interval())).await; } - Err(errors::ConnectorError::ProcessingStepFailed(None).into()) + Err(ConnectorError::ProcessingStepFailed(None).into()) } async fn capture_payment( @@ -453,7 +453,7 @@ pub trait ConnectorActions: Connector { } tokio::time::sleep(Duration::from_secs(self.get_request_interval())).await; } - Err(errors::ConnectorError::ProcessingStepFailed(None).into()) + Err(ConnectorError::ProcessingStepFailed(None).into()) } #[cfg(feature = "payouts")] @@ -523,7 +523,7 @@ pub trait ConnectorActions: Connector { .unwrap(), connector_meta_data: info .clone() - .and_then(|a| a.connector_meta_data.map(masking::Secret::new)), + .and_then(|a| a.connector_meta_data.map(Secret::new)), amount_captured: None, access_token: info.clone().and_then(|a| a.access_token), session_token: None, @@ -902,7 +902,7 @@ impl Default for CCardType { card_type: None, card_issuing_country: None, bank_code: None, - nick_name: Some(masking::Secret::new("nick_name".into())), + nick_name: Some(Secret::new("nick_name".into())), }) } } diff --git a/crates/router/tests/connectors/volt.rs b/crates/router/tests/connectors/volt.rs index 7bcdf63d918..efe6d603e70 100644 --- a/crates/router/tests/connectors/volt.rs +++ b/crates/router/tests/connectors/volt.rs @@ -92,7 +92,7 @@ async fn should_sync_authorized_payment() { .psync_retry_till_status_matches( enums::AttemptStatus::Authorized, Some(types::PaymentsSyncData { - connector_transaction_id: router::types::ResponseId::ConnectorTransactionId( + connector_transaction_id: types::ResponseId::ConnectorTransactionId( txn_id.unwrap(), ), ..Default::default() @@ -212,7 +212,7 @@ async fn should_sync_auto_captured_payment() { .psync_retry_till_status_matches( enums::AttemptStatus::Charged, Some(types::PaymentsSyncData { - connector_transaction_id: router::types::ResponseId::ConnectorTransactionId( + connector_transaction_id: types::ResponseId::ConnectorTransactionId( txn_id.unwrap(), ), capture_method: Some(enums::CaptureMethod::Automatic), @@ -302,7 +302,7 @@ async fn should_fail_payment_for_incorrect_cvc() { let response = CONNECTOR .make_payment( Some(types::PaymentsAuthorizeData { - payment_method_data: types::domain::PaymentMethodData::Card(domain::Card { + payment_method_data: domain::PaymentMethodData::Card(domain::Card { card_cvc: Secret::new("12345".to_string()), ..utils::CCardType::default().0 }), @@ -324,7 +324,7 @@ async fn should_fail_payment_for_invalid_exp_month() { let response = CONNECTOR .make_payment( Some(types::PaymentsAuthorizeData { - payment_method_data: types::domain::PaymentMethodData::Card(domain::Card { + payment_method_data: domain::PaymentMethodData::Card(domain::Card { card_exp_month: Secret::new("20".to_string()), ..utils::CCardType::default().0 }), @@ -346,7 +346,7 @@ async fn should_fail_payment_for_incorrect_expiry_year() { let response = CONNECTOR .make_payment( Some(types::PaymentsAuthorizeData { - payment_method_data: types::domain::PaymentMethodData::Card(domain::Card { + payment_method_data: domain::PaymentMethodData::Card(domain::Card { card_exp_year: Secret::new("2000".to_string()), ..utils::CCardType::default().0 }), diff --git a/crates/router/tests/connectors/wise.rs b/crates/router/tests/connectors/wise.rs index b85fb4f1c96..fd5bbc109d9 100644 --- a/crates/router/tests/connectors/wise.rs +++ b/crates/router/tests/connectors/wise.rs @@ -1,38 +1,36 @@ -#[cfg(feature = "payouts")] use api_models::payments::{Address, AddressDetails}; -#[cfg(feature = "payouts")] use masking::Secret; -use router::types; -#[cfg(feature = "payouts")] -use router::types::{api, storage::enums, PaymentAddress}; +use router::{ + types, + types::{api, storage::enums, PaymentAddress}, +}; -#[cfg(feature = "payouts")] -use crate::utils::PaymentInfo; use crate::{ connector_auth, - utils::{self, ConnectorActions}, + utils::{self, ConnectorActions, PaymentInfo}, }; struct WiseTest; + impl ConnectorActions for WiseTest {} + impl utils::Connector for WiseTest { - fn get_data(&self) -> types::api::ConnectorData { + fn get_data(&self) -> api::ConnectorData { use router::connector::Adyen; - types::api::ConnectorData { + api::ConnectorData { connector: Box::new(&Adyen), connector_name: types::Connector::Adyen, - get_token: types::api::GetToken::Connector, + get_token: api::GetToken::Connector, merchant_connector_id: None, } } - #[cfg(feature = "payouts")] - fn get_payout_data(&self) -> Option { + fn get_payout_data(&self) -> Option { use router::connector::Wise; - Some(types::api::ConnectorData { + Some(api::ConnectorData { connector: Box::new(&Wise), connector_name: types::Connector::Wise, - get_token: types::api::GetToken::Connector, + get_token: api::GetToken::Connector, merchant_connector_id: None, }) } @@ -52,7 +50,6 @@ impl utils::Connector for WiseTest { } impl WiseTest { - #[cfg(feature = "payouts")] fn get_payout_info() -> Option { Some(PaymentInfo { country: Some(api_models::enums::CountryAlpha2::NL), @@ -86,12 +83,11 @@ impl WiseTest { } } -#[cfg(feature = "payouts")] static CONNECTOR: WiseTest = WiseTest {}; /******************** Payouts test cases ********************/ // Creates a recipient at connector's end -#[cfg(feature = "payouts")] + #[actix_web::test] async fn should_create_payout_recipient() { let payout_type = enums::PayoutType::Bank; @@ -107,7 +103,7 @@ async fn should_create_payout_recipient() { } // Create BACS payout -#[cfg(feature = "payouts")] + #[actix_web::test] async fn should_create_bacs_payout() { let payout_type = enums::PayoutType::Bank; @@ -138,7 +134,7 @@ async fn should_create_bacs_payout() { } // Create and fulfill BACS payout -#[cfg(feature = "payouts")] + #[actix_web::test] async fn should_create_and_fulfill_bacs_payout() { let payout_type = enums::PayoutType::Bank; diff --git a/crates/router/tests/connectors/worldline.rs b/crates/router/tests/connectors/worldline.rs index 21a5a574122..4f8119bfc52 100644 --- a/crates/router/tests/connectors/worldline.rs +++ b/crates/router/tests/connectors/worldline.rs @@ -81,7 +81,7 @@ impl WorldlineTest { card_type: None, card_issuing_country: None, bank_code: None, - nick_name: Some(masking::Secret::new("nick_name".into())), + nick_name: Some(Secret::new("nick_name".into())), }), confirm: true, statement_descriptor_suffix: None, @@ -231,7 +231,7 @@ async fn should_sync_manual_auth_payment() { let sync_response = connector .sync_payment( Some(types::PaymentsSyncData { - connector_transaction_id: router::types::ResponseId::ConnectorTransactionId( + connector_transaction_id: types::ResponseId::ConnectorTransactionId( connector_payment_id, ), capture_method: Some(enums::CaptureMethod::Manual), @@ -264,7 +264,7 @@ async fn should_sync_auto_auth_payment() { let sync_response = connector .sync_payment( Some(types::PaymentsSyncData { - connector_transaction_id: router::types::ResponseId::ConnectorTransactionId( + connector_transaction_id: types::ResponseId::ConnectorTransactionId( connector_payment_id, ), capture_method: Some(enums::CaptureMethod::Automatic), diff --git a/crates/router/tests/connectors/worldpay.rs b/crates/router/tests/connectors/worldpay.rs index 649b1bebbb1..571de7d959d 100644 --- a/crates/router/tests/connectors/worldpay.rs +++ b/crates/router/tests/connectors/worldpay.rs @@ -62,7 +62,7 @@ async fn should_authorize_gpay_payment() { let response = conn .authorize_payment( Some(types::PaymentsAuthorizeData { - payment_method_data: types::domain::PaymentMethodData::Wallet( + payment_method_data: domain::PaymentMethodData::Wallet( domain::WalletData::GooglePay(domain::GooglePayWalletData { pm_type: "CARD".to_string(), description: "Visa1234567890".to_string(), @@ -97,7 +97,7 @@ async fn should_authorize_applepay_payment() { let response = conn .authorize_payment( Some(types::PaymentsAuthorizeData { - payment_method_data: types::domain::PaymentMethodData::Wallet( + payment_method_data: domain::PaymentMethodData::Wallet( domain::WalletData::ApplePay(domain::ApplePayWalletData { payment_data: "someData".to_string(), transaction_identifier: "someId".to_string(), @@ -149,7 +149,7 @@ async fn should_sync_payment() { let response = connector .sync_payment( Some(types::PaymentsSyncData { - connector_transaction_id: router::types::ResponseId::ConnectorTransactionId( + connector_transaction_id: types::ResponseId::ConnectorTransactionId( "112233".to_string(), ), ..Default::default() diff --git a/crates/router/tests/connectors/zen.rs b/crates/router/tests/connectors/zen.rs index e076aee42c4..b11b78025d0 100644 --- a/crates/router/tests/connectors/zen.rs +++ b/crates/router/tests/connectors/zen.rs @@ -95,7 +95,7 @@ async fn should_sync_authorized_payment() { .psync_retry_till_status_matches( enums::AttemptStatus::Authorized, Some(types::PaymentsSyncData { - connector_transaction_id: router::types::ResponseId::ConnectorTransactionId( + connector_transaction_id: types::ResponseId::ConnectorTransactionId( txn_id.unwrap(), ), encoded_data: None, @@ -211,7 +211,7 @@ async fn should_sync_auto_captured_payment() { .psync_retry_till_status_matches( enums::AttemptStatus::Charged, Some(types::PaymentsSyncData { - connector_transaction_id: router::types::ResponseId::ConnectorTransactionId( + connector_transaction_id: types::ResponseId::ConnectorTransactionId( txn_id.unwrap(), ), encoded_data: None, @@ -310,7 +310,7 @@ async fn should_fail_payment_for_incorrect_card_number() { let response = CONNECTOR .make_payment( Some(types::PaymentsAuthorizeData { - payment_method_data: types::domain::PaymentMethodData::Card(domain::Card { + payment_method_data: domain::PaymentMethodData::Card(domain::Card { card_number: CardNumber::from_str("1234567891011").unwrap(), ..utils::CCardType::default().0 }), @@ -352,7 +352,7 @@ async fn should_fail_payment_for_incorrect_cvc() { let response = CONNECTOR .make_payment( Some(types::PaymentsAuthorizeData { - payment_method_data: types::domain::PaymentMethodData::Card(domain::Card { + payment_method_data: domain::PaymentMethodData::Card(domain::Card { card_cvc: Secret::new("12345".to_string()), ..utils::CCardType::default().0 }), @@ -394,7 +394,7 @@ async fn should_fail_payment_for_invalid_exp_month() { let response = CONNECTOR .make_payment( Some(types::PaymentsAuthorizeData { - payment_method_data: types::domain::PaymentMethodData::Card(domain::Card { + payment_method_data: domain::PaymentMethodData::Card(domain::Card { card_exp_month: Secret::new("20".to_string()), ..utils::CCardType::default().0 }), @@ -436,7 +436,7 @@ async fn should_fail_payment_for_incorrect_expiry_year() { let response = CONNECTOR .make_payment( Some(types::PaymentsAuthorizeData { - payment_method_data: types::domain::PaymentMethodData::Card(domain::Card { + payment_method_data: domain::PaymentMethodData::Card(domain::Card { card_exp_year: Secret::new("2000".to_string()), ..utils::CCardType::default().0 }), diff --git a/crates/router/tests/payments.rs b/crates/router/tests/payments.rs index 646a11d4cdd..f4325d01b90 100644 --- a/crates/router/tests/payments.rs +++ b/crates/router/tests/payments.rs @@ -26,7 +26,7 @@ use uuid::Uuid; async fn payments_create_stripe() { Box::pin(utils::setup()).await; - let payment_id = format!("test_{}", uuid::Uuid::new_v4()); + let payment_id = format!("test_{}", Uuid::new_v4()); let api_key = ("API-KEY", "MySecretApiKey"); let request = serde_json::json!({ @@ -95,7 +95,7 @@ async fn payments_create_stripe() { async fn payments_create_adyen() { Box::pin(utils::setup()).await; - let payment_id = format!("test_{}", uuid::Uuid::new_v4()); + let payment_id = format!("test_{}", Uuid::new_v4()); let api_key = ("API-KEY", "321"); let request = serde_json::json!({ @@ -164,7 +164,7 @@ async fn payments_create_adyen() { async fn payments_create_fail() { Box::pin(utils::setup()).await; - let payment_id = format!("test_{}", uuid::Uuid::new_v4()); + let payment_id = format!("test_{}", Uuid::new_v4()); let api_key = ("API-KEY", "MySecretApiKey"); let invalid_request = serde_json::json!({ diff --git a/crates/router/tests/payments2.rs b/crates/router/tests/payments2.rs index 9b622d11fd1..56ab453a0f5 100644 --- a/crates/router/tests/payments2.rs +++ b/crates/router/tests/payments2.rs @@ -14,7 +14,7 @@ use uuid::Uuid; #[test] fn connector_list() { - let connector_list = router::types::ConnectorsList { + let connector_list = types::ConnectorsList { connectors: vec![String::from("stripe"), "adyen".to_string()], }; @@ -22,7 +22,7 @@ fn connector_list() { println!("{}", &json); - let newlist: router::types::ConnectorsList = serde_json::from_str(&json).unwrap(); + let newlist: types::ConnectorsList = serde_json::from_str(&json).unwrap(); println!("{newlist:#?}"); assert_eq!(true, true); @@ -125,7 +125,7 @@ async fn payments_create_core() { }; let expected_response = services::ApplicationResponse::JsonWithHeaders((expected_response, vec![])); - let actual_response = Box::pin(router::core::payments::payments_core::< + let actual_response = Box::pin(payments::payments_core::< api::Authorize, api::PaymentsResponse, _, @@ -316,7 +316,7 @@ async fn payments_create_core_adyen_no_redirect() { }, vec![], )); - let actual_response = Box::pin(router::core::payments::payments_core::< + let actual_response = Box::pin(payments::payments_core::< api::Authorize, api::PaymentsResponse, _, diff --git a/crates/router_derive/src/macros/api_error/helpers.rs b/crates/router_derive/src/macros/api_error/helpers.rs index 5781d786ee5..deb1b349cb1 100644 --- a/crates/router_derive/src/macros/api_error/helpers.rs +++ b/crates/router_derive/src/macros/api_error/helpers.rs @@ -260,9 +260,9 @@ pub(super) fn get_unused_fields( ignore: &std::collections::HashSet, ) -> Vec { let fields = match fields { - syn::Fields::Unit => Vec::new(), - syn::Fields::Unnamed(_) => Vec::new(), - syn::Fields::Named(fields) => fields.named.iter().cloned().collect(), + Fields::Unit => Vec::new(), + Fields::Unnamed(_) => Vec::new(), + Fields::Named(fields) => fields.named.iter().cloned().collect(), }; fields .iter() diff --git a/crates/router_derive/src/macros/helpers.rs b/crates/router_derive/src/macros/helpers.rs index b6490c4d629..1cb7e21bb9d 100644 --- a/crates/router_derive/src/macros/helpers.rs +++ b/crates/router_derive/src/macros/helpers.rs @@ -58,7 +58,7 @@ pub(super) fn get_struct_fields( Ok(named.to_owned()) } else { Err(syn::Error::new( - proc_macro2::Span::call_site(), + Span::call_site(), "This macro cannot be used on structs with no fields", )) } diff --git a/crates/router_derive/src/macros/try_get_enum.rs b/crates/router_derive/src/macros/try_get_enum.rs index f607b7f06c9..bdfbdb66938 100644 --- a/crates/router_derive/src/macros/try_get_enum.rs +++ b/crates/router_derive/src/macros/try_get_enum.rs @@ -68,7 +68,7 @@ pub fn try_get_enum_variant( let try_into_fn = syn::Ident::new( &format!("try_into_{}", variant_name.to_string().to_lowercase()), - proc_macro2::Span::call_site(), + Span::call_site(), ); Ok(quote::quote! { diff --git a/crates/router_env/src/logger/setup.rs b/crates/router_env/src/logger/setup.rs index af77991e804..3a9e719db18 100644 --- a/crates/router_env/src/logger/setup.rs +++ b/crates/router_env/src/logger/setup.rs @@ -265,7 +265,7 @@ fn setup_tracing_pipeline( .with_exporter(get_opentelemetry_exporter(config)) .with_batch_config(batch_config) .with_trace_config(trace_config) - .install_batch(opentelemetry::runtime::TokioCurrentThread) + .install_batch(runtime::TokioCurrentThread) .map(|tracer| tracing_opentelemetry::layer().with_tracer(tracer)); if config.ignore_errors { diff --git a/crates/scheduler/src/producer.rs b/crates/scheduler/src/producer.rs index fd2b9b654a4..397aab84e91 100644 --- a/crates/scheduler/src/producer.rs +++ b/crates/scheduler/src/producer.rs @@ -43,11 +43,10 @@ where tokio::time::sleep(Duration::from_millis(timeout.sample(&mut rng))).await; - let mut interval = tokio::time::interval(std::time::Duration::from_millis( - scheduler_settings.loop_interval, - )); + let mut interval = + tokio::time::interval(Duration::from_millis(scheduler_settings.loop_interval)); - let mut shutdown_interval = tokio::time::interval(std::time::Duration::from_millis( + let mut shutdown_interval = tokio::time::interval(Duration::from_millis( scheduler_settings.graceful_shutdown_interval, )); diff --git a/crates/scheduler/src/utils.rs b/crates/scheduler/src/utils.rs index 11924dd1be2..8b80b7c72c1 100644 --- a/crates/scheduler/src/utils.rs +++ b/crates/scheduler/src/utils.rs @@ -254,7 +254,7 @@ pub fn get_time_from_delta(delta: Option) -> Option( +pub async fn consumer_operation_handler( state: T, settings: sync::Arc, error_handler_fun: E, @@ -263,7 +263,7 @@ pub async fn consumer_operation_handler( ) where // Error handler function E: FnOnce(error_stack::Report), - T: SchedulerAppState, + T: SchedulerAppState + Send + Sync + 'static, { consumer_operation_counter.fetch_add(1, atomic::Ordering::SeqCst); let start_time = std_time::Instant::now(); diff --git a/crates/storage_impl/src/errors.rs b/crates/storage_impl/src/errors.rs index 41099bea75f..4356b6b7997 100644 --- a/crates/storage_impl/src/errors.rs +++ b/crates/storage_impl/src/errors.rs @@ -8,7 +8,7 @@ use hyperswitch_domain_models::errors::StorageError as DataStorageError; pub use redis_interface::errors::RedisError; use router_env::opentelemetry::metrics::MetricsError; -use crate::{errors as storage_errors, store::errors::DatabaseError}; +use crate::store::errors::DatabaseError; pub type ApplicationResult = Result; @@ -56,20 +56,16 @@ impl Into for &StorageError { fn into(self) -> DataStorageError { match self { StorageError::DatabaseError(i) => match i.current_context() { - storage_errors::DatabaseError::DatabaseConnectionError => { - DataStorageError::DatabaseConnectionError - } + DatabaseError::DatabaseConnectionError => DataStorageError::DatabaseConnectionError, // TODO: Update this error type to encompass & propagate the missing type (instead of generic `db value not found`) - storage_errors::DatabaseError::NotFound => { + DatabaseError::NotFound => { DataStorageError::ValueNotFound(String::from("db value not found")) } // TODO: Update this error type to encompass & propagate the duplicate type (instead of generic `db value not found`) - storage_errors::DatabaseError::UniqueViolation => { - DataStorageError::DuplicateValue { - entity: "db entity", - key: None, - } - } + DatabaseError::UniqueViolation => DataStorageError::DuplicateValue { + entity: "db entity", + key: None, + }, err => DataStorageError::DatabaseError(error_stack::report!(*err)), }, StorageError::ValueNotFound(i) => DataStorageError::ValueNotFound(i.clone()), diff --git a/crates/storage_impl/src/lib.rs b/crates/storage_impl/src/lib.rs index e35d1f41511..7a3ef30bef3 100644 --- a/crates/storage_impl/src/lib.rs +++ b/crates/storage_impl/src/lib.rs @@ -217,7 +217,7 @@ impl KVRouterStore { partition_key: redis::kv_store::PartitionKey<'_>, ) -> error_stack::Result<(), RedisError> where - R: crate::redis::kv_store::KvStorePartition, + R: redis::kv_store::KvStorePartition, { let global_id = format!("{}", partition_key); let request_id = self.request_id.clone().unwrap_or_default(); diff --git a/crates/storage_impl/src/mock_db.rs b/crates/storage_impl/src/mock_db.rs index 5cf0123eb7b..3657201f878 100644 --- a/crates/storage_impl/src/mock_db.rs +++ b/crates/storage_impl/src/mock_db.rs @@ -41,9 +41,9 @@ pub struct MockDb { pub disputes: Arc>>, pub lockers: Arc>>, pub mandates: Arc>>, - pub captures: Arc>>, - pub merchant_key_store: Arc>>, - pub business_profiles: Arc>>, + pub captures: Arc>>, + pub merchant_key_store: Arc>>, + pub business_profiles: Arc>>, pub reverse_lookups: Arc>>, pub payment_link: Arc>>, pub organizations: Arc>>, diff --git a/crates/storage_impl/src/payments/payment_intent.rs b/crates/storage_impl/src/payments/payment_intent.rs index fcc4ec63ff2..2cbcbaaf01e 100644 --- a/crates/storage_impl/src/payments/payment_intent.rs +++ b/crates/storage_impl/src/payments/payment_intent.rs @@ -257,7 +257,7 @@ impl PaymentIntentInterface for KVRouterStore { _storage_scheme: MerchantStorageScheme, ) -> error_stack::Result { match payment.active_attempt.clone() { - hyperswitch_domain_models::RemoteStorageObject::ForeignID(attempt_id) => { + RemoteStorageObject::ForeignID(attempt_id) => { let conn = pg_connection_read(self).await?; let pa = DieselPaymentAttempt::find_by_merchant_id_attempt_id( @@ -271,11 +271,10 @@ impl PaymentIntentInterface for KVRouterStore { er.change_context(new_err) }) .map(PaymentAttempt::from_storage_model)?; - payment.active_attempt = - hyperswitch_domain_models::RemoteStorageObject::Object(pa.clone()); + payment.active_attempt = RemoteStorageObject::Object(pa.clone()); Ok(pa) } - hyperswitch_domain_models::RemoteStorageObject::Object(pa) => Ok(pa.clone()), + RemoteStorageObject::Object(pa) => Ok(pa.clone()), } } @@ -397,7 +396,7 @@ impl PaymentIntentInterface for crate::RouterStore { _storage_scheme: MerchantStorageScheme, ) -> error_stack::Result { match &payment.active_attempt { - hyperswitch_domain_models::RemoteStorageObject::ForeignID(attempt_id) => { + RemoteStorageObject::ForeignID(attempt_id) => { let conn = pg_connection_read(self).await?; let pa = DieselPaymentAttempt::find_by_merchant_id_attempt_id( @@ -411,11 +410,10 @@ impl PaymentIntentInterface for crate::RouterStore { er.change_context(new_err) }) .map(PaymentAttempt::from_storage_model)?; - payment.active_attempt = - hyperswitch_domain_models::RemoteStorageObject::Object(pa.clone()); + payment.active_attempt = RemoteStorageObject::Object(pa.clone()); Ok(pa) } - hyperswitch_domain_models::RemoteStorageObject::Object(pa) => Ok(pa.clone()), + RemoteStorageObject::Object(pa) => Ok(pa.clone()), } } diff --git a/crates/test_utils/src/newman_runner.rs b/crates/test_utils/src/newman_runner.rs index e70c630cffe..c90292683ff 100644 --- a/crates/test_utils/src/newman_runner.rs +++ b/crates/test_utils/src/newman_runner.rs @@ -63,7 +63,7 @@ fn get_dir_path(name: impl AsRef) -> String { // This function currently allows you to add only custom headers. // In future, as we scale, this can be modified based on the need -fn insert_content(dir: T, content_to_insert: U) -> std::io::Result<()> +fn insert_content(dir: T, content_to_insert: U) -> io::Result<()> where T: AsRef, U: AsRef, @@ -72,7 +72,7 @@ where let file_path = dir.as_ref().join(file_name); // Open the file in write mode or create it if it doesn't exist - let mut file = std::fs::OpenOptions::new() + let mut file = OpenOptions::new() .append(true) .create(true) .open(file_path)?; From 5d7481ec29021f426eea9acd784f782c533e869d Mon Sep 17 00:00:00 2001 From: likhinbopanna <131246334+likhinbopanna@users.noreply.github.com> Date: Tue, 7 May 2024 17:14:00 +0530 Subject: [PATCH 18/34] ci(cypress): Added 3DS manual capture flow (#4481) Co-authored-by: Likhin Bopanna --- .../00015-ThreeDSManualCapture.cy.js | 152 ++++++++++++++++++ .../cypress/e2e/ConnectorUtils/Adyen.js | 2 +- .../e2e/ConnectorUtils/BankOfAmerica.js | 2 +- .../cypress/e2e/ConnectorUtils/Bluesnap.js | 2 +- .../cypress/e2e/ConnectorUtils/Cybersource.js | 2 +- .../cypress/e2e/ConnectorUtils/Nmi.js | 2 +- .../cypress/e2e/ConnectorUtils/Paypal.js | 2 +- .../cypress/e2e/ConnectorUtils/Stripe.js | 2 +- .../cypress/e2e/ConnectorUtils/Trustpay.js | 2 +- .../cypress/fixtures/create-confirm-body.json | 3 +- cypress-tests/cypress/support/commands.js | 2 + 11 files changed, 163 insertions(+), 10 deletions(-) create mode 100644 cypress-tests/cypress/e2e/ConnectorTest/00015-ThreeDSManualCapture.cy.js diff --git a/cypress-tests/cypress/e2e/ConnectorTest/00015-ThreeDSManualCapture.cy.js b/cypress-tests/cypress/e2e/ConnectorTest/00015-ThreeDSManualCapture.cy.js new file mode 100644 index 00000000000..9b3a29a92e0 --- /dev/null +++ b/cypress-tests/cypress/e2e/ConnectorTest/00015-ThreeDSManualCapture.cy.js @@ -0,0 +1,152 @@ +import createPaymentBody from "../../fixtures/create-payment-body.json"; +import createConfirmPaymentBody from "../../fixtures/create-confirm-body.json"; +import confirmBody from "../../fixtures/confirm-body.json"; +import getConnectorDetails from "../ConnectorUtils/utils"; +import State from "../../utils/State"; +import captureBody from "../../fixtures/capture-flow-body.json"; + +let globalState; + +describe("Card - ThreeDS Manual payment flow test", () => { + + before("seed global state", () => { + cy.task('getGlobalState').then((state) => { + globalState = new State(state); + console.log("seeding globalState -> " + JSON.stringify(globalState)); + }) + }) + + after("flush global state", () => { + console.log("flushing globalState -> " + JSON.stringify(globalState)); + cy.task('setGlobalState', globalState.data); + }) + + context("Card - ThreeDS Manual Full Capture payment flow test", () => { + context("payment Create and Confirm", () => { + it("create-payment-call-test", () => { + let det = getConnectorDetails(globalState.get("connectorId"))["3DS"]; + cy.createPaymentIntentTest(createPaymentBody, det, "three_ds", "manual", globalState); + }); + + it("payment_methods-call-test", () => { + cy.paymentMethodsCallTest(globalState); + }); + + it("confirm-call-test", () => { + let det = getConnectorDetails(globalState.get("connectorId"))["3DS"]; + cy.confirmCallTest(confirmBody, det, true, globalState); + }); + + it("Handle redirection", () => { + let expected_redirection = confirmBody["return_url"]; + cy.handleRedirection(globalState, expected_redirection); + }) + + it("retrieve-payment-call-test", () => { + cy.retrievePaymentCallTest(globalState); + }); + + it("capture-call-test", () => { + let det = getConnectorDetails(globalState.get("connectorId"))["3DS"]; + cy.captureCallTest(captureBody, 6500, det.paymentSuccessfulStatus, globalState); + }); + + it("retrieve-payment-call-test", () => { + cy.retrievePaymentCallTest(globalState); + }); + + }); + + context("Payment Create+Confirm", () => { + it("create+confirm-payment-call-test", () => { + let det = getConnectorDetails(globalState.get("connectorId"))["3DS"]; + cy.createConfirmPaymentTest(createConfirmPaymentBody, det, "three_ds", "manual", globalState); + }); + + it("Handle redirection", () => { + let expected_redirection = createConfirmPaymentBody["return_url"]; + cy.handleRedirection(globalState, expected_redirection); + }) + + + it("retrieve-payment-call-test", () => { + cy.retrievePaymentCallTest(globalState); + }); + + it("capture-call-test", () => { + let det = getConnectorDetails(globalState.get("connectorId"))["3DS"]; + cy.captureCallTest(captureBody, 6540, det.paymentSuccessfulStatus, globalState); + }); + + it("retrieve-payment-call-test", () => { + cy.retrievePaymentCallTest(globalState); + }); + }); + + + }); + + context("Card - ThreeDS Manual Partial Capture payment flow test - Create and Confirm", () => { + context("payment Create and Payment Confirm", () => { + it("create-payment-call-test", () => { + let det = getConnectorDetails(globalState.get("connectorId"))["3DS"]; + cy.createPaymentIntentTest(createPaymentBody, det, "three_ds", "manual", globalState); + }); + + it("payment_methods-call-test", () => { + cy.paymentMethodsCallTest(globalState); + }); + + it("confirm-call-test", () => { + let det = getConnectorDetails(globalState.get("connectorId"))["3DS"]; + cy.confirmCallTest(confirmBody, det, true, globalState); + }); + + it("Handle redirection", () => { + let expected_redirection = confirmBody["return_url"]; + cy.handleRedirection(globalState, expected_redirection); + }) + + it("retrieve-payment-call-test", () => { + cy.retrievePaymentCallTest(globalState); + }); + + it("capture-call-test", () => { + let det = getConnectorDetails(globalState.get("connectorId"))["3DS"]; + cy.captureCallTest(captureBody, 100, det.paymentSuccessfulStatus, globalState); + }); + + it("retrieve-payment-call-test", () => { + cy.retrievePaymentCallTest(globalState); + }); + }); + + context("payment + Confirm", () => { + it("create+confirm-payment-call-test", () => { + let det = getConnectorDetails(globalState.get("connectorId"))["3DS"]; + cy.createConfirmPaymentTest(createConfirmPaymentBody, det, "three_ds", "manual", globalState); + }); + + it("Handle redirection", () => { + let expected_redirection = createConfirmPaymentBody["return_url"]; + cy.handleRedirection(globalState, expected_redirection); + }) + + it("retrieve-payment-call-test", () => { + cy.retrievePaymentCallTest(globalState); + }); + + it("capture-call-test", () => { + let det = getConnectorDetails(globalState.get("connectorId"))["3DS"]; + cy.captureCallTest(captureBody, 5000, det.paymentSuccessfulStatus, globalState); + }); + + it("retrieve-payment-call-test", () => { + cy.retrievePaymentCallTest(globalState); + }); + + }); + + + }); +}); \ No newline at end of file diff --git a/cypress-tests/cypress/e2e/ConnectorUtils/Adyen.js b/cypress-tests/cypress/e2e/ConnectorUtils/Adyen.js index 56ef464984d..68f3303e7fe 100644 --- a/cypress-tests/cypress/e2e/ConnectorUtils/Adyen.js +++ b/cypress-tests/cypress/e2e/ConnectorUtils/Adyen.js @@ -19,7 +19,7 @@ export const connectorDetails = { "3DS": { "card": successfulThreeDSTestCardDetails, "currency": "USD", - "paymentSuccessfulStatus": "requires_customer_action", + "paymentSuccessfulStatus": "succeeded", "paymentSyncStatus": "succeeded", "refundStatus": "pending", "refundSyncStatus": "pending", diff --git a/cypress-tests/cypress/e2e/ConnectorUtils/BankOfAmerica.js b/cypress-tests/cypress/e2e/ConnectorUtils/BankOfAmerica.js index 996122faa7e..f89d7279ff7 100644 --- a/cypress-tests/cypress/e2e/ConnectorUtils/BankOfAmerica.js +++ b/cypress-tests/cypress/e2e/ConnectorUtils/BankOfAmerica.js @@ -19,7 +19,7 @@ export const connectorDetails = { "3DS": { "card": successfulThreeDSTestCardDetails, "currency": "USD", - "paymentSuccessfulStatus": "requires_customer_action", + "paymentSuccessfulStatus": "succeeded", "paymentSyncStatus": "succeeded", "refundStatus": "pending", "refundSyncStatus": "pending", diff --git a/cypress-tests/cypress/e2e/ConnectorUtils/Bluesnap.js b/cypress-tests/cypress/e2e/ConnectorUtils/Bluesnap.js index c42ef5ba5a8..2b3f63529b1 100644 --- a/cypress-tests/cypress/e2e/ConnectorUtils/Bluesnap.js +++ b/cypress-tests/cypress/e2e/ConnectorUtils/Bluesnap.js @@ -18,7 +18,7 @@ export const connectorDetails = { "3DS": { "card": successfulThreeDSTestCardDetails, "currency": "USD", - "paymentSuccessfulStatus": "requires_customer_action", + "paymentSuccessfulStatus": "succeeded", "paymentSyncStatus": "succeeded", "refundStatus": "succeeded", "refundSyncStatus": "succeeded", diff --git a/cypress-tests/cypress/e2e/ConnectorUtils/Cybersource.js b/cypress-tests/cypress/e2e/ConnectorUtils/Cybersource.js index 8daef2ffecf..53a936adb11 100644 --- a/cypress-tests/cypress/e2e/ConnectorUtils/Cybersource.js +++ b/cypress-tests/cypress/e2e/ConnectorUtils/Cybersource.js @@ -18,7 +18,7 @@ export const connectorDetails = { "3DS": { "card": successfulThreeDSTestCardDetails, "currency": "USD", - "paymentSuccessfulStatus": "requires_customer_action", + "paymentSuccessfulStatus": "succeeded", "paymentSyncStatus": "succeeded", "refundStatus": "pending", "refundSyncStatus": "pending", diff --git a/cypress-tests/cypress/e2e/ConnectorUtils/Nmi.js b/cypress-tests/cypress/e2e/ConnectorUtils/Nmi.js index e2e4d4e80f8..280778028b3 100644 --- a/cypress-tests/cypress/e2e/ConnectorUtils/Nmi.js +++ b/cypress-tests/cypress/e2e/ConnectorUtils/Nmi.js @@ -18,7 +18,7 @@ export const connectorDetails = { "3DS": { "card": successfulThreeDSTestCardDetails, "currency": "USD", - "paymentSuccessfulStatus": "requires_customer_action", + "paymentSuccessfulStatus": "succeeded", "paymentSyncStatus": "succeeded", "refundStatus": "pending", "refundSyncStatus": "succeeded", diff --git a/cypress-tests/cypress/e2e/ConnectorUtils/Paypal.js b/cypress-tests/cypress/e2e/ConnectorUtils/Paypal.js index 5337c3eac69..4a3bb998cf7 100644 --- a/cypress-tests/cypress/e2e/ConnectorUtils/Paypal.js +++ b/cypress-tests/cypress/e2e/ConnectorUtils/Paypal.js @@ -19,7 +19,7 @@ export const connectorDetails = { "3DS": { "card": successfulThreeDSTestCardDetails, "currency": "USD", - "paymentSuccessfulStatus": "requires_customer_action", + "paymentSuccessfulStatus": "processing", "paymentSyncStatus": "processing", "customer_acceptance": null, "setup_future_usage": "on_session", diff --git a/cypress-tests/cypress/e2e/ConnectorUtils/Stripe.js b/cypress-tests/cypress/e2e/ConnectorUtils/Stripe.js index 2d8d5efa81b..43c136a87d2 100644 --- a/cypress-tests/cypress/e2e/ConnectorUtils/Stripe.js +++ b/cypress-tests/cypress/e2e/ConnectorUtils/Stripe.js @@ -21,7 +21,7 @@ export const connectorDetails = { "3DS": { "card": successfulThreeDSTestCardDetails, "currency": "USD", - "paymentSuccessfulStatus": "requires_customer_action", + "paymentSuccessfulStatus": "succeeded", "paymentSyncStatus": "succeeded", "refundStatus": "succeeded", "refundSyncStatus": "succeeded", diff --git a/cypress-tests/cypress/e2e/ConnectorUtils/Trustpay.js b/cypress-tests/cypress/e2e/ConnectorUtils/Trustpay.js index f6d6a88ffbb..596da099406 100644 --- a/cypress-tests/cypress/e2e/ConnectorUtils/Trustpay.js +++ b/cypress-tests/cypress/e2e/ConnectorUtils/Trustpay.js @@ -18,7 +18,7 @@ export const connectorDetails = { "3DS": { "card": successfulThreeDSTestCardDetails, "currency": "USD", - "paymentSuccessfulStatus": "requires_customer_action", + "paymentSuccessfulStatus": "succeeded", "paymentSyncStatus": "succeeded", "refundStatus": "succeeded", "refundSyncStatus": "succeeded", diff --git a/cypress-tests/cypress/fixtures/create-confirm-body.json b/cypress-tests/cypress/fixtures/create-confirm-body.json index 1f3307b61f5..1399f181342 100644 --- a/cypress-tests/cypress/fixtures/create-confirm-body.json +++ b/cypress-tests/cypress/fixtures/create-confirm-body.json @@ -12,7 +12,7 @@ "phone_country_code": "+65", "description": "Its my first payment request", "authentication_type": "no_three_ds", - "return_url": "https://duck.com", + "return_url": "https://hyperswitch.io", "setup_future_usage": "on_session", "customer_acceptance": { "acceptance_type": "offline", @@ -33,7 +33,6 @@ "card_cvc": "123" } }, - "billing": { "address": { "line1": "1467", diff --git a/cypress-tests/cypress/support/commands.js b/cypress-tests/cypress/support/commands.js index 61ce6a0970d..3caed6e0771 100644 --- a/cypress-tests/cypress/support/commands.js +++ b/cypress-tests/cypress/support/commands.js @@ -269,6 +269,7 @@ Cypress.Commands.add("createConfirmPaymentTest", (createConfirmPaymentBody, deta if (response.body.authentication_type === "three_ds") { expect(response.body).to.have.property("next_action") .to.have.property("redirect_to_url") + globalState.set("nextActionUrl", response.body.next_action.redirect_to_url); } else if (response.body.authentication_type === "no_three_ds") { expect(details.paymentSuccessfulStatus).to.equal(response.body.status); @@ -281,6 +282,7 @@ Cypress.Commands.add("createConfirmPaymentTest", (createConfirmPaymentBody, deta if (response.body.authentication_type === "three_ds") { expect(response.body).to.have.property("next_action") .to.have.property("redirect_to_url") + globalState.set("nextActionUrl", response.body.next_action.redirect_to_url); } else if (response.body.authentication_type === "no_three_ds") { expect("requires_capture").to.equal(response.body.status); From 1b5b566387da83a2582216e05be4ceb1aa7251be Mon Sep 17 00:00:00 2001 From: Chethan Rao <70657455+Chethan-rao@users.noreply.github.com> Date: Tue, 7 May 2024 17:18:49 +0530 Subject: [PATCH 19/34] refactor: store `card_cvc` in extended_card_info and extend max ttl (#4568) Co-authored-by: hyperswitch-bot[bot] <148525504+hyperswitch-bot[bot]@users.noreply.github.com> --- crates/api_models/src/admin.rs | 4 ++-- crates/api_models/src/payments.rs | 5 +++++ crates/common_utils/src/consts.rs | 2 +- openapi/openapi_spec.json | 10 ++++++++-- 4 files changed, 16 insertions(+), 5 deletions(-) diff --git a/crates/api_models/src/admin.rs b/crates/api_models/src/admin.rs index e845317b693..3beeb9071a2 100644 --- a/crates/api_models/src/admin.rs +++ b/crates/api_models/src/admin.rs @@ -1113,7 +1113,7 @@ pub struct ExtendedCardInfoConfig { #[schema(value_type = String)] pub public_key: Secret, /// TTL for extended card info - #[schema(default = 900, maximum = 3600, value_type = u16)] + #[schema(default = 900, maximum = 7200, value_type = u16)] #[serde(default)] pub ttl_in_secs: TtlForExtendedCardInfo, } @@ -1137,7 +1137,7 @@ impl<'de> Deserialize<'de> for TtlForExtendedCardInfo { // Check if value exceeds the maximum allowed if value > consts::MAX_TTL_FOR_EXTENDED_CARD_INFO { Err(serde::de::Error::custom( - "ttl_in_secs must be less than or equal to 3600 (1hr)", + "ttl_in_secs must be less than or equal to 7200 (2hrs)", )) } else { Ok(Self(value)) diff --git a/crates/api_models/src/payments.rs b/crates/api_models/src/payments.rs index 0e46eb19931..69b4700acbc 100644 --- a/crates/api_models/src/payments.rs +++ b/crates/api_models/src/payments.rs @@ -934,6 +934,10 @@ pub struct ExtendedCardInfo { #[schema(value_type = String, example = "John Test")] pub card_holder_name: Option>, + /// The CVC number for the card + #[schema(value_type = String, example = "242")] + pub card_cvc: Secret, + /// The name of the issuer of card #[schema(example = "chase")] pub card_issuer: Option, @@ -959,6 +963,7 @@ impl From for ExtendedCardInfo { card_exp_month: value.card_exp_month, card_exp_year: value.card_exp_year, card_holder_name: value.card_holder_name, + card_cvc: value.card_cvc, card_issuer: value.card_issuer, card_network: value.card_network, card_type: value.card_type, diff --git a/crates/common_utils/src/consts.rs b/crates/common_utils/src/consts.rs index 509056152eb..94b53766db0 100644 --- a/crates/common_utils/src/consts.rs +++ b/crates/common_utils/src/consts.rs @@ -82,4 +82,4 @@ pub const DEFAULT_ENABLE_SAVED_PAYMENT_METHOD: bool = false; pub const DEFAULT_TTL_FOR_EXTENDED_CARD_INFO: u16 = 15 * 60; /// Max ttl for Extended card info in redis (in seconds) -pub const MAX_TTL_FOR_EXTENDED_CARD_INFO: u16 = 60 * 60; +pub const MAX_TTL_FOR_EXTENDED_CARD_INFO: u16 = 60 * 60 * 2; diff --git a/openapi/openapi_spec.json b/openapi/openapi_spec.json index c45a4aaaa10..ec7d820a139 100644 --- a/openapi/openapi_spec.json +++ b/openapi/openapi_spec.json @@ -8717,7 +8717,8 @@ "card_number", "card_exp_month", "card_exp_year", - "card_holder_name" + "card_holder_name", + "card_cvc" ], "properties": { "card_number": { @@ -8740,6 +8741,11 @@ "description": "The card holder's name", "example": "John Test" }, + "card_cvc": { + "type": "string", + "description": "The CVC number for the card", + "example": "242" + }, "card_issuer": { "type": "string", "description": "The name of the issuer of card", @@ -8786,7 +8792,7 @@ "format": "int32", "description": "TTL for extended card info", "default": 900, - "maximum": 3600, + "maximum": 7200, "minimum": 0 } } From 25fe4deb8e9152b37467ac1fea18b3074f0e7624 Mon Sep 17 00:00:00 2001 From: Riddhiagrawal001 <50551695+Riddhiagrawal001@users.noreply.github.com> Date: Tue, 7 May 2024 18:38:46 +0530 Subject: [PATCH 20/34] fix(users): add password validations (#4555) Co-authored-by: Rachit Naithani --- crates/router/src/consts/user.rs | 2 ++ crates/router/src/types/domain/user.rs | 44 ++++++++++++++++++++---- crates/router/src/utils/user/password.rs | 18 ++++++++++ 3 files changed, 58 insertions(+), 6 deletions(-) diff --git a/crates/router/src/consts/user.rs b/crates/router/src/consts/user.rs index 1cda969f780..f14610649f4 100644 --- a/crates/router/src/consts/user.rs +++ b/crates/router/src/consts/user.rs @@ -1,3 +1,5 @@ pub const MAX_NAME_LENGTH: usize = 70; pub const MAX_COMPANY_NAME_LENGTH: usize = 70; pub const BUSINESS_EMAIL: &str = "biz@hyperswitch.io"; +pub const MAX_PASSWORD_LENGTH: usize = 70; +pub const MIN_PASSWORD_LENGTH: usize = 8; diff --git a/crates/router/src/types/domain/user.rs b/crates/router/src/types/domain/user.rs index b4b5df2f46c..5716e4e161d 100644 --- a/crates/router/src/types/domain/user.rs +++ b/crates/router/src/types/domain/user.rs @@ -158,11 +158,43 @@ pub struct UserPassword(Secret); impl UserPassword { pub fn new(password: Secret) -> UserResult { let password = password.expose(); + + let mut has_upper_case = false; + let mut has_lower_case = false; + let mut has_numeric_value = false; + let mut has_special_character = false; + let mut has_whitespace = false; + + for c in password.chars() { + has_upper_case = has_upper_case || c.is_uppercase(); + has_lower_case = has_lower_case || c.is_lowercase(); + has_numeric_value = has_numeric_value || c.is_numeric(); + has_special_character = + has_special_character || !(c.is_alphanumeric() && c.is_whitespace()); + has_whitespace = has_whitespace || c.is_whitespace(); + } + + let is_password_format_valid = has_upper_case + && has_lower_case + && has_numeric_value + && has_special_character + && !has_whitespace; + + let is_too_long = password.graphemes(true).count() > consts::user::MAX_PASSWORD_LENGTH; + let is_too_short = password.graphemes(true).count() < consts::user::MIN_PASSWORD_LENGTH; + + if is_too_short || is_too_long || !is_password_format_valid { + return Err(UserErrors::PasswordParsingError.into()); + } + Ok(Self(password.into())) + } + + pub fn new_password_without_validation(password: Secret) -> UserResult { + let password = password.expose(); if password.is_empty() { - Err(UserErrors::PasswordParsingError.into()) - } else { - Ok(Self(password.into())) + return Err(UserErrors::PasswordParsingError.into()); } + Ok(Self(password.into())) } pub fn get_secret(&self) -> Secret { @@ -636,7 +668,7 @@ impl TryFrom for NewUser { let user_id = uuid::Uuid::new_v4().to_string(); let email = value.email.clone().try_into()?; let name = UserName::try_from(value.email.clone())?; - let password = UserPassword::new(uuid::Uuid::new_v4().to_string().into())?; + let password = UserPassword::new(password::get_temp_password())?; let new_merchant = NewUserMerchant::try_from(value)?; Ok(Self { @@ -680,7 +712,7 @@ impl TryFrom for NewUser { user_id: user.0.user_id, name: UserName::new(user.0.name)?, email: user.0.email.clone().try_into()?, - password: UserPassword::new(user.0.password)?, + password: UserPassword::new_password_without_validation(user.0.password)?, new_merchant, }) } @@ -692,7 +724,7 @@ impl TryFrom for NewUser { let user_id = uuid::Uuid::new_v4().to_string(); let email = value.0.email.clone().try_into()?; let name = UserName::new(value.0.name.clone())?; - let password = UserPassword::new(uuid::Uuid::new_v4().to_string().into())?; + let password = UserPassword::new(password::get_temp_password())?; let new_merchant = NewUserMerchant::try_from(value)?; Ok(Self { diff --git a/crates/router/src/utils/user/password.rs b/crates/router/src/utils/user/password.rs index 5ed8d83658d..beb87c325b6 100644 --- a/crates/router/src/utils/user/password.rs +++ b/crates/router/src/utils/user/password.rs @@ -8,6 +8,7 @@ use argon2::{ use common_utils::errors::CustomResult; use error_stack::ResultExt; use masking::{ExposeInterface, Secret}; +use rand::{seq::SliceRandom, Rng}; use crate::core::errors::UserErrors; @@ -38,3 +39,20 @@ pub fn is_correct_password( } .change_context(UserErrors::InternalServerError) } + +pub fn get_temp_password() -> Secret { + let uuid_pass = uuid::Uuid::new_v4().to_string(); + let mut rng = rand::thread_rng(); + + let special_chars: Vec = "!@#$%^&*()-_=+[]{}|;:,.<>?".chars().collect(); + let special_char = special_chars.choose(&mut rng).unwrap_or(&'@'); + + Secret::new(format!( + "{}{}{}{}{}", + uuid_pass, + rng.gen_range('A'..='Z'), + special_char, + rng.gen_range('a'..='z'), + rng.gen_range('0'..='9'), + )) +} From d974e6e7c2e1e3cd99607183b647c420f4b14d20 Mon Sep 17 00:00:00 2001 From: Swangi Kumari <85639103+swangi-kumari@users.noreply.github.com> Date: Tue, 7 May 2024 19:20:43 +0530 Subject: [PATCH 21/34] feat(connector): [MiFinity] add connector template code (#4447) Co-authored-by: hyperswitch-bot[bot] <148525504+hyperswitch-bot[bot]@users.noreply.github.com> --- config/config.example.toml | 4 +- config/deployments/integration_test.toml | 1 + config/deployments/production.toml | 1 + config/deployments/sandbox.toml | 1 + config/development.toml | 4 +- config/docker_compose.toml | 4 +- crates/api_models/src/enums.rs | 2 + crates/common_enums/src/enums.rs | 1 + crates/router/src/configs/settings.rs | 1 + crates/router/src/connector.rs | 15 +- crates/router/src/connector/mifinity.rs | 561 ++++++++++++++++++ .../src/connector/mifinity/transformers.rs | 244 ++++++++ crates/router/src/core/admin.rs | 4 + crates/router/src/core/payments/flows.rs | 29 + crates/router/src/types/api.rs | 1 + crates/router/src/types/transformers.rs | 1 + crates/router/tests/connectors/main.rs | 1 + crates/router/tests/connectors/mifinity.rs | 421 +++++++++++++ .../router/tests/connectors/sample_auth.toml | 4 + crates/test_utils/src/connector_auth.rs | 1 + loadtest/config/development.toml | 4 +- openapi/openapi_spec.json | 1 + scripts/add_connector.sh | 2 +- 23 files changed, 1296 insertions(+), 12 deletions(-) create mode 100644 crates/router/src/connector/mifinity.rs create mode 100644 crates/router/src/connector/mifinity/transformers.rs create mode 100644 crates/router/tests/connectors/mifinity.rs diff --git a/config/config.example.toml b/config/config.example.toml index f0dad483c65..d43668c4466 100644 --- a/config/config.example.toml +++ b/config/config.example.toml @@ -194,6 +194,7 @@ gocardless.base_url = "https://api-sandbox.gocardless.com" helcim.base_url = "https://api.helcim.com/" iatapay.base_url = "https://sandbox.iata-pay.iata.org/api/v1" klarna.base_url = "https://api-na.playground.klarna.com/" +mifinity.base_url = "https://demo.mifinity.com/" mollie.base_url = "https://api.mollie.com/v2/" mollie.secondary_base_url = "https://api.cc.mollie.com/v1/" multisafepay.base_url = "https://testapi.multisafepay.com/" @@ -259,7 +260,8 @@ stripe = { banks = "alior_bank,bank_millennium,bank_nowy_bfg_sa,bank_pekao_sa,ba # This data is used to call respective connectors for wallets and cards [connectors.supported] -wallets = ["klarna", "braintree", "applepay"] +wallets = ["klarna", + "mifinity", "braintree", "applepay"] rewards = ["cashtocode", "zen"] cards = [ "adyen", diff --git a/config/deployments/integration_test.toml b/config/deployments/integration_test.toml index faf3bb7f466..1c4dc0e7bf2 100644 --- a/config/deployments/integration_test.toml +++ b/config/deployments/integration_test.toml @@ -49,6 +49,7 @@ gocardless.base_url = "https://api-sandbox.gocardless.com" helcim.base_url = "https://api.helcim.com/" iatapay.base_url = "https://sandbox.iata-pay.iata.org/api/v1" klarna.base_url = "https://api-na.playground.klarna.com/" +mifinity.base_url = "https://demo.mifinity.com/" mollie.base_url = "https://api.mollie.com/v2/" mollie.secondary_base_url = "https://api.cc.mollie.com/v1/" multisafepay.base_url = "https://testapi.multisafepay.com/" diff --git a/config/deployments/production.toml b/config/deployments/production.toml index f564b976bc0..e136c05342a 100644 --- a/config/deployments/production.toml +++ b/config/deployments/production.toml @@ -53,6 +53,7 @@ gocardless.base_url = "https://api.gocardless.com" helcim.base_url = "https://api.helcim.com/" iatapay.base_url = "https://iata-pay.iata.org/api/v1" klarna.base_url = "https://api-na.playground.klarna.com/" +mifinity.base_url = "https://secure.mifinity.com/" mollie.base_url = "https://api.mollie.com/v2/" mollie.secondary_base_url = "https://api.cc.mollie.com/v1/" multisafepay.base_url = "https://testapi.multisafepay.com/" diff --git a/config/deployments/sandbox.toml b/config/deployments/sandbox.toml index 35c8a2ba3d6..3468c443a1d 100644 --- a/config/deployments/sandbox.toml +++ b/config/deployments/sandbox.toml @@ -53,6 +53,7 @@ gocardless.base_url = "https://api-sandbox.gocardless.com" helcim.base_url = "https://api.helcim.com/" iatapay.base_url = "https://sandbox.iata-pay.iata.org/api/v1" klarna.base_url = "https://api-na.playground.klarna.com/" +mifinity.base_url = "https://demo.mifinity.com/" mollie.base_url = "https://api.mollie.com/v2/" mollie.secondary_base_url = "https://api.cc.mollie.com/v1/" multisafepay.base_url = "https://testapi.multisafepay.com/" diff --git a/config/development.toml b/config/development.toml index 3f05872bc6a..83730662adc 100644 --- a/config/development.toml +++ b/config/development.toml @@ -88,7 +88,8 @@ vault_private_key = "" tunnel_private_key = "" [connectors.supported] -wallets = ["klarna", "braintree", "applepay", "adyen"] +wallets = ["klarna", + "mifinity", "braintree", "applepay", "adyen"] rewards = ["cashtocode", "zen"] cards = [ "aci", @@ -193,6 +194,7 @@ gocardless.base_url = "https://api-sandbox.gocardless.com" helcim.base_url = "https://api.helcim.com/" iatapay.base_url = "https://sandbox.iata-pay.iata.org/api/v1" klarna.base_url = "https://api-na.playground.klarna.com/" +mifinity.base_url = "https://demo.mifinity.com/" mollie.base_url = "https://api.mollie.com/v2/" mollie.secondary_base_url = "https://api.cc.mollie.com/v1/" multisafepay.base_url = "https://testapi.multisafepay.com/" diff --git a/config/docker_compose.toml b/config/docker_compose.toml index 9fd22f46707..7dfab5548bf 100644 --- a/config/docker_compose.toml +++ b/config/docker_compose.toml @@ -128,6 +128,7 @@ gocardless.base_url = "https://api-sandbox.gocardless.com" helcim.base_url = "https://api.helcim.com/" iatapay.base_url = "https://sandbox.iata-pay.iata.org/api/v1" klarna.base_url = "https://api-na.playground.klarna.com/" +mifinity.base_url = "https://demo.mifinity.com/" mollie.base_url = "https://api.mollie.com/v2/" mollie.secondary_base_url = "https://api.cc.mollie.com/v1/" multisafepay.base_url = "https://testapi.multisafepay.com/" @@ -172,7 +173,8 @@ zsl.base_url = "https://api.sitoffalb.net/" apple_pay = { country = "AU,CN,HK,JP,MO,MY,NZ,SG,TW,AM,AT,AZ,BY,BE,BG,HR,CY,CZ,DK,EE,FO,FI,FR,GE,DE,GR,GL,GG,HU,IS,IE,IM,IT,KZ,JE,LV,LI,LT,LU,MT,MD,MC,ME,NL,NO,PL,PT,RO,SM,RS,SK,SI,ES,SE,CH,UA,GB,AR,CO,CR,BR,MX,PE,BH,IL,JO,KW,PS,QA,SA,AE,CA,UM,US,KR,VN,MA,ZA,VA,CL,SV,GT,HN,PA", currency = "AED,AUD,CHF,CAD,EUR,GBP,HKD,SGD,USD" } [connectors.supported] -wallets = ["klarna", "braintree", "applepay"] +wallets = ["klarna", + "mifinity", "braintree", "applepay"] rewards = ["cashtocode", "zen"] cards = [ "aci", diff --git a/crates/api_models/src/enums.rs b/crates/api_models/src/enums.rs index aed9915366f..93e20dc2e2c 100644 --- a/crates/api_models/src/enums.rs +++ b/crates/api_models/src/enums.rs @@ -98,6 +98,7 @@ pub enum Connector { Helcim, Iatapay, Klarna, + // Mifinity, Added as template code for future usage Mollie, Multisafepay, Netcetera, @@ -210,6 +211,7 @@ impl Connector { | Self::Helcim | Self::Iatapay | Self::Klarna + // | Self::Mifinity Added as template code for future usage | Self::Mollie | Self::Multisafepay | Self::Nexinets diff --git a/crates/common_enums/src/enums.rs b/crates/common_enums/src/enums.rs index 3c86064731b..70dc1ade0a6 100644 --- a/crates/common_enums/src/enums.rs +++ b/crates/common_enums/src/enums.rs @@ -137,6 +137,7 @@ pub enum RoutableConnectors { Helcim, Iatapay, Klarna, + Mifinity, Mollie, Multisafepay, Nexinets, diff --git a/crates/router/src/configs/settings.rs b/crates/router/src/configs/settings.rs index 5b55ca8b0e3..a07fa9ad2b4 100644 --- a/crates/router/src/configs/settings.rs +++ b/crates/router/src/configs/settings.rs @@ -515,6 +515,7 @@ pub struct Connectors { pub helcim: ConnectorParams, pub iatapay: ConnectorParams, pub klarna: ConnectorParams, + pub mifinity: ConnectorParams, pub mollie: ConnectorParams, pub multisafepay: ConnectorParams, pub netcetera: ConnectorParams, diff --git a/crates/router/src/connector.rs b/crates/router/src/connector.rs index 01c22651cd0..3f61a554f86 100644 --- a/crates/router/src/connector.rs +++ b/crates/router/src/connector.rs @@ -26,6 +26,7 @@ pub mod gocardless; pub mod helcim; pub mod iatapay; pub mod klarna; +pub mod mifinity; pub mod mollie; pub mod multisafepay; pub mod netcetera; @@ -69,11 +70,11 @@ pub use self::{ checkout::Checkout, coinbase::Coinbase, cryptopay::Cryptopay, cybersource::Cybersource, dlocal::Dlocal, ebanx::Ebanx, fiserv::Fiserv, forte::Forte, globalpay::Globalpay, globepay::Globepay, gocardless::Gocardless, helcim::Helcim, iatapay::Iatapay, klarna::Klarna, - mollie::Mollie, multisafepay::Multisafepay, netcetera::Netcetera, nexinets::Nexinets, nmi::Nmi, - noon::Noon, nuvei::Nuvei, opayo::Opayo, opennode::Opennode, payeezy::Payeezy, payme::Payme, - paypal::Paypal, payu::Payu, placetopay::Placetopay, powertranz::Powertranz, - prophetpay::Prophetpay, rapyd::Rapyd, riskified::Riskified, shift4::Shift4, signifyd::Signifyd, - square::Square, stax::Stax, stripe::Stripe, threedsecureio::Threedsecureio, trustpay::Trustpay, - tsys::Tsys, volt::Volt, wise::Wise, worldline::Worldline, worldpay::Worldpay, zen::Zen, - zsl::Zsl, + mifinity::Mifinity, mollie::Mollie, multisafepay::Multisafepay, netcetera::Netcetera, + nexinets::Nexinets, nmi::Nmi, noon::Noon, nuvei::Nuvei, opayo::Opayo, opennode::Opennode, + payeezy::Payeezy, payme::Payme, paypal::Paypal, payu::Payu, placetopay::Placetopay, + powertranz::Powertranz, prophetpay::Prophetpay, rapyd::Rapyd, riskified::Riskified, + shift4::Shift4, signifyd::Signifyd, square::Square, stax::Stax, stripe::Stripe, + threedsecureio::Threedsecureio, trustpay::Trustpay, tsys::Tsys, volt::Volt, wise::Wise, + worldline::Worldline, worldpay::Worldpay, zen::Zen, zsl::Zsl, }; diff --git a/crates/router/src/connector/mifinity.rs b/crates/router/src/connector/mifinity.rs new file mode 100644 index 00000000000..7183dd755e1 --- /dev/null +++ b/crates/router/src/connector/mifinity.rs @@ -0,0 +1,561 @@ +pub mod transformers; + +use std::fmt::Debug; + +use error_stack::{report, ResultExt}; +use masking::ExposeInterface; +use transformers as mifinity; + +use crate::{ + configs::settings, + core::errors::{self, CustomResult}, + events::connector_api_logs::ConnectorEvent, + headers, + services::{ + self, + request::{self, Mask}, + ConnectorIntegration, ConnectorValidation, + }, + types::{ + self, + api::{self, ConnectorCommon, ConnectorCommonExt}, + ErrorResponse, RequestContent, Response, + }, + utils::BytesExt, +}; + +#[derive(Debug, Clone)] +pub struct Mifinity; + +impl api::Payment for Mifinity {} +impl api::PaymentSession for Mifinity {} +impl api::ConnectorAccessToken for Mifinity {} +impl api::MandateSetup for Mifinity {} +impl api::PaymentAuthorize for Mifinity {} +impl api::PaymentSync for Mifinity {} +impl api::PaymentCapture for Mifinity {} +impl api::PaymentVoid for Mifinity {} +impl api::Refund for Mifinity {} +impl api::RefundExecute for Mifinity {} +impl api::RefundSync for Mifinity {} +impl api::PaymentToken for Mifinity {} + +impl + ConnectorIntegration< + api::PaymentMethodToken, + types::PaymentMethodTokenizationData, + types::PaymentsResponseData, + > for Mifinity +{ + // Not Implemented (R) +} + +impl ConnectorCommonExt for Mifinity +where + Self: ConnectorIntegration, +{ + fn build_headers( + &self, + req: &types::RouterData, + _connectors: &settings::Connectors, + ) -> CustomResult)>, errors::ConnectorError> { + let mut header = vec![( + headers::CONTENT_TYPE.to_string(), + self.get_content_type().to_string().into(), + )]; + let mut api_key = self.get_auth_header(&req.connector_auth_type)?; + header.append(&mut api_key); + Ok(header) + } +} + +impl ConnectorCommon for Mifinity { + fn id(&self) -> &'static str { + "mifinity" + } + + fn get_currency_unit(&self) -> api::CurrencyUnit { + api::CurrencyUnit::Base + } + + fn common_get_content_type(&self) -> &'static str { + "application/json" + } + + fn base_url<'a>(&self, connectors: &'a settings::Connectors) -> &'a str { + connectors.mifinity.base_url.as_ref() + } + + fn get_auth_header( + &self, + auth_type: &types::ConnectorAuthType, + ) -> CustomResult)>, errors::ConnectorError> { + let auth = mifinity::MifinityAuthType::try_from(auth_type) + .change_context(errors::ConnectorError::FailedToObtainAuthType)?; + Ok(vec![( + headers::AUTHORIZATION.to_string(), + auth.api_key.expose().into_masked(), + )]) + } + + fn build_error_response( + &self, + res: Response, + event_builder: Option<&mut ConnectorEvent>, + ) -> CustomResult { + let response: mifinity::MifinityErrorResponse = res + .response + .parse_struct("MifinityErrorResponse") + .change_context(errors::ConnectorError::ResponseDeserializationFailed)?; + + event_builder.map(|i| i.set_response_body(&response)); + router_env::logger::info!(connector_response=?response); + + Ok(ErrorResponse { + status_code: res.status_code, + code: response.code, + message: response.message, + reason: response.reason, + attempt_status: None, + connector_transaction_id: None, + }) + } +} + +impl ConnectorValidation for Mifinity { + //TODO: implement functions when support enabled +} + +impl ConnectorIntegration + for Mifinity +{ + //TODO: implement sessions flow +} + +impl ConnectorIntegration + for Mifinity +{ +} + +impl + ConnectorIntegration< + api::SetupMandate, + types::SetupMandateRequestData, + types::PaymentsResponseData, + > for Mifinity +{ +} + +impl ConnectorIntegration + for Mifinity +{ + fn get_headers( + &self, + req: &types::PaymentsAuthorizeRouterData, + connectors: &settings::Connectors, + ) -> CustomResult)>, errors::ConnectorError> { + self.build_headers(req, connectors) + } + + fn get_content_type(&self) -> &'static str { + self.common_get_content_type() + } + + fn get_url( + &self, + _req: &types::PaymentsAuthorizeRouterData, + _connectors: &settings::Connectors, + ) -> CustomResult { + Err(errors::ConnectorError::NotImplemented("get_url method".to_string()).into()) + } + + fn get_request_body( + &self, + req: &types::PaymentsAuthorizeRouterData, + _connectors: &settings::Connectors, + ) -> CustomResult { + let connector_router_data = mifinity::MifinityRouterData::try_from(( + &self.get_currency_unit(), + req.request.currency, + req.request.amount, + req, + ))?; + let connector_req = mifinity::MifinityPaymentsRequest::try_from(&connector_router_data)?; + Ok(RequestContent::Json(Box::new(connector_req))) + } + + fn build_request( + &self, + req: &types::PaymentsAuthorizeRouterData, + connectors: &settings::Connectors, + ) -> CustomResult, errors::ConnectorError> { + Ok(Some( + services::RequestBuilder::new() + .method(services::Method::Post) + .url(&types::PaymentsAuthorizeType::get_url( + self, req, connectors, + )?) + .attach_default_headers() + .headers(types::PaymentsAuthorizeType::get_headers( + self, req, connectors, + )?) + .set_body(types::PaymentsAuthorizeType::get_request_body( + self, req, connectors, + )?) + .build(), + )) + } + + fn handle_response( + &self, + data: &types::PaymentsAuthorizeRouterData, + event_builder: Option<&mut ConnectorEvent>, + res: Response, + ) -> CustomResult { + let response: mifinity::MifinityPaymentsResponse = res + .response + .parse_struct("Mifinity PaymentsAuthorizeResponse") + .change_context(errors::ConnectorError::ResponseDeserializationFailed)?; + event_builder.map(|i| i.set_response_body(&response)); + router_env::logger::info!(connector_response=?response); + types::RouterData::try_from(types::ResponseRouterData { + response, + data: data.clone(), + http_code: res.status_code, + }) + } + + fn get_error_response( + &self, + res: Response, + event_builder: Option<&mut ConnectorEvent>, + ) -> CustomResult { + self.build_error_response(res, event_builder) + } +} + +impl ConnectorIntegration + for Mifinity +{ + fn get_headers( + &self, + req: &types::PaymentsSyncRouterData, + connectors: &settings::Connectors, + ) -> CustomResult)>, errors::ConnectorError> { + self.build_headers(req, connectors) + } + + fn get_content_type(&self) -> &'static str { + self.common_get_content_type() + } + + fn get_url( + &self, + _req: &types::PaymentsSyncRouterData, + _connectors: &settings::Connectors, + ) -> CustomResult { + Err(errors::ConnectorError::NotImplemented("get_url method".to_string()).into()) + } + + fn build_request( + &self, + req: &types::PaymentsSyncRouterData, + connectors: &settings::Connectors, + ) -> CustomResult, errors::ConnectorError> { + Ok(Some( + services::RequestBuilder::new() + .method(services::Method::Get) + .url(&types::PaymentsSyncType::get_url(self, req, connectors)?) + .attach_default_headers() + .headers(types::PaymentsSyncType::get_headers(self, req, connectors)?) + .build(), + )) + } + + fn handle_response( + &self, + data: &types::PaymentsSyncRouterData, + event_builder: Option<&mut ConnectorEvent>, + res: Response, + ) -> CustomResult { + let response: mifinity::MifinityPaymentsResponse = res + .response + .parse_struct("mifinity PaymentsSyncResponse") + .change_context(errors::ConnectorError::ResponseDeserializationFailed)?; + event_builder.map(|i| i.set_response_body(&response)); + router_env::logger::info!(connector_response=?response); + types::RouterData::try_from(types::ResponseRouterData { + response, + data: data.clone(), + http_code: res.status_code, + }) + } + + fn get_error_response( + &self, + res: Response, + event_builder: Option<&mut ConnectorEvent>, + ) -> CustomResult { + self.build_error_response(res, event_builder) + } +} + +impl ConnectorIntegration + for Mifinity +{ + fn get_headers( + &self, + req: &types::PaymentsCaptureRouterData, + connectors: &settings::Connectors, + ) -> CustomResult)>, errors::ConnectorError> { + self.build_headers(req, connectors) + } + + fn get_content_type(&self) -> &'static str { + self.common_get_content_type() + } + + fn get_url( + &self, + _req: &types::PaymentsCaptureRouterData, + _connectors: &settings::Connectors, + ) -> CustomResult { + Err(errors::ConnectorError::NotImplemented("get_url method".to_string()).into()) + } + + fn get_request_body( + &self, + _req: &types::PaymentsCaptureRouterData, + _connectors: &settings::Connectors, + ) -> CustomResult { + Err(errors::ConnectorError::NotImplemented("get_request_body method".to_string()).into()) + } + + fn build_request( + &self, + req: &types::PaymentsCaptureRouterData, + connectors: &settings::Connectors, + ) -> CustomResult, errors::ConnectorError> { + Ok(Some( + services::RequestBuilder::new() + .method(services::Method::Post) + .url(&types::PaymentsCaptureType::get_url(self, req, connectors)?) + .attach_default_headers() + .headers(types::PaymentsCaptureType::get_headers( + self, req, connectors, + )?) + .set_body(types::PaymentsCaptureType::get_request_body( + self, req, connectors, + )?) + .build(), + )) + } + + fn handle_response( + &self, + data: &types::PaymentsCaptureRouterData, + event_builder: Option<&mut ConnectorEvent>, + res: Response, + ) -> CustomResult { + let response: mifinity::MifinityPaymentsResponse = res + .response + .parse_struct("Mifinity PaymentsCaptureResponse") + .change_context(errors::ConnectorError::ResponseDeserializationFailed)?; + event_builder.map(|i| i.set_response_body(&response)); + router_env::logger::info!(connector_response=?response); + types::RouterData::try_from(types::ResponseRouterData { + response, + data: data.clone(), + http_code: res.status_code, + }) + } + + fn get_error_response( + &self, + res: Response, + event_builder: Option<&mut ConnectorEvent>, + ) -> CustomResult { + self.build_error_response(res, event_builder) + } +} + +impl ConnectorIntegration + for Mifinity +{ +} + +impl ConnectorIntegration + for Mifinity +{ + fn get_headers( + &self, + req: &types::RefundsRouterData, + connectors: &settings::Connectors, + ) -> CustomResult)>, errors::ConnectorError> { + self.build_headers(req, connectors) + } + + fn get_content_type(&self) -> &'static str { + self.common_get_content_type() + } + + fn get_url( + &self, + _req: &types::RefundsRouterData, + _connectors: &settings::Connectors, + ) -> CustomResult { + Err(errors::ConnectorError::NotImplemented("get_url method".to_string()).into()) + } + + fn get_request_body( + &self, + req: &types::RefundsRouterData, + _connectors: &settings::Connectors, + ) -> CustomResult { + let connector_router_data = mifinity::MifinityRouterData::try_from(( + &self.get_currency_unit(), + req.request.currency, + req.request.refund_amount, + req, + ))?; + let connector_req = mifinity::MifinityRefundRequest::try_from(&connector_router_data)?; + Ok(RequestContent::Json(Box::new(connector_req))) + } + + fn build_request( + &self, + req: &types::RefundsRouterData, + connectors: &settings::Connectors, + ) -> CustomResult, errors::ConnectorError> { + let request = services::RequestBuilder::new() + .method(services::Method::Post) + .url(&types::RefundExecuteType::get_url(self, req, connectors)?) + .attach_default_headers() + .headers(types::RefundExecuteType::get_headers( + self, req, connectors, + )?) + .set_body(types::RefundExecuteType::get_request_body( + self, req, connectors, + )?) + .build(); + Ok(Some(request)) + } + + fn handle_response( + &self, + data: &types::RefundsRouterData, + event_builder: Option<&mut ConnectorEvent>, + res: Response, + ) -> CustomResult, errors::ConnectorError> { + let response: mifinity::RefundResponse = res + .response + .parse_struct("mifinity RefundResponse") + .change_context(errors::ConnectorError::ResponseDeserializationFailed)?; + event_builder.map(|i| i.set_response_body(&response)); + router_env::logger::info!(connector_response=?response); + types::RouterData::try_from(types::ResponseRouterData { + response, + data: data.clone(), + http_code: res.status_code, + }) + } + + fn get_error_response( + &self, + res: Response, + event_builder: Option<&mut ConnectorEvent>, + ) -> CustomResult { + self.build_error_response(res, event_builder) + } +} + +impl ConnectorIntegration for Mifinity { + fn get_headers( + &self, + req: &types::RefundSyncRouterData, + connectors: &settings::Connectors, + ) -> CustomResult)>, errors::ConnectorError> { + self.build_headers(req, connectors) + } + + fn get_content_type(&self) -> &'static str { + self.common_get_content_type() + } + + fn get_url( + &self, + _req: &types::RefundSyncRouterData, + _connectors: &settings::Connectors, + ) -> CustomResult { + Err(errors::ConnectorError::NotImplemented("get_url method".to_string()).into()) + } + + fn build_request( + &self, + req: &types::RefundSyncRouterData, + connectors: &settings::Connectors, + ) -> CustomResult, errors::ConnectorError> { + Ok(Some( + services::RequestBuilder::new() + .method(services::Method::Get) + .url(&types::RefundSyncType::get_url(self, req, connectors)?) + .attach_default_headers() + .headers(types::RefundSyncType::get_headers(self, req, connectors)?) + .set_body(types::RefundSyncType::get_request_body( + self, req, connectors, + )?) + .build(), + )) + } + + fn handle_response( + &self, + data: &types::RefundSyncRouterData, + event_builder: Option<&mut ConnectorEvent>, + res: Response, + ) -> CustomResult { + let response: mifinity::RefundResponse = res + .response + .parse_struct("mifinity RefundSyncResponse") + .change_context(errors::ConnectorError::ResponseDeserializationFailed)?; + event_builder.map(|i| i.set_response_body(&response)); + router_env::logger::info!(connector_response=?response); + types::RouterData::try_from(types::ResponseRouterData { + response, + data: data.clone(), + http_code: res.status_code, + }) + } + + fn get_error_response( + &self, + res: Response, + event_builder: Option<&mut ConnectorEvent>, + ) -> CustomResult { + self.build_error_response(res, event_builder) + } +} + +#[async_trait::async_trait] +impl api::IncomingWebhook for Mifinity { + fn get_webhook_object_reference_id( + &self, + _request: &api::IncomingWebhookRequestDetails<'_>, + ) -> CustomResult { + Err(report!(errors::ConnectorError::WebhooksNotImplemented)) + } + + fn get_webhook_event_type( + &self, + _request: &api::IncomingWebhookRequestDetails<'_>, + ) -> CustomResult { + Err(report!(errors::ConnectorError::WebhooksNotImplemented)) + } + + fn get_webhook_resource_object( + &self, + _request: &api::IncomingWebhookRequestDetails<'_>, + ) -> CustomResult, errors::ConnectorError> { + Err(report!(errors::ConnectorError::WebhooksNotImplemented)) + } +} diff --git a/crates/router/src/connector/mifinity/transformers.rs b/crates/router/src/connector/mifinity/transformers.rs new file mode 100644 index 00000000000..e1c3d18d0be --- /dev/null +++ b/crates/router/src/connector/mifinity/transformers.rs @@ -0,0 +1,244 @@ +use masking::Secret; +use serde::{Deserialize, Serialize}; + +use crate::{ + connector::utils::PaymentsAuthorizeRequestData, + core::errors, + types::{self, api, domain, storage::enums}, +}; + +//TODO: Fill the struct with respective fields +pub struct MifinityRouterData { + pub amount: i64, // The type of amount that a connector accepts, for example, String, i64, f64, etc. + pub router_data: T, +} + +impl + TryFrom<( + &types::api::CurrencyUnit, + types::storage::enums::Currency, + i64, + T, + )> for MifinityRouterData +{ + type Error = error_stack::Report; + fn try_from( + (_currency_unit, _currency, amount, item): ( + &types::api::CurrencyUnit, + types::storage::enums::Currency, + i64, + T, + ), + ) -> Result { + //Todo : use utils to convert the amount to the type of amount that a connector accepts + Ok(Self { + amount, + router_data: item, + }) + } +} + +//TODO: Fill the struct with respective fields +#[derive(Default, Debug, Serialize, Eq, PartialEq)] +pub struct MifinityPaymentsRequest { + amount: i64, + card: MifinityCard, +} + +#[derive(Default, Debug, Serialize, Eq, PartialEq)] +pub struct MifinityCard { + number: cards::CardNumber, + expiry_month: Secret, + expiry_year: Secret, + cvc: Secret, + complete: bool, +} + +impl TryFrom<&MifinityRouterData<&types::PaymentsAuthorizeRouterData>> for MifinityPaymentsRequest { + type Error = error_stack::Report; + fn try_from( + item: &MifinityRouterData<&types::PaymentsAuthorizeRouterData>, + ) -> Result { + match item.router_data.request.payment_method_data.clone() { + domain::PaymentMethodData::Card(req_card) => { + let card = MifinityCard { + 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.router_data.request.is_auto_capture()?, + }; + Ok(Self { + amount: item.amount.to_owned(), + card, + }) + } + _ => Err(errors::ConnectorError::NotImplemented("Payment methods".to_string()).into()), + } + } +} + +//TODO: Fill the struct with respective fields +// Auth Struct +pub struct MifinityAuthType { + pub(super) api_key: Secret, +} + +impl TryFrom<&types::ConnectorAuthType> for MifinityAuthType { + type Error = error_stack::Report; + fn try_from(auth_type: &types::ConnectorAuthType) -> Result { + match auth_type { + types::ConnectorAuthType::HeaderKey { api_key } => Ok(Self { + api_key: api_key.to_owned(), + }), + _ => Err(errors::ConnectorError::FailedToObtainAuthType.into()), + } + } +} +// PaymentsResponse +//TODO: Append the remaining status flags +#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq)] +#[serde(rename_all = "lowercase")] +pub enum MifinityPaymentStatus { + Succeeded, + Failed, + #[default] + Processing, +} + +impl From for enums::AttemptStatus { + fn from(item: MifinityPaymentStatus) -> Self { + match item { + MifinityPaymentStatus::Succeeded => Self::Charged, + MifinityPaymentStatus::Failed => Self::Failure, + MifinityPaymentStatus::Processing => Self::Authorizing, + } + } +} + +//TODO: Fill the struct with respective fields +#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq)] +pub struct MifinityPaymentsResponse { + status: MifinityPaymentStatus, + id: String, +} + +impl + TryFrom> + for types::RouterData +{ + type Error = error_stack::Report; + fn try_from( + item: types::ResponseRouterData< + F, + MifinityPaymentsResponse, + T, + 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), + redirection_data: None, + mandate_reference: None, + connector_metadata: None, + network_txn_id: None, + connector_response_reference_id: None, + incremental_authorization_allowed: None, + }), + ..item.data + }) + } +} + +//TODO: Fill the struct with respective fields +// REFUND : +// Type definition for RefundRequest +#[derive(Default, Debug, Serialize)] +pub struct MifinityRefundRequest { + pub amount: i64, +} + +impl TryFrom<&MifinityRouterData<&types::RefundsRouterData>> for MifinityRefundRequest { + type Error = error_stack::Report; + fn try_from( + item: &MifinityRouterData<&types::RefundsRouterData>, + ) -> Result { + Ok(Self { + amount: item.amount.to_owned(), + }) + } +} + +// 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 + } + } +} + +//TODO: Fill the struct with respective fields +#[derive(Default, Debug, Clone, Serialize, Deserialize)] +pub struct RefundResponse { + id: String, + 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.to_string(), + refund_status: enums::RefundStatus::from(item.response.status), + }), + ..item.data + }) + } +} + +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.to_string(), + refund_status: enums::RefundStatus::from(item.response.status), + }), + ..item.data + }) + } +} + +//TODO: Fill the struct with respective fields +#[derive(Default, Debug, Serialize, Deserialize, PartialEq)] +pub struct MifinityErrorResponse { + pub status_code: u16, + pub code: String, + pub message: String, + pub reason: Option, +} diff --git a/crates/router/src/core/admin.rs b/crates/router/src/core/admin.rs index ee06097a599..e0cfe0dee89 100644 --- a/crates/router/src/core/admin.rs +++ b/crates/router/src/core/admin.rs @@ -1788,6 +1788,10 @@ pub(crate) fn validate_auth_and_metadata_type( use crate::connector::*; match connector_name { + // api_enums::Connector::Mifinity => { + // mifinity::transformers::MifinityAuthType::try_from(val)?; + // Ok(()) + // } Added as template code for future usage #[cfg(feature = "dummy_connector")] api_enums::Connector::DummyConnector1 | api_enums::Connector::DummyConnector2 diff --git a/crates/router/src/core/payments/flows.rs b/crates/router/src/core/payments/flows.rs index 0687f5986ba..6980a362f75 100644 --- a/crates/router/src/core/payments/flows.rs +++ b/crates/router/src/core/payments/flows.rs @@ -160,6 +160,7 @@ default_imp_for_complete_authorize!( connector::Helcim, connector::Iatapay, connector::Klarna, + connector::Mifinity, connector::Multisafepay, connector::Netcetera, connector::Nexinets, @@ -238,6 +239,7 @@ default_imp_for_webhook_source_verification!( connector::Helcim, connector::Iatapay, connector::Klarna, + connector::Mifinity, connector::Mollie, connector::Multisafepay, connector::Netcetera, @@ -324,6 +326,7 @@ default_imp_for_create_customer!( connector::Helcim, connector::Iatapay, connector::Klarna, + connector::Mifinity, connector::Mollie, connector::Multisafepay, connector::Netcetera, @@ -405,6 +408,7 @@ default_imp_for_connector_redirect_response!( connector::Helcim, connector::Iatapay, connector::Klarna, + connector::Mifinity, connector::Multisafepay, connector::Netcetera, connector::Nexinets, @@ -468,6 +472,7 @@ default_imp_for_connector_request_id!( connector::Gocardless, connector::Iatapay, connector::Klarna, + connector::Mifinity, connector::Mollie, connector::Multisafepay, connector::Netcetera, @@ -556,6 +561,7 @@ default_imp_for_accept_dispute!( connector::Helcim, connector::Iatapay, connector::Klarna, + connector::Mifinity, connector::Mollie, connector::Multisafepay, connector::Netcetera, @@ -665,6 +671,7 @@ default_imp_for_file_upload!( connector::Helcim, connector::Iatapay, connector::Klarna, + connector::Mifinity, connector::Mollie, connector::Multisafepay, connector::Netcetera, @@ -751,6 +758,7 @@ default_imp_for_submit_evidence!( connector::Helcim, connector::Iatapay, connector::Klarna, + connector::Mifinity, connector::Mollie, connector::Multisafepay, connector::Netcetera, @@ -837,6 +845,7 @@ default_imp_for_defend_dispute!( connector::Helcim, connector::Iatapay, connector::Klarna, + connector::Mifinity, connector::Mollie, connector::Multisafepay, connector::Netcetera, @@ -921,6 +930,7 @@ default_imp_for_pre_processing_steps!( connector::Globepay, connector::Helcim, connector::Klarna, + connector::Mifinity, connector::Mollie, connector::Multisafepay, connector::Netcetera, @@ -985,6 +995,7 @@ default_imp_for_payouts!( connector::Helcim, connector::Iatapay, connector::Klarna, + connector::Mifinity, connector::Mollie, connector::Multisafepay, connector::Netcetera, @@ -1069,6 +1080,7 @@ default_imp_for_payouts_create!( connector::Helcim, connector::Iatapay, connector::Klarna, + connector::Mifinity, connector::Mollie, connector::Multisafepay, connector::Netcetera, @@ -1156,6 +1168,7 @@ default_imp_for_payouts_eligibility!( connector::Helcim, connector::Iatapay, connector::Klarna, + connector::Mifinity, connector::Mollie, connector::Multisafepay, connector::Netcetera, @@ -1241,6 +1254,7 @@ default_imp_for_payouts_fulfill!( connector::Helcim, connector::Iatapay, connector::Klarna, + connector::Mifinity, connector::Mollie, connector::Multisafepay, connector::Netcetera, @@ -1325,6 +1339,7 @@ default_imp_for_payouts_cancel!( connector::Helcim, connector::Iatapay, connector::Klarna, + connector::Mifinity, connector::Mollie, connector::Multisafepay, connector::Netcetera, @@ -1411,6 +1426,7 @@ default_imp_for_payouts_quote!( connector::Helcim, connector::Iatapay, connector::Klarna, + connector::Mifinity, connector::Mollie, connector::Multisafepay, connector::Netcetera, @@ -1498,6 +1514,7 @@ default_imp_for_payouts_recipient!( connector::Helcim, connector::Iatapay, connector::Klarna, + connector::Mifinity, connector::Mollie, connector::Multisafepay, connector::Netcetera, @@ -1588,6 +1605,7 @@ default_imp_for_payouts_recipient_account!( connector::Helcim, connector::Iatapay, connector::Klarna, + connector::Mifinity, connector::Mollie, connector::Multisafepay, connector::Netcetera, @@ -1675,6 +1693,7 @@ default_imp_for_approve!( connector::Helcim, connector::Iatapay, connector::Klarna, + connector::Mifinity, connector::Mollie, connector::Multisafepay, connector::Netcetera, @@ -1763,6 +1782,7 @@ default_imp_for_reject!( connector::Helcim, connector::Iatapay, connector::Klarna, + connector::Mifinity, connector::Mollie, connector::Multisafepay, connector::Netcetera, @@ -1835,6 +1855,7 @@ default_imp_for_fraud_check!( connector::Helcim, connector::Iatapay, connector::Klarna, + connector::Mifinity, connector::Mollie, connector::Multisafepay, connector::Netcetera, @@ -1923,6 +1944,7 @@ default_imp_for_frm_sale!( connector::Helcim, connector::Iatapay, connector::Klarna, + connector::Mifinity, connector::Mollie, connector::Multisafepay, connector::Netcetera, @@ -2011,6 +2033,7 @@ default_imp_for_frm_checkout!( connector::Helcim, connector::Iatapay, connector::Klarna, + connector::Mifinity, connector::Mollie, connector::Multisafepay, connector::Netcetera, @@ -2099,6 +2122,7 @@ default_imp_for_frm_transaction!( connector::Helcim, connector::Iatapay, connector::Klarna, + connector::Mifinity, connector::Mollie, connector::Multisafepay, connector::Netcetera, @@ -2187,6 +2211,7 @@ default_imp_for_frm_fulfillment!( connector::Helcim, connector::Iatapay, connector::Klarna, + connector::Mifinity, connector::Mollie, connector::Multisafepay, connector::Netcetera, @@ -2275,6 +2300,7 @@ default_imp_for_frm_record_return!( connector::Helcim, connector::Iatapay, connector::Klarna, + connector::Mifinity, connector::Mollie, connector::Multisafepay, connector::Netcetera, @@ -2360,6 +2386,7 @@ default_imp_for_incremental_authorization!( connector::Helcim, connector::Iatapay, connector::Klarna, + connector::Mifinity, connector::Mollie, connector::Multisafepay, connector::Netcetera, @@ -2445,6 +2472,7 @@ default_imp_for_revoking_mandates!( connector::Helcim, connector::Iatapay, connector::Klarna, + connector::Mifinity, connector::Mollie, connector::Multisafepay, connector::Netcetera, @@ -2572,6 +2600,7 @@ default_imp_for_connector_authentication!( connector::Helcim, connector::Iatapay, connector::Klarna, + connector::Mifinity, connector::Mollie, connector::Multisafepay, connector::Nexinets, diff --git a/crates/router/src/types/api.rs b/crates/router/src/types/api.rs index 57f51c7b8b0..4eb4538e77d 100644 --- a/crates/router/src/types/api.rs +++ b/crates/router/src/types/api.rs @@ -361,6 +361,7 @@ impl ConnectorData { 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::Mifinity => Ok(Box::new(&connector::Mifinity)), Added as template code for future usage enums::Connector::Mollie => Ok(Box::new(&connector::Mollie)), enums::Connector::Nmi => Ok(Box::new(&connector::Nmi)), enums::Connector::Noon => Ok(Box::new(&connector::Noon)), diff --git a/crates/router/src/types/transformers.rs b/crates/router/src/types/transformers.rs index ce11251b83e..8b8ba753db3 100644 --- a/crates/router/src/types/transformers.rs +++ b/crates/router/src/types/transformers.rs @@ -231,6 +231,7 @@ impl ForeignTryFrom for common_enums::RoutableConnectors { api_enums::Connector::Helcim => Self::Helcim, api_enums::Connector::Iatapay => Self::Iatapay, api_enums::Connector::Klarna => Self::Klarna, + // api_enums::Connector::Mifinity => Self::Mifinity, Added as template code for future usage api_enums::Connector::Mollie => Self::Mollie, api_enums::Connector::Multisafepay => Self::Multisafepay, api_enums::Connector::Netcetera => { diff --git a/crates/router/tests/connectors/main.rs b/crates/router/tests/connectors/main.rs index 78e67b7670a..2682c1fcf99 100644 --- a/crates/router/tests/connectors/main.rs +++ b/crates/router/tests/connectors/main.rs @@ -34,6 +34,7 @@ mod globepay; mod gocardless; mod helcim; mod iatapay; +mod mifinity; mod mollie; mod multisafepay; mod netcetera; diff --git a/crates/router/tests/connectors/mifinity.rs b/crates/router/tests/connectors/mifinity.rs new file mode 100644 index 00000000000..f0b58342684 --- /dev/null +++ b/crates/router/tests/connectors/mifinity.rs @@ -0,0 +1,421 @@ +use masking::Secret; +use router::types::{self, domain, storage::enums}; +use test_utils::connector_auth; + +use crate::utils::{self, ConnectorActions}; + +#[derive(Clone, Copy)] +struct MifinityTest; +impl ConnectorActions for MifinityTest {} +impl utils::Connector for MifinityTest { + fn get_data(&self) -> types::api::ConnectorData { + use router::connector::Mifinity; + types::api::ConnectorData { + connector: Box::new(&Mifinity), + connector_name: types::Connector::Adyen, + // Added as Dummy connector as template code is added for future usage + get_token: types::api::GetToken::Connector, + merchant_connector_id: None, + } + } + + fn get_auth_token(&self) -> types::ConnectorAuthType { + utils::to_connector_auth_type( + connector_auth::ConnectorAuthentication::new() + .mifinity + .expect("Missing connector authentication configuration") + .into(), + ) + } + + fn get_name(&self) -> String { + "mifinity".to_string() + } +} + +static CONNECTOR: MifinityTest = MifinityTest {}; + +fn get_default_payment_info() -> Option { + None +} + +fn payment_method_details() -> Option { + None +} + +// Cards Positive Tests +// Creates a payment using the manual capture flow (Non 3DS). +#[actix_web::test] +async fn should_only_authorize_payment() { + let response = CONNECTOR + .authorize_payment(payment_method_details(), get_default_payment_info()) + .await + .expect("Authorize payment response"); + assert_eq!(response.status, enums::AttemptStatus::Authorized); +} + +// Captures a payment using the manual capture flow (Non 3DS). +#[actix_web::test] +async fn should_capture_authorized_payment() { + let response = CONNECTOR + .authorize_and_capture_payment(payment_method_details(), None, get_default_payment_info()) + .await + .expect("Capture payment response"); + assert_eq!(response.status, enums::AttemptStatus::Charged); +} + +// Partially captures a payment using the manual capture flow (Non 3DS). +#[actix_web::test] +async fn should_partially_capture_authorized_payment() { + let response = CONNECTOR + .authorize_and_capture_payment( + payment_method_details(), + Some(types::PaymentsCaptureData { + amount_to_capture: 50, + ..utils::PaymentCaptureType::default().0 + }), + get_default_payment_info(), + ) + .await + .expect("Capture payment response"); + assert_eq!(response.status, enums::AttemptStatus::Charged); +} + +// Synchronizes a payment using the manual capture flow (Non 3DS). +#[actix_web::test] +async fn should_sync_authorized_payment() { + let authorize_response = CONNECTOR + .authorize_payment(payment_method_details(), get_default_payment_info()) + .await + .expect("Authorize payment response"); + let txn_id = utils::get_connector_transaction_id(authorize_response.response); + let response = CONNECTOR + .psync_retry_till_status_matches( + enums::AttemptStatus::Authorized, + Some(types::PaymentsSyncData { + connector_transaction_id: router::types::ResponseId::ConnectorTransactionId( + txn_id.unwrap(), + ), + ..Default::default() + }), + get_default_payment_info(), + ) + .await + .expect("PSync response"); + assert_eq!(response.status, enums::AttemptStatus::Authorized,); +} + +// Voids a payment using the manual capture flow (Non 3DS). +#[actix_web::test] +async fn should_void_authorized_payment() { + let response = CONNECTOR + .authorize_and_void_payment( + payment_method_details(), + Some(types::PaymentsCancelData { + connector_transaction_id: String::from(""), + cancellation_reason: Some("requested_by_customer".to_string()), + ..Default::default() + }), + get_default_payment_info(), + ) + .await + .expect("Void payment response"); + assert_eq!(response.status, enums::AttemptStatus::Voided); +} + +// Refunds a payment using the manual capture flow (Non 3DS). +#[actix_web::test] +async fn should_refund_manually_captured_payment() { + let response = CONNECTOR + .capture_payment_and_refund( + payment_method_details(), + None, + None, + get_default_payment_info(), + ) + .await + .unwrap(); + assert_eq!( + response.response.unwrap().refund_status, + enums::RefundStatus::Success, + ); +} + +// Partially refunds a payment using the manual capture flow (Non 3DS). +#[actix_web::test] +async fn should_partially_refund_manually_captured_payment() { + let response = CONNECTOR + .capture_payment_and_refund( + payment_method_details(), + None, + Some(types::RefundsData { + refund_amount: 50, + ..utils::PaymentRefundType::default().0 + }), + get_default_payment_info(), + ) + .await + .unwrap(); + assert_eq!( + response.response.unwrap().refund_status, + enums::RefundStatus::Success, + ); +} + +// Synchronizes a refund using the manual capture flow (Non 3DS). +#[actix_web::test] +async fn should_sync_manually_captured_refund() { + let refund_response = CONNECTOR + .capture_payment_and_refund( + payment_method_details(), + None, + None, + get_default_payment_info(), + ) + .await + .unwrap(); + let response = CONNECTOR + .rsync_retry_till_status_matches( + enums::RefundStatus::Success, + refund_response.response.unwrap().connector_refund_id, + None, + get_default_payment_info(), + ) + .await + .unwrap(); + assert_eq!( + response.response.unwrap().refund_status, + enums::RefundStatus::Success, + ); +} + +// Creates a payment using the automatic capture flow (Non 3DS). +#[actix_web::test] +async fn should_make_payment() { + let authorize_response = CONNECTOR + .make_payment(payment_method_details(), get_default_payment_info()) + .await + .unwrap(); + assert_eq!(authorize_response.status, enums::AttemptStatus::Charged); +} + +// Synchronizes a payment using the automatic capture flow (Non 3DS). +#[actix_web::test] +async fn should_sync_auto_captured_payment() { + let authorize_response = CONNECTOR + .make_payment(payment_method_details(), get_default_payment_info()) + .await + .unwrap(); + assert_eq!(authorize_response.status, enums::AttemptStatus::Charged); + let txn_id = utils::get_connector_transaction_id(authorize_response.response); + assert_ne!(txn_id, None, "Empty connector transaction id"); + let response = CONNECTOR + .psync_retry_till_status_matches( + enums::AttemptStatus::Charged, + Some(types::PaymentsSyncData { + connector_transaction_id: router::types::ResponseId::ConnectorTransactionId( + txn_id.unwrap(), + ), + capture_method: Some(enums::CaptureMethod::Automatic), + ..Default::default() + }), + get_default_payment_info(), + ) + .await + .unwrap(); + assert_eq!(response.status, enums::AttemptStatus::Charged,); +} + +// Refunds a payment using the automatic capture flow (Non 3DS). +#[actix_web::test] +async fn should_refund_auto_captured_payment() { + let response = CONNECTOR + .make_payment_and_refund(payment_method_details(), None, get_default_payment_info()) + .await + .unwrap(); + assert_eq!( + response.response.unwrap().refund_status, + enums::RefundStatus::Success, + ); +} + +// Partially refunds a payment using the automatic capture flow (Non 3DS). +#[actix_web::test] +async fn should_partially_refund_succeeded_payment() { + let refund_response = CONNECTOR + .make_payment_and_refund( + payment_method_details(), + Some(types::RefundsData { + refund_amount: 50, + ..utils::PaymentRefundType::default().0 + }), + get_default_payment_info(), + ) + .await + .unwrap(); + assert_eq!( + refund_response.response.unwrap().refund_status, + enums::RefundStatus::Success, + ); +} + +// Creates multiple refunds against a payment using the automatic capture flow (Non 3DS). +#[actix_web::test] +async fn should_refund_succeeded_payment_multiple_times() { + CONNECTOR + .make_payment_and_multiple_refund( + payment_method_details(), + Some(types::RefundsData { + refund_amount: 50, + ..utils::PaymentRefundType::default().0 + }), + get_default_payment_info(), + ) + .await; +} + +// Synchronizes a refund using the automatic capture flow (Non 3DS). +#[actix_web::test] +async fn should_sync_refund() { + let refund_response = CONNECTOR + .make_payment_and_refund(payment_method_details(), None, get_default_payment_info()) + .await + .unwrap(); + let response = CONNECTOR + .rsync_retry_till_status_matches( + enums::RefundStatus::Success, + refund_response.response.unwrap().connector_refund_id, + None, + get_default_payment_info(), + ) + .await + .unwrap(); + assert_eq!( + response.response.unwrap().refund_status, + enums::RefundStatus::Success, + ); +} + +// Cards Negative scenarios +// Creates a payment with incorrect CVC. +#[actix_web::test] +async fn should_fail_payment_for_incorrect_cvc() { + let response = CONNECTOR + .make_payment( + Some(types::PaymentsAuthorizeData { + payment_method_data: types::domain::PaymentMethodData::Card(domain::Card { + card_cvc: Secret::new("12345".to_string()), + ..utils::CCardType::default().0 + }), + ..utils::PaymentAuthorizeType::default().0 + }), + get_default_payment_info(), + ) + .await + .unwrap(); + assert_eq!( + response.response.unwrap_err().message, + "Your card's security code is invalid.".to_string(), + ); +} + +// Creates a payment with incorrect expiry month. +#[actix_web::test] +async fn should_fail_payment_for_invalid_exp_month() { + let response = CONNECTOR + .make_payment( + Some(types::PaymentsAuthorizeData { + payment_method_data: types::domain::PaymentMethodData::Card(domain::Card { + card_exp_month: Secret::new("20".to_string()), + ..utils::CCardType::default().0 + }), + ..utils::PaymentAuthorizeType::default().0 + }), + get_default_payment_info(), + ) + .await + .unwrap(); + assert_eq!( + response.response.unwrap_err().message, + "Your card's expiration month is invalid.".to_string(), + ); +} + +// Creates a payment with incorrect expiry year. +#[actix_web::test] +async fn should_fail_payment_for_incorrect_expiry_year() { + let response = CONNECTOR + .make_payment( + Some(types::PaymentsAuthorizeData { + payment_method_data: types::domain::PaymentMethodData::Card(domain::Card { + card_exp_year: Secret::new("2000".to_string()), + ..utils::CCardType::default().0 + }), + ..utils::PaymentAuthorizeType::default().0 + }), + get_default_payment_info(), + ) + .await + .unwrap(); + assert_eq!( + response.response.unwrap_err().message, + "Your card's expiration year is invalid.".to_string(), + ); +} + +// Voids a payment using automatic capture flow (Non 3DS). +#[actix_web::test] +async fn should_fail_void_payment_for_auto_capture() { + let authorize_response = CONNECTOR + .make_payment(payment_method_details(), get_default_payment_info()) + .await + .unwrap(); + assert_eq!(authorize_response.status, enums::AttemptStatus::Charged); + let txn_id = utils::get_connector_transaction_id(authorize_response.response); + assert_ne!(txn_id, None, "Empty connector transaction id"); + let void_response = CONNECTOR + .void_payment(txn_id.unwrap(), None, get_default_payment_info()) + .await + .unwrap(); + assert_eq!( + void_response.response.unwrap_err().message, + "You cannot cancel this PaymentIntent because it has a status of succeeded." + ); +} + +// Captures a payment using invalid connector payment id. +#[actix_web::test] +async fn should_fail_capture_for_invalid_payment() { + let capture_response = CONNECTOR + .capture_payment("123456789".to_string(), None, get_default_payment_info()) + .await + .unwrap(); + assert_eq!( + capture_response.response.unwrap_err().message, + String::from("No such payment_intent: '123456789'") + ); +} + +// Refunds a payment with refund amount higher than payment amount. +#[actix_web::test] +async fn should_fail_for_refund_amount_higher_than_payment_amount() { + let response = CONNECTOR + .make_payment_and_refund( + payment_method_details(), + Some(types::RefundsData { + refund_amount: 150, + ..utils::PaymentRefundType::default().0 + }), + get_default_payment_info(), + ) + .await + .unwrap(); + assert_eq!( + response.response.unwrap_err().message, + "Refund amount (₹1.50) is greater than charge amount (₹1.00)", + ); +} + +// Connector dependent test cases goes here + +// [#478]: add unit tests for non 3DS, wallets & webhooks in connector tests diff --git a/crates/router/tests/connectors/sample_auth.toml b/crates/router/tests/connectors/sample_auth.toml index a1f7919cd78..b712eb2b384 100644 --- a/crates/router/tests/connectors/sample_auth.toml +++ b/crates/router/tests/connectors/sample_auth.toml @@ -218,3 +218,7 @@ api_key="API Key" [zsl] api_key= "Key" key1= "Merchant id" + + +[mifinity] +api_key="API Key" diff --git a/crates/test_utils/src/connector_auth.rs b/crates/test_utils/src/connector_auth.rs index b5c18cfa5d4..61ef75ee0fb 100644 --- a/crates/test_utils/src/connector_auth.rs +++ b/crates/test_utils/src/connector_auth.rs @@ -38,6 +38,7 @@ pub struct ConnectorAuthentication { pub gocardless: Option, pub helcim: Option, pub iatapay: Option, + pub mifinity: Option, pub mollie: Option, pub multisafepay: Option, pub netcetera: Option, diff --git a/loadtest/config/development.toml b/loadtest/config/development.toml index 799582c733f..892cf94005e 100644 --- a/loadtest/config/development.toml +++ b/loadtest/config/development.toml @@ -95,6 +95,7 @@ gocardless.base_url = "https://api-sandbox.gocardless.com" helcim.base_url = "https://api.helcim.com/" iatapay.base_url = "https://sandbox.iata-pay.iata.org/api/v1" klarna.base_url = "https://api-na.playground.klarna.com/" +mifinity.base_url = "https://demo.mifinity.com/" mollie.base_url = "https://api.mollie.com/v2/" mollie.secondary_base_url = "https://api.cc.mollie.com/v1/" multisafepay.base_url = "https://testapi.multisafepay.com/" @@ -138,7 +139,8 @@ zsl.base_url = "https://api.sitoffalb.net/" apple_pay = { country = "AU,CN,HK,JP,MO,MY,NZ,SG,TW,AM,AT,AZ,BY,BE,BG,HR,CY,CZ,DK,EE,FO,FI,FR,GE,DE,GR,GL,GG,HU,IS,IE,IM,IT,KZ,JE,LV,LI,LT,LU,MT,MD,MC,ME,NL,NO,PL,PT,RO,SM,RS,SK,SI,ES,SE,CH,UA,GB,AR,CO,CR,BR,MX,PE,BH,IL,JO,KW,PS,QA,SA,AE,CA,UM,US,KR,VN,MA,ZA,VA,CL,SV,GT,HN,PA", currency = "AED,AUD,CHF,CAD,EUR,GBP,HKD,SGD,USD" } [connectors.supported] -wallets = ["klarna", "braintree", "applepay"] +wallets = ["klarna", + "mifinity", "braintree", "applepay"] rewards = ["cashtocode", "zen"] cards = [ "aci", diff --git a/openapi/openapi_spec.json b/openapi/openapi_spec.json index ec7d820a139..201f6582dda 100644 --- a/openapi/openapi_spec.json +++ b/openapi/openapi_spec.json @@ -17306,6 +17306,7 @@ "helcim", "iatapay", "klarna", + "mifinity", "mollie", "multisafepay", "nexinets", diff --git a/scripts/add_connector.sh b/scripts/add_connector.sh index d8e789408bb..9054712ba10 100755 --- a/scripts/add_connector.sh +++ b/scripts/add_connector.sh @@ -6,7 +6,7 @@ function find_prev_connector() { git checkout $self cp $self $self.tmp # Add new connector to existing list and sort it - connectors=(aci adyen airwallex applepay authorizedotnet bambora bankofamerica billwerk bitpay bluesnap boku braintree cashtocode checkout coinbase cryptopay cybersource dlocal dummyconnector ebanx fiserv forte globalpay globepay gocardless helcim iatapay klarna mollie multisafepay netcetera nexinets noon nuvei opayo opennode payeezy payme paypal payu placetopay powertranz prophetpay rapyd shift4 square stax stripe threedsecureio trustpay tsys volt wise worldline worldpay zsl "$1") + connectors=(aci adyen airwallex applepay authorizedotnet bambora bankofamerica billwerk bitpay bluesnap boku braintree cashtocode checkout coinbase cryptopay cybersource dlocal dummyconnector ebanx fiserv forte globalpay globepay gocardless helcim iatapay klarna mifinity mollie multisafepay netcetera nexinets noon nuvei opayo opennode payeezy payme paypal payu placetopay powertranz prophetpay rapyd shift4 square stax stripe threedsecureio trustpay tsys volt wise worldline worldpay zsl "$1") IFS=$'\n' sorted=($(sort <<<"${connectors[*]}")); unset IFS res=`echo ${sorted[@]}` sed -i'' -e "s/^ connectors=.*/ connectors=($res \"\$1\")/" $self.tmp From 59e79ff205dfc2fded993b7a9130b9953bdd07e2 Mon Sep 17 00:00:00 2001 From: Apoorv Dixit <64925866+apoorvdixit88@users.noreply.github.com> Date: Tue, 7 May 2024 19:41:24 +0530 Subject: [PATCH 22/34] feat(users): implement force set and force change password (#4564) Co-authored-by: hyperswitch-bot[bot] <148525504+hyperswitch-bot[bot]@users.noreply.github.com> --- config/config.example.toml | 3 + config/deployments/integration_test.toml | 3 + config/deployments/production.toml | 3 + config/deployments/sandbox.toml | 3 + config/development.toml | 3 + config/docker_compose.toml | 3 + crates/api_models/src/events/user.rs | 5 +- crates/api_models/src/user.rs | 5 + crates/common_enums/src/enums.rs | 1 + crates/diesel_models/src/schema.rs | 1 + crates/diesel_models/src/user.rs | 20 +++- .../src/configs/secrets_transformers.rs | 1 + crates/router/src/configs/settings.rs | 6 + crates/router/src/core/errors/user.rs | 2 +- crates/router/src/core/user.rs | 104 ++++++++++++++++-- crates/router/src/db/user.rs | 15 ++- crates/router/src/routes/app.rs | 1 + crates/router/src/routes/lock_utils.rs | 1 + crates/router/src/routes/user.rs | 56 ++++++++-- crates/router/src/types/domain/user.rs | 20 ++++ .../src/types/domain/user/decision_manager.rs | 9 +- crates/router_env/src/logger/types.rs | 2 + loadtest/config/development.toml | 3 + .../down.sql | 2 + .../up.sql | 2 + 25 files changed, 240 insertions(+), 34 deletions(-) create mode 100644 migrations/2024-05-07-092445_add_last_password_modified_at_column_to_users/down.sql create mode 100644 migrations/2024-05-07-092445_add_last_password_modified_at_column_to_users/up.sql diff --git a/config/config.example.toml b/config/config.example.toml index d43668c4466..a50aa2ec214 100644 --- a/config/config.example.toml +++ b/config/config.example.toml @@ -347,6 +347,9 @@ active_email_client = "SES" # The currently active email client email_role_arn = "" # The amazon resource name ( arn ) of the role which has permission to send emails sts_role_session_name = "" # An identifier for the assumed role session, used to uniquely identify a session. +[user] +password_validity_in_days = 90 # Number of days after which password should be updated + #tokenization configuration which describe token lifetime and payment method for specific connector [tokenization] stripe = { long_lived_token = false, payment_method = "wallet", payment_method_type = { type = "disable_only", list = "google_pay" } } diff --git a/config/deployments/integration_test.toml b/config/deployments/integration_test.toml index 1c4dc0e7bf2..0a5d4243737 100644 --- a/config/deployments/integration_test.toml +++ b/config/deployments/integration_test.toml @@ -109,6 +109,9 @@ refund_tolerance = 100 # Fake d refund_ttl = 172800 # Time to live for dummy connector refund in redis slack_invite_url = "https://join.slack.com/t/hyperswitch-io/shared_invite/zt-2awm23agh-p_G5xNpziv6yAiedTkkqLg" # Slack invite url for hyperswitch +[user] +password_validity_in_days = 90 + [frm] enabled = true diff --git a/config/deployments/production.toml b/config/deployments/production.toml index e136c05342a..a7be2a2dcf3 100644 --- a/config/deployments/production.toml +++ b/config/deployments/production.toml @@ -116,6 +116,9 @@ refund_tolerance = 100 # Fake d refund_ttl = 172800 # Time to live for dummy connector refund in redis slack_invite_url = "https://join.slack.com/t/hyperswitch-io/shared_invite/zt-2awm23agh-p_G5xNpziv6yAiedTkkqLg" # Slack invite url for hyperswitch +[user] +password_validity_in_days = 90 + [frm] enabled = false diff --git a/config/deployments/sandbox.toml b/config/deployments/sandbox.toml index 3468c443a1d..51bd5bc7211 100644 --- a/config/deployments/sandbox.toml +++ b/config/deployments/sandbox.toml @@ -116,6 +116,9 @@ refund_tolerance = 100 # Fake d refund_ttl = 172800 # Time to live for dummy connector refund in redis slack_invite_url = "https://join.slack.com/t/hyperswitch-io/shared_invite/zt-2awm23agh-p_G5xNpziv6yAiedTkkqLg" # Slack invite url for hyperswitch +[user] +password_validity_in_days = 90 + [frm] enabled = true diff --git a/config/development.toml b/config/development.toml index 83730662adc..3f6a1b4b055 100644 --- a/config/development.toml +++ b/config/development.toml @@ -263,6 +263,9 @@ active_email_client = "SES" email_role_arn = "" sts_role_session_name = "" +[user] +password_validity_in_days = 90 + [bank_config.eps] stripe = { banks = "arzte_und_apotheker_bank,austrian_anadi_bank_ag,bank_austria,bankhaus_carl_spangler,bankhaus_schelhammer_und_schattera_ag,bawag_psk_ag,bks_bank_ag,brull_kallmus_bank_ag,btv_vier_lander_bank,capital_bank_grawe_gruppe_ag,dolomitenbank,easybank_ag,erste_bank_und_sparkassen,hypo_alpeadriabank_international_ag,hypo_noe_lb_fur_niederosterreich_u_wien,hypo_oberosterreich_salzburg_steiermark,hypo_tirol_bank_ag,hypo_vorarlberg_bank_ag,hypo_bank_burgenland_aktiengesellschaft,marchfelder_bank,oberbank_ag,raiffeisen_bankengruppe_osterreich,schoellerbank_ag,sparda_bank_wien,volksbank_gruppe,volkskreditbank_ag,vr_bank_braunau" } adyen = { banks = "bank_austria,bawag_psk_ag,dolomitenbank,easybank_ag,erste_bank_und_sparkassen,hypo_tirol_bank_ag,posojilnica_bank_e_gen,raiffeisen_bankengruppe_osterreich,schoellerbank_ag,sparda_bank_wien,volksbank_gruppe,volkskreditbank_ag" } diff --git a/config/docker_compose.toml b/config/docker_compose.toml index 7dfab5548bf..aae6447884f 100644 --- a/config/docker_compose.toml +++ b/config/docker_compose.toml @@ -51,6 +51,9 @@ jwt_secret = "secret" master_enc_key = "73ad7bbbbc640c845a150f67d058b279849370cd2c1f3c67c4dd6c869213e13a" recon_admin_api_key = "recon_test_admin" +[user] +password_validity_in_days = 90 + [locker] host = "" host_rs = "" diff --git a/crates/api_models/src/events/user.rs b/crates/api_models/src/events/user.rs index b29b28f0136..dab9ace3ac2 100644 --- a/crates/api_models/src/events/user.rs +++ b/crates/api_models/src/events/user.rs @@ -14,8 +14,8 @@ use crate::user::{ CreateInternalUserRequest, DashboardEntryResponse, ForgotPasswordRequest, GetUserDetailsResponse, GetUserRoleDetailsRequest, GetUserRoleDetailsResponse, InviteUserRequest, ListUsersResponse, ReInviteUserRequest, ResetPasswordRequest, - SendVerifyEmailRequest, SignInResponse, SignUpRequest, SignUpWithMerchantIdRequest, - SwitchMerchantIdRequest, TokenOrPayloadResponse, TokenResponse, + RotatePasswordRequest, SendVerifyEmailRequest, SignInResponse, SignUpRequest, + SignUpWithMerchantIdRequest, SwitchMerchantIdRequest, TokenOrPayloadResponse, TokenResponse, UpdateUserAccountDetailsRequest, UserFromEmailRequest, UserMerchantCreate, VerifyEmailRequest, }; @@ -60,6 +60,7 @@ common_utils::impl_misc_api_event_type!( ConnectAccountRequest, ForgotPasswordRequest, ResetPasswordRequest, + RotatePasswordRequest, InviteUserRequest, ReInviteUserRequest, VerifyEmailRequest, diff --git a/crates/api_models/src/user.rs b/crates/api_models/src/user.rs index fe4e37f8a8c..b2128cc949c 100644 --- a/crates/api_models/src/user.rs +++ b/crates/api_models/src/user.rs @@ -91,6 +91,11 @@ pub struct ResetPasswordRequest { pub password: Secret, } +#[derive(serde::Deserialize, Debug, serde::Serialize)] +pub struct RotatePasswordRequest { + pub password: Secret, +} + #[derive(Debug, serde::Deserialize, serde::Serialize, Clone)] pub struct InviteUserRequest { pub email: pii::Email, diff --git a/crates/common_enums/src/enums.rs b/crates/common_enums/src/enums.rs index 70dc1ade0a6..4ffd4ba23dd 100644 --- a/crates/common_enums/src/enums.rs +++ b/crates/common_enums/src/enums.rs @@ -2717,6 +2717,7 @@ pub enum TokenPurpose { TOTP, VerifyEmail, AcceptInvitationFromEmail, + ForceSetPassword, ResetPassword, AcceptInvite, UserInfo, diff --git a/crates/diesel_models/src/schema.rs b/crates/diesel_models/src/schema.rs index 2e28bc5d30f..810d82d321f 100644 --- a/crates/diesel_models/src/schema.rs +++ b/crates/diesel_models/src/schema.rs @@ -1191,6 +1191,7 @@ diesel::table! { last_modified_at -> Timestamp, #[max_length = 64] preferred_merchant_id -> Nullable, + last_password_modified_at -> Nullable, } } diff --git a/crates/diesel_models/src/user.rs b/crates/diesel_models/src/user.rs index 84fe8710060..850619f8af6 100644 --- a/crates/diesel_models/src/user.rs +++ b/crates/diesel_models/src/user.rs @@ -20,6 +20,7 @@ pub struct User { pub created_at: PrimitiveDateTime, pub last_modified_at: PrimitiveDateTime, pub preferred_merchant_id: Option, + pub last_password_modified_at: Option, } #[derive( @@ -35,6 +36,7 @@ pub struct UserNew { pub created_at: Option, pub last_modified_at: Option, pub preferred_merchant_id: Option, + pub last_password_modified_at: Option, } #[derive(Clone, Debug, AsChangeset, router_derive::DebugAsDisplay)] @@ -45,6 +47,7 @@ pub struct UserUpdateInternal { is_verified: Option, last_modified_at: PrimitiveDateTime, preferred_merchant_id: Option, + last_password_modified_at: Option, } #[derive(Debug)] @@ -52,10 +55,12 @@ pub enum UserUpdate { VerifyUser, AccountUpdate { name: Option, - password: Option>, is_verified: Option, preferred_merchant_id: Option, }, + PasswordUpdate { + password: Option>, + }, } impl From for UserUpdateInternal { @@ -68,18 +73,27 @@ impl From for UserUpdateInternal { is_verified: Some(true), last_modified_at, preferred_merchant_id: None, + last_password_modified_at: None, }, UserUpdate::AccountUpdate { name, - password, is_verified, preferred_merchant_id, } => Self { name, - password, + password: None, is_verified, last_modified_at, preferred_merchant_id, + last_password_modified_at: None, + }, + UserUpdate::PasswordUpdate { password } => Self { + name: None, + password, + is_verified: None, + last_modified_at, + preferred_merchant_id: None, + last_password_modified_at: Some(last_modified_at), }, } } diff --git a/crates/router/src/configs/secrets_transformers.rs b/crates/router/src/configs/secrets_transformers.rs index 423f8e03d23..dc8b5dd6bd5 100644 --- a/crates/router/src/configs/secrets_transformers.rs +++ b/crates/router/src/configs/secrets_transformers.rs @@ -334,6 +334,7 @@ pub(crate) async fn fetch_raw_secrets( dummy_connector: conf.dummy_connector, #[cfg(feature = "email")] email: conf.email, + user: conf.user, mandates: conf.mandates, network_transaction_id_supported_connectors: conf .network_transaction_id_supported_connectors, diff --git a/crates/router/src/configs/settings.rs b/crates/router/src/configs/settings.rs index a07fa9ad2b4..d2310aa585a 100644 --- a/crates/router/src/configs/settings.rs +++ b/crates/router/src/configs/settings.rs @@ -89,6 +89,7 @@ pub struct Settings { pub dummy_connector: DummyConnector, #[cfg(feature = "email")] pub email: EmailSettings, + pub user: UserSettings, pub cors: CorsSettings, pub mandates: Mandates, pub network_transaction_id_supported_connectors: NetworkTransactionIdSupportedConnectors, @@ -390,6 +391,11 @@ pub struct Secrets { pub master_enc_key: Secret, } +#[derive(Debug, Clone, Default, Deserialize)] +pub struct UserSettings { + pub password_validity_in_days: u16, +} + #[derive(Debug, Deserialize, Clone)] #[serde(default)] pub struct Locker { diff --git a/crates/router/src/core/errors/user.rs b/crates/router/src/core/errors/user.rs index 8a7d77cdbc9..ddcd10c32e4 100644 --- a/crates/router/src/core/errors/user.rs +++ b/crates/router/src/core/errors/user.rs @@ -198,7 +198,7 @@ impl UserErrors { Self::IpAddressParsingFailed => "Something went wrong", Self::InvalidMetadataRequest => "Invalid Metadata Request", Self::MerchantIdParsingError => "Invalid Merchant Id", - Self::ChangePasswordError => "Old and new password cannot be the same", + Self::ChangePasswordError => "Old and new password cannot be same", Self::InvalidDeleteOperation => "Delete Operation Not Supported", Self::MaxInvitationsError => "Maximum invite count per request exceeded", Self::RoleNotFound => "Role Not Found", diff --git a/crates/router/src/core/user.rs b/crates/router/src/core/user.rs index 6ad0afa9104..e51ad6120c9 100644 --- a/crates/router/src/core/user.rs +++ b/crates/router/src/core/user.rs @@ -349,11 +349,8 @@ pub async fn change_password( .store .update_user_by_user_id( user.get_user_id(), - diesel_models::user::UserUpdate::AccountUpdate { - name: None, + diesel_models::user::UserUpdate::PasswordUpdate { password: Some(new_password_hash), - is_verified: None, - preferred_merchant_id: None, }, ) .await @@ -419,9 +416,48 @@ pub async fn forgot_password( Ok(ApplicationResponse::StatusOk) } +pub async fn rotate_password( + state: AppState, + user_token: auth::UserFromSinglePurposeToken, + request: user_api::RotatePasswordRequest, + _req_state: ReqState, +) -> UserResponse<()> { + let user: domain::UserFromStorage = state + .store + .find_user_by_id(&user_token.user_id) + .await + .change_context(UserErrors::InternalServerError)? + .into(); + + let password = domain::UserPassword::new(request.password.to_owned())?; + let hash_password = utils::user::password::generate_password_hash(password.get_secret())?; + + if user.compare_password(request.password).is_ok() { + return Err(UserErrors::ChangePasswordError.into()); + } + + let user = state + .store + .update_user_by_user_id( + &user_token.user_id, + storage_user::UserUpdate::PasswordUpdate { + password: Some(hash_password), + }, + ) + .await + .change_context(UserErrors::InternalServerError)?; + + let _ = auth::blacklist::insert_user_in_blacklist(&state, &user.user_id) + .await + .map_err(|e| logger::error!(?e)); + + Ok(ApplicationResponse::StatusOk) +} + #[cfg(feature = "email")] -pub async fn reset_password( +pub async fn reset_password_token_only_flow( state: AppState, + user_token: auth::UserFromSinglePurposeToken, request: user_api::ResetPasswordRequest, ) -> UserResponse<()> { let token = request.token.expose(); @@ -431,8 +467,60 @@ pub async fn reset_password( auth::blacklist::check_email_token_in_blacklist(&state, &token).await?; + let user_from_db: domain::UserFromStorage = state + .store + .find_user_by_email( + &email_token + .get_email() + .change_context(UserErrors::InternalServerError)?, + ) + .await + .change_context(UserErrors::InternalServerError)? + .into(); + + if user_from_db.get_user_id() != user_token.user_id { + return Err(UserErrors::LinkInvalid.into()); + } + let password = domain::UserPassword::new(request.password)?; + let hash_password = utils::user::password::generate_password_hash(password.get_secret())?; + + let user = state + .store + .update_user_by_email( + &email_token + .get_email() + .change_context(UserErrors::InternalServerError)?, + storage_user::UserUpdate::PasswordUpdate { + password: Some(hash_password), + }, + ) + .await + .change_context(UserErrors::InternalServerError)?; + let _ = auth::blacklist::insert_email_token_in_blacklist(&state, &token) + .await + .map_err(|e| logger::error!(?e)); + let _ = auth::blacklist::insert_user_in_blacklist(&state, &user.user_id) + .await + .map_err(|e| logger::error!(?e)); + + Ok(ApplicationResponse::StatusOk) +} + +#[cfg(feature = "email")] +pub async fn reset_password( + state: AppState, + request: user_api::ResetPasswordRequest, +) -> UserResponse<()> { + let token = request.token.expose(); + let email_token = auth::decode_jwt::(&token, &state) + .await + .change_context(UserErrors::LinkInvalid)?; + + auth::blacklist::check_email_token_in_blacklist(&state, &token).await?; + + let password = domain::UserPassword::new(request.password)?; let hash_password = utils::user::password::generate_password_hash(password.get_secret())?; let user = state @@ -441,11 +529,8 @@ pub async fn reset_password( &email_token .get_email() .change_context(UserErrors::InternalServerError)?, - storage_user::UserUpdate::AccountUpdate { - name: None, + storage_user::UserUpdate::PasswordUpdate { password: Some(hash_password), - is_verified: Some(true), - preferred_merchant_id: None, }, ) .await @@ -1449,7 +1534,6 @@ pub async fn update_user_details( let user_update = storage_user::UserUpdate::AccountUpdate { name: name.map(|x| x.get_secret().expose()), - password: None, is_verified: None, preferred_merchant_id: req.preferred_merchant_id, }; diff --git a/crates/router/src/db/user.rs b/crates/router/src/db/user.rs index 47b3d376a16..9ec7cf6fab4 100644 --- a/crates/router/src/db/user.rs +++ b/crates/router/src/db/user.rs @@ -162,6 +162,7 @@ impl UserInterface for MockDb { created_at: user_data.created_at.unwrap_or(time_now), last_modified_at: user_data.created_at.unwrap_or(time_now), preferred_merchant_id: user_data.preferred_merchant_id, + last_password_modified_at: user_data.last_password_modified_at, }; users.push(user.clone()); Ok(user) @@ -218,18 +219,21 @@ impl UserInterface for MockDb { }, storage::UserUpdate::AccountUpdate { name, - password, is_verified, preferred_merchant_id, } => storage::User { name: name.clone().map(Secret::new).unwrap_or(user.name.clone()), - password: password.clone().unwrap_or(user.password.clone()), is_verified: is_verified.unwrap_or(user.is_verified), preferred_merchant_id: preferred_merchant_id .clone() .or(user.preferred_merchant_id.clone()), ..user.to_owned() }, + storage::UserUpdate::PasswordUpdate { password } => storage::User { + password: password.clone().unwrap_or(user.password.clone()), + last_password_modified_at: Some(common_utils::date_time::now()), + ..user.to_owned() + }, }; user.to_owned() }) @@ -258,18 +262,21 @@ impl UserInterface for MockDb { }, storage::UserUpdate::AccountUpdate { name, - password, is_verified, preferred_merchant_id, } => storage::User { name: name.clone().map(Secret::new).unwrap_or(user.name.clone()), - password: password.clone().unwrap_or(user.password.clone()), is_verified: is_verified.unwrap_or(user.is_verified), preferred_merchant_id: preferred_merchant_id .clone() .or(user.preferred_merchant_id.clone()), ..user.to_owned() }, + storage::UserUpdate::PasswordUpdate { password } => storage::User { + password: password.clone().unwrap_or(user.password.clone()), + last_password_modified_at: Some(common_utils::date_time::now()), + ..user.to_owned() + }, }; user.to_owned() }) diff --git a/crates/router/src/routes/app.rs b/crates/router/src/routes/app.rs index 0ee44429756..857da69d4b3 100644 --- a/crates/router/src/routes/app.rs +++ b/crates/router/src/routes/app.rs @@ -1180,6 +1180,7 @@ impl User { .service(web::resource("").route(web::get().to(get_user_details))) .service(web::resource("/v2/signin").route(web::post().to(user_signin))) .service(web::resource("/signout").route(web::post().to(signout))) + .service(web::resource("/rotate_password").route(web::post().to(rotate_password))) .service(web::resource("/change_password").route(web::post().to(change_password))) .service(web::resource("/internal_signup").route(web::post().to(internal_user_signup))) .service(web::resource("/switch_merchant").route(web::post().to(switch_merchant_id))) diff --git a/crates/router/src/routes/lock_utils.rs b/crates/router/src/routes/lock_utils.rs index 1e301b069ae..5f8346a8f23 100644 --- a/crates/router/src/routes/lock_utils.rs +++ b/crates/router/src/routes/lock_utils.rs @@ -203,6 +203,7 @@ impl From for ApiIdentifier { | Flow::ListUsersForMerchantAccount | Flow::ForgotPassword | Flow::ResetPassword + | Flow::RotatePassword | Flow::InviteMultipleUser | Flow::ReInviteUser | Flow::UserSignUpWithMerchantId diff --git a/crates/router/src/routes/user.rs b/crates/router/src/routes/user.rs index 1ffb4dc8d1a..f990438b2f1 100644 --- a/crates/router/src/routes/user.rs +++ b/crates/router/src/routes/user.rs @@ -5,6 +5,7 @@ use api_models::{ errors::types::ApiErrorResponse, user::{self as user_api}, }; +use common_enums::TokenPurpose; use common_utils::errors::ReportSwitchExt; use router_env::Flow; @@ -358,43 +359,78 @@ pub async fn list_users_for_merchant_account( .await } -#[cfg(feature = "email")] -pub async fn forgot_password( +pub async fn rotate_password( state: web::Data, req: HttpRequest, - payload: web::Json, + payload: web::Json, ) -> HttpResponse { - let flow = Flow::ForgotPassword; + let flow = Flow::RotatePassword; Box::pin(api::server_wrap( flow, state.clone(), &req, payload.into_inner(), - |state, _, payload, _| user_core::forgot_password(state, payload), - &auth::NoAuth, + user_core::rotate_password, + &auth::SinglePurposeJWTAuth(TokenPurpose::ForceSetPassword), api_locking::LockAction::NotApplicable, )) .await } #[cfg(feature = "email")] -pub async fn reset_password( +pub async fn forgot_password( state: web::Data, req: HttpRequest, - payload: web::Json, + payload: web::Json, ) -> HttpResponse { - let flow = Flow::ResetPassword; + let flow = Flow::ForgotPassword; Box::pin(api::server_wrap( flow, state.clone(), &req, payload.into_inner(), - |state, _, payload, _| user_core::reset_password(state, payload), + |state, _, payload, _| user_core::forgot_password(state, payload), &auth::NoAuth, api_locking::LockAction::NotApplicable, )) .await } + +#[cfg(feature = "email")] +pub async fn reset_password( + state: web::Data, + req: HttpRequest, + payload: web::Json, + query: web::Query, +) -> HttpResponse { + let flow = Flow::ResetPassword; + let is_token_only = query.into_inner().token_only; + if let Some(true) = is_token_only { + Box::pin(api::server_wrap( + flow, + state.clone(), + &req, + payload.into_inner(), + |state, user, payload, _| { + user_core::reset_password_token_only_flow(state, user, payload) + }, + &auth::SinglePurposeJWTAuth(TokenPurpose::ResetPassword), + api_locking::LockAction::NotApplicable, + )) + .await + } else { + Box::pin(api::server_wrap( + flow, + state.clone(), + &req, + payload.into_inner(), + |state, _, payload, _| user_core::reset_password(state, payload), + &auth::NoAuth, + api_locking::LockAction::NotApplicable, + )) + .await + } +} pub async fn invite_multiple_user( state: web::Data, req: HttpRequest, diff --git a/crates/router/src/types/domain/user.rs b/crates/router/src/types/domain/user.rs index 5716e4e161d..7e1a1eee3e8 100644 --- a/crates/router/src/types/domain/user.rs +++ b/crates/router/src/types/domain/user.rs @@ -806,6 +806,26 @@ impl UserFromStorage { Ok(Some(days_left_for_verification.whole_days())) } + pub fn is_password_rotate_required(&self, state: &AppState) -> UserResult { + let last_password_modified_at = + if let Some(last_password_modified_at) = self.0.last_password_modified_at { + last_password_modified_at.date() + } else { + return Ok(true); + }; + + let password_change_duration = + time::Duration::days(state.conf.user.password_validity_in_days.into()); + let last_date_for_password_rotate = last_password_modified_at + .checked_add(password_change_duration) + .ok_or(UserErrors::InternalServerError)?; + + let today = common_utils::date_time::now().date(); + let days_left_for_password_rotate = last_date_for_password_rotate - today; + + Ok(days_left_for_password_rotate.whole_days() < 0) + } + pub fn get_preferred_merchant_id(&self) -> Option { self.0.preferred_merchant_id.clone() } diff --git a/crates/router/src/types/domain/user/decision_manager.rs b/crates/router/src/types/domain/user/decision_manager.rs index 616c595b244..d635ac064fe 100644 --- a/crates/router/src/types/domain/user/decision_manager.rs +++ b/crates/router/src/types/domain/user/decision_manager.rs @@ -44,8 +44,7 @@ impl SPTFlow { Self::AcceptInvitationFromEmail | Self::ResetPassword => Ok(true), Self::VerifyEmail => Ok(user.0.is_verified), // Final Checks - // TODO: this should be based on last_password_modified_at as a placeholder using false - Self::ForceSetPassword => Ok(false), + Self::ForceSetPassword => user.is_password_rotate_required(state), Self::MerchantSelect => user .get_roles_from_db(state) .await @@ -159,8 +158,9 @@ const ACCEPT_INVITATION_FROM_EMAIL_FLOW: [UserFlow; 5] = [ UserFlow::JWTFlow(JWTFlow::UserInfo), ]; -const RESET_PASSWORD_FLOW: [UserFlow; 2] = [ +const RESET_PASSWORD_FLOW: [UserFlow; 3] = [ UserFlow::SPTFlow(SPTFlow::TOTP), + UserFlow::SPTFlow(SPTFlow::VerifyEmail), UserFlow::SPTFlow(SPTFlow::ResetPassword), ]; @@ -286,7 +286,8 @@ impl From for TokenPurpose { SPTFlow::VerifyEmail => Self::VerifyEmail, SPTFlow::AcceptInvitationFromEmail => Self::AcceptInvitationFromEmail, SPTFlow::MerchantSelect => Self::AcceptInvite, - SPTFlow::ResetPassword | SPTFlow::ForceSetPassword => Self::ResetPassword, + SPTFlow::ResetPassword => Self::ResetPassword, + SPTFlow::ForceSetPassword => Self::ForceSetPassword, } } } diff --git a/crates/router_env/src/logger/types.rs b/crates/router_env/src/logger/types.rs index 14b235eadb2..b360d20fed1 100644 --- a/crates/router_env/src/logger/types.rs +++ b/crates/router_env/src/logger/types.rs @@ -360,6 +360,8 @@ pub enum Flow { ForgotPassword, /// Reset password using link ResetPassword, + /// Force set or force change password + RotatePassword, /// Invite multiple users InviteMultipleUser, /// Reinvite user diff --git a/loadtest/config/development.toml b/loadtest/config/development.toml index 892cf94005e..8add22ee1db 100644 --- a/loadtest/config/development.toml +++ b/loadtest/config/development.toml @@ -28,6 +28,9 @@ host = "redis-queue" admin_api_key = "test_admin" jwt_secret = "secret" +[user] +password_validity_in_days = 90 + [locker] host = "" host_rs = "" diff --git a/migrations/2024-05-07-092445_add_last_password_modified_at_column_to_users/down.sql b/migrations/2024-05-07-092445_add_last_password_modified_at_column_to_users/down.sql new file mode 100644 index 00000000000..89253d525ce --- /dev/null +++ b/migrations/2024-05-07-092445_add_last_password_modified_at_column_to_users/down.sql @@ -0,0 +1,2 @@ +-- This file should undo anything in `up.sql` +ALTER TABLE users DROP COLUMN IF EXISTS last_password_modified_at; \ No newline at end of file diff --git a/migrations/2024-05-07-092445_add_last_password_modified_at_column_to_users/up.sql b/migrations/2024-05-07-092445_add_last_password_modified_at_column_to_users/up.sql new file mode 100644 index 00000000000..d5846b0fad4 --- /dev/null +++ b/migrations/2024-05-07-092445_add_last_password_modified_at_column_to_users/up.sql @@ -0,0 +1,2 @@ +-- Your SQL goes here +ALTER TABLE users ADD COLUMN IF NOT EXISTS last_password_modified_at TIMESTAMP; \ No newline at end of file From 9e2d76bd10216fd0fcc163805b93ffc9450609a8 Mon Sep 17 00:00:00 2001 From: github-actions <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 8 May 2024 00:12:42 +0000 Subject: [PATCH 23/34] chore(version): 2024.05.08.0 --- CHANGELOG.md | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 461484deb4f..fd3f44b64e7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,38 @@ All notable changes to HyperSwitch will be documented here. - - - +## 2024.05.08.0 + +### Features + +- **FRM:** Add missing fields in Signifyd payment request ([#4554](https://github.com/juspay/hyperswitch/pull/4554)) ([`df2c2ca`](https://github.com/juspay/hyperswitch/commit/df2c2ca22dc4cea986cbbf30850311d3e85000c5)) +- **connector:** + - [Cybersource] Add payout flows for Card ([#4511](https://github.com/juspay/hyperswitch/pull/4511)) ([`a72f040`](https://github.com/juspay/hyperswitch/commit/a72f040d9281744bceb928ef2e8d3a26783aae9e)) + - [MiFinity] add connector template code ([#4447](https://github.com/juspay/hyperswitch/pull/4447)) ([`d974e6e`](https://github.com/juspay/hyperswitch/commit/d974e6e7c2e1e3cd99607183b647c420f4b14d20)) +- **router:** Add an api to enable `connector_agnostic_mit` feature ([#4480](https://github.com/juspay/hyperswitch/pull/4480)) ([`e769abe`](https://github.com/juspay/hyperswitch/commit/e769abe501470185fcca29e0abede0654579da06)) +- **users:** + - Create Token only support for pre-login user flow APIs ([#4558](https://github.com/juspay/hyperswitch/pull/4558)) ([`5ec00d9`](https://github.com/juspay/hyperswitch/commit/5ec00d96de49ae0e0f76c5b19e22db11e7db6dd2)) + - Implement force set and force change password ([#4564](https://github.com/juspay/hyperswitch/pull/4564)) ([`59e79ff`](https://github.com/juspay/hyperswitch/commit/59e79ff205dfc2fded993b7a9130b9953bdd07e2)) + +### Bug Fixes + +- **payment_methods:** Fix deserialization errors for `sdk_eligible_payment_methods` ([#4565](https://github.com/juspay/hyperswitch/pull/4565)) ([`f63a970`](https://github.com/juspay/hyperswitch/commit/f63a97024c755fd30a3403e2146812fe4edb8067)) +- **users:** Add password validations ([#4555](https://github.com/juspay/hyperswitch/pull/4555)) ([`25fe4de`](https://github.com/juspay/hyperswitch/commit/25fe4deb8e9152b37467ac1fea18b3074f0e7624)) + +### Refactors + +- **core:** Refactor authentication core to fetch authentication only within it ([#4138](https://github.com/juspay/hyperswitch/pull/4138)) ([`71a070e`](https://github.com/juspay/hyperswitch/commit/71a070e26989f080031d92a88aa0143836d1ea7b)) +- Remove `configs/pg_agnostic_mit` api as it will not be used ([#4486](https://github.com/juspay/hyperswitch/pull/4486)) ([`99bbc39`](https://github.com/juspay/hyperswitch/commit/99bbc3982fa30f6ffd43334b1fa5da963975fe93)) +- Store `card_cvc` in extended_card_info and extend max ttl ([#4568](https://github.com/juspay/hyperswitch/pull/4568)) ([`1b5b566`](https://github.com/juspay/hyperswitch/commit/1b5b566387da83a2582216e05be4ceb1aa7251be)) + +### Miscellaneous Tasks + +- Address Rust 1.78 clippy lints ([#4545](https://github.com/juspay/hyperswitch/pull/4545)) ([`2216a88`](https://github.com/juspay/hyperswitch/commit/2216a88d25c42ede9862f6d036e7b0586a2e7c28)) + +**Full Changelog:** [`2024.05.07.0...2024.05.08.0`](https://github.com/juspay/hyperswitch/compare/2024.05.07.0...2024.05.08.0) + +- - - + ## 2024.05.07.0 ### Features From 3e1c7eba49de3110a2d71cea8e0540c7182d2058 Mon Sep 17 00:00:00 2001 From: Narayan Bhat <48803246+Narayanbhat166@users.noreply.github.com> Date: Wed, 8 May 2024 11:33:32 +0530 Subject: [PATCH 24/34] feat(business_profile): feature add a config to use `billing` as `payment_method_billing` (#4557) Co-authored-by: hyperswitch-bot[bot] <148525504+hyperswitch-bot[bot]@users.noreply.github.com> --- crates/api_models/src/admin.rs | 9 +++++ crates/diesel_models/src/business_profile.rs | 9 +++++ crates/diesel_models/src/schema.rs | 1 + crates/router/src/core/admin.rs | 2 ++ .../router/src/core/payment_methods/cards.rs | 11 +++++- crates/router/src/core/payments.rs | 35 +++++++++++-------- .../payments/operations/payment_approve.rs | 1 + .../payments/operations/payment_cancel.rs | 1 + .../payments/operations/payment_capture.rs | 1 + .../operations/payment_complete_authorize.rs | 1 + .../payments/operations/payment_confirm.rs | 1 + .../payments/operations/payment_create.rs | 1 + .../payments/operations/payment_reject.rs | 1 + .../payments/operations/payment_session.rs | 1 + .../core/payments/operations/payment_start.rs | 1 + .../payments/operations/payment_status.rs | 1 + .../payments/operations/payment_update.rs | 1 + .../payments_incremental_authorization.rs | 2 +- crates/router/src/core/routing/helpers.rs | 2 ++ crates/router/src/core/utils.rs | 2 +- crates/router/src/types/api/admin.rs | 4 +++ .../router/src/types/api/verify_connector.rs | 2 +- crates/router/tests/connectors/aci.rs | 1 + crates/router/tests/connectors/adyen.rs | 2 ++ crates/router/tests/connectors/airwallex.rs | 1 + crates/router/tests/connectors/bitpay.rs | 1 + crates/router/tests/connectors/bluesnap.rs | 1 + crates/router/tests/connectors/cashtocode.rs | 1 + crates/router/tests/connectors/coinbase.rs | 1 + crates/router/tests/connectors/cryptopay.rs | 1 + crates/router/tests/connectors/cybersource.rs | 1 + crates/router/tests/connectors/dlocal.rs | 1 + crates/router/tests/connectors/forte.rs | 1 + crates/router/tests/connectors/globalpay.rs | 1 + crates/router/tests/connectors/iatapay.rs | 1 + .../router/tests/connectors/multisafepay.rs | 1 + crates/router/tests/connectors/opennode.rs | 1 + crates/router/tests/connectors/payeezy.rs | 1 + crates/router/tests/connectors/payme.rs | 1 + crates/router/tests/connectors/trustpay.rs | 1 + crates/router/tests/connectors/utils.rs | 1 + crates/router/tests/connectors/wise.rs | 1 + crates/router/tests/connectors/worldline.rs | 3 +- .../down.sql | 2 ++ .../up.sql | 3 ++ openapi/openapi_spec.json | 9 +++++ 46 files changed, 108 insertions(+), 20 deletions(-) create mode 100644 migrations/2024-05-06-065226_add_billing_config_to_business_profile/down.sql create mode 100644 migrations/2024-05-06-065226_add_billing_config_to_business_profile/up.sql diff --git a/crates/api_models/src/admin.rs b/crates/api_models/src/admin.rs index 3beeb9071a2..bf4eeb79fef 100644 --- a/crates/api_models/src/admin.rs +++ b/crates/api_models/src/admin.rs @@ -913,6 +913,9 @@ pub struct BusinessProfileCreate { /// External 3DS authentication details pub authentication_connector_details: Option, + + /// Whether to use the billing details passed when creating the intent as payment method billing + pub use_billing_as_payment_method_billing: Option, } #[derive(Clone, Debug, ToSchema, Serialize)] @@ -982,6 +985,9 @@ pub struct BusinessProfileResponse { /// External 3DS authentication details pub authentication_connector_details: Option, + + // Whether to use the billing details passed when creating the intent as payment method billing + pub use_billing_as_payment_method_billing: Option, } #[derive(Clone, Debug, Deserialize, ToSchema, Serialize)] @@ -1046,6 +1052,9 @@ pub struct BusinessProfileUpdate { /// Merchant's config to support extended card info feature pub extended_card_info_config: Option, + + // Whether to use the billing details passed when creating the intent as payment method billing + pub use_billing_as_payment_method_billing: Option, } #[derive(Clone, Debug, serde::Deserialize, serde::Serialize, PartialEq, ToSchema)] diff --git a/crates/diesel_models/src/business_profile.rs b/crates/diesel_models/src/business_profile.rs index 1131e9ad674..41a938d83ed 100644 --- a/crates/diesel_models/src/business_profile.rs +++ b/crates/diesel_models/src/business_profile.rs @@ -38,6 +38,7 @@ pub struct BusinessProfile { pub is_extended_card_info_enabled: Option, pub extended_card_info_config: Option, pub is_connector_agnostic_mit_enabled: Option, + pub use_billing_as_payment_method_billing: Option, } #[derive(Clone, Debug, Insertable, router_derive::DebugAsDisplay)] @@ -67,6 +68,7 @@ pub struct BusinessProfileNew { pub is_extended_card_info_enabled: Option, pub extended_card_info_config: Option, pub is_connector_agnostic_mit_enabled: Option, + pub use_billing_as_payment_method_billing: Option, } #[derive(Clone, Debug, Default, AsChangeset, router_derive::DebugAsDisplay)] @@ -93,6 +95,7 @@ pub struct BusinessProfileUpdateInternal { pub is_extended_card_info_enabled: Option, pub extended_card_info_config: Option, pub is_connector_agnostic_mit_enabled: Option, + pub use_billing_as_payment_method_billing: Option, } #[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] @@ -116,6 +119,7 @@ pub enum BusinessProfileUpdate { session_expiry: Option, authentication_connector_details: Option, extended_card_info_config: Option, + use_billing_as_payment_method_billing: Option, }, ExtendedCardInfoUpdate { is_extended_card_info_enabled: Option, @@ -147,6 +151,7 @@ impl From for BusinessProfileUpdateInternal { session_expiry, authentication_connector_details, extended_card_info_config, + use_billing_as_payment_method_billing, } => Self { profile_name, modified_at, @@ -166,6 +171,7 @@ impl From for BusinessProfileUpdateInternal { session_expiry, authentication_connector_details, extended_card_info_config, + use_billing_as_payment_method_billing, ..Default::default() }, BusinessProfileUpdate::ExtendedCardInfoUpdate { @@ -210,6 +216,7 @@ impl From for BusinessProfile { is_connector_agnostic_mit_enabled: new.is_connector_agnostic_mit_enabled, is_extended_card_info_enabled: new.is_extended_card_info_enabled, extended_card_info_config: new.extended_card_info_config, + use_billing_as_payment_method_billing: new.use_billing_as_payment_method_billing, } } } @@ -237,6 +244,7 @@ impl BusinessProfileUpdate { is_extended_card_info_enabled, extended_card_info_config, is_connector_agnostic_mit_enabled, + use_billing_as_payment_method_billing, } = self.into(); BusinessProfile { profile_name: profile_name.unwrap_or(source.profile_name), @@ -261,6 +269,7 @@ impl BusinessProfileUpdate { is_extended_card_info_enabled, is_connector_agnostic_mit_enabled, extended_card_info_config, + use_billing_as_payment_method_billing, ..source } } diff --git a/crates/diesel_models/src/schema.rs b/crates/diesel_models/src/schema.rs index 810d82d321f..70a227a310a 100644 --- a/crates/diesel_models/src/schema.rs +++ b/crates/diesel_models/src/schema.rs @@ -195,6 +195,7 @@ diesel::table! { is_extended_card_info_enabled -> Nullable, extended_card_info_config -> Nullable, is_connector_agnostic_mit_enabled -> Nullable, + use_billing_as_payment_method_billing -> Nullable, } } diff --git a/crates/router/src/core/admin.rs b/crates/router/src/core/admin.rs index e0cfe0dee89..6433a3819f6 100644 --- a/crates/router/src/core/admin.rs +++ b/crates/router/src/core/admin.rs @@ -440,6 +440,7 @@ pub async fn update_business_profile_cascade( session_expiry: None, authentication_connector_details: None, extended_card_info_config: None, + use_billing_as_payment_method_billing: None, }; let update_futures = business_profiles.iter().map(|business_profile| async { @@ -1691,6 +1692,7 @@ pub async fn update_business_profile( field_name: "authentication_connector_details", })?, extended_card_info_config, + use_billing_as_payment_method_billing: request.use_billing_as_payment_method_billing, }; let updated_business_profile = db diff --git a/crates/router/src/core/payment_methods/cards.rs b/crates/router/src/core/payment_methods/cards.rs index 9ae327269cf..efde37b92a8 100644 --- a/crates/router/src/core/payment_methods/cards.rs +++ b/crates/router/src/core/payment_methods/cards.rs @@ -2135,10 +2135,19 @@ pub async fn list_payment_methods( .to_not_found_response(errors::ApiErrorResponse::PaymentNotFound)?; } + // Check for `use_billing_as_payment_method_billing` config under business_profile + // If this is disabled, then the billing details in required fields will be empty and have to be collected by the customer + let billing_address_for_calculating_required_fields = business_profile + .as_ref() + .and_then(|business_profile| business_profile.use_billing_as_payment_method_billing) + .unwrap_or(true) + .then_some(billing_address.as_ref()) + .flatten(); + let req = api_models::payments::PaymentsRequest::foreign_from(( payment_attempt.as_ref(), shipping_address.as_ref(), - billing_address.as_ref(), + billing_address_for_calculating_required_fields, customer.as_ref(), )); let req_val = serde_json::to_value(req).ok(); diff --git a/crates/router/src/core/payments.rs b/crates/router/src/core/payments.rs index 0e10c2a5417..578b8d80302 100644 --- a/crates/router/src/core/payments.rs +++ b/crates/router/src/core/payments.rs @@ -2426,23 +2426,28 @@ pub mod payment_address { shipping: Option, billing: Option, payment_method_billing: Option, + should_unify_address: Option, ) -> Self { // billing -> .billing, this is the billing details passed in the root of payments request - // payment_method_billing -> .payment_method_data.billing - - // Merge the billing details field from both `payment.billing` and `payment.payment_method_data.billing` - // The unified payment_method_billing will be used as billing address and passed to the connector module - // This unification is required in order to provide backwards compatibility - // so that if `payment.billing` is passed it should be sent to the connector module - // Unify the billing details with `payment_method_data.billing` - let unified_payment_method_billing = payment_method_billing - .as_ref() - .map(|payment_method_billing| { - payment_method_billing - .clone() - .unify_address(billing.as_ref()) - }) - .or(billing.clone()); + // payment_method_billing -> payment_method_data.billing + + let unified_payment_method_billing = if should_unify_address.unwrap_or(true) { + // Merge the billing details field from both `payment.billing` and `payment.payment_method_data.billing` + // The unified payment_method_billing will be used as billing address and passed to the connector module + // This unification is required in order to provide backwards compatibility + // so that if `payment.billing` is passed it should be sent to the connector module + // Unify the billing details with `payment_method_data.billing` + payment_method_billing + .as_ref() + .map(|payment_method_billing| { + payment_method_billing + .clone() + .unify_address(billing.as_ref()) + }) + .or(billing.clone()) + } else { + payment_method_billing.clone() + }; Self { shipping, diff --git a/crates/router/src/core/payments/operations/payment_approve.rs b/crates/router/src/core/payments/operations/payment_approve.rs index 8dffd8eaec3..bc60d8a3581 100644 --- a/crates/router/src/core/payments/operations/payment_approve.rs +++ b/crates/router/src/core/payments/operations/payment_approve.rs @@ -150,6 +150,7 @@ impl shipping_address.as_ref().map(From::from), billing_address.as_ref().map(From::from), payment_method_billing.as_ref().map(From::from), + business_profile.use_billing_as_payment_method_billing, ), confirm: None, payment_method_data: None, diff --git a/crates/router/src/core/payments/operations/payment_cancel.rs b/crates/router/src/core/payments/operations/payment_cancel.rs index 32035391fb7..2d05fa21520 100644 --- a/crates/router/src/core/payments/operations/payment_cancel.rs +++ b/crates/router/src/core/payments/operations/payment_cancel.rs @@ -165,6 +165,7 @@ impl shipping_address.as_ref().map(From::from), billing_address.as_ref().map(From::from), payment_method_billing.as_ref().map(From::from), + business_profile.use_billing_as_payment_method_billing, ), confirm: None, payment_method_data: None, diff --git a/crates/router/src/core/payments/operations/payment_capture.rs b/crates/router/src/core/payments/operations/payment_capture.rs index d4c3f0acad7..71549a17c2a 100644 --- a/crates/router/src/core/payments/operations/payment_capture.rs +++ b/crates/router/src/core/payments/operations/payment_capture.rs @@ -207,6 +207,7 @@ impl shipping_address.as_ref().map(From::from), billing_address.as_ref().map(From::from), payment_method_billing.as_ref().map(From::from), + business_profile.use_billing_as_payment_method_billing, ), confirm: None, payment_method_data: None, diff --git a/crates/router/src/core/payments/operations/payment_complete_authorize.rs b/crates/router/src/core/payments/operations/payment_complete_authorize.rs index d31ec14a723..f1cb0fa92ef 100644 --- a/crates/router/src/core/payments/operations/payment_complete_authorize.rs +++ b/crates/router/src/core/payments/operations/payment_complete_authorize.rs @@ -279,6 +279,7 @@ impl shipping_address.as_ref().map(From::from), billing_address.as_ref().map(From::from), payment_method_billing.as_ref().map(From::from), + business_profile.use_billing_as_payment_method_billing, ), confirm: request.confirm, payment_method_data: request diff --git a/crates/router/src/core/payments/operations/payment_confirm.rs b/crates/router/src/core/payments/operations/payment_confirm.rs index 3d4f2a3cad8..ee4f499d426 100644 --- a/crates/router/src/core/payments/operations/payment_confirm.rs +++ b/crates/router/src/core/payments/operations/payment_confirm.rs @@ -609,6 +609,7 @@ impl shipping_address.as_ref().map(From::from), billing_address.as_ref().map(From::from), payment_method_billing.as_ref().map(From::from), + business_profile.use_billing_as_payment_method_billing, ), token_data, confirm: request.confirm, diff --git a/crates/router/src/core/payments/operations/payment_create.rs b/crates/router/src/core/payments/operations/payment_create.rs index 0d241202208..e90617f5382 100644 --- a/crates/router/src/core/payments/operations/payment_create.rs +++ b/crates/router/src/core/payments/operations/payment_create.rs @@ -424,6 +424,7 @@ impl shipping_address.as_ref().map(From::from), billing_address.as_ref().map(From::from), payment_method_billing_address.as_ref().map(From::from), + business_profile.use_billing_as_payment_method_billing, ), token_data: None, confirm: request.confirm, diff --git a/crates/router/src/core/payments/operations/payment_reject.rs b/crates/router/src/core/payments/operations/payment_reject.rs index 8845fe836e2..8ba627dc560 100644 --- a/crates/router/src/core/payments/operations/payment_reject.rs +++ b/crates/router/src/core/payments/operations/payment_reject.rs @@ -148,6 +148,7 @@ impl shipping_address.as_ref().map(From::from), billing_address.as_ref().map(From::from), payment_method_billing.as_ref().map(From::from), + business_profile.use_billing_as_payment_method_billing, ), token_data: None, confirm: None, diff --git a/crates/router/src/core/payments/operations/payment_session.rs b/crates/router/src/core/payments/operations/payment_session.rs index 808eb20eb12..485e25c2c9c 100644 --- a/crates/router/src/core/payments/operations/payment_session.rs +++ b/crates/router/src/core/payments/operations/payment_session.rs @@ -174,6 +174,7 @@ impl shipping_address.as_ref().map(From::from), billing_address.as_ref().map(From::from), payment_method_billing.as_ref().map(From::from), + business_profile.use_billing_as_payment_method_billing, ), confirm: None, payment_method_data: None, diff --git a/crates/router/src/core/payments/operations/payment_start.rs b/crates/router/src/core/payments/operations/payment_start.rs index c7042908b63..01ab8586354 100644 --- a/crates/router/src/core/payments/operations/payment_start.rs +++ b/crates/router/src/core/payments/operations/payment_start.rs @@ -159,6 +159,7 @@ impl shipping_address.as_ref().map(From::from), billing_address.as_ref().map(From::from), payment_method_billing.as_ref().map(From::from), + business_profile.use_billing_as_payment_method_billing, ), token_data, confirm: Some(payment_attempt.confirm), diff --git a/crates/router/src/core/payments/operations/payment_status.rs b/crates/router/src/core/payments/operations/payment_status.rs index 06ad57346f5..85e7fa2bf51 100644 --- a/crates/router/src/core/payments/operations/payment_status.rs +++ b/crates/router/src/core/payments/operations/payment_status.rs @@ -459,6 +459,7 @@ async fn get_tracker_for_sync< shipping_address.as_ref().map(From::from), billing_address.as_ref().map(From::from), payment_method_billing.as_ref().map(From::from), + business_profile.use_billing_as_payment_method_billing, ), token_data: None, confirm: Some(request.force_sync), diff --git a/crates/router/src/core/payments/operations/payment_update.rs b/crates/router/src/core/payments/operations/payment_update.rs index de83b935ff0..c7479672c75 100644 --- a/crates/router/src/core/payments/operations/payment_update.rs +++ b/crates/router/src/core/payments/operations/payment_update.rs @@ -425,6 +425,7 @@ impl shipping_address.as_ref().map(From::from), billing_address.as_ref().map(From::from), payment_method_billing.as_ref().map(From::from), + business_profile.use_billing_as_payment_method_billing, ), confirm: request.confirm, payment_method_data: request diff --git a/crates/router/src/core/payments/operations/payments_incremental_authorization.rs b/crates/router/src/core/payments/operations/payments_incremental_authorization.rs index ddba82fc04d..c28e6b35d50 100644 --- a/crates/router/src/core/payments/operations/payments_incremental_authorization.rs +++ b/crates/router/src/core/payments/operations/payments_incremental_authorization.rs @@ -125,7 +125,7 @@ impl customer_acceptance: None, token: None, token_data: None, - address: PaymentAddress::new(None, None, None), + address: PaymentAddress::new(None, None, None, None), confirm: None, payment_method_data: None, payment_method_info: None, diff --git a/crates/router/src/core/routing/helpers.rs b/crates/router/src/core/routing/helpers.rs index 3702498767a..3449838164b 100644 --- a/crates/router/src/core/routing/helpers.rs +++ b/crates/router/src/core/routing/helpers.rs @@ -264,7 +264,9 @@ pub async fn update_business_profile_active_algorithm_ref( session_expiry: None, authentication_connector_details: None, extended_card_info_config: None, + use_billing_as_payment_method_billing: None, }; + db.update_business_profile_by_profile_id(current_business_profile, business_profile_update) .await .change_context(errors::ApiErrorResponse::InternalServerError) diff --git a/crates/router/src/core/utils.rs b/crates/router/src/core/utils.rs index ac546ef5e49..ae6dc0e86f9 100644 --- a/crates/router/src/core/utils.rs +++ b/crates/router/src/core/utils.rs @@ -117,7 +117,7 @@ pub async fn construct_payout_router_data<'a, F>( } }); - let address = PaymentAddress::new(None, billing_address, None); + let address = PaymentAddress::new(None, billing_address, None, None); let test_mode: Option = merchant_connector_account.is_test_mode_on(); let payouts = &payout_data.payouts; diff --git a/crates/router/src/types/api/admin.rs b/crates/router/src/types/api/admin.rs index 793d042ea49..262999af949 100644 --- a/crates/router/src/types/api/admin.rs +++ b/crates/router/src/types/api/admin.rs @@ -79,6 +79,7 @@ impl ForeignTryFrom for BusinessProf authentication_connector_details.parse_value("AuthenticationDetails") }) .transpose()?, + use_billing_as_payment_method_billing: item.use_billing_as_payment_method_billing, }) } } @@ -178,6 +179,9 @@ impl ForeignTryFrom<(domain::MerchantAccount, BusinessProfileCreate)> is_connector_agnostic_mit_enabled: None, is_extended_card_info_enabled: None, extended_card_info_config: None, + use_billing_as_payment_method_billing: request + .use_billing_as_payment_method_billing + .or(Some(true)), }) } } diff --git a/crates/router/src/types/api/verify_connector.rs b/crates/router/src/types/api/verify_connector.rs index af7cb9160b2..d35abd233f1 100644 --- a/crates/router/src/types/api/verify_connector.rs +++ b/crates/router/src/types/api/verify_connector.rs @@ -89,7 +89,7 @@ impl VerifyConnectorData { recurring_mandate_payment_data: None, payment_method_status: None, connector_request_reference_id: attempt_id, - address: types::PaymentAddress::new(None, None, None), + address: types::PaymentAddress::new(None, None, None, None), payment_id: common_utils::generate_id_with_default_len( consts::VERIFY_CONNECTOR_ID_PREFIX, ), diff --git a/crates/router/tests/connectors/aci.rs b/crates/router/tests/connectors/aci.rs index 2e5e8748f48..3548bc5e5bd 100644 --- a/crates/router/tests/connectors/aci.rs +++ b/crates/router/tests/connectors/aci.rs @@ -92,6 +92,7 @@ fn construct_payment_router_data() -> types::PaymentsAuthorizeRouterData { }), email: None, }), + None, ), connector_meta_data: None, amount_captured: None, diff --git a/crates/router/tests/connectors/adyen.rs b/crates/router/tests/connectors/adyen.rs index d19fff29003..4d4318064e2 100644 --- a/crates/router/tests/connectors/adyen.rs +++ b/crates/router/tests/connectors/adyen.rs @@ -72,6 +72,7 @@ impl AdyenTest { email: None, }), None, + None, )), ..Default::default() }) @@ -100,6 +101,7 @@ impl AdyenTest { email: None, }), None, + None, )), payout_method_data: match payout_type { enums::PayoutType::Card => Some(types::api::PayoutMethodData::Card( diff --git a/crates/router/tests/connectors/airwallex.rs b/crates/router/tests/connectors/airwallex.rs index 2ef61099ca7..d976aca7ce7 100644 --- a/crates/router/tests/connectors/airwallex.rs +++ b/crates/router/tests/connectors/airwallex.rs @@ -64,6 +64,7 @@ fn get_default_payment_info() -> Option { phone: None, email: None, }), + None, )), ..Default::default() }) diff --git a/crates/router/tests/connectors/bitpay.rs b/crates/router/tests/connectors/bitpay.rs index 85b388f2fdb..2dd90e704b9 100644 --- a/crates/router/tests/connectors/bitpay.rs +++ b/crates/router/tests/connectors/bitpay.rs @@ -58,6 +58,7 @@ fn get_default_payment_info() -> Option { email: None, }), None, + None, )), ..Default::default() }) diff --git a/crates/router/tests/connectors/bluesnap.rs b/crates/router/tests/connectors/bluesnap.rs index 3a654eda839..88b6fbec246 100644 --- a/crates/router/tests/connectors/bluesnap.rs +++ b/crates/router/tests/connectors/bluesnap.rs @@ -58,6 +58,7 @@ fn get_payment_info() -> Option { email: None, }), None, + None, )), ..Default::default() }) diff --git a/crates/router/tests/connectors/cashtocode.rs b/crates/router/tests/connectors/cashtocode.rs index 025d9cdac64..60620d1b4c4 100644 --- a/crates/router/tests/connectors/cashtocode.rs +++ b/crates/router/tests/connectors/cashtocode.rs @@ -88,6 +88,7 @@ impl CashtocodeTest { email: None, }), None, + None, )), return_url: Some("https://google.com".to_owned()), ..Default::default() diff --git a/crates/router/tests/connectors/coinbase.rs b/crates/router/tests/connectors/coinbase.rs index 0bcb3c705ab..5dd42a2c97c 100644 --- a/crates/router/tests/connectors/coinbase.rs +++ b/crates/router/tests/connectors/coinbase.rs @@ -59,6 +59,7 @@ fn get_default_payment_info() -> Option { email: None, }), None, + None, )), connector_meta_data: Some(json!({"pricing_type": "fixed_price"})), ..Default::default() diff --git a/crates/router/tests/connectors/cryptopay.rs b/crates/router/tests/connectors/cryptopay.rs index 626c05fe114..c26bad8c567 100644 --- a/crates/router/tests/connectors/cryptopay.rs +++ b/crates/router/tests/connectors/cryptopay.rs @@ -58,6 +58,7 @@ fn get_default_payment_info() -> Option { email: None, }), None, + None, )), return_url: Some(String::from("https://google.com")), ..Default::default() diff --git a/crates/router/tests/connectors/cybersource.rs b/crates/router/tests/connectors/cybersource.rs index 700e9a2d946..bb74e805c3b 100644 --- a/crates/router/tests/connectors/cybersource.rs +++ b/crates/router/tests/connectors/cybersource.rs @@ -55,6 +55,7 @@ fn get_default_payment_info() -> Option { email: None, }), None, + None, )), ..Default::default() }) diff --git a/crates/router/tests/connectors/dlocal.rs b/crates/router/tests/connectors/dlocal.rs index b715e20fec4..2f04904d7b8 100644 --- a/crates/router/tests/connectors/dlocal.rs +++ b/crates/router/tests/connectors/dlocal.rs @@ -438,6 +438,7 @@ pub fn get_payment_info() -> PaymentInfo { email: None, }), None, + None, )), auth_type: None, access_token: None, diff --git a/crates/router/tests/connectors/forte.rs b/crates/router/tests/connectors/forte.rs index 1c99c2ff7b5..d690524bd12 100644 --- a/crates/router/tests/connectors/forte.rs +++ b/crates/router/tests/connectors/forte.rs @@ -71,6 +71,7 @@ fn get_default_payment_info() -> Option { email: None, }), None, + None, )), ..Default::default() }) diff --git a/crates/router/tests/connectors/globalpay.rs b/crates/router/tests/connectors/globalpay.rs index 8ca4d6f1ae3..9472b600a51 100644 --- a/crates/router/tests/connectors/globalpay.rs +++ b/crates/router/tests/connectors/globalpay.rs @@ -68,6 +68,7 @@ impl Globalpay { ..Default::default() }), None, + None, )), access_token: get_access_token(), connector_meta_data: CONNECTOR.get_connector_meta(), diff --git a/crates/router/tests/connectors/iatapay.rs b/crates/router/tests/connectors/iatapay.rs index dfb8e097be3..4d3904a0c21 100644 --- a/crates/router/tests/connectors/iatapay.rs +++ b/crates/router/tests/connectors/iatapay.rs @@ -75,6 +75,7 @@ fn get_default_payment_info() -> Option { email: None, }), None, + None, )), access_token: get_access_token(), return_url: Some(String::from("https://hyperswitch.io")), diff --git a/crates/router/tests/connectors/multisafepay.rs b/crates/router/tests/connectors/multisafepay.rs index 606b60b2490..ba852ea2ebf 100644 --- a/crates/router/tests/connectors/multisafepay.rs +++ b/crates/router/tests/connectors/multisafepay.rs @@ -56,6 +56,7 @@ fn get_default_payment_info() -> Option { email: None, }), None, + None, )); Some(PaymentInfo { address, diff --git a/crates/router/tests/connectors/opennode.rs b/crates/router/tests/connectors/opennode.rs index c91126953de..1e2cea554e9 100644 --- a/crates/router/tests/connectors/opennode.rs +++ b/crates/router/tests/connectors/opennode.rs @@ -58,6 +58,7 @@ fn get_default_payment_info() -> Option { email: None, }), None, + None, )), return_url: Some(String::from("https://google.com")), ..Default::default() diff --git a/crates/router/tests/connectors/payeezy.rs b/crates/router/tests/connectors/payeezy.rs index bfc0cb13650..334b26f0009 100644 --- a/crates/router/tests/connectors/payeezy.rs +++ b/crates/router/tests/connectors/payeezy.rs @@ -66,6 +66,7 @@ impl PayeezyTest { email: None, }), None, + None, )), ..Default::default() }) diff --git a/crates/router/tests/connectors/payme.rs b/crates/router/tests/connectors/payme.rs index 234f3a0eeb8..6554ddbefbf 100644 --- a/crates/router/tests/connectors/payme.rs +++ b/crates/router/tests/connectors/payme.rs @@ -60,6 +60,7 @@ fn get_default_payment_info() -> Option { email: None, }), None, + None, )), auth_type: None, access_token: None, diff --git a/crates/router/tests/connectors/trustpay.rs b/crates/router/tests/connectors/trustpay.rs index fba612863b4..b8aeefce49f 100644 --- a/crates/router/tests/connectors/trustpay.rs +++ b/crates/router/tests/connectors/trustpay.rs @@ -84,6 +84,7 @@ fn get_default_payment_info() -> Option { email: None, }), None, + None, )), ..Default::default() }) diff --git a/crates/router/tests/connectors/utils.rs b/crates/router/tests/connectors/utils.rs index 3196c4c763b..c19fdb26ad6 100644 --- a/crates/router/tests/connectors/utils.rs +++ b/crates/router/tests/connectors/utils.rs @@ -69,6 +69,7 @@ impl PaymentInfo { phone: None, email: None, }), + None, )), ..Default::default() } diff --git a/crates/router/tests/connectors/wise.rs b/crates/router/tests/connectors/wise.rs index fd5bbc109d9..36a16635e0d 100644 --- a/crates/router/tests/connectors/wise.rs +++ b/crates/router/tests/connectors/wise.rs @@ -68,6 +68,7 @@ impl WiseTest { email: None, }), None, + None, )), payout_method_data: Some(api::PayoutMethodData::Bank(api::payouts::BankPayout::Bacs( api::BacsBankTransfer { diff --git a/crates/router/tests/connectors/worldline.rs b/crates/router/tests/connectors/worldline.rs index 4f8119bfc52..36d0bba6c46 100644 --- a/crates/router/tests/connectors/worldline.rs +++ b/crates/router/tests/connectors/worldline.rs @@ -56,6 +56,7 @@ impl WorldlineTest { email: None, }), None, + None, )), ..Default::default() }) @@ -179,7 +180,7 @@ async fn should_throw_missing_required_field_for_country() { .make_payment( authorize_data, Some(PaymentInfo { - address: Some(PaymentAddress::new(None, None, None)), + address: Some(PaymentAddress::new(None, None, None, None)), ..Default::default() }), ) diff --git a/migrations/2024-05-06-065226_add_billing_config_to_business_profile/down.sql b/migrations/2024-05-06-065226_add_billing_config_to_business_profile/down.sql new file mode 100644 index 00000000000..9fc70d18d45 --- /dev/null +++ b/migrations/2024-05-06-065226_add_billing_config_to_business_profile/down.sql @@ -0,0 +1,2 @@ +-- This file should undo anything in `up.sql` +ALTER TABLE business_profile DROP COLUMN IF EXISTS use_billing_as_payment_method_billing; diff --git a/migrations/2024-05-06-065226_add_billing_config_to_business_profile/up.sql b/migrations/2024-05-06-065226_add_billing_config_to_business_profile/up.sql new file mode 100644 index 00000000000..de841e4f5e0 --- /dev/null +++ b/migrations/2024-05-06-065226_add_billing_config_to_business_profile/up.sql @@ -0,0 +1,3 @@ +-- Your SQL goes here +ALTER TABLE business_profile +ADD COLUMN IF NOT EXISTS use_billing_as_payment_method_billing BOOLEAN DEFAULT TRUE; diff --git a/openapi/openapi_spec.json b/openapi/openapi_spec.json index 201f6582dda..c330816cea8 100644 --- a/openapi/openapi_spec.json +++ b/openapi/openapi_spec.json @@ -6668,6 +6668,11 @@ } ], "nullable": true + }, + "use_billing_as_payment_method_billing": { + "type": "boolean", + "description": "Whether to use the billing details passed when creating the intent as payment method billing", + "nullable": true } }, "additionalProperties": false @@ -6787,6 +6792,10 @@ } ], "nullable": true + }, + "use_billing_as_payment_method_billing": { + "type": "boolean", + "nullable": true } } }, From 625b53182e20b50fde5def338e122a43457da0f2 Mon Sep 17 00:00:00 2001 From: Swangi Kumari <85639103+swangi-kumari@users.noreply.github.com> Date: Wed, 8 May 2024 11:45:47 +0530 Subject: [PATCH 25/34] refactor(bank-debit): remove billingdetails from bankdebit pmd (#4371) Co-authored-by: hyperswitch-bot[bot] <148525504+hyperswitch-bot[bot]@users.noreply.github.com> --- crates/api_models/src/payments.rs | 42 +++---- .../src/connector/adyen/transformers.rs | 42 +++---- .../src/connector/gocardless/transformers.rs | 103 +++--------------- .../src/connector/mollie/transformers.rs | 24 ++-- .../router/src/connector/stax/transformers.rs | 3 +- .../src/connector/stripe/transformers.rs | 62 ++--------- crates/router/src/connector/utils.rs | 27 +++-- crates/router/src/core/pm_auth.rs | 10 +- crates/router/src/types/domain/payments.rs | 61 +---------- openapi/openapi_spec.json | 42 ++++--- .../Payments - Create/request.json | 13 +++ .../stripe.postman_collection.json | 2 +- 12 files changed, 142 insertions(+), 289 deletions(-) diff --git a/crates/api_models/src/payments.rs b/crates/api_models/src/payments.rs index 69b4700acbc..2dc578ef75e 100644 --- a/crates/api_models/src/payments.rs +++ b/crates/api_models/src/payments.rs @@ -1148,7 +1148,7 @@ pub enum BankDebitData { /// Payment Method data for Ach bank debit AchBankDebit { /// Billing details for bank debit - billing_details: BankDebitBilling, + billing_details: Option, /// Account number for ach bank debit payment #[schema(value_type = String, example = "000123456789")] account_number: Secret, @@ -1173,7 +1173,7 @@ pub enum BankDebitData { }, SepaBankDebit { /// Billing details for bank debit - billing_details: BankDebitBilling, + billing_details: Option, /// International bank account number (iban) for SEPA #[schema(value_type = String, example = "DE89370400440532013000")] iban: Secret, @@ -1183,7 +1183,7 @@ pub enum BankDebitData { }, BecsBankDebit { /// Billing details for bank debit - billing_details: BankDebitBilling, + billing_details: Option, /// Account number for Becs payment method #[schema(value_type = String, example = "000123456")] account_number: Secret, @@ -1196,7 +1196,7 @@ pub enum BankDebitData { }, BacsBankDebit { /// Billing details for bank debit - billing_details: BankDebitBilling, + billing_details: Option, /// Account number for Bacs payment method #[schema(value_type = String, example = "00012345")] account_number: Secret, @@ -1212,11 +1212,12 @@ pub enum BankDebitData { impl GetAddressFromPaymentMethodData for BankDebitData { fn get_billing_address(&self) -> Option
{ fn get_billing_address_inner( - bank_debit_billing: &BankDebitBilling, + bank_debit_billing: Option<&BankDebitBilling>, bank_account_holder_name: Option<&Secret>, ) -> Option
{ // We will always have address here - let mut address = bank_debit_billing.get_billing_address()?; + let mut address = bank_debit_billing + .and_then(GetAddressFromPaymentMethodData::get_billing_address)?; // Prefer `account_holder_name` over `name` address.address.as_mut().map(|address| { @@ -1248,7 +1249,10 @@ impl GetAddressFromPaymentMethodData for BankDebitData { billing_details, bank_account_holder_name, .. - } => get_billing_address_inner(billing_details, bank_account_holder_name.as_ref()), + } => get_billing_address_inner( + billing_details.as_ref(), + bank_account_holder_name.as_ref(), + ), } } } @@ -1447,7 +1451,7 @@ impl GetAddressFromPaymentMethodData for PaymentMethodData { Self::Wallet(wallet_data) => wallet_data.get_billing_address(), Self::PayLater(pay_later) => pay_later.get_billing_address(), Self::BankRedirect(_) => None, - Self::BankDebit(_) => None, + Self::BankDebit(bank_debit_data) => bank_debit_data.get_billing_address(), Self::BankTransfer(_) => None, Self::Voucher(voucher_data) => voucher_data.get_billing_address(), Self::Crypto(_) @@ -2241,11 +2245,11 @@ impl GetAddressFromPaymentMethodData for BankTransferData { #[derive(serde::Deserialize, serde::Serialize, Debug, Clone, ToSchema, Eq, PartialEq)] pub struct BankDebitBilling { /// The billing name for bank debits - #[schema(value_type = String, example = "John Doe")] - pub name: Secret, + #[schema(value_type = Option, example = "John Doe")] + pub name: Option>, /// The billing email for bank debits - #[schema(value_type = String, example = "example@example.com")] - pub email: Email, + #[schema(value_type = Option, example = "example@example.com")] + pub email: Option, /// The billing address for bank debits pub address: Option, } @@ -2253,19 +2257,19 @@ pub struct BankDebitBilling { impl GetAddressFromPaymentMethodData for BankDebitBilling { fn get_billing_address(&self) -> Option
{ let address = if let Some(mut address) = self.address.clone() { - address.first_name = Some(self.name.clone()); + address.first_name = self.name.clone().or(address.first_name); Address { address: Some(address), - email: Some(self.email.clone()), + email: self.email.clone(), phone: None, } } else { Address { address: Some(AddressDetails { - first_name: Some(self.name.clone()), + first_name: self.name.clone(), ..AddressDetails::default() }), - email: Some(self.email.clone()), + email: self.email.clone(), phone: None, } }; @@ -4898,14 +4902,14 @@ mod billing_from_payment_method_data { let test_first_name = Secret::new(String::from("Chaser")); let bank_redirect_billing = BankDebitBilling { - name: test_first_name.clone(), + name: Some(test_first_name.clone()), address: None, - email: test_email.clone(), + email: Some(test_email.clone()), }; let ach_bank_debit_payment_method_data = PaymentMethodData::BankDebit(BankDebitData::AchBankDebit { - billing_details: bank_redirect_billing, + billing_details: Some(bank_redirect_billing), account_number: Secret::new("1234".to_string()), routing_number: Secret::new("1235".to_string()), card_holder_name: None, diff --git a/crates/router/src/connector/adyen/transformers.rs b/crates/router/src/connector/adyen/transformers.rs index 77abaacf40b..c911af50e5c 100644 --- a/crates/router/src/connector/adyen/transformers.rs +++ b/crates/router/src/connector/adyen/transformers.rs @@ -1820,56 +1820,42 @@ fn build_shopper_reference(customer_id: &Option, merchant_id: String) -> .map(|c_id| format!("{}_{}", merchant_id, c_id)) } -impl<'a> TryFrom<&domain::BankDebitData> for AdyenPaymentMethod<'a> { +impl<'a> TryFrom<(&domain::BankDebitData, &types::PaymentsAuthorizeRouterData)> + for AdyenPaymentMethod<'a> +{ type Error = Error; - fn try_from(bank_debit_data: &domain::BankDebitData) -> Result { + fn try_from( + (bank_debit_data, item): (&domain::BankDebitData, &types::PaymentsAuthorizeRouterData), + ) -> Result { match bank_debit_data { domain::BankDebitData::AchBankDebit { account_number, routing_number, - card_holder_name, .. } => Ok(AdyenPaymentMethod::AchDirectDebit(Box::new( AchDirectDebitData { payment_type: PaymentType::AchDirectDebit, bank_account_number: account_number.clone(), bank_location_id: routing_number.clone(), - owner_name: card_holder_name.clone().ok_or( - errors::ConnectorError::MissingRequiredField { - field_name: "card_holder_name", - }, - )?, + owner_name: item.get_billing_full_name()?, }, ))), - domain::BankDebitData::SepaBankDebit { - iban, - bank_account_holder_name, - .. - } => Ok(AdyenPaymentMethod::SepaDirectDebit(Box::new( - SepaDirectDebitData { - owner_name: bank_account_holder_name.clone().ok_or( - errors::ConnectorError::MissingRequiredField { - field_name: "bank_account_holder_name", - }, - )?, + domain::BankDebitData::SepaBankDebit { iban, .. } => Ok( + AdyenPaymentMethod::SepaDirectDebit(Box::new(SepaDirectDebitData { + owner_name: item.get_billing_full_name()?, iban_number: iban.clone(), - }, - ))), + })), + ), domain::BankDebitData::BacsBankDebit { account_number, sort_code, - bank_account_holder_name, .. } => Ok(AdyenPaymentMethod::BacsDirectDebit(Box::new( BacsDirectDebitData { payment_type: PaymentType::BacsDirectDebit, bank_account_number: account_number.clone(), bank_location_id: sort_code.clone(), - holder_name: bank_account_holder_name.clone().ok_or( - errors::ConnectorError::MissingRequiredField { - field_name: "bank_account_holder_name", - }, - )?, + holder_name: item.get_billing_full_name()?, }, ))), domain::BankDebitData::BecsBankDebit { .. } => { @@ -2713,7 +2699,7 @@ impl<'a> let browser_info = get_browser_info(item.router_data)?; let additional_data = get_additional_data(item.router_data); let return_url = item.router_data.request.get_return_url()?; - let payment_method = AdyenPaymentMethod::try_from(bank_debit_data)?; + let payment_method = AdyenPaymentMethod::try_from((bank_debit_data, item.router_data))?; let country_code = get_country_code(item.router_data.get_optional_billing()); let request = AdyenPaymentRequest { amount, diff --git a/crates/router/src/connector/gocardless/transformers.rs b/crates/router/src/connector/gocardless/transformers.rs index e05f1b469ac..97f1146b506 100644 --- a/crates/router/src/connector/gocardless/transformers.rs +++ b/crates/router/src/connector/gocardless/transformers.rs @@ -8,9 +8,8 @@ use serde::{Deserialize, Serialize}; use crate::{ connector::utils::{ - self, AddressDetailsData, BankDirectDebitBillingData, BrowserInformationData, - ConnectorCustomerData, PaymentsAuthorizeRequestData, PaymentsSetupMandateRequestData, - RouterData, + self, AddressDetailsData, BrowserInformationData, ConnectorCustomerData, + PaymentsAuthorizeRequestData, PaymentsSetupMandateRequestData, RouterData, }, core::errors, types::{ @@ -67,64 +66,19 @@ impl TryFrom<&types::ConnectorCustomerRouterData> for GocardlessCustomerRequest type Error = error_stack::Report; fn try_from(item: &types::ConnectorCustomerRouterData) -> Result { let email = item.request.get_email()?; - let billing_details = match &item.request.payment_method_data { - domain::PaymentMethodData::BankDebit(bank_debit_data) => { - match bank_debit_data.clone() { - domain::BankDebitData::AchBankDebit { - billing_details, .. - } => Ok(billing_details), - domain::BankDebitData::SepaBankDebit { - billing_details, .. - } => Ok(billing_details), - domain::BankDebitData::BecsBankDebit { - billing_details, .. - } => Ok(billing_details), - domain::BankDebitData::BacsBankDebit { .. } => { - Err(errors::ConnectorError::NotImplemented( - utils::get_unimplemented_payment_method_error_message("Gocardless"), - )) - } - } - } - domain::PaymentMethodData::Card(_) - | domain::PaymentMethodData::CardRedirect(_) - | domain::PaymentMethodData::Wallet(_) - | domain::PaymentMethodData::PayLater(_) - | domain::PaymentMethodData::BankRedirect(_) - | domain::PaymentMethodData::BankTransfer(_) - | domain::PaymentMethodData::Crypto(_) - | domain::PaymentMethodData::MandatePayment - | domain::PaymentMethodData::Reward - | domain::PaymentMethodData::Upi(_) - | domain::PaymentMethodData::Voucher(_) - | domain::PaymentMethodData::GiftCard(_) - | domain::PaymentMethodData::CardToken(_) => { - Err(errors::ConnectorError::NotImplemented( - utils::get_unimplemented_payment_method_error_message("Gocardless"), - )) - } - }?; - - let billing_details_name = billing_details.name.expose(); + let billing_details_name = item.get_billing_full_name()?.expose(); - if billing_details_name.is_empty() { - Err(errors::ConnectorError::MissingRequiredField { - field_name: "billing_details.name", - })? - } let (given_name, family_name) = billing_details_name .trim() .rsplit_once(' ') .unwrap_or((&billing_details_name, &billing_details_name)); - let billing_address = billing_details - .address - .ok_or_else(utils::missing_field_err("billing_details.address"))?; + let billing_address = item.get_billing_address()?; let metadata = CustomerMetaData { crm_id: item.customer_id.clone().map(Secret::new), }; - let region = get_region(&billing_address)?; + let region = get_region(billing_address)?; Ok(Self { customers: GocardlessCustomer { email, @@ -141,7 +95,7 @@ impl TryFrom<&types::ConnectorCustomerRouterData> for GocardlessCustomerRequest postal_code: billing_address.zip.to_owned(), // Should be populated based on the billing country swedish_identity_number: None, - city: billing_address.city.map(Secret::new), + city: billing_address.city.clone().map(Secret::new), }, }) } @@ -274,7 +228,7 @@ impl TryFrom<&types::TokenizationRouterData> for CustomerBankAccount { fn try_from(item: &types::TokenizationRouterData) -> Result { match &item.request.payment_method_data { domain::PaymentMethodData::BankDebit(bank_debit_data) => { - Self::try_from(bank_debit_data) + Self::try_from((bank_debit_data, item)) } domain::PaymentMethodData::Card(_) | domain::PaymentMethodData::CardRedirect(_) @@ -298,26 +252,21 @@ impl TryFrom<&types::TokenizationRouterData> for CustomerBankAccount { } } -impl TryFrom<&domain::BankDebitData> for CustomerBankAccount { +impl TryFrom<(&domain::BankDebitData, &types::TokenizationRouterData)> for CustomerBankAccount { type Error = error_stack::Report; - fn try_from(item: &domain::BankDebitData) -> Result { - match item { + fn try_from( + (bank_debit_data, item): (&domain::BankDebitData, &types::TokenizationRouterData), + ) -> Result { + match bank_debit_data { domain::BankDebitData::AchBankDebit { - billing_details, account_number, routing_number, bank_type, - bank_account_holder_name, .. } => { let bank_type = bank_type.ok_or_else(utils::missing_field_err("bank_type"))?; - let country_code = billing_details.get_billing_country()?; - let account_holder_name = - bank_account_holder_name - .clone() - .ok_or_else(utils::missing_field_err( - "payment_method_data.bank_debit.ach_bank_debit.bank_account_holder_name", - ))?; + let country_code = item.get_billing_country()?; + let account_holder_name = item.get_billing_full_name()?; let us_bank_account = USBankAccount { country_code, account_number: account_number.clone(), @@ -328,18 +277,11 @@ impl TryFrom<&domain::BankDebitData> for CustomerBankAccount { Ok(Self::USBankAccount(us_bank_account)) } domain::BankDebitData::BecsBankDebit { - billing_details, account_number, bsb_number, - bank_account_holder_name, } => { - let country_code = billing_details.get_billing_country()?; - let account_holder_name = - bank_account_holder_name - .clone() - .ok_or_else(utils::missing_field_err( - "payment_method_data.bank_debit.becs_bank_debit.bank_account_holder_name", - ))?; + let country_code = item.get_billing_country()?; + let account_holder_name = item.get_billing_full_name()?; let au_bank_account = AUBankAccount { country_code, account_number: account_number.clone(), @@ -348,17 +290,8 @@ impl TryFrom<&domain::BankDebitData> for CustomerBankAccount { }; Ok(Self::AUBankAccount(au_bank_account)) } - domain::BankDebitData::SepaBankDebit { - iban, - bank_account_holder_name, - .. - } => { - let account_holder_name = - bank_account_holder_name - .clone() - .ok_or_else(utils::missing_field_err( - "payment_method_data.bank_debit.sepa_bank_debit.bank_account_holder_name", - ))?; + domain::BankDebitData::SepaBankDebit { iban, .. } => { + let account_holder_name = item.get_billing_full_name()?; let international_bank_account = InternationalBankAccount { iban: iban.clone(), account_holder_name, diff --git a/crates/router/src/connector/mollie/transformers.rs b/crates/router/src/connector/mollie/transformers.rs index b61122fbfa2..e2c3fc66992 100644 --- a/crates/router/src/connector/mollie/transformers.rs +++ b/crates/router/src/connector/mollie/transformers.rs @@ -195,7 +195,7 @@ impl TryFrom<&MollieRouterData<&types::PaymentsAuthorizeRouterData>> for MollieP get_payment_method_for_wallet(item.router_data, wallet_data) } domain::PaymentMethodData::BankDebit(ref directdebit_data) => { - PaymentMethodData::try_from(directdebit_data) + PaymentMethodData::try_from((directdebit_data, item.router_data)) } _ => Err( errors::ConnectorError::NotImplemented("Payment Method".to_string()).into(), @@ -254,18 +254,18 @@ impl TryFrom<&domain::BankRedirectData> for PaymentMethodData { } } -impl TryFrom<&domain::BankDebitData> for PaymentMethodData { +impl TryFrom<(&domain::BankDebitData, &types::PaymentsAuthorizeRouterData)> for PaymentMethodData { type Error = Error; - fn try_from(value: &domain::BankDebitData) -> Result { - match value { - domain::BankDebitData::SepaBankDebit { - bank_account_holder_name, - iban, - .. - } => Ok(Self::DirectDebit(Box::new(DirectDebitMethodData { - consumer_name: bank_account_holder_name.clone(), - consumer_account: iban.clone(), - }))), + fn try_from( + (bank_debit_data, item): (&domain::BankDebitData, &types::PaymentsAuthorizeRouterData), + ) -> Result { + match bank_debit_data { + domain::BankDebitData::SepaBankDebit { iban, .. } => { + Ok(Self::DirectDebit(Box::new(DirectDebitMethodData { + consumer_name: item.get_optional_billing_full_name(), + consumer_account: iban.clone(), + }))) + } _ => Err(errors::ConnectorError::NotImplemented("Payment method".to_string()).into()), } } diff --git a/crates/router/src/connector/stax/transformers.rs b/crates/router/src/connector/stax/transformers.rs index d48a2b1e82b..95e5dbe4f4f 100644 --- a/crates/router/src/connector/stax/transformers.rs +++ b/crates/router/src/connector/stax/transformers.rs @@ -227,7 +227,6 @@ impl TryFrom<&types::TokenizationRouterData> for StaxTokenRequest { Ok(Self::Card(stax_card_data)) } domain::PaymentMethodData::BankDebit(domain::BankDebitData::AchBankDebit { - billing_details, account_number, routing_number, bank_name, @@ -236,7 +235,7 @@ impl TryFrom<&types::TokenizationRouterData> for StaxTokenRequest { .. }) => { let stax_bank_data = StaxBankTokenizeData { - person_name: billing_details.name, + person_name: item.get_billing_full_name()?, bank_account: account_number, bank_routing: routing_number, bank_name: bank_name.ok_or_else(missing_field_err("bank_name"))?, diff --git a/crates/router/src/connector/stripe/transformers.rs b/crates/router/src/connector/stripe/transformers.rs index 6b82c87b5d1..6c8277288cc 100644 --- a/crates/router/src/connector/stripe/transformers.rs +++ b/crates/router/src/connector/stripe/transformers.rs @@ -1057,37 +1057,6 @@ impl From<&domain::BankDebitData> for StripePaymentMethodType { } } -impl From<&domain::BankDebitBilling> for StripeBillingAddress { - fn from(item: &domain::BankDebitBilling) -> Self { - Self { - email: Some(item.email.to_owned()), - country: item - .address - .as_ref() - .and_then(|address| address.country.to_owned()), - name: Some(item.name.to_owned()), - city: item - .address - .as_ref() - .and_then(|address| address.city.to_owned()), - address_line1: item - .address - .as_ref() - .and_then(|address| address.line1.to_owned()), - address_line2: item - .address - .as_ref() - .and_then(|address| address.line2.to_owned()), - zip_code: item - .address - .as_ref() - .and_then(|address| address.zip.to_owned()), - state: None, - phone: None, - } - } -} - impl TryFrom<(&domain::BankRedirectData, Option)> for StripeBillingAddress { type Error = error_stack::Report; @@ -1190,10 +1159,9 @@ impl TryFrom<(&domain::BankRedirectData, Option)> for StripeBillingAddress fn get_bank_debit_data( bank_debit_data: &domain::BankDebitData, -) -> (StripePaymentMethodType, BankDebitData, StripeBillingAddress) { +) -> (StripePaymentMethodType, BankDebitData) { match bank_debit_data { domain::BankDebitData::AchBankDebit { - billing_details, account_number, routing_number, .. @@ -1203,24 +1171,15 @@ fn get_bank_debit_data( account_number: account_number.to_owned(), routing_number: routing_number.to_owned(), }; - - let billing_data = StripeBillingAddress::from(billing_details); - (StripePaymentMethodType::Ach, ach_data, billing_data) + (StripePaymentMethodType::Ach, ach_data) } - domain::BankDebitData::SepaBankDebit { - billing_details, - iban, - .. - } => { + domain::BankDebitData::SepaBankDebit { iban, .. } => { let sepa_data = BankDebitData::Sepa { iban: iban.to_owned(), }; - - let billing_data = StripeBillingAddress::from(billing_details); - (StripePaymentMethodType::Sepa, sepa_data, billing_data) + (StripePaymentMethodType::Sepa, sepa_data) } domain::BankDebitData::BecsBankDebit { - billing_details, account_number, bsb_number, .. @@ -1229,12 +1188,9 @@ fn get_bank_debit_data( account_number: account_number.to_owned(), bsb_number: bsb_number.to_owned(), }; - - let billing_data = StripeBillingAddress::from(billing_details); - (StripePaymentMethodType::Becs, becs_data, billing_data) + (StripePaymentMethodType::Becs, becs_data) } domain::BankDebitData::BacsBankDebit { - billing_details, account_number, sort_code, .. @@ -1243,9 +1199,7 @@ fn get_bank_debit_data( account_number: account_number.to_owned(), sort_code: Secret::new(sort_code.clone().expose().replace('-', "")), }; - - let billing_data = StripeBillingAddress::from(billing_details); - (StripePaymentMethodType::Bacs, bacs_data, billing_data) + (StripePaymentMethodType::Bacs, bacs_data) } } } @@ -1308,7 +1262,7 @@ fn create_stripe_payment_method( )) } domain::PaymentMethodData::BankDebit(bank_debit_data) => { - let (pm_type, bank_debit_data, billing_address) = get_bank_debit_data(bank_debit_data); + let (pm_type, bank_debit_data) = get_bank_debit_data(bank_debit_data); let pm_data = StripePaymentMethodData::BankDebit(StripeBankDebitData { bank_specific_data: bank_debit_data, @@ -3680,7 +3634,7 @@ impl Ok(Self::try_from((wallet_data, None))?) } domain::PaymentMethodData::BankDebit(bank_debit_data) => { - let (_pm_type, bank_data, _) = get_bank_debit_data(&bank_debit_data); + let (_pm_type, bank_data) = get_bank_debit_data(&bank_debit_data); Ok(Self::BankDebit(StripeBankDebitData { bank_specific_data: bank_data, diff --git a/crates/router/src/connector/utils.rs b/crates/router/src/connector/utils.rs index 5a5b1242611..872d287e874 100644 --- a/crates/router/src/connector/utils.rs +++ b/crates/router/src/connector/utils.rs @@ -75,6 +75,7 @@ pub trait RouterData { fn get_connector_meta(&self) -> Result; fn get_session_token(&self) -> Result; fn get_billing_first_name(&self) -> Result, Error>; + fn get_billing_full_name(&self) -> Result, Error>; fn get_billing_email(&self) -> Result; fn get_billing_phone_number(&self) -> Result, Error>; fn to_connector_meta(&self) -> Result @@ -171,7 +172,9 @@ impl RouterData for types::RouterData Result<&api::PhoneDetails, Error> { @@ -233,6 +236,15 @@ impl RouterData for types::RouterData Result, Error> { + self.get_optional_billing() + .and_then(|billing_details| billing_details.address.as_ref()) + .and_then(|billing_address| billing_address.get_optional_full_name()) + .ok_or_else(missing_field_err( + "payment_method_data.billing.address.first_name", + )) + } + fn get_billing_email(&self) -> Result { self.address .get_payment_method_billing() @@ -1502,19 +1514,6 @@ impl BankRedirectBillingData for domain::BankRedirectBilling { } } -pub trait BankDirectDebitBillingData { - fn get_billing_country(&self) -> Result; -} - -impl BankDirectDebitBillingData for domain::BankDebitBilling { - fn get_billing_country(&self) -> Result { - self.address - .as_ref() - .and_then(|address| address.country) - .ok_or_else(missing_field_err("billing_details.country")) - } -} - pub trait MandateData { fn get_end_date(&self, format: date_time::DateFormat) -> Result; fn get_metadata(&self) -> Result; diff --git a/crates/router/src/core/pm_auth.rs b/crates/router/src/core/pm_auth.rs index 88138d7909f..1de52e940e2 100644 --- a/crates/router/src/core/pm_auth.rs +++ b/crates/router/src/core/pm_auth.rs @@ -751,15 +751,15 @@ pub async fn retrieve_payment_method_from_auth_service( .get_required_value("email")?; let billing_details = BankDebitBilling { - name, - email, + name: Some(name), + email: Some(email), address: address_details, }; let payment_method_data = match &bank_account.account_details { pm_auth_types::PaymentMethodTypeDetails::Ach(ach) => { PaymentMethodData::BankDebit(BankDebitData::AchBankDebit { - billing_details, + billing_details: Some(billing_details), account_number: ach.account_number.clone(), routing_number: ach.routing_number.clone(), card_holder_name: None, @@ -771,7 +771,7 @@ pub async fn retrieve_payment_method_from_auth_service( } pm_auth_types::PaymentMethodTypeDetails::Bacs(bacs) => { PaymentMethodData::BankDebit(BankDebitData::BacsBankDebit { - billing_details, + billing_details: Some(billing_details), account_number: bacs.account_number.clone(), sort_code: bacs.sort_code.clone(), bank_account_holder_name: None, @@ -779,7 +779,7 @@ pub async fn retrieve_payment_method_from_auth_service( } pm_auth_types::PaymentMethodTypeDetails::Sepa(sepa) => { PaymentMethodData::BankDebit(BankDebitData::SepaBankDebit { - billing_details, + billing_details: Some(billing_details), iban: sepa.iban.clone(), bank_account_holder_name: None, }) diff --git a/crates/router/src/types/domain/payments.rs b/crates/router/src/types/domain/payments.rs index 58f2fa98eef..195681ff91a 100644 --- a/crates/router/src/types/domain/payments.rs +++ b/crates/router/src/types/domain/payments.rs @@ -382,41 +382,25 @@ pub struct CardToken { #[serde(rename_all = "snake_case")] pub enum BankDebitData { AchBankDebit { - billing_details: BankDebitBilling, account_number: Secret, routing_number: Secret, - card_holder_name: Option>, - bank_account_holder_name: Option>, bank_name: Option, bank_type: Option, bank_holder_type: Option, }, SepaBankDebit { - billing_details: BankDebitBilling, iban: Secret, - bank_account_holder_name: Option>, }, BecsBankDebit { - billing_details: BankDebitBilling, account_number: Secret, bsb_number: Secret, - bank_account_holder_name: Option>, }, BacsBankDebit { - billing_details: BankDebitBilling, account_number: Secret, sort_code: Secret, - bank_account_holder_name: Option>, }, } -#[derive(serde::Deserialize, serde::Serialize, Debug, Clone, Eq, PartialEq)] -pub struct BankDebitBilling { - pub name: Secret, - pub email: Email, - pub address: Option, -} - #[derive(Eq, PartialEq, Clone, Debug, serde::Deserialize, serde::Serialize)] #[serde(rename_all = "snake_case")] pub enum BankTransferData { @@ -899,70 +883,37 @@ impl From for BankDebitData { fn from(value: api_models::payments::BankDebitData) -> Self { match value { api_models::payments::BankDebitData::AchBankDebit { - billing_details, account_number, routing_number, - card_holder_name, - bank_account_holder_name, bank_name, bank_type, bank_holder_type, + .. } => Self::AchBankDebit { - billing_details: BankDebitBilling { - name: billing_details.name, - email: billing_details.email, - address: billing_details.address, - }, account_number, routing_number, - card_holder_name, - bank_account_holder_name, bank_name, bank_type, bank_holder_type, }, - api_models::payments::BankDebitData::SepaBankDebit { - billing_details, - iban, - bank_account_holder_name, - } => Self::SepaBankDebit { - billing_details: BankDebitBilling { - name: billing_details.name, - email: billing_details.email, - address: billing_details.address, - }, - iban, - bank_account_holder_name, - }, + api_models::payments::BankDebitData::SepaBankDebit { iban, .. } => { + Self::SepaBankDebit { iban } + } api_models::payments::BankDebitData::BecsBankDebit { - billing_details, account_number, bsb_number, - bank_account_holder_name, + .. } => Self::BecsBankDebit { - billing_details: BankDebitBilling { - name: billing_details.name, - email: billing_details.email, - address: billing_details.address, - }, account_number, bsb_number, - bank_account_holder_name, }, api_models::payments::BankDebitData::BacsBankDebit { - billing_details, account_number, sort_code, - bank_account_holder_name, + .. } => Self::BacsBankDebit { - billing_details: BankDebitBilling { - name: billing_details.name, - email: billing_details.email, - address: billing_details.address, - }, account_number, sort_code, - bank_account_holder_name, }, } } diff --git a/openapi/openapi_spec.json b/openapi/openapi_spec.json index c330816cea8..391a9176698 100644 --- a/openapi/openapi_spec.json +++ b/openapi/openapi_spec.json @@ -5272,20 +5272,18 @@ }, "BankDebitBilling": { "type": "object", - "required": [ - "name", - "email" - ], "properties": { "name": { "type": "string", "description": "The billing name for bank debits", - "example": "John Doe" + "example": "John Doe", + "nullable": true }, "email": { "type": "string", "description": "The billing email for bank debits", - "example": "example@example.com" + "example": "example@example.com", + "nullable": true }, "address": { "allOf": [ @@ -5309,7 +5307,6 @@ "type": "object", "description": "Payment Method data for Ach bank debit", "required": [ - "billing_details", "account_number", "routing_number", "card_holder_name", @@ -5320,7 +5317,12 @@ ], "properties": { "billing_details": { - "$ref": "#/components/schemas/BankDebitBilling" + "allOf": [ + { + "$ref": "#/components/schemas/BankDebitBilling" + } + ], + "nullable": true }, "account_number": { "type": "string", @@ -5365,13 +5367,17 @@ "sepa_bank_debit": { "type": "object", "required": [ - "billing_details", "iban", "bank_account_holder_name" ], "properties": { "billing_details": { - "$ref": "#/components/schemas/BankDebitBilling" + "allOf": [ + { + "$ref": "#/components/schemas/BankDebitBilling" + } + ], + "nullable": true }, "iban": { "type": "string", @@ -5396,13 +5402,17 @@ "becs_bank_debit": { "type": "object", "required": [ - "billing_details", "account_number", "bsb_number" ], "properties": { "billing_details": { - "$ref": "#/components/schemas/BankDebitBilling" + "allOf": [ + { + "$ref": "#/components/schemas/BankDebitBilling" + } + ], + "nullable": true }, "account_number": { "type": "string", @@ -5433,14 +5443,18 @@ "bacs_bank_debit": { "type": "object", "required": [ - "billing_details", "account_number", "sort_code", "bank_account_holder_name" ], "properties": { "billing_details": { - "$ref": "#/components/schemas/BankDebitBilling" + "allOf": [ + { + "$ref": "#/components/schemas/BankDebitBilling" + } + ], + "nullable": true }, "account_number": { "type": "string", diff --git a/postman/collection-dir/stripe/Flow Testcases/Happy Cases/Scenario20-Bank Debit-ach/Payments - Create/request.json b/postman/collection-dir/stripe/Flow Testcases/Happy Cases/Scenario20-Bank Debit-ach/Payments - Create/request.json index 78d949505f4..53fe9371e49 100644 --- a/postman/collection-dir/stripe/Flow Testcases/Happy Cases/Scenario20-Bank Debit-ach/Payments - Create/request.json +++ b/postman/collection-dir/stripe/Flow Testcases/Happy Cases/Scenario20-Bank Debit-ach/Payments - Create/request.json @@ -75,6 +75,19 @@ } } }, + "billing": { + "address": { + "line1": "1467", + "line2": "HarrisonStreet", + "line3": "HarrisonStreet", + "city": "SanFransico", + "state": "California", + "zip": "94122", + "country": "US", + "first_name": "Swangi", + "last_name": "Kumari" + } + }, "metadata": { "order_details": { "product_name": "Apple iphone 15", diff --git a/postman/collection-json/stripe.postman_collection.json b/postman/collection-json/stripe.postman_collection.json index d4ed3bd3ae0..09fec42b0ba 100644 --- a/postman/collection-json/stripe.postman_collection.json +++ b/postman/collection-json/stripe.postman_collection.json @@ -16707,7 +16707,7 @@ "language": "json" } }, - "raw": "{\"amount\":1800,\"currency\":\"USD\",\"confirm\":true,\"business_label\":\"default\",\"capture_method\":\"automatic\",\"connector\":[\"stripe\"],\"customer_id\":\"klarna\",\"capture_on\":\"2022-09-10T10:11:12Z\",\"authentication_type\":\"three_ds\",\"email\":\"guest@example.com\",\"name\":\"John Doe\",\"phone\":\"999999999\",\"phone_country_code\":\"+65\",\"description\":\"Its my first payment request\",\"return_url\":\"https://google.com\",\"statement_descriptor_name\":\"Juspay\",\"statement_descriptor_suffix\":\"Router\",\"setup_future_usage\":\"off_session\",\"business_country\":\"US\",\"mandate_data\":{\"customer_acceptance\":{\"acceptance_type\":\"online\",\"accepted_at\":\"2022-09-10T10:11:12Z\",\"online\":{\"ip_address\":\"123.32.25.123\",\"user_agent\":\"Mozilla/5.0 (Linux; Android 12; SM-S906N Build/QP1A.190711.020; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/80.0.3987.119 Mobile Safari/537.36\"}},\"mandate_type\":{\"single_use\":{\"amount\":6540,\"currency\":\"USD\"}}},\"customer_acceptance\":{\"acceptance_type\":\"online\",\"accepted_at\":\"2022-09-10T10:11:12Z\",\"online\":{\"ip_address\":\"123.32.25.123\",\"user_agent\":\"Mozilla/5.0 (Linux; Android 12; SM-S906N Build/QP1A.190711.020; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/80.0.3987.119 Mobile Safari/537.36\"}},\"payment_method\":\"bank_debit\",\"payment_method_type\":\"ach\",\"payment_method_data\":{\"bank_debit\":{\"ach_bank_debit\":{\"billing_details\":{\"name\":\"John Doe\",\"email\":\"johndoe@example.com\"},\"account_number\":\"000123456789\",\"routing_number\":\"110000000\"}}},\"metadata\":{\"order_details\":{\"product_name\":\"Apple iphone 15\",\"quantity\":1,\"amount\":1800,\"account_name\":\"transaction_processing\"}},\"routing\":{\"type\":\"single\",\"data\":\"stripe\"}}" + "raw": "{\"amount\":1800,\"currency\":\"USD\",\"confirm\":true,\"business_label\":\"default\",\"capture_method\":\"automatic\",\"connector\":[\"stripe\"],\"customer_id\":\"klarna\",\"capture_on\":\"2022-09-10T10:11:12Z\",\"authentication_type\":\"three_ds\",\"email\":\"guest@example.com\",\"name\":\"JohnDoe\",\"phone\":\"999999999\",\"phone_country_code\":\"+65\",\"description\":\"Itsmyfirstpaymentrequest\",\"return_url\":\"https://google.com\",\"statement_descriptor_name\":\"Juspay\",\"statement_descriptor_suffix\":\"Router\",\"setup_future_usage\":\"off_session\",\"business_country\":\"US\",\"mandate_data\":{\"customer_acceptance\":{\"acceptance_type\":\"online\",\"accepted_at\":\"2022-09-10T10:11:12Z\",\"online\":{\"ip_address\":\"123.32.25.123\",\"user_agent\":\"Mozilla/5.0(Linux;Android12;SM-S906NBuild/QP1A.190711.020;wv)AppleWebKit/537.36(KHTML,likeGecko)Version/4.0Chrome/80.0.3987.119MobileSafari/537.36\"}},\"mandate_type\":{\"single_use\":{\"amount\":6540,\"currency\":\"USD\"}}},\"customer_acceptance\":{\"acceptance_type\":\"online\",\"accepted_at\":\"2022-09-10T10:11:12Z\",\"online\":{\"ip_address\":\"123.32.25.123\",\"user_agent\":\"Mozilla/5.0(Linux;Android12;SM-S906NBuild/QP1A.190711.020;wv)AppleWebKit/537.36(KHTML,likeGecko)Version/4.0Chrome/80.0.3987.119MobileSafari/537.36\"}},\"payment_method\":\"bank_debit\",\"payment_method_type\":\"ach\",\"payment_method_data\":{\"bank_debit\":{\"ach_bank_debit\":{\"billing_details\":{\"name\":\"JohnDoe\",\"email\":\"johndoe@example.com\"},\"account_number\":\"000123456789\",\"routing_number\":\"110000000\"}}},\"billing\":{\"address\":{\"line1\":\"1467\",\"line2\":\"HarrisonStreet\",\"line3\":\"HarrisonStreet\",\"city\":\"SanFransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"Swangi\",\"last_name\":\"Kumari\"}},\"metadata\":{\"order_details\":{\"product_name\":\"Appleiphone15\",\"quantity\":1,\"amount\":1800,\"account_name\":\"transaction_processing\"}},\"routing\":{\"type\":\"single\",\"data\":\"stripe\"}}" }, "url": { "raw": "{{baseUrl}}/payments", From c47cac815792df865e416d5ffc6c46faf6662053 Mon Sep 17 00:00:00 2001 From: Swangi Kumari <85639103+swangi-kumari@users.noreply.github.com> Date: Wed, 8 May 2024 12:54:37 +0530 Subject: [PATCH 26/34] feat(connector-configs): [Cashtocode] add CNY currency for evoucher (#4578) --- crates/connector_configs/toml/development.toml | 8 ++++++++ crates/connector_configs/toml/production.toml | 8 ++++++++ crates/connector_configs/toml/sandbox.toml | 8 ++++++++ 3 files changed, 24 insertions(+) diff --git a/crates/connector_configs/toml/development.toml b/crates/connector_configs/toml/development.toml index b6be7bb0eb3..61d74863422 100644 --- a/crates/connector_configs/toml/development.toml +++ b/crates/connector_configs/toml/development.toml @@ -712,6 +712,14 @@ merchant_id_classic="MerchantId Classic" password_evoucher="Password Evoucher" username_evoucher="Username Evoucher" merchant_id_evoucher="MerchantId Evoucher" +[cashtocode.connector_auth.CurrencyAuthKey.auth_key_map.CNY.classic] +password_classic="Password Classic" +username_classic="Username Classic" +merchant_id_classic="MerchantId Classic" +[cashtocode.connector_auth.CurrencyAuthKey.auth_key_map.CNY.evoucher] +password_evoucher="Password Evoucher" +username_evoucher="Username Evoucher" +merchant_id_evoucher="MerchantId Evoucher" [cashtocode.connector_webhook_details] merchant_secret="Source verification key" diff --git a/crates/connector_configs/toml/production.toml b/crates/connector_configs/toml/production.toml index 311a029488c..10cd9d6f7cf 100644 --- a/crates/connector_configs/toml/production.toml +++ b/crates/connector_configs/toml/production.toml @@ -589,6 +589,14 @@ merchant_id_classic="MerchantId Classic" password_evoucher="Password Evoucher" username_evoucher="Username Evoucher" merchant_id_evoucher="MerchantId Evoucher" +[cashtocode.connector_auth.CurrencyAuthKey.auth_key_map.CNY.classic] +password_classic="Password Classic" +username_classic="Username Classic" +merchant_id_classic="MerchantId Classic" +[cashtocode.connector_auth.CurrencyAuthKey.auth_key_map.CNY.evoucher] +password_evoucher="Password Evoucher" +username_evoucher="Username Evoucher" +merchant_id_evoucher="MerchantId Evoucher" [cashtocode.connector_webhook_details] merchant_secret="Source verification key" diff --git a/crates/connector_configs/toml/sandbox.toml b/crates/connector_configs/toml/sandbox.toml index 502141382b7..7f4935d4887 100644 --- a/crates/connector_configs/toml/sandbox.toml +++ b/crates/connector_configs/toml/sandbox.toml @@ -712,6 +712,14 @@ merchant_id_classic="MerchantId Classic" password_evoucher="Password Evoucher" username_evoucher="Username Evoucher" merchant_id_evoucher="MerchantId Evoucher" +[cashtocode.connector_auth.CurrencyAuthKey.auth_key_map.CNY.classic] +password_classic="Password Classic" +username_classic="Username Classic" +merchant_id_classic="MerchantId Classic" +[cashtocode.connector_auth.CurrencyAuthKey.auth_key_map.CNY.evoucher] +password_evoucher="Password Evoucher" +username_evoucher="Username Evoucher" +merchant_id_evoucher="MerchantId Evoucher" [cashtocode.connector_webhook_details] merchant_secret="Source verification key" From 3db5b82d0de45130695e1a47b9e71473020fd84d Mon Sep 17 00:00:00 2001 From: Mani Chandra <84711804+ThisIsMani@users.noreply.github.com> Date: Wed, 8 May 2024 13:47:50 +0530 Subject: [PATCH 27/34] fix(users): Correct the condition for `verify_email` flow in decision manger (#4580) --- crates/router/src/types/domain/user/decision_manager.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/router/src/types/domain/user/decision_manager.rs b/crates/router/src/types/domain/user/decision_manager.rs index d635ac064fe..5a0388c7f36 100644 --- a/crates/router/src/types/domain/user/decision_manager.rs +++ b/crates/router/src/types/domain/user/decision_manager.rs @@ -42,7 +42,7 @@ impl SPTFlow { Self::TOTP => Ok(true), // Main email APIs Self::AcceptInvitationFromEmail | Self::ResetPassword => Ok(true), - Self::VerifyEmail => Ok(user.0.is_verified), + Self::VerifyEmail => Ok(!user.0.is_verified), // Final Checks Self::ForceSetPassword => user.is_password_rotate_required(state), Self::MerchantSelect => user From 339da8b0c9a1e388b65ff5d82a162e758c85ec6b Mon Sep 17 00:00:00 2001 From: AkshayaFoiger <131388445+AkshayaFoiger@users.noreply.github.com> Date: Wed, 8 May 2024 14:28:35 +0530 Subject: [PATCH 28/34] Fix(connector): [BOA/CYBS] make rsync status optional (#4570) --- .../connector/bankofamerica/transformers.rs | 35 ++++++++++++++----- .../src/connector/cybersource/transformers.rs | 35 ++++++++++++++----- 2 files changed, 52 insertions(+), 18 deletions(-) diff --git a/crates/router/src/connector/bankofamerica/transformers.rs b/crates/router/src/connector/bankofamerica/transformers.rs index 2d7b2307039..2535cfaedba 100644 --- a/crates/router/src/connector/bankofamerica/transformers.rs +++ b/crates/router/src/connector/bankofamerica/transformers.rs @@ -2644,9 +2644,16 @@ pub struct RsyncApplicationInformation { status: BankofamericaRefundStatus, } +#[derive(Debug, Deserialize, Serialize)] +#[serde(untagged)] +pub enum BankOfAmericaRsyncResponse { + RsyncApplicationResponse(Box), + ErrorInformation(BankOfAmericaErrorInformationResponse), +} + #[derive(Debug, Deserialize, Serialize)] #[serde(rename_all = "camelCase")] -pub struct BankOfAmericaRsyncResponse { +pub struct BankOfAmericaRsyncApplicationResponse { id: String, application_information: RsyncApplicationInformation, } @@ -2658,15 +2665,25 @@ impl TryFrom, ) -> Result { - Ok(Self { - response: Ok(types::RefundsResponseData { - connector_refund_id: item.response.id, - refund_status: enums::RefundStatus::from( - item.response.application_information.status, - ), + match item.response { + BankOfAmericaRsyncResponse::RsyncApplicationResponse(rsync_response) => Ok(Self { + response: Ok(types::RefundsResponseData { + connector_refund_id: rsync_response.id, + refund_status: enums::RefundStatus::from( + rsync_response.application_information.status, + ), + }), + ..item.data }), - ..item.data - }) + BankOfAmericaRsyncResponse::ErrorInformation(error_response) => Ok(Self { + status: item.data.status, + response: Ok(types::RefundsResponseData { + refund_status: common_enums::RefundStatus::Pending, + connector_refund_id: error_response.id.clone(), + }), + ..item.data + }), + } } } diff --git a/crates/router/src/connector/cybersource/transformers.rs b/crates/router/src/connector/cybersource/transformers.rs index 545ee672ed9..fc1e3a6597c 100644 --- a/crates/router/src/connector/cybersource/transformers.rs +++ b/crates/router/src/connector/cybersource/transformers.rs @@ -2742,11 +2742,18 @@ pub struct RsyncApplicationInformation { #[derive(Debug, Deserialize, Serialize)] #[serde(rename_all = "camelCase")] -pub struct CybersourceRsyncResponse { +pub struct CybersourceRsyncApplicationResponse { id: String, application_information: RsyncApplicationInformation, } +#[derive(Debug, Deserialize, Serialize)] +#[serde(untagged)] +pub enum CybersourceRsyncResponse { + RsyncApplicationResponse(Box), + ErrorInformation(CybersourceErrorInformationResponse), +} + impl TryFrom> for types::RefundsRouterData { @@ -2754,15 +2761,25 @@ impl TryFrom, ) -> Result { - Ok(Self { - response: Ok(types::RefundsResponseData { - connector_refund_id: item.response.id, - refund_status: enums::RefundStatus::from( - item.response.application_information.status, - ), + match item.response { + CybersourceRsyncResponse::RsyncApplicationResponse(rsync_reponse) => Ok(Self { + response: Ok(types::RefundsResponseData { + connector_refund_id: rsync_reponse.id, + refund_status: enums::RefundStatus::from( + rsync_reponse.application_information.status, + ), + }), + ..item.data }), - ..item.data - }) + CybersourceRsyncResponse::ErrorInformation(error_response) => Ok(Self { + status: item.data.status, + response: Ok(types::RefundsResponseData { + refund_status: common_enums::RefundStatus::Pending, + connector_refund_id: error_response.id.clone(), + }), + ..item.data + }), + } } } From a97016fea41c3b74149d8eaa5c0271ec1347bc39 Mon Sep 17 00:00:00 2001 From: Mani Chandra <84711804+ThisIsMani@users.noreply.github.com> Date: Wed, 8 May 2024 18:25:45 +0530 Subject: [PATCH 29/34] feat(users): Create `user_key_store` table and `begin_totp` API (#4577) Co-authored-by: hyperswitch-bot[bot] <148525504+hyperswitch-bot[bot]@users.noreply.github.com> --- Cargo.lock | 31 ++++- crates/api_models/src/events/user.rs | 18 +-- crates/api_models/src/user.rs | 13 +- crates/diesel_models/src/enums.rs | 27 +++- crates/diesel_models/src/lib.rs | 1 + crates/diesel_models/src/query.rs | 1 + .../diesel_models/src/query/user_key_store.rs | 24 ++++ crates/diesel_models/src/schema.rs | 16 +++ crates/diesel_models/src/user.rs | 43 ++++++- crates/diesel_models/src/user_key_store.rs | 21 +++ crates/router/Cargo.toml | 1 + crates/router/src/consts/user.rs | 9 ++ crates/router/src/core/user.rs | 65 +++++++++- crates/router/src/db.rs | 2 + crates/router/src/db/kafka_store.rs | 24 ++++ crates/router/src/db/user.rs | 27 ++++ crates/router/src/db/user_key_store.rs | 121 ++++++++++++++++++ crates/router/src/routes/app.rs | 3 +- crates/router/src/routes/lock_utils.rs | 3 +- crates/router/src/routes/user.rs | 14 ++ crates/router/src/types/domain.rs | 2 + crates/router/src/types/domain/user.rs | 83 +++++++++++- .../router/src/types/domain/user_key_store.rs | 59 +++++++++ crates/router/src/utils/user.rs | 40 ++++-- crates/router_env/src/logger/types.rs | 2 + crates/storage_impl/src/mock_db.rs | 2 + .../down.sql | 2 + .../up.sql | 6 + .../2024-05-07-080628_user_totp/down.sql | 6 + migrations/2024-05-07-080628_user_totp/up.sql | 10 ++ 30 files changed, 649 insertions(+), 27 deletions(-) create mode 100644 crates/diesel_models/src/query/user_key_store.rs create mode 100644 crates/diesel_models/src/user_key_store.rs create mode 100644 crates/router/src/db/user_key_store.rs create mode 100644 crates/router/src/types/domain/user_key_store.rs create mode 100644 migrations/2024-05-06-105026_user_key_store_table/down.sql create mode 100644 migrations/2024-05-06-105026_user_key_store_table/up.sql create mode 100644 migrations/2024-05-07-080628_user_totp/down.sql create mode 100644 migrations/2024-05-07-080628_user_totp/up.sql diff --git a/Cargo.lock b/Cargo.lock index 84fdee38021..b823f9e5c96 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1430,6 +1430,12 @@ dependencies = [ "rustc-demangle", ] +[[package]] +name = "base32" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23ce669cd6c8588f79e15cf450314f9638f967fc5770ff1c7c1deb0925ea7cfa" + [[package]] name = "base64" version = "0.13.1" @@ -1559,7 +1565,7 @@ dependencies = [ "arrayvec", "cc", "cfg-if 1.0.0", - "constant_time_eq", + "constant_time_eq 0.3.0", ] [[package]] @@ -2044,6 +2050,12 @@ dependencies = [ "tiny-keccak", ] +[[package]] +name = "constant_time_eq" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21a53c0a4d288377e7415b53dcfc3c04da5cdc2cc95c8d5ac178b58f0b861ad6" + [[package]] name = "constant_time_eq" version = "0.3.0" @@ -5683,6 +5695,7 @@ dependencies = [ "thiserror", "time", "tokio 1.37.0", + "totp-rs", "tracing-futures", "unicode-segmentation", "url", @@ -7473,6 +7486,22 @@ dependencies = [ "tracing-futures", ] +[[package]] +name = "totp-rs" +version = "5.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c4ae9724c5888c0417d2396037ed3b60665925624766416e3e342b6ba5dbd3f" +dependencies = [ + "base32", + "constant_time_eq 0.2.6", + "hmac", + "rand", + "sha1", + "sha2", + "url", + "urlencoding", +] + [[package]] name = "tower" version = "0.4.13" diff --git a/crates/api_models/src/events/user.rs b/crates/api_models/src/events/user.rs index dab9ace3ac2..1d91a47bf56 100644 --- a/crates/api_models/src/events/user.rs +++ b/crates/api_models/src/events/user.rs @@ -10,13 +10,14 @@ use crate::user::{ dashboard_metadata::{ GetMetaDataRequest, GetMetaDataResponse, GetMultipleMetaDataPayload, SetMetaDataRequest, }, - AcceptInviteFromEmailRequest, AuthorizeResponse, ChangePasswordRequest, ConnectAccountRequest, - CreateInternalUserRequest, DashboardEntryResponse, ForgotPasswordRequest, - GetUserDetailsResponse, GetUserRoleDetailsRequest, GetUserRoleDetailsResponse, - InviteUserRequest, ListUsersResponse, ReInviteUserRequest, ResetPasswordRequest, - RotatePasswordRequest, SendVerifyEmailRequest, SignInResponse, SignUpRequest, - SignUpWithMerchantIdRequest, SwitchMerchantIdRequest, TokenOrPayloadResponse, TokenResponse, - UpdateUserAccountDetailsRequest, UserFromEmailRequest, UserMerchantCreate, VerifyEmailRequest, + AcceptInviteFromEmailRequest, AuthorizeResponse, BeginTotpResponse, ChangePasswordRequest, + ConnectAccountRequest, CreateInternalUserRequest, DashboardEntryResponse, + ForgotPasswordRequest, GetUserDetailsResponse, GetUserRoleDetailsRequest, + GetUserRoleDetailsResponse, InviteUserRequest, ListUsersResponse, ReInviteUserRequest, + ResetPasswordRequest, RotatePasswordRequest, SendVerifyEmailRequest, SignInResponse, + SignUpRequest, SignUpWithMerchantIdRequest, SwitchMerchantIdRequest, TokenOrPayloadResponse, + TokenResponse, UpdateUserAccountDetailsRequest, UserFromEmailRequest, UserMerchantCreate, + VerifyEmailRequest, }; impl ApiEventMetric for DashboardEntryResponse { @@ -72,7 +73,8 @@ common_utils::impl_misc_api_event_type!( GetUserRoleDetailsRequest, GetUserRoleDetailsResponse, TokenResponse, - UserFromEmailRequest + UserFromEmailRequest, + BeginTotpResponse ); #[cfg(feature = "dummy_connector")] diff --git a/crates/api_models/src/user.rs b/crates/api_models/src/user.rs index b2128cc949c..0dde73d0545 100644 --- a/crates/api_models/src/user.rs +++ b/crates/api_models/src/user.rs @@ -236,8 +236,19 @@ pub enum TokenOrPayloadResponse { Token(TokenResponse), Payload(T), } - #[derive(Debug, serde::Deserialize, serde::Serialize)] pub struct UserFromEmailRequest { pub token: Secret, } + +#[derive(Debug, serde::Deserialize, serde::Serialize)] +pub struct BeginTotpResponse { + pub secret: Option, +} + +#[derive(Debug, serde::Deserialize, serde::Serialize)] +pub struct TotpSecret { + pub secret: Secret, + pub totp_url: Secret, + pub recovery_codes: Vec>, +} diff --git a/crates/diesel_models/src/enums.rs b/crates/diesel_models/src/enums.rs index 85a7e3d92df..d78a6b11489 100644 --- a/crates/diesel_models/src/enums.rs +++ b/crates/diesel_models/src/enums.rs @@ -18,8 +18,8 @@ pub mod diesel_exports { DbRefundStatus as RefundStatus, DbRefundType as RefundType, DbRequestIncrementalAuthorization as RequestIncrementalAuthorization, DbRoleScope as RoleScope, DbRoutingAlgorithmKind as RoutingAlgorithmKind, - DbTransactionType as TransactionType, DbUserStatus as UserStatus, - DbWebhookDeliveryAttempt as WebhookDeliveryAttempt, + DbTotpStatus as TotpStatus, DbTransactionType as TransactionType, + DbUserStatus as UserStatus, DbWebhookDeliveryAttempt as WebhookDeliveryAttempt, }; } pub use common_enums::*; @@ -350,3 +350,26 @@ pub enum DashboardMetadata { IsChangePasswordRequired, OnboardingSurvey, } + +#[derive( + Clone, + Copy, + Debug, + Default, + Eq, + PartialEq, + serde::Serialize, + serde::Deserialize, + strum::Display, + strum::EnumString, + frunk::LabelledGeneric, +)] +#[diesel_enum(storage_type = "db_enum")] +#[serde(rename_all = "snake_case")] +#[strum(serialize_all = "snake_case")] +pub enum TotpStatus { + Set, + InProgress, + #[default] + NotSet, +} diff --git a/crates/diesel_models/src/lib.rs b/crates/diesel_models/src/lib.rs index 24df19ff737..d7d10569f7f 100644 --- a/crates/diesel_models/src/lib.rs +++ b/crates/diesel_models/src/lib.rs @@ -44,6 +44,7 @@ pub mod routing_algorithm; #[allow(unused_qualifications)] pub mod schema; pub mod user; +pub mod user_key_store; pub mod user_role; use diesel_impl::{DieselArray, OptionalDieselArray}; diff --git a/crates/diesel_models/src/query.rs b/crates/diesel_models/src/query.rs index b839fcc9b63..335c2db916d 100644 --- a/crates/diesel_models/src/query.rs +++ b/crates/diesel_models/src/query.rs @@ -36,4 +36,5 @@ pub mod reverse_lookup; pub mod role; pub mod routing_algorithm; pub mod user; +pub mod user_key_store; pub mod user_role; diff --git a/crates/diesel_models/src/query/user_key_store.rs b/crates/diesel_models/src/query/user_key_store.rs new file mode 100644 index 00000000000..42dfe223b1a --- /dev/null +++ b/crates/diesel_models/src/query/user_key_store.rs @@ -0,0 +1,24 @@ +use diesel::{associations::HasTable, ExpressionMethods}; + +use super::generics; +use crate::{ + schema::user_key_store::dsl, + user_key_store::{UserKeyStore, UserKeyStoreNew}, + PgPooledConn, StorageResult, +}; + +impl UserKeyStoreNew { + pub async fn insert(self, conn: &PgPooledConn) -> StorageResult { + generics::generic_insert(conn, self).await + } +} + +impl UserKeyStore { + pub async fn find_by_user_id(conn: &PgPooledConn, user_id: &str) -> StorageResult { + generics::generic_find_one::<::Table, _, _>( + conn, + dsl::user_id.eq(user_id.to_owned()), + ) + .await + } +} diff --git a/crates/diesel_models/src/schema.rs b/crates/diesel_models/src/schema.rs index 70a227a310a..20296adb65c 100644 --- a/crates/diesel_models/src/schema.rs +++ b/crates/diesel_models/src/schema.rs @@ -1149,6 +1149,18 @@ diesel::table! { } } +diesel::table! { + use diesel::sql_types::*; + use crate::enums::diesel_exports::*; + + user_key_store (user_id) { + #[max_length = 64] + user_id -> Varchar, + key -> Bytea, + created_at -> Timestamp, + } +} + diesel::table! { use diesel::sql_types::*; use crate::enums::diesel_exports::*; @@ -1192,6 +1204,9 @@ diesel::table! { last_modified_at -> Timestamp, #[max_length = 64] preferred_merchant_id -> Nullable, + totp_status -> TotpStatus, + totp_secret -> Nullable, + totp_recovery_codes -> Nullable>>, last_password_modified_at -> Nullable, } } @@ -1232,6 +1247,7 @@ diesel::allow_tables_to_appear_in_same_query!( reverse_lookup, roles, routing_algorithm, + user_key_store, user_roles, users, ); diff --git a/crates/diesel_models/src/user.rs b/crates/diesel_models/src/user.rs index 850619f8af6..6a040e41468 100644 --- a/crates/diesel_models/src/user.rs +++ b/crates/diesel_models/src/user.rs @@ -3,7 +3,9 @@ use diesel::{AsChangeset, Identifiable, Insertable, Queryable}; use masking::Secret; use time::PrimitiveDateTime; -use crate::schema::users; +use crate::{ + diesel_impl::OptionalDieselArray, encryption::Encryption, enums::TotpStatus, schema::users, +}; pub mod dashboard_metadata; @@ -20,6 +22,10 @@ pub struct User { pub created_at: PrimitiveDateTime, pub last_modified_at: PrimitiveDateTime, pub preferred_merchant_id: Option, + pub totp_status: TotpStatus, + pub totp_secret: Option, + #[diesel(deserialize_as = OptionalDieselArray>)] + pub totp_recovery_codes: Option>>, pub last_password_modified_at: Option, } @@ -36,6 +42,9 @@ pub struct UserNew { pub created_at: Option, pub last_modified_at: Option, pub preferred_merchant_id: Option, + pub totp_status: TotpStatus, + pub totp_secret: Option, + pub totp_recovery_codes: Option>>, pub last_password_modified_at: Option, } @@ -47,6 +56,9 @@ pub struct UserUpdateInternal { is_verified: Option, last_modified_at: PrimitiveDateTime, preferred_merchant_id: Option, + totp_status: Option, + totp_secret: Option, + totp_recovery_codes: Option>>, last_password_modified_at: Option, } @@ -58,6 +70,11 @@ pub enum UserUpdate { is_verified: Option, preferred_merchant_id: Option, }, + TotpUpdate { + totp_status: Option, + totp_secret: Option, + totp_recovery_codes: Option>>, + }, PasswordUpdate { password: Option>, }, @@ -73,6 +90,9 @@ impl From for UserUpdateInternal { is_verified: Some(true), last_modified_at, preferred_merchant_id: None, + totp_status: None, + totp_secret: None, + totp_recovery_codes: None, last_password_modified_at: None, }, UserUpdate::AccountUpdate { @@ -85,6 +105,24 @@ impl From for UserUpdateInternal { is_verified, last_modified_at, preferred_merchant_id, + totp_status: None, + totp_secret: None, + totp_recovery_codes: None, + last_password_modified_at: None, + }, + UserUpdate::TotpUpdate { + totp_status, + totp_secret, + totp_recovery_codes, + } => Self { + name: None, + password: None, + is_verified: None, + last_modified_at, + preferred_merchant_id: None, + totp_status, + totp_secret, + totp_recovery_codes, last_password_modified_at: None, }, UserUpdate::PasswordUpdate { password } => Self { @@ -94,6 +132,9 @@ impl From for UserUpdateInternal { last_modified_at, preferred_merchant_id: None, last_password_modified_at: Some(last_modified_at), + totp_status: None, + totp_secret: None, + totp_recovery_codes: None, }, } } diff --git a/crates/diesel_models/src/user_key_store.rs b/crates/diesel_models/src/user_key_store.rs new file mode 100644 index 00000000000..a35b4d9d169 --- /dev/null +++ b/crates/diesel_models/src/user_key_store.rs @@ -0,0 +1,21 @@ +use diesel::{Identifiable, Insertable, Queryable}; +use time::PrimitiveDateTime; + +use crate::{encryption::Encryption, schema::user_key_store}; + +#[derive(Clone, Debug, serde::Serialize, serde::Deserialize, Identifiable, Queryable)] +#[diesel(table_name = user_key_store)] +#[diesel(primary_key(user_id))] +pub struct UserKeyStore { + pub user_id: String, + pub key: Encryption, + pub created_at: PrimitiveDateTime, +} + +#[derive(Clone, Debug, serde::Serialize, serde::Deserialize, Insertable)] +#[diesel(table_name = user_key_store)] +pub struct UserKeyStoreNew { + pub user_id: String, + pub key: Encryption, + pub created_at: PrimitiveDateTime, +} diff --git a/crates/router/Cargo.toml b/crates/router/Cargo.toml index 7ff47b927d8..144d6f07988 100644 --- a/crates/router/Cargo.toml +++ b/crates/router/Cargo.toml @@ -126,6 +126,7 @@ isocountry = "0.3.2" iso_currency = "0.4.4" actix-http = "3.6.0" events = { version = "0.1.0", path = "../events" } +totp-rs = { version = "5.5.1", features = ["gen_secret", "otpauth"]} [build-dependencies] router_env = { version = "0.1.0", path = "../router_env", default-features = false } diff --git a/crates/router/src/consts/user.rs b/crates/router/src/consts/user.rs index f14610649f4..8d6aa6265d8 100644 --- a/crates/router/src/consts/user.rs +++ b/crates/router/src/consts/user.rs @@ -1,5 +1,14 @@ pub const MAX_NAME_LENGTH: usize = 70; pub const MAX_COMPANY_NAME_LENGTH: usize = 70; pub const BUSINESS_EMAIL: &str = "biz@hyperswitch.io"; +pub const RECOVERY_CODES_COUNT: usize = 8; +pub const RECOVERY_CODE_LENGTH: usize = 8; // This is without counting the hyphen in between +pub const TOTP_ISSUER_NAME: &str = "Hyperswitch"; +/// The number of digits composing the auth code. +pub const TOTP_DIGITS: usize = 6; +/// Duration in seconds of a step. +pub const TOTP_VALIDITY_DURATION_IN_SECONDS: u64 = 30; +/// Number of totps allowed as network delay. 1 would mean one totp before current totp and one totp after are valids. +pub const TOTP_TOLERANCE: u8 = 1; pub const MAX_PASSWORD_LENGTH: usize = 70; pub const MIN_PASSWORD_LENGTH: usize = 8; diff --git a/crates/router/src/core/user.rs b/crates/router/src/core/user.rs index e51ad6120c9..e01ed4b1a23 100644 --- a/crates/router/src/core/user.rs +++ b/crates/router/src/core/user.rs @@ -1,9 +1,13 @@ use api_models::user::{self as user_api, InviteMultipleUserResponse}; #[cfg(feature = "email")] use diesel_models::user_role::UserRoleUpdate; -use diesel_models::{enums::UserStatus, user as storage_user, user_role::UserRoleNew}; +use diesel_models::{ + enums::{TotpStatus, UserStatus}, + user as storage_user, + user_role::UserRoleNew, +}; use error_stack::{report, ResultExt}; -use masking::ExposeInterface; +use masking::{ExposeInterface, PeekInterface}; #[cfg(feature = "email")] use router_env::env; use router_env::logger; @@ -1581,3 +1585,60 @@ pub async fn user_from_email( }; auth::cookies::set_cookie_response(response, token) } + +pub async fn begin_totp( + state: AppState, + user_token: auth::UserFromSinglePurposeToken, +) -> UserResponse { + let user_from_db: domain::UserFromStorage = state + .store + .find_user_by_id(&user_token.user_id) + .await + .change_context(UserErrors::InternalServerError)? + .into(); + + if user_from_db.get_totp_status() == TotpStatus::Set { + return Ok(ApplicationResponse::Json(user_api::BeginTotpResponse { + secret: None, + })); + } + + let totp = utils::user::generate_default_totp(user_from_db.get_email(), None)?; + let recovery_codes = domain::RecoveryCodes::generate_new(); + + let key_store = user_from_db.get_or_create_key_store(&state).await?; + + state + .store + .update_user_by_user_id( + user_from_db.get_user_id(), + storage_user::UserUpdate::TotpUpdate { + totp_status: Some(TotpStatus::InProgress), + totp_secret: Some( + // TODO: Impl conversion trait for User and move this there + domain::types::encrypt::( + totp.get_secret_base32().into(), + key_store.key.peek(), + ) + .await + .change_context(UserErrors::InternalServerError)? + .into(), + ), + totp_recovery_codes: Some( + recovery_codes + .get_hashed() + .change_context(UserErrors::InternalServerError)?, + ), + }, + ) + .await + .change_context(UserErrors::InternalServerError)?; + + Ok(ApplicationResponse::Json(user_api::BeginTotpResponse { + secret: Some(user_api::TotpSecret { + secret: totp.get_secret_base32().into(), + totp_url: totp.get_url().into(), + recovery_codes: recovery_codes.into_inner(), + }), + })) +} diff --git a/crates/router/src/db.rs b/crates/router/src/db.rs index ca9432fcba9..c34bcaa1e38 100644 --- a/crates/router/src/db.rs +++ b/crates/router/src/db.rs @@ -33,6 +33,7 @@ pub mod reverse_lookup; pub mod role; pub mod routing_algorithm; pub mod user; +pub mod user_key_store; pub mod user_role; use diesel_models::{ @@ -118,6 +119,7 @@ pub trait StorageInterface: + user::sample_data::BatchSampleDataInterface + health_check::HealthCheckDbInterface + role::RoleInterface + + user_key_store::UserKeyStoreInterface + authentication::AuthenticationInterface + 'static { diff --git a/crates/router/src/db/kafka_store.rs b/crates/router/src/db/kafka_store.rs index 0aaa47365fc..4a9ab7fc8f6 100644 --- a/crates/router/src/db/kafka_store.rs +++ b/crates/router/src/db/kafka_store.rs @@ -32,6 +32,7 @@ use super::{ dashboard_metadata::DashboardMetadataInterface, role::RoleInterface, user::{sample_data::BatchSampleDataInterface, UserInterface}, + user_key_store::UserKeyStoreInterface, user_role::UserRoleInterface, }; #[cfg(feature = "payouts")] @@ -2743,3 +2744,26 @@ impl RoleInterface for KafkaStore { self.diesel_store.list_all_roles(merchant_id, org_id).await } } + +#[async_trait::async_trait] +impl UserKeyStoreInterface for KafkaStore { + async fn insert_user_key_store( + &self, + user_key_store: domain::UserKeyStore, + key: &Secret>, + ) -> CustomResult { + self.diesel_store + .insert_user_key_store(user_key_store, key) + .await + } + + async fn get_user_key_store_by_user_id( + &self, + user_id: &str, + key: &Secret>, + ) -> CustomResult { + self.diesel_store + .get_user_key_store_by_user_id(user_id, key) + .await + } +} diff --git a/crates/router/src/db/user.rs b/crates/router/src/db/user.rs index 9ec7cf6fab4..200513ae8d0 100644 --- a/crates/router/src/db/user.rs +++ b/crates/router/src/db/user.rs @@ -162,6 +162,9 @@ impl UserInterface for MockDb { created_at: user_data.created_at.unwrap_or(time_now), last_modified_at: user_data.created_at.unwrap_or(time_now), preferred_merchant_id: user_data.preferred_merchant_id, + totp_status: user_data.totp_status, + totp_secret: user_data.totp_secret, + totp_recovery_codes: user_data.totp_recovery_codes, last_password_modified_at: user_data.last_password_modified_at, }; users.push(user.clone()); @@ -229,6 +232,18 @@ impl UserInterface for MockDb { .or(user.preferred_merchant_id.clone()), ..user.to_owned() }, + storage::UserUpdate::TotpUpdate { + totp_status, + totp_secret, + totp_recovery_codes, + } => storage::User { + totp_status: totp_status.unwrap_or(user.totp_status), + totp_secret: totp_secret.clone().or(user.totp_secret.clone()), + totp_recovery_codes: totp_recovery_codes + .clone() + .or(user.totp_recovery_codes.clone()), + ..user.to_owned() + }, storage::UserUpdate::PasswordUpdate { password } => storage::User { password: password.clone().unwrap_or(user.password.clone()), last_password_modified_at: Some(common_utils::date_time::now()), @@ -272,6 +287,18 @@ impl UserInterface for MockDb { .or(user.preferred_merchant_id.clone()), ..user.to_owned() }, + storage::UserUpdate::TotpUpdate { + totp_status, + totp_secret, + totp_recovery_codes, + } => storage::User { + totp_status: totp_status.unwrap_or(user.totp_status), + totp_secret: totp_secret.clone().or(user.totp_secret.clone()), + totp_recovery_codes: totp_recovery_codes + .clone() + .or(user.totp_recovery_codes.clone()), + ..user.to_owned() + }, storage::UserUpdate::PasswordUpdate { password } => storage::User { password: password.clone().unwrap_or(user.password.clone()), last_password_modified_at: Some(common_utils::date_time::now()), diff --git a/crates/router/src/db/user_key_store.rs b/crates/router/src/db/user_key_store.rs new file mode 100644 index 00000000000..e08d17d280e --- /dev/null +++ b/crates/router/src/db/user_key_store.rs @@ -0,0 +1,121 @@ +use common_utils::errors::CustomResult; +use error_stack::{report, ResultExt}; +use masking::Secret; +use router_env::{instrument, tracing}; +use storage_impl::MockDb; + +use crate::{ + connection, + core::errors, + services::Store, + types::domain::{ + self, + behaviour::{Conversion, ReverseConversion}, + }, +}; + +#[async_trait::async_trait] +pub trait UserKeyStoreInterface { + async fn insert_user_key_store( + &self, + user_key_store: domain::UserKeyStore, + key: &Secret>, + ) -> CustomResult; + + async fn get_user_key_store_by_user_id( + &self, + user_id: &str, + key: &Secret>, + ) -> CustomResult; +} + +#[async_trait::async_trait] +impl UserKeyStoreInterface for Store { + #[instrument(skip_all)] + async fn insert_user_key_store( + &self, + user_key_store: domain::UserKeyStore, + key: &Secret>, + ) -> CustomResult { + let conn = connection::pg_connection_write(self).await?; + user_key_store + .construct_new() + .await + .change_context(errors::StorageError::EncryptionError)? + .insert(&conn) + .await + .map_err(|error| report!(errors::StorageError::from(error)))? + .convert(key) + .await + .change_context(errors::StorageError::DecryptionError) + } + + #[instrument(skip_all)] + async fn get_user_key_store_by_user_id( + &self, + user_id: &str, + key: &Secret>, + ) -> CustomResult { + let conn = connection::pg_connection_read(self).await?; + + diesel_models::user_key_store::UserKeyStore::find_by_user_id(&conn, user_id) + .await + .map_err(|error| report!(errors::StorageError::from(error)))? + .convert(key) + .await + .change_context(errors::StorageError::DecryptionError) + } +} + +#[async_trait::async_trait] +impl UserKeyStoreInterface for MockDb { + #[instrument(skip_all)] + async fn insert_user_key_store( + &self, + user_key_store: domain::UserKeyStore, + key: &Secret>, + ) -> CustomResult { + let mut locked_user_key_store = self.user_key_store.lock().await; + + if locked_user_key_store + .iter() + .any(|user_key| user_key.user_id == user_key_store.user_id) + { + Err(errors::StorageError::DuplicateValue { + entity: "user_key_store", + key: Some(user_key_store.user_id.clone()), + })?; + } + + let user_key_store = Conversion::convert(user_key_store) + .await + .change_context(errors::StorageError::MockDbError)?; + locked_user_key_store.push(user_key_store.clone()); + + user_key_store + .convert(key) + .await + .change_context(errors::StorageError::DecryptionError) + } + + #[instrument(skip_all)] + async fn get_user_key_store_by_user_id( + &self, + user_id: &str, + key: &Secret>, + ) -> CustomResult { + self.user_key_store + .lock() + .await + .iter() + .find(|user_key_store| user_key_store.user_id == user_id) + .cloned() + .ok_or(errors::StorageError::ValueNotFound(format!( + "No user_key_store is found for user_id={}", + user_id + )))? + .convert(key) + .await + .change_context(errors::StorageError::DecryptionError) + } +} diff --git a/crates/router/src/routes/app.rs b/crates/router/src/routes/app.rs index 857da69d4b3..cff4fc67db3 100644 --- a/crates/router/src/routes/app.rs +++ b/crates/router/src/routes/app.rs @@ -1197,7 +1197,8 @@ impl User { web::resource("/data") .route(web::get().to(get_multiple_dashboard_metadata)) .route(web::post().to(set_dashboard_metadata)), - ); + ) + .service(web::resource("/totp/begin").route(web::get().to(totp_begin))); #[cfg(feature = "email")] { diff --git a/crates/router/src/routes/lock_utils.rs b/crates/router/src/routes/lock_utils.rs index 5f8346a8f23..5bef68073f0 100644 --- a/crates/router/src/routes/lock_utils.rs +++ b/crates/router/src/routes/lock_utils.rs @@ -210,7 +210,8 @@ impl From for ApiIdentifier { | Flow::VerifyEmail | Flow::AcceptInviteFromEmail | Flow::VerifyEmailRequest - | Flow::UpdateUserAccountDetails => Self::User, + | Flow::UpdateUserAccountDetails + | Flow::TotpBegin => Self::User, Flow::ListRoles | Flow::GetRole diff --git a/crates/router/src/routes/user.rs b/crates/router/src/routes/user.rs index f990438b2f1..db12729d01a 100644 --- a/crates/router/src/routes/user.rs +++ b/crates/router/src/routes/user.rs @@ -612,3 +612,17 @@ pub async fn user_from_email( )) .await } + +pub async fn totp_begin(state: web::Data, req: HttpRequest) -> HttpResponse { + let flow = Flow::TotpBegin; + Box::pin(api::server_wrap( + flow, + state.clone(), + &req, + (), + |state, user, _, _| user_core::begin_totp(state, user), + &auth::SinglePurposeJWTAuth(common_enums::TokenPurpose::TOTP), + api_locking::LockAction::NotApplicable, + )) + .await +} diff --git a/crates/router/src/types/domain.rs b/crates/router/src/types/domain.rs index e5f6b8c9660..d18ae0d0190 100644 --- a/crates/router/src/types/domain.rs +++ b/crates/router/src/types/domain.rs @@ -9,6 +9,7 @@ pub mod payments; pub mod types; #[cfg(feature = "olap")] pub mod user; +pub mod user_key_store; pub use address::*; pub use customer::*; @@ -19,3 +20,4 @@ pub use merchant_key_store::*; pub use payments::*; #[cfg(feature = "olap")] pub use user::*; +pub use user_key_store::*; diff --git a/crates/router/src/types/domain/user.rs b/crates/router/src/types/domain/user.rs index 7e1a1eee3e8..00881626c1c 100644 --- a/crates/router/src/types/domain/user.rs +++ b/crates/router/src/types/domain/user.rs @@ -6,7 +6,7 @@ use api_models::{ use common_enums::TokenPurpose; use common_utils::{errors::CustomResult, pii}; use diesel_models::{ - enums::UserStatus, + enums::{TotpStatus, UserStatus}, organization as diesel_org, organization::Organization, user as storage_user, @@ -15,6 +15,7 @@ use diesel_models::{ use error_stack::{report, ResultExt}; use masking::{ExposeInterface, PeekInterface, Secret}; use once_cell::sync::Lazy; +use rand::distributions::{Alphanumeric, DistString}; use router_env::env; use unicode_segmentation::UnicodeSegmentation; @@ -26,7 +27,7 @@ use crate::{ }, db::StorageInterface, routes::AppState, - services::{authentication as auth, authentication::UserFromToken, authorization::info}, + services::{self, authentication as auth, authentication::UserFromToken, authorization::info}, types::transformers::ForeignFrom, utils::{self, user::password}, }; @@ -35,6 +36,8 @@ pub mod dashboard_metadata; pub mod decision_manager; pub use decision_manager::*; +use super::{types as domain_types, UserKeyStore}; + #[derive(Clone)] pub struct UserName(Secret); @@ -863,6 +866,49 @@ impl UserFromStorage { ) } } + + pub async fn get_or_create_key_store(&self, state: &AppState) -> UserResult { + let master_key = state.store.get_master_key(); + let key_store_result = state + .store + .get_user_key_store_by_user_id(self.get_user_id(), &master_key.to_vec().into()) + .await; + + if let Ok(key_store) = key_store_result { + Ok(key_store) + } else if key_store_result + .as_ref() + .map_err(|e| e.current_context().is_db_not_found()) + .err() + .unwrap_or(false) + { + let key = services::generate_aes256_key() + .change_context(UserErrors::InternalServerError) + .attach_printable("Unable to generate aes 256 key")?; + + let key_store = UserKeyStore { + user_id: self.get_user_id().to_string(), + key: domain_types::encrypt(key.to_vec().into(), master_key) + .await + .change_context(UserErrors::InternalServerError)?, + created_at: common_utils::date_time::now(), + }; + state + .store + .insert_user_key_store(key_store, &master_key.to_vec().into()) + .await + .change_context(UserErrors::InternalServerError) + } else { + Err(key_store_result + .err() + .map(|e| e.change_context(UserErrors::InternalServerError)) + .unwrap_or(UserErrors::InternalServerError.into())) + } + } + + pub fn get_totp_status(&self) -> TotpStatus { + self.0.totp_status + } } impl From for user_role_api::ModuleInfo { @@ -1031,3 +1077,36 @@ impl RoleName { self.0 } } + +#[derive(serde::Serialize, serde::Deserialize, Debug)] +pub struct RecoveryCodes(pub Vec>); + +impl RecoveryCodes { + pub fn generate_new() -> Self { + let mut rand = rand::thread_rng(); + let recovery_codes = (0..consts::user::RECOVERY_CODES_COUNT) + .map(|_| { + let code_part_1 = + Alphanumeric.sample_string(&mut rand, consts::user::RECOVERY_CODE_LENGTH / 2); + let code_part_2 = + Alphanumeric.sample_string(&mut rand, consts::user::RECOVERY_CODE_LENGTH / 2); + + Secret::new(format!("{}-{}", code_part_1, code_part_2)) + }) + .collect::>(); + + Self(recovery_codes) + } + + pub fn get_hashed(&self) -> UserResult>> { + self.0 + .iter() + .cloned() + .map(password::generate_password_hash) + .collect::, _>>() + } + + pub fn into_inner(self) -> Vec> { + self.0 + } +} diff --git a/crates/router/src/types/domain/user_key_store.rs b/crates/router/src/types/domain/user_key_store.rs new file mode 100644 index 00000000000..4c1427d58dc --- /dev/null +++ b/crates/router/src/types/domain/user_key_store.rs @@ -0,0 +1,59 @@ +use common_utils::{ + crypto::{Encryptable, GcmAes256}, + date_time, +}; +use error_stack::ResultExt; +use masking::{PeekInterface, Secret}; +use time::PrimitiveDateTime; + +use crate::{ + errors::{CustomResult, ValidationError}, + types::domain::types::TypeEncryption, +}; + +#[derive(Clone, Debug, serde::Serialize)] +pub struct UserKeyStore { + pub user_id: String, + pub key: Encryptable>>, + pub created_at: PrimitiveDateTime, +} + +#[async_trait::async_trait] +impl super::behaviour::Conversion for UserKeyStore { + type DstType = diesel_models::user_key_store::UserKeyStore; + type NewDstType = diesel_models::user_key_store::UserKeyStoreNew; + + async fn convert(self) -> CustomResult { + Ok(diesel_models::user_key_store::UserKeyStore { + key: self.key.into(), + user_id: self.user_id, + created_at: self.created_at, + }) + } + + async fn convert_back( + item: Self::DstType, + key: &Secret>, + ) -> CustomResult + where + Self: Sized, + { + Ok(Self { + key: Encryptable::decrypt(item.key, key.peek(), GcmAes256) + .await + .change_context(ValidationError::InvalidValue { + message: "Failed while decrypting customer data".to_string(), + })?, + user_id: item.user_id, + created_at: item.created_at, + }) + } + + async fn construct_new(self) -> CustomResult { + Ok(diesel_models::user_key_store::UserKeyStoreNew { + user_id: self.user_id, + key: self.key.into(), + created_at: date_time::now(), + }) + } +} diff --git a/crates/router/src/utils/user.rs b/crates/router/src/utils/user.rs index 33e9aa6769c..4980958c9ac 100644 --- a/crates/router/src/utils/user.rs +++ b/crates/router/src/utils/user.rs @@ -1,12 +1,14 @@ use std::collections::HashMap; use api_models::user as user_api; -use common_utils::errors::CustomResult; +use common_utils::{errors::CustomResult, pii}; use diesel_models::{enums::UserStatus, user_role::UserRole}; use error_stack::ResultExt; -use masking::Secret; +use masking::ExposeInterface; +use totp_rs::{Algorithm, TOTP}; use crate::{ + consts, core::errors::{StorageError, UserErrors, UserResult}, routes::AppState, services::{ @@ -74,7 +76,7 @@ pub async fn generate_jwt_auth_token( state: &AppState, user: &UserFromStorage, user_role: &UserRole, -) -> UserResult> { +) -> UserResult> { let token = AuthToken::new_token( user.get_user_id().to_string(), user_role.merchant_id.clone(), @@ -83,7 +85,7 @@ pub async fn generate_jwt_auth_token( user_role.org_id.clone(), ) .await?; - Ok(Secret::new(token)) + Ok(masking::Secret::new(token)) } pub async fn generate_jwt_auth_token_with_custom_role_attributes( @@ -92,7 +94,7 @@ pub async fn generate_jwt_auth_token_with_custom_role_attributes( merchant_id: String, org_id: String, role_id: String, -) -> UserResult> { +) -> UserResult> { let token = AuthToken::new_token( user.get_user_id().to_string(), merchant_id, @@ -101,14 +103,14 @@ pub async fn generate_jwt_auth_token_with_custom_role_attributes( org_id, ) .await?; - Ok(Secret::new(token)) + Ok(masking::Secret::new(token)) } pub fn get_dashboard_entry_response( state: &AppState, user: UserFromStorage, user_role: UserRole, - token: Secret, + token: masking::Secret, ) -> UserResult { let verification_days_left = get_verification_days_left(state, &user)?; @@ -185,9 +187,31 @@ pub async fn get_user_from_db_by_email( .map(UserFromStorage::from) } -pub fn get_token_from_signin_response(resp: &user_api::SignInResponse) -> Secret { +pub fn get_token_from_signin_response(resp: &user_api::SignInResponse) -> masking::Secret { match resp { user_api::SignInResponse::DashboardEntry(data) => data.token.clone(), user_api::SignInResponse::MerchantSelect(data) => data.token.clone(), } } + +pub fn generate_default_totp( + email: pii::Email, + secret: Option>, +) -> UserResult { + let secret = secret + .map(|sec| totp_rs::Secret::Encoded(sec.expose())) + .unwrap_or_else(totp_rs::Secret::generate_secret) + .to_bytes() + .change_context(UserErrors::InternalServerError)?; + + TOTP::new( + Algorithm::SHA1, + consts::user::TOTP_DIGITS, + consts::user::TOTP_TOLERANCE, + consts::user::TOTP_VALIDITY_DURATION_IN_SECONDS, + secret, + Some(consts::user::TOTP_ISSUER_NAME.to_string()), + email.expose().expose(), + ) + .change_context(UserErrors::InternalServerError) +} diff --git a/crates/router_env/src/logger/types.rs b/crates/router_env/src/logger/types.rs index b360d20fed1..b3252302413 100644 --- a/crates/router_env/src/logger/types.rs +++ b/crates/router_env/src/logger/types.rs @@ -396,6 +396,8 @@ pub enum Flow { UpdateRole, /// User email flow start UserFromEmail, + /// Begin TOTP + TotpBegin, /// List initial webhook delivery attempts WebhookEventInitialDeliveryAttemptList, /// List delivery attempts for a webhook event diff --git a/crates/storage_impl/src/mock_db.rs b/crates/storage_impl/src/mock_db.rs index 3657201f878..0ada6513ff0 100644 --- a/crates/storage_impl/src/mock_db.rs +++ b/crates/storage_impl/src/mock_db.rs @@ -57,6 +57,7 @@ pub struct MockDb { pub payouts: Arc>>, pub authentications: Arc>>, pub roles: Arc>>, + pub user_key_store: Arc>>, } impl MockDb { @@ -100,6 +101,7 @@ impl MockDb { payouts: Default::default(), authentications: Default::default(), roles: Default::default(), + user_key_store: Default::default(), }) } } diff --git a/migrations/2024-05-06-105026_user_key_store_table/down.sql b/migrations/2024-05-06-105026_user_key_store_table/down.sql new file mode 100644 index 00000000000..63df9500997 --- /dev/null +++ b/migrations/2024-05-06-105026_user_key_store_table/down.sql @@ -0,0 +1,2 @@ +-- This file should undo anything in `up.sql` +DROP TABLE IF EXISTS user_key_store; diff --git a/migrations/2024-05-06-105026_user_key_store_table/up.sql b/migrations/2024-05-06-105026_user_key_store_table/up.sql new file mode 100644 index 00000000000..48147e6f597 --- /dev/null +++ b/migrations/2024-05-06-105026_user_key_store_table/up.sql @@ -0,0 +1,6 @@ +-- Your SQL goes here +CREATE TABLE IF NOT EXISTS user_key_store ( + user_id VARCHAR(64) PRIMARY KEY, + key bytea NOT NULL, + created_at TIMESTAMP NOT NULL DEFAULT now() +); diff --git a/migrations/2024-05-07-080628_user_totp/down.sql b/migrations/2024-05-07-080628_user_totp/down.sql new file mode 100644 index 00000000000..d8e5840e35c --- /dev/null +++ b/migrations/2024-05-07-080628_user_totp/down.sql @@ -0,0 +1,6 @@ +-- This file should undo anything in `up.sql` +ALTER TABLE users DROP COLUMN totp_status; +ALTER TABLE users DROP COLUMN totp_secret; +ALTER TABLE users DROP COLUMN totp_recovery_codes; + +DROP TYPE "TotpStatus"; diff --git a/migrations/2024-05-07-080628_user_totp/up.sql b/migrations/2024-05-07-080628_user_totp/up.sql new file mode 100644 index 00000000000..770a3fbd4c5 --- /dev/null +++ b/migrations/2024-05-07-080628_user_totp/up.sql @@ -0,0 +1,10 @@ +-- Your SQL goes here +CREATE TYPE "TotpStatus" AS ENUM ( + 'set', + 'in_progress', + 'not_set' +); + +ALTER TABLE users ADD COLUMN IF NOT EXISTS totp_status "TotpStatus" DEFAULT 'not_set' NOT NULL; +ALTER TABLE users ADD COLUMN IF NOT EXISTS totp_secret bytea DEFAULT NULL; +ALTER TABLE users ADD COLUMN IF NOT EXISTS totp_recovery_codes TEXT[] DEFAULT NULL; From dca15aeeb501a499fd7334d5cc68b8053757cad4 Mon Sep 17 00:00:00 2001 From: Subhajit Ghosh <99127578+subhajit20@users.noreply.github.com> Date: Wed, 8 May 2024 18:35:32 +0530 Subject: [PATCH 30/34] refactor(db): Add TenantId field to the KafkaStore struct (#4512) --- crates/router/src/db.rs | 2 +- crates/router/src/db/kafka_store.rs | 8 +++++++- crates/router/src/routes/app.rs | 1 + 3 files changed, 9 insertions(+), 2 deletions(-) diff --git a/crates/router/src/db.rs b/crates/router/src/db.rs index c34bcaa1e38..6ba5b601a37 100644 --- a/crates/router/src/db.rs +++ b/crates/router/src/db.rs @@ -19,7 +19,7 @@ pub mod file; pub mod fraud_check; pub mod gsm; pub mod health_check; -mod kafka_store; +pub mod kafka_store; pub mod locker_mock_up; pub mod mandate; pub mod merchant_account; diff --git a/crates/router/src/db/kafka_store.rs b/crates/router/src/db/kafka_store.rs index 4a9ab7fc8f6..a64e3cc7e38 100644 --- a/crates/router/src/db/kafka_store.rs +++ b/crates/router/src/db/kafka_store.rs @@ -25,6 +25,7 @@ use scheduler::{ db::{process_tracker::ProcessTrackerInterface, queue::QueueInterface}, SchedulerInterface, }; +use serde::Serialize; use storage_impl::redis::kv_store::RedisConnInterface; use time::PrimitiveDateTime; @@ -75,17 +76,22 @@ use crate::{ }, }; +#[derive(Clone, Serialize)] +pub struct TenantID(pub String); + #[derive(Clone)] pub struct KafkaStore { kafka_producer: KafkaProducer, pub diesel_store: Store, + pub tenant_id: TenantID, } impl KafkaStore { - pub async fn new(store: Store, kafka_producer: KafkaProducer) -> Self { + pub async fn new(store: Store, kafka_producer: KafkaProducer, tenant_id: TenantID) -> Self { Self { kafka_producer, diesel_store: store, + tenant_id, } } } diff --git a/crates/router/src/routes/app.rs b/crates/router/src/routes/app.rs index cff4fc67db3..1560578f66f 100644 --- a/crates/router/src/routes/app.rs +++ b/crates/router/src/routes/app.rs @@ -198,6 +198,7 @@ impl AppState { .await .expect("Failed to create store"), kafka_client.clone(), + crate::db::kafka_store::TenantID("default".to_string()), ) .await, ), From ec3b60e37c0b178c3e5e3fe79db88f83fd195722 Mon Sep 17 00:00:00 2001 From: Hrithikesh <61539176+hrithikesh026@users.noreply.github.com> Date: Wed, 8 May 2024 19:06:07 +0530 Subject: [PATCH 31/34] fix(core): drop three_dsserver_trans_id from authentication table (#4587) --- crates/diesel_models/src/authentication.rs | 6 ------ crates/diesel_models/src/schema.rs | 1 - crates/router/src/core/authentication/utils.rs | 1 - crates/router/src/db/authentication.rs | 1 - crates/router/src/types/api/authentication.rs | 2 +- .../down.sql | 2 ++ .../up.sql | 2 ++ 7 files changed, 5 insertions(+), 10 deletions(-) create mode 100644 migrations/2024-05-08-111348_delete_unused_column_from_authentication/down.sql create mode 100644 migrations/2024-05-08-111348_delete_unused_column_from_authentication/up.sql diff --git a/crates/diesel_models/src/authentication.rs b/crates/diesel_models/src/authentication.rs index 8840d287e54..ce2f02d087f 100644 --- a/crates/diesel_models/src/authentication.rs +++ b/crates/diesel_models/src/authentication.rs @@ -38,7 +38,6 @@ pub struct Authentication { pub challenge_request: Option, pub acs_reference_number: Option, pub acs_trans_id: Option, - pub three_ds_server_trans_id: Option, pub acs_signed_content: Option, pub profile_id: String, pub payment_id: Option, @@ -83,7 +82,6 @@ pub struct AuthenticationNew { pub challenge_request: Option, pub acs_reference_number: Option, pub acs_trans_id: Option, - pub three_dsserver_trans_id: Option, pub acs_signed_content: Option, pub profile_id: String, pub payment_id: Option, @@ -160,7 +158,6 @@ pub struct AuthenticationUpdateInternal { pub challenge_request: Option, pub acs_reference_number: Option, pub acs_trans_id: Option, - pub three_dsserver_trans_id: Option, pub acs_signed_content: Option, } @@ -191,7 +188,6 @@ impl Default for AuthenticationUpdateInternal { challenge_request: Default::default(), acs_reference_number: Default::default(), acs_trans_id: Default::default(), - three_dsserver_trans_id: Default::default(), acs_signed_content: Default::default(), } } @@ -224,7 +220,6 @@ impl AuthenticationUpdateInternal { challenge_request, acs_reference_number, acs_trans_id, - three_dsserver_trans_id, acs_signed_content, } = self; Authentication { @@ -256,7 +251,6 @@ impl AuthenticationUpdateInternal { challenge_request: challenge_request.or(source.challenge_request), acs_reference_number: acs_reference_number.or(source.acs_reference_number), acs_trans_id: acs_trans_id.or(source.acs_trans_id), - three_ds_server_trans_id: three_dsserver_trans_id.or(source.three_ds_server_trans_id), acs_signed_content: acs_signed_content.or(source.acs_signed_content), ..source } diff --git a/crates/diesel_models/src/schema.rs b/crates/diesel_models/src/schema.rs index 20296adb65c..0bea0402b51 100644 --- a/crates/diesel_models/src/schema.rs +++ b/crates/diesel_models/src/schema.rs @@ -108,7 +108,6 @@ diesel::table! { challenge_request -> Nullable, acs_reference_number -> Nullable, acs_trans_id -> Nullable, - three_dsserver_trans_id -> Nullable, acs_signed_content -> Nullable, #[max_length = 64] profile_id -> Varchar, diff --git a/crates/router/src/core/authentication/utils.rs b/crates/router/src/core/authentication/utils.rs index bece5459236..e29c8b10232 100644 --- a/crates/router/src/core/authentication/utils.rs +++ b/crates/router/src/core/authentication/utils.rs @@ -177,7 +177,6 @@ pub async fn create_new_authentication( challenge_request: None, acs_reference_number: None, acs_trans_id: None, - three_dsserver_trans_id: None, acs_signed_content: None, profile_id, payment_id, diff --git a/crates/router/src/db/authentication.rs b/crates/router/src/db/authentication.rs index 0f4aef679c5..398af72f8bd 100644 --- a/crates/router/src/db/authentication.rs +++ b/crates/router/src/db/authentication.rs @@ -143,7 +143,6 @@ impl AuthenticationInterface for MockDb { challenge_request: authentication.challenge_request, acs_reference_number: authentication.acs_reference_number, acs_trans_id: authentication.acs_trans_id, - three_ds_server_trans_id: authentication.three_dsserver_trans_id, acs_signed_content: authentication.acs_signed_content, profile_id: authentication.profile_id, payment_id: authentication.payment_id, diff --git a/crates/router/src/types/api/authentication.rs b/crates/router/src/types/api/authentication.rs index 92bcd1ae73b..3e4a494f658 100644 --- a/crates/router/src/types/api/authentication.rs +++ b/crates/router/src/types/api/authentication.rs @@ -50,7 +50,7 @@ impl TryFrom for AuthenticationResponse { challenge_request: authentication.challenge_request, acs_reference_number: authentication.acs_reference_number, acs_trans_id: authentication.acs_trans_id, - three_dsserver_trans_id: authentication.three_ds_server_trans_id, + three_dsserver_trans_id: authentication.threeds_server_transaction_id, acs_signed_content: authentication.acs_signed_content, }) } diff --git a/migrations/2024-05-08-111348_delete_unused_column_from_authentication/down.sql b/migrations/2024-05-08-111348_delete_unused_column_from_authentication/down.sql new file mode 100644 index 00000000000..8459dd79c5a --- /dev/null +++ b/migrations/2024-05-08-111348_delete_unused_column_from_authentication/down.sql @@ -0,0 +1,2 @@ +-- This file should undo anything in `up.sql` +ALTER TABLE authentication ADD COLUMN three_dsserver_trans_id VARCHAR; \ No newline at end of file diff --git a/migrations/2024-05-08-111348_delete_unused_column_from_authentication/up.sql b/migrations/2024-05-08-111348_delete_unused_column_from_authentication/up.sql new file mode 100644 index 00000000000..fdbe2332b32 --- /dev/null +++ b/migrations/2024-05-08-111348_delete_unused_column_from_authentication/up.sql @@ -0,0 +1,2 @@ +-- Your SQL goes here +ALTER TABLE authentication DROP COLUMN three_dsserver_trans_id; \ No newline at end of file From d85f245182875dcc59d8355cd07c91bfaaf08e1a Mon Sep 17 00:00:00 2001 From: github-actions <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 9 May 2024 00:12:35 +0000 Subject: [PATCH 32/34] chore(postman): update Postman collection files --- postman/collection-json/stripe.postman_collection.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/postman/collection-json/stripe.postman_collection.json b/postman/collection-json/stripe.postman_collection.json index 09fec42b0ba..b73b3bafa2e 100644 --- a/postman/collection-json/stripe.postman_collection.json +++ b/postman/collection-json/stripe.postman_collection.json @@ -16707,7 +16707,7 @@ "language": "json" } }, - "raw": "{\"amount\":1800,\"currency\":\"USD\",\"confirm\":true,\"business_label\":\"default\",\"capture_method\":\"automatic\",\"connector\":[\"stripe\"],\"customer_id\":\"klarna\",\"capture_on\":\"2022-09-10T10:11:12Z\",\"authentication_type\":\"three_ds\",\"email\":\"guest@example.com\",\"name\":\"JohnDoe\",\"phone\":\"999999999\",\"phone_country_code\":\"+65\",\"description\":\"Itsmyfirstpaymentrequest\",\"return_url\":\"https://google.com\",\"statement_descriptor_name\":\"Juspay\",\"statement_descriptor_suffix\":\"Router\",\"setup_future_usage\":\"off_session\",\"business_country\":\"US\",\"mandate_data\":{\"customer_acceptance\":{\"acceptance_type\":\"online\",\"accepted_at\":\"2022-09-10T10:11:12Z\",\"online\":{\"ip_address\":\"123.32.25.123\",\"user_agent\":\"Mozilla/5.0(Linux;Android12;SM-S906NBuild/QP1A.190711.020;wv)AppleWebKit/537.36(KHTML,likeGecko)Version/4.0Chrome/80.0.3987.119MobileSafari/537.36\"}},\"mandate_type\":{\"single_use\":{\"amount\":6540,\"currency\":\"USD\"}}},\"customer_acceptance\":{\"acceptance_type\":\"online\",\"accepted_at\":\"2022-09-10T10:11:12Z\",\"online\":{\"ip_address\":\"123.32.25.123\",\"user_agent\":\"Mozilla/5.0(Linux;Android12;SM-S906NBuild/QP1A.190711.020;wv)AppleWebKit/537.36(KHTML,likeGecko)Version/4.0Chrome/80.0.3987.119MobileSafari/537.36\"}},\"payment_method\":\"bank_debit\",\"payment_method_type\":\"ach\",\"payment_method_data\":{\"bank_debit\":{\"ach_bank_debit\":{\"billing_details\":{\"name\":\"JohnDoe\",\"email\":\"johndoe@example.com\"},\"account_number\":\"000123456789\",\"routing_number\":\"110000000\"}}},\"billing\":{\"address\":{\"line1\":\"1467\",\"line2\":\"HarrisonStreet\",\"line3\":\"HarrisonStreet\",\"city\":\"SanFransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"Swangi\",\"last_name\":\"Kumari\"}},\"metadata\":{\"order_details\":{\"product_name\":\"Appleiphone15\",\"quantity\":1,\"amount\":1800,\"account_name\":\"transaction_processing\"}},\"routing\":{\"type\":\"single\",\"data\":\"stripe\"}}" + "raw": "{\"amount\":1800,\"currency\":\"USD\",\"confirm\":true,\"business_label\":\"default\",\"capture_method\":\"automatic\",\"connector\":[\"stripe\"],\"customer_id\":\"klarna\",\"capture_on\":\"2022-09-10T10:11:12Z\",\"authentication_type\":\"three_ds\",\"email\":\"guest@example.com\",\"name\":\"John Doe\",\"phone\":\"999999999\",\"phone_country_code\":\"+65\",\"description\":\"Its my first payment request\",\"return_url\":\"https://google.com\",\"statement_descriptor_name\":\"Juspay\",\"statement_descriptor_suffix\":\"Router\",\"setup_future_usage\":\"off_session\",\"business_country\":\"US\",\"mandate_data\":{\"customer_acceptance\":{\"acceptance_type\":\"online\",\"accepted_at\":\"2022-09-10T10:11:12Z\",\"online\":{\"ip_address\":\"123.32.25.123\",\"user_agent\":\"Mozilla/5.0 (Linux; Android 12; SM-S906N Build/QP1A.190711.020; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/80.0.3987.119 Mobile Safari/537.36\"}},\"mandate_type\":{\"single_use\":{\"amount\":6540,\"currency\":\"USD\"}}},\"customer_acceptance\":{\"acceptance_type\":\"online\",\"accepted_at\":\"2022-09-10T10:11:12Z\",\"online\":{\"ip_address\":\"123.32.25.123\",\"user_agent\":\"Mozilla/5.0 (Linux; Android 12; SM-S906N Build/QP1A.190711.020; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/80.0.3987.119 Mobile Safari/537.36\"}},\"payment_method\":\"bank_debit\",\"payment_method_type\":\"ach\",\"payment_method_data\":{\"bank_debit\":{\"ach_bank_debit\":{\"billing_details\":{\"name\":\"John Doe\",\"email\":\"johndoe@example.com\"},\"account_number\":\"000123456789\",\"routing_number\":\"110000000\"}}},\"billing\":{\"address\":{\"line1\":\"1467\",\"line2\":\"HarrisonStreet\",\"line3\":\"HarrisonStreet\",\"city\":\"SanFransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"Swangi\",\"last_name\":\"Kumari\"}},\"metadata\":{\"order_details\":{\"product_name\":\"Apple iphone 15\",\"quantity\":1,\"amount\":1800,\"account_name\":\"transaction_processing\"}},\"routing\":{\"type\":\"single\",\"data\":\"stripe\"}}" }, "url": { "raw": "{{baseUrl}}/payments", From f3115c45114c1445456af229f11ca19459c9536d Mon Sep 17 00:00:00 2001 From: github-actions <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 9 May 2024 00:14:50 +0000 Subject: [PATCH 33/34] chore(version): 2024.05.09.0 --- CHANGELOG.md | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index fd3f44b64e7..c7e012a84cb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,33 @@ All notable changes to HyperSwitch will be documented here. - - - +## 2024.05.09.0 + +### Features + +- **business_profile:** Feature add a config to use `billing` as `payment_method_billing` ([#4557](https://github.com/juspay/hyperswitch/pull/4557)) ([`3e1c7eb`](https://github.com/juspay/hyperswitch/commit/3e1c7eba49de3110a2d71cea8e0540c7182d2058)) +- **connector-configs:** [Cashtocode] add CNY currency for evoucher ([#4578](https://github.com/juspay/hyperswitch/pull/4578)) ([`c47cac8`](https://github.com/juspay/hyperswitch/commit/c47cac815792df865e416d5ffc6c46faf6662053)) +- **users:** Create `user_key_store` table and `begin_totp` API ([#4577](https://github.com/juspay/hyperswitch/pull/4577)) ([`a97016f`](https://github.com/juspay/hyperswitch/commit/a97016fea41c3b74149d8eaa5c0271ec1347bc39)) + +### Bug Fixes + +- **connector:** [BOA/CYBS] make rsync status optional ([#4570](https://github.com/juspay/hyperswitch/pull/4570)) ([`339da8b`](https://github.com/juspay/hyperswitch/commit/339da8b0c9a1e388b65ff5d82a162e758c85ec6b)) +- **core:** Drop three_dsserver_trans_id from authentication table ([#4587](https://github.com/juspay/hyperswitch/pull/4587)) ([`ec3b60e`](https://github.com/juspay/hyperswitch/commit/ec3b60e37c0b178c3e5e3fe79db88f83fd195722)) +- **users:** Correct the condition for `verify_email` flow in decision manger ([#4580](https://github.com/juspay/hyperswitch/pull/4580)) ([`3db5b82`](https://github.com/juspay/hyperswitch/commit/3db5b82d0de45130695e1a47b9e71473020fd84d)) + +### Refactors + +- **bank-debit:** Remove billingdetails from bankdebit pmd ([#4371](https://github.com/juspay/hyperswitch/pull/4371)) ([`625b531`](https://github.com/juspay/hyperswitch/commit/625b53182e20b50fde5def338e122a43457da0f2)) +- **db:** Add TenantId field to the KafkaStore struct ([#4512](https://github.com/juspay/hyperswitch/pull/4512)) ([`dca15ae`](https://github.com/juspay/hyperswitch/commit/dca15aeeb501a499fd7334d5cc68b8053757cad4)) + +### Miscellaneous Tasks + +- **postman:** Update Postman collection files ([`d85f245`](https://github.com/juspay/hyperswitch/commit/d85f245182875dcc59d8355cd07c91bfaaf08e1a)) + +**Full Changelog:** [`2024.05.08.0...2024.05.09.0`](https://github.com/juspay/hyperswitch/compare/2024.05.08.0...2024.05.09.0) + +- - - + ## 2024.05.08.0 ### Features From 91354232e03a8dbd9ad9eccc8620eac321765dd7 Mon Sep 17 00:00:00 2001 From: Mani Chandra <84711804+ThisIsMani@users.noreply.github.com> Date: Thu, 9 May 2024 11:31:46 +0530 Subject: [PATCH 34/34] feat(users): Create API to Verify TOTP (#4597) --- crates/api_models/src/events/user.rs | 5 ++- crates/api_models/src/user.rs | 5 +++ crates/router/src/core/errors/user.rs | 12 +++++ crates/router/src/core/user.rs | 62 ++++++++++++++++++++++++++ crates/router/src/routes/app.rs | 3 +- crates/router/src/routes/lock_utils.rs | 3 +- crates/router/src/routes/user.rs | 18 ++++++++ crates/router/src/types/domain/user.rs | 28 +++++++++++- crates/router_env/src/logger/types.rs | 2 + 9 files changed, 133 insertions(+), 5 deletions(-) diff --git a/crates/api_models/src/events/user.rs b/crates/api_models/src/events/user.rs index 1d91a47bf56..e9eb5157095 100644 --- a/crates/api_models/src/events/user.rs +++ b/crates/api_models/src/events/user.rs @@ -17,7 +17,7 @@ use crate::user::{ ResetPasswordRequest, RotatePasswordRequest, SendVerifyEmailRequest, SignInResponse, SignUpRequest, SignUpWithMerchantIdRequest, SwitchMerchantIdRequest, TokenOrPayloadResponse, TokenResponse, UpdateUserAccountDetailsRequest, UserFromEmailRequest, UserMerchantCreate, - VerifyEmailRequest, + VerifyEmailRequest, VerifyTotpRequest, }; impl ApiEventMetric for DashboardEntryResponse { @@ -74,7 +74,8 @@ common_utils::impl_misc_api_event_type!( GetUserRoleDetailsResponse, TokenResponse, UserFromEmailRequest, - BeginTotpResponse + BeginTotpResponse, + VerifyTotpRequest ); #[cfg(feature = "dummy_connector")] diff --git a/crates/api_models/src/user.rs b/crates/api_models/src/user.rs index 0dde73d0545..7dbf867d1a0 100644 --- a/crates/api_models/src/user.rs +++ b/crates/api_models/src/user.rs @@ -252,3 +252,8 @@ pub struct TotpSecret { pub totp_url: Secret, pub recovery_codes: Vec>, } + +#[derive(Debug, serde::Deserialize, serde::Serialize)] +pub struct VerifyTotpRequest { + pub totp: Option>, +} diff --git a/crates/router/src/core/errors/user.rs b/crates/router/src/core/errors/user.rs index ddcd10c32e4..580e34ba7e8 100644 --- a/crates/router/src/core/errors/user.rs +++ b/crates/router/src/core/errors/user.rs @@ -66,6 +66,10 @@ pub enum UserErrors { RoleNameParsingError, #[error("RoleNameAlreadyExists")] RoleNameAlreadyExists, + #[error("TOTPNotSetup")] + TotpNotSetup, + #[error("InvalidTOTP")] + InvalidTotp, } impl common_utils::errors::ErrorSwitch for UserErrors { @@ -169,6 +173,12 @@ impl common_utils::errors::ErrorSwitch { AER::BadRequest(ApiError::new(sub_code, 35, self.get_error_message(), None)) } + Self::TotpNotSetup => { + AER::BadRequest(ApiError::new(sub_code, 36, self.get_error_message(), None)) + } + Self::InvalidTotp => { + AER::BadRequest(ApiError::new(sub_code, 37, self.get_error_message(), None)) + } } } } @@ -205,6 +215,8 @@ impl UserErrors { Self::InvalidRoleOperationWithMessage(error_message) => error_message, Self::RoleNameParsingError => "Invalid Role Name", Self::RoleNameAlreadyExists => "Role name already exists", + Self::TotpNotSetup => "TOTP not setup", + Self::InvalidTotp => "Invalid TOTP", } } } diff --git a/crates/router/src/core/user.rs b/crates/router/src/core/user.rs index e01ed4b1a23..83cdd1d318b 100644 --- a/crates/router/src/core/user.rs +++ b/crates/router/src/core/user.rs @@ -1642,3 +1642,65 @@ pub async fn begin_totp( }), })) } + +pub async fn verify_totp( + state: AppState, + user_token: auth::UserFromSinglePurposeToken, + req: user_api::VerifyTotpRequest, +) -> UserResponse { + let user_from_db: domain::UserFromStorage = state + .store + .find_user_by_id(&user_token.user_id) + .await + .change_context(UserErrors::InternalServerError)? + .into(); + + if let Some(user_totp) = req.totp { + if user_from_db.get_totp_status() == TotpStatus::NotSet { + return Err(UserErrors::TotpNotSetup.into()); + } + + let user_totp_secret = user_from_db + .decrypt_and_get_totp_secret(&state) + .await? + .ok_or(UserErrors::InternalServerError)?; + + let totp = + utils::user::generate_default_totp(user_from_db.get_email(), Some(user_totp_secret))?; + + if totp + .generate_current() + .change_context(UserErrors::InternalServerError)? + != user_totp.expose() + { + return Err(UserErrors::InvalidTotp.into()); + } + + if user_from_db.get_totp_status() == TotpStatus::InProgress { + state + .store + .update_user_by_user_id( + user_from_db.get_user_id(), + storage_user::UserUpdate::TotpUpdate { + totp_status: Some(TotpStatus::Set), + totp_secret: None, + totp_recovery_codes: None, + }, + ) + .await + .change_context(UserErrors::InternalServerError)?; + } + } + + let current_flow = domain::CurrentFlow::new(user_token.origin, domain::SPTFlow::TOTP.into())?; + let next_flow = current_flow.next(user_from_db, &state).await?; + let token = next_flow.get_token(&state).await?; + + auth::cookies::set_cookie_response( + user_api::TokenResponse { + token: token.clone(), + token_type: next_flow.get_flow().into(), + }, + token, + ) +} diff --git a/crates/router/src/routes/app.rs b/crates/router/src/routes/app.rs index 1560578f66f..49a8e063183 100644 --- a/crates/router/src/routes/app.rs +++ b/crates/router/src/routes/app.rs @@ -1199,7 +1199,8 @@ impl User { .route(web::get().to(get_multiple_dashboard_metadata)) .route(web::post().to(set_dashboard_metadata)), ) - .service(web::resource("/totp/begin").route(web::get().to(totp_begin))); + .service(web::resource("/totp/begin").route(web::get().to(totp_begin))) + .service(web::resource("/totp/verify").route(web::post().to(totp_verify))); #[cfg(feature = "email")] { diff --git a/crates/router/src/routes/lock_utils.rs b/crates/router/src/routes/lock_utils.rs index 5bef68073f0..ee42cc50fe3 100644 --- a/crates/router/src/routes/lock_utils.rs +++ b/crates/router/src/routes/lock_utils.rs @@ -211,7 +211,8 @@ impl From for ApiIdentifier { | Flow::AcceptInviteFromEmail | Flow::VerifyEmailRequest | Flow::UpdateUserAccountDetails - | Flow::TotpBegin => Self::User, + | Flow::TotpBegin + | Flow::TotpVerify => Self::User, Flow::ListRoles | Flow::GetRole diff --git a/crates/router/src/routes/user.rs b/crates/router/src/routes/user.rs index db12729d01a..a901988e51e 100644 --- a/crates/router/src/routes/user.rs +++ b/crates/router/src/routes/user.rs @@ -626,3 +626,21 @@ pub async fn totp_begin(state: web::Data, req: HttpRequest) -> HttpRes )) .await } + +pub async fn totp_verify( + state: web::Data, + req: HttpRequest, + json_payload: web::Json, +) -> HttpResponse { + let flow = Flow::TotpVerify; + Box::pin(api::server_wrap( + flow, + state.clone(), + &req, + json_payload.into_inner(), + |state, user, req_body, _| user_core::verify_totp(state, user, req_body), + &auth::SinglePurposeJWTAuth(common_enums::TokenPurpose::TOTP), + api_locking::LockAction::NotApplicable, + )) + .await +} diff --git a/crates/router/src/types/domain/user.rs b/crates/router/src/types/domain/user.rs index 00881626c1c..45f5d74d6f6 100644 --- a/crates/router/src/types/domain/user.rs +++ b/crates/router/src/types/domain/user.rs @@ -4,7 +4,7 @@ use api_models::{ admin as admin_api, organization as api_org, user as user_api, user_role as user_role_api, }; use common_enums::TokenPurpose; -use common_utils::{errors::CustomResult, pii}; +use common_utils::{crypto::Encryptable, errors::CustomResult, pii}; use diesel_models::{ enums::{TotpStatus, UserStatus}, organization as diesel_org, @@ -909,6 +909,32 @@ impl UserFromStorage { pub fn get_totp_status(&self) -> TotpStatus { self.0.totp_status } + + pub async fn decrypt_and_get_totp_secret( + &self, + state: &AppState, + ) -> UserResult>> { + if self.0.totp_secret.is_none() { + return Ok(None); + } + + let user_key_store = state + .store + .get_user_key_store_by_user_id( + self.get_user_id(), + &state.store.get_master_key().to_vec().into(), + ) + .await + .change_context(UserErrors::InternalServerError)?; + + Ok(domain_types::decrypt::( + self.0.totp_secret.clone(), + user_key_store.key.peek(), + ) + .await + .change_context(UserErrors::InternalServerError)? + .map(Encryptable::into_inner)) + } } impl From for user_role_api::ModuleInfo { diff --git a/crates/router_env/src/logger/types.rs b/crates/router_env/src/logger/types.rs index b3252302413..9ea86167fcf 100644 --- a/crates/router_env/src/logger/types.rs +++ b/crates/router_env/src/logger/types.rs @@ -398,6 +398,8 @@ pub enum Flow { UserFromEmail, /// Begin TOTP TotpBegin, + /// Verify TOTP + TotpVerify, /// List initial webhook delivery attempts WebhookEventInitialDeliveryAttemptList, /// List delivery attempts for a webhook event