mirror of
				https://github.com/juspay/hyperswitch.git
				synced 2025-10-31 10:06:32 +08:00 
			
		
		
		
	feat(router): add api to migrate card from basilisk to rust (#2853)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
This commit is contained in:
		| @ -112,6 +112,7 @@ kms_encrypted_recon_admin_api_key = ""     # Base64-encoded (KMS encrypted) ciph | |||||||
| # like card details | # like card details | ||||||
| [locker] | [locker] | ||||||
| host = ""                      # Locker host | host = ""                      # Locker host | ||||||
|  | host_rs = ""                   # Rust Locker host  | ||||||
| mock_locker = true             # Emulate a locker locally using Postgres | mock_locker = true             # Emulate a locker locally using Postgres | ||||||
| basilisk_host = ""             # Basilisk host | basilisk_host = ""             # Basilisk host | ||||||
| locker_signing_key_id = "1"    # Key_id to sign basilisk hs locker | locker_signing_key_id = "1"    # Key_id to sign basilisk hs locker | ||||||
| @ -130,6 +131,7 @@ locker_encryption_key2 = "" # public key 2 in pem format, corresponding private | |||||||
| locker_decryption_key1 = "" # private key 1 in pem format, corresponding public key in basilisk | locker_decryption_key1 = "" # private key 1 in pem format, corresponding public key in basilisk | ||||||
| locker_decryption_key2 = "" # private key 2 in pem format, corresponding public key in basilisk | locker_decryption_key2 = "" # private key 2 in pem format, corresponding public key in basilisk | ||||||
| vault_encryption_key = ""   # public key in pem format, corresponding private key in basilisk-hs | vault_encryption_key = ""   # public key in pem format, corresponding private key in basilisk-hs | ||||||
|  | rust_locker_encryption_key = "" # public key in pem format, corresponding private key in rust locker | ||||||
| vault_private_key = ""      # private key in pem format, corresponding public key in basilisk-hs | vault_private_key = ""      # private key in pem format, corresponding public key in basilisk-hs | ||||||
|  |  | ||||||
|  |  | ||||||
|  | |||||||
| @ -48,6 +48,7 @@ applepay_endpoint = "DOMAIN SPECIFIC ENDPOINT" | |||||||
|  |  | ||||||
| [locker] | [locker] | ||||||
| host = "" | host = "" | ||||||
|  | host_rs = "" | ||||||
| mock_locker = true | mock_locker = true | ||||||
| basilisk_host = "" | basilisk_host = "" | ||||||
|  |  | ||||||
| @ -59,6 +60,7 @@ locker_encryption_key2 = "" | |||||||
| locker_decryption_key1 = "" | locker_decryption_key1 = "" | ||||||
| locker_decryption_key2 = "" | locker_decryption_key2 = "" | ||||||
| vault_encryption_key = "" | vault_encryption_key = "" | ||||||
|  | rust_locker_encryption_key = "" | ||||||
| vault_private_key = "" | vault_private_key = "" | ||||||
| tunnel_private_key = "" | tunnel_private_key = "" | ||||||
|  |  | ||||||
|  | |||||||
| @ -44,6 +44,7 @@ recon_admin_api_key = "recon_test_admin" | |||||||
|  |  | ||||||
| [locker] | [locker] | ||||||
| host = "" | host = "" | ||||||
|  | host_rs = "" | ||||||
| mock_locker = true | mock_locker = true | ||||||
| basilisk_host = "" | basilisk_host = "" | ||||||
|  |  | ||||||
| @ -55,6 +56,7 @@ locker_encryption_key2 = "" | |||||||
| locker_decryption_key1 = "" | locker_decryption_key1 = "" | ||||||
| locker_decryption_key2 = "" | locker_decryption_key2 = "" | ||||||
| vault_encryption_key = "" | vault_encryption_key = "" | ||||||
|  | rust_locker_encryption_key = "" | ||||||
| vault_private_key = "" | vault_private_key = "" | ||||||
|  |  | ||||||
| [redis] | [redis] | ||||||
|  | |||||||
| @ -562,3 +562,9 @@ pub enum RetryAction { | |||||||
|     /// Denotes that the payment is requeued |     /// Denotes that the payment is requeued | ||||||
|     Requeue, |     Requeue, | ||||||
| } | } | ||||||
|  |  | ||||||
|  | #[derive(Clone, Copy)] | ||||||
|  | pub enum LockerChoice { | ||||||
|  |     Basilisk, | ||||||
|  |     Tartarus, | ||||||
|  | } | ||||||
|  | |||||||
| @ -1,5 +1,6 @@ | |||||||
| pub mod customer; | pub mod customer; | ||||||
| pub mod gsm; | pub mod gsm; | ||||||
|  | mod locker_migration; | ||||||
| pub mod payment; | pub mod payment; | ||||||
| #[cfg(feature = "payouts")] | #[cfg(feature = "payouts")] | ||||||
| pub mod payouts; | pub mod payouts; | ||||||
|  | |||||||
							
								
								
									
										9
									
								
								crates/api_models/src/events/locker_migration.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								crates/api_models/src/events/locker_migration.rs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,9 @@ | |||||||
