Skip to content

Commit e82b8d3

Browse files
Merge pull request #102 from riyazpanjwani/main
Adding support for Get App Transaction Info endpoint
2 parents 4fd52d1 + 3fb6625 commit e82b8d3

File tree

7 files changed

+108
-0
lines changed

7 files changed

+108
-0
lines changed

Sources/AppStoreServerLibrary/AppStoreServerAPIClient.swift

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -310,6 +310,7 @@ public class AppStoreServerAPIClient {
310310
}
311311
return await makeRequestWithResponseBody(path: "/inApps/" + version.rawValue + "/history/" + transactionId, method: .GET, queryParameters: queryParams, body: request)
312312
}
313+
313314
///Get information about a single transaction for your app.
314315
///- Parameter transactionId: The identifier of a transaction that belongs to the customer, and which may be an original transaction identifier.
315316
///- Returns: A response that contains signed transaction information for a single transaction.
@@ -452,6 +453,15 @@ public class AppStoreServerAPIClient {
452453
return await makeRequestWithoutResponseBody(path: "/inApps/v1/messaging/default/" + productId + "/" + locale, method: .DELETE, queryParameters: [:], body: request)
453454
}
454455

456+
///Get a customer's app transaction information for your app.
457+
///- Parameter transactionId: Any originalTransactionId, transactionId or appTransactionId that belongs to the customer for your app.
458+
///- Returns: A response that contains signed app transaction information for a customer.
459+
///[Get App Transaction Info](https://developer.apple.com/documentation/appstoreserverapi/get-app-transaction-info)
460+
public func getAppTransactionInfo(transactionId: String) async -> APIResult<AppTransactionInfoResponse> {
461+
let request: String? = nil
462+
return await makeRequestWithResponseBody(path: "/inApps/v1/transactions/appTransactions/" + transactionId, method: .GET, queryParameters: [:], body: request)
463+
}
464+
455465
internal struct AppStoreServerAPIJWT: JWTPayload, Equatable {
456466
var exp: ExpirationClaim
457467
var iss: IssuerClaim
@@ -808,6 +818,11 @@ public enum APIError: Int64 {
808818
///[TransactionIdNotFoundError](https://developer.apple.com/documentation/appstoreserverapi/transactionidnotfounderror)
809819
case transactionIdNotFound = 4040010
810820

821+
///An error response that indicates an app transaction doesn't exist for the specified customer.
822+
///
823+
///[AppTransactionDoesNotExistError](https://developer.apple.com/documentation/appstoreserverapi/apptransactiondoesnotexisterror)
824+
case AppTransactionDoesNotExistError = 4040019
825+
811826
///An error that indicates the system can't find the image identifier.
812827
///
813828
///[ImageNotFoundError](https://developer.apple.com/documentation/retentionmessaging/imagenotfounderror)
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
// Copyright (c) 2025 Apple Inc. Licensed under MIT License.
2+
3+
///A response that contains signed app transaction information for a customer.
4+
///
5+
///[AppTransactionInfoResponse](https://developer.apple.com/documentation/appstoreserverapi/apptransactioninforesponse)
6+
public struct AppTransactionInfoResponse: Decodable, Encodable, Hashable, Sendable {
7+
8+
public init(signedAppTransactionInfo: String? = nil) {
9+
self.signedAppTransactionInfo = signedAppTransactionInfo
10+
}
11+
12+
///A customer’s app transaction information, signed by Apple, in JSON Web Signature (JWS) format.
13+
///
14+
///[JWSAppTransaction](https://developer.apple.com/documentation/appstoreserverapi/jwsapptransaction)
15+
public var signedAppTransactionInfo: String?
16+
}

Tests/AppStoreServerLibraryTests/AppStoreServerAPIClientTests.swift

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -852,6 +852,68 @@ final class AppStoreServerAPIClientTests: XCTestCase {
852852
}
853853
}
854854

855+
public func testGetAppTransactionInfo() async throws {
856+
let client = try getClientWithBody("resources/models/appTransactionInfoResponse.json") { request, body in
857+
XCTAssertEqual(.GET, request.method)
858+
XCTAssertEqual("https://local-testing-base-url/inApps/v1/transactions/appTransactions/1234", request.url)
859+
XCTAssertNil(request.body)
860+
}
861+
862+
let response = await client.getAppTransactionInfo(transactionId: "1234")
863+
864+
guard case .success(let appTransactionInfoResponse) = response else {
865+
XCTAssertTrue(false)
866+
return
867+
}
868+
XCTAssertEqual("signed_app_transaction_info_value", appTransactionInfoResponse.signedAppTransactionInfo)
869+
TestingUtility.confirmCodableInternallyConsistent(appTransactionInfoResponse)
870+
}
871+
872+
public func testGetAppTransactionInfoInvalidTransactionId() async throws {
873+
let body = TestingUtility.readFile("resources/models/invalidTransactionIdError.json")
874+
let client = try getAppStoreServerAPIClient(body, .badRequest, nil)
875+
let result = await client.getAppTransactionInfo(transactionId: "invalid_id")
876+
guard case .failure(let statusCode, let rawApiError, let apiError, let errorMessage, let causedBy) = result else {
877+
XCTAssertTrue(false)
878+
return
879+
}
880+
XCTAssertEqual(400, statusCode)
881+
XCTAssertEqual(APIError.invalidTransactionId, apiError)
882+
XCTAssertEqual(4000006, rawApiError)
883+
XCTAssertEqual("Invalid transaction id.", errorMessage)
884+
XCTAssertNil(causedBy)
885+
}
886+
887+
public func testGetAppTransactionInfoTransactionIdNotFound() async throws {
888+
let body = TestingUtility.readFile("resources/models/transactionIdNotFoundError.json")
889+
let client = try getAppStoreServerAPIClient(body, .notFound, nil)
890+
let result = await client.getAppTransactionInfo(transactionId: "not_found_id")
891+
guard case .failure(let statusCode, let rawApiError, let apiError, let errorMessage, let causedBy) = result else {
892+
XCTAssertTrue(false)
893+
return
894+
}
895+
XCTAssertEqual(404, statusCode)
896+
XCTAssertEqual(APIError.transactionIdNotFound, apiError)
897+
XCTAssertEqual(4040010, rawApiError)
898+
XCTAssertEqual("Transaction id not found.", errorMessage)
899+
XCTAssertNil(causedBy)
900+
}
901+
902+
public func testGetAppTransactionInfoAppTransactionDoesNotExist() async throws {
903+
let body = TestingUtility.readFile("resources/models/appTransactionDoesNotExistError.json")
904+
let client = try getAppStoreServerAPIClient(body, .notFound, nil)
905+
let result = await client.getAppTransactionInfo(transactionId: "no_app_transaction")
906+
guard case .failure(let statusCode, let rawApiError, let apiError, let errorMessage, let causedBy) = result else {
907+
XCTAssertTrue(false)
908+
return
909+
}
910+
XCTAssertEqual(404, statusCode)
911+
XCTAssertEqual(APIError.AppTransactionDoesNotExistError, apiError)
912+
XCTAssertEqual(4040019, rawApiError)
913+
XCTAssertEqual("No AppTransaction exists for the customer.", errorMessage)
914+
XCTAssertNil(causedBy)
915+
}
916+
855917
public func getClientWithBody(_ path: String, _ requestVerifier: @escaping RequestVerifier) throws -> AppStoreServerAPIClient {
856918
let body = TestingUtility.readFile(path)
857919
return try getAppStoreServerAPIClient(body, requestVerifier)
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
{
2+
"errorCode": 4040019,
3+
"errorMessage": "No AppTransaction exists for the customer."
4+
}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
{
2+
"signedAppTransactionInfo": "signed_app_transaction_info_value"
3+
}
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
{
2+
"errorCode": 4000006,
3+
"errorMessage": "Invalid transaction id."
4+
}
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
{
2+
"errorCode": 4040010,
3+
"errorMessage": "Transaction id not found."
4+
}

0 commit comments

Comments
 (0)