mirror of
https://github.com/juspay/hyperswitch.git
synced 2025-10-29 00:49:42 +08:00
fix: api lock on PaymentsCreate (#2916)
This commit is contained in:
@ -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())
|
||||||
|
|||||||
@ -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!(
|
||||||
|
|||||||
@ -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!(
|
||||||
|
|||||||
@ -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!(
|
||||||
|
|||||||
@ -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!(
|
||||||
|
|||||||
@ -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!(
|
||||||
|
|||||||
@ -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
|
||||||
|
|||||||
Reference in New Issue
Block a user