WICG / digital-goods

Other
50 stars 28 forks source link

Design problems concerning `acknowledge` method #19

Closed mgiuca closed 3 years ago

mgiuca commented 4 years ago

Re-opening the design questions that were previously debated in #4 and #7.

We've had internal feedback that the current acknowledge design is inadequate and potentially confusing, and we think a redesign is needed.

Background

Currently, acknowledge takes a mandatory enum parameter to say whether this is for a "one-time" (i.e., non-consumable) or "repeatable" (i.e., consumable) purchase.

If you call it with "onetime":

itemService.acknowledge(purchaseToken, 'onetime');

then you are acknowledging the purchase of the item, but expecting it to remain in the user's "inventory" on the backend server, as a permanent marker that the user has purchased this item, and it is expected to grant some permanent benefit (such as an ongoing premium experience or permanent buff in a video game).

In the Android Play Billing implementation, this represents a call to acknowledgePurchase.

Alternatively, if you call it with "repeatable":

itemService.acknowledge(purchaseToken, 'repeatable');

then you are both acknowledging and consuming the purchase of the item, removing it from the user's "inventory" on the backend server, and allowing it to be purchased again. This is expected to grant the user a temporary bonus, such as a one-off level boost in a video game, or be transferred into the app's own data store as some form of in-app currency. Note that "repeatable" doesn't have anything to do with subscriptions, which are a separate concept.

In the Android Play Billing implementation, this represents a call to consumeAsync, which also implicitly acknowledges the purchase.

In the Web API design, we decided to combine these into a single method called acknowledge, since they are conceptually very similar, the only difference being whether the item is a "one-time" purchase that only needs to be acknowledged, or a "repeatable" purchase, that needs to be consumed to make it available for repurchasing. We were trying to generalize the design to avoid being conceptually over-fitted to the Android Play Billing APIs.

In #4, we considered alternatives like:

Problems

An internal review has identified a number of problems with this approach.

  1. There may be are situations where you want to "acknowledge" a purchase, and subsequently "consume" it. For example, on Android Play Billing, you are required to "acknowledge" a purchase within 72 hours, but you may wish to hold the item in the user's "inventory" until much later, when the user takes some action (such as spending the in-app item associated with the consumable item). With the current API, the way you do this is to first call acknowledge(token, "onetime"), and later, acknowledge(token, "repeatable"). Yuck. Basically, pretending that it's a one-time purchase, just to do an "acknowledge without a consume".
  2. You may wish to "consume" a non-consumable item, as a developer, for testing purposes, so that you can test the purchase flow multiple times. With the current API, you call acknowledge(token, "repeatable") on the non-consumable item. This isn't too bad since you are effectively asking the API to make that purchase repeatable again, but it still feels a bit weird.
  3. The name "one-time" and "repeatable" may imply a non-subscription vs subscription product, respectively. Whereas in the current design, they both refer to different types of non-subscription items.

I think in general, the problem we created was that we thought of "one-time" (or non-consumable) and "repeatable" (or consumable) as being two distinct types of item: like there would be a static distinction between a) items that are only ever purchased and never consumed, and b) items that are purchased and consumed over and over. But really, these are two separate dynamic actions: "acknowledge" (which must be done for all items, but is implicitly done by consuming), and "consume" (which should only be done for consumable items, but which sometimes makes sense to do on any item).

Solution

Having considered all of the above, I think it makes the most sense to just go back to the two separate methods, and give them roughly the semantics they have in the Android Play Billing library:

We have to check with other possible implementations if these two operations make sense outside of the Play Billing semantics.

I really did want to avoid the impression that we're "just copy+pasting" the Android API, but I think in doing so, we tied ourselves up in complexity that could have been avoided if we just exposed those two underlying operations and didn't attempt to abstract over them.

mgiuca commented 4 years ago

@emilieroberts who raised this issue inside Google -- thanks! @phoglenix

phoglenix commented 3 years ago

This is fixed by v2.0 changes (#34). Acknowledge method has been removed entirely, and consume is split to a separate method.