如何在沙盒环境中进行内购测试

在 iOS 沙盒环境中完成内购(IAP)测试需要配置测试账号、搭建测试环境、验证全流程,以下是 详细指南,覆盖从账号创建到代码验证的全环节:

前提条件

  1. 已在 App Store Connect 配置好内购商品(需填写商品 ID、价格、描述等,状态至少为「准备提交」);
  2. 测试设备为 iOS 真机(模拟器不支持内购),且已登录沙盒测试账号;
  3. 项目已开启内购权限(Xcode 中开启 In-App Purchase 能力,Bundle ID 与 App Store Connect 一致)。

第一步:创建沙盒测试账号(App Store Connect)

沙盒测试账号(Sandbox Tester)是测试内购的核心,不能用普通 Apple ID 测试,必须单独创建:

  1. 登录 App Store Connect,进入「用户和访问」→「沙盒测试员」;
  2. 点击「+」创建新测试员:
    • 填写「电子邮箱」(未注册过 Apple ID 的邮箱)、「密码」、「姓名」;
    • 选择「地区 / 国家」(需与内购商品的定价地区匹配);
    • 无需填写支付信息(沙盒环境无真实扣费);
  3. 保存后,该邮箱会收到验证邮件,点击验证完成账号创建。

⚠️ 注意:

  • 沙盒测试账号有使用限制(如订阅自动续订次数、测试时长),过期后需重新创建;
  • 一个测试账号可测试多个 App 的内购,但地区需与商品地区一致。

第二步:配置测试设备与 App

  1. 测试设备准备
    • 退出设备上的真实 Apple ID(设置 → Apple ID → 退出登录);
    • 登录刚创建的沙盒测试账号(无需在 App Store 登录,内购时会自动触发登录);
    • 确保设备未越狱,且安装的是包含内购逻辑的测试包(Ad Hoc/Development 包)。
  2. Xcode 配置
    • 项目 Signing & Capabilities 中开启 In-App Purchase 能力;
    • Bundle ID 必须与 App Store Connect 中注册的一致;
    • 测试包需用开发者证书签名(不能用临时证书)。

第三步:客户端触发沙盒内购(核心流程)

客户端需调用 iOS 内购框架(StoreKit),以下是关键逻辑(Swift 示例):

swift

复制代码
import StoreKit

// 1. 请求内购商品列表(需匹配 App Store Connect 配置的商品ID)
func fetchProducts() {
    let productIds = Set(["com.xx.xx.xx.xx"]) // 你的内购商品ID
    let request = SKProductsRequest(productIdentifiers: productIds)
    request.delegate = self
    request.start()
}

// 2. 接收商品列表回调
extension YourViewController: SKProductsRequestDelegate {
    func productsRequest(_ request: SKProductsRequest, didReceive response: SKProductsResponse) {
        guard let product = response.products.first else {
            print("商品不存在:\(response.invalidProductIdentifiers)")
            return
        }
        // 3. 触发支付(沙盒环境会模拟支付流程)
        let payment = SKPayment(product: product)
        SKPaymentQueue.default().add(payment)
        SKPaymentQueue.default().add(self) // 监听支付结果
    }
}

// 4. 接收支付结果回调
extension YourViewController: SKPaymentTransactionObserver {
    func paymentQueue(_ queue: SKPaymentQueue, updatedTransactions transactions: [SKPaymentTransaction]) {
        for transaction in transactions {
            switch transaction.transactionState {
            case .purchased:
                // 支付成功:获取收据并上传到服务端验证
                if let receiptUrl = Bundle.main.appStoreReceiptURL,
                   let receiptData = try? Data(contentsOf: receiptUrl) {
                    let receiptBase64 = receiptData.base64EncodedString()
                    // 上传 receiptBase64 到你的服务端(即你PHP代码接收的$receipt)
                    uploadReceiptToServer(receipt: receiptBase64)
                }
                queue.finishTransaction(transaction) // 必须结束交易
            case .failed:
                print("支付失败:\(transaction.error?.localizedDescription ?? "未知错误")")
                queue.finishTransaction(transaction)
            case .restored:
                // 恢复购买(订阅类需处理)
                queue.finishTransaction(transaction)
            default:
                break
            }
        }
    }
}
沙盒支付的特殊表现:
  • 支付时不会跳转到真实的 Apple Pay,而是显示「Sandbox」标识的模拟支付界面;
  • 无需输入银行卡,直接点击「购买」即可完成支付;
  • 支付成功后,App 会获取到沙盒收据 (与生产收据格式一致,但 environmentSandbox)。

第四步:服务端验证沙盒收据(你的 PHP 代码)

