diff --git a/src/main/java/com/ibanity/apis/client/jsonapi/isabel_connect/CollectionApiModel.java b/src/main/java/com/ibanity/apis/client/jsonapi/isabel_connect/CollectionApiModel.java index 40b42279..1993bd0c 100644 --- a/src/main/java/com/ibanity/apis/client/jsonapi/isabel_connect/CollectionApiModel.java +++ b/src/main/java/com/ibanity/apis/client/jsonapi/isabel_connect/CollectionApiModel.java @@ -20,10 +20,10 @@ public class CollectionApiModel { private List data = emptyList(); public Integer getPaginationOffset() { - return meta.getPaginationOffset(); + return meta == null ? null : meta.getPaginationOffset(); } public Integer getPaginationTotal() { - return meta.getPaginationTotal(); + return meta == null ? null : meta.getPaginationTotal(); } } \ No newline at end of file diff --git a/src/main/java/com/ibanity/apis/client/jsonapi/isabel_connect/DataApiModel.java b/src/main/java/com/ibanity/apis/client/jsonapi/isabel_connect/DataApiModel.java index 44202a89..4a506dd5 100644 --- a/src/main/java/com/ibanity/apis/client/jsonapi/isabel_connect/DataApiModel.java +++ b/src/main/java/com/ibanity/apis/client/jsonapi/isabel_connect/DataApiModel.java @@ -1,5 +1,6 @@ package com.ibanity.apis.client.jsonapi.isabel_connect; +import com.ibanity.apis.client.jsonapi.RelationshipsApiModel; import lombok.*; import java.util.Map; @@ -18,4 +19,6 @@ public class DataApiModel { @Builder.Default private Map attributes = emptyMap(); + @Builder.Default + private Map relationships = emptyMap(); } diff --git a/src/main/java/com/ibanity/apis/client/jsonapi/isabel_connect/MetaApiModel.java b/src/main/java/com/ibanity/apis/client/jsonapi/isabel_connect/MetaApiModel.java index 593d19ee..0cc785b7 100644 --- a/src/main/java/com/ibanity/apis/client/jsonapi/isabel_connect/MetaApiModel.java +++ b/src/main/java/com/ibanity/apis/client/jsonapi/isabel_connect/MetaApiModel.java @@ -12,10 +12,10 @@ public class MetaApiModel { private PagingApiModel paging; public Integer getPaginationOffset() { - return paging.getOffset(); + return paging == null ? null : paging.getOffset(); } public Integer getPaginationTotal() { - return paging.getTotal(); + return paging == null ? null : paging.getTotal(); } } diff --git a/src/main/java/com/ibanity/apis/client/products/isabel_connect/models/PaymentNotification.java b/src/main/java/com/ibanity/apis/client/products/isabel_connect/models/PaymentNotification.java new file mode 100644 index 00000000..bd4387f6 --- /dev/null +++ b/src/main/java/com/ibanity/apis/client/products/isabel_connect/models/PaymentNotification.java @@ -0,0 +1,25 @@ +package com.ibanity.apis.client.products.isabel_connect.models; + +import lombok.AccessLevel; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; +import lombok.ToString; +import lombok.experimental.SuperBuilder; + +import java.time.Instant; + +@Data +@ToString(callSuper = true) +@EqualsAndHashCode(callSuper = true) +@SuperBuilder(toBuilder = true) +@NoArgsConstructor +@AllArgsConstructor(access = AccessLevel.PRIVATE) +public class PaymentNotification extends IsabelModel { + public static final String RESOURCE_TYPE = "paymentNotification"; + + private String notificationType; + private Instant createdAt; + private String paymentId; +} diff --git a/src/main/java/com/ibanity/apis/client/products/isabel_connect/models/delete/PaymentNotificationDeleteQuery.java b/src/main/java/com/ibanity/apis/client/products/isabel_connect/models/delete/PaymentNotificationDeleteQuery.java new file mode 100644 index 00000000..032c4b57 --- /dev/null +++ b/src/main/java/com/ibanity/apis/client/products/isabel_connect/models/delete/PaymentNotificationDeleteQuery.java @@ -0,0 +1,22 @@ +package com.ibanity.apis.client.products.isabel_connect.models.delete; + +import lombok.Builder; +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.ToString; + +import java.util.Map; + +import static java.util.Collections.emptyMap; + +@Getter +@Builder +@ToString +@EqualsAndHashCode +public class PaymentNotificationDeleteQuery { + private String accessToken; + private String paymentNotificationId; + + @Builder.Default + private Map additionalHeaders = emptyMap(); +} diff --git a/src/main/java/com/ibanity/apis/client/products/isabel_connect/models/read/PaymentNotificationsReadQuery.java b/src/main/java/com/ibanity/apis/client/products/isabel_connect/models/read/PaymentNotificationsReadQuery.java new file mode 100644 index 00000000..f138cd64 --- /dev/null +++ b/src/main/java/com/ibanity/apis/client/products/isabel_connect/models/read/PaymentNotificationsReadQuery.java @@ -0,0 +1,22 @@ +package com.ibanity.apis.client.products.isabel_connect.models.read; + +import lombok.Builder; +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.ToString; + +import java.util.Map; + +import static java.util.Collections.emptyMap; + +@Getter +@Builder +@ToString +@EqualsAndHashCode +public class PaymentNotificationsReadQuery { + private String accessToken; + private IsabelPagingSpec pagingSpec; + + @Builder.Default + private Map additionalHeaders = emptyMap(); +} diff --git a/src/main/java/com/ibanity/apis/client/products/isabel_connect/models/read/PaymentStatusReadQuery.java b/src/main/java/com/ibanity/apis/client/products/isabel_connect/models/read/PaymentStatusReadQuery.java new file mode 100644 index 00000000..c346e030 --- /dev/null +++ b/src/main/java/com/ibanity/apis/client/products/isabel_connect/models/read/PaymentStatusReadQuery.java @@ -0,0 +1,23 @@ +package com.ibanity.apis.client.products.isabel_connect.models.read; + +import lombok.Builder; +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.ToString; + +import java.util.Map; + +import static java.util.Collections.emptyMap; + +@Getter +@Builder +@ToString +@EqualsAndHashCode +public class PaymentStatusReadQuery { + private String accessToken; + private String bulkPaymentInitiationRequestId; + private String notificationId; + + @Builder.Default + private Map additionalHeaders = emptyMap(); +} diff --git a/src/main/java/com/ibanity/apis/client/products/isabel_connect/services/IsabelConnectService.java b/src/main/java/com/ibanity/apis/client/products/isabel_connect/services/IsabelConnectService.java index e6889f5a..2cf7db5c 100644 --- a/src/main/java/com/ibanity/apis/client/products/isabel_connect/services/IsabelConnectService.java +++ b/src/main/java/com/ibanity/apis/client/products/isabel_connect/services/IsabelConnectService.java @@ -15,4 +15,8 @@ public interface IsabelConnectService { TransactionService transactionService(); TokenService tokenService(); + + PaymentNotificationService paymentNotificationService(); + + PaymentStatusService paymentStatusService(); } diff --git a/src/main/java/com/ibanity/apis/client/products/isabel_connect/services/PaymentNotificationService.java b/src/main/java/com/ibanity/apis/client/products/isabel_connect/services/PaymentNotificationService.java new file mode 100644 index 00000000..a30fc66d --- /dev/null +++ b/src/main/java/com/ibanity/apis/client/products/isabel_connect/services/PaymentNotificationService.java @@ -0,0 +1,11 @@ +package com.ibanity.apis.client.products.isabel_connect.services; + +import com.ibanity.apis.client.models.IsabelCollection; +import com.ibanity.apis.client.products.isabel_connect.models.PaymentNotification; +import com.ibanity.apis.client.products.isabel_connect.models.delete.PaymentNotificationDeleteQuery; +import com.ibanity.apis.client.products.isabel_connect.models.read.PaymentNotificationsReadQuery; + +public interface PaymentNotificationService { + IsabelCollection list(PaymentNotificationsReadQuery query); + void delete(PaymentNotificationDeleteQuery query); +} diff --git a/src/main/java/com/ibanity/apis/client/products/isabel_connect/services/PaymentStatusService.java b/src/main/java/com/ibanity/apis/client/products/isabel_connect/services/PaymentStatusService.java new file mode 100644 index 00000000..f76b3dee --- /dev/null +++ b/src/main/java/com/ibanity/apis/client/products/isabel_connect/services/PaymentStatusService.java @@ -0,0 +1,7 @@ +package com.ibanity.apis.client.products.isabel_connect.services; + +import com.ibanity.apis.client.products.isabel_connect.models.read.PaymentStatusReadQuery; + +public interface PaymentStatusService { + String find(PaymentStatusReadQuery query); +} diff --git a/src/main/java/com/ibanity/apis/client/products/isabel_connect/services/impl/IsabelConnectServiceImpl.java b/src/main/java/com/ibanity/apis/client/products/isabel_connect/services/impl/IsabelConnectServiceImpl.java index f7d88467..0eee32b7 100644 --- a/src/main/java/com/ibanity/apis/client/products/isabel_connect/services/impl/IsabelConnectServiceImpl.java +++ b/src/main/java/com/ibanity/apis/client/products/isabel_connect/services/impl/IsabelConnectServiceImpl.java @@ -14,6 +14,8 @@ public class IsabelConnectServiceImpl implements IsabelConnectService { private final IntradayTransactionService intradayTransactionService; private final TransactionService transactionService; private final TokenService tokenService; + private final PaymentNotificationService paymentNotificationService; + private final PaymentStatusService paymentStatusService; public IsabelConnectServiceImpl( ApiUrlProvider apiUrlProvider, @@ -26,6 +28,8 @@ public IsabelConnectServiceImpl( transactionService = new TransactionServiceImpl(apiUrlProvider, ibanityHttpClient); tokenService = new TokenServiceImpl(apiUrlProvider, oAuthHttpClient); bulkPaymentInitiationRequestService = new BulkPaymentInitiationRequestServiceImpl(apiUrlProvider, new IbanityResponseHandler(), ibanityHttpClient); + paymentNotificationService = new PaymentNotificationServiceImpl(apiUrlProvider, ibanityHttpClient); + paymentStatusService = new PaymentStatusServiceImpl(apiUrlProvider, ibanityHttpClient); } @Override @@ -48,4 +52,10 @@ public IsabelConnectServiceImpl( @Override public TokenService tokenService() { return tokenService; } + + @Override + public PaymentNotificationService paymentNotificationService() { return paymentNotificationService; } + + @Override + public PaymentStatusService paymentStatusService() { return paymentStatusService; } } diff --git a/src/main/java/com/ibanity/apis/client/products/isabel_connect/services/impl/PaymentNotificationServiceImpl.java b/src/main/java/com/ibanity/apis/client/products/isabel_connect/services/impl/PaymentNotificationServiceImpl.java new file mode 100644 index 00000000..399e3a89 --- /dev/null +++ b/src/main/java/com/ibanity/apis/client/products/isabel_connect/services/impl/PaymentNotificationServiceImpl.java @@ -0,0 +1,69 @@ +package com.ibanity.apis.client.products.isabel_connect.services.impl; + +import com.ibanity.apis.client.http.IbanityHttpClient; +import com.ibanity.apis.client.jsonapi.RelationshipsApiModel; +import com.ibanity.apis.client.jsonapi.isabel_connect.DataApiModel; +import com.ibanity.apis.client.mappers.IsabelModelMapper; +import com.ibanity.apis.client.models.IbanityProduct; +import com.ibanity.apis.client.models.IsabelCollection; +import com.ibanity.apis.client.products.isabel_connect.models.PaymentNotification; +import com.ibanity.apis.client.products.isabel_connect.models.delete.PaymentNotificationDeleteQuery; +import com.ibanity.apis.client.products.isabel_connect.models.read.PaymentNotificationsReadQuery; +import com.ibanity.apis.client.products.isabel_connect.services.PaymentNotificationService; +import com.ibanity.apis.client.services.ApiUrlProvider; +import org.apache.commons.lang3.StringUtils; +import org.apache.http.HttpResponse; + +import java.net.URI; +import java.util.Optional; + +import static com.ibanity.apis.client.utils.URIHelper.buildUri; + +public class PaymentNotificationServiceImpl implements PaymentNotificationService { + private final ApiUrlProvider apiUrlProvider; + private final IbanityHttpClient ibanityHttpClient; + + public PaymentNotificationServiceImpl(ApiUrlProvider apiUrlProvider, IbanityHttpClient ibanityHttpClient) { + this.apiUrlProvider = apiUrlProvider; + this.ibanityHttpClient = ibanityHttpClient; + } + + @Override + public IsabelCollection list(PaymentNotificationsReadQuery query) { + URI uri = buildUri(getNotificationsUrl(), query.getPagingSpec()); + HttpResponse response = ibanityHttpClient.get(uri, query.getAdditionalHeaders(), query.getAccessToken()); + return IsabelModelMapper.mapCollection(response, data -> { + PaymentNotification notification = IsabelModelMapper.toIsabelModel(data, PaymentNotification.class); + notification.setPaymentId(extractPaymentId(data)); + return notification; + }); + } + + @Override + public void delete(PaymentNotificationDeleteQuery query) { + URI uri = buildUri(getNotificationUrl(query.getPaymentNotificationId())); + ibanityHttpClient.delete(uri, query.getAdditionalHeaders(), query.getAccessToken()); + } + + private String extractPaymentId(DataApiModel data) { + return Optional.ofNullable(data.getRelationships()) + .map(relationships -> relationships.get("payment")) + .map(RelationshipsApiModel::getData) + .map(paymentData -> paymentData.getId()) + .orElse(null); + } + + private String getNotificationsUrl() { + String url = apiUrlProvider + .find(IbanityProduct.IsabelConnect, "bulkPaymentInitiationRequests") + .replace("{bulkPaymentInitiationRequestId}", "notifications"); + return StringUtils.removeEnd(url, "/"); + } + + private String getNotificationUrl(String paymentNotificationId) { + String url = apiUrlProvider + .find(IbanityProduct.IsabelConnect, "bulkPaymentInitiationRequests") + .replace("{bulkPaymentInitiationRequestId}", "notifications/" + paymentNotificationId); + return StringUtils.removeEnd(url, "/"); + } +} diff --git a/src/main/java/com/ibanity/apis/client/products/isabel_connect/services/impl/PaymentStatusServiceImpl.java b/src/main/java/com/ibanity/apis/client/products/isabel_connect/services/impl/PaymentStatusServiceImpl.java new file mode 100644 index 00000000..5003b61d --- /dev/null +++ b/src/main/java/com/ibanity/apis/client/products/isabel_connect/services/impl/PaymentStatusServiceImpl.java @@ -0,0 +1,51 @@ +package com.ibanity.apis.client.products.isabel_connect.services.impl; + +import com.ibanity.apis.client.http.IbanityHttpClient; +import com.ibanity.apis.client.models.IbanityProduct; +import com.ibanity.apis.client.products.isabel_connect.models.read.IsabelPagingSpec; +import com.ibanity.apis.client.products.isabel_connect.models.read.PaymentStatusReadQuery; +import com.ibanity.apis.client.products.isabel_connect.services.PaymentStatusService; +import com.ibanity.apis.client.services.ApiUrlProvider; +import org.apache.commons.lang3.StringUtils; +import org.apache.http.HttpResponse; + +import java.io.IOException; +import java.net.URI; +import java.util.HashMap; +import java.util.Map; + +import static com.ibanity.apis.client.mappers.ModelMapperHelper.readResponseContent; +import static com.ibanity.apis.client.utils.URIHelper.buildUri; + +public class PaymentStatusServiceImpl implements PaymentStatusService { + private final ApiUrlProvider apiUrlProvider; + private final IbanityHttpClient ibanityHttpClient; + + public PaymentStatusServiceImpl(ApiUrlProvider apiUrlProvider, IbanityHttpClient ibanityHttpClient) { + this.apiUrlProvider = apiUrlProvider; + this.ibanityHttpClient = ibanityHttpClient; + } + + @Override + public String find(PaymentStatusReadQuery query) { + Map queryParams = new HashMap<>(); + if (StringUtils.isNotBlank(query.getNotificationId())) { + queryParams.put("notificationId", query.getNotificationId()); + } + IsabelPagingSpec emptyPagingSpec = IsabelPagingSpec.builder().size(null).build(); + URI uri = buildUri(getUrl(query.getBulkPaymentInitiationRequestId()), emptyPagingSpec, queryParams); + HttpResponse response = ibanityHttpClient.get(uri, query.getAdditionalHeaders(), query.getAccessToken()); + try { + return readResponseContent(response.getEntity()); + } catch (IOException exception) { + throw new IllegalArgumentException("Response cannot be parsed", exception); + } + } + + private String getUrl(String bulkPaymentInitiationRequestId) { + String url = apiUrlProvider + .find(IbanityProduct.IsabelConnect, "bulkPaymentInitiationRequests") + .replace("{bulkPaymentInitiationRequestId}", bulkPaymentInitiationRequestId); + return StringUtils.removeEnd(url, "/") + "/payment-status"; + } +} diff --git a/src/main/java/com/ibanity/apis/client/utils/WebhooksUtils.java b/src/main/java/com/ibanity/apis/client/utils/WebhooksUtils.java index ab11d492..51be4ee4 100644 --- a/src/main/java/com/ibanity/apis/client/utils/WebhooksUtils.java +++ b/src/main/java/com/ibanity/apis/client/utils/WebhooksUtils.java @@ -2,6 +2,7 @@ import com.ibanity.apis.client.exceptions.IbanityRuntimeException; import com.ibanity.apis.client.models.IbanityWebhookEvent; +import com.ibanity.apis.client.webhooks.models.isabel_connect.PaymentStatusUpdated; import com.ibanity.apis.client.webhooks.models.xs2a.*; import org.apache.commons.codec.digest.MessageDigestAlgorithms; @@ -46,6 +47,8 @@ public static String getDigest(String payload) { public static IbanityWebhookEvent webhookEventParser(String payload, String type) { if (type.contains("xs2a.")) { return parseXs2aEvent(payload, type); + } else if (type.contains("isabelConnect.")) { + return parseIsabelConnectEvent(payload, type); } else { return parsePontoConnectEvent(payload, type); } @@ -122,4 +125,13 @@ private static IbanityWebhookEvent parsePontoConnectEvent(String payload, String throw new IbanityRuntimeException(format("Event Type not handled by the java library \"%s\".", type)); } + + private static IbanityWebhookEvent parseIsabelConnectEvent(String payload, String type) { + switch (type) { + case PaymentStatusUpdated.TYPE: + return mapWebhookResource(payload, PaymentStatusUpdated.mappingFunction()); + } + + throw new IbanityRuntimeException(format("Event Type not handled by the java library \"%s\".", type)); + } } diff --git a/src/main/java/com/ibanity/apis/client/webhooks/models/isabel_connect/PaymentStatusUpdated.java b/src/main/java/com/ibanity/apis/client/webhooks/models/isabel_connect/PaymentStatusUpdated.java new file mode 100644 index 00000000..5ad0cbbb --- /dev/null +++ b/src/main/java/com/ibanity/apis/client/webhooks/models/isabel_connect/PaymentStatusUpdated.java @@ -0,0 +1,44 @@ +package com.ibanity.apis.client.webhooks.models.isabel_connect; + +import com.ibanity.apis.client.jsonapi.DataApiModel; +import com.ibanity.apis.client.jsonapi.RelationshipsApiModel; +import com.ibanity.apis.client.models.IbanityWebhookEvent; +import lombok.AccessLevel; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.experimental.SuperBuilder; + +import java.time.Instant; +import java.util.UUID; +import java.util.function.Function; + +import static com.ibanity.apis.client.mappers.IbanityWebhookEventMapper.toIbanityWebhooks; + +@Data +@SuperBuilder(toBuilder = true) +@NoArgsConstructor +@AllArgsConstructor(access = AccessLevel.PRIVATE) +public class PaymentStatusUpdated implements IbanityWebhookEvent { + + public final static String TYPE = "isabelConnect.payment.status.updated"; + + private UUID id; + private String type; + private String notificationType; + private Instant createdAt; + private String paymentId; + + public static Function mappingFunction() { + return dataApiModel -> { + PaymentStatusUpdated paymentStatusUpdated = toIbanityWebhooks(dataApiModel, PaymentStatusUpdated.class); + + RelationshipsApiModel paymentRelationship = dataApiModel.getRelationships().get("payment"); + if (paymentRelationship != null) { + paymentStatusUpdated.setPaymentId(paymentRelationship.getData().getId()); + } + + return paymentStatusUpdated; + }; + } +} diff --git a/src/test/java/com/ibanity/apis/client/products/isabel_connect/services/impl/PaymentNotificationServiceImplTest.java b/src/test/java/com/ibanity/apis/client/products/isabel_connect/services/impl/PaymentNotificationServiceImplTest.java new file mode 100644 index 00000000..991007c5 --- /dev/null +++ b/src/test/java/com/ibanity/apis/client/products/isabel_connect/services/impl/PaymentNotificationServiceImplTest.java @@ -0,0 +1,85 @@ +package com.ibanity.apis.client.products.isabel_connect.services.impl; + +import com.ibanity.apis.client.http.IbanityHttpClient; +import com.ibanity.apis.client.models.IbanityProduct; +import com.ibanity.apis.client.models.IsabelCollection; +import com.ibanity.apis.client.products.isabel_connect.models.PaymentNotification; +import com.ibanity.apis.client.products.isabel_connect.models.delete.PaymentNotificationDeleteQuery; +import com.ibanity.apis.client.products.isabel_connect.models.read.PaymentNotificationsReadQuery; +import com.ibanity.apis.client.services.ApiUrlProvider; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.mockito.junit.jupiter.MockitoSettings; +import org.mockito.quality.Strictness; + +import java.net.URI; +import java.time.Instant; +import java.util.Collections; + +import static com.ibanity.apis.client.helpers.IbanityTestHelper.createHttpResponse; +import static com.ibanity.apis.client.helpers.IbanityTestHelper.loadHttpResponse; +import static org.assertj.core.api.AssertionsForClassTypes.assertThat; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +@ExtendWith(MockitoExtension.class) +@MockitoSettings(strictness = Strictness.LENIENT) +public class PaymentNotificationServiceImplTest { + private static final String ACCESS_TOKEN = "thisIsAnAccessToken"; + private static final String BPIR_ENDPOINT = "https://api.ibanity.localhost/isabel-connect/bulk-payment-initiation-requests/{bulkPaymentInitiationRequestId}"; + private static final String LIST_ENDPOINT = "https://api.ibanity.localhost/isabel-connect/bulk-payment-initiation-requests/notifications?size=20"; + private static final String NOTIFICATION_ID = "14e2bff5-e365-4bc7-bf48-76b7bcd464e9"; + private static final String DELETE_ENDPOINT = "https://api.ibanity.localhost/isabel-connect/bulk-payment-initiation-requests/notifications/" + NOTIFICATION_ID; + + @InjectMocks + private PaymentNotificationServiceImpl service; + + @Mock + private ApiUrlProvider apiUrlProvider; + + @Mock + private IbanityHttpClient ibanityHttpClient; + + @BeforeEach + void setUp() { + when(apiUrlProvider.find(IbanityProduct.IsabelConnect, "bulkPaymentInitiationRequests")) + .thenReturn(BPIR_ENDPOINT); + } + + @Test + public void list() throws Exception { + when(ibanityHttpClient.get(new URI(LIST_ENDPOINT), Collections.emptyMap(), ACCESS_TOKEN)) + .thenReturn(loadHttpResponse("json/isabel-connect/payment_notifications.json")); + + IsabelCollection actual = service.list(PaymentNotificationsReadQuery.builder() + .accessToken(ACCESS_TOKEN) + .build()); + + assertThat(actual.getItems().size()).isEqualTo(1); + assertThat(actual.getPagingOffset()).isEqualTo(0); + assertThat(actual.getPagingTotal()).isEqualTo(1); + + PaymentNotification notification = actual.getItems().get(0); + assertThat(notification.getId()).isEqualTo(NOTIFICATION_ID); + assertThat(notification.getNotificationType()).isEqualTo("payment.status.updated"); + assertThat(notification.getCreatedAt()).isEqualTo(Instant.parse("2026-06-04T14:30:00.000Z")); + assertThat(notification.getPaymentId()).isEqualTo("90000036388319"); + } + + @Test + public void delete() throws Exception { + when(ibanityHttpClient.delete(new URI(DELETE_ENDPOINT), Collections.emptyMap(), ACCESS_TOKEN)) + .thenReturn(createHttpResponse("")); + + service.delete(PaymentNotificationDeleteQuery.builder() + .accessToken(ACCESS_TOKEN) + .paymentNotificationId(NOTIFICATION_ID) + .build()); + + verify(ibanityHttpClient).delete(new URI(DELETE_ENDPOINT), Collections.emptyMap(), ACCESS_TOKEN); + } +} diff --git a/src/test/java/com/ibanity/apis/client/products/isabel_connect/services/impl/PaymentStatusServiceImplTest.java b/src/test/java/com/ibanity/apis/client/products/isabel_connect/services/impl/PaymentStatusServiceImplTest.java new file mode 100644 index 00000000..5fea63a9 --- /dev/null +++ b/src/test/java/com/ibanity/apis/client/products/isabel_connect/services/impl/PaymentStatusServiceImplTest.java @@ -0,0 +1,76 @@ +package com.ibanity.apis.client.products.isabel_connect.services.impl; + +import com.ibanity.apis.client.http.IbanityHttpClient; +import com.ibanity.apis.client.models.IbanityProduct; +import com.ibanity.apis.client.products.isabel_connect.models.read.PaymentStatusReadQuery; +import com.ibanity.apis.client.services.ApiUrlProvider; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.mockito.junit.jupiter.MockitoSettings; +import org.mockito.quality.Strictness; + +import java.net.URI; +import java.util.Collections; + +import static com.ibanity.apis.client.helpers.IbanityTestHelper.createHttpResponse; +import static com.ibanity.apis.client.helpers.IbanityTestHelper.loadFile; +import static org.assertj.core.api.AssertionsForClassTypes.assertThat; +import static org.mockito.Mockito.when; + +@ExtendWith(MockitoExtension.class) +@MockitoSettings(strictness = Strictness.LENIENT) +public class PaymentStatusServiceImplTest { + private static final String ACCESS_TOKEN = "thisIsAnAccessToken"; + private static final String BPIR_ENDPOINT = "https://api.ibanity.localhost/isabel-connect/bulk-payment-initiation-requests/{bulkPaymentInitiationRequestId}"; + private static final String PAYMENT_ID = "90000036388323"; + private static final String FIND_ENDPOINT = "https://api.ibanity.localhost/isabel-connect/bulk-payment-initiation-requests/" + PAYMENT_ID + "/payment-status"; + private static final String FIND_WITH_NOTIFICATION_ENDPOINT = FIND_ENDPOINT + "?notificationId=a"; + + @InjectMocks + private PaymentStatusServiceImpl service; + + @Mock + private ApiUrlProvider apiUrlProvider; + + @Mock + private IbanityHttpClient ibanityHttpClient; + + @BeforeEach + void setUp() { + when(apiUrlProvider.find(IbanityProduct.IsabelConnect, "bulkPaymentInitiationRequests")) + .thenReturn(BPIR_ENDPOINT); + } + + @Test + public void find() throws Exception { + String xmlContent = loadFile("xml/isabel-connect/payment_status.xml"); + when(ibanityHttpClient.get(new URI(FIND_ENDPOINT), Collections.emptyMap(), ACCESS_TOKEN)) + .thenReturn(createHttpResponse(xmlContent)); + + String actual = service.find(PaymentStatusReadQuery.builder() + .accessToken(ACCESS_TOKEN) + .bulkPaymentInitiationRequestId(PAYMENT_ID) + .build()); + + assertThat(actual).isEqualTo(xmlContent); + } + + @Test + public void findWithNotificationId() throws Exception { + String xmlContent = loadFile("xml/isabel-connect/payment_status.xml"); + when(ibanityHttpClient.get(new URI(FIND_WITH_NOTIFICATION_ENDPOINT), Collections.emptyMap(), ACCESS_TOKEN)) + .thenReturn(createHttpResponse(xmlContent)); + + String actual = service.find(PaymentStatusReadQuery.builder() + .accessToken(ACCESS_TOKEN) + .bulkPaymentInitiationRequestId(PAYMENT_ID) + .notificationId("a") + .build()); + + assertThat(actual).isEqualTo(xmlContent); + } +} diff --git a/src/test/java/com/ibanity/apis/client/utils/WebhooksUtilsTest.java b/src/test/java/com/ibanity/apis/client/utils/WebhooksUtilsTest.java index 53210cad..002bff88 100644 --- a/src/test/java/com/ibanity/apis/client/utils/WebhooksUtilsTest.java +++ b/src/test/java/com/ibanity/apis/client/utils/WebhooksUtilsTest.java @@ -1,6 +1,7 @@ package com.ibanity.apis.client.utils; import com.ibanity.apis.client.models.IbanityWebhookEvent; +import com.ibanity.apis.client.webhooks.models.isabel_connect.PaymentStatusUpdated; import com.ibanity.apis.client.webhooks.models.ponto_connect.SynchronizationSucceededWithoutChange; import org.junit.jupiter.api.Test; @@ -27,6 +28,23 @@ void webhookEventParser_pontoConnect() throws IOException { assertThat(ibanityWebhookEvent).isEqualTo(createExpectedWebhook()).usingRecursiveComparison(); } + @Test + void webhookEventParser_isabelConnect() throws IOException { + String payload = loadFile("json/webhooks/isabel-connect/paymentStatusUpdated.json"); + IbanityWebhookEvent ibanityWebhookEvent = WebhooksUtils.webhookEventParser(payload, "isabelConnect.payment.status.updated"); + assertThat(ibanityWebhookEvent).isEqualTo(createExpectedIsabelConnectWebhook()).usingRecursiveComparison(); + } + + private PaymentStatusUpdated createExpectedIsabelConnectWebhook() { + return PaymentStatusUpdated.builder() + .type("isabelConnect.payment.status.updated") + .notificationType("payment.status.updated") + .paymentId("90000036388319") + .id(UUID.fromString("550e8400-e29b-41d4-a716-446655440000")) + .createdAt(Instant.parse("2026-06-04T14:30:00.000Z")) + .build(); + } + private SynchronizationSucceededWithoutChange createExpectedWebhook() { return SynchronizationSucceededWithoutChange.builder() .type("pontoConnect.synchronization.succeededWithoutChange") diff --git a/src/test/resources/json/isabel-connect/payment_notifications.json b/src/test/resources/json/isabel-connect/payment_notifications.json new file mode 100644 index 00000000..d6819782 --- /dev/null +++ b/src/test/resources/json/isabel-connect/payment_notifications.json @@ -0,0 +1,26 @@ +{ + "meta": { + "paging": { + "offset": 0, + "total": 1 + } + }, + "data": [ + { + "attributes": { + "notificationType": "payment.status.updated", + "createdAt": "2026-06-04T14:30:00.000Z" + }, + "type": "paymentNotification", + "id": "14e2bff5-e365-4bc7-bf48-76b7bcd464e9", + "relationships": { + "payment": { + "data": { + "id": "90000036388319", + "type": "bulkPayment" + } + } + } + } + ] +} diff --git a/src/test/resources/json/webhooks/isabel-connect/paymentStatusUpdated.json b/src/test/resources/json/webhooks/isabel-connect/paymentStatusUpdated.json new file mode 100644 index 00000000..d7843c9c --- /dev/null +++ b/src/test/resources/json/webhooks/isabel-connect/paymentStatusUpdated.json @@ -0,0 +1,18 @@ +{ + "data": { + "attributes": { + "createdAt": "2026-06-04T14:30:00.000Z", + "notificationType": "payment.status.updated" + }, + "id": "550e8400-e29b-41d4-a716-446655440000", + "relationships": { + "payment": { + "data": { + "id": "90000036388319", + "type": "bulkPayment" + } + } + }, + "type": "isabelConnect.payment.status.updated" + } +} diff --git a/src/test/resources/xml/isabel-connect/payment_status.xml b/src/test/resources/xml/isabel-connect/payment_status.xml new file mode 100644 index 00000000..be0ef1d1 --- /dev/null +++ b/src/test/resources/xml/isabel-connect/payment_status.xml @@ -0,0 +1,18 @@ + + + + + ISABEL-20260603-0001 + 2026-06-03T14:30:00.000 + ISABEL + + + MSGID-2026-06-03-001 + pain.001.001.09 + + + PMTINF-001 + ACTC + + +