Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(webhook): Add is_delivered filter to list initial attempts endpoint #7344

Open
wants to merge 5 commits into
base: return-total-events-count
Choose a base branch
from

Conversation

AmeyWale
Copy link
Contributor

@AmeyWale AmeyWale commented Feb 21, 2025

Type of Change

  • Bugfix
  • New feature
  • Enhancement
  • Refactoring
  • Dependency updates
  • Documentation
  • CI/CD

Description

This PR features addition of new filter called is_delivered which will allow merchants to filter out webhook events based on the fact that if a particular event was ultimately delivered or not.

The is_delivery_successful field now takes data from is_overall_delivery_successful column of the events table and not is_webhook_notified to represent the eventual delivery of the event.

Additional Changes

  • This PR modifies the API contract
  • This PR modifies the database schema
  • This PR modifies application configuration/environment variables

Motivation and Context

Upon merging this PR, the merchants would be able to filter webhook events based on ultimate delivery status.

How did you test it?

  • API request to fetch webhook events with no filter
curl --location 'http://localhost:8080/events/merchant_1740390998' \
--header 'Content-Type: application/json' \
--header 'admin_api_key: test_admin' \
--header 'api-key: test_admin' \
--data ''
  • Response from the above call
{"events":[{"event_id":"evt_01953774e7eb7c9190e280ea6756d032","merchant_id":"merchant_1740390998","profile_id":"pro_IzLLBXNTTbYTO6TehKsK","object_id":"pay_QlQAl1DOJug8H0kk9FtW","event_type":"payment_succeeded","event_class":"payments","is_delivery_successful":true,"initial_attempt_id":"evt_01953774e7eb7c9190e280ea6756d032","created":"2025-02-24T10:16:03.308Z"},{"event_id":"evt_01953774d6b57762a2ff4fd96e27cce3","merchant_id":"merchant_1740390998","profile_id":"pro_IzLLBXNTTbYTO6TehKsK","object_id":"pay_lKLVcse8HzUlytYrQtKJ","event_type":"payment_succeeded","event_class":"payments","is_delivery_successful":true,"initial_attempt_id":"evt_01953774d6b57762a2ff4fd96e27cce3","created":"2025-02-24T10:15:58.901Z"},{"event_id":"evt_01953774c1147c41a855a13536beafd7","merchant_id":"merchant_1740390998","profile_id":"pro_IzLLBXNTTbYTO6TehKsK","object_id":"pay_TpPAwHW9pb1VtDoQR1fn","event_type":"payment_succeeded","event_class":"payments","is_delivery_successful":true,"initial_attempt_id":"evt_01953774c1147c41a855a13536beafd7","created":"2025-02-24T10:15:53.365Z"},{"event_id":"evt_019537748a317841814d292122fe384b","merchant_id":"merchant_1740390998","profile_id":"pro_IzLLBXNTTbYTO6TehKsK","object_id":"pay_Wfg8KwLIKpNukRyI7LGN","event_type":"payment_succeeded","event_class":"payments","is_delivery_successful":false,"initial_attempt_id":"evt_019537748a317841814d292122fe384b","created":"2025-02-24T10:15:39.313Z"},{"event_id":"evt_019537728f3b7000b60bbf18cb8b1373","merchant_id":"merchant_1740390998","profile_id":"pro_IzLLBXNTTbYTO6TehKsK","object_id":"pay_ISS739fX9arr9CNf6w73","event_type":"payment_succeeded","event_class":"payments","is_delivery_successful":false,"initial_attempt_id":"evt_019537728f3b7000b60bbf18cb8b1373","created":"2025-02-24T10:13:29.532Z"},{"event_id":"evt_01953771f29d7911be93a3753f1eb0c4","merchant_id":"merchant_1740390998","profile_id":"pro_IzLLBXNTTbYTO6TehKsK","object_id":"pay_PEAL9Kt0lSt3tllLtSD6","event_type":"payment_succeeded","event_class":"payments","is_delivery_successful":false,"initial_attempt_id":"evt_01953771f29d7911be93a3753f1eb0c4","created":"2025-02-24T10:12:49.438Z"},{"event_id":"evt_019537707b8b70838e753daf51eb1d67","merchant_id":"merchant_1740390998","profile_id":"pro_IzLLBXNTTbYTO6TehKsK","object_id":"pay_hXCyBkIAVf00lf03jyMQ","event_type":"payment_succeeded","event_class":"payments","is_delivery_successful":false,"initial_attempt_id":"evt_019537707b8b70838e753daf51eb1d67","created":"2025-02-24T10:11:13.420Z"},{"event_id":"evt_0195376867a57282999dd9ac75b137f9","merchant_id":"merchant_1740390998","profile_id":"pro_IzLLBXNTTbYTO6TehKsK","object_id":"pay_g2TOloPWFFrhm02n9kuv","event_type":"payment_succeeded","event_class":"payments","is_delivery_successful":false,"initial_attempt_id":"evt_0195376867a57282999dd9ac75b137f9","created":"2025-02-24T10:02:24.038Z"},{"event_id":"evt_0195376750287e53bb08204068a6a902","merchant_id":"merchant_1740390998","profile_id":"pro_IzLLBXNTTbYTO6TehKsK","object_id":"pay_47LGuikk3tqhnHNxrQfL","event_type":"payment_succeeded","event_class":"payments","is_delivery_successful":false,"initial_attempt_id":"evt_0195376750287e53bb08204068a6a902","created":"2025-02-24T10:01:12.489Z"},{"event_id":"evt_01953764d84a7811b03e83c906fd8009","merchant_id":"merchant_1740390998","profile_id":"pro_IzLLBXNTTbYTO6TehKsK","object_id":"pay_d4MTTk0iuQzjQcChWHsa","event_type":"payment_succeeded","event_class":"payments","is_delivery_successful":false,"initial_attempt_id":"evt_01953764d84a7811b03e83c906fd8009","created":"2025-02-24T09:58:30.731Z"},{"event_id":"evt_0195376421b477909d06c2b227c734f7","merchant_id":"merchant_1740390998","profile_id":"pro_IzLLBXNTTbYTO6TehKsK","object_id":"pay_9yrvfEwJyUYS1BScPn8Y","event_type":"payment_succeeded","event_class":"payments","is_delivery_successful":false,"initial_attempt_id":"evt_0195376421b477909d06c2b227c734f7","created":"2025-02-24T09:57:43.988Z"},{"event_id":"evt_0195376386e97a93a6b464285a262d8f","merchant_id":"merchant_1740390998","profile_id":"pro_IzLLBXNTTbYTO6TehKsK","object_id":"pay_43vhcdhitA2NdA7cALAx","event_type":"payment_succeeded","event_class":"payments","is_delivery_successful":false,"initial_attempt_id":"evt_0195376386e97a93a6b464285a262d8f","created":"2025-02-24T09:57:04.361Z"}],"total_count":12}
  • API call to fetch webhook events with is_delivered having false value.
