From a4e9f8525f4ae6d2624f0e8fbc856e5966c072c8 Mon Sep 17 00:00:00 2001 From: malkoas Date: Tue, 1 Sep 2026 13:00:44 +0300 Subject: [PATCH 1/6] Add ChangedAmount field and WithdrawalBodyChanged event handler --- pom.xml | 2 +- .../WithdrawalBodyChangedHandler.java | 95 ++++++++++++++ ...drawalBodyChangedHookMessageGenerator.java | 124 ++++++++++++++++++ ...awalStatusChangedHookMessageGenerator.java | 9 +- .../db/migration/V8__add_event_type.sql | 1 + .../hooker/handler/TestBeanFactory.java | 24 +++- .../handler/WithdrawalEventHandlerTest.java | 32 ++++- 7 files changed, 271 insertions(+), 16 deletions(-) create mode 100644 src/main/java/dev/vality/wallets/hooker/handler/withdrawal/WithdrawalBodyChangedHandler.java create mode 100644 src/main/java/dev/vality/wallets/hooker/handler/withdrawal/generator/WithdrawalBodyChangedHookMessageGenerator.java create mode 100644 src/main/resources/db/migration/V8__add_event_type.sql diff --git a/pom.xml b/pom.xml index 85e3b56..7766f2d 100644 --- a/pom.xml +++ b/pom.xml @@ -134,7 +134,7 @@ dev.vality swag-wallets-webhook-events-server - 1.43-1b943c0 + 1.45-4390ebb com.google.guava diff --git a/src/main/java/dev/vality/wallets/hooker/handler/withdrawal/WithdrawalBodyChangedHandler.java b/src/main/java/dev/vality/wallets/hooker/handler/withdrawal/WithdrawalBodyChangedHandler.java new file mode 100644 index 0000000..d831450 --- /dev/null +++ b/src/main/java/dev/vality/wallets/hooker/handler/withdrawal/WithdrawalBodyChangedHandler.java @@ -0,0 +1,95 @@ +package dev.vality.wallets.hooker.handler.withdrawal; + +import dev.vality.fistful.withdrawal.BodyChange; +import dev.vality.fistful.withdrawal.TimestampedChange; +import dev.vality.machinegun.eventsink.MachineEvent; +import dev.vality.wallets.hooker.dao.webhook.WebHookDao; +import dev.vality.wallets.hooker.dao.withdrawal.WithdrawalReferenceDao; +import dev.vality.wallets.hooker.domain.WebHookModel; +import dev.vality.wallets.hooker.domain.enums.EventType; +import dev.vality.wallets.hooker.exception.HandleEventException; +import dev.vality.wallets.hooker.handler.withdrawal.generator.WithdrawalBodyChangedHookMessageGenerator; +import dev.vality.wallets.hooker.model.MessageGenParams; +import dev.vality.wallets.hooker.service.WebHookMessageSenderService; +import dev.vality.webhook.dispatcher.WebhookMessage; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Component; + +@Slf4j +@Component +@RequiredArgsConstructor +public class WithdrawalBodyChangedHandler implements WithdrawalEventHandler { + + private final WithdrawalReferenceDao withdrawalReferenceDao; + private final WebHookDao webHookDao; + private final WithdrawalBodyChangedHookMessageGenerator withdrawalBodyChangedHookMessageGenerator; + private final WebHookMessageSenderService webHookMessageSenderService; + + @Override + public boolean accept(TimestampedChange change) { + return change.getChange().isSetBodyChanged(); + } + + @Override + public void handle(TimestampedChange change, MachineEvent event) { + String withdrawalId = event.getSourceId(); + log.info("Start handling WithdrawalBodyChange: withdrawalId={} change={}", withdrawalId, change); + + handleChangeBody( + change, + event, + withdrawalId, + EventType.WITHDRAWAL_CASH_CHANGED); + + log.info("Finish handling WithdrawalBodyChange: withdrawalId={}", withdrawalId); + } + + public void handleChangeBody( + TimestampedChange change, + MachineEvent event, + String withdrawalId, + EventType eventType) { + try { + var reference = withdrawalReferenceDao.get(withdrawalId); + ; + Long parentId = Long.valueOf(reference.getEventId()); + + webHookDao.getByPartyAndEventType(reference.getPartyId(), eventType).stream() + .filter(webHook -> webHook.getWalletId() == null + || webHook.getWalletId().equals(reference.getWalletId())) + .map(webhook -> generateWithdrawalBodyChangeHookMsg( + change.getChange().getBodyChanged(), + webhook, + withdrawalId, + event.getEventId(), + parentId, + event.getCreatedAt(), + reference.getExternalId())) + .forEach(webHookMessageSenderService::send); + } catch (Exception e) { + log.error("Error while handling WithdrawalBodyChange: {}, withdrawalId: {}", + change, withdrawalId, e); + throw new HandleEventException("Error while handling WithdrawalBodyChange", e); + } + } + + private WebhookMessage generateWithdrawalBodyChangeHookMsg( + BodyChange bodyChanged, + WebHookModel webhook, + String withdrawalId, + long eventId, + Long parentId, + String createdAt, + String externalId) { + MessageGenParams messageGenParams = MessageGenParams.builder() + .sourceId(withdrawalId) + .eventId(eventId) + .parentId(parentId) + .createdAt(createdAt) + .externalId(externalId) + .build(); + + return withdrawalBodyChangedHookMessageGenerator.generate(bodyChanged, webhook, messageGenParams); + } +} diff --git a/src/main/java/dev/vality/wallets/hooker/handler/withdrawal/generator/WithdrawalBodyChangedHookMessageGenerator.java b/src/main/java/dev/vality/wallets/hooker/handler/withdrawal/generator/WithdrawalBodyChangedHookMessageGenerator.java new file mode 100644 index 0000000..b85a64f --- /dev/null +++ b/src/main/java/dev/vality/wallets/hooker/handler/withdrawal/generator/WithdrawalBodyChangedHookMessageGenerator.java @@ -0,0 +1,124 @@ +package dev.vality.wallets.hooker.handler.withdrawal.generator; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import dev.vality.fistful.withdrawal.BodyChange; +import dev.vality.fistful.withdrawal.WithdrawalState; +import dev.vality.swag.wallets.webhook.events.model.Event; +import dev.vality.swag.wallets.webhook.events.model.Fee; +import dev.vality.swag.wallets.webhook.events.model.WithdrawalBody; +import dev.vality.swag.wallets.webhook.events.model.WithdrawalCashChanged; +import dev.vality.wallets.hooker.domain.WebHookModel; +import dev.vality.wallets.hooker.domain.enums.EventType; +import dev.vality.wallets.hooker.exception.GenerateMessageException; +import dev.vality.wallets.hooker.handler.AdditionalHeadersGenerator; +import dev.vality.wallets.hooker.model.MessageGenParams; +import dev.vality.wallets.hooker.service.BaseHookMessageGenerator; +import dev.vality.wallets.hooker.service.WebHookMessageGeneratorServiceImpl; +import dev.vality.wallets.hooker.service.WithdrawalClient; +import dev.vality.wallets.hooker.utils.CashFlowUtils; +import dev.vality.webhook.dispatcher.WebhookMessage; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Component; + +import java.time.OffsetDateTime; + +@Slf4j +@Component +public class WithdrawalBodyChangedHookMessageGenerator extends BaseHookMessageGenerator { + + private final WebHookMessageGeneratorServiceImpl generatorService; + private final ObjectMapper objectMapper; + private final AdditionalHeadersGenerator additionalHeadersGenerator; + private final WithdrawalClient withdrawalClient; + + public WithdrawalBodyChangedHookMessageGenerator( + WebHookMessageGeneratorServiceImpl generatorService, + ObjectMapper objectMapper, + AdditionalHeadersGenerator additionalHeadersGenerator, + WithdrawalClient withdrawalClient, + @Value("${parent.not.exist.id}") Long parentId) { + super(parentId); + this.generatorService = generatorService; + this.objectMapper = objectMapper; + this.additionalHeadersGenerator = additionalHeadersGenerator; + this.withdrawalClient = withdrawalClient; + } + + @Override + protected WebhookMessage generateMessage( + BodyChange event, + WebHookModel model, + MessageGenParams messageGenParams) { + try { + var withdrawalId = messageGenParams.getSourceId(); + var eventId = messageGenParams.getEventId(); + WithdrawalState withdrawalState = getWithdrawalState(withdrawalId, eventId); + Fee fee = calculateFee(withdrawalId, eventId, withdrawalState); + WithdrawalCashChanged withdrawalCashChanged = new WithdrawalCashChanged() + .withdrawalID(withdrawalId) + .externalID(messageGenParams.getExternalId()) + .fee(fee) + .body(new WithdrawalBody() + .amount(event.getOldBody().getAmount()) + .changedAmount(event.getNewBody().getAmount()) + .currency(event.getNewBody().getCurrency().getSymbolicCode())); + withdrawalCashChanged.setEventType(Event.EventTypeEnum.WITHDRAWAL_CASH_CHANGED); + withdrawalCashChanged.setEventID(eventId.toString()); + withdrawalCashChanged.setOccuredAt(OffsetDateTime.parse(messageGenParams.getCreatedAt())); + withdrawalCashChanged.setTopic(Event.TopicEnum.WITHDRAWAL_TOPIC); + String message = objectMapper.writeValueAsString(withdrawalCashChanged); + + WebhookMessage webhookMessage = generatorService.generate(event, model, messageGenParams); + webhookMessage.setParentEventId(initPatenId(model, messageGenParams.getParentId())); + webhookMessage.setRequestBody(message.getBytes()); + webhookMessage.setAdditionalHeaders(additionalHeadersGenerator.generate(model, message)); + + log.info( + "Webhook message from withdrawal_event_body_changed was generated, " + + "withdrawalId={}, bodyChange={}, model={}, body={}, externalId={}", + messageGenParams.getSourceId(), event, model, message, + messageGenParams.getExternalId()); + + return webhookMessage; + } catch (JsonProcessingException e) { + log.error("Error when generate WithdrawalCashChanged event: {} model: {} eventId: {} e: ", + event, model, messageGenParams.getEventId(), e); + throw new GenerateMessageException("WithdrawalCreated error when generate webhookMessage!", e); + } + + } + + private Long initPatenId(WebHookModel model, Long parentId) { + if (model.getEventTypes() != null && model.getEventTypes().contains(EventType.WITHDRAWAL_CREATED)) { + return parentId; + } + + return super.parentIsNotExistId; + } + + private WithdrawalState getWithdrawalState(String withdrawalId, Long eventId) { + try { + return withdrawalClient.getWithdrawalInfo(withdrawalId, eventId); + } catch (Exception e) { + log.warn("Error getting withdrawal state for withdrawalId={}, eventId={}: {}", + withdrawalId, eventId, e.getMessage()); + return null; + } + } + + private Fee calculateFee(String withdrawalId, Long eventId, WithdrawalState withdrawalState) { + if (withdrawalState != null + && withdrawalState.getEffectiveFinalCashFlow() != null + && withdrawalState.getEffectiveFinalCashFlow().getPostings() != null) { + long amount = CashFlowUtils.getWithdrawalFee(withdrawalState.getEffectiveFinalCashFlow().getPostings()); + String currency = withdrawalState.getBody().getCurrency().getSymbolicCode(); + return new Fee().amount(amount).currency(currency); + } + log.warn("Unable to calculate fee for withdrawalId={}, eventId={}: missing cash flow data", + withdrawalId, eventId); + return null; + } + +} diff --git a/src/main/java/dev/vality/wallets/hooker/handler/withdrawal/generator/WithdrawalStatusChangedHookMessageGenerator.java b/src/main/java/dev/vality/wallets/hooker/handler/withdrawal/generator/WithdrawalStatusChangedHookMessageGenerator.java index c3d1a82..a343e84 100644 --- a/src/main/java/dev/vality/wallets/hooker/handler/withdrawal/generator/WithdrawalStatusChangedHookMessageGenerator.java +++ b/src/main/java/dev/vality/wallets/hooker/handler/withdrawal/generator/WithdrawalStatusChangedHookMessageGenerator.java @@ -154,7 +154,7 @@ private WithdrawalBody initNewBody(WithdrawalState withdrawalState) { if (!amountChanged(withdrawalState)) { return null; } - return initBody(withdrawalState.getNewBody()); + return initBody(withdrawalState.getNewBody(), withdrawalState.getBody()); } private boolean amountChanged(WithdrawalState withdrawalState) { @@ -162,10 +162,11 @@ private boolean amountChanged(WithdrawalState withdrawalState) { && withdrawalState.isSetNewBody(); } - private WithdrawalBody initBody(Cash body) { + private WithdrawalBody initBody(Cash newBody, Cash oldBody) { var withdrawalBody = new WithdrawalBody(); - withdrawalBody.setAmount(body.getAmount()); - withdrawalBody.setCurrency(body.getCurrency().getSymbolicCode()); + withdrawalBody.setAmount(oldBody.getAmount()); + withdrawalBody.setChangedAmount(newBody.getAmount()); + withdrawalBody.setCurrency(newBody.getCurrency().getSymbolicCode()); return withdrawalBody; } diff --git a/src/main/resources/db/migration/V8__add_event_type.sql b/src/main/resources/db/migration/V8__add_event_type.sql new file mode 100644 index 0000000..85c04ed --- /dev/null +++ b/src/main/resources/db/migration/V8__add_event_type.sql @@ -0,0 +1 @@ +ALTER TYPE whook.event_type ADD VALUE 'WITHDRAWAL_CASH_CHANGED'; diff --git a/src/test/java/dev/vality/wallets/hooker/handler/TestBeanFactory.java b/src/test/java/dev/vality/wallets/hooker/handler/TestBeanFactory.java index 816455c..9588460 100644 --- a/src/test/java/dev/vality/wallets/hooker/handler/TestBeanFactory.java +++ b/src/test/java/dev/vality/wallets/hooker/handler/TestBeanFactory.java @@ -10,10 +10,7 @@ import dev.vality.fistful.cashflow.WalletCashFlowAccount; import dev.vality.fistful.destination.Destination; import dev.vality.fistful.destination.TimestampedChange; -import dev.vality.fistful.withdrawal.CreatedChange; -import dev.vality.fistful.withdrawal.StatusChange; -import dev.vality.fistful.withdrawal.Withdrawal; -import dev.vality.fistful.withdrawal.WithdrawalState; +import dev.vality.fistful.withdrawal.*; import dev.vality.fistful.withdrawal.status.Status; import dev.vality.fistful.withdrawal.status.Succeeded; import dev.vality.kafka.common.serialization.ThriftSerializer; @@ -139,6 +136,24 @@ public static MachineEvent createWithdrawalSucceeded(Long eventId) { timestampedChange); } + public static MachineEvent createWithdrawalCashChanged(Long eventId) { + dev.vality.fistful.withdrawal.Change change = new dev.vality.fistful.withdrawal.Change(); + change.setBodyChanged(new BodyChange() + .setOldBody(createCash(1000, "USD")) + .setNewBody(createCash(1500, "USD"))); + + dev.vality.fistful.withdrawal.TimestampedChange timestampedChange = + new dev.vality.fistful.withdrawal.TimestampedChange() + .setOccuredAt("2016-03-22T06:12:27Z") + .setChange(change); + + return machineEvent( + WITHDRAWAL_ID, + eventId, + new ThriftSerializer<>(), + timestampedChange); + } + public static WithdrawalState createWithdrawalState() { return new WithdrawalState() .setId(WITHDRAWAL_ID) @@ -162,6 +177,7 @@ public static WebHookModel createWebhookModel() { LinkedHashSet eventTypes = new LinkedHashSet<>(); eventTypes.add(EventType.WITHDRAWAL_CREATED); eventTypes.add(EventType.WITHDRAWAL_SUCCEEDED); + eventTypes.add(EventType.WITHDRAWAL_CASH_CHANGED); return WebHookModel.builder() .enabled(true) .partyId(TestBeanFactory.PARTY_ID) diff --git a/src/test/java/dev/vality/wallets/hooker/handler/WithdrawalEventHandlerTest.java b/src/test/java/dev/vality/wallets/hooker/handler/WithdrawalEventHandlerTest.java index cbff401..8bb5651 100644 --- a/src/test/java/dev/vality/wallets/hooker/handler/WithdrawalEventHandlerTest.java +++ b/src/test/java/dev/vality/wallets/hooker/handler/WithdrawalEventHandlerTest.java @@ -18,12 +18,8 @@ import java.util.List; import java.util.concurrent.CountDownLatch; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.anyLong; -import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.timeout; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; +import static org.mockito.ArgumentMatchers.*; +import static org.mockito.Mockito.*; @PostgresqlSpringBootITest class WithdrawalEventHandlerTest { @@ -83,7 +79,29 @@ void handleWithdrawalSucceededWithNewBodyInSucceededWebhook() { .map(WebhookMessage::getRequestBody) .map(String::new) .anyMatch(body -> body.contains("\"eventType\":\"WithdrawalSucceeded\"") - && body.contains("\"body\":{\"amount\":1500,\"currency\":\"USD\"}") + && body.contains("\"body\":{\"amount\":1000,\"changedAmount\":1500,\"currency\":\"USD\"}") && body.contains("\"fee\":{\"amount\":25,\"currency\":\"RUB\"}"))); } + + @Test + void handleWithdrawalCashChangedInSucceededWebhook() { + WebHookModel webhook = TestBeanFactory.createWebhookModel(); + when(withdrawalClient.getWithdrawalInfo(eq(TestBeanFactory.WITHDRAWAL_ID), anyLong())) + .thenReturn(TestBeanFactory.createWithdrawalState()); + + webHookDao.create(webhook); + + withdrawalEventService.handleEvents(List.of(TestBeanFactory.createWithdrawalEvent())); + withdrawalEventService.handleEvents(List.of(TestBeanFactory.createWithdrawalSucceeded(69L))); + withdrawalEventService.handleEvents(List.of(TestBeanFactory.createWithdrawalCashChanged(70L))); + + ArgumentCaptor captor = ArgumentCaptor.forClass(WebhookMessage.class); + verify(webHookMessageSenderService, timeout(1000L).times(3)) + .send(captor.capture()); + Assertions.assertTrue(captor.getAllValues().stream() + .map(WebhookMessage::getRequestBody) + .map(String::new) + .anyMatch(body -> body.contains("\"eventType\":\"WithdrawalCashChanged\"") + && body.contains("\"body\":{\"amount\":1000,\"changedAmount\":1500,\"currency\":\"USD\"}"))); + } } From 8a9ab2026e1faf9ea97b4e65d6c4e2edbcd96b37 Mon Sep 17 00:00:00 2001 From: malkoas Date: Wed, 2 Sep 2026 15:07:51 +0300 Subject: [PATCH 2/6] change handle to adjustment --- .../WithdrawalAdjustmentSucceededHandler.java | 60 +++++++++ .../WithdrawalBodyChangedHandler.java | 95 -------------- .../WithdrawalChangeStatusHandler.java | 18 ++- .../withdrawal/WithdrawalFailedHandler.java | 18 ++- .../WithdrawalSucceededHandler.java | 20 ++- ...drawalBodyChangedHookMessageGenerator.java | 124 ------------------ ...awalStatusChangedHookMessageGenerator.java | 41 ++---- .../hooker/model/MessageGenParams.java | 3 + .../db/migration/V8__add_event_type.sql | 1 - .../hooker/handler/TestBeanFactory.java | 23 ++-- .../handler/WithdrawalEventHandlerTest.java | 8 +- 11 files changed, 138 insertions(+), 273 deletions(-) create mode 100644 src/main/java/dev/vality/wallets/hooker/handler/withdrawal/WithdrawalAdjustmentSucceededHandler.java delete mode 100644 src/main/java/dev/vality/wallets/hooker/handler/withdrawal/WithdrawalBodyChangedHandler.java delete mode 100644 src/main/java/dev/vality/wallets/hooker/handler/withdrawal/generator/WithdrawalBodyChangedHookMessageGenerator.java delete mode 100644 src/main/resources/db/migration/V8__add_event_type.sql diff --git a/src/main/java/dev/vality/wallets/hooker/handler/withdrawal/WithdrawalAdjustmentSucceededHandler.java b/src/main/java/dev/vality/wallets/hooker/handler/withdrawal/WithdrawalAdjustmentSucceededHandler.java new file mode 100644 index 0000000..9439a66 --- /dev/null +++ b/src/main/java/dev/vality/wallets/hooker/handler/withdrawal/WithdrawalAdjustmentSucceededHandler.java @@ -0,0 +1,60 @@ +package dev.vality.wallets.hooker.handler.withdrawal; + +import dev.vality.fistful.withdrawal.TimestampedChange; +import dev.vality.fistful.withdrawal.WithdrawalState; +import dev.vality.machinegun.eventsink.MachineEvent; +import dev.vality.wallets.hooker.domain.enums.EventType; +import dev.vality.wallets.hooker.service.WithdrawalClient; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Component; + +@Slf4j +@Component +@RequiredArgsConstructor +public class WithdrawalAdjustmentSucceededHandler implements WithdrawalEventHandler { + + private final WithdrawalChangeStatusHandler withdrawalChangeStatusHandler; + private final WithdrawalClient withdrawalClient; + + @Override + public boolean accept(TimestampedChange change) { + return change.getChange().isSetAdjustment() + && change.getChange().getAdjustment().isSetPayload() + && change.getChange().getAdjustment().getPayload().isSetStatusChanged() + && change.getChange().getAdjustment().getPayload().getStatusChanged().isSetStatus() + && change.getChange().getAdjustment().getPayload().getStatusChanged().getStatus().isSetSucceeded(); + } + + @Override + public void handle(TimestampedChange change, MachineEvent event) { + String withdrawalId = event.getSourceId(); + WithdrawalState withdrawalState = getWithdrawalState(withdrawalId, event.getEventId()); + if (withdrawalState != null + && withdrawalState.getStatus() != null + && !withdrawalState.getStatus().isSetPending()) { + log.info("Start handling WithdrawalAdjustmentChange: withdrawalId={} change={}", withdrawalId, change); + + withdrawalChangeStatusHandler.handleChangeStatus( + change, + event, + withdrawalId, + withdrawalState.getStatus().isSetSucceeded() + ? EventType.WITHDRAWAL_SUCCEEDED + : EventType.WITHDRAWAL_FAILED, + withdrawalState); + + log.info("Finish handling WithdrawalAdjustmentChange: withdrawalId={}", withdrawalId); + } + } + + private WithdrawalState getWithdrawalState(String withdrawalId, Long eventId) { + try { + return withdrawalClient.getWithdrawalInfo(withdrawalId, eventId); + } catch (Exception e) { + log.warn("Error getting withdrawal state for withdrawalId={}, eventId={}: {}", + withdrawalId, eventId, e.getMessage()); + return null; + } + } +} diff --git a/src/main/java/dev/vality/wallets/hooker/handler/withdrawal/WithdrawalBodyChangedHandler.java b/src/main/java/dev/vality/wallets/hooker/handler/withdrawal/WithdrawalBodyChangedHandler.java deleted file mode 100644 index d831450..0000000 --- a/src/main/java/dev/vality/wallets/hooker/handler/withdrawal/WithdrawalBodyChangedHandler.java +++ /dev/null @@ -1,95 +0,0 @@ -package dev.vality.wallets.hooker.handler.withdrawal; - -import dev.vality.fistful.withdrawal.BodyChange; -import dev.vality.fistful.withdrawal.TimestampedChange; -import dev.vality.machinegun.eventsink.MachineEvent; -import dev.vality.wallets.hooker.dao.webhook.WebHookDao; -import dev.vality.wallets.hooker.dao.withdrawal.WithdrawalReferenceDao; -import dev.vality.wallets.hooker.domain.WebHookModel; -import dev.vality.wallets.hooker.domain.enums.EventType; -import dev.vality.wallets.hooker.exception.HandleEventException; -import dev.vality.wallets.hooker.handler.withdrawal.generator.WithdrawalBodyChangedHookMessageGenerator; -import dev.vality.wallets.hooker.model.MessageGenParams; -import dev.vality.wallets.hooker.service.WebHookMessageSenderService; -import dev.vality.webhook.dispatcher.WebhookMessage; -import lombok.RequiredArgsConstructor; -import lombok.extern.slf4j.Slf4j; -import org.springframework.stereotype.Component; - -@Slf4j -@Component -@RequiredArgsConstructor -public class WithdrawalBodyChangedHandler implements WithdrawalEventHandler { - - private final WithdrawalReferenceDao withdrawalReferenceDao; - private final WebHookDao webHookDao; - private final WithdrawalBodyChangedHookMessageGenerator withdrawalBodyChangedHookMessageGenerator; - private final WebHookMessageSenderService webHookMessageSenderService; - - @Override - public boolean accept(TimestampedChange change) { - return change.getChange().isSetBodyChanged(); - } - - @Override - public void handle(TimestampedChange change, MachineEvent event) { - String withdrawalId = event.getSourceId(); - log.info("Start handling WithdrawalBodyChange: withdrawalId={} change={}", withdrawalId, change); - - handleChangeBody( - change, - event, - withdrawalId, - EventType.WITHDRAWAL_CASH_CHANGED); - - log.info("Finish handling WithdrawalBodyChange: withdrawalId={}", withdrawalId); - } - - public void handleChangeBody( - TimestampedChange change, - MachineEvent event, - String withdrawalId, - EventType eventType) { - try { - var reference = withdrawalReferenceDao.get(withdrawalId); - ; - Long parentId = Long.valueOf(reference.getEventId()); - - webHookDao.getByPartyAndEventType(reference.getPartyId(), eventType).stream() - .filter(webHook -> webHook.getWalletId() == null - || webHook.getWalletId().equals(reference.getWalletId())) - .map(webhook -> generateWithdrawalBodyChangeHookMsg( - change.getChange().getBodyChanged(), - webhook, - withdrawalId, - event.getEventId(), - parentId, - event.getCreatedAt(), - reference.getExternalId())) - .forEach(webHookMessageSenderService::send); - } catch (Exception e) { - log.error("Error while handling WithdrawalBodyChange: {}, withdrawalId: {}", - change, withdrawalId, e); - throw new HandleEventException("Error while handling WithdrawalBodyChange", e); - } - } - - private WebhookMessage generateWithdrawalBodyChangeHookMsg( - BodyChange bodyChanged, - WebHookModel webhook, - String withdrawalId, - long eventId, - Long parentId, - String createdAt, - String externalId) { - MessageGenParams messageGenParams = MessageGenParams.builder() - .sourceId(withdrawalId) - .eventId(eventId) - .parentId(parentId) - .createdAt(createdAt) - .externalId(externalId) - .build(); - - return withdrawalBodyChangedHookMessageGenerator.generate(bodyChanged, webhook, messageGenParams); - } -} diff --git a/src/main/java/dev/vality/wallets/hooker/handler/withdrawal/WithdrawalChangeStatusHandler.java b/src/main/java/dev/vality/wallets/hooker/handler/withdrawal/WithdrawalChangeStatusHandler.java index 1328f90..77d7d25 100644 --- a/src/main/java/dev/vality/wallets/hooker/handler/withdrawal/WithdrawalChangeStatusHandler.java +++ b/src/main/java/dev/vality/wallets/hooker/handler/withdrawal/WithdrawalChangeStatusHandler.java @@ -1,7 +1,7 @@ package dev.vality.wallets.hooker.handler.withdrawal; -import dev.vality.fistful.withdrawal.StatusChange; import dev.vality.fistful.withdrawal.TimestampedChange; +import dev.vality.fistful.withdrawal.WithdrawalState; import dev.vality.machinegun.eventsink.MachineEvent; import dev.vality.wallets.hooker.dao.webhook.WebHookDao; import dev.vality.wallets.hooker.dao.withdrawal.WithdrawalReferenceDao; @@ -35,7 +35,8 @@ public void handleChangeStatus( TimestampedChange change, MachineEvent event, String withdrawalId, - EventType eventType) { + EventType eventType, + WithdrawalState withdrawalState) { try { var reference = waitReferenceWithdrawal(withdrawalId); Long parentId = Long.valueOf(reference.getEventId()); @@ -44,13 +45,14 @@ public void handleChangeStatus( .filter(webHook -> webHook.getWalletId() == null || webHook.getWalletId().equals(reference.getWalletId())) .map(webhook -> generateWithdrawalStatusChangeHookMsg( - change.getChange().getStatusChanged(), + change, webhook, withdrawalId, event.getEventId(), parentId, event.getCreatedAt(), - reference.getExternalId())) + reference.getExternalId(), + withdrawalState)) .forEach(webHookMessageSenderService::send); } catch (Exception e) { log.error("Error while handling WithdrawalStatusChangedChange: {}, withdrawalId: {}", @@ -76,22 +78,24 @@ private WithdrawalReference waitReferenceWithdrawal(String withdrawalId) { } private WebhookMessage generateWithdrawalStatusChangeHookMsg( - StatusChange statusChanged, + TimestampedChange change, WebHookModel webhook, String withdrawalId, long eventId, Long parentId, String createdAt, - String externalId) { + String externalId, + WithdrawalState withdrawalState) { MessageGenParams messageGenParams = MessageGenParams.builder() .sourceId(withdrawalId) .eventId(eventId) .parentId(parentId) .createdAt(createdAt) .externalId(externalId) + .withdrawalState(withdrawalState) .build(); - return withdrawalStatusChangedHookMessageGenerator.generate(statusChanged, webhook, messageGenParams); + return withdrawalStatusChangedHookMessageGenerator.generate(change, webhook, messageGenParams); } } diff --git a/src/main/java/dev/vality/wallets/hooker/handler/withdrawal/WithdrawalFailedHandler.java b/src/main/java/dev/vality/wallets/hooker/handler/withdrawal/WithdrawalFailedHandler.java index 6477c24..3385f5b 100644 --- a/src/main/java/dev/vality/wallets/hooker/handler/withdrawal/WithdrawalFailedHandler.java +++ b/src/main/java/dev/vality/wallets/hooker/handler/withdrawal/WithdrawalFailedHandler.java @@ -1,8 +1,10 @@ package dev.vality.wallets.hooker.handler.withdrawal; import dev.vality.fistful.withdrawal.TimestampedChange; +import dev.vality.fistful.withdrawal.WithdrawalState; import dev.vality.machinegun.eventsink.MachineEvent; import dev.vality.wallets.hooker.domain.enums.EventType; +import dev.vality.wallets.hooker.service.WithdrawalClient; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Component; @@ -13,6 +15,8 @@ public class WithdrawalFailedHandler implements WithdrawalEventHandler { private final WithdrawalChangeStatusHandler withdrawalChangeStatusHandler; + private final WithdrawalClient withdrawalClient; + @Override public boolean accept(TimestampedChange change) { @@ -24,14 +28,26 @@ public boolean accept(TimestampedChange change) { @Override public void handle(TimestampedChange change, MachineEvent event) { String withdrawalId = event.getSourceId(); + WithdrawalState withdrawalState = getWithdrawalState(withdrawalId, event.getEventId()); log.info("Start handling WithdrawalFailedChange: withdrawalId={} change={}", withdrawalId, change); withdrawalChangeStatusHandler.handleChangeStatus( change, event, withdrawalId, - EventType.WITHDRAWAL_FAILED); + EventType.WITHDRAWAL_FAILED, + withdrawalState); log.info("Finish handling WithdrawalFailedChange: withdrawalId={}", withdrawalId); } + + private WithdrawalState getWithdrawalState(String withdrawalId, Long eventId) { + try { + return withdrawalClient.getWithdrawalInfo(withdrawalId, eventId); + } catch (Exception e) { + log.warn("Error getting withdrawal state for withdrawalId={}, eventId={}: {}", + withdrawalId, eventId, e.getMessage()); + return null; + } + } } diff --git a/src/main/java/dev/vality/wallets/hooker/handler/withdrawal/WithdrawalSucceededHandler.java b/src/main/java/dev/vality/wallets/hooker/handler/withdrawal/WithdrawalSucceededHandler.java index f12cec0..751605e 100644 --- a/src/main/java/dev/vality/wallets/hooker/handler/withdrawal/WithdrawalSucceededHandler.java +++ b/src/main/java/dev/vality/wallets/hooker/handler/withdrawal/WithdrawalSucceededHandler.java @@ -1,8 +1,10 @@ package dev.vality.wallets.hooker.handler.withdrawal; import dev.vality.fistful.withdrawal.TimestampedChange; +import dev.vality.fistful.withdrawal.WithdrawalState; import dev.vality.machinegun.eventsink.MachineEvent; import dev.vality.wallets.hooker.domain.enums.EventType; +import dev.vality.wallets.hooker.service.WithdrawalClient; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Component; @@ -13,6 +15,7 @@ public class WithdrawalSucceededHandler implements WithdrawalEventHandler { private final WithdrawalChangeStatusHandler withdrawalChangeStatusHandler; + private final WithdrawalClient withdrawalClient; @Override public boolean accept(TimestampedChange change) { @@ -24,14 +27,27 @@ public boolean accept(TimestampedChange change) { @Override public void handle(TimestampedChange change, MachineEvent event) { String withdrawalId = event.getSourceId(); + WithdrawalState withdrawalState = getWithdrawalState(withdrawalId, event.getEventId()); + log.info("Start handling WithdrawalSucceededChange: withdrawalId={} change={}", withdrawalId, change); withdrawalChangeStatusHandler.handleChangeStatus( change, event, withdrawalId, - EventType.WITHDRAWAL_SUCCEEDED); + EventType.WITHDRAWAL_SUCCEEDED, + withdrawalState); log.info("Finish handling WithdrawalSucceededChange: withdrawalId={}", withdrawalId); } -} + + private WithdrawalState getWithdrawalState(String withdrawalId, Long eventId) { + try { + return withdrawalClient.getWithdrawalInfo(withdrawalId, eventId); + } catch (Exception e) { + log.warn("Error getting withdrawal state for withdrawalId={}, eventId={}: {}", + withdrawalId, eventId, e.getMessage()); + return null; + } + } +} \ No newline at end of file diff --git a/src/main/java/dev/vality/wallets/hooker/handler/withdrawal/generator/WithdrawalBodyChangedHookMessageGenerator.java b/src/main/java/dev/vality/wallets/hooker/handler/withdrawal/generator/WithdrawalBodyChangedHookMessageGenerator.java deleted file mode 100644 index b85a64f..0000000 --- a/src/main/java/dev/vality/wallets/hooker/handler/withdrawal/generator/WithdrawalBodyChangedHookMessageGenerator.java +++ /dev/null @@ -1,124 +0,0 @@ -package dev.vality.wallets.hooker.handler.withdrawal.generator; - -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.ObjectMapper; -import dev.vality.fistful.withdrawal.BodyChange; -import dev.vality.fistful.withdrawal.WithdrawalState; -import dev.vality.swag.wallets.webhook.events.model.Event; -import dev.vality.swag.wallets.webhook.events.model.Fee; -import dev.vality.swag.wallets.webhook.events.model.WithdrawalBody; -import dev.vality.swag.wallets.webhook.events.model.WithdrawalCashChanged; -import dev.vality.wallets.hooker.domain.WebHookModel; -import dev.vality.wallets.hooker.domain.enums.EventType; -import dev.vality.wallets.hooker.exception.GenerateMessageException; -import dev.vality.wallets.hooker.handler.AdditionalHeadersGenerator; -import dev.vality.wallets.hooker.model.MessageGenParams; -import dev.vality.wallets.hooker.service.BaseHookMessageGenerator; -import dev.vality.wallets.hooker.service.WebHookMessageGeneratorServiceImpl; -import dev.vality.wallets.hooker.service.WithdrawalClient; -import dev.vality.wallets.hooker.utils.CashFlowUtils; -import dev.vality.webhook.dispatcher.WebhookMessage; -import lombok.extern.slf4j.Slf4j; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.stereotype.Component; - -import java.time.OffsetDateTime; - -@Slf4j -@Component -public class WithdrawalBodyChangedHookMessageGenerator extends BaseHookMessageGenerator { - - private final WebHookMessageGeneratorServiceImpl generatorService; - private final ObjectMapper objectMapper; - private final AdditionalHeadersGenerator additionalHeadersGenerator; - private final WithdrawalClient withdrawalClient; - - public WithdrawalBodyChangedHookMessageGenerator( - WebHookMessageGeneratorServiceImpl generatorService, - ObjectMapper objectMapper, - AdditionalHeadersGenerator additionalHeadersGenerator, - WithdrawalClient withdrawalClient, - @Value("${parent.not.exist.id}") Long parentId) { - super(parentId); - this.generatorService = generatorService; - this.objectMapper = objectMapper; - this.additionalHeadersGenerator = additionalHeadersGenerator; - this.withdrawalClient = withdrawalClient; - } - - @Override - protected WebhookMessage generateMessage( - BodyChange event, - WebHookModel model, - MessageGenParams messageGenParams) { - try { - var withdrawalId = messageGenParams.getSourceId(); - var eventId = messageGenParams.getEventId(); - WithdrawalState withdrawalState = getWithdrawalState(withdrawalId, eventId); - Fee fee = calculateFee(withdrawalId, eventId, withdrawalState); - WithdrawalCashChanged withdrawalCashChanged = new WithdrawalCashChanged() - .withdrawalID(withdrawalId) - .externalID(messageGenParams.getExternalId()) - .fee(fee) - .body(new WithdrawalBody() - .amount(event.getOldBody().getAmount()) - .changedAmount(event.getNewBody().getAmount()) - .currency(event.getNewBody().getCurrency().getSymbolicCode())); - withdrawalCashChanged.setEventType(Event.EventTypeEnum.WITHDRAWAL_CASH_CHANGED); - withdrawalCashChanged.setEventID(eventId.toString()); - withdrawalCashChanged.setOccuredAt(OffsetDateTime.parse(messageGenParams.getCreatedAt())); - withdrawalCashChanged.setTopic(Event.TopicEnum.WITHDRAWAL_TOPIC); - String message = objectMapper.writeValueAsString(withdrawalCashChanged); - - WebhookMessage webhookMessage = generatorService.generate(event, model, messageGenParams); - webhookMessage.setParentEventId(initPatenId(model, messageGenParams.getParentId())); - webhookMessage.setRequestBody(message.getBytes()); - webhookMessage.setAdditionalHeaders(additionalHeadersGenerator.generate(model, message)); - - log.info( - "Webhook message from withdrawal_event_body_changed was generated, " + - "withdrawalId={}, bodyChange={}, model={}, body={}, externalId={}", - messageGenParams.getSourceId(), event, model, message, - messageGenParams.getExternalId()); - - return webhookMessage; - } catch (JsonProcessingException e) { - log.error("Error when generate WithdrawalCashChanged event: {} model: {} eventId: {} e: ", - event, model, messageGenParams.getEventId(), e); - throw new GenerateMessageException("WithdrawalCreated error when generate webhookMessage!", e); - } - - } - - private Long initPatenId(WebHookModel model, Long parentId) { - if (model.getEventTypes() != null && model.getEventTypes().contains(EventType.WITHDRAWAL_CREATED)) { - return parentId; - } - - return super.parentIsNotExistId; - } - - private WithdrawalState getWithdrawalState(String withdrawalId, Long eventId) { - try { - return withdrawalClient.getWithdrawalInfo(withdrawalId, eventId); - } catch (Exception e) { - log.warn("Error getting withdrawal state for withdrawalId={}, eventId={}: {}", - withdrawalId, eventId, e.getMessage()); - return null; - } - } - - private Fee calculateFee(String withdrawalId, Long eventId, WithdrawalState withdrawalState) { - if (withdrawalState != null - && withdrawalState.getEffectiveFinalCashFlow() != null - && withdrawalState.getEffectiveFinalCashFlow().getPostings() != null) { - long amount = CashFlowUtils.getWithdrawalFee(withdrawalState.getEffectiveFinalCashFlow().getPostings()); - String currency = withdrawalState.getBody().getCurrency().getSymbolicCode(); - return new Fee().amount(amount).currency(currency); - } - log.warn("Unable to calculate fee for withdrawalId={}, eventId={}: missing cash flow data", - withdrawalId, eventId); - return null; - } - -} diff --git a/src/main/java/dev/vality/wallets/hooker/handler/withdrawal/generator/WithdrawalStatusChangedHookMessageGenerator.java b/src/main/java/dev/vality/wallets/hooker/handler/withdrawal/generator/WithdrawalStatusChangedHookMessageGenerator.java index a343e84..365dc27 100644 --- a/src/main/java/dev/vality/wallets/hooker/handler/withdrawal/generator/WithdrawalStatusChangedHookMessageGenerator.java +++ b/src/main/java/dev/vality/wallets/hooker/handler/withdrawal/generator/WithdrawalStatusChangedHookMessageGenerator.java @@ -3,14 +3,10 @@ import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; import dev.vality.fistful.base.Cash; +import dev.vality.fistful.withdrawal.TimestampedChange; import dev.vality.fistful.withdrawal.WithdrawalState; -import dev.vality.fistful.withdrawal.StatusChange; import dev.vality.fistful.withdrawal.status.Status; -import dev.vality.swag.wallets.webhook.events.model.Event; -import dev.vality.swag.wallets.webhook.events.model.Fee; -import dev.vality.swag.wallets.webhook.events.model.WithdrawalBody; -import dev.vality.swag.wallets.webhook.events.model.WithdrawalFailed; -import dev.vality.swag.wallets.webhook.events.model.WithdrawalSucceeded; +import dev.vality.swag.wallets.webhook.events.model.*; import dev.vality.wallets.hooker.domain.WebHookModel; import dev.vality.wallets.hooker.domain.enums.EventType; import dev.vality.wallets.hooker.exception.GenerateMessageException; @@ -18,7 +14,6 @@ import dev.vality.wallets.hooker.model.MessageGenParams; import dev.vality.wallets.hooker.service.BaseHookMessageGenerator; import dev.vality.wallets.hooker.service.WebHookMessageGeneratorServiceImpl; -import dev.vality.wallets.hooker.service.WithdrawalClient; import dev.vality.wallets.hooker.utils.CashFlowUtils; import dev.vality.webhook.dispatcher.WebhookMessage; import lombok.extern.slf4j.Slf4j; @@ -30,38 +25,38 @@ @Slf4j @Component -public class WithdrawalStatusChangedHookMessageGenerator extends BaseHookMessageGenerator { +public class WithdrawalStatusChangedHookMessageGenerator extends BaseHookMessageGenerator { - private final WebHookMessageGeneratorServiceImpl generatorService; + private final WebHookMessageGeneratorServiceImpl generatorService; private final ObjectMapper objectMapper; private final AdditionalHeadersGenerator additionalHeadersGenerator; - private final WithdrawalClient withdrawalClient; public WithdrawalStatusChangedHookMessageGenerator( - WebHookMessageGeneratorServiceImpl generatorService, + WebHookMessageGeneratorServiceImpl generatorService, ObjectMapper objectMapper, AdditionalHeadersGenerator additionalHeadersGenerator, - WithdrawalClient withdrawalClient, @Value("${parent.not.exist.id}") Long parentId) { super(parentId); this.generatorService = generatorService; this.objectMapper = objectMapper; this.additionalHeadersGenerator = additionalHeadersGenerator; - this.withdrawalClient = withdrawalClient; } @Override protected WebhookMessage generateMessage( - StatusChange event, + TimestampedChange event, WebHookModel model, MessageGenParams messageGenParams) { try { String message = initRequestBody( - event.getStatus(), + event.getChange().isSetStatusChanged() + ? event.getChange().getStatusChanged().getStatus() + : messageGenParams.getWithdrawalState().getStatus(), messageGenParams.getSourceId(), messageGenParams.getEventId(), messageGenParams.getCreatedAt(), - messageGenParams.getExternalId()); + messageGenParams.getExternalId(), + messageGenParams.getWithdrawalState()); WebhookMessage webhookMessage = generatorService.generate(event, model, messageGenParams); webhookMessage.setParentEventId(initPatenId(model, messageGenParams.getParentId())); @@ -95,7 +90,8 @@ private String initRequestBody( String withdrawalId, Long eventId, String createdAt, - String externalId) throws JsonProcessingException { + String externalId, + WithdrawalState withdrawalState) throws JsonProcessingException { if (status.isSetFailed()) { WithdrawalFailed withdrawalFailed = new WithdrawalFailed() .withdrawalID(withdrawalId) @@ -106,7 +102,6 @@ private String initRequestBody( withdrawalFailed.setTopic(Event.TopicEnum.WITHDRAWAL_TOPIC); return objectMapper.writeValueAsString(withdrawalFailed); } else if (status.isSetSucceeded()) { - WithdrawalState withdrawalState = getWithdrawalState(withdrawalId, eventId); Fee fee = calculateFee(withdrawalId, eventId, withdrawalState); WithdrawalSucceeded withdrawalSucceeded = new WithdrawalSucceeded() .withdrawalID(withdrawalId) @@ -127,16 +122,6 @@ private String initRequestBody( } } - private WithdrawalState getWithdrawalState(String withdrawalId, Long eventId) { - try { - return withdrawalClient.getWithdrawalInfo(withdrawalId, eventId); - } catch (Exception e) { - log.warn("Error getting withdrawal state for withdrawalId={}, eventId={}: {}", - withdrawalId, eventId, e.getMessage()); - return null; - } - } - private Fee calculateFee(String withdrawalId, Long eventId, WithdrawalState withdrawalState) { if (withdrawalState != null && withdrawalState.getEffectiveFinalCashFlow() != null diff --git a/src/main/java/dev/vality/wallets/hooker/model/MessageGenParams.java b/src/main/java/dev/vality/wallets/hooker/model/MessageGenParams.java index 2af9c47..53159cc 100644 --- a/src/main/java/dev/vality/wallets/hooker/model/MessageGenParams.java +++ b/src/main/java/dev/vality/wallets/hooker/model/MessageGenParams.java @@ -1,5 +1,6 @@ package dev.vality.wallets.hooker.model; +import dev.vality.fistful.withdrawal.WithdrawalState; import lombok.Builder; import lombok.Data; @@ -17,4 +18,6 @@ public class MessageGenParams { private String externalId; + private WithdrawalState withdrawalState; + } diff --git a/src/main/resources/db/migration/V8__add_event_type.sql b/src/main/resources/db/migration/V8__add_event_type.sql deleted file mode 100644 index 85c04ed..0000000 --- a/src/main/resources/db/migration/V8__add_event_type.sql +++ /dev/null @@ -1 +0,0 @@ -ALTER TYPE whook.event_type ADD VALUE 'WITHDRAWAL_CASH_CHANGED'; diff --git a/src/test/java/dev/vality/wallets/hooker/handler/TestBeanFactory.java b/src/test/java/dev/vality/wallets/hooker/handler/TestBeanFactory.java index 9588460..6cd7cc0 100644 --- a/src/test/java/dev/vality/wallets/hooker/handler/TestBeanFactory.java +++ b/src/test/java/dev/vality/wallets/hooker/handler/TestBeanFactory.java @@ -2,12 +2,7 @@ import dev.vality.fistful.account.Account; import dev.vality.fistful.base.*; -import dev.vality.fistful.cashflow.CashFlowAccount; -import dev.vality.fistful.cashflow.FinalCashFlow; -import dev.vality.fistful.cashflow.FinalCashFlowAccount; -import dev.vality.fistful.cashflow.FinalCashFlowPosting; -import dev.vality.fistful.cashflow.SystemCashFlowAccount; -import dev.vality.fistful.cashflow.WalletCashFlowAccount; +import dev.vality.fistful.cashflow.*; import dev.vality.fistful.destination.Destination; import dev.vality.fistful.destination.TimestampedChange; import dev.vality.fistful.withdrawal.*; @@ -136,11 +131,17 @@ public static MachineEvent createWithdrawalSucceeded(Long eventId) { timestampedChange); } - public static MachineEvent createWithdrawalCashChanged(Long eventId) { + public static MachineEvent createWithdrawalAdjustmentChange(Long eventId) { dev.vality.fistful.withdrawal.Change change = new dev.vality.fistful.withdrawal.Change(); - change.setBodyChanged(new BodyChange() - .setOldBody(createCash(1000, "USD")) - .setNewBody(createCash(1500, "USD"))); + var adjustment = new AdjustmentChange(); + var payload = new dev.vality.fistful.withdrawal.adjustment.Change(); + var status = new dev.vality.fistful.withdrawal.adjustment.StatusChange( + dev.vality.fistful.withdrawal.adjustment.Status.succeeded( + new dev.vality.fistful.withdrawal.adjustment.Succeeded())); + payload.setStatusChanged(status); + adjustment.setId("1"); + adjustment.setPayload(payload); + change.setAdjustment(adjustment); dev.vality.fistful.withdrawal.TimestampedChange timestampedChange = new dev.vality.fistful.withdrawal.TimestampedChange() @@ -168,6 +169,7 @@ public static WithdrawalState createWithdrawalState() { public static WithdrawalState createWithdrawalStateWithNewBody() { return createWithdrawalState() + .setStatus(Status.succeeded(new Succeeded())) .setNewBody(createCash(1500, "USD")) .setEffectiveFinalCashFlow(new FinalCashFlow() .setPostings(List.of(createFeePosting()))); @@ -177,7 +179,6 @@ public static WebHookModel createWebhookModel() { LinkedHashSet eventTypes = new LinkedHashSet<>(); eventTypes.add(EventType.WITHDRAWAL_CREATED); eventTypes.add(EventType.WITHDRAWAL_SUCCEEDED); - eventTypes.add(EventType.WITHDRAWAL_CASH_CHANGED); return WebHookModel.builder() .enabled(true) .partyId(TestBeanFactory.PARTY_ID) diff --git a/src/test/java/dev/vality/wallets/hooker/handler/WithdrawalEventHandlerTest.java b/src/test/java/dev/vality/wallets/hooker/handler/WithdrawalEventHandlerTest.java index 8bb5651..439c95c 100644 --- a/src/test/java/dev/vality/wallets/hooker/handler/WithdrawalEventHandlerTest.java +++ b/src/test/java/dev/vality/wallets/hooker/handler/WithdrawalEventHandlerTest.java @@ -84,16 +84,16 @@ void handleWithdrawalSucceededWithNewBodyInSucceededWebhook() { } @Test - void handleWithdrawalCashChangedInSucceededWebhook() { + void handleWithdrawalAdjustmentSucceededWebhook() { WebHookModel webhook = TestBeanFactory.createWebhookModel(); when(withdrawalClient.getWithdrawalInfo(eq(TestBeanFactory.WITHDRAWAL_ID), anyLong())) - .thenReturn(TestBeanFactory.createWithdrawalState()); + .thenReturn(TestBeanFactory.createWithdrawalStateWithNewBody()); webHookDao.create(webhook); withdrawalEventService.handleEvents(List.of(TestBeanFactory.createWithdrawalEvent())); withdrawalEventService.handleEvents(List.of(TestBeanFactory.createWithdrawalSucceeded(69L))); - withdrawalEventService.handleEvents(List.of(TestBeanFactory.createWithdrawalCashChanged(70L))); + withdrawalEventService.handleEvents(List.of(TestBeanFactory.createWithdrawalAdjustmentChange(70L))); ArgumentCaptor captor = ArgumentCaptor.forClass(WebhookMessage.class); verify(webHookMessageSenderService, timeout(1000L).times(3)) @@ -101,7 +101,7 @@ void handleWithdrawalCashChangedInSucceededWebhook() { Assertions.assertTrue(captor.getAllValues().stream() .map(WebhookMessage::getRequestBody) .map(String::new) - .anyMatch(body -> body.contains("\"eventType\":\"WithdrawalCashChanged\"") + .anyMatch(body -> body.contains("\"eventType\":\"WithdrawalSucceeded\"") && body.contains("\"body\":{\"amount\":1000,\"changedAmount\":1500,\"currency\":\"USD\"}"))); } } From 14587b264da107c8aab984eb562cc4760738aca3 Mon Sep 17 00:00:00 2001 From: malkoas Date: Wed, 2 Sep 2026 15:08:24 +0300 Subject: [PATCH 3/6] change handle to adjustment --- .../WithdrawalStatusChangedHookMessageGenerator.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/dev/vality/wallets/hooker/handler/withdrawal/generator/WithdrawalStatusChangedHookMessageGenerator.java b/src/main/java/dev/vality/wallets/hooker/handler/withdrawal/generator/WithdrawalStatusChangedHookMessageGenerator.java index 365dc27..be81b4c 100644 --- a/src/main/java/dev/vality/wallets/hooker/handler/withdrawal/generator/WithdrawalStatusChangedHookMessageGenerator.java +++ b/src/main/java/dev/vality/wallets/hooker/handler/withdrawal/generator/WithdrawalStatusChangedHookMessageGenerator.java @@ -59,7 +59,7 @@ protected WebhookMessage generateMessage( messageGenParams.getWithdrawalState()); WebhookMessage webhookMessage = generatorService.generate(event, model, messageGenParams); - webhookMessage.setParentEventId(initPatenId(model, messageGenParams.getParentId())); + webhookMessage.setParentEventId(initParenId(model, messageGenParams.getParentId())); webhookMessage.setRequestBody(message.getBytes()); webhookMessage.setAdditionalHeaders(additionalHeadersGenerator.generate(model, message)); @@ -77,7 +77,7 @@ protected WebhookMessage generateMessage( } - private Long initPatenId(WebHookModel model, Long parentId) { + private Long initParenId(WebHookModel model, Long parentId) { if (model.getEventTypes() != null && model.getEventTypes().contains(EventType.WITHDRAWAL_CREATED)) { return parentId; } From 0c380385e9033994d882ec489ff2f42b0c58b921 Mon Sep 17 00:00:00 2001 From: malkoas Date: Wed, 2 Sep 2026 15:09:29 +0300 Subject: [PATCH 4/6] change handle to adjustment --- .../dev/vality/wallets/hooker/handler/TestBeanFactory.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/test/java/dev/vality/wallets/hooker/handler/TestBeanFactory.java b/src/test/java/dev/vality/wallets/hooker/handler/TestBeanFactory.java index 6cd7cc0..953a3af 100644 --- a/src/test/java/dev/vality/wallets/hooker/handler/TestBeanFactory.java +++ b/src/test/java/dev/vality/wallets/hooker/handler/TestBeanFactory.java @@ -132,15 +132,15 @@ public static MachineEvent createWithdrawalSucceeded(Long eventId) { } public static MachineEvent createWithdrawalAdjustmentChange(Long eventId) { - dev.vality.fistful.withdrawal.Change change = new dev.vality.fistful.withdrawal.Change(); - var adjustment = new AdjustmentChange(); - var payload = new dev.vality.fistful.withdrawal.adjustment.Change(); var status = new dev.vality.fistful.withdrawal.adjustment.StatusChange( dev.vality.fistful.withdrawal.adjustment.Status.succeeded( new dev.vality.fistful.withdrawal.adjustment.Succeeded())); + var payload = new dev.vality.fistful.withdrawal.adjustment.Change(); payload.setStatusChanged(status); + var adjustment = new AdjustmentChange(); adjustment.setId("1"); adjustment.setPayload(payload); + dev.vality.fistful.withdrawal.Change change = new dev.vality.fistful.withdrawal.Change(); change.setAdjustment(adjustment); dev.vality.fistful.withdrawal.TimestampedChange timestampedChange = From 98491e7033e663e5d7d9c16df8242a693ec7198c Mon Sep 17 00:00:00 2001 From: malkoas Date: Wed, 2 Sep 2026 15:18:24 +0300 Subject: [PATCH 5/6] change handle to adjustment --- .../withdrawal/WithdrawalAdjustmentSucceededHandler.java | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/main/java/dev/vality/wallets/hooker/handler/withdrawal/WithdrawalAdjustmentSucceededHandler.java b/src/main/java/dev/vality/wallets/hooker/handler/withdrawal/WithdrawalAdjustmentSucceededHandler.java index 9439a66..9723e72 100644 --- a/src/main/java/dev/vality/wallets/hooker/handler/withdrawal/WithdrawalAdjustmentSucceededHandler.java +++ b/src/main/java/dev/vality/wallets/hooker/handler/withdrawal/WithdrawalAdjustmentSucceededHandler.java @@ -32,16 +32,14 @@ public void handle(TimestampedChange change, MachineEvent event) { WithdrawalState withdrawalState = getWithdrawalState(withdrawalId, event.getEventId()); if (withdrawalState != null && withdrawalState.getStatus() != null - && !withdrawalState.getStatus().isSetPending()) { + && withdrawalState.getStatus().isSetSucceeded()) { log.info("Start handling WithdrawalAdjustmentChange: withdrawalId={} change={}", withdrawalId, change); withdrawalChangeStatusHandler.handleChangeStatus( change, event, withdrawalId, - withdrawalState.getStatus().isSetSucceeded() - ? EventType.WITHDRAWAL_SUCCEEDED - : EventType.WITHDRAWAL_FAILED, + EventType.WITHDRAWAL_SUCCEEDED, withdrawalState); log.info("Finish handling WithdrawalAdjustmentChange: withdrawalId={}", withdrawalId); From 1a2028d461e82fb401bf2d42f11dda8e3deb9907 Mon Sep 17 00:00:00 2001 From: malkoas Date: Wed, 2 Sep 2026 15:21:10 +0300 Subject: [PATCH 6/6] change handle to adjustment --- .../handler/withdrawal/WithdrawalFailedHandler.java | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) diff --git a/src/main/java/dev/vality/wallets/hooker/handler/withdrawal/WithdrawalFailedHandler.java b/src/main/java/dev/vality/wallets/hooker/handler/withdrawal/WithdrawalFailedHandler.java index 3385f5b..f4f6cd3 100644 --- a/src/main/java/dev/vality/wallets/hooker/handler/withdrawal/WithdrawalFailedHandler.java +++ b/src/main/java/dev/vality/wallets/hooker/handler/withdrawal/WithdrawalFailedHandler.java @@ -15,7 +15,6 @@ public class WithdrawalFailedHandler implements WithdrawalEventHandler { private final WithdrawalChangeStatusHandler withdrawalChangeStatusHandler; - private final WithdrawalClient withdrawalClient; @Override @@ -28,7 +27,6 @@ public boolean accept(TimestampedChange change) { @Override public void handle(TimestampedChange change, MachineEvent event) { String withdrawalId = event.getSourceId(); - WithdrawalState withdrawalState = getWithdrawalState(withdrawalId, event.getEventId()); log.info("Start handling WithdrawalFailedChange: withdrawalId={} change={}", withdrawalId, change); withdrawalChangeStatusHandler.handleChangeStatus( @@ -36,18 +34,9 @@ public void handle(TimestampedChange change, MachineEvent event) { event, withdrawalId, EventType.WITHDRAWAL_FAILED, - withdrawalState); + null); log.info("Finish handling WithdrawalFailedChange: withdrawalId={}", withdrawalId); } - private WithdrawalState getWithdrawalState(String withdrawalId, Long eventId) { - try { - return withdrawalClient.getWithdrawalInfo(withdrawalId, eventId); - } catch (Exception e) { - log.warn("Error getting withdrawal state for withdrawalId={}, eventId={}: {}", - withdrawalId, eventId, e.getMessage()); - return null; - } - } }