Skip to content

Commit 648edcf

Browse files
Merge pull request #165 from riyazpanjwani/main
Add support for Set App Account Token endpoint
2 parents ba4fe24 + 6260d10 commit 648edcf

File tree

6 files changed

+170
-0
lines changed

6 files changed

+170
-0
lines changed

src/main/java/com/apple/itunes/storekit/client/APIError.java

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -300,6 +300,27 @@ public enum APIError {
300300
*/
301301
APP_TRANSACTION_ID_NOT_SUPPORTED_ERROR(4000048L),
302302

303+
/**
304+
* An error that indicates the app account token value is not a valid UUID.
305+
*
306+
* @see <a href="https://developer.apple.com/documentation/appstoreserverapi/invalidappaccounttokenuuiderror">InvalidAppAccountTokenUUIDError</a>
307+
*/
308+
INVALID_APP_ACCOUNT_TOKEN_UUID_ERROR(4000183L),
309+
310+
/**
311+
* An error that indicates the transaction is for a product the customer obtains through Family Sharing, which the endpoint doesn’t support.
312+
*
313+
* @see <a href="https://developer.apple.com/documentation/appstoreserverapi/familytransactionnotsupportederror">FamilyTransactionNotSupportedError</a>
314+
*/
315+
FAMILY_TRANSACTION_NOT_SUPPORTED_ERROR(4000185L),
316+
317+
/**
318+
* An error that indicates the endpoint expects an original transaction identifier.
319+
*
320+
* @see <a href="https://developer.apple.com/documentation/appstoreserverapi/transactionidisnotoriginaltransactioniderror">TransactionIdIsNotOriginalTransactionIdError</a>
321+
*/
322+
TRANSACTION_ID_IS_NOT_ORIGINAL_TRANSACTION_ID_ERROR(4000187L),
323+
303324
/**
304325
* An error that indicates the subscription doesn't qualify for a renewal-date extension due to its subscription state.
305326
*

src/main/java/com/apple/itunes/storekit/client/BaseAppStoreServerAPIClient.java

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
import com.apple.itunes.storekit.model.StatusResponse;
2222
import com.apple.itunes.storekit.model.TransactionHistoryRequest;
2323
import com.apple.itunes.storekit.model.TransactionInfoResponse;
24+
import com.apple.itunes.storekit.model.UpdateAppAccountTokenRequest;
2425
import com.fasterxml.jackson.annotation.JsonAutoDetect;
2526
import com.fasterxml.jackson.core.JsonProcessingException;
2627
import com.fasterxml.jackson.databind.ObjectMapper;
@@ -359,6 +360,20 @@ public void sendConsumptionData(String transactionId, ConsumptionRequest consump
359360
makeHttpCall("/inApps/v1/transactions/consumption/" + transactionId, "PUT", Map.of(), consumptionRequest, Void.class);
360361
}
361362

363+
364+
/**
365+
* Sets the app account token value for a purchase the customer makes outside your app, or updates its value in an existing transaction.
366+
*
367+
* @param originalTransactionId The original transaction identifier of the transaction to receive the app account token update.
368+
* @param updateAppAccountTokenRequest The request body that contains a valid app account token value.
369+
* @throws APIException If a response was returned indicating the request could not be processed.
370+
* @throws IOException If an exception was thrown while making the request.
371+
* @see <a href="https://developer.apple.com/documentation/appstoreserverapi/set-app-account-token">Set App Account Token</a>
372+
*/
373+
public void setAppAccountToken(String originalTransactionId, UpdateAppAccountTokenRequest updateAppAccountTokenRequest) throws APIException, IOException {
374+
makeHttpCall("/inApps/v1/transactions/" + originalTransactionId + "/appAccountToken", "PUT", Map.of(), updateAppAccountTokenRequest, Void.class);
375+
}
376+
362377
protected interface HttpResponseInterface extends Closeable {
363378
/**
364379
* @return The HTTP status code of the response
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
// Copyright (c) 2025 Apple Inc. Licensed under MIT License.
2+
3+
package com.apple.itunes.storekit.model;
4+
5+
import com.fasterxml.jackson.annotation.JsonProperty;
6+
7+
import java.util.Objects;
8+
import java.util.UUID;
9+
10+
/**
11+
* The request body that contains an app account token value.
12+
*
13+
* @see <a href="https://developer.apple.com/documentation/appstoreserverapi/updateappaccounttokenrequest">UpdateAppAccountTokenRequest</a>
14+
**/
15+
public class UpdateAppAccountTokenRequest {
16+
private static final String SERIALIZED_NAME_APP_ACCOUNT_TOKEN = "appAccountToken";
17+
@JsonProperty(value = SERIALIZED_NAME_APP_ACCOUNT_TOKEN, required = true)
18+
private UUID appAccountToken;
19+
20+
private UpdateAppAccountTokenRequest() {
21+
}
22+
23+
public UpdateAppAccountTokenRequest(UUID appAccountToken) {
24+
Objects.requireNonNull(appAccountToken);
25+
this.appAccountToken = appAccountToken;
26+
}
27+
28+
public UpdateAppAccountTokenRequest appAccountToken(UUID appAccountToken) {
29+
Objects.requireNonNull(appAccountToken);
30+
this.appAccountToken = appAccountToken;
31+
return this;
32+
}
33+
34+
/**
35+
* The UUID that an app optionally generates to map a customer’s in-app purchase with its resulting App Store transaction.
36+
*
37+
* @return appAccountToken
38+
* @see <a href="https://developer.apple.com/documentation/appstoreserverapi/appaccounttoken">appAccountToken</a>
39+
**/
40+
public UUID getAppAccountToken() {
41+
return appAccountToken;
42+
}
43+
44+
public void setAppAccountToken(UUID appAccountToken) {
45+
Objects.requireNonNull(appAccountToken);
46+
this.appAccountToken = appAccountToken;
47+
}
48+
49+
@Override
50+
public boolean equals(Object o) {
51+
if (!(o instanceof UpdateAppAccountTokenRequest)) return false;
52+
UpdateAppAccountTokenRequest that = (UpdateAppAccountTokenRequest) o;
53+
return Objects.equals(getAppAccountToken(), that.getAppAccountToken());
54+
}
55+
56+
@Override
57+
public int hashCode() {
58+
return Objects.hashCode(getAppAccountToken());
59+
}
60+
61+
@Override
62+
public String toString() {
63+
return "UpdateAppAccountTokenRequest{" +
64+
"appAccountToken=" + appAccountToken +
65+
'}';
66+
}
67+
}

src/test/java/com/apple/itunes/storekit/client/AppStoreServerAPIClientTest.java

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@
3838
import com.apple.itunes.storekit.model.Subtype;
3939
import com.apple.itunes.storekit.model.TransactionHistoryRequest;
4040
import com.apple.itunes.storekit.model.TransactionInfoResponse;
41+
import com.apple.itunes.storekit.model.UpdateAppAccountTokenRequest;
4142
import com.apple.itunes.storekit.model.UserStatus;
4243
import com.apple.itunes.storekit.util.TestingUtility;
4344
import com.auth0.jwt.JWT;
@@ -682,6 +683,64 @@ public void testXcodeEnvironmentNotSupportedError() throws IOException {
682683
Assertions.fail();
683684
}
684685

686+
@Test
687+
public void testSetAppAccountToken() throws IOException, APIException {
688+
AppStoreServerAPIClient client = getAppStoreServerAPIClient("", request -> {
689+
Assertions.assertEquals("PUT", request.method());
690+
Assertions.assertEquals("/inApps/v1/transactions/49571273/appAccountToken", request.url().encodedPath());
691+
RequestBody body = request.body();
692+
Assertions.assertNotNull(body);
693+
Assertions.assertEquals(expectedMediaType, body.contentType());
694+
Buffer buffer = new Buffer();
695+
try {
696+
body.writeTo(buffer);
697+
} catch (IOException e) {
698+
throw new RuntimeException(e);
699+
}
700+
Map<String, Object> root;
701+
try {
702+
root = new ObjectMapper().readValue(buffer.readUtf8(), Map.class);
703+
} catch (JsonProcessingException e) {
704+
throw new RuntimeException(e);
705+
}
706+
Assertions.assertEquals("7389a31a-fb6d-4569-a2a6-db7d85d84813", root.get("appAccountToken"));
707+
});
708+
709+
UpdateAppAccountTokenRequest updateAppAccountTokenRequest = new UpdateAppAccountTokenRequest(UUID.fromString("7389a31a-fb6d-4569-a2a6-db7d85d84813"));
710+
711+
client.setAppAccountToken("49571273", updateAppAccountTokenRequest);
712+
}
713+
714+
@Test
715+
public void testFamilyTransactionNotSupportedError() throws IOException {
716+
String body = TestingUtility.readFile("models/familyTransactionNotSupportedError.json");
717+
AppStoreServerAPIClient client = getAppStoreServerAPIClient(body, request -> {}, 400);
718+
try {
719+
UpdateAppAccountTokenRequest updateAppAccountTokenRequest = new UpdateAppAccountTokenRequest(UUID.fromString("7389a31a-fb6d-4569-a2a6-db7d85d84813"));
720+
client.setAppAccountToken("1234", updateAppAccountTokenRequest);
721+
} catch (APIException e) {
722+
Assertions.assertEquals(400, e.getHttpStatusCode());
723+
Assertions.assertEquals(APIError.FAMILY_TRANSACTION_NOT_SUPPORTED_ERROR, e.getApiError());
724+
return;
725+
}
726+
Assertions.fail();
727+
}
728+
729+
@Test
730+
public void testTransactionIdNotOriginalTransactionId() throws IOException {
731+
String body = TestingUtility.readFile("models/transactionIdNotOriginalTransactionId.json");
732+
AppStoreServerAPIClient client = getAppStoreServerAPIClient(body, request -> {}, 400);
733+
try {
734+
UpdateAppAccountTokenRequest updateAppAccountTokenRequest = new UpdateAppAccountTokenRequest(UUID.fromString("7389a31a-fb6d-4569-a2a6-db7d85d84813"));
735+
client.setAppAccountToken("1234", updateAppAccountTokenRequest);
736+
} catch (APIException e) {
737+
Assertions.assertEquals(400, e.getHttpStatusCode());
738+
Assertions.assertEquals(APIError.TRANSACTION_ID_IS_NOT_ORIGINAL_TRANSACTION_ID_ERROR, e.getApiError());
739+
return;
740+
}
741+
Assertions.fail();
742+
}
743+
685744
public AppStoreServerAPIClient getClientWithBody(String path, Consumer<Request> requestVerifier) throws IOException {
686745
String body = TestingUtility.readFile(path);
687746
return getAppStoreServerAPIClient(body, requestVerifier);
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
{
2+
"errorCode": 4000185,
3+
"errorMessage": "Invalid request. Family Sharing transactions aren't supported by this endpoint."
4+
}
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
{
2+
"errorCode": 4000187,
3+
"errorMessage": "Invalid request. The transaction ID provided is not an original transaction ID."
4+
}

0 commit comments

Comments
 (0)