curl --location 'http://localhost:8080/events/merchant_1740390998?is_delivered=false' \
--header 'Content-Type: application/json' \
--header 'admin_api_key: test_admin' \
--header 'api-key: test_admin' \
--data ''
  • Response from above call
{"events":[{"event_id":"evt_019537748a317841814d292122fe384b","merchant_id":"merchant_1740390998","profile_id":"pro_IzLLBXNTTbYTO6TehKsK","object_id":"pay_Wfg8KwLIKpNukRyI7LGN","event_type":"payment_succeeded","event_class":"payments","is_delivery_successful":false,"initial_attempt_id":"evt_019537748a317841814d292122fe384b","created":"2025-02-24T10:15:39.313Z"},{"event_id":"evt_019537728f3b7000b60bbf18cb8b1373","merchant_id":"merchant_1740390998","profile_id":"pro_IzLLBXNTTbYTO6TehKsK","object_id":"pay_ISS739fX9arr9CNf6w73","event_type":"payment_succeeded","event_class":"payments","is_delivery_successful":false,"initial_attempt_id":"evt_019537728f3b7000b60bbf18cb8b1373","created":"2025-02-24T10:13:29.532Z"},{"event_id":"evt_01953771f29d7911be93a3753f1eb0c4","merchant_id":"merchant_1740390998","profile_id":"pro_IzLLBXNTTbYTO6TehKsK","object_id":"pay_PEAL9Kt0lSt3tllLtSD6","event_type":"payment_succeeded","event_class":"payments","is_delivery_successful":false,"initial_attempt_id":"evt_01953771f29d7911be93a3753f1eb0c4","created":"2025-02-24T10:12:49.438Z"},{"event_id":"evt_019537707b8b70838e753daf51eb1d67","merchant_id":"merchant_1740390998","profile_id":"pro_IzLLBXNTTbYTO6TehKsK","object_id":"pay_hXCyBkIAVf00lf03jyMQ","event_type":"payment_succeeded","event_class":"payments","is_delivery_successful":false,"initial_attempt_id":"evt_019537707b8b70838e753daf51eb1d67","created":"2025-02-24T10:11:13.420Z"},{"event_id":"evt_0195376867a57282999dd9ac75b137f9","merchant_id":"merchant_1740390998","profile_id":"pro_IzLLBXNTTbYTO6TehKsK","object_id":"pay_g2TOloPWFFrhm02n9kuv","event_type":"payment_succeeded","event_class":"payments","is_delivery_successful":false,"initial_attempt_id":"evt_0195376867a57282999dd9ac75b137f9","created":"2025-02-24T10:02:24.038Z"},{"event_id":"evt_0195376750287e53bb08204068a6a902","merchant_id":"merchant_1740390998","profile_id":"pro_IzLLBXNTTbYTO6TehKsK","object_id":"pay_47LGuikk3tqhnHNxrQfL","event_type":"payment_succeeded","event_class":"payments","is_delivery_successful":false,"initial_attempt_id":"evt_0195376750287e53bb08204068a6a902","created":"2025-02-24T10:01:12.489Z"},{"event_id":"evt_01953764d84a7811b03e83c906fd8009","merchant_id":"merchant_1740390998","profile_id":"pro_IzLLBXNTTbYTO6TehKsK","object_id":"pay_d4MTTk0iuQzjQcChWHsa","event_type":"payment_succeeded","event_class":"payments","is_delivery_successful":false,"initial_attempt_id":"evt_01953764d84a7811b03e83c906fd8009","created":"2025-02-24T09:58:30.731Z"},{"event_id":"evt_0195376421b477909d06c2b227c734f7","merchant_id":"merchant_1740390998","profile_id":"pro_IzLLBXNTTbYTO6TehKsK","object_id":"pay_9yrvfEwJyUYS1BScPn8Y","event_type":"payment_succeeded","event_class":"payments","is_delivery_successful":false,"initial_attempt_id":"evt_0195376421b477909d06c2b227c734f7","created":"2025-02-24T09:57:43.988Z"},{"event_id":"evt_0195376386e97a93a6b464285a262d8f","merchant_id":"merchant_1740390998","profile_id":"pro_IzLLBXNTTbYTO6TehKsK","object_id":"pay_43vhcdhitA2NdA7cALAx","event_type":"payment_succeeded","event_class":"payments","is_delivery_successful":false,"initial_attempt_id":"evt_0195376386e97a93a6b464285a262d8f","created":"2025-02-24T09:57:04.361Z"}],"total_count":9}
  • API call to fetch webhook events with is_delivered filter having true value.
