Acknowledge Galaxy Subscription Purchases#2939
Conversation
| """.trimIndent() | ||
|
|
||
| return ProductVo(json) | ||
| return mockk<ProductVo>(relaxed = true).also { productVo -> |
There was a problem hiding this comment.
I started having some issues instantiating ProductVo and PurchaseVo directly, so I decided to mock them out instead
tonidero
left a comment
There was a problem hiding this comment.
Looking good! Just some comments but almost there!
| clearInFlightRequest() | ||
| onError?.invoke(purchasesError) | ||
| return | ||
| } else if (acknowledgementResults.count() > 1) { |
There was a problem hiding this comment.
Hmm so I guess you can acknowledge multiple purchases simultaneously? Interesting... but yeah I think it's fine to do one per request as we're doing now, so I imagine this should never happen indeed 👍
There was a problem hiding this comment.
Yeah, you can acknowledge/consume multiple purchases at once. In fact, their docs even strongly recommend that you don't acknowledge them serially:
We recommend reporting purchased consumable items immediately after verifying their purchase and reporting all unreported products in one consumePurchasedItems() or acknowledgePurchases() method call in order to avoid system overload or malfuction.
However, our system is only architected today to handle acknowleding/consuming one purchase at a time - see the BillingAbstract.consumeAndSave function's signature:
abstract fun consumeAndSave(
finishTransactions: Boolean,
purchase: StoreTransaction,
shouldConsume: Boolean,
initiationSource: PostReceiptInitiationSource,
)I think that this is okay for the purchase scenario - the Galaxy Store doesn't have multi-line subscriptions, so only one purchase should go through at a time. The only case I can think of where we might have multiple to do at once is in a restore scenario, or maybe when multiple distinct POST /receipt requests have failed, and the SDK needs to re-POST receipts. Even in that scenario though, we'll be calling this endpoint serially, so I think it should be fine, what do you think?
There was a problem hiding this comment.
Yeah, I think it should be fine... probably worth seeing how it behaves in the wild though, hopefully it's not rate limited on their side 😬
There was a problem hiding this comment.
cc @MarkVillacampa this might be a good thing to ask the Samsung team
| initiationSource: PostReceiptInitiationSource, | ||
| ) { | ||
| warnLog { "Unimplemented: GalaxyBillingWrapper.consumeAndSave" } | ||
| if (!finishTransactions || purchase.type == ProductType.UNKNOWN) return |
There was a problem hiding this comment.
In these cases, I think we should still call addSuccessfullyPostedToken so we're not posting the same tokens over and over? As long as it's not in a pending state though.
There was a problem hiding this comment.
It looks like we aren't calling addSuccessfullyPostedToken for UNKNOWN product types on the the Amazon or Play Store today:
Google:
Amazon:
Where those two stores differ is whether they call addSuccessfullyPostedToken in observer mode 😅
- We don't for Amazon
- We do for the Play Store
There was a problem hiding this comment.
I think Amazon is wrong in this case... If we don't add it, we will be posting the token on every app open I think?
There was a problem hiding this comment.
Yep, I believe you're correct 😅 I'll add the token for the Galaxy Store for both UNKNOWN product types and when observer mode is enabled
There was a problem hiding this comment.
Updated in 7dbaab9 to store the token when the product type is Unknown or when observer mode is enabled 👍
|
|
||
| @Test | ||
| fun `consumeAndSave does nothing when finishTransactions is false`() { | ||
| val wrapper = spyk(createWrapper()) |
There was a problem hiding this comment.
Hmm not a super fan of creating a spy just to verify that a method of the class we're testing is not called... I think the proper thing to do would be to verify whether the expected side effects (in this case, calling the IAPHelper consume/acknowledge methods are not called)
There was a problem hiding this comment.
Removed the spies from the tests here! 🚫🕵️♂️
|
@tonidero Since your last review, I've made the following changes to this PR:
|
| private val deviceCache: DeviceCache, | ||
| val billingMode: GalaxyBillingMode, | ||
| private val iapHelperProvider: IAPHelperProvider = DefaultIAPHelperProvider( | ||
| private val iapHelper: IAPHelperProvider = DefaultIAPHelperProvider( |
There was a problem hiding this comment.
I'm wondering if we should call it IAPHelperWrapper instead of Provider... but that should probably be in a separate PR.
| } | ||
|
|
||
| override fun onGetProducts(error: ErrorVo, products: ArrayList<ProductVo>) { | ||
| override fun onGetProducts(error: ErrorVo, products: ArrayList<ProductVo?>) { |
There was a problem hiding this comment.
It's quite interesting this can return a nullable product list... Any idea in what scenarios this would return a list with null elements?
There was a problem hiding this comment.
I cant' think of any scenarios where this list should contain null elements, but because of the way that the function's signature is written in Java, it's a scenario that is allowed by the API. The signature in Java looks like this:
public interface OnGetProductsDetailsListener {
void onGetProducts(@NotNull ErrorVo var1, @NotNull ArrayList<ProductVo> var2);
}var2 is marked as @NotNull, which means that var2 itself is not null, but it doesn't guarantee that the objects inside the list are non-null. In fact, when we use Android Studio's auto complete to write the function, it includes the ? on the ProductVo. If we didn't mark ProductVo as optional in this signature, I think that we would get a crash when the code bridges from Java to Kotlin if there are any null elements in the list 😬
I realized this while working on the acknowledgement listener, which also receives an ArrayList of nullable objects, so I went back and fixed it here before I forgot about it :)
There was a problem hiding this comment.
Oh right 😓 Makes sense. thanks for explaining!!
Description
Adds support for acknowledging Galaxy subscription purchases.