Skip to content

Commit

Permalink
Update the logic to refresh the receipt
Browse files Browse the repository at this point in the history
The method includes a new parameter, `updateTransactions`, which forces the update of transactions first. By default, this parameter is set to `false`.
  • Loading branch information
ns-vasilev committed Aug 8, 2024
1 parent 100feb9 commit 604f6b8
Show file tree
Hide file tree
Showing 5 changed files with 119 additions and 17 deletions.
8 changes: 8 additions & 0 deletions Sources/Flare/Classes/Flare.swift
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,14 @@ extension Flare: IFlare {
iapProvider.restore(completion)
}

public func receipt(updateTransactions: Bool) async throws -> String {
try await iapProvider.refreshReceipt(updateTransactions: updateTransactions)
}

public func receipt(updateTransactions: Bool, completion: @escaping (Result<String, IAPError>) -> Void) {
iapProvider.refreshReceipt(updateTransactions: updateTransactions, completion: completion)
}

#if os(iOS) || VISION_OS
@available(iOS 15.0, *)
@available(macOS, unavailable)
Expand Down
44 changes: 37 additions & 7 deletions Sources/Flare/Classes/IFlare.swift
Original file line number Diff line number Diff line change
Expand Up @@ -105,17 +105,31 @@ public protocol IFlare {
promotionalOffer: PromotionalOffer?
) async throws -> StoreTransaction

/// Refreshes the receipt, representing the user's transactions with your app.
/// Refreshes the receipt and optionally updates transactions.
///
/// - Parameter completion: The closure to be executed when the refresh operation ends.
func receipt(completion: @escaping Closure<Result<String, IAPError>>)
/// - Parameters:
/// - updateTransactions: A boolean indicating whether to update transactions.
/// - If `true`, the method will refresh completed transactions.
/// - If `false`, only the receipt will be refreshed.
/// - completion: A closure that gets called with the result of the refresh operation.
/// - On success, it returns a `Result<String, IAPError>` containing the updated receipt information as a `String`.
/// - On failure, it returns a `Result<String, IAPError>` with an `IAPError` describing the issue.
///
/// - Note: Use this method to handle asynchronous receipt refreshing and transaction updates with completion handler feedback.
func receipt(updateTransactions: Bool, completion: @escaping (Result<String, IAPError>) -> Void)

/// Refreshes the receipt, representing the user's transactions with your app.
/// Refreshes the receipt and optionally updates transactions.
///
/// `IAPError(error:)` if the request did fail with error.
/// - Parameter updateTransactions: A boolean indicating whether to update transactions.
/// - If `true`, the method will refresh completed transactions.
/// - If `false`, only the receipt will be refreshed.
///
/// - Returns: A receipt.
func receipt() async throws -> String
/// - Returns: A `String` containing the updated receipt information.
///
/// - Throws: An `IAPError` if the refresh process encounters an issue.
///
/// - Note: Use this method for an asynchronous refresh operation with error handling and receipt data retrieval.
func receipt(updateTransactions: Bool) async throws -> String

/// Removes a finished (i.e. failed or completed) transaction from the queue.
/// Attempting to finish a purchasing transaction will throw an exception.
Expand Down Expand Up @@ -280,4 +294,20 @@ public extension IFlare {
) async throws -> StoreTransaction {
try await purchase(product: product, options: options, promotionalOffer: nil)
}

/// Refreshes the receipt, representing the user's transactions with your app.
///
/// - Parameter completion: The closure to be executed when the refresh operation ends.
func receipt(completion: @escaping Closure<Result<String, IAPError>>) {
receipt(updateTransactions: false, completion: completion)
}

/// Refreshes the receipt, representing the user's transactions with your app.
///
/// `IAPError(error:)` if the request did fail with error.
///
/// - Returns: A receipt.
func receipt() async throws -> String {
try await receipt(updateTransactions: false)
}
}
47 changes: 37 additions & 10 deletions Sources/Flare/Classes/Providers/IAPProvider/IAPProvider.swift
Original file line number Diff line number Diff line change
Expand Up @@ -143,19 +143,46 @@ final class IAPProvider: IIAPProvider {
}
}