curl --location 'http://localhost:8080/events/merchant_1740390998?is_delivered=true' \
--header 'Content-Type: application/json' \
--header 'admin_api_key: test_admin' \
--header 'api-key: test_admin' \
--data ''
  • Response from above call
{"events":[{"event_id":"evt_01953774e7eb7c9190e280ea6756d032","merchant_id":"merchant_1740390998","profile_id":"pro_IzLLBXNTTbYTO6TehKsK","object_id":"pay_QlQAl1DOJug8H0kk9FtW","event_type":"payment_succeeded","event_class":"payments","is_delivery_successful":true,"initial_attempt_id":"evt_01953774e7eb7c9190e280ea6756d032","created":"2025-02-24T10:16:03.308Z"},{"event_id":"evt_01953774d6b57762a2ff4fd96e27cce3","merchant_id":"merchant_1740390998","profile_id":"pro_IzLLBXNTTbYTO6TehKsK","object_id":"pay_lKLVcse8HzUlytYrQtKJ","event_type":"payment_succeeded","event_class":"payments","is_delivery_successful":true,"initial_attempt_id":"evt_01953774d6b57762a2ff4fd96e27cce3","created":"2025-02-24T10:15:58.901Z"},{"event_id":"evt_01953774c1147c41a855a13536beafd7","merchant_id":"merchant_1740390998","profile_id":"pro_IzLLBXNTTbYTO6TehKsK","object_id":"pay_TpPAwHW9pb1VtDoQR1fn","event_type":"payment_succeeded","event_class":"payments","is_delivery_successful":true,"initial_attempt_id":"evt_01953774c1147c41a855a13536beafd7","created":"2025-02-24T10:15:53.365Z"}],"total_count":3}

