mirror of
https://github.com/juspay/hyperswitch.git
synced 2025-10-29 00:49:42 +08:00
enhance(router/webhooks): expose additional incoming request details to webhooks flow (#637)
This commit is contained in:
@ -421,21 +421,21 @@ impl
|
||||
impl api::IncomingWebhook for {{project-name | downcase | pascal_case}} {
|
||||
fn get_webhook_object_reference_id(
|
||||
&self,
|
||||
_body: &[u8],
|
||||
_request: &api::IncomingWebhookRequestDetails<'_>,
|
||||
) -> CustomResult<String, errors::ConnectorError> {
|
||||
Err(errors::ConnectorError::WebhooksNotImplemented).into_report()
|
||||
}
|
||||
|
||||
fn get_webhook_event_type(
|
||||
&self,
|
||||
_body: &[u8],
|
||||
_request: &api::IncomingWebhookRequestDetails<'_>,
|
||||
) -> CustomResult<api::IncomingWebhookEvent, errors::ConnectorError> {
|
||||
Err(errors::ConnectorError::WebhooksNotImplemented).into_report()
|
||||
}
|
||||
|
||||
fn get_webhook_resource_object(
|
||||
&self,
|
||||
_body: &[u8],
|
||||
_request: &api::IncomingWebhookRequestDetails<'_>,
|
||||
) -> CustomResult<serde_json::Value, errors::ConnectorError> {
|
||||
Err(errors::ConnectorError::WebhooksNotImplemented).into_report()
|
||||
}
|
||||
|
||||
@ -9,12 +9,14 @@ use crate::{enums as api_enums, payments};
|
||||
pub enum IncomingWebhookEvent {
|
||||
PaymentIntentFailure,
|
||||
PaymentIntentSuccess,
|
||||
EndpointVerification,
|
||||
}
|
||||
|
||||
pub enum WebhookFlow {
|
||||
Payment,
|
||||
Refund,
|
||||
Subscription,
|
||||
ReturnResponse,
|
||||
}
|
||||
|
||||
impl From<IncomingWebhookEvent> for WebhookFlow {
|
||||
@ -22,10 +24,17 @@ impl From<IncomingWebhookEvent> for WebhookFlow {
|
||||
match evt {
|
||||
IncomingWebhookEvent::PaymentIntentFailure => Self::Payment,
|
||||
IncomingWebhookEvent::PaymentIntentSuccess => Self::Payment,
|
||||
IncomingWebhookEvent::EndpointVerification => Self::ReturnResponse,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct IncomingWebhookRequestDetails<'a> {
|
||||
pub method: actix_web::http::Method,
|
||||
pub headers: &'a actix_web::http::header::HeaderMap,
|
||||
pub body: &'a [u8],
|
||||
}
|
||||
|
||||
pub type MerchantWebhookConfig = std::collections::HashSet<IncomingWebhookEvent>;
|
||||
|
||||
pub struct IncomingWebhookDetails {
|
||||
|
||||
@ -530,21 +530,21 @@ impl services::ConnectorIntegration<api::RSync, types::RefundsData, types::Refun
|
||||
impl api::IncomingWebhook for Aci {
|
||||
fn get_webhook_object_reference_id(
|
||||
&self,
|
||||
_body: &[u8],
|
||||
_request: &api::IncomingWebhookRequestDetails<'_>,
|
||||
) -> CustomResult<String, errors::ConnectorError> {
|
||||
Err(errors::ConnectorError::WebhooksNotImplemented).into_report()
|
||||
}
|
||||
|
||||
fn get_webhook_event_type(
|
||||
&self,
|
||||
_body: &[u8],
|
||||
_request: &api::IncomingWebhookRequestDetails<'_>,
|
||||
) -> CustomResult<api::IncomingWebhookEvent, errors::ConnectorError> {
|
||||
Err(errors::ConnectorError::WebhooksNotImplemented).into_report()
|
||||
}
|
||||
|
||||
fn get_webhook_resource_object(
|
||||
&self,
|
||||
_body: &[u8],
|
||||
_request: &api::IncomingWebhookRequestDetails<'_>,
|
||||
) -> CustomResult<serde_json::Value, errors::ConnectorError> {
|
||||
Err(errors::ConnectorError::WebhooksNotImplemented).into_report()
|
||||
}
|
||||
|
||||
@ -656,18 +656,16 @@ fn get_webhook_object_from_body(
|
||||
impl api::IncomingWebhook for Adyen {
|
||||
fn get_webhook_source_verification_algorithm(
|
||||
&self,
|
||||
_headers: &actix_web::http::header::HeaderMap,
|
||||
_body: &[u8],
|
||||
_request: &api::IncomingWebhookRequestDetails<'_>,
|
||||
) -> CustomResult<Box<dyn crypto::VerifySignature + Send>, errors::ConnectorError> {
|
||||
Ok(Box::new(crypto::HmacSha256))
|
||||
}
|
||||
|
||||
fn get_webhook_source_verification_signature(
|
||||
&self,
|
||||
_headers: &actix_web::http::header::HeaderMap,
|
||||
body: &[u8],
|
||||
request: &api::IncomingWebhookRequestDetails<'_>,
|
||||
) -> CustomResult<Vec<u8>, errors::ConnectorError> {
|
||||
let notif_item = get_webhook_object_from_body(body)
|
||||
let notif_item = get_webhook_object_from_body(request.body)
|
||||
.change_context(errors::ConnectorError::WebhookSourceVerificationFailed)?;
|
||||
|
||||
let base64_signature = notif_item.additional_data.hmac_signature;
|
||||
@ -682,12 +680,11 @@ impl api::IncomingWebhook for Adyen {
|
||||
|
||||
fn get_webhook_source_verification_message(
|
||||
&self,
|
||||
_headers: &actix_web::http::header::HeaderMap,
|
||||
body: &[u8],
|
||||
request: &api::IncomingWebhookRequestDetails<'_>,
|
||||
_merchant_id: &str,
|
||||
_secret: &[u8],
|
||||
) -> CustomResult<Vec<u8>, errors::ConnectorError> {
|
||||
let notif = get_webhook_object_from_body(body)
|
||||
let notif = get_webhook_object_from_body(request.body)
|
||||
.change_context(errors::ConnectorError::WebhookSourceVerificationFailed)?;
|
||||
|
||||
let message = format!(
|
||||
@ -721,9 +718,9 @@ impl api::IncomingWebhook for Adyen {
|
||||
|
||||
fn get_webhook_object_reference_id(
|
||||
&self,
|
||||
body: &[u8],
|
||||
request: &api::IncomingWebhookRequestDetails<'_>,
|
||||
) -> CustomResult<String, errors::ConnectorError> {
|
||||
let notif = get_webhook_object_from_body(body)
|
||||
let notif = get_webhook_object_from_body(request.body)
|
||||
.change_context(errors::ConnectorError::WebhookReferenceIdNotFound)?;
|
||||
|
||||
Ok(notif.psp_reference)
|
||||
@ -731,9 +728,9 @@ impl api::IncomingWebhook for Adyen {
|
||||
|
||||
fn get_webhook_event_type(
|
||||
&self,
|
||||
body: &[u8],
|
||||
request: &api::IncomingWebhookRequestDetails<'_>,
|
||||
) -> CustomResult<api::IncomingWebhookEvent, errors::ConnectorError> {
|
||||
let notif = get_webhook_object_from_body(body)
|
||||
let notif = get_webhook_object_from_body(request.body)
|
||||
.change_context(errors::ConnectorError::WebhookEventTypeNotFound)?;
|
||||
|
||||
Ok(match notif.event_code.as_str() {
|
||||
@ -744,9 +741,9 @@ impl api::IncomingWebhook for Adyen {
|
||||
|
||||
fn get_webhook_resource_object(
|
||||
&self,
|
||||
body: &[u8],
|
||||
request: &api::IncomingWebhookRequestDetails<'_>,
|
||||
) -> CustomResult<serde_json::Value, errors::ConnectorError> {
|
||||
let notif = get_webhook_object_from_body(body)
|
||||
let notif = get_webhook_object_from_body(request.body)
|
||||
.change_context(errors::ConnectorError::WebhookEventTypeNotFound)?;
|
||||
|
||||
let response: adyen::AdyenResponse = notif.into();
|
||||
@ -760,6 +757,7 @@ impl api::IncomingWebhook for Adyen {
|
||||
|
||||
fn get_webhook_api_response(
|
||||
&self,
|
||||
_request: &api::IncomingWebhookRequestDetails<'_>,
|
||||
) -> CustomResult<services::api::ApplicationResponse<serde_json::Value>, errors::ConnectorError>
|
||||
{
|
||||
Ok(services::api::ApplicationResponse::TextPlain(
|
||||
|
||||
@ -242,21 +242,21 @@ impl services::ConnectorRedirectResponse for Applepay {}
|
||||
impl api::IncomingWebhook for Applepay {
|
||||
fn get_webhook_object_reference_id(
|
||||
&self,
|
||||
_body: &[u8],
|
||||
_request: &api::IncomingWebhookRequestDetails<'_>,
|
||||
) -> CustomResult<String, errors::ConnectorError> {
|
||||
Err(errors::ConnectorError::WebhooksNotImplemented).into_report()
|
||||
}
|
||||
|
||||
fn get_webhook_event_type(
|
||||
&self,
|
||||
_body: &[u8],
|
||||
_request: &api::IncomingWebhookRequestDetails<'_>,
|
||||
) -> CustomResult<api::IncomingWebhookEvent, errors::ConnectorError> {
|
||||
Err(errors::ConnectorError::WebhooksNotImplemented).into_report()
|
||||
}
|
||||
|
||||
fn get_webhook_resource_object(
|
||||
&self,
|
||||
_body: &[u8],
|
||||
_request: &api::IncomingWebhookRequestDetails<'_>,
|
||||
) -> CustomResult<serde_json::Value, errors::ConnectorError> {
|
||||
Err(errors::ConnectorError::WebhooksNotImplemented).into_report()
|
||||
}
|
||||
|
||||
@ -572,21 +572,21 @@ impl services::ConnectorIntegration<api::RSync, types::RefundsData, types::Refun
|
||||
impl api::IncomingWebhook for Authorizedotnet {
|
||||
fn get_webhook_object_reference_id(
|
||||
&self,
|
||||
_body: &[u8],
|
||||
_request: &api::IncomingWebhookRequestDetails<'_>,
|
||||
) -> CustomResult<String, errors::ConnectorError> {
|
||||
Err(errors::ConnectorError::WebhooksNotImplemented).into_report()
|
||||
}
|
||||
|
||||
fn get_webhook_event_type(
|
||||
&self,
|
||||
_body: &[u8],
|
||||
_request: &api::IncomingWebhookRequestDetails<'_>,
|
||||
) -> CustomResult<api::IncomingWebhookEvent, errors::ConnectorError> {
|
||||
Err(errors::ConnectorError::WebhooksNotImplemented).into_report()
|
||||
}
|
||||
|
||||
fn get_webhook_resource_object(
|
||||
&self,
|
||||
_body: &[u8],
|
||||
_request: &api::IncomingWebhookRequestDetails<'_>,
|
||||
) -> CustomResult<serde_json::Value, errors::ConnectorError> {
|
||||
Err(errors::ConnectorError::WebhooksNotImplemented).into_report()
|
||||
}
|
||||
|
||||
@ -688,21 +688,21 @@ impl services::ConnectorIntegration<api::RSync, types::RefundsData, types::Refun
|
||||
impl api::IncomingWebhook for Braintree {
|
||||
fn get_webhook_object_reference_id(
|
||||
&self,
|
||||
_body: &[u8],
|
||||
_request: &api::IncomingWebhookRequestDetails<'_>,
|
||||
) -> CustomResult<String, errors::ConnectorError> {
|
||||
Err(errors::ConnectorError::NotImplemented("braintree".to_string()).into())
|
||||
}
|
||||
|
||||
fn get_webhook_event_type(
|
||||
&self,
|
||||
_body: &[u8],
|
||||
_request: &api::IncomingWebhookRequestDetails<'_>,
|
||||
) -> CustomResult<api::IncomingWebhookEvent, errors::ConnectorError> {
|
||||
Err(errors::ConnectorError::NotImplemented("braintree".to_string()).into())
|
||||
}
|
||||
|
||||
fn get_webhook_resource_object(
|
||||
&self,
|
||||
_body: &[u8],
|
||||
_request: &api::IncomingWebhookRequestDetails<'_>,
|
||||
) -> CustomResult<serde_json::Value, errors::ConnectorError> {
|
||||
Err(errors::ConnectorError::NotImplemented("braintree".to_string()).into())
|
||||
}
|
||||
|
||||
@ -734,21 +734,21 @@ impl services::ConnectorIntegration<api::RSync, types::RefundsData, types::Refun
|
||||
impl api::IncomingWebhook for Checkout {
|
||||
fn get_webhook_object_reference_id(
|
||||
&self,
|
||||
_body: &[u8],
|
||||
_request: &api::IncomingWebhookRequestDetails<'_>,
|
||||
) -> CustomResult<String, errors::ConnectorError> {
|
||||
Err(errors::ConnectorError::WebhooksNotImplemented).into_report()
|
||||
}
|
||||
|
||||
fn get_webhook_event_type(
|
||||
&self,
|
||||
_body: &[u8],
|
||||
_request: &api::IncomingWebhookRequestDetails<'_>,
|
||||
) -> CustomResult<api::IncomingWebhookEvent, errors::ConnectorError> {
|
||||
Err(errors::ConnectorError::WebhooksNotImplemented).into_report()
|
||||
}
|
||||
|
||||
fn get_webhook_resource_object(
|
||||
&self,
|
||||
_body: &[u8],
|
||||
_request: &api::IncomingWebhookRequestDetails<'_>,
|
||||
) -> CustomResult<serde_json::Value, errors::ConnectorError> {
|
||||
Err(errors::ConnectorError::WebhooksNotImplemented).into_report()
|
||||
}
|
||||
|
||||
@ -696,21 +696,21 @@ impl ConnectorIntegration<api::RSync, types::RefundsData, types::RefundsResponse
|
||||
impl api::IncomingWebhook for Cybersource {
|
||||
fn get_webhook_object_reference_id(
|
||||
&self,
|
||||
_body: &[u8],
|
||||
_request: &api::IncomingWebhookRequestDetails<'_>,
|
||||
) -> CustomResult<String, errors::ConnectorError> {
|
||||
Err(errors::ConnectorError::NotImplemented("cybersource".to_string()).into())
|
||||
}
|
||||
|
||||
fn get_webhook_event_type(
|
||||
&self,
|
||||
_body: &[u8],
|
||||
_request: &api::IncomingWebhookRequestDetails<'_>,
|
||||
) -> CustomResult<api::IncomingWebhookEvent, errors::ConnectorError> {
|
||||
Err(errors::ConnectorError::NotImplemented("cybersource".to_string()).into())
|
||||
}
|
||||
|
||||
fn get_webhook_resource_object(
|
||||
&self,
|
||||
_body: &[u8],
|
||||
_request: &api::IncomingWebhookRequestDetails<'_>,
|
||||
) -> CustomResult<serde_json::Value, errors::ConnectorError> {
|
||||
Err(errors::ConnectorError::NotImplemented("cybersource".to_string()).into())
|
||||
}
|
||||
|
||||
@ -416,21 +416,21 @@ impl services::ConnectorIntegration<api::RSync, types::RefundsData, types::Refun
|
||||
impl api::IncomingWebhook for Fiserv {
|
||||
fn get_webhook_object_reference_id(
|
||||
&self,
|
||||
_body: &[u8],
|
||||
_request: &api::IncomingWebhookRequestDetails<'_>,
|
||||
) -> CustomResult<String, errors::ConnectorError> {
|
||||
Err(errors::ConnectorError::NotImplemented("fiserv".to_string()).into())
|
||||
}
|
||||
|
||||
fn get_webhook_event_type(
|
||||
&self,
|
||||
_body: &[u8],
|
||||
_request: &api::IncomingWebhookRequestDetails<'_>,
|
||||
) -> CustomResult<api::IncomingWebhookEvent, errors::ConnectorError> {
|
||||
Err(errors::ConnectorError::NotImplemented("fiserv".to_string()).into())
|
||||
}
|
||||
|
||||
fn get_webhook_resource_object(
|
||||
&self,
|
||||
_body: &[u8],
|
||||
_request: &api::IncomingWebhookRequestDetails<'_>,
|
||||
) -> CustomResult<serde_json::Value, errors::ConnectorError> {
|
||||
Err(errors::ConnectorError::NotImplemented("fiserv".to_string()).into())
|
||||
}
|
||||
|
||||
@ -693,21 +693,21 @@ impl ConnectorIntegration<api::RSync, types::RefundsData, types::RefundsResponse
|
||||
impl api::IncomingWebhook for Globalpay {
|
||||
fn get_webhook_object_reference_id(
|
||||
&self,
|
||||
_body: &[u8],
|
||||
_request: &api::IncomingWebhookRequestDetails<'_>,
|
||||
) -> CustomResult<String, errors::ConnectorError> {
|
||||
Err(errors::ConnectorError::WebhooksNotImplemented).into_report()
|
||||
}
|
||||
|
||||
fn get_webhook_event_type(
|
||||
&self,
|
||||
_body: &[u8],
|
||||
_request: &api::IncomingWebhookRequestDetails<'_>,
|
||||
) -> CustomResult<api::IncomingWebhookEvent, errors::ConnectorError> {
|
||||
Err(errors::ConnectorError::WebhooksNotImplemented).into_report()
|
||||
}
|
||||
|
||||
fn get_webhook_resource_object(
|
||||
&self,
|
||||
_body: &[u8],
|
||||
_request: &api::IncomingWebhookRequestDetails<'_>,
|
||||
) -> CustomResult<serde_json::Value, errors::ConnectorError> {
|
||||
Err(errors::ConnectorError::WebhooksNotImplemented).into_report()
|
||||
}
|
||||
|
||||
@ -360,21 +360,21 @@ impl services::ConnectorIntegration<api::RSync, types::RefundsData, types::Refun
|
||||
impl api::IncomingWebhook for Klarna {
|
||||
fn get_webhook_object_reference_id(
|
||||
&self,
|
||||
_body: &[u8],
|
||||
_request: &api::IncomingWebhookRequestDetails<'_>,
|
||||
) -> CustomResult<String, errors::ConnectorError> {
|
||||
Err(errors::ConnectorError::WebhooksNotImplemented).into_report()
|
||||
}
|
||||
|
||||
fn get_webhook_event_type(
|
||||
&self,
|
||||
_body: &[u8],
|
||||
_request: &api::IncomingWebhookRequestDetails<'_>,
|
||||
) -> CustomResult<api::IncomingWebhookEvent, errors::ConnectorError> {
|
||||
Err(errors::ConnectorError::WebhooksNotImplemented).into_report()
|
||||
}
|
||||
|
||||
fn get_webhook_resource_object(
|
||||
&self,
|
||||
_body: &[u8],
|
||||
_request: &api::IncomingWebhookRequestDetails<'_>,
|
||||
) -> CustomResult<serde_json::Value, errors::ConnectorError> {
|
||||
Err(errors::ConnectorError::WebhooksNotImplemented).into_report()
|
||||
}
|
||||
|
||||
@ -684,21 +684,21 @@ impl ConnectorIntegration<api::RSync, types::RefundsData, types::RefundsResponse
|
||||
impl api::IncomingWebhook for Payu {
|
||||
fn get_webhook_object_reference_id(
|
||||
&self,
|
||||
_body: &[u8],
|
||||
_request: &api::IncomingWebhookRequestDetails<'_>,
|
||||
) -> CustomResult<String, errors::ConnectorError> {
|
||||
Err(errors::ConnectorError::WebhooksNotImplemented).into_report()
|
||||
}
|
||||
|
||||
fn get_webhook_event_type(
|
||||
&self,
|
||||
_body: &[u8],
|
||||
_request: &api::IncomingWebhookRequestDetails<'_>,
|
||||
) -> CustomResult<api::IncomingWebhookEvent, errors::ConnectorError> {
|
||||
Err(errors::ConnectorError::WebhooksNotImplemented).into_report()
|
||||
}
|
||||
|
||||
fn get_webhook_resource_object(
|
||||
&self,
|
||||
_body: &[u8],
|
||||
_request: &api::IncomingWebhookRequestDetails<'_>,
|
||||
) -> CustomResult<serde_json::Value, errors::ConnectorError> {
|
||||
Err(errors::ConnectorError::WebhooksNotImplemented).into_report()
|
||||
}
|
||||
|
||||
@ -666,18 +666,16 @@ impl services::ConnectorIntegration<api::RSync, types::RefundsData, types::Refun
|
||||
impl api::IncomingWebhook for Rapyd {
|
||||
fn get_webhook_source_verification_algorithm(
|
||||
&self,
|
||||
_headers: &actix_web::http::header::HeaderMap,
|
||||
_body: &[u8],
|
||||
_request: &api::IncomingWebhookRequestDetails<'_>,
|
||||
) -> CustomResult<Box<dyn crypto::VerifySignature + Send>, errors::ConnectorError> {
|
||||
Ok(Box::new(crypto::HmacSha256))
|
||||
}
|
||||
|
||||
fn get_webhook_source_verification_signature(
|
||||
&self,
|
||||
headers: &actix_web::http::header::HeaderMap,
|
||||
_body: &[u8],
|
||||
request: &api::IncomingWebhookRequestDetails<'_>,
|
||||
) -> CustomResult<Vec<u8>, errors::ConnectorError> {
|
||||
let base64_signature = conn_utils::get_header_key_value("signature", headers)?;
|
||||
let base64_signature = conn_utils::get_header_key_value("signature", request.headers)?;
|
||||
let signature = consts::BASE64_ENGINE_URL_SAFE
|
||||
.decode(base64_signature.as_bytes())
|
||||
.into_report()
|
||||
@ -701,16 +699,15 @@ impl api::IncomingWebhook for Rapyd {
|
||||
|
||||
fn get_webhook_source_verification_message(
|
||||
&self,
|
||||
headers: &actix_web::http::header::HeaderMap,
|
||||
body: &[u8],
|
||||
request: &api::IncomingWebhookRequestDetails<'_>,
|
||||
merchant_id: &str,
|
||||
secret: &[u8],
|
||||
) -> CustomResult<Vec<u8>, errors::ConnectorError> {
|
||||
let host = conn_utils::get_header_key_value("host", headers)?;
|
||||
let host = conn_utils::get_header_key_value("host", request.headers)?;
|
||||
let connector = self.id();
|
||||
let url_path = format!("https://{host}/webhooks/{merchant_id}/{connector}");
|
||||
let salt = conn_utils::get_header_key_value("salt", headers)?;
|
||||
let timestamp = conn_utils::get_header_key_value("timestamp", headers)?;
|
||||
let salt = conn_utils::get_header_key_value("salt", request.headers)?;
|
||||
let timestamp = conn_utils::get_header_key_value("timestamp", request.headers)?;
|
||||
let stringify_auth = String::from_utf8(secret.to_vec())
|
||||
.into_report()
|
||||
.change_context(errors::ConnectorError::WebhookSourceVerificationFailed)
|
||||
@ -720,7 +717,7 @@ impl api::IncomingWebhook for Rapyd {
|
||||
.change_context(errors::ConnectorError::WebhookSourceVerificationFailed)?;
|
||||
let access_key = auth.access_key;
|
||||
let secret_key = auth.secret_key;
|
||||
let body_string = String::from_utf8(body.to_vec())
|
||||
let body_string = String::from_utf8(request.body.to_vec())
|
||||
.into_report()
|
||||
.change_context(errors::ConnectorError::WebhookSourceVerificationFailed)
|
||||
.attach_printable("Could not convert body to UTF-8")?;
|
||||
@ -732,19 +729,18 @@ impl api::IncomingWebhook for Rapyd {
|
||||
async fn verify_webhook_source(
|
||||
&self,
|
||||
db: &dyn StorageInterface,
|
||||
headers: &actix_web::http::header::HeaderMap,
|
||||
body: &[u8],
|
||||
request: &api::IncomingWebhookRequestDetails<'_>,
|
||||
merchant_id: &str,
|
||||
) -> CustomResult<bool, errors::ConnectorError> {
|
||||
let signature = self
|
||||
.get_webhook_source_verification_signature(headers, body)
|
||||
.get_webhook_source_verification_signature(request)
|
||||
.change_context(errors::ConnectorError::WebhookSourceVerificationFailed)?;
|
||||
let secret = self
|
||||
.get_webhook_source_verification_merchant_secret(db, merchant_id)
|
||||
.await
|
||||
.change_context(errors::ConnectorError::WebhookSourceVerificationFailed)?;
|
||||
let message = self
|
||||
.get_webhook_source_verification_message(headers, body, merchant_id, &secret)
|
||||
.get_webhook_source_verification_message(request, merchant_id, &secret)
|
||||
.change_context(errors::ConnectorError::WebhookSourceVerificationFailed)?;
|
||||
|
||||
let stringify_auth = String::from_utf8(secret.to_vec())
|
||||
@ -763,11 +759,13 @@ impl api::IncomingWebhook for Rapyd {
|
||||
|
||||
fn get_webhook_object_reference_id(
|
||||
&self,
|
||||
body: &[u8],
|
||||
request: &api::IncomingWebhookRequestDetails<'_>,
|
||||
) -> CustomResult<String, errors::ConnectorError> {
|
||||
let webhook: transformers::RapydIncomingWebhook = body
|
||||
let webhook: transformers::RapydIncomingWebhook = request
|
||||
.body
|
||||
.parse_struct("RapydIncomingWebhook")
|
||||
.change_context(errors::ConnectorError::WebhookEventTypeNotFound)?;
|
||||
|
||||
Ok(match webhook.data {
|
||||
transformers::WebhookData::PaymentData(payment_data) => payment_data.id,
|
||||
transformers::WebhookData::RefundData(refund_data) => refund_data.id,
|
||||
@ -776,19 +774,22 @@ impl api::IncomingWebhook for Rapyd {
|
||||
|
||||
fn get_webhook_event_type(
|
||||
&self,
|
||||
body: &[u8],
|
||||
request: &api::IncomingWebhookRequestDetails<'_>,
|
||||
) -> CustomResult<api::IncomingWebhookEvent, errors::ConnectorError> {
|
||||
let webhook: transformers::RapydIncomingWebhook = body
|
||||
let webhook: transformers::RapydIncomingWebhook = request
|
||||
.body
|
||||
.parse_struct("RapydIncomingWebhook")
|
||||
.change_context(errors::ConnectorError::WebhookEventTypeNotFound)?;
|
||||
|
||||
webhook.webhook_type.try_into()
|
||||
}
|
||||
|
||||
fn get_webhook_resource_object(
|
||||
&self,
|
||||
body: &[u8],
|
||||
request: &api::IncomingWebhookRequestDetails<'_>,
|
||||
) -> CustomResult<serde_json::Value, errors::ConnectorError> {
|
||||
let webhook: transformers::RapydIncomingWebhook = body
|
||||
let webhook: transformers::RapydIncomingWebhook = request
|
||||
.body
|
||||
.parse_struct("RapydIncomingWebhook")
|
||||
.change_context(errors::ConnectorError::WebhookEventTypeNotFound)?;
|
||||
let response = match webhook.data {
|
||||
|
||||
@ -499,9 +499,10 @@ impl ConnectorIntegration<api::RSync, types::RefundsData, types::RefundsResponse
|
||||
impl api::IncomingWebhook for Shift4 {
|
||||
fn get_webhook_object_reference_id(
|
||||
&self,
|
||||
body: &[u8],
|
||||
request: &api::IncomingWebhookRequestDetails<'_>,
|
||||
) -> CustomResult<String, errors::ConnectorError> {
|
||||
let details: shift4::Shift4WebhookObjectId = body
|
||||
let details: shift4::Shift4WebhookObjectId = request
|
||||
.body
|
||||
.parse_struct("Shift4WebhookObjectId")
|
||||
.change_context(errors::ConnectorError::WebhookReferenceIdNotFound)?;
|
||||
|
||||
@ -510,9 +511,10 @@ impl api::IncomingWebhook for Shift4 {
|
||||
|
||||
fn get_webhook_event_type(
|
||||
&self,
|
||||
body: &[u8],
|
||||
request: &api::IncomingWebhookRequestDetails<'_>,
|
||||
) -> CustomResult<api::IncomingWebhookEvent, errors::ConnectorError> {
|
||||
let details: shift4::Shift4WebhookObjectEventType = body
|
||||
let details: shift4::Shift4WebhookObjectEventType = request
|
||||
.body
|
||||
.parse_struct("Shift4WebhookObjectEventType")
|
||||
.change_context(errors::ConnectorError::WebhookEventTypeNotFound)?;
|
||||
Ok(match details.event_type {
|
||||
@ -524,9 +526,10 @@ impl api::IncomingWebhook for Shift4 {
|
||||
|
||||
fn get_webhook_resource_object(
|
||||
&self,
|
||||
body: &[u8],
|
||||
request: &api::IncomingWebhookRequestDetails<'_>,
|
||||
) -> CustomResult<serde_json::Value, errors::ConnectorError> {
|
||||
let details: shift4::Shift4WebhookObjectResource = body
|
||||
let details: shift4::Shift4WebhookObjectResource = request
|
||||
.body
|
||||
.parse_struct("Shift4WebhookObjectResource")
|
||||
.change_context(errors::ConnectorError::WebhookResourceObjectNotFound)?;
|
||||
Ok(details.data)
|
||||
|
||||
@ -880,18 +880,16 @@ fn get_signature_elements_from_header(
|
||||
impl api::IncomingWebhook for Stripe {
|
||||
fn get_webhook_source_verification_algorithm(
|
||||
&self,
|
||||
_headers: &actix_web::http::header::HeaderMap,
|
||||
_body: &[u8],
|
||||
_request: &api::IncomingWebhookRequestDetails<'_>,
|
||||
) -> CustomResult<Box<dyn crypto::VerifySignature + Send>, errors::ConnectorError> {
|
||||
Ok(Box::new(crypto::HmacSha256))
|
||||
}
|
||||
|
||||
fn get_webhook_source_verification_signature(
|
||||
&self,
|
||||
headers: &actix_web::http::header::HeaderMap,
|
||||
_body: &[u8],
|
||||
request: &api::IncomingWebhookRequestDetails<'_>,
|
||||
) -> CustomResult<Vec<u8>, errors::ConnectorError> {
|
||||
let mut security_header_kvs = get_signature_elements_from_header(headers)?;
|
||||
let mut security_header_kvs = get_signature_elements_from_header(request.headers)?;
|
||||
|
||||
let signature = security_header_kvs
|
||||
.remove("v1")
|
||||
@ -905,12 +903,11 @@ impl api::IncomingWebhook for Stripe {
|
||||
|
||||
fn get_webhook_source_verification_message(
|
||||
&self,
|
||||
headers: &actix_web::http::header::HeaderMap,
|
||||
body: &[u8],
|
||||
request: &api::IncomingWebhookRequestDetails<'_>,
|
||||
_merchant_id: &str,
|
||||
_secret: &[u8],
|
||||
) -> CustomResult<Vec<u8>, errors::ConnectorError> {
|
||||
let mut security_header_kvs = get_signature_elements_from_header(headers)?;
|
||||
let mut security_header_kvs = get_signature_elements_from_header(request.headers)?;
|
||||
|
||||
let timestamp = security_header_kvs
|
||||
.remove("t")
|
||||
@ -920,7 +917,7 @@ impl api::IncomingWebhook for Stripe {
|
||||
Ok(format!(
|
||||
"{}.{}",
|
||||
String::from_utf8_lossy(×tamp),
|
||||
String::from_utf8_lossy(body)
|
||||
String::from_utf8_lossy(request.body)
|
||||
)
|
||||
.into_bytes())
|
||||
}
|
||||
@ -941,9 +938,10 @@ impl api::IncomingWebhook for Stripe {
|
||||
|
||||
fn get_webhook_object_reference_id(
|
||||
&self,
|
||||
body: &[u8],
|
||||
request: &api::IncomingWebhookRequestDetails<'_>,
|
||||
) -> CustomResult<String, errors::ConnectorError> {
|
||||
let details: stripe::StripeWebhookObjectId = body
|
||||
let details: stripe::StripeWebhookObjectId = request
|
||||
.body
|
||||
.parse_struct("StripeWebhookObjectId")
|
||||
.change_context(errors::ConnectorError::WebhookReferenceIdNotFound)?;
|
||||
|
||||
@ -952,9 +950,10 @@ impl api::IncomingWebhook for Stripe {
|
||||
|
||||
fn get_webhook_event_type(
|
||||
&self,
|
||||
body: &[u8],
|
||||
request: &api::IncomingWebhookRequestDetails<'_>,
|
||||
) -> CustomResult<api::IncomingWebhookEvent, errors::ConnectorError> {
|
||||
let details: stripe::StripeWebhookObjectEventType = body
|
||||
let details: stripe::StripeWebhookObjectEventType = request
|
||||
.body
|
||||
.parse_struct("StripeWebhookObjectEventType")
|
||||
.change_context(errors::ConnectorError::WebhookEventTypeNotFound)?;
|
||||
|
||||
@ -967,9 +966,10 @@ impl api::IncomingWebhook for Stripe {
|
||||
|
||||
fn get_webhook_resource_object(
|
||||
&self,
|
||||
body: &[u8],
|
||||
request: &api::IncomingWebhookRequestDetails<'_>,
|
||||
) -> CustomResult<serde_json::Value, errors::ConnectorError> {
|
||||
let details: stripe::StripeWebhookObjectResource = body
|
||||
let details: stripe::StripeWebhookObjectResource = request
|
||||
.body
|
||||
.parse_struct("StripeWebhookObjectResource")
|
||||
.change_context(errors::ConnectorError::WebhookResourceObjectNotFound)?;
|
||||
|
||||
|
||||
@ -655,21 +655,21 @@ impl ConnectorIntegration<api::RSync, types::RefundsData, types::RefundsResponse
|
||||
impl api::IncomingWebhook for Worldline {
|
||||
fn get_webhook_object_reference_id(
|
||||
&self,
|
||||
_body: &[u8],
|
||||
_request: &api::IncomingWebhookRequestDetails<'_>,
|
||||
) -> CustomResult<String, errors::ConnectorError> {
|
||||
Err(errors::ConnectorError::WebhooksNotImplemented).into_report()
|
||||
}
|
||||
|
||||
fn get_webhook_event_type(
|
||||
&self,
|
||||
_body: &[u8],
|
||||
_request: &api::IncomingWebhookRequestDetails<'_>,
|
||||
) -> CustomResult<api::IncomingWebhookEvent, errors::ConnectorError> {
|
||||
Err(errors::ConnectorError::WebhooksNotImplemented).into_report()
|
||||
}
|
||||
|
||||
fn get_webhook_resource_object(
|
||||
&self,
|
||||
_body: &[u8],
|
||||
_request: &api::IncomingWebhookRequestDetails<'_>,
|
||||
) -> CustomResult<serde_json::Value, errors::ConnectorError> {
|
||||
Err(errors::ConnectorError::WebhooksNotImplemented).into_report()
|
||||
}
|
||||
|
||||
@ -594,21 +594,21 @@ impl ConnectorIntegration<api::RSync, types::RefundsData, types::RefundsResponse
|
||||
impl api::IncomingWebhook for Worldpay {
|
||||
fn get_webhook_object_reference_id(
|
||||
&self,
|
||||
_body: &[u8],
|
||||
_request: &api::IncomingWebhookRequestDetails<'_>,
|
||||
) -> CustomResult<String, errors::ConnectorError> {
|
||||
Err(errors::ConnectorError::WebhooksNotImplemented).into_report()
|
||||
}
|
||||
|
||||
fn get_webhook_event_type(
|
||||
&self,
|
||||
_body: &[u8],
|
||||
_request: &api::IncomingWebhookRequestDetails<'_>,
|
||||
) -> CustomResult<api::IncomingWebhookEvent, errors::ConnectorError> {
|
||||
Err(errors::ConnectorError::WebhooksNotImplemented).into_report()
|
||||
}
|
||||
|
||||
fn get_webhook_resource_object(
|
||||
&self,
|
||||
_body: &[u8],
|
||||
_request: &api::IncomingWebhookRequestDetails<'_>,
|
||||
) -> CustomResult<serde_json::Value, errors::ConnectorError> {
|
||||
Err(errors::ConnectorError::WebhooksNotImplemented).into_report()
|
||||
}
|
||||
|
||||
@ -211,11 +211,16 @@ pub async fn webhooks_core(
|
||||
|
||||
let connector = connector.connector;
|
||||
|
||||
let mut request_details = api::IncomingWebhookRequestDetails {
|
||||
method: req.method().clone(),
|
||||
headers: req.headers(),
|
||||
body: &body,
|
||||
};
|
||||
|
||||
let source_verified = connector
|
||||
.verify_webhook_source(
|
||||
&*state.store,
|
||||
req.headers(),
|
||||
&body,
|
||||
&request_details,
|
||||
&merchant_account.merchant_id,
|
||||
)
|
||||
.await
|
||||
@ -225,16 +230,17 @@ pub async fn webhooks_core(
|
||||
let decoded_body = connector
|
||||
.decode_webhook_body(
|
||||
&*state.store,
|
||||
req.headers(),
|
||||
&body,
|
||||
&request_details,
|
||||
&merchant_account.merchant_id,
|
||||
)
|
||||
.await
|
||||
.change_context(errors::ApiErrorResponse::InternalServerError)
|
||||
.attach_printable("There was an error in incoming webhook body decoding")?;
|
||||
|
||||
request_details.body = &decoded_body;
|
||||
|
||||
let event_type = connector
|
||||
.get_webhook_event_type(&decoded_body)
|
||||
.get_webhook_event_type(&request_details)
|
||||
.change_context(errors::ApiErrorResponse::InternalServerError)
|
||||
.attach_printable("Could not find event type in incoming webhook body")?;
|
||||
|
||||
@ -248,12 +254,12 @@ pub async fn webhooks_core(
|
||||
|
||||
if process_webhook_further {
|
||||
let object_ref_id = connector
|
||||
.get_webhook_object_reference_id(&decoded_body)
|
||||
.get_webhook_object_reference_id(&request_details)
|
||||
.change_context(errors::ApiErrorResponse::InternalServerError)
|
||||
.attach_printable("Could not find object reference id in incoming webhook body")?;
|
||||
|
||||
let event_object = connector
|
||||
.get_webhook_resource_object(&decoded_body)
|
||||
.get_webhook_resource_object(&request_details)
|
||||
.change_context(errors::ApiErrorResponse::InternalServerError)
|
||||
.attach_printable("Could not find resource object in incoming webhook body")?;
|
||||
|
||||
@ -277,6 +283,9 @@ pub async fn webhooks_core(
|
||||
.await
|
||||
.change_context(errors::ApiErrorResponse::InternalServerError)
|
||||
.attach_printable("Incoming webhook flow for payments failed")?,
|
||||
|
||||
api::WebhookFlow::ReturnResponse => {}
|
||||
|
||||
_ => Err(errors::ApiErrorResponse::InternalServerError)
|
||||
.into_report()
|
||||
.attach_printable("Unsupported Flow Type received in incoming webhooks")?,
|
||||
@ -284,7 +293,7 @@ pub async fn webhooks_core(
|
||||
}
|
||||
|
||||
let response = connector
|
||||
.get_webhook_api_response()
|
||||
.get_webhook_api_response(&request_details)
|
||||
.change_context(errors::ApiErrorResponse::InternalServerError)
|
||||
.attach_printable("Could not get incoming webhook api response from connector")?;
|
||||
|
||||
|
||||
@ -315,7 +315,8 @@ impl Webhooks {
|
||||
.app_data(web::Data::new(config))
|
||||
.service(
|
||||
web::resource("/{merchant_id}/{connector}")
|
||||
.route(web::post().to(receive_incoming_webhook)),
|
||||
.route(web::post().to(receive_incoming_webhook))
|
||||
.route(web::get().to(receive_incoming_webhook)),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
pub use api_models::webhooks::{
|
||||
IncomingWebhookDetails, IncomingWebhookEvent, MerchantWebhookConfig, OutgoingWebhook,
|
||||
OutgoingWebhookContent, WebhookFlow,
|
||||
IncomingWebhookDetails, IncomingWebhookEvent, IncomingWebhookRequestDetails,
|
||||
MerchantWebhookConfig, OutgoingWebhook, OutgoingWebhookContent, WebhookFlow,
|
||||
};
|
||||
use error_stack::ResultExt;
|
||||
|
||||
@ -16,8 +16,7 @@ use crate::{
|
||||
pub trait IncomingWebhook: ConnectorCommon + Sync {
|
||||
fn get_webhook_body_decoding_algorithm(
|
||||
&self,
|
||||
_headers: &actix_web::http::header::HeaderMap,
|
||||
_body: &[u8],
|
||||
_request: &IncomingWebhookRequestDetails<'_>,
|
||||
) -> CustomResult<Box<dyn crypto::DecodeMessage + Send>, errors::ConnectorError> {
|
||||
Ok(Box::new(crypto::NoAlgorithm))
|
||||
}
|
||||
@ -32,23 +31,21 @@ pub trait IncomingWebhook: ConnectorCommon + Sync {
|
||||
|
||||
fn get_webhook_body_decoding_message(
|
||||
&self,
|
||||
_headers: &actix_web::http::header::HeaderMap,
|
||||
body: &[u8],
|
||||
request: &IncomingWebhookRequestDetails<'_>,
|
||||
) -> CustomResult<Vec<u8>, errors::ConnectorError> {
|
||||
Ok(body.to_vec())
|
||||
Ok(request.body.to_vec())
|
||||
}
|
||||
|
||||
async fn decode_webhook_body(
|
||||
&self,
|
||||
db: &dyn StorageInterface,
|
||||
headers: &actix_web::http::header::HeaderMap,
|
||||
body: &[u8],
|
||||
request: &IncomingWebhookRequestDetails<'_>,
|
||||
merchant_id: &str,
|
||||
) -> CustomResult<Vec<u8>, errors::ConnectorError> {
|
||||
let algorithm = self.get_webhook_body_decoding_algorithm(headers, body)?;
|
||||
let algorithm = self.get_webhook_body_decoding_algorithm(request)?;
|
||||
|
||||
let message = self
|
||||
.get_webhook_body_decoding_message(headers, body)
|
||||
.get_webhook_body_decoding_message(request)
|
||||
.change_context(errors::ConnectorError::WebhookBodyDecodingFailed)?;
|
||||
let secret = self
|
||||
.get_webhook_body_decoding_merchant_secret(db, merchant_id)
|
||||
@ -62,8 +59,7 @@ pub trait IncomingWebhook: ConnectorCommon + Sync {
|
||||
|
||||
fn get_webhook_source_verification_algorithm(
|
||||
&self,
|
||||
_headers: &actix_web::http::header::HeaderMap,
|
||||
_body: &[u8],
|
||||
_request: &IncomingWebhookRequestDetails<'_>,
|
||||
) -> CustomResult<Box<dyn crypto::VerifySignature + Send>, errors::ConnectorError> {
|
||||
Ok(Box::new(crypto::NoAlgorithm))
|
||||
}
|
||||
@ -78,16 +74,14 @@ pub trait IncomingWebhook: ConnectorCommon + Sync {
|
||||
|
||||
fn get_webhook_source_verification_signature(
|
||||
&self,
|
||||
_headers: &actix_web::http::header::HeaderMap,
|
||||
_body: &[u8],
|
||||
_request: &IncomingWebhookRequestDetails<'_>,
|
||||
) -> CustomResult<Vec<u8>, errors::ConnectorError> {
|
||||
Ok(Vec::new())
|
||||
}
|
||||
|
||||
fn get_webhook_source_verification_message(
|
||||
&self,
|
||||
_headers: &actix_web::http::header::HeaderMap,
|
||||
_body: &[u8],
|
||||
_request: &IncomingWebhookRequestDetails<'_>,
|
||||
_merchant_id: &str,
|
||||
_secret: &[u8],
|
||||
) -> CustomResult<Vec<u8>, errors::ConnectorError> {
|
||||
@ -97,23 +91,22 @@ pub trait IncomingWebhook: ConnectorCommon + Sync {
|
||||
async fn verify_webhook_source(
|
||||
&self,
|
||||
db: &dyn StorageInterface,
|
||||
headers: &actix_web::http::header::HeaderMap,
|
||||
body: &[u8],
|
||||
request: &IncomingWebhookRequestDetails<'_>,
|
||||
merchant_id: &str,
|
||||
) -> CustomResult<bool, errors::ConnectorError> {
|
||||
let algorithm = self
|
||||
.get_webhook_source_verification_algorithm(headers, body)
|
||||
.get_webhook_source_verification_algorithm(request)
|
||||
.change_context(errors::ConnectorError::WebhookSourceVerificationFailed)?;
|
||||
|
||||
let signature = self
|
||||
.get_webhook_source_verification_signature(headers, body)
|
||||
.get_webhook_source_verification_signature(request)
|
||||
.change_context(errors::ConnectorError::WebhookSourceVerificationFailed)?;
|
||||
let secret = self
|
||||
.get_webhook_source_verification_merchant_secret(db, merchant_id)
|
||||
.await
|
||||
.change_context(errors::ConnectorError::WebhookSourceVerificationFailed)?;
|
||||
let message = self
|
||||
.get_webhook_source_verification_message(headers, body, merchant_id, &secret)
|
||||
.get_webhook_source_verification_message(request, merchant_id, &secret)
|
||||
.change_context(errors::ConnectorError::WebhookSourceVerificationFailed)?;
|
||||
|
||||
algorithm
|
||||
@ -123,21 +116,22 @@ pub trait IncomingWebhook: ConnectorCommon + Sync {
|
||||
|
||||
fn get_webhook_object_reference_id(
|
||||
&self,
|
||||
_body: &[u8],
|
||||
_request: &IncomingWebhookRequestDetails<'_>,
|
||||
) -> CustomResult<String, errors::ConnectorError>;
|
||||
|
||||
fn get_webhook_event_type(
|
||||
&self,
|
||||
_body: &[u8],
|
||||
_request: &IncomingWebhookRequestDetails<'_>,
|
||||
) -> CustomResult<IncomingWebhookEvent, errors::ConnectorError>;
|
||||
|
||||
fn get_webhook_resource_object(
|
||||
&self,
|
||||
_body: &[u8],
|
||||
_request: &IncomingWebhookRequestDetails<'_>,
|
||||
) -> CustomResult<serde_json::Value, errors::ConnectorError>;
|
||||
|
||||
fn get_webhook_api_response(
|
||||
&self,
|
||||
_request: &IncomingWebhookRequestDetails<'_>,
|
||||
) -> CustomResult<services::api::ApplicationResponse<serde_json::Value>, errors::ConnectorError>
|
||||
{
|
||||
Ok(services::api::ApplicationResponse::StatusOk)
|
||||
|
||||
Reference in New Issue
Block a user