首页
/ Expo-IAP 常见问题与技术指南:从基础到进阶

Expo-IAP 常见问题与技术指南:从基础到进阶

2025-07-02 22:44:29作者:丁柯新Fawn

什么是 Expo-IAP?

Expo-IAP 是一个专为 Expo 和 React Native 应用设计的应用内购买解决方案。它为开发者提供了跨平台的统一 API,支持 TypeScript 和现代 React Hooks,简化了在 iOS 和 Android 平台上实现应用内购买功能的复杂度。

核心特性解析

平台兼容性

Expo-IAP 在设计上具有以下特点:

  • 同时支持 Expo 托管项目和裸 React Native 项目
  • 针对 Expo 工作流进行了优化
  • 内置对新架构的支持

与 react-native-iap 的关系

Expo-IAP 是 react-native-iap 的官方继任者,迁移的主要原因包括:

  1. 采用 Expo 优先的开发方式
  2. 提供更好的性能表现
  3. 降低维护成本
  4. 实现渲染器无关性
  5. 生产环境验证

配置与初始化

开发环境要求

测试限制:

  • 无法在 Expo Go 中进行测试
  • 需要创建自定义开发客户端或使用裸工作流

平台配置要求:

iOS (App Store Connect):

  1. 完成协议、财务和银行信息设置
  2. 创建应用内购买产品
  3. 设置沙盒测试账户

Android (Google Play 控制台):

  1. 将应用上传到测试轨道
  2. 创建应用内产品
  3. 添加测试账户

产品可用性时间

  • iOS:产品在沙盒环境中最多需要 24 小时才能可用
  • Android:产品通常在应用上传后立即可用

产品与购买流程

产品获取问题排查

getProducts() 返回空数组时,常见原因包括:

  1. 连接未建立 - 检查 connected 状态
  2. 产品 ID 与商店配置不匹配
  3. 产品尚未批准/可用(iOS)
  4. 应用未上传到商店(Android)
  5. 在模拟器/仿真器上测试

购买前必要步骤

必须首先调用 getProducts(),原因包括:

  • 确保产品可用且配置正确
  • 获取最新的价格和产品信息
  • 建立商店连接

订阅管理

订阅取消流程

用户无法在应用内直接取消订阅,需要引导用户到平台特定的订阅管理页面:

import { deepLinkToSubscriptions } from 'expo-iap';

const openSubscriptionManagement = () => {
  deepLinkToSubscriptions();
};

购买恢复实现

对于非消耗型产品和订阅:

const { getAvailablePurchases } = useIAP();

const restorePurchases = async () => {
  try {
    const purchases = await getAvailablePurchases();
    // 处理和验证恢复的购买
    for (const purchase of purchases) {
      await validateAndGrantPurchase(purchase);
    }
  } catch (error) {
    console.error('恢复失败:', error);
  }
};

收据验证最佳实践

服务器端验证必要性

客户端验证不安全,必须:

  1. 在安全服务器上验证收据
  2. 授予用户购买内容
  3. 完成交易
  4. 记录交易信息

交易完成的重要性

未调用 finishTransaction() 会导致:

  • 购买保持待处理状态
  • 商店持续通知应用
  • iOS 用户可能看到重复购买提示
  • Android 购买可能自动退款

错误处理策略

常见错误类型处理

const handlePurchaseError = (error) => {
  switch (error.code) {
    case 'E_USER_CANCELLED':
      // 用户取消 - 无需操作
      break;
    case 'E_NETWORK_ERROR':
      // 显示重试选项
      showRetryDialog();
      break;
    case 'E_ITEM_UNAVAILABLE':
      // 产品不可用
      showProductUnavailableMessage();
      break;
    case 'E_ALREADY_OWNED':
      // 用户已拥有该产品
      showAlreadyOwnedMessage();
      break;
    default:
      // 记录错误以供调查
      console.error('购买错误:', error);
      break;
  }
};

网络错误处理原则

  1. 不要立即重试购买
  2. 在应用重启时检查待处理的购买
  3. 为用户提供适当的错误信息

测试指南

测试环境要求

  • 不支持模拟器测试
  • iOS:使用沙盒账户的真实 iPhone/iPad
  • Android:使用签名构建的真实 Android 设备

订阅取消测试限制

无法直接在沙盒中测试取消,但可以:

  1. 测试订阅购买流程
  2. 测试订阅恢复
  3. 使用服务器端 webhook 通知处理取消

性能优化技巧

连接初始化时机

尽可能早地在应用生命周期中初始化:

function App() {
  const { connected } = useIAP(); // 连接自动开始
  return <YourAppContent />;
}

防止多次购买尝试

实现购买状态管理:

const [isPurchasing, setIsPurchasing] = useState(false);

