谷歌充值修复
This commit is contained in:
parent
5501345a7e
commit
f9b7c53142
@ -6,6 +6,7 @@ import 'package:firebase_core/firebase_core.dart';
|
||||
import 'package:firebase_crashlytics/firebase_crashlytics.dart';
|
||||
import 'package:fluro/fluro.dart' as fluro;
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/gestures.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
@ -299,6 +300,7 @@ class _YumiApplicationState extends State<YumiApplication>
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
_initRouter();
|
||||
unawaited(_initLink());
|
||||
unawaited(_reconcilePendingGooglePurchases(force: true));
|
||||
});
|
||||
}
|
||||
|
||||
@ -325,6 +327,24 @@ class _YumiApplicationState extends State<YumiApplication>
|
||||
context.read<RtcProvider>().releaseRtcEngineForAppTermination(),
|
||||
);
|
||||
}
|
||||
if (state == AppLifecycleState.resumed) {
|
||||
unawaited(_reconcilePendingGooglePurchases());
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _reconcilePendingGooglePurchases({bool force = false}) async {
|
||||
if (!Platform.isAndroid) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
await context
|
||||
.read<AndroidPaymentProcessor>()
|
||||
.reconcilePendingGooglePurchases(context: context, force: force);
|
||||
} catch (error) {
|
||||
if (kDebugMode) {
|
||||
debugPrint('reconcile pending google purchases failed: $error');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
|
||||
@ -33,6 +33,9 @@ class AndroidPaymentProcessor extends ChangeNotifier {
|
||||
|
||||
// 维护商品类型映射
|
||||
final Map<String, String> _productTypeMap = {};
|
||||
final Set<String> _verifyingPurchaseKeys = {};
|
||||
bool _isReconcilingPastPurchases = false;
|
||||
DateTime? _lastPastPurchaseReconcileAt;
|
||||
|
||||
// 公开访问器
|
||||
bool get isAvailable => _isAvailable;
|
||||
@ -60,12 +63,7 @@ class AndroidPaymentProcessor extends ChangeNotifier {
|
||||
SCTts.show("Google Play payment service is unavailable");
|
||||
return;
|
||||
}
|
||||
_subscription?.cancel();
|
||||
// 设置购买流监听
|
||||
_subscription = iap.purchaseStream.listen(
|
||||
_handlePurchase,
|
||||
onError: (error, stackTrace) => _handleError(error, stackTrace),
|
||||
);
|
||||
_ensurePurchaseStreamListener();
|
||||
|
||||
// 先获取商品配置
|
||||
await fetchProductConfiguration();
|
||||
@ -75,6 +73,7 @@ class AndroidPaymentProcessor extends ChangeNotifier {
|
||||
|
||||
// 最后恢复购买,处理未完成交易
|
||||
await recoverTransactions();
|
||||
await reconcilePendingGooglePurchases(force: true);
|
||||
} catch (e) {
|
||||
SCTts.show("init fail: $e");
|
||||
_errorMessage = 'init fail: ${e.toString()}';
|
||||
@ -84,6 +83,13 @@ class AndroidPaymentProcessor extends ChangeNotifier {
|
||||
}
|
||||
}
|
||||
|
||||
void _ensurePurchaseStreamListener() {
|
||||
_subscription ??= iap.purchaseStream.listen(
|
||||
_handlePurchase,
|
||||
onError: (error, stackTrace) => _handleError(error, stackTrace),
|
||||
);
|
||||
}
|
||||
|
||||
///用户获取商店配置的商品列表
|
||||
Future fetchProductConfiguration() async {
|
||||
productMap.clear();
|
||||
@ -155,6 +161,9 @@ class AndroidPaymentProcessor extends ChangeNotifier {
|
||||
|
||||
// 获取商品类型
|
||||
String _getProductType(String productId) {
|
||||
if (productId.startsWith('coins.')) {
|
||||
return 'consumable';
|
||||
}
|
||||
return _productTypeMap[productId] ?? 'nonConsumable';
|
||||
}
|
||||
|
||||
@ -283,7 +292,7 @@ class AndroidPaymentProcessor extends ChangeNotifier {
|
||||
|
||||
case PurchaseStatus.restored:
|
||||
// 处理恢复的购买
|
||||
_verifyPayment(purchase);
|
||||
_verifyPayment(purchase, forceSubmit: true);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
@ -304,21 +313,20 @@ class AndroidPaymentProcessor extends ChangeNotifier {
|
||||
listen: false,
|
||||
);
|
||||
try {
|
||||
// 如果是消耗型商品,尝试消耗
|
||||
if (_getProductType(purchase.productID) == 'consumable') {
|
||||
await consumePurchase(purchase);
|
||||
// 消耗后重新获取余额
|
||||
profileManager.balance();
|
||||
_refreshFirstRechargeRewardState(firstRechargeManager);
|
||||
SCTts.show('outstanding purchases have been reinstated');
|
||||
}
|
||||
await reconcilePendingGooglePurchases(force: true, showToast: true);
|
||||
profileManager.balance();
|
||||
_refreshFirstRechargeRewardState(firstRechargeManager);
|
||||
} catch (e) {
|
||||
debugPrint('handle already-owned purchase failed: $e');
|
||||
}
|
||||
}
|
||||
|
||||
// 验证支付凭证
|
||||
Future<void> _verifyPayment(PurchaseDetails purchase) async {
|
||||
Future<void> _verifyPayment(
|
||||
PurchaseDetails purchase, {
|
||||
bool forceSubmit = false,
|
||||
bool showToast = true,
|
||||
}) async {
|
||||
final profileManager = Provider.of<SocialChatUserProfileManager>(
|
||||
context,
|
||||
listen: false,
|
||||
@ -331,12 +339,22 @@ class AndroidPaymentProcessor extends ChangeNotifier {
|
||||
// 1. 基本验证
|
||||
if (purchase.verificationData.serverVerificationData.isEmpty) {
|
||||
_errorMessage = '无效的支付凭证';
|
||||
SCTts.show("Invalid payment voucher");
|
||||
if (showToast) {
|
||||
SCTts.show("Invalid payment voucher");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// 2. 检查是否已经处理过这个购买
|
||||
if (!purchase.pendingCompletePurchase) {
|
||||
// 2. 正常购买流会依赖 pendingCompletePurchase;启动/回前台补偿流必须允许
|
||||
// 已确认但仍未消费的一次性商品再次上报后端,否则用户付款后退出 App 会漏单。
|
||||
if (!forceSubmit && !purchase.pendingCompletePurchase) {
|
||||
return;
|
||||
}
|
||||
|
||||
final purchaseKey =
|
||||
purchase.purchaseID ??
|
||||
purchase.verificationData.serverVerificationData;
|
||||
if (purchaseKey.isNotEmpty && !_verifyingPurchaseKeys.add(purchaseKey)) {
|
||||
return;
|
||||
}
|
||||
|
||||
@ -371,16 +389,93 @@ class AndroidPaymentProcessor extends ChangeNotifier {
|
||||
profileManager.balance();
|
||||
_refreshFirstRechargeRewardState(firstRechargeManager);
|
||||
|
||||
SCTts.show('purchase successful');
|
||||
if (showToast) {
|
||||
SCTts.show('purchase successful');
|
||||
}
|
||||
} catch (e) {
|
||||
SCTts.show("verification failed: $e");
|
||||
if (showToast) {
|
||||
SCTts.show("verification failed: $e");
|
||||
}
|
||||
_errorMessage = '验证失败: ${e.toString()}';
|
||||
} finally {
|
||||
final purchaseKey =
|
||||
purchase.purchaseID ??
|
||||
purchase.verificationData.serverVerificationData;
|
||||
if (purchaseKey.isNotEmpty) {
|
||||
_verifyingPurchaseKeys.remove(purchaseKey);
|
||||
}
|
||||
SCLoadingManager.hide();
|
||||
notifyListeners();
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> reconcilePendingGooglePurchases({
|
||||
BuildContext? context,
|
||||
bool force = false,
|
||||
bool showToast = false,
|
||||
}) async {
|
||||
if (!Platform.isAndroid) {
|
||||
return;
|
||||
}
|
||||
if (context != null) {
|
||||
this.context = context;
|
||||
}
|
||||
if (_isReconcilingPastPurchases) {
|
||||
return;
|
||||
}
|
||||
final lastCheck = _lastPastPurchaseReconcileAt;
|
||||
if (!force &&
|
||||
lastCheck != null &&
|
||||
DateTime.now().difference(lastCheck) < const Duration(seconds: 20)) {
|
||||
return;
|
||||
}
|
||||
|
||||
_isReconcilingPastPurchases = true;
|
||||
_lastPastPurchaseReconcileAt = DateTime.now();
|
||||
try {
|
||||
_ensurePurchaseStreamListener();
|
||||
_isAvailable = await iap.isAvailable();
|
||||
if (!_isAvailable) {
|
||||
return;
|
||||
}
|
||||
|
||||
final InAppPurchaseAndroidPlatformAddition androidAddition =
|
||||
iap.getPlatformAddition<InAppPurchaseAndroidPlatformAddition>();
|
||||
final response = await androidAddition.queryPastPurchases();
|
||||
if (response.error != null) {
|
||||
debugPrint('query past google purchases failed: ${response.error}');
|
||||
return;
|
||||
}
|
||||
|
||||
final purchases =
|
||||
response.pastPurchases.where((purchase) {
|
||||
final isPurchased =
|
||||
purchase.status == PurchaseStatus.purchased ||
|
||||
purchase.status == PurchaseStatus.restored;
|
||||
final productType = _getProductType(purchase.productID);
|
||||
final isUnacknowledged =
|
||||
purchase.pendingCompletePurchase ||
|
||||
!purchase.billingClientPurchase.isAcknowledged;
|
||||
// Google 只会返回未消费的一次性商品;即使它已被确认,也说明仍可补上报后端。
|
||||
return isPurchased &&
|
||||
(isUnacknowledged || productType == 'consumable');
|
||||
}).toList();
|
||||
|
||||
if (purchases.isEmpty) {
|
||||
return;
|
||||
}
|
||||
_purchases = purchases;
|
||||
for (final purchase in purchases) {
|
||||
await _verifyPayment(purchase, forceSubmit: true, showToast: showToast);
|
||||
}
|
||||
} catch (e) {
|
||||
debugPrint('reconcile pending google purchases failed: $e');
|
||||
} finally {
|
||||
_isReconcilingPastPurchases = false;
|
||||
notifyListeners();
|
||||
}
|
||||
}
|
||||
|
||||
// 交付商品
|
||||
Future<void> _deliverProduct(String productId) async {
|
||||
// 实现您的业务逻辑
|
||||
@ -506,7 +601,7 @@ class AndroidPaymentProcessor extends ChangeNotifier {
|
||||
if (productType == 'consumable') {
|
||||
success = await iap.buyConsumable(
|
||||
purchaseParam: purchaseParam,
|
||||
autoConsume: kReleaseMode, // 生产环境自动消耗,调试时手动控制
|
||||
autoConsume: false,
|
||||
);
|
||||
} else {
|
||||
success = await iap.buyNonConsumable(purchaseParam: purchaseParam);
|
||||
@ -528,6 +623,8 @@ class AndroidPaymentProcessor extends ChangeNotifier {
|
||||
// 新增:检查未完成的购买
|
||||
Future<void> _checkPendingPurchases() async {
|
||||
try {
|
||||
await reconcilePendingGooglePurchases();
|
||||
|
||||
// 恢复购买以获取所有未完成交易
|
||||
await iap.restorePurchases();
|
||||
|
||||
|
||||
@ -2,7 +2,6 @@ import 'dart:async';
|
||||
import 'dart:convert';
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_screenutil/flutter_screenutil.dart';
|
||||
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
|
||||
@ -1336,7 +1335,7 @@ class _RoomRedPacketRechargeSheetState
|
||||
SCLoadingManager.show();
|
||||
final success = await processor.iap.buyConsumable(
|
||||
purchaseParam: PurchaseParam(productDetails: product.produc),
|
||||
autoConsume: kReleaseMode,
|
||||
autoConsume: false,
|
||||
);
|
||||
if (!success) {
|
||||
SCTts.show('Purchase failed, please check your network connection');
|
||||
|
||||
@ -1,6 +1,5 @@
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_screenutil/flutter_screenutil.dart';
|
||||
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
|
||||
@ -367,7 +366,7 @@ class _RoomRechargeBottomSheetState extends State<RoomRechargeBottomSheet> {
|
||||
SCLoadingManager.show();
|
||||
final success = await processor.iap.buyConsumable(
|
||||
purchaseParam: PurchaseParam(productDetails: product.produc),
|
||||
autoConsume: kReleaseMode,
|
||||
autoConsume: false,
|
||||
);
|
||||
if (!success) {
|
||||
SCTts.show('Purchase failed, please check your network connection');
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user