用你提供的 PHP 代码验证沙盒收据,关键配置:

php

运行

复制代码
<?php
require_once __DIR__ . "/../vendor/autoload.php";
use sn01615\iap\ios\Verify;

try {
    // 客户端上传的沙盒收据(base64编码)
    $receipt = $_POST['receipt'] ?? ''; 
    if (empty($receipt)) {
        throw new Exception("收据不能为空");
    }

    $cc = new Verify();
    $cc->endpoint(true); // 明确指定沙盒环境(测试阶段建议固定为true)
    $cc->setPassword('123'); // 订阅类需设置共享密钥
    $cc->setExcludeOldTransactions(true);
    $vv = $cc->query($receipt);

    // 验证结果:status=0 表示成功
    if ($vv->status === 0 && $vv->environment === 'Sandbox') {
        echo "沙盒内购验证成功!";
        // 解析交易信息(如product_id、transaction_id),完成业务逻辑(发货、更新订单)
        $transaction = $vv->receipt->in_app[0];
        echo "商品ID:{$transaction->product_id},交易ID:{$transaction->transaction_id}";
    } else {
        echo "验证失败,状态码:{$vv->status}";
    }
} catch (Exception $e) {
    echo "错误:{$e->getMessage()}";
}
常见沙盒验证错误及解决:
状态码 原因 解决方法
21007 沙盒收据发到生产接口 确保 endpoint(true),或开启自动环境切换
21002 收据格式错误 检查客户端是否正确传递 base64 编码的收据
21004 共享密钥错误 核对 App Store Connect 中的订阅共享密钥
21003 收据无法验证 确认商品已配置、测试账号地区匹配

第五步:测试关键场景

沙盒环境需重点测试以下场景,确保逻辑完整:

  1. 普通商品购买:验证支付成功、收据上传、服务端验证、业务发货全流程;
  2. 订阅类商品
    • 首次订阅(含试用):检查 is_trial_period 是否为 true
    • 自动续订:沙盒订阅续订会加速(如月订阅几小时续订一次),验证续订收据;
    • 取消订阅:测试取消后是否停止续订;
  3. 恢复购买 :测试 SKPaymentQueue.restoreCompletedTransactions(),验证已购商品恢复逻辑;
  4. 支付失败:模拟网络错误、用户取消支付等场景,确保客户端能正确处理。

第六步:提审前的沙盒验证

苹果审核人员会使用沙盒环境测试你的内购功能,提审前需:

  1. 确保 App Store Connect 中内购商品状态为「准备提交」(与 App 版本关联);
  2. 服务端代码开启「自动环境切换」(先生产、再沙盒),无需单独配置;
  3. 测试账号保留,审核人员可能会用你的沙盒测试账号验证。

关键注意事项

  1. 沙盒环境的订阅续订逻辑与生产不同(加速续订),生产环境需按真实周期;
  2. 测试设备需关闭「限制广告跟踪」,否则可能影响内购;
  3. 沙盒收据有效期较短,测试后需及时验证;
  4. 不要用沙盒测试账号登录 App Store(仅用于内购测试),否则可能被禁用。

总结

沙盒内购测试的核心是「专用测试账号 + 沙盒接口验证 + 全流程覆盖」:先创建沙盒测试账号,配置设备和 App,客户端触发沙盒支付,服务端验证沙盒收据,最后测试关键场景。确保每一步的环境(沙盒 / 生产)匹配,避免 21007/21008 等验证错误。

相关推荐
d111111111d2 小时前
STM32定时器中断配置详解:以TIM2为例
笔记·stm32·单片机·嵌入式硬件·学习
QT 小鲜肉2 小时前
【Linux命令大全】001.文件管理之rcp命令(实操篇)
linux·服务器·网络·chrome·笔记
老王熬夜敲代码2 小时前
模版元编程variant
c++·笔记
代码游侠2 小时前
学习笔记——SQLite3 编程与 HTML 基础
网络·笔记·算法·sqlite·html
TheNextByte12 小时前
如何使用数据线或无线方式将照片从Mac传输到 iPhone?
macos·ios·iphone
im_AMBER2 小时前
Leetcode 91 子序列首尾元素的最大乘积
数据结构·笔记·学习·算法·leetcode
Aliex_git2 小时前
Vue 2 - 模板编译源码理解
前端·javascript·vue.js·笔记·前端框架
saadiya~2 小时前
实战笔记:在 Ubuntu 离线部署 Vue + Nginx 踩坑与避雷指南
vue.js·笔记·nginx
被遗忘在角落的死小孩2 小时前
SSD 存储安全协议 TCG KPIO 笔记
笔记·安全