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.
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.
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:
Making a Boolean parameter ("isRepeatable" or "alsoConsume") instead of an enum.
Making the method take an optional dictionary with an optional Boolean (forcing the client to explicitly name the parameter).
Leaving them as two separate methods.
Problems
An internal review has identified a number of problems with this approach.
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".
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.
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:
acknowledge would drop its second argument, and always do acknowledging only. (Note: for backwards compatibility during the origin trial we are running on Chrome, we would keep the second argument for some time, but phase it out, but it would be removed from the spec.)
consume would be added, to do the consuming. It would be specced as implicitly calling acknowledge first, for items that had not already been acknowledged.
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.
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"
: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"
: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.
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".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.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:
acknowledge
would drop its second argument, and always do acknowledging only. (Note: for backwards compatibility during the origin trial we are running on Chrome, we would keep the second argument for some time, but phase it out, but it would be removed from the spec.)consume
would be added, to do the consuming. It would be specced as implicitly callingacknowledge
first, for items that had not already been acknowledged.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.