From ff4fad1ba1cb07bb33577fe66d43db4955e4c968 Mon Sep 17 00:00:00 2001 From: Alex Baker Date: Fri, 27 Oct 2023 16:04:17 -0700 Subject: [PATCH] Improve unit test coverage --- .../client/AppStoreServerAPIClient.java | 2 +- .../itunes/storekit/model/StatusResponse.java | 17 +- .../client/AppStoreServerAPIClientTest.java | 503 ++++++++++++++++++ .../client/BearerTokenAuthenticatorTest.java | 10 +- .../migration/ReceiptUtilityTest.java | 45 ++ .../storekit/model/AppTransactionTest.java | 38 ++ .../JWSRenewalInfoDecodedPayloadTest.java | 43 ++ .../JWSTransactionDecodedPayloadTest.java | 54 ++ .../ResponseBodyV2DecodedPayloadTest.java | 70 +++ .../PromotionalOfferSignatureCreatorTest.java | 11 +- .../storekit/util/SignedDataCreator.java | 31 ++ .../verification/SignedDataVerifierTest.java | 66 +-- .../XcodeSignedDataVerifierTest.java | 13 +- src/test/resources/{ => certs}/testCA.key | 0 src/test/resources/{ => certs}/testCA.pem | 0 .../{ => certs}/testIntermediate.key | 0 .../{ => certs}/testIntermediate.pem | 0 .../testInvalidIntermediateLeaf.pem | 0 .../testInvalidOIDIntermediate.pem | 0 .../{ => certs}/testInvalidOIDLeaf.pem | 0 src/test/resources/{ => certs}/testLeaf.key | 0 src/test/resources/{ => certs}/testLeaf.pem | 0 .../resources/{ => certs}/testSigningKey.p8 | 0 .../mock_signed_data/legacyTransaction | 1 + .../mock_signed_data/missingX5CHeaderClaim | 1 + .../resources/mock_signed_data/renewalInfo | 1 + .../mock_signed_data/testNotification | 1 + .../mock_signed_data/transactionInfo | 1 + .../resources/mock_signed_data/wrongBundleId | 1 + src/test/resources/models/apiException.json | 4 + .../models/apiTooManyRequestsException.json | 4 + src/test/resources/models/appTransaction.json | 13 + ...alDateForAllActiveSubscribersResponse.json | 3 + ...extendSubscriptionRenewalDateResponse.json | 6 + .../getAllSubscriptionStatusesResponse.json | 35 ++ .../getNotificationHistoryResponse.json | 27 + .../models/getRefundHistoryResponse.json | 8 + ...criptionRenewalDateExtensionsResponse.json | 7 + .../getTestNotificationStatusResponse.json | 12 + .../models/lookupOrderIdResponse.json | 7 + .../requestTestNotificationResponse.json | 3 + .../resources/models/signedNotification.json | 16 + .../resources/models/signedRenewalInfo.json | 16 + .../models/signedSummaryNotification.json | 21 + .../resources/models/signedTransaction.json | 25 + .../models/transactionHistoryResponse.json | 11 + .../models/transactionInfoResponse.json | 3 + 47 files changed, 1077 insertions(+), 53 deletions(-) create mode 100644 src/test/java/com/apple/itunes/storekit/client/AppStoreServerAPIClientTest.java create mode 100644 src/test/java/com/apple/itunes/storekit/migration/ReceiptUtilityTest.java create mode 100644 src/test/java/com/apple/itunes/storekit/model/AppTransactionTest.java create mode 100644 src/test/java/com/apple/itunes/storekit/model/JWSRenewalInfoDecodedPayloadTest.java create mode 100644 src/test/java/com/apple/itunes/storekit/model/JWSTransactionDecodedPayloadTest.java create mode 100644 src/test/java/com/apple/itunes/storekit/model/ResponseBodyV2DecodedPayloadTest.java create mode 100644 src/test/java/com/apple/itunes/storekit/util/SignedDataCreator.java rename src/test/resources/{ => certs}/testCA.key (100%) rename src/test/resources/{ => certs}/testCA.pem (100%) rename src/test/resources/{ => certs}/testIntermediate.key (100%) rename src/test/resources/{ => certs}/testIntermediate.pem (100%) rename src/test/resources/{ => certs}/testInvalidIntermediateLeaf.pem (100%) rename src/test/resources/{ => certs}/testInvalidOIDIntermediate.pem (100%) rename src/test/resources/{ => certs}/testInvalidOIDLeaf.pem (100%) rename src/test/resources/{ => certs}/testLeaf.key (100%) rename src/test/resources/{ => certs}/testLeaf.pem (100%) rename src/test/resources/{ => certs}/testSigningKey.p8 (100%) create mode 100644 src/test/resources/mock_signed_data/legacyTransaction create mode 100644 src/test/resources/mock_signed_data/missingX5CHeaderClaim create mode 100644 src/test/resources/mock_signed_data/renewalInfo create mode 100644 src/test/resources/mock_signed_data/testNotification create mode 100644 src/test/resources/mock_signed_data/transactionInfo create mode 100644 src/test/resources/mock_signed_data/wrongBundleId create mode 100644 src/test/resources/models/apiException.json create mode 100644 src/test/resources/models/apiTooManyRequestsException.json create mode 100644 src/test/resources/models/appTransaction.json create mode 100644 src/test/resources/models/extendRenewalDateForAllActiveSubscribersResponse.json create mode 100644 src/test/resources/models/extendSubscriptionRenewalDateResponse.json create mode 100644 src/test/resources/models/getAllSubscriptionStatusesResponse.json create mode 100644 src/test/resources/models/getNotificationHistoryResponse.json create mode 100644 src/test/resources/models/getRefundHistoryResponse.json create mode 100644 src/test/resources/models/getStatusOfSubscriptionRenewalDateExtensionsResponse.json create mode 100644 src/test/resources/models/getTestNotificationStatusResponse.json create mode 100644 src/test/resources/models/lookupOrderIdResponse.json create mode 100644 src/test/resources/models/requestTestNotificationResponse.json create mode 100644 src/test/resources/models/signedNotification.json create mode 100644 src/test/resources/models/signedRenewalInfo.json create mode 100644 src/test/resources/models/signedSummaryNotification.json create mode 100644 src/test/resources/models/signedTransaction.json create mode 100644 src/test/resources/models/transactionHistoryResponse.json create mode 100644 src/test/resources/models/transactionInfoResponse.json diff --git a/src/main/java/com/apple/itunes/storekit/client/AppStoreServerAPIClient.java b/src/main/java/com/apple/itunes/storekit/client/AppStoreServerAPIClient.java index 881c9b6a..f566cadd 100644 --- a/src/main/java/com/apple/itunes/storekit/client/AppStoreServerAPIClient.java +++ b/src/main/java/com/apple/itunes/storekit/client/AppStoreServerAPIClient.java @@ -286,7 +286,7 @@ public HistoryResponse getTransactionHistory(String transactionId, String revisi * @see Get Transaction Info */ public TransactionInfoResponse getTransactionInfo(String transactionId) throws APIException, IOException { - return makeHttpCall("inApps/v1/transactions/" + transactionId, "GET", Map.of(), null, TransactionInfoResponse.class); + return makeHttpCall("/inApps/v1/transactions/" + transactionId, "GET", Map.of(), null, TransactionInfoResponse.class); } /** diff --git a/src/main/java/com/apple/itunes/storekit/model/StatusResponse.java b/src/main/java/com/apple/itunes/storekit/model/StatusResponse.java index 772106cc..00cce787 100644 --- a/src/main/java/com/apple/itunes/storekit/model/StatusResponse.java +++ b/src/main/java/com/apple/itunes/storekit/model/StatusResponse.java @@ -19,7 +19,7 @@ public class StatusResponse { private static final String SERIALIZED_NAME_APP_APPLE_ID = "appAppleId"; private static final String SERIALIZED_NAME_DATA = "data"; @SerializedName(SERIALIZED_NAME_ENVIRONMENT) - private Environment environment; + private String environment; @SerializedName(SERIALIZED_NAME_BUNDLE_ID) private String bundleId; @SerializedName(SERIALIZED_NAME_APP_APPLE_ID) @@ -32,7 +32,7 @@ public StatusResponse() { } public StatusResponse environment(Environment environment) { - this.environment = environment; + this.environment = environment != null ? environment.getValue() : null; return this; } @@ -43,11 +43,22 @@ public StatusResponse environment(Environment environment) { * @see environment **/ public Environment getEnvironment() { + return environment != null ? Environment.fromValue(environment) : null; + } + + /** + * @see #getEnvironment() + */ + public String getRawEnvironment() { return environment; } public void setEnvironment(Environment environment) { - this.environment = environment; + this.environment = environment != null ? environment.getValue() : null; + } + + public void setRawEnvironment(String rawEnvironment) { + this.environment = rawEnvironment; } public StatusResponse bundleId(String bundleId) { diff --git a/src/test/java/com/apple/itunes/storekit/client/AppStoreServerAPIClientTest.java b/src/test/java/com/apple/itunes/storekit/client/AppStoreServerAPIClientTest.java new file mode 100644 index 00000000..c4fff965 --- /dev/null +++ b/src/test/java/com/apple/itunes/storekit/client/AppStoreServerAPIClientTest.java @@ -0,0 +1,503 @@ +// Copyright (c) 2023 Apple Inc. Licensed under MIT License. + +package com.apple.itunes.storekit.client; + +import com.apple.itunes.storekit.model.AccountTenure; +import com.apple.itunes.storekit.model.CheckTestNotificationResponse; +import com.apple.itunes.storekit.model.ConsumptionRequest; +import com.apple.itunes.storekit.model.ConsumptionStatus; +import com.apple.itunes.storekit.model.DeliveryStatus; +import com.apple.itunes.storekit.model.Environment; +import com.apple.itunes.storekit.model.ExtendReasonCode; +import com.apple.itunes.storekit.model.ExtendRenewalDateRequest; +import com.apple.itunes.storekit.model.ExtendRenewalDateResponse; +import com.apple.itunes.storekit.model.HistoryResponse; +import com.apple.itunes.storekit.model.InAppOwnershipType; +import com.apple.itunes.storekit.model.LastTransactionsItem; +import com.apple.itunes.storekit.model.LifetimeDollarsPurchased; +import com.apple.itunes.storekit.model.LifetimeDollarsRefunded; +import com.apple.itunes.storekit.model.MassExtendRenewalDateRequest; +import com.apple.itunes.storekit.model.MassExtendRenewalDateResponse; +import com.apple.itunes.storekit.model.MassExtendRenewalDateStatusResponse; +import com.apple.itunes.storekit.model.NotificationHistoryRequest; +import com.apple.itunes.storekit.model.NotificationHistoryResponse; +import com.apple.itunes.storekit.model.NotificationHistoryResponseItem; +import com.apple.itunes.storekit.model.NotificationTypeV2; +import com.apple.itunes.storekit.model.OrderLookupResponse; +import com.apple.itunes.storekit.model.OrderLookupStatus; +import com.apple.itunes.storekit.model.Platform; +import com.apple.itunes.storekit.model.PlayTime; +import com.apple.itunes.storekit.model.RefundHistoryResponse; +import com.apple.itunes.storekit.model.SendAttemptItem; +import com.apple.itunes.storekit.model.SendAttemptResult; +import com.apple.itunes.storekit.model.SendTestNotificationResponse; +import com.apple.itunes.storekit.model.Status; +import com.apple.itunes.storekit.model.StatusResponse; +import com.apple.itunes.storekit.model.SubscriptionGroupIdentifierItem; +import com.apple.itunes.storekit.model.Subtype; +import com.apple.itunes.storekit.model.TransactionHistoryRequest; +import com.apple.itunes.storekit.model.TransactionInfoResponse; +import com.apple.itunes.storekit.model.UserStatus; +import com.apple.itunes.storekit.util.TestingUtility; +import com.auth0.jwt.JWT; +import com.auth0.jwt.interfaces.DecodedJWT; +import com.google.gson.Gson; +import okhttp3.MediaType; +import okhttp3.Protocol; +import okhttp3.Request; +import okhttp3.RequestBody; +import okhttp3.Response; +import okhttp3.ResponseBody; +import okio.Buffer; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +import java.io.IOException; +import java.io.InputStream; +import java.util.List; +import java.util.Map; +import java.util.UUID; +import java.util.function.Consumer; + +public class AppStoreServerAPIClientTest { + + private final MediaType expectedMediaType = MediaType.parse("application/json; charset=utf-8"); + + @Test + public void testExtendRenewalDateForAllActiveSubscribers() throws IOException, APIException { + AppStoreServerAPIClient client = getClientWithBody("models/extendRenewalDateForAllActiveSubscribersResponse.json", request -> { + Assertions.assertEquals("POST", request.method()); + Assertions.assertEquals("/inApps/v1/subscriptions/extend/mass", request.url().encodedPath()); + RequestBody body = request.body(); + Assertions.assertNotNull(body); + Assertions.assertEquals(expectedMediaType, body.contentType()); + Buffer buffer = new Buffer(); + try { + body.writeTo(buffer); + } catch (IOException e) { + throw new RuntimeException(e); + } + Map root = new Gson().fromJson(buffer.readUtf8(), Map.class); + Assertions.assertEquals(45, ((Number) root.get("extendByDays")).intValue()); + Assertions.assertEquals(1, ((Number) root.get("extendReasonCode")).intValue()); + Assertions.assertEquals("fdf964a4-233b-486c-aac1-97d8d52688ac", root.get("requestIdentifier")); + Assertions.assertEquals(List.of("USA", "MEX"), root.get("storefrontCountryCodes")); + Assertions.assertEquals("com.example.productId", root.get("productId")); + }); + + MassExtendRenewalDateRequest extendRenewalDateRequest = new MassExtendRenewalDateRequest() + .extendByDays(45) + .extendReasonCode(ExtendReasonCode.CUSTOMER_SATISFACTION) + .requestIdentifier("fdf964a4-233b-486c-aac1-97d8d52688ac") + .storefrontCountryCodes(List.of("USA", "MEX")) + .productId("com.example.productId"); + + MassExtendRenewalDateResponse massExtendRenewalDateResponse = client.extendRenewalDateForAllActiveSubscribers(extendRenewalDateRequest); + + Assertions.assertNotNull(massExtendRenewalDateResponse); + Assertions.assertEquals("758883e8-151b-47b7-abd0-60c4d804c2f5", massExtendRenewalDateResponse.getRequestIdentifier()); + } + + @Test + public void testExtendSubscriptionRenewalDate() throws IOException, APIException { + AppStoreServerAPIClient client = getClientWithBody("models/extendSubscriptionRenewalDateResponse.json", request -> { + Assertions.assertEquals("PUT", request.method()); + Assertions.assertEquals("/inApps/v1/subscriptions/extend/4124214", request.url().encodedPath()); + RequestBody body = request.body(); + Assertions.assertNotNull(body); + Assertions.assertEquals(expectedMediaType, body.contentType()); + Buffer buffer = new Buffer(); + try { + body.writeTo(buffer); + } catch (IOException e) { + throw new RuntimeException(e); + } + Map root = new Gson().fromJson(buffer.readUtf8(), Map.class); + Assertions.assertEquals(45, ((Number) root.get("extendByDays")).intValue()); + Assertions.assertEquals(1, ((Number) root.get("extendReasonCode")).intValue()); + Assertions.assertEquals("fdf964a4-233b-486c-aac1-97d8d52688ac", root.get("requestIdentifier")); + }); + + ExtendRenewalDateRequest extendRenewalDateRequest = new ExtendRenewalDateRequest() + .extendByDays(45) + .extendReasonCode(ExtendReasonCode.CUSTOMER_SATISFACTION) + .requestIdentifier("fdf964a4-233b-486c-aac1-97d8d52688ac"); + + ExtendRenewalDateResponse extendRenewalDateResponse = client.extendSubscriptionRenewalDate("4124214", extendRenewalDateRequest); + + Assertions.assertNotNull(extendRenewalDateResponse); + Assertions.assertEquals("2312412", extendRenewalDateResponse.getOriginalTransactionId()); + Assertions.assertEquals("9993", extendRenewalDateResponse.getWebOrderLineItemId()); + Assertions.assertTrue(extendRenewalDateResponse.getSuccess()); + Assertions.assertEquals(1698148900000L, extendRenewalDateResponse.getEffectiveDate()); + } + + @Test + public void testGetAllSubscriptionStatuses() throws APIException, IOException { + AppStoreServerAPIClient client = getClientWithBody("models/getAllSubscriptionStatusesResponse.json", request -> { + Assertions.assertEquals("GET", request.method()); + Assertions.assertEquals("/inApps/v1/subscriptions/4321", request.url().encodedPath()); + Assertions.assertEquals(List.of("2", "1"), request.url().queryParameterValues("status")); + Assertions.assertNull(request.body()); + }); + + StatusResponse statusResponse = client.getAllSubscriptionStatuses("4321", new Status[] {Status.EXPIRED, Status.ACTIVE}); + + Assertions.assertNotNull(statusResponse); + Assertions.assertEquals(Environment.LOCAL_TESTING, statusResponse.getEnvironment()); + Assertions.assertEquals("LocalTesting", statusResponse.getRawEnvironment()); + Assertions.assertEquals("com.example", statusResponse.getBundleId()); + Assertions.assertEquals(5454545L, statusResponse.getAppAppleId()); + + SubscriptionGroupIdentifierItem item = new SubscriptionGroupIdentifierItem() + .subscriptionGroupIdentifier("sub_group_one") + .lastTransactions(List.of( + new LastTransactionsItem() + .status(Status.ACTIVE) + .originalTransactionId("3749183") + .signedTransactionInfo("signed_transaction_one") + .signedRenewalInfo("signed_renewal_one"), + new LastTransactionsItem() + .status(Status.REVOKED) + .originalTransactionId("5314314134") + .signedTransactionInfo("signed_transaction_two") + .signedRenewalInfo("signed_renewal_two") + )); + SubscriptionGroupIdentifierItem secondItem = new SubscriptionGroupIdentifierItem() + .subscriptionGroupIdentifier("sub_group_two") + .lastTransactions(List.of( + new LastTransactionsItem() + .status(Status.EXPIRED) + .originalTransactionId("3413453") + .signedTransactionInfo("signed_transaction_three") + .signedRenewalInfo("signed_renewal_three") + )); + Assertions.assertEquals(List.of(item, secondItem), statusResponse.getData()); + } + + @Test + public void testGetRefundHistory() throws APIException, IOException { + AppStoreServerAPIClient client = getClientWithBody("models/getRefundHistoryResponse.json", request -> { + Assertions.assertEquals("GET", request.method()); + Assertions.assertEquals("/inApps/v2/refund/lookup/555555", request.url().encodedPath()); + Assertions.assertEquals("revision_input", request.url().queryParameter("revision")); + Assertions.assertNull(request.body()); + }); + + RefundHistoryResponse refundHistoryResponse = client.getRefundHistory("555555", "revision_input"); + + Assertions.assertNotNull(refundHistoryResponse); + Assertions.assertEquals(List.of("signed_transaction_one", "signed_transaction_two"), refundHistoryResponse.getSignedTransactions()); + Assertions.assertEquals("revision_output", refundHistoryResponse.getRevision()); + Assertions.assertTrue(refundHistoryResponse.getHasMore()); + } + + @Test + public void testGetStatusOfSubscriptionRenewalDateExtensions() throws APIException, IOException { + AppStoreServerAPIClient client = getClientWithBody("models/getStatusOfSubscriptionRenewalDateExtensionsResponse.json", request -> { + Assertions.assertEquals("GET", request.method()); + Assertions.assertEquals("/inApps/v1/subscriptions/extend/mass/20fba8a0-2b80-4a7d-a17f-85c1854727f8/com.example.product", request.url().encodedPath()); + Assertions.assertNull(request.body()); + }); + + MassExtendRenewalDateStatusResponse massExtendRenewalDateStatusResponse = client.getStatusOfSubscriptionRenewalDateExtensions("com.example.product", "20fba8a0-2b80-4a7d-a17f-85c1854727f8"); + + Assertions.assertNotNull(massExtendRenewalDateStatusResponse); + Assertions.assertEquals("20fba8a0-2b80-4a7d-a17f-85c1854727f8", massExtendRenewalDateStatusResponse.getRequestIdentifier()); + Assertions.assertTrue(massExtendRenewalDateStatusResponse.getComplete()); + Assertions.assertEquals(1698148900000L, massExtendRenewalDateStatusResponse.getCompleteDate()); + Assertions.assertEquals(30, massExtendRenewalDateStatusResponse.getSucceededCount()); + Assertions.assertEquals(2, massExtendRenewalDateStatusResponse.getFailedCount()); + } + + @Test + public void testGetTestNotificationStatus() throws APIException, IOException { + AppStoreServerAPIClient client = getClientWithBody("models/getTestNotificationStatusResponse.json", request -> { + Assertions.assertEquals("GET", request.method()); + Assertions.assertEquals("/inApps/v1/notifications/test/8cd2974c-f905-492a-bf9a-b2f47c791d19", request.url().encodedPath()); + Assertions.assertNull(request.body()); + }); + + CheckTestNotificationResponse checkTestNotificationResponse = client.getTestNotificationStatus("8cd2974c-f905-492a-bf9a-b2f47c791d19"); + + Assertions.assertNotNull(checkTestNotificationResponse); + Assertions.assertEquals("signed_payload", checkTestNotificationResponse.getSignedPayload()); + List sendAttemptItems = List.of( + new SendAttemptItem() + .attemptDate(1698148900000L) + .sendAttemptResult(SendAttemptResult.NO_RESPONSE), + new SendAttemptItem() + .attemptDate(1698148950000L) + .sendAttemptResult(SendAttemptResult.SUCCESS) + ); + Assertions.assertEquals(sendAttemptItems, checkTestNotificationResponse.getSendAttempts()); + } + + @Test + public void testGetNotificationHistory() throws APIException, IOException { + AppStoreServerAPIClient client = getClientWithBody("models/getNotificationHistoryResponse.json", request -> { + Assertions.assertEquals("POST", request.method()); + Assertions.assertEquals("/inApps/v1/notifications/history", request.url().encodedPath()); + Assertions.assertEquals("a036bc0e-52b8-4bee-82fc-8c24cb6715d6", request.url().queryParameter("paginationToken")); + RequestBody body = request.body(); + Assertions.assertNotNull(body); + Assertions.assertEquals(expectedMediaType, body.contentType()); + Buffer buffer = new Buffer(); + try { + body.writeTo(buffer); + } catch (IOException e) { + throw new RuntimeException(e); + } + Map root = new Gson().fromJson(buffer.readUtf8(), Map.class); + Assertions.assertEquals(1698148900000L, ((Number) root.get("startDate")).longValue()); + Assertions.assertEquals(1698148950000L, ((Number) root.get("endDate")).longValue()); + Assertions.assertEquals("SUBSCRIBED", root.get("notificationType")); + Assertions.assertEquals("INITIAL_BUY", root.get("notificationSubtype")); + Assertions.assertEquals("999733843", root.get("transactionId")); + Assertions.assertTrue((Boolean) root.get("onlyFailures")); + }); + + NotificationHistoryRequest notificationHistoryRequest = new NotificationHistoryRequest() + .startDate(1698148900000L) + .endDate(1698148950000L) + .notificationType(NotificationTypeV2.SUBSCRIBED) + .notificationSubtype(Subtype.INITIAL_BUY) + .transactionId("999733843") + .onlyFailures(true); + + NotificationHistoryResponse notificationHistoryResponse = client.getNotificationHistory("a036bc0e-52b8-4bee-82fc-8c24cb6715d6", notificationHistoryRequest); + + Assertions.assertNotNull(notificationHistoryResponse); + Assertions.assertEquals("57715481-805a-4283-8499-1c19b5d6b20a", notificationHistoryResponse.getPaginationToken()); + Assertions.assertTrue(notificationHistoryResponse.getHasMore()); + List expectedNotificationHistory = List.of( + new NotificationHistoryResponseItem() + .sendAttempts(List.of( + new SendAttemptItem() + .attemptDate(1698148900000L) + .sendAttemptResult(SendAttemptResult.NO_RESPONSE), + new SendAttemptItem() + .attemptDate(1698148950000L) + .sendAttemptResult(SendAttemptResult.SUCCESS) + )) + .signedPayload("signed_payload_one"), + new NotificationHistoryResponseItem() + .sendAttempts(List.of( + new SendAttemptItem() + .attemptDate(1698148800000L) + .sendAttemptResult(SendAttemptResult.CIRCULAR_REDIRECT) + )) + .signedPayload("signed_payload_two") + ); + Assertions.assertEquals(expectedNotificationHistory, notificationHistoryResponse.getNotificationHistory()); + } + + @Test + public void testGetTransactionHistory() throws APIException, IOException { + AppStoreServerAPIClient client = getClientWithBody("models/transactionHistoryResponse.json", request -> { + Assertions.assertEquals("GET", request.method()); + Assertions.assertEquals("/inApps/v1/history/1234", request.url().encodedPath()); + Assertions.assertEquals("revision_input", request.url().queryParameter("revision")); + Assertions.assertEquals("123455", request.url().queryParameter("startDate")); + Assertions.assertEquals("123456", request.url().queryParameter("endDate")); + Assertions.assertEquals(List.of("com.example.1", "com.example.2"), request.url().queryParameterValues("productId")); + Assertions.assertEquals(List.of("CONSUMABLE", "AUTO_RENEWABLE"), request.url().queryParameterValues("productType")); + Assertions.assertEquals("ASCENDING", request.url().queryParameter("sort")); + Assertions.assertEquals(List.of("sub_group_id", "sub_group_id_2"), request.url().queryParameterValues("subscriptionGroupIdentifier")); + Assertions.assertEquals("FAMILY_SHARED", request.url().queryParameter("inAppOwnershipType")); + Assertions.assertEquals("false", request.url().queryParameter("revoked")); + Assertions.assertNull(request.body()); + }); + + TransactionHistoryRequest request = new TransactionHistoryRequest() + .sort(TransactionHistoryRequest.Order.ASCENDING) + .productTypes(List.of(TransactionHistoryRequest.ProductType.CONSUMABLE, TransactionHistoryRequest.ProductType.AUTO_RENEWABLE)) + .endDate(123456L) + .startDate(123455L) + .revoked(false) + .inAppOwnershipType(InAppOwnershipType.FAMILY_SHARED) + .productIds(List.of("com.example.1", "com.example.2")) + .subscriptionGroupIdentifiers(List.of("sub_group_id", "sub_group_id_2")); + + HistoryResponse historyResponse = client.getTransactionHistory("1234", "revision_input", request); + + Assertions.assertNotNull(historyResponse); + Assertions.assertEquals("revision_output", historyResponse.getRevision()); + Assertions.assertTrue(historyResponse.getHasMore()); + Assertions.assertEquals("com.example", historyResponse.getBundleId()); + Assertions.assertEquals(323232L, historyResponse.getAppAppleId()); + Assertions.assertEquals(Environment.LOCAL_TESTING, historyResponse.getEnvironment()); + Assertions.assertEquals("LocalTesting", historyResponse.getRawEnvironment()); + Assertions.assertEquals(List.of("signed_transaction_value", "signed_transaction_value2"), historyResponse.getSignedTransactions()); + } + + @Test + public void testGetTransactionInfo() throws APIException, IOException { + AppStoreServerAPIClient client = getClientWithBody("models/transactionInfoResponse.json", request -> { + Assertions.assertEquals("GET", request.method()); + Assertions.assertEquals("/inApps/v1/transactions/1234", request.url().encodedPath()); + Assertions.assertNull(request.body()); + }); + + TransactionInfoResponse transactionInfoResponse = client.getTransactionInfo("1234"); + + Assertions.assertNotNull(transactionInfoResponse); + Assertions.assertEquals("signed_transaction_info_value", transactionInfoResponse.getSignedTransactionInfo()); + } + + @Test + public void testLookUpOrderId() throws APIException, IOException { + AppStoreServerAPIClient client = getClientWithBody("models/lookupOrderIdResponse.json", request -> { + Assertions.assertEquals("GET", request.method()); + Assertions.assertEquals("/inApps/v1/lookup/W002182", request.url().encodedPath()); + Assertions.assertNull(request.body()); + }); + + OrderLookupResponse orderLookupResponse = client.lookUpOrderId("W002182"); + + Assertions.assertNotNull(orderLookupResponse); + Assertions.assertEquals(OrderLookupStatus.INVALID, orderLookupResponse.getStatus()); + Assertions.assertEquals(1, orderLookupResponse.getRawStatus()); + Assertions.assertEquals(List.of("signed_transaction_one", "signed_transaction_two"), orderLookupResponse.getSignedTransactions()); + } + + @Test + public void testRequestTestNotification() throws APIException, IOException { + AppStoreServerAPIClient client = getClientWithBody("models/requestTestNotificationResponse.json", request -> { + Assertions.assertEquals("POST", request.method()); + Assertions.assertEquals("/inApps/v1/notifications/test", request.url().encodedPath()); + RequestBody body = request.body(); + Assertions.assertNotNull(body); + Assertions.assertNull(body.contentType()); + try { + Assertions.assertEquals(0, body.contentLength()); + } catch (IOException e) { + throw new RuntimeException(e); + } + }); + + SendTestNotificationResponse sendTestNotificationResponse = client.requestTestNotification(); + + Assertions.assertNotNull(sendTestNotificationResponse); + Assertions.assertEquals("ce3af791-365e-4c60-841b-1674b43c1609", sendTestNotificationResponse.getTestNotificationToken()); + } + + @Test + public void testSendConsumptionData() throws APIException, IOException { + AppStoreServerAPIClient client = getAppStoreServerAPIClient("", request -> { + Assertions.assertEquals("PUT", request.method()); + Assertions.assertEquals("/inApps/v1/transactions/consumption/49571273", request.url().encodedPath()); + RequestBody body = request.body(); + Assertions.assertNotNull(body); + Assertions.assertEquals(expectedMediaType, body.contentType()); + Buffer buffer = new Buffer(); + try { + body.writeTo(buffer); + } catch (IOException e) { + throw new RuntimeException(e); + } + Map root = new Gson().fromJson(buffer.readUtf8(), Map.class); + Assertions.assertTrue((Boolean) root.get("customerConsented")); + Assertions.assertEquals(1, ((Number) root.get("consumptionStatus")).intValue()); + Assertions.assertEquals(2, ((Number) root.get("platform")).intValue()); + Assertions.assertFalse((Boolean) root.get("sampleContentProvided")); + Assertions.assertEquals(3, ((Number) root.get("deliveryStatus")).intValue()); + Assertions.assertEquals("7389a31a-fb6d-4569-a2a6-db7d85d84813", root.get("appAccountToken")); + Assertions.assertEquals(4, ((Number) root.get("accountTenure")).intValue()); + Assertions.assertEquals(5, ((Number) root.get("playTime")).intValue()); + Assertions.assertEquals(6, ((Number) root.get("lifetimeDollarsRefunded")).intValue()); + Assertions.assertEquals(7, ((Number) root.get("lifetimeDollarsPurchased")).intValue()); + Assertions.assertEquals(4, ((Number) root.get("userStatus")).intValue()); + }); + + ConsumptionRequest consumptionRequest = new ConsumptionRequest() + .customerConsented(true) + .consumptionStatus(ConsumptionStatus.NOT_CONSUMED) + .platform(Platform.NON_APPLE) + .sampleContentProvided(false) + .deliveryStatus(DeliveryStatus.DID_NOT_DELIVER_DUE_TO_SERVER_OUTAGE) + .appAccountToken(UUID.fromString("7389a31a-fb6d-4569-a2a6-db7d85d84813")) + .accountTenure(AccountTenure.THIRTY_DAYS_TO_NINETY_DAYS) + .playTime(PlayTime.ONE_DAY_TO_FOUR_DAYS) + .lifetimeDollarsRefunded(LifetimeDollarsRefunded.ONE_THOUSAND_DOLLARS_TO_ONE_THOUSAND_NINE_HUNDRED_NINETY_NINE_DOLLARS_AND_NINETY_NINE_CENTS) + .lifetimeDollarsPurchased(LifetimeDollarsPurchased.TWO_THOUSAND_DOLLARS_OR_GREATER) + .userStatus(UserStatus.LIMITED_ACCESS); + + client.sendConsumptionData("49571273", consumptionRequest); + } + + @Test + public void testHeaders() throws APIException, IOException { + AppStoreServerAPIClient client = getClientWithBody("models/transactionInfoResponse.json", request -> { + Assertions.assertTrue(request.header("User-Agent").startsWith("app-store-server-library/java")); + Assertions.assertEquals("application/json", request.header("Accept")); + String authorization = request.header("Authorization"); + Assertions.assertTrue(authorization.startsWith("Bearer ")); + DecodedJWT token = JWT.decode(authorization.substring(7)); + Assertions.assertEquals(List.of("appstoreconnect-v1"), token.getAudience()); + Assertions.assertEquals("issuerId", token.getIssuer()); + Assertions.assertEquals("keyId", token.getKeyId()); + Assertions.assertEquals("com.example", token.getClaim("bid").asString()); + Assertions.assertEquals("ES256", token.getAlgorithm()); + }); + + client.getTransactionInfo("1234"); + } + + @Test + public void testAPIError() throws IOException { + String body = TestingUtility.readFile("models/apiException.json"); + AppStoreServerAPIClient client = getAppStoreServerAPIClient(body, request -> {}, 500); + try { + client.getTransactionInfo("1234"); + } catch (APIException e) { + Assertions.assertEquals(500 , e.getHttpStatusCode()); + Assertions.assertEquals(APIError.GENERAL_INTERNAL, e.getApiError()); + Assertions.assertEquals(5000000L, e.getRawApiError()); + return; + } + Assertions.fail(); + } + + @Test + public void testAPITooManyRequests() throws IOException { + String body = TestingUtility.readFile("models/apiTooManyRequestsException.json"); + AppStoreServerAPIClient client = getAppStoreServerAPIClient(body, request -> {}, 429); + try { + client.getTransactionInfo("1234"); + } catch (APIException e) { + Assertions.assertEquals(429 , e.getHttpStatusCode()); + Assertions.assertEquals(APIError.RATE_LIMIT_EXCEEDED, e.getApiError()); + Assertions.assertEquals(4290000L, e.getRawApiError()); + return; + } + Assertions.fail(); + } + + public AppStoreServerAPIClient getClientWithBody(String path, Consumer requestVerifier) throws IOException { + String body = TestingUtility.readFile(path); + return getAppStoreServerAPIClient(body, requestVerifier); + } + + private AppStoreServerAPIClient getAppStoreServerAPIClient(String body, Consumer requestVerifier) throws IOException { + return getAppStoreServerAPIClient(body, requestVerifier, 200); + } + + private AppStoreServerAPIClient getAppStoreServerAPIClient(String body, Consumer requestVerifier, int statusCode) throws IOException { + try (InputStream key = this.getClass().getClassLoader().getResourceAsStream("certs/testSigningKey.p8")) { + return new AppStoreServerAPIClient(new String(key.readAllBytes()), "keyId", "issuerId", "com.example", Environment.LOCAL_TESTING) { + @Override + protected Response getResponse(Request request) { + requestVerifier.accept(request); + return new Response.Builder() + .body(ResponseBody.create(body, MediaType.parse("application/json"))) + .code(statusCode) + .request(request) + .protocol(Protocol.HTTP_1_1) + .message("") + .build(); + } + }; + } + } +} diff --git a/src/test/java/com/apple/itunes/storekit/client/BearerTokenAuthenticatorTest.java b/src/test/java/com/apple/itunes/storekit/client/BearerTokenAuthenticatorTest.java index 9f6757d4..151e31dd 100644 --- a/src/test/java/com/apple/itunes/storekit/client/BearerTokenAuthenticatorTest.java +++ b/src/test/java/com/apple/itunes/storekit/client/BearerTokenAuthenticatorTest.java @@ -2,6 +2,7 @@ package com.apple.itunes.storekit.client; +import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import java.io.InputStream; @@ -9,10 +10,11 @@ class BearerTokenAuthenticatorTest { @Test - void testConstructor() throws Exception { - try (InputStream key = this.getClass().getClassLoader().getResourceAsStream("testSigningKey.p8")) { - new BearerTokenAuthenticator(new String(key.readAllBytes()), "keyId", "issuerId", "bundleId"); + void testCreatingToken() throws Exception { + try (InputStream key = this.getClass().getClassLoader().getResourceAsStream("certs/testSigningKey.p8")) { + var tokenGenerator = new BearerTokenAuthenticator(new String(key.readAllBytes()), "keyId", "issuerId", "bundleId"); + String token = tokenGenerator.generateToken(); + Assertions.assertNotNull(token); } } - } diff --git a/src/test/java/com/apple/itunes/storekit/migration/ReceiptUtilityTest.java b/src/test/java/com/apple/itunes/storekit/migration/ReceiptUtilityTest.java new file mode 100644 index 00000000..a20b48a5 --- /dev/null +++ b/src/test/java/com/apple/itunes/storekit/migration/ReceiptUtilityTest.java @@ -0,0 +1,45 @@ +// Copyright (c) 2023 Apple Inc. Licensed under MIT License. + +package com.apple.itunes.storekit.migration; + +import com.apple.itunes.storekit.util.TestingUtility; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +import java.io.IOException; + +public class ReceiptUtilityTest { + + private static final String APP_RECEIPT_EXPECTED_TRANSACTION_ID = "0"; + private static final String TRANSACTION_RECEIPT_EXPECTED_TRANSACTION_ID = "33993399"; + + @Test + public void testXcodeAppReceiptExtractionWithNoTransactions() throws IOException { + String receipt = TestingUtility.readFile("xcode/xcode-app-receipt-empty"); + + ReceiptUtility util = new ReceiptUtility(); + String extractedTransactionId = util.extractTransactionIdFromAppReceipt(receipt); + + Assertions.assertNull(extractedTransactionId); + } + + @Test + public void testXcodeAppReceiptExtractionWithTransactions() throws IOException { + String receipt = TestingUtility.readFile("xcode/xcode-app-receipt-with-transaction"); + + ReceiptUtility util = new ReceiptUtility(); + String extractedTransactionId = util.extractTransactionIdFromAppReceipt(receipt); + + Assertions.assertEquals(APP_RECEIPT_EXPECTED_TRANSACTION_ID, extractedTransactionId); + } + + @Test + public void testTransactionReceiptExtraction() throws IOException { + String receipt = TestingUtility.readFile("mock_signed_data/legacyTransaction"); + + ReceiptUtility util = new ReceiptUtility(); + String extractedTransactionId = util.extractTransactionIdFromTransactionReceipt(receipt); + + Assertions.assertEquals(TRANSACTION_RECEIPT_EXPECTED_TRANSACTION_ID, extractedTransactionId); + } +} diff --git a/src/test/java/com/apple/itunes/storekit/model/AppTransactionTest.java b/src/test/java/com/apple/itunes/storekit/model/AppTransactionTest.java new file mode 100644 index 00000000..760a2ec7 --- /dev/null +++ b/src/test/java/com/apple/itunes/storekit/model/AppTransactionTest.java @@ -0,0 +1,38 @@ +// Copyright (c) 2023 Apple Inc. Licensed under MIT License. + +package com.apple.itunes.storekit.model; + + +import com.apple.itunes.storekit.util.SignedDataCreator; +import com.apple.itunes.storekit.util.TestingUtility; +import com.apple.itunes.storekit.verification.VerificationException; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +import java.io.IOException; +import java.security.NoSuchAlgorithmException; +import java.security.spec.InvalidKeySpecException; +import java.util.UUID; + +public class AppTransactionTest { + + @Test + public void testAppTransactionDecoding() throws IOException, NoSuchAlgorithmException, VerificationException { + String signedAppTransaction = SignedDataCreator.createSignedDataFromJson("models/appTransaction.json"); + + AppTransaction appTransaction = TestingUtility.getSignedPayloadVerifier().verifyAndDecodeAppTransaction(signedAppTransaction); + + Assertions.assertEquals(Environment.LOCAL_TESTING, appTransaction.getReceiptType()); + Assertions.assertEquals("LocalTesting", appTransaction.getRawReceiptType()); + Assertions.assertEquals(531412, appTransaction.getAppAppleId()); + Assertions.assertEquals("com.example", appTransaction.getBundleId()); + Assertions.assertEquals("1.2.3", appTransaction.getApplicationVersion()); + Assertions.assertEquals(512, appTransaction.versionExternalIdentifier()); + Assertions.assertEquals(1698148900000L, appTransaction.getReceiptCreationDate()); + Assertions.assertEquals(1698148800000L, appTransaction.originalPurchaseDate()); + Assertions.assertEquals("1.1.2", appTransaction.getOriginalApplicationVersion()); + Assertions.assertEquals("device_verification_value", appTransaction.getDeviceVerification()); + Assertions.assertEquals(UUID.fromString("48ccfa42-7431-4f22-9908-7e88983e105a"), appTransaction.getDeviceVerificationNonce()); + Assertions.assertEquals(1698148700000L, appTransaction.getPreorderDate()); + } +} diff --git a/src/test/java/com/apple/itunes/storekit/model/JWSRenewalInfoDecodedPayloadTest.java b/src/test/java/com/apple/itunes/storekit/model/JWSRenewalInfoDecodedPayloadTest.java new file mode 100644 index 00000000..78dba267 --- /dev/null +++ b/src/test/java/com/apple/itunes/storekit/model/JWSRenewalInfoDecodedPayloadTest.java @@ -0,0 +1,43 @@ +// Copyright (c) 2023 Apple Inc. Licensed under MIT License. + +package com.apple.itunes.storekit.model; + +import com.apple.itunes.storekit.util.SignedDataCreator; +import com.apple.itunes.storekit.util.TestingUtility; +import com.apple.itunes.storekit.verification.VerificationException; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +import java.io.IOException; +import java.security.NoSuchAlgorithmException; +import java.security.spec.InvalidKeySpecException; + +public class JWSRenewalInfoDecodedPayloadTest { + + @Test + public void testRenewalInfoDecoding() throws IOException, NoSuchAlgorithmException, InvalidKeySpecException, VerificationException { + String signedRenewalInfo = SignedDataCreator.createSignedDataFromJson("models/signedRenewalInfo.json"); + + JWSRenewalInfoDecodedPayload renewalInfo = TestingUtility.getSignedPayloadVerifier().verifyAndDecodeRenewalInfo(signedRenewalInfo); + + Assertions.assertEquals(ExpirationIntent.CUSTOMER_CANCELLED, renewalInfo.getExpirationIntent()); + Assertions.assertEquals(1, renewalInfo.getRawExpirationIntent()); + Assertions.assertEquals("12345", renewalInfo.getOriginalTransactionId()); + Assertions.assertEquals("com.example.product.2", renewalInfo.getAutoRenewProductId()); + Assertions.assertEquals("com.example.product", renewalInfo.getProductId()); + Assertions.assertEquals(AutoRenewStatus.ON, renewalInfo.getAutoRenewStatus()); + Assertions.assertEquals(1, renewalInfo.getRawAutoRenewStatus()); + Assertions.assertTrue(renewalInfo.getIsInBillingRetryPeriod()); + Assertions.assertEquals(PriceIncreaseStatus.CUSTOMER_HAS_NOT_RESPONDED, renewalInfo.getPriceIncreaseStatus()); + Assertions.assertEquals(0, renewalInfo.getRawPriceIncreaseStatus()); + Assertions.assertEquals(1698148900000L, renewalInfo.getGracePeriodExpiresDate()); + Assertions.assertEquals(OfferType.PROMOTIONAL_OFFER, renewalInfo.getOfferType()); + Assertions.assertEquals(2, renewalInfo.getRawOfferType()); + Assertions.assertEquals("abc.123", renewalInfo.getOfferIdentifier()); + Assertions.assertEquals(1698148800000L, renewalInfo.getSignedDate()); + Assertions.assertEquals(Environment.LOCAL_TESTING, renewalInfo.getEnvironment()); + Assertions.assertEquals("LocalTesting", renewalInfo.getRawEnvironment()); + Assertions.assertEquals(1698148800000L, renewalInfo.getRecentSubscriptionStartDate()); + Assertions.assertEquals(1698148850000L, renewalInfo.getRenewalDate()); + } +} diff --git a/src/test/java/com/apple/itunes/storekit/model/JWSTransactionDecodedPayloadTest.java b/src/test/java/com/apple/itunes/storekit/model/JWSTransactionDecodedPayloadTest.java new file mode 100644 index 00000000..2c091f1d --- /dev/null +++ b/src/test/java/com/apple/itunes/storekit/model/JWSTransactionDecodedPayloadTest.java @@ -0,0 +1,54 @@ +// Copyright (c) 2023 Apple Inc. Licensed under MIT License. + +package com.apple.itunes.storekit.model; + +import com.apple.itunes.storekit.util.SignedDataCreator; +import com.apple.itunes.storekit.util.TestingUtility; +import com.apple.itunes.storekit.verification.VerificationException; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +import java.io.IOException; +import java.security.NoSuchAlgorithmException; +import java.security.spec.InvalidKeySpecException; +import java.util.UUID; + +public class JWSTransactionDecodedPayloadTest { + + @Test + public void testTransactionDecoding() throws IOException, NoSuchAlgorithmException, InvalidKeySpecException, VerificationException { + String signedTransaction = SignedDataCreator.createSignedDataFromJson("models/signedTransaction.json"); + + JWSTransactionDecodedPayload transaction = TestingUtility.getSignedPayloadVerifier().verifyAndDecodeTransaction(signedTransaction); + + Assertions.assertEquals("12345", transaction.getOriginalTransactionId()); + Assertions.assertEquals("23456", transaction.getTransactionId()); + Assertions.assertEquals("34343", transaction.getWebOrderLineItemId()); + Assertions.assertEquals("com.example", transaction.getBundleId()); + Assertions.assertEquals("com.example.product", transaction.getProductId()); + Assertions.assertEquals("55555", transaction.getSubscriptionGroupIdentifier()); + Assertions.assertEquals(1698148800000L, transaction.getOriginalPurchaseDate()); + Assertions.assertEquals(1698148900000L, transaction.getPurchaseDate()); + Assertions.assertEquals(1698148950000L, transaction.getRevocationDate()); + Assertions.assertEquals(1698149000000L, transaction.getExpiresDate()); + Assertions.assertEquals(1, transaction.getQuantity()); + Assertions.assertEquals(Type.AUTO_RENEWABLE_SUBSCRIPTION, transaction.getType()); + Assertions.assertEquals("Auto-Renewable Subscription", transaction.getRawType()); + Assertions.assertEquals(UUID.fromString("7e3fb20b-4cdb-47cc-936d-99d65f608138"), transaction.getAppAccountToken()); + Assertions.assertEquals(InAppOwnershipType.PURCHASED, transaction.getInAppOwnershipType()); + Assertions.assertEquals("PURCHASED", transaction.getRawInAppOwnershipType()); + Assertions.assertEquals(1698148900000L, transaction.getSignedDate()); + Assertions.assertEquals(RevocationReason.REFUNDED_DUE_TO_ISSUE, transaction.getRevocationReason()); + Assertions.assertEquals(1, transaction.getRawRevocationReason()); + Assertions.assertEquals("abc.123", transaction.getOfferIdentifier()); + Assertions.assertTrue(transaction.getIsUpgraded()); + Assertions.assertEquals(OfferType.INTRODUCTORY_OFFER, transaction.getOfferType()); + Assertions.assertEquals(1, transaction.getRawOfferType()); + Assertions.assertEquals("USA", transaction.getStorefront()); + Assertions.assertEquals("143441", transaction.getStorefrontId()); + Assertions.assertEquals(TransactionReason.PURCHASE, transaction.getTransactionReason()); + Assertions.assertEquals("PURCHASE", transaction.getRawTransactionReason()); + Assertions.assertEquals(Environment.LOCAL_TESTING, transaction.getEnvironment()); + Assertions.assertEquals("LocalTesting", transaction.getRawEnvironment()); + } +} diff --git a/src/test/java/com/apple/itunes/storekit/model/ResponseBodyV2DecodedPayloadTest.java b/src/test/java/com/apple/itunes/storekit/model/ResponseBodyV2DecodedPayloadTest.java new file mode 100644 index 00000000..4ed22e1f --- /dev/null +++ b/src/test/java/com/apple/itunes/storekit/model/ResponseBodyV2DecodedPayloadTest.java @@ -0,0 +1,70 @@ +// Copyright (c) 2023 Apple Inc. Licensed under MIT License. + +package com.apple.itunes.storekit.model; + +import com.apple.itunes.storekit.util.SignedDataCreator; +import com.apple.itunes.storekit.util.TestingUtility; +import com.apple.itunes.storekit.verification.VerificationException; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +import java.io.IOException; +import java.security.NoSuchAlgorithmException; +import java.security.spec.InvalidKeySpecException; +import java.util.List; +import java.util.UUID; + +public class ResponseBodyV2DecodedPayloadTest { + + @Test + public void testNotificationDecoding() throws IOException, NoSuchAlgorithmException, InvalidKeySpecException, VerificationException { + String signedNotification = SignedDataCreator.createSignedDataFromJson("models/signedNotification.json"); + + ResponseBodyV2DecodedPayload notification = TestingUtility.getSignedPayloadVerifier().verifyAndDecodeNotification(signedNotification); + + Assertions.assertEquals(NotificationTypeV2.SUBSCRIBED, notification.getNotificationType()); + Assertions.assertEquals("SUBSCRIBED", notification.getRawNotificationType()); + Assertions.assertEquals(Subtype.INITIAL_BUY, notification.getSubtype()); + Assertions.assertEquals("INITIAL_BUY", notification.getRawSubtype()); + Assertions.assertEquals("002e14d5-51f5-4503-b5a8-c3a1af68eb20", notification.getNotificationUUID()); + Assertions.assertEquals("2.0", notification.getVersion()); + Assertions.assertEquals(1698148900000L, notification.getSignedDate()); + Assertions.assertNotNull(notification.getData()); + Assertions.assertNull(notification.getSummary()); + Assertions.assertEquals(Environment.LOCAL_TESTING, notification.getData().getEnvironment()); + Assertions.assertEquals("LocalTesting", notification.getData().getRawEnvironment()); + Assertions.assertEquals(41234L, notification.getData().getAppAppleId()); + Assertions.assertEquals("com.example", notification.getData().getBundleId()); + Assertions.assertEquals("1.2.3", notification.getData().getBundleVersion()); + Assertions.assertEquals("signed_transaction_info_value", notification.getData().getSignedTransactionInfo()); + Assertions.assertEquals("signed_renewal_info_value", notification.getData().getSignedRenewalInfo()); + Assertions.assertEquals(Status.ACTIVE, notification.getData().getStatus()); + Assertions.assertEquals(1, notification.getData().getRawStatus()); + } + + @Test + public void testSummaryNotificationDecoding() throws IOException, NoSuchAlgorithmException, InvalidKeySpecException, VerificationException { + String signedNotification = SignedDataCreator.createSignedDataFromJson("models/signedSummaryNotification.json"); + + ResponseBodyV2DecodedPayload notification = TestingUtility.getSignedPayloadVerifier().verifyAndDecodeNotification(signedNotification); + + Assertions.assertEquals(NotificationTypeV2.RENEWAL_EXTENSION, notification.getNotificationType()); + Assertions.assertEquals("RENEWAL_EXTENSION", notification.getRawNotificationType()); + Assertions.assertEquals(Subtype.SUMMARY, notification.getSubtype()); + Assertions.assertEquals("SUMMARY", notification.getRawSubtype()); + Assertions.assertEquals("002e14d5-51f5-4503-b5a8-c3a1af68eb20", notification.getNotificationUUID()); + Assertions.assertEquals("2.0", notification.getVersion()); + Assertions.assertEquals(1698148900000L, notification.getSignedDate()); + Assertions.assertNull(notification.getData()); + Assertions.assertNotNull(notification.getSummary()); + Assertions.assertEquals(Environment.LOCAL_TESTING, notification.getSummary().getEnvironment()); + Assertions.assertEquals("LocalTesting", notification.getSummary().getRawEnvironment()); + Assertions.assertEquals(41234L, notification.getSummary().getAppAppleId()); + Assertions.assertEquals("com.example", notification.getSummary().getBundleId()); + Assertions.assertEquals("com.example.product", notification.getSummary().getProductId()); + Assertions.assertEquals("efb27071-45a4-4aca-9854-2a1e9146f265", notification.getSummary().getRequestIdentifier()); + Assertions.assertEquals(List.of("CAN", "USA", "MEX"), notification.getSummary().getStorefrontCountryCodes()); + Assertions.assertEquals(5, notification.getSummary().getSucceededCount()); + Assertions.assertEquals(2, notification.getSummary().getFailedCount()); + } +} diff --git a/src/test/java/com/apple/itunes/storekit/offers/PromotionalOfferSignatureCreatorTest.java b/src/test/java/com/apple/itunes/storekit/offers/PromotionalOfferSignatureCreatorTest.java index ca59f421..20e07dda 100644 --- a/src/test/java/com/apple/itunes/storekit/offers/PromotionalOfferSignatureCreatorTest.java +++ b/src/test/java/com/apple/itunes/storekit/offers/PromotionalOfferSignatureCreatorTest.java @@ -2,17 +2,20 @@ package com.apple.itunes.storekit.offers; +import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import java.io.InputStream; +import java.util.UUID; class PromotionalOfferSignatureCreatorTest { @Test - void testConstructor() throws Exception { - try (InputStream key = this.getClass().getClassLoader().getResourceAsStream("testSigningKey.p8")) { - new PromotionalOfferSignatureCreator(new String(key.readAllBytes()), "keyId", "bundleId"); + void testSignatureCreator() throws Exception { + try (InputStream key = this.getClass().getClassLoader().getResourceAsStream("certs/testSigningKey.p8")) { + PromotionalOfferSignatureCreator signatureCreator = new PromotionalOfferSignatureCreator(new String(key.readAllBytes()), "keyId", "bundleId"); + String signature = signatureCreator.createSignature("productId", "offerId", "applicationUsername", UUID.fromString("20fba8a0-2b80-4a7d-a17f-85c1854727f8"), 1698148900000L); + Assertions.assertNotNull(signature); } } - } diff --git a/src/test/java/com/apple/itunes/storekit/util/SignedDataCreator.java b/src/test/java/com/apple/itunes/storekit/util/SignedDataCreator.java new file mode 100644 index 00000000..2738bb42 --- /dev/null +++ b/src/test/java/com/apple/itunes/storekit/util/SignedDataCreator.java @@ -0,0 +1,31 @@ +// Copyright (c) 2023 Apple Inc. Licensed under MIT License. + +package com.apple.itunes.storekit.util; + +import com.auth0.jwt.JWT; +import com.auth0.jwt.algorithms.Algorithm; +import com.auth0.jwt.interfaces.ECDSAKeyProvider; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.security.KeyFactory; +import java.security.KeyPairGenerator; +import java.security.NoSuchAlgorithmException; +import java.security.interfaces.ECPrivateKey; +import java.security.spec.InvalidKeySpecException; +import java.security.spec.PKCS8EncodedKeySpec; +import java.util.Base64; +import java.util.Map; + +public class SignedDataCreator { + + public static String createSignedDataFromJson(String path) throws IOException, NoSuchAlgorithmException { + String json = TestingUtility.readFile(path); + KeyPairGenerator ec = KeyPairGenerator.getInstance("EC"); + ec.initialize(256); + return JWT.create() + .withPayload(json) + .sign(Algorithm.ECDSA256((ECPrivateKey) ec.generateKeyPair().getPrivate())); + } +} diff --git a/src/test/java/com/apple/itunes/storekit/verification/SignedDataVerifierTest.java b/src/test/java/com/apple/itunes/storekit/verification/SignedDataVerifierTest.java index d51e3c51..9e060280 100644 --- a/src/test/java/com/apple/itunes/storekit/verification/SignedDataVerifierTest.java +++ b/src/test/java/com/apple/itunes/storekit/verification/SignedDataVerifierTest.java @@ -7,86 +7,74 @@ import com.apple.itunes.storekit.model.JWSTransactionDecodedPayload; import com.apple.itunes.storekit.model.NotificationTypeV2; import com.apple.itunes.storekit.model.ResponseBodyV2DecodedPayload; +import com.apple.itunes.storekit.util.TestingUtility; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; -import java.io.ByteArrayInputStream; -import java.util.Base64; -import java.util.Set; +import java.io.IOException; public class SignedDataVerifierTest { - private static final String ROOT_CA_BASE64_ENCODED = "MIIBgjCCASmgAwIBAgIJALUc5ALiH5pbMAoGCCqGSM49BAMDMDYxCzAJBgNVBAYTAlVTMRMwEQYDVQQIDApDYWxpZm9ybmlhMRIwEAYDVQQHDAlDdXBlcnRpbm8wHhcNMjMwMTA1MjEzMDIyWhcNMzMwMTAyMjEzMDIyWjA2MQswCQYDVQQGEwJVUzETMBEGA1UECAwKQ2FsaWZvcm5pYTESMBAGA1UEBwwJQ3VwZXJ0aW5vMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEc+/Bl+gospo6tf9Z7io5tdKdrlN1YdVnqEhEDXDShzdAJPQijamXIMHf8xWWTa1zgoYTxOKpbuJtDplz1XriTaMgMB4wDAYDVR0TBAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwCgYIKoZIzj0EAwMDRwAwRAIgemWQXnMAdTad2JDJWng9U4uBBL5mA7WI05H7oH7c6iQCIHiRqMjNfzUAyiu9h6rOU/K+iTR0I/3Y/NSWsXHX+acc"; - - private static final String TEST_NOTIFICATION = "eyJ4NWMiOlsiTUlJQm9EQ0NBVWFnQXdJQkFnSUJDekFLQmdncWhrak9QUVFEQWpCTk1Rc3dDUVlEVlFRR0V3SlZVekVUTUJFR0ExVUVDQXdLUTJGc2FXWnZjbTVwWVRFU01CQUdBMVVFQnd3SlEzVndaWEowYVc1dk1SVXdFd1lEVlFRS0RBeEpiblJsY20xbFpHbGhkR1V3SGhjTk1qTXdNVEEwTVRZek56TXhXaGNOTXpJeE1qTXhNVFl6TnpNeFdqQkZNUXN3Q1FZRFZRUUdFd0pWVXpFVE1CRUdBMVVFQ0F3S1EyRnNhV1p2Y201cFlURVNNQkFHQTFVRUJ3d0pRM1Z3WlhKMGFXNXZNUTB3Q3dZRFZRUUtEQVJNWldGbU1Ga3dFd1lIS29aSXpqMENBUVlJS29aSXpqMERBUWNEUWdBRTRyV0J4R21GYm5QSVBRSTB6c0JLekx4c2o4cEQydnFicjB5UElTVXgyV1F5eG1yTnFsOWZoSzhZRUV5WUZWNysrcDVpNFlVU1Ivbzl1UUlnQ1BJaHJLTWZNQjB3Q1FZRFZSMFRCQUl3QURBUUJnb3Foa2lHOTJOa0Jnc0JCQUlUQURBS0JnZ3Foa2pPUFFRREFnTklBREJGQWlFQWtpRVprb0ZNa2o0Z1huK1E5alhRWk1qWjJnbmpaM2FNOE5ZcmdmVFVpdlFDSURKWVowRmFMZTduU0lVMkxXTFRrNXRYVENjNEU4R0pTWWYvc1lSeEVGaWUiLCJNSUlCbHpDQ0FUMmdBd0lCQWdJQkJqQUtCZ2dxaGtqT1BRUURBakEyTVFzd0NRWURWUVFHRXdKVlV6RVRNQkVHQTFVRUNBd0tRMkZzYVdadmNtNXBZVEVTTUJBR0ExVUVCd3dKUTNWd1pYSjBhVzV2TUI0WERUSXpNREV3TkRFMk1qWXdNVm9YRFRNeU1USXpNVEUyTWpZd01Wb3dUVEVMTUFrR0ExVUVCaE1DVlZNeEV6QVJCZ05WQkFnTUNrTmhiR2xtYjNKdWFXRXhFakFRQmdOVkJBY01DVU4xY0dWeWRHbHViekVWTUJNR0ExVUVDZ3dNU1c1MFpYSnRaV1JwWVhSbE1Ga3dFd1lIS29aSXpqMENBUVlJS29aSXpqMERBUWNEUWdBRUZRM2xYMnNxTjlHSXdBaWlNUURRQy9reW5TZ1g0N1J3dmlET3RNWFh2eUtkUWU2Q1BzUzNqbzJ1UkR1RXFBeFdlT2lDcmpsRFdzeXo1d3dkVTBndGFxTWxNQ013RHdZRFZSMFRCQWd3QmdFQi93SUJBREFRQmdvcWhraUc5Mk5rQmdJQkJBSVRBREFLQmdncWhrak9QUVFEQWdOSUFEQkZBaUVBdm56TWNWMjY4Y1JiMS9GcHlWMUVoVDNXRnZPenJCVVdQNi9Ub1RoRmF2TUNJRmJhNXQ2WUt5MFIySkR0eHF0T2pKeTY2bDZWN2QvUHJBRE5wa21JUFcraSIsIk1JSUJYRENDQVFJQ0NRQ2ZqVFVHTERuUjlqQUtCZ2dxaGtqT1BRUURBekEyTVFzd0NRWURWUVFHRXdKVlV6RVRNQkVHQTFVRUNBd0tRMkZzYVdadmNtNXBZVEVTTUJBR0ExVUVCd3dKUTNWd1pYSjBhVzV2TUI0WERUSXpNREV3TkRFMk1qQXpNbG9YRFRNek1ERXdNVEUyTWpBek1sb3dOakVMTUFrR0ExVUVCaE1DVlZNeEV6QVJCZ05WQkFnTUNrTmhiR2xtYjNKdWFXRXhFakFRQmdOVkJBY01DVU4xY0dWeWRHbHViekJaTUJNR0J5cUdTTTQ5QWdFR0NDcUdTTTQ5QXdFSEEwSUFCSFB2d1pmb0tMS2FPclgvV2U0cU9iWFNuYTVUZFdIVlo2aElSQTF3MG9jM1FDVDBJbzJwbHlEQjMvTVZsazJ0YzRLR0U4VGlxVzdpYlE2WmM5VjY0azB3Q2dZSUtvWkl6ajBFQXdNRFNBQXdSUUloQU1USGhXdGJBUU4waFN4SVhjUDRDS3JEQ0gvZ3N4V3B4NmpUWkxUZVorRlBBaUIzNW53azVxMHpjSXBlZnZZSjBNVS95R0dIU1dlejBicTBwRFlVTy9ubUR3PT0iXSwidHlwIjoiSldUIiwiYWxnIjoiRVMyNTYifQ.eyJkYXRhIjp7ImFwcEFwcGxlSWQiOjEyMzQsImVudmlyb25tZW50IjoiU2FuZGJveCIsImJ1bmRsZUlkIjoiY29tLmV4YW1wbGUifSwibm90aWZpY2F0aW9uVVVJRCI6IjlhZDU2YmQyLTBiYzYtNDJlMC1hZjI0LWZkOTk2ZDg3YTFlNiIsInNpZ25lZERhdGUiOjE2ODEzMTQzMjQwMDAsIm5vdGlmaWNhdGlvblR5cGUiOiJURVNUIn0.VVXYwuNm2Y3XsOUva-BozqatRCsDuykA7xIe_CCRw6aIAAxJ1nb2sw871jfZ6dcgNhUuhoZ93hfbc1v_5zB7Og"; - private static final String MISSING_X5C_HEADER_CLAIM = "eyJ0eXAiOiJKV1QiLCJhbGciOiJFUzI1NiIsIng1Y3dyb25nIjpbIk1JSUJvRENDQVVhZ0F3SUJBZ0lCRERBS0JnZ3Foa2pPUFFRREF6QkZNUXN3Q1FZRFZRUUdFd0pWVXpFTE1Ba0dBMVVFQ0F3Q1EwRXhFakFRQmdOVkJBY01DVU4xY0dWeWRHbHViekVWTUJNR0ExVUVDZ3dNU1c1MFpYSnRaV1JwWVhSbE1CNFhEVEl6TURFd05USXhNekV6TkZvWERUTXpNREV3TVRJeE16RXpORm93UFRFTE1Ba0dBMVVFQmhNQ1ZWTXhDekFKQmdOVkJBZ01Ba05CTVJJd0VBWURWUVFIREFsRGRYQmxjblJwYm04eERUQUxCZ05WQkFvTUJFeGxZV1l3V1RBVEJnY3Foa2pPUFFJQkJnZ3Foa2pPUFFNQkJ3TkNBQVRpdFlIRWFZVnVjOGc5QWpUT3dFck12R3lQeWtQYStwdXZUSThoSlRIWlpETEdhczJxWDErRXJ4Z1FUSmdWWHY3Nm5tTGhoUkpIK2oyNUFpQUk4aUdzb3k4d0xUQUpCZ05WSFJNRUFqQUFNQTRHQTFVZER3RUIvd1FFQXdJSGdEQVFCZ29xaGtpRzkyTmtCZ3NCQkFJRkFEQUtCZ2dxaGtqT1BRUURBd05JQURCRkFpQlg0YytUMEZwNW5KNVFSQ2xSZnU1UFNCeVJ2TlB0dWFUc2swdlBCM1dBSUFJaEFOZ2FhdUFqL1lQOXMwQWtFaHlKaHhRTy82UTJ6b3VaK0gxQ0lPZWhuTXpRIiwiTUlJQm56Q0NBVVdnQXdJQkFnSUJDekFLQmdncWhrak9QUVFEQXpBMk1Rc3dDUVlEVlFRR0V3SlZVekVUTUJFR0ExVUVDQXdLUTJGc2FXWnZjbTVwWVRFU01CQUdBMVVFQnd3SlEzVndaWEowYVc1dk1CNFhEVEl6TURFd05USXhNekV3TlZvWERUTXpNREV3TVRJeE16RXdOVm93UlRFTE1Ba0dBMVVFQmhNQ1ZWTXhDekFKQmdOVkJBZ01Ba05CTVJJd0VBWURWUVFIREFsRGRYQmxjblJwYm04eEZUQVRCZ05WQkFvTURFbHVkR1Z5YldWa2FXRjBaVEJaTUJNR0J5cUdTTTQ5QWdFR0NDcUdTTTQ5QXdFSEEwSUFCQlVONVY5cktqZlJpTUFJb2pFQTBBdjVNcDBvRitPMGNMNGd6clRGMTc4aW5VSHVnajdFdDQ2TnJrUTdoS2dNVm5qb2dxNDVRMXJNcytjTUhWTklMV3FqTlRBek1BOEdBMVVkRXdRSU1BWUJBZjhDQVFBd0RnWURWUjBQQVFIL0JBUURBZ0VHTUJBR0NpcUdTSWIzWTJRR0FnRUVBZ1VBTUFvR0NDcUdTTTQ5QkFNREEwZ0FNRVVDSVFDbXNJS1lzNDF1bGxzc0hYNHJWdmVVVDBaN0lzNS9oTEsxbEZQVHR1bjNoQUlnYzIrMlJHNStnTmNGVmNzK1hKZUVsNEdaK29qbDNST09tbGwreWU3ZHluUT0iLCJNSUlCZ2pDQ0FTbWdBd0lCQWdJSkFMVWM1QUxpSDVwYk1Bb0dDQ3FHU000OUJBTURNRFl4Q3pBSkJnTlZCQVlUQWxWVE1STXdFUVlEVlFRSURBcERZV3hwWm05eWJtbGhNUkl3RUFZRFZRUUhEQWxEZFhCbGNuUnBibTh3SGhjTk1qTXdNVEExTWpFek1ESXlXaGNOTXpNd01UQXlNakV6TURJeVdqQTJNUXN3Q1FZRFZRUUdFd0pWVXpFVE1CRUdBMVVFQ0F3S1EyRnNhV1p2Y201cFlURVNNQkFHQTFVRUJ3d0pRM1Z3WlhKMGFXNXZNRmt3RXdZSEtvWkl6ajBDQVFZSUtvWkl6ajBEQVFjRFFnQUVjKy9CbCtnb3NwbzZ0ZjlaN2lvNXRkS2RybE4xWWRWbnFFaEVEWERTaHpkQUpQUWlqYW1YSU1IZjh4V1dUYTF6Z29ZVHhPS3BidUp0RHBsejFYcmlUYU1nTUI0d0RBWURWUjBUQkFVd0F3RUIvekFPQmdOVkhROEJBZjhFQkFNQ0FRWXdDZ1lJS29aSXpqMEVBd01EUndBd1JBSWdlbVdRWG5NQWRUYWQySkRKV25nOVU0dUJCTDVtQTdXSTA1SDdvSDdjNmlRQ0lIaVJxTWpOZnpVQXlpdTloNnJPVS9LK2lUUjBJLzNZL05TV3NYSFgrYWNjIl19.eyJkYXRhIjp7ImJ1bmRsZUlkIjoiY29tLmV4YW1wbGUifSwibm90aWZpY2F0aW9uVVVJRCI6IjlhZDU2YmQyLTBiYzYtNDJlMC1hZjI0LWZkOTk2ZDg3YTFlNiIsIm5vdGlmaWNhdGlvblR5cGUiOiJURVNUIn0.1TFhjDR4WwQJNgizVGYXz3WE3ajxTdH1wKLQQ71MtrkadSxxOo3yPo_6L9Z03unIU7YK-NRNzSIb5bh5WqTprQ"; - private static final String WRONG_BUNDLE_ID = "eyJ4NWMiOlsiTUlJQm9EQ0NBVWFnQXdJQkFnSUJEREFLQmdncWhrak9QUVFEQXpCRk1Rc3dDUVlEVlFRR0V3SlZVekVMTUFrR0ExVUVDQXdDUTBFeEVqQVFCZ05WQkFjTUNVTjFjR1Z5ZEdsdWJ6RVZNQk1HQTFVRUNnd01TVzUwWlhKdFpXUnBZWFJsTUI0WERUSXpNREV3TlRJeE16RXpORm9YRFRNek1ERXdNVEl4TXpFek5Gb3dQVEVMTUFrR0ExVUVCaE1DVlZNeEN6QUpCZ05WQkFnTUFrTkJNUkl3RUFZRFZRUUhEQWxEZFhCbGNuUnBibTh4RFRBTEJnTlZCQW9NQkV4bFlXWXdXVEFUQmdjcWhrak9QUUlCQmdncWhrak9QUU1CQndOQ0FBVGl0WUhFYVlWdWM4ZzlBalRPd0VyTXZHeVB5a1BhK3B1dlRJOGhKVEhaWkRMR2FzMnFYMStFcnhnUVRKZ1ZYdjc2bm1MaGhSSkgrajI1QWlBSThpR3NveTh3TFRBSkJnTlZIUk1FQWpBQU1BNEdBMVVkRHdFQi93UUVBd0lIZ0RBUUJnb3Foa2lHOTJOa0Jnc0JCQUlGQURBS0JnZ3Foa2pPUFFRREF3TklBREJGQWlCWDRjK1QwRnA1bko1UVJDbFJmdTVQU0J5UnZOUHR1YVRzazB2UEIzV0FJQUloQU5nYWF1QWovWVA5czBBa0VoeUpoeFFPLzZRMnpvdVorSDFDSU9laG5NelEiLCJNSUlCbnpDQ0FVV2dBd0lCQWdJQkN6QUtCZ2dxaGtqT1BRUURBekEyTVFzd0NRWURWUVFHRXdKVlV6RVRNQkVHQTFVRUNBd0tRMkZzYVdadmNtNXBZVEVTTUJBR0ExVUVCd3dKUTNWd1pYSjBhVzV2TUI0WERUSXpNREV3TlRJeE16RXdOVm9YRFRNek1ERXdNVEl4TXpFd05Wb3dSVEVMTUFrR0ExVUVCaE1DVlZNeEN6QUpCZ05WQkFnTUFrTkJNUkl3RUFZRFZRUUhEQWxEZFhCbGNuUnBibTh4RlRBVEJnTlZCQW9NREVsdWRHVnliV1ZrYVdGMFpUQlpNQk1HQnlxR1NNNDlBZ0VHQ0NxR1NNNDlBd0VIQTBJQUJCVU41VjlyS2pmUmlNQUlvakVBMEF2NU1wMG9GK08wY0w0Z3pyVEYxNzhpblVIdWdqN0V0NDZOcmtRN2hLZ01WbmpvZ3E0NVExck1zK2NNSFZOSUxXcWpOVEF6TUE4R0ExVWRFd1FJTUFZQkFmOENBUUF3RGdZRFZSMFBBUUgvQkFRREFnRUdNQkFHQ2lxR1NJYjNZMlFHQWdFRUFnVUFNQW9HQ0NxR1NNNDlCQU1EQTBnQU1FVUNJUUNtc0lLWXM0MXVsbHNzSFg0clZ2ZVVUMFo3SXM1L2hMSzFsRlBUdHVuM2hBSWdjMisyUkc1K2dOY0ZWY3MrWEplRWw0R1orb2psM1JPT21sbCt5ZTdkeW5RPSIsIk1JSUJnakNDQVNtZ0F3SUJBZ0lKQUxVYzVBTGlINXBiTUFvR0NDcUdTTTQ5QkFNRE1EWXhDekFKQmdOVkJBWVRBbFZUTVJNd0VRWURWUVFJREFwRFlXeHBabTl5Ym1saE1SSXdFQVlEVlFRSERBbERkWEJsY25ScGJtOHdIaGNOTWpNd01UQTFNakV6TURJeVdoY05Nek13TVRBeU1qRXpNREl5V2pBMk1Rc3dDUVlEVlFRR0V3SlZVekVUTUJFR0ExVUVDQXdLUTJGc2FXWnZjbTVwWVRFU01CQUdBMVVFQnd3SlEzVndaWEowYVc1dk1Ga3dFd1lIS29aSXpqMENBUVlJS29aSXpqMERBUWNEUWdBRWMrL0JsK2dvc3BvNnRmOVo3aW81dGRLZHJsTjFZZFZucUVoRURYRFNoemRBSlBRaWphbVhJTUhmOHhXV1RhMXpnb1lUeE9LcGJ1SnREcGx6MVhyaVRhTWdNQjR3REFZRFZSMFRCQVV3QXdFQi96QU9CZ05WSFE4QkFmOEVCQU1DQVFZd0NnWUlLb1pJemowRUF3TURSd0F3UkFJZ2VtV1FYbk1BZFRhZDJKREpXbmc5VTR1QkJMNW1BN1dJMDVIN29IN2M2aVFDSUhpUnFNak5melVBeWl1OWg2ck9VL0sraVRSMEkvM1kvTlNXc1hIWCthY2MiXSwidHlwIjoiSldUIiwiYWxnIjoiRVMyNTYifQ.eyJkYXRhIjp7ImJ1bmRsZUlkIjoiY29tLmV4YW1wbGUud3JvbmcifSwibm90aWZpY2F0aW9uVVVJRCI6IjlhZDU2YmQyLTBiYzYtNDJlMC1hZjI0LWZkOTk2ZDg3YTFlNiIsIm5vdGlmaWNhdGlvblR5cGUiOiJURVNUIn0.WWE31hTB_mcv2O_lf-xI-MNY3d8txc0MzpqFx4QnYDfFIxB95Lo2Fm3r46YSjLLdL7xCWdEJrJP5bHgRCejAGg"; - private static final String RENEWAL_INFO = "eyJ4NWMiOlsiTUlJQm9EQ0NBVWFnQXdJQkFnSUJEREFLQmdncWhrak9QUVFEQXpCRk1Rc3dDUVlEVlFRR0V3SlZVekVMTUFrR0ExVUVDQXdDUTBFeEVqQVFCZ05WQkFjTUNVTjFjR1Z5ZEdsdWJ6RVZNQk1HQTFVRUNnd01TVzUwWlhKdFpXUnBZWFJsTUI0WERUSXpNREV3TlRJeE16RXpORm9YRFRNek1ERXdNVEl4TXpFek5Gb3dQVEVMTUFrR0ExVUVCaE1DVlZNeEN6QUpCZ05WQkFnTUFrTkJNUkl3RUFZRFZRUUhEQWxEZFhCbGNuUnBibTh4RFRBTEJnTlZCQW9NQkV4bFlXWXdXVEFUQmdjcWhrak9QUUlCQmdncWhrak9QUU1CQndOQ0FBVGl0WUhFYVlWdWM4ZzlBalRPd0VyTXZHeVB5a1BhK3B1dlRJOGhKVEhaWkRMR2FzMnFYMStFcnhnUVRKZ1ZYdjc2bm1MaGhSSkgrajI1QWlBSThpR3NveTh3TFRBSkJnTlZIUk1FQWpBQU1BNEdBMVVkRHdFQi93UUVBd0lIZ0RBUUJnb3Foa2lHOTJOa0Jnc0JCQUlGQURBS0JnZ3Foa2pPUFFRREF3TklBREJGQWlCWDRjK1QwRnA1bko1UVJDbFJmdTVQU0J5UnZOUHR1YVRzazB2UEIzV0FJQUloQU5nYWF1QWovWVA5czBBa0VoeUpoeFFPLzZRMnpvdVorSDFDSU9laG5NelEiLCJNSUlCbnpDQ0FVV2dBd0lCQWdJQkN6QUtCZ2dxaGtqT1BRUURBekEyTVFzd0NRWURWUVFHRXdKVlV6RVRNQkVHQTFVRUNBd0tRMkZzYVdadmNtNXBZVEVTTUJBR0ExVUVCd3dKUTNWd1pYSjBhVzV2TUI0WERUSXpNREV3TlRJeE16RXdOVm9YRFRNek1ERXdNVEl4TXpFd05Wb3dSVEVMTUFrR0ExVUVCaE1DVlZNeEN6QUpCZ05WQkFnTUFrTkJNUkl3RUFZRFZRUUhEQWxEZFhCbGNuUnBibTh4RlRBVEJnTlZCQW9NREVsdWRHVnliV1ZrYVdGMFpUQlpNQk1HQnlxR1NNNDlBZ0VHQ0NxR1NNNDlBd0VIQTBJQUJCVU41VjlyS2pmUmlNQUlvakVBMEF2NU1wMG9GK08wY0w0Z3pyVEYxNzhpblVIdWdqN0V0NDZOcmtRN2hLZ01WbmpvZ3E0NVExck1zK2NNSFZOSUxXcWpOVEF6TUE4R0ExVWRFd1FJTUFZQkFmOENBUUF3RGdZRFZSMFBBUUgvQkFRREFnRUdNQkFHQ2lxR1NJYjNZMlFHQWdFRUFnVUFNQW9HQ0NxR1NNNDlCQU1EQTBnQU1FVUNJUUNtc0lLWXM0MXVsbHNzSFg0clZ2ZVVUMFo3SXM1L2hMSzFsRlBUdHVuM2hBSWdjMisyUkc1K2dOY0ZWY3MrWEplRWw0R1orb2psM1JPT21sbCt5ZTdkeW5RPSIsIk1JSUJnakNDQVNtZ0F3SUJBZ0lKQUxVYzVBTGlINXBiTUFvR0NDcUdTTTQ5QkFNRE1EWXhDekFKQmdOVkJBWVRBbFZUTVJNd0VRWURWUVFJREFwRFlXeHBabTl5Ym1saE1SSXdFQVlEVlFRSERBbERkWEJsY25ScGJtOHdIaGNOTWpNd01UQTFNakV6TURJeVdoY05Nek13TVRBeU1qRXpNREl5V2pBMk1Rc3dDUVlEVlFRR0V3SlZVekVUTUJFR0ExVUVDQXdLUTJGc2FXWnZjbTVwWVRFU01CQUdBMVVFQnd3SlEzVndaWEowYVc1dk1Ga3dFd1lIS29aSXpqMENBUVlJS29aSXpqMERBUWNEUWdBRWMrL0JsK2dvc3BvNnRmOVo3aW81dGRLZHJsTjFZZFZucUVoRURYRFNoemRBSlBRaWphbVhJTUhmOHhXV1RhMXpnb1lUeE9LcGJ1SnREcGx6MVhyaVRhTWdNQjR3REFZRFZSMFRCQVV3QXdFQi96QU9CZ05WSFE4QkFmOEVCQU1DQVFZd0NnWUlLb1pJemowRUF3TURSd0F3UkFJZ2VtV1FYbk1BZFRhZDJKREpXbmc5VTR1QkJMNW1BN1dJMDVIN29IN2M2aVFDSUhpUnFNak5melVBeWl1OWg2ck9VL0sraVRSMEkvM1kvTlNXc1hIWCthY2MiXSwidHlwIjoiSldUIiwiYWxnIjoiRVMyNTYifQ.eyJlbnZpcm9ubWVudCI6IlNhbmRib3giLCJzaWduZWREYXRlIjoxNjcyOTU2MTU0MDAwfQ.FbK2OL-t6l4892W7fzWyus_g9mIl2CzWLbVt7Kgcnt6zzVulF8bzovgpe0v_y490blROGixy8KDoe2dSU53-Xw"; - private static final String TRANSACTION_INFO = "eyJ4NWMiOlsiTUlJQm9EQ0NBVWFnQXdJQkFnSUJDekFLQmdncWhrak9QUVFEQWpCTk1Rc3dDUVlEVlFRR0V3SlZVekVUTUJFR0ExVUVDQXdLUTJGc2FXWnZjbTVwWVRFU01CQUdBMVVFQnd3SlEzVndaWEowYVc1dk1SVXdFd1lEVlFRS0RBeEpiblJsY20xbFpHbGhkR1V3SGhjTk1qTXdNVEEwTVRZek56TXhXaGNOTXpJeE1qTXhNVFl6TnpNeFdqQkZNUXN3Q1FZRFZRUUdFd0pWVXpFVE1CRUdBMVVFQ0F3S1EyRnNhV1p2Y201cFlURVNNQkFHQTFVRUJ3d0pRM1Z3WlhKMGFXNXZNUTB3Q3dZRFZRUUtEQVJNWldGbU1Ga3dFd1lIS29aSXpqMENBUVlJS29aSXpqMERBUWNEUWdBRTRyV0J4R21GYm5QSVBRSTB6c0JLekx4c2o4cEQydnFicjB5UElTVXgyV1F5eG1yTnFsOWZoSzhZRUV5WUZWNysrcDVpNFlVU1Ivbzl1UUlnQ1BJaHJLTWZNQjB3Q1FZRFZSMFRCQUl3QURBUUJnb3Foa2lHOTJOa0Jnc0JCQUlUQURBS0JnZ3Foa2pPUFFRREFnTklBREJGQWlFQWtpRVprb0ZNa2o0Z1huK1E5alhRWk1qWjJnbmpaM2FNOE5ZcmdmVFVpdlFDSURKWVowRmFMZTduU0lVMkxXTFRrNXRYVENjNEU4R0pTWWYvc1lSeEVGaWUiLCJNSUlCbHpDQ0FUMmdBd0lCQWdJQkJqQUtCZ2dxaGtqT1BRUURBakEyTVFzd0NRWURWUVFHRXdKVlV6RVRNQkVHQTFVRUNBd0tRMkZzYVdadmNtNXBZVEVTTUJBR0ExVUVCd3dKUTNWd1pYSjBhVzV2TUI0WERUSXpNREV3TkRFMk1qWXdNVm9YRFRNeU1USXpNVEUyTWpZd01Wb3dUVEVMTUFrR0ExVUVCaE1DVlZNeEV6QVJCZ05WQkFnTUNrTmhiR2xtYjNKdWFXRXhFakFRQmdOVkJBY01DVU4xY0dWeWRHbHViekVWTUJNR0ExVUVDZ3dNU1c1MFpYSnRaV1JwWVhSbE1Ga3dFd1lIS29aSXpqMENBUVlJS29aSXpqMERBUWNEUWdBRUZRM2xYMnNxTjlHSXdBaWlNUURRQy9reW5TZ1g0N1J3dmlET3RNWFh2eUtkUWU2Q1BzUzNqbzJ1UkR1RXFBeFdlT2lDcmpsRFdzeXo1d3dkVTBndGFxTWxNQ013RHdZRFZSMFRCQWd3QmdFQi93SUJBREFRQmdvcWhraUc5Mk5rQmdJQkJBSVRBREFLQmdncWhrak9QUVFEQWdOSUFEQkZBaUVBdm56TWNWMjY4Y1JiMS9GcHlWMUVoVDNXRnZPenJCVVdQNi9Ub1RoRmF2TUNJRmJhNXQ2WUt5MFIySkR0eHF0T2pKeTY2bDZWN2QvUHJBRE5wa21JUFcraSIsIk1JSUJYRENDQVFJQ0NRQ2ZqVFVHTERuUjlqQUtCZ2dxaGtqT1BRUURBekEyTVFzd0NRWURWUVFHRXdKVlV6RVRNQkVHQTFVRUNBd0tRMkZzYVdadmNtNXBZVEVTTUJBR0ExVUVCd3dKUTNWd1pYSjBhVzV2TUI0WERUSXpNREV3TkRFMk1qQXpNbG9YRFRNek1ERXdNVEUyTWpBek1sb3dOakVMTUFrR0ExVUVCaE1DVlZNeEV6QVJCZ05WQkFnTUNrTmhiR2xtYjNKdWFXRXhFakFRQmdOVkJBY01DVU4xY0dWeWRHbHViekJaTUJNR0J5cUdTTTQ5QWdFR0NDcUdTTTQ5QXdFSEEwSUFCSFB2d1pmb0tMS2FPclgvV2U0cU9iWFNuYTVUZFdIVlo2aElSQTF3MG9jM1FDVDBJbzJwbHlEQjMvTVZsazJ0YzRLR0U4VGlxVzdpYlE2WmM5VjY0azB3Q2dZSUtvWkl6ajBFQXdNRFNBQXdSUUloQU1USGhXdGJBUU4waFN4SVhjUDRDS3JEQ0gvZ3N4V3B4NmpUWkxUZVorRlBBaUIzNW53azVxMHpjSXBlZnZZSjBNVS95R0dIU1dlejBicTBwRFlVTy9ubUR3PT0iXSwidHlwIjoiSldUIiwiYWxnIjoiRVMyNTYifQ.eyJlbnZpcm9ubWVudCI6IlNhbmRib3giLCJidW5kbGVJZCI6ImNvbS5leGFtcGxlIiwic2lnbmVkRGF0ZSI6MTY3Mjk1NjE1NDAwMH0.PnHWpeIJZ8f2Q218NSGLo_aR0IBEJvC6PxmxKXh-qfYTrZccx2suGl223OSNAX78e4Ylf2yJCG2N-FfU-NIhZQ"; @Test - public void testAppStoreServerNotificationDecoding() throws VerificationException { - SignedDataVerifier verifier = getSignedPayloadVerifier(); - ResponseBodyV2DecodedPayload notification = verifier.verifyAndDecodeNotification(TEST_NOTIFICATION); + public void testAppStoreServerNotificationDecoding() throws VerificationException, IOException { + SignedDataVerifier verifier = TestingUtility.getSignedPayloadVerifier(Environment.SANDBOX, "com.example"); + ResponseBodyV2DecodedPayload notification = verifier.verifyAndDecodeNotification(TestingUtility.readFile("mock_signed_data/testNotification")); Assertions.assertEquals(NotificationTypeV2.TEST, notification.getNotificationType()); } @Test - public void testMissingX5CHeader() { - SignedDataVerifier verifier = getSignedPayloadVerifier(); - VerificationException exception = Assertions.assertThrows(VerificationException.class, () -> verifier.verifyAndDecodeNotification(MISSING_X5C_HEADER_CLAIM)); + public void testMissingX5CHeader() throws IOException { + SignedDataVerifier verifier = TestingUtility.getSignedPayloadVerifier(Environment.SANDBOX, "com.example"); + VerificationException exception = Assertions.assertThrows(VerificationException.class, () -> verifier.verifyAndDecodeNotification(TestingUtility.readFile("mock_signed_data/missingX5CHeaderClaim"))); Assertions.assertEquals(Status.VERIFICATION_FAILURE, exception.getStatus()); } @Test - public void testWrongBundleIdForServerNotification() { - SignedDataVerifier verifier = getSignedPayloadVerifier(); - VerificationException exception = Assertions.assertThrows(VerificationException.class, () -> verifier.verifyAndDecodeNotification(WRONG_BUNDLE_ID)); + public void testWrongBundleIdForServerNotification() throws IOException { + SignedDataVerifier verifier = TestingUtility.getSignedPayloadVerifier(Environment.SANDBOX, "com.example"); + VerificationException exception = Assertions.assertThrows(VerificationException.class, () -> verifier.verifyAndDecodeNotification(TestingUtility.readFile("mock_signed_data/wrongBundleId"))); Assertions.assertEquals(Status.INVALID_APP_IDENTIFIER, exception.getStatus()); } @Test - public void testWrongBundleIdForTransaction() { - SignedDataVerifier verifier = new SignedDataVerifier(Set.of(new ByteArrayInputStream(Base64.getDecoder().decode(ROOT_CA_BASE64_ENCODED))), "com.example.x", 1234L, Environment.SANDBOX, false); - VerificationException exception = Assertions.assertThrows(VerificationException.class, () -> verifier.verifyAndDecodeTransaction(TRANSACTION_INFO)); + public void testWrongBundleIdForTransaction() throws IOException { + SignedDataVerifier verifier = TestingUtility.getSignedPayloadVerifier(Environment.SANDBOX, "com.example.x"); + VerificationException exception = Assertions.assertThrows(VerificationException.class, () -> verifier.verifyAndDecodeTransaction(TestingUtility.readFile("mock_signed_data/transactionInfo"))); Assertions.assertEquals(Status.INVALID_APP_IDENTIFIER, exception.getStatus()); } @Test - public void testWrongEnvironmentForServerNotification() { - SignedDataVerifier verifier = new SignedDataVerifier(Set.of(new ByteArrayInputStream(Base64.getDecoder().decode(ROOT_CA_BASE64_ENCODED))), "com.example", 1234L, Environment.PRODUCTION, false); - VerificationException exception = Assertions.assertThrows(VerificationException.class, () -> verifier.verifyAndDecodeNotification(TEST_NOTIFICATION)); + public void testWrongEnvironmentForServerNotification() throws IOException { + SignedDataVerifier verifier = TestingUtility.getSignedPayloadVerifier(Environment.PRODUCTION, "com.example"); + VerificationException exception = Assertions.assertThrows(VerificationException.class, () -> verifier.verifyAndDecodeNotification(TestingUtility.readFile("mock_signed_data/testNotification"))); Assertions.assertEquals(Status.INVALID_ENVIRONMENT, exception.getStatus()); } @Test - public void testRenewalInfoDecoding() throws VerificationException { - SignedDataVerifier verifier = getSignedPayloadVerifier(); - JWSRenewalInfoDecodedPayload renewalInfo = verifier.verifyAndDecodeRenewalInfo(RENEWAL_INFO); + public void testRenewalInfoDecoding() throws VerificationException, IOException { + SignedDataVerifier verifier = TestingUtility.getSignedPayloadVerifier(Environment.SANDBOX, "com.example"); + JWSRenewalInfoDecodedPayload renewalInfo = verifier.verifyAndDecodeRenewalInfo(TestingUtility.readFile("mock_signed_data/renewalInfo")); Assertions.assertEquals(Environment.SANDBOX, renewalInfo.getEnvironment()); } @Test - public void testTransactionInfoDecoding() throws VerificationException { - SignedDataVerifier verifier = getSignedPayloadVerifier(); - JWSTransactionDecodedPayload transaction = verifier.verifyAndDecodeTransaction(TRANSACTION_INFO); + public void testTransactionInfoDecoding() throws VerificationException, IOException { + SignedDataVerifier verifier = TestingUtility.getSignedPayloadVerifier(Environment.SANDBOX, "com.example"); + JWSTransactionDecodedPayload transaction = verifier.verifyAndDecodeTransaction(TestingUtility.readFile("mock_signed_data/transactionInfo")); Assertions.assertEquals(Environment.SANDBOX, transaction.getEnvironment()); } @Test - public void testMalformedJWTWithTooManyParts() { - SignedDataVerifier verifier = getSignedPayloadVerifier(); + public void testMalformedJWTWithTooManyParts() throws IOException { + SignedDataVerifier verifier = TestingUtility.getSignedPayloadVerifier(); VerificationException exception = Assertions.assertThrows(VerificationException.class, () -> verifier.verifyAndDecodeNotification("a.b.c.d")); Assertions.assertEquals(Status.VERIFICATION_FAILURE, exception.getStatus()); } @Test - public void testMalformedJWTWithMalformedData() { - SignedDataVerifier verifier = getSignedPayloadVerifier(); + public void testMalformedJWTWithMalformedData() throws IOException { + SignedDataVerifier verifier = TestingUtility.getSignedPayloadVerifier(); VerificationException exception = Assertions.assertThrows(VerificationException.class, () -> verifier.verifyAndDecodeNotification("a.b.c")); Assertions.assertEquals(Status.VERIFICATION_FAILURE, exception.getStatus()); } - - private SignedDataVerifier getSignedPayloadVerifier() { - return new SignedDataVerifier(Set.of(new ByteArrayInputStream(Base64.getDecoder().decode(ROOT_CA_BASE64_ENCODED))), "com.example", 1234L, Environment.SANDBOX, false); - } } diff --git a/src/test/java/com/apple/itunes/storekit/verification/XcodeSignedDataVerifierTest.java b/src/test/java/com/apple/itunes/storekit/verification/XcodeSignedDataVerifierTest.java index 2c7536c6..ff42499d 100644 --- a/src/test/java/com/apple/itunes/storekit/verification/XcodeSignedDataVerifierTest.java +++ b/src/test/java/com/apple/itunes/storekit/verification/XcodeSignedDataVerifierTest.java @@ -39,14 +39,16 @@ public void testXcodeSignedAppTransaction() throws IOException, VerificationExce Assertions.assertEquals("cYUsXc53EbYc0pOeXG5d6/31LGHeVGf84sqSN0OrJi5u/j2H89WWKgS8N0hMsMlf", appTransaction.getDeviceVerification()); Assertions.assertEquals(UUID.fromString("48c8b92d-ce0d-4229-bedf-e61b4f9cfc92"), appTransaction.getDeviceVerificationNonce()); Assertions.assertNull(appTransaction.getPreorderDate()); + Assertions.assertEquals(Environment.XCODE, appTransaction.getReceiptType()); + Assertions.assertEquals("Xcode", appTransaction.getRawReceiptType()); } @Test public void testXcodeSignedTransaction() throws IOException, VerificationException { SignedDataVerifier verifier = TestingUtility.getSignedPayloadVerifier(Environment.XCODE, XCODE_BUNDLE_ID); - String encodedTransactino = TestingUtility.readFile("xcode/xcode-signed-transaction"); + String encodedTransaction = TestingUtility.readFile("xcode/xcode-signed-transaction"); - JWSTransactionDecodedPayload transaction = verifier.verifyAndDecodeTransaction(encodedTransactino); + JWSTransactionDecodedPayload transaction = verifier.verifyAndDecodeTransaction(encodedTransaction); Assertions.assertEquals("0", transaction.getOriginalTransactionId()); Assertions.assertEquals("0", transaction.getTransactionId()); @@ -59,18 +61,23 @@ public void testXcodeSignedTransaction() throws IOException, VerificationExcepti Assertions.assertEquals(1700358336049L, transaction.getExpiresDate()); Assertions.assertEquals(1, transaction.getQuantity()); Assertions.assertEquals(Type.AUTO_RENEWABLE_SUBSCRIPTION, transaction.getType()); + Assertions.assertEquals("Auto-Renewable Subscription", transaction.getRawType()); Assertions.assertNull(transaction.getAppAccountToken()); Assertions.assertEquals(InAppOwnershipType.PURCHASED, transaction.getInAppOwnershipType()); + Assertions.assertEquals("PURCHASED", transaction.getRawInAppOwnershipType()); Assertions.assertEquals(1697679936056L, transaction.getSignedDate()); Assertions.assertNull(transaction.getRevocationReason()); Assertions.assertNull(transaction.getRevocationDate()); Assertions.assertFalse(transaction.getIsUpgraded()); Assertions.assertEquals(OfferType.INTRODUCTORY_OFFER, transaction.getOfferType()); + Assertions.assertEquals(1, transaction.getRawOfferType()); Assertions.assertNull(transaction.getOfferIdentifier()); Assertions.assertEquals(Environment.XCODE, transaction.getEnvironment()); + Assertions.assertEquals("Xcode", transaction.getRawEnvironment()); Assertions.assertEquals("USA", transaction.getStorefront()); Assertions.assertEquals("143441", transaction.getStorefrontId()); Assertions.assertEquals(TransactionReason.PURCHASE, transaction.getTransactionReason()); + Assertions.assertEquals("PURCHASE", transaction.getRawTransactionReason()); } @Test @@ -86,6 +93,7 @@ public void testXcodeSignedRenewalInfo() throws IOException, VerificationExcepti Assertions.assertEquals("pass.premium", renewalInfo.getAutoRenewProductId()); Assertions.assertEquals("pass.premium", renewalInfo.getProductId()); Assertions.assertEquals(AutoRenewStatus.ON, renewalInfo.getAutoRenewStatus()); + Assertions.assertEquals(1, renewalInfo.getRawAutoRenewStatus()); Assertions.assertNull(renewalInfo.getIsInBillingRetryPeriod()); Assertions.assertNull(renewalInfo.getPriceIncreaseStatus()); Assertions.assertNull(renewalInfo.getGracePeriodExpiresDate()); @@ -93,6 +101,7 @@ public void testXcodeSignedRenewalInfo() throws IOException, VerificationExcepti Assertions.assertNull(renewalInfo.getOfferIdentifier()); Assertions.assertEquals(1697679936711L, renewalInfo.getSignedDate()); Assertions.assertEquals(Environment.XCODE, renewalInfo.getEnvironment()); + Assertions.assertEquals("Xcode", renewalInfo.getRawEnvironment()); Assertions.assertEquals(1697679936049L, renewalInfo.getRecentSubscriptionStartDate()); Assertions.assertEquals(1700358336049L, renewalInfo.getRenewalDate()); } diff --git a/src/test/resources/testCA.key b/src/test/resources/certs/testCA.key similarity index 100% rename from src/test/resources/testCA.key rename to src/test/resources/certs/testCA.key diff --git a/src/test/resources/testCA.pem b/src/test/resources/certs/testCA.pem similarity index 100% rename from src/test/resources/testCA.pem rename to src/test/resources/certs/testCA.pem diff --git a/src/test/resources/testIntermediate.key b/src/test/resources/certs/testIntermediate.key similarity index 100% rename from src/test/resources/testIntermediate.key rename to src/test/resources/certs/testIntermediate.key diff --git a/src/test/resources/testIntermediate.pem b/src/test/resources/certs/testIntermediate.pem similarity index 100% rename from src/test/resources/testIntermediate.pem rename to src/test/resources/certs/testIntermediate.pem diff --git a/src/test/resources/testInvalidIntermediateLeaf.pem b/src/test/resources/certs/testInvalidIntermediateLeaf.pem similarity index 100% rename from src/test/resources/testInvalidIntermediateLeaf.pem rename to src/test/resources/certs/testInvalidIntermediateLeaf.pem diff --git a/src/test/resources/testInvalidOIDIntermediate.pem b/src/test/resources/certs/testInvalidOIDIntermediate.pem similarity index 100% rename from src/test/resources/testInvalidOIDIntermediate.pem rename to src/test/resources/certs/testInvalidOIDIntermediate.pem diff --git a/src/test/resources/testInvalidOIDLeaf.pem b/src/test/resources/certs/testInvalidOIDLeaf.pem similarity index 100% rename from src/test/resources/testInvalidOIDLeaf.pem rename to src/test/resources/certs/testInvalidOIDLeaf.pem diff --git a/src/test/resources/testLeaf.key b/src/test/resources/certs/testLeaf.key similarity index 100% rename from src/test/resources/testLeaf.key rename to src/test/resources/certs/testLeaf.key diff --git a/src/test/resources/testLeaf.pem b/src/test/resources/certs/testLeaf.pem similarity index 100% rename from src/test/resources/testLeaf.pem rename to src/test/resources/certs/testLeaf.pem diff --git a/src/test/resources/testSigningKey.p8 b/src/test/resources/certs/testSigningKey.p8 similarity index 100% rename from src/test/resources/testSigningKey.p8 rename to src/test/resources/certs/testSigningKey.p8 diff --git a/src/test/resources/mock_signed_data/legacyTransaction b/src/test/resources/mock_signed_data/legacyTransaction new file mode 100644 index 00000000..534391da --- /dev/null +++ b/src/test/resources/mock_signed_data/legacyTransaction @@ -0,0 +1 @@ +ewoicHVyY2hhc2UtaW5mbyIgPSAiZXdvaWRISmhibk5oWTNScGIyNHRhV1FpSUQwZ0lqTXpPVGt6TXprNUlqc0tmUW89IjsKfQo= \ No newline at end of file diff --git a/src/test/resources/mock_signed_data/missingX5CHeaderClaim b/src/test/resources/mock_signed_data/missingX5CHeaderClaim new file mode 100644 index 00000000..b4112b4a --- /dev/null +++ b/src/test/resources/mock_signed_data/missingX5CHeaderClaim @@ -0,0 +1 @@ +eyJ0eXAiOiJKV1QiLCJhbGciOiJFUzI1NiIsIng1Y3dyb25nIjpbIk1JSUJvRENDQVVhZ0F3SUJBZ0lCRERBS0JnZ3Foa2pPUFFRREF6QkZNUXN3Q1FZRFZRUUdFd0pWVXpFTE1Ba0dBMVVFQ0F3Q1EwRXhFakFRQmdOVkJBY01DVU4xY0dWeWRHbHViekVWTUJNR0ExVUVDZ3dNU1c1MFpYSnRaV1JwWVhSbE1CNFhEVEl6TURFd05USXhNekV6TkZvWERUTXpNREV3TVRJeE16RXpORm93UFRFTE1Ba0dBMVVFQmhNQ1ZWTXhDekFKQmdOVkJBZ01Ba05CTVJJd0VBWURWUVFIREFsRGRYQmxjblJwYm04eERUQUxCZ05WQkFvTUJFeGxZV1l3V1RBVEJnY3Foa2pPUFFJQkJnZ3Foa2pPUFFNQkJ3TkNBQVRpdFlIRWFZVnVjOGc5QWpUT3dFck12R3lQeWtQYStwdXZUSThoSlRIWlpETEdhczJxWDErRXJ4Z1FUSmdWWHY3Nm5tTGhoUkpIK2oyNUFpQUk4aUdzb3k4d0xUQUpCZ05WSFJNRUFqQUFNQTRHQTFVZER3RUIvd1FFQXdJSGdEQVFCZ29xaGtpRzkyTmtCZ3NCQkFJRkFEQUtCZ2dxaGtqT1BRUURBd05JQURCRkFpQlg0YytUMEZwNW5KNVFSQ2xSZnU1UFNCeVJ2TlB0dWFUc2swdlBCM1dBSUFJaEFOZ2FhdUFqL1lQOXMwQWtFaHlKaHhRTy82UTJ6b3VaK0gxQ0lPZWhuTXpRIiwiTUlJQm56Q0NBVVdnQXdJQkFnSUJDekFLQmdncWhrak9QUVFEQXpBMk1Rc3dDUVlEVlFRR0V3SlZVekVUTUJFR0ExVUVDQXdLUTJGc2FXWnZjbTVwWVRFU01CQUdBMVVFQnd3SlEzVndaWEowYVc1dk1CNFhEVEl6TURFd05USXhNekV3TlZvWERUTXpNREV3TVRJeE16RXdOVm93UlRFTE1Ba0dBMVVFQmhNQ1ZWTXhDekFKQmdOVkJBZ01Ba05CTVJJd0VBWURWUVFIREFsRGRYQmxjblJwYm04eEZUQVRCZ05WQkFvTURFbHVkR1Z5YldWa2FXRjBaVEJaTUJNR0J5cUdTTTQ5QWdFR0NDcUdTTTQ5QXdFSEEwSUFCQlVONVY5cktqZlJpTUFJb2pFQTBBdjVNcDBvRitPMGNMNGd6clRGMTc4aW5VSHVnajdFdDQ2TnJrUTdoS2dNVm5qb2dxNDVRMXJNcytjTUhWTklMV3FqTlRBek1BOEdBMVVkRXdRSU1BWUJBZjhDQVFBd0RnWURWUjBQQVFIL0JBUURBZ0VHTUJBR0NpcUdTSWIzWTJRR0FnRUVBZ1VBTUFvR0NDcUdTTTQ5QkFNREEwZ0FNRVVDSVFDbXNJS1lzNDF1bGxzc0hYNHJWdmVVVDBaN0lzNS9oTEsxbEZQVHR1bjNoQUlnYzIrMlJHNStnTmNGVmNzK1hKZUVsNEdaK29qbDNST09tbGwreWU3ZHluUT0iLCJNSUlCZ2pDQ0FTbWdBd0lCQWdJSkFMVWM1QUxpSDVwYk1Bb0dDQ3FHU000OUJBTURNRFl4Q3pBSkJnTlZCQVlUQWxWVE1STXdFUVlEVlFRSURBcERZV3hwWm05eWJtbGhNUkl3RUFZRFZRUUhEQWxEZFhCbGNuUnBibTh3SGhjTk1qTXdNVEExTWpFek1ESXlXaGNOTXpNd01UQXlNakV6TURJeVdqQTJNUXN3Q1FZRFZRUUdFd0pWVXpFVE1CRUdBMVVFQ0F3S1EyRnNhV1p2Y201cFlURVNNQkFHQTFVRUJ3d0pRM1Z3WlhKMGFXNXZNRmt3RXdZSEtvWkl6ajBDQVFZSUtvWkl6ajBEQVFjRFFnQUVjKy9CbCtnb3NwbzZ0ZjlaN2lvNXRkS2RybE4xWWRWbnFFaEVEWERTaHpkQUpQUWlqYW1YSU1IZjh4V1dUYTF6Z29ZVHhPS3BidUp0RHBsejFYcmlUYU1nTUI0d0RBWURWUjBUQkFVd0F3RUIvekFPQmdOVkhROEJBZjhFQkFNQ0FRWXdDZ1lJS29aSXpqMEVBd01EUndBd1JBSWdlbVdRWG5NQWRUYWQySkRKV25nOVU0dUJCTDVtQTdXSTA1SDdvSDdjNmlRQ0lIaVJxTWpOZnpVQXlpdTloNnJPVS9LK2lUUjBJLzNZL05TV3NYSFgrYWNjIl19.eyJkYXRhIjp7ImJ1bmRsZUlkIjoiY29tLmV4YW1wbGUifSwibm90aWZpY2F0aW9uVVVJRCI6IjlhZDU2YmQyLTBiYzYtNDJlMC1hZjI0LWZkOTk2ZDg3YTFlNiIsIm5vdGlmaWNhdGlvblR5cGUiOiJURVNUIn0.1TFhjDR4WwQJNgizVGYXz3WE3ajxTdH1wKLQQ71MtrkadSxxOo3yPo_6L9Z03unIU7YK-NRNzSIb5bh5WqTprQ \ No newline at end of file diff --git a/src/test/resources/mock_signed_data/renewalInfo b/src/test/resources/mock_signed_data/renewalInfo new file mode 100644 index 00000000..d14a20dc --- /dev/null +++ b/src/test/resources/mock_signed_data/renewalInfo @@ -0,0 +1 @@ +eyJ4NWMiOlsiTUlJQm9EQ0NBVWFnQXdJQkFnSUJEREFLQmdncWhrak9QUVFEQXpCRk1Rc3dDUVlEVlFRR0V3SlZVekVMTUFrR0ExVUVDQXdDUTBFeEVqQVFCZ05WQkFjTUNVTjFjR1Z5ZEdsdWJ6RVZNQk1HQTFVRUNnd01TVzUwWlhKdFpXUnBZWFJsTUI0WERUSXpNREV3TlRJeE16RXpORm9YRFRNek1ERXdNVEl4TXpFek5Gb3dQVEVMTUFrR0ExVUVCaE1DVlZNeEN6QUpCZ05WQkFnTUFrTkJNUkl3RUFZRFZRUUhEQWxEZFhCbGNuUnBibTh4RFRBTEJnTlZCQW9NQkV4bFlXWXdXVEFUQmdjcWhrak9QUUlCQmdncWhrak9QUU1CQndOQ0FBVGl0WUhFYVlWdWM4ZzlBalRPd0VyTXZHeVB5a1BhK3B1dlRJOGhKVEhaWkRMR2FzMnFYMStFcnhnUVRKZ1ZYdjc2bm1MaGhSSkgrajI1QWlBSThpR3NveTh3TFRBSkJnTlZIUk1FQWpBQU1BNEdBMVVkRHdFQi93UUVBd0lIZ0RBUUJnb3Foa2lHOTJOa0Jnc0JCQUlGQURBS0JnZ3Foa2pPUFFRREF3TklBREJGQWlCWDRjK1QwRnA1bko1UVJDbFJmdTVQU0J5UnZOUHR1YVRzazB2UEIzV0FJQUloQU5nYWF1QWovWVA5czBBa0VoeUpoeFFPLzZRMnpvdVorSDFDSU9laG5NelEiLCJNSUlCbnpDQ0FVV2dBd0lCQWdJQkN6QUtCZ2dxaGtqT1BRUURBekEyTVFzd0NRWURWUVFHRXdKVlV6RVRNQkVHQTFVRUNBd0tRMkZzYVdadmNtNXBZVEVTTUJBR0ExVUVCd3dKUTNWd1pYSjBhVzV2TUI0WERUSXpNREV3TlRJeE16RXdOVm9YRFRNek1ERXdNVEl4TXpFd05Wb3dSVEVMTUFrR0ExVUVCaE1DVlZNeEN6QUpCZ05WQkFnTUFrTkJNUkl3RUFZRFZRUUhEQWxEZFhCbGNuUnBibTh4RlRBVEJnTlZCQW9NREVsdWRHVnliV1ZrYVdGMFpUQlpNQk1HQnlxR1NNNDlBZ0VHQ0NxR1NNNDlBd0VIQTBJQUJCVU41VjlyS2pmUmlNQUlvakVBMEF2NU1wMG9GK08wY0w0Z3pyVEYxNzhpblVIdWdqN0V0NDZOcmtRN2hLZ01WbmpvZ3E0NVExck1zK2NNSFZOSUxXcWpOVEF6TUE4R0ExVWRFd1FJTUFZQkFmOENBUUF3RGdZRFZSMFBBUUgvQkFRREFnRUdNQkFHQ2lxR1NJYjNZMlFHQWdFRUFnVUFNQW9HQ0NxR1NNNDlCQU1EQTBnQU1FVUNJUUNtc0lLWXM0MXVsbHNzSFg0clZ2ZVVUMFo3SXM1L2hMSzFsRlBUdHVuM2hBSWdjMisyUkc1K2dOY0ZWY3MrWEplRWw0R1orb2psM1JPT21sbCt5ZTdkeW5RPSIsIk1JSUJnakNDQVNtZ0F3SUJBZ0lKQUxVYzVBTGlINXBiTUFvR0NDcUdTTTQ5QkFNRE1EWXhDekFKQmdOVkJBWVRBbFZUTVJNd0VRWURWUVFJREFwRFlXeHBabTl5Ym1saE1SSXdFQVlEVlFRSERBbERkWEJsY25ScGJtOHdIaGNOTWpNd01UQTFNakV6TURJeVdoY05Nek13TVRBeU1qRXpNREl5V2pBMk1Rc3dDUVlEVlFRR0V3SlZVekVUTUJFR0ExVUVDQXdLUTJGc2FXWnZjbTVwWVRFU01CQUdBMVVFQnd3SlEzVndaWEowYVc1dk1Ga3dFd1lIS29aSXpqMENBUVlJS29aSXpqMERBUWNEUWdBRWMrL0JsK2dvc3BvNnRmOVo3aW81dGRLZHJsTjFZZFZucUVoRURYRFNoemRBSlBRaWphbVhJTUhmOHhXV1RhMXpnb1lUeE9LcGJ1SnREcGx6MVhyaVRhTWdNQjR3REFZRFZSMFRCQVV3QXdFQi96QU9CZ05WSFE4QkFmOEVCQU1DQVFZd0NnWUlLb1pJemowRUF3TURSd0F3UkFJZ2VtV1FYbk1BZFRhZDJKREpXbmc5VTR1QkJMNW1BN1dJMDVIN29IN2M2aVFDSUhpUnFNak5melVBeWl1OWg2ck9VL0sraVRSMEkvM1kvTlNXc1hIWCthY2MiXSwidHlwIjoiSldUIiwiYWxnIjoiRVMyNTYifQ.eyJlbnZpcm9ubWVudCI6IlNhbmRib3giLCJzaWduZWREYXRlIjoxNjcyOTU2MTU0MDAwfQ.FbK2OL-t6l4892W7fzWyus_g9mIl2CzWLbVt7Kgcnt6zzVulF8bzovgpe0v_y490blROGixy8KDoe2dSU53-Xw \ No newline at end of file diff --git a/src/test/resources/mock_signed_data/testNotification b/src/test/resources/mock_signed_data/testNotification new file mode 100644 index 00000000..7bb78cf1 --- /dev/null +++ b/src/test/resources/mock_signed_data/testNotification @@ -0,0 +1 @@ +eyJ4NWMiOlsiTUlJQm9EQ0NBVWFnQXdJQkFnSUJDekFLQmdncWhrak9QUVFEQWpCTk1Rc3dDUVlEVlFRR0V3SlZVekVUTUJFR0ExVUVDQXdLUTJGc2FXWnZjbTVwWVRFU01CQUdBMVVFQnd3SlEzVndaWEowYVc1dk1SVXdFd1lEVlFRS0RBeEpiblJsY20xbFpHbGhkR1V3SGhjTk1qTXdNVEEwTVRZek56TXhXaGNOTXpJeE1qTXhNVFl6TnpNeFdqQkZNUXN3Q1FZRFZRUUdFd0pWVXpFVE1CRUdBMVVFQ0F3S1EyRnNhV1p2Y201cFlURVNNQkFHQTFVRUJ3d0pRM1Z3WlhKMGFXNXZNUTB3Q3dZRFZRUUtEQVJNWldGbU1Ga3dFd1lIS29aSXpqMENBUVlJS29aSXpqMERBUWNEUWdBRTRyV0J4R21GYm5QSVBRSTB6c0JLekx4c2o4cEQydnFicjB5UElTVXgyV1F5eG1yTnFsOWZoSzhZRUV5WUZWNysrcDVpNFlVU1Ivbzl1UUlnQ1BJaHJLTWZNQjB3Q1FZRFZSMFRCQUl3QURBUUJnb3Foa2lHOTJOa0Jnc0JCQUlUQURBS0JnZ3Foa2pPUFFRREFnTklBREJGQWlFQWtpRVprb0ZNa2o0Z1huK1E5alhRWk1qWjJnbmpaM2FNOE5ZcmdmVFVpdlFDSURKWVowRmFMZTduU0lVMkxXTFRrNXRYVENjNEU4R0pTWWYvc1lSeEVGaWUiLCJNSUlCbHpDQ0FUMmdBd0lCQWdJQkJqQUtCZ2dxaGtqT1BRUURBakEyTVFzd0NRWURWUVFHRXdKVlV6RVRNQkVHQTFVRUNBd0tRMkZzYVdadmNtNXBZVEVTTUJBR0ExVUVCd3dKUTNWd1pYSjBhVzV2TUI0WERUSXpNREV3TkRFMk1qWXdNVm9YRFRNeU1USXpNVEUyTWpZd01Wb3dUVEVMTUFrR0ExVUVCaE1DVlZNeEV6QVJCZ05WQkFnTUNrTmhiR2xtYjNKdWFXRXhFakFRQmdOVkJBY01DVU4xY0dWeWRHbHViekVWTUJNR0ExVUVDZ3dNU1c1MFpYSnRaV1JwWVhSbE1Ga3dFd1lIS29aSXpqMENBUVlJS29aSXpqMERBUWNEUWdBRUZRM2xYMnNxTjlHSXdBaWlNUURRQy9reW5TZ1g0N1J3dmlET3RNWFh2eUtkUWU2Q1BzUzNqbzJ1UkR1RXFBeFdlT2lDcmpsRFdzeXo1d3dkVTBndGFxTWxNQ013RHdZRFZSMFRCQWd3QmdFQi93SUJBREFRQmdvcWhraUc5Mk5rQmdJQkJBSVRBREFLQmdncWhrak9QUVFEQWdOSUFEQkZBaUVBdm56TWNWMjY4Y1JiMS9GcHlWMUVoVDNXRnZPenJCVVdQNi9Ub1RoRmF2TUNJRmJhNXQ2WUt5MFIySkR0eHF0T2pKeTY2bDZWN2QvUHJBRE5wa21JUFcraSIsIk1JSUJYRENDQVFJQ0NRQ2ZqVFVHTERuUjlqQUtCZ2dxaGtqT1BRUURBekEyTVFzd0NRWURWUVFHRXdKVlV6RVRNQkVHQTFVRUNBd0tRMkZzYVdadmNtNXBZVEVTTUJBR0ExVUVCd3dKUTNWd1pYSjBhVzV2TUI0WERUSXpNREV3TkRFMk1qQXpNbG9YRFRNek1ERXdNVEUyTWpBek1sb3dOakVMTUFrR0ExVUVCaE1DVlZNeEV6QVJCZ05WQkFnTUNrTmhiR2xtYjNKdWFXRXhFakFRQmdOVkJBY01DVU4xY0dWeWRHbHViekJaTUJNR0J5cUdTTTQ5QWdFR0NDcUdTTTQ5QXdFSEEwSUFCSFB2d1pmb0tMS2FPclgvV2U0cU9iWFNuYTVUZFdIVlo2aElSQTF3MG9jM1FDVDBJbzJwbHlEQjMvTVZsazJ0YzRLR0U4VGlxVzdpYlE2WmM5VjY0azB3Q2dZSUtvWkl6ajBFQXdNRFNBQXdSUUloQU1USGhXdGJBUU4waFN4SVhjUDRDS3JEQ0gvZ3N4V3B4NmpUWkxUZVorRlBBaUIzNW53azVxMHpjSXBlZnZZSjBNVS95R0dIU1dlejBicTBwRFlVTy9ubUR3PT0iXSwidHlwIjoiSldUIiwiYWxnIjoiRVMyNTYifQ.eyJkYXRhIjp7ImFwcEFwcGxlSWQiOjEyMzQsImVudmlyb25tZW50IjoiU2FuZGJveCIsImJ1bmRsZUlkIjoiY29tLmV4YW1wbGUifSwibm90aWZpY2F0aW9uVVVJRCI6IjlhZDU2YmQyLTBiYzYtNDJlMC1hZjI0LWZkOTk2ZDg3YTFlNiIsInNpZ25lZERhdGUiOjE2ODEzMTQzMjQwMDAsIm5vdGlmaWNhdGlvblR5cGUiOiJURVNUIn0.VVXYwuNm2Y3XsOUva-BozqatRCsDuykA7xIe_CCRw6aIAAxJ1nb2sw871jfZ6dcgNhUuhoZ93hfbc1v_5zB7Og \ No newline at end of file diff --git a/src/test/resources/mock_signed_data/transactionInfo b/src/test/resources/mock_signed_data/transactionInfo new file mode 100644 index 00000000..3ddf0b02 --- /dev/null +++ b/src/test/resources/mock_signed_data/transactionInfo @@ -0,0 +1 @@ +eyJ4NWMiOlsiTUlJQm9EQ0NBVWFnQXdJQkFnSUJDekFLQmdncWhrak9QUVFEQWpCTk1Rc3dDUVlEVlFRR0V3SlZVekVUTUJFR0ExVUVDQXdLUTJGc2FXWnZjbTVwWVRFU01CQUdBMVVFQnd3SlEzVndaWEowYVc1dk1SVXdFd1lEVlFRS0RBeEpiblJsY20xbFpHbGhkR1V3SGhjTk1qTXdNVEEwTVRZek56TXhXaGNOTXpJeE1qTXhNVFl6TnpNeFdqQkZNUXN3Q1FZRFZRUUdFd0pWVXpFVE1CRUdBMVVFQ0F3S1EyRnNhV1p2Y201cFlURVNNQkFHQTFVRUJ3d0pRM1Z3WlhKMGFXNXZNUTB3Q3dZRFZRUUtEQVJNWldGbU1Ga3dFd1lIS29aSXpqMENBUVlJS29aSXpqMERBUWNEUWdBRTRyV0J4R21GYm5QSVBRSTB6c0JLekx4c2o4cEQydnFicjB5UElTVXgyV1F5eG1yTnFsOWZoSzhZRUV5WUZWNysrcDVpNFlVU1Ivbzl1UUlnQ1BJaHJLTWZNQjB3Q1FZRFZSMFRCQUl3QURBUUJnb3Foa2lHOTJOa0Jnc0JCQUlUQURBS0JnZ3Foa2pPUFFRREFnTklBREJGQWlFQWtpRVprb0ZNa2o0Z1huK1E5alhRWk1qWjJnbmpaM2FNOE5ZcmdmVFVpdlFDSURKWVowRmFMZTduU0lVMkxXTFRrNXRYVENjNEU4R0pTWWYvc1lSeEVGaWUiLCJNSUlCbHpDQ0FUMmdBd0lCQWdJQkJqQUtCZ2dxaGtqT1BRUURBakEyTVFzd0NRWURWUVFHRXdKVlV6RVRNQkVHQTFVRUNBd0tRMkZzYVdadmNtNXBZVEVTTUJBR0ExVUVCd3dKUTNWd1pYSjBhVzV2TUI0WERUSXpNREV3TkRFMk1qWXdNVm9YRFRNeU1USXpNVEUyTWpZd01Wb3dUVEVMTUFrR0ExVUVCaE1DVlZNeEV6QVJCZ05WQkFnTUNrTmhiR2xtYjNKdWFXRXhFakFRQmdOVkJBY01DVU4xY0dWeWRHbHViekVWTUJNR0ExVUVDZ3dNU1c1MFpYSnRaV1JwWVhSbE1Ga3dFd1lIS29aSXpqMENBUVlJS29aSXpqMERBUWNEUWdBRUZRM2xYMnNxTjlHSXdBaWlNUURRQy9reW5TZ1g0N1J3dmlET3RNWFh2eUtkUWU2Q1BzUzNqbzJ1UkR1RXFBeFdlT2lDcmpsRFdzeXo1d3dkVTBndGFxTWxNQ013RHdZRFZSMFRCQWd3QmdFQi93SUJBREFRQmdvcWhraUc5Mk5rQmdJQkJBSVRBREFLQmdncWhrak9QUVFEQWdOSUFEQkZBaUVBdm56TWNWMjY4Y1JiMS9GcHlWMUVoVDNXRnZPenJCVVdQNi9Ub1RoRmF2TUNJRmJhNXQ2WUt5MFIySkR0eHF0T2pKeTY2bDZWN2QvUHJBRE5wa21JUFcraSIsIk1JSUJYRENDQVFJQ0NRQ2ZqVFVHTERuUjlqQUtCZ2dxaGtqT1BRUURBekEyTVFzd0NRWURWUVFHRXdKVlV6RVRNQkVHQTFVRUNBd0tRMkZzYVdadmNtNXBZVEVTTUJBR0ExVUVCd3dKUTNWd1pYSjBhVzV2TUI0WERUSXpNREV3TkRFMk1qQXpNbG9YRFRNek1ERXdNVEUyTWpBek1sb3dOakVMTUFrR0ExVUVCaE1DVlZNeEV6QVJCZ05WQkFnTUNrTmhiR2xtYjNKdWFXRXhFakFRQmdOVkJBY01DVU4xY0dWeWRHbHViekJaTUJNR0J5cUdTTTQ5QWdFR0NDcUdTTTQ5QXdFSEEwSUFCSFB2d1pmb0tMS2FPclgvV2U0cU9iWFNuYTVUZFdIVlo2aElSQTF3MG9jM1FDVDBJbzJwbHlEQjMvTVZsazJ0YzRLR0U4VGlxVzdpYlE2WmM5VjY0azB3Q2dZSUtvWkl6ajBFQXdNRFNBQXdSUUloQU1USGhXdGJBUU4waFN4SVhjUDRDS3JEQ0gvZ3N4V3B4NmpUWkxUZVorRlBBaUIzNW53azVxMHpjSXBlZnZZSjBNVS95R0dIU1dlejBicTBwRFlVTy9ubUR3PT0iXSwidHlwIjoiSldUIiwiYWxnIjoiRVMyNTYifQ.eyJlbnZpcm9ubWVudCI6IlNhbmRib3giLCJidW5kbGVJZCI6ImNvbS5leGFtcGxlIiwic2lnbmVkRGF0ZSI6MTY3Mjk1NjE1NDAwMH0.PnHWpeIJZ8f2Q218NSGLo_aR0IBEJvC6PxmxKXh-qfYTrZccx2suGl223OSNAX78e4Ylf2yJCG2N-FfU-NIhZQ \ No newline at end of file diff --git a/src/test/resources/mock_signed_data/wrongBundleId b/src/test/resources/mock_signed_data/wrongBundleId new file mode 100644 index 00000000..cc7091e6 --- /dev/null +++ b/src/test/resources/mock_signed_data/wrongBundleId @@ -0,0 +1 @@ +eyJ4NWMiOlsiTUlJQm9EQ0NBVWFnQXdJQkFnSUJEREFLQmdncWhrak9QUVFEQXpCRk1Rc3dDUVlEVlFRR0V3SlZVekVMTUFrR0ExVUVDQXdDUTBFeEVqQVFCZ05WQkFjTUNVTjFjR1Z5ZEdsdWJ6RVZNQk1HQTFVRUNnd01TVzUwWlhKdFpXUnBZWFJsTUI0WERUSXpNREV3TlRJeE16RXpORm9YRFRNek1ERXdNVEl4TXpFek5Gb3dQVEVMTUFrR0ExVUVCaE1DVlZNeEN6QUpCZ05WQkFnTUFrTkJNUkl3RUFZRFZRUUhEQWxEZFhCbGNuUnBibTh4RFRBTEJnTlZCQW9NQkV4bFlXWXdXVEFUQmdjcWhrak9QUUlCQmdncWhrak9QUU1CQndOQ0FBVGl0WUhFYVlWdWM4ZzlBalRPd0VyTXZHeVB5a1BhK3B1dlRJOGhKVEhaWkRMR2FzMnFYMStFcnhnUVRKZ1ZYdjc2bm1MaGhSSkgrajI1QWlBSThpR3NveTh3TFRBSkJnTlZIUk1FQWpBQU1BNEdBMVVkRHdFQi93UUVBd0lIZ0RBUUJnb3Foa2lHOTJOa0Jnc0JCQUlGQURBS0JnZ3Foa2pPUFFRREF3TklBREJGQWlCWDRjK1QwRnA1bko1UVJDbFJmdTVQU0J5UnZOUHR1YVRzazB2UEIzV0FJQUloQU5nYWF1QWovWVA5czBBa0VoeUpoeFFPLzZRMnpvdVorSDFDSU9laG5NelEiLCJNSUlCbnpDQ0FVV2dBd0lCQWdJQkN6QUtCZ2dxaGtqT1BRUURBekEyTVFzd0NRWURWUVFHRXdKVlV6RVRNQkVHQTFVRUNBd0tRMkZzYVdadmNtNXBZVEVTTUJBR0ExVUVCd3dKUTNWd1pYSjBhVzV2TUI0WERUSXpNREV3TlRJeE16RXdOVm9YRFRNek1ERXdNVEl4TXpFd05Wb3dSVEVMTUFrR0ExVUVCaE1DVlZNeEN6QUpCZ05WQkFnTUFrTkJNUkl3RUFZRFZRUUhEQWxEZFhCbGNuUnBibTh4RlRBVEJnTlZCQW9NREVsdWRHVnliV1ZrYVdGMFpUQlpNQk1HQnlxR1NNNDlBZ0VHQ0NxR1NNNDlBd0VIQTBJQUJCVU41VjlyS2pmUmlNQUlvakVBMEF2NU1wMG9GK08wY0w0Z3pyVEYxNzhpblVIdWdqN0V0NDZOcmtRN2hLZ01WbmpvZ3E0NVExck1zK2NNSFZOSUxXcWpOVEF6TUE4R0ExVWRFd1FJTUFZQkFmOENBUUF3RGdZRFZSMFBBUUgvQkFRREFnRUdNQkFHQ2lxR1NJYjNZMlFHQWdFRUFnVUFNQW9HQ0NxR1NNNDlCQU1EQTBnQU1FVUNJUUNtc0lLWXM0MXVsbHNzSFg0clZ2ZVVUMFo3SXM1L2hMSzFsRlBUdHVuM2hBSWdjMisyUkc1K2dOY0ZWY3MrWEplRWw0R1orb2psM1JPT21sbCt5ZTdkeW5RPSIsIk1JSUJnakNDQVNtZ0F3SUJBZ0lKQUxVYzVBTGlINXBiTUFvR0NDcUdTTTQ5QkFNRE1EWXhDekFKQmdOVkJBWVRBbFZUTVJNd0VRWURWUVFJREFwRFlXeHBabTl5Ym1saE1SSXdFQVlEVlFRSERBbERkWEJsY25ScGJtOHdIaGNOTWpNd01UQTFNakV6TURJeVdoY05Nek13TVRBeU1qRXpNREl5V2pBMk1Rc3dDUVlEVlFRR0V3SlZVekVUTUJFR0ExVUVDQXdLUTJGc2FXWnZjbTVwWVRFU01CQUdBMVVFQnd3SlEzVndaWEowYVc1dk1Ga3dFd1lIS29aSXpqMENBUVlJS29aSXpqMERBUWNEUWdBRWMrL0JsK2dvc3BvNnRmOVo3aW81dGRLZHJsTjFZZFZucUVoRURYRFNoemRBSlBRaWphbVhJTUhmOHhXV1RhMXpnb1lUeE9LcGJ1SnREcGx6MVhyaVRhTWdNQjR3REFZRFZSMFRCQVV3QXdFQi96QU9CZ05WSFE4QkFmOEVCQU1DQVFZd0NnWUlLb1pJemowRUF3TURSd0F3UkFJZ2VtV1FYbk1BZFRhZDJKREpXbmc5VTR1QkJMNW1BN1dJMDVIN29IN2M2aVFDSUhpUnFNak5melVBeWl1OWg2ck9VL0sraVRSMEkvM1kvTlNXc1hIWCthY2MiXSwidHlwIjoiSldUIiwiYWxnIjoiRVMyNTYifQ.eyJkYXRhIjp7ImJ1bmRsZUlkIjoiY29tLmV4YW1wbGUud3JvbmcifSwibm90aWZpY2F0aW9uVVVJRCI6IjlhZDU2YmQyLTBiYzYtNDJlMC1hZjI0LWZkOTk2ZDg3YTFlNiIsIm5vdGlmaWNhdGlvblR5cGUiOiJURVNUIn0.WWE31hTB_mcv2O_lf-xI-MNY3d8txc0MzpqFx4QnYDfFIxB95Lo2Fm3r46YSjLLdL7xCWdEJrJP5bHgRCejAGg \ No newline at end of file diff --git a/src/test/resources/models/apiException.json b/src/test/resources/models/apiException.json new file mode 100644 index 00000000..a270c53f --- /dev/null +++ b/src/test/resources/models/apiException.json @@ -0,0 +1,4 @@ +{ + "errorCode": 5000000, + "errorMessage": "An unknown error occurred." +} \ No newline at end of file diff --git a/src/test/resources/models/apiTooManyRequestsException.json b/src/test/resources/models/apiTooManyRequestsException.json new file mode 100644 index 00000000..3b3cb9c9 --- /dev/null +++ b/src/test/resources/models/apiTooManyRequestsException.json @@ -0,0 +1,4 @@ +{ + "errorCode": 4290000, + "errorMessage": "Rate limit exceeded." +} \ No newline at end of file diff --git a/src/test/resources/models/appTransaction.json b/src/test/resources/models/appTransaction.json new file mode 100644 index 00000000..b3b937b8 --- /dev/null +++ b/src/test/resources/models/appTransaction.json @@ -0,0 +1,13 @@ +{ + "receiptType": "LocalTesting", + "appAppleId": 531412, + "bundleId": "com.example", + "applicationVersion": "1.2.3", + "versionExternalIdentifier": 512, + "receiptCreationDate": 1698148900000, + "originalPurchaseDate": 1698148800000, + "originalApplicationVersion": "1.1.2", + "deviceVerification": "device_verification_value", + "deviceVerificationNonce": "48ccfa42-7431-4f22-9908-7e88983e105a", + "preorderDate": 1698148700000 +} \ No newline at end of file diff --git a/src/test/resources/models/extendRenewalDateForAllActiveSubscribersResponse.json b/src/test/resources/models/extendRenewalDateForAllActiveSubscribersResponse.json new file mode 100644 index 00000000..21fd582d --- /dev/null +++ b/src/test/resources/models/extendRenewalDateForAllActiveSubscribersResponse.json @@ -0,0 +1,3 @@ +{ + "requestIdentifier": "758883e8-151b-47b7-abd0-60c4d804c2f5" +} \ No newline at end of file diff --git a/src/test/resources/models/extendSubscriptionRenewalDateResponse.json b/src/test/resources/models/extendSubscriptionRenewalDateResponse.json new file mode 100644 index 00000000..6c5f89f0 --- /dev/null +++ b/src/test/resources/models/extendSubscriptionRenewalDateResponse.json @@ -0,0 +1,6 @@ +{ + "originalTransactionId": "2312412", + "webOrderLineItemId": "9993", + "success": true, + "effectiveDate": 1698148900000 +} \ No newline at end of file diff --git a/src/test/resources/models/getAllSubscriptionStatusesResponse.json b/src/test/resources/models/getAllSubscriptionStatusesResponse.json new file mode 100644 index 00000000..b5d139c1 --- /dev/null +++ b/src/test/resources/models/getAllSubscriptionStatusesResponse.json @@ -0,0 +1,35 @@ +{ + "environment": "LocalTesting", + "bundleId": "com.example", + "appAppleId": 5454545, + "data": [ + { + "subscriptionGroupIdentifier": "sub_group_one", + "lastTransactions": [ + { + "status": 1, + "originalTransactionId": "3749183", + "signedTransactionInfo": "signed_transaction_one", + "signedRenewalInfo": "signed_renewal_one" + }, + { + "status": 5, + "originalTransactionId": "5314314134", + "signedTransactionInfo": "signed_transaction_two", + "signedRenewalInfo": "signed_renewal_two" + } + ] + }, + { + "subscriptionGroupIdentifier": "sub_group_two", + "lastTransactions": [ + { + "status": 2, + "originalTransactionId": "3413453", + "signedTransactionInfo": "signed_transaction_three", + "signedRenewalInfo": "signed_renewal_three" + } + ] + } + ] +} \ No newline at end of file diff --git a/src/test/resources/models/getNotificationHistoryResponse.json b/src/test/resources/models/getNotificationHistoryResponse.json new file mode 100644 index 00000000..75c27b7e --- /dev/null +++ b/src/test/resources/models/getNotificationHistoryResponse.json @@ -0,0 +1,27 @@ +{ + "paginationToken": "57715481-805a-4283-8499-1c19b5d6b20a", + "hasMore": true, + "notificationHistory": [ + { + "sendAttempts": [ + { + "attemptDate": 1698148900000, + "sendAttemptResult": "NO_RESPONSE" + }, { + "attemptDate": 1698148950000, + "sendAttemptResult": "SUCCESS" + } + ], + "signedPayload": "signed_payload_one" + }, + { + "sendAttempts": [ + { + "attemptDate": 1698148800000, + "sendAttemptResult": "CIRCULAR_REDIRECT" + } + ], + "signedPayload": "signed_payload_two" + } + ] +} \ No newline at end of file diff --git a/src/test/resources/models/getRefundHistoryResponse.json b/src/test/resources/models/getRefundHistoryResponse.json new file mode 100644 index 00000000..d1ff65a9 --- /dev/null +++ b/src/test/resources/models/getRefundHistoryResponse.json @@ -0,0 +1,8 @@ +{ + "signedTransactions": [ + "signed_transaction_one", + "signed_transaction_two" + ], + "revision": "revision_output", + "hasMore": true +} \ No newline at end of file diff --git a/src/test/resources/models/getStatusOfSubscriptionRenewalDateExtensionsResponse.json b/src/test/resources/models/getStatusOfSubscriptionRenewalDateExtensionsResponse.json new file mode 100644 index 00000000..9bd7ddc2 --- /dev/null +++ b/src/test/resources/models/getStatusOfSubscriptionRenewalDateExtensionsResponse.json @@ -0,0 +1,7 @@ +{ + "requestIdentifier": "20fba8a0-2b80-4a7d-a17f-85c1854727f8", + "complete": true, + "completeDate": 1698148900000, + "succeededCount": 30, + "failedCount": 2 +} \ No newline at end of file diff --git a/src/test/resources/models/getTestNotificationStatusResponse.json b/src/test/resources/models/getTestNotificationStatusResponse.json new file mode 100644 index 00000000..9f83b901 --- /dev/null +++ b/src/test/resources/models/getTestNotificationStatusResponse.json @@ -0,0 +1,12 @@ +{ + "signedPayload": "signed_payload", + "sendAttempts": [ + { + "attemptDate": 1698148900000, + "sendAttemptResult": "NO_RESPONSE" + }, { + "attemptDate": 1698148950000, + "sendAttemptResult": "SUCCESS" + } + ] +} \ No newline at end of file diff --git a/src/test/resources/models/lookupOrderIdResponse.json b/src/test/resources/models/lookupOrderIdResponse.json new file mode 100644 index 00000000..09a43d55 --- /dev/null +++ b/src/test/resources/models/lookupOrderIdResponse.json @@ -0,0 +1,7 @@ +{ + "status": 1, + "signedTransactions": [ + "signed_transaction_one", + "signed_transaction_two" + ] +} \ No newline at end of file diff --git a/src/test/resources/models/requestTestNotificationResponse.json b/src/test/resources/models/requestTestNotificationResponse.json new file mode 100644 index 00000000..3ecc9e21 --- /dev/null +++ b/src/test/resources/models/requestTestNotificationResponse.json @@ -0,0 +1,3 @@ +{ + "testNotificationToken": "ce3af791-365e-4c60-841b-1674b43c1609" +} \ No newline at end of file diff --git a/src/test/resources/models/signedNotification.json b/src/test/resources/models/signedNotification.json new file mode 100644 index 00000000..bd473c44 --- /dev/null +++ b/src/test/resources/models/signedNotification.json @@ -0,0 +1,16 @@ +{ + "notificationType": "SUBSCRIBED", + "subtype": "INITIAL_BUY", + "notificationUUID": "002e14d5-51f5-4503-b5a8-c3a1af68eb20", + "data": { + "environment": "LocalTesting", + "appAppleId": 41234, + "bundleId": "com.example", + "bundleVersion": "1.2.3", + "signedTransactionInfo": "signed_transaction_info_value", + "signedRenewalInfo": "signed_renewal_info_value", + "status": 1 + }, + "version": "2.0", + "signedDate": 1698148900000 +} \ No newline at end of file diff --git a/src/test/resources/models/signedRenewalInfo.json b/src/test/resources/models/signedRenewalInfo.json new file mode 100644 index 00000000..17c07a8e --- /dev/null +++ b/src/test/resources/models/signedRenewalInfo.json @@ -0,0 +1,16 @@ +{ + "expirationIntent": 1, + "originalTransactionId": "12345", + "autoRenewProductId": "com.example.product.2", + "productId": "com.example.product", + "autoRenewStatus": 1, + "isInBillingRetryPeriod": true, + "priceIncreaseStatus": 0, + "gracePeriodExpiresDate": 1698148900000, + "offerType": 2, + "offerIdentifier": "abc.123", + "signedDate": 1698148800000, + "environment": "LocalTesting", + "recentSubscriptionStartDate": 1698148800000, + "renewalDate": 1698148850000 +} \ No newline at end of file diff --git a/src/test/resources/models/signedSummaryNotification.json b/src/test/resources/models/signedSummaryNotification.json new file mode 100644 index 00000000..3a22ec09 --- /dev/null +++ b/src/test/resources/models/signedSummaryNotification.json @@ -0,0 +1,21 @@ +{ + "notificationType": "RENEWAL_EXTENSION", + "subtype": "SUMMARY", + "notificationUUID": "002e14d5-51f5-4503-b5a8-c3a1af68eb20", + "version": "2.0", + "signedDate": 1698148900000, + "summary": { + "environment": "LocalTesting", + "appAppleId": 41234, + "bundleId": "com.example", + "productId": "com.example.product", + "requestIdentifier": "efb27071-45a4-4aca-9854-2a1e9146f265", + "storefrontCountryCodes": [ + "CAN", + "USA", + "MEX" + ], + "succeededCount": 5, + "failedCount": 2 + } +} \ No newline at end of file diff --git a/src/test/resources/models/signedTransaction.json b/src/test/resources/models/signedTransaction.json new file mode 100644 index 00000000..3c44a569 --- /dev/null +++ b/src/test/resources/models/signedTransaction.json @@ -0,0 +1,25 @@ +{ + "transactionId":"23456", + "originalTransactionId":"12345", + "webOrderLineItemId":"34343", + "bundleId":"com.example", + "productId":"com.example.product", + "subscriptionGroupIdentifier":"55555", + "purchaseDate":1698148900000, + "originalPurchaseDate":1698148800000, + "expiresDate":1698149000000, + "quantity":1, + "type":"Auto-Renewable Subscription", + "appAccountToken": "7e3fb20b-4cdb-47cc-936d-99d65f608138", + "inAppOwnershipType":"PURCHASED", + "signedDate":1698148900000, + "revocationReason": 1, + "revocationDate": 1698148950000, + "isUpgraded": true, + "offerType":1, + "offerIdentifier": "abc.123", + "environment":"LocalTesting", + "transactionReason":"PURCHASE", + "storefront":"USA", + "storefrontId":"143441" +} \ No newline at end of file diff --git a/src/test/resources/models/transactionHistoryResponse.json b/src/test/resources/models/transactionHistoryResponse.json new file mode 100644 index 00000000..c5cc6383 --- /dev/null +++ b/src/test/resources/models/transactionHistoryResponse.json @@ -0,0 +1,11 @@ +{ + "revision": "revision_output", + "hasMore": true, + "bundleId": "com.example", + "appAppleId": 323232, + "environment": "LocalTesting", + "signedTransactions": [ + "signed_transaction_value", + "signed_transaction_value2" + ] +} \ No newline at end of file diff --git a/src/test/resources/models/transactionInfoResponse.json b/src/test/resources/models/transactionInfoResponse.json new file mode 100644 index 00000000..57d84e20 --- /dev/null +++ b/src/test/resources/models/transactionInfoResponse.json @@ -0,0 +1,3 @@ +{ + "signedTransactionInfo": "signed_transaction_info_value" +} \ No newline at end of file