Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions src/main/java/com/apple/itunes/storekit/client/APIError.java
Original file line number Diff line number Diff line change
Expand Up @@ -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 <a href="https://developer.apple.com/documentation/appstoreserverapi/apptransactiondoesnotexisterror">AppTransactionDoesNotExistError</a>
*/
APP_TRANSACTION_DOES_NOT_EXIST_ERROR(4040019L),

/**
* An error that indicates the image identifier already exists.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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 <a href="https://developer.apple.com/documentation/appstoreserverapi/get-app-transaction-info">Get App Transaction Info</a>
*/
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
Expand Down
Original file line number Diff line number Diff line change
@@ -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 <a href="https://developer.apple.com/documentation/appstoreserverapi/apptransactioninforesponse">AppTransactionInfoResponse</a>
*/
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<String, Object> 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 <a href="https://developer.apple.com/documentation/appstoreserverapi/jwsapptransaction">JWSAppTransaction</a>
**/
public String getSignedAppTransactionInfo() {
return signedAppTransactionInfo;
}

public void setSignedAppTransactionInfo(String signedAppTransactionInfo) {
this.signedAppTransactionInfo = signedAppTransactionInfo;
}

public AppTransactionInfoResponse unknownFields(Map<String, Object> unknownFields) {
this.unknownFields = unknownFields;
return this;
}

/**
* Fields that are not recognized for this object.
*
* @return A map of JSON keys to objects.
*/
public Map<String, Object> getUnknownFields() {
return unknownFields;
}

public void setUnknownFields(Map<String, Object> 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 +
'}';
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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<Request> requestVerifier) throws IOException {
String body = TestingUtility.readFile(path);
return getAppStoreServerAPIClient(body, requestVerifier);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"errorCode": 4040019,
"errorMessage": "No AppTransaction exists for the customer."
}
3 changes: 3 additions & 0 deletions src/test/resources/models/appTransactionInfoResponse.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"signedAppTransactionInfo": "signed_app_transaction_info_value"
}
4 changes: 4 additions & 0 deletions src/test/resources/models/invalidTransactionIdError.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"errorCode": 4000006,
"errorMessage": "Invalid transaction id."
}
4 changes: 4 additions & 0 deletions src/test/resources/models/transactionIdNotFoundError.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"errorCode": 4040010,
"errorMessage": "Transaction id not found."
}
Loading