Files
purchases-ios/Sources/Purchasing/SimulatedStore/SimulatedStorePurchaseHandler.swift
T

105 lines
3.6 KiB
Swift
Raw Normal View History

2025-07-28 19:11:13 +02:00
//
// SimulatedStorePurchaseHandler.swift
2025-07-28 19:11:13 +02:00
// RevenueCat
//
// Created by Antonio Pallares on 16/7/25.
// Copyright © 2025 RevenueCat, Inc. All rights reserved.
//
import Foundation
enum TestPurchaseResult {
case cancel
case failure(PurchasesError)
case success(StoreTransaction)
}
protocol SimulatedStorePurchaseHandlerType: AnyObject, Sendable {
2025-07-28 19:11:13 +02:00
@MainActor
func purchase(product: TestStoreProduct) async -> TestPurchaseResult
2025-07-28 19:11:13 +02:00
}
/// The object that handles purchases in the Simulated Store.
2025-07-28 19:11:13 +02:00
///
/// This class is used to handle purchases when using a Simulated Store API key.
actor SimulatedStorePurchaseHandler: SimulatedStorePurchaseHandlerType {
2025-07-28 19:11:13 +02:00
private let purchaseUI: SimulatedStorePurchaseUI
private let dateProvider: DateProvider
2025-07-28 19:11:13 +02:00
private var currentPurchaseTask: Task<TestPurchaseResult, Never>?
private var purchaseInProgress: Bool {
return self.currentPurchaseTask != nil
}
2025-07-28 19:11:13 +02:00
init(systemInfo: SystemInfo) {
self.purchaseUI = DefaultSimulatedStorePurchaseUI(systemInfo: systemInfo)
self.dateProvider = DateProvider()
}
// For testing purposes
init(purchaseUI: SimulatedStorePurchaseUI, dateProvider: DateProvider) {
self.purchaseUI = purchaseUI
self.dateProvider = dateProvider
2025-07-28 19:11:13 +02:00
}
func purchase(product: TestStoreProduct) async -> TestPurchaseResult {
2025-07-28 19:11:13 +02:00
guard !self.purchaseInProgress else {
return .failure(ErrorUtils.operationAlreadyInProgressError())
2025-07-28 19:11:13 +02:00
}
let newPurchaseTask = Task<TestPurchaseResult, Never> { [weak self] in
guard let self else {
return .failure(ErrorUtils.unknownError())
}
let result = await self.purchaseUI.presentPurchaseUI(for: product)
let purchaseResult: TestPurchaseResult
switch result {
case .cancel:
purchaseResult = .cancel
case .error(let error):
purchaseResult = .failure(error)
case .simulateFailure:
purchaseResult = .failure(self.simulatedError)
case .simulateSuccess:
2025-10-09 15:45:51 +02:00
Logger.debug(Strings.purchase.simulating_purchase_success)
let transaction = await self.createStoreTransaction(product: product)
purchaseResult = .success(transaction)
}
return purchaseResult
}
self.currentPurchaseTask = newPurchaseTask
let purchaseResult = await newPurchaseTask.value
self.currentPurchaseTask = nil
return purchaseResult
2025-07-28 19:11:13 +02:00
}
private func createStoreTransaction(product: TestStoreProduct) async -> StoreTransaction {
let purchaseDate = self.dateProvider.now()
let purchaseToken = "test_\(purchaseDate.millisecondsSince1970)_\(UUID().uuidString)"
let storefront = await Storefront.currentStorefront
let simulatedStoreTransaction = SimulatedStoreTransaction(productIdentifier: product.productIdentifier,
purchaseDate: purchaseDate,
transactionIdentifier: purchaseToken,
storefront: storefront,
jwsRepresentation: purchaseToken)
return StoreTransaction(simulatedStoreTransaction)
}
nonisolated private var simulatedError: PurchasesError {
return ErrorUtils.testStoreSimulatedPurchaseError()
}
2025-07-28 19:11:13 +02:00
}
// MARK: - Purchase Alert Presentation
2025-07-28 19:11:13 +02:00
private extension SimulatedStorePurchaseHandler {
2025-07-28 19:11:13 +02:00
}