Skip to content

Commit 3c43c15

Browse files
author
Riyaz Panjwani
committed
Add support for Set App Account Token endpoint
1 parent 098851a commit 3c43c15

File tree

7 files changed

+213
-4
lines changed

7 files changed

+213
-4
lines changed

appstoreserverlibrary/api_client.py

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
from .models.StatusResponse import StatusResponse
3232
from .models.TransactionHistoryRequest import TransactionHistoryRequest
3333
from .models.TransactionInfoResponse import TransactionInfoResponse
34+
from .models.UpdateAppAccountTokenRequest import UpdateAppAccountTokenRequest
3435

3536
T = TypeVar('T')
3637

@@ -326,6 +327,28 @@ class APIError(IntEnum):
326327
https://developer.apple.com/documentation/appstoreserverapi/apptransactionidnotsupportederror
327328
"""
328329

330+
INVALID_APP_ACCOUNT_TOKEN_UUID_ERROR = 4000183
331+
"""
332+
An error that indicates the app account token value is not a valid UUID.
333+
334+
https://developer.apple.com/documentation/appstoreserverapi/invalidappaccounttokenuuiderror
335+
"""
336+
337+
FAMILY_TRANSACTION_NOT_SUPPORTED_ERROR = 4000185
338+
"""
339+
An error that indicates the transaction is for a product the customer obtains through Family Sharing,
340+
which the endpoint doesn’t support.
341+
342+
https://developer.apple.com/documentation/appstoreserverapi/familytransactionnotsupportederror
343+
"""
344+
345+
TRANSACTION_ID_IS_NOT_ORIGINAL_TRANSACTION_ID_ERROR = 4000187
346+
"""
347+
An error that indicates the endpoint expects an original transaction identifier.
348+
349+
https://developer.apple.com/documentation/appstoreserverapi/transactionidisnotoriginaltransactioniderror
350+
"""
351+
329352
SUBSCRIPTION_EXTENSION_INELIGIBLE = 4030004
330353
"""
331354
An error that indicates the subscription doesn't qualify for a renewal-date extension due to its subscription state.
@@ -724,6 +747,16 @@ def send_consumption_data(self, transaction_id: str, consumption_request: Consum
724747
"""
725748
self._make_request("/inApps/v1/transactions/consumption/" + transaction_id, "PUT", {}, consumption_request, None)
726749

750+
def set_app_account_token(self, original_transaction_id: str, update_app_account_token_request: UpdateAppAccountTokenRequest):
751+
"""
752+
Sets the app account token value for a purchase the customer makes outside your app, or updates its value in an existing transaction.
753+
https://developer.apple.com/documentation/appstoreserverapi/set-app-account-token
754+
755+
:param original_transaction_id The original transaction identifier of the transaction to receive the app account token update.
756+
:param update_app_account_token_request The request body that contains a valid app account token value.
757+
:raises APIException: If a response was returned indicating the request could not be processed
758+
"""
759+
self._make_request("/inApps/v1/transactions/" + original_transaction_id + "/appAccountToken", "PUT", {}, update_app_account_token_request, None)
727760

728761
class AsyncAppStoreServerAPIClient(BaseAppStoreServerAPIClient):
729762
def __init__(self, signing_key: bytes, key_id: str, issuer_id: str, bundle_id: str, environment: Environment):
@@ -926,3 +959,14 @@ async def send_consumption_data(self, transaction_id: str, consumption_request:
926959
:raises APIException: If a response was returned indicating the request could not be processed
927960
"""
928961
await self._make_request("/inApps/v1/transactions/consumption/" + transaction_id, "PUT", {}, consumption_request, None)
962+
963+
async def set_app_account_token(self, original_transaction_id: str, update_app_account_token_request: UpdateAppAccountTokenRequest):
964+
"""
965+
Sets the app account token value for a purchase the customer makes outside your app, or updates its value in an existing transaction.
966+
https://developer.apple.com/documentation/appstoreserverapi/set-app-account-token
967+
968+
:param original_transaction_id The original transaction identifier of the transaction to receive the app account token update.
969+
:param update_app_account_token_request The request body that contains a valid app account token value.
970+
:raises APIException: If a response was returned indicating the request could not be processed
971+
"""
972+
await self._make_request("/inApps/v1/transactions/" + original_transaction_id + "/appAccountToken", "PUT", {}, update_app_account_token_request, None)
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
# Copyright (c) 2025 Apple Inc. Licensed under MIT License.
2+
from attr import define
3+
import attr
4+
5+
@define
6+
class UpdateAppAccountTokenRequest:
7+
"""
8+
The request body that contains an app account token value.
9+
10+
https://developer.apple.com/documentation/appstoreserverapi/updateappaccounttokenrequest
11+
"""
12+
13+
appAccountToken: str = attr.ib()
14+
"""
15+
The UUID that an app optionally generates to map a customer's in-app purchase with its resulting App Store transaction.
16+
17+
https://developer.apple.com/documentation/appstoreserverapi/appaccounttoken
18+
"""
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": 4000183,
3+
"errorMessage": "Invalid request. The app account token field must be a valid UUID."
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+
}

