mirror of
https://github.com/juspay/hyperswitch.git
synced 2025-10-30 01:27:31 +08:00
feat(router): add webhook source verification support for multiple mca of the same connector (#1897)
This commit is contained in:
committed by
GitHub
parent
29f068b205
commit
3554fec1c1
@ -5,6 +5,7 @@ pub mod ext_traits;
|
||||
#[cfg(feature = "kv_store")]
|
||||
pub mod storage_partitioning;
|
||||
|
||||
use api_models::{payments, webhooks};
|
||||
use base64::Engine;
|
||||
pub use common_utils::{
|
||||
crypto,
|
||||
@ -23,10 +24,11 @@ use uuid::Uuid;
|
||||
pub use self::ext_traits::{OptionExt, ValidateCall};
|
||||
use crate::{
|
||||
consts,
|
||||
core::errors::{self, CustomResult, RouterResult},
|
||||
core::errors::{self, CustomResult, RouterResult, StorageErrorExt},
|
||||
db::StorageInterface,
|
||||
logger,
|
||||
routes::metrics,
|
||||
types,
|
||||
types::{self, domain, storage},
|
||||
};
|
||||
|
||||
pub mod error_parser {
|
||||
@ -180,6 +182,157 @@ mod tests {
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn find_payment_intent_from_payment_id_type(
|
||||
db: &dyn StorageInterface,
|
||||
payment_id_type: payments::PaymentIdType,
|
||||
merchant_account: &domain::MerchantAccount,
|
||||
) -> CustomResult<storage::PaymentIntent, errors::ApiErrorResponse> {
|
||||
match payment_id_type {
|
||||
payments::PaymentIdType::PaymentIntentId(payment_id) => db
|
||||
.find_payment_intent_by_payment_id_merchant_id(
|
||||
&payment_id,
|
||||
&merchant_account.merchant_id,
|
||||
merchant_account.storage_scheme,
|
||||
)
|
||||
.await
|
||||
.to_not_found_response(errors::ApiErrorResponse::PaymentNotFound),
|
||||
payments::PaymentIdType::ConnectorTransactionId(connector_transaction_id) => {
|
||||
let attempt = db
|
||||
.find_payment_attempt_by_merchant_id_connector_txn_id(
|
||||
&merchant_account.merchant_id,
|
||||
&connector_transaction_id,
|
||||
merchant_account.storage_scheme,
|
||||
)
|
||||
.await
|
||||
.to_not_found_response(errors::ApiErrorResponse::PaymentNotFound)?;
|
||||
db.find_payment_intent_by_payment_id_merchant_id(
|
||||
&attempt.payment_id,
|
||||
&merchant_account.merchant_id,
|
||||
merchant_account.storage_scheme,
|
||||
)
|
||||
.await
|
||||
.to_not_found_response(errors::ApiErrorResponse::PaymentNotFound)
|
||||
}
|
||||
payments::PaymentIdType::PaymentAttemptId(attempt_id) => {
|
||||
let attempt = db
|
||||
.find_payment_attempt_by_attempt_id_merchant_id(
|
||||
&attempt_id,
|
||||
&merchant_account.merchant_id,
|
||||
merchant_account.storage_scheme,
|
||||
)
|
||||
.await
|
||||
.to_not_found_response(errors::ApiErrorResponse::PaymentNotFound)?;
|
||||
db.find_payment_intent_by_payment_id_merchant_id(
|
||||
&attempt.payment_id,
|
||||
&merchant_account.merchant_id,
|
||||
merchant_account.storage_scheme,
|
||||
)
|
||||
.await
|
||||
.to_not_found_response(errors::ApiErrorResponse::PaymentNotFound)
|
||||
}
|
||||
payments::PaymentIdType::PreprocessingId(_) => {
|
||||
Err(errors::ApiErrorResponse::PaymentNotFound)?
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn find_payment_intent_from_refund_id_type(
|
||||
db: &dyn StorageInterface,
|
||||
refund_id_type: webhooks::RefundIdType,
|
||||
merchant_account: &domain::MerchantAccount,
|
||||
connector_name: &str,
|
||||
) -> CustomResult<storage::PaymentIntent, errors::ApiErrorResponse> {
|
||||
let refund = match refund_id_type {
|
||||
webhooks::RefundIdType::RefundId(id) => db
|
||||
.find_refund_by_merchant_id_refund_id(
|
||||
&merchant_account.merchant_id,
|
||||
&id,
|
||||
merchant_account.storage_scheme,
|
||||
)
|
||||
.await
|
||||
.to_not_found_response(errors::ApiErrorResponse::RefundNotFound)?,
|
||||
webhooks::RefundIdType::ConnectorRefundId(id) => db
|
||||
.find_refund_by_merchant_id_connector_refund_id_connector(
|
||||
&merchant_account.merchant_id,
|
||||
&id,
|
||||
connector_name,
|
||||
merchant_account.storage_scheme,
|
||||
)
|
||||
.await
|
||||
.to_not_found_response(errors::ApiErrorResponse::RefundNotFound)?,
|
||||
};
|
||||
let attempt = db
|
||||
.find_payment_attempt_by_attempt_id_merchant_id(
|
||||
&refund.attempt_id,
|
||||
&merchant_account.merchant_id,
|
||||
merchant_account.storage_scheme,
|
||||
)
|
||||
.await
|
||||
.to_not_found_response(errors::ApiErrorResponse::PaymentNotFound)?;
|
||||
db.find_payment_intent_by_payment_id_merchant_id(
|
||||
&attempt.payment_id,
|
||||
&merchant_account.merchant_id,
|
||||
merchant_account.storage_scheme,
|
||||
)
|
||||
.await
|
||||
.to_not_found_response(errors::ApiErrorResponse::PaymentNotFound)
|
||||
}
|
||||
|
||||
pub async fn get_connector_label_using_object_reference_id(
|
||||
db: &dyn StorageInterface,
|
||||
object_reference_id: webhooks::ObjectReferenceId,
|
||||
merchant_account: &domain::MerchantAccount,
|
||||
connector_name: &str,
|
||||
) -> CustomResult<String, errors::ApiErrorResponse> {
|
||||
let mut primary_business_details = merchant_account
|
||||
.primary_business_details
|
||||
.clone()
|
||||
.parse_value::<Vec<api_models::admin::PrimaryBusinessDetails>>("PrimaryBusinessDetails")
|
||||
.change_context(errors::ApiErrorResponse::InternalServerError)
|
||||
.attach_printable("failed to parse primary business details")?;
|
||||
//check if there is only one primary business details and get those details
|
||||
let (option_business_country, option_business_label) = match primary_business_details.pop() {
|
||||
Some(business_details) => {
|
||||
if primary_business_details.is_empty() {
|
||||
(
|
||||
Some(business_details.country),
|
||||
Some(business_details.business),
|
||||
)
|
||||
} else {
|
||||
(None, None)
|
||||
}
|
||||
}
|
||||
None => (None, None),
|
||||
};
|
||||
match (option_business_country, option_business_label) {
|
||||
(Some(business_country), Some(business_label)) => Ok(format!(
|
||||
"{connector_name}_{}_{}",
|
||||
business_country, business_label
|
||||
)),
|
||||
_ => {
|
||||
let payment_intent = match object_reference_id {
|
||||
webhooks::ObjectReferenceId::PaymentId(payment_id_type) => {
|
||||
find_payment_intent_from_payment_id_type(db, payment_id_type, merchant_account)
|
||||
.await?
|
||||
}
|
||||
webhooks::ObjectReferenceId::RefundId(refund_id_type) => {
|
||||
find_payment_intent_from_refund_id_type(
|
||||
db,
|
||||
refund_id_type,
|
||||
merchant_account,
|
||||
connector_name,
|
||||
)
|
||||
.await?
|
||||
}
|
||||
};
|
||||
Ok(format!(
|
||||
"{connector_name}_{}_{}",
|
||||
payment_intent.business_country, payment_intent.business_label
|
||||
))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// validate json format for the error
|
||||
pub fn handle_json_response_deserialization_failure(
|
||||
res: types::Response,
|
||||
|
||||
Reference in New Issue
Block a user