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

Flutter iOS内购verifyPurchase验证问题求助

iOS内购Firebase云函数验证修复方案

问题代码中的核心错误

  • onCall参数顺序错误:functions.https.onCall的第一个参数是请求数据data,第二个才是上下文context,你把context放在第一个位置,导致无法获取前端传入的订单凭证,反而硬编码使用常量PURCHASE_TOKEN,这是调用失败的关键原因。
  • JSON语法错误:JSON.stringify的对象中,"password": PURCHASE_PASSWORD后缺少逗号,会导致JSON序列化失败。
  • 响应处理错误:response.body是可读流对象,不能直接返回给客户端,需要先解析为JSON或文本。
  • 环境接口混用:测试阶段应该使用苹果沙箱验证接口https://sandbox.itunes.apple.com/verifyReceipt,生产环境再切换到正式接口。
  • 错误处理不完善:仅判断response.ok不够,需要解析苹果返回的错误码,针对性处理。

修正后的云函数代码

import * as admin from "firebase-admin";
import * as functions from "firebase-functions";
import { CLOUD_REGION } from "./constants";
import fetch from 'node-fetch';

const functionsInstance = functions.region(CLOUD_REGION);
admin.initializeApp();

// 替换为你的共享密钥(在App Store Connect的内购设置中获取)
const APP_SHARED_SECRET = "你的共享密钥";

export const verifyPurchase = functionsInstance.https.onCall(
  async (data, context): Promise<any> => {
    // 验证用户身份(可选但推荐,防止恶意调用)
    if (!context.auth) {
      throw new functions.https.HttpsError(
        "unauthenticated",
        "用户未登录,无法验证订单"
      );
    }

    const { receiptData } = data;
    if (!receiptData) {
      throw new functions.https.HttpsError(
        "invalid-argument",
        "缺少订单凭证数据"
      );
    }

    // 根据环境选择验证接口,测试用沙箱,生产用正式
    const verifyUrl = process.env.NODE_ENV === 'production' 
      ? "https://buy.itunes.apple.com/verifyReceipt" 
      : "https://sandbox.itunes.apple.com/verifyReceipt";

    try {
      const response = await fetch(verifyUrl, {
        method: 'POST',
        body: JSON.stringify({
          "receipt-data": receiptData,
          "password": APP_SHARED_SECRET,
          "exclude-old-transactions": true,
        }),
        headers: {
          'Content-Type': 'application/json',
        },
      });

      const result = await response.json();
      
      // 处理苹果返回的验证结果
      switch (result.status) {
        case 0:
          // 验证成功,可在此添加用户权益发放逻辑
          return { success: true, data: result };
        case 21007:
          // 沙箱收据用了正式接口,自动重试沙箱接口
          const sandboxResponse = await fetch("https://sandbox.itunes.apple.com/verifyReceipt", {
            method: 'POST',
            body: JSON.stringify({
              "receipt-data": receiptData,
              "password": APP_SHARED_SECRET,
              "exclude-old-transactions": true,
            }),
            headers: { 'Content-Type': 'application/json' },
          });
          const sandboxResult = await sandboxResponse.json();
          return sandboxResult.status === 0 
            ? { success: true, data: sandboxResult } 
            : { success: false, error: sandboxResult };
        default:
          return { success: false, error: result };
      }
    } catch (error) {
      throw new functions.https.HttpsError(
        "internal",
        "验证请求失败",
        error instanceof Error ? error.message : String(error)
      );
    }
  }
);

关键优化点说明

  • 参数传递:前端调用时需传入receiptData(即iOS内购返回的收据字符串),云函数通过data.receiptData获取。
  • 环境适配:自动根据Node环境切换验证接口,避免测试和生产环境混用导致的验证失败。
  • 错误重试:针对状态码21007(沙箱收据用正式接口)自动重试沙箱接口,提升兼容性。
  • 身份验证:添加用户登录验证,确保只有合法用户能调用验证接口。
  • 结构化返回:统一返回success标识和对应数据/错误信息,方便Flutter端处理。

Flutter端调用示例

final HttpsCallable callable = FirebaseFunctions.instance.httpsCallable('verifyPurchase');
try {
  final result = await callable.call({
    'receiptData': '你的iOS内购收据字符串',
  });
  bool isSuccess = result.data['success'] as bool;
  if (isSuccess) {
    // 验证成功,处理用户权益
  } else {
    // 验证失败,提示用户
  }
} catch (e) {
  // 处理调用错误
}

测试注意事项

  • Postman测试onCall函数时,需遵循Firebase调用规范:
    • 请求URL为云函数部署地址
    • 请求体格式为{"data": {"receiptData": "测试收据字符串"}}
    • 请求头携带Firebase ID Token(格式:Authorization: Bearer <ID_TOKEN>),否则会触发身份验证错误。
  • 测试用收据必须是苹果沙箱环境生成的,不能使用生产环境收据。

参考实现思路

  • 优先阅读Firebase官方onCall函数文档,明确参数结构和调用规范。
  • 苹果官方收据验证文档详细说明了返回状态码的含义,需根据状态码做对应处理。
  • 可在云函数中添加日志(functions.logger.info()),方便排查部署后的调用问题。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.24 21:54:16