mirror of
https://github.com/juspay/hyperswitch.git
synced 2025-10-29 00:49:42 +08:00
feat(router): add retrieve dispute evidence API (#1114)
This commit is contained in:
committed by
GitHub
parent
9f47f20702
commit
354ee0137a
@ -3,6 +3,7 @@ use time::PrimitiveDateTime;
|
||||
use utoipa::ToSchema;
|
||||
|
||||
use super::enums::{DisputeStage, DisputeStatus};
|
||||
use crate::files;
|
||||
|
||||
#[derive(Clone, Debug, Serialize, ToSchema, Eq, PartialEq)]
|
||||
pub struct DisputeResponse {
|
||||
@ -74,6 +75,30 @@ pub struct DisputeResponsePaymentsRetrieve {
|
||||
pub created_at: PrimitiveDateTime,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, strum::Display, 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(Clone, Debug, Serialize, ToSchema)]
|
||||
pub struct DisputeEvidenceBlock {
|
||||
/// Evidence type
|
||||
pub evidence_type: EvidenceType,
|
||||
/// File metadata
|
||||
pub file_metadata_response: files::FileMetadataResponse,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Deserialize, ToSchema)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
pub struct DisputeListConstraints {
|
||||
|
||||
@ -5,3 +5,17 @@ pub struct CreateFileResponse {
|
||||
/// ID of the file created
|
||||
pub file_id: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, serde::Serialize, ToSchema, Clone)]
|
||||
pub struct FileMetadataResponse {
|
||||
/// ID of the file created
|
||||
pub file_id: String,
|
||||
/// Name of the file
|
||||
pub file_name: Option<String>,
|
||||
/// Size of the file
|
||||
pub file_size: i32,
|
||||
/// Type of the file
|
||||
pub file_type: String,
|
||||
/// File availability
|
||||
pub available: bool,
|
||||
}
|
||||
|
||||
@ -378,3 +378,27 @@ pub async fn attach_evidence(
|
||||
})?;
|
||||
Ok(create_file_response)
|
||||
}
|
||||
|
||||
#[instrument(skip(state))]
|
||||
pub async fn retrieve_dispute_evidence(
|
||||
state: &AppState,
|
||||
merchant_account: storage::MerchantAccount,
|
||||
req: disputes::DisputeId,
|
||||
) -> RouterResponse<Vec<api_models::disputes::DisputeEvidenceBlock>> {
|
||||
let dispute = state
|
||||
.store
|
||||
.find_dispute_by_merchant_id_dispute_id(&merchant_account.merchant_id, &req.dispute_id)
|
||||
.await
|
||||
.to_not_found_response(errors::ApiErrorResponse::DisputeNotFound {
|
||||
dispute_id: req.dispute_id,
|
||||
})?;
|
||||
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 dispute_evidence_vec =
|
||||
transformers::get_dispute_evidence_vec(state, merchant_account, dispute_evidence).await?;
|
||||
Ok(services::ApplicationResponse::Json(dispute_evidence_vec))
|
||||
}
|
||||
|
||||
@ -1,10 +1,14 @@
|
||||
use api_models::disputes::EvidenceType;
|
||||
use common_utils::errors::CustomResult;
|
||||
use error_stack::ResultExt;
|
||||
|
||||
use crate::{
|
||||
core::{errors, files::helpers::retrieve_file_and_provider_file_id_from_file_id},
|
||||
routes::AppState,
|
||||
types::{
|
||||
api::{self, DisputeEvidence},
|
||||
storage,
|
||||
transformers::ForeignFrom,
|
||||
SubmitEvidenceRequestData,
|
||||
},
|
||||
};
|
||||
@ -186,3 +190,146 @@ pub fn update_dispute_evidence(
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn get_dispute_evidence_block(
|
||||
state: &AppState,
|
||||
merchant_account: &storage::MerchantAccount,
|
||||
evidence_type: EvidenceType,
|
||||
file_id: String,
|
||||
) -> CustomResult<api_models::disputes::DisputeEvidenceBlock, errors::ApiErrorResponse> {
|
||||
let file_metadata = state
|
||||
.store
|
||||
.find_file_metadata_by_merchant_id_file_id(&merchant_account.merchant_id, &file_id)
|
||||
.await
|
||||
.change_context(errors::ApiErrorResponse::FileNotFound)
|
||||
.attach_printable("Unable to retrieve file_metadata")?;
|
||||
let file_metadata_response =
|
||||
api_models::files::FileMetadataResponse::foreign_from(file_metadata);
|
||||
Ok(api_models::disputes::DisputeEvidenceBlock {
|
||||
evidence_type,
|
||||
file_metadata_response,
|
||||
})
|
||||
}
|
||||
|
||||
pub async fn get_dispute_evidence_vec(
|
||||
state: &AppState,
|
||||
merchant_account: storage::MerchantAccount,
|
||||
dispute_evidence: DisputeEvidence,
|
||||
) -> CustomResult<Vec<api_models::disputes::DisputeEvidenceBlock>, errors::ApiErrorResponse> {
|
||||
let mut dispute_evidence_blocks: Vec<api_models::disputes::DisputeEvidenceBlock> = vec![];
|
||||
if let Some(cancellation_policy_block) = dispute_evidence.cancellation_policy {
|
||||
dispute_evidence_blocks.push(
|
||||
get_dispute_evidence_block(
|
||||
state,
|
||||
&merchant_account,
|
||||
EvidenceType::CancellationPolicy,
|
||||
cancellation_policy_block,
|
||||
)
|
||||
.await?,
|
||||
)
|
||||
}
|
||||
if let Some(customer_communication_block) = dispute_evidence.customer_communication {
|
||||
dispute_evidence_blocks.push(
|
||||
get_dispute_evidence_block(
|
||||
state,
|
||||
&merchant_account,
|
||||
EvidenceType::CustomerCommunication,
|
||||
customer_communication_block,
|
||||
)
|
||||
.await?,
|
||||
)
|
||||
}
|
||||
if let Some(customer_signature_block) = dispute_evidence.customer_signature {
|
||||
dispute_evidence_blocks.push(
|
||||
get_dispute_evidence_block(
|
||||
state,
|
||||
&merchant_account,
|
||||
EvidenceType::CustomerSignature,
|
||||
customer_signature_block,
|
||||
)
|
||||
.await?,
|
||||
)
|
||||
}
|
||||
if let Some(receipt_block) = dispute_evidence.receipt {
|
||||
dispute_evidence_blocks.push(
|
||||
get_dispute_evidence_block(
|
||||
state,
|
||||
&merchant_account,
|
||||
EvidenceType::Receipt,
|
||||
receipt_block,
|
||||
)
|
||||
.await?,
|
||||
)
|
||||
}
|
||||
if let Some(refund_policy_block) = dispute_evidence.refund_policy {
|
||||
dispute_evidence_blocks.push(
|
||||
get_dispute_evidence_block(
|
||||
state,
|
||||
&merchant_account,
|
||||
EvidenceType::RefundPolicy,
|
||||
refund_policy_block,
|
||||
)
|
||||
.await?,
|
||||
)
|
||||
}
|
||||
if let Some(service_documentation_block) = dispute_evidence.service_documentation {
|
||||
dispute_evidence_blocks.push(
|
||||
get_dispute_evidence_block(
|
||||
state,
|
||||
&merchant_account,
|
||||
EvidenceType::ServiceDocumentation,
|
||||
service_documentation_block,
|
||||
)
|
||||
.await?,
|
||||
)
|
||||
}
|
||||
if let Some(shipping_documentation_block) = dispute_evidence.shipping_documentation {
|
||||
dispute_evidence_blocks.push(
|
||||
get_dispute_evidence_block(
|
||||
state,
|
||||
&merchant_account,
|
||||
EvidenceType::ShippingDocumentation,
|
||||
shipping_documentation_block,
|
||||
)
|
||||
.await?,
|
||||
)
|
||||
}
|
||||
if let Some(invoice_showing_distinct_transactions_block) =
|
||||
dispute_evidence.invoice_showing_distinct_transactions
|
||||
{
|
||||
dispute_evidence_blocks.push(
|
||||
get_dispute_evidence_block(
|
||||
state,
|
||||
&merchant_account,
|
||||
EvidenceType::InvoiceShowingDistinctTransactions,
|
||||
invoice_showing_distinct_transactions_block,
|
||||
)
|
||||
.await?,
|
||||
)
|
||||
}
|
||||
if let Some(recurring_transaction_agreement_block) =
|
||||
dispute_evidence.recurring_transaction_agreement
|
||||
{
|
||||
dispute_evidence_blocks.push(
|
||||
get_dispute_evidence_block(
|
||||
state,
|
||||
&merchant_account,
|
||||
EvidenceType::RecurringTransactionAgreement,
|
||||
recurring_transaction_agreement_block,
|
||||
)
|
||||
.await?,
|
||||
)
|
||||
}
|
||||
if let Some(uncategorized_file_block) = dispute_evidence.uncategorized_file {
|
||||
dispute_evidence_blocks.push(
|
||||
get_dispute_evidence_block(
|
||||
state,
|
||||
&merchant_account,
|
||||
EvidenceType::UncategorizedFile,
|
||||
uncategorized_file_block,
|
||||
)
|
||||
.await?,
|
||||
)
|
||||
}
|
||||
Ok(dispute_evidence_blocks)
|
||||
}
|
||||
|
||||
@ -430,6 +430,10 @@ impl Disputes {
|
||||
.route(web::post().to(submit_dispute_evidence))
|
||||
.route(web::put().to(attach_dispute_evidence)),
|
||||
)
|
||||
.service(
|
||||
web::resource("/evidence/{dispute_id}")
|
||||
.route(web::get().to(retrieve_dispute_evidence)),
|
||||
)
|
||||
.service(web::resource("/{dispute_id}").route(web::get().to(retrieve_dispute)))
|
||||
}
|
||||
}
|
||||
|
||||
@ -195,3 +195,39 @@ pub async fn attach_dispute_evidence(
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
/// Diputes - Retrieve Dispute
|
||||
#[utoipa::path(
|
||||
get,
|
||||
path = "/disputes/evidence/{dispute_id}",
|
||||
params(
|
||||
("dispute_id" = String, Path, description = "The identifier for dispute")
|
||||
),
|
||||
responses(
|
||||
(status = 200, description = "The dispute evidence was retrieved successfully", body = DisputeResponse),
|
||||
(status = 404, description = "Dispute does not exist in our records")
|
||||
),
|
||||
tag = "Disputes",
|
||||
operation_id = "Retrieve a Dispute Evidence",
|
||||
security(("api_key" = []))
|
||||
)]
|
||||
#[instrument(skip_all, fields(flow = ?Flow::RetrieveDisputeEvidence))]
|
||||
pub async fn retrieve_dispute_evidence(
|
||||
state: web::Data<AppState>,
|
||||
req: HttpRequest,
|
||||
path: web::Path<String>,
|
||||
) -> HttpResponse {
|
||||
let flow = Flow::RetrieveDisputeEvidence;
|
||||
let dispute_id = dispute_types::DisputeId {
|
||||
dispute_id: path.into_inner(),
|
||||
};
|
||||
api::server_wrap(
|
||||
flow,
|
||||
state.get_ref(),
|
||||
&req,
|
||||
dispute_id,
|
||||
disputes::retrieve_dispute_evidence,
|
||||
auth::auth_type(&auth::ApiKeyAuth, &auth::JWTAuth, req.headers()),
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
@ -526,6 +526,18 @@ impl ForeignFrom<storage::Dispute> for api_models::disputes::DisputeResponsePaym
|
||||
}
|
||||
}
|
||||
|
||||
impl ForeignFrom<storage::FileMetadata> for api_models::files::FileMetadataResponse {
|
||||
fn foreign_from(file_metadata: storage::FileMetadata) -> Self {
|
||||
Self {
|
||||
file_id: file_metadata.file_id,
|
||||
file_name: file_metadata.file_name,
|
||||
file_size: file_metadata.file_size,
|
||||
file_type: file_metadata.file_type,
|
||||
available: file_metadata.available,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ForeignFrom<storage_models::cards_info::CardInfo>
|
||||
for api_models::cards_info::CardInfoResponse
|
||||
{
|
||||
|
||||
@ -180,6 +180,8 @@ pub enum Flow {
|
||||
CreateConfigKey,
|
||||
/// Attach Dispute Evidence flow
|
||||
AttachDisputeEvidence,
|
||||
/// Retrieve Dispute Evidence flow
|
||||
RetrieveDisputeEvidence,
|
||||
}
|
||||
|
||||
///
|
||||
|
||||
Reference in New Issue
Block a user