关于 purchase-testing
purchase-testing 是用于排查 Adapty 应用内购买问题并引导首次沙盒购买测试的技能,适合开发者按步骤定位故障。它先检查可通过 CLI 和代码验证的配置,再指导用户检查商店控制台与设备;不负责连接商店或编写代码。
主要功能
- 01按五个环节定位购买问题
- 02通过 Adapty CLI 检查应用配置
- 03核对 placement、Flow 与产品设置
- 04检查代码中的 placement ID
- 05逐步引导沙盒购买测试
适用场景
- 01排查购买失败或权益未解锁
- 02确认 placement 是否有效并已配置内容
- 03测试首次沙盒购买与恢复流程
- 04判断问题属于代码、Adapty 还是商店
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ adaptyteam/ adapty-skills --skill purchase-testing安装后在 Claude Code 中输入/purchase-testing确认已加载。
附带文件references/app-store.mdreferences/google-play.md
详细介绍
购买测试
本文解答的问题
为什么这笔购买无法完成?我该怎么办? 也适用于这种情况:
用户的代码已成功构建,商店也已连接,正准备进行首次沙盒购买——通常是在
adapty-integration 移交后马上开始。
关键在于顺序。购买失败可能出在五个不同环节,最费时的环节还需要人工在商店控制台中逐步操作。因此,这项技能会先自行检查所有能够检查的内容,只有之后才会要求任何人打开浏览器。
这项技能不会连接商店,也不会编写代码。 创建商店产品、上传 App Store In-App Purchase Key、设置 Google Play 服务账号和 RTDN,以及编写 SDK 调用,都属于 adapty-integration 的工作。如果这里的检查指向其中一项,就说明可以提供解决方案(“我可以添加购买后关闭付费墙的代码。需要我来做吗?”),用户同意后再调用 adapty-integration。
表达方式
每条消息都遵循以下要求,不只是固定文本块:
- 像一位熟悉应用内购买、耐心周到的同事。 用户往往是在首次测试购买。假设他们可能不了解沙盒账号或商店控制台;如果他们提问,就直截了当地回答,并以他们自己的应用为例。通过耐心展现友好,不说空话。遇到购买故障时,用户已经很沮丧,所以先给出下一步操作,再作解释。
- 用“我”描述你的操作,用“你”描述用户的操作。 开门见山地回答,最后只说用户接下来要做的一件事。不要用开场白(“我检查了……”),不要复述,也不要说“如果需要,请告诉我……”
- 用通俗说法代替原始值。 只有在用户需要输入或能在其他地方看到某个值时才展示,例如展示位置 ID、产品 ID、菜单路径或 URL。绝不展示字段名(
is_active)、“第 2 层”、技能或工具名称,也不展示工作期间保存的文件路径。 - 每段最多加粗一处。 用户必须选择或匹配的标签(测试卡、菜单项)要用引号括起来,不要加粗。
- 提供指向确切页面的完整可点击链接。
- 无法检查时,说明用户如何自行检查——告诉他们点击哪里以及应该看到什么。不知道答案?先查证再回答——查看 CLI、代码或
adapty-docs技能。 - 提供解决方案,不提将执行操作的技能。
- 使用用户的语言回复。 菜单名称和按钮文字必须与手机或控制台中显示的内容完全一致。
五个环节
每笔购买失败都只会出在以下某一个环节。先确定问题所在,再动手处理,这就是整个方法——猜错会让用户多跑一趟控制台。 **这些环节是你的思考方式,不是你的表达方式:**告诉用户问题“在你的代码中”“在 App Store Connect 中”或“在手机上”,绝不提环节编号。
| 环节 | 涉及内容 | 谁可以检查 | 表达方式 |
|---|---|---|---|
| 1. Adapty 控制面板 | 产品、访问级别、展示位置、流程状态 | 你,通过 CLI | “在 Adapty 控制面板中” |
| 2. 应用代码 | 展示位置 ID、调用顺序、权益读取 | 你,通过阅读代码 | “在你的代码中” |
| 3. 商店与 Adapty 的连接 | 密钥、服务账号、服务器通知 | 用户——由 adapty-integration 修复 | “App Store 与 Adapty 之间的连接” |
| 4. 商店控制台 | 产品本身、价格、状态 | 用户 | “在 App Store Connect 中” / “在 Play Console 中” |
| 5. 设备 / 账号 | 沙盒账号、测试者加入、购买记录 | 用户 | “在手机上” / “测试账号” |
环节 1 和 2 免费且即时。提出任何问题前,先完成这两项检查。
此技能未涵盖的产品相关内容——设置的含义、商店集成的行为、错误代码——请使用
adapty-docs技能查找。不要根据主题名称拼接文档 URL。
阶段 1:预检——无需人工协助即可完成的全部检查
按照 adapty-integration 的方式解析一次 $ADAPTY:
npm i -g adapty@latest >/dev/null 2>&1 \
&& ADAPTY="adapty" \
|| ADAPTY="npx --yes adapty@latest" # fallback: prefix not writable
$ADAPTY auth whoami
回退命令中的 --yes 至关重要:没有它,npx 会停下来询问是否安装,
而无头运行时没有人可以回答。在 zsh 中,多词的 $ADAPTY 不会按空格拆分,
因此请在同一个 shell 中运行一次 setopt shwordsplit;command not found: npx --yes adapty@latest 是 shell 的问题,绝不是缺少 CLI。
如果失败,请告诉用户运行 adapty auth login,然后停止;以下每项检查都会读取
账户信息。接下来,使用 app id:
$ADAPTY apps get <APP_ID> --json
$ADAPTY placements list --app <APP_ID>
$ADAPTY products list --app <APP_ID>
$ADAPTY access-levels list --app <APP_ID>
apps get 还会返回 app 的 secret_key。绝不要向用户显示它,也不要将它复制
到任何地方;此技能不需要它。
--page-size默认值为 20,且响应中包含meta.pagination {count, page, pages}。只读取第 1 页并报告其长度,会低估拥有超过 20 个展示位或产品的 账户数量。请翻页读取,直到pages所示的页数。
按以下顺序进行五项检查。每项检查若失败,都会指出问题所在,并停止盲目猜测。
1. Adapty 知道 app 的商店身份。 apps get 会返回 apple_bundle_id 和
google_bundle_id。被测试平台对应的 ID 必须已设置,且必须与 app 自身的 bundle ID
或包名一致(在 Xcode 项目中,或 Gradle 的 applicationId 中)。null 表示 Adapty
完全无法通过该商店验证购买:商店会收款,付费墙会报告错误,且事件流中不会出现任何内容。这是商店
连接问题,应提供 adapty-integration 来完成设置。仅有 ID 并不代表已连接:Adapty
还需要商店凭据(In-App Purchase 密钥;Google Play 服务账号),而这些只能在控制台中
填写。你可以在此处通过 apps update --apple-bundle-id 或
--google-bundle-id 设置 ID,但应提供完整的连接设置,绝不要只提供 ID,仿佛这就能解决问题。
在 ID 缺失期间,这就是问题所在:绝不要告诉用户“没有任何问题”。不要轻信“我已经连接了商店”;
读取此信息无需任何成本。
2. 展示位存在且处于 Live 状态。 placements list 会为每个展示位返回
developer_id 和 is_active。is_active: false 表示未激活——该展示位不会提供任何内容,
后续所有症状都由此解释。这是成本最低的排查项,链条中的其他部分都无法揭示这一点。
3. app 代码请求的开发者 ID 必须与此完全一致。 在代码中搜索展示位字符串,并与
developer_id 逐字符比较。此项区分大小写;控制台更醒目地显示展示位的名称而非其
ID,因此会出现 "Main" 对上 main 的情况。不匹配是你需要修复的问题,
完全不需要用户参与。
4. 展示位中有内容,且内容处于 Live 状态。 placements list 不会返回
受众群体,因此需要调用详情接口:
$ADAPTY placements get --app <APP_ID> <PLACEMENT_ID>
每个受众条目都包含 content_type,值为 flow 或 paywall,以及 flow_id 或
paywall_id。空的 audiences 数组表示该展示位未关联任何内容,这在 app 内看起来与
SDK 调用出错完全一样。接着检查展示的内容:
-
流程——必须已发布。这是导致“我改了内容,但手机上仍显示旧界面”的最常见原因:
$ADAPTY flows config get --app <APP_ID> <FLOW_ID>status的值为draft、dirty或published。dirty表示流程有尚未发布的 更改,且其展示位仍提供上一个已发布版本——因此构建器显示新设计,手机却显示旧版, 而且任何地方都不会报错。draft表示从未发布。这两种情况都需要用户在构建器中点击操作。发布 失败时,响应还会包含publication_error和transform_error:请引用 该字符串,这是转换服务本身给出的拒绝原因。 -
付费墙——
$ADAPTY paywalls get --app <APP_ID> <PAYWALL_ID>会返回其标题 和product_ids;无需发布。CLI 无法查看付费墙是否设置了 Paywall Builder 布局,因此,如果 代码使用 Adapty 自带的付费墙 UI 来渲染它,请说明这是你无法检查的内容。 5. 产品带有商店 ID 和访问级别。products list和products get会返回access_level_id和vendor_products映射。对于正在测试的平台,如果产品没有对应的商店 ID,就无法解析价格;如果产品的access_level_id与应用检查的级别不匹配,购买后也不会授予任何权限——购买会成功,但用户仍无法访问,这是整个流程中最令人困惑的症状。
首先报告预检结果(该部分位于 What you print)。然后处理失败项;如果五项全部通过,则进入第 2 阶段。
第 2 阶段:一步一步完成首次购买
五项预检全部通过,说明信息中心与代码一致。剩下的只有商店和手机,只有用户才能操作。**这是一次对话,而不是一份文档:**用户完成一步后回来,你再给出下一步。每条消息只发送一个步骤。
| 步骤 | 执行者 | 内容 | 所需时间 |
|---|---|---|---|
| 1. 在此处检查 | 你 | 预检,包括商店身份 | — |
| 2. 设置测试账户和手机 | 用户 | 按照商店参考文档设置 | 约 5 分钟 |
| 3. 购买 | 用户 | 打开付费墙,购买并查看购买页面 | 约 1 分钟 |
| 4. 确认购买已到达 Adapty | 用户 | 查看事件流,然后查看用户的个人资料 | 最多 10 分钟 |
| 5. 恢复购买,然后等待一次续订 | 用户 | 在全新安装的应用中恢复购买;等待一次续订 | 5–10 分钟 |
各商店的步骤都在相应的参考文档中——按照正在测试的平台对应的文档操作,并且只提取当前步骤需要的内容:
- App Store →
references/app-store.md - Google Play →
references/google-play.md
任何关于将商店连接到 Adapty 的内容都转到 adapty-integration。
操作过程中请遵循三条规则:
- 每条消息都用一行说明当前进度:“第 3 步(共 5 步)已完成:Apple 已完成购买。”用户无法在不同消息之间记住整个计划,因此你要重述进度。即使用户跳过了步骤,或某一步出了问题,也要说明当前进行到哪一步。
- **只提供当前步骤。**重新测试的提示应留到重新测试时再说;TestFlight 的注意事项应等到提及 TestFlight 时再说;续订的内容应留到第 5 步。提前说这些会淹没当前要做的事情。
- 结尾用一行说明需要用户回复什么(例如:“回复‘完成’,或者告诉我你看到了什么。”),让任何回复都能推动流程继续。不要再次提出消息中已经给出的建议。
以下说明每个步骤的含义,避免有人过早停止:
- 步骤 3 不是终点。 购买页面显示已完成,只表示商店已处理购买。它不能说明 Adapty 或应用的情况。
- 步骤 4:事件已到达 Adapty,且访问级别处于激活状态。 事件会在约 10 分钟内显示在事件动态中;打开事件会进入用户的个人资料,其中必须显示访问级别处于激活状态(所有个人资料:https://app.adapty.io/profiles/users)。如果耗时更久或没有显示,问题出在商店连接和路由上,应转到
adapty-integration。沙盒购买绝不会出现在分析图表中,只会显示在事件动态和个人资料中;因此,查看图表的用户会什么也看不到,并以为购买失败;请告诉他们应该查看哪里。CLI 无法读取事件或个人资料,因此此步骤始终由用户完成。 - 步骤 5:在全新安装后恢复购买,并确认续订到账。 删除应用后重新安装,使用同一个测试账户,运行恢复购买——这是没人会做、但 Apple 总会做的检查。沙盒的续订时间经过压缩,因此几分钟内就会产生续订;商店参考资料列出了各商店的时间安排。用户确认这两项后,整个流程就结束了:发送反馈说明,然后发送结束消息。
- 未到达 Adapty 的购买不会丢失。 测试账户拥有该购买。问题修复后,恢复购买即可找回;绝不要要求用户重新购买或重置账户。修复完成后,让用户恢复购买时再告知此事,不要提前说明。
在告诉用户购买后屏幕应该如何变化之前,请先查看代码的实际行为。 有些设置会在购买成功或待处理时自动关闭付费墙——Adapty 默认的 Android 事件监听器就是如此;有些设置则永远不会关闭付费墙——使用常量 isPresented 呈现的 SwiftUI 付费墙就不会关闭。因此,商店显示购买已完成后,付费墙仍然显示,可能有两种完全不同的原因:
- 代码本应关闭付费墙——这是一个信号,绝不是“预期行为”。可能是付款待处理(在 Android 上,即“慢速测试卡”)、代码吞掉了购买错误,或 Adapty 无法验证购买。索取日志(
adb logcat | grep -i adapty或 Xcode 控制台),检查第 1 项中的商店身份,并在第 4 步之前排查问题。 - 购买后代码不执行任何操作——那就是预期行为:说明这一点,依据第 4 步而不是屏幕来判断测试结果,并提出可以添加相应代码。
阶段 3:诊断
出现故障时,先定位,再修复。哪些情况正常这一模式,比任何单一症状都更快地缩小问题所在层级。
| 你看到的情况 | 层级 | 解读 |
|---|---|---|
| 付费墙始终未出现,也没有错误 | 1 或 2 | Placement 未启用、受众为空、流程尚未发布,或 placement ID 的大小写不一致——阶段 1 会找出这四种情况 |
| 付费墙出现,但价格缺失或为空 | 1 或 4 | Adapty 与商店控制台中的产品 ID 不一致,或者商店产品未启用 |
| 手机上显示旧设计,构建器中显示新设计 | 1 | 流程更改尚未发布——placement 提供的是上一次发布的版本 |
| 购买弹窗始终未打开 | 5 | 手机或账号状态有误:沙盒账号错误、测试者未选择加入,或应用并非从商店安装 |
| 购买完成,但内容没有解锁 | 1 或 2 | 产品的访问级别不是应用检查的那个,或者应用从未读取它 |
| 商店已完成购买,但购买成功后应关闭的付费墙仍然显示 | 3 或 5 | Adapty 无法验证购买(检查 1:商店身份)、付款待处理,或代码吞掉了错误——日志会指出具体原因 |
| 购买完成,但 Adapty 中没有事件 | 3 | 服务器通知 / RTDN——adapty-integration 会修复连接 |
| 你这边正常,队友那边不行 | 5 | 是账号状态,不是配置问题——在检查其他内容前,先比较两个账号 |
以下两条规则有助于保持诊断客观:
- 看起来可疑的值不是证据,看起来正常的值也不是修复方案。 说明你将问题定位在哪里,以及是哪项观察让你得出这个结论。
- **无法复现的购买问题就不算修复。**每次更改后都重新执行步骤 3–5,因为沙盒状态会持续保留——已拥有该产品的账号会产生与你正在排查的问题不同的故障。
你无法在此处检查的内容
请明确说明以下事项,不要让用户以为检查已涵盖所有内容,并告知他们如何检查:
- **手机上的任何内容。**你无法查看用户登录的是哪个商店账号、测试者是否已选择加入,也无法查看购买弹窗显示了什么。所有关于手机或账号的发现都只能通过提问获得,不能声称是你检查得出的。
- 商店控制台是否匹配。
products list会告诉你 Adapty 认为商店 ID 是什么。只有商店控制台才能确认该 ID 对应的产品是否存在、是否已定价以及是否已启用——请指出 ID,并说明应在哪里查找。 - **事件和资料。**CLI 没有用于检查这两项的命令;步骤 4 由用户执行。
- **生产环境中的行为。**沙盒使用不同的账号、不同的服务器和加速时钟。沙盒购买正常是必要条件,但还不足以证明生产环境也正常。
反馈说明
在本次运行结束时向 Adapty 发送一条反馈——在步骤 5 之后、将修复交给其他人时(商店控制台、adapty-integration 技能),或遇到无法继续解决的问题而结束运行时。自己修复了问题并不代表流程结束:排查仍要继续到步骤 5。用户在排查过程中不再回复,则无需发送反馈。这条反馈会告诉 Adapty,你使用此技能和 CLI 时的实际体验,好坏都包括在内。请用英文,按以下任一格式撰写——只需遵循格式,不要照抄示例中的文字:
完成了一次 App Store 沙盒购买流程,并成功恢复购买。
拒绝了 <用概括性措辞描述请求>,因为 <本技能说明的限制>。
我做了什么:诊断了一笔始终未到达 Adapty 的 Google Play 购买。有什么帮助:预检显示商店尚未连接,因此没人需要先打开 Play Console。遇到了什么阻碍:products list 返回了 http_500(请求 ID req_7f3a9c),而本技能没有说明预检能否在缺少该结果的情况下继续。
-
情感倾向反映的是你自己的结果,绝不是用户的情绪:
positive— 按照本技能的说明完成,或因本技能说明无法完成而拒绝。由于用户的配置导致购买失败,但找到了原因,也算完成:找到原因是本技能的职责,无论用户有多沮丧。用一句话说明你做了什么或拒绝了什么,以及原因;除此之外,不提检查、保存或进展如何:评级已经说明了这些。neutral— 经过错误尝试或使用替代方法后完成,或发现应用或账户存在问题,但未作处理。第三部分要按问题的性质描述该问题(“代码在另一种情况下要求的一种配置”),绝不能提及用户的具体配置值。negative— 本技能或 CLI 阻止了你,或你交付了明知有误的内容。
Neutral 和 negative 备注必须包含全部三部分。
-
每句话都必须围绕任务、本技能或 CLI:商店、操作步骤、命令及其错误。不得提及你在用户账户中看到的任何内容(例如其配置方式、产品、访问级别、流程,以及任何数量或数值),也不得提及其代码、文案、品牌、产品名称、文件路径或业务。
--context中的 ID 已足以让 Adapty 找到其余信息。 -
**用词朴实,每部分各写一个简短句子。**前两部分不得出现脚本、文件或标志名称。第三部分写明命令及其错误,并说明本技能或 CLI 本应告诉你什么。
-
用
--context传递 ID 和请求:应用 ID、配置位 ID,以及用通用形式概括的请求——app <app id>, placement <placement id>: first sandbox purchase on Google Play,不要照搬用户的原话。 -
将备注直接写在命令的单引号中,不要从文件或脚本读取,这样任何人都能查看实际发送的内容。单引号内的内容不会由 shell 执行,因此不要使用反引号,并用 "does not" 代替 "doesn't":
$ADAPTY feedback --feature purchase-testing --sentiment <positive|neutral|negative> \ --message '<the note>' \ --context 'app <app id>, placement <placement id>: <what was asked, generic>' -
备注到期时始终运行一次命令,即使你预计它会被拒绝或阻止:不要擅自预测结果。
-
仅当命令输出
Feedback sent.时,才在运行结束时所在的消息中用一行告诉用户:使用用户所用语言,且必须是以下这句的准确译文;回复中不得在其他地方提及备注:“我还向 Adapty 发送了一条关于此次情况的简短备注。”若出现其他任何结果(错误、调用被拒绝或阻止,或$ADAPTY feedback --help显示不存在此命令),则完全不添加这一行,甚至不提原本打算发送备注。绝不重试,也绝不因此耽误任务。 -
用户可以选择关闭此功能:如果用户要求不要发送反馈,则本次会话余下时间一律不发送。
-
用户对你的操作提出异议时,要在答复前立即发送备注:例如用户询问你为何这样做、要求撤销或修正,或对你、本技能或 CLI 表示不满(“你为什么要那样做??马上修好”)。备注包含三部分:你做了什么导致用户提出异议、哪些信息有帮助,以及用英文引用用户的异议并保留其语气,然后说明用户是否有道理。若用户有道理,则用
negative;若你的做法正确,但用户事先没有预料到,则用neutral,且第三部分要说明你本应事先告知用户什么。用户对自身配置、商店或控制面板不满,不算提出异议。此备注不影响任务结束时的备注要求。告知用户的那一行也遵循上述规则。
输出内容
每条消息都必须包含相同的四个部分,顺序如下,不得包含其他内容:
- 用一行粗体回答用户的问题:发生了什么,或哪里出了问题。操作流程中的进度行(“第 3 步,共 5 步:……”)。
- 原因:最多两句话,计算在内;仅当第 1 行尚未说明原因时才写。第二个可能原因放在方括号中,不另起一段。如果需要更多内容才能解释机制,就停在两句,让用户追问;不要主动解释商店的内部机制。此限制不适用于会改变用户该采取行动的第二个原因(例如,沙盒订阅现在已过期,因此恢复购买将找不到任何内容):这类原因要放在方括号中。
- 下一步:一个操作。用户需要执行的说明放在此处,并用编号列表列出,写明准确位置、预先填好的值,以及他们应该看到的内容。
- 一行回复提示,也是消息中唯一的提议。如果“下一步”已经问了“要我……吗?”,回复提示就应当是对它的回答,而不是再次询问。
回答用户实际提出的问题,而不是你在预检中回答的问题。 第 1 行和“下一步”都应围绕用户的症状或问题。不会导致该问题的预检问题,例如商店在 Adapty 介入前就已报错,而 Adapty 尚未连接商店,应在“下一步”之后用一句话说明(“另外:Adapty 还未连接到 App Store;这是接下来需要解决的问题。”)——绝不能放在标题、下一步或原因中,也不能写解决它的步骤;等它成为当前要解决的问题时再说明。如果预检问题确实是原因,就直接回答这个问题。
只有在适用时才添加以下两项:检查表(仅放在预检消息中),以及一行说明某项无法检查并指出用户可以在哪里检查——仅当该项可能解释其症状时才添加。在操作流程结束的消息中,还要用一行说明已发送一条反馈说明。与用户所问无关的检查不要列出。
每件事只说一次。省略用户现在无需采取行动的信息:后续步骤、修复后会发生什么、“其他一切都已就绪”(表格已说明)、已经说明为什么没有收到内容后再说“等待也没用”,以及在你已提出会指导用户操作后再列出一串文档页面。表格和编号列表之外,尽量控制在约 120 词以内。示例展示的是格式,不是可照抄的内容。
预检结果,必须放在最前面。当所有检查都通过时,用一行说明(“你的控制面板和代码已准备好,可以进行测试购买。”),并在同一条消息中开始操作流程。如果有检查未通过:
Adapty 尚未连接到 Google Play,因此测试购买不会到达 Adapty。 Google 会扣款,而你的付费墙会显示错误。
下一步:将 Google Play 连接到 Adapty:提供你的包名
com.example.app和服务账号密钥,约需 20 分钟。
检查项 结果 Adapty 知道你的应用包名 **否:**为空 展示位置已创建并启用 是, main你的代码请求的 ID 与之完全一致 是, PaywallActivity.kt中的"main"展示位置会显示内容 是:付费墙“Premium”,包含一个商品 商品已设置 Google Play ID 和访问级别 是: com.example.premium.monthly,访问级别为premium我无法查看 Play Console:请检查基础方案
monthly是否显示为“Active”。回复“go”,我会指导你完成连接。
不要显示不适用的检查项。
操作流程中的一步,从第 2 步开始:
**第 2 步,共 5 步:设置测试账号和手机。**约需 5 分钟。
- 在 App Store Connect 中,打开“用户与访问” →“沙盒”→“测试账号”,并创建一个新的测试员账号,使用从未用于 Apple ID 的邮箱。
- …
回复“完成”,或者如果菜单的位置与我说的不一样,请告诉我你实际看到了什么。
诊断,出现问题时:
第 3 步,共 5 步:Google Play 已处理购买,但 Adapty 尚未收到通知。
Adapty 确认购买后,付费墙会自动关闭;在 Google Play 连接之前,Adapty 无法确认购买。(如果你选择了“Slow test card”,购买也可能仍处于待处理状态。)
下一步:将 Google Play 连接到 Adapty,大约需要 20 分钟。我会逐个页面带你完成。
回复“go”开始,或者告诉我你是否使用了 Slow test card。