提交 d6302c91 authored 作者: 王鹏飞's avatar 王鹏飞

feat: migrate from flutter_inapp_purchase to official in_app_purchase plugin

Replace deprecated flutter_inapp_purchase (StoreKit 1 only) with the official in_app_purchase plugin supporting StoreKit 2 on iOS 15+. Update all IAP-related controllers (recharge, order awaiting/cancel/completed) to use InAppPurchase.instance, purchaseStream, and buyConsumable API.
上级 70946588
...@@ -7,8 +7,6 @@ PODS: ...@@ -7,8 +7,6 @@ PODS:
- Flutter (1.0.0) - Flutter (1.0.0)
- flutter_app_update (0.0.1): - flutter_app_update (0.0.1):
- Flutter - Flutter
- flutter_inapp_purchase (0.0.1):
- Flutter
- flutter_inappwebview_ios (0.0.1): - flutter_inappwebview_ios (0.0.1):
- Flutter - Flutter
- flutter_inappwebview_ios/Core (= 0.0.1) - flutter_inappwebview_ios/Core (= 0.0.1)
...@@ -30,6 +28,9 @@ PODS: ...@@ -30,6 +28,9 @@ PODS:
- WechatOpenSDK-XCFramework (~> 2.0.4) - WechatOpenSDK-XCFramework (~> 2.0.4)
- image_picker_ios (0.0.1): - image_picker_ios (0.0.1):
- Flutter - Flutter
- in_app_purchase_storekit (0.0.1):
- Flutter
- FlutterMacOS
- just_audio (0.0.1): - just_audio (0.0.1):
- Flutter - Flutter
- FlutterMacOS - FlutterMacOS
...@@ -57,12 +58,12 @@ DEPENDENCIES: ...@@ -57,12 +58,12 @@ DEPENDENCIES:
- connectivity_plus (from `.symlinks/plugins/connectivity_plus/ios`) - connectivity_plus (from `.symlinks/plugins/connectivity_plus/ios`)
- Flutter (from `Flutter`) - Flutter (from `Flutter`)
- flutter_app_update (from `.symlinks/plugins/flutter_app_update/ios`) - flutter_app_update (from `.symlinks/plugins/flutter_app_update/ios`)
- flutter_inapp_purchase (from `.symlinks/plugins/flutter_inapp_purchase/ios`)
- flutter_inappwebview_ios (from `.symlinks/plugins/flutter_inappwebview_ios/ios`) - flutter_inappwebview_ios (from `.symlinks/plugins/flutter_inappwebview_ios/ios`)
- flutter_sound (from `.symlinks/plugins/flutter_sound/ios`) - flutter_sound (from `.symlinks/plugins/flutter_sound/ios`)
- flutter_tts (from `.symlinks/plugins/flutter_tts/ios`) - flutter_tts (from `.symlinks/plugins/flutter_tts/ios`)
- fluwx (from `.symlinks/plugins/fluwx/ios`) - fluwx (from `.symlinks/plugins/fluwx/ios`)
- image_picker_ios (from `.symlinks/plugins/image_picker_ios/ios`) - image_picker_ios (from `.symlinks/plugins/image_picker_ios/ios`)
- in_app_purchase_storekit (from `.symlinks/plugins/in_app_purchase_storekit/darwin`)
- just_audio (from `.symlinks/plugins/just_audio/darwin`) - just_audio (from `.symlinks/plugins/just_audio/darwin`)
- package_info_plus (from `.symlinks/plugins/package_info_plus/ios`) - package_info_plus (from `.symlinks/plugins/package_info_plus/ios`)
- permission_handler_apple (from `.symlinks/plugins/permission_handler_apple/ios`) - permission_handler_apple (from `.symlinks/plugins/permission_handler_apple/ios`)
...@@ -86,8 +87,6 @@ EXTERNAL SOURCES: ...@@ -86,8 +87,6 @@ EXTERNAL SOURCES:
:path: Flutter :path: Flutter
flutter_app_update: flutter_app_update:
:path: ".symlinks/plugins/flutter_app_update/ios" :path: ".symlinks/plugins/flutter_app_update/ios"
flutter_inapp_purchase:
:path: ".symlinks/plugins/flutter_inapp_purchase/ios"
flutter_inappwebview_ios: flutter_inappwebview_ios:
:path: ".symlinks/plugins/flutter_inappwebview_ios/ios" :path: ".symlinks/plugins/flutter_inappwebview_ios/ios"
flutter_sound: flutter_sound:
...@@ -98,6 +97,8 @@ EXTERNAL SOURCES: ...@@ -98,6 +97,8 @@ EXTERNAL SOURCES:
:path: ".symlinks/plugins/fluwx/ios" :path: ".symlinks/plugins/fluwx/ios"
image_picker_ios: image_picker_ios:
:path: ".symlinks/plugins/image_picker_ios/ios" :path: ".symlinks/plugins/image_picker_ios/ios"
in_app_purchase_storekit:
:path: ".symlinks/plugins/in_app_purchase_storekit/darwin"
just_audio: just_audio:
:path: ".symlinks/plugins/just_audio/darwin" :path: ".symlinks/plugins/just_audio/darwin"
package_info_plus: package_info_plus:
...@@ -116,13 +117,13 @@ SPEC CHECKSUMS: ...@@ -116,13 +117,13 @@ SPEC CHECKSUMS:
connectivity_plus: 481668c94744c30c53b8895afb39159d1e619bdf connectivity_plus: 481668c94744c30c53b8895afb39159d1e619bdf
Flutter: cabc95a1d2626b1b06e7179b784ebcf0c0cde467 Flutter: cabc95a1d2626b1b06e7179b784ebcf0c0cde467
flutter_app_update: 816fdb2e30e4832a7c45e3f108d391c42ef040a9 flutter_app_update: 816fdb2e30e4832a7c45e3f108d391c42ef040a9
flutter_inapp_purchase: 722da12971a50f306f37e62fc1aaf576b1cbecf6
flutter_inappwebview_ios: b89ba3482b96fb25e00c967aae065701b66e9b99 flutter_inappwebview_ios: b89ba3482b96fb25e00c967aae065701b66e9b99
flutter_sound: d95194f6476c9ad211d22b3a414d852c12c7ca44 flutter_sound: d95194f6476c9ad211d22b3a414d852c12c7ca44
flutter_sound_core: 7f2626d249d3a57bfa6da892ef7e22d234482c1a flutter_sound_core: 7f2626d249d3a57bfa6da892ef7e22d234482c1a
flutter_tts: b88dbc8655d3dc961bc4a796e4e16a4cc1795833 flutter_tts: b88dbc8655d3dc961bc4a796e4e16a4cc1795833
fluwx: 49b4472b54762abde51daa0bc348ba05520016ac fluwx: 49b4472b54762abde51daa0bc348ba05520016ac
image_picker_ios: e0ece4aa2a75771a7de3fa735d26d90817041326 image_picker_ios: e0ece4aa2a75771a7de3fa735d26d90817041326
in_app_purchase_storekit: 22cca7d08eebca9babdf4d07d0baccb73325d3c8
just_audio: 4e391f57b79cad2b0674030a00453ca5ce817eed just_audio: 4e391f57b79cad2b0674030a00453ca5ce817eed
OrderedSet: e539b66b644ff081c73a262d24ad552a69be3a94 OrderedSet: e539b66b644ff081c73a262d24ad552a69be3a94
package_info_plus: 566e1b7a2f3900e4b0020914ad3fc051dcc95596 package_info_plus: 566e1b7a2f3900e4b0020914ad3fc051dcc95596
......
...@@ -7,7 +7,7 @@ import 'package:flutter/material.dart'; ...@@ -7,7 +7,7 @@ import 'package:flutter/material.dart';
import 'package:flutter_book/apis/index.dart'; import 'package:flutter_book/apis/index.dart';
import 'package:flutter_book/models/index.dart'; import 'package:flutter_book/models/index.dart';
import 'package:flutter_book/theme.dart'; import 'package:flutter_book/theme.dart';
import 'package:flutter_inapp_purchase/flutter_inapp_purchase.dart'; import 'package:in_app_purchase/in_app_purchase.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:fluwx/fluwx.dart'; import 'package:fluwx/fluwx.dart';
import 'package:get/get.dart'; import 'package:get/get.dart';
...@@ -24,4 +24,4 @@ import 'dart:io'; ...@@ -24,4 +24,4 @@ import 'dart:io';
part 'view.dart'; part 'view.dart';
part 'controller.dart'; part 'controller.dart';
\ No newline at end of file
...@@ -56,8 +56,11 @@ class _CoinRechargePageState extends State<CoinRechargePage> ...@@ -56,8 +56,11 @@ class _CoinRechargePageState extends State<CoinRechargePage>
Container( Container(
margin: EdgeInsets.symmetric(horizontal: 15.w), margin: EdgeInsets.symmetric(horizontal: 15.w),
child: CustomGradientButton( child: CustomGradientButton(
text: '立即充值${controller.rechargeModel.priceName ?? ''}', text: controller.isSubmitting
isEnabled: true, ? '正在充值...'
: '立即充值${controller.rechargeModel.priceName ?? ''}',
isEnabled: controller.rechargeModel.identifying != null &&
!controller.isSubmitting,
onPressed: () { onPressed: () {
controller.createRechargeOrder(); controller.createRechargeOrder();
}, },
......
...@@ -20,14 +20,10 @@ class UserOrderCoinAwaitingController extends GetxController { ...@@ -20,14 +20,10 @@ class UserOrderCoinAwaitingController extends GetxController {
// 微信 // 微信
Fluwx fluwx = Fluwx(); Fluwx fluwx = Fluwx();
late PayOrderModel payOrderModel; late PayOrderModel payOrderModel;
dynamic _purchaseUpdatedSubscription; StreamSubscription<List<PurchaseDetails>>? _purchaseUpdatedSubscription;
dynamic _purchaseErrorSubscription; PurchaseDetails? _resultItem;
dynamic _connectionSubscription;
PurchasedItem? _resultItem;
List<IAPItem> _items = []; List<ProductDetails> _items = [];
///未完成的订单
final List<PurchasedItem> _pendingPurchases = [];
// 紫荆币数据 // 紫荆币数据
List<CoinModel> data = []; List<CoinModel> data = [];
...@@ -37,7 +33,6 @@ class UserOrderCoinAwaitingController extends GetxController { ...@@ -37,7 +33,6 @@ class UserOrderCoinAwaitingController extends GetxController {
if(Platform.isIOS){ if(Platform.isIOS){
_getProduct(); _getProduct();
initPlatformState(); initPlatformState();
_getPendingPurchase();
} }
super.onInit(); super.onInit();
} }
...@@ -50,42 +45,27 @@ class UserOrderCoinAwaitingController extends GetxController { ...@@ -50,42 +45,27 @@ class UserOrderCoinAwaitingController extends GetxController {
@override @override
void onClose() { void onClose() {
// controller.onRefresh();
if (_connectionSubscription != null) {
_connectionSubscription.cancel();
_connectionSubscription = null;
}
if (_purchaseUpdatedSubscription != null) { if (_purchaseUpdatedSubscription != null) {
_purchaseUpdatedSubscription.cancel(); _purchaseUpdatedSubscription!.cancel();
_purchaseUpdatedSubscription = null; _purchaseUpdatedSubscription = null;
} }
if (_purchaseErrorSubscription != null) {
_purchaseErrorSubscription.cancel();
_purchaseErrorSubscription = null;
}
super.onClose(); super.onClose();
} }
/// 初始化 /// 初始化
Future<void> initPlatformState() async{ Future<void> initPlatformState() async{
await FlutterInappPurchase.instance.initialize(); final available = await InAppPurchase.instance.isAvailable();
_iapLog('IAP store available: $available');
_connectionSubscription = FlutterInappPurchase.connectionUpdated.listen((connected) { _purchaseUpdatedSubscription =
}); InAppPurchase.instance.purchaseStream.listen(
_listenToPurchaseUpdated,
_purchaseUpdatedSubscription = FlutterInappPurchase.purchaseUpdated.listen((productItem) { onError: (error) {
CustomToast.dismiss(); CustomToast.dismiss();
if(productItem != null){ _iapLog('IAP purchase stream error: $error');
_resultItem = productItem; Toast.show('内购发生错误,请稍后重试');
requestOrderStatus(); },
} );
});
_purchaseErrorSubscription = FlutterInappPurchase.purchaseError.listen((purchaseError) {
CustomToast.dismiss();
Toast.show(purchaseError!.message.toString());
});
} }
...@@ -130,14 +110,20 @@ class UserOrderCoinAwaitingController extends GetxController { ...@@ -130,14 +110,20 @@ class UserOrderCoinAwaitingController extends GetxController {
.map((coinModel) => coinModel.identifying!) .map((coinModel) => coinModel.identifying!)
.toList(); .toList();
List<IAPItem> items = await FlutterInappPurchase.instance.getProducts(productList); if (productList.isEmpty) return;
for (var item in items) {
_items.add(item); _iapLog('IAP productList: $productList');
try {
final response =
await InAppPurchase.instance.queryProductDetails(productList.toSet());
_items = response.productDetails;
_iapLog('IAP query error: ${response.error}');
_iapLog('IAP not found ids: ${response.notFoundIDs}');
_iapLog('IAP loaded items: ${_items.map((item) => item.id).toList()}');
} catch (error) {
_items = [];
_iapLog('IAP getProducts failed: $error');
} }
_items = items;
// update();
} }
/// 支付订单 /// 支付订单
...@@ -145,8 +131,8 @@ class UserOrderCoinAwaitingController extends GetxController { ...@@ -145,8 +131,8 @@ class UserOrderCoinAwaitingController extends GetxController {
// 苹果支付 // 苹果支付
if (model.payType ==3){ if (model.payType ==3){
// 发起苹果支付 // 发起苹果支付
IAPItem targetItem = _items.firstWhere( ProductDetails targetItem = _items.firstWhere(
(item) => item.productId == model.goodsId, (item) => item.id == model.goodsId,
); );
_requestPurchase(targetItem); _requestPurchase(targetItem);
} }
...@@ -178,10 +164,19 @@ class UserOrderCoinAwaitingController extends GetxController { ...@@ -178,10 +164,19 @@ class UserOrderCoinAwaitingController extends GetxController {
} }
/// 苹果支付 /// 苹果支付
void _requestPurchase(IAPItem item) { void _requestPurchase(ProductDetails item) {
CustomToast.loading(); CustomToast.loading();
FlutterInappPurchase.instance try {
.requestPurchase(item.productId!); final purchaseParam = PurchaseParam(productDetails: item);
InAppPurchase.instance.buyConsumable(
purchaseParam: purchaseParam,
autoConsume: true,
);
} catch (error) {
CustomToast.dismiss();
_iapLog('IAP request purchase failed: $error');
Toast.show('内购发起失败,请稍后重试');
}
} }
/// 支付宝支付 /// 支付宝支付
...@@ -220,7 +215,7 @@ class UserOrderCoinAwaitingController extends GetxController { ...@@ -220,7 +215,7 @@ class UserOrderCoinAwaitingController extends GetxController {
/// 获取订单状态 /// 获取订单状态
void requestOrderStatus() async { void requestOrderStatus() async {
final result = await ShopAPI.orderStatus(orderNumber: model.ordersnum??'',receipt:_resultItem != null?_resultItem!.transactionReceipt.toString():''); final result = await ShopAPI.orderStatus(orderNumber: model.ordersnum??'',receipt:_resultItem != null?_resultItem!.verificationData.serverVerificationData.toString():'');
if (result.paySuccess == 1){ if (result.paySuccess == 1){
Toast.show('订单支付完成'); Toast.show('订单支付完成');
if(context.mounted){ if(context.mounted){
...@@ -239,7 +234,6 @@ class UserOrderCoinAwaitingController extends GetxController { ...@@ -239,7 +234,6 @@ class UserOrderCoinAwaitingController extends GetxController {
} }
StorageService.to.setObject(kFailOrder, temp); StorageService.to.setObject(kFailOrder, temp);
} }
FlutterInappPurchase.instance.finishTransactionIOS(_resultItem!.transactionId!);
} }
} }
else{ else{
...@@ -248,8 +242,8 @@ class UserOrderCoinAwaitingController extends GetxController { ...@@ -248,8 +242,8 @@ class UserOrderCoinAwaitingController extends GetxController {
// 保存未支付的订单 // 保存未支付的订单
Map<String,String> failOrder = { Map<String,String> failOrder = {
'orderNum':payOrderModel.ordersnum??'', 'orderNum':payOrderModel.ordersnum??'',
'transactionReceipt':_resultItem!.transactionReceipt.toString(), 'transactionReceipt':_resultItem!.verificationData.serverVerificationData.toString(),
'transactionId':_resultItem!.transactionId.toString() 'transactionId':_resultItem!.purchaseID.toString()
}; };
List<Map> failOrderList = []; List<Map> failOrderList = [];
failOrderList.add(failOrder); failOrderList.add(failOrder);
...@@ -259,23 +253,33 @@ class UserOrderCoinAwaitingController extends GetxController { ...@@ -259,23 +253,33 @@ class UserOrderCoinAwaitingController extends GetxController {
} }
///获取未完成的购买 void _iapLog(String message) {
Future _getPendingPurchase() async { Console.log(message);
List<PurchasedItem>? items = await FlutterInappPurchase.instance.getPurchaseHistory(); debugPrint(message);
for (var item in items!) { }
_pendingPurchases.add(item);
} Future<void> _listenToPurchaseUpdated(
if(StorageService.to.getObject(kFailOrder) !=null){ List<PurchaseDetails> purchaseDetailsList) async {
List<Map<String,String>> failOrderList = StorageService.to.getObject(kFailOrder) as List<Map<String, String>>; for (final purchaseDetails in purchaseDetailsList) {
for (var item in _pendingPurchases) { _iapLog(
for (var element in failOrderList) { 'IAP purchase update: ${purchaseDetails.productID}, ${purchaseDetails.status}, ${purchaseDetails.error}');
if(element['transactionId'] == item.transactionId){ if (purchaseDetails.status == PurchaseStatus.pending) {
requestOrderStatus(); continue;
} }
} CustomToast.dismiss();
if (purchaseDetails.status == PurchaseStatus.error) {
Toast.show(purchaseDetails.error?.message ?? '内购发生错误,请稍后重试');
} else if (purchaseDetails.status == PurchaseStatus.canceled) {
Toast.show('支付已取消');
} else if (purchaseDetails.status == PurchaseStatus.purchased ||
purchaseDetails.status == PurchaseStatus.restored) {
_resultItem = purchaseDetails;
requestOrderStatus();
}
if (purchaseDetails.pendingCompletePurchase) {
await InAppPurchase.instance.completePurchase(purchaseDetails);
} }
} }
} }
} }
library user_order_coin_awaiting; library user_order_coin_awaiting;
import 'dart:async';
import 'dart:io'; import 'dart:io';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_book/theme.dart'; import 'package:flutter_book/theme.dart';
import 'package:flutter_book/utils/index.dart'; import 'package:flutter_book/utils/index.dart';
import 'package:flutter_book/widgets/index.dart'; import 'package:flutter_book/widgets/index.dart';
import 'package:flutter_inapp_purchase/flutter_inapp_purchase.dart'; import 'package:in_app_purchase/in_app_purchase.dart';
import 'package:fluwx/fluwx.dart'; import 'package:fluwx/fluwx.dart';
import 'package:get/get.dart'; import 'package:get/get.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart';
......
...@@ -20,14 +20,10 @@ class UserOrderCoinCancelController extends GetxController { ...@@ -20,14 +20,10 @@ class UserOrderCoinCancelController extends GetxController {
// 微信 // 微信
Fluwx fluwx = Fluwx(); Fluwx fluwx = Fluwx();
late PayOrderModel payOrderModel; late PayOrderModel payOrderModel;
dynamic _purchaseUpdatedSubscription; StreamSubscription<List<PurchaseDetails>>? _purchaseUpdatedSubscription;
dynamic _purchaseErrorSubscription; PurchaseDetails? _resultItem;
dynamic _connectionSubscription;
PurchasedItem? _resultItem;
List<IAPItem> _items = []; List<ProductDetails> _items = [];
///未完成的订单
final List<PurchasedItem> _pendingPurchases = [];
List<CoinModel> data = []; List<CoinModel> data = [];
...@@ -37,7 +33,6 @@ class UserOrderCoinCancelController extends GetxController { ...@@ -37,7 +33,6 @@ class UserOrderCoinCancelController extends GetxController {
if(Platform.isIOS){ if(Platform.isIOS){
_getProduct(); _getProduct();
initPlatformState(); initPlatformState();
_getPendingPurchase();
} }
super.onInit(); super.onInit();
} }
...@@ -50,43 +45,27 @@ class UserOrderCoinCancelController extends GetxController { ...@@ -50,43 +45,27 @@ class UserOrderCoinCancelController extends GetxController {
@override @override
void onClose() { void onClose() {
// controller.onRefresh();
if (_connectionSubscription != null) {
_connectionSubscription.cancel();
_connectionSubscription = null;
}
if (_purchaseUpdatedSubscription != null) { if (_purchaseUpdatedSubscription != null) {
_purchaseUpdatedSubscription.cancel(); _purchaseUpdatedSubscription!.cancel();
_purchaseUpdatedSubscription = null; _purchaseUpdatedSubscription = null;
} }
if (_purchaseErrorSubscription != null) {
_purchaseErrorSubscription.cancel();
_purchaseErrorSubscription = null;
}
super.onClose(); super.onClose();
} }
/// 初始化 /// 初始化
Future<void> initPlatformState() async{ Future<void> initPlatformState() async{
await FlutterInappPurchase.instance.initialize(); final available = await InAppPurchase.instance.isAvailable();
_iapLog('IAP store available: $available');
_purchaseUpdatedSubscription =
_connectionSubscription = FlutterInappPurchase.connectionUpdated.listen((connected) { InAppPurchase.instance.purchaseStream.listen(
}); _listenToPurchaseUpdated,
onError: (error) {
_purchaseUpdatedSubscription = FlutterInappPurchase.purchaseUpdated.listen((productItem) { CustomToast.dismiss();
CustomToast.dismiss(); _iapLog('IAP purchase stream error: $error');
if(productItem != null){ Toast.show('内购发生错误,请稍后重试');
_resultItem = productItem; },
requestOrderStatus(); );
}
});
_purchaseErrorSubscription = FlutterInappPurchase.purchaseError.listen((purchaseError) {
CustomToast.dismiss();
Toast.show(purchaseError!.message.toString());
});
} }
...@@ -131,13 +110,20 @@ class UserOrderCoinCancelController extends GetxController { ...@@ -131,13 +110,20 @@ class UserOrderCoinCancelController extends GetxController {
.map((coinModel) => coinModel.identifying!) .map((coinModel) => coinModel.identifying!)
.toList(); .toList();
List<IAPItem> items = await FlutterInappPurchase.instance.getProducts(productList); if (productList.isEmpty) return;
for (var item in items) {
_items.add(item); _iapLog('IAP productList: $productList');
try {
final response =
await InAppPurchase.instance.queryProductDetails(productList.toSet());
_items = response.productDetails;
_iapLog('IAP query error: ${response.error}');
_iapLog('IAP not found ids: ${response.notFoundIDs}');
_iapLog('IAP loaded items: ${_items.map((item) => item.id).toList()}');
} catch (error) {
_items = [];
_iapLog('IAP getProducts failed: $error');
} }
_items = items;
// update();
} }
/// 支付订单 /// 支付订单
...@@ -145,8 +131,8 @@ class UserOrderCoinCancelController extends GetxController { ...@@ -145,8 +131,8 @@ class UserOrderCoinCancelController extends GetxController {
// 苹果支付 // 苹果支付
if (model.payType ==3){ if (model.payType ==3){
// 发起苹果支付 // 发起苹果支付
IAPItem targetItem = _items.firstWhere( ProductDetails targetItem = _items.firstWhere(
(item) => item.productId == model.goodsId, (item) => item.id == model.goodsId,
); );
_requestPurchase(targetItem); _requestPurchase(targetItem);
} }
...@@ -178,10 +164,19 @@ class UserOrderCoinCancelController extends GetxController { ...@@ -178,10 +164,19 @@ class UserOrderCoinCancelController extends GetxController {
} }
/// 苹果支付 /// 苹果支付
void _requestPurchase(IAPItem item) { void _requestPurchase(ProductDetails item) {
CustomToast.loading(); CustomToast.loading();
FlutterInappPurchase.instance try {
.requestPurchase(item.productId!); final purchaseParam = PurchaseParam(productDetails: item);
InAppPurchase.instance.buyConsumable(
purchaseParam: purchaseParam,
autoConsume: true,
);
} catch (error) {
CustomToast.dismiss();
_iapLog('IAP request purchase failed: $error');
Toast.show('内购发起失败,请稍后重试');
}
} }
/// 支付宝支付 /// 支付宝支付
...@@ -220,7 +215,7 @@ class UserOrderCoinCancelController extends GetxController { ...@@ -220,7 +215,7 @@ class UserOrderCoinCancelController extends GetxController {
/// 获取订单状态 /// 获取订单状态
void requestOrderStatus() async { void requestOrderStatus() async {
final result = await ShopAPI.orderStatus(orderNumber: model.ordersnum??'',receipt:_resultItem != null?_resultItem!.transactionReceipt.toString():''); final result = await ShopAPI.orderStatus(orderNumber: model.ordersnum??'',receipt:_resultItem != null?_resultItem!.verificationData.serverVerificationData.toString():'');
if (result.paySuccess == 1){ if (result.paySuccess == 1){
Toast.show('订单支付完成'); Toast.show('订单支付完成');
if(context.mounted){ if(context.mounted){
...@@ -239,7 +234,6 @@ class UserOrderCoinCancelController extends GetxController { ...@@ -239,7 +234,6 @@ class UserOrderCoinCancelController extends GetxController {
} }
StorageService.to.setObject(kFailOrder, temp); StorageService.to.setObject(kFailOrder, temp);
} }
FlutterInappPurchase.instance.finishTransactionIOS(_resultItem!.transactionId!);
} }
} }
else{ else{
...@@ -248,8 +242,8 @@ class UserOrderCoinCancelController extends GetxController { ...@@ -248,8 +242,8 @@ class UserOrderCoinCancelController extends GetxController {
// 保存未支付的订单 // 保存未支付的订单
Map<String,String> failOrder = { Map<String,String> failOrder = {
'orderNum':payOrderModel.ordersnum??'', 'orderNum':payOrderModel.ordersnum??'',
'transactionReceipt':_resultItem!.transactionReceipt.toString(), 'transactionReceipt':_resultItem!.verificationData.serverVerificationData.toString(),
'transactionId':_resultItem!.transactionId.toString() 'transactionId':_resultItem!.purchaseID.toString()
}; };
List<Map> failOrderList = []; List<Map> failOrderList = [];
failOrderList.add(failOrder); failOrderList.add(failOrder);
...@@ -259,25 +253,33 @@ class UserOrderCoinCancelController extends GetxController { ...@@ -259,25 +253,33 @@ class UserOrderCoinCancelController extends GetxController {
} }
///获取未完成的购买 void _iapLog(String message) {
Future _getPendingPurchase() async { Console.log(message);
List<PurchasedItem>? items = await FlutterInappPurchase.instance.getPurchaseHistory(); debugPrint(message);
for (var item in items!) { }
_pendingPurchases.add(item);
} Future<void> _listenToPurchaseUpdated(
if(StorageService.to.getObject(kFailOrder) !=null){ List<PurchaseDetails> purchaseDetailsList) async {
List<Map<String,String>> failOrderList = StorageService.to.getObject(kFailOrder) as List<Map<String, String>>; for (final purchaseDetails in purchaseDetailsList) {
for (var item in _pendingPurchases) { _iapLog(
for (var element in failOrderList) { 'IAP purchase update: ${purchaseDetails.productID}, ${purchaseDetails.status}, ${purchaseDetails.error}');
if(element['transactionId'] == item.transactionId){ if (purchaseDetails.status == PurchaseStatus.pending) {
requestOrderStatus(); continue;
} }
} CustomToast.dismiss();
if (purchaseDetails.status == PurchaseStatus.error) {
Toast.show(purchaseDetails.error?.message ?? '内购发生错误,请稍后重试');
} else if (purchaseDetails.status == PurchaseStatus.canceled) {
Toast.show('支付已取消');
} else if (purchaseDetails.status == PurchaseStatus.purchased ||
purchaseDetails.status == PurchaseStatus.restored) {
_resultItem = purchaseDetails;
requestOrderStatus();
}
if (purchaseDetails.pendingCompletePurchase) {
await InAppPurchase.instance.completePurchase(purchaseDetails);
} }
} }
} }
} }
library user_order_coin_cancel; library user_order_coin_cancel;
import 'dart:async';
import 'dart:io'; import 'dart:io';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_book/theme.dart'; import 'package:flutter_book/theme.dart';
import 'package:flutter_book/utils/index.dart'; import 'package:flutter_book/utils/index.dart';
import 'package:flutter_book/widgets/index.dart'; import 'package:flutter_book/widgets/index.dart';
import 'package:flutter_inapp_purchase/flutter_inapp_purchase.dart'; import 'package:in_app_purchase/in_app_purchase.dart';
import 'package:fluwx/fluwx.dart'; import 'package:fluwx/fluwx.dart';
import 'package:get/get.dart'; import 'package:get/get.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart';
......
...@@ -20,13 +20,9 @@ class UserOrderCoinCompletedController extends GetxController { ...@@ -20,13 +20,9 @@ class UserOrderCoinCompletedController extends GetxController {
// 微信 // 微信
Fluwx fluwx = Fluwx(); Fluwx fluwx = Fluwx();
late PayOrderModel payOrderModel; late PayOrderModel payOrderModel;
dynamic _purchaseUpdatedSubscription; StreamSubscription<List<PurchaseDetails>>? _purchaseUpdatedSubscription;
dynamic _purchaseErrorSubscription; PurchaseDetails? _resultItem;
dynamic _connectionSubscription; List<ProductDetails> _items = [];
PurchasedItem? _resultItem;
List<IAPItem> _items = [];
///未完成的订单
final List<PurchasedItem> _pendingPurchases = [];
List<CoinModel> data = []; List<CoinModel> data = [];
...@@ -36,7 +32,6 @@ class UserOrderCoinCompletedController extends GetxController { ...@@ -36,7 +32,6 @@ class UserOrderCoinCompletedController extends GetxController {
if(Platform.isIOS){ if(Platform.isIOS){
_getProduct(); _getProduct();
initPlatformState(); initPlatformState();
_getPendingPurchase();
} }
super.onInit(); super.onInit();
} }
...@@ -49,43 +44,27 @@ class UserOrderCoinCompletedController extends GetxController { ...@@ -49,43 +44,27 @@ class UserOrderCoinCompletedController extends GetxController {
@override @override
void onClose() { void onClose() {
// controller.onRefresh();
if (_connectionSubscription != null) {
_connectionSubscription.cancel();
_connectionSubscription = null;
}
if (_purchaseUpdatedSubscription != null) { if (_purchaseUpdatedSubscription != null) {
_purchaseUpdatedSubscription.cancel(); _purchaseUpdatedSubscription!.cancel();
_purchaseUpdatedSubscription = null; _purchaseUpdatedSubscription = null;
} }
if (_purchaseErrorSubscription != null) {
_purchaseErrorSubscription.cancel();
_purchaseErrorSubscription = null;
}
super.onClose(); super.onClose();
} }
/// 初始化 /// 初始化
Future<void> initPlatformState() async{ Future<void> initPlatformState() async{
await FlutterInappPurchase.instance.initialize(); final available = await InAppPurchase.instance.isAvailable();
_iapLog('IAP store available: $available');
_purchaseUpdatedSubscription =
_connectionSubscription = FlutterInappPurchase.connectionUpdated.listen((connected) { InAppPurchase.instance.purchaseStream.listen(
}); _listenToPurchaseUpdated,
onError: (error) {
_purchaseUpdatedSubscription = FlutterInappPurchase.purchaseUpdated.listen((productItem) { CustomToast.dismiss();
CustomToast.dismiss(); _iapLog('IAP purchase stream error: $error');
if(productItem != null){ Toast.show('内购发生错误,请稍后重试');
_resultItem = productItem; },
requestOrderStatus(); );
}
});
_purchaseErrorSubscription = FlutterInappPurchase.purchaseError.listen((purchaseError) {
CustomToast.dismiss();
Toast.show(purchaseError!.message.toString());
});
} }
...@@ -131,13 +110,20 @@ class UserOrderCoinCompletedController extends GetxController { ...@@ -131,13 +110,20 @@ class UserOrderCoinCompletedController extends GetxController {
.map((coinModel) => coinModel.identifying!) .map((coinModel) => coinModel.identifying!)
.toList(); .toList();
List<IAPItem> items = await FlutterInappPurchase.instance.getProducts(productList); if (productList.isEmpty) return;
for (var item in items) {
_items.add(item); _iapLog('IAP productList: $productList');
try {
final response =
await InAppPurchase.instance.queryProductDetails(productList.toSet());
_items = response.productDetails;
_iapLog('IAP query error: ${response.error}');
_iapLog('IAP not found ids: ${response.notFoundIDs}');
_iapLog('IAP loaded items: ${_items.map((item) => item.id).toList()}');
} catch (error) {
_items = [];
_iapLog('IAP getProducts failed: $error');
} }
_items = items;
// update();
} }
/// 支付订单 /// 支付订单
...@@ -145,8 +131,8 @@ class UserOrderCoinCompletedController extends GetxController { ...@@ -145,8 +131,8 @@ class UserOrderCoinCompletedController extends GetxController {
// 苹果支付 // 苹果支付
if (model.payType ==3){ if (model.payType ==3){
// 发起苹果支付 // 发起苹果支付
IAPItem targetItem = _items.firstWhere( ProductDetails targetItem = _items.firstWhere(
(item) => item.productId == model.goodsId, (item) => item.id == model.goodsId,
); );
_requestPurchase(targetItem); _requestPurchase(targetItem);
} }
...@@ -178,10 +164,19 @@ class UserOrderCoinCompletedController extends GetxController { ...@@ -178,10 +164,19 @@ class UserOrderCoinCompletedController extends GetxController {
} }
/// 苹果支付 /// 苹果支付
void _requestPurchase(IAPItem item) { void _requestPurchase(ProductDetails item) {
CustomToast.loading(); CustomToast.loading();
FlutterInappPurchase.instance try {
.requestPurchase(item.productId!); final purchaseParam = PurchaseParam(productDetails: item);
InAppPurchase.instance.buyConsumable(
purchaseParam: purchaseParam,
autoConsume: true,
);
} catch (error) {
CustomToast.dismiss();
_iapLog('IAP request purchase failed: $error');
Toast.show('内购发起失败,请稍后重试');
}
} }
/// 支付宝支付 /// 支付宝支付
...@@ -220,7 +215,7 @@ class UserOrderCoinCompletedController extends GetxController { ...@@ -220,7 +215,7 @@ class UserOrderCoinCompletedController extends GetxController {
/// 获取订单状态 /// 获取订单状态
void requestOrderStatus() async { void requestOrderStatus() async {
final result = await ShopAPI.orderStatus(orderNumber: model.ordersnum??'',receipt:_resultItem != null?_resultItem!.transactionReceipt.toString():''); final result = await ShopAPI.orderStatus(orderNumber: model.ordersnum??'',receipt:_resultItem != null?_resultItem!.verificationData.serverVerificationData.toString():'');
if (result.paySuccess == 1){ if (result.paySuccess == 1){
Toast.show('订单支付完成'); Toast.show('订单支付完成');
if(context.mounted){ if(context.mounted){
...@@ -239,7 +234,6 @@ class UserOrderCoinCompletedController extends GetxController { ...@@ -239,7 +234,6 @@ class UserOrderCoinCompletedController extends GetxController {
} }
StorageService.to.setObject(kFailOrder, temp); StorageService.to.setObject(kFailOrder, temp);
} }
FlutterInappPurchase.instance.finishTransactionIOS(_resultItem!.transactionId!);
} }
} }
else{ else{
...@@ -248,8 +242,8 @@ class UserOrderCoinCompletedController extends GetxController { ...@@ -248,8 +242,8 @@ class UserOrderCoinCompletedController extends GetxController {
// 保存未支付的订单 // 保存未支付的订单
Map<String,String> failOrder = { Map<String,String> failOrder = {
'orderNum':payOrderModel.ordersnum??'', 'orderNum':payOrderModel.ordersnum??'',
'transactionReceipt':_resultItem!.transactionReceipt.toString(), 'transactionReceipt':_resultItem!.verificationData.serverVerificationData.toString(),
'transactionId':_resultItem!.transactionId.toString() 'transactionId':_resultItem!.purchaseID.toString()
}; };
List<Map> failOrderList = []; List<Map> failOrderList = [];
failOrderList.add(failOrder); failOrderList.add(failOrder);
...@@ -259,25 +253,33 @@ class UserOrderCoinCompletedController extends GetxController { ...@@ -259,25 +253,33 @@ class UserOrderCoinCompletedController extends GetxController {
} }
///获取未完成的购买 void _iapLog(String message) {
Future _getPendingPurchase() async { Console.log(message);
List<PurchasedItem>? items = await FlutterInappPurchase.instance.getPurchaseHistory(); debugPrint(message);
for (var item in items!) { }
_pendingPurchases.add(item);
} Future<void> _listenToPurchaseUpdated(
if(StorageService.to.getObject(kFailOrder) !=null){ List<PurchaseDetails> purchaseDetailsList) async {
List<Map<String,String>> failOrderList = StorageService.to.getObject(kFailOrder) as List<Map<String, String>>; for (final purchaseDetails in purchaseDetailsList) {
for (var item in _pendingPurchases) { _iapLog(
for (var element in failOrderList) { 'IAP purchase update: ${purchaseDetails.productID}, ${purchaseDetails.status}, ${purchaseDetails.error}');
if(element['transactionId'] == item.transactionId){ if (purchaseDetails.status == PurchaseStatus.pending) {
requestOrderStatus(); continue;
} }
} CustomToast.dismiss();
if (purchaseDetails.status == PurchaseStatus.error) {
Toast.show(purchaseDetails.error?.message ?? '内购发生错误,请稍后重试');
} else if (purchaseDetails.status == PurchaseStatus.canceled) {
Toast.show('支付已取消');
} else if (purchaseDetails.status == PurchaseStatus.purchased ||
purchaseDetails.status == PurchaseStatus.restored) {
_resultItem = purchaseDetails;
requestOrderStatus();
}
if (purchaseDetails.pendingCompletePurchase) {
await InAppPurchase.instance.completePurchase(purchaseDetails);
} }
} }
} }
} }
library user_order_coin_completed; library user_order_coin_completed;
import 'dart:async';
import 'dart:io'; import 'dart:io';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_book/theme.dart'; import 'package:flutter_book/theme.dart';
import 'package:flutter_book/utils/index.dart'; import 'package:flutter_book/utils/index.dart';
import 'package:flutter_book/widgets/index.dart'; import 'package:flutter_book/widgets/index.dart';
import 'package:flutter_inapp_purchase/flutter_inapp_purchase.dart'; import 'package:in_app_purchase/in_app_purchase.dart';
import 'package:fluwx/fluwx.dart'; import 'package:fluwx/fluwx.dart';
import 'package:get/get.dart'; import 'package:get/get.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart';
......
差异被折叠。
...@@ -16,7 +16,7 @@ publish_to: 'none' # Remove this line if you wish to publish to pub.dev ...@@ -16,7 +16,7 @@ publish_to: 'none' # Remove this line if you wish to publish to pub.dev
# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
# In Windows, build-name is used as the major, minor, and patch parts # In Windows, build-name is used as the major, minor, and patch parts
# of the product and file versions while build-number is used as the build suffix. # of the product and file versions while build-number is used as the build suffix.
version: 1.0.1+5 version: 1.0.1+6
environment: environment:
sdk: '>=3.0.0 <4.0.0' sdk: '>=3.0.0 <4.0.0'
...@@ -95,7 +95,7 @@ dependencies: ...@@ -95,7 +95,7 @@ dependencies:
# 支付宝 # 支付宝
tobias: ^3.3.0 tobias: ^3.3.0
# 内购 # 内购
flutter_inapp_purchase: ^5.6.1 in_app_purchase: ^3.2.1
encrypt: ^5.0.3 encrypt: ^5.0.3
# 阿里oss直传 # 阿里oss直传
flutter_oss_aliyun: ^6.4.1 flutter_oss_aliyun: ^6.4.1
......
Markdown 格式
0% 或
您添加了 0 人 到此讨论。请谨慎行事。
请先完成此评论的编辑!
请 注册 或者 后发表评论