|
| 1 | +// Copyright 2013 The Flutter Authors. All rights reserved. |
| 2 | +// Use of this source code is governed by a BSD-style license that can be |
| 3 | +// found in the LICENSE file. |
| 4 | + |
| 5 | +import 'dart:async'; |
| 6 | + |
| 7 | +import 'package:plugin_platform_interface/plugin_platform_interface.dart'; |
| 8 | + |
| 9 | +import 'types/types.dart'; |
| 10 | + |
| 11 | +/// The interface that implementations of in_app_purchase must implement. |
| 12 | +/// |
| 13 | +/// Platform implementations should extend this class rather than implement it as `in_app_purchase` |
| 14 | +/// does not consider newly added methods to be breaking changes. Extending this class |
| 15 | +/// (using `extends`) ensures that the subclass will get the default implementation, while |
| 16 | +/// platform implementations that `implements` this interface will be broken by newly added |
| 17 | +/// [InAppPurchasePlatform] methods. |
| 18 | +abstract class InAppPurchasePlatform extends PlatformInterface { |
| 19 | + /// Constructs a InAppPurchasePlatform. |
| 20 | + InAppPurchasePlatform() : super(token: _token); |
| 21 | + |
| 22 | + static final Object _token = Object(); |
| 23 | + |
| 24 | + /// The instance of [InAppPurchasePlatform] to use. |
| 25 | + /// |
| 26 | + /// Defaults to `null`. |
| 27 | + static InAppPurchasePlatform? get instance => _instance; |
| 28 | + |
| 29 | + static InAppPurchasePlatform? _instance; |
| 30 | + |
| 31 | + /// Platform-specific plugins should set this with their own platform-specific |
| 32 | + /// class that extends [InAppPurchasePlatform] when they register themselves. |
| 33 | + // TODO(amirh): Extract common platform interface logic. |
| 34 | + // https://github.com/flutter/flutter/issues/43368 |
| 35 | + static void setInstance(InAppPurchasePlatform instance) { |
| 36 | + PlatformInterface.verifyToken(instance, _token); |
| 37 | + _instance = instance; |
| 38 | + } |
| 39 | + |
| 40 | + /// Listen to this broadcast stream to get real time update for purchases. |
| 41 | + /// |
| 42 | + /// This stream will never close as long as the app is active. |
| 43 | + /// |
| 44 | + /// Purchase updates can happen in several situations: |
| 45 | + /// * When a purchase is triggered by user in the app. |
| 46 | + /// * When a purchase is triggered by user from the platform specific store front. |
| 47 | + /// * When a purchase is restored on the device by the user in the app. |
| 48 | + /// * If a purchase is not completed ([completePurchase] is not called on the |
| 49 | + /// purchase object) from the last app session. Purchase updates will happen |
| 50 | + /// when a new app session starts instead. |
| 51 | + /// |
| 52 | + /// IMPORTANT! You must subscribe to this stream as soon as your app launches, |
| 53 | + /// preferably before returning your main App Widget in main(). Otherwise you |
| 54 | + /// will miss purchase updated made before this stream is subscribed to. |
| 55 | + /// |
| 56 | + /// We also recommend listening to the stream with one subscription at a given |
| 57 | + /// time. If you choose to have multiple subscription at the same time, you |
| 58 | + /// should be careful at the fact that each subscription will receive all the |
| 59 | + /// events after they start to listen. |
| 60 | + Stream<List<PurchaseDetails>> get purchaseStream => |
| 61 | + throw UnimplementedError('purchaseStream has not been implemented.'); |
| 62 | + |
| 63 | + /// Returns `true` if the payment platform is ready and available. |
| 64 | + Future<bool> isAvailable() => |
| 65 | + throw UnimplementedError('isAvailable() has not been implemented.'); |
| 66 | + |
| 67 | + /// Query product details for the given set of IDs. |
| 68 | + /// |
| 69 | + /// Identifiers in the underlying payment platform, for example, [App Store |
| 70 | + /// Connect](https://appstoreconnect.apple.com/) for iOS and [Google Play |
| 71 | + /// Console](https://play.google.com/) for Android. |
| 72 | + Future<ProductDetailsResponse> queryProductDetails(Set<String> identifiers) => |
| 73 | + throw UnimplementedError( |
| 74 | + 'queryProductDetails() had not been implemented.'); |
| 75 | + |
| 76 | + /// Buy a non consumable product or subscription. |
| 77 | + /// |
| 78 | + /// Non consumable items can only be bought once. For example, a purchase that |
| 79 | + /// unlocks a special content in your app. Subscriptions are also non |
| 80 | + /// consumable products. |
| 81 | + /// |
| 82 | + /// You always need to restore all the non consumable products for user when |
| 83 | + /// they switch their phones. |
| 84 | + /// |
| 85 | + /// This method does not return the result of the purchase. Instead, after |
| 86 | + /// triggering this method, purchase updates will be sent to |
| 87 | + /// [purchaseStream]. You should [Stream.listen] to [purchaseStream] to get |
| 88 | + /// [PurchaseDetails] objects in different [PurchaseDetails.status] and update |
| 89 | + /// your UI accordingly. When the [PurchaseDetails.status] is |
| 90 | + /// [PurchaseStatus.purchased], [PurchaseStatus.restored] or |
| 91 | + /// [PurchaseStatus.error] you should deliver the content or handle the error, |
| 92 | + /// then call [completePurchase] to finish the purchasing process. |
| 93 | + /// |
| 94 | + /// This method does return whether or not the purchase request was initially |
| 95 | + /// sent successfully. |
| 96 | + /// |
| 97 | + /// Consumable items are defined differently by the different underlying |
| 98 | + /// payment platforms, and there's no way to query for whether or not the |
| 99 | + /// [ProductDetail] is a consumable at runtime. |
| 100 | + /// |
| 101 | + /// See also: |
| 102 | + /// |
| 103 | + /// * [buyConsumable], for buying a consumable product. |
| 104 | + /// * [restorePurchases], for restoring non consumable products. |
| 105 | + /// |
| 106 | + /// Calling this method for consumable items will cause unwanted behaviors! |
| 107 | + Future<bool> buyNonConsumable({required PurchaseParam purchaseParam}) => |
| 108 | + throw UnimplementedError('buyNonConsumable() has not been implemented.'); |
| 109 | + |
| 110 | + /// Buy a consumable product. |
| 111 | + /// |
| 112 | + /// Consumable items can be "consumed" to mark that they've been used and then |
| 113 | + /// bought additional times. For example, a health potion. |
| 114 | + /// |
| 115 | + /// To restore consumable purchases across devices, you should keep track of |
| 116 | + /// those purchase on your own server and restore the purchase for your users. |
| 117 | + /// Consumed products are no longer considered to be "owned" by payment |
| 118 | + /// platforms and will not be delivered by calling [restorePurchases]. |
| 119 | + /// |
| 120 | + /// Consumable items are defined differently by the different underlying |
| 121 | + /// payment platforms, and there's no way to query for whether or not the |
| 122 | + /// [ProductDetail] is a consumable at runtime. |
| 123 | + /// |
| 124 | + /// `autoConsume` is provided as a utility and will instruct the plugin to |
| 125 | + /// automatically consume the product after a succesful purchase. |
| 126 | + /// `autoConsume` is `true` by default. |
| 127 | + /// |
| 128 | + /// This method does not return the result of the purchase. Instead, after |
| 129 | + /// triggering this method, purchase updates will be sent to |
| 130 | + /// [purchaseStream]. You should [Stream.listen] to |
| 131 | + /// [purchaseStream] to get [PurchaseDetails] objects in different |
| 132 | + /// [PurchaseDetails.status] and update your UI accordingly. When the |
| 133 | + /// [PurchaseDetails.status] is [PurchaseStatus.purchased] or |
| 134 | + /// [PurchaseStatus.error], you should deliver the content or handle the |
| 135 | + /// error, then call [completePurchase] to finish the purchasing process. |
| 136 | + /// |
| 137 | + /// This method does return whether or not the purchase request was initially |
| 138 | + /// sent succesfully. |
| 139 | + /// |
| 140 | + /// See also: |
| 141 | + /// |
| 142 | + /// * [buyNonConsumable], for buying a non consumable product or |
| 143 | + /// subscription. |
| 144 | + /// * [restorePurchases], for restoring non consumable products. |
| 145 | + /// |
| 146 | + /// Calling this method for non consumable items will cause unwanted |
| 147 | + /// behaviors! |
| 148 | + Future<bool> buyConsumable({ |
| 149 | + required PurchaseParam purchaseParam, |
| 150 | + bool autoConsume = true, |
| 151 | + }) => |
| 152 | + throw UnimplementedError('buyConsumable() has not been implemented.'); |
| 153 | + |
| 154 | + /// Mark that purchased content has been delivered to the user. |
| 155 | + /// |
| 156 | + /// You are responsible for completing every [PurchaseDetails] whose |
| 157 | + /// [PurchaseDetails.status] is [PurchaseStatus.purchased] or |
| 158 | + /// [PurchaseStatus.restored]. |
| 159 | + /// Completing a [PurchaseStatus.pending] purchase will cause an exception. |
| 160 | + /// For convenience, [PurchaseDetails.pendingCompletePurchase] indicates if a |
| 161 | + /// purchase is pending for completion. |
| 162 | + /// |
| 163 | + /// The method will throw a [PurchaseException] when the purchase could not be |
| 164 | + /// finished. Depending on the [PurchaseException.errorCode] the developer |
| 165 | + /// should try to complete the purchase via this method again, or retry the |
| 166 | + /// [completePurchase] method at a later time. If the |
| 167 | + /// [PurchaseException.errorCode] indicates you should not retry there might |
| 168 | + /// be some issue with the app's code or the configuration of the app in the |
| 169 | + /// respective store. The developer is responsible to fix this issue. The |
| 170 | + /// [PurchaseException.message] field might provide more information on what |
| 171 | + /// went wrong. |
| 172 | + Future<void> completePurchase(PurchaseDetails purchase) => |
| 173 | + throw UnimplementedError('completePurchase() has not been implemented.'); |
| 174 | + |
| 175 | + /// Restore all previous purchases. |
| 176 | + /// |
| 177 | + /// The `applicationUserName` should match whatever was sent in the initial |
| 178 | + /// `PurchaseParam`, if anything. If no `applicationUserName` was specified in the initial |
| 179 | + /// `PurchaseParam`, use `null`. |
| 180 | + /// |
| 181 | + /// Restored purchases are delivered through the [purchaseStream] with a |
| 182 | + /// status of [PurchaseStatus.restored]. You should listen for these purchases, |
| 183 | + /// validate their receipts, deliver the content and mark the purchase complete |
| 184 | + /// by calling the [finishPurchase] method for each purchase. |
| 185 | + /// |
| 186 | + /// This does not return consumed products. If you want to restore unused |
| 187 | + /// consumable products, you need to persist consumable product information |
| 188 | + /// for your user on your own server. |
| 189 | + /// |
| 190 | + /// See also: |
| 191 | + /// |
| 192 | + /// * [refreshPurchaseVerificationData], for reloading failed |
| 193 | + /// [PurchaseDetails.verificationData]. |
| 194 | + Future<void> restorePurchases({String? applicationUserName}) => |
| 195 | + throw UnimplementedError('restorePurchases() has not been implemented.'); |
| 196 | +} |
0 commit comments