diff --git a/crates/api_models/src/disputes.rs b/crates/api_models/src/disputes.rs index ca701ddf9b..e4c38cef86 100644 --- a/crates/api_models/src/disputes.rs +++ b/crates/api_models/src/disputes.rs @@ -79,7 +79,7 @@ pub struct DisputeResponsePaymentsRetrieve { pub created_at: PrimitiveDateTime, } -#[derive(Debug, Serialize, strum::Display, Clone)] +#[derive(Debug, Serialize, Deserialize, strum::Display, Clone)] #[serde(rename_all = "snake_case")] #[strum(serialize_all = "snake_case")] pub enum EvidenceType { @@ -196,3 +196,11 @@ pub struct SubmitEvidenceRequest { /// Any additional evidence statements pub uncategorized_text: Option, } + +#[derive(Clone, Debug, Serialize, Deserialize, ToSchema)] +pub struct DeleteEvidenceRequest { + /// Id of the dispute + pub dispute_id: String, + /// Evidence Type to be deleted + pub evidence_type: EvidenceType, +} diff --git a/crates/api_models/src/events/dispute.rs b/crates/api_models/src/events/dispute.rs index 101dba3ca0..1ea6f4e855 100644 --- a/crates/api_models/src/events/dispute.rs +++ b/crates/api_models/src/events/dispute.rs @@ -1,6 +1,8 @@ use common_utils::events::{ApiEventMetric, ApiEventsType}; -use super::{DisputeResponse, DisputeResponsePaymentsRetrieve, SubmitEvidenceRequest}; +use super::{ + DeleteEvidenceRequest, DisputeResponse, DisputeResponsePaymentsRetrieve, SubmitEvidenceRequest, +}; impl ApiEventMetric for SubmitEvidenceRequest { fn get_api_event_type(&self) -> Option { @@ -23,3 +25,10 @@ impl ApiEventMetric for DisputeResponse { }) } } +impl ApiEventMetric for DeleteEvidenceRequest { + fn get_api_event_type(&self) -> Option { + Some(ApiEventsType::Dispute { + dispute_id: self.dispute_id.clone(), + }) + } +} diff --git a/crates/router/src/core/disputes.rs b/crates/router/src/core/disputes.rs index 535d004e90..285baa33d1 100644 --- a/crates/router/src/core/disputes.rs +++ b/crates/router/src/core/disputes.rs @@ -423,3 +423,43 @@ pub async fn retrieve_dispute_evidence( transformers::get_dispute_evidence_vec(&state, merchant_account, dispute_evidence).await?; Ok(services::ApplicationResponse::Json(dispute_evidence_vec)) } + +pub async fn delete_evidence( + state: AppState, + merchant_account: domain::MerchantAccount, + delete_evidence_request: dispute_models::DeleteEvidenceRequest, +) -> RouterResponse { + let dispute_id = delete_evidence_request.dispute_id.clone(); + let dispute = state + .store + .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(), + })?; + 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::delete_evidence_file(dispute_evidence, delete_evidence_request.evidence_type); + let update_dispute = diesel_models::dispute::DisputeUpdate::EvidenceUpdate { + evidence: utils::Encode::::encode_to_value(&updated_dispute_evidence) + .change_context(errors::ApiErrorResponse::InternalServerError) + .attach_printable("Error while encoding dispute evidence")? + .into(), + }; + state + .store + .update_dispute(dispute, update_dispute) + .await + .to_not_found_response(errors::ApiErrorResponse::DisputeNotFound { + dispute_id: dispute_id.to_owned(), + }) + .attach_printable_lazy(|| { + format!("Unable to update dispute with dispute_id: {dispute_id}") + })?; + Ok(services::ApplicationResponse::StatusOk) +} diff --git a/crates/router/src/core/disputes/transformers.rs b/crates/router/src/core/disputes/transformers.rs index 8fb4f41e98..936184fc99 100644 --- a/crates/router/src/core/disputes/transformers.rs +++ b/crates/router/src/core/disputes/transformers.rs @@ -222,6 +222,54 @@ pub async fn get_dispute_evidence_block( }) } +pub fn delete_evidence_file( + dispute_evidence: DisputeEvidence, + evidence_type: EvidenceType, +) -> DisputeEvidence { + match evidence_type { + EvidenceType::CancellationPolicy => DisputeEvidence { + cancellation_policy: None, + ..dispute_evidence + }, + EvidenceType::CustomerCommunication => DisputeEvidence { + customer_communication: None, + ..dispute_evidence + }, + EvidenceType::CustomerSignature => DisputeEvidence { + customer_signature: None, + ..dispute_evidence + }, + EvidenceType::Receipt => DisputeEvidence { + receipt: None, + ..dispute_evidence + }, + EvidenceType::RefundPolicy => DisputeEvidence { + refund_policy: None, + ..dispute_evidence + }, + EvidenceType::ServiceDocumentation => DisputeEvidence { + service_documentation: None, + ..dispute_evidence + }, + EvidenceType::ShippingDocumentation => DisputeEvidence { + shipping_documentation: None, + ..dispute_evidence + }, + EvidenceType::InvoiceShowingDistinctTransactions => DisputeEvidence { + invoice_showing_distinct_transactions: None, + ..dispute_evidence + }, + EvidenceType::RecurringTransactionAgreement => DisputeEvidence { + recurring_transaction_agreement: None, + ..dispute_evidence + }, + EvidenceType::UncategorizedFile => DisputeEvidence { + uncategorized_file: None, + ..dispute_evidence + }, + } +} + pub async fn get_dispute_evidence_vec( state: &AppState, merchant_account: domain::MerchantAccount, diff --git a/crates/router/src/routes/app.rs b/crates/router/src/routes/app.rs index 5fbb4b3aff..1a8a2d696b 100644 --- a/crates/router/src/routes/app.rs +++ b/crates/router/src/routes/app.rs @@ -848,7 +848,8 @@ impl Disputes { .service( web::resource("/evidence") .route(web::post().to(submit_dispute_evidence)) - .route(web::put().to(attach_dispute_evidence)), + .route(web::put().to(attach_dispute_evidence)) + .route(web::delete().to(delete_dispute_evidence)), ) .service( web::resource("/evidence/{dispute_id}") diff --git a/crates/router/src/routes/disputes.rs b/crates/router/src/routes/disputes.rs index 7bcd8ad351..60f6dd7418 100644 --- a/crates/router/src/routes/disputes.rs +++ b/crates/router/src/routes/disputes.rs @@ -264,3 +264,41 @@ pub async fn retrieve_dispute_evidence( )) .await } + +/// Disputes - Delete Evidence attached to a Dispute +/// +/// To delete an evidence file attached to a dispute +#[utoipa::path( + put, + path = "/disputes/evidence", + request_body=DeleteEvidenceRequest, + responses( + (status = 200, description = "Evidence deleted from a dispute"), + (status = 400, description = "Bad Request") + ), + tag = "Disputes", + operation_id = "Delete Evidence attached to a Dispute", + security(("api_key" = [])) +)] +#[instrument(skip_all, fields(flow = ?Flow::DeleteDisputeEvidence))] +pub async fn delete_dispute_evidence( + state: web::Data, + req: HttpRequest, + json_payload: web::Json, +) -> HttpResponse { + let flow = Flow::DeleteDisputeEvidence; + Box::pin(api::server_wrap( + flow, + state, + &req, + json_payload.into_inner(), + |state, auth, req| disputes::delete_evidence(state, auth.merchant_account, req), + auth::auth_type( + &auth::ApiKeyAuth, + &auth::JWTAuth(Permission::DisputeWrite), + req.headers(), + ), + api_locking::LockAction::NotApplicable, + )) + .await +} diff --git a/crates/router/src/routes/lock_utils.rs b/crates/router/src/routes/lock_utils.rs index 02a45408ba..9393e8ae21 100644 --- a/crates/router/src/routes/lock_utils.rs +++ b/crates/router/src/routes/lock_utils.rs @@ -136,7 +136,8 @@ impl From for ApiIdentifier { | Flow::DisputesList | Flow::DisputesEvidenceSubmit | Flow::AttachDisputeEvidence - | Flow::RetrieveDisputeEvidence => Self::Disputes, + | Flow::RetrieveDisputeEvidence + | Flow::DeleteDisputeEvidence => Self::Disputes, Flow::CardsInfo => Self::CardsInfo, diff --git a/crates/router_env/src/logger/types.rs b/crates/router_env/src/logger/types.rs index 2f4d48bea7..6649b89911 100644 --- a/crates/router_env/src/logger/types.rs +++ b/crates/router_env/src/logger/types.rs @@ -235,6 +235,8 @@ pub enum Flow { CreateConfigKey, /// Attach Dispute Evidence flow AttachDisputeEvidence, + /// Delete Dispute Evidence flow + DeleteDisputeEvidence, /// Retrieve Dispute Evidence flow RetrieveDisputeEvidence, /// Invalidate cache flow