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
72 changes: 52 additions & 20 deletions appstoreserverlibrary/api_client.py

Large diffs are not rendered by default.

20 changes: 20 additions & 0 deletions appstoreserverlibrary/models/AppTransactionInfoResponse.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# Copyright (c) 2025 Apple Inc. Licensed under MIT License.
from typing import Optional

from attr import define
import attr

@define
class AppTransactionInfoResponse:
"""
A response that contains signed app transaction information for a customer.

https://developer.apple.com/documentation/appstoreserverapi/apptransactioninforesponse
"""

signedAppTransactionInfo: Optional[str] = attr.ib(default=None)
"""
A customer’s app transaction information, signed by Apple, in JSON Web Signature (JWS) format.

https://developer.apple.com/documentation/appstoreserverapi/jwsapptransaction
"""
4 changes: 4 additions & 0 deletions tests/resources/models/appTransactionDoesNotExistError.json
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 tests/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 tests/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 tests/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."
}
65 changes: 65 additions & 0 deletions tests/test_api_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -654,6 +654,71 @@ def test_delete_default_message(self):
None)
client.delete_default_message('com.example.product', 'en-US')

def test_get_app_transaction_info_success(self):
client = self.get_client_with_body_from_file('tests/resources/models/appTransactionInfoResponse.json',
'GET',
'https://local-testing-base-url/inApps/v1/transactions/appTransactions/1234',
{},
None)

app_transaction_info_response = client.get_app_transaction_info('1234')

self.assertIsNotNone(app_transaction_info_response)
self.assertEqual('signed_app_transaction_info_value', app_transaction_info_response.signedAppTransactionInfo)

def test_get_app_transaction_info_invalid_transaction_id(self):
client = self.get_client_with_body_from_file('tests/resources/models/invalidTransactionIdError.json',
'GET',
'https://local-testing-base-url/inApps/v1/transactions/appTransactions/invalid_id',
{},
None,
400)
try:
client.get_app_transaction_info('invalid_id')
except APIException as e:
self.assertEqual(400, e.http_status_code)
self.assertEqual(4000006, e.raw_api_error)
self.assertEqual(APIError.INVALID_TRANSACTION_ID, e.api_error)
self.assertEqual("Invalid transaction id.", e.error_message)
return
self.assertFalse(True)

def test_get_app_transaction_info_app_transaction_does_not_exist(self):
client = self.get_client_with_body_from_file('tests/resources/models/appTransactionDoesNotExistError.json',
'GET',
'https://local-testing-base-url/inApps/v1/transactions/appTransactions/nonexistent_id',
{},
None,
404)
try:
client.get_app_transaction_info('nonexistent_id')
except APIException as e:
self.assertEqual(404, e.http_status_code)
self.assertEqual(4040019, e.raw_api_error)
self.assertEqual(APIError.APP_TRANSACTION_DOES_NOT_EXIST_ERROR, e.api_error)
self.assertEqual("No AppTransaction exists for the customer.", e.error_message)
return

self.assertFalse(True)

def test_get_app_transaction_info_transaction_id_not_found(self):
client = self.get_client_with_body_from_file('tests/resources/models/transactionIdNotFoundError.json',
'GET',
'https://local-testing-base-url/inApps/v1/transactions/appTransactions/not_found_id',
{},
None,
404)
try:
client.get_app_transaction_info('not_found_id')
except APIException as e:
self.assertEqual(404, e.http_status_code)
self.assertEqual(4040010, e.raw_api_error)
self.assertEqual(APIError.TRANSACTION_ID_NOT_FOUND, e.api_error)
self.assertEqual("Transaction id not found.", e.error_message)
return

self.assertFalse(True)


def get_signing_key(self):
return read_data_from_binary_file('tests/resources/certs/testSigningKey.p8')
Expand Down
67 changes: 67 additions & 0 deletions tests/test_api_client_async.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

from appstoreserverlibrary.api_client import APIError, APIException, AsyncAppStoreServerAPIClient, GetTransactionHistoryVersion
from appstoreserverlibrary.models.AccountTenure import AccountTenure
from appstoreserverlibrary.models.AppTransactionInfoResponse import AppTransactionInfoResponse
from appstoreserverlibrary.models.AutoRenewStatus import AutoRenewStatus
from appstoreserverlibrary.models.ConsumptionRequest import ConsumptionRequest
from appstoreserverlibrary.models.ConsumptionStatus import ConsumptionStatus
Expand Down Expand Up @@ -657,6 +658,72 @@ async def test_delete_default_message(self):
{},
None)
await client.delete_default_message('com.example.product', 'en-US')

async def test_get_app_transaction_info_success(self):
client = self.get_client_with_body_from_file('tests/resources/models/appTransactionInfoResponse.json',
'GET',
'https://local-testing-base-url/inApps/v1/transactions/appTransactions/1234',
{},
None)

app_transaction_info_response = await client.get_app_transaction_info('1234')

self.assertIsNotNone(app_transaction_info_response)
self.assertEqual('signed_app_transaction_info_value', app_transaction_info_response.signedAppTransactionInfo)

async def test_get_app_transaction_info_invalid_transaction_id(self):
client = self.get_client_with_body_from_file('tests/resources/models/invalidTransactionIdError.json',
'GET',
'https://local-testing-base-url/inApps/v1/transactions/appTransactions/invalid_id',
{},
None,
400)
try:
await client.get_app_transaction_info('invalid_id')
except APIException as e:
self.assertEqual(400, e.http_status_code)
self.assertEqual(4000006, e.raw_api_error)
self.assertEqual(APIError.INVALID_TRANSACTION_ID, e.api_error)
self.assertEqual("Invalid transaction id.", e.error_message)
return

self.assertFalse(True)

async def test_get_app_transaction_info_app_transaction_does_not_exist(self):
client = self.get_client_with_body_from_file('tests/resources/models/appTransactionDoesNotExistError.json',
'GET',
'https://local-testing-base-url/inApps/v1/transactions/appTransactions/nonexistent_id',
{},
None,
404)
try:
await client.get_app_transaction_info('nonexistent_id')
except APIException as e:
self.assertEqual(404, e.http_status_code)
self.assertEqual(4040019, e.raw_api_error)
self.assertEqual(APIError.APP_TRANSACTION_DOES_NOT_EXIST_ERROR, e.api_error)
self.assertEqual("No AppTransaction exists for the customer.", e.error_message)
return

self.assertFalse(True)

async def test_get_app_transaction_info_transaction_id_not_found(self):
client = self.get_client_with_body_from_file('tests/resources/models/transactionIdNotFoundError.json',
'GET',
'https://local-testing-base-url/inApps/v1/transactions/appTransactions/not_found_id',
{},
None,
404)
try:
await client.get_app_transaction_info('not_found_id')
except APIException as e:
self.assertEqual(404, e.http_status_code)
self.assertEqual(4040010, e.raw_api_error)
self.assertEqual(APIError.TRANSACTION_ID_NOT_FOUND, e.api_error)
self.assertEqual("Transaction id not found.", e.error_message)
return

self.assertFalse(True)

def get_signing_key(self):
return read_data_from_binary_file('tests/resources/certs/testSigningKey.p8')
Expand Down