|  | use common_utils::events::ApiEventMetric; | ||||||
|  |  | ||||||
|  | use crate::locker_migration::MigrateCardResponse; | ||||||
|  |  | ||||||
|  | impl ApiEventMetric for MigrateCardResponse { | ||||||
|  |     fn get_api_event_type(&self) -> Option<common_utils::events::ApiEventsType> { | ||||||
|  |         Some(common_utils::events::ApiEventsType::RustLocker) | ||||||
|  |     } | ||||||
|  | } | ||||||
| @ -13,6 +13,7 @@ pub mod errors; | |||||||
| pub mod events; | pub mod events; | ||||||
| pub mod files; | pub mod files; | ||||||
| pub mod gsm; | pub mod gsm; | ||||||
|  | pub mod locker_migration; | ||||||
| pub mod mandates; | pub mod mandates; | ||||||
| pub mod organization; | pub mod organization; | ||||||
| pub mod payment_methods; | pub mod payment_methods; | ||||||
|  | |||||||
							
								
								
									
										8
									
								
								crates/api_models/src/locker_migration.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								crates/api_models/src/locker_migration.rs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,8 @@ | |||||||
|  | #[derive(Debug, Clone, serde::Serialize)] | ||||||
|  | #[serde(rename_all = "camelCase")] | ||||||
|  | pub struct MigrateCardResponse { | ||||||
|  |     pub status_message: String, | ||||||
|  |     pub status_code: String, | ||||||
|  |     pub customers_moved: usize, | ||||||
|  |     pub cards_moved: usize, | ||||||
|  | } | ||||||
| @ -44,6 +44,7 @@ pub enum ApiEventsType { | |||||||
|     Gsm, |     Gsm, | ||||||
|     // TODO: This has to be removed once the corresponding apiEventTypes are created |     // TODO: This has to be removed once the corresponding apiEventTypes are created | ||||||
|     Miscellaneous, |     Miscellaneous, | ||||||
|  |     RustLocker, | ||||||
| } | } | ||||||
|  |  | ||||||
| impl ApiEventMetric for serde_json::Value {} | impl ApiEventMetric for serde_json::Value {} | ||||||
|  | |||||||
| @ -48,6 +48,7 @@ impl Default for super::settings::Locker { | |||||||
|     fn default() -> Self { |     fn default() -> Self { | ||||||
|         Self { |         Self { | ||||||
|             host: "localhost".into(), |             host: "localhost".into(), | ||||||
|  |             host_rs: "localhost".into(), | ||||||
|             mock_locker: true, |             mock_locker: true, | ||||||
|             basilisk_host: "localhost".into(), |             basilisk_host: "localhost".into(), | ||||||
|             locker_signing_key_id: "1".into(), |             locker_signing_key_id: "1".into(), | ||||||
|  | |||||||
| @ -18,6 +18,7 @@ impl KmsDecrypt for settings::Jwekey { | |||||||
|             self.locker_decryption_key1, |             self.locker_decryption_key1, | ||||||
|             self.locker_decryption_key2, |             self.locker_decryption_key2, | ||||||
|             self.vault_encryption_key, |             self.vault_encryption_key, | ||||||
|  |             self.rust_locker_encryption_key, | ||||||
|             self.vault_private_key, |             self.vault_private_key, | ||||||
|             self.tunnel_private_key, |             self.tunnel_private_key, | ||||||
|         ) = tokio::try_join!( |         ) = tokio::try_join!( | ||||||
| @ -26,6 +27,7 @@ impl KmsDecrypt for settings::Jwekey { | |||||||
|             kms_client.decrypt(self.locker_decryption_key1), |             kms_client.decrypt(self.locker_decryption_key1), | ||||||
|             kms_client.decrypt(self.locker_decryption_key2), |             kms_client.decrypt(self.locker_decryption_key2), | ||||||
|             kms_client.decrypt(self.vault_encryption_key), |             kms_client.decrypt(self.vault_encryption_key), | ||||||
|  |             kms_client.decrypt(self.rust_locker_encryption_key), | ||||||
|             kms_client.decrypt(self.vault_private_key), |             kms_client.decrypt(self.vault_private_key), | ||||||
|             kms_client.decrypt(self.tunnel_private_key), |             kms_client.decrypt(self.tunnel_private_key), | ||||||
|         )?; |         )?; | ||||||
|  | |||||||
| @ -420,6 +420,7 @@ pub struct Secrets { | |||||||
| #[serde(default)] | #[serde(default)] | ||||||
| pub struct Locker { | pub struct Locker { | ||||||
|     pub host: String, |     pub host: String, | ||||||
|  |     pub host_rs: String, | ||||||
|     pub mock_locker: bool, |     pub mock_locker: bool, | ||||||
|     pub basilisk_host: String, |     pub basilisk_host: String, | ||||||
|     pub locker_signing_key_id: String, |     pub locker_signing_key_id: String, | ||||||
| @ -448,6 +449,7 @@ pub struct Jwekey { | |||||||
|     pub locker_decryption_key1: String, |     pub locker_decryption_key1: String, | ||||||
|     pub locker_decryption_key2: String, |     pub locker_decryption_key2: String, | ||||||
|     pub vault_encryption_key: String, |     pub vault_encryption_key: String, | ||||||
|  |     pub rust_locker_encryption_key: String, | ||||||
|     pub vault_private_key: String, |     pub vault_private_key: String, | ||||||
|     pub tunnel_private_key: String, |     pub tunnel_private_key: String, | ||||||
| } | } | ||||||
|  | |||||||
| @ -9,6 +9,7 @@ pub mod disputes; | |||||||
| pub mod errors; | pub mod errors; | ||||||
| pub mod files; | pub mod files; | ||||||
| pub mod gsm; | pub mod gsm; | ||||||
|  | pub mod locker_migration; | ||||||
| pub mod mandate; | pub mod mandate; | ||||||
| pub mod metrics; | pub mod metrics; | ||||||
| pub mod payment_link; | pub mod payment_link; | ||||||
|  | |||||||
							
								
								
									
										131
									
								
								crates/router/src/core/locker_migration.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										131
									
								
								crates/router/src/core/locker_migration.rs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,131 @@ | |||||||
|  | use api_models::{enums as api_enums, locker_migration::MigrateCardResponse}; | ||||||
|  | use common_utils::errors::CustomResult; | ||||||
|  | use diesel_models::PaymentMethod; | ||||||
|  | use error_stack::{FutureExt, ResultExt}; | ||||||
|  | use futures::TryFutureExt; | ||||||
|  |  | ||||||
|  | use super::{errors::StorageErrorExt, payment_methods::cards}; | ||||||
|  | use crate::{ | ||||||
|  |     errors, | ||||||
|  |     routes::AppState, | ||||||
|  |     services::{self, logger}, | ||||||
|  |     types::{api, domain}, | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | pub async fn rust_locker_migration( | ||||||
|  |     state: AppState, | ||||||
|  |     merchant_id: &str, | ||||||
|  | ) -> CustomResult<services::ApplicationResponse<MigrateCardResponse>, errors::ApiErrorResponse> { | ||||||
|  |     let db = state.store.as_ref(); | ||||||
|  |  | ||||||
|  |     let key_store = state | ||||||
|  |         .store | ||||||
|  |         .get_merchant_key_store_by_merchant_id( | ||||||
|  |             merchant_id, | ||||||
|  |             &state.store.get_master_key().to_vec().into(), | ||||||
|  |         ) | ||||||
|  |         .await | ||||||
|  |         .change_context(errors::ApiErrorResponse::InternalServerError)?; | ||||||
|  |  | ||||||
|  |     let merchant_account = db | ||||||
|  |         .find_merchant_account_by_merchant_id(merchant_id, &key_store) | ||||||
|  |         .await | ||||||
|  |         .to_not_found_response(errors::ApiErrorResponse::MerchantAccountNotFound) | ||||||
|  |         .change_context(errors::ApiErrorResponse::InternalServerError)?; | ||||||
|  |  | ||||||
|  |     let domain_customers = db | ||||||
|  |         .list_customers_by_merchant_id(merchant_id, &key_store) | ||||||
|  |         .await | ||||||
|  |         .change_context(errors::ApiErrorResponse::InternalServerError)?; | ||||||
|  |  | ||||||
|  |     let mut customers_moved = 0; | ||||||
|  |     let mut cards_moved = 0; | ||||||
|  |  | ||||||
|  |     for customer in domain_customers { | ||||||
|  |         let result = db | ||||||
|  |             .find_payment_method_by_customer_id_merchant_id_list(&customer.customer_id, merchant_id) | ||||||
|  |             .change_context(errors::ApiErrorResponse::InternalServerError) | ||||||
|  |             .and_then(|pm| { | ||||||
|  |                 call_to_locker( | ||||||
|  |                     &state, | ||||||
|  |                     pm, | ||||||
|  |                     &customer.customer_id, | ||||||
|  |                     merchant_id, | ||||||
|  |                     &merchant_account, | ||||||
|  |                 ) | ||||||
|  |             }) | ||||||
|  |             .await?; | ||||||
|  |  | ||||||
|  |         customers_moved += 1; | ||||||
|  |         cards_moved += result; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     Ok(services::api::ApplicationResponse::Json( | ||||||
|  |         MigrateCardResponse { | ||||||
|  |             status_code: "200".to_string(), | ||||||
|  |             status_message: "Card migration completed".to_string(), | ||||||
|  |             customers_moved, | ||||||
|  |             cards_moved, | ||||||
|  |         }, | ||||||
|  |     )) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | pub async fn call_to_locker( | ||||||
|  |     state: &AppState, | ||||||
|  |     payment_methods: Vec<PaymentMethod>, | ||||||
|  |     customer_id: &String, | ||||||
|  |     merchant_id: &str, | ||||||
|  |     merchant_account: &domain::MerchantAccount, | ||||||
|  | ) -> CustomResult<usize, errors::ApiErrorResponse> { | ||||||
|  |     let mut cards_moved = 0; | ||||||
|  |  | ||||||
|  |     for pm in payment_methods { | ||||||
|  |         let card = | ||||||
|  |             cards::get_card_from_locker(state, customer_id, merchant_id, &pm.payment_method_id) | ||||||
|  |                 .await?; | ||||||
|  |  | ||||||
|  |         let card_details = api::CardDetail { | ||||||
|  |             card_number: card.card_number, | ||||||
|  |             card_exp_month: card.card_exp_month, | ||||||
|  |             card_exp_year: card.card_exp_year, | ||||||
|  |             card_holder_name: card.name_on_card, | ||||||
|  |             nick_name: card.nick_name.map(masking::Secret::new), | ||||||
|  |         }; | ||||||
|  |  | ||||||
|  |         let pm_create = api::PaymentMethodCreate { | ||||||
|  |             payment_method: pm.payment_method, | ||||||
|  |             payment_method_type: pm.payment_method_type, | ||||||
|  |             payment_method_issuer: pm.payment_method_issuer, | ||||||
|  |             payment_method_issuer_code: pm.payment_method_issuer_code, | ||||||
|  |             card: Some(card_details.clone()), | ||||||
|  |             metadata: pm.metadata, | ||||||
|  |             customer_id: Some(pm.customer_id), | ||||||
|  |             card_network: card.card_brand, | ||||||
|  |         }; | ||||||
|  |  | ||||||
|  |         let (_add_card_rs_resp, _is_duplicate) = cards::add_card_hs( | ||||||
|  |             state, | ||||||
|  |             pm_create, | ||||||
|  |             card_details, | ||||||
|  |             customer_id.to_string(), | ||||||
|  |             merchant_account, | ||||||
|  |             api_enums::LockerChoice::Tartarus, | ||||||
|  |             Some(&pm.payment_method_id), | ||||||
|  |         ) | ||||||
|  |         .await | ||||||
|  |         .change_context(errors::ApiErrorResponse::InternalServerError) | ||||||
|  |         .attach_printable(format!( | ||||||
|  |             "Card migration failed for merchant_id: {merchant_id}, customer_id: {customer_id}, payment_method_id: {} ", | ||||||
|  |             pm.payment_method_id | ||||||
|  |         ))?; | ||||||
|  |  | ||||||
|  |         cards_moved += 1; | ||||||
|  |  | ||||||
|  |         logger::info!( | ||||||
|  |             "Card migrated for merchant_id: {merchant_id}, customer_id: {customer_id}, payment_method_id: {} ", | ||||||
|  |             pm.payment_method_id | ||||||
|  |         ); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     Ok(cards_moved) | ||||||
|  | } | ||||||
| @ -214,7 +214,15 @@ pub async fn add_card_to_locker( | |||||||
|     metrics::STORED_TO_LOCKER.add(&metrics::CONTEXT, 1, &[]); |     metrics::STORED_TO_LOCKER.add(&metrics::CONTEXT, 1, &[]); | ||||||
|     request::record_operation_time( |     request::record_operation_time( | ||||||
|         async { |         async { | ||||||
|             add_card_hs(state, req, card, customer_id, merchant_account) |             add_card_hs( | ||||||
|  |                 state, | ||||||
|  |                 req, | ||||||
|  |                 card, | ||||||
|  |                 customer_id, | ||||||
|  |                 merchant_account, | ||||||
|  |                 api_enums::LockerChoice::Basilisk, | ||||||
|  |                 None, | ||||||
|  |             ) | ||||||
|             .await |             .await | ||||||
|             .map_err(|error| { |             .map_err(|error| { | ||||||
|                 metrics::CARD_LOCKER_FAILURES.add(&metrics::CONTEXT, 1, &[]); |                 metrics::CARD_LOCKER_FAILURES.add(&metrics::CONTEXT, 1, &[]); | ||||||
| @ -282,10 +290,13 @@ pub async fn add_card_hs( | |||||||
|     card: api::CardDetail, |     card: api::CardDetail, | ||||||
|     customer_id: String, |     customer_id: String, | ||||||
|     merchant_account: &domain::MerchantAccount, |     merchant_account: &domain::MerchantAccount, | ||||||
|  |     locker_choice: api_enums::LockerChoice, | ||||||
|  |     card_reference: Option<&str>, | ||||||
| ) -> errors::CustomResult<(api::PaymentMethodResponse, bool), errors::VaultError> { | ) -> errors::CustomResult<(api::PaymentMethodResponse, bool), errors::VaultError> { | ||||||
|     let payload = payment_methods::StoreLockerReq::LockerCard(payment_methods::StoreCardReq { |     let payload = payment_methods::StoreLockerReq::LockerCard(payment_methods::StoreCardReq { | ||||||
|         merchant_id: &merchant_account.merchant_id, |         merchant_id: &merchant_account.merchant_id, | ||||||
|         merchant_customer_id: customer_id.to_owned(), |         merchant_customer_id: customer_id.to_owned(), | ||||||
|  |         card_reference: card_reference.map(str::to_string), | ||||||
|         card: payment_methods::Card { |         card: payment_methods::Card { | ||||||
|             card_number: card.card_number.to_owned(), |             card_number: card.card_number.to_owned(), | ||||||
|             name_on_card: card.card_holder_name.to_owned(), |             name_on_card: card.card_holder_name.to_owned(), | ||||||
| @ -296,7 +307,8 @@ pub async fn add_card_hs( | |||||||
|             nick_name: card.nick_name.as_ref().map(masking::Secret::peek).cloned(), |             nick_name: card.nick_name.as_ref().map(masking::Secret::peek).cloned(), | ||||||
|         }, |         }, | ||||||
|     }); |     }); | ||||||
|     let store_card_payload = call_to_locker_hs(state, &payload, &customer_id).await?; |     let store_card_payload = | ||||||
|  |         call_to_locker_hs(state, &payload, &customer_id, locker_choice).await?; | ||||||
|  |  | ||||||
|     let payment_method_resp = payment_methods::mk_add_card_response_hs( |     let payment_method_resp = payment_methods::mk_add_card_response_hs( | ||||||
|         card, |         card, | ||||||
| @ -394,6 +406,7 @@ pub async fn call_to_locker_hs<'a>( | |||||||
|     state: &routes::AppState, |     state: &routes::AppState, | ||||||
|     payload: &payment_methods::StoreLockerReq<'a>, |     payload: &payment_methods::StoreLockerReq<'a>, | ||||||
|     customer_id: &str, |     customer_id: &str, | ||||||
|  |     locker_choice: api_enums::LockerChoice, | ||||||
| ) -> errors::CustomResult<payment_methods::StoreCardRespPayload, errors::VaultError> { | ) -> errors::CustomResult<payment_methods::StoreCardRespPayload, errors::VaultError> { | ||||||
|     let locker = &state.conf.locker; |     let locker = &state.conf.locker; | ||||||
|     #[cfg(not(feature = "kms"))] |     #[cfg(not(feature = "kms"))] | ||||||
| @ -402,7 +415,9 @@ pub async fn call_to_locker_hs<'a>( | |||||||
|     let jwekey = &state.kms_secrets; |     let jwekey = &state.kms_secrets; | ||||||
|     let db = &*state.store; |     let db = &*state.store; | ||||||
|     let stored_card_response = if !locker.mock_locker { |     let stored_card_response = if !locker.mock_locker { | ||||||
|         let request = payment_methods::mk_add_locker_request_hs(jwekey, locker, payload).await?; |         let request = | ||||||
|  |             payment_methods::mk_add_locker_request_hs(jwekey, locker, payload, locker_choice) | ||||||
|  |                 .await?; | ||||||
|         let response = services::call_connector_api(state, request) |         let response = services::call_connector_api(state, request) | ||||||
|             .await |             .await | ||||||
|             .change_context(errors::VaultError::SaveCardFailed); |             .change_context(errors::VaultError::SaveCardFailed); | ||||||
|  | |||||||
| @ -1,5 +1,6 @@ | |||||||
| use std::str::FromStr; | use std::str::FromStr; | ||||||
|  |  | ||||||
|  | use api_models::enums as api_enums; | ||||||
| use common_utils::{ext_traits::StringExt, pii::Email}; | use common_utils::{ext_traits::StringExt, pii::Email}; | ||||||
| use error_stack::ResultExt; | use error_stack::ResultExt; | ||||||
| use josekit::jwe; | use josekit::jwe; | ||||||
| @ -26,6 +27,8 @@ pub enum StoreLockerReq<'a> { | |||||||
| pub struct StoreCardReq<'a> { | pub struct StoreCardReq<'a> { | ||||||
|     pub merchant_id: &'a str, |     pub merchant_id: &'a str, | ||||||
|     pub merchant_customer_id: String, |     pub merchant_customer_id: String, | ||||||
|  |     #[serde(skip_serializing_if = "Option::is_none")] | ||||||
|  |     pub card_reference: Option<String>, | ||||||
|     pub card: Card, |     pub card: Card, | ||||||
| } | } | ||||||
|  |  | ||||||
| @ -224,6 +227,7 @@ pub async fn mk_basilisk_req( | |||||||
|     #[cfg(feature = "kms")] jwekey: &settings::ActiveKmsSecrets, |     #[cfg(feature = "kms")] jwekey: &settings::ActiveKmsSecrets, | ||||||
|     #[cfg(not(feature = "kms"))] jwekey: &settings::Jwekey, |     #[cfg(not(feature = "kms"))] jwekey: &settings::Jwekey, | ||||||
|     jws: &str, |     jws: &str, | ||||||
|  |     locker_choice: api_enums::LockerChoice, | ||||||
| ) -> CustomResult<encryption::JweBody, errors::VaultError> { | ) -> CustomResult<encryption::JweBody, errors::VaultError> { | ||||||
|     let jws_payload: Vec<&str> = jws.split('.').collect(); |     let jws_payload: Vec<&str> = jws.split('.').collect(); | ||||||
|  |  | ||||||
| @ -241,10 +245,18 @@ pub async fn mk_basilisk_req( | |||||||
|         .change_context(errors::VaultError::SaveCardFailed)?; |         .change_context(errors::VaultError::SaveCardFailed)?; | ||||||
|  |  | ||||||
|     #[cfg(feature = "kms")] |     #[cfg(feature = "kms")] | ||||||
|     let public_key = jwekey.jwekey.peek().vault_encryption_key.as_bytes(); |     let public_key = match locker_choice { | ||||||
|  |         api_enums::LockerChoice::Basilisk => jwekey.jwekey.peek().vault_encryption_key.as_bytes(), | ||||||
|  |         api_enums::LockerChoice::Tartarus => { | ||||||
|  |             jwekey.jwekey.peek().rust_locker_encryption_key.as_bytes() | ||||||
|  |         } | ||||||
|  |     }; | ||||||
|  |  | ||||||
|     #[cfg(not(feature = "kms"))] |     #[cfg(not(feature = "kms"))] | ||||||
|     let public_key = jwekey.vault_encryption_key.as_bytes(); |     let public_key = match locker_choice { | ||||||
|  |         api_enums::LockerChoice::Basilisk => jwekey.vault_encryption_key.as_bytes(), | ||||||
|  |         api_enums::LockerChoice::Tartarus => jwekey.rust_locker_encryption_key.as_bytes(), | ||||||
|  |     }; | ||||||
|  |  | ||||||
|     let jwe_encrypted = encryption::encrypt_jwe(&payload, public_key) |     let jwe_encrypted = encryption::encrypt_jwe(&payload, public_key) | ||||||
|         .await |         .await | ||||||
| @ -272,6 +284,7 @@ pub async fn mk_add_locker_request_hs<'a>( | |||||||
|     #[cfg(feature = "kms")] jwekey: &settings::ActiveKmsSecrets, |     #[cfg(feature = "kms")] jwekey: &settings::ActiveKmsSecrets, | ||||||
|     locker: &settings::Locker, |     locker: &settings::Locker, | ||||||
|     payload: &StoreLockerReq<'a>, |     payload: &StoreLockerReq<'a>, | ||||||
|  |     locker_choice: api_enums::LockerChoice, | ||||||
| ) -> CustomResult<services::Request, errors::VaultError> { | ) -> CustomResult<services::Request, errors::VaultError> { | ||||||
|     let payload = utils::Encode::<StoreCardReq<'_>>::encode_to_vec(&payload) |     let payload = utils::Encode::<StoreCardReq<'_>>::encode_to_vec(&payload) | ||||||
|         .change_context(errors::VaultError::RequestEncodingFailed)?; |         .change_context(errors::VaultError::RequestEncodingFailed)?; | ||||||
| @ -286,11 +299,14 @@ pub async fn mk_add_locker_request_hs<'a>( | |||||||
|         .await |         .await | ||||||
|         .change_context(errors::VaultError::RequestEncodingFailed)?; |         .change_context(errors::VaultError::RequestEncodingFailed)?; | ||||||
|  |  | ||||||
|     let jwe_payload = mk_basilisk_req(jwekey, &jws).await?; |     let jwe_payload = mk_basilisk_req(jwekey, &jws, locker_choice).await?; | ||||||
|  |  | ||||||
|     let body = utils::Encode::<encryption::JweBody>::encode_to_value(&jwe_payload) |     let body = utils::Encode::<encryption::JweBody>::encode_to_value(&jwe_payload) | ||||||
|         .change_context(errors::VaultError::RequestEncodingFailed)?; |         .change_context(errors::VaultError::RequestEncodingFailed)?; | ||||||
|     let mut url = locker.host.to_owned(); |     let mut url = match locker_choice { | ||||||
|  |         api_enums::LockerChoice::Basilisk => locker.host.to_owned(), | ||||||
|  |         api_enums::LockerChoice::Tartarus => locker.host_rs.to_owned(), | ||||||
|  |     }; | ||||||
|     url.push_str("/cards/add"); |     url.push_str("/cards/add"); | ||||||
|     let mut request = services::Request::new(services::Method::Post, &url); |     let mut request = services::Request::new(services::Method::Post, &url); | ||||||
|     request.add_header(headers::CONTENT_TYPE, "application/json".into()); |     request.add_header(headers::CONTENT_TYPE, "application/json".into()); | ||||||
| @ -432,7 +448,7 @@ pub async fn mk_get_card_request_hs( | |||||||
|         .await |         .await | ||||||
|         .change_context(errors::VaultError::RequestEncodingFailed)?; |         .change_context(errors::VaultError::RequestEncodingFailed)?; | ||||||
|  |  | ||||||
|     let jwe_payload = mk_basilisk_req(jwekey, &jws).await?; |     let jwe_payload = mk_basilisk_req(jwekey, &jws, api_enums::LockerChoice::Basilisk).await?; | ||||||
|  |  | ||||||
|     let body = utils::Encode::<encryption::JweBody>::encode_to_value(&jwe_payload) |     let body = utils::Encode::<encryption::JweBody>::encode_to_value(&jwe_payload) | ||||||
|         .change_context(errors::VaultError::RequestEncodingFailed)?; |         .change_context(errors::VaultError::RequestEncodingFailed)?; | ||||||
| @ -512,7 +528,7 @@ pub async fn mk_delete_card_request_hs( | |||||||
|         .await |         .await | ||||||
|         .change_context(errors::VaultError::RequestEncodingFailed)?; |         .change_context(errors::VaultError::RequestEncodingFailed)?; | ||||||
|  |  | ||||||
|     let jwe_payload = mk_basilisk_req(jwekey, &jws).await?; |     let jwe_payload = mk_basilisk_req(jwekey, &jws, api_enums::LockerChoice::Basilisk).await?; | ||||||
|  |  | ||||||
|     let body = utils::Encode::<encryption::JweBody>::encode_to_value(&jwe_payload) |     let body = utils::Encode::<encryption::JweBody>::encode_to_value(&jwe_payload) | ||||||
|         .change_context(errors::VaultError::RequestEncodingFailed)?; |         .change_context(errors::VaultError::RequestEncodingFailed)?; | ||||||
|  | |||||||
| @ -152,6 +152,7 @@ pub async fn save_payout_data_to_locker( | |||||||
|                     card_isin: None, |                     card_isin: None, | ||||||
|                     nick_name: None, |                     nick_name: None, | ||||||
|                 }, |                 }, | ||||||
|  |                 card_reference: None, | ||||||
|             }); |             }); | ||||||
|             ( |             ( | ||||||
|                 payload, |                 payload, | ||||||
| @ -195,7 +196,12 @@ pub async fn save_payout_data_to_locker( | |||||||
|         } |         } | ||||||
|     }; |     }; | ||||||
|     // Store payout method in locker |     // Store payout method in locker | ||||||
|     let stored_resp = cards::call_to_locker_hs(state, &locker_req, &payout_attempt.customer_id) |     let stored_resp = cards::call_to_locker_hs( | ||||||
|  |         state, | ||||||
|  |         &locker_req, | ||||||
|  |         &payout_attempt.customer_id, | ||||||
|  |         api_enums::LockerChoice::Basilisk, | ||||||
|  |     ) | ||||||
|     .await |     .await | ||||||
|     .change_context(errors::ApiErrorResponse::InternalServerError)?; |     .change_context(errors::ApiErrorResponse::InternalServerError)?; | ||||||
|  |  | ||||||
|  | |||||||
| @ -145,6 +145,7 @@ pub fn mk_app( | |||||||
|             .service(routes::Disputes::server(state.clone())) |             .service(routes::Disputes::server(state.clone())) | ||||||
|             .service(routes::Analytics::server(state.clone())) |             .service(routes::Analytics::server(state.clone())) | ||||||
|             .service(routes::Routing::server(state.clone())) |             .service(routes::Routing::server(state.clone())) | ||||||
|  |             .service(routes::LockerMigrate::server(state.clone())) | ||||||
|             .service(routes::Gsm::server(state.clone())) |             .service(routes::Gsm::server(state.clone())) | ||||||
|             .service(routes::User::server(state.clone())) |             .service(routes::User::server(state.clone())) | ||||||
|     } |     } | ||||||
|  | |||||||
| @ -29,6 +29,7 @@ pub mod user; | |||||||
| pub mod verification; | pub mod verification; | ||||||
| pub mod webhooks; | pub mod webhooks; | ||||||
|  |  | ||||||
|  | pub mod locker_migration; | ||||||
| #[cfg(feature = "dummy_connector")] | #[cfg(feature = "dummy_connector")] | ||||||
| pub use self::app::DummyConnector; | pub use self::app::DummyConnector; | ||||||
| #[cfg(feature = "payouts")] | #[cfg(feature = "payouts")] | ||||||
| @ -39,8 +40,8 @@ pub use self::app::Routing; | |||||||
| pub use self::app::Verify; | pub use self::app::Verify; | ||||||
| pub use self::app::{ | pub use self::app::{ | ||||||
|     ApiKeys, AppState, BusinessProfile, Cache, Cards, Configs, Customers, Disputes, EphemeralKey, |     ApiKeys, AppState, BusinessProfile, Cache, Cards, Configs, Customers, Disputes, EphemeralKey, | ||||||
|     Files, Gsm, Health, Mandates, MerchantAccount, MerchantConnectorAccount, PaymentLink, |     Files, Gsm, Health, LockerMigrate, Mandates, MerchantAccount, MerchantConnectorAccount, | ||||||
|     PaymentMethods, Payments, Refunds, User, Webhooks, |     PaymentLink, PaymentMethods, Payments, Refunds, User, Webhooks, | ||||||
| }; | }; | ||||||
| #[cfg(feature = "stripe")] | #[cfg(feature = "stripe")] | ||||||
| pub use super::compatibility::stripe::StripeApis; | pub use super::compatibility::stripe::StripeApis; | ||||||
|  | |||||||
| @ -19,7 +19,7 @@ use super::routing as cloud_routing; | |||||||
| #[cfg(all(feature = "olap", feature = "kms"))] | #[cfg(all(feature = "olap", feature = "kms"))] | ||||||
| use super::verification::{apple_pay_merchant_registration, retrieve_apple_pay_verified_domains}; | use super::verification::{apple_pay_merchant_registration, retrieve_apple_pay_verified_domains}; | ||||||
| #[cfg(feature = "olap")] | #[cfg(feature = "olap")] | ||||||
| use super::{admin::*, api_keys::*, disputes::*, files::*, gsm::*, user::*}; | use super::{admin::*, api_keys::*, disputes::*, files::*, gsm::*, locker_migration, user::*}; | ||||||
| use super::{cache::*, health::*, payment_link::*}; | use super::{cache::*, health::*, payment_link::*}; | ||||||
| #[cfg(any(feature = "olap", feature = "oltp"))] | #[cfg(any(feature = "olap", feature = "oltp"))] | ||||||
| use super::{configs::*, customers::*, mandates::*, payments::*, refunds::*}; | use super::{configs::*, customers::*, mandates::*, payments::*, refunds::*}; | ||||||
| @ -743,3 +743,16 @@ impl User { | |||||||
|             .service(web::resource("/v2/signup").route(web::post().to(user_connect_account))) |             .service(web::resource("/v2/signup").route(web::post().to(user_connect_account))) | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
|  | pub struct LockerMigrate; | ||||||
|  |  | ||||||
|  | #[cfg(feature = "olap")] | ||||||
|  | impl LockerMigrate { | ||||||
|  |     pub fn server(state: AppState) -> Scope { | ||||||
|  |         web::scope("locker_migration/{merchant_id}") | ||||||
|  |             .app_data(web::Data::new(state)) | ||||||
|  |             .service( | ||||||
|  |                 web::resource("").route(web::post().to(locker_migration::rust_locker_migration)), | ||||||
|  |             ) | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | |||||||
| @ -23,6 +23,7 @@ pub enum ApiIdentifier { | |||||||
|     ApiKeys, |     ApiKeys, | ||||||
|     PaymentLink, |     PaymentLink, | ||||||
|     Routing, |     Routing, | ||||||
|  |     RustLockerMigration, | ||||||
|     Gsm, |     Gsm, | ||||||
|     User, |     User, | ||||||
| } | } | ||||||
| @ -131,6 +132,7 @@ impl From<Flow> for ApiIdentifier { | |||||||
|             Flow::Verification => Self::Verification, |             Flow::Verification => Self::Verification, | ||||||
|  |  | ||||||
|             Flow::PaymentLinkInitiate | Flow::PaymentLinkRetrieve => Self::PaymentLink, |             Flow::PaymentLinkInitiate | Flow::PaymentLinkRetrieve => Self::PaymentLink, | ||||||
|  |             Flow::RustLockerMigration => Self::RustLockerMigration, | ||||||
|             Flow::GsmRuleCreate |             Flow::GsmRuleCreate | ||||||
|             | Flow::GsmRuleRetrieve |             | Flow::GsmRuleRetrieve | ||||||
|             | Flow::GsmRuleUpdate |             | Flow::GsmRuleUpdate | ||||||
|  | |||||||
							
								
								
									
										27
									
								
								crates/router/src/routes/locker_migration.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										27
									
								
								crates/router/src/routes/locker_migration.rs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,27 @@ | |||||||
|  | use actix_web::{web, HttpRequest, HttpResponse}; | ||||||
|  | use router_env::Flow; | ||||||
|  |  | ||||||
|  | use super::AppState; | ||||||
|  | use crate::{ | ||||||
|  |     core::{api_locking, locker_migration}, | ||||||
|  |     services::{api, authentication as auth}, | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | pub async fn rust_locker_migration( | ||||||
|  |     state: web::Data<AppState>, | ||||||
|  |     req: HttpRequest, | ||||||
|  |     path: web::Path<String>, | ||||||
|  | ) -> HttpResponse { | ||||||
|  |     let flow = Flow::RustLockerMigration; | ||||||
|  |     let merchant_id = path.into_inner(); | ||||||
|  |     api::server_wrap( | ||||||
|  |         flow, | ||||||
|  |         state, | ||||||
|  |         &req, | ||||||
|  |         &merchant_id, | ||||||
|  |         |state, _, _| locker_migration::rust_locker_migration(state, &merchant_id), | ||||||
|  |         &auth::AdminApiAuth, | ||||||
|  |         api_locking::LockAction::NotApplicable, | ||||||
|  |     ) | ||||||
|  |     .await | ||||||
|  | } | ||||||
| @ -235,6 +235,8 @@ pub enum Flow { | |||||||
|     BusinessProfileList, |     BusinessProfileList, | ||||||
|     /// Different verification flows |     /// Different verification flows | ||||||
|     Verification, |     Verification, | ||||||
|  |     /// Rust locker migration | ||||||
|  |     RustLockerMigration, | ||||||
|     /// Gsm Rule Creation flow |     /// Gsm Rule Creation flow | ||||||
|     GsmRuleCreate, |     GsmRuleCreate, | ||||||
|     /// Gsm Rule Retrieve flow |     /// Gsm Rule Retrieve flow | ||||||
|  | |||||||
| @ -30,6 +30,7 @@ jwt_secret = "secret" | |||||||
|  |  | ||||||
| [locker] | [locker] | ||||||
| host = "" | host = "" | ||||||
|  | host_rs = "" | ||||||
| mock_locker = true | mock_locker = true | ||||||
| basilisk_host = "" | basilisk_host = "" | ||||||
|  |  | ||||||
| @ -48,6 +49,7 @@ locker_encryption_key2 = "" | |||||||
| locker_decryption_key1 = "" | locker_decryption_key1 = "" | ||||||
| locker_decryption_key2 = "" | locker_decryption_key2 = "" | ||||||
| vault_encryption_key = "" | vault_encryption_key = "" | ||||||
|  | rust_locker_encryption_key = "" | ||||||
| vault_private_key = "" | vault_private_key = "" | ||||||
|  |  | ||||||
| [webhooks] | [webhooks] | ||||||
|  | |||||||
		Reference in New Issue
	
	Block a user
	 Shankar Singh C
					Shankar Singh C