-
Notifications
You must be signed in to change notification settings - Fork 316
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
ReceiptFetcher
: added retry mechanism
#1945
Conversation
e0eb66b
to
ba64e84
Compare
ReceiptFetcher
: added retry mechanismReceiptFetcher
: added retry mechanism
@@ -740,6 +741,15 @@ private extension PurchasesOrchestrator { | |||
} | |||
} | |||
|
|||
private func refreshRequestPolicy(forProductIdentifier productIdentifier: String) -> ReceiptRefreshPolicy { | |||
if self.systemInfo.isSandbox { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Enabled only in sandbox for now
576f825
to
4f405cc
Compare
4517a42
to
65a0562
Compare
|
||
var isActiveSubscription: Bool { | ||
guard self.productType?.isSubscription == true else { return false } | ||
guard let expiration = self.expiresDate else { return true } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
When would this happen?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A purchase without expiration?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
non subscriptions won't have expiration dates
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
so in theory you'd never get here, but better safe than sorry
|
||
func containsActivePurchase(forProductIdentifier identifier: String) -> Bool { | ||
return (self.inAppPurchases.contains { $0.isActiveSubscription } || | ||
self.inAppPurchases.contains { $0.productId == identifier }) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since you're doing ||
... Wouldn't this statement return true
if there's an active subscription for a product identifier? It would also return true if there's an inactive subscription for that productId.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, I think this is the logic we ended up agreeing on to avoid false negatives during a presubscription. @aboedo did I do this right?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ohhh I see what @vegaro means, though - if you made any purchases for a past subscription this will now be true.
it should be either of:
- you have any active subscriptions at all (this is just in case of upgrades, where product id won't be the same as the one you purchased)
- you have a non-subscription purchase for that product identifier
But if you only have an expired subscription for that product identifier this should be false
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Okay I'll update this logic 👍🏻
Once that's finished, are we okay merging this PR enabled only for sandbox then? At the very least it will help integration tests. We could also only enable it for tests.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
we can start by having it for tests only and figure out next steps once we have a clearer picture of how to tackle triage-82
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That would be risk-free, since the only difference is a separate fetch policy that for now is unused :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Okay logic updated.
…xpires` to fix flakiness (#1952) Fixes [CSDK-479]. ### Changes: - Removed extra `verifyEntitlementWentThrough`. #1880 introduced a change so that weekly subscriptions aren't verified, because they expire within a second. This make the test flaky due to that race condition. - Removed `assertNoActiveSubscription`: I tried calling `SKTestSession.disableAutoRenewForTransaction`, but sometimes the server still thinks the subscription has auto-renewed. This was making the test flaky. Turns out that even if it's not active, the eligibility test still passes as expected. - Removed call to `syncPurchases`. It doesn't matter what state the server is in, as long as locally `SKTestSession` has an expired subscription. This, together with #1945, should fix the last of the issues causing flaky integration tests 🤞🏻 [CSDK-479]: https://revenuecats.atlassian.net/browse/CSDK-479?atlOrigin=eyJpIjoiNWRkNTljNzYxNjVmNDY3MDlhMDU5Y2ZhYzA5YTRkZjUiLCJwIjoiZ2l0aHViLWNvbS1KU1cifQ
9805380
to
1033914
Compare
cd63ddd
to
cc52ff1
Compare
Purchases.logLevel = .debug | ||
Purchases.shared.delegate = self.purchasesDelegate | ||
} | ||
|
||
private var dangerousSettings: DangerousSettings { | ||
return .init(autoSyncPurchases: true, | ||
internalSettings: .init(enableReceiptFetchRetry: true)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
New DangerousSettings
API to be able to set options without exposing them as public
.
Co-authored-by: Cesar de la Vega <cesarvegaro@gmail.com>
cc52ff1
to
0936f4e
Compare
…ons` This changes the default back to `StoreKit 1`. We decided to do this for the following reasons: - Purchasing with `PromotionalOffer`s does not work with StoreKit 2 due to an Apple bug (see #2020 (comment)) - `checkTrialOrIntroDiscountEligibility` is significantly slower with StoreKit 2 (#1893). We're adding optimizations to help with that (#2007), but the underlying logic will still be slow. - A rare race-condition where `StoreKit 2` does not have transactions after a purchase ([TRIAGE-82]). We have some workarounds (#1945), but it's still being investigated. _ Note: This effectively reverts 0ee540a. That commit made it easier to only change the default in one place which is why this PR is basically just one line._
… "invalid" receipt See #1945. To make this retry mechanism less risky when eventually enabled, this change makes the "worst case scenario" (where retrying didn't help, or `AppleReceipt.containsActivePurchase` has issues) equivalent to `ReceiptFetcher.onlyIfEmpty`.
…ons` (#2022) This changes the default back to `StoreKit 1`. We decided to do this for the following reasons: - Purchasing with `PromotionalOffer`s does not work with StoreKit 2 due to an Apple bug (see #2020 (comment)) - `checkTrialOrIntroDiscountEligibility` is significantly slower with `StoreKit 2` (#1893). We're adding optimizations to help with that (#2007), but the underlying logic will still be slow. - A rare race-condition where `StoreKit 2` does not have transactions after a purchase ([TRIAGE-82]). We have some workarounds (#1945), but it's still being investigated. _Note: This effectively reverts 0ee540a. That commit made it easier to only change the default in one place which is why this PR is basically just one line._ [TRIAGE-82]: https://revenuecats.atlassian.net/browse/TRIAGE-82?atlOrigin=eyJpIjoiNWRkNTljNzYxNjVmNDY3MDlhMDU5Y2ZhYzA5YTRkZjUiLCJwIjoiZ2l0aHViLWNvbS1KU1cifQ
… "invalid" receipt (#2024) See #1945. To make this retry mechanism less risky when eventually enabled, this change makes the "worst case scenario" (where retrying didn't help, or `AppleReceipt.containsActivePurchase` has issues) equivalent to `ReceiptFetcher.onlyIfEmpty`. _Note: this is currently not enabled in production, only in Integration Tests._
See RevenueCat/purchases-ios#1945. Hopefully this will fail the tests in #278.
See RevenueCat/purchases-ios#1945. Hopefully this will fail the tests in #278.
Fixes #2260 and [SDKONCALL-216]. Follow up to #2245. The change in #2245 was meant to avoid receipt refresh throttling errors (#2116). However, that was a regression when using StoreKit config files because those don't get refreshed in the backend. Unfortunately the workaround introduced in #1945 prevented us from noticing this, because we use `DangerousSettings.InternalSettings.enableReceiptFetchRetry` in our own integration tests. I added an integration test that explicitly covers this scenario. That fails without this change if disabling that dangerous setting. I thought about making that test run without the setting, but that might lead to flaky failures in CI, which is why we introduced that workaround in the first place. But at least now we have an explicit integration test for this, as well as 2 unit tests.
…2280) Fixes #2260 and [SDKONCALL-216]. Follow up to #2245. The change in #2245 was meant to avoid receipt refresh throttling errors (#2116). However, that was a regression when using StoreKit config files because those don't get refreshed in the backend. Unfortunately the workaround introduced in #1945 prevented us from noticing this, because we use `DangerousSettings.InternalSettings.enableReceiptFetchRetry` in our own integration tests. I added an integration test that explicitly covers this scenario. That fails without this change if disabling that dangerous setting. I thought about making that test run without the setting, but that might lead to flaky failures in CI, which is why we introduced that workaround in the first place. But at least now we have an explicit integration test for this, as well as 2 unit tests. [SDKONCALL-216]: https://revenuecats.atlassian.net/browse/SDKONCALL-216?atlOrigin=eyJpIjoiNWRkNTljNzYxNjVmNDY3MDlhMDU5Y2ZhYzA5YTRkZjUiLCJwIjoiZ2l0aHViLWNvbS1KU1cifQ
Fixes CSDK-478
Depends on #1941, #1942, and #1943.Example:
Log of a successful purchase after retrying 🎉