Checklist

  • I formatted the code cargo +nightly fmt --all
  • I addressed lints thrown by cargo clippy
  • I reviewed the submitted code
  • I added unit tests for my changes where possible

@AmeyWale AmeyWale requested review from a team as code owners February 21, 2025 13:09
@AmeyWale AmeyWale changed the base branch from main to return-total-events-count February 21, 2025 13:26
@AmeyWale AmeyWale self-assigned this Feb 21, 2025
@hyperswitch-bot hyperswitch-bot bot added the M-database-changes Metadata: This PR involves database schema changes label Feb 21, 2025
@hyperswitch-bot hyperswitch-bot bot added the M-api-contract-changes Metadata: This PR involves API contract changes label Feb 21, 2025
Comment on lines +32 to +33
// Filter all events by overall_delivery_status.
pub is_delivered: Option<bool>,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please make this a documentation comment instead.

And update the documentation comment to reflect the current field name, in case you think we actually need to reference the field name here.

@@ -68,7 +72,7 @@ pub struct EventListItemResponse {
/// Specifies the class of event (the type of object: Payment, Refund, etc.)
pub event_class: EventClass,

/// Indicates whether the webhook delivery attempt was successful.
/// Indicates whether the webhook was ultimately delivery.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit:

Suggested change
/// Indicates whether the webhook was ultimately delivery.
/// Indicates whether the webhook was ultimately delivered.

@@ -0,0 +1,2 @@
-- Your SQL goes here
ALTER TABLE events ADD COLUMN IF NOT EXISTS is_overall_delivery_successful BOOLEAN NOT NULL DEFAULT FALSE;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is this column non-nullable? Why can't the old data have null values?

Comment on lines +838 to +863
let parent_update = domain::EventUpdate::ParentUpdate {
is_overall_delivery_successful: status_code.is_success(),
};

let parent_event_id = current_event.initial_attempt_id.clone();
let delivery_attempt = current_event.delivery_attempt;

if let Some((
parent_event_id,
enums::WebhookDeliveryAttempt::InitialAttempt
| enums::WebhookDeliveryAttempt::AutomaticRetry,
)) = parent_event_id.zip(delivery_attempt)
{
state
.store
.update_event_by_merchant_id_event_id(
key_manager_state,
merchant_id,
parent_event_id.as_str(),
parent_update,
&merchant_key_store,
)
.await
.change_context(errors::WebhooksFlowError::WebhookEventUpdationFailed)
.attach_printable("Failed to update parent event")?;
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we extract updating of the initial attempt to a different function, and call it in trigger_webhook_to_merchant()? We'd prefer to keep the update_event_in_storage() function responsible for updating only the current event that we're working with.

Ideally, we'd want to keep a function to have only one responsibility...

Comment on lines +838 to +840
let parent_update = domain::EventUpdate::ParentUpdate {
is_overall_delivery_successful: status_code.is_success(),
};
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Once we've extracted it to a separate function, let's name this enum variant to indicate which field is being updated, rather than which event is being updated: something along the lines of OverallDeliveryStatusUpdate.

Comment on lines +37 to 38
/// Reference to the object type for which the webhook was created.
pub primary_object_type: EventObjectType,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
/// Reference to the object type for which the webhook was created.
pub primary_object_type: EventObjectType,
/// Type of the object for which the webhook was created.
pub primary_object_type: EventObjectType,

Comment on lines +43 to 47
/// Merchant identifier to which the webhook was sent.
pub merchant_id: Option<common_utils::id_type::MerchantId>,

/// Business Profile identifier to which the webhook was sent.
pub business_profile_id: Option<common_utils::id_type::ProfileId>,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These actually are the identifiers of the merchant account and business profile the object is associated with.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
M-api-contract-changes Metadata: This PR involves API contract changes M-database-changes Metadata: This PR involves database schema changes
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants