读取你的服务器早已收到的退款原因代码,它会告诉你该修复应用还是与客户争辩
Apple 和 Google 发送到你服务器的每一笔退款都带有一个原因代码。Google Play 为每次作废标记九种原因之一和一个来源,Apple 则标示退款是否归咎于你的应用。这里说明每个代码的含义,如何将它们分类为修复、争辩或接受,以及它们在金钱上值多少。

要点
- Apple 或 Google 发送到你服务器的每一笔退款都带有一个退款原因代码,它是钱已经转走之后你唯一还能读到的退款信息。它告诉你退款为何发生,从而告诉你下一步该做什么。
- Google Play 的 Voided Purchases API 为每次作废标记两个数字:一个从 0 到 8 的 voidedReason(其他、反悔、未收到、有缺陷、误购、欺诈、善意欺诈、拒付、未确认的购买),以及一个 voidedSource,0 表示用户、1 表示开发者、2 表示 Google。
- Apple 给你的是一个更窄但更锐利的信号。在一笔已退款的交易上,当 App Store 因你应用内实际或可感知的问题而退款时 revocationReason 为 1,因其他原因(例如误购)退款时为 0。
- 这些代码分成三堆。有缺陷、未收到、未确认,以及 Apple 的应用内问题代码都指向你的产品,所以你去修复它们。欺诈、善意欺诈和拒付是你去争辩或预防的争议。反悔和误购从来就不是你能阻止的。
- voidedReason 8,未确认的购买,是你给自己开的一笔退款。对于你的应用在三天内未确认的任何购买,Google 会自动退款并撤销,而这个代码正是你在自己集成中找到那个缺陷的方式。
- voidedReason 7,拒付,是昂贵的那一个。对于在 2026 年 8 月 3 日当天或之后下的 Google Play 订单,一笔败诉的拒付会让开发者损失购买价格减去 Play 的服务费再加上银行的拒付费,所以统计你标为拒付的作废就是在统计真实增加的成本。
- Voided Purchases API 只回溯 30 天,而且它按 Google 看到作废的时间来筛选,而不是购买发生的时间,所以你没有在那个窗口内捕获的原因代码,就是你永久失去的原因代码。
当 Apple 或 Google 为你的某位客户退款时,钱通常在你有发言权之前就已经没了。之后落到你服务器上的东西看起来像一张收据,大多数团队也把它当作收据对待。它不止于此。每一笔退款都带有一个退款原因代码,它是决定做出之后你仍然可以读到的唯一那部分退款信息。Google Play 告诉你这笔退款是一次拒付,或一次反悔请求,或一次你自己的应用从未确认的购买。Apple 告诉你退款是否归咎于你应用内的某个东西。读懂这个代码,退款就不再是报告里的一行,而变成一条指令:修复这个、争辩这个,或者放过这一笔。这里说明每个代码的含义、如何分类,以及每一个的成本。
退款原因代码究竟是什么
退款原因代码是商店自己为一笔购买为何被撤销所贴的标签。你无法设置它,也无法与它争辩。它在事后附在退款上到达,两家商店以不同的形态和非常不同的可查期限暴露它。
Google Play 为每次作废标记一个原因和一个来源
Google Play 的 Voided Purchases API 为每笔被撤销的购买返回一条记录,每条记录带有两个重要的整数。voidedReason 说明购买为何被作废。voidedSource 说明是谁发起的。二者合在一起,把一笔干巴巴的退款变成一句话:这个订单因拒付而作废,由 Google 发起,或因反悔而作废,由用户发起。你可以通过轮询 API,或订阅在作废到达时触发的实时开发者通知来读取它们。无论哪种方式,这两个数字都是值得保留的负载。
Apple 给你一个更窄但锐利的信号
Apple 不会给你一个九选一的原因。在一笔已退款的交易上,Apple 把 revocationReason 设为两个值之一。1 表示 App Store 因你应用内实际或可感知的问题而退款该交易。0 表示它因其他原因退款,例如一次误购。该字段仅出现在被退款或被撤销的交易上,与一个 revocationDate 一起,位于 REFUND App Store Server Notification 的已签名交易信息内。两个值不算多,但重要的那个,1,是 Apple 在告诉你这笔退款关乎你的产品,而不是客户的二次考虑。
Google Play 给你的九种原因
Google 的 voidedReason 是两者中更丰富的,每个值都值得一眼认出,因为每个都指向不同的地方。这里是完整的一组,直接来自 VoidedPurchase 资源,附上每个代码实际在告诉你去做什么。
| voidedReason | Google 的标签 | 这个代码在告诉你什么 |
|---|---|---|
| 0 | 其他 | 没有记录具体原因。归入一桶并关注总量,而非单个案例。 |
| 1 | 反悔 | 客户改变了主意。你的应用没有任何问题。 |
| 2 | 未收到 | 客户说他们从未拿到所付款的东西。一个需要检查的交付问题。 |
| 3 | 有缺陷 | 购买没有起作用。一个产品缺陷,也是这份清单上最可操作的代码。 |
| 4 | 误购 | 一次误点或无意的购买。考虑一个更清晰的确认步骤。 |
| 5 | 欺诈 | Google 将该交易标记为欺诈。不是你的客户,也不是你该保留的收入。 |
| 6 | 善意欺诈 | 买家对自己发起并收到的一笔扣款提出了争议。证据仍然能触及这一类。 |
| 7 | 拒付 | 银行撤销了扣款。最昂贵的路径,现在还附带一笔费用。 |
| 8 | 未确认的购买 | 你的应用从未确认该购买,所以 Google 自动退款。你代码中的一个缺陷。 |
voidedSource 告诉你是谁扣动了扳机
紧挨着原因的是 voidedSource,它回答一个不同的问题:是谁撤销了这笔。0 表示用户做的,通过自助或银行。1 表示开发者做的,也就是你或你自己的工具在发起退款。2 表示 Google 做的,出于它自己的判断,包括对未确认购买的自动退款。当你看到作废激增时,来源是第一刀切分。一整片来源 2 是 Google 在对你的账户采取行动,而那通常是一个指回你的集成而非你客户的信号。
把每一笔退款分类为修复、争辩或接受
代码之所以有用,是因为它告诉你一笔退款应得三种回应中的哪一种。大多数团队对所有退款一视同仁,把精力烧在他们永远赢不了的那些上。这些代码分得干净利落。
修复:你的产品造成的退款
有些代码是披着退款外衣的缺陷报告。Google 上的有缺陷(3)和未收到(2),以及 Apple 上的 revocationReason 为 1,都说着同一件事:客户付了钱,而你的应用没有交付。未确认的购买(8)是这些之中最尖锐的,因为过错完全在你的计费代码里。这些是消除起来最便宜的退款,因为你消除它们靠的是修复某个属于你自己的东西,而不是说服任何人。这一堆里不断上升的计数是一个附带美元数字的产品缺陷。
争辩:有人在做手脚的退款
欺诈(5)、善意欺诈(6)和拒付(7)是争议。纯粹的欺诈(5)不是你的客户,也不是你本来就要保留的收入。善意欺诈(6),买家拿到了他们所付款的确切东西然后又提出争议,是你的证据仍然可以撼动的那种争议,而拒付(7)正是那份证据被提交的地方。当其中之一到达时,如果你还没撤销访问权就撤销它,并且在有审核窗口开放的地方,用你对该账户所了解的信息来回应它。
接受:从来就不是你能阻止的退款
反悔(1)和误购(4)是客户自己的回心转意。Apple 的 revocationReason 为 0 也归在这里。没有功能失败,也没有欺诈发生。你可以用更清晰的购买确认来缓和误购这一桶,但你无法把一笔反悔退款争辩掉,花在尝试上的时间是从修复那一堆里夺走的时间,而钱其实就在那一堆里。
| 类别 | Google 代码 | Apple 信号 | 你的行动 |
|---|---|---|---|
| 修复 | 2 未收到、3 有缺陷、8 未确认 | revocationReason 1 | 追根究底解决其背后的产品或计费缺陷 |
| 争辩 | 5 欺诈、6 善意欺诈、7 拒付 | (通过 REFUND 显现,而非原因) | 撤销访问权,用证据回应审核窗口 |
| 接受 | 1 反悔、4 误购 | revocationReason 0 | 记录它,调优购买流程,继续前进 |

