diff --git a/src/main/java/com/apple/itunes/storekit/client/APIError.java b/src/main/java/com/apple/itunes/storekit/client/APIError.java
index 80ad04e6..dc3a84d2 100644
--- a/src/main/java/com/apple/itunes/storekit/client/APIError.java
+++ b/src/main/java/com/apple/itunes/storekit/client/APIError.java
@@ -496,6 +496,13 @@ public enum APIError {
*/
MESSAGE_NOT_FOUND(4040015L),
+ /**
+ * An error response that indicates an app transaction doesn’t exist for the specified customer.
+ *
+ * @see AppTransactionDoesNotExistError
+ */
+ APP_TRANSACTION_DOES_NOT_EXIST_ERROR(4040019L),
+
/**
* An error that indicates the image identifier already exists.
*
diff --git a/src/main/java/com/apple/itunes/storekit/client/BaseAppStoreServerAPIClient.java b/src/main/java/com/apple/itunes/storekit/client/BaseAppStoreServerAPIClient.java
index 588297c8..fc3e458c 100644
--- a/src/main/java/com/apple/itunes/storekit/client/BaseAppStoreServerAPIClient.java
+++ b/src/main/java/com/apple/itunes/storekit/client/BaseAppStoreServerAPIClient.java
@@ -2,6 +2,7 @@
package com.apple.itunes.storekit.client;
+import com.apple.itunes.storekit.model.AppTransactionInfoResponse;
import com.apple.itunes.storekit.model.CheckTestNotificationResponse;
import com.apple.itunes.storekit.model.ConsumptionRequest;
import com.apple.itunes.storekit.model.DefaultConfigurationRequest;
@@ -485,6 +486,19 @@ public void deleteDefaultMessage(String productId, String locale) throws APIExce
makeHttpCall("/inApps/v1/messaging/default/" + productId + "/" + locale, "DELETE", Map.of(), null, Void.class, null);
}
+ /**
+ * Get a customer’s app transaction information for your app.
+ *
+ * @param transactionId Any originalTransactionId, transactionId or appTransactionId that belongs to the customer for your app.
+ * @return A response that contains signed app transaction information for a customer.
+ * @throws APIException If a response was returned indicating the request could not be processed.
+ * @throws IOException If an exception was thrown while making the request.
+ * @see Get App Transaction Info
+ */
+ public AppTransactionInfoResponse getAppTransactionInfo(String transactionId) throws APIException, IOException {
+ return makeHttpCall("/inApps/v1/transactions/appTransactions/" + transactionId, "GET", Map.of(), null, AppTransactionInfoResponse.class, null);
+ }
+
protected interface HttpResponseInterface extends Closeable {
/**
* @return The HTTP status code of the response
diff --git a/src/main/java/com/apple/itunes/storekit/model/AppTransactionInfoResponse.java b/src/main/java/com/apple/itunes/storekit/model/AppTransactionInfoResponse.java
new file mode 100644
index 00000000..3af17c76
--- /dev/null
+++ b/src/main/java/com/apple/itunes/storekit/model/AppTransactionInfoResponse.java
@@ -0,0 +1,89 @@
+// Copyright (c) 2025 Apple Inc. Licensed under MIT License.
+
+package com.apple.itunes.storekit.model;
+
+import com.fasterxml.jackson.annotation.JsonAnySetter;
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+import java.util.Map;
+import java.util.Objects;
+
+/**
+ * A response that contains signed app transaction information for a customer.
+ *
+ * @see AppTransactionInfoResponse
+ */
+public class AppTransactionInfoResponse {
+ private static final String SERIALIZED_NAME_SIGNED_APP_TRANSACTION_INFO = "signedAppTransactionInfo";
+
+ @JsonProperty(SERIALIZED_NAME_SIGNED_APP_TRANSACTION_INFO)
+ private String signedAppTransactionInfo;
+ @JsonAnySetter
+ private Map unknownFields;
+
+ public AppTransactionInfoResponse() {
+ }
+
+ public AppTransactionInfoResponse signedAppTransactionInfo(String signedAppTransactionInfo) {
+ this.signedAppTransactionInfo = signedAppTransactionInfo;
+ return this;
+ }
+
+ /**
+ * A customer’s app transaction information, signed by Apple, in JSON Web Signature (JWS) format.
+ *
+ * @return signedAppTransactionInfo
+ * @see JWSAppTransaction
+ **/
+ public String getSignedAppTransactionInfo() {
+ return signedAppTransactionInfo;
+ }
+
+ public void setSignedAppTransactionInfo(String signedAppTransactionInfo) {
+ this.signedAppTransactionInfo = signedAppTransactionInfo;
+ }
+
+ public AppTransactionInfoResponse unknownFields(Map unknownFields) {
+ this.unknownFields = unknownFields;
+ return this;
+ }
+
+ /**
+ * Fields that are not recognized for this object.
+ *
+ * @return A map of JSON keys to objects.
+ */
+ public Map getUnknownFields() {
+ return unknownFields;
+ }
+
+ public void setUnknownFields(Map unknownFields) {
+ this.unknownFields = unknownFields;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+ AppTransactionInfoResponse that = (AppTransactionInfoResponse) o;
+ return Objects.equals(this.signedAppTransactionInfo, that.signedAppTransactionInfo) &&
+ Objects.equals(this.unknownFields, that.unknownFields);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(signedAppTransactionInfo, unknownFields);
+ }
+
+ @Override
+ public String toString() {
+ return "AppTransactionInfoResponse{" +
+ "signedAppTransactionInfo='" + signedAppTransactionInfo + '\'' +
+ ", unknownFields=" + unknownFields +
+ '}';
+ }
+}
\ No newline at end of file
diff --git a/src/test/java/com/apple/itunes/storekit/client/AppStoreServerAPIClientTest.java b/src/test/java/com/apple/itunes/storekit/client/AppStoreServerAPIClientTest.java
index 9d4d47a5..ffe55ace 100644
--- a/src/test/java/com/apple/itunes/storekit/client/AppStoreServerAPIClientTest.java
+++ b/src/test/java/com/apple/itunes/storekit/client/AppStoreServerAPIClientTest.java
@@ -3,6 +3,7 @@
package com.apple.itunes.storekit.client;
import com.apple.itunes.storekit.model.AccountTenure;
+import com.apple.itunes.storekit.model.AppTransactionInfoResponse;
import com.apple.itunes.storekit.model.CheckTestNotificationResponse;
import com.apple.itunes.storekit.model.ConsumptionRequest;
import com.apple.itunes.storekit.model.ConsumptionStatus;
@@ -913,6 +914,68 @@ public void testTransactionIdNotOriginalTransactionId() throws IOException {
Assertions.fail();
}
+ @Test
+ public void testGetAppTransactionInfo() throws APIException, IOException {
+ AppStoreServerAPIClient client = getClientWithBody("models/appTransactionInfoResponse.json", request -> {
+ Assertions.assertEquals("GET", request.method());
+ Assertions.assertEquals("/inApps/v1/transactions/appTransactions/1234", request.url().encodedPath());
+ Assertions.assertNull(request.body());
+ });
+
+ AppTransactionInfoResponse appTransactionInfoResponse = client.getAppTransactionInfo("1234");
+
+ Assertions.assertNotNull(appTransactionInfoResponse);
+ Assertions.assertEquals("signed_app_transaction_info_value", appTransactionInfoResponse.getSignedAppTransactionInfo());
+ }
+
+ @Test
+ public void testGetAppTransactionInfoInvalidTransactionIdError() throws IOException {
+ String body = TestingUtility.readFile("models/invalidTransactionIdError.json");
+ AppStoreServerAPIClient client = getAppStoreServerAPIClient(body, request -> {}, 400);
+ try {
+ client.getAppTransactionInfo("invalid_transaction_id");
+ } catch (APIException e) {
+ Assertions.assertEquals(400, e.getHttpStatusCode());
+ Assertions.assertEquals(APIError.INVALID_TRANSACTION_ID, e.getApiError());
+ Assertions.assertEquals(4000006L, e.getRawApiError());
+ Assertions.assertEquals("Invalid transaction id.", e.getApiErrorMessage());
+ return;
+ }
+ Assertions.fail();
+ }
+
+ @Test
+ public void testGetAppTransactionInfoAppTransactionDoesNotExistError() throws IOException {
+ String body = TestingUtility.readFile("models/appTransactionDoesNotExistError.json");
+ AppStoreServerAPIClient client = getAppStoreServerAPIClient(body, request -> {}, 404);
+ try {
+ client.getAppTransactionInfo("nonexistent_transaction_id");
+ } catch (APIException e) {
+ Assertions.assertEquals(404, e.getHttpStatusCode());
+ Assertions.assertEquals(APIError.APP_TRANSACTION_DOES_NOT_EXIST_ERROR, e.getApiError());
+ Assertions.assertEquals(4040019L, e.getRawApiError());
+ Assertions.assertEquals("No AppTransaction exists for the customer.", e.getApiErrorMessage());
+ return;
+ }
+ Assertions.fail();
+ }
+
+ @Test
+ public void testGetAppTransactionInfoTransactionIdNotFoundError() throws IOException {
+ String body = TestingUtility.readFile("models/transactionIdNotFoundError.json");
+ AppStoreServerAPIClient client = getAppStoreServerAPIClient(body, request -> {}, 404);
+ try {
+ client.getAppTransactionInfo("not_found_transaction_id");
+ } catch (APIException e) {
+ Assertions.assertEquals(404, e.getHttpStatusCode());
+ Assertions.assertEquals(APIError.TRANSACTION_ID_NOT_FOUND, e.getApiError());
+ Assertions.assertEquals(4040010L, e.getRawApiError());
+ Assertions.assertEquals("Transaction id not found.", e.getApiErrorMessage());
+ return;
+ }
+ Assertions.fail();
+ }
+
public AppStoreServerAPIClient getClientWithBody(String path, Consumer requestVerifier) throws IOException {
String body = TestingUtility.readFile(path);
return getAppStoreServerAPIClient(body, requestVerifier);
diff --git a/src/test/resources/models/appTransactionDoesNotExistError.json b/src/test/resources/models/appTransactionDoesNotExistError.json
new file mode 100644
index 00000000..a5730eae
--- /dev/null
+++ b/src/test/resources/models/appTransactionDoesNotExistError.json
@@ -0,0 +1,4 @@
+{
+ "errorCode": 4040019,
+ "errorMessage": "No AppTransaction exists for the customer."
+}
\ No newline at end of file
diff --git a/src/test/resources/models/appTransactionInfoResponse.json b/src/test/resources/models/appTransactionInfoResponse.json
new file mode 100644
index 00000000..6984d671
--- /dev/null
+++ b/src/test/resources/models/appTransactionInfoResponse.json
@@ -0,0 +1,3 @@
+{
+ "signedAppTransactionInfo": "signed_app_transaction_info_value"
+}
\ No newline at end of file
diff --git a/src/test/resources/models/invalidTransactionIdError.json b/src/test/resources/models/invalidTransactionIdError.json
new file mode 100644
index 00000000..32fc281a
--- /dev/null
+++ b/src/test/resources/models/invalidTransactionIdError.json
@@ -0,0 +1,4 @@
+{
+ "errorCode": 4000006,
+ "errorMessage": "Invalid transaction id."
+}
\ No newline at end of file
diff --git a/src/test/resources/models/transactionIdNotFoundError.json b/src/test/resources/models/transactionIdNotFoundError.json
new file mode 100644
index 00000000..f445639b
--- /dev/null
+++ b/src/test/resources/models/transactionIdNotFoundError.json
@@ -0,0 +1,4 @@
+{
+ "errorCode": 4040010,
+ "errorMessage": "Transaction id not found."
+}
\ No newline at end of file