const handlePurchase = async (productId) => {
  if (isPurchasing) return;
  
  setIsPurchasing(true);
  try {
    await requestPurchase({ sku: productId });
  } finally {
    setIsPurchasing(false);
  }
};

产品信息缓存策略

const [cachedProducts, setCachedProducts] = useState({});

const getProductsWithCache = async (skus) => {
  const uncachedSkus = skus.filter(sku => !cachedProducts[sku]);
  
  if (uncachedSkus.length > 0) {
    const products = await getProducts({ skus: uncachedSkus });
    // 缓存产品
    setCachedProducts(prev => ({
      ...prev,
      ...products.reduce((acc, product) => {
        acc[product.productId] = product;
        return acc;
      }, {})
    }));
  }
  
  return skus.map(sku => cachedProducts[sku]).filter(Boolean);
};

迁移指南

从 react-native-iap 迁移步骤

安装变更:

# 移除 react-native-iap
npm uninstall react-native-iap

# 安装 expo-iap
npx expo install expo-iap

API 变化:

// react-native-iap (旧版)
import { useIAP, withIAPContext } from 'react-native-iap';

function App() {
  return (
    <withIAPContext>
      <YourApp />
    </withIAPContext>
  );
}

// expo-iap (新版)
import { useIAP } from 'expo-iap'; // 不需要上下文包装器

function App() {
  return <YourApp />; // Hook 可在应用任何位置使用
}

高级问题排查

iOS 交易监听器被调用两次

现象:

  • 第一次调用:购买成功后立即
  • 第二次调用:调用 finishTransaction() 后(或产品应用重启时)
  • 两次调用具有相同的 transactionId

解决方案: 跟踪已处理的交易以避免重复处理:

const processedTransactions = new Set();

const purchaseListener = purchaseUpdatedListener(async (purchase) => {
  const transactionId = purchase.transactionId;
  
  // 如果已处理则跳过
  if (processedTransactions.has(transactionId)) {
    console.log('交易已处理:', transactionId);
    return;
  }
  
  // 标记为已处理
  processedTransactions.add(transactionId);
  
  try {
    console.log('处理购买:', transactionId);
    await validateOnServer(purchase);
    await finishTransaction({ purchase, isConsumable: false });
  } catch (error) {
    // 如果验证失败则从处理集中移除
    processedTransactions.delete(transactionId);
    console.error('购买处理失败:', error);
  }
});

总结

Expo-IAP 为 React Native 开发者提供了强大且易用的应用内购买解决方案。通过理解其核心概念、掌握最佳实践并遵循本指南中的建议,开发者可以高效地实现可靠的应用内购买功能。无论是新项目开始还是从 react-native-iap 迁移,Expo-IAP 都能提供现代化的开发体验和稳定的运行表现。

登录后查看全文
热门项目推荐

最新内容推荐

项目优选

收起
ohos_react_nativeohos_react_native
React Native鸿蒙化仓库
C++
176
260
RuoYi-Vue3RuoYi-Vue3
🎉 (RuoYi)官方仓库 基于SpringBoot,Spring Security,JWT,Vue3 & Vite、Element Plus 的前后端分离权限管理系统
Vue
854
505
openGauss-serveropenGauss-server
openGauss kernel ~ openGauss is an open source relational database management system
C++
129
182
openHiTLSopenHiTLS
旨在打造算法先进、性能卓越、高效敏捷、安全可靠的密码套件,通过轻量级、可剪裁的软件技术架构满足各行业不同场景的多样化要求,让密码技术应用更简单,同时探索后量子等先进算法创新实践,构建密码前沿技术底座!
C
254
295
ShopXO开源商城ShopXO开源商城
🔥🔥🔥ShopXO企业级免费开源商城系统,可视化DIY拖拽装修、包含PC、H5、多端小程序(微信+支付宝+百度+头条&抖音+QQ+快手)、APP、多仓库、多商户、多门店、IM客服、进销存,遵循MIT开源协议发布、基于ThinkPHP8框架研发
JavaScript
93
15
Cangjie-ExamplesCangjie-Examples
本仓将收集和展示高质量的仓颉示例代码,欢迎大家投稿,让全世界看到您的妙趣设计,也让更多人通过您的编码理解和喜爱仓颉语言。
Cangjie
331
1.08 K
HarmonyOS-ExamplesHarmonyOS-Examples
本仓将收集和展示仓颉鸿蒙应用示例代码,欢迎大家投稿,在仓颉鸿蒙社区展现你的妙趣设计!
Cangjie
397
370
note-gennote-gen
一款跨平台的 Markdown AI 笔记软件,致力于使用 AI 建立记录和写作的桥梁。
TSX
83
4
CangjieCommunityCangjieCommunity
为仓颉编程语言开发者打造活跃、开放、高质量的社区环境
Markdown
1.07 K
0
kernelkernel
deepin linux kernel
C
21
5