diff --git a/pom.xml b/pom.xml
index cf188b1..cc5e718 100644
--- a/pom.xml
+++ b/pom.xml
@@ -81,7 +81,7 @@
dev.vality
damsel
- 1.611-958e5f0
+ 1.619-da17a84
dev.vality
@@ -127,7 +127,7 @@
dev.vality
swag-webhook-events
- 1.112-a02ceb6-client
+ 1.122-9daaca8-client
dev.vality
diff --git a/src/main/java/dev/vality/hooker/converter/InvoiceChangeToUserInteractionConverter.java b/src/main/java/dev/vality/hooker/converter/InvoiceChangeToUserInteractionConverter.java
new file mode 100644
index 0000000..637322f
--- /dev/null
+++ b/src/main/java/dev/vality/hooker/converter/InvoiceChangeToUserInteractionConverter.java
@@ -0,0 +1,56 @@
+package dev.vality.hooker.converter;
+
+import dev.vality.damsel.payment_processing.InvoiceChange;
+import dev.vality.damsel.user_interaction.*;
+import dev.vality.hooker.model.interaction.PaymentTerminalReceipt;
+import dev.vality.hooker.model.interaction.UserInteraction;
+import dev.vality.hooker.model.interaction.*;
+import lombok.RequiredArgsConstructor;
+import org.jetbrains.annotations.Nullable;
+import org.springframework.core.convert.converter.Converter;
+import org.springframework.stereotype.Component;
+
+@Component
+@RequiredArgsConstructor
+public class InvoiceChangeToUserInteractionConverter implements Converter {
+
+ @Override
+ public UserInteraction convert(InvoiceChange source) {
+ var interaction = source.getInvoicePaymentChange().getPayload().getInvoicePaymentSessionChange().getPayload()
+ .getSessionInteractionChanged().getInteraction();
+ return createUserInteraction(interaction);
+ }
+
+ @Nullable
+ private static UserInteraction createUserInteraction(
+ dev.vality.damsel.user_interaction.UserInteraction interaction) {
+ UserInteraction userInteraction = null;
+ if (interaction.isSetApiExtensionRequest()) {
+ userInteraction = new ApiExtension(interaction.getApiExtensionRequest().getApiType());
+ } else if (interaction.isSetCryptoCurrencyTransferRequest()) {
+ CryptoCurrencyTransferRequest cryptoCurrencyTransferRequest =
+ interaction.getCryptoCurrencyTransferRequest();
+ CryptoCash cryptoCash = cryptoCurrencyTransferRequest.getCryptoCash();
+ userInteraction = new CryptoCurrencyTransfer(cryptoCurrencyTransferRequest.getCryptoAddress(),
+ cryptoCash.getCryptoAmount(),
+ cryptoCash.getCryptoSymbolicCode());
+ } else if (interaction.isSetPaymentTerminalReciept()) {
+ dev.vality.damsel.user_interaction.PaymentTerminalReceipt paymentTerminalReciept =
+ interaction.getPaymentTerminalReciept();
+ userInteraction = new PaymentTerminalReceipt(paymentTerminalReciept.getShortPaymentId(),
+ paymentTerminalReciept.getDue());
+ } else if (interaction.isSetQrCodeDisplayRequest()) {
+ QrCode qrCode = interaction.getQrCodeDisplayRequest().getQrCode();
+ userInteraction = new QrCodeDisplay(qrCode.getPayload());
+ } else if (interaction.isSetRedirect()) {
+ if (interaction.getRedirect().isSetPostRequest()) {
+ BrowserPostRequest postRequest = interaction.getRedirect().getPostRequest();
+ userInteraction = new BrowserHttpInteraction("post", postRequest.getUri(), postRequest.getForm());
+ } else {
+ BrowserGetRequest getRequest = interaction.getRedirect().getGetRequest();
+ userInteraction = new BrowserHttpInteraction("get", getRequest.getUri(), null);
+ }
+ }
+ return userInteraction;
+ }
+}
diff --git a/src/main/java/dev/vality/hooker/converter/UserInteractionConverter.java b/src/main/java/dev/vality/hooker/converter/UserInteractionConverter.java
new file mode 100644
index 0000000..312bb89
--- /dev/null
+++ b/src/main/java/dev/vality/hooker/converter/UserInteractionConverter.java
@@ -0,0 +1,98 @@
+package dev.vality.hooker.converter;
+
+import dev.vality.hooker.model.InvoicingMessage;
+import dev.vality.hooker.model.interaction.PaymentTerminalReceipt;
+import dev.vality.hooker.model.interaction.*;
+import dev.vality.hooker.utils.TimeUtils;
+import dev.vality.swag_webhook_events.model.*;
+import lombok.RequiredArgsConstructor;
+import org.jetbrains.annotations.NotNull;
+import org.springframework.core.convert.converter.Converter;
+import org.springframework.stereotype.Component;
+
+@Component
+@RequiredArgsConstructor
+public class UserInteractionConverter implements Converter {
+
+ @Override
+ public UserInteractionDetails convert(InvoicingMessage invoicingMessage) {
+ UserInteractionDetails userInteractionDetails = new UserInteractionDetails();
+ UserInteraction interaction = invoicingMessage.getUserInteraction();
+ if (interaction instanceof BrowserHttpInteraction browserHttpInteraction) {
+ return createBrowserHttpRequest(browserHttpInteraction);
+ } else if (interaction instanceof ApiExtension apiExtension) {
+ return createApiExtensionRequest(apiExtension);
+ } else if (interaction instanceof CryptoCurrencyTransfer cryptoCurrencyTransfer) {
+ return createCryptoCurrencyTransferRequest(cryptoCurrencyTransfer);
+ } else if (interaction instanceof PaymentTerminalReceipt paymentTerminalReceipt) {
+ return createPaymentTerminalReceipt(paymentTerminalReceipt);
+ } else if (interaction instanceof QrCodeDisplay qrCodeDisplay) {
+ return createQrCodeDisplayRequest(qrCodeDisplay);
+ }
+ return userInteractionDetails;
+ }
+
+ @NotNull
+ private static QrCodeDisplayRequest createQrCodeDisplayRequest(QrCodeDisplay qrCodeDisplay) {
+ QrCodeDisplayRequest qrCodeDisplayRequest = new QrCodeDisplayRequest();
+ QrCodeDisplayInfo qrCodeDisplayInfo = new QrCodeDisplayInfo();
+ qrCodeDisplayInfo.setQrCode(new String(qrCodeDisplay.getQrCode()));
+ qrCodeDisplayRequest.setQrCodeDisplayInfo(qrCodeDisplayInfo);
+ qrCodeDisplayRequest.setUserInteractionType(
+ UserInteractionDetails.UserInteractionTypeEnum.QRCODEDISPLAYREQUEST);
+ return qrCodeDisplayRequest;
+ }
+
+ @NotNull
+ private static dev.vality.swag_webhook_events.model.PaymentTerminalReceipt createPaymentTerminalReceipt(
+ PaymentTerminalReceipt paymentTerminalReceipt) {
+ var paymentTerminalReceiptRequest = new dev.vality.swag_webhook_events.model.PaymentTerminalReceipt();
+ PaymentTerminalReceiptInfo paymentTerminalReceiptInfo = new PaymentTerminalReceiptInfo();
+ paymentTerminalReceiptInfo.setShortPaymentId(paymentTerminalReceipt.getShortPaymentId());
+ paymentTerminalReceiptInfo.setDue(TimeUtils.toOffsetDateTime(paymentTerminalReceipt.getDue()));
+ paymentTerminalReceiptRequest.setPaymentTerminalReceiptInfo(paymentTerminalReceiptInfo);
+ paymentTerminalReceiptRequest.setUserInteractionType(
+ UserInteractionDetails.UserInteractionTypeEnum.PAYMENTTERMINALRECEIPT);
+ return paymentTerminalReceiptRequest;
+ }
+
+ @NotNull
+ private static CryptoCurrencyTransferRequest createCryptoCurrencyTransferRequest(
+ CryptoCurrencyTransfer cryptoCurrencyTransfer) {
+ CryptoCurrencyTransferInfo cryptoCurrencyTransferInfo = new CryptoCurrencyTransferInfo();
+ cryptoCurrencyTransferInfo.setCryptoAddress(cryptoCurrencyTransfer.getCryptoAddress());
+ cryptoCurrencyTransferInfo.setCryptoCurrency(cryptoCurrencyTransfer.getCryptoSymbolicCode());
+ Rational cryptoAmount = new Rational();
+ cryptoAmount.setDenominator(cryptoCurrencyTransfer.getCryptoAmount().getP());
+ cryptoAmount.setDivider(cryptoCurrencyTransfer.getCryptoAmount().getQ());
+ cryptoCurrencyTransferInfo.setCryptoAmount(cryptoAmount);
+ CryptoCurrencyTransferRequest cryptoCurrencyTransferRequest = new CryptoCurrencyTransferRequest();
+ cryptoCurrencyTransferRequest.setCryptoCurrencyTransferInfo(cryptoCurrencyTransferInfo);
+ cryptoCurrencyTransferRequest.setUserInteractionType(
+ UserInteractionDetails.UserInteractionTypeEnum.CRYPTOCURRENCYTRANSFERREQUEST);
+ return cryptoCurrencyTransferRequest;
+ }
+
+ @NotNull
+ private static ApiExtensionRequest createApiExtensionRequest(ApiExtension apiExtension) {
+ ApiExtensionRequest apiExtensionRequest = new ApiExtensionRequest();
+ ApiExtensionInfo apiExtensionInfo = new ApiExtensionInfo();
+ apiExtensionInfo.setApiType(apiExtension.getApiType());
+ apiExtensionRequest.setApiExtensionInfo(apiExtensionInfo);
+ apiExtensionRequest.setUserInteractionType(UserInteractionDetails.UserInteractionTypeEnum.APIEXTENSIONREQUEST);
+ return apiExtensionRequest;
+ }
+
+ @NotNull
+ private static BrowserHTTPRequest createBrowserHttpRequest(BrowserHttpInteraction browserHttpInteraction) {
+ BrowserHTTPInfo browserHttpInfo = new BrowserHTTPInfo();
+ browserHttpInfo.setUrl(browserHttpInteraction.getUrl());
+ browserHttpInfo.setRequestType(
+ BrowserHTTPInfo.RequestTypeEnum.fromValue(browserHttpInteraction.getRequestType()));
+ browserHttpInfo.setForm(browserHttpInteraction.getForm());
+ BrowserHTTPRequest browserHttpRequest = new BrowserHTTPRequest();
+ browserHttpRequest.setBrowserHTTPInfo(browserHttpInfo);
+ browserHttpRequest.setUserInteractionType(UserInteractionDetails.UserInteractionTypeEnum.BROWSERHTTPREQUEST);
+ return browserHttpRequest;
+ }
+}
diff --git a/src/main/java/dev/vality/hooker/handler/invoicing/InvoicePaymentUserInteractionChangeCompletedMapper.java b/src/main/java/dev/vality/hooker/handler/invoicing/InvoicePaymentUserInteractionChangeCompletedMapper.java
new file mode 100644
index 0000000..729a0b2
--- /dev/null
+++ b/src/main/java/dev/vality/hooker/handler/invoicing/InvoicePaymentUserInteractionChangeCompletedMapper.java
@@ -0,0 +1,68 @@
+package dev.vality.hooker.handler.invoicing;
+
+import dev.vality.damsel.payment_processing.InvoiceChange;
+import dev.vality.geck.filter.Filter;
+import dev.vality.geck.filter.PathConditionFilter;
+import dev.vality.geck.filter.condition.IsNullCondition;
+import dev.vality.geck.filter.rule.PathConditionRule;
+import dev.vality.hooker.converter.InvoiceChangeToUserInteractionConverter;
+import dev.vality.hooker.dao.InvoicingMessageDao;
+import dev.vality.hooker.model.EventType;
+import dev.vality.hooker.model.InvoicingMessage;
+import dev.vality.hooker.model.InvoicingMessageEnum;
+import dev.vality.hooker.model.InvoicingMessageKey;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+@Component
+public class InvoicePaymentUserInteractionChangeCompletedMapper extends NeedReadInvoiceEventMapper {
+
+ @Autowired
+ private InvoiceChangeToUserInteractionConverter userInteractionConverter;
+
+ private EventType eventType = EventType.INVOICE_PAYMENT_USER_INTERACTION_CHANGE_COMPLETED;
+
+ private Filter filter =
+ new PathConditionFilter(new PathConditionRule(eventType.getThriftPath(), new IsNullCondition().not()));
+
+ public InvoicePaymentUserInteractionChangeCompletedMapper(InvoicingMessageDao messageDao) {
+ super(messageDao);
+ }
+
+ @Override
+ public Filter getFilter() {
+ return filter;
+ }
+
+ @Override
+ protected InvoicingMessageKey getMessageKey(String invoiceId, InvoiceChange ic) {
+ return InvoicingMessageKey.builder()
+ .invoiceId(invoiceId)
+ .paymentId(ic.getInvoicePaymentChange().getId())
+ .type(InvoicingMessageEnum.PAYMENT)
+ .build();
+ }
+
+ @Override
+ protected InvoicingMessageEnum getMessageType() {
+ return InvoicingMessageEnum.PAYMENT;
+ }
+
+ @Override
+ protected EventType getEventType() {
+ return eventType;
+ }
+
+ @Override
+ protected void modifyMessage(InvoiceChange ic, InvoicingMessage message) {
+ message.setUserInteraction(userInteractionConverter.convert(ic));
+ }
+
+ @Override
+ public boolean accept(InvoiceChange change) {
+ return getFilter().match(change)
+ && !change.getInvoicePaymentChange().getPayload().getInvoicePaymentSessionChange().getPayload()
+ .getSessionInteractionChanged()
+ .getStatus().isSetCompleted();
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/dev/vality/hooker/handler/invoicing/InvoicePaymentUserInteractionChangeRequestedMapper.java b/src/main/java/dev/vality/hooker/handler/invoicing/InvoicePaymentUserInteractionChangeRequestedMapper.java
new file mode 100644
index 0000000..b71d8c2
--- /dev/null
+++ b/src/main/java/dev/vality/hooker/handler/invoicing/InvoicePaymentUserInteractionChangeRequestedMapper.java
@@ -0,0 +1,63 @@
+package dev.vality.hooker.handler.invoicing;
+
+import dev.vality.damsel.payment_processing.InvoiceChange;
+import dev.vality.geck.filter.Filter;
+import dev.vality.geck.filter.PathConditionFilter;
+import dev.vality.geck.filter.condition.IsNullCondition;
+import dev.vality.geck.filter.rule.PathConditionRule;
+import dev.vality.hooker.dao.InvoicingMessageDao;
+import dev.vality.hooker.model.*;
+import org.springframework.stereotype.Component;
+
+@Component
+public class InvoicePaymentUserInteractionChangeRequestedMapper extends NeedReadInvoiceEventMapper {
+
+ private EventType eventType = EventType.INVOICE_PAYMENT_USER_INTERACTION_CHANGE_REQUESTED;
+
+ private Filter filter =
+ new PathConditionFilter(new PathConditionRule(eventType.getThriftPath(), new IsNullCondition().not()));
+
+ public InvoicePaymentUserInteractionChangeRequestedMapper(InvoicingMessageDao messageDao) {
+ super(messageDao);
+ }
+
+ @Override
+ public Filter getFilter() {
+ return filter;
+ }
+
+ @Override
+ protected InvoicingMessageKey getMessageKey(String invoiceId, InvoiceChange ic) {
+ return InvoicingMessageKey.builder()
+ .invoiceId(invoiceId)
+ .paymentId(ic.getInvoicePaymentChange().getId())
+ .type(InvoicingMessageEnum.PAYMENT)
+ .build();
+ }
+
+ @Override
+ protected InvoicingMessageEnum getMessageType() {
+ return InvoicingMessageEnum.PAYMENT;
+ }
+
+ @Override
+ protected EventType getEventType() {
+ return eventType;
+ }
+
+ @Override
+ protected void modifyMessage(InvoiceChange ic, InvoicingMessage message) {
+ message.setPaymentStatus(PaymentStatusEnum.lookup(ic.getInvoicePaymentChange().getPayload()
+ .getInvoicePaymentStatusChanged().getStatus().getSetField().getFieldName()));
+ }
+
+ @Override
+ public boolean accept(InvoiceChange change) {
+ return getFilter().match(change)
+ && !change.getInvoicePaymentChange().getPayload().getInvoicePaymentSessionChange()
+ .getPayload()
+ .getSessionInteractionChanged()
+ .getStatus()
+ .isSetRequested();
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/dev/vality/hooker/model/EventType.java b/src/main/java/dev/vality/hooker/model/EventType.java
index c1e4041..985390e 100644
--- a/src/main/java/dev/vality/hooker/model/EventType.java
+++ b/src/main/java/dev/vality/hooker/model/EventType.java
@@ -18,6 +18,9 @@ public enum EventType {
INVOICE_PAYMENT_CASH_FLOW_CHANGED("invoice_payment_change.payload.invoice_payment_cash_flow_changed"),
INVOICE_PAYMENT_CASH_CHANGED("invoice_payment_change.payload.invoice_payment_cash_changed"),
+ INVOICE_PAYMENT_USER_INTERACTION_CHANGE_REQUESTED("invoice_payment_change.payload.user_interaction.status.requested"),
+ INVOICE_PAYMENT_USER_INTERACTION_CHANGE_COMPLETED("invoice_payment_change.payload.user_interaction.status.completed"),
+
CUSTOMER_CREATED("customer_created"),
CUSTOMER_DELETED("customer_deleted"),
CUSTOMER_READY("customer_status_changed.status.ready"),
diff --git a/src/main/java/dev/vality/hooker/model/InvoicingMessage.java b/src/main/java/dev/vality/hooker/model/InvoicingMessage.java
index e277e7e..3fa1989 100644
--- a/src/main/java/dev/vality/hooker/model/InvoicingMessage.java
+++ b/src/main/java/dev/vality/hooker/model/InvoicingMessage.java
@@ -1,5 +1,6 @@
package dev.vality.hooker.model;
+import dev.vality.hooker.model.interaction.UserInteraction;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.ToString;
@@ -16,6 +17,7 @@ public class InvoicingMessage extends Message {
private PaymentStatusEnum paymentStatus;
private String refundId;
private RefundStatusEnum refundStatus;
+ private UserInteraction userInteraction;
public boolean isInvoice() {
return type == InvoicingMessageEnum.INVOICE;
diff --git a/src/main/java/dev/vality/hooker/model/interaction/ApiExtension.java b/src/main/java/dev/vality/hooker/model/interaction/ApiExtension.java
new file mode 100644
index 0000000..7c6b07d
--- /dev/null
+++ b/src/main/java/dev/vality/hooker/model/interaction/ApiExtension.java
@@ -0,0 +1,12 @@
+package dev.vality.hooker.model.interaction;
+
+import lombok.AllArgsConstructor;
+import lombok.Data;
+
+@Data
+@AllArgsConstructor
+public class ApiExtension implements UserInteraction {
+
+ private String apiType;
+
+}
diff --git a/src/main/java/dev/vality/hooker/model/interaction/BrowserHttpInteraction.java b/src/main/java/dev/vality/hooker/model/interaction/BrowserHttpInteraction.java
new file mode 100644
index 0000000..280ed74
--- /dev/null
+++ b/src/main/java/dev/vality/hooker/model/interaction/BrowserHttpInteraction.java
@@ -0,0 +1,16 @@
+package dev.vality.hooker.model.interaction;
+
+import lombok.AllArgsConstructor;
+import lombok.Data;
+
+import java.util.Map;
+
+@Data
+@AllArgsConstructor
+public class BrowserHttpInteraction implements UserInteraction {
+
+ private String requestType;
+ private String url;
+ private Map form;
+
+}
diff --git a/src/main/java/dev/vality/hooker/model/interaction/CryptoCurrencyTransfer.java b/src/main/java/dev/vality/hooker/model/interaction/CryptoCurrencyTransfer.java
new file mode 100644
index 0000000..464a5fb
--- /dev/null
+++ b/src/main/java/dev/vality/hooker/model/interaction/CryptoCurrencyTransfer.java
@@ -0,0 +1,15 @@
+package dev.vality.hooker.model.interaction;
+
+import dev.vality.damsel.base.Rational;
+import lombok.AllArgsConstructor;
+import lombok.Data;
+
+@Data
+@AllArgsConstructor
+public class CryptoCurrencyTransfer implements UserInteraction {
+
+ private String cryptoAddress;
+ public Rational cryptoAmount;
+ public String cryptoSymbolicCode;
+
+}
diff --git a/src/main/java/dev/vality/hooker/model/interaction/PaymentTerminalReceipt.java b/src/main/java/dev/vality/hooker/model/interaction/PaymentTerminalReceipt.java
new file mode 100644
index 0000000..85508e5
--- /dev/null
+++ b/src/main/java/dev/vality/hooker/model/interaction/PaymentTerminalReceipt.java
@@ -0,0 +1,13 @@
+package dev.vality.hooker.model.interaction;
+
+import lombok.AllArgsConstructor;
+import lombok.Data;
+
+@Data
+@AllArgsConstructor
+public class PaymentTerminalReceipt implements UserInteraction {
+
+ private String shortPaymentId;
+ private String due;
+
+}
diff --git a/src/main/java/dev/vality/hooker/model/interaction/QrCodeDisplay.java b/src/main/java/dev/vality/hooker/model/interaction/QrCodeDisplay.java
new file mode 100644
index 0000000..f38e89d
--- /dev/null
+++ b/src/main/java/dev/vality/hooker/model/interaction/QrCodeDisplay.java
@@ -0,0 +1,12 @@
+package dev.vality.hooker.model.interaction;
+
+import lombok.AllArgsConstructor;
+import lombok.Data;
+
+@Data
+@AllArgsConstructor
+public class QrCodeDisplay implements UserInteraction {
+
+ private byte[] qrCode;
+
+}
diff --git a/src/main/java/dev/vality/hooker/model/interaction/UserInteraction.java b/src/main/java/dev/vality/hooker/model/interaction/UserInteraction.java
new file mode 100644
index 0000000..e3df607
--- /dev/null
+++ b/src/main/java/dev/vality/hooker/model/interaction/UserInteraction.java
@@ -0,0 +1,4 @@
+package dev.vality.hooker.model.interaction;
+
+public interface UserInteraction {
+}
diff --git a/src/main/java/dev/vality/hooker/service/InvoicingEventService.java b/src/main/java/dev/vality/hooker/service/InvoicingEventService.java
index 292ad92..dcc8788 100644
--- a/src/main/java/dev/vality/hooker/service/InvoicingEventService.java
+++ b/src/main/java/dev/vality/hooker/service/InvoicingEventService.java
@@ -7,6 +7,7 @@ import dev.vality.damsel.payment_processing.InvoicingSrv;
import dev.vality.hooker.converter.InvoiceConverter;
import dev.vality.hooker.converter.PaymentConverter;
import dev.vality.hooker.converter.RefundConverter;
+import dev.vality.hooker.converter.UserInteractionConverter;
import dev.vality.hooker.exception.NotFoundException;
import dev.vality.hooker.exception.RemoteHostException;
import dev.vality.hooker.model.ExpandedPayment;
@@ -26,6 +27,7 @@ public class InvoicingEventService
private final InvoiceConverter invoiceConverter;
private final PaymentConverter paymentConverter;
private final RefundConverter refundConverter;
+ private final UserInteractionConverter userInteractionConverter;
@Override
public Event getEventByMessage(InvoicingMessage message) {
@@ -73,6 +75,10 @@ public class InvoicingEventService
.eventType(Event.EventTypeEnum.REFUNDCREATED);
case INVOICE_PAYMENT_REFUND_STATUS_CHANGED -> resolveRefundStatusChanged(m, invoiceInfo);
case INVOICE_PAYMENT_CASH_CHANGED -> resolvePaymentCashChange(m, invoiceInfo);
+ case INVOICE_PAYMENT_USER_INTERACTION_CHANGE_REQUESTED -> resolvePaymentUserInteraction(m)
+ .eventType(Event.EventTypeEnum.PAYMENTINTERACTIONREQUESTED);
+ case INVOICE_PAYMENT_USER_INTERACTION_CHANGE_COMPLETED -> resolvePaymentUserInteraction(m)
+ .eventType(Event.EventTypeEnum.PAYMENTINTERACTIONCOMPLETED);
default -> throw new UnsupportedOperationException("Unknown event type " + m.getEventType());
};
}
@@ -207,7 +213,7 @@ public class InvoicingEventService
}
private Event resolvePaymentCashChange(InvoicingMessage message,
- dev.vality.damsel.payment_processing.Invoice invoiceInfo) {
+ dev.vality.damsel.payment_processing.Invoice invoiceInfo) {
Invoice swagInvoice = getSwagInvoice(invoiceInfo);
ExpandedPayment swagPayment = getSwagPayment(message, invoiceInfo);
return new PaymentCashChanged()
@@ -215,4 +221,9 @@ public class InvoicingEventService
.payment(swagPayment)
.eventType(Event.EventTypeEnum.PAYMENTCASHCHANGED);
}
+
+ private Event resolvePaymentUserInteraction(InvoicingMessage message) {
+ return new PaymentInteractionRequested()
+ .userInteractionDetails(userInteractionConverter.convert(message));
+ }
}
diff --git a/src/main/java/dev/vality/hooker/utils/EventFilterUtils.java b/src/main/java/dev/vality/hooker/utils/EventFilterUtils.java
index 88445ce..8efec69 100644
--- a/src/main/java/dev/vality/hooker/utils/EventFilterUtils.java
+++ b/src/main/java/dev/vality/hooker/utils/EventFilterUtils.java
@@ -1,5 +1,6 @@
package dev.vality.hooker.utils;
+import dev.vality.damsel.user_interaction.UserInteraction;
import dev.vality.damsel.webhooker.*;
import dev.vality.hooker.dao.WebhookAdditionalFilter;
import dev.vality.hooker.model.EventType;
@@ -204,6 +205,14 @@ public class EventFilterUtils {
.getFieldName());
}
}
+ } else if (payment.isSetUserInteraction()) {
+ if (payment.getUserInteraction().getStatus().isSetRequested()) {
+ webhookAdditionalFilter.setEventType(
+ EventType.INVOICE_PAYMENT_USER_INTERACTION_CHANGE_REQUESTED);
+ } else if (payment.getUserInteraction().getStatus().isSetCompleted()) {
+ webhookAdditionalFilter.setEventType(
+ EventType.INVOICE_PAYMENT_USER_INTERACTION_CHANGE_COMPLETED);
+ }
}
}
}
diff --git a/src/test/java/dev/vality/hooker/model/InvoicingMessageTest.java b/src/test/java/dev/vality/hooker/model/InvoicingMessageTest.java
index 0c5e491..ee8beb2 100644
--- a/src/test/java/dev/vality/hooker/model/InvoicingMessageTest.java
+++ b/src/test/java/dev/vality/hooker/model/InvoicingMessageTest.java
@@ -9,7 +9,7 @@ public class InvoicingMessageTest {
@Test
public void testCopy() {
- InvoicingMessage invoicingMessage = random(InvoicingMessage.class);
+ InvoicingMessage invoicingMessage = random(InvoicingMessage.class, "userInteraction");
InvoicingMessage copy = invoicingMessage.copy();
invoicingMessage.setRefundId("asd");
assertNotEquals("asd", copy.getRefundId());
diff --git a/src/test/java/dev/vality/hooker/service/InvoicingEventServiceTest.java b/src/test/java/dev/vality/hooker/service/InvoicingEventServiceTest.java
index a59303b..2a123c5 100644
--- a/src/test/java/dev/vality/hooker/service/InvoicingEventServiceTest.java
+++ b/src/test/java/dev/vality/hooker/service/InvoicingEventServiceTest.java
@@ -2,6 +2,7 @@ package dev.vality.hooker.service;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
+import dev.vality.damsel.base.Rational;
import dev.vality.damsel.domain.InvoicePaid;
import dev.vality.damsel.domain.InvoicePaymentPending;
import dev.vality.damsel.domain.InvoicePaymentStatus;
@@ -9,11 +10,14 @@ import dev.vality.damsel.domain.InvoiceStatus;
import dev.vality.damsel.payment_processing.InvoicingSrv;
import dev.vality.hooker.config.PostgresqlSpringBootITest;
import dev.vality.hooker.model.*;
+import dev.vality.hooker.model.interaction.*;
import dev.vality.hooker.utils.BuildUtils;
import dev.vality.swag_webhook_events.model.Event;
import dev.vality.swag_webhook_events.model.RefundSucceeded;
+import org.jetbrains.annotations.NotNull;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.RepeatedTest;
+import org.junit.jupiter.api.Test;
import org.mockito.Mockito;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.mock.mockito.MockBean;
@@ -45,7 +49,7 @@ public class InvoicingEventServiceTest {
@RepeatedTest(7)
public void testRefundSucceeded() {
- InvoicingMessage message = random(InvoicingMessage.class);
+ InvoicingMessage message = random(InvoicingMessage.class, "userInteraction");
message.setPaymentId("1");
message.setRefundId("1");
message.setType(InvoicingMessageEnum.REFUND);
@@ -64,7 +68,7 @@ public class InvoicingEventServiceTest {
@RepeatedTest(7)
public void testJson() throws JsonProcessingException {
- InvoicingMessage message = random(InvoicingMessage.class);
+ InvoicingMessage message = random(InvoicingMessage.class, "userInteraction");
message.setPaymentId("1");
message.setType(InvoicingMessageEnum.PAYMENT);
message.setEventTime("2016-03-22T06:12:27Z");
@@ -77,4 +81,74 @@ public class InvoicingEventServiceTest {
assertTrue(json.contains("\"externalId\":\"payment-external-id\""));
assertTrue(json.contains("\"externalId\":\"invoice-external-id\""));
}
+
+ @Test
+ public void testUserInteractions() throws JsonProcessingException {
+ InvoicingMessage message = createDefaultInvoicingMessage();
+ message.setEventType(EventType.INVOICE_PAYMENT_USER_INTERACTION_CHANGE_REQUESTED);
+ message.setUserInteraction(new BrowserHttpInteraction("get", "http://test", null));
+ Event event = service.getEventByMessage(message);
+ String json = objectMapper.writeValueAsString(event);
+ assertTrue(json.contains("\"eventType\":\"PaymentInteractionRequested\""));
+ assertTrue(json.contains("\"requestType\":\"get\""));
+ assertTrue(json.contains("\"userInteractionType\":\"BrowserHTTPRequest\""));
+ }
+
+ @Test
+ public void testUserInteractionsCompleted() throws JsonProcessingException {
+ InvoicingMessage message = createDefaultInvoicingMessage();
+ message.setEventType(EventType.INVOICE_PAYMENT_USER_INTERACTION_CHANGE_COMPLETED);
+ message.setUserInteraction(new QrCodeDisplay("wefvqewvrq32fveqrw".getBytes()));
+ Event event = service.getEventByMessage(message);
+ String json = objectMapper.writeValueAsString(event);
+ assertTrue(json.contains("\"eventType\":\"PaymentInteractionCompleted\""));
+ assertTrue(json.contains("\"userInteractionType\":\"QrCodeDisplayRequest\""));
+ }
+
+ @Test
+ public void testUserInteractionsCompletedApiExtension() throws JsonProcessingException {
+ InvoicingMessage message = createDefaultInvoicingMessage();
+ message.setEventType(EventType.INVOICE_PAYMENT_USER_INTERACTION_CHANGE_COMPLETED);
+ message.setUserInteraction(new ApiExtension("p2p"));
+ Event event = service.getEventByMessage(message);
+ String json = objectMapper.writeValueAsString(event);
+ assertTrue(json.contains("\"eventType\":\"PaymentInteractionCompleted\""));
+ assertTrue(json.contains("\"userInteractionType\":\"ApiExtensionRequest\""));
+ }
+
+ @Test
+ public void testUserInteractionsCompletedPaymentTerminal() throws JsonProcessingException {
+ InvoicingMessage message = createDefaultInvoicingMessage();
+ message.setEventType(EventType.INVOICE_PAYMENT_USER_INTERACTION_CHANGE_COMPLETED);
+ message.setUserInteraction(new PaymentTerminalReceipt("p2p", "2016-03-22T06:12:27Z"));
+ Event event = service.getEventByMessage(message);
+ String json = objectMapper.writeValueAsString(event);
+ assertTrue(json.contains("\"eventType\":\"PaymentInteractionCompleted\""));
+ assertTrue(json.contains("\"userInteractionType\":\"PaymentTerminalReceipt\""));
+ }
+
+ @Test
+ public void testUserInteractionsCompletedCrypto() throws JsonProcessingException {
+ InvoicingMessage message = createDefaultInvoicingMessage();
+ message.setEventType(EventType.INVOICE_PAYMENT_USER_INTERACTION_CHANGE_COMPLETED);
+ message.setUserInteraction(new CryptoCurrencyTransfer("address", new Rational(1L, 10L), "bitcoin"));
+ Event event = service.getEventByMessage(message);
+ String json = objectMapper.writeValueAsString(event);
+ assertTrue(json.contains("\"eventType\":\"PaymentInteractionCompleted\""));
+ assertTrue(json.contains("\"userInteractionType\":\"CryptoCurrencyTransferRequest\""));
+ assertTrue(json.contains("\"cryptoAddress\":\"address\""));
+ assertTrue(json.contains("\"cryptoCurrency\":\"bitcoin\""));
+ assertTrue(json.contains("\"denominator\":1"));
+ }
+
+ @NotNull
+ private static InvoicingMessage createDefaultInvoicingMessage() {
+ InvoicingMessage message = new InvoicingMessage();
+ message.setId(123L);
+ message.setPaymentId("271771960");
+ message.setSequenceId(123L);
+ message.setType(InvoicingMessageEnum.PAYMENT);
+ message.setEventTime("2016-03-22T06:12:27Z");
+ return message;
+ }
}