Cancelling a purchase and failing a purchase reach your Flutter code the same way, as a PlatformException. That is how a paywall ends up showing a red error to someone who simply changed their mind on the store sheet.
PurchasesErrorHelper.getErrorCode is what distinguishes them, and two outcomes are not enough. A single call to Purchases.purchase can end five ways: cancelled, pending, completed, failed and unresolved. Pending is the one that catches people out. A parent still has to approve the purchase, or a slow payment method has to settle, and RevenueCat grants no entitlement until the store confirms it.
Which means the paywall screen should not be what decides access. A pending purchase can complete after the paywall closes or after the app has been killed, and RevenueCat does not push that change to the device. Keep a CustomerInfo listener in an object that lives as long as the app, and have every paid feature read the entitlement from it rather than from whatever the purchase call returned.
https://www.duskolicanin.com/blog/flutter-subscription-pending-cancelled-purchase