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

react-native-macos开发Mac App如何实现In App Purchases?

解决React Native macOS订阅类内购问题的可行方案

一、先排查react-native-iap的配置问题

如果已经尝试过react-native-iap但失败,先确认以下配置是否到位:

  • 确保MacOS项目已启用In-App Purchases能力:打开Xcode的MacOS target,进入Signing & Capabilities,点击+ Capability添加该能力。
  • 验证Pod依赖:在项目的macos目录下执行npx pod-install,确保react-native-iap的MacOS端依赖正确安装,Podfile中需包含对应的pod条目。
  • 核对产品ID与Bundle ID:App Store Connect中创建的订阅产品ID,必须和RN代码中传入的完全一致;项目Bundle ID也要和开发者后台配置的匹配。
  • 沙箱测试环境:测试必须使用App Store沙箱账号,MacOS端可通过系统设置 -> App Store -> 登录沙箱账号切换环境。

二、原生封装StoreKit 2实现订阅(推荐)

如果react-native-iap对MacOS的适配存在问题,直接基于StoreKit 2封装原生模块是更可靠的方案:

1. 创建Swift原生模块

在MacOS项目中新建Swift文件(比如RNIAPMac.swift),实现核心内购逻辑:

import Foundation
import StoreKit

@objc(RNIAPMac)
class RNIAPMac: NSObject {
  // 获取订阅产品列表
  @objc func getSubscriptions(_ productIds: [String], resolver resolve: RCTPromiseResolveBlock, rejecter reject: RCTPromiseRejectBlock) {
    Task {
      do {
        let products = try await Product.products(for: productIds)
        let formattedProducts = products.map { product in
          [
            "id": product.id,
            "title": product.displayName,
            "description": product.description,
            "price": product.price.formatted(),
            "priceAmount": product.price.doubleValue
          ]
        }
        resolve(formattedProducts)
      } catch {
        reject("PRODUCT_FETCH_FAILED", "Failed to fetch products: \(error.localizedDescription)", error)
      }
    }
  }

  // 发起订阅购买
  @objc func purchaseSubscription(_ productId: String, resolver resolve: RCTPromiseResolveBlock, rejecter reject: RCTPromiseRejectBlock) {
    Task {
      do {
        guard let product = try await Product.products(for: [productId]).first else {
          reject("PRODUCT_NOT_FOUND", "Product not found", nil)
          return
        }
        let result = try await product.purchase()
        switch result {
        case .success(let verificationResult):
          switch verificationResult {
          case .verified(let transaction):
            // 验证成功,处理交易
            await transaction.finish()
            resolve(["status": "success", "transactionId": transaction.id.uuidString])
          case .unverified(_, let error):
            reject("TRANSACTION_UNVERIFIED", "Transaction verification failed: \(error?.localizedDescription ?? "Unknown error")", error)
          }
        case .userCancelled:
          resolve(["status": "cancelled"])
        case .pending:
          resolve(["status": "pending"])
        @unknown default:
          reject("UNKNOWN_STATUS", "Unknown purchase status", nil)
        }
      } catch {
        reject("PURCHASE_FAILED", "Purchase failed: \(error.localizedDescription)", error)
      }
    }
  }

  // 查询当前订阅状态
  @objc func checkSubscriptionStatus(resolver resolve: RCTPromiseResolveBlock, rejecter reject: RCTPromiseRejectBlock) {
    Task {
      do {
        let statuses = try await AppStore.current.subscriptionStatuses
        let activeSubscriptions = statuses.filter { $0.state == .active || $0.state == .inGracePeriod }
        resolve(["hasActiveSubscription": !activeSubscriptions.isEmpty])
      } catch {
        reject("STATUS_CHECK_FAILED", "Failed to check subscription status: \(error.localizedDescription)", error)
      }
    }
  }
}

// 注册模块
@objc(RNIAPMacModule)
class RNIAPMacModule: RCTBridgeModule {
  static func moduleName() -> String! {
    return "RNIAPMac"
  }

  static func requiresMainQueueSetup() -> Bool {
    return true
  }

  override class func supportedEvents() -> [String]! {
    return []
  }
}

2. 配置RN端调用

在RN中创建工具类调用原生模块:

import { NativeModules } from 'react-native';

const { RNIAPMac } = NativeModules;

export const IAPMac = {
  getSubscriptions: async (productIds) => {
    return await RNIAPMac.getSubscriptions(productIds);
  },
  purchaseSubscription: async (productId) => {
    return await RNIAPMac.purchaseSubscription(productId);
  },
  checkSubscriptionStatus: async () => {
    return await RNIAPMac.checkSubscriptionStatus();
  }
};

三、关键注意事项

  • 订阅状态验证:生产环境必须将交易凭证发送到苹果服务器验证,避免客户端伪造订阅状态。
  • 沙箱测试:沙箱订阅会自动续期,测试时需注意状态变化;如果遇到无法购买,检查沙箱账号是否有效、产品是否处于“准备提交”状态。
  • 权限处理:确保MacOS App的权限配置正确,内购不需要额外权限,但需保证App能访问App Store服务。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.08 22:47:24