func refreshReceipt(completion: @escaping Closure<Result<String, IAPError>>) {
receiptRefreshProvider.refresh(requestID: UUID().uuidString) { [weak self] result in
switch result {
case .success:
if let receipt = self?.receiptRefreshProvider.receipt {
completion(.success(receipt))
} else {
completion(.failure(.receiptNotFound))
func refreshReceipt(updateTransactions: Bool) async throws -> String {
try await withCheckedThrowingContinuation { continuation in
refreshReceipt(updateTransactions: updateTransactions) { result in
continuation.resume(with: result)
}
}
}

func refreshReceipt(updateTransactions: Bool, completion: @escaping (Result<String, IAPError>) -> Void) {
let refresh = { [weak self] in
self?.receiptRefreshProvider.refresh(requestID: UUID().uuidString) { [weak self] result in
switch result {
case .success:
if let receipt = self?.receiptRefreshProvider.receipt {
completion(.success(receipt))
} else {
completion(.failure(.receiptNotFound))
}
case let .failure(error):
completion(.failure(error))
}
case let .failure(error):
completion(.failure(error))
}
}

if updateTransactions {
restore { result in
switch result {
case .success:
refresh()
case let .failure(error):
completion(.failure(IAPError.with(error: error)))
}
}
} else {
refresh()
}
}

func refreshReceipt(completion: @escaping Closure<Result<String, IAPError>>) {
refreshReceipt(updateTransactions: false, completion: completion)
}

func refreshReceipt() async throws -> String {
Expand Down
26 changes: 26 additions & 0 deletions Sources/Flare/Classes/Providers/IAPProvider/IIAPProvider.swift
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,32 @@ public protocol IIAPProvider {
promotionalOffer: PromotionalOffer?
) async throws -> StoreTransaction

/// Refreshes the receipt and optionally updates transactions.
///
/// - Parameters:
/// - updateTransactions: A boolean indicating whether to update transactions.
/// - If `true`, the method will refresh completed transactions.
/// - If `false`, only the receipt will be refreshed.
/// - completion: A closure that gets called with the result of the refresh operation.
/// - On success, it returns a `Result<String, IAPError>` containing the updated receipt information as a `String`.
/// - On failure, it returns a `Result<String, IAPError>` with an `IAPError` describing the issue.
///
/// - Note: Use this method to handle asynchronous receipt refreshing and transaction updates with completion handler feedback.
func refreshReceipt(updateTransactions: Bool, completion: @escaping (Result<String, IAPError>) -> Void)

/// Refreshes the receipt and optionally updates transactions.
///
/// - Parameter updateTransactions: A boolean indicating whether to update transactions.
/// - If `true`, the method will refresh completed transactions.
/// - If `false`, only the receipt will be refreshed.
///
/// - Returns: A `String` containing the updated receipt information.
///
/// - Throws: An `IAPError` if the refresh process encounters an issue.
///
/// - Note: Use this method for an asynchronous refresh operation with error handling and receipt data retrieval.
func refreshReceipt(updateTransactions: Bool) async throws -> String

/// Refreshes the receipt, representing the user's transactions with your app.
///
/// - Parameter completion: The closure to be executed when the refresh operation ends.
Expand Down
11 changes: 11 additions & 0 deletions Tests/FlareTests/UnitTests/TestHelpers/Mocks/IAPProviderMock.swift
Original file line number Diff line number Diff line change
Expand Up @@ -300,4 +300,15 @@ final class IAPProviderMock: IIAPProvider {
func restore() async throws {}

func restore(_: @escaping (Result<Void, any Error>) -> Void) {}

var invokedRefreshReceiptUpdateTransaction = false
var invokedRefreshReceiptUpdateTransactionCount = 0
var stubbedInvokedRefreshReceiptUpdateTransaction: String = ""
func refreshReceipt(updateTransactions _: Bool) async throws -> String {
invokedRefreshReceiptUpdateTransaction = true
invokedRefreshReceiptUpdateTransactionCount += 1
return stubbedInvokedRefreshReceiptUpdateTransaction
}

func refreshReceipt(updateTransactions _: Bool, completion _: @escaping (Result<String, IAPError>) -> Void) {}
}

0 comments on commit 604f6b8

Please sign in to comment.