Skip to content

Commit

Permalink
refactor(refund): add validation for charge options
Browse files Browse the repository at this point in the history
  • Loading branch information
kashif-m committed May 22, 2024
1 parent a47534b commit 33cb687
Show file tree
Hide file tree
Showing 3 changed files with 113 additions and 52 deletions.
82 changes: 80 additions & 2 deletions crates/router/src/core/refunds.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,9 @@ use std::collections::HashMap;

#[cfg(feature = "olap")]
use api_models::admin::MerchantConnectorInfo;
use common_utils::ext_traits::AsyncExt;
use common_utils::ext_traits::{AsyncExt, ValueExt};
use error_stack::{report, ResultExt};
use masking::PeekInterface;
use router_env::{instrument, tracing};
use scheduler::{consumer::types::process_data, utils as process_tracker_utils};
#[cfg(feature = "olap")]
Expand All @@ -16,7 +17,7 @@ use crate::{
consts,
core::{
errors::{self, ConnectorErrorExt, RouterResponse, RouterResult, StorageErrorExt},
payments::{self, access_token},
payments::{self, access_token, types::PaymentCharges},
utils as core_utils,
},
db, logger,
Expand All @@ -28,6 +29,7 @@ use crate::{
domain,
storage::{self, enums},
transformers::{ForeignFrom, ForeignInto},
ChargeRefunds,
},
utils::{self, OptionExt},
workflows::payment_sync,
Expand Down Expand Up @@ -128,6 +130,7 @@ pub async fn refund_create_core(
.map(services::ApplicationResponse::Json)
}

#[allow(clippy::too_many_arguments)]
#[instrument(skip_all)]
pub async fn trigger_refund_to_gateway(
state: &AppState,
Expand All @@ -137,6 +140,7 @@ pub async fn trigger_refund_to_gateway(
payment_attempt: &storage::PaymentAttempt,
payment_intent: &storage::PaymentIntent,
creds_identifier: Option<String>,
charges: Option<ChargeRefunds>,
) -> RouterResult<storage::Refund> {
let routed_through = payment_attempt
.connector
Expand Down Expand Up @@ -179,6 +183,7 @@ pub async fn trigger_refund_to_gateway(
payment_attempt,
refund,
creds_identifier,
charges,
)
.await?;

Expand Down Expand Up @@ -458,6 +463,7 @@ pub async fn sync_refund_with_gateway(
payment_attempt,
refund,
creds_identifier,
None,
)
.await?;

Expand Down Expand Up @@ -588,6 +594,39 @@ pub async fn validate_and_create_refund(
) -> RouterResult<refunds::RefundResponse> {
let db = &*state.store;

// Validate charge_id and refund options
let charges = match (
payment_intent.charges.as_ref(),
payment_attempt.charge_id.as_ref(),
) {
(Some(charges), Some(charge_id)) => {
let refund_charge_request = req.charges.clone().get_required_value("charges")?;
utils::when(*charge_id != refund_charge_request.charge_id, || {
Err(report!(errors::ApiErrorResponse::InvalidDataValue {
field_name: "charges.charge_id"
}))
.attach_printable("charge_id sent in request mismatches with original charge_id")
})?;
let payment_charges: PaymentCharges = charges
.peek()
.clone()
.parse_value("PaymentCharges")
.change_context(errors::ApiErrorResponse::InternalServerError)
.attach_printable("Failed to parse charges in to PaymentCharges")?;
let options = validator::validate_charge_refund(
&refund_charge_request,
&payment_charges.charge_type,
)?;
Some(ChargeRefunds {
charge_id: charge_id.to_string(),
charge_type: payment_charges.charge_type,
transfer_account_id: payment_charges.transfer_account_id,
options,
})
}
_ => None,
};

// Only for initial dev and testing
let refund_type = req.refund_type.unwrap_or_default();

Expand Down Expand Up @@ -695,6 +734,7 @@ pub async fn validate_and_create_refund(
payment_attempt,
payment_intent,
creds_identifier,
charges,
)
.await?
}
Expand Down Expand Up @@ -867,6 +907,7 @@ pub async fn schedule_refund_execution(
payment_attempt: &storage::PaymentAttempt,
payment_intent: &storage::PaymentIntent,
creds_identifier: Option<String>,
charges: Option<ChargeRefunds>,
) -> RouterResult<storage::Refund> {
// refunds::RefundResponse> {
let db = &*state.store;
Expand Down Expand Up @@ -903,6 +944,7 @@ pub async fn schedule_refund_execution(
payment_attempt,
payment_intent,
creds_identifier,
charges,
)
.await
}
Expand Down Expand Up @@ -1082,6 +1124,41 @@ pub async fn trigger_refund_execute_workflow(
.await
.to_not_found_response(errors::ApiErrorResponse::PaymentNotFound)?;

let charges = match (
payment_intent.charges.as_ref(),
payment_attempt.charge_id.as_ref(),
) {
(Some(charges), Some(charge_id)) => {
let refund_charge_request =
refund.charges.clone().get_required_value("charges")?;
utils::when(*charge_id != refund_charge_request.charge_id, || {
Err(report!(errors::ApiErrorResponse::InvalidDataValue {
field_name: "charges.charge_id"
}))
.attach_printable(
"charge_id sent in request mismatches with original charge_id",
)
})?;
let payment_charges: PaymentCharges = charges
.peek()
.clone()
.parse_value("PaymentCharges")
.change_context(errors::ApiErrorResponse::InternalServerError)
.attach_printable("Failed to parse charges in to PaymentCharges")?;
let options = validator::validate_charge_refund(
&refund_charge_request,
&payment_charges.charge_type,
)?;
Some(ChargeRefunds {
charge_id: charge_id.to_string(),
charge_type: payment_charges.charge_type,
transfer_account_id: payment_charges.transfer_account_id,
options,
})
}
_ => None,
};

//trigger refund request to gateway
let updated_refund = trigger_refund_to_gateway(
state,
Expand All @@ -1091,6 +1168,7 @@ pub async fn trigger_refund_execute_workflow(
&payment_attempt,
&payment_intent,
None,
charges,
)
.await?;
add_refund_sync_task(
Expand Down
31 changes: 30 additions & 1 deletion crates/router/src/core/refunds/validator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,11 @@ use time::PrimitiveDateTime;

use crate::{
core::errors::{self, CustomResult, RouterResult},
types::storage::{self, enums},
types::{
self,
api::enums as api_enums,
storage::{self, enums},
},
utils::{self, OptionExt},
};

Expand Down Expand Up @@ -144,3 +148,28 @@ pub fn validate_for_valid_refunds(
_ => Ok(()),
}
}

pub fn validate_charge_refund(
charges: &common_utils::types::ChargeRefunds,
charge_type: &api_enums::PaymentChargeType,
) -> RouterResult<types::ChargeRefundsOptions> {
match charge_type {
api_enums::PaymentChargeType::Stripe(api_enums::StripeChargeType::Direct) => Ok(
types::ChargeRefundsOptions::Direct(types::DirectChargeRefund {
revert_platform_fee: charges
.revert_platform_fee
.get_required_value("revert_platform_fee")?,
}),
),
api_enums::PaymentChargeType::Stripe(api_enums::StripeChargeType::Destination) => Ok(
types::ChargeRefundsOptions::Destination(types::DestinationChargeRefund {
revert_platform_fee: charges
.revert_platform_fee
.get_required_value("revert_platform_fee")?,
revert_transfer: charges
.revert_transfer
.get_required_value("revert_transfer")?,
}),
),
}
}
52 changes: 3 additions & 49 deletions crates/router/src/core/utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,16 +22,13 @@ use crate::core::payments;
use crate::{
configs::Settings,
consts,
core::{
errors::{self, RouterResult, StorageErrorExt},
payments::types::PaymentCharges,
},
core::errors::{self, RouterResult, StorageErrorExt},
db::StorageInterface,
routes::AppState,
types::{
self, domain,
storage::{self, enums},
ChargeRefunds, PollConfig,
PollConfig,
},
utils::{generate_id, generate_uuid, OptionExt, ValueExt},
};
Expand Down Expand Up @@ -225,6 +222,7 @@ pub async fn construct_refund_router_data<'a, F>(
payment_attempt: &storage::PaymentAttempt,
refund: &'a storage::Refund,
creds_identifier: Option<String>,
charges: Option<types::ChargeRefunds>,
) -> RouterResult<types::RefundsRouterData<F>> {
let profile_id = get_profile_id_from_business_details(
payment_intent.business_country,
Expand Down Expand Up @@ -301,50 +299,6 @@ pub async fn construct_refund_router_data<'a, F>(
field_name: "browser_info",
})?;

let charges = match (
payment_intent.charges.as_ref(),
payment_attempt.charge_id.as_ref(),
) {
(Some(charges), Some(charge_id)) => {
let payment_charges: PaymentCharges = charges
.peek()
.clone()
.parse_value("PaymentCharges")
.change_context(errors::ApiErrorResponse::InternalServerError)
.attach_printable("Failed to parse charges in to PaymentCharges")?;

let refund_request = refund.charges.clone().get_required_value("charges")?;

let options = match payment_charges.charge_type {
api_models::enums::PaymentChargeType::Stripe(
api_models::enums::StripeChargeType::Direct,
) => types::ChargeRefundsOptions::Direct(types::DirectChargeRefund {
revert_platform_fee: refund_request
.revert_platform_fee
.get_required_value("revert_platform_fee")?,
}),
api_models::enums::PaymentChargeType::Stripe(
api_models::enums::StripeChargeType::Destination,
) => types::ChargeRefundsOptions::Destination(types::DestinationChargeRefund {
revert_platform_fee: refund_request
.revert_platform_fee
.get_required_value("revert_platform_fee")?,
revert_transfer: refund_request
.revert_transfer
.get_required_value("revert_transfer")?,
}),
};

Some(ChargeRefunds {
charge_id: charge_id.to_string(),
charge_type: payment_charges.charge_type,
transfer_account_id: payment_charges.transfer_account_id,
options,
})
}
_ => None,
};

let router_data = types::RouterData {
flow: PhantomData,
merchant_id: merchant_account.merchant_id.clone(),
Expand Down

0 comments on commit 33cb687

Please sign in to comment.