谷歌充值修复

This commit is contained in:
zhx 2026-06-10 15:50:36 +08:00
parent 5501345a7e
commit f9b7c53142
4 changed files with 141 additions and 26 deletions

View File

@ -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

View File

@ -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();

View File

@ -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');

View File

@ -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');