mirror of
				https://github.com/juspay/hyperswitch.git
				synced 2025-11-01 02:57:02 +08:00 
			
		
		
		
	feat(core): Add support for cards bin update (#7194)
Co-authored-by: Mrudul Vajpayee <mrudul.vajpayee@mrudulvajpayee-XJWXCWP7HF.local> Co-authored-by: Mrudul Vajpayee <mrudul.vajpayee@mrudulvWXCWP7HF.lan>
This commit is contained in:
		| @ -1,15 +1,24 @@ | ||||
| use actix_multipart::form::{bytes::Bytes, MultipartForm}; | ||||
| use api_models::cards_info as cards_info_api_types; | ||||
| use common_utils::fp_utils::when; | ||||
| use csv::Reader; | ||||
| use diesel_models::cards_info as card_info_models; | ||||
| use error_stack::{report, ResultExt}; | ||||
| use rdkafka::message::ToBytes; | ||||
| use router_env::{instrument, tracing}; | ||||
|  | ||||
| use crate::{ | ||||
|     core::{ | ||||
|         errors::{self, RouterResponse}, | ||||
|         errors::{self, RouterResponse, RouterResult, StorageErrorExt}, | ||||
|         payments::helpers, | ||||
|     }, | ||||
|     db::cards_info::CardsInfoInterface, | ||||
|     routes, | ||||
|     services::ApplicationResponse, | ||||
|     types::{domain, transformers::ForeignFrom}, | ||||
|     types::{ | ||||
|         domain, | ||||
|         transformers::{ForeignFrom, ForeignInto}, | ||||
|     }, | ||||
| }; | ||||
|  | ||||
| fn verify_iin_length(card_iin: &str) -> Result<(), errors::ApiErrorResponse> { | ||||
| @ -24,8 +33,8 @@ pub async fn retrieve_card_info( | ||||
|     state: routes::SessionState, | ||||
|     merchant_account: domain::MerchantAccount, | ||||
|     key_store: domain::MerchantKeyStore, | ||||
|     request: api_models::cards_info::CardsInfoRequest, | ||||
| ) -> RouterResponse<api_models::cards_info::CardInfoResponse> { | ||||
|     request: cards_info_api_types::CardsInfoRequest, | ||||
| ) -> RouterResponse<cards_info_api_types::CardInfoResponse> { | ||||
|     let db = state.store.as_ref(); | ||||
|  | ||||
|     verify_iin_length(&request.card_iin)?; | ||||
| @ -45,6 +54,286 @@ pub async fn retrieve_card_info( | ||||
|         .ok_or(report!(errors::ApiErrorResponse::InvalidCardIin))?; | ||||
|  | ||||
|     Ok(ApplicationResponse::Json( | ||||
|         api_models::cards_info::CardInfoResponse::foreign_from(card_info), | ||||
|         cards_info_api_types::CardInfoResponse::foreign_from(card_info), | ||||
|     )) | ||||
| } | ||||
|  | ||||
| #[instrument(skip_all)] | ||||
| pub async fn create_card_info( | ||||
|     state: routes::SessionState, | ||||
|     card_info_request: cards_info_api_types::CardInfoCreateRequest, | ||||
| ) -> RouterResponse<cards_info_api_types::CardInfoResponse> { | ||||
|     let db = state.store.as_ref(); | ||||
|     CardsInfoInterface::add_card_info(db, card_info_request.foreign_into()) | ||||
|         .await | ||||
|         .to_duplicate_response(errors::ApiErrorResponse::GenericDuplicateError { | ||||
|             message: "CardInfo with given key already exists in our records".to_string(), | ||||
|         }) | ||||
|         .map(|card_info| ApplicationResponse::Json(card_info.foreign_into())) | ||||
| } | ||||
|  | ||||
| #[instrument(skip_all)] | ||||
| pub async fn update_card_info( | ||||
|     state: routes::SessionState, | ||||
|     card_info_request: cards_info_api_types::CardInfoUpdateRequest, | ||||
| ) -> RouterResponse<cards_info_api_types::CardInfoResponse> { | ||||
|     let db = state.store.as_ref(); | ||||
|     CardsInfoInterface::update_card_info( | ||||
|         db, | ||||
|         card_info_request.card_iin, | ||||
|         card_info_models::UpdateCardInfo { | ||||
|             card_issuer: card_info_request.card_issuer, | ||||
|             card_network: card_info_request.card_network, | ||||
|             card_type: card_info_request.card_type, | ||||
|             card_subtype: card_info_request.card_subtype, | ||||
|             card_issuing_country: card_info_request.card_issuing_country, | ||||
|             bank_code_id: card_info_request.bank_code_id, | ||||
|             bank_code: card_info_request.bank_code, | ||||
|             country_code: card_info_request.country_code, | ||||
|             last_updated: Some(common_utils::date_time::now()), | ||||
|             last_updated_provider: card_info_request.last_updated_provider, | ||||
|         }, | ||||
|     ) | ||||
|     .await | ||||
|     .to_not_found_response(errors::ApiErrorResponse::GenericNotFoundError { | ||||
|         message: "Card info with given key does not exist in our records".to_string(), | ||||
|     }) | ||||
|     .attach_printable("Failed while updating card info") | ||||
|     .map(|card_info| ApplicationResponse::Json(card_info.foreign_into())) | ||||
| } | ||||
|  | ||||
| #[derive(Debug, MultipartForm)] | ||||
| pub struct CardsInfoUpdateForm { | ||||
|     #[multipart(limit = "1MB")] | ||||
|     pub file: Bytes, | ||||
| } | ||||
|  | ||||
| fn parse_cards_bin_csv( | ||||
|     data: &[u8], | ||||
| ) -> csv::Result<Vec<cards_info_api_types::CardInfoUpdateRequest>> { | ||||
|     let mut csv_reader = Reader::from_reader(data); | ||||
|     let mut records = Vec::new(); | ||||
|     let mut id_counter = 0; | ||||
|     for result in csv_reader.deserialize() { | ||||
|         let mut record: cards_info_api_types::CardInfoUpdateRequest = result?; | ||||
|         id_counter += 1; | ||||
|         record.line_number = Some(id_counter); | ||||
|         records.push(record); | ||||
|     } | ||||
|     Ok(records) | ||||
| } | ||||
|  | ||||
| pub fn get_cards_bin_records( | ||||
|     form: CardsInfoUpdateForm, | ||||
| ) -> Result<Vec<cards_info_api_types::CardInfoUpdateRequest>, errors::ApiErrorResponse> { | ||||
|     match parse_cards_bin_csv(form.file.data.to_bytes()) { | ||||
|         Ok(records) => Ok(records), | ||||
|         Err(e) => Err(errors::ApiErrorResponse::PreconditionFailed { | ||||
|             message: e.to_string(), | ||||
|         }), | ||||
|     } | ||||
| } | ||||
|  | ||||
| #[instrument(skip_all)] | ||||
| pub async fn migrate_cards_info( | ||||
|     state: routes::SessionState, | ||||
|     card_info_records: Vec<cards_info_api_types::CardInfoUpdateRequest>, | ||||
| ) -> RouterResponse<Vec<cards_info_api_types::CardInfoMigrationResponse>> { | ||||
|     let mut result = Vec::new(); | ||||
|     for record in card_info_records { | ||||
|         let res = card_info_flow(record.clone(), state.clone()).await; | ||||
|         result.push(cards_info_api_types::CardInfoMigrationResponse::from(( | ||||
|             match res { | ||||
|                 Ok(ApplicationResponse::Json(response)) => Ok(response), | ||||
|                 Err(e) => Err(e.to_string()), | ||||
|                 _ => Err("Failed to migrate card info".to_string()), | ||||
|             }, | ||||
|             record, | ||||
|         ))); | ||||
|     } | ||||
|     Ok(ApplicationResponse::Json(result)) | ||||
| } | ||||
|  | ||||
| pub trait State {} | ||||
| pub trait TransitionTo<S: State> {} | ||||
| // Available states for card info migration | ||||
| pub struct CardInfoFetch; | ||||
| pub struct CardInfoAdd; | ||||
| pub struct CardInfoUpdate; | ||||
| pub struct CardInfoResponse; | ||||
|  | ||||
| impl State for CardInfoFetch {} | ||||
| impl State for CardInfoAdd {} | ||||
| impl State for CardInfoUpdate {} | ||||
| impl State for CardInfoResponse {} | ||||
|  | ||||
| // State transitions for card info migration | ||||
| impl TransitionTo<CardInfoAdd> for CardInfoFetch {} | ||||
| impl TransitionTo<CardInfoUpdate> for CardInfoFetch {} | ||||
| impl TransitionTo<CardInfoResponse> for CardInfoAdd {} | ||||
| impl TransitionTo<CardInfoResponse> for CardInfoUpdate {} | ||||
|  | ||||
| // Async executor | ||||
| pub struct CardInfoMigrateExecutor<'a> { | ||||
|     state: &'a routes::SessionState, | ||||
|     record: &'a cards_info_api_types::CardInfoUpdateRequest, | ||||
| } | ||||
|  | ||||
| impl<'a> CardInfoMigrateExecutor<'a> { | ||||
|     fn new( | ||||
|         state: &'a routes::SessionState, | ||||
|         record: &'a cards_info_api_types::CardInfoUpdateRequest, | ||||
|     ) -> Self { | ||||
|         Self { state, record } | ||||
|     } | ||||
|  | ||||
|     async fn fetch_card_info(&self) -> RouterResult<Option<card_info_models::CardInfo>> { | ||||
|         let db = self.state.store.as_ref(); | ||||
|         let maybe_card_info = db | ||||
|             .get_card_info(&self.record.card_iin) | ||||
|             .await | ||||
|             .change_context(errors::ApiErrorResponse::InvalidCardIin)?; | ||||
|         Ok(maybe_card_info) | ||||
|     } | ||||
|  | ||||
|     async fn add_card_info(&self) -> RouterResult<card_info_models::CardInfo> { | ||||
|         let db = self.state.store.as_ref(); | ||||
|         let card_info = CardsInfoInterface::add_card_info(db, self.record.clone().foreign_into()) | ||||
|             .await | ||||
|             .to_duplicate_response(errors::ApiErrorResponse::GenericDuplicateError { | ||||
|                 message: "CardInfo with given key already exists in our records".to_string(), | ||||
|             })?; | ||||
|         Ok(card_info) | ||||
|     } | ||||
|  | ||||
|     async fn update_card_info(&self) -> RouterResult<card_info_models::CardInfo> { | ||||
|         let db = self.state.store.as_ref(); | ||||
|         let card_info = CardsInfoInterface::update_card_info( | ||||
|             db, | ||||
|             self.record.card_iin.clone(), | ||||
|             card_info_models::UpdateCardInfo { | ||||
|                 card_issuer: self.record.card_issuer.clone(), | ||||
|                 card_network: self.record.card_network.clone(), | ||||
|                 card_type: self.record.card_type.clone(), | ||||
|                 card_subtype: self.record.card_subtype.clone(), | ||||
|                 card_issuing_country: self.record.card_issuing_country.clone(), | ||||
|                 bank_code_id: self.record.bank_code_id.clone(), | ||||
|                 bank_code: self.record.bank_code.clone(), | ||||
|                 country_code: self.record.country_code.clone(), | ||||
|                 last_updated: Some(common_utils::date_time::now()), | ||||
|                 last_updated_provider: self.record.last_updated_provider.clone(), | ||||
|             }, | ||||
|         ) | ||||
|         .await | ||||
|         .to_not_found_response(errors::ApiErrorResponse::GenericNotFoundError { | ||||
|             message: "Card info with given key does not exist in our records".to_string(), | ||||
|         }) | ||||
|         .attach_printable("Failed while updating card info")?; | ||||
|         Ok(card_info) | ||||
|     } | ||||
| } | ||||
|  | ||||
| // Builder | ||||
| pub struct CardInfoBuilder<S: State> { | ||||
|     state: std::marker::PhantomData<S>, | ||||
|     pub card_info: Option<card_info_models::CardInfo>, | ||||
| } | ||||
|  | ||||
| impl CardInfoBuilder<CardInfoFetch> { | ||||
|     fn new() -> Self { | ||||
|         Self { | ||||
|             state: std::marker::PhantomData, | ||||
|             card_info: None, | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl CardInfoBuilder<CardInfoFetch> { | ||||
|     fn set_card_info( | ||||
|         self, | ||||
|         card_info: card_info_models::CardInfo, | ||||
|     ) -> CardInfoBuilder<CardInfoUpdate> { | ||||
|         CardInfoBuilder { | ||||
|             state: std::marker::PhantomData, | ||||
|             card_info: Some(card_info), | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     fn transition(self) -> CardInfoBuilder<CardInfoAdd> { | ||||
|         CardInfoBuilder { | ||||
|             state: std::marker::PhantomData, | ||||
|             card_info: None, | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl CardInfoBuilder<CardInfoUpdate> { | ||||
|     fn set_updated_card_info( | ||||
|         self, | ||||
|         card_info: card_info_models::CardInfo, | ||||
|     ) -> CardInfoBuilder<CardInfoResponse> { | ||||
|         CardInfoBuilder { | ||||
|             state: std::marker::PhantomData, | ||||
|             card_info: Some(card_info), | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl CardInfoBuilder<CardInfoAdd> { | ||||
|     fn set_added_card_info( | ||||
|         self, | ||||
|         card_info: card_info_models::CardInfo, | ||||
|     ) -> CardInfoBuilder<CardInfoResponse> { | ||||
|         CardInfoBuilder { | ||||
|             state: std::marker::PhantomData, | ||||
|             card_info: Some(card_info), | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl CardInfoBuilder<CardInfoResponse> { | ||||
|     pub fn build(self) -> cards_info_api_types::CardInfoMigrateResponseRecord { | ||||
|         match self.card_info { | ||||
|             Some(card_info) => cards_info_api_types::CardInfoMigrateResponseRecord { | ||||
|                 card_iin: Some(card_info.card_iin), | ||||
|                 card_issuer: card_info.card_issuer, | ||||
|                 card_network: card_info.card_network.map(|cn| cn.to_string()), | ||||
|                 card_type: card_info.card_type, | ||||
|                 card_sub_type: card_info.card_subtype, | ||||
|                 card_issuing_country: card_info.card_issuing_country, | ||||
|             }, | ||||
|             None => cards_info_api_types::CardInfoMigrateResponseRecord { | ||||
|                 card_iin: None, | ||||
|                 card_issuer: None, | ||||
|                 card_network: None, | ||||
|                 card_type: None, | ||||
|                 card_sub_type: None, | ||||
|                 card_issuing_country: None, | ||||
|             }, | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| async fn card_info_flow( | ||||
|     record: cards_info_api_types::CardInfoUpdateRequest, | ||||
|     state: routes::SessionState, | ||||
| ) -> RouterResponse<cards_info_api_types::CardInfoMigrateResponseRecord> { | ||||
|     let builder = CardInfoBuilder::new(); | ||||
|     let executor = CardInfoMigrateExecutor::new(&state, &record); | ||||
|     let fetched_card_info_details = executor.fetch_card_info().await?; | ||||
|  | ||||
|     let builder = match fetched_card_info_details { | ||||
|         Some(card_info) => { | ||||
|             let builder = builder.set_card_info(card_info); | ||||
|             let updated_card_info = executor.update_card_info().await?; | ||||
|             builder.set_updated_card_info(updated_card_info) | ||||
|         } | ||||
|         None => { | ||||
|             let builder = builder.transition(); | ||||
|             let added_card_info = executor.add_card_info().await?; | ||||
|             builder.set_added_card_info(added_card_info) | ||||
|         } | ||||
|     }; | ||||
|  | ||||
|     Ok(ApplicationResponse::Json(builder.build())) | ||||
| } | ||||
|  | ||||
| @ -6,7 +6,7 @@ use crate::{ | ||||
|     core::errors::{self, CustomResult}, | ||||
|     db::MockDb, | ||||
|     services::Store, | ||||
|     types::storage::cards_info::CardInfo, | ||||
|     types::storage::cards_info::{CardInfo, UpdateCardInfo}, | ||||
| }; | ||||
|  | ||||
| #[async_trait::async_trait] | ||||
| @ -15,6 +15,12 @@ pub trait CardsInfoInterface { | ||||
|         &self, | ||||
|         _card_iin: &str, | ||||
|     ) -> CustomResult<Option<CardInfo>, errors::StorageError>; | ||||
|     async fn add_card_info(&self, data: CardInfo) -> CustomResult<CardInfo, errors::StorageError>; | ||||
|     async fn update_card_info( | ||||
|         &self, | ||||
|         card_iin: String, | ||||
|         data: UpdateCardInfo, | ||||
|     ) -> CustomResult<CardInfo, errors::StorageError>; | ||||
| } | ||||
|  | ||||
| #[async_trait::async_trait] | ||||
| @ -29,6 +35,26 @@ impl CardsInfoInterface for Store { | ||||
|             .await | ||||
|             .map_err(|error| report!(errors::StorageError::from(error))) | ||||
|     } | ||||
|  | ||||
|     #[instrument(skip_all)] | ||||
|     async fn add_card_info(&self, data: CardInfo) -> CustomResult<CardInfo, errors::StorageError> { | ||||
|         let conn = connection::pg_connection_write(self).await?; | ||||
|         data.insert(&conn) | ||||
|             .await | ||||
|             .map_err(|error| report!(errors::StorageError::from(error))) | ||||
|     } | ||||
|  | ||||
|     #[instrument(skip_all)] | ||||
|     async fn update_card_info( | ||||
|         &self, | ||||
|         card_iin: String, | ||||
|         data: UpdateCardInfo, | ||||
|     ) -> CustomResult<CardInfo, errors::StorageError> { | ||||
|         let conn = connection::pg_connection_write(self).await?; | ||||
|         CardInfo::update(&conn, card_iin, data) | ||||
|             .await | ||||
|             .map_err(|error| report!(errors::StorageError::from(error))) | ||||
|     } | ||||
| } | ||||
|  | ||||
| #[async_trait::async_trait] | ||||
| @ -46,4 +72,16 @@ impl CardsInfoInterface for MockDb { | ||||
|             .find(|ci| ci.card_iin == card_iin) | ||||
|             .cloned()) | ||||
|     } | ||||
|  | ||||
|     async fn add_card_info(&self, _data: CardInfo) -> CustomResult<CardInfo, errors::StorageError> { | ||||
|         Err(errors::StorageError::MockDbError)? | ||||
|     } | ||||
|  | ||||
|     async fn update_card_info( | ||||
|         &self, | ||||
|         _card_iin: String, | ||||
|         _data: UpdateCardInfo, | ||||
|     ) -> CustomResult<CardInfo, errors::StorageError> { | ||||
|         Err(errors::StorageError::MockDbError)? | ||||
|     } | ||||
| } | ||||
|  | ||||
| @ -291,6 +291,21 @@ impl CardsInfoInterface for KafkaStore { | ||||
|     ) -> CustomResult<Option<storage::CardInfo>, errors::StorageError> { | ||||
|         self.diesel_store.get_card_info(card_iin).await | ||||
|     } | ||||
|  | ||||
|     async fn add_card_info( | ||||
|         &self, | ||||
|         data: storage::CardInfo, | ||||
|     ) -> CustomResult<storage::CardInfo, errors::StorageError> { | ||||
|         self.diesel_store.add_card_info(data).await | ||||
|     } | ||||
|  | ||||
|     async fn update_card_info( | ||||
|         &self, | ||||
|         card_iin: String, | ||||
|         data: storage::UpdateCardInfo, | ||||
|     ) -> CustomResult<storage::CardInfo, errors::StorageError> { | ||||
|         self.diesel_store.update_card_info(card_iin, data).await | ||||
|     } | ||||
| } | ||||
|  | ||||
| #[async_trait::async_trait] | ||||
|  | ||||
| @ -71,7 +71,9 @@ use crate::analytics::AnalyticsProvider; | ||||
| #[cfg(feature = "partial-auth")] | ||||
| use crate::errors::RouterResult; | ||||
| #[cfg(feature = "v1")] | ||||
| use crate::routes::cards_info::card_iin_info; | ||||
| use crate::routes::cards_info::{ | ||||
|     card_iin_info, create_cards_info, migrate_cards_info, update_cards_info, | ||||
| }; | ||||
| #[cfg(all(feature = "olap", feature = "v1"))] | ||||
| use crate::routes::feature_matrix; | ||||
| #[cfg(all(feature = "frm", feature = "oltp"))] | ||||
| @ -1794,11 +1796,14 @@ impl Disputes { | ||||
|  | ||||
| pub struct Cards; | ||||
|  | ||||
| #[cfg(feature = "v1")] | ||||
| #[cfg(all(feature = "oltp", feature = "v1"))] | ||||
| impl Cards { | ||||
|     pub fn server(state: AppState) -> Scope { | ||||
|         web::scope("/cards") | ||||
|             .app_data(web::Data::new(state)) | ||||
|             .service(web::resource("/create").route(web::post().to(create_cards_info))) | ||||
|             .service(web::resource("/update").route(web::post().to(update_cards_info))) | ||||
|             .service(web::resource("/update-batch").route(web::post().to(migrate_cards_info))) | ||||
|             .service(web::resource("/{bin}").route(web::get().to(card_iin_info))) | ||||
|     } | ||||
| } | ||||
|  | ||||
| @ -1,4 +1,6 @@ | ||||
| use actix_web::{web, HttpRequest, Responder}; | ||||
| use actix_multipart::form::MultipartForm; | ||||
| use actix_web::{web, HttpRequest, HttpResponse, Responder}; | ||||
| use api_models::cards_info as cards_info_api_types; | ||||
| use router_env::{instrument, tracing, Flow}; | ||||
|  | ||||
| use super::app::AppState; | ||||
| @ -55,3 +57,70 @@ pub async fn card_iin_info( | ||||
|     )) | ||||
|     .await | ||||
| } | ||||
|  | ||||
| #[instrument(skip_all, fields(flow = ?Flow::CardsInfoCreate))] | ||||
| pub async fn create_cards_info( | ||||
|     state: web::Data<AppState>, | ||||
|     req: HttpRequest, | ||||
|     json_payload: web::Json<cards_info_api_types::CardInfoCreateRequest>, | ||||
| ) -> impl Responder { | ||||
|     let payload = json_payload.into_inner(); | ||||
|     let flow = Flow::CardsInfoCreate; | ||||
|     Box::pin(api::server_wrap( | ||||
|         flow, | ||||
|         state.clone(), | ||||
|         &req, | ||||
|         payload, | ||||
|         |state, _, payload, _| cards_info::create_card_info(state, payload), | ||||
|         &auth::AdminApiAuth, | ||||
|         api_locking::LockAction::NotApplicable, | ||||
|     )) | ||||
|     .await | ||||
| } | ||||
|  | ||||
| #[instrument(skip_all, fields(flow = ?Flow::CardsInfoUpdate))] | ||||
| pub async fn update_cards_info( | ||||
|     state: web::Data<AppState>, | ||||
|     req: HttpRequest, | ||||
|     json_payload: web::Json<cards_info_api_types::CardInfoUpdateRequest>, | ||||
| ) -> impl Responder { | ||||
|     let payload = json_payload.into_inner(); | ||||
|     let flow = Flow::CardsInfoUpdate; | ||||
|     Box::pin(api::server_wrap( | ||||
|         flow, | ||||
|         state.clone(), | ||||
|         &req, | ||||
|         payload, | ||||
|         |state, _, payload, _| cards_info::update_card_info(state, payload), | ||||
|         &auth::AdminApiAuth, | ||||
|         api_locking::LockAction::NotApplicable, | ||||
|     )) | ||||
|     .await | ||||
| } | ||||
|  | ||||
| #[cfg(all( | ||||
|     any(feature = "v1", feature = "v2", feature = "olap", feature = "oltp"), | ||||
|     not(feature = "customer_v2") | ||||
| ))] | ||||
| #[instrument(skip_all, fields(flow = ?Flow::CardsInfoMigrate))] | ||||
| pub async fn migrate_cards_info( | ||||
|     state: web::Data<AppState>, | ||||
|     req: HttpRequest, | ||||
|     MultipartForm(form): MultipartForm<cards_info::CardsInfoUpdateForm>, | ||||
| ) -> HttpResponse { | ||||
|     let flow = Flow::CardsInfoMigrate; | ||||
|     let records = match cards_info::get_cards_bin_records(form) { | ||||
|         Ok(records) => records, | ||||
|         Err(e) => return api::log_and_return_error_response(e.into()), | ||||
|     }; | ||||
|     Box::pin(api::server_wrap( | ||||
|         flow, | ||||
|         state.clone(), | ||||
|         &req, | ||||
|         records, | ||||
|         |state, _, payload, _| cards_info::migrate_cards_info(state, payload), | ||||
|         &auth::AdminApiAuth, | ||||
|         api_locking::LockAction::NotApplicable, | ||||
|     )) | ||||
|     .await | ||||
| } | ||||
|  | ||||
| @ -196,7 +196,10 @@ impl From<Flow> for ApiIdentifier { | ||||
|             | Flow::DisputesAggregate | ||||
|             | Flow::DeleteDisputeEvidence => Self::Disputes, | ||||
|  | ||||
|             Flow::CardsInfo => Self::CardsInfo, | ||||
|             Flow::CardsInfo | ||||
|             | Flow::CardsInfoCreate | ||||
|             | Flow::CardsInfoUpdate | ||||
|             | Flow::CardsInfoMigrate => Self::CardsInfo, | ||||
|  | ||||
|             Flow::CreateFile | Flow::DeleteFile | Flow::RetrieveFile => Self::Files, | ||||
|  | ||||
|  | ||||
| @ -1 +1 @@ | ||||
| pub use diesel_models::cards_info::CardInfo; | ||||
| pub use diesel_models::cards_info::{CardInfo, UpdateCardInfo}; | ||||
|  | ||||
| @ -1,8 +1,8 @@ | ||||
| // use actix_web::HttpMessage; | ||||
| use actix_web::http::header::HeaderMap; | ||||
| use api_models::{ | ||||
|     enums as api_enums, gsm as gsm_api_types, payment_methods, payments, | ||||
|     routing::ConnectorSelection, | ||||
|     cards_info as card_info_types, enums as api_enums, gsm as gsm_api_types, payment_methods, | ||||
|     payments, routing::ConnectorSelection, | ||||
| }; | ||||
| use common_utils::{ | ||||
|     consts::X_HS_LATENCY, | ||||
| @ -2269,3 +2269,41 @@ impl ForeignFrom<diesel_models::business_profile::BusinessGenericLinkConfig> | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl ForeignFrom<card_info_types::CardInfoCreateRequest> for storage::CardInfo { | ||||
|     fn foreign_from(value: card_info_types::CardInfoCreateRequest) -> Self { | ||||
|         Self { | ||||
|             card_iin: value.card_iin, | ||||
|             card_issuer: value.card_issuer, | ||||
|             card_network: value.card_network, | ||||
|             card_type: value.card_type, | ||||
|             card_subtype: value.card_subtype, | ||||
|             card_issuing_country: value.card_issuing_country, | ||||
|             bank_code_id: value.bank_code_id, | ||||
|             bank_code: value.bank_code, | ||||
|             country_code: value.country_code, | ||||
|             date_created: common_utils::date_time::now(), | ||||
|             last_updated: Some(common_utils::date_time::now()), | ||||
|             last_updated_provider: value.last_updated_provider, | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl ForeignFrom<card_info_types::CardInfoUpdateRequest> for storage::CardInfo { | ||||
|     fn foreign_from(value: card_info_types::CardInfoUpdateRequest) -> Self { | ||||
|         Self { | ||||
|             card_iin: value.card_iin, | ||||
|             card_issuer: value.card_issuer, | ||||
|             card_network: value.card_network, | ||||
|             card_type: value.card_type, | ||||
|             card_subtype: value.card_subtype, | ||||
|             card_issuing_country: value.card_issuing_country, | ||||
|             bank_code_id: value.bank_code_id, | ||||
|             bank_code: value.bank_code, | ||||
|             country_code: value.country_code, | ||||
|             date_created: common_utils::date_time::now(), | ||||
|             last_updated: Some(common_utils::date_time::now()), | ||||
|             last_updated_provider: value.last_updated_provider, | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
		Reference in New Issue
	
	Block a user
	 Mrudul Vajpayee
					Mrudul Vajpayee