From eccdb462f6153373c1b459fb9e65aeba58690f8f Mon Sep 17 00:00:00 2001 From: Riyaz Panjwani Date: Wed, 29 Oct 2025 11:51:45 -0700 Subject: [PATCH] Adding support for Get App Transaction Info endpoint --- appstoreserverlibrary/api_client.py | 72 +++++++++++++------ .../models/AppTransactionInfoResponse.py | 20 ++++++ .../appTransactionDoesNotExistError.json | 4 ++ .../models/appTransactionInfoResponse.json | 3 + .../models/invalidTransactionIdError.json | 4 ++ .../models/transactionIdNotFoundError.json | 4 ++ tests/test_api_client.py | 65 +++++++++++++++++ tests/test_api_client_async.py | 67 +++++++++++++++++ 8 files changed, 219 insertions(+), 20 deletions(-) create mode 100644 appstoreserverlibrary/models/AppTransactionInfoResponse.py create mode 100644 tests/resources/models/appTransactionDoesNotExistError.json create mode 100644 tests/resources/models/appTransactionInfoResponse.json create mode 100644 tests/resources/models/invalidTransactionIdError.json create mode 100644 tests/resources/models/transactionIdNotFoundError.json diff --git a/appstoreserverlibrary/api_client.py b/appstoreserverlibrary/api_client.py index 8b661ddd..bd583464 100644 --- a/appstoreserverlibrary/api_client.py +++ b/appstoreserverlibrary/api_client.py @@ -33,6 +33,7 @@ from .models.StatusResponse import StatusResponse from .models.TransactionHistoryRequest import TransactionHistoryRequest from .models.TransactionInfoResponse import TransactionInfoResponse +from .models.AppTransactionInfoResponse import AppTransactionInfoResponse from .models.UpdateAppAccountTokenRequest import UpdateAppAccountTokenRequest from .models.UploadMessageRequestBody import UploadMessageRequestBody from uuid import UUID @@ -528,6 +529,13 @@ class APIError(IntEnum): https://developer.apple.com/documentation/retentionmessaging/messagenotfounderror """ + APP_TRANSACTION_DOES_NOT_EXIST_ERROR = 4040019 + """ + An error response that indicates an app transaction doesn’t exist for the specified customer. + + https://developer.apple.com/documentation/appstoreserverapi/apptransactiondoesnotexisterror + """ + IMAGE_ALREADY_EXISTS = 4090000 """ An error that indicates the image identifier already exists. @@ -627,7 +635,7 @@ def _get_full_url(self, path) -> str: def _get_headers(self) -> Dict[str, str]: return { 'User-Agent': "app-store-server-library/python/1.9.0", - 'Authorization': 'Bearer ' + self._generate_token(), + 'Authorization': f'Bearer {self._generate_token()}', 'Accept': 'application/json' } @@ -697,7 +705,7 @@ def extend_subscription_renewal_date(self, original_transaction_id: str, extend_ :return: A response that indicates whether an individual renewal-date extension succeeded, and related details. :throws APIException: If a response was returned indicating the request could not be processed """ - return self._make_request("/inApps/v1/subscriptions/extend/" + original_transaction_id, "PUT", {}, extend_renewal_date_request, ExtendRenewalDateResponse, None) + return self._make_request(f"/inApps/v1/subscriptions/extend/{original_transaction_id}", "PUT", {}, extend_renewal_date_request, ExtendRenewalDateResponse, None) def get_all_subscription_statuses(self, transaction_id: str, status: Optional[List[Status]] = None) -> StatusResponse: """ @@ -713,7 +721,7 @@ def get_all_subscription_statuses(self, transaction_id: str, status: Optional[Li if status is not None: queryParameters["status"] = [s.value for s in status] - return self._make_request("/inApps/v1/subscriptions/" + transaction_id, "GET", queryParameters, None, StatusResponse, None) + return self._make_request(f"/inApps/v1/subscriptions/{transaction_id}", "GET", queryParameters, None, StatusResponse, None) def get_refund_history(self, transaction_id: str, revision: Optional[str]) -> RefundHistoryResponse: """ @@ -730,7 +738,7 @@ def get_refund_history(self, transaction_id: str, revision: Optional[str]) -> Re if revision is not None: queryParameters["revision"] = [revision] - return self._make_request("/inApps/v2/refund/lookup/" + transaction_id, "GET", queryParameters, None, RefundHistoryResponse, None) + return self._make_request(f"/inApps/v2/refund/lookup/{transaction_id}", "GET", queryParameters, None, RefundHistoryResponse, None) def get_status_of_subscription_renewal_date_extensions(self, request_identifier: str, product_id: str) -> MassExtendRenewalDateStatusResponse: """ @@ -742,7 +750,7 @@ def get_status_of_subscription_renewal_date_extensions(self, request_identifier: :return: A response that indicates the current status of a request to extend the subscription renewal date to all eligible subscribers. :throws APIException: If a response was returned indicating the request could not be processed """ - return self._make_request("/inApps/v1/subscriptions/extend/mass/" + product_id + "/" + request_identifier, "GET", {}, None, MassExtendRenewalDateStatusResponse, None) + return self._make_request(f"/inApps/v1/subscriptions/extend/mass/{product_id}/{request_identifier}", "GET", {}, None, MassExtendRenewalDateStatusResponse, None) def get_test_notification_status(self, test_notification_token: str) -> CheckTestNotificationResponse: """ @@ -753,7 +761,7 @@ def get_test_notification_status(self, test_notification_token: str) -> CheckTes :return: A response that contains the contents of the test notification sent by the App Store server and the result from your server. :throws APIException: If a response was returned indicating the request could not be processed """ - return self._make_request("/inApps/v1/notifications/test/" + test_notification_token, "GET", {}, None, CheckTestNotificationResponse, None) + return self._make_request(f"/inApps/v1/notifications/test/{test_notification_token}", "GET", {}, None, CheckTestNotificationResponse, None) def get_notification_history(self, pagination_token: Optional[str], notification_history_request: NotificationHistoryRequest) -> NotificationHistoryResponse: """ @@ -811,7 +819,7 @@ def get_transaction_history(self, transaction_id: str, revision: Optional[str], if transaction_history_request.revoked is not None: queryParameters["revoked"] = [str(transaction_history_request.revoked)] - return self._make_request("/inApps/" + version + "/history/" + transaction_id, "GET", queryParameters, None, HistoryResponse, None) + return self._make_request("/inApps/{}/history/{}".format(version.value, transaction_id), "GET", queryParameters, None, HistoryResponse, None) def get_transaction_info(self, transaction_id: str) -> TransactionInfoResponse: """ @@ -822,7 +830,7 @@ def get_transaction_info(self, transaction_id: str) -> TransactionInfoResponse: :return: A response that contains signed transaction information for a single transaction. :throws APIException: If a response was returned indicating the request could not be processed """ - return self._make_request("/inApps/v1/transactions/" + transaction_id, "GET", {}, None, TransactionInfoResponse, None) + return self._make_request(f"/inApps/v1/transactions/{transaction_id}", "GET", {}, None, TransactionInfoResponse, None) def look_up_order_id(self, order_id: str) -> OrderLookupResponse: """ @@ -833,7 +841,8 @@ def look_up_order_id(self, order_id: str) -> OrderLookupResponse: :return: A response that includes the order lookup status and an array of signed transactions for the in-app purchases in the order. :throws APIException: If a response was returned indicating the request could not be processed """ - return self._make_request("/inApps/v1/lookup/" + order_id, "GET", {}, None, OrderLookupResponse, None) + return self._make_request(f"/inApps/v1/lookup/{order_id}", "GET", {}, None, OrderLookupResponse, None) + def request_test_notification(self) -> SendTestNotificationResponse: """ Ask App Store Server Notifications to send a test notification to your server. @@ -853,7 +862,7 @@ def send_consumption_data(self, transaction_id: str, consumption_request: Consum :param consumption_request: The request body containing consumption information. :raises APIException: If a response was returned indicating the request could not be processed """ - self._make_request("/inApps/v1/transactions/consumption/" + transaction_id, "PUT", {}, consumption_request, None, None) + self._make_request(f"/inApps/v1/transactions/consumption/{transaction_id}", "PUT", {}, consumption_request, None, None) def set_app_account_token(self, original_transaction_id: str, update_app_account_token_request: UpdateAppAccountTokenRequest): """ @@ -864,7 +873,7 @@ def set_app_account_token(self, original_transaction_id: str, update_app_account :param update_app_account_token_request The request body that contains a valid app account token value. :raises APIException: If a response was returned indicating the request could not be processed """ - self._make_request("/inApps/v1/transactions/" + original_transaction_id + "/appAccountToken", "PUT", {}, update_app_account_token_request, None, None) + self._make_request(f"/inApps/v1/transactions/{original_transaction_id}/appAccountToken", "PUT", {}, update_app_account_token_request, None, None) def upload_image(self, image_identifier: UUID, image: bytes): """ @@ -950,6 +959,17 @@ def delete_default_message(self, product_id: str, locale: str): :see: https://developer.apple.com/documentation/retentionmessaging/delete-default-message """ self._make_request(f"/inApps/v1/messaging/default/{product_id}/{locale}", "DELETE", {}, None, None, None) + + def get_app_transaction_info(self, transaction_id: str) -> AppTransactionInfoResponse: + """ + Get a customer's app transaction information for your app. + + :param transaction_id 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. + :raises APIException: If a response was returned indicating the request could not be processed + :see: https://developer.apple.com/documentation/appstoreserverapi/get-app-transaction-info + """ + return self._make_request(f"/inApps/v1/transactions/appTransactions/{transaction_id}", "GET", {}, None, AppTransactionInfoResponse, None) class AsyncAppStoreServerAPIClient(BaseAppStoreServerAPIClient): def __init__(self, signing_key: bytes, key_id: str, issuer_id: str, bundle_id: str, environment: Environment): @@ -1003,7 +1023,7 @@ async def extend_subscription_renewal_date(self, original_transaction_id: str, e :return: A response that indicates whether an individual renewal-date extension succeeded, and related details. :throws APIException: If a response was returned indicating the request could not be processed """ - return await self._make_request("/inApps/v1/subscriptions/extend/" + original_transaction_id, "PUT", {}, extend_renewal_date_request, ExtendRenewalDateResponse, None) + return await self._make_request(f"/inApps/v1/subscriptions/extend/{original_transaction_id}", "PUT", {}, extend_renewal_date_request, ExtendRenewalDateResponse, None) async def get_all_subscription_statuses(self, transaction_id: str, status: Optional[List[Status]] = None) -> StatusResponse: """ @@ -1019,7 +1039,7 @@ async def get_all_subscription_statuses(self, transaction_id: str, status: Optio if status is not None: queryParameters["status"] = [s.value for s in status] - return await self._make_request("/inApps/v1/subscriptions/" + transaction_id, "GET", queryParameters, None, StatusResponse, None) + return await self._make_request(f"/inApps/v1/subscriptions/{transaction_id}", "GET", queryParameters, None, StatusResponse, None) async def get_refund_history(self, transaction_id: str, revision: Optional[str]) -> RefundHistoryResponse: """ @@ -1036,7 +1056,7 @@ async def get_refund_history(self, transaction_id: str, revision: Optional[str]) if revision is not None: queryParameters["revision"] = [revision] - return await self._make_request("/inApps/v2/refund/lookup/" + transaction_id, "GET", queryParameters, None, RefundHistoryResponse, None) + return await self._make_request(f"/inApps/v2/refund/lookup/{transaction_id}", "GET", queryParameters, None, RefundHistoryResponse, None) async def get_status_of_subscription_renewal_date_extensions(self, request_identifier: str, product_id: str) -> MassExtendRenewalDateStatusResponse: """ @@ -1048,7 +1068,7 @@ async def get_status_of_subscription_renewal_date_extensions(self, request_ident :return: A response that indicates the current status of a request to extend the subscription renewal date to all eligible subscribers. :throws APIException: If a response was returned indicating the request could not be processed """ - return await self._make_request("/inApps/v1/subscriptions/extend/mass/" + product_id + "/" + request_identifier, "GET", {}, None, MassExtendRenewalDateStatusResponse, None) + return await self._make_request(f"/inApps/v1/subscriptions/extend/mass/{product_id}/{request_identifier}", "GET", {}, None, MassExtendRenewalDateStatusResponse, None) async def get_test_notification_status(self, test_notification_token: str) -> CheckTestNotificationResponse: """ @@ -1059,7 +1079,7 @@ async def get_test_notification_status(self, test_notification_token: str) -> Ch :return: A response that contains the contents of the test notification sent by the App Store server and the result from your server. :throws APIException: If a response was returned indicating the request could not be processed """ - return await self._make_request("/inApps/v1/notifications/test/" + test_notification_token, "GET", {}, None, CheckTestNotificationResponse, None) + return await self._make_request(f"/inApps/v1/notifications/test/{test_notification_token}", "GET", {}, None, CheckTestNotificationResponse, None) async def get_notification_history(self, pagination_token: Optional[str], notification_history_request: NotificationHistoryRequest) -> NotificationHistoryResponse: """ @@ -1128,7 +1148,7 @@ async def get_transaction_info(self, transaction_id: str) -> TransactionInfoResp :return: A response that contains signed transaction information for a single transaction. :throws APIException: If a response was returned indicating the request could not be processed """ - return await self._make_request("/inApps/v1/transactions/" + transaction_id, "GET", {}, None, TransactionInfoResponse, None) + return await self._make_request(f"/inApps/v1/transactions/{transaction_id}", "GET", {}, None, TransactionInfoResponse, None) async def look_up_order_id(self, order_id: str) -> OrderLookupResponse: """ @@ -1139,7 +1159,7 @@ async def look_up_order_id(self, order_id: str) -> OrderLookupResponse: :return: A response that includes the order lookup status and an array of signed transactions for the in-app purchases in the order. :throws APIException: If a response was returned indicating the request could not be processed """ - return await self._make_request("/inApps/v1/lookup/" + order_id, "GET", {}, None, OrderLookupResponse, None) + return await self._make_request(f"/inApps/v1/lookup/{order_id}", "GET", {}, None, OrderLookupResponse, None) async def request_test_notification(self) -> SendTestNotificationResponse: """ Ask App Store Server Notifications to send a test notification to your server. @@ -1159,7 +1179,7 @@ async def send_consumption_data(self, transaction_id: str, consumption_request: :param consumption_request: The request body containing consumption information. :raises APIException: If a response was returned indicating the request could not be processed """ - await self._make_request("/inApps/v1/transactions/consumption/" + transaction_id, "PUT", {}, consumption_request, None, None) + await self._make_request(f"/inApps/v1/transactions/consumption/{transaction_id}", "PUT", {}, consumption_request, None, None) async def set_app_account_token(self, original_transaction_id: str, update_app_account_token_request: UpdateAppAccountTokenRequest): """ @@ -1170,7 +1190,7 @@ async def set_app_account_token(self, original_transaction_id: str, update_app_a :param update_app_account_token_request The request body that contains a valid app account token value. :raises APIException: If a response was returned indicating the request could not be processed """ - await self._make_request("/inApps/v1/transactions/" + original_transaction_id + "/appAccountToken", "PUT", {}, update_app_account_token_request, None, None) + await self._make_request(f"/inApps/v1/transactions/{original_transaction_id}/appAccountToken", "PUT", {}, update_app_account_token_request, None, None) async def upload_image(self, image_identifier: UUID, image: bytes): """ @@ -1256,3 +1276,15 @@ async def delete_default_message(self, product_id: str, locale: str): :see: https://developer.apple.com/documentation/retentionmessaging/delete-default-message """ await self._make_request(f"/inApps/v1/messaging/default/{product_id}/{locale}", "DELETE", {}, None, None, None) + + async def get_app_transaction_info(self, transaction_id: str) -> AppTransactionInfoResponse: + """ + Get a customer's app transaction information for your app. + + :param transaction_id 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. + :raises APIException: If a response was returned indicating the request could not be processed + :see: https://developer.apple.com/documentation/appstoreserverapi/get-app-transaction-info + """ + return await self._make_request(f"/inApps/v1/transactions/appTransactions/{transaction_id}", "GET", {}, None, AppTransactionInfoResponse, None) + diff --git a/appstoreserverlibrary/models/AppTransactionInfoResponse.py b/appstoreserverlibrary/models/AppTransactionInfoResponse.py new file mode 100644 index 00000000..5deb7fde --- /dev/null +++ b/appstoreserverlibrary/models/AppTransactionInfoResponse.py @@ -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 + """ \ No newline at end of file diff --git a/tests/resources/models/appTransactionDoesNotExistError.json b/tests/resources/models/appTransactionDoesNotExistError.json new file mode 100644 index 00000000..a5730eae --- /dev/null +++ b/tests/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/tests/resources/models/appTransactionInfoResponse.json b/tests/resources/models/appTransactionInfoResponse.json new file mode 100644 index 00000000..2cef0346 --- /dev/null +++ b/tests/resources/models/appTransactionInfoResponse.json @@ -0,0 +1,3 @@ +{ + "signedAppTransactionInfo": "signed_app_transaction_info_value" +} \ No newline at end of file diff --git a/tests/resources/models/invalidTransactionIdError.json b/tests/resources/models/invalidTransactionIdError.json new file mode 100644 index 00000000..32fc281a --- /dev/null +++ b/tests/resources/models/invalidTransactionIdError.json @@ -0,0 +1,4 @@ +{ + "errorCode": 4000006, + "errorMessage": "Invalid transaction id." +} \ No newline at end of file diff --git a/tests/resources/models/transactionIdNotFoundError.json b/tests/resources/models/transactionIdNotFoundError.json new file mode 100644 index 00000000..f445639b --- /dev/null +++ b/tests/resources/models/transactionIdNotFoundError.json @@ -0,0 +1,4 @@ +{ + "errorCode": 4040010, + "errorMessage": "Transaction id not found." +} \ No newline at end of file diff --git a/tests/test_api_client.py b/tests/test_api_client.py index c7d8aa45..3d683c57 100644 --- a/tests/test_api_client.py +++ b/tests/test_api_client.py @@ -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') diff --git a/tests/test_api_client_async.py b/tests/test_api_client_async.py index f15b3aa7..f6a9ad1b 100644 --- a/tests/test_api_client_async.py +++ b/tests/test_api_client_async.py @@ -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 @@ -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')