fix(webhooks): send stripe compatible webhooks for stripe compatible merchants (#1986)

This commit is contained in:
Abhishek Marrivagu
2023-08-23 16:24:18 +05:30
committed by GitHub
parent 698677263b
commit 36631ad97b
8 changed files with 138 additions and 57 deletions

View File

@ -69,7 +69,7 @@ pub struct MerchantAccountCreate {
/// You can specify up to 50 keys, with key names up to 40 characters long and values up to 500 characters long. Metadata is useful for storing additional, structured information on an object.
#[schema(value_type = Option<Object>, example = r#"{ "city": "NY", "unit": "245" }"#)]
pub metadata: Option<pii::SecretSerdeValue>,
pub metadata: Option<MerchantAccountMetadata>,
/// API key that will be used for server side API access
#[schema(example = "AH3423bkjbkjdsfbkj")]
@ -96,6 +96,13 @@ pub struct MerchantAccountCreate {
pub organization_id: Option<String>,
}
#[derive(Clone, Debug, Deserialize, Serialize, ToSchema)]
pub struct MerchantAccountMetadata {
pub compatible_connector: Option<api_enums::Connector>,
#[serde(flatten)]
pub data: Option<pii::SecretSerdeValue>,
}
#[derive(Clone, Debug, Deserialize, ToSchema)]
#[serde(deny_unknown_fields)]
pub struct MerchantAccountUpdate {

View File

@ -30,7 +30,7 @@ pub async fn customer_create(
let flow = Flow::CustomersCreate;
wrap::compatibility_api_wrap::<
Box::pin(wrap::compatibility_api_wrap::<
_,
_,
_,
@ -48,7 +48,7 @@ pub async fn customer_create(
customers::create_customer(&*state.store, auth.merchant_account, auth.key_store, req)
},
&auth::ApiKeyAuth,
)
))
.await
}
@ -64,7 +64,7 @@ pub async fn customer_retrieve(
let flow = Flow::CustomersRetrieve;
wrap::compatibility_api_wrap::<
Box::pin(wrap::compatibility_api_wrap::<
_,
_,
_,
@ -82,7 +82,7 @@ pub async fn customer_retrieve(
customers::retrieve_customer(&*state.store, auth.merchant_account, auth.key_store, req)
},
&auth::ApiKeyAuth,
)
))
.await
}
@ -107,7 +107,7 @@ pub async fn customer_update(
let flow = Flow::CustomersUpdate;
wrap::compatibility_api_wrap::<
Box::pin(wrap::compatibility_api_wrap::<
_,
_,
_,
@ -125,7 +125,7 @@ pub async fn customer_update(
customers::update_customer(&*state.store, auth.merchant_account, req, auth.key_store)
},
&auth::ApiKeyAuth,
)
))
.await
}
@ -141,7 +141,7 @@ pub async fn customer_delete(
let flow = Flow::CustomersDelete;
wrap::compatibility_api_wrap::<
Box::pin(wrap::compatibility_api_wrap::<
_,
_,
_,
@ -159,7 +159,7 @@ pub async fn customer_delete(
customers::delete_customer(state, auth.merchant_account, req, auth.key_store)
},
&auth::ApiKeyAuth,
)
))
.await
}
@ -174,7 +174,7 @@ pub async fn list_customer_payment_method_api(
let customer_id = path.into_inner();
let flow = Flow::CustomerPaymentMethodsList;
wrap::compatibility_api_wrap::<
Box::pin(wrap::compatibility_api_wrap::<
_,
_,
_,
@ -198,6 +198,6 @@ pub async fn list_customer_payment_method_api(
)
},
&auth::ApiKeyAuth,
)
))
.await
}

View File

