From df32e82500fe6ba446d4558d862632501363fd26 Mon Sep 17 00:00:00 2001 From: github-actions <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 18 Nov 2024 00:22:37 +0000 Subject: [PATCH 1/6] chore(version): 2024.11.18.0 --- CHANGELOG.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index fb5fa4101d6f..7daa3cd109bb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,16 @@ All notable changes to HyperSwitch will be documented here. - - - +## 2024.11.18.0 + +### Features + +- **payments_v2:** Add finish redirection endpoint ([#6549](https://github.com/juspay/hyperswitch/pull/6549)) ([`0805a93`](https://github.com/juspay/hyperswitch/commit/0805a937b1bc12ac1dfb23922036733ed971a87a)) + +**Full Changelog:** [`2024.11.15.0...2024.11.18.0`](https://github.com/juspay/hyperswitch/compare/2024.11.15.0...2024.11.18.0) + +- - - + ## 2024.11.15.0 ### Features From d32397f060731f51a15634e221117a554b8b3721 Mon Sep 17 00:00:00 2001 From: Mani Chandra <84711804+ThisIsMani@users.noreply.github.com> Date: Mon, 18 Nov 2024 12:56:00 +0530 Subject: [PATCH 2/6] refactor(users): Make `profile_id` in the JWT non-optional (#6537) --- crates/diesel_models/src/query/user_role.rs | 7 +-- crates/router/src/core/user.rs | 61 ++++++------------- crates/router/src/core/user_role.rs | 22 +++---- crates/router/src/db/kafka_store.rs | 4 +- crates/router/src/db/user_role.rs | 20 +++--- crates/router/src/services/authentication.rs | 50 +++++---------- .../src/types/domain/user/decision_manager.rs | 2 +- crates/router/src/utils/user.rs | 2 +- 8 files changed, 58 insertions(+), 110 deletions(-) diff --git a/crates/diesel_models/src/query/user_role.rs b/crates/diesel_models/src/query/user_role.rs index 2496d22447eb..ed018cc2381e 100644 --- a/crates/diesel_models/src/query/user_role.rs +++ b/crates/diesel_models/src/query/user_role.rs @@ -87,7 +87,7 @@ impl UserRole { user_id: String, org_id: id_type::OrganizationId, merchant_id: id_type::MerchantId, - profile_id: Option, + profile_id: id_type::ProfileId, version: UserRoleVersion, ) -> StorageResult { // Checking in user roles, for a user in token hierarchy, only one of the relation will be true, either org level, merchant level or profile level @@ -103,7 +103,6 @@ impl UserRole { .or(dsl::org_id.eq(org_id).and( dsl::merchant_id .eq(merchant_id) - //TODO: In case of None, profile_id = NULL its unexpected behaviour, after V1 profile id will not be option .and(dsl::profile_id.eq(profile_id)), )); @@ -137,7 +136,6 @@ impl UserRole { .or(dsl::org_id.eq(org_id).and( dsl::merchant_id .eq(merchant_id) - //TODO: In case of None, profile_id = NULL its unexpected behaviour, after V1 profile id will not be option .and(dsl::profile_id.eq(profile_id)), )); @@ -160,7 +158,7 @@ impl UserRole { user_id: String, org_id: id_type::OrganizationId, merchant_id: id_type::MerchantId, - profile_id: Option, + profile_id: id_type::ProfileId, version: UserRoleVersion, ) -> StorageResult { // Checking in user roles, for a user in token hierarchy, only one of the relation will be true, either org level, merchant level or profile level @@ -176,7 +174,6 @@ impl UserRole { .or(dsl::org_id.eq(org_id).and( dsl::merchant_id .eq(merchant_id) - //TODO: In case of None, profile_id = NULL its unexpected behaviour, after V1 profile id will not be option .and(dsl::profile_id.eq(profile_id)), )); diff --git a/crates/router/src/core/user.rs b/crates/router/src/core/user.rs index e0ae1c531a9f..bff6205f5db8 100644 --- a/crates/router/src/core/user.rs +++ b/crates/router/src/core/user.rs @@ -119,9 +119,7 @@ pub async fn get_user_details( org_id: user_from_token.org_id, is_two_factor_auth_setup: user.get_totp_status() == TotpStatus::Set, recovery_codes_left: user.get_recovery_codes().map(|codes| codes.len()), - profile_id: user_from_token - .profile_id - .ok_or(UserErrors::JwtProfileIdMissing)?, + profile_id: user_from_token.profile_id, entity_type: role_info.get_entity_type(), }, )) @@ -603,7 +601,7 @@ async fn handle_existing_user_invitation( invitee_user_from_db.get_user_id(), &user_from_token.org_id, &user_from_token.merchant_id, - user_from_token.profile_id.as_ref(), + &user_from_token.profile_id, UserRoleVersion::V1, ) .await @@ -619,7 +617,7 @@ async fn handle_existing_user_invitation( invitee_user_from_db.get_user_id(), &user_from_token.org_id, &user_from_token.merchant_id, - user_from_token.profile_id.as_ref(), + &user_from_token.profile_id, UserRoleVersion::V2, ) .await @@ -673,10 +671,6 @@ async fn handle_existing_user_invitation( .await? } EntityType::Profile => { - let profile_id = user_from_token - .profile_id - .clone() - .ok_or(UserErrors::InternalServerError)?; user_role .add_entity(domain::ProfileLevel { tenant_id: user_from_token @@ -685,7 +679,7 @@ async fn handle_existing_user_invitation( .unwrap_or(state.tenant.tenant_id.clone()), org_id: user_from_token.org_id.clone(), merchant_id: user_from_token.merchant_id.clone(), - profile_id: profile_id.clone(), + profile_id: user_from_token.profile_id.clone(), }) .insert_in_v2(state) .await? @@ -705,16 +699,10 @@ async fn handle_existing_user_invitation( entity_id: user_from_token.merchant_id.get_string_repr().to_owned(), entity_type: EntityType::Merchant, }, - EntityType::Profile => { - let profile_id = user_from_token - .profile_id - .clone() - .ok_or(UserErrors::InternalServerError)?; - email_types::Entity { - entity_id: profile_id.get_string_repr().to_owned(), - entity_type: EntityType::Profile, - } - } + EntityType::Profile => email_types::Entity { + entity_id: user_from_token.profile_id.get_string_repr().to_owned(), + entity_type: EntityType::Profile, + }, }; let email_contents = email_types::InviteUser { @@ -812,10 +800,6 @@ async fn handle_new_user_invitation( .await? } EntityType::Profile => { - let profile_id = user_from_token - .profile_id - .clone() - .ok_or(UserErrors::InternalServerError)?; user_role .add_entity(domain::ProfileLevel { tenant_id: user_from_token @@ -824,7 +808,7 @@ async fn handle_new_user_invitation( .unwrap_or(state.tenant.tenant_id.clone()), org_id: user_from_token.org_id.clone(), merchant_id: user_from_token.merchant_id.clone(), - profile_id: profile_id.clone(), + profile_id: user_from_token.profile_id.clone(), }) .insert_in_v2(state) .await? @@ -848,16 +832,10 @@ async fn handle_new_user_invitation( entity_id: user_from_token.merchant_id.get_string_repr().to_owned(), entity_type: EntityType::Merchant, }, - EntityType::Profile => { - let profile_id = user_from_token - .profile_id - .clone() - .ok_or(UserErrors::InternalServerError)?; - email_types::Entity { - entity_id: profile_id.get_string_repr().to_owned(), - entity_type: EntityType::Profile, - } - } + EntityType::Profile => email_types::Entity { + entity_id: user_from_token.profile_id.get_string_repr().to_owned(), + entity_type: EntityType::Profile, + }, }; let email_contents = email_types::InviteUser { @@ -887,7 +865,7 @@ async fn handle_new_user_invitation( merchant_id: user_from_token.merchant_id.clone(), org_id: user_from_token.org_id.clone(), role_id: request.role_id.clone(), - profile_id: None, + profile_id: user_from_token.profile_id.clone(), tenant_id: user_from_token.tenant_id.clone(), }; @@ -939,7 +917,7 @@ pub async fn resend_invite( user.get_user_id(), &user_from_token.org_id, &user_from_token.merchant_id, - user_from_token.profile_id.as_ref(), + &user_from_token.profile_id, UserRoleVersion::V2, ) .await @@ -962,7 +940,7 @@ pub async fn resend_invite( user.get_user_id(), &user_from_token.org_id, &user_from_token.merchant_id, - user_from_token.profile_id.as_ref(), + &user_from_token.profile_id, UserRoleVersion::V1, ) .await @@ -1235,10 +1213,7 @@ pub async fn list_user_roles_details( merchant_id: (requestor_role_info.get_entity_type() <= EntityType::Merchant) .then_some(&user_from_token.merchant_id), profile_id: (requestor_role_info.get_entity_type() <= EntityType::Profile) - .then_some(&user_from_token.profile_id) - .cloned() - .flatten() - .as_ref(), + .then_some(&user_from_token.profile_id), entity_id: None, version: None, status: None, @@ -2865,7 +2840,7 @@ pub async fn switch_profile_for_user_in_org_and_merchant( request: user_api::SwitchProfileRequest, user_from_token: auth::UserFromToken, ) -> UserResponse { - if user_from_token.profile_id == Some(request.profile_id.clone()) { + if user_from_token.profile_id == request.profile_id { return Err(UserErrors::InvalidRoleOperationWithMessage( "User switching to same profile".to_string(), ) diff --git a/crates/router/src/core/user_role.rs b/crates/router/src/core/user_role.rs index 95a8b7d51d1d..6641e553fd80 100644 --- a/crates/router/src/core/user_role.rs +++ b/crates/router/src/core/user_role.rs @@ -161,7 +161,7 @@ pub async fn update_user_role( user_to_be_updated.get_user_id(), &user_from_token.org_id, &user_from_token.merchant_id, - user_from_token.profile_id.as_ref(), + &user_from_token.profile_id, UserRoleVersion::V2, ) .await @@ -215,7 +215,7 @@ pub async fn update_user_role( user_to_be_updated.get_user_id(), &user_from_token.org_id, Some(&user_from_token.merchant_id), - user_from_token.profile_id.as_ref(), + Some(&user_from_token.profile_id), UserRoleUpdate::UpdateRole { role_id: req.role_id.clone(), modified_by: user_from_token.user_id.clone(), @@ -234,7 +234,7 @@ pub async fn update_user_role( user_to_be_updated.get_user_id(), &user_from_token.org_id, &user_from_token.merchant_id, - user_from_token.profile_id.as_ref(), + &user_from_token.profile_id, UserRoleVersion::V1, ) .await @@ -288,7 +288,7 @@ pub async fn update_user_role( user_to_be_updated.get_user_id(), &user_from_token.org_id, Some(&user_from_token.merchant_id), - user_from_token.profile_id.as_ref(), + Some(&user_from_token.profile_id), UserRoleUpdate::UpdateRole { role_id: req.role_id.clone(), modified_by: user_from_token.user_id, @@ -475,7 +475,7 @@ pub async fn delete_user_role( user_from_db.get_user_id(), &user_from_token.org_id, &user_from_token.merchant_id, - user_from_token.profile_id.as_ref(), + &user_from_token.profile_id, UserRoleVersion::V2, ) .await @@ -522,7 +522,7 @@ pub async fn delete_user_role( user_from_db.get_user_id(), &user_from_token.org_id, &user_from_token.merchant_id, - user_from_token.profile_id.as_ref(), + &user_from_token.profile_id, UserRoleVersion::V2, ) .await @@ -537,7 +537,7 @@ pub async fn delete_user_role( user_from_db.get_user_id(), &user_from_token.org_id, &user_from_token.merchant_id, - user_from_token.profile_id.as_ref(), + &user_from_token.profile_id, UserRoleVersion::V1, ) .await @@ -584,7 +584,7 @@ pub async fn delete_user_role( user_from_db.get_user_id(), &user_from_token.org_id, &user_from_token.merchant_id, - user_from_token.profile_id.as_ref(), + &user_from_token.profile_id, UserRoleVersion::V1, ) .await @@ -676,17 +676,13 @@ pub async fn list_users_in_lineage( .await? } EntityType::Profile => { - let Some(profile_id) = user_from_token.profile_id.as_ref() else { - return Err(UserErrors::JwtProfileIdMissing.into()); - }; - utils::user_role::fetch_user_roles_by_payload( &state, ListUserRolesByOrgIdPayload { user_id: None, org_id: &user_from_token.org_id, merchant_id: Some(&user_from_token.merchant_id), - profile_id: Some(profile_id), + profile_id: Some(&user_from_token.profile_id), version: None, limit: None, }, diff --git a/crates/router/src/db/kafka_store.rs b/crates/router/src/db/kafka_store.rs index d7d283819e5f..93181c66142f 100644 --- a/crates/router/src/db/kafka_store.rs +++ b/crates/router/src/db/kafka_store.rs @@ -3030,7 +3030,7 @@ impl UserRoleInterface for KafkaStore { user_id: &str, org_id: &id_type::OrganizationId, merchant_id: &id_type::MerchantId, - profile_id: Option<&id_type::ProfileId>, + profile_id: &id_type::ProfileId, version: enums::UserRoleVersion, ) -> CustomResult { self.diesel_store @@ -3070,7 +3070,7 @@ impl UserRoleInterface for KafkaStore { user_id: &str, org_id: &id_type::OrganizationId, merchant_id: &id_type::MerchantId, - profile_id: Option<&id_type::ProfileId>, + profile_id: &id_type::ProfileId, version: enums::UserRoleVersion, ) -> CustomResult { self.diesel_store diff --git a/crates/router/src/db/user_role.rs b/crates/router/src/db/user_role.rs index 2d9a949879aa..e4e564dc9a40 100644 --- a/crates/router/src/db/user_role.rs +++ b/crates/router/src/db/user_role.rs @@ -45,7 +45,7 @@ pub trait UserRoleInterface { user_id: &str, org_id: &id_type::OrganizationId, merchant_id: &id_type::MerchantId, - profile_id: Option<&id_type::ProfileId>, + profile_id: &id_type::ProfileId, version: enums::UserRoleVersion, ) -> CustomResult; @@ -64,7 +64,7 @@ pub trait UserRoleInterface { user_id: &str, org_id: &id_type::OrganizationId, merchant_id: &id_type::MerchantId, - profile_id: Option<&id_type::ProfileId>, + profile_id: &id_type::ProfileId, version: enums::UserRoleVersion, ) -> CustomResult; @@ -100,7 +100,7 @@ impl UserRoleInterface for Store { user_id: &str, org_id: &id_type::OrganizationId, merchant_id: &id_type::MerchantId, - profile_id: Option<&id_type::ProfileId>, + profile_id: &id_type::ProfileId, version: enums::UserRoleVersion, ) -> CustomResult { let conn = connection::pg_connection_read(self).await?; @@ -109,7 +109,7 @@ impl UserRoleInterface for Store { user_id.to_owned(), org_id.to_owned(), merchant_id.to_owned(), - profile_id.cloned(), + profile_id.to_owned(), version, ) .await @@ -146,7 +146,7 @@ impl UserRoleInterface for Store { user_id: &str, org_id: &id_type::OrganizationId, merchant_id: &id_type::MerchantId, - profile_id: Option<&id_type::ProfileId>, + profile_id: &id_type::ProfileId, version: enums::UserRoleVersion, ) -> CustomResult { let conn = connection::pg_connection_write(self).await?; @@ -155,7 +155,7 @@ impl UserRoleInterface for Store { user_id.to_owned(), org_id.to_owned(), merchant_id.to_owned(), - profile_id.cloned(), + profile_id.to_owned(), version, ) .await @@ -245,7 +245,7 @@ impl UserRoleInterface for MockDb { user_id: &str, org_id: &id_type::OrganizationId, merchant_id: &id_type::MerchantId, - profile_id: Option<&id_type::ProfileId>, + profile_id: &id_type::ProfileId, version: enums::UserRoleVersion, ) -> CustomResult { let user_roles = self.user_roles.lock().await; @@ -261,7 +261,7 @@ impl UserRoleInterface for MockDb { let profile_level_check = user_role.org_id.as_ref() == Some(org_id) && user_role.merchant_id.as_ref() == Some(merchant_id) - && user_role.profile_id.as_ref() == profile_id; + && user_role.profile_id.as_ref() == Some(profile_id); // Check if any condition matches and the version matches if user_role.user_id == user_id @@ -338,7 +338,7 @@ impl UserRoleInterface for MockDb { user_id: &str, org_id: &id_type::OrganizationId, merchant_id: &id_type::MerchantId, - profile_id: Option<&id_type::ProfileId>, + profile_id: &id_type::ProfileId, version: enums::UserRoleVersion, ) -> CustomResult { let mut user_roles = self.user_roles.lock().await; @@ -355,7 +355,7 @@ impl UserRoleInterface for MockDb { let profile_level_check = role.org_id.as_ref() == Some(org_id) && role.merchant_id.as_ref() == Some(merchant_id) - && role.profile_id.as_ref() == profile_id; + && role.profile_id.as_ref() == Some(profile_id); // Check if the user role matches the conditions and the version matches role.user_id == user_id diff --git a/crates/router/src/services/authentication.rs b/crates/router/src/services/authentication.rs index 43f04487416a..d4ac3bc846dd 100644 --- a/crates/router/src/services/authentication.rs +++ b/crates/router/src/services/authentication.rs @@ -87,7 +87,7 @@ pub struct AuthenticationDataWithUser { pub merchant_account: domain::MerchantAccount, pub key_store: domain::MerchantKeyStore, pub user: storage::User, - pub profile_id: Option, + pub profile_id: id_type::ProfileId, } #[derive(Clone, Debug, Eq, PartialEq, Serialize)] @@ -231,7 +231,7 @@ pub struct AuthToken { pub role_id: String, pub exp: u64, pub org_id: id_type::OrganizationId, - pub profile_id: Option, + pub profile_id: id_type::ProfileId, pub tenant_id: Option, } @@ -243,7 +243,7 @@ impl AuthToken { role_id: String, settings: &Settings, org_id: id_type::OrganizationId, - profile_id: Option, + profile_id: id_type::ProfileId, tenant_id: Option, ) -> UserResult { let exp_duration = std::time::Duration::from_secs(consts::JWT_TOKEN_TIME_IN_SECS); @@ -267,7 +267,7 @@ pub struct UserFromToken { pub merchant_id: id_type::MerchantId, pub role_id: String, pub org_id: id_type::OrganizationId, - pub profile_id: Option, + pub profile_id: id_type::ProfileId, pub tenant_id: Option, } @@ -1829,7 +1829,7 @@ where let auth = AuthenticationData { merchant_account: merchant, key_store, - profile_id: payload.profile_id, + profile_id: Some(payload.profile_id), }; Ok(( @@ -2077,7 +2077,7 @@ where let auth = AuthenticationData { merchant_account: merchant, key_store, - profile_id: payload.profile_id, + profile_id: Some(payload.profile_id), }; Ok(( auth.clone(), @@ -2253,11 +2253,7 @@ where return Err(report!(errors::ApiErrorResponse::InvalidJwtToken)); } - if payload - .profile_id - .as_ref() - .is_some_and(|profile_id| *profile_id != self.profile_id) - { + if payload.profile_id != self.profile_id { return Err(report!(errors::ApiErrorResponse::InvalidJwtToken)); } @@ -2290,7 +2286,7 @@ where let auth = AuthenticationData { merchant_account: merchant, key_store, - profile_id: payload.profile_id, + profile_id: Some(payload.profile_id), }; Ok(( auth.clone(), @@ -2354,30 +2350,14 @@ where .to_not_found_response(errors::ApiErrorResponse::InvalidJwtToken) .attach_printable("Failed to fetch merchant account for the merchant id")?; - if let Some(ref payload_profile_id) = payload.profile_id { - if *payload_profile_id != self.profile_id { - return Err(report!(errors::ApiErrorResponse::InvalidJwtToken)); - } else { - // if both of them are same then proceed with the profile id present in the request - let auth = AuthenticationData { - merchant_account: merchant, - key_store, - profile_id: Some(self.profile_id.clone()), - }; - Ok(( - auth.clone(), - AuthenticationType::MerchantJwt { - merchant_id: auth.merchant_account.get_id().clone(), - user_id: Some(payload.user_id), - }, - )) - } + if payload.profile_id != self.profile_id { + return Err(report!(errors::ApiErrorResponse::InvalidJwtToken)); } else { - // if profile_id is not present in the auth_layer itself then no change in behaviour + // if both of them are same then proceed with the profile id present in the request let auth = AuthenticationData { merchant_account: merchant, key_store, - profile_id: payload.profile_id, + profile_id: Some(self.profile_id.clone()), }; Ok(( auth.clone(), @@ -2527,7 +2507,7 @@ where let auth = AuthenticationData { merchant_account: merchant, key_store, - profile_id: payload.profile_id, + profile_id: Some(payload.profile_id), }; Ok(( auth, @@ -2663,7 +2643,7 @@ where let auth = AuthenticationData { merchant_account: merchant, key_store, - profile_id: payload.profile_id, + profile_id: Some(payload.profile_id), }; Ok(( (auth.clone(), payload.user_id.clone()), @@ -2781,7 +2761,7 @@ where let auth = AuthenticationData { merchant_account: merchant, key_store, - profile_id: payload.profile_id, + profile_id: Some(payload.profile_id), }; Ok(( auth.clone(), diff --git a/crates/router/src/types/domain/user/decision_manager.rs b/crates/router/src/types/domain/user/decision_manager.rs index 86f10f1ceb78..634c781da7fc 100644 --- a/crates/router/src/types/domain/user/decision_manager.rs +++ b/crates/router/src/types/domain/user/decision_manager.rs @@ -132,7 +132,7 @@ impl JWTFlow { .clone() .ok_or(report!(UserErrors::InternalServerError)) .attach_printable("org_id not found")?, - Some(profile_id), + profile_id, Some(user_role.tenant_id.clone()), ) .await diff --git a/crates/router/src/utils/user.rs b/crates/router/src/utils/user.rs index c4647907ff9b..8a9daefb2872 100644 --- a/crates/router/src/utils/user.rs +++ b/crates/router/src/utils/user.rs @@ -100,7 +100,7 @@ pub async fn generate_jwt_auth_token_with_attributes( role_id, &state.conf, org_id, - Some(profile_id), + profile_id, tenant_id, ) .await?; From 0e026b70b6502c4e82f3e8cccc5441deb472119e Mon Sep 17 00:00:00 2001 From: chikke srujan <121822803+srujanchikke@users.noreply.github.com> Date: Mon, 18 Nov 2024 17:01:53 +0530 Subject: [PATCH 3/6] feat(payments): [Payment links] add hide card nickname field config for secure payment links (#6554) Co-authored-by: Chikke Srujan Co-authored-by: hyperswitch-bot[bot] <148525504+hyperswitch-bot[bot]@users.noreply.github.com> --- api-reference-v2/openapi_spec.json | 14 ++++++++- api-reference/openapi_spec.json | 14 ++++++++- crates/api_models/src/admin.rs | 5 ++++ crates/api_models/src/payments.rs | 2 ++ crates/common_utils/src/consts.rs | 25 ---------------- crates/diesel_models/src/business_profile.rs | 1 + crates/diesel_models/src/payment_intent.rs | 2 ++ crates/hyperswitch_domain_models/src/lib.rs | 3 ++ crates/router/src/consts.rs | 28 +++++++++++++++++ crates/router/src/core/payment_link.rs | 30 ++++++++++++++----- .../payment_link_initiator.js | 1 + .../secure_payment_link_initiator.js | 2 ++ .../router/src/core/payments/transformers.rs | 2 ++ crates/router/src/types/transformers.rs | 2 ++ 14 files changed, 96 insertions(+), 35 deletions(-) diff --git a/api-reference-v2/openapi_spec.json b/api-reference-v2/openapi_spec.json index d2fa50d86293..f00998f154f4 100644 --- a/api-reference-v2/openapi_spec.json +++ b/api-reference-v2/openapi_spec.json @@ -11948,7 +11948,8 @@ "seller_name", "sdk_layout", "display_sdk_only", - "enabled_saved_payment_method" + "enabled_saved_payment_method", + "hide_card_nickname_field" ], "properties": { "theme": { @@ -11975,6 +11976,10 @@ "type": "boolean", "description": "Enable saved payment method option for payment link" }, + "hide_card_nickname_field": { + "type": "boolean", + "description": "Hide card nickname field option for payment link" + }, "allowed_domains": { "type": "array", "items": { @@ -12039,6 +12044,13 @@ "example": true, "nullable": true }, + "hide_card_nickname_field": { + "type": "boolean", + "description": "Hide card nickname field option for payment link", + "default": false, + "example": true, + "nullable": true + }, "transaction_details": { "type": "array", "items": { diff --git a/api-reference/openapi_spec.json b/api-reference/openapi_spec.json index 34d5be1b0ec0..d4c5717d2156 100644 --- a/api-reference/openapi_spec.json +++ b/api-reference/openapi_spec.json @@ -15074,7 +15074,8 @@ "seller_name", "sdk_layout", "display_sdk_only", - "enabled_saved_payment_method" + "enabled_saved_payment_method", + "hide_card_nickname_field" ], "properties": { "theme": { @@ -15101,6 +15102,10 @@ "type": "boolean", "description": "Enable saved payment method option for payment link" }, + "hide_card_nickname_field": { + "type": "boolean", + "description": "Hide card nickname field option for payment link" + }, "allowed_domains": { "type": "array", "items": { @@ -15165,6 +15170,13 @@ "example": true, "nullable": true }, + "hide_card_nickname_field": { + "type": "boolean", + "description": "Hide card nickname field option for payment link", + "default": false, + "example": true, + "nullable": true + }, "transaction_details": { "type": "array", "items": { diff --git a/crates/api_models/src/admin.rs b/crates/api_models/src/admin.rs index 7f5ae1556d27..d80a4ea11b4c 100644 --- a/crates/api_models/src/admin.rs +++ b/crates/api_models/src/admin.rs @@ -2692,6 +2692,9 @@ pub struct PaymentLinkConfigRequest { /// Enable saved payment method option for payment link #[schema(default = false, example = true)] pub enabled_saved_payment_method: Option, + /// Hide card nickname field option for payment link + #[schema(default = false, example = true)] + pub hide_card_nickname_field: Option, /// Dynamic details related to merchant to be rendered in payment link pub transaction_details: Option>, } @@ -2735,6 +2738,8 @@ pub struct PaymentLinkConfig { pub display_sdk_only: bool, /// Enable saved payment method option for payment link pub enabled_saved_payment_method: bool, + /// Hide card nickname field option for payment link + pub hide_card_nickname_field: bool, /// A list of allowed domains (glob patterns) where this link can be embedded / opened from pub allowed_domains: Option>, /// Dynamic details related to merchant to be rendered in payment link diff --git a/crates/api_models/src/payments.rs b/crates/api_models/src/payments.rs index 75dda21097ad..37389308aa39 100644 --- a/crates/api_models/src/payments.rs +++ b/crates/api_models/src/payments.rs @@ -6556,6 +6556,7 @@ pub struct PaymentLinkDetails { pub merchant_description: Option, pub sdk_layout: String, pub display_sdk_only: bool, + pub hide_card_nickname_field: bool, pub locale: Option, pub transaction_details: Option>, } @@ -6563,6 +6564,7 @@ pub struct PaymentLinkDetails { #[derive(Debug, serde::Serialize, Clone)] pub struct SecurePaymentLinkDetails { pub enabled_saved_payment_method: bool, + pub hide_card_nickname_field: bool, #[serde(flatten)] pub payment_link_details: PaymentLinkDetails, } diff --git a/crates/common_utils/src/consts.rs b/crates/common_utils/src/consts.rs index a50555ced920..b43efcb1feb1 100644 --- a/crates/common_utils/src/consts.rs +++ b/crates/common_utils/src/consts.rs @@ -1,7 +1,5 @@ //! Commonly used constants -use std::collections::HashSet; - /// Number of characters in a generated ID pub const ID_LENGTH: usize = 20; @@ -51,26 +49,12 @@ pub const SURCHARGE_PERCENTAGE_PRECISION_LENGTH: u8 = 2; /// Header Key for application overhead of a request pub const X_HS_LATENCY: &str = "x-hs-latency"; -/// Default Payment Link Background color -pub const DEFAULT_BACKGROUND_COLOR: &str = "#212E46"; - -/// Default product Img Link -pub const DEFAULT_PRODUCT_IMG: &str = - "https://live.hyperswitch.io/payment-link-assets/cart_placeholder.png"; - -/// Default Merchant Logo Link -pub const DEFAULT_MERCHANT_LOGO: &str = - "https://live.hyperswitch.io/payment-link-assets/Merchant_placeholder.png"; - /// Redirect url for Prophetpay pub const PROPHETPAY_REDIRECT_URL: &str = "https://ccm-thirdparty.cps.golf/hp/tokenize/"; /// Variable which store the card token for Prophetpay pub const PROPHETPAY_TOKEN: &str = "cctoken"; -/// Default SDK Layout -pub const DEFAULT_SDK_LAYOUT: &str = "tabs"; - /// Payment intent default client secret expiry (in seconds) pub const DEFAULT_SESSION_EXPIRY: i64 = 15 * 60; @@ -80,15 +64,6 @@ pub const DEFAULT_INTENT_FULFILLMENT_TIME: i64 = 15 * 60; /// Payment order fulfillment time (in seconds) pub const DEFAULT_ORDER_FULFILLMENT_TIME: i64 = 15 * 60; -/// Default bool for Display sdk only -pub const DEFAULT_DISPLAY_SDK_ONLY: bool = false; - -/// Default bool to enable saved payment method -pub const DEFAULT_ENABLE_SAVED_PAYMENT_METHOD: bool = false; - -/// Default allowed domains for payment links -pub const DEFAULT_ALLOWED_DOMAINS: Option> = None; - /// Default ttl for Extended card info in redis (in seconds) pub const DEFAULT_TTL_FOR_EXTENDED_CARD_INFO: u16 = 15 * 60; diff --git a/crates/diesel_models/src/business_profile.rs b/crates/diesel_models/src/business_profile.rs index 05c124c4c9cf..f4c7b86850eb 100644 --- a/crates/diesel_models/src/business_profile.rs +++ b/crates/diesel_models/src/business_profile.rs @@ -542,6 +542,7 @@ pub struct PaymentLinkConfigRequest { pub sdk_layout: Option, pub display_sdk_only: Option, pub enabled_saved_payment_method: Option, + pub hide_card_nickname_field: Option, } common_utils::impl_to_sql_from_sql_json!(BusinessPaymentLinkConfig); diff --git a/crates/diesel_models/src/payment_intent.rs b/crates/diesel_models/src/payment_intent.rs index 18248048110a..26cb0b8c8a84 100644 --- a/crates/diesel_models/src/payment_intent.rs +++ b/crates/diesel_models/src/payment_intent.rs @@ -152,6 +152,8 @@ pub struct PaymentLinkConfigRequestForPayments { pub display_sdk_only: Option, /// Enable saved payment method option for payment link pub enabled_saved_payment_method: Option, + /// Hide card nickname field option for payment link + pub hide_card_nickname_field: Option, /// Dynamic details related to merchant to be rendered in payment link pub transaction_details: Option>, } diff --git a/crates/hyperswitch_domain_models/src/lib.rs b/crates/hyperswitch_domain_models/src/lib.rs index 02795481ad9c..386e0f01f383 100644 --- a/crates/hyperswitch_domain_models/src/lib.rs +++ b/crates/hyperswitch_domain_models/src/lib.rs @@ -190,6 +190,7 @@ impl ApiModelToDieselModelConvertor sdk_layout: item.sdk_layout, display_sdk_only: item.display_sdk_only, enabled_saved_payment_method: item.enabled_saved_payment_method, + hide_card_nickname_field: item.hide_card_nickname_field, transaction_details: item.transaction_details.map(|transaction_details| { transaction_details .into_iter() @@ -210,6 +211,7 @@ impl ApiModelToDieselModelConvertor sdk_layout, display_sdk_only, enabled_saved_payment_method, + hide_card_nickname_field, transaction_details, } = self; api_models::admin::PaymentLinkConfigRequest { @@ -219,6 +221,7 @@ impl ApiModelToDieselModelConvertor sdk_layout, display_sdk_only, enabled_saved_payment_method, + hide_card_nickname_field, transaction_details: transaction_details.map(|transaction_details| { transaction_details .into_iter() diff --git a/crates/router/src/consts.rs b/crates/router/src/consts.rs index 3f4faedff81b..51385593e9d1 100644 --- a/crates/router/src/consts.rs +++ b/crates/router/src/consts.rs @@ -3,6 +3,8 @@ pub mod opensearch; pub mod user; pub mod user_role; +use std::collections::HashSet; + use common_utils::consts; pub use hyperswitch_interfaces::consts::{NO_ERROR_CODE, NO_ERROR_MESSAGE}; // ID generation @@ -145,6 +147,32 @@ pub const RECON_FEATURE_TAG: &str = "RECONCILIATION AND SETTLEMENT"; // Length of the unique reference ID generated for connector mandate requests pub const CONNECTOR_MANDATE_REQUEST_REFERENCE_ID_LENGTH: usize = 18; +/// Default allowed domains for payment links +pub const DEFAULT_ALLOWED_DOMAINS: Option> = None; + +/// Default hide card nickname field +pub const DEFAULT_HIDE_CARD_NICKNAME_FIELD: bool = false; + +/// Default bool for Display sdk only +pub const DEFAULT_DISPLAY_SDK_ONLY: bool = false; + +/// Default bool to enable saved payment method +pub const DEFAULT_ENABLE_SAVED_PAYMENT_METHOD: bool = false; + +/// Default Merchant Logo Link +pub const DEFAULT_MERCHANT_LOGO: &str = + "https://live.hyperswitch.io/payment-link-assets/Merchant_placeholder.png"; + +/// Default Payment Link Background color +pub const DEFAULT_BACKGROUND_COLOR: &str = "#212E46"; + +/// Default product Img Link +pub const DEFAULT_PRODUCT_IMG: &str = + "https://live.hyperswitch.io/payment-link-assets/cart_placeholder.png"; + +/// Default SDK Layout +pub const DEFAULT_SDK_LAYOUT: &str = "tabs"; + /// Vault Add request url #[cfg(all(feature = "v2", feature = "payment_methods_v2"))] pub const ADD_VAULT_REQUEST_URL: &str = "/vault/add"; diff --git a/crates/router/src/core/payment_link.rs b/crates/router/src/core/payment_link.rs index 09a2457197cb..91829e56f2cc 100644 --- a/crates/router/src/core/payment_link.rs +++ b/crates/router/src/core/payment_link.rs @@ -5,11 +5,7 @@ use api_models::{ payments::{PaymentLinkData, PaymentLinkStatusWrap}, }; use common_utils::{ - consts::{ - DEFAULT_ALLOWED_DOMAINS, DEFAULT_BACKGROUND_COLOR, DEFAULT_DISPLAY_SDK_ONLY, - DEFAULT_ENABLE_SAVED_PAYMENT_METHOD, DEFAULT_LOCALE, DEFAULT_MERCHANT_LOGO, - DEFAULT_PRODUCT_IMG, DEFAULT_SDK_LAYOUT, DEFAULT_SESSION_EXPIRY, - }, + consts::{DEFAULT_LOCALE, DEFAULT_SESSION_EXPIRY}, ext_traits::{AsyncExt, OptionExt, ValueExt}, types::{AmountConvertor, StringMajorUnitForCore}, }; @@ -25,7 +21,11 @@ use super::{ payments::helpers, }; use crate::{ - consts, + consts::{ + self, DEFAULT_ALLOWED_DOMAINS, DEFAULT_BACKGROUND_COLOR, DEFAULT_DISPLAY_SDK_ONLY, + DEFAULT_ENABLE_SAVED_PAYMENT_METHOD, DEFAULT_HIDE_CARD_NICKNAME_FIELD, + DEFAULT_MERCHANT_LOGO, DEFAULT_PRODUCT_IMG, DEFAULT_SDK_LAYOUT, + }, errors::RouterResponse, get_payment_link_config_value, get_payment_link_config_value_based_on_priority, headers::ACCEPT_LANGUAGE, @@ -125,6 +125,7 @@ pub async fn form_payment_link_data( sdk_layout: DEFAULT_SDK_LAYOUT.to_owned(), display_sdk_only: DEFAULT_DISPLAY_SDK_ONLY, enabled_saved_payment_method: DEFAULT_ENABLE_SAVED_PAYMENT_METHOD, + hide_card_nickname_field: DEFAULT_HIDE_CARD_NICKNAME_FIELD, allowed_domains: DEFAULT_ALLOWED_DOMAINS, transaction_details: None, } @@ -265,6 +266,7 @@ pub async fn form_payment_link_data( merchant_description: payment_intent.description, sdk_layout: payment_link_config.sdk_layout.clone(), display_sdk_only: payment_link_config.display_sdk_only, + hide_card_nickname_field: payment_link_config.hide_card_nickname_field, locale, transaction_details: payment_link_config.transaction_details.clone(), }; @@ -322,6 +324,7 @@ pub async fn initiate_secure_payment_link_flow( PaymentLinkData::PaymentLinkDetails(link_details) => { let secure_payment_link_details = api_models::payments::SecurePaymentLinkDetails { enabled_saved_payment_method: payment_link_config.enabled_saved_payment_method, + hide_card_nickname_field: payment_link_config.hide_card_nickname_field, payment_link_details: *link_details.to_owned(), }; let js_script = format!( @@ -607,7 +610,15 @@ pub fn get_payment_link_config_based_on_priority( (default_domain_name, None, None) }; - let (theme, logo, seller_name, sdk_layout, display_sdk_only, enabled_saved_payment_method) = get_payment_link_config_value!( + let ( + theme, + logo, + seller_name, + sdk_layout, + display_sdk_only, + enabled_saved_payment_method, + hide_card_nickname_field, + ) = get_payment_link_config_value!( payment_create_link_config, business_theme_configs, (theme, DEFAULT_BACKGROUND_COLOR.to_string()), @@ -618,7 +629,8 @@ pub fn get_payment_link_config_based_on_priority( ( enabled_saved_payment_method, DEFAULT_ENABLE_SAVED_PAYMENT_METHOD - ) + ), + (hide_card_nickname_field, DEFAULT_HIDE_CARD_NICKNAME_FIELD) ); let payment_link_config = PaymentLinkConfig { theme, @@ -627,6 +639,7 @@ pub fn get_payment_link_config_based_on_priority( sdk_layout, display_sdk_only, enabled_saved_payment_method, + hide_card_nickname_field, allowed_domains, transaction_details: payment_create_link_config .and_then(|payment_link_config| payment_link_config.theme_config.transaction_details), @@ -729,6 +742,7 @@ pub async fn get_payment_link_status( sdk_layout: DEFAULT_SDK_LAYOUT.to_owned(), display_sdk_only: DEFAULT_DISPLAY_SDK_ONLY, enabled_saved_payment_method: DEFAULT_ENABLE_SAVED_PAYMENT_METHOD, + hide_card_nickname_field: DEFAULT_HIDE_CARD_NICKNAME_FIELD, allowed_domains: DEFAULT_ALLOWED_DOMAINS, transaction_details: None, } diff --git a/crates/router/src/core/payment_link/payment_link_initiate/payment_link_initiator.js b/crates/router/src/core/payment_link/payment_link_initiate/payment_link_initiator.js index 8d1183a2535d..1264915592d6 100644 --- a/crates/router/src/core/payment_link/payment_link_initiate/payment_link_initiator.js +++ b/crates/router/src/core/payment_link/payment_link_initiate/payment_link_initiator.js @@ -56,6 +56,7 @@ function initializeSDK() { height: 55, }, }, + hideCardNicknameField: false, }; // @ts-ignore unifiedCheckout = widgets.create("payment", unifiedCheckoutOptions); diff --git a/crates/router/src/core/payment_link/payment_link_initiate/secure_payment_link_initiator.js b/crates/router/src/core/payment_link/payment_link_initiate/secure_payment_link_initiator.js index c901148bbfae..5080970ce3c5 100644 --- a/crates/router/src/core/payment_link/payment_link_initiate/secure_payment_link_initiator.js +++ b/crates/router/src/core/payment_link/payment_link_initiate/secure_payment_link_initiator.js @@ -63,6 +63,7 @@ if (!isFramed) { : paymentDetails.sdk_layout; var enableSavedPaymentMethod = paymentDetails.enabled_saved_payment_method; + var hideCardNicknameField = paymentDetails.hide_card_nickname_field; var unifiedCheckoutOptions = { displaySavedPaymentMethodsCheckbox: enableSavedPaymentMethod, displaySavedPaymentMethods: enableSavedPaymentMethod, @@ -79,6 +80,7 @@ if (!isFramed) { height: 55, }, }, + hideCardNicknameField: hideCardNicknameField, }; // @ts-ignore unifiedCheckout = widgets.create("payment", unifiedCheckoutOptions); diff --git a/crates/router/src/core/payments/transformers.rs b/crates/router/src/core/payments/transformers.rs index b09034e28cb3..8a0983eccab0 100644 --- a/crates/router/src/core/payments/transformers.rs +++ b/crates/router/src/core/payments/transformers.rs @@ -3518,6 +3518,7 @@ impl ForeignFrom sdk_layout: config.sdk_layout, display_sdk_only: config.display_sdk_only, enabled_saved_payment_method: config.enabled_saved_payment_method, + hide_card_nickname_field: config.hide_card_nickname_field, transaction_details: config.transaction_details.map(|transaction_details| { transaction_details .iter() @@ -3570,6 +3571,7 @@ impl ForeignFrom sdk_layout: config.sdk_layout, display_sdk_only: config.display_sdk_only, enabled_saved_payment_method: config.enabled_saved_payment_method, + hide_card_nickname_field: config.hide_card_nickname_field, transaction_details: config.transaction_details.map(|transaction_details| { transaction_details .iter() diff --git a/crates/router/src/types/transformers.rs b/crates/router/src/types/transformers.rs index c97084a22977..36865f5ce09b 100644 --- a/crates/router/src/types/transformers.rs +++ b/crates/router/src/types/transformers.rs @@ -1987,6 +1987,7 @@ impl ForeignFrom sdk_layout: item.sdk_layout, display_sdk_only: item.display_sdk_only, enabled_saved_payment_method: item.enabled_saved_payment_method, + hide_card_nickname_field: item.hide_card_nickname_field, } } } @@ -2002,6 +2003,7 @@ impl ForeignFrom sdk_layout: item.sdk_layout, display_sdk_only: item.display_sdk_only, enabled_saved_payment_method: item.enabled_saved_payment_method, + hide_card_nickname_field: item.hide_card_nickname_field, transaction_details: None, } } From 053f8109302a98e6b6d30d957b2af618ea73055f Mon Sep 17 00:00:00 2001 From: Prajjwal Kumar Date: Mon, 18 Nov 2024 18:53:50 +0530 Subject: [PATCH 4/6] refactor(core): add profile_id for default_fallback api (#6546) Co-authored-by: hyperswitch-bot[bot] <148525504+hyperswitch-bot[bot]@users.noreply.github.com> --- crates/router/src/core/routing.rs | 26 ++++++++++++++----------- crates/router/src/routes/app.rs | 30 +++++++++-------------------- crates/router/src/routes/routing.rs | 16 ++++++--------- 3 files changed, 30 insertions(+), 42 deletions(-) diff --git a/crates/router/src/core/routing.rs b/crates/router/src/core/routing.rs index 0bd38918ee77..94eb9bd741ee 100644 --- a/crates/router/src/core/routing.rs +++ b/crates/router/src/core/routing.rs @@ -909,26 +909,30 @@ pub async fn retrieve_default_fallback_algorithm_for_profile( } #[cfg(feature = "v1")] - pub async fn retrieve_default_routing_config( state: SessionState, + profile_id: Option, merchant_account: domain::MerchantAccount, transaction_type: &enums::TransactionType, ) -> RouterResponse> { metrics::ROUTING_RETRIEVE_DEFAULT_CONFIG.add(&metrics::CONTEXT, 1, &[]); let db = state.store.as_ref(); + let id = profile_id + .map(|profile_id| profile_id.get_string_repr().to_owned()) + .unwrap_or_else(|| merchant_account.get_id().get_string_repr().to_string()); - helpers::get_merchant_default_config( - db, - merchant_account.get_id().get_string_repr(), - transaction_type, - ) - .await - .map(|conn_choice| { - metrics::ROUTING_RETRIEVE_DEFAULT_CONFIG_SUCCESS_RESPONSE.add(&metrics::CONTEXT, 1, &[]); - service_api::ApplicationResponse::Json(conn_choice) - }) + helpers::get_merchant_default_config(db, &id, transaction_type) + .await + .map(|conn_choice| { + metrics::ROUTING_RETRIEVE_DEFAULT_CONFIG_SUCCESS_RESPONSE.add( + &metrics::CONTEXT, + 1, + &[], + ); + service_api::ApplicationResponse::Json(conn_choice) + }) } + #[cfg(feature = "v2")] pub async fn retrieve_routing_config_under_profile( state: SessionState, diff --git a/crates/router/src/routes/app.rs b/crates/router/src/routes/app.rs index ebf6c7d384d2..892dfa62a369 100644 --- a/crates/router/src/routes/app.rs +++ b/crates/router/src/routes/app.rs @@ -758,22 +758,14 @@ impl Routing { }, ))) .service( - web::resource("/default") - .route(web::get().to(|state, req| { - routing::routing_retrieve_default_config( - state, - req, - &TransactionType::Payment, - ) - })) - .route(web::post().to(|state, req, payload| { - routing::routing_update_default_config( - state, - req, - payload, - &TransactionType::Payment, - ) - })), + web::resource("/default").route(web::post().to(|state, req, payload| { + routing::routing_update_default_config( + state, + req, + payload, + &TransactionType::Payment, + ) + })), ) .service( web::resource("/deactivate").route(web::post().to(|state, req, payload| { @@ -807,11 +799,7 @@ impl Routing { ) .service( web::resource("/default/profile").route(web::get().to(|state, req| { - routing::routing_retrieve_default_config_for_profiles( - state, - req, - &TransactionType::Payment, - ) + routing::routing_retrieve_default_config(state, req, &TransactionType::Payment) })), ); diff --git a/crates/router/src/routes/routing.rs b/crates/router/src/routes/routing.rs index f0269bd029ab..b861b54b5b52 100644 --- a/crates/router/src/routes/routing.rs +++ b/crates/router/src/routes/routing.rs @@ -565,17 +565,13 @@ pub async fn routing_retrieve_default_config( &req, (), |state, auth: auth::AuthenticationData, _, _| { - routing::retrieve_default_routing_config(state, auth.merchant_account, transaction_type) + routing::retrieve_default_routing_config( + state, + auth.profile_id, + auth.merchant_account, + transaction_type, + ) }, - #[cfg(not(feature = "release"))] - auth::auth_type( - &auth::HeaderAuth(auth::ApiKeyAuth), - &auth::JWTAuth { - permission: Permission::ProfileRoutingRead, - }, - req.headers(), - ), - #[cfg(feature = "release")] &auth::JWTAuth { permission: Permission::ProfileRoutingRead, }, From 6881ce2ed3d11006c33fef9863107f0d823ebddb Mon Sep 17 00:00:00 2001 From: Debarati Ghatak <88573135+cookieg13@users.noreply.github.com> Date: Mon, 18 Nov 2024 19:18:51 +0530 Subject: [PATCH 5/6] feat(connector): [Novalnet] Add support for disputes (#6560) --- .../src/connectors/novalnet.rs | 42 ++++++++++++++++++- .../src/connectors/novalnet/transformers.rs | 23 ++++++++++ 2 files changed, 64 insertions(+), 1 deletion(-) diff --git a/crates/hyperswitch_connectors/src/connectors/novalnet.rs b/crates/hyperswitch_connectors/src/connectors/novalnet.rs index 7450a48884a7..8331e229acb9 100644 --- a/crates/hyperswitch_connectors/src/connectors/novalnet.rs +++ b/crates/hyperswitch_connectors/src/connectors/novalnet.rs @@ -37,7 +37,7 @@ use hyperswitch_interfaces::{ ConnectorValidation, }, configs::Connectors, - errors, + disputes, errors, events::connector_api_logs::ConnectorEvent, types::{self, Response}, webhooks, @@ -877,4 +877,44 @@ impl webhooks::IncomingWebhook for Novalnet { .change_context(errors::ConnectorError::WebhookResourceObjectNotFound)?; Ok(Box::new(notif)) } + + fn get_dispute_details( + &self, + request: &webhooks::IncomingWebhookRequestDetails<'_>, + ) -> CustomResult { + let notif: transformers::NovalnetWebhookNotificationResponse = + get_webhook_object_from_body(request.body) + .change_context(errors::ConnectorError::WebhookBodyDecodingFailed)?; + let (amount, currency, reason, reason_code) = match notif.transaction { + novalnet::NovalnetWebhookTransactionData::CaptureTransactionData(data) => { + (data.amount, data.currency, None, None) + } + novalnet::NovalnetWebhookTransactionData::CancelTransactionData(data) => { + (data.amount, data.currency, None, None) + } + + novalnet::NovalnetWebhookTransactionData::RefundsTransactionData(data) => { + (data.amount, data.currency, None, None) + } + + novalnet::NovalnetWebhookTransactionData::SyncTransactionData(data) => { + (data.amount, data.currency, data.reason, data.reason_code) + } + }; + + let dispute_status = + novalnet::get_novalnet_dispute_status(notif.event.event_type).to_string(); + Ok(disputes::DisputePayload { + amount: novalnet::option_to_result(amount)?.to_string(), + currency: novalnet::option_to_result(currency)?.to_string(), + dispute_stage: api_models::enums::DisputeStage::Dispute, + connector_dispute_id: notif.event.tid.to_string(), + connector_reason: reason, + connector_reason_code: reason_code, + challenge_required_by: None, + connector_status: dispute_status, + created_at: None, + updated_at: None, + }) + } } diff --git a/crates/hyperswitch_connectors/src/connectors/novalnet/transformers.rs b/crates/hyperswitch_connectors/src/connectors/novalnet/transformers.rs index 313e35eeb0e6..1888511e9ba2 100644 --- a/crates/hyperswitch_connectors/src/connectors/novalnet/transformers.rs +++ b/crates/hyperswitch_connectors/src/connectors/novalnet/transformers.rs @@ -1294,6 +1294,8 @@ pub enum WebhookEventType { TransactionCapture, TransactionCancel, TransactionRefund, + Chargeback, + Credit, } #[derive(Serialize, Deserialize, Debug)] @@ -1356,9 +1358,30 @@ pub fn get_incoming_webhook_event( } _ => IncomingWebhookEvent::RefundFailure, }, + WebhookEventType::Chargeback => IncomingWebhookEvent::DisputeOpened, + WebhookEventType::Credit => IncomingWebhookEvent::DisputeWon, } } pub fn reverse_string(s: &str) -> String { s.chars().rev().collect() } + +#[derive(Display, Debug, Serialize, Deserialize)] +pub enum WebhookDisputeStatus { + DisputeOpened, + DisputeWon, + Unknown, +} + +pub fn get_novalnet_dispute_status(status: WebhookEventType) -> WebhookDisputeStatus { + match status { + WebhookEventType::Chargeback => WebhookDisputeStatus::DisputeOpened, + WebhookEventType::Credit => WebhookDisputeStatus::DisputeWon, + _ => WebhookDisputeStatus::Unknown, + } +} + +pub fn option_to_result(opt: Option) -> Result { + opt.ok_or(errors::ConnectorError::WebhookBodyDecodingFailed) +} From 65bf75a75e1e7d705de3ee3e9080a7a86099ffc3 Mon Sep 17 00:00:00 2001 From: github-actions <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 19 Nov 2024 00:21:48 +0000 Subject: [PATCH 6/6] chore(version): 2024.11.19.0 --- CHANGELOG.md | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7daa3cd109bb..640a259efa9c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,22 @@ All notable changes to HyperSwitch will be documented here. - - - +## 2024.11.19.0 + +### Features + +- **connector:** [Novalnet] Add support for disputes ([#6560](https://github.com/juspay/hyperswitch/pull/6560)) ([`6881ce2`](https://github.com/juspay/hyperswitch/commit/6881ce2ed3d11006c33fef9863107f0d823ebddb)) +- **payments:** [Payment links] add hide card nickname field config for secure payment links ([#6554](https://github.com/juspay/hyperswitch/pull/6554)) ([`0e026b7`](https://github.com/juspay/hyperswitch/commit/0e026b70b6502c4e82f3e8cccc5441deb472119e)) + +### Refactors + +- **core:** Add profile_id for default_fallback api ([#6546](https://github.com/juspay/hyperswitch/pull/6546)) ([`053f810`](https://github.com/juspay/hyperswitch/commit/053f8109302a98e6b6d30d957b2af618ea73055f)) +- **users:** Make `profile_id` in the JWT non-optional ([#6537](https://github.com/juspay/hyperswitch/pull/6537)) ([`d32397f`](https://github.com/juspay/hyperswitch/commit/d32397f060731f51a15634e221117a554b8b3721)) + +**Full Changelog:** [`2024.11.18.0...2024.11.19.0`](https://github.com/juspay/hyperswitch/compare/2024.11.18.0...2024.11.19.0) + +- - - + ## 2024.11.18.0 ### Features