tests/test_api_client.py

Lines changed: 70 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -26,17 +26,14 @@
2626
from appstoreserverlibrary.models.OrderLookupStatus import OrderLookupStatus
2727
from appstoreserverlibrary.models.Platform import Platform
2828
from appstoreserverlibrary.models.PlayTime import PlayTime
29-
from appstoreserverlibrary.models.PriceIncreaseStatus import PriceIncreaseStatus
3029
from appstoreserverlibrary.models.RefundPreference import RefundPreference
31-
from appstoreserverlibrary.models.RevocationReason import RevocationReason
3230
from appstoreserverlibrary.models.SendAttemptItem import SendAttemptItem
3331
from appstoreserverlibrary.models.SendAttemptResult import SendAttemptResult
3432
from appstoreserverlibrary.models.Status import Status
3533
from appstoreserverlibrary.models.SubscriptionGroupIdentifierItem import SubscriptionGroupIdentifierItem
3634
from appstoreserverlibrary.models.Subtype import Subtype
3735
from appstoreserverlibrary.models.TransactionHistoryRequest import Order, ProductType, TransactionHistoryRequest
38-
from appstoreserverlibrary.models.TransactionReason import TransactionReason
39-
from appstoreserverlibrary.models.Type import Type
36+
from appstoreserverlibrary.models.UpdateAppAccountTokenRequest import UpdateAppAccountTokenRequest
4037
from appstoreserverlibrary.models.UserStatus import UserStatus
4138

4239
from tests.util import decode_json_from_signed_date, read_data_from_binary_file, read_data_from_file
@@ -495,6 +492,75 @@ def test_get_transaction_history_with_malformed_app_apple_id(self):
495492

496493
self.assertFalse(True)
497494

495+
def test_set_app_account_token(self):
496+
client = self.get_client_with_body(b'',
497+
'PUT',
498+
'https://local-testing-base-url/inApps/v1/transactions/49571273/appAccountToken',
499+
{},
500+
{
501+
'appAccountToken': '7389a31a-fb6d-4569-a2a6-db7d85d84813'
502+
})
503+
update_app_account_token_request = UpdateAppAccountTokenRequest(appAccountToken='7389a31a-fb6d-4569-a2a6-db7d85d84813')
504+
client.set_app_account_token('49571273', update_app_account_token_request)
505+
506+
def test_invalid_app_account_token_error(self):
507+
client = self.get_client_with_body_from_file('tests/resources/models/invalidAppAccountTokenUUIDError.json',
508+
'PUT',
509+
'https://local-testing-base-url/inApps/v1/transactions/49571273/appAccountToken',
510+
{},
511+
None,
512+
400)
513+
try:
514+
client.set_app_account_token('49571273', None)
515+
except APIException as e:
516+
self.assertEqual(400, e.http_status_code)
517+
self.assertEqual(4000183, e.raw_api_error)
518+
self.assertEqual(APIError.INVALID_APP_ACCOUNT_TOKEN_UUID_ERROR, e.api_error)
519+
self.assertEqual("Invalid request. The app account token field must be a valid UUID.", e.error_message)
520+
return
521+
522+
self.assertFalse(True)
523+
524+
def test_family_transaction_not_supported_error(self):
525+
client = self.get_client_with_body_from_file('tests/resources/models/familyTransactionNotSupportedError.json',
526+
'PUT',
527+
'https://local-testing-base-url/inApps/v1/transactions/1234'
528+
'/appAccountToken',
529+
{},
530+
None,
531+
400)
532+
try:
533+
client.set_app_account_token('1234', None)
534+
except APIException as e:
535+
self.assertEqual(400, e.http_status_code)
536+
self.assertEqual(4000185, e.raw_api_error)
537+
self.assertEqual(APIError.FAMILY_TRANSACTION_NOT_SUPPORTED_ERROR, e.api_error)
538+
self.assertEqual("Invalid request. Family Sharing transactions aren't supported by this endpoint.", e.error_message)
539+
return
540+
541+
self.assertFalse(True)
542+
543+
def test_transaction_id_not_original_transaction_id_error(self):
544+
client = self.get_client_with_body_from_file(
545+
'tests/resources/models/transactionIdNotOriginalTransactionId.json',
546+
'PUT',
547+
'https://local-testing-base-url/inApps/v1/transactions/1234'
548+
'/appAccountToken',
549+
{},
550+
None,
551+
400)
552+
try:
553+
client.set_app_account_token('1234', None)
554+
except APIException as e:
555+
self.assertEqual(400, e.http_status_code)
556+
self.assertEqual(4000187, e.raw_api_error)
557+
self.assertEqual(APIError.TRANSACTION_ID_IS_NOT_ORIGINAL_TRANSACTION_ID_ERROR, e.api_error)
558+
self.assertEqual("Invalid request. The transaction ID provided is not an original transaction ID.", e.error_message)
559+
return
560+
561+
self.assertFalse(True)
562+
563+
498564
def get_signing_key(self):
499565
return read_data_from_binary_file('tests/resources/certs/testSigningKey.p8')
500566

