Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(router): add refunds manual-update api #5094

Merged
merged 2 commits into from
Jul 2, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 12 additions & 2 deletions crates/api_models/src/events/refund.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
use common_utils::events::{ApiEventMetric, ApiEventsType};

use crate::refunds::{
RefundListFilters, RefundListMetaData, RefundListRequest, RefundListResponse, RefundRequest,
RefundResponse, RefundUpdateRequest, RefundsRetrieveRequest,
RefundListFilters, RefundListMetaData, RefundListRequest, RefundListResponse,
RefundManualUpdateRequest, RefundRequest, RefundResponse, RefundUpdateRequest,
RefundsRetrieveRequest,
};

impl ApiEventMetric for RefundRequest {
Expand Down Expand Up @@ -44,6 +45,15 @@ impl ApiEventMetric for RefundUpdateRequest {
}
}

impl ApiEventMetric for RefundManualUpdateRequest {
fn get_api_event_type(&self) -> Option<ApiEventsType> {
Some(ApiEventsType::Refund {
payment_id: None,
refund_id: self.refund_id.clone(),
})
}
}

impl ApiEventMetric for RefundListRequest {
fn get_api_event_type(&self) -> Option<ApiEventsType> {
Some(ApiEventsType::ResourceListAPI)
Expand Down
26 changes: 26 additions & 0 deletions crates/api_models/src/refunds.rs
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,21 @@ pub struct RefundUpdateRequest {
pub metadata: Option<pii::SecretSerdeValue>,
}

#[derive(Default, Debug, ToSchema, Clone, Deserialize, Serialize)]
#[serde(deny_unknown_fields)]
pub struct RefundManualUpdateRequest {
#[serde(skip)]
pub refund_id: String,
/// Merchant ID
pub merchant_id: String,
/// The status for refund
pub status: Option<RefundStatus>,
/// The code for the error
pub error_code: Option<String>,
/// The error message
pub error_message: Option<String>,
}

/// To indicate whether to refund needs to be instant or scheduled
#[derive(
Default, Debug, Clone, Copy, ToSchema, Deserialize, Serialize, Eq, PartialEq, strum::Display,
Expand Down Expand Up @@ -244,3 +259,14 @@ impl From<enums::RefundStatus> for RefundStatus {
}
}
}

impl From<RefundStatus> for enums::RefundStatus {
fn from(status: RefundStatus) -> Self {
match status {
RefundStatus::Failed => Self::Failure,
RefundStatus::Review => Self::ManualReview,
RefundStatus::Pending => Self::Pending,
RefundStatus::Succeeded => Self::Success,
}
}
}
18 changes: 18 additions & 0 deletions crates/diesel_models/src/refund.rs
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,12 @@ pub enum RefundUpdate {
refund_error_code: Option<String>,
updated_by: String,
},
ManualUpdate {
refund_status: Option<storage_enums::RefundStatus>,
refund_error_message: Option<String>,
refund_error_code: Option<String>,
updated_by: String,
},
}

#[derive(Clone, Debug, Default, AsChangeset, router_derive::DebugAsDisplay)]
Expand Down Expand Up @@ -201,6 +207,18 @@ impl From<RefundUpdate> for RefundUpdateInternal {
updated_by,
..Default::default()
},
RefundUpdate::ManualUpdate {
refund_status,
refund_error_message,
refund_error_code,
updated_by,
} => Self {
refund_status,
refund_error_message,
refund_error_code,
updated_by,
..Default::default()
},
}
}
}
Expand Down
54 changes: 54 additions & 0 deletions crates/router/src/core/refunds.rs
Original file line number Diff line number Diff line change
Expand Up @@ -836,6 +836,60 @@ pub async fn refund_filter_list(
Ok(services::ApplicationResponse::Json(filter_list))
}

