Repo created

This commit is contained in:
Fr4nz D13trich 2025-11-22 13:58:55 +01:00
parent 4af19165ec
commit 68073add76
12458 changed files with 12350765 additions and 2 deletions

View file

@ -0,0 +1,59 @@
final class BillingPendingTransaction: NSObject, IBillingPendingTransaction {
private var pendingTransaction: SKPaymentTransaction?
override init() {
super.init()
SKPaymentQueue.default().add(self)
}
deinit {
SKPaymentQueue.default().remove(self)
}
var status: TransactionStatus {
let routeTransactions = SKPaymentQueue.default().transactions.filter {
var isOk = !Subscription.legacyProductIds.contains($0.payment.productIdentifier) &&
!Subscription.productIds.contains($0.payment.productIdentifier)
if isOk && $0.transactionState == .purchasing {
isOk = false
Statistics.logEvent("Pending_purchasing_transaction",
withParameters: ["productId" : $0.payment.productIdentifier])
}
return isOk
}
if routeTransactions.count > 1 {
pendingTransaction = routeTransactions.last
routeTransactions.prefix(routeTransactions.count - 1).forEach {
SKPaymentQueue.default().finishTransaction($0)
}
} else if routeTransactions.count == 1 {
pendingTransaction = routeTransactions[0]
} else {
return .none
}
switch pendingTransaction!.transactionState {
case .purchasing, .failed:
return .failed
case .purchased, .restored, .deferred:
return .paid
}
}
func finishTransaction() {
guard let transaction = pendingTransaction else {
assert(false, "There is no pending transactions")
return
}
SKPaymentQueue.default().finishTransaction(transaction)
pendingTransaction = nil
}
}
extension BillingPendingTransaction: SKPaymentTransactionObserver {
func paymentQueue(_ queue: SKPaymentQueue, updatedTransactions transactions: [SKPaymentTransaction]) {
// Do nothing. Only for SKPaymentQueue.default().transactions to work
}
}

View file

@ -0,0 +1,121 @@
fileprivate struct BillingProduct: IBillingProduct {
var productId: String {
return product.productIdentifier
}
var localizedName: String {
return product.localizedTitle
}
var price: NSDecimalNumber {
return product.price
}
var priceLocale: Locale {
return product.priceLocale
}
let product: SKProduct
init(_ product: SKProduct) {
self.product = product
}
}
final class InAppBilling: NSObject, IInAppBilling {
private var productsCompletion: ProductsCompletion?
private var paymentCompletion: PaymentCompletion?
private var productRequest: SKProductsRequest?
private var billingProduct: BillingProduct?
private var pendingTransaction: SKPaymentTransaction?
override init() {
super.init()
SKPaymentQueue.default().add(self)
}
deinit {
productRequest?.cancel()
productRequest?.delegate = nil
SKPaymentQueue.default().remove(self)
}
func requestProducts(_ productIds: Set<String>, completion: @escaping ProductsCompletion) {
productsCompletion = completion
productRequest = SKProductsRequest(productIdentifiers: productIds)
productRequest!.delegate = self
productRequest!.start()
}
func makePayment(_ product: IBillingProduct, completion: @escaping PaymentCompletion) {
guard let billingProduct = product as? BillingProduct else {
assert(false, "Wrong product type")
return
}
paymentCompletion = completion
self.billingProduct = billingProduct
SKPaymentQueue.default().add(SKPayment(product: billingProduct.product))
}
func finishTransaction() {
guard let transaction = pendingTransaction else {
assert(false, "You must call makePayment() first")
return
}
SKPaymentQueue.default().finishTransaction(transaction)
billingProduct = nil
pendingTransaction = nil
}
}
extension InAppBilling: SKProductsRequestDelegate {
func productsRequest(_ request: SKProductsRequest, didReceive response: SKProductsResponse) {
DispatchQueue.main.async { [weak self] in
let products = response.products.map { BillingProduct($0) }
self?.productsCompletion?(products, nil)
self?.productsCompletion = nil
self?.productRequest = nil
}
}
func request(_ request: SKRequest, didFailWithError error: Error) {
DispatchQueue.main.async { [weak self] in
self?.productsCompletion?(nil, error)
self?.productsCompletion = nil
self?.productRequest = nil
}
}
}
extension InAppBilling: SKPaymentTransactionObserver {
func paymentQueue(_ queue: SKPaymentQueue, updatedTransactions transactions: [SKPaymentTransaction]) {
guard let productId = billingProduct?.productId else { return }
transactions.forEach {
if ($0.payment.productIdentifier != productId) { return }
self.pendingTransaction = $0
switch $0.transactionState {
case .purchasing:
break
case .purchased:
paymentCompletion?(.success, nil)
break
case .failed:
if ($0.error?._code == SKError.paymentCancelled.rawValue) {
paymentCompletion?(.userCancelled, $0.error)
} else {
paymentCompletion?(.failed, $0.error)
}
break
case .restored:
break
case .deferred:
paymentCompletion?(.deferred, nil)
break
}
}
}
}