tests/test_api_client_async.py

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@
3838
from appstoreserverlibrary.models.TransactionHistoryRequest import Order, ProductType, TransactionHistoryRequest
3939
from appstoreserverlibrary.models.TransactionReason import TransactionReason
4040
from appstoreserverlibrary.models.Type import Type
41+
from appstoreserverlibrary.models.UpdateAppAccountTokenRequest import UpdateAppAccountTokenRequest
4142
from appstoreserverlibrary.models.UserStatus import UserStatus
4243

4344
from tests.util import decode_json_from_signed_date, read_data_from_binary_file, read_data_from_file
@@ -496,6 +497,74 @@ async def test_get_transaction_history_with_malformed_app_apple_id(self):
496497

497498
self.assertFalse(True)
498499

500+
async def test_set_app_account_token(self):
501+
client = self.get_client_with_body(b'',
502+
'PUT',
503+
'https://local-testing-base-url/inApps/v1/transactions/49571273/appAccountToken',
504+
{},
505+
{
506+
'appAccountToken': '7389a31a-fb6d-4569-a2a6-db7d85d84813'
507+
})
508+
update_app_account_token_request = UpdateAppAccountTokenRequest(appAccountToken='7389a31a-fb6d-4569-a2a6-db7d85d84813')
509+
await client.set_app_account_token('49571273', update_app_account_token_request)
510+
511+
async def test_invalid_app_account_token_error(self):
512+
client = self.get_client_with_body_from_file('tests/resources/models/invalidAppAccountTokenUUIDError.json',
513+
'PUT',
514+
'https://local-testing-base-url/inApps/v1/transactions/49571273/appAccountToken',
515+
{},
516+
None,
517+
400)
518+
try:
519+
await client.set_app_account_token('49571273', None)
520+
except APIException as e:
521+
self.assertEqual(400, e.http_status_code)
522+
self.assertEqual(4000183, e.raw_api_error)
523+
self.assertEqual(APIError.INVALID_APP_ACCOUNT_TOKEN_UUID_ERROR, e.api_error)
524+
self.assertEqual("Invalid request. The app account token field must be a valid UUID.", e.error_message)
525+
return
526+
527+
self.assertFalse(True)
528+
529+
async def test_family_transaction_not_supported_error(self):
530+
client = self.get_client_with_body_from_file('tests/resources/models/familyTransactionNotSupportedError.json',
531+
'PUT',
532+
'https://local-testing-base-url/inApps/v1/transactions/1234'
533+
'/appAccountToken',
534+
{},
535+
None,
536+
400)
537+
try:
538+
await client.set_app_account_token('1234', None)
539+
except APIException as e:
540+
self.assertEqual(400, e.http_status_code)
541+
self.assertEqual(4000185, e.raw_api_error)
542+
self.assertEqual(APIError.FAMILY_TRANSACTION_NOT_SUPPORTED_ERROR, e.api_error)
543+
self.assertEqual("Invalid request. Family Sharing transactions aren't supported by this endpoint.", e.error_message)
544+
return
545+
546+
self.assertFalse(True)
547+
548+
async def test_transaction_id_not_original_transaction_id_error(self):
549+
client = self.get_client_with_body_from_file(
550+
'tests/resources/models/transactionIdNotOriginalTransactionId.json',
551+
'PUT',
552+
'https://local-testing-base-url/inApps/v1/transactions/1234'
553+
'/appAccountToken',
554+
{},
555+
None,
556+
400)
557+
try:
558+
await client.set_app_account_token('1234', None)
559+
except APIException as e:
560+
self.assertEqual(400, e.http_status_code)
561+
self.assertEqual(4000187, e.raw_api_error)
562+
self.assertEqual(APIError.TRANSACTION_ID_IS_NOT_ORIGINAL_TRANSACTION_ID_ERROR, e.api_error)
563+
self.assertEqual("Invalid request. The transaction ID provided is not an original transaction ID.", e.error_message)
564+
return
565+
566+
self.assertFalse(True)
567+
499568
def get_signing_key(self):
500569
return read_data_from_binary_file('tests/resources/certs/testSigningKey.p8')
501570

0 commit comments

Comments
 (0)