Refund Abuse & Fraud Prevention

连环退款套路让你双重买单,而两大应用商店都给了拆穿它的工具

第四次申请退款的人,并不是不满意的客户,而是在按套路行事,这会让你既损失退回的收入,又搭上你为履行该订单所花费的一切。苹果和谷歌都提供了一个身份字段,能把这些看似孤立的事件,变成清晰可见的模式。

5 min read
连环退款套路让你双重买单,而两大应用商店都给了拆穿它的工具

第四次申请退款的人,并不是不满意的客户。他们是在按套路行事,这会让你损失退回的收入,外加你为履行该订单所耗费的一切。苹果和谷歌各自提供了一个身份字段,一端是 appAccountToken,另一端是 obfuscated account ID,能把这些原本孤立的事件,转变成清晰可见的模式。

核心要点

  • 你提供的证据,只有在两种情况下才能真正送达应用商店:苹果的 CONSUMPTION_REQUEST(需在 12 小时内回应),以及谷歌 Play 通过 Review Refund API 进行的 chargeback review(需在 24 小时内回应)。除此之外的所有情况,都会在你缺席的状态下做出决定。

  • 在苹果平台上,你生成的 UUID 会以 appAccountToken 的形式随购买一同传递。StoreKit 会把它绑定到该笔交易上,这意味着日后发生的任何退款,都能在你自己的记录中对应到某个可识别的账户。

  • 谷歌对应的字段是一个不超过 64 个字符的哈希字符串。谷歌自己会读取这个字段,用它来发现诸如同一账户在短时间内于不合常理数量的设备上进行购买之类的异常行为。

  • 如果该谷歌字段中包含明文个人数据,购买会被直接拦截。这里需要填入加密值或单向哈希值,而不是原始的电子邮箱地址。

  • Play 的 voidedReason 会把 friendly_fraud 和 fraud 与普通的“改变主意”退款区分开来,这就让你有了依据,把惯犯导入审核队列,而不是走默认的“退款了事”流程。

  • 谷歌报告称,2022 年期间拦截了超过 20 亿美元的欺诈和滥用交易。这些防御机制依赖的身份信号,只有在你的购买数据携带这些字段时才会存在。

  • 在退款之后依然保留的访问权限,会持续消耗你已经拿不到报酬的存储、算力和 API 预算,而蓄意退款的人,正是在反复制造这样的结果。

引言

退款总会发生,偶尔出现一次,不过是正常的运营成本。真正改变局面的是:同一个账户第四次回来退款,而之前每一次都已经消费了所购商品。只有当每一笔交易都携带你分配的标识符时,这种行为模式对你来说才是可辨识的。两大平台都提供了这样的标识符,也都在结果确定之前,开放了一个狭窄的回应窗口。一旦跳过标识符,或是错过了这个窗口,钻空子的人就会和真正有合理不满的首次购买者没有任何区别。两者之间的区别何在、这种区别在金钱上又意味着什么,正是下文要讲的内容。

退款滥用,本质上是披着退款外衣的身份识别问题

退款决定权属于应用商店。客户会提出申请,而在大多数流程中,商店会直接同意,你没有任何置喙的余地。你能施加影响的地方在别处:确认这个特定账户是否有过这类行为的历史,以及他们所购买的东西是否真的被交付并使用了。这两个问题归根结底都是身份问题——这笔交易背后是哪个用户,他之前做过什么。如果购买数据与你的用户记录之间没有稳定的关联,这两个问题就都无法回答,于是每一笔退款都在脱离背景信息的情况下落地。

这正是值得弥补的缺口。规则机制是固定的,你无法撼动。但证据要靠你自己来搜集,而搜集工作从结账那一刻就开始了,而不是等到退款发生时才开始。

你提交的内容真正起作用的两个窗口

只有两种流程会真正把你提交的内容当作意见来对待。客户针对消耗型商品或自动续订订阅申请退款,会触发苹果的 CONSUMPTION_REQUEST,你需要在 12 小时内作答。客户就某笔扣款向银行发起争议,会触发谷歌 Play 的 chargeback review,你可以通过 Review Refund API 在 24 小时内回应。除了这两种情况,决定都会在没有你参与的情况下做出:Play 的 48 小时自助退款、客服批准的退款、针对你从未确认过的购买所做的自动退款。

当交易本身已经携带你的标识符时,这两条渠道的效力都会显著增强。发送给苹果的消费信息中,可以在该账户的消费和历史记录旁附上 appAccountToken;而谷歌的审核,则依赖于自购买完成以来就一直在观察的 obfuscated account ID。没有标识符,也就没有历史记录可以提交。

