You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Flutter同一设备多用户使用相同productID订阅报错问题

解决同一设备不同用户重复购买同ID订阅的问题

核心原因

切换应用账号后,in_app_purchase 包残留了上一用户的购买缓存,或与应用商店的用户会话绑定未解除,导致校验时判定当前设备的商店用户已拥有该订阅,无法重复发起购买请求。

具体解决方案

1. 登出时重置购买状态缓存

在用户登出逻辑中,调用包内方法清除本地购买缓存,同时清理自身业务存储的订阅记录:

import 'package:in_app_purchase/in_app_purchase.dart';
import 'package:shared_preferences/shared_preferences.dart';

void handleUserLogout() {
  // 重置in_app_purchase客户端状态
  InAppPurchase.instance.restorePurchases();
  
  // 清除本地存储的订阅相关数据(若自行维护)
  final prefs = await SharedPreferences.getInstance();
  prefs.remove('user_subscription_status');
}

restorePurchases()会触发应用商店的购买恢复流程,同时重置本地缓存,确保新用户进入时无残留数据干扰。

2. Android端:重置BillingClient连接

Android需在登出时断开并重建BillingClient连接,清除商店会话缓存。通过MethodChannel调用原生逻辑:

// Android原生代码示例
fun resetBillingClient(context: Context) {
    val billingClient = BillingClient.newBuilder(context)
        .enablePendingPurchases()
        .setListener(purchaseUpdateListener)
        .build()
    
    if (billingClient.isReady) {
        billingClient.endConnection()
    }
    
    billingClient.startConnection(object : BillingClientStateListener {
        override fun onBillingSetupFinished(result: BillingResult) {
            // 连接完成后等待新用户操作
        }
        override fun onBillingServiceDisconnected() {
            // 处理断开重连逻辑
        }
    })
}

3. iOS端:重置SKPaymentQueue观察者

iOS需在登出时移除旧的支付队列观察者,新用户登录后重新添加,避免旧观察者持有上一用户状态:

// iOS原生代码示例
func resetPaymentQueue() {
    // 移除旧观察者
    SKPaymentQueue.default().removeTransactionObserver(existingObserver)
    // 新用户登录后添加新观察者
    SKPaymentQueue.default().add(newObserver)
}

4. 购买前通过后端校验订阅状态

发起购买前,调用后端接口校验当前应用账号的订阅状态,完全依赖业务后端数据而非本地缓存:

Future<bool> checkUserSubscription(String productId, String userId) async {
    final response = await http.get(
        Uri.parse("${yourBackendDomain}/api/check-sub?userId=$userId&productId=$productId")
    );
    return response.statusCode == 200 && response.body == "true";
}

void startPurchase(String productId, String userId) async {
    final hasSub = await checkUserSubscription(productId, userId);
    if (!hasSub) {
        // 获取商品详情并发起购买
        final productDetails = await fetchProductDetails(productId);
        final purchaseParam = PurchaseParam(productDetails: productDetails);
        InAppPurchase.instance.buyNonConsumable(purchaseParam: purchaseParam);
    }
}

5. 处理"已购买"类错误回调

针对Android的ITEM_ALREADY_OWNED、iOS的SKErrorPaymentAlreadyOwned错误,触发购买恢复并提交凭证给后端验证归属:

void listenPurchaseStream(String currentUserId) {
    InAppPurchase.instance.purchaseStream.listen((purchaseList) {
        for (var purchase in purchaseList) {
            if (purchase.status == PurchaseStatus.error) {
                final errorMsg = purchase.error?.message ?? "";
                if (errorMsg.contains("ITEM_ALREADY_OWNED") || errorMsg.contains("SKErrorPaymentAlreadyOwned")) {
                    InAppPurchase.instance.restorePurchases();
                }
            } else if (purchase.status == PurchaseStatus.restored) {
                // 将恢复的凭证提交后端验证是否属于当前用户
                submitTokenToBackend(
                    purchase.verificationData.serverVerificationData,
                    currentUserId
                );
            }
        }
    });
}

后端验证凭证不属于当前用户时,允许重新发起购买或标记凭证失效。

内容的提问来源于stack exchange,提问作者Sagar

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.22 16:13:27