import 'dart:async'; import 'dart:convert'; import 'package:flutter/foundation.dart'; import 'package:in_app_purchase/in_app_purchase.dart'; import 'package:in_app_purchase_android/in_app_purchase_android.dart'; import 'package:pets_hero_ai/core/api/api.dart'; import '../../core/log/app_logger.dart'; import 'models/google_pay_verification_payload.dart'; /// 调起 Google Play 内购,所有内购均通过本方法发起并返回凭据用于服务端回调。 abstract final class GooglePlayPurchaseService { static final _log = AppLogger('GooglePlayPurchase'); /// 发起购买并返回服务端回调所需凭据(purchaseData=originalJson, signature)。 /// 成功返回 [GooglePayVerificationPayload],取消/失败返回 null。调用方填入 id(federation)、userId 后组 merchant 上报。 static Future launchPurchaseAndReturnData( String productId) async { _log.d('谷歌支付请求商品 ID(helm): "$productId"'); if (defaultTargetPlatform != TargetPlatform.android) { _log.d('非 Android,跳过内购'); return null; } final iap = InAppPurchase.instance; if (!await iap.isAvailable()) { _log.w('Billing 不可用'); return null; } final response = await iap.queryProductDetails({productId}); if (response.notFoundIDs.isNotEmpty || response.productDetails.isEmpty) { _log.w( '商品未找到: 请求的 productId="$productId", notFoundIDs=${response.notFoundIDs}, 请核对与 Play 后台配置的「产品 ID」是否完全一致(区分大小写)'); return null; } final product = response.productDetails.first; final completer = Completer(); StreamSubscription>? sub; sub = iap.purchaseStream.listen( (purchases) { // 把 purchases 转为 JSON 输出,方便调试 try { final list = purchases.map((p) { final base = { 'productID': p.productID, 'status': p.status.toString(), 'transactionDate': p.transactionDate, 'verificationData': { 'serverVerificationData': p.verificationData.serverVerificationData, 'localVerificationData': p.verificationData.localVerificationData, 'source': p.verificationData.source, }, 'pendingCompletePurchase': p.pendingCompletePurchase, 'error': p.error?.message, }; if (p is GooglePlayPurchaseDetails) { final b = p.billingClientPurchase; base['googlePlay'] = { 'orderId': b.orderId, 'packageName': b.packageName, 'purchaseTime': b.purchaseTime, 'purchaseToken': b.purchaseToken, 'signature': b.signature, 'originalJson': b.originalJson, 'isAcknowledged': b.isAcknowledged, 'purchaseState': b.purchaseState.toString(), }; } return base; }).toList(); _log.d('Google Play purchases json: ${jsonEncode(list)}'); logWithEmbeddedJson(jsonEncode(list)); } catch (e) { _log.w('序列化 purchases 失败: $e'); } for (final p in purchases) { if (p.productID != productId) continue; if (p.status == PurchaseStatus.purchased || p.status == PurchaseStatus.restored) { _log.d('购买成功: ${p.toString()}'); if (!completer.isCompleted) { GooglePayVerificationPayload? payload; if (p is GooglePlayPurchaseDetails) { final b = p.billingClientPurchase; payload = GooglePayVerificationPayload( purchaseData: b.originalJson, signature: b.signature, ); } iap.completePurchase(p); completer.complete(payload); } sub?.cancel(); return; } if (p.status == PurchaseStatus.error || p.status == PurchaseStatus.canceled) { if (!completer.isCompleted) completer.complete(null); sub?.cancel(); return; } } }, onError: (e) { if (!completer.isCompleted) completer.complete(null); sub?.cancel(); }, ); final success = await iap.buyConsumable( purchaseParam: PurchaseParam(productDetails: product), ); if (!success) { sub.cancel(); return null; } return completer.future.timeout( const Duration(seconds: 120), onTimeout: () { sub?.cancel(); return null; }, ); } }