Files
purchases-ios/Tests/StoreKitUnitTests/LocalReceiptParserStoreKitTests.swift
Pol Miro a59ca8f4ea Expose apiKey on Purchases via @_spi(Internal) (#6635)
Adapter modules that import the SDK with `@_spi(Internal) import RevenueCat`
(e.g. `RevenueCatAdMob`) need to read the configured public API key when
constructing payloads such as the AdMob SSV `customRewardString`.

- Store the API key on `SystemInfo` and wire it through `Purchases` init.
- Add `@_spi(Internal) public var apiKey: String` on `Purchases`.
- Add a Swift API tester case so any future signature change is caught.
- Add a unit test verifying the accessor returns the configured key.

The key is already public plaintext in the app binary, so exposing it as SPI
does not introduce any new security risk.
2026-04-21 08:14:42 +02:00

107 lines
4.1 KiB
Swift

//
// Copyright RevenueCat Inc. All Rights Reserved.
//
// Licensed under the MIT License (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://opensource.org/licenses/MIT
//
// LocalReceiptParserStoreKitTests.swift
//
// Created by Andrés Boedo on 4/4/22.
import Foundation
import Nimble
@testable import RevenueCat
import StoreKit
import XCTest
@available(iOS 14.0, tvOS 14.0, macOS 11.0, watchOS 7.0, *)
class LocalReceiptParserStoreKitTests: StoreKitConfigTestCase {
private let operationDispatcher: OperationDispatcher = .default
private let receiptRefreshRequestFactory = ReceiptRefreshRequestFactory()
private var requestFetcher: StoreKitRequestFetcher!
private var systemInfo: SystemInfo!
private var receiptFetcher: ReceiptFetcher!
private var parser: PurchasesReceiptParser!
override func setUpWithError() throws {
try super.setUpWithError()
self.requestFetcher = StoreKitRequestFetcher(requestFactory: receiptRefreshRequestFactory,
operationDispatcher: operationDispatcher)
self.systemInfo = SystemInfo(platformInfo: Purchases.platformInfo,
finishTransactions: true,
operationDispatcher: operationDispatcher,
storeKitVersion: .storeKit1,
apiKey: "api_key",
preferredLocalesProvider: .mock())
self.receiptFetcher = ReceiptFetcher(requestFetcher: self.requestFetcher, systemInfo: systemInfo)
self.parser = .default
}
@MainActor
func testReceiptParserParsesEmptyReceipt() async throws {
let data = try await XCTAsyncUnwrap(await self.receiptFetcher.receiptData(refreshPolicy: .always))
let receipt = try self.parser.parse(from: data)
expect(receipt.bundleId) == "com.revenuecat.StoreKitUnitTestsHostApp"
#if os(macOS)
expect(receipt.applicationVersion) == SystemInfo.frameworkVersion.replacingOccurrences(
of: "-SNAPSHOT",
with: ""
)
#else
expect(receipt.applicationVersion) == "1"
#endif
expect(receipt.originalApplicationVersion).to(beNil())
expect(receipt.opaqueValue).toNot(beNil())
expect(receipt.sha1Hash).toNot(beNil())
expect(receipt.creationDate).to(beCloseToNow())
expect(receipt.expirationDate).toNot(beNil())
expect(receipt.expirationDate).toNot(beCloseToNow())
expect(receipt.inAppPurchases).to(beEmpty())
}
@available(iOS 15.0, tvOS 15.0, macOS 12.0, watchOS 8.0, *)
func testReceiptParserParsesReceiptWithSingleIAP() async throws {
try AvailabilityChecks.iOS16APIAvailableOrSkipTest()
let product = try await fetchSk2Product()
let purchaseTime = Date()
_ = try await product.purchase()
let receiptData = await self.receiptFetcher.receiptData(refreshPolicy: .always)
let data = try XCTUnwrap(receiptData)
let receipt = try self.parser.parse(from: data)
let firstPurchase = try XCTUnwrap(receipt.inAppPurchases.onlyElement)
expect(firstPurchase.quantity) == 1
expect(firstPurchase.productId) == product.id
expect(firstPurchase.transactionId).toNot(beNil())
expect(firstPurchase.originalTransactionId).to(beNil())
expect(firstPurchase.productType) == .unknown
expect(firstPurchase.purchaseDate).to(beCloseTo(purchaseTime, within: 5))
expect(firstPurchase.originalPurchaseDate).to(beNil())
expect(firstPurchase.expiresDate).toNot(beNil())
expect(firstPurchase.expiresDate).toNot(beCloseToNow())
expect(firstPurchase.isSubscription) == true
expect(firstPurchase.cancellationDate).to(beNil())
expect(firstPurchase.isInTrialPeriod).to(beNil())
expect(firstPurchase.isInIntroOfferPeriod) == true
expect(firstPurchase.webOrderLineItemId).to(beNil())
expect(firstPurchase.promotionalOfferIdentifier).to(beNil())
}
}