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

React Native IAP iOS订阅expires_date_ms异常问题求助

iOS内购订阅有效性判断错误:expires_date_ms小于当前时间(react-native-iap 3.3.9 + RN 0.59.9)

我目前用的是react-native-iap 3.3.9版本和React Native 0.59.9版本,在iOS端检查订阅是否有效时遇到了问题:获取到的expires_date_ms总是小于当前时间,但我的明明是1个月期的订阅,导致判断逻辑一直认为订阅失效。

相关代码如下:

if (Platform.OS === 'ios') {
  try {
    console.log('In Checking')
    const availablePurchases = await RNIap.getAvailablePurchases();
    console.log("Available Purchase", availablePurchases)
    const sortedAvailablePurchases = availablePurchases.sort(
      (a, b) => b.transactionDate - a.transactionDate
    );
    if (sortedAvailablePurchases.length > 0) {
      const latestAvailableReceipt = sortedAvailablePurchases[0].transactionReceipt;
      const isTestEnvironment = __DEV__;
      const decodeReceipt = await RNIap.validateReceiptIos({
        'receipt-data': latestAvailableReceipt,
        'password': ''
      }, isTestEnvironment);
      console.log('decodeReceipt',decodeReceipt)
      const { latest_receipt_info: latestReceiptInfo } = decodeReceipt;
      const isSubValid = !!latestReceiptInfo.find(receipt => {
        const expirationInMilliseconds = Number(receipt.expires_date_ms);
        const nowInMilliseconds = Date.now();
        return expirationInMilliseconds > nowInMilliseconds;
      });
      return isSubValid
    } else {
      return false;
    }
  } catch (err) {
    console.log("Got an error while getting purchases", err);
    return false;
  }
}

求指导如何解决这个问题。


解决方案

我之前在旧版本的react-native-iap上踩过类似的坑,结合你的代码和使用的版本,给你几个具体的排查和修复方向:

1. 先确认expires_date_ms的数值准确性

首先你可以在代码里打印出receipt.expires_date_ms和Date.now()的具体值,看看两者的差值是否合理。比如如果差值是几小时,大概率是时区问题——iOS返回的expires_date_ms是UTC时间戳,虽然Date.now()本质也是UTC时间,但如果设备时区设置异常,或者旧版本react-native-iap在解析时存在偏移,就会出现判断错误。

如果是时区问题,可以尝试用UTC时间来做对比:

// 获取当前UTC时间的时间戳
const nowUTC = Date.UTC(
  new Date().getUTCFullYear(),
  new Date().getUTCMonth(),
  new Date().getUTCDate(),
  new Date().getUTCHours(),
  new Date().getUTCMinutes(),
  new Date().getUTCSeconds()
);
const expirationInMilliseconds = parseInt(receipt.expires_date_ms, 10);
return expirationInMilliseconds > nowUTC;

2. 优化latest_receipt_info的遍历逻辑

苹果返回的latest_receipt_info包含了所有订阅交易记录(包括自动续期的),你当前用find查找未过期记录的逻辑没问题,但要注意:旧版本的react-native-iap返回的这个数组可能不是按过期时间排序的。

你可以先把记录按过期时间倒序排列,再取第一条判断,这样更稳妥:

// 先按过期时间从晚到早排序
const sortedReceipts = latestReceiptInfo.sort((a, b) => 
  parseInt(b.expires_date_ms, 10) - parseInt(a.expires_date_ms, 10)
);
// 只需要判断最新的一条是否未过期
const isSubValid = sortedReceipts.length > 0 && parseInt(sortedReceipts[0].expires_date_ms, 10) > Date.now();

3. 补全Receipt验证的必要参数

你当前传的password是空字符串,但如果是自动续期订阅,苹果要求必须传入App Store Connect里生成的共享密钥(Shared Secret),空密码会导致验证返回的receipt信息不完整,甚至返回旧的测试记录。

另外,用__DEV__判断测试环境也不太准确(比如TestFlight测试时__DEV__为false,但仍需走测试环境验证),可以改成手动控制环境:

const isTestEnvironment = true; // 根据实际测试/生产环境调整
const decodeReceipt = await RNIap.validateReceiptIos({
  'receipt-data': latestAvailableReceipt,
  'password': '你的App共享密钥' // 从App Store Connect的"用户和访问"->"密钥"中生成
}, isTestEnvironment);

4. 排查react-native-iap旧版本的解析bug

react-native-iap 3.3.9是比较老旧的版本,可能存在receipt字段解析的问题:比如expires_date_ms被解析成了字符串而非数字,或者时间戳位数错误(苹果返回的是毫秒,但旧版本可能误处理成秒)。

你可以在代码里打印receipt.expires_date_ms的原始类型,确保转换正确:

console.log('expires_date_ms type:', typeof receipt.expires_date_ms);
// 用parseInt转换比Number更可靠
const expirationInMilliseconds = parseInt(receipt.expires_date_ms, 10);

5. 沙盒测试环境的特殊处理

如果你在沙盒环境测试,要注意:

  • 沙盒的自动续期订阅会加速过期(比如5分钟续一次),但有时候沙盒时间同步会有问题,建议重启设备并确保系统时间正确;
  • 订阅过期后不会立即从本地availablePurchases移除,必须依赖receipt验证的结果来判断有效性,不要只信任本地缓存。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.14 07:04:13