diff --git a/openapi.json b/openapi.json index 2275863..0eeabed 100644 --- a/openapi.json +++ b/openapi.json @@ -14285,6 +14285,94 @@ } }, "webhooks": { + "sales.completed": { + "post": { + "operationId": "SaleCompletedWebhook", + "tags": [ + "Sales" + ], + "summary": "Sale completed", + "description": "Sent once per sale when its completion is observed: the sale is closed and has been fully paid. The event created_at is the sale's recorded completion time, rather than the time the notification is delivered. A delayed notification may arrive after subsequent updates, refunds, or chargebacks. Fetch the current sale using object.url with an access token that has the sales.read scope. The sale may briefly be unavailable while the Sales API catches up; retry fetching it after a 404 response. Automatic retries and manual redelivery may deliver the same event more than once; deduplicate by event id.", + "x-object-type": "sale", + "x-object": { + "$ref": "#/components/schemas/Sale" + }, + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Event" + }, + "examples": { + "completed": { + "summary": "A sale completed webhook event.", + "value": { + "id": "event_01JQCG7EF8F2DA02V31HSEXXCM", + "type": "sales.completed", + "created_at": "2026-10-14T12:00:00Z", + "object": { + "id": "847ed267-c617-4d38-9e5a-4ef728daa5c3", + "type": "sale", + "url": "https://api.sumup.com/v1.0/merchants/MC0DE/sales/847ed267-c617-4d38-9e5a-4ef728daa5c3" + } + } + } + } + } + } + }, + "responses": { + "2XX": { + "description": "Return any 2xx response to acknowledge successful delivery." + } + } + } + }, + "sales.updated": { + "post": { + "operationId": "SaleUpdatedWebhook", + "tags": [ + "Sales" + ], + "summary": "Sale updated", + "description": "Sent when a closed, fully paid sale is published or updated, including subsequent refunds and chargebacks. The first notification can occur when the sale becomes fully paid and closed. Fetch the current sale using object.url with an access token that has the sales.read scope. The sale may briefly be unavailable while the Sales API catches up; retry fetching it after a 404 response. Notifications contain the sale identity rather than a snapshot, and delivery order is not guaranteed.", + "x-object-type": "sale", + "x-object": { + "$ref": "#/components/schemas/Sale" + }, + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Event" + }, + "examples": { + "updated": { + "summary": "A sale updated webhook event.", + "value": { + "id": "event_01JQCG7EF8F2DA02V31HSEXXCM", + "type": "sales.updated", + "created_at": "2026-10-14T12:00:00Z", + "object": { + "id": "847ed267-c617-4d38-9e5a-4ef728daa5c3", + "type": "sale", + "url": "https://api.sumup.com/v1.0/merchants/MC0DE/sales/847ed267-c617-4d38-9e5a-4ef728daa5c3" + } + } + } + } + } + } + }, + "responses": { + "2XX": { + "description": "Return any 2xx response to acknowledge successful delivery." + } + } + } + }, "checkouts.created": { "post": { "operationId": "CheckoutCreatedWebhook", diff --git a/src/main/java/com/sumup/sdk/events/AsyncEventsHandler.java b/src/main/java/com/sumup/sdk/events/AsyncEventsHandler.java index 2ac53cb..a8a948b 100644 --- a/src/main/java/com/sumup/sdk/events/AsyncEventsHandler.java +++ b/src/main/java/com/sumup/sdk/events/AsyncEventsHandler.java @@ -204,6 +204,30 @@ public AsyncEventsHandler onRoleUpdated(AsyncEventCallback cal return this; } + /** + * Registers the callback for {@code "sales.completed"}, replacing any previous registration. + * + * @param callback callback to complete before acknowledging delivery + * @return this handler for chaining + */ + public AsyncEventsHandler onSaleCompleted(AsyncEventCallback callback) { + Objects.requireNonNull(callback); + callbacks.put("sales.completed", event -> callback.handle((SaleCompletedEvent) event)); + return this; + } + + /** + * Registers the callback for {@code "sales.updated"}, replacing any previous registration. + * + * @param callback callback to complete before acknowledging delivery + * @return this handler for chaining + */ + public AsyncEventsHandler onSaleUpdated(AsyncEventCallback callback) { + Objects.requireNonNull(callback); + callbacks.put("sales.updated", event -> callback.handle((SaleUpdatedEvent) event)); + return this; + } + /** * Verifies, parses, and completes when the selected callback finishes. * diff --git a/src/main/java/com/sumup/sdk/events/EventNotification.java b/src/main/java/com/sumup/sdk/events/EventNotification.java index 6ddae3b..5bdf71c 100644 --- a/src/main/java/com/sumup/sdk/events/EventNotification.java +++ b/src/main/java/com/sumup/sdk/events/EventNotification.java @@ -111,6 +111,8 @@ private static EventNotification parseBody(ApiClient client, byte[] body) { case "roles.created" -> RoleCreatedEvent.class; case "roles.deleted" -> RoleDeletedEvent.class; case "roles.updated" -> RoleUpdatedEvent.class; + case "sales.completed" -> SaleCompletedEvent.class; + case "sales.updated" -> SaleUpdatedEvent.class; default -> EventNotification.class; }; EventNotification event = MAPPER.treeToValue(root, type); diff --git a/src/main/java/com/sumup/sdk/events/EventsHandler.java b/src/main/java/com/sumup/sdk/events/EventsHandler.java index 9d713ed..ef067f2 100644 --- a/src/main/java/com/sumup/sdk/events/EventsHandler.java +++ b/src/main/java/com/sumup/sdk/events/EventsHandler.java @@ -253,6 +253,38 @@ public EventsHandler onRoleUpdated(EventCallback callback) { return this; } + /** + * Registers the callback for {@code "sales.completed"}, replacing any previous registration. + * + * @param callback callback to complete before acknowledging delivery + * @return this handler for chaining + */ + public EventsHandler onSaleCompleted(EventCallback callback) { + Objects.requireNonNull(callback); + callbacks.put( + "sales.completed", + event -> { + callback.handle((SaleCompletedEvent) event); + }); + return this; + } + + /** + * Registers the callback for {@code "sales.updated"}, replacing any previous registration. + * + * @param callback callback to complete before acknowledging delivery + * @return this handler for chaining + */ + public EventsHandler onSaleUpdated(EventCallback callback) { + Objects.requireNonNull(callback); + callbacks.put( + "sales.updated", + event -> { + callback.handle((SaleUpdatedEvent) event); + }); + return this; + } + /** * Verifies, parses, and invokes the selected callback. * diff --git a/src/main/java/com/sumup/sdk/events/SaleCompletedEvent.java b/src/main/java/com/sumup/sdk/events/SaleCompletedEvent.java new file mode 100644 index 0000000..abb967d --- /dev/null +++ b/src/main/java/com/sumup/sdk/events/SaleCompletedEvent.java @@ -0,0 +1,21 @@ +// Code generated by sumup-java/codegen. DO NOT EDIT. +package com.sumup.sdk.events; + +import com.fasterxml.jackson.core.type.TypeReference; +import com.sumup.sdk.models.Sale; + +/** + * Sent once per sale when its completion is observed: the sale is closed and has been fully paid. + * The event created_at is the sale's recorded completion time, rather than the time the + * notification is delivered. A delayed notification may arrive after subsequent updates, refunds, + * or chargebacks. Fetch the current sale using object.url with an access token that has the + * sales.read scope. The sale may briefly be unavailable while the Sales API catches up; retry + * fetching it after a 404 response. Automatic retries and manual redelivery may deliver the same + * event more than once; deduplicate by event id. + */ +public final class SaleCompletedEvent extends FetchableEvent { + @Override + TypeReference resourceType() { + return new TypeReference<>() {}; + } +} diff --git a/src/main/java/com/sumup/sdk/events/SaleUpdatedEvent.java b/src/main/java/com/sumup/sdk/events/SaleUpdatedEvent.java new file mode 100644 index 0000000..211659d --- /dev/null +++ b/src/main/java/com/sumup/sdk/events/SaleUpdatedEvent.java @@ -0,0 +1,20 @@ +// Code generated by sumup-java/codegen. DO NOT EDIT. +package com.sumup.sdk.events; + +import com.fasterxml.jackson.core.type.TypeReference; +import com.sumup.sdk.models.Sale; + +/** + * Sent when a closed, fully paid sale is published or updated, including subsequent refunds and + * chargebacks. The first notification can occur when the sale becomes fully paid and closed. Fetch + * the current sale using object.url with an access token that has the sales.read scope. The sale + * may briefly be unavailable while the Sales API catches up; retry fetching it after a 404 + * response. Notifications contain the sale identity rather than a snapshot, and delivery order is + * not guaranteed. + */ +public final class SaleUpdatedEvent extends FetchableEvent { + @Override + TypeReference resourceType() { + return new TypeReference<>() {}; + } +}