Commit Graph
105 Commits
Author SHA1 Message Date
Antonio Pallares 05fc0a90f3 feat: disable postAttributionData requests when in UI preview mode (#4824)
* feat: disable postAttributionData requests when in UI preview mode

* tests: add unit test to check `AttributionPoster`'s new `clearPostponedAttributionData` method

* tests: add unit tests to check that `AttributionData` is never posted in UI preview mode

* refactor: simplify implementation of avoiding `AttributionPoster` requests in UI preview mode
2025-03-05 18:04:59 +01:00
Cody Kerns 7bca3bc39c Add convenience method for setting PostHog User ID (#4679) 2025-01-17 09:04:05 -05:00
Toni Rico 0a5e2aa8ce [Trusted Entitlements] Enable Trusted Entitlements by default (#4672)
* [Trusted Entitlements] Enable Trusted Entitlements by default

* Disable response verification during tests by default
2025-01-16 16:49:00 +01:00
Cesar de la VegaandRevenueCat Git Bot 9b7313e400 run-test-ios-15 in xcode 15 to fix incompatibilities with emergetools (#4319)
Co-authored-by: RevenueCat Git Bot <72824662+RCGitBot@users.noreply.github.com>
2024-12-05 19:40:29 +01:00
Cesar de la VegaandAndy Boedo 658782b68d Adds subscriptions to CustomerInfo (#4508)
* adds SubscriptionInfo

* fix nullabilities

* purchaseDate is not nullable

* add isActive

* add store

* add the product identifier

* add willRenew

* private extension

* made storeTransactionId nil

* fix tests and docs

* fix tests

* fix more tests

* fix compilation

* fix test

* API tests and other fixes

* Update Sources/Purchasing/NonSubscriptionTransaction.swift

Co-authored-by: Andy Boedo <andresboedo@gmail.com>

* use ProductIdentifier and subscriptionsByProductIdentifier

* fix lint

* disable file_length

---------

Co-authored-by: Andy Boedo <andresboedo@gmail.com>
2024-12-04 12:00:27 +01:00
98e4a33225 Support anonymous web purchase redemptions (#4439)
Co-authored-by: RevenueCat Git Bot <72824662+RCGitBot@users.noreply.github.com>
Co-authored-by: andy boedo <andresboedo@gmail.com>
Co-authored-by: Cesar de la Vega <cesarvegaro@gmail.com>
2024-11-25 16:14:32 +01:00
Mark Villacampa dc61b42f78 [Paywalls] Send paywall events when the app is backgrounded and after a successful purchase (#4438)
* Send paywall events when the app is backgrounded.

* - Add tests for syncing paywall events when backgrounding
- Send events after a successful purchase + tests

* typo

* typo
2024-11-07 16:52:59 +07:00
Will Taylor 6de169a23e support fetching eligible win-back offers for a product 2024-11-06 15:46:04 -06:00
Toni Rico a4e42e6d0a Add tenjinAnalyticsInstallationId setter property (#4437) 2024-11-05 09:39:34 +01:00
JayShortway 8198217e08 Fixes macOS snapshots (#4383) 2024-10-21 10:09:26 +02:00
JayShortway 57766ce7a2 Adds X-Is-Debug-Build header (#4364) 2024-10-10 17:00:03 +02:00
Will Taylor d9e4258c65 run CI jobs on iOS18/watchOS11 & use Xcode 16 2024-10-01 10:04:54 -05:00
Toni Rico f95799901a [Diagnostics] Add apple_products_request event (#4247)
* [Diagnostics] Add apple_products_request event

* Fixes from review

* Change tests to use to(beNil())

* Make MockDiagnosticsTracker sendable to fix tests

* Add new property to track storekit error type

* Use new method to process sk errors
2024-09-26 14:46:16 +02:00
Toni RicoandRevenueCat Git Bot fe181bd5e9 Add kochava integration (#4274)
* Reapply "Add `$kochavaDeviceId` attribution convenience method (#4238)" (#4265)

This reverts commit f2ce4416da.

* Add userAgent tracking for attribution networks

* Fix tests

* Add X-Platform-Device header

* Rename user agent to device version

* Use correct attribution method

* [skip ci] Generating new test snapshots (#4278)

* [skip ci] Generating new test snapshots (#4279)

* [skip ci] Generating new test snapshots (#4280)

* [skip ci] Generating new test snapshots (#4281)

* [skip ci] Generating new test snapshots (#4282)

* [skip ci] Generating new test snapshots (#4283)

* Generating new test snapshots (#4284)

* Fix ttests

---------

Co-authored-by: RevenueCat Git Bot <72824662+RCGitBot@users.noreply.github.com>
2024-09-25 19:59:36 +02:00
Toni Rico f2ce4416da Revert "Add $kochavaDeviceId attribution convenience method (#4238)" (#4265)
This reverts commit 871a785d60.
2024-09-11 19:01:55 +02:00
Toni Rico 871a785d60 Add $kochavaDeviceId attribution convenience method (#4238)
### Description
This adds a new API `setKochavaDeviceId` that allows to set the kochava
device ID required for the Kochava integration.

Keeping on hold until backend support is added
2024-09-11 10:06:55 +02:00
RevenueCat Git Bot c5589e7a88 Generating new test snapshots for main - ios-16 (#4182)
Requested by @vegaro for
[main](https://github.com/RevenueCat/purchases-ios/tree/main)
2024-08-14 06:29:57 -03:00
Will Taylor a4055375f6 retry requests with HTTP status 429 2024-07-23 12:41:16 -05:00
05c8bec967 [AUTOMATIC] Release/5.1.0 (#4036)
**This is an automatic release.**

### New Features
* Paywalls with custom purchase and restore logic handlers (#3973) via
James Borthwick (@jamesrb1)
### Bugfixes
* Prevent paywall PurchaseHandler from being cleared on rerender (#4035)
via Josh Holtz (@joshdholtz)
* Update Purchase Tester for 5.0.0 (#4015) via Will Taylor
(@fire-at-will)
### Dependency Updates
* Bump fastlane from 2.221.0 to 2.221.1 (#3977) via dependabot[bot]
(@dependabot[bot])
### Other Changes
* Bring official `xcodes` back to CI (#4029) via Cesar de la Vega
(@vegaro)
* Paywalls tester with sandbox purchases (#4024) via James Borthwick
(@jamesrb1)
* Update v5 migration guide to contain current latest version (#4019)
via Toni Rico (@tonidero)
* CI Build Docs Improvements (#4014) via Will Taylor (@fire-at-will)
* Use available resource class for backend-integration-tests-offline-job
(#4013) via Will Taylor (@fire-at-will)
* Add `X-Preferred-Locales` header (#4008) via Cesar de la Vega
(@vegaro)

---------

Co-authored-by: Toni Rico <antonio.rico.diez@revenuecat.com>
Co-authored-by: Josh Holtz <me@joshholtz.com>
2024-07-16 16:52:35 -05:00
Cesar de la Vega 29250a4f11 Add X-Preferred-Locales header (#4008)
Added a new `X-Supported-Locales` header to be used in the new customer
center feature
2024-07-02 17:32:50 +02:00
NachoSoto 2bcd0ce328 5.0.0 changes: support SK2 by default 2024-07-01 14:37:21 -05:00
Cesar de la Vega bc6ccfc7c3 Remove AdClient framework related code (#3993)
We had a report of a developer that is seeing that users (who are using
iOS 18 beta) are reporting app crashes on launch with 55 unique devices
so far. They are using using iOS SDK 4.17.7.

The line that is crashing is:

```
client = clientClass.perform(#selector(FakeAfficheClient.sharedClient)).takeUnretainedValue()
```

We just couldn’t have a single variable named `adClient` without an app
getting rejected, even if it wasn’t using it at all. We built the
`AfficheClient` so prevent app rejections.

It turns out Apple has removed documentation for `AdClient`, and by
looking into
https://developer.apple.com/documentation/iad/setting-up-apple-search-ads-attribution#overview

> After February 7, 2023, all requests made to the Apple Search Ads iAd
Attribution API will return with a value of "iad-attribution" = false,
or errors. See ADClient/requestAttributionDetails(_:). Use the
AdServices framework for current attribution integration with the Apple
Search Ads Campaign Management API for devices using iOS 14.3 and later.
Attribution isn't available for downloads and redownloads from devices
using iOS 14.2 or earlier.

So this wasn't even working anyways. I removed everything related but
left the public API in place to not break anyone.
2024-06-25 23:16:14 +02:00
Toni Rico bb8066d9b3 Add http_request_performed diagnostics event (#3897)
### Description
This adds a new diagnostics event `http_request_performed` with some
useful information we would like to track about the http requests
performed to our servers.
2024-05-16 15:13:40 +02:00
Cesar de la Vega ac3bf199cb Add diagnosticsQueue to BackendConfiguration (#3794)
Create a different OperationQueue to be used when sending Diagnostics
2024-04-10 14:48:09 +02:00
61d66bbc32 Add currentOffering(forPlacement: String) to Offerings (#3707)
### Motivation

New function to get `Offering` by a placement identifier

### Description

New `currentOffering(forPlacement: String): Offering?` method on
`Offerings`
- Added demo button for this in purchase tester app

```swift
// Example usage
if let offering = offerings.currentOffering(forPlacement: "onboarding") {
  showPaywall(offering)
}
```

#### Logic flow

- If a placement identifier explicitly exists within the
`placements.offeringIdsByPlacement` dictionary, the method attempts to
fetch the corresponding `Offering` without resorting to a fallback
- Example: when there is a `nil` / null value for a placement, we don't
want to use the fallback
- If the placement identifier does not exist, the method then attempts
to fetch a fallback offering defined by `placements.fallbackOfferingId`

#### New placements object in offerings response

```js
{
    // other offerings stuff
    "placements": {
        "fallback_offering_id": "offering_a", // can be null
        "offering_ids_by_placement": {
            "onboarding_start": null,
            "onboarding_end": "offering_b",
        }
  }
}
```

---------

Co-authored-by: Guido Torres <guido@revenuecat.com>
Co-authored-by: Josh Holtz <me@joshholtz.com>
Co-authored-by: RevenueCat Git Bot <72824662+RCGitBot@users.noreply.github.com>
2024-02-28 15:11:48 -06:00
NachoSoto 589580c8fa Tests: running tests on macOS (#3533) 2024-01-09 18:07:05 -08:00
Andy Boedo daa0e1f3d0 Tests: improve test flakiness (#3552)
Attempting to fix some test flakiness. 

Not the cleanest fixes, but flaky tests are dangerous. 

My methodology was to run tests over and over and fix the ones that
would occasionally fail.
2024-01-05 15:48:39 -03:00
32c63cdb46 [SK2] Add support for StoreKit Config files in SK2 (#3365)
When using StoreKit Config file, we do not have access to the
transaction history or subscription status server-side.

To work around this, we introduce a new `StoreKit2Receipt` type that
closely resembles the structure of an SK1 receipt and contains all the
transaction history and subscription status for the customer. It can be
sent in a single request to `/receipt`

---------

Co-authored-by: RevenueCat Git Bot <72824662+RCGitBot@users.noreply.github.com>
Co-authored-by: Distiller <distiller@static.38.39.183.137.cyberlynk.net>
Co-authored-by: Distiller <distiller@static.38.23.40.89.cyberlynk.net>
Co-authored-by: Distiller <distiller@static.38.39.185.65.cyberlynk.net>
Co-authored-by: NachoSoto <NachoSoto@users.noreply.github.com>
2023-12-13 17:00:37 +01:00
NachoSoto e31a531ca1 HTTPClient: test all request headers (#3425)
This allows us to verify exactly what headers are included in all
requests.

It also simplifies the implementation for #3424.
2023-11-17 12:19:49 -08:00
235a7dcdc1 StoreKit 2: Optionally send JWS tokens instead of receipts to the backend (#3227)
- Added a new option `usesStoreKit2JWS` under `DangerousSettings`. If
enabled, the SDK will send a JWS token instead of a receipt to the
applicable backend endpoints.
- The option must be used in conjunction with `usesStoreKit2IfAvailable`
configuration option.

### ToDo

- [x] Send JWS token when making a purchase in SK2 mode
- [x] Send JWS token when calculating promo offer eligibility
- [x] Update `syncTransactions` to send the latest transaction JWS.
- [x] Add tests

---------

Co-authored-by: RevenueCat Git Bot <72824662+RCGitBot@users.noreply.github.com>
Co-authored-by: Distiller <distiller@static.38.39.183.118.cyberlynk.net>
Co-authored-by: Distiller <distiller@static.198.206.135.125.macminivault.com>
Co-authored-by: Distiller <distiller@static.38.23.39.112.cyberlynk.net>
Co-authored-by: Distiller <distiller@static.38.39.183.58.cyberlynk.net>
Co-authored-by: Distiller <distiller@static.38.39.185.87.cyberlynk.net>
2023-10-16 22:45:29 +02:00
Raquel Diez acb01f69e2 Add Attribution.setOnesignalUserID (#3268)
### Motivation
OneSignal is migrating to a new User Centric API (v11.0). Adding a new
reserved attribute to provide the new OneSignal User Id.

### Description
* Added $onesignalUserId reserved attribute with setter methods
2023-10-03 11:38:31 -05:00
Toni RicoandNachoSoto 773fd84a65 StoreKit In App messages support (#3252)
### Description
StoreKit displays different types of in-app messages on app launch, but
it provides an API to disable that and show these messages at a
different time. This PR wraps those APIs to make them easier to use
through our SDK.

#### New APIs
- Add new configuration parameter to defer StoreKit's automatic in-app
messages
- Add new method in `Purchases` to display a list of specific message
types
- New `StoreKitMessageType` enum mapping to the types used by StoreKit

---------

Co-authored-by: NachoSoto <NachoSoto@users.noreply.github.com>
2023-10-03 16:58:53 +02:00
NachoSoto cc307399c3 Paywalls: added Purchases.track(paywallEvent:) (#3160)
This uses `PaywallEventsManager` (#3159) to track events, and saves the
presented `PaywallEvent` to be send along with
`PostReceiptDataOperation`.
2023-09-14 18:28:11 -07:00
NachoSoto c353d8dfd4 Paywalls: extracted PaywallCacheWarming (#2977)
This refactors #2860 extracting it to a separate type so we can easily
extend it to pre-warm the image cache as well (PWL-10).
2023-09-14 18:28:11 -07:00
NachoSoto b55de6ae09 CI: enable iOS 17 tests (#2829)
Filed `FB12653515` because `StoreKitUnitTests` hang when beginning:
> Runner never began executing tests after launching

This at least enables iOS 17 without StoreKit tests.
2023-09-14 12:21:36 -07:00
NachoSoto e522ec1aa6 SystemInfo: added ClockType (#3014) 2023-08-16 07:17:30 -07:00
NachoSoto b38f7b1434 BasePurchasesTests: consolidate to only initialize one DeviceCache (#2863)
I moved the `Purchases.logLevel` change to the beginning of the test
too, which makes this more obvious: every test was creating 2 instances
of `MockDeviceCache`.

This also required setting
`MockCachingTrialOrIntroPriceEligibilityChecker` to `nil` because that
was making `IdentityManager` leak, and therefore one of the 2
`MockDeviceCache`s leak!
2023-07-24 11:39:32 +00:00
NachoSoto 79aa55fc16 SystemInfo.init no longer throws (#2803)
Not sure when we stopped throwing errors, but we no longer do, which
simplifies code a lot.
2023-07-13 08:24:51 -07:00
NachoSoto 557b25e281 TestLogHandler: changed all tests to explicitly deinitialize it (#2784) 2023-07-12 06:34:12 -07:00
NachoSoto 6f07f30fe2 Tests: fixed another flaky failure with asynchronous check (#2786) 2023-07-12 06:01:43 -07:00
NachoSoto 01ca7ae635 PostReceiptDataOperation: add new testReceiptIdentifier parameter (#2749) 2023-07-05 10:37:43 -07:00
NachoSoto 1dc19f6750 Refactored Data.asString implementation (#2695) 2023-06-23 07:31:55 -07:00
NachoSoto b28f9fbf05 Removed preventPurchasePopupCallFromTriggeringCacheRefresh, update caches on willEnterForeground (#2623)
This method was a hack to deal with apps getting `didBecomeActive`
notifications in the middle of a purchase when the popups were
displayed.
This had several problems:
- It was only being used for StoreKit 1 purchases
- It wasn't used for StoreKit 2 purchases through the SDK
- It wasn't used for purchases initiated outside the SDK (like using the
upcoming paywall screens)
- It updated the cache timestamp to "now", including offerings, despite
not actually updating data
- It was an indirect and implicit way of solving a problem instead of an
explicit one

This solves the same by observing `willEnterForeground`, which only
happens when the app actually comes back from the background, and not
when it simply loses focus.
2023-06-12 16:26:14 -07:00
NachoSoto 14a7ea1edc Tests: added iOS 17 snapshots (#2603) 2023-06-09 07:42:54 -07:00
NachoSoto a18d3b4fca PostReceiptOperation: added ability to also post AdServices token (#2566)
Cherry-picked #2549 post refactors (#2540 and #2542). This becomes a lot
simpler since #2542, thanks to the fact that we can put this new
parameter in `PurchasedTransactionData`.

### Changes:
- Added `aadAttributionToken` to `PurchasedTransactionData`
- Exposed `AttributionPoster.adServicesTokenToPostIfNeeded`
- Added snapshot test to verify it's sent
- Added `PurchasesOrchestrator` tests (SK1/SK2) for sending the
attribution token
- Added missing `PurchasesOrchestrator` tests for sending attributes
- Added log when marking `AdServices` token as synced
- Exposed `Purchases.attribution` for custom entitlement computation
framework (and added to API tester)
- Exposed `Purchases.enableAdServicesAttributionTokenCollection` for
custom entitlement computation framework (and added to API tester)
2023-05-31 10:03:55 -07:00
NachoSoto de47c5771b GetCustomerInfo posts receipts if there are pending transactions (#2533)
This fixes the last edge-case for offline entitlements. Solves the
following scenario:
```swift
func testPurchaseWhileServerIsDownPostsReceiptWhenForegroundingApp() async throws {
    let logger = TestLogHandler()

    // 1. Purchase while server is down
    self.serverDown()
    try await self.purchaseMonthlyProduct()

    logger.verifyMessageWasNotLogged("Finishing transaction")

    // 2. Server is back
    self.serverUp()

    // 3. Request current CustomerInfo
    let info1 = try await Purchases.shared.customerInfo()
    try await self.verifyEntitlementWentThrough(info1)

    // 4. Ensure transaction is finished
    logger.verifyMessageWasLogged("Finishing transaction", level: .info)

    // 5. Restart app
    Purchases.shared.invalidateCustomerInfoCache()
    await self.resetSingleton()

    // 6. To ensure (with a clean cache) that the receipt was posted
    let info2 = try await Purchases.shared.customerInfo()
    try await self.verifyEntitlementWentThrough(info2)
}
```

This race condition happened when fetching `CustomerInfo` after we had
computed an offline `CustomerInfo` with a purchase: if the server is
still not aware of the purchase, then we lose that until some arbitrary
amount of time when `StoreKit` might decide to send the pending
transactions in the queue.
For that reason, instead of waiting, we proactively check now whenever
`CustomerInfoManager` fetches new info.

The refactors #2540 and #2542 where required to break a retain cycle.
Also thanks to #2534 we know for sure that there aren't any cycles.

Note that this is an iOS 15.0+ only feature. It would be useful to
better ensure consistency in older versions, but it's not strictly
required, as those versions don't support offline entitlements either.
2023-05-30 12:38:18 -07:00
NachoSoto 4b2138367b Refactored TransactionPoster: removed 2 dependencies and abstracted parameters (#2542)
Follow up to #2540.

This was necessary to ensure that we can implement #2533 without
reference cycles.
This was the hierarchy before this change:

![IMG_B25ECE86C2F9-1](https://github.com/RevenueCat/purchases-ios/assets/685609/70e1d939-8fa5-424c-8673-bb519f386118)

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.
2023-05-30 12:16:24 -07:00
NachoSoto 456b96bf59 Backend.ResponseHandler is now @Sendable (#2541)
This was necessary for a follow-up refactor to #2540. The main change is
in `Backend`, the rest are test changes that are required now that the
closure is `@Sendable`.
2023-05-26 15:29:34 -07:00
NachoSoto 43eb42be4d Extracted TransactionPoster from PurchasesOrchestrator (#2540)
Necessary refactor for #2533. This allows `CustomerInfoManager` to have
an instance of `TransactionPosterType` instead of the whole
`PurchasesOrchestrator`.

Essentially this becomes the hierarchy now:

![IMG_B8C56C11B7E5-1](https://github.com/RevenueCat/purchases-ios/assets/685609/36f5c30b-fc82-4590-b574-ce6d95eef13f)

### Changes:
- Created stateless `TransactionPoster`
- Extracted `handlePurchasedTransaction` into `TransactionPoster`
- The new implementation there is _mostly_ the same, with the one
difference that it's stateless. Instead of holding
`purchaseCompleteCallbacksByProductID`, that's done by
`PurchasesOrchestrator`. The methods there all take a callback, so
they're easier to use and don't require keeping a callback around.
- Created `PurchaseSource` to abstract the combo of `isRestore` and
`InitiationSource`. This is still messy using the deprecated
`allowSharingAppStoreAccount`, but at least that's abstracted out and
the new `TransactionPoster` won't deal with that deprecated property.
- I didn't mock `TransactionPoster`, which means that all existing tests
that check `PurchasesOrchestrator` continue working the same way. This
was on purpose to keep this refactor as simple as possible.
- This supersedes #2536: the change made there was now possible here
without any integration test failures, thanks to the fact that we can
process transactions independently without messing with the
`purchaseCompleteCallbacksByProductID` state now.

### Future improvements
This is just a start, there's a lot more than can be done to simplify
the `PurchasesOrchestrator` monster. The main thing would be to remove
all the custom code for `restorePurchases`, which is largely the same as
what's in `TransactionPoster` now.

### Notes:
Just posting this here for posteriority. I had to trace the call
hierarchy for both SK1 and SK2. This "block" is what's now in
`TransactionPoster`:

![IMG_C60274393F7F-1](https://github.com/RevenueCat/purchases-ios/assets/685609/5a7377f0-8301-474f-a774-a90b6e232bc3)
2023-05-26 15:06:14 -07:00
NachoSoto 789a0e5e4d PurchasedProductsFetcher: cache current entitlements (#2507)
To optimize potentially computing offline `CustomerInfo`, this caches
what's likely the most expensive part.
2023-05-19 18:08:03 -07:00