mirror of
https://github.com/RevenueCat/purchases-ios
synced 2026-11-11 22:55:55 +05:45
178 lines
6.7 KiB
Swift
178 lines
6.7 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
|
||
//
|
||
// StoreTransaction.swift
|
||
//
|
||
// Created by Andrés Boedo on 2/12/21.
|
||
|
||
import Foundation
|
||
import StoreKit
|
||
|
||
/// TypeAlias to StoreKit 1's Transaction type, called `StoreKit.SKPaymentTransaction`
|
||
public typealias SK1Transaction = SKPaymentTransaction
|
||
|
||
/// TypeAlias to StoreKit 2's Transaction type, called `StoreKit.Transaction`
|
||
@available(iOS 15.0, tvOS 15.0, watchOS 8.0, macOS 12.0, *)
|
||
public typealias SK2Transaction = StoreKit.Transaction
|
||
|
||
/// Abstract class that provides access to properties of a transaction.
|
||
/// ``StoreTransaction``s can represent transactions from StoreKit 1, StoreKit 2 or
|
||
/// transactions made from other places, like Stripe, Google Play or Amazon Store.
|
||
@objc(RCStoreTransaction) public final class StoreTransaction: NSObject, StoreTransactionType {
|
||
|
||
private let transaction: StoreTransactionType
|
||
|
||
init(_ transaction: StoreTransactionType) {
|
||
self.transaction = transaction
|
||
|
||
super.init()
|
||
}
|
||
|
||
// Note: docs are inherited through `StoreTransactionType`
|
||
// swiftlint:disable missing_docs
|
||
|
||
@objc public var productIdentifier: String { self.transaction.productIdentifier }
|
||
@objc public var purchaseDate: Date { self.transaction.purchaseDate }
|
||
@objc public var transactionIdentifier: String { self.transaction.transactionIdentifier }
|
||
@objc public var quantity: Int { self.transaction.quantity }
|
||
@objc public var storefront: Storefront? { self.transaction.storefront }
|
||
@objc internal var jwsRepresentation: String? { self.transaction.jwsRepresentation }
|
||
internal var environment: StoreEnvironment? { self.transaction.environment }
|
||
|
||
var hasKnownPurchaseDate: Bool { return self.transaction.hasKnownPurchaseDate }
|
||
var hasKnownTransactionIdentifier: Bool { self.transaction.hasKnownTransactionIdentifier }
|
||
|
||
func finish(_ wrapper: PaymentQueueWrapperType, completion: @escaping @Sendable () -> Void) {
|
||
self.transaction.finish(wrapper, completion: completion)
|
||
}
|
||
|
||
// swiftlint:enable missing_docs
|
||
|
||
/// Creates an instance from any `StoreTransactionType`.
|
||
/// If `transaction` is already a wrapped `StoreTransaction` then this returns it instead.
|
||
static func from(transaction: StoreTransactionType) -> StoreTransaction {
|
||
return transaction as? StoreTransaction
|
||
?? StoreTransaction(transaction)
|
||
}
|
||
|
||
public override func isEqual(_ object: Any?) -> Bool {
|
||
self.transactionIdentifier == (object as? StoreTransactionType)?.transactionIdentifier
|
||
}
|
||
|
||
public override var hash: Int {
|
||
var hasher = Hasher()
|
||
hasher.combine(self.transactionIdentifier)
|
||
|
||
return hasher.finalize()
|
||
}
|
||
|
||
public override var description: String {
|
||
return """
|
||
<\(String(describing: StoreTransaction.self)):
|
||
identifier="\(self.transactionIdentifier)"
|
||
product="\(self.productIdentifier)"
|
||
date="\(self.purchaseDate)"
|
||
quantity=\(self.quantity)
|
||
>
|
||
"""
|
||
}
|
||
|
||
}
|
||
|
||
/// Information that represents the customer’s purchase of a product.
|
||
internal protocol StoreTransactionType: Sendable {
|
||
|
||
/// The product identifier.
|
||
var productIdentifier: String { get }
|
||
|
||
/// The date that App Store charged the user’s account for a purchased or restored product,
|
||
/// or for a subscription purchase or renewal after a lapse.
|
||
var purchaseDate: Date { get }
|
||
|
||
/// Whether the underlying transaction has a non-nil purchase date.
|
||
/// See `SK1StoreTransaction/purchaseDate``
|
||
var hasKnownPurchaseDate: Bool { get }
|
||
|
||
/// The unique identifier for the transaction.
|
||
var transactionIdentifier: String { get }
|
||
|
||
/// Whether the underlying transaction has a known transaction identifier.
|
||
/// See `SKPaymentTransaction.transactionID`.
|
||
var hasKnownTransactionIdentifier: Bool { get }
|
||
|
||
/// The number of consumable products purchased.
|
||
/// - Note: multi-quantity purchases aren't currently supported.
|
||
var quantity: Int { get }
|
||
|
||
/// The App Store storefront associated with the transaction.
|
||
/// - Note: this is only available for StoreKit 2 transactions starting with iOS 17.
|
||
var storefront: Storefront? { get }
|
||
|
||
/// The raw JWS repesentation of the transaction.
|
||
/// - Note: this is only available for StoreKit 2 transactions.
|
||
var jwsRepresentation: String? { get }
|
||
|
||
/// The server environment where the receipt was generated.
|
||
/// - Note: this is only available for StoreKit 2 transactions.
|
||
var environment: StoreEnvironment? { get }
|
||
|
||
/// Indicates to the App Store that the app delivered the purchased content
|
||
/// or enabled the service to finish the transaction.
|
||
func finish(_ wrapper: PaymentQueueWrapperType, completion: @escaping @Sendable () -> Void)
|
||
|
||
}
|
||
|
||
// MARK: - Wrapper constructors / getters
|
||
|
||
extension StoreTransaction {
|
||
|
||
internal convenience init(sk1Transaction: SK1Transaction) {
|
||
self.init(SK1StoreTransaction(sk1Transaction: sk1Transaction))
|
||
}
|
||
|
||
@available(iOS 15.0, tvOS 15.0, watchOS 8.0, macOS 12.0, *)
|
||
internal convenience init(sk2Transaction: SK2Transaction,
|
||
jwsRepresentation: String,
|
||
environmentOverride: StoreEnvironment? = nil) {
|
||
self.init(SK2StoreTransaction(sk2Transaction: sk2Transaction,
|
||
jwsRepresentation: jwsRepresentation,
|
||
environmentOverride: environmentOverride))
|
||
}
|
||
|
||
/// Returns the `SKPaymentTransaction` if this `StoreTransaction` represents a `SKPaymentTransaction`.
|
||
@objc public var sk1Transaction: SK1Transaction? {
|
||
return (self.transaction as? SK1StoreTransaction)?.underlyingSK1Transaction
|
||
}
|
||
|
||
/// Returns the `StoreKit.Transaction` if this `StoreTransaction` represents a `StoreKit.Transaction`.
|
||
@available(iOS 15.0, tvOS 15.0, watchOS 8.0, macOS 12.0, *)
|
||
public var sk2Transaction: SK2Transaction? {
|
||
return (self.transaction as? SK2StoreTransaction)?.underlyingSK2Transaction
|
||
}
|
||
|
||
}
|
||
|
||
extension StoreTransactionType {
|
||
|
||
/// - Returns: the `Storefront` associated to this transaction, or `Storefront.currentStorefront` if not available.
|
||
var storefrontOrCurrent: Storefront? {
|
||
get async {
|
||
return await self.storefront ??? (await Storefront.currentStorefront)
|
||
}
|
||
}
|
||
|
||
}
|
||
|
||
extension StoreTransaction: Identifiable {
|
||
|
||
/// The stable identity of the entity associated with this instance.
|
||
public var id: String { return self.transactionIdentifier }
|
||
|
||
}
|