两个身份字段的对比

两家公司在不同的命名和不同的限制条件下,得出了几乎相同的解决方案。两者都允许你给购买打上一个标签,这个值能映射回你自己的用户记录。两者都不会替你自动启用。两者都只有在购买发生时就设置好,才有意义,否则毫无用处。

Apple: appAccountToken

生成一个 UUID,在购买开始时传入,StoreKit 就会把它绑定到由此产生的交易上。随后它会出现在你服务器验证的交易数据中,也会再次出现在你在 CONSUMPTION_REQUEST 期间返回的消费信息中。由于这个值本就由你生成,它能在不涉及任何姓名或邮箱的情况下,把一笔商店交易映射到数据库中的一条记录上。只接受真正的 UUID,其他一律会被拒绝。苹果后来还推出了 Set App Account Token 接口,能够为已经发生的交易写入或更新该令牌,优惠代码兑换以及类似的应用外购买也包含在内。

Google Play: the obfuscated account ID

setObfuscatedAccountId 是谷歌的版本,它是一个绑定在购买账户上的可选字符串。谷歌的文档对其用途说得很直白:Play 利用这个值来检测异常活动——文档给出的例子是同一账户在短时间内于多台设备上进行交易——并在部分欺诈性购买完成之前将其拦截。该字段上限为 64 个字符,且禁止填入明文个人数据。放入原始邮箱地址会导致购买被拦截,因为这个字段期望接收的是加密值或单向哈希值。对于单个账户下包含多个资料的应用,还有一个配套字段 setObfuscatedProfileId。

  • appAccountToken 是苹果的字段。它必须是 UUID,别无其他形式;由你的代码在结账时设置,如果你不添加,它就不会存在。苹果用它把交易锚定到你的用户身上,它也会再次出现在交易和消费数据中。

  • obfuscatedAccountId 是谷歌 Play 的字段。它接受一个最多 64 个字符的哈希字符串,同样由你的代码在结账时设置,默认情况下也不存在。谷歌会把它输入到异常活动检测中,并用它在部分欺诈性购买完成之前将其拦截。

有一处不对称之处格外重要。谷歌完全不提供任何回填机制。一笔在没有标识符的情况下完成的 Play 购买,将永久处于匿名状态,唯一可行的补救办法是给未来的购买打上标签。而苹果的 Set App Account Token 接口能够修复历史记录,这使它成为例外,而非常态。

为惯犯的退款算一笔账

退回的收入,是看得见的那一部分,同时也是较小的那一部分。批准一笔退款,会把销售价格退回去,你的账本会记录这一笔。但账本上这一行并不会记录,为生产出客户现在免费持有的东西,你究竟付出了什么。

设想一种会启动实际工作的消耗型商品:生成的图片、向某个模型供应商发起的一连串调用、导出的一段视频、向创作者发放的一笔款项。这些算力是在使用的那一刻就被购买的。退款只能拿回销售价格,服务供应商的账单却原封不动地留在那里。那些已经摸清“商品比退款活得更久”这一点的人,只会不断重复这个序列——购买、消费、退款——每一次循环,都会让你再次承担一次交付成本。

此外,平台也在把成本转嫁给你。从 2026 年 8 月 3 日起,对于该日期之后下的订单,谷歌 Play 会把购买价款和银行的拒付手续费都记到开发者头上。原本主要由谷歌承担的争议费用,就变成了你损失的收入外加一笔手续费,而习惯性发起拒付的人,每尝试一次都会把这个数字进一步推高。正是身份字段,才使得你有可能在 24 小时窗口内,向 Review Refund API 提交真正值得一看的内容。

谷歌给出了这个问题规模的具体数字:2022 年期间拦截了超过 20 亿美元的欺诈和滥用交易。这个数字之所以存在,是因为底层的信号——其中就包括 obfuscated account ID——确实被填充了数据。让购买数据带着这些字段空置发出,等于是主动选择站在这层防护之外。

在商店做出裁决之前,先识别出惯犯

为每一笔购买打上你掌控的身份标记

两大平台都不应该看到没有身份标记的购买。StoreKit 购买携带 appAccountToken,Play Billing 购买携带 setObfuscatedAccountId,这两个值都源自你自己的用户记录。苹果期望得到映射到该账户的真实 UUID,谷歌期望得到经过哈希处理的账户密钥,确保没有任何个人信息以明文形式传输。结账那一刻是两者共同的设置时机,而在谷歌这边,这个时机事后无法重新商量。

维护一本以该身份为键的账本

