fix: api lock on PaymentsCreate (#2916)

This commit is contained in:
Kartikeya Hegde
2023-11-20 20:52:56 +05:30
committed by GitHub
parent 922dc90019
commit cfabfa60db
7 changed files with 87 additions and 62 deletions

View File

@ -1679,6 +1679,20 @@ impl std::fmt::Display for PaymentIdType {
} }
} }
impl PaymentIdType {
pub fn and_then<F, E>(self, f: F) -> Result<Self, E>
where
F: FnOnce(String) -> Result<String, E>,
{
match self {
Self::PaymentIntentId(s) => f(s).map(Self::PaymentIntentId),
Self::ConnectorTransactionId(s) => f(s).map(Self::ConnectorTransactionId),
Self::PaymentAttemptId(s) => f(s).map(Self::PaymentAttemptId),
Self::PreprocessingId(s) => f(s).map(Self::PreprocessingId),
}
}
}
impl Default for PaymentIdType { impl Default for PaymentIdType {
fn default() -> Self { fn default() -> Self {
Self::PaymentIntentId(Default::default()) Self::PaymentIntentId(Default::default())

View File

@ -3,7 +3,7 @@ use std::marker::PhantomData;
use api_models::enums::FrmSuggestion; use api_models::enums::FrmSuggestion;
use async_trait::async_trait; use async_trait::async_trait;
use data_models::mandates::MandateData; use data_models::mandates::MandateData;
use error_stack::ResultExt; use error_stack::{report, IntoReport, ResultExt};
use router_derive::PaymentOperation; use router_derive::PaymentOperation;
use router_env::{instrument, tracing}; use router_env::{instrument, tracing};
@ -399,15 +399,6 @@ impl<F: Send + Clone, Ctx: PaymentMethodRetrieve> ValidateRequest<F, api::Paymen
BoxedOperation<'b, F, api::PaymentsRequest, Ctx>, BoxedOperation<'b, F, api::PaymentsRequest, Ctx>,
operations::ValidateResult<'a>, operations::ValidateResult<'a>,
)> { )> {
let given_payment_id = match &request.payment_id {
Some(id_type) => Some(
id_type
.get_payment_intent_id()
.change_context(errors::ApiErrorResponse::PaymentNotFound)?,
),
None => None,
};
let request_merchant_id = request.merchant_id.as_deref(); let request_merchant_id = request.merchant_id.as_deref();
helpers::validate_merchant_id(&merchant_account.merchant_id, request_merchant_id) helpers::validate_merchant_id(&merchant_account.merchant_id, request_merchant_id)
.change_context(errors::ApiErrorResponse::InvalidDataFormat { .change_context(errors::ApiErrorResponse::InvalidDataFormat {
@ -419,13 +410,18 @@ impl<F: Send + Clone, Ctx: PaymentMethodRetrieve> ValidateRequest<F, api::Paymen
let mandate_type = let mandate_type =
helpers::validate_mandate(request, payments::is_operation_confirm(self))?; helpers::validate_mandate(request, payments::is_operation_confirm(self))?;
let payment_id = core_utils::get_or_generate_id("payment_id", &given_payment_id, "pay")?; let payment_id = request
.payment_id
.clone()
.ok_or(report!(errors::ApiErrorResponse::PaymentNotFound))?;
Ok(( Ok((
Box::new(self), Box::new(self),
operations::ValidateResult { operations::ValidateResult {
merchant_id: &merchant_account.merchant_id, merchant_id: &merchant_account.merchant_id,
payment_id: api::PaymentIdType::PaymentIntentId(payment_id), payment_id: payment_id
.and_then(|id| core_utils::validate_id(id, "payment_id"))
.into_report()?,
mandate_type, mandate_type,
storage_scheme: merchant_account.storage_scheme, storage_scheme: merchant_account.storage_scheme,
requeue: matches!( requeue: matches!(

View File

@ -2,7 +2,7 @@ use std::marker::PhantomData;
use api_models::enums::FrmSuggestion; use api_models::enums::FrmSuggestion;
use async_trait::async_trait; use async_trait::async_trait;
use error_stack::ResultExt; use error_stack::{report, IntoReport, ResultExt};
use router_derive::PaymentOperation; use router_derive::PaymentOperation;
use router_env::{instrument, tracing}; use router_env::{instrument, tracing};
@ -374,14 +374,10 @@ impl<F: Send + Clone, Ctx: PaymentMethodRetrieve> ValidateRequest<F, api::Paymen
BoxedOperation<'b, F, api::PaymentsRequest, Ctx>, BoxedOperation<'b, F, api::PaymentsRequest, Ctx>,
operations::ValidateResult<'a>, operations::ValidateResult<'a>,
)> { )> {
let given_payment_id = match &request.payment_id { let payment_id = request
Some(id_type) => Some( .payment_id
id_type .clone()
.get_payment_intent_id() .ok_or(report!(errors::ApiErrorResponse::PaymentNotFound))?;
.change_context(errors::ApiErrorResponse::PaymentNotFound)?,
),
None => None,
};
let request_merchant_id = request.merchant_id.as_deref(); let request_merchant_id = request.merchant_id.as_deref();
helpers::validate_merchant_id(&merchant_account.merchant_id, request_merchant_id) helpers::validate_merchant_id(&merchant_account.merchant_id, request_merchant_id)
@ -394,13 +390,14 @@ impl<F: Send + Clone, Ctx: PaymentMethodRetrieve> ValidateRequest<F, api::Paymen
let mandate_type = let mandate_type =
helpers::validate_mandate(request, payments::is_operation_confirm(self))?; helpers::validate_mandate(request, payments::is_operation_confirm(self))?;
let payment_id = core_utils::get_or_generate_id("payment_id", &given_payment_id, "pay")?;
Ok(( Ok((
Box::new(self), Box::new(self),
operations::ValidateResult { operations::ValidateResult {
merchant_id: &merchant_account.merchant_id, merchant_id: &merchant_account.merchant_id,
payment_id: api::PaymentIdType::PaymentIntentId(payment_id), payment_id: payment_id
.and_then(|id| core_utils::validate_id(id, "payment_id"))
.into_report()?,
mandate_type, mandate_type,
storage_scheme: merchant_account.storage_scheme, storage_scheme: merchant_account.storage_scheme,
requeue: matches!( requeue: matches!(

View File

@ -6,7 +6,7 @@ use api_models::{
}; };
use async_trait::async_trait; use async_trait::async_trait;
use common_utils::ext_traits::{AsyncExt, Encode}; use common_utils::ext_traits::{AsyncExt, Encode};
use error_stack::ResultExt; use error_stack::{report, IntoReport, ResultExt};
use futures::FutureExt; use futures::FutureExt;
use redis_interface::errors::RedisError; use redis_interface::errors::RedisError;
use router_derive::PaymentOperation; use router_derive::PaymentOperation;
@ -19,7 +19,7 @@ use crate::{
errors::{self, CustomResult, RouterResult, StorageErrorExt}, errors::{self, CustomResult, RouterResult, StorageErrorExt},
payment_methods::PaymentMethodRetrieve, payment_methods::PaymentMethodRetrieve,
payments::{self, helpers, operations, CustomerDetails, PaymentAddress, PaymentData}, payments::{self, helpers, operations, CustomerDetails, PaymentAddress, PaymentData},
utils::get_individual_surcharge_detail_from_redis, utils::{self as core_utils, get_individual_surcharge_detail_from_redis},
}, },
db::StorageInterface, db::StorageInterface,
routes::AppState, routes::AppState,
@ -820,14 +820,6 @@ impl<F: Send + Clone, Ctx: PaymentMethodRetrieve> ValidateRequest<F, api::Paymen
operations::ValidateResult<'a>, operations::ValidateResult<'a>,
)> { )> {
helpers::validate_customer_details_in_request(request)?; helpers::validate_customer_details_in_request(request)?;
let given_payment_id = match &request.payment_id {
Some(id_type) => Some(
id_type
.get_payment_intent_id()
.change_context(errors::ApiErrorResponse::PaymentNotFound)?,
),
None => None,
};
let request_merchant_id = request.merchant_id.as_deref(); let request_merchant_id = request.merchant_id.as_deref();
helpers::validate_merchant_id(&merchant_account.merchant_id, request_merchant_id) helpers::validate_merchant_id(&merchant_account.merchant_id, request_merchant_id)
@ -840,14 +832,19 @@ impl<F: Send + Clone, Ctx: PaymentMethodRetrieve> ValidateRequest<F, api::Paymen
let mandate_type = let mandate_type =
helpers::validate_mandate(request, payments::is_operation_confirm(self))?; helpers::validate_mandate(request, payments::is_operation_confirm(self))?;
let payment_id =
crate::core::utils::get_or_generate_id("payment_id", &given_payment_id, "pay")?; let payment_id = request
.payment_id
.clone()
.ok_or(report!(errors::ApiErrorResponse::PaymentNotFound))?;
Ok(( Ok((
Box::new(self), Box::new(self),
operations::ValidateResult { operations::ValidateResult {
merchant_id: &merchant_account.merchant_id, merchant_id: &merchant_account.merchant_id,
payment_id: api::PaymentIdType::PaymentIntentId(payment_id), payment_id: payment_id
.and_then(|id| core_utils::validate_id(id, "payment_id"))
.into_report()?,
mandate_type, mandate_type,
storage_scheme: merchant_account.storage_scheme, storage_scheme: merchant_account.storage_scheme,
requeue: matches!( requeue: matches!(

View File

@ -529,14 +529,9 @@ impl<F: Send + Clone, Ctx: PaymentMethodRetrieve> ValidateRequest<F, api::Paymen
)?; )?;
} }
let given_payment_id = match &request.payment_id { let payment_id = request.payment_id.clone().ok_or(error_stack::report!(
Some(id_type) => Some( errors::ApiErrorResponse::PaymentNotFound
id_type ))?;
.get_payment_intent_id()
.change_context(errors::ApiErrorResponse::PaymentNotFound)?,
),
None => None,
};
let request_merchant_id = request.merchant_id.as_deref(); let request_merchant_id = request.merchant_id.as_deref();
helpers::validate_merchant_id(&merchant_account.merchant_id, request_merchant_id) helpers::validate_merchant_id(&merchant_account.merchant_id, request_merchant_id)
@ -555,8 +550,6 @@ impl<F: Send + Clone, Ctx: PaymentMethodRetrieve> ValidateRequest<F, api::Paymen
helpers::validate_payment_method_fields_present(request)?; helpers::validate_payment_method_fields_present(request)?;
let payment_id = core_utils::get_or_generate_id("payment_id", &given_payment_id, "pay")?;
let mandate_type = let mandate_type =
helpers::validate_mandate(request, payments::is_operation_confirm(self))?; helpers::validate_mandate(request, payments::is_operation_confirm(self))?;
@ -583,7 +576,7 @@ impl<F: Send + Clone, Ctx: PaymentMethodRetrieve> ValidateRequest<F, api::Paymen
Box::new(self), Box::new(self),
operations::ValidateResult { operations::ValidateResult {
merchant_id: &merchant_account.merchant_id, merchant_id: &merchant_account.merchant_id,
payment_id: api::PaymentIdType::PaymentIntentId(payment_id), payment_id,
mandate_type, mandate_type,
storage_scheme: merchant_account.storage_scheme, storage_scheme: merchant_account.storage_scheme,
requeue: matches!( requeue: matches!(

View File

@ -3,7 +3,7 @@ use std::marker::PhantomData;
use api_models::enums::FrmSuggestion; use api_models::enums::FrmSuggestion;
use async_trait::async_trait; use async_trait::async_trait;
use common_utils::ext_traits::{AsyncExt, Encode, ValueExt}; use common_utils::ext_traits::{AsyncExt, Encode, ValueExt};
use error_stack::ResultExt; use error_stack::{report, IntoReport, ResultExt};
use router_derive::PaymentOperation; use router_derive::PaymentOperation;
use router_env::{instrument, tracing}; use router_env::{instrument, tracing};
@ -607,14 +607,10 @@ impl<F: Send + Clone, Ctx: PaymentMethodRetrieve> ValidateRequest<F, api::Paymen
operations::ValidateResult<'a>, operations::ValidateResult<'a>,
)> { )> {
helpers::validate_customer_details_in_request(request)?; helpers::validate_customer_details_in_request(request)?;
let given_payment_id = match &request.payment_id { let payment_id = request
Some(id_type) => Some( .payment_id
id_type .clone()
.get_payment_intent_id() .ok_or(report!(errors::ApiErrorResponse::PaymentNotFound))?;
.change_context(errors::ApiErrorResponse::PaymentNotFound)?,
),
None => None,
};
let request_merchant_id = request.merchant_id.as_deref(); let request_merchant_id = request.merchant_id.as_deref();
helpers::validate_merchant_id(&merchant_account.merchant_id, request_merchant_id) helpers::validate_merchant_id(&merchant_account.merchant_id, request_merchant_id)
@ -635,13 +631,14 @@ impl<F: Send + Clone, Ctx: PaymentMethodRetrieve> ValidateRequest<F, api::Paymen
helpers::validate_payment_method_fields_present(request)?; helpers::validate_payment_method_fields_present(request)?;
let mandate_type = helpers::validate_mandate(request, false)?; let mandate_type = helpers::validate_mandate(request, false)?;
let payment_id = core_utils::get_or_generate_id("payment_id", &given_payment_id, "pay")?;
Ok(( Ok((
Box::new(self), Box::new(self),
operations::ValidateResult { operations::ValidateResult {
merchant_id: &merchant_account.merchant_id, merchant_id: &merchant_account.merchant_id,
payment_id: api::PaymentIdType::PaymentIntentId(payment_id), payment_id: payment_id
.and_then(|id| core_utils::validate_id(id, "payment_id"))
.into_report()?,
mandate_type, mandate_type,
storage_scheme: merchant_account.storage_scheme, storage_scheme: merchant_account.storage_scheme,
requeue: matches!( requeue: matches!(

View File

@ -3,15 +3,16 @@ pub mod helpers;
use actix_web::{web, Responder}; use actix_web::{web, Responder};
use api_models::payments::HeaderPayload; use api_models::payments::HeaderPayload;
use error_stack::report; use error_stack::{report, IntoReport};
use router_env::{env, instrument, tracing, types, Flow}; use router_env::{env, instrument, tracing, types, Flow};
use crate::{ use crate::{
self as app, self as app,
core::{ core::{
errors::http_not_implemented, errors::{self, http_not_implemented},
payment_methods::{Oss, PaymentMethodRetrieve}, payment_methods::{Oss, PaymentMethodRetrieve},
payments::{self, PaymentRedirectFlow}, payments::{self, PaymentRedirectFlow},
utils as core_utils,
}, },
// openapi::examples::{ // openapi::examples::{
// PAYMENTS_CREATE, PAYMENTS_CREATE_MINIMUM_FIELDS, PAYMENTS_CREATE_WITH_ADDRESS, // PAYMENTS_CREATE, PAYMENTS_CREATE_MINIMUM_FIELDS, PAYMENTS_CREATE_WITH_ADDRESS,
@ -22,7 +23,10 @@ use crate::{
routes::lock_utils, routes::lock_utils,
services::{api, authentication as auth}, services::{api, authentication as auth},
types::{ types::{
api::{self as api_types, enums as api_enums, payments as payment_types}, api::{
self as api_types, enums as api_enums,
payments::{self as payment_types, PaymentIdTypeExt},
},
domain, domain,
transformers::ForeignTryFrom, transformers::ForeignTryFrom,
}, },
@ -94,12 +98,16 @@ pub async fn payments_create(
json_payload: web::Json<payment_types::PaymentsRequest>, json_payload: web::Json<payment_types::PaymentsRequest>,
) -> impl Responder { ) -> impl Responder {
let flow = Flow::PaymentsCreate; let flow = Flow::PaymentsCreate;
let payload = json_payload.into_inner(); let mut payload = json_payload.into_inner();
if let Some(api_enums::CaptureMethod::Scheduled) = payload.capture_method { if let Some(api_enums::CaptureMethod::Scheduled) = payload.capture_method {
return http_not_implemented(); return http_not_implemented();
}; };
if let Err(err) = get_or_generate_payment_id(&mut payload) {
return api::log_and_return_error_response(err);
}
let locking_action = payload.get_locking_input(flow.clone()); let locking_action = payload.get_locking_input(flow.clone());
Box::pin(api::server_wrap( Box::pin(api::server_wrap(
@ -959,6 +967,29 @@ where
} }
} }
pub fn get_or_generate_payment_id(
payload: &mut payment_types::PaymentsRequest,
) -> errors::RouterResult<()> {
let given_payment_id = payload
.payment_id
.clone()
.map(|payment_id| {
payment_id
.get_payment_intent_id()
.map_err(|err| err.change_context(errors::ApiErrorResponse::PaymentNotFound))
})
.transpose()?;
let payment_id =
core_utils::get_or_generate_id("payment_id", &given_payment_id, "pay").into_report()?;
payload.payment_id = Some(api_models::payments::PaymentIdType::PaymentIntentId(
payment_id,
));
Ok(())
}
impl GetLockingInput for payment_types::PaymentsRequest { impl GetLockingInput for payment_types::PaymentsRequest {
fn get_locking_input<F>(&self, flow: F) -> api_locking::LockAction fn get_locking_input<F>(&self, flow: F) -> api_locking::LockAction
where where