Skip to content

Commit 2f19ce8

Browse files
committed
Add Transaction.unfinished API and expose appAccountToken in SK2PurchaseDetails
This PR adds two new features to in_app_purchase_storekit for StoreKit 2: 1. SK2Transaction.unfinishedTransactions() - Queries only unfinished transactions, mirroring Apple's Transaction.unfinished API for better performance. 2. SK2PurchaseDetails.appAccountToken - Exposes the UUID that associates transactions with users in custom backend systems. Both features are additive and maintain full backward compatibility. Tests included for both features.
1 parent 8f72e4b commit 2f19ce8

File tree

10 files changed

+388
-269
lines changed

10 files changed

+388
-269
lines changed

packages/in_app_purchase/in_app_purchase_storekit/CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,8 @@
1+
## 0.4.7
2+
3+
* Adds `SK2Transaction.unfinishedTransactions()` method to query only unfinished transactions.
4+
* Exposes `appAccountToken` property in `SK2PurchaseDetails` for user identification.
5+
16
## 0.4.6+2
27

38
* Updates to Pigeon 26.

packages/in_app_purchase/in_app_purchase_storekit/darwin/in_app_purchase_storekit/Sources/in_app_purchase_storekit/StoreKit2/InAppPurchasePlugin+StoreKit2.swift

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -230,6 +230,22 @@ extension InAppPurchasePlugin: InAppPurchase2API {
230230
}
231231
}
232232

233+
/// Wrapper method around StoreKit2's Transaction.unfinished
234+
/// https://developer.apple.com/documentation/storekit/transaction/unfinished
235+
func unfinishedTransactions(
236+
completion: @escaping (Result<[SK2TransactionMessage], Error>) -> Void
237+
) {
238+
Task {
239+
@MainActor in
240+
do {
241+
let transactionsMsgs = await rawUnfinishedTransactions().map {
242+
$0.convertToPigeon(receipt: nil)
243+
}
244+
completion(.success(transactionsMsgs))
245+
}
246+
}
247+
}
248+
233249
func restorePurchases(completion: @escaping (Result<Void, Error>) -> Void) {
234250
Task { [weak self] in
235251
guard let self = self else { return }
@@ -362,6 +378,20 @@ extension InAppPurchasePlugin: InAppPurchase2API {
362378
return transactions
363379
}
364380

381+
/// Helper function that fetches and unwraps all verified unfinished transactions
382+
func rawUnfinishedTransactions() async -> [Transaction] {
383+
var transactions: [Transaction] = []
384+
for await verificationResult in Transaction.unfinished {
385+
switch verificationResult {
386+
case .verified(let transaction):
387+
transactions.append(transaction)
388+
case .unverified:
389+
break
390+
}
391+
}
392+
return transactions
393+
}
394+
365395
/// Helper function to fetch specific transaction
366396
func fetchTransaction(by id: UInt64) async throws -> Transaction? {
367397
for await result in Transaction.all {

packages/in_app_purchase/in_app_purchase_storekit/darwin/in_app_purchase_storekit/Sources/in_app_purchase_storekit/StoreKit2/StoreKit2Messages.g.swift

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ private func nilOrValue<T>(_ value: Any?) -> T? {
7373
return value as! T?
7474
}
7575

76-
func deepEqualsStoreKit2Messages(_ lhs: Any?, _ rhs: Any?) -> Bool {
76+
func deepEqualssk2_pigeon(_ lhs: Any?, _ rhs: Any?) -> Bool {
7777
let cleanLhs = nilOrValue(lhs) as Any?
7878
let cleanRhs = nilOrValue(rhs) as Any?
7979
switch (cleanLhs, cleanRhs) {
@@ -114,7 +114,7 @@ func deepEqualsStoreKit2Messages(_ lhs: Any?, _ rhs: Any?) -> Bool {
114114
}
115115
}
116116

117-
func deepHashStoreKit2Messages(value: Any?, hasher: inout Hasher) {
117+
func deepHashsk2_pigeon(value: Any?, hasher: inout Hasher) {
118118
if let valueList = value as? [AnyHashable] {
119119
for item in valueList { deepHashStoreKit2Messages(value: item, hasher: &hasher) }
120120
return
@@ -727,6 +727,8 @@ protocol InAppPurchase2API {
727727
func isIntroductoryOfferEligible(
728728
productId: String, completion: @escaping (Result<Bool, Error>) -> Void)
729729
func transactions(completion: @escaping (Result<[SK2TransactionMessage], Error>) -> Void)
730+
func unfinishedTransactions(
731+
completion: @escaping (Result<[SK2TransactionMessage], Error>) -> Void)
730732
func finish(id: Int64, completion: @escaping (Result<Void, Error>) -> Void)
731733
func startListeningToTransactions() throws
732734
func stopListeningToTransactions() throws
@@ -860,6 +862,24 @@ class InAppPurchase2APISetup {
860862
} else {
861863
transactionsChannel.setMessageHandler(nil)
862864
}
865+
let unfinishedTransactionsChannel = FlutterBasicMessageChannel(
866+
name:
867+
"dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchase2API.unfinishedTransactions\(channelSuffix)",
868+
binaryMessenger: binaryMessenger, codec: codec)
869+
if let api = api {
870+
unfinishedTransactionsChannel.setMessageHandler { _, reply in
871+
api.unfinishedTransactions { result in
872+
switch result {
873+
case .success(let res):
874+
reply(wrapResult(res))
875+
case .failure(let error):
876+
reply(wrapError(error))
877+
}
878+
}
879+
}
880+
} else {
881+
unfinishedTransactionsChannel.setMessageHandler(nil)
882+
}
863883
let finishChannel = FlutterBasicMessageChannel(
864884
name: "dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchase2API.finish\(channelSuffix)",
865885
binaryMessenger: binaryMessenger, codec: codec)

0 commit comments

Comments
 (0)