把交付记录、消费事件、购买记录和退款记录全部归档在这个标识符之下。这样一来,一次 CONSUMPTION_REQUEST 或 chargeback review,就能让这个账户的全部历史——包括之前的退款记录——只需一次查询即可获得。苹果的消费信息专门提供了用于承载这类材料的字段:consumptionStatus、deliveryStatus、playTime、accountTenure、lifetimeDollarsPurchased,以及你自己设置的 refundPreference。

把 voidedReason 当作路由决策来对待

被作废的 Play 购买会带有一个说明原因的 voidedReason,friendly_fraud 和 fraud 会与普通的“反悔”退款分开记录。这类情况应当被导入审核队列,并采取更严格的政策,而不是像首次退款那样走自动处理流程。谷歌的指导方针描述了一套逐步升级的应对措施:起初是警告,再次发生时采取更强硬的手段,屡教不改的账户则会被禁止购买。

绝不能错过响应窗口

迟交的证据,等于不存在。把 CONSUMPTION_REQUEST 的回应自动化,确保能从容地在 12 小时内发出,chargeback review 则要在 24 小时内完成。一份在截止时间之后才提交的、完美无瑕的账户历史记录,商店里不会有任何人去打开它。

  • 要识别同一账户同时在不合常理数量的设备上进行交易的情况,可以依靠谷歌 Play 的异常活动检测,它会读取 obfuscated account ID。

  • 要查看该账户此前的退款记录,可以依靠你自己的记录,也就是你以某一平台的标识符为键建立的账本。

  • 要确认商品确实送达了买家并被使用,可以依靠苹果,consumptionStatus 和 deliveryStatus 就存在于消费信息之中。

  • 要识别一场实际上是买家在欺骗你的争议,可以依靠谷歌 Play,此时 voidedReason 会返回 friendly_fraud。

平台能帮到什么程度,又止步于何处

把责任划分讲清楚是值得的,因为人们很容易想当然地以为商店会处理滥用问题,而商店也很少明确宣布它们的边界在哪里。谷歌确实会读取 obfuscated account ID,在购买完成之前拦截部分欺诈行为,平台级系统也确实拦截了相当大的量。苹果则把每一个退款决定都留给自己来做,并把你的消费信息当作证据,而非投票。两家商店都不会替你维护一份滥用者名单,不会把一笔已退款的消耗型商品重新计入你的余额,也不会替你拒绝某个惯犯的请求。在边界地带,身份识别的主动权在商店手里;除此之外的一切,主动权都在你手里。

出于同样的原因,Refund Sensor 正是围绕这一身份标识构建的。属于同一账户的退款、chargeback review 和消费请求,会汇聚成一份单一的历史记录,这样一来,重复出现的模式在第二次发生时就已清晰可见,而不必等到第四张账单已经付清之后才被发现。

这些规则的官方文档出处

常见问题

这指的是客户在收到并使用了所购买的商品后,反复申请退款,既保留了购买所得的好处,又拿回了付款。在应用商店场景下,这通常表现为同一个账户反复为消耗型商品或订阅退款,又或者表现为“友好欺诈”——即一笔本应有效的扣款,却被客户通过银行发起争议处理。

在苹果平台上,你需要生成一个 UUID,在购买开始时将其作为 appAccountToken 传入,StoreKit 会把它附加到生成的交易上,之后它会同时出现在交易数据和消费数据中。在谷歌平台上,你需要调用 setObfuscatedAccountId,传入由该账户派生出的哈希值。这两种方式都能在不暴露任何个人信息的前提下,将商店交易与你自己的用户记录对应起来。

两者都不会,至少不会直接告诉你。谷歌会读取 obfuscated account ID 来识别异常活动,并在购买完成前拦截部分欺诈行为;苹果则会在消费请求(consumption request)中接受你提交的账户历史记录。但无论哪家公司,都不会反过来给你一份“惯犯名单”。这份名单需要你自己整理,依据的正是你按这些标识符归档的记录。

通常原因是 obfuscated account ID 中包含了明文个人数据。比如在其中填入原始的电子邮箱地址,就会导致 Play 直接拦截该笔购买。应当改为提供加密值或单向哈希值,并控制在 64 个字符以内。

它能减少滥用,但无法彻底杜绝。CONSUMPTION_REQUEST 是仅有的两条能让你的证据在决策之前送达的渠道之一,账户历史记录结合交付与消费数据,有助于苹果识别退货滥用和友好欺诈。不过最终决定权仍在苹果手中,而那些通过你无法申诉的流程发生的滥用行为,依然不受影响。

#refund abuse#chargebacks#appaccounttoken#obfuscated account id#fraud prevention
Refund SensorRefund Sensor TeamRefund defense for App Store and Google Play developers