mirror of
				https://github.com/juspay/hyperswitch.git
				synced 2025-11-01 02:57:02 +08:00 
			
		
		
		
	feat(router): add attach dispute evidence api (#1070)
Co-authored-by: Sanchith Hegde <22217505+SanchithHegde@users.noreply.github.com>
This commit is contained in:
		 Sai Harsha Vardhan
					Sai Harsha Vardhan
				
			
				
					committed by
					
						 GitHub
						GitHub
					
				
			
			
				
	
			
			
			 GitHub
						GitHub
					
				
			
						parent
						
							cc121d0feb
						
					
				
				
					commit
					a5756aaecf
				
			| @ -1,5 +1,6 @@ | ||||
| use api_models::disputes as dispute_models; | ||||
| use error_stack::ResultExt; | ||||
| use api_models::{disputes as dispute_models, files as files_api_models}; | ||||
| use common_utils::ext_traits::ValueExt; | ||||
| use error_stack::{IntoReport, ResultExt}; | ||||
| use router_env::{instrument, tracing}; | ||||
| pub mod transformers; | ||||
|  | ||||
| @ -8,7 +9,7 @@ use super::{ | ||||
|     metrics, | ||||
| }; | ||||
| use crate::{ | ||||
|     core::{payments, utils}, | ||||
|     core::{files, payments, utils as core_utils}, | ||||
|     routes::AppState, | ||||
|     services, | ||||
|     types::{ | ||||
| @ -18,6 +19,7 @@ use crate::{ | ||||
|         AcceptDisputeRequestData, AcceptDisputeResponse, DefendDisputeRequestData, | ||||
|         DefendDisputeResponse, SubmitEvidenceRequestData, SubmitEvidenceResponse, | ||||
|     }, | ||||
|     utils, | ||||
| }; | ||||
|  | ||||
| #[instrument(skip(state))] | ||||
| @ -111,7 +113,7 @@ pub async fn accept_dispute( | ||||
|         AcceptDisputeRequestData, | ||||
|         AcceptDisputeResponse, | ||||
|     > = connector_data.connector.get_connector_integration(); | ||||
|     let router_data = utils::construct_accept_dispute_router_data( | ||||
|     let router_data = core_utils::construct_accept_dispute_router_data( | ||||
|         state, | ||||
|         &payment_intent, | ||||
|         &payment_attempt, | ||||
| @ -150,7 +152,7 @@ pub async fn accept_dispute( | ||||
|         .await | ||||
|         .change_context(errors::ApiErrorResponse::InternalServerError) | ||||
|         .attach_printable_lazy(|| { | ||||
|             format!("Unable to update dispute with dispute_id: {}", dispute_id) | ||||
|             format!("Unable to update dispute with dispute_id: {dispute_id}") | ||||
|         })?; | ||||
|     let dispute_response = api_models::disputes::DisputeResponse::foreign_from(updated_dispute); | ||||
|     Ok(services::ApplicationResponse::Json(dispute_response)) | ||||
| @ -217,7 +219,7 @@ pub async fn submit_evidence( | ||||
|         SubmitEvidenceRequestData, | ||||
|         SubmitEvidenceResponse, | ||||
|     > = connector_data.connector.get_connector_integration(); | ||||
|     let router_data = utils::construct_submit_evidence_router_data( | ||||
|     let router_data = core_utils::construct_submit_evidence_router_data( | ||||
|         state, | ||||
|         &payment_intent, | ||||
|         &payment_attempt, | ||||
| @ -254,7 +256,7 @@ pub async fn submit_evidence( | ||||
|                 DefendDisputeRequestData, | ||||
|                 DefendDisputeResponse, | ||||
|             > = connector_data.connector.get_connector_integration(); | ||||
|             let defend_dispute_router_data = utils::construct_defend_dispute_router_data( | ||||
|             let defend_dispute_router_data = core_utils::construct_defend_dispute_router_data( | ||||
|                 state, | ||||
|                 &payment_intent, | ||||
|                 &payment_attempt, | ||||
| @ -299,8 +301,80 @@ pub async fn submit_evidence( | ||||
|         .await | ||||
|         .change_context(errors::ApiErrorResponse::InternalServerError) | ||||
|         .attach_printable_lazy(|| { | ||||
|             format!("Unable to update dispute with dispute_id: {}", dispute_id) | ||||
|             format!("Unable to update dispute with dispute_id: {dispute_id}") | ||||
|         })?; | ||||
|     let dispute_response = api_models::disputes::DisputeResponse::foreign_from(updated_dispute); | ||||
|     Ok(services::ApplicationResponse::Json(dispute_response)) | ||||
| } | ||||
|  | ||||
| pub async fn attach_evidence( | ||||
|     state: &AppState, | ||||
|     merchant_account: storage::MerchantAccount, | ||||
|     attach_evidence_request: api::AttachEvidenceRequest, | ||||
| ) -> RouterResponse<files_api_models::CreateFileResponse> { | ||||
|     let db = &state.store; | ||||
|     let dispute_id = attach_evidence_request | ||||
|         .create_file_request | ||||
|         .dispute_id | ||||
|         .clone() | ||||
|         .ok_or(errors::ApiErrorResponse::MissingDisputeId)?; | ||||
|     let dispute = db | ||||
|         .find_dispute_by_merchant_id_dispute_id(&merchant_account.merchant_id, &dispute_id) | ||||
|         .await | ||||
|         .to_not_found_response(errors::ApiErrorResponse::DisputeNotFound { | ||||
|             dispute_id: dispute_id.clone(), | ||||
|         })?; | ||||
|     common_utils::fp_utils::when( | ||||
|         !(dispute.dispute_stage == storage_enums::DisputeStage::Dispute | ||||
|             && dispute.dispute_status == storage_enums::DisputeStatus::DisputeOpened), | ||||
|         || { | ||||
|             metrics::ATTACH_EVIDENCE_DISPUTE_STATUS_VALIDATION_FAILURE_METRIC.add( | ||||
|                 &metrics::CONTEXT, | ||||
|                 1, | ||||
|                 &[], | ||||
|             ); | ||||
|             Err(errors::ApiErrorResponse::DisputeStatusValidationFailed { | ||||
|                 reason: format!( | ||||
|                 "Evidence cannot be attached because the dispute is in {} stage and has {} status", | ||||
|                 dispute.dispute_stage, dispute.dispute_status | ||||
|             ), | ||||
|             }) | ||||
|         }, | ||||
|     )?; | ||||
|     let create_file_response = files::files_create_core( | ||||
|         state, | ||||
|         merchant_account, | ||||
|         attach_evidence_request.create_file_request, | ||||
|     ) | ||||
|     .await?; | ||||
|     let file_id = match &create_file_response { | ||||
|         services::ApplicationResponse::Json(res) => res.file_id.clone(), | ||||
|         _ => Err(errors::ApiErrorResponse::InternalServerError) | ||||
|             .into_report() | ||||
|             .attach_printable("Unexpected response received from files create core")?, | ||||
|     }; | ||||
|     let dispute_evidence: api::DisputeEvidence = dispute | ||||
|         .evidence | ||||
|         .clone() | ||||
|         .parse_value("DisputeEvidence") | ||||
|         .change_context(errors::ApiErrorResponse::InternalServerError) | ||||
|         .attach_printable("Error while parsing dispute evidence record")?; | ||||
|     let updated_dispute_evidence = transformers::update_dispute_evidence( | ||||
|         dispute_evidence, | ||||
|         attach_evidence_request.evidence_type, | ||||
|         file_id, | ||||
|     ); | ||||
|     let update_dispute = storage_models::dispute::DisputeUpdate::EvidenceUpdate { | ||||
|         evidence: utils::Encode::<api::DisputeEvidence>::encode_to_value(&updated_dispute_evidence) | ||||
|             .change_context(errors::ApiErrorResponse::InternalServerError) | ||||
|             .attach_printable("Error while encoding dispute evidence")? | ||||
|             .into(), | ||||
|     }; | ||||
|     db.update_dispute(dispute, update_dispute) | ||||
|         .await | ||||
|         .change_context(errors::ApiErrorResponse::InternalServerError) | ||||
|         .attach_printable_lazy(|| { | ||||
|             format!("Unable to update dispute with dispute_id: {dispute_id}") | ||||
|         })?; | ||||
|     Ok(create_file_response) | ||||
| } | ||||
|  | ||||
| @ -3,7 +3,10 @@ use common_utils::errors::CustomResult; | ||||
| use crate::{ | ||||
|     core::{errors, files::helpers::retrieve_file_and_provider_file_id_from_file_id}, | ||||
|     routes::AppState, | ||||
|     types::{api, SubmitEvidenceRequestData}, | ||||
|     types::{ | ||||
|         api::{self, DisputeEvidence}, | ||||
|         SubmitEvidenceRequestData, | ||||
|     }, | ||||
| }; | ||||
|  | ||||
| pub async fn get_evidence_request_data( | ||||
| @ -134,3 +137,52 @@ pub async fn get_evidence_request_data( | ||||
|         uncategorized_text: evidence_request.uncategorized_text, | ||||
|     }) | ||||
| } | ||||
|  | ||||
| pub fn update_dispute_evidence( | ||||
|     dispute_evidence: DisputeEvidence, | ||||
|     evidence_type: api::EvidenceType, | ||||
|     file_id: String, | ||||
| ) -> DisputeEvidence { | ||||
|     match evidence_type { | ||||
|         api::EvidenceType::CancellationPolicy => DisputeEvidence { | ||||
|             cancellation_policy: Some(file_id), | ||||
|             ..dispute_evidence | ||||
|         }, | ||||
|         api::EvidenceType::CustomerCommunication => DisputeEvidence { | ||||
|             customer_communication: Some(file_id), | ||||
|             ..dispute_evidence | ||||
|         }, | ||||
|         api::EvidenceType::CustomerSignature => DisputeEvidence { | ||||
|             customer_signature: Some(file_id), | ||||
|             ..dispute_evidence | ||||
|         }, | ||||
|         api::EvidenceType::Receipt => DisputeEvidence { | ||||
|             receipt: Some(file_id), | ||||
|             ..dispute_evidence | ||||
|         }, | ||||
|         api::EvidenceType::RefundPolicy => DisputeEvidence { | ||||
|             refund_policy: Some(file_id), | ||||
|             ..dispute_evidence | ||||
|         }, | ||||
|         api::EvidenceType::ServiceDocumentation => DisputeEvidence { | ||||
|             service_documentation: Some(file_id), | ||||
|             ..dispute_evidence | ||||
|         }, | ||||
|         api::EvidenceType::ShippingDocumentation => DisputeEvidence { | ||||
|             shipping_documentation: Some(file_id), | ||||
|             ..dispute_evidence | ||||
|         }, | ||||
|         api::EvidenceType::InvoiceShowingDistinctTransactions => DisputeEvidence { | ||||
|             invoice_showing_distinct_transactions: Some(file_id), | ||||
|             ..dispute_evidence | ||||
|         }, | ||||
|         api::EvidenceType::RecurringTransactionAgreement => DisputeEvidence { | ||||
|             recurring_transaction_agreement: Some(file_id), | ||||
|             ..dispute_evidence | ||||
|         }, | ||||
|         api::EvidenceType::UncategorizedFile => DisputeEvidence { | ||||
|             uncategorized_file: Some(file_id), | ||||
|             ..dispute_evidence | ||||
|         }, | ||||
|     } | ||||
| } | ||||
|  | ||||
| @ -21,8 +21,13 @@ counter_metric!( | ||||
| counter_metric!( | ||||
|     ACCEPT_DISPUTE_STATUS_VALIDATION_FAILURE_METRIC, | ||||
|     GLOBAL_METER | ||||
| ); //No. of status validation fialures while accpeting a dispute | ||||
| ); //No. of status validation failures while accpeting a dispute | ||||
| counter_metric!( | ||||
|     EVIDENCE_SUBMISSION_DISPUTE_STATUS_VALIDATION_FAILURE_METRIC, | ||||
|     GLOBAL_METER | ||||
| ); //No. of status validation fialures while submitting evidence for a dispute | ||||
| ); //No. of status validation failures while submitting evidence for a dispute | ||||
|    //No. of status validation failures while attaching evidence for a dispute | ||||
| counter_metric!( | ||||
|     ATTACH_EVIDENCE_DISPUTE_STATUS_VALIDATION_FAILURE_METRIC, | ||||
|     GLOBAL_METER | ||||
| ); | ||||
|  | ||||
| @ -258,6 +258,7 @@ async fn get_or_update_dispute_object( | ||||
|                 challenge_required_by: dispute_details.challenge_required_by, | ||||
|                 connector_created_at: dispute_details.created_at, | ||||
|                 connector_updated_at: dispute_details.updated_at, | ||||
|                 evidence: None, | ||||
|             }; | ||||
|             state | ||||
|                 .store | ||||
|  | ||||
| @ -424,7 +424,11 @@ impl Disputes { | ||||
|             .app_data(web::Data::new(state)) | ||||
|             .service(web::resource("/list").route(web::get().to(retrieve_disputes_list))) | ||||
|             .service(web::resource("/accept/{dispute_id}").route(web::post().to(accept_dispute))) | ||||
|             .service(web::resource("/evidence").route(web::post().to(submit_dispute_evidence))) | ||||
|             .service( | ||||
|                 web::resource("/evidence") | ||||
|                     .route(web::post().to(submit_dispute_evidence)) | ||||
|                     .route(web::put().to(attach_dispute_evidence)), | ||||
|             ) | ||||
|             .service(web::resource("/{dispute_id}").route(web::get().to(retrieve_dispute))) | ||||
|     } | ||||
| } | ||||
|  | ||||
| @ -1,12 +1,14 @@ | ||||
| use actix_multipart::Multipart; | ||||
| use actix_web::{web, HttpRequest, HttpResponse}; | ||||
| use api_models::disputes as dispute_models; | ||||
| use router_env::{instrument, tracing, Flow}; | ||||
| pub mod utils; | ||||
|  | ||||
| use super::app::AppState; | ||||
| use crate::{ | ||||
|     core::disputes, | ||||
|     services::{api, authentication as auth}, | ||||
|     types::api::disputes::{self as dispute_types}, | ||||
|     types::api::disputes as dispute_types, | ||||
| }; | ||||
|  | ||||
| /// Diputes - Retrieve Dispute | ||||
| @ -154,3 +156,42 @@ pub async fn submit_dispute_evidence( | ||||
|     ) | ||||
|     .await | ||||
| } | ||||
|  | ||||
| /// Disputes - Attach Evidence to Dispute | ||||
| /// | ||||
| /// To attach an evidence file to dispute | ||||
| #[utoipa::path( | ||||
|     put, | ||||
|     path = "/disputes/evidence", | ||||
|     request_body=MultipartRequestWithFile, | ||||
|     responses( | ||||
|         (status = 200, description = "Evidence attached to dispute", body = CreateFileResponse), | ||||
|         (status = 400, description = "Bad Request") | ||||
|     ), | ||||
|     tag = "Disputes", | ||||
|     operation_id = "Attach Evidence to Dispute", | ||||
|     security(("api_key" = [])) | ||||
| )] | ||||
| #[instrument(skip_all, fields(flow = ?Flow::AttachDisputeEvidence))] | ||||
| pub async fn attach_dispute_evidence( | ||||
|     state: web::Data<AppState>, | ||||
|     req: HttpRequest, | ||||
|     payload: Multipart, | ||||
| ) -> HttpResponse { | ||||
|     let flow = Flow::AttachDisputeEvidence; | ||||
|     //Get attach_evidence_request from the multipart request | ||||
|     let attach_evidence_request_result = utils::get_attach_evidence_request(payload).await; | ||||
|     let attach_evidence_request = match attach_evidence_request_result { | ||||
|         Ok(valid_request) => valid_request, | ||||
|         Err(err) => return api::log_and_return_error_response(err), | ||||
|     }; | ||||
|     api::server_wrap( | ||||
|         flow, | ||||
|         state.get_ref(), | ||||
|         &req, | ||||
|         attach_evidence_request, | ||||
|         disputes::attach_evidence, | ||||
|         auth::auth_type(&auth::ApiKeyAuth, &auth::JWTAuth, req.headers()), | ||||
|     ) | ||||
|     .await | ||||
| } | ||||
|  | ||||
							
								
								
									
										102
									
								
								crates/router/src/routes/disputes/utils.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										102
									
								
								crates/router/src/routes/disputes/utils.rs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,102 @@ | ||||
| use actix_multipart::{Field, Multipart}; | ||||
| use actix_web::web::Bytes; | ||||
| use common_utils::{errors::CustomResult, ext_traits::StringExt, fp_utils}; | ||||
| use error_stack::{IntoReport, ResultExt}; | ||||
| use futures::{StreamExt, TryStreamExt}; | ||||
|  | ||||
| use crate::{ | ||||
|     core::{errors, files::helpers}, | ||||
|     types::api::{disputes, files}, | ||||
|     utils::OptionExt, | ||||
| }; | ||||
|  | ||||
| pub async fn parse_evidence_type( | ||||
|     field: &mut Field, | ||||
| ) -> CustomResult<Option<disputes::EvidenceType>, errors::ApiErrorResponse> { | ||||
|     let purpose = helpers::read_string(field).await; | ||||
|     match purpose { | ||||
|         Some(evidence_type) => Ok(Some( | ||||
|             evidence_type | ||||
|                 .parse_enum("Evidence Type") | ||||
|                 .change_context(errors::ApiErrorResponse::InternalServerError) | ||||
|                 .attach_printable("Error parsing evidence type")?, | ||||
|         )), | ||||
|         _ => Ok(None), | ||||
|     } | ||||
| } | ||||
|  | ||||
| pub async fn get_attach_evidence_request( | ||||
|     mut payload: Multipart, | ||||
| ) -> CustomResult<disputes::AttachEvidenceRequest, errors::ApiErrorResponse> { | ||||
|     let mut option_evidence_type: Option<disputes::EvidenceType> = None; | ||||
|     let mut dispute_id: Option<String> = None; | ||||
|  | ||||
|     let mut file_name: Option<String> = None; | ||||
|     let mut file_content: Option<Vec<Bytes>> = None; | ||||
|  | ||||
|     while let Ok(Some(mut field)) = payload.try_next().await { | ||||
|         let content_disposition = field.content_disposition(); | ||||
|         let field_name = content_disposition.get_name(); | ||||
|         // Parse the different parameters expected in the multipart request | ||||
|         match field_name { | ||||
|             Some("file") => { | ||||
|                 file_name = content_disposition.get_filename().map(String::from); | ||||
|                 //Collect the file content and throw error if something fails | ||||
|                 let mut file_data = Vec::new(); | ||||
|                 let mut stream = field.into_stream(); | ||||
|                 while let Some(chunk) = stream.next().await { | ||||
|                     match chunk { | ||||
|                         Ok(bytes) => file_data.push(bytes), | ||||
|                         Err(err) => Err(errors::ApiErrorResponse::InternalServerError) | ||||
|                             .into_report() | ||||
|                             .attach_printable_lazy(|| format!("File parsing error: {err}"))?, | ||||
|                     } | ||||
|                 } | ||||
|                 file_content = Some(file_data) | ||||
|             } | ||||
|             Some("dispute_id") => { | ||||
|                 dispute_id = helpers::read_string(&mut field).await; | ||||
|             } | ||||
|             Some("evidence_type") => { | ||||
|                 option_evidence_type = parse_evidence_type(&mut field).await?; | ||||
|             } | ||||
|             // Can ignore other params | ||||
|             _ => (), | ||||
|         } | ||||
|     } | ||||
|     let evidence_type = option_evidence_type.get_required_value("evidence_type")?; | ||||
|     let file = file_content.get_required_value("file")?.concat().to_vec(); | ||||
|     //Get and validate file size | ||||
|     let file_size: i32 = file | ||||
|         .len() | ||||
|         .try_into() | ||||
|         .into_report() | ||||
|         .change_context(errors::ApiErrorResponse::InternalServerError) | ||||
|         .attach_printable("File size error")?; | ||||
|     // Check if empty file and throw error | ||||
|     fp_utils::when(file_size <= 0, || { | ||||
|         Err(errors::ApiErrorResponse::MissingFile) | ||||
|             .into_report() | ||||
|             .attach_printable("Missing / Invalid file in the request") | ||||
|     })?; | ||||
|     // Get file mime type using 'infer' | ||||
|     let kind = infer::get(&file).ok_or(errors::ApiErrorResponse::MissingFileContentType)?; | ||||
|     let file_type = kind | ||||
|         .mime_type() | ||||
|         .parse::<mime::Mime>() | ||||
|         .into_report() | ||||
|         .change_context(errors::ApiErrorResponse::MissingFileContentType) | ||||
|         .attach_printable("File content type error")?; | ||||
|     let create_file_request = files::CreateFileRequest { | ||||
|         file, | ||||
|         file_name, | ||||
|         file_size, | ||||
|         file_type, | ||||
|         purpose: files::FilePurpose::DisputeEvidence, | ||||
|         dispute_id, | ||||
|     }; | ||||
|     Ok(disputes::AttachEvidenceRequest { | ||||
|         evidence_type, | ||||
|         create_file_request, | ||||
|     }) | ||||
| } | ||||
| @ -22,6 +22,42 @@ pub struct DisputePayload { | ||||
|     pub updated_at: Option<PrimitiveDateTime>, | ||||
| } | ||||
|  | ||||
| #[derive(Default, Debug, Deserialize, Serialize)] | ||||
| pub struct DisputeEvidence { | ||||
|     pub cancellation_policy: Option<String>, | ||||
|     pub customer_communication: Option<String>, | ||||
|     pub customer_signature: Option<String>, | ||||
|     pub receipt: Option<String>, | ||||
|     pub refund_policy: Option<String>, | ||||
|     pub service_documentation: Option<String>, | ||||
|     pub shipping_documentation: Option<String>, | ||||
|     pub invoice_showing_distinct_transactions: Option<String>, | ||||
|     pub recurring_transaction_agreement: Option<String>, | ||||
|     pub uncategorized_file: Option<String>, | ||||
| } | ||||
|  | ||||
| #[derive(Debug, Clone)] | ||||
| pub struct AttachEvidenceRequest { | ||||
|     pub create_file_request: types::api::CreateFileRequest, | ||||
|     pub evidence_type: EvidenceType, | ||||
| } | ||||
|  | ||||
| #[derive(Debug, serde::Deserialize, strum::Display, strum::EnumString, Clone)] | ||||
| #[serde(rename_all = "snake_case")] | ||||
| #[strum(serialize_all = "snake_case")] | ||||
| pub enum EvidenceType { | ||||
|     CancellationPolicy, | ||||
|     CustomerCommunication, | ||||
|     CustomerSignature, | ||||
|     Receipt, | ||||
|     RefundPolicy, | ||||
|     ServiceDocumentation, | ||||
|     ShippingDocumentation, | ||||
|     InvoiceShowingDistinctTransactions, | ||||
|     RecurringTransactionAgreement, | ||||
|     UncategorizedFile, | ||||
| } | ||||
|  | ||||
| #[derive(Debug, Clone)] | ||||
| pub struct Accept; | ||||
|  | ||||
|  | ||||
| @ -176,6 +176,8 @@ pub enum Flow { | ||||
|     RetrieveFile, | ||||
|     /// Dispute Evidence submission flow | ||||
|     DisputesEvidenceSubmit, | ||||
|     /// Attach Dispute Evidence flow | ||||
|     AttachDisputeEvidence, | ||||
| } | ||||
|  | ||||
| /// | ||||
|  | ||||
| @ -1,5 +1,6 @@ | ||||
| use common_utils::custom_serde; | ||||
| use diesel::{AsChangeset, Identifiable, Insertable, Queryable}; | ||||
| use masking::Secret; | ||||
| use serde::Serialize; | ||||
| use time::PrimitiveDateTime; | ||||
|  | ||||
| @ -25,6 +26,7 @@ pub struct DisputeNew { | ||||
|     pub connector_created_at: Option<PrimitiveDateTime>, | ||||
|     pub connector_updated_at: Option<PrimitiveDateTime>, | ||||
|     pub connector: String, | ||||
|     pub evidence: Option<Secret<serde_json::Value>>, | ||||
| } | ||||
|  | ||||
| #[derive(Clone, Debug, Serialize, Identifiable, Queryable)] | ||||
| @ -52,6 +54,7 @@ pub struct Dispute { | ||||
|     #[serde(with = "custom_serde::iso8601")] | ||||
|     pub modified_at: PrimitiveDateTime, | ||||
|     pub connector: String, | ||||
|     pub evidence: Secret<serde_json::Value>, | ||||
| } | ||||
|  | ||||
| #[derive(Debug)] | ||||
| @ -69,19 +72,23 @@ pub enum DisputeUpdate { | ||||
|         dispute_status: storage_enums::DisputeStatus, | ||||
|         connector_status: Option<String>, | ||||
|     }, | ||||
|     EvidenceUpdate { | ||||
|         evidence: Secret<serde_json::Value>, | ||||
|     }, | ||||
| } | ||||
|  | ||||
| #[derive(Clone, Debug, Default, AsChangeset, router_derive::DebugAsDisplay)] | ||||
| #[diesel(table_name = dispute)] | ||||
| pub struct DisputeUpdateInternal { | ||||
|     dispute_stage: Option<storage_enums::DisputeStage>, | ||||
|     dispute_status: storage_enums::DisputeStatus, | ||||
|     dispute_status: Option<storage_enums::DisputeStatus>, | ||||
|     connector_status: Option<String>, | ||||
|     connector_reason: Option<String>, | ||||
|     connector_reason_code: Option<String>, | ||||
|     challenge_required_by: Option<PrimitiveDateTime>, | ||||
|     connector_updated_at: Option<PrimitiveDateTime>, | ||||
|     modified_at: Option<PrimitiveDateTime>, | ||||
|     evidence: Option<Secret<serde_json::Value>>, | ||||
| } | ||||
|  | ||||
| impl From<DisputeUpdate> for DisputeUpdateInternal { | ||||
| @ -97,23 +104,28 @@ impl From<DisputeUpdate> for DisputeUpdateInternal { | ||||
|                 connector_updated_at, | ||||
|             } => Self { | ||||
|                 dispute_stage: Some(dispute_stage), | ||||
|                 dispute_status, | ||||
|                 dispute_status: Some(dispute_status), | ||||
|                 connector_status: Some(connector_status), | ||||
|                 connector_reason, | ||||
|                 connector_reason_code, | ||||
|                 challenge_required_by, | ||||
|                 connector_updated_at, | ||||
|                 modified_at: Some(common_utils::date_time::now()), | ||||
|                 ..Default::default() | ||||
|             }, | ||||
|             DisputeUpdate::StatusUpdate { | ||||
|                 dispute_status, | ||||
|                 connector_status, | ||||
|             } => Self { | ||||
|                 dispute_status, | ||||
|                 dispute_status: Some(dispute_status), | ||||
|                 connector_status, | ||||
|                 modified_at: Some(common_utils::date_time::now()), | ||||
|                 ..Default::default() | ||||
|             }, | ||||
|             DisputeUpdate::EvidenceUpdate { evidence } => Self { | ||||
|                 evidence: Some(evidence), | ||||
|                 ..Default::default() | ||||
|             }, | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| @ -135,6 +135,7 @@ diesel::table! { | ||||
|         created_at -> Timestamp, | ||||
|         modified_at -> Timestamp, | ||||
|         connector -> Varchar, | ||||
|         evidence -> Jsonb, | ||||
|     } | ||||
| } | ||||
|  | ||||
|  | ||||
| @ -0,0 +1 @@ | ||||
| ALTER TABLE dispute DROP COLUMN evidence; | ||||
| @ -0,0 +1,3 @@ | ||||
| -- Your SQL goes here | ||||
| ALTER TABLE dispute | ||||
| ADD COLUMN evidence JSONB NOT NULL DEFAULT '{}'::JSONB; | ||||
		Reference in New Issue
	
	Block a user