feat(router): add webhook source verification support for multiple mca of the same connector (#1897)

This commit is contained in:
Sai Harsha Vardhan
2023-08-10 15:24:17 +05:30
committed by GitHub
parent 29f068b205
commit 3554fec1c1
10 changed files with 230 additions and 30 deletions

View File

@ -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,