在 iOS 沙盒环境中完成内购(IAP)测试需要配置测试账号、搭建测试环境、验证全流程,以下是 详细指南,覆盖从账号创建到代码验证的全环节:
前提条件
- 已在 App Store Connect 配置好内购商品(需填写商品 ID、价格、描述等,状态至少为「准备提交」);
- 测试设备为 iOS 真机(模拟器不支持内购),且已登录沙盒测试账号;
- 项目已开启内购权限(Xcode 中开启
In-App Purchase能力,Bundle ID 与 App Store Connect 一致)。
第一步:创建沙盒测试账号(App Store Connect)
沙盒测试账号(Sandbox Tester)是测试内购的核心,不能用普通 Apple ID 测试,必须单独创建:
- 登录 App Store Connect,进入「用户和访问」→「沙盒测试员」;
- 点击「+」创建新测试员:
- 填写「电子邮箱」(未注册过 Apple ID 的邮箱)、「密码」、「姓名」;
- 选择「地区 / 国家」(需与内购商品的定价地区匹配);
- 无需填写支付信息(沙盒环境无真实扣费);
- 保存后,该邮箱会收到验证邮件,点击验证完成账号创建。
⚠️ 注意:
- 沙盒测试账号有使用限制(如订阅自动续订次数、测试时长),过期后需重新创建;
- 一个测试账号可测试多个 App 的内购,但地区需与商品地区一致。
第二步:配置测试设备与 App
- 测试设备准备 :
- 退出设备上的真实 Apple ID(设置 → Apple ID → 退出登录);
- 登录刚创建的沙盒测试账号(无需在 App Store 登录,内购时会自动触发登录);
- 确保设备未越狱,且安装的是包含内购逻辑的测试包(Ad Hoc/Development 包)。
- 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 会获取到沙盒收据 (与生产收据格式一致,但
environment为Sandbox)。
第四步:服务端验证沙盒收据(你的 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 | 收据无法验证 | 确认商品已配置、测试账号地区匹配 |
第五步:测试关键场景
沙盒环境需重点测试以下场景,确保逻辑完整:
- 普通商品购买:验证支付成功、收据上传、服务端验证、业务发货全流程;
- 订阅类商品 :
- 首次订阅(含试用):检查
is_trial_period是否为true; - 自动续订:沙盒订阅续订会加速(如月订阅几小时续订一次),验证续订收据;
- 取消订阅:测试取消后是否停止续订;
- 首次订阅(含试用):检查
- 恢复购买 :测试
SKPaymentQueue.restoreCompletedTransactions(),验证已购商品恢复逻辑; - 支付失败:模拟网络错误、用户取消支付等场景,确保客户端能正确处理。
第六步:提审前的沙盒验证
苹果审核人员会使用沙盒环境测试你的内购功能,提审前需:
- 确保 App Store Connect 中内购商品状态为「准备提交」(与 App 版本关联);
- 服务端代码开启「自动环境切换」(先生产、再沙盒),无需单独配置;
- 测试账号保留,审核人员可能会用你的沙盒测试账号验证。
关键注意事项
- 沙盒环境的订阅续订逻辑与生产不同(加速续订),生产环境需按真实周期;
- 测试设备需关闭「限制广告跟踪」,否则可能影响内购;
- 沙盒收据有效期较短,测试后需及时验证;
- 不要用沙盒测试账号登录 App Store(仅用于内购测试),否则可能被禁用。
总结
沙盒内购测试的核心是「专用测试账号 + 沙盒接口验证 + 全流程覆盖」:先创建沙盒测试账号,配置设备和 App,客户端触发沙盒支付,服务端验证沙盒收据,最后测试关键场景。确保每一步的环境(沙盒 / 生产)匹配,避免 21007/21008 等验证错误。