Commit Graph
25 Commits
Author SHA1 Message Date
Dave DeLong 6ed515865a Simplify HTTP path types (#7987) 2026-10-23 11:11:52 -06:00
Dave DeLongandÁlvaro Brey 3eb01c32bc HTTP Paths can change their absolute path based on IAM enablement (#7422)
* ci: announce modifications in the SDK API feed (#7451)

An attribute-only modification is neither an addition nor a break, so it reached the feed as a headline and a link, with the headline claiming new API.

* HTTP Paths can change their absolute path based on IAM enablement

* missed one

* fix failures

* fix crashing test

* address feedback

* unit tests

* slightly more graceful

* fix signing test

* fix other test

* log on invalid path

* throwAssertion is unavailable on watchOS

* fix backend integration tests

* fix import

---------

Co-authored-by: Álvaro Brey <alvaro.brey@revenuecat.com>
2026-08-21 14:04:21 -06:00
Rick f620703c93 feat(remote-config): send X-RC-Last-Refresh-Time on config requests (#7308)
* Add config last refresh header

* Refine remote config refresh header

* Address remote config refresh header reviews

* Sign final merged request headers

* Snapshot remote config refresh time

* Improve remote config refresh timestamp tests
2026-07-29 16:39:22 +02:00
Rick b82223e5a1 feat(remote-config): add static fallback config endpoint (#7182)
* feat(remote-config): add static fallback config endpoint

* refactor(remote-config): clarify static fallback path name

* test(remote-config): cover static fallback signature failures

* fix(remote-config): use static fallback only without cache

* test(remote-config): cover static fallback external blobs

* refactor(remote-config): align static fallback naming

* refactor(remote-config): scope static fallback path

* fix(remote-config): mark fallback no content fresh

* fix(remote-config): align fallback accept headers

* refactor(remote-config): rename fallback endpoint types

* fix(remote-config): enable etags for fallback config

* fix(remote-config): skip fallback when proxy is configured

* fix(remote-config): rely on etag cache for fallback

* refactor(remote-config): reuse fallback retry eligibility

* fix(remote-config): disable after fallback client errors
2026-07-10 14:17:31 +02:00
Pol Miro 2658f2b8fa Refactor: Rename PaywallEventsManager to EventsManager and separate ad events (#5718)
* Rename paywallsEventsManager to eventsManager

* Rename PaywallEventsManagerType to EventsManagerType

* Rename PaywallEventsManager to EventsManager

* Rename PaywallEventStore to FeatureEventStore

* Rename InternalAPI.postPaywallEvents to postFeatureEvents

* Rename StoredEventSerializer to StoredFeatureEventSerializer

* Rename createStoredEvent to createFeatureStoredEvent

* Rename StoredEvent to StoredFeatureEvent

* Rename HTTPRequest.PaywallPath to HTTPRequest.FeatureEventsPath

* Rearrange events files

* Rename event related files

* Extract shared events endpoint configuration to EventsHTTPRequestPath

Both FeatureEventsPath and AdPath share the same /v1/events endpoint
configuration, differing only in domain (api-paywalls.revenuecat.com vs
a.revenue.cat) and name. Extract common logic to EventsHTTPRequestPath
protocol in Sources/Events/Networking.

* Move Sources/FeatureEvents to Sources/Events/FeatureEvents

* Rename EventsRequest to FeatureEventsRequest

- Rename EventsRequest.swift to FeatureEventsRequest.swift
- Move file to Sources/Events/FeatureEvents/Networking/
- Update all references in source and test files
- Update Xcode project file references

This makes it clear that this request type is specifically for the
feature events endpoint, not a generic events request.

* Remove ad events from feature events system

Ad events should not be sent through the feature events system as they
require their own separate infrastructure with a different API endpoint.

Changes:
- Remove FeatureEvent conformance from AdEvent
- Remove .ads case from Feature enum
- Remove ad events handling from FeatureEventsRequest
- Add no-op track(adEvent:) method to EventsManager with comment
- Update AdTracker to call the new track(adEvent:) method

Ad events tracking now becomes a no-op until proper implementation with:
- StoredAdEvent structure
- AdEventStore for persistence
- InternalAPI.postAdEvents() using HTTPRequest.AdPath.postEvents

* Rename PostEventsOperation to PostFeatureEventsOperation

- Rename PostEventsOperation.swift to PostFeatureEventsOperation.swift
- Move file to Sources/Events/FeatureEvents/Networking/
- Update class name and remove misleading "generic" comment
- Update references in InternalAPI.swift
- Update Xcode project file references

This makes it clear that this operation is specifically for posting
feature events, not a generic operation for various event types.
2025-10-30 14:52:35 +01:00
Pol MiroandClaude 470ce93f4f Add non paid revenue reporting infra (#5640)
* Add RC Ads event tracking infrastructure

Implements event tracking for RevenueCat Ads following the Paywalls Events pattern.

- Adds three event types: ad displayed, ad opened, and ad revenue
- Uses actor-based concurrency with file-based event storage
- Posts events to e.revenue.cat/v1/events endpoint
- Marks track(adEvent:) method as @_spi(Internal) for internal use
- Includes comprehensive unit tests with snapshot testing
- Available on iOS 15.0+, tvOS 15.0+, macOS 12.0+, watchOS 8.0+

Event schema follows khepri RC Ads input events specification with
app_session_id field matching backend expectations.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>

* Avoid mapping in EventsRequest+Ad

* Consolidate ad events into PaywallEventsManager

Eliminates duplication between AdEventsManager and PaywallEventsManager by:
- Making PaywallEventsManager handle all event types (ads, paywalls, customerCenter)
- Adding feature-aware routing in flushEvents() to send events to correct domains
  - Ad events → https://e.revenue.cat/v1/events
  - Other events → https://api-paywalls.revenuecat.com/v1/events
- Removing AdEventsManager, AdEventStore, and their tests (~500 lines)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>

* Remove AdsStrings and use PaywallsStrings instead

AdsStrings only had 2 strings in use, both of which already exist in PaywallsStrings. Consolidates logging strings to eliminate duplication.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>

* Namespace Precision enum inside AdEvent.RevenueData

Moves Precision from a top-level public enum to AdEvent.RevenueData.Precision to avoid polluting the global namespace and reduce autocomplete noise.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>

* Move Precision conformances to end of file

Follows the pattern of other types by declaring protocol conformances at the end of the file rather than inline with the type definition.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>

* Remove redundant sessionIdentifier from AdImpressionData

The sessionIdentifier field in AdImpressionData was redundant since
StoredEvent already tracks appSessionID. This change:

- Removes sessionIdentifier from AdImpressionData struct
- Uses storedEvent.appSessionID directly when building requests
- Renames sessionId to appSessionId to match backend field app_session_id
- Updates documentation examples to remove sessionIdentifier parameter
- Adds guard statement for optional appSessionID
- Updates all test fixtures and regenerates snapshots
- Removes iOS26 snapshot references from project

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>

* Remove unneeded SessionId

* Add proper assertions to PurchasesAdEventsTests

Enhanced test coverage for public ad tracking APIs:
- Verify events are tracked via mockPaywallEventsManager
- Assert correct event types (displayed, opened, revenue)
- Validate all impression data fields
- Verify revenue-specific fields (revenueMicros, currency, precision)

All 3 tests now pass with proper verification.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>

* Add ENABLE_AD_EVENTS_TRACKING compiler flag

* Simplify track ad events APIs

* Fix ENABLE_AD_EVENTS_TRACKING scope and refactor Precision to struct

Compiler flag fixes:
- Move ENABLE_AD_EVENTS_TRACKING #endif to wrap all ad event code including internal enum
- Add compiler flag guards to all three test files

Precision API refactor:
- Change AdRevenue.Precision from enum to struct following Apple's API patterns (like Message.Reason)
- Use internal init(rawValue:) to prevent custom precision values
- Convert enum cases to static properties maintaining same public API

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>

* Refactor mediatorName from String to MediatorName struct

Replace string-based mediator names with a type-safe struct following Apple's API patterns:
- Add MediatorName struct with public init(rawValue:) to support custom values
- Include predefined static properties for .adMob and .appLovin
- Update AdEventData protocol to require MediatorName type
- Update all event types (AdDisplayed, AdOpened, AdRevenue) to use MediatorName
- Use .rawValue when serializing to network requests
- Update all tests and documentation examples to use .appLovin

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>

* Remove RawRepresentable conformance from Precision and update test snapshots

- Remove RawRepresentable conformance from AdRevenue.Precision to allow internal init
- Update test assertions to use .appLovin instead of string comparison
- Update all snapshot test files to use "AppLovin" instead of "MAX"
- All ad event tests now passing (11/11)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>

* Fix PaywallDataDecodingTests locale check for region overrides

The test was failing when the system locale included region overrides
(e.g., "en_US@rg=eszzzz" instead of just "en_US").

Changes:
- Use beginWith("en_US") instead of exact equality check
- Compare against enConfig directly instead of fetching via Locale.current

This makes the test more robust while maintaining the same behavior validation.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>

* Rename timestampMs to timestamp but keep serialized timestamp_ms

* Use different serverHostURL

* Simplify and paralelize postEvents

* Adjust usage of ENABLE_AD_EVENTS_TRACKING flag

* Move track ad event methods to AdTracker

* Remove PaywallEventsManager implementation

* Improving naming overlapping naming AdEvent->AdEventRequest

* Simplify compiler flag usage

* Use Experimental

* Remove duplicate with PostPaywallEventsOperation and PostAdEventsOperation

* Remove postAdEvents methods assuming reuse of event storage

* Make Precision rawValue internal

* Remove runtime checks

* Trigger build

* Move Public Types MARK comment after internal protocol

* Add documentation for AdDisplayed, AdOpened, and AdRevenue properties

* Remove redundant smoke tests

The "DoesNotCrash" tests were completely redundant with the
comprehensive "StoresEvent" tests. The comprehensive tests already:
1. Verify methods don't crash (implicitly)
2. Verify events are stored
3. Verify event data is correct

Removed 3 redundant tests, keeping only the 3 comprehensive tests.

* Fix bug not tracking initialized events

---------

Co-authored-by: Claude <noreply@anthropic.com>
2025-10-29 11:56:48 +01:00
Toni Rico c7edb46f31 Add endpoint for fetching web billing products (#5055)
* Add support for fetching web billing products

* Some fixes

* Escape product ids

* Fixes from review

* Support etags

* Change to Int64

* Add method to MockOfferingsAPI
2025-05-06 23:28:59 +02:00
Will Taylor 348258a41f only retry post receipt for 429 status codes 2024-07-25 16:46:31 -05:00
Cesar de la Vega 03d93bb061 Add HTTPRequest.DiagnosticsPath (#3791)
Create DiagnosticsHTTPRequestPath for Diagnostics

Depends on #3790 which adds the name
2024-04-10 13:00:48 +02:00
NachoSoto 5e968ad268 Paywalls: implemented PostPaywallEventsOperation (#3158)
This adds the operation + `PaywallEventsRequest` and associated tests.
Also added method to `InternalAPI` for posting events.
2023-09-14 18:28:11 -07:00
NachoSoto 833524314d HTTPClient: extracted HTTPRequestPath protocol (#2986)
This paves the way to add a second set of paths with a separate server
URL to be able to shed requests to offerings and product entitlement
mapping to a separate server.

### Changes:
- New `HTTPRequestPath` with the same properties as `HTTPRequest.Path` +
a new `serverHostURL`
- Moved proxy logic from `SystemInfo`/`HTTPClient` to `HTTPRequestPath`
- Simplified `HTTPRequest` implementation
2023-08-10 15:23:13 -07:00
NachoSoto f7e36fb993 Trusted Entitlements: add support for signing POST body (#2753)
### Changes:
- `HTTPClient` has a new `X-Post-Params-Hash` request header
- Replaced `HTTPRequest.Method.post`'s `Encodable` value with a more
specific `HTTPRequestBody` (like `HTTPResponseBody`)
- `HTTPRequestBody` allows overriding `contentForSignature` (it defaults
to `[]`) which then `Signing` uses for the hash
- Fixed `LogInOperation` not passing the `VerificationResult` to the
result `CustomerInfo`. This is captured in unit and integration tests
now.
- Add support for signing `POST` requests in `Path.logIn` and
`Path.postReceiptData`

Depends on https://github.com/RevenueCat/khepri/pull/6246
2023-07-13 07:45:16 -07:00
NachoSoto bd3ac5faf6 Trusted Entitlements: produce verification failures for static endpoints with no signature (#2752)
Equivalent to https://github.com/RevenueCat/purchases-android/pull/1119
2023-07-06 10:08:45 -07:00
NachoSoto 3da1c50925 HTTPRequest.Path: escape appUserID (#2747)
We were escaping users for every (hopefully) request. This refactor
moves that responsibility to the path creation in `HTTPRequest.Path` to
ensure it's done consistently.

Additionally, this is required so that `SigningTests` produce a
consistent path (see #2746).
2023-07-05 15:41:51 -07:00
NachoSoto 9c1bb9dbaf ProductEntitlementMapping: enabled entitlement mapping fetching (#2425) 2023-04-24 06:04:30 -07:00
NachoSoto d1c6fa23ee Offline Entitlements: added support for fetching ProductEntitlementMappingResponse in OfflineEntitlementsAPI (#2353) 2023-03-23 09:18:51 -07:00
NachoSoto c5f963e4fa HTTPClient: changed header search to be case-insensitive (#2308) 2023-02-22 11:30:20 -08:00
NachoSoto 457ba021d9 HTTPClient: automatically add nonce based on HTTPRequest.Path (#2286)
Instead of manually choosing when to include `nonce` in requests, this
data-driven approach ensures that all requests to endpoints that support
signature verification will make `HTTPClient` do the right thing.

See also #2277 for how `CustomerInfo` and `EntitlementInfo` will use
this.
2023-02-22 09:20:25 -08:00
NachoSoto dbe93dbbf4 PurchasesDiagnostics: added step to verify signature verification (#2267)
⚠️ 🎉 This also changes integration tests to use
`EntitlementVerificationLevel.enforced` so that integration tests fail
if signatures are invalid.

#### Depends on:
- https://github.com/RevenueCat/khepri/pull/5191
- https://github.com/RevenueCat/khepri/pull/5204
- #2214
- #2215 
- #2216
- #2272

_Marking this as `feat`ure because it contains a new error in
`PurchasesDiagnostics.Error`_
2023-02-16 10:27:04 -07:00
NachoSoto f05aabd176 HTTPClient: added signature validation and introduced ErrorCode.signatureVerificationFailed (#2272)
Fixes [CSDK-634].

## Changes:
- Created `HTTPResponseValidationResult`
- Added `HTTPResponseValidationResult` and `HTTPClient.ResponseHeaders`
to `HTTPResponse`
- Added new `ErrorCode.signatureVerificationFailed`
- Extracted `SigningType` to create `MockSigning` to test `HTTPClient`
without bothering with signatures
- Refactored `ETagManager` to use `HTTPResponse`, making it easier to
pass through response headers and verification results
- Stored `HTTPResponseValidationResult` in `ETagManager` cache
- `HTTPClient` handles signatures from response based on
`SystemInfo.responseVerificationLevel`
- Renamed `HTTPRequest.createIntegrityEnforcedRequestRequest` to
`HTTPRequest.createWithResponseVerification`
- Added lots of tests for this new behavior in `HTTPClient`
- Added lots of new tests for `ETagManager`

[CSDK-634]:
https://revenuecats.atlassian.net/browse/CSDK-634?atlOrigin=eyJpIjoiNWRkNTljNzYxNjVmNDY3MDlhMDU5Y2ZhYzA5YTRkZjUiLCJwIjoiZ2l0aHViLWNvbS1KU1cifQ
2023-02-16 03:06:59 -08:00
NachoSoto 1b2f405db1 HTTPRequest.Path.health: don't cache using ETagManager (#2278)
This breaks signature validation in #2267 because `PurchasesDiagnostics`
uses that endpoint with and without signature validation, so the first
time it makes a request, it stores the cached response without a
validated signature.

The best solution is to simply not send etags for this endpoint, since
it's used precisely to make sure that requests can be made with no
errors, and we wouldn't want false positives.
2023-02-13 07:02:18 -08:00
NachoSoto 88a78411f6 HTTPClient: added support for sending X-Nonce (#2214)
Fixes [CSDK-631].


[CSDK-631]:
https://revenuecats.atlassian.net/browse/CSDK-631?atlOrigin=eyJpIjoiNWRkNTljNzYxNjVmNDY3MDlhMDU5Y2ZhYzA5YTRkZjUiLCJwIjoiZ2l0aHViLWNvbS1KU1cifQ
2023-02-08 09:05:20 -08:00
NachoSoto f06a8ce647 Created InternalAPI for "health" request (#1971) 2022-10-20 13:03:15 -05:00
343ba7eaf4 AdServices (#1727)
* [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>
2022-08-08 07:42:42 -05:00
Joshua Liebowitz 858bef3f9b Extract Offerings-related things into OfferingsAPI (#1706)
Extract Offerings-related things into OfferingsAPI
2022-06-16 14:00:40 -07:00