所有文章
Deep dive阅读需 9 分钟

待处理购买看起来像一笔销售,但钱还没到账,提前发放就等于把产品白送出去

App Store 和 Google Play 都有待处理购买状态,也就是商店已经受理但尚未扣款的订单。在付款到账之前就解锁,每一笔最终失败的订单都是纯粹的成本。本文介绍待处理购买在两家商店上如何运作,错误发放一笔会付出什么代价,以及如何在不造成损失的情况下处理它们。

在暖光下,收银员把一个包好的小包裹从柜台递过来,说明待处理购买中商品在付款到账之前就已交付

要点

  • 待处理购买是商店已经受理但尚未扣款的真实订单。你的代码看到一笔新购买,但钱还没到账,而且可能永远不会到账。
  • 只有在 Google Play 上状态为 PURCHASED,或在 Apple 上交易已完成时,才发放权益。绝不要在 PENDING 状态或 Apple 的待处理结果上解锁。
  • 在 Google Play 上,门店现金付款、银行转账以及部分运营商代扣是通过外部渠道结算的,因此在客户真正付款之前,购买返回的是 PENDING 而不是 PURCHASED。
  • 在 Apple 上,待处理购买通常是 Ask to Buy,需要家庭组织者批准。批准可能需要数小时或数天,完成后的交易稍后通过 Transaction.updates 送达。
  • 在一笔随后被取消的待处理订单上解锁,你就在一笔从未到账的收费上花掉了算力、API 调用、存储,或消耗型商品的实际发放。与退款不同,这里没有钱可以追回,因为从来就没有收到过钱。
  • 当一笔待处理订单失败时,商店会通知你。Google 发送 ONE_TIME_PRODUCT_CANCELED,类型 2,或 SUBSCRIPTION_PENDING_PURCHASE_CANCELED,类型 20。Apple 则干脆永远不送达已完成的交易。
  • 待处理订单可能在你的应用关闭期间变成真实销售,所以在返回时要重新检查:在 Google Play 上于 onResume() 中调用 queryPurchasesAsync(),在 Apple 上持续监听 Transaction.updates。

有人在你的应用里点了购买。你的日志显示一笔新订单,你的计费监听器触发,于是你交付了商品。对绝大多数购买来说,这样做完全正确。但对待处理购买来说这是个错误,因为订单存在,钱却不存在。客户选择了一种稍后才结算的付款方式,商店还在等着收款,而你刚刚为一笔可能永远不会到账的收费交付了一项付费功能。这是退款的沉默表亲。这里没有任何东西被撤销,因为从来就没有收到过任何钱。你只是把产品白送了出去。

待处理购买是一笔处于尚未付款状态的真实订单,App Store 和 Google Play 都有这种状态。两家商店都明白地告诉你要等待。陷阱在于,待处理订单在你的代码里看起来几乎和已完成订单一模一样,因此一个把每笔新购买都当作销售的集成,会在商店还在尝试收款的订单上就交付商品。处理得当,你不会有任何损失。处理不当,每一笔失败的慢速付款都是纯粹的成本,由你自掏腰包交付。

待处理购买究竟是什么

待处理购买是商店已经记录但尚未扣款的订单。买家开始了流程,商店受理了它,而结算正在你的应用看不到的某个地方进行。Google Play 把这称为 PENDING 状态。Apple 称之为待处理交易,或延迟交易。名称不同,事实相同:商店在等待收款的同时让一笔订单保持开放,并且它已经告诉你不要把那笔订单当作钱。

Google Play:付款正在别处进行

有些付款方式是通过外部渠道结算的。实体门店现金、银行转账以及部分运营商代扣,都在点击与扣款之间需要额外的步骤。当客户选择其中一种时,Google 返回的购买处于 PENDING 状态而不是 PURCHASED。对于现金付款,客户会通过通知和电子邮件收到一个代码,带着它前往参与门店,向收银员付款。在此之前,Google 什么都没收到,你也一样。Google 的规则只有一句话:使用 getPurchaseState(),只有在状态为 PURCHASED 时才发放权益。它还告诉你不要在购买处于 PENDING 时确认它,因为确认属于已付款的订单,而不是一个被承诺的订单。

Apple:这笔购买在等别人点一下

Apple 的待处理状态意味着这笔交易在完成之前需要一个外部动作。最常见的是 Ask to Buy,孩子发起一笔购买,需要家庭组织者批准。在 StoreKit 2 中,购买调用返回 Product.PurchaseResult.pending。在较旧的 StoreKit 中,这笔交易被报告为 deferred。无论哪种方式,Apple 都还没有向任何人扣款,而完成后的交易(如果会来的话)会通过 Transaction.updates 异步送达。向客户显示等待状态,在已完成的交易到达之前不要解锁任何东西。

为什么发放待处理购买会让你付出真金白银

这里的损失不是退款那种,把你已经入账的钱抽回去。它在一个特定方面更糟:根本没有钱可抽回,因为从来就没有收到过。

你交付了,商店却从未收款

当你在一笔随后被取消的待处理订单上解锁时,你已经为服务它花了钱。运行该功能的算力、你付费的第三方 API 调用、你分配的存储,以及对消耗型商品来说你所售之物的实际发放。所有这些都随着一笔没有产生任何收入的订单流了出去。退款至少始于一笔真实发生过的收费。而一笔被错误发放的待处理购买根本就没有过收费,所以它甚至不会显示为流出的钱。它什么都不显示,而这正是它容易被忽略、也容易反复发生的原因。

取消信号,以及它的含义

当一笔待处理订单失效时,商店确实会通知你。在 Google Play 上,一款失败的一次性商品会发送 ONE_TIME_PRODUCT_CANCELED 通知,类型 2,而一个曾处于待处理的订阅会发送 SUBSCRIPTION_PENDING_PURCHASE_CANCELED,类型 20。当同样的订单反过来成功时,你会收到 ONE_TIME_PRODUCT_PURCHASED,类型 1,或 SUBSCRIPTION_PURCHASED,类型 4。在 Apple 上没有可以捕获的取消事件,因为一笔被拒绝的延迟交易根本就不会变成已完成的交易。如果你提前解锁了,那份沉默就是账单。

问题Google PlayApple
由什么触发现金、银行转账、部分运营商代扣Ask to Buy 批准,或其他必需的操作
你看到的状态PurchaseState PENDING待处理结果,或一笔延迟交易
何时发放访问权限状态为 PURCHASED交易已完成
它成功了ONE_TIME_PRODUCT_PURCHASED (1)、SUBSCRIPTION_PURCHASED (4)通过 Transaction.updates 送达的已完成交易
它失败了ONE_TIME_PRODUCT_CANCELED (2)、SUBSCRIPTION_PENDING_PURCHASE_CANCELED (20)永远不会有已完成的交易送达
是否已收款否,直到 PURCHASED 才收款否,直到交易完成才收款

这段等待期,以及谁在等谁

待处理购买不是一个你在与之赛跑的计时器。从你这一侧看,它是一个还没有开始走的计时器。

Google Play 给客户的是数天,而不是数分钟

现金或银行转账付款按客户的节奏结算,而不是你的。订单会停留在 PENDING,直到客户付款,或者时限用尽、Google 取消它。你自己的三天确认时限,也就是那个在你未确认购买时会自动退款的时限,要等到购买从 PENDING 转为 PURCHASED 之后才开始计时。所以没有必要急着去服务一笔待处理订单。需要的只是等待状态改变的那份自律。

Apple 的批准在家庭组织者的手机上

一个 Ask to Buy 请求会作为提示出现在组织者的设备上,他们会在有空的时候批准或拒绝。那可能是几分钟、几小时,或一天之后,你的应用无法催促它。唯一正确的做法是呈现等待状态,并让 StoreKit 在批准到来时(如果会到来的话)把已完成的交易交给你。

一个封好的纸箱和一个沙漏并排放在桌上,说明待处理购买中商品已经备好但付款尚未到账

如何在不造成损失的情况下处理待处理购买

整件事归结为四个习惯。没有一个是难事,而漏掉其中任何一个,就是钱流失的地方。

在已付款状态上发放,绝不在待处理状态上发放

在 Google Play 上,检查 getPurchaseState(),只在 PURCHASED 时发放,并且不要在购买处于 PENDING 时确认它。在 Apple 上,只在已完成的交易上解锁,绝不在待处理结果上解锁。这一条规则就堵住了整个漏洞。其余的一切都是为了确保你在已付款状态到达时真的能注意到。

应用回到前台时重新检查

从待处理到已付款的转变常常发生在你的应用没有运行的时候。在 Google Play 上,在你的 onResume() 处理程序中调用 queryPurchasesAsync(),以捕获在后台变为 PURCHASED 的订单,并把你的 Real-time Developer Notifications 监听器作为服务器端的事实来源。在 Apple 上,在应用的整个生命周期内监听 Transaction.updates,因为一笔获批的交易可能在原始购买调用返回很久之后才到达。

启用待处理支持,并测试两种结局

Google 要求你在构建 BillingClient 时调用 enablePendingPurchases(),而且为一次性商品支持待处理交易是强制的,不是可选的。在发布之前测试它。许可测试者会获得两种额外的延迟付款测试工具,其中付款会在几分钟后自动完成或自动取消,这样你就能端到端地观察付款路径和失败路径。

告诉客户订单还没完成

一个处于待处理状态的买家是一位正处在购买中途的真实客户,不是一次失败。告诉他们订单正在等待他们的付款或一次批准,并给他们一条清晰的返回路径去完成它。一个沉默的待处理状态会丢失那些被清晰标注就能挽回的销售,因为这些买家中的大多数仍然想要那件东西,只是还差一步。

RefundHalt 为退款和拒付已经在读取的那些 Real-time Developer Notifications 和 App Store Server Notifications 数据流,同样携带着这些信号。那条表示一笔待处理订单终于到账的已购买通知,以及那条表示它没有到账的已取消通知,都会落在你的仪表盘里,与其余的收入事件并排,这样一笔失败的待处理订单就成了你能看得见的东西,而不是你意外为之买了单的东西。

简而言之

待处理购买是一笔没有付款的订单,两家商店都明确表示你应该等待。Google Play 把现金、银行转账以及部分运营商代扣的订单以 PENDING 状态返回,并告诉你只在 PURCHASED 时才发放访问权限。Apple 为 Ask to Buy 和其他必需操作返回一笔待处理或延迟交易,并在稍后通过 Transaction.updates 送达已完成的交易。在已付款状态上解锁,在应用恢复时重新检查,启用并测试待处理支持,并为客户标注这笔等待中的订单。做到这些,待处理购买就不会让你付出任何代价。跳过它,你就为一笔从未到来的收费交付了一款付费产品,而这是唯一一种拿不出收据来指认的损失。

常见问题解答

什么是待处理购买?
待处理购买是商店已经受理但尚未扣款的订单。在 Google Play 上,它是 PENDING 购买状态,用于稍后才结算的付款方式,例如现金、银行转账以及部分运营商代扣。在 Apple 上,它是一笔待处理或延迟交易,最常见的是等待家庭组织者批准的 Ask to Buy 购买。这两种情况下都还没有收到钱,所以你不应该发放访问权限。
购买处于待处理状态时我应该发放访问权限吗?
不应该。只有在 Google Play 上状态为 PURCHASED,或在 Apple 上交易已完成时,才发放权益。如果你在订单仍处于待处理时就解锁了功能,而付款始终没有到账,你就免费交付了产品,而且没有收费可以撤销,因为根本就没有发生过收费。
哪些付款方式会在 Google Play 上导致待处理购买?
通过外部渠道结算的付款方式。实体门店的现金付款、银行转账以及部分运营商代扣选项,在点击与扣款之间需要额外的步骤,所以 Google 会以 PENDING 状态而不是 PURCHASED 返回购买。对于现金付款,客户会通过通知和电子邮件收到一个代码,然后在参与门店付款。
什么是 Ask to Buy,它与待处理购买有什么关系?
Ask to Buy 是 Apple 的 Family Sharing 功能,它让孩子可以发起一笔需要家庭组织者批准的购买。在请求等待期间,购买处于 Apple 的待处理状态,在 StoreKit 2 中返回为 Product.PurchaseResult.pending,在较旧的 StoreKit 中返回为一笔延迟交易。只有当组织者批准时,已完成的交易才会通过 Transaction.updates 到达。
如果一笔待处理购买始终没有付款会怎样?
订单会被取消,没有钱易手。在 Google Play 上,对于一次性商品你会收到 ONE_TIME_PRODUCT_CANCELED 通知,类型 2,对于订阅则会收到 SUBSCRIPTION_PENDING_PURCHASE_CANCELED,类型 20。在 Apple 上,那笔延迟交易根本不会变成已完成的交易。如果你已经发放了访问权限,那一刻损失就变成了真实的。
待处理购买和退款是一回事吗?
不是。退款撤销的是一笔真正收到过的付款。一笔失败的待处理购买从一开始就没有被扣款,所以没有任何东西可撤销,退款报告里也不会出现任何东西。如果你提前解锁了它,代价就是你为服务一笔没有产生任何收入的订单而花掉的算力、API 调用、存储或消耗型商品。

来源和延伸阅读

RefundHalt

App Store 和 Google Play 退款自动驾驶

继续阅读

下一项退款请求已经在路上。

读完又一封关于未能申辩退款的支持邮件所需的时间,足够您完成 RefundHalt 设置。