in_app_purchase_util.dart 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420
  1. import 'dart:async';
  2. import 'dart:io';
  3. import 'package:flutter/foundation.dart';
  4. import 'package:in_app_purchase/in_app_purchase.dart';
  5. import 'package:in_app_purchase_storekit/in_app_purchase_storekit.dart';
  6. import 'package:in_app_purchase_storekit/store_kit_wrappers.dart';
  7. /// 内购工具类
  8. /// 封装了 in_app_purchase 的常用功能
  9. class InAppPurchaseUtil {
  10. InAppPurchaseUtil._();
  11. static final InAppPurchaseUtil _instance = InAppPurchaseUtil._();
  12. static InAppPurchaseUtil get instance => _instance;
  13. /// 内购实例
  14. final InAppPurchase _inAppPurchase = InAppPurchase.instance;
  15. /// 购买流订阅
  16. StreamSubscription<List<PurchaseDetails>>? _subscription;
  17. /// 是否可用
  18. bool _available = false;
  19. /// 产品列表
  20. List<ProductDetails> _products = [];
  21. /// 购买状态回调
  22. Function(List<PurchaseDetails>)? onPurchaseUpdate;
  23. /// 购买成功回调
  24. Function(PurchaseDetails)? onPurchaseSuccess;
  25. /// 购买失败回调
  26. Function(PurchaseDetails)? onPurchaseError;
  27. /// 购买取消回调
  28. Function(PurchaseDetails)? onPurchaseCancelled;
  29. /// 购买等待中回调
  30. Function(PurchaseDetails)? onPurchasePending;
  31. /// 恢复购买成功回调
  32. Function(PurchaseDetails)? onRestoreSuccess;
  33. /// 获取内购是否可用
  34. bool get isAvailable => _available;
  35. /// 获取产品列表
  36. List<ProductDetails> get products => _products;
  37. /// 初始化内购
  38. ///
  39. /// [productIds] 产品ID列表
  40. /// [onUpdate] 购买状态更新回调
  41. /// [onSuccess] 购买成功回调
  42. /// [onError] 购买失败回调
  43. /// [onCancelled] 购买取消回调
  44. /// [onPending] 购买等待中回调
  45. /// [onRestore] 恢复购买成功回调
  46. Future<bool> initialize({
  47. Function(List<PurchaseDetails>)? onUpdate,
  48. Function(PurchaseDetails)? onSuccess,
  49. Function(PurchaseDetails)? onError,
  50. Function(PurchaseDetails)? onCancelled,
  51. Function(PurchaseDetails)? onPending,
  52. Function(PurchaseDetails)? onRestore,
  53. }) async {
  54. // 设置回调
  55. onPurchaseUpdate = onUpdate;
  56. onPurchaseSuccess = onSuccess;
  57. onPurchaseError = onError;
  58. onPurchaseCancelled = onCancelled;
  59. onPurchasePending = onPending;
  60. onRestoreSuccess = onRestore;
  61. // 检查内购是否可用
  62. _available = await _inAppPurchase.isAvailable();
  63. if (!_available) {
  64. debugPrint('内购不可用');
  65. return false;
  66. }
  67. // 初始化平台特定的配置
  68. if (Platform.isIOS) {
  69. final iosPlatformAddition = _inAppPurchase
  70. .getPlatformAddition<InAppPurchaseStoreKitPlatformAddition>();
  71. await iosPlatformAddition.setDelegate(ExamplePaymentQueueDelegate());
  72. }
  73. // 监听购买流
  74. _subscription = _inAppPurchase.purchaseStream.listen(
  75. _handlePurchaseUpdate,
  76. onDone: () {
  77. _subscription?.cancel();
  78. },
  79. onError: (error) {
  80. debugPrint('购买流错误: $error');
  81. },
  82. );
  83. debugPrint('内购初始化成功');
  84. return true;
  85. }
  86. /// 加载产品信息
  87. ///
  88. /// [productIds] 产品ID列表
  89. /// 返回是否加载成功
  90. Future<bool> loadProducts(Set<String> productIds) async {
  91. if (!_available) {
  92. debugPrint('内购不可用,无法加载产品');
  93. return false;
  94. }
  95. if (productIds.isEmpty) {
  96. debugPrint('产品ID列表为空');
  97. return false;
  98. }
  99. try {
  100. final ProductDetailsResponse response = await _inAppPurchase
  101. .queryProductDetails(productIds);
  102. if (response.error != null) {
  103. debugPrint('加载产品失败: ${response.error}');
  104. return false;
  105. }
  106. if (response.productDetails.isEmpty) {
  107. debugPrint('未找到任何产品');
  108. return false;
  109. }
  110. _products = response.productDetails;
  111. // 打印产品信息
  112. for (var product in _products) {
  113. debugPrint(
  114. '产品: ${product.id}, 价格: ${product.price}, 标题: ${product.title}',
  115. );
  116. }
  117. return true;
  118. } catch (e) {
  119. debugPrint('加载产品异常: $e');
  120. return false;
  121. }
  122. }
  123. /// 根据产品ID获取产品详情
  124. ///
  125. /// [productId] 产品ID
  126. /// 返回产品详情,如果未找到返回 null
  127. ProductDetails? getProductById(String productId) {
  128. try {
  129. return _products.firstWhere((product) => product.id == productId);
  130. } catch (e) {
  131. debugPrint('未找到产品: $productId');
  132. return null;
  133. }
  134. }
  135. /// 购买产品
  136. ///
  137. /// [productDetails] 产品详情
  138. /// [isConsumable] 是否是消耗型产品,默认为 false(非消耗型产品,如订阅)
  139. /// 返回是否发起购买成功
  140. Future<bool> purchaseProduct(
  141. ProductDetails productDetails, {
  142. bool isConsumable = false,
  143. }) async {
  144. if (!_available) {
  145. debugPrint('内购不可用');
  146. return false;
  147. }
  148. try {
  149. final PurchaseParam purchaseParam = PurchaseParam(
  150. productDetails: productDetails,
  151. );
  152. bool success;
  153. if (isConsumable) {
  154. success = await _inAppPurchase.buyConsumable(
  155. purchaseParam: purchaseParam,
  156. );
  157. } else {
  158. success = await _inAppPurchase.buyNonConsumable(
  159. purchaseParam: purchaseParam,
  160. );
  161. }
  162. if (success) {
  163. debugPrint('发起购买成功: ${productDetails.id}');
  164. } else {
  165. debugPrint('发起购买失败: ${productDetails.id}');
  166. }
  167. return success;
  168. } catch (e) {
  169. debugPrint('购买异常: $e');
  170. return false;
  171. }
  172. }
  173. /// 通过产品ID购买产品
  174. ///
  175. /// [productId] 产品ID
  176. /// 返回是否发起购买成功
  177. Future<bool> purchaseProductById(String productId) async {
  178. final product = getProductById(productId);
  179. if (product == null) {
  180. debugPrint('未找到产品: $productId,请先调用 loadProducts');
  181. return false;
  182. }
  183. return await purchaseProduct(product);
  184. }
  185. /// 恢复购买
  186. ///
  187. /// 返回是否发起恢复成功
  188. Future<bool> restorePurchases() async {
  189. if (!_available) {
  190. debugPrint('内购不可用');
  191. return false;
  192. }
  193. try {
  194. await _inAppPurchase.restorePurchases();
  195. debugPrint('发起恢复购买');
  196. return true;
  197. } catch (e) {
  198. debugPrint('恢复购买异常: $e');
  199. return false;
  200. }
  201. }
  202. /// 完成购买
  203. ///
  204. /// [purchaseDetails] 购买详情
  205. Future<void> completePurchase(PurchaseDetails purchaseDetails) async {
  206. await _inAppPurchase.completePurchase(purchaseDetails);
  207. debugPrint('完成购买: ${purchaseDetails.productID}');
  208. }
  209. /// 处理购买更新
  210. void _handlePurchaseUpdate(List<PurchaseDetails> purchaseDetailsList) {
  211. // 触发统一回调
  212. onPurchaseUpdate?.call(purchaseDetailsList);
  213. // 处理每个购买详情
  214. for (var purchaseDetails in purchaseDetailsList) {
  215. debugPrint(
  216. '购买状态: ${purchaseDetails.status}, 产品: ${purchaseDetails.productID}',
  217. );
  218. switch (purchaseDetails.status) {
  219. case PurchaseStatus.pending:
  220. // 购买等待中
  221. onPurchasePending?.call(purchaseDetails);
  222. break;
  223. case PurchaseStatus.purchased:
  224. // 购买成功
  225. _handlePurchaseSuccess(purchaseDetails);
  226. break;
  227. case PurchaseStatus.restored:
  228. // 恢复购买成功
  229. _handleRestoreSuccess(purchaseDetails);
  230. break;
  231. case PurchaseStatus.error:
  232. // 购买失败
  233. _handlePurchaseError(purchaseDetails);
  234. break;
  235. case PurchaseStatus.canceled:
  236. // 购买取消
  237. onPurchaseCancelled?.call(purchaseDetails);
  238. if (purchaseDetails.pendingCompletePurchase) {
  239. completePurchase(purchaseDetails);
  240. }
  241. break;
  242. }
  243. }
  244. }
  245. /// 处理购买成功
  246. Future<void> _handlePurchaseSuccess(PurchaseDetails purchaseDetails) async {
  247. // 这里可以添加服务器验证逻辑
  248. bool valid = await _verifyPurchase(purchaseDetails);
  249. if (valid) {
  250. debugPrint('购买验证成功: ${purchaseDetails.productID}');
  251. onPurchaseSuccess?.call(purchaseDetails);
  252. } else {
  253. debugPrint('购买验证失败: ${purchaseDetails.productID}');
  254. onPurchaseError?.call(purchaseDetails);
  255. }
  256. // 完成购买
  257. if (purchaseDetails.pendingCompletePurchase) {
  258. await completePurchase(purchaseDetails);
  259. }
  260. }
  261. /// 处理恢复购买成功
  262. Future<void> _handleRestoreSuccess(PurchaseDetails purchaseDetails) async {
  263. // 验证恢复的购买
  264. bool valid = await _verifyPurchase(purchaseDetails);
  265. if (valid) {
  266. debugPrint('恢复购买验证成功: ${purchaseDetails.productID}');
  267. onRestoreSuccess?.call(purchaseDetails);
  268. }
  269. // 完成购买
  270. if (purchaseDetails.pendingCompletePurchase) {
  271. await completePurchase(purchaseDetails);
  272. }
  273. }
  274. /// 处理购买失败
  275. Future<void> _handlePurchaseError(PurchaseDetails purchaseDetails) async {
  276. debugPrint('购买失败: ${purchaseDetails.error}');
  277. onPurchaseError?.call(purchaseDetails);
  278. // 完成购买
  279. if (purchaseDetails.pendingCompletePurchase) {
  280. await completePurchase(purchaseDetails);
  281. }
  282. }
  283. /// 验证购买(需要根据实际业务实现)
  284. ///
  285. /// [purchaseDetails] 购买详情
  286. /// 返回是否验证通过
  287. Future<bool> _verifyPurchase(PurchaseDetails purchaseDetails) async {
  288. // TODO: 在这里实现服务器验证逻辑
  289. // 1. 将购买凭证发送到你的服务器
  290. // 2. 服务器向 Apple/Google 验证购买
  291. // 3. 返回验证结果
  292. // 临时返回 true,实际使用时需要实现服务器验证
  293. return true;
  294. }
  295. /// 获取过去的购买记录
  296. ///
  297. /// 注意:iOS 和 Android 都建议使用 restorePurchases() 方法来恢复购买
  298. /// 本方法已弃用,建议使用 restorePurchases() 替代
  299. @Deprecated('使用 restorePurchases() 方法替代')
  300. Future<List<PurchaseDetails>> getPastPurchases() async {
  301. if (!_available) {
  302. debugPrint('内购不可用');
  303. return [];
  304. }
  305. try {
  306. if (Platform.isIOS) {
  307. debugPrint('iOS 平台请使用 restorePurchases 来恢复购买');
  308. return [];
  309. } else if (Platform.isAndroid) {
  310. debugPrint('Android 平台请使用 restorePurchases 来恢复购买');
  311. return [];
  312. }
  313. } catch (e) {
  314. debugPrint('获取过去购买记录异常: $e');
  315. }
  316. return [];
  317. }
  318. /// 检查用户是否有有效订阅
  319. Future<bool> hasActiveSubscription(Set<String> subscriptionIds) async {
  320. if (!_available) {
  321. return false;
  322. }
  323. try {
  324. // 恢复购买
  325. await _inAppPurchase.restorePurchases();
  326. // 这里需要根据实际情况检查订阅状态
  327. // 通常需要结合服务器验证来判断
  328. return false;
  329. } catch (e) {
  330. debugPrint('检查订阅状态异常: $e');
  331. return false;
  332. }
  333. }
  334. /// 清理资源
  335. void dispose() {
  336. _subscription?.cancel();
  337. _subscription = null;
  338. debugPrint('内购资源已清理');
  339. }
  340. }
  341. /// iOS 支付队列代理示例
  342. class ExamplePaymentQueueDelegate implements SKPaymentQueueDelegateWrapper {
  343. @override
  344. bool shouldContinueTransaction(
  345. SKPaymentTransactionWrapper transaction,
  346. SKStorefrontWrapper storefront,
  347. ) {
  348. return true;
  349. }
  350. @override
  351. bool shouldShowPriceConsent() {
  352. return false;
  353. }
  354. }