* Small cleanup
* add `LocalTransactionDetailsStorage`
* Add models for local transaction metadata caching. Reset PurchasesOrchestrator
* Complete `Codable`, lossless implementation of `LocalTransactionMetadata`
* Rename `LocalTransactionMetadataCache` to `LocalTransactionMetadataStore`
* Comment
* lint
* Remove unnecessary import
* Remove unused method
* Replace `Storefront` with `storeCountry` in both `ProductRequestData` and `PurchasedTransactionData`
This removes the need of a `StorefrontEncodedWrapper` object
* Disambiguate `asDictionary()` method
Now that `SubscriberAttribute` is `Codable`, `Encodable`'s `asDictionary()` and `SubscriberAttribute`'s `asDictionary()` were clashing
* Undo `Codable` conformance of `PresentedOfferingContext` and `TargetingContext`
* lint
* `LocalTransactionMetadata` now refers to a single transaction. `LocalTransactionMetadataStore` now stores one file per transaction and does not use an in-memory cache
* Temporarily remove `LocalTransactionMetadata` and `LocalTransactionMetadataStore` for easier PR review
* Revert "Temporarily remove `LocalTransactionMetadata` and `LocalTransactionMetadataStore` for easier PR review"
This reverts commit d958601711.
* Small code cleanup
* Remove `EncodedAppleReceipt` from `LocalTransactionMetadata`
* Undo `Codable` conformance of `EncodedAppleReceipt`
Not needed for now
* Remove `self.appTransactionJWS` from `LocalTransactionMetadata`
* Adapt to API changes in `SynchronizedLargeItemCache`
* Plug in `LocalTransactionMetadataStore` and store + get stored `LocalTransactionMetadata`
* Removed unused parameter
* Remove `schemaVersion` property from `LocalTransactionMetadata`
* Add `MockLocalTransactionMetadataStore` for tests
* Remove `appUserID` from `PurchasedTransactionData`
* Update tests
* Add `transaction_id` field to POST receipt
* adapt tests
* Add transaction ID parameter to cache key for `PostReceiptDataOperation`'s factory
* [skip ci] Generating new test snapshots (#6024)
* [skip ci] Generating new test snapshots (#6025)
* [skip ci] Generating new test snapshots (#6027)
* [skip ci] Generating new test snapshots (#6026)
* [skip ci] Generating new test snapshots (#6028)
* [skip ci] Generating new test snapshots (#6029)
* [skip ci] Generating new test snapshots (#6033)
* [skip ci] Generating new test snapshots (#6032)
* Fix lint
* Only send transaction ID when there's associated metadata
* Fix tests compilation
* Add unit test
* Add tests
* Add tests for `LocalTransactionMetadataStore`
* Have `PurchasesOrchestrator` post transaction with syncing purchases in SK2 go through `TransactionPoster` to include the `LocalTransactionMetadata` logic there
* Correct values for `purchaseCompletedBy` field in `PostReceiptDataOperation`
* Fix finish transaction logic
* Add more tests and fix test compilation
* More tests
* [skip ci] Generating new test snapshots (#6057)
* [skip ci] Generating new test snapshots (#6050)
* [skip ci] Generating new test snapshots (#6051)
* [skip ci] Generating new test snapshots (#6052)
* [skip ci] Generating new test snapshots (#6053)
* [skip ci] Generating new test snapshots (#6054)
* [skip ci] Generating new test snapshots (#6055)
* [skip ci] Generating new test snapshots (#6056)
* Fix lint
* Remove TODO
* Remove redundancy
* Fix tests and add test
* [skip ci] Generating new test snapshots (#6059)
* [skip ci] Generating new test snapshots (#6060)
* [skip ci] Generating new test snapshots (#6061)
* [skip ci] Generating new test snapshots (#6062)
* [skip ci] Generating new test snapshots (#6063)
* [skip ci] Generating new test snapshots (#6064)
* [skip ci] Generating new test snapshots (#6068)
* [skip ci] Generating new test snapshots (#6069)
* Fix lint
* Fix recovering `PresentedOfferingContext` from `LocalTransactionMetadata`
* Take `source: PurchaseSource` out from `PurchasedTransactionData`
* Rename `PurchaseSource` to `PostReceiptSource` and `ProductRequestData.InitiationSource` to `PostReceiptSource.InitiationSource`
* Pass receipt as parameter directly
* Fix lint
* Remove unused variable
* fix test with previous value in snapshot
* Fix compilation of tests
* Introduce `sdk_originated` parameter in POST /receipt
* Adapt tests to new `sdkOriginated` property
* TEMP: Remove sdkOriginated field (to split into 2 PRs)
* Store transaction metadata when offering context or paywall is present
* Fix PaywallEvent initialization in tests
* Use variable
* Fix tests after merge from main
* Fix and improved tests
* Fix tests
* test improvements
* Use `transactionId` in `PostData` as cacheKey only if it `containsAttributionData`
This means that POST /receipt for renewals are deduplicated, while purchases with attribution data are guaranteed to always be sent (regardless of whether there's an in-flight POST /receipt request for a renewal of that purchase)
* Adapt tests to new behavior + new tests
* Update Sources/Networking/Operations/PostReceiptDataOperation.swift
Co-authored-by: Toni Rico <toni.rico.diez@revenuecat.com>
* Do not clear `LocalTransactionMetadata` when receiving offline-computed CustomerInfo
* Add tests
* Fix test + add test
* Fix test
* Fix lint
* Fix tests
---------
Co-authored-by: RevenueCat Git Bot <72824662+RCGitBot@users.noreply.github.com>
Co-authored-by: Toni Rico <toni.rico.diez@revenuecat.com>
* Small cleanup
* add `LocalTransactionDetailsStorage`
* Add models for local transaction metadata caching. Reset PurchasesOrchestrator
* Complete `Codable`, lossless implementation of `LocalTransactionMetadata`
* Rename `LocalTransactionMetadataCache` to `LocalTransactionMetadataStore`
* Comment
* lint
* Remove unnecessary import
* Remove unused method
* Replace `Storefront` with `storeCountry` in both `ProductRequestData` and `PurchasedTransactionData`
This removes the need of a `StorefrontEncodedWrapper` object
* Disambiguate `asDictionary()` method
Now that `SubscriberAttribute` is `Codable`, `Encodable`'s `asDictionary()` and `SubscriberAttribute`'s `asDictionary()` were clashing
* Undo `Codable` conformance of `PresentedOfferingContext` and `TargetingContext`
* lint
* `LocalTransactionMetadata` now refers to a single transaction. `LocalTransactionMetadataStore` now stores one file per transaction and does not use an in-memory cache
* Temporarily remove `LocalTransactionMetadata` and `LocalTransactionMetadataStore` for easier PR review
* Revert "Temporarily remove `LocalTransactionMetadata` and `LocalTransactionMetadataStore` for easier PR review"
This reverts commit d958601711.
* Small code cleanup
* Remove `EncodedAppleReceipt` from `LocalTransactionMetadata`
* Undo `Codable` conformance of `EncodedAppleReceipt`
Not needed for now
* Remove `self.appTransactionJWS` from `LocalTransactionMetadata`
* Adapt to API changes in `SynchronizedLargeItemCache`
* Plug in `LocalTransactionMetadataStore` and store + get stored `LocalTransactionMetadata`
* Removed unused parameter
* Remove `schemaVersion` property from `LocalTransactionMetadata`
* Add `MockLocalTransactionMetadataStore` for tests
* Remove `appUserID` from `PurchasedTransactionData`
* Update tests
* Add tests
* Add tests for `LocalTransactionMetadataStore`
* Have `PurchasesOrchestrator` post transaction with syncing purchases in SK2 go through `TransactionPoster` to include the `LocalTransactionMetadata` logic there
* Correct values for `purchaseCompletedBy` field in `PostReceiptDataOperation`
* Fix finish transaction logic
* Add more tests and fix test compilation
* More tests
* [skip ci] Generating new test snapshots (#6057)
* [skip ci] Generating new test snapshots (#6050)
* [skip ci] Generating new test snapshots (#6051)
* [skip ci] Generating new test snapshots (#6052)
* [skip ci] Generating new test snapshots (#6053)
* [skip ci] Generating new test snapshots (#6054)
* [skip ci] Generating new test snapshots (#6055)
* [skip ci] Generating new test snapshots (#6056)
* Fix lint
* Remove TODO
* Fix recovering `PresentedOfferingContext` from `LocalTransactionMetadata`
* Take `source: PurchaseSource` out from `PurchasedTransactionData`
* Rename `PurchaseSource` to `PostReceiptSource` and `ProductRequestData.InitiationSource` to `PostReceiptSource.InitiationSource`
* Pass receipt as parameter directly
* Fix lint
* Remove unused variable
* fix test with previous value in snapshot
* Use variable
* Fix tests after merge from main
* Fix and improved tests
* Fix tests
---------
Co-authored-by: RevenueCat Git Bot <72824662+RCGitBot@users.noreply.github.com>
* Small cleanup
* add `LocalTransactionDetailsStorage`
* Add models for local transaction metadata caching. Reset PurchasesOrchestrator
* Complete `Codable`, lossless implementation of `LocalTransactionMetadata`
* Rename `LocalTransactionMetadataCache` to `LocalTransactionMetadataStore`
* Comment
* lint
* Remove unnecessary import
* Remove unused method
* Replace `Storefront` with `storeCountry` in both `ProductRequestData` and `PurchasedTransactionData`
This removes the need of a `StorefrontEncodedWrapper` object
* Disambiguate `asDictionary()` method
Now that `SubscriberAttribute` is `Codable`, `Encodable`'s `asDictionary()` and `SubscriberAttribute`'s `asDictionary()` were clashing
* Undo `Codable` conformance of `PresentedOfferingContext` and `TargetingContext`
* lint
* `LocalTransactionMetadata` now refers to a single transaction. `LocalTransactionMetadataStore` now stores one file per transaction and does not use an in-memory cache
* Temporarily remove `LocalTransactionMetadata` and `LocalTransactionMetadataStore` for easier PR review
* Revert "Temporarily remove `LocalTransactionMetadata` and `LocalTransactionMetadataStore` for easier PR review"
This reverts commit d958601711.
* Small code cleanup
* Remove `EncodedAppleReceipt` from `LocalTransactionMetadata`
* Undo `Codable` conformance of `EncodedAppleReceipt`
Not needed for now
* Remove `self.appTransactionJWS` from `LocalTransactionMetadata`
* Adapt to API changes in `SynchronizedLargeItemCache`
* Plug in `LocalTransactionMetadataStore` and store + get stored `LocalTransactionMetadata`
* Removed unused parameter
* Remove `schemaVersion` property from `LocalTransactionMetadata`
* Add `MockLocalTransactionMetadataStore` for tests
* Remove `appUserID` from `PurchasedTransactionData`
* Update tests
* Add tests
* Add tests for `LocalTransactionMetadataStore`
* Have `PurchasesOrchestrator` post transaction with syncing purchases in SK2 go through `TransactionPoster` to include the `LocalTransactionMetadata` logic there
* Correct values for `purchaseCompletedBy` field in `PostReceiptDataOperation`
* Fix finish transaction logic
* Add more tests and fix test compilation
* More tests
* [skip ci] Generating new test snapshots (#6057)
* [skip ci] Generating new test snapshots (#6050)
* [skip ci] Generating new test snapshots (#6051)
* [skip ci] Generating new test snapshots (#6052)
* [skip ci] Generating new test snapshots (#6053)
* [skip ci] Generating new test snapshots (#6054)
* [skip ci] Generating new test snapshots (#6055)
* [skip ci] Generating new test snapshots (#6056)
* Fix lint
* Remove TODO
* Fix recovering `PresentedOfferingContext` from `LocalTransactionMetadata`
* Pass receipt as parameter directly
* Fix lint
* Fix tests after merge from main
* Fix and improved tests
---------
Co-authored-by: RevenueCat Git Bot <72824662+RCGitBot@users.noreply.github.com>
* Prevent duplicate post receipt requests caused by timestamp-only attribute changes in the `consentStatus` SubscriberAttribute
* unit tests
* More tests
* Simplified MockDateProvider
* Add unit test covering edge case
* Fix lint
Follow up to #2540.
This was necessary to ensure that we can implement #2533 without
reference cycles.
This was the hierarchy before this change:

The red arrows are what this PR removes.
## Changes:
- `TransactionPoster` no longer has references to `IdentityManager` and
`Attribution`
- `markSyncedIfNeeded` is no longer in `TransactionPoster`, moved back
to `PurchasesOrchestrator`
- Changed `TransactionPoster`'s callback to return `Result<CustomerInfo,
BackendError>`, providing greater flexibility for users to be able to
read the specific errors.
- Extracted `PurchasedTransactionData` to be reduce the number of
parameters passed around. This removed the need for 2
`swiftlint:disable:next function_parameter_count`. This is the majority
of this diff. It was helpful to combine in this PR because I needed to
add another parameter to the flow: `unsyncedAttributes`, since
`TransactionPoster` could no longer read these directly.
### Changes:
- Created `PurchasedProductsFetcherType` to be able to mock and hold
fetchers, even on older devices
- Created `ProductEntitlementMappingFetcher` to abstract injecting
`ProductEntitlementMapping`
- Added logic to `CustomerInfoResponseHandler` to compute offline
`CustomerInfo` after a server error (see #2367). **This is used by all
`CustomerInfo` requests as well as `PostReceiptDataOperation`**.
- Wrote comprehensive `CustomerInfoResponseHandler` tests
### TODO:
- [x] Don't cache offline computed `CustomerInfo`: #2378
- [x] Integration tests: #2501.
### Changes:
- Added `Purchases.configureInCustomEntitlementsComputationMode`
- Added `Purchases.switchUser(to:)`
- Disabled parts of the SDK in this new mode
- New example app for testing `customEntitlementsComputation` mode
---------
Co-authored-by: NachoSoto <ignaciosoto90@gmail.com>
### Changes:
- Created `CacheableNetworkOperationFactory` to abstract operation and
cache key creation
- Instead of always creating an operation, we create a factory first,
which pre-computes the cache key
- This cache key is used to determine if it's a cache hit, as well as
for the subsequently created operation
I'm still investigating a leak (#2105), and I suspect it's coming from
us creating and storing completion blocks, and those staying in memory
beyond the lifetime of the operation.
This refactor and performance improvement helps us avoid get more things
in memory than we need to.
See #2138 for how this is tested.
Fixes [CSDK-419].
This is an improvement over the fix in #1292. In there, we avoided
appending on an existing `CustomerInfoOperation` if there were any
`PostReceiptDataOperation`s in progress, because those might come back
with outdated entitlements.
This fix removes that cache key hack, and instead *reuses* the entire
response to that `PostReceiptDataOperation` by "stealing" that request's
cache key.
This is perfectly captured by the existing test
`BackendPostReceiptDataTests.testGetsUpdatedSubscriberInfoAfterPost`,
added in #1292. Amazingly enough, that test still passes with one minor
change: the entire process requires one fewer API call 🎉🐼
[CSDK-419]:
https://revenuecats.atlassian.net/browse/CSDK-419?atlOrigin=eyJpIjoiNWRkNTljNzYxNjVmNDY3MDlhMDU5Y2ZhYzA5YTRkZjUiLCJwIjoiZ2l0aHViLWNvbS1KU1cifQ
We don't actually need to share a single queue to synchronize all the callback caches.
This new implementation also avoids using the risky `DispatchQueue.sync`.
This also fixes potential issues because `cachedCallbacksByKey` was exposed, and it could have been read without synchronizing access with the queue. This is now impossible.
* [CF-106] Fetch AdServices Token (#1519)
Co-authored-by: NachoSoto <NachoSoto@users.noreply.github.com>
Co-authored-by: Josh Holtz <me@joshholtz.com>
* [CF-553] Post AdServices token (#1534)
Co-authored-by: NachoSoto <NachoSoto@users.noreply.github.com>
Co-authored-by: Josh Holtz <me@joshholtz.com>
* Update ios 14 snapshots tets
* Reworked adservices to use new Purchases.shared.attribution API
* Updated with some comments from PR
* Add iAd code back (#1739)
Co-authored-by: NachoSoto <NachoSoto@users.noreply.github.com>
* use new mapKeys dictionary extension for device cache
* update for some pr comments
* remove purchasestests
* generate missing snapshots
* fix deprecation messages to clarify method now called on shared instance
* Fix for inserting nonstring into userdefaults
* Add tests for bug
* fix lint
* Added availablility checks for tests and removed unused varialbes
* Whoops... wrong class
Co-authored-by: beylmk <maddie@revenuecat.com>
Co-authored-by: NachoSoto <NachoSoto@users.noreply.github.com>
### Motivation:
Initially I wanted to make `SubscriberAttribute.asDictionary` work using `Codable`. However, this isn't the right refactor because we need the actual raw types to be encoded inside `UserDefaults`.
At least this refactor provides several improvements:
### Changes:
- `SubscriberAttribute` now has value semantics
- ⚠️ `SubscriberAttribute.isSynced` used to be mutable, with reference semantics, which was dangerous
- `SubscriberAttribute: Equatable` implementation is now automatic
- Cleaned up `SubscriberAttribute.asDictionary` implementation, extracting keys into an `enum`
- Moved `Dictionary` -> `SubscriberAttribute` creation to the type itself
- ⚠️ `SubscriberAttribute.init(dictionary:)` returns an `Optional` instead of using force-unwrap, avoiding potential crashes
- Added more serialization / deserialization tests
- Removed `SubscriberAttributesMarshaller` and moved method into `SubscriberAttribute` extension
- Moved `SubscriberAttributeDict` into `SubscriberAttribute.Dictionary`