让原因代码容易丢失的 30 天陷阱
Google 那边有一个硬性限制,把这件事从一个报告功能变成一条截止期限。如果你没有持续地捕获这些代码,你就在丢失它们。
Voided Purchases API 只回溯 30 天
Google 明确表示,该 API 只能显示过去 30 天内的作废购买。无论你传入什么 startTime,更早的作废都不会被返回,而 startTime 值本身也不能设得比 30 天前更早。对一个天真的集成来说更糟的是,这 30 天窗口是按 Google 系统看到一笔购买被作废的时间来度量的,而不是按购买发生的时间,甚至也不是按记录中的 voidedTimeMillis。所以一个你没有在那个窗口内拉取的退款原因代码就没了,而任何有间隙的月度导出任务都会悄悄丢掉它反应太慢没能抓到的作废。
这些原因代码在金钱上值多少
有两个代码带着具体的价格,读它们正是你为那些否则会藏在一个聚合退款率里的问题标上一个数字的方式。
一个代码是你给自己开的账单
voidedReason 8,未确认的购买,是你造成的退款最干净的例子。Google Play 要求你的应用在授予权益后的三天内确认一笔购买,如果你不这么做,Google 会自动退款该订单并撤销该项目。每一个标为 8 的作废都是一笔真实的销售,来自一位想要这个产品的客户,因为一个用于确认购买的调用从未触发而被交还回去。损失的金额是完整的销售价格,加上你为交付它已经花掉的计算、API 调用和存储。这不是一笔你去谈判的退款。它是一个你去关掉的缺陷,而这个代码正是你找到它的方式。
拒付代码现在带着一笔费用
voidedReason 7,拒付,在 2026 年 8 月 3 日改变了成本。对于在那个日期当天或之后下的 Google Play 订单,一笔败诉的拒付会让开发者损失购买价格减去 Play 的服务费,再加上银行的拒付费,而 Google 只承担它自己的服务费。因为拒付费是固定的,而产品价格不是,所以在一笔便宜的应用内购买上,单是这笔费用就可能超过客户所付的金额。统计你的代码 7 作废现在是在统计一个费用项目,而不只是一笔失去的销售,这正是为什么拒付这一桶值得在你构建的任何退款报告里占据它自己的一行。
| 原因代码 | 它让你损失什么 | 为什么这个代码重要 |
|---|---|---|
| 8 未确认的购买 | 完整的销售价格加上交付成本,在一笔客户想要的销售上 | 它是自己造成的,所以这个代码是一个缺陷追踪器 |
| 7 拒付(2026 年 8 月 3 日当天或之后的订单) | 销售价格减去 Play 的服务费,再加上银行的拒付费 | 唯一在失去的销售之上再加一笔费用的代码 |
| 3 有缺陷 | 销售价格加上交付成本,对每一个撞上该缺陷的客户重复 | 这个代码里的量把一个产品缺陷用美元标出大小 |
| 1 反悔 | 销售价格,以及你已经花掉的交付成本 | 真实的成本,但不是一次代码更改能挽回的 |
Apple 和 Google 如何对应
两家商店以不同的分辨率回答同一个问题,所以一份跨商店的退款报告必须把它们规范化,而不是指望它们相互匹配。
| 问题 | App Store | Google Play |
|---|---|---|
| 代码存在哪里 | REFUND 通知的已签名交易中的 revocationReason | Voided Purchases API 及其通知中的 voidedReason |
| 有多少种原因 | 两种:1 你应用内的问题,0 其他 | 九种,从 0 其他到 8 未确认的购买 |
| 是谁做的 | 未单独列出 | voidedSource:0 用户,1 开发者,2 Google |
| 能回读多久 | 无论何时查询交易都可用 | 只有过去 30 天的作废 |
| 最锐利的信号 | 一个 1 表示退款关乎你的产品 | 代码 3、8 和 7 各自指向一个不同的、可修复的成本 |
这两家商店永远不会为同一笔退款给你同一个代码,这没关系。要紧的是两家都递给你一个机器可读的原因,两家都会奖励一个去读它的团队。Apple 那一个比特告诉你一笔退款何时是你产品的过错。Google 的九种原因和它的来源标志告诉你,你面对的是哪个产品缺陷、哪场争议,以及哪个自己造成的计费缺口。没有哪个代码能阻止一笔退款。两者都告诉你该做什么,好让下一笔不再发生。
RefundHalt 在每一笔退款到达的那一刻就捕获其原因代码,在两家商店上都如此,并把它牢牢地保留在 Google 的 30 天窗口以内,让什么都不会溜走。它把每次作废分类为修复、争辩或接受,所以代码 3 有缺陷的激增到达你这里是一条产品警报,代码 8 未确认的激增到达你这里是一个集成缺陷,而不是收入里一次含糊的下滑。它在 12 小时内回应 Apple 的 CONSUMPTION_REQUEST,在 24 小时内回应 Google Play 的拒付审核,并在一笔退款或拒付到达的那一刻撤销访问权。你无法改变一家商店盖在退款上的代码。你可以确保读到每一个,并对那些真正该由你修复的采取行动。
常见问题解答
- 在 App Store 和 Google Play 上,退款原因代码是什么?
- 它是商店自己为一笔购买为何被撤销所贴的标签,随退款一起送到你的服务器。Google Play 的 Voided Purchases API 返回一个从 0 到 8 的 voidedReason,以及一个 voidedSource,0 用户、1 开发者或 2 Google。Apple 在退款是因你应用内的问题时把 revocationReason 设为 1,因其他原因(例如误购)时设为 0。你无法设置这个代码,也无法更改它,但读它会告诉你退款指向的是你的产品、一场争议,还是客户的回心转意。
- Google Play 的 voidedReason 有哪些值?
- 共有九个:0 其他、1 反悔、2 未收到、3 有缺陷、4 误购、5 欺诈、6 善意欺诈、7 拒付,以及 8 未确认的购买。Voided Purchases API 会为每笔被作废的购买返回其中一个,同时附上一个说明是谁发起作废的 voidedSource。代码 2、3 和 8 指向你自己应用中的问题,代码 5、6 和 7 是争议,而代码 1 和 4 是客户自己的决定。
- Apple 的 revocationReason 为 1 是什么意思?
- 它意味着 App Store 因你应用内实际或可感知的问题而退款该交易,与之相对的是值 0,那意味着退款因其他原因发生,例如一次误购。该字段仅出现在被退款或被撤销的交易上,与一个 revocationDate 一起,位于 REFUND App Store Server Notification 的已签名交易信息内。一个 1 就是 Apple 在告诉你这笔退款关乎你的产品。
- Google 为什么以未确认原因代码退了一笔购买的款?
- 因为你的应用没有及时确认该购买。Google Play 要求你在授予权益后的三天内确认一笔购买,如果你不这么做,Google 会自动退款该订单并撤销该项目,把该作废标记为 voidedReason 8。这是一笔你用一个计费缺陷造成的退款,而不是客户的请求,所以修复之处在你的购买处理代码里,而不在任何谈判中。
- 我能回读多久以前的退款原因代码?
- 在 Google Play 上,只有 30 天。Voided Purchases API 返回过去 30 天的作废,并忽略任何比那更早的 startTime,而且它按 Google 看到作废的时间来度量窗口,而不是购买发生的时间。一个你没有在 30 天内捕获的代码就丢失了,所以你应当订阅实时的作废购买通知,或按一个稳稳处于窗口以内的时间表轮询。Apple 的 revocationReason 则无论你何时查询都留在交易上。
来源和延伸阅读
- Google Play Developer API: REST Resource purchases.voidedpurchases (voidedReason and voidedSource values)
- Google Play Developer API: Method purchases.voidedpurchases.list (30-day lookback window)
- Google Play: Voided Purchases API overview
- Apple Developer: revocationReason (App Store Server Notifications)
- Apple Developer: Transaction.RevocationReason (StoreKit)
- Apple Developer: Handling refund notifications (revocationDate and revocationReason on REFUND)
- Google Play Console Help: Updates to refund protection and chargeback cost responsibility (August 3, 2026)
- Google Play Billing: Integrate the Google Play Billing Library (acknowledge within three days or auto-refund)
RefundHalt
App Store 和 Google Play 退款自动驾驶
继续阅读
儿童未经授权的应用内购买几乎每次都会退款给家长,而成本由你承担
当孩子在家长的手机上购买一个金币包时,Apple 和 Google 都会退款,而且都不会先征询你的意见。监管机构就是这样设计的。下面介绍这些未经授权的应用内购买退款在每个商店如何运作、资金流失的那 15 分钟窗口,以及一次退款到底会让你付出多少代价。
应用退款税从来不是你的,所以退款损失的是你的那一份,而不是收据上的总额
为一笔应用内购买办理退款,收据上会显示价格加税一并退回。那笔税从来都不是你的钱。Apple 和 Google 作为登记商户负责收取并上缴,退款时再以同样方式冲回,完全不动你的那一份。这篇讲清楚一笔退款到底损失多少,以及唯一一种让税变成你责任的设置。