#[instrument(skip_all)]
#[cfg(feature = "olap")]
pub async fn refund_manual_update(
state: SessionState,
req: api_models::refunds::RefundManualUpdateRequest,
) -> RouterResponse<serde_json::Value> {
let key_store = state
.store
.get_merchant_key_store_by_merchant_id(
&req.merchant_id,
&state.store.get_master_key().to_vec().into(),
)
.await
.to_not_found_response(errors::ApiErrorResponse::MerchantAccountNotFound)
.attach_printable("Error while fetching the key store by merchant_id")?;
let merchant_account = state
.store
.find_merchant_account_by_merchant_id(&req.merchant_id, &key_store)
.await
.to_not_found_response(errors::ApiErrorResponse::MerchantAccountNotFound)
.attach_printable("Error while fetching the merchant_account by merchant_id")?;
let refund = state
.store
.find_refund_by_merchant_id_refund_id(
&merchant_account.merchant_id,
&req.refund_id,
merchant_account.storage_scheme,
)
.await
.to_not_found_response(errors::ApiErrorResponse::RefundNotFound)?;
let refund_update = storage::RefundUpdate::ManualUpdate {
refund_status: req.status.map(common_enums::RefundStatus::from),
refund_error_message: req.error_message,
refund_error_code: req.error_code,
updated_by: merchant_account.storage_scheme.to_string(),
};
state
.store
.update_refund(
refund.to_owned(),
refund_update,
merchant_account.storage_scheme,
)
.await
.to_not_found_response(errors::ApiErrorResponse::InternalServerError)
.attach_printable_lazy(|| {
format!(
"Failed while updating refund: refund_id: {}",
refund.refund_id
)
})?;
Ok(services::ApplicationResponse::StatusOk)
}

#[instrument(skip_all)]
#[cfg(feature = "olap")]
pub async fn get_filters_for_refunds(
Expand Down
6 changes: 5 additions & 1 deletion crates/router/src/routes/app.rs
Original file line number Diff line number Diff line change
Expand Up @@ -851,7 +851,11 @@ impl Refunds {
route = route
.service(web::resource("/list").route(web::post().to(refunds_list)))
.service(web::resource("/filter").route(web::post().to(refunds_filter_list)))
.service(web::resource("/v2/filter").route(web::get().to(get_refunds_filters)));
.service(web::resource("/v2/filter").route(web::get().to(get_refunds_filters)))
.service(
web::resource("/{id}/manual-update")
.route(web::put().to(refunds_manual_update)),
);
}
#[cfg(feature = "oltp")]
{
Expand Down
3 changes: 2 additions & 1 deletion crates/router/src/routes/lock_utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,8 @@ impl From<Flow> for ApiIdentifier {
| Flow::RefundsRetrieveForceSync
| Flow::RefundsUpdate
| Flow::RefundsList
| Flow::RefundsFilters => Self::Refunds,
| Flow::RefundsFilters
| Flow::RefundsManualUpdate => Self::Refunds,

Flow::FrmFulfillment
| Flow::IncomingWebhookReceive
Expand Down
23 changes: 23 additions & 0 deletions crates/router/src/routes/refunds.rs
Original file line number Diff line number Diff line change
Expand Up @@ -301,3 +301,26 @@ pub async fn get_refunds_filters(state: web::Data<AppState>, req: HttpRequest) -
))
.await
}

#[instrument(skip_all, fields(flow = ?Flow::RefundsManualUpdate))]
#[cfg(feature = "olap")]
pub async fn refunds_manual_update(
state: web::Data<AppState>,
req: HttpRequest,
payload: web::Json<api_models::refunds::RefundManualUpdateRequest>,
path: web::Path<String>,
) -> HttpResponse {
let flow = Flow::RefundsManualUpdate;
let mut refund_manual_update_req = payload.into_inner();
refund_manual_update_req.refund_id = path.into_inner();
Box::pin(api::server_wrap(
flow,
state,
&req,
refund_manual_update_req,
|state, _auth, req, _| refund_manual_update(state, req),
&auth::AdminApiAuth,
api_locking::LockAction::NotApplicable,
))
.await
}
2 changes: 2 additions & 0 deletions crates/router_env/src/logger/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -452,6 +452,8 @@ pub enum Flow {
ToggleConnectorAgnosticMit,
/// Get the extended card info associated to a payment_id
GetExtendedCardInfo,
/// Manually update the refund details like status, error code, error message etc.
RefundsManualUpdate,
/// Manually update the payment details like status, error code, error message etc.
PaymentsManualUpdate,
}
Expand Down
Loading