Repository navigation
[in_app_purchase_storekit] Use purchase(confirmIn:) for StoreKit 2 purchases - #13410
Conversation
|
This pull request is not mergeable in its current state, likely because of a merge conflict. Pre-submit CI jobs were not triggered. Pushing a new commit to this branch that resolves the issue will result in pre-submit jobs being scheduled. |
ec9ad44 to
606db97
Compare
|
@vashworth Could you try this branch? I ran locally with ios 27 on ipad simulator and it passed |
…rchases - Route StoreKit 2 purchases through Product.purchase(confirmIn:options:) when a view controller in a window scene (iOS 17+) or NSWindow (macOS 15.2+) is available, falling back to Product.purchase(options:). - Wire FlutterPluginRegistrarStub into InAppPurchase2PluginTests.setUp so tests exercise the confirmIn: path. - Update testGetTransactionJsonRepresentation to use SKTestTransaction.identifier instead of originalTransactionIdentifier, and skip failed-purchase tests on iOS 27+ due to StoreKitTest dialog hang (FB/184255116).
606db97 to
bc23db6
Compare
There was a problem hiding this comment.
Code Review
This pull request updates the StoreKit 2 purchase implementation to use Product.purchase(confirmIn:options:) when a view controller, window scene, or window is available, fallbacking to purchase(options:) otherwise. It also updates the test suite to anchor purchases to the host app's window scene and introduces a helper to skip failing simulator tests. The review feedback identifies a systematic typo where 17 is written as 27 across the changelog, code comments, and test skip conditions, which would cause the test skip logic to be bypassed on iOS 17 and 18.
| ) async throws -> Product.PurchaseResult { | ||
| #if os(iOS) | ||
| if let viewController = self.registrar?.viewController, | ||
| let windowScene = viewController.view.window?.windowScene |
There was a problem hiding this comment.
uber nit: move this to if #available (iOS 17.0, *)? Just in case accessing windowScene has side effects.
| return nil | ||
| } | ||
|
|
||
| /// Purchases `product`, anchoring the confirmation UI to the Flutter view when possible. |
There was a problem hiding this comment.
Should this be in public API docs?
|
@LouiseHsu Is this ready to merge? |
flutter/packages@670b30e...0478162 2026-11-05 [email protected] [material_ui] Document RefreshIndicator mouse behavior on desktop and web (flutter/packages#13286) 2026-11-05 [email protected] [material_ui] Make SearchAnchor.bar respect SearchBarThemeData padding (flutter/packages#13360) 2026-11-04 [email protected] [pointer_interceptor] Use dart.library.js_interop in example export (flutter/packages#13558) 2026-11-04 [email protected] [in_app_purchase_storekit] Use purchase(confirmIn:) for StoreKit 2 purchases (flutter/packages#13410) 2026-11-04 [email protected] [material_ui] Fix dismissal near minimum extent when shouldCloseOnMinExtent (flutter/packages#12545) 2026-11-04 [email protected] [material_ui] Add Material 3 Expressive `ElevatedButton` (flutter/packages#13084) 2026-11-04 [email protected] [go_router] Stop inactive StatefulShellRoute branches from vetoing pops (flutter/packages#12972) 2026-11-04 [email protected] Manual roll Flutter (stable) from abaf9c5 to c197ca5 (14 revisions) (flutter/packages#13542) 2026-11-04 [email protected] [in_app_purchase] Convert example app to Swift (flutter/packages#13556) 2026-11-04 [email protected] Sync release-go_router-18.1.3 to main (flutter/packages#13559) 2026-11-04 [email protected] Sync release-material_ui-1.10.0 to main (flutter/packages#13546) 2026-11-04 [email protected] [camera_android_camerax] Add magic number guideline to `TESTING.md` (flutter/packages#13081) 2026-11-04 [email protected] Sync release-cupertino_ui-1.1.6 to main (flutter/packages#13547) 2026-11-04 [email protected] [file_selector] Convert unit tests to Kotlin (flutter/packages#13102) 2026-11-04 [email protected] Roll Flutter from 9347202 to 3623d2d (25 revisions) (flutter/packages#13555) If this roll has caused a breakage, revert this CL and set the roller to dry run mode using the controls here: https://autoroll.skia.org/r/flutter-packages-flutter-autoroll Please CC [email protected] on the revert to ensure that a human is aware of the problem. To file a bug in Flutter: https://github.com/flutter/flutter/issues/new/choose To report a problem with the AutoRoller itself, please file a bug: https://issues.skia.org/issues/new?component=1389291&template=1850622 Documentation for the AutoRoller is here: https://skia.googlesource.com/buildbot/+doc/main/autoroll/README.md
Fixes flutter/flutter#194373 Fixes flutter/flutter#195494
This PR switches StoreKit 2 purchases from the scene-less Product.purchase(options:) to Product.purchase(confirmIn:options:) whenever the plugin has a view controller attached to a window scene (iOS 17+) or an NSWindow (macOS 15.2+).
purchase(confirmIn:)is Apple's recommended purchase API for UIKit/AppKit apps. Thescene-less variant relies on StoreKit locating a UI anchor itself; on iOS 27 / iPadOS 27
that lookup fails (
Could not find a UI anchor for … purchase) and theawaitneverreturns. In the field this surfaced as App Review rejecting iPad subscription purchases
(#194373); on the iOS 27 simulator it surfaced as every StoreKit 2 purchase test timing
out (#195494). The scene-less call remains the fallback for iOS < 17 / macOS < 15.2 and
for headless engines whose registrar has no view in a window.
Test changes
The test
setUpnow hands the plugin a registrar stub backed by the test host's rootview controller, mirroring how the plugin runs inside a real Flutter app, so the
existing purchase tests exercise the
confirmIn:path.Two further adjustments were needed for the iOS 27 ipad simulator:
testGetTransactionJsonRepresentationmakes a purchase and then asks the plugin to lookthat transaction up by its ID. It took the ID from the test framework's
SKTestTransaction.originalTransactionIdentifier, which only worked because, on earlierOS versions, that value happened to equal the StoreKit 2
Transaction.idthe pluginmatches on (both are 0 for the first purchase). On iOS 27 StoreKitTest numbers
transactions differently (
identifieris a non-zero counter,originalTransactionIdentifieris reported as 0), so the lookup found nothing. The test now uses
identifier, which is thefield that corresponds to
Transaction.idon every OS version.testFailedProductUnavilablePurchaseandtestPurchaseWithIntroductoryOfferEligibilityJWSare skipped on iOS 27+. Both make a purchase that fails by design, and on iOS 27
StoreKitTest shows an error dialog for failed purchases even with
disableDialogs(iOS 27 release notes, known issue 184255116), so the purchase never completes. Since StoreKit serializes purchases per process, each of those also blocked every later purchase test, which is the cascade you see in the CI log.
Pre-Review Checklist
[shared_preferences]///).