@ -35,7 +35,7 @@ pub async fn payment_intents_create(
let flow = Flow::PaymentsCreate;
wrap::compatibility_api_wrap::<
Box::pin(wrap::compatibility_api_wrap::<
_,
_,
_,
@ -61,7 +61,7 @@ pub async fn payment_intents_create(
)
},
&auth::ApiKeyAuth,
)
))
.await
}
@ -91,7 +91,7 @@ pub async fn payment_intents_retrieve(
let flow = Flow::PaymentsRetrieve;
wrap::compatibility_api_wrap::<
Box::pin(wrap::compatibility_api_wrap::<
_,
_,
_,
@ -117,7 +117,7 @@ pub async fn payment_intents_retrieve(
)
},
&*auth_type,
)
))
.await
}
@ -152,7 +152,7 @@ pub async fn payment_intents_retrieve_with_gateway_creds(
let flow = Flow::PaymentsRetrieve;
wrap::compatibility_api_wrap::<
Box::pin(wrap::compatibility_api_wrap::<
_,
_,
_,
@ -178,7 +178,7 @@ pub async fn payment_intents_retrieve_with_gateway_creds(
)
},
&*auth_type,
)
))
.await
}
@ -214,7 +214,7 @@ pub async fn payment_intents_update(
let flow = Flow::PaymentsUpdate;
wrap::compatibility_api_wrap::<
Box::pin(wrap::compatibility_api_wrap::<
_,
_,
_,
@ -240,7 +240,7 @@ pub async fn payment_intents_update(
)
},
&*auth_type,
)
))
.await
}
@ -278,7 +278,7 @@ pub async fn payment_intents_confirm(
let flow = Flow::PaymentsConfirm;
wrap::compatibility_api_wrap::<
Box::pin(wrap::compatibility_api_wrap::<
_,
_,
_,
@ -304,7 +304,7 @@ pub async fn payment_intents_confirm(
)
},
&*auth_type,
)
))
.await
}
@ -332,7 +332,7 @@ pub async fn payment_intents_capture(
let flow = Flow::PaymentsCapture;
wrap::compatibility_api_wrap::<
Box::pin(wrap::compatibility_api_wrap::<
_,
_,
_,
@ -358,7 +358,7 @@ pub async fn payment_intents_capture(
)
},
&auth::ApiKeyAuth,
)
))
.await
}
@ -390,7 +390,7 @@ pub async fn payment_intents_cancel(
let flow = Flow::PaymentsCancel;
wrap::compatibility_api_wrap::<
Box::pin(wrap::compatibility_api_wrap::<
_,
_,
_,
@ -416,7 +416,7 @@ pub async fn payment_intents_cancel(
)
},
&*auth_type,
)
))
.await
}
@ -434,7 +434,7 @@ pub async fn payment_intent_list(
let flow = Flow::PaymentsList;
wrap::compatibility_api_wrap::<
Box::pin(wrap::compatibility_api_wrap::<
_,
_,
_,
@ -450,6 +450,6 @@ pub async fn payment_intent_list(
payload,
|state, auth, req| payments::list_payments(&*state.store, auth.merchant_account, req),
&auth::ApiKeyAuth,
)
))
.await
}

View File

@ -31,7 +31,7 @@ pub async fn refund_create(
let flow = Flow::RefundsCreate;
wrap::compatibility_api_wrap::<
Box::pin(wrap::compatibility_api_wrap::<
_,
_,
_,
@ -49,7 +49,7 @@ pub async fn refund_create(
refunds::refund_create_core(state, auth.merchant_account, auth.key_store, req)
},
&auth::ApiKeyAuth,
)
))
.await
}
@ -70,7 +70,7 @@ pub async fn refund_retrieve_with_gateway_creds(
let flow = Flow::RefundsRetrieve;
wrap::compatibility_api_wrap::<
Box::pin(wrap::compatibility_api_wrap::<
_,
_,
_,
@ -94,7 +94,7 @@ pub async fn refund_retrieve_with_gateway_creds(
)
},
&auth::ApiKeyAuth,
)
))
.await
}
@ -112,7 +112,7 @@ pub async fn refund_retrieve(
let flow = Flow::RefundsRetrieve;
wrap::compatibility_api_wrap::<
Box::pin(wrap::compatibility_api_wrap::<
_,
_,
_,
@ -136,7 +136,7 @@ pub async fn refund_retrieve(
)
},
&auth::ApiKeyAuth,
)
))
.await
}
@ -152,7 +152,7 @@ pub async fn refund_update(
let create_refund_update_req: refund_types::RefundUpdateRequest = payload.into();
let flow = Flow::RefundsUpdate;
wrap::compatibility_api_wrap::<
Box::pin(wrap::compatibility_api_wrap::<
_,
_,
_,
@ -170,6 +170,6 @@ pub async fn refund_update(
refunds::refund_update_core(&*state.store, auth.merchant_account, &refund_id, req)
},
&auth::ApiKeyAuth,
)
))
.await
}

View File

@ -39,7 +39,7 @@ pub async fn setup_intents_create(
let flow = Flow::PaymentsCreate;
wrap::compatibility_api_wrap::<
Box::pin(wrap::compatibility_api_wrap::<
_,
_,
_,
@ -65,7 +65,7 @@ pub async fn setup_intents_create(
)
},
&auth::ApiKeyAuth,
)
))
.await
}
@ -95,7 +95,7 @@ pub async fn setup_intents_retrieve(
let flow = Flow::PaymentsRetrieve;
wrap::compatibility_api_wrap::<
Box::pin(wrap::compatibility_api_wrap::<
_,
_,
_,
@ -121,7 +121,7 @@ pub async fn setup_intents_retrieve(
)
},
&*auth_type,
)
))
.await
}
@ -158,7 +158,7 @@ pub async fn setup_intents_update(
let flow = Flow::PaymentsUpdate;
wrap::compatibility_api_wrap::<
Box::pin(wrap::compatibility_api_wrap::<
_,
_,
_,
@ -184,7 +184,7 @@ pub async fn setup_intents_update(
)
},
&*auth_type,
)
))
.await
}
@ -222,7 +222,7 @@ pub async fn setup_intents_confirm(
let flow = Flow::PaymentsConfirm;
wrap::compatibility_api_wrap::<
Box::pin(wrap::compatibility_api_wrap::<
_,
_,
_,
@ -248,6 +248,6 @@ pub async fn setup_intents_confirm(
)
},
&*auth_type,
)
))
.await
}

View File

@ -1,4 +1,4 @@
use api_models::{admin::PrimaryBusinessDetails, enums as api_enums};
use api_models::{admin as admin_types, enums as api_enums};
use common_utils::{
crypto::{generate_cryptographically_secure_random_string, OptionalSecretValue},
date_time,
@ -51,7 +51,8 @@ pub async fn create_merchant_account(
let publishable_key = Some(create_merchant_publishable_key());
let primary_business_details = utils::Encode::<Vec<PrimaryBusinessDetails>>::encode_to_value(
let primary_business_details =
utils::Encode::<Vec<admin_types::PrimaryBusinessDetails>>::encode_to_value(
&req.primary_business_details.unwrap_or_default(),
)
.change_context(errors::ApiErrorResponse::InvalidDataValue {
@ -117,7 +118,17 @@ pub async fn create_merchant_account(
&key_store,
)
.await?;
let metadata = req
.metadata
.as_ref()
.map(|meta| {
utils::Encode::<admin_types::MerchantAccountMetadata>::encode_to_value(meta)
.change_context(errors::ApiErrorResponse::InvalidDataValue {
field_name: "metadata",
})
})
.transpose()?
.map(Secret::new);
let merchant_account = async {
Ok(domain::MerchantAccount {
merchant_id: req.merchant_id,
@ -140,7 +151,7 @@ pub async fn create_merchant_account(
.unwrap_or_default(),
publishable_key,
locker_id: req.locker_id,
metadata: req.metadata,
metadata,
storage_scheme: MerchantStorageScheme::PostgresOnly,
primary_business_details,
created_at: date_time::now(),
@ -231,7 +242,9 @@ pub async fn merchant_account_update(
.primary_business_details
.as_ref()
.map(|primary_business_details| {
utils::Encode::<Vec<PrimaryBusinessDetails>>::encode_to_value(primary_business_details)
utils::Encode::<Vec<admin_types::PrimaryBusinessDetails>>::encode_to_value(
primary_business_details,
)
.change_context(errors::ApiErrorResponse::InvalidDataValue {
field_name: "primary_business_details",
})

View File

@ -7,6 +7,8 @@ use masking::ExposeInterface;
use router_env::{instrument, tracing};
use super::{errors::StorageErrorExt, metrics};
#[cfg(feature = "stripe")]
use crate::compatibility::stripe::webhooks as stripe_webhooks;
use crate::{
consts,
core::{
@ -477,6 +479,49 @@ async fn bank_transfer_webhook_flow<W: types::OutgoingWebhookType>(
Ok(())
}
#[allow(clippy::too_many_arguments)]
#[instrument(skip_all)]
pub async fn create_event_and_trigger_appropriate_outgoing_webhook(
state: AppState,
merchant_account: domain::MerchantAccount,
event_type: enums::EventType,
event_class: enums::EventClass,
intent_reference_id: Option<String>,
primary_object_id: String,
primary_object_type: enums::EventObjectType,
content: api::OutgoingWebhookContent,
) -> CustomResult<(), errors::ApiErrorResponse> {
match merchant_account.get_compatible_connector() {
#[cfg(feature = "stripe")]
Some(api_models::enums::Connector::Stripe) => {
create_event_and_trigger_outgoing_webhook::<stripe_webhooks::StripeOutgoingWebhook>(
state.clone(),
merchant_account,
event_type,
event_class,
intent_reference_id,
primary_object_id,
primary_object_type,
content,
)
.await
}
_ => {
create_event_and_trigger_outgoing_webhook::<api_models::webhooks::OutgoingWebhook>(
state.clone(),
merchant_account,
event_type,
event_class,
intent_reference_id,
primary_object_id,
primary_object_type,
content,
)
.await
}
}
}
#[allow(clippy::too_many_arguments)]
#[instrument(skip_all)]
pub async fn create_event_and_trigger_outgoing_webhook<W: types::OutgoingWebhookType>(

View File

@ -1,11 +1,14 @@
use common_utils::{
crypto::{OptionalEncryptableName, OptionalEncryptableValue},
date_time, pii,
date_time,
ext_traits::ValueExt,
pii,
};
use data_models::MerchantStorageScheme;
use diesel_models::{encryption::Encryption, merchant_account::MerchantAccountUpdateInternal};
use error_stack::ResultExt;
use masking::{PeekInterface, Secret};
use router_env::logger;
use storage_impl::DataModelExt;
use crate::{
@ -236,3 +239,16 @@ impl super::behaviour::Conversion for MerchantAccount {
})
}
}
impl MerchantAccount {
pub fn get_compatible_connector(&self) -> Option<api_models::enums::Connector> {
let metadata: Option<api_models::admin::MerchantAccountMetadata> =
self.metadata.as_ref().and_then(|meta| {
meta.clone()
.parse_value("MerchantAccountMetadata")
.map_err(|err| logger::error!("Failed to deserialize {:?}", err))
.ok()
});
metadata.and_then(|a| a.compatible_connector)
}
}