enhance(router/webhooks): expose additional incoming request details to webhooks flow (#637)

This commit is contained in:
ItsMeShashank
2023-02-24 17:53:16 +05:30
committed by GitHub
parent aaf372505c
commit 1b3b7f5bc6
21 changed files with 144 additions and 129 deletions

View File

@ -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()
}

View File

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

View File

@ -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()
}

View File

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

View File

@ -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()
}

View File

@ -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()
}

View File

@ -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())
}

View File

@ -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()
}

View File

@ -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())
}

View File

@ -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())
}

View File

@ -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()
}

View File

@ -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()
}

View File

@ -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()
}

View File

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

View File

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

View File

@ -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(&timestamp),
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)?;

View File

@ -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()
}

View File

@ -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()
}

View File

@ -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")?;

View File

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

View File

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