mirror of
https://github.com/juspay/hyperswitch.git
synced 2025-10-29 00:49:42 +08:00
fix(core): payment create throws duplicate payment error (#319)
This commit is contained in:
@ -157,6 +157,8 @@ pub enum StripeErrorCode {
|
||||
},
|
||||
#[error(error_type = StripeErrorType::InvalidRequestError, code = "", message = "The mandate information is invalid. {message}")]
|
||||
PaymentIntentMandateInvalid { message: String },
|
||||
#[error(error_type = StripeErrorType::InvalidRequestError, code = "", message = "The payment with the specified payment_id '{payment_id}' already exists in our records.")]
|
||||
DuplicatePayment { payment_id: String },
|
||||
// [#216]: https://github.com/juspay/hyperswitch/issues/216
|
||||
// Implement the remaining stripe error codes
|
||||
|
||||
@ -409,6 +411,9 @@ impl From<errors::ApiErrorResponse> for StripeErrorCode {
|
||||
current_value,
|
||||
states,
|
||||
},
|
||||
errors::ApiErrorResponse::DuplicatePayment { payment_id } => {
|
||||
Self::DuplicatePayment { payment_id }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -451,7 +456,8 @@ impl actix_web::ResponseError for StripeErrorCode {
|
||||
| Self::AddressNotFound
|
||||
| Self::ResourceIdNotFound
|
||||
| Self::PaymentIntentMandateInvalid { .. }
|
||||
| Self::PaymentIntentUnexpectedState { .. } => StatusCode::BAD_REQUEST,
|
||||
| Self::PaymentIntentUnexpectedState { .. }
|
||||
| Self::DuplicatePayment { .. } => StatusCode::BAD_REQUEST,
|
||||
Self::RefundFailed
|
||||
| Self::InternalServerError
|
||||
| Self::MandateActive
|
||||
|
||||
@ -113,6 +113,8 @@ pub enum ApiErrorResponse {
|
||||
DuplicateMerchantConnectorAccount,
|
||||
#[error(error_type = ErrorType::DuplicateRequest, code = "RE_04", message = "The payment method with the specified details already exists in our records.")]
|
||||
DuplicatePaymentMethod,
|
||||
#[error(error_type = ErrorType::DuplicateRequest, code = "RE_04", message = "The payment with the specified payment_id '{payment_id}' already exists in our records.")]
|
||||
DuplicatePayment { payment_id: String },
|
||||
#[error(error_type= ErrorType::InvalidRequestError, code = "RE_05", message = "The payment has not succeeded yet")]
|
||||
PaymentNotSucceeded,
|
||||
#[error(error_type= ErrorType::ObjectNotFound, code = "RE_05", message = "Successful payment not found for the given payment id")]
|
||||
@ -166,7 +168,7 @@ impl actix_web::ResponseError for ApiErrorResponse {
|
||||
| Self::MandateValidationFailed { .. } => StatusCode::BAD_REQUEST, // 400
|
||||
|
||||
Self::InternalServerError => StatusCode::INTERNAL_SERVER_ERROR, // 500
|
||||
Self::DuplicateRefundRequest => StatusCode::BAD_REQUEST, // 400
|
||||
Self::DuplicateRefundRequest | Self::DuplicatePayment { .. } => StatusCode::BAD_REQUEST, // 400
|
||||
Self::RefundNotFound
|
||||
| Self::CustomerNotFound
|
||||
| Self::MandateActive
|
||||
@ -186,9 +188,9 @@ impl actix_web::ResponseError for ApiErrorResponse {
|
||||
| Self::DuplicateMerchantConnectorAccount
|
||||
| Self::DuplicatePaymentMethod
|
||||
| Self::DuplicateMandate => StatusCode::BAD_REQUEST, // 400
|
||||
Self::ReturnUrlUnavailable => StatusCode::SERVICE_UNAVAILABLE, // 503
|
||||
Self::PaymentNotSucceeded => StatusCode::BAD_REQUEST, // 400
|
||||
Self::NotImplemented => StatusCode::NOT_IMPLEMENTED, // 501
|
||||
Self::ReturnUrlUnavailable => StatusCode::SERVICE_UNAVAILABLE, // 503
|
||||
Self::PaymentNotSucceeded => StatusCode::BAD_REQUEST, // 400
|
||||
Self::NotImplemented => StatusCode::NOT_IMPLEMENTED, // 501
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -469,7 +469,6 @@ pub struct CustomerDetails {
|
||||
}
|
||||
|
||||
pub fn if_not_create_change_operation<'a, Op, F>(
|
||||
is_update: bool,
|
||||
status: storage_enums::IntentStatus,
|
||||
confirm: Option<bool>,
|
||||
current: &'a Op,
|
||||
@ -485,13 +484,7 @@ where
|
||||
match status {
|
||||
storage_enums::IntentStatus::RequiresConfirmation
|
||||
| storage_enums::IntentStatus::RequiresCustomerAction
|
||||
| storage_enums::IntentStatus::RequiresPaymentMethod => {
|
||||
if is_update {
|
||||
Box::new(&PaymentUpdate)
|
||||
} else {
|
||||
Box::new(current)
|
||||
}
|
||||
}
|
||||
| storage_enums::IntentStatus::RequiresPaymentMethod => Box::new(current),
|
||||
_ => Box::new(&PaymentStatus),
|
||||
}
|
||||
}
|
||||
|
||||
@ -56,8 +56,6 @@ impl<F: Send + Clone> GetTracker<F, PaymentData<F>, api::PaymentsRequest> for Pa
|
||||
|
||||
let money @ (amount, currency) = payments_create_request_validation(request)?;
|
||||
|
||||
let mut is_update = false;
|
||||
|
||||
let payment_id = payment_id
|
||||
.get_payment_intent_id()
|
||||
.change_context(errors::ApiErrorResponse::PaymentNotFound)?;
|
||||
@ -100,7 +98,7 @@ impl<F: Send + Clone> GetTracker<F, PaymentData<F>, api::PaymentsRequest> for Pa
|
||||
field_name: "browser_info",
|
||||
})?;
|
||||
|
||||
payment_attempt = match db
|
||||
payment_attempt = db
|
||||
.insert_payment_attempt(
|
||||
Self::make_payment_attempt(
|
||||
&payment_id,
|
||||
@ -113,28 +111,13 @@ impl<F: Send + Clone> GetTracker<F, PaymentData<F>, api::PaymentsRequest> for Pa
|
||||
storage_scheme,
|
||||
)
|
||||
.await
|
||||
{
|
||||
Ok(payment_attempt) => Ok(payment_attempt),
|
||||
.map_err(|err| {
|
||||
err.to_duplicate_response(errors::ApiErrorResponse::DuplicatePayment {
|
||||
payment_id: payment_id.clone(),
|
||||
})
|
||||
})?;
|
||||
|
||||
Err(err) => {
|
||||
if err.current_context().is_db_unique_violation() {
|
||||
is_update = true;
|
||||
db.find_payment_attempt_by_payment_id_merchant_id(
|
||||
&payment_id,
|
||||
merchant_id,
|
||||
storage_scheme,
|
||||
)
|
||||
.await
|
||||
.map_err(|error| {
|
||||
error.to_not_found_response(errors::ApiErrorResponse::PaymentNotFound)
|
||||
})
|
||||
} else {
|
||||
Err(err).change_context(errors::ApiErrorResponse::InternalServerError)
|
||||
}
|
||||
}
|
||||
}?;
|
||||
|
||||
payment_intent = match db
|
||||
payment_intent = db
|
||||
.insert_payment_intent(
|
||||
Self::make_payment_intent(
|
||||
&payment_id,
|
||||
@ -147,47 +130,22 @@ impl<F: Send + Clone> GetTracker<F, PaymentData<F>, api::PaymentsRequest> for Pa
|
||||
storage_scheme,
|
||||
)
|
||||
.await
|
||||
{
|
||||
Ok(payment_intent) => Ok(payment_intent),
|
||||
|
||||
Err(err) => {
|
||||
if err.current_context().is_db_unique_violation() {
|
||||
is_update = true;
|
||||
db.find_payment_intent_by_payment_id_merchant_id(
|
||||
&payment_id,
|
||||
merchant_id,
|
||||
storage_scheme,
|
||||
)
|
||||
.await
|
||||
.map_err(|error| {
|
||||
error.to_not_found_response(errors::ApiErrorResponse::PaymentNotFound)
|
||||
})
|
||||
} else {
|
||||
Err(err).change_context(errors::ApiErrorResponse::InternalServerError)
|
||||
}
|
||||
}
|
||||
}?;
|
||||
|
||||
connector_response = match db
|
||||
.map_err(|err| {
|
||||
err.to_duplicate_response(errors::ApiErrorResponse::DuplicatePayment {
|
||||
payment_id: payment_id.clone(),
|
||||
})
|
||||
})?;
|
||||
connector_response = db
|
||||
.insert_connector_response(
|
||||
Self::make_connector_response(&payment_attempt),
|
||||
storage_scheme,
|
||||
)
|
||||
.await
|
||||
{
|
||||
Ok(connector_resp) => Ok(connector_resp),
|
||||
Err(err) => {
|
||||
if err.current_context().is_db_unique_violation() {
|
||||
Err(err)
|
||||
.change_context(errors::ApiErrorResponse::InternalServerError)
|
||||
.attach_printable("Duplicate connector response in the database")
|
||||
} else {
|
||||
Err(err)
|
||||
.change_context(errors::ApiErrorResponse::InternalServerError)
|
||||
.attach_printable("Error occured when inserting connector response")
|
||||
}
|
||||
}
|
||||
}?;
|
||||
.map_err(|err| {
|
||||
err.to_duplicate_response(errors::ApiErrorResponse::DuplicatePayment {
|
||||
payment_id: payment_id.clone(),
|
||||
})
|
||||
})?;
|
||||
|
||||
let mandate_id = request
|
||||
.mandate_id
|
||||
@ -206,7 +164,6 @@ impl<F: Send + Clone> GetTracker<F, PaymentData<F>, api::PaymentsRequest> for Pa
|
||||
.transpose()?;
|
||||
|
||||
let operation = payments::if_not_create_change_operation::<_, F>(
|
||||
is_update,
|
||||
payment_intent.status,
|
||||
request.confirm,
|
||||
self,
|
||||
|
||||
Reference in New Issue
Block a user