Skip to content

Add non-subscriptions support to offline customer info#958

Merged
vegaro merged 3 commits into
mainfrom
cesar/sdk-3054-android-add-non-subscriptions-support-to
Apr 12, 2023
Merged

Add non-subscriptions support to offline customer info#958
vegaro merged 3 commits into
mainfrom
cesar/sdk-3054-android-add-non-subscriptions-support-to

Conversation

@vegaro

@vegaro vegaro commented Apr 10, 2023

Copy link
Copy Markdown
Member

Since we don't have the RevenueCat ID and we are doing this mainly for entitlement granting purposes, I mimicked what the backend does, which is add the non-subscriptions to the subscriptions array.

@vegaro vegaro requested a review from a team April 10, 2023 17:11
@vegaro vegaro added the pr:fix A bug fix label Apr 10, 2023
@vegaro

vegaro commented Apr 10, 2023

Copy link
Copy Markdown
Member Author

Oops tests are not passing

val subscriptions = JSONObject()

purchasedProducts.filter { it.storeTransaction.type == ProductType.SUBS }.forEach { product ->
purchasedProducts.forEach { product ->

@tonidero tonidero Apr 11, 2023

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm I'm not sure I understand this change. So this will add inapps to the subscriptions section of the response. Is that correct? This is independent of the entitlements section of the response.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As mentioned by @aboedo here #885 (comment), the load shedder sends the non_subscriptions in the subscriptions array. For example, a consumable product would be sent like this by the load shedder:

{
   "request_date":"2023-03-24T19:43:25Z",
   "request_date_ms":1679687005900,
   "subscriber":{
      "entitlements":{
         "another_pro_1":{
            "expires_date":"2023-03-25T19:43:25Z",
            "grace_period_expires_date":null,
            "product_identifier":"consumable",
            "purchase_date":"2023-03-24T19:43:25Z"
         }
      },
      "first_seen":"2023-03-24T19:43:25Z",
      "last_seen":"2023-03-24T19:43:25Z",
      "management_url":"https:\/\/play.google.com\/store\/account\/subscriptions",
      "non_subscriptions":{
         
      },
      "original_app_user_id":"$RCAnonymousID:e142926d24dc49c1989e575448bc54db",
      "original_application_version":"1",
      "original_purchase_date":"2023-03-24T19:43:25Z",
      "other_purchases":{
         "consumable":{
            "purchase_date":"2023-03-24T19:43:25Z"
         }
      },
      "subscriptions":{
         "consumable":{
            "auto_resume_date":null,
            "billing_issues_detected_at":null,
            "expires_date":"2023-03-25T19:43:25Z",
            "grace_period_expires_date":null,
            "is_sandbox":false,
            "original_purchase_date":"2023-03-24T19:43:25Z",
            "ownership_type":"PURCHASED",
            "period_type":"normal",
            "purchase_date":"2023-03-24T19:43:25Z",
            "refunded_at":null,
            "store":"play_store",
            "unsubscribe_detected_at":null
         }
      }
   }
}

instead of being sent in the non_subscriptions:

"non_subscriptions": {
    "consumable": [
	      {
	        "id": "72c26cc69c",
	        "is_sandbox": false,
	        "original_purchase_date": "12023-03-24T19:43:25Z",
	        "purchase_date": "2023-03-24T19:43:25Z",
	        "store": "play_store"
	      }
    ],
    ...

The problem we have in the SDK is that we don't know what to set as id.

Thinking about it a bit more, I don't think the id is critical information in the SDK. I consider more critical to show it as a non_subscription instead. So maybe we should set the non_subscriptions correctly with maybe an empty or a random id?

What do you think? I believe the load shedder sets these as subscriptions because it can't determine the type of product, but we can.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, to me, adding this information to the subscriptions section when we can figure out that it's not, is not our best option. I understand the load shedder does this since it doesn't have a way to differentiate between types, but I think we should separate them for offline entitlements since that info is available.

I don't think the id is critical information in the SDK

I agree, I would say it's not a critical piece of information for the clients (let us know if we're wrong @aboedo). So I think having an empty id might be the best option? Also, we might want to document this behavior, if we end up going this route...

@aboedo aboedo Apr 11, 2023

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, the id is the biggest blocker here... As for how critical it is: how would you tell apart 2 consumable purchases without it? Genuine question.

Like, I go in and buy 1 pack of 50 coins, and then 2 days later I go buy another one. How do you distinguish them? Just 2 entries? How do I know which one I've already granted to the user?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note that ideally this will only be enabled for a very short time. So I don't think it should be a problem if they buy multiple consumables in the span of a few days. If the user buys multiple of the same consumable while offline entitlements is enabled it could be a problem indeed... And I'm also concerned about giving an arbitrary or empty id, since devs might use it on their system to avoid double-granting, but if we change it later, it could lead to double-granting...

Honestly, I'm just thinking about not supporting consumables while offline entitlements is on... We can still grant the entitlements, but feels like the non-subscriptions section of the customer info could remain empty. As for making it part of the subscriptions section, I don't think that makes sense... What do you think @aboedo @vegaro ?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the overall perfect solution is to design a system where the SDK can generate an ID when needed, and then pass that to the backend. It's not trivial because we need to figure out how to handle collisions, though.
As for whether to treat one-times as subscriptions temporarily vs returning an error... I think an argument can be made for either.
What we can't do is return a success and not grant anything. So if we didn't have this as a subscription, we'd have to return an error (assuming we don't have unique IDs yet)

But... once we return an error, we need to return errors for all transactions going forward until the server is back up, right? Otherwise, we have no way to guarantee that we return all the things a user has paid for.

And there we start getting into tricky territory. So for now I'd vote to just show something as a subscription temporarily, like the load shedder does, and start thinking about a system for unique IDs

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

as for "this will be enabled for a very short time", well... ideally. But realistically, a user could fake the 5xxs, so we need a system that can run sustainably.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just wanted to add that both queryAllPurchases and queryPurchases will only return the latest purchase for a particular consumable. So there will never be more than one purchase for a non_subscription right now, until we start merging with a cached customer info.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right, considering we won't have the exploit we could have in iOS and that it's not something new since it's what the load shedder does, I think we can go with this for now. We can revisit the logic later, doesn't need to be perfect for V1 👍

val subscriptions = JSONObject()

purchasedProducts.filter { it.storeTransaction.type == ProductType.SUBS }.forEach { product ->
purchasedProducts.forEach { product ->

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right, considering we won't have the exploit we could have in iOS and that it's not something new since it's what the load shedder does, I think we can go with this for now. We can revisit the logic later, doesn't need to be perfect for V1 👍

@vegaro vegaro merged commit ecd1aa2 into main Apr 12, 2023
@vegaro vegaro deleted the cesar/sdk-3054-android-add-non-subscriptions-support-to branch April 12, 2023 17:55
tonidero added a commit that referenced this pull request May 18, 2023
**This is an automatic release.**

### New Features
* CAT-859 Expose whether or not a SubscriptionOption is Prepaid in the
SDK (#1005) via Deema AlShamaa (@dalshamaa)
### Bugfixes
* [CF-1324] Fix personalizedPrice defaulting to false (#952) via beylmk
(@beylmk)
### Performance Improvements
* Store and return ETag last refresh time header (#978) via Toni Rico
(@tonidero)
### Dependency Updates
* Bump fastlane-plugin-revenuecat_internal from `3b03efa` to `fe45299`
(#991) via dependabot[bot] (@dependabot[bot])
* Bump danger from 9.2.0 to 9.3.0 (#981) via dependabot[bot]
(@dependabot[bot])
* Bump fastlane-plugin-revenuecat_internal from `8482a43` to `3b03efa`
(#974) via dependabot[bot] (@dependabot[bot])
* Bump fastlane from 2.212.1 to 2.212.2 (#973) via dependabot[bot]
(@dependabot[bot])
* Bump fastlane-plugin-revenuecat_internal from `9255366` to `8482a43`
(#961) via dependabot[bot] (@dependabot[bot])
### Other Changes
* Add proration modes to post to backend (#977) via swehner (@swehner)
* Added ENTITLEMENTS_COMPUTED_ON_DEVICE (#939) via Cesar de la Vega
(@vegaro)
* Fix flaky test in OfflineCustomerInfoCalculatorTest (#997) via Cesar
de la Vega (@vegaro)
* Fix `OfflineCustomerInfoCalculatorTest` `Unresolved reference:
ProducType` (#995) via Cesar de la Vega (@vegaro)
* Add support for product_plan_identifier for offline customer info
(#959) via Cesar de la Vega (@vegaro)
* Add non-subscriptions support to offline customer info (#958) via
Cesar de la Vega (@vegaro)
* Query only active purchases when generating offline entitlements
customer info (#1003) via Toni Rico (@tonidero)
* Fix `PurchasesIntegrationTest` building issue (#996 into main) (#998)
via Cesar de la Vega (@vegaro)
* Fail offline entitlements computation if product entitlement mapping
not available (#999) via Toni Rico (@tonidero)
* Fix  build_magic_weather lane (#993) via Cesar de la Vega (@vegaro)
* Add backend integration tests and test product entitlement mapping
endpoint (#988) via Toni Rico (@tonidero)
* Fix purchases integration tests (#980) via Toni Rico (@tonidero)
* Disable offline entitlements if active inapp purchases exist (#983)
via Toni Rico (@tonidero)
* Clear cached customer info upon entering offline entitlements mode
(#989) via Toni Rico (@tonidero)
* Update product entitlement mapping request to new format (#976) via
Toni Rico (@tonidero)
* Support enabling/disabling offline entitlements (#964) via Toni Rico
(@tonidero)
* Add back integration tests automation (#972) via Toni Rico (@tonidero)
* Upgrade to AGP 8.0 (#975) via Toni Rico (@tonidero)
* Extract post receipt logic to PostReceiptHelper (#967) via Toni Rico
(@tonidero)
* Add isServerDown to error callback for postReceipt and getCustomerInfo
requests (#963) via Toni Rico (@tonidero)
* Add back integration test flavors (#962) via Toni Rico (@tonidero)
* Fix storing test results (#966) via Cesar de la Vega (@vegaro)
* Extract detekt job from test job (#965) via Cesar de la Vega (@vegaro)


[CF-1324]:
https://revenuecats.atlassian.net/browse/CF-1324?atlOrigin=eyJpIjoiNWRkNTljNzYxNjVmNDY3MDlhMDU5Y2ZhYzA5YTRkZjUiLCJwIjoiZ2l0aHViLWNvbS1KU1cifQ

---------

Co-authored-by: revenuecat-ops <ops@revenuecat.com>
Co-authored-by: Toni Rico <antonio.rico.diez@revenuecat.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

pr:fix A bug fix

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants