mirror of
https://github.com/RevenueCat/purchases-ios
synced 2026-11-11 22:55:55 +05:45
e32933780bbc1fbbdeeaeffd2bd0a2b59e5df213
90
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
23d012abe2 |
Add workflow-based paywall resolution (#6675)
* Add workflows network layer for multipage paywalls - Add `getWorkflows` and `getWorkflow` endpoint paths - Add `WorkflowsListResponse` and `PublishedWorkflow` response models - Add `WorkflowDetailProcessor` to handle `inline`/`use_cdn` response actions - Add `WorkflowCdnFetcher` protocol and `DirectWorkflowCdnFetcher` implementation - Add `GetWorkflowsOperation` and `GetWorkflowOperation` cacheable network operations - Add `WorkflowsAPI` facade and wire into `Backend` - Add unit tests for all new components iOS equivalent of https://github.com/RevenueCat/purchases-android/pull/3300 Made-with: Cursor * fix xcodeproj * reset Package.resolved * Update WorkflowStep models to match actual backend response - Add WorkflowTrigger struct (name, type, action_id, component_id) - Add triggers, outputs, and metadata fields to WorkflowStep - Add metadata field to PublishedWorkflow - Remove value field from WorkflowTriggerAction (backend uses step_id only) Made-with: Cursor * Update WorkflowResponseTests to match updated models - Replace value/resolvedTargetStepId assertions with stepId - Remove testDecodeWorkflowTriggerActionValueTakesPrecedence (value field removed) - Add testDecodeWorkflowTrigger for new WorkflowTrigger struct - Add testDecodePublishedWorkflowWithMetadata - Update testDecodeWorkflowStepDefaults to cover triggers, outputs, metadata - Add testDecodeWorkflowStepMatchingActualBackendResponse with real backend payload Made-with: Cursor * Disable file_length lint rule in HTTPRequestPath.swift Made-with: Cursor * Fix GetWorkflowOperation to compute result once before distributing to callbacks CDN fetch and JSON decoding were running once per deduplicated callback. Compute the Result<WorkflowFetchResult, BackendError> once outside the performOnAllItemsAndRemoveFromCache loop, matching the pattern used by GetOfferingsOperation and other operations in the codebase. Made-with: Cursor * Fix CDN fetcher: use URLSession instead of Data(contentsOf:), classify errors correctly - Replace Data(contentsOf:) with URLSession.shared.dataTask + DispatchSemaphore in DirectWorkflowCdnFetcher; gets URLSession timeout, HTTP status validation, and proper network stack semantics - Add WorkflowDetailProcessingError.cdnFetchFailed typed error so CDN I/O failures are distinguishable from envelope parsing failures - Catch cdnFetchFailed in GetWorkflowOperation and map to NetworkError.networkError instead of NetworkError.decoding, fixing misleading error classification - Update WorkflowDetailProcessorTests to assert the typed error is thrown Made-with: Cursor * Replace semaphore CDN fetch with async/await using withCheckedThrowingContinuation - Make WorkflowCdnFetcher.fetchCompiledWorkflowData async throws; use withCheckedThrowingContinuation to bridge URLSession.dataTask into async, avoiding any thread-blocking - Make WorkflowDetailProcessor.process async throws to propagate async - Bridge into async in GetWorkflowOperation via Task {}; completion() is called inside the Task after CDN fetch and decoding complete - Update WorkflowDetailProcessorTests to async throws with await Made-with: Cursor * Make CDN fetcher and processor completion-handler based for consistency Avoids Task{} in GetWorkflowOperation and keeps all operations calling completion() synchronously from within the HTTP callback, matching every other operation in the codebase. - WorkflowCdnFetcher.fetchCompiledWorkflowData now takes a completion handler; DirectWorkflowCdnFetcher uses URLSession.dataTask (non-blocking, no semaphore) - WorkflowDetailProcessor.process now takes a completion handler; inline action completes synchronously, use_cdn fans out to the fetcher callback - GetWorkflowOperation splits into getWorkflow/handleResponse/backendResult/ distribute helpers to stay within line-length limits - WorkflowDetailProcessorTests updated to use waitUntilValue pattern Made-with: Cursor * Fix ambiguous cache key delimiter in GetWorkflowOperation Space-separated appUserID+workflowId could collide (e.g. user 'a b' + workflow 'c' == user 'a' + workflow 'b c'). Use newline as delimiter, matching the precedent set by GetWebBillingProductsOperation. Made-with: Cursor * PR comments * remove cdn fetcher * fix response in BackendGetWorkflowsTests.swift * fix WorkflowResponseTests * fix error * [skip ci] Generating new test snapshots (#6584) * [skip ci] Generating new test snapshots (#6585) * [skip ci] Generating new test snapshots (#6586) * [skip ci] Generating new test snapshots (#6587) * [skip ci] Generating new test snapshots (#6588) * [skip ci] Generating new test snapshots (#6589) * [skip ci] Generating new test snapshots (#6590) * Test CDN mock is not re-assignable per test * [skip ci] Generating new test snapshots (#6597) * [skip ci] Generating new test snapshots (#6598) * [skip ci] Generating new test snapshots (#6599) * [skip ci] Generating new test snapshots (#6600) * [skip ci] Generating new test snapshots (#6601) * [skip ci] Generating new test snapshots (#6602) * [skip ci] Generating new test snapshots (#6603) * getWorkflow is signed * add type parameter * add response verification for CDN response * remove workflows list * add value to WorkflowTriggerAction * step id * hash and filerepo * missingCdnHash * linter and project * fix project * revert Package.resolved * @unchecked Sendable * use hash in generateOrGetCachedFileURL * fix compilation * skip responseVerificationMode in cdn * change basePath of caches * Add workflow-based paywall resolution (iOS parity with Android #3350) Implements the workflows endpoint so paywalls can be resolved from a workflow document rather than the offerings cache, gated behind the ENABLE_WORKFLOWS_ENDPOINT compile flag. Validates the step→screen→ offeringId chain and maps the workflow screen into Offering.PaywallComponents using the existing V2 rendering path. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * Add tests for workflow networking layer and WorkflowScreenMapper BackendGetWorkflowTests covers HTTP call, request caching, per-user and per-workflow-ID deduplication, error propagation, empty appUserID, and full response decoding. WorkflowScreenMapperTests verifies all PaywallComponentsData fields and the UIConfig are mapped correctly. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * remove reserialization * Use explicit type in GetWorkflowOperation.createFactory for greppability Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * Use explicit type in GetWorkflowOperation.createFactory for greppability Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * Fix lint errors and refactor workflow offering resolution into PaywallViewConfiguration - Add swiftlint:disable comments for _revision identifier name in WorkflowsResponse.swift - Add missing_docs disable in WorkflowsResponse.swift (all @_spi(Internal) types) - Add file_length disable in Offering.swift - Fix identifier name 'd' → 'doubleValue' in WorkflowDetailProcessorTests.swift - Move workflow offering resolution from PaywallView into PaywallViewConfiguration.resolveOfferingOrThrow() - Add cachedInitialOffering() to PaywallViewConfiguration.Content - Add PaywallViewConfigurationTests Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * Fix swiftlint identifier_name violation in WorkflowDetailProcessorTests Rename short variable `d` to `doubleValue` in two `if case .double(let d)` patterns to satisfy the identifier_name rule requiring names >= 3 characters. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * Inject PaywallPurchasesType into offering resolution; remove dead code - Add offerings(), cachedOfferings, and workflow() to PaywallPurchasesType so PaywallViewConfiguration.Content no longer calls Purchases.shared directly - Thread purchases through cachedInitialOffering/resolveOffering/resolveOfferingOrThrow and all private helpers; callers pass purchaseHandler.purchasesInstance - Expose purchasesInstance on PurchaseHandler for call sites - Add stubs to NotConfiguredPurchases, LoadingPaywallPurchases, and MockPurchases (MockPurchases gets offeringsBlock/workflowBlock for test injection) - Remove dead WorkflowDetailProcessingError.missingInlineData case - Replace manual JSONSerialization parseEnvelope with Data.asJSONDictionary() - Collapse double #if nesting into single compound condition - Remove WHAT doc comment from WorkflowDetailProcessor - Refactor resolveOffering() to delegate to resolveOfferingOrThrow() - Parallelize workflow + offerings fetches with async let Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * Move offering resolution from Content to PurchaseHandler Resolving an offering requires a purchases instance, so it belongs on PurchaseHandler rather than as a method on the Content enum that takes purchases as a parameter. purchasesInstance accessor removed. Also fix MockPurchases.map() to forward offeringsBlock, workflowBlock, and cachedOfferings to the mapped instance, and simplify PaywallViewConfigurationTests to use MockPurchases directly instead of configuring a real Purchases singleton. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * Add clarifying comment to workflow(forOfferingIdentifier:) explaining parameter naming Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * Fix CI failures: remove duplicate switch cases and add @_spi(Internal) to test imports - Remove duplicate .getWorkflow cases in HTTPRequestPath.swift switches (auto-merge artifact that caused duplicate_conditions lint violations) - Add @_spi(Internal) to @testable imports in three unit test files so they can access WorkflowFetchResult, PublishedWorkflow, and WorkflowStep which are now @_spi(Internal) public Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * Fix @_spi access in WorkflowDetailProcessorTests WorkflowResponseTests already had @_spi(Internal) @testable import RevenueCat; WorkflowDetailProcessorTests was missing the @_spi annotation, causing 'id', 'steps', and 'initialStepId' to be inaccessible on PublishedWorkflow. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * Fix MockWorkflowsAPI.swift missing from StoreKitUnitTests and UnitTests targets The PBXBuildFile entries for DB7EA77F2F9764F700BCC082 and DB7EA7802F9764F700BCC082 were referenced in the StoreKitUnitTests and UnitTests source phases but never defined, causing MockWorkflowsAPI to be missing at compile time and MockBackend.swift to fail with 'cannot find MockWorkflowsAPI in scope'. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * Add offering_identifier field to WorkflowScreen for parity with Android Android PR #3350 split offering_id (internal ID) from offering_identifier (human-readable identifier used for lookup). iOS was only decoding offering_id and using it for the offering lookup, which would fail if the workflow response sends the public identifier in offering_identifier. - Add offeringIdentifier (from offering_identifier) to WorkflowScreen - Update PurchaseHandler to prefer offeringIdentifier, falling back to offeringId - Fix typo: workflow.screelns -> workflow.screens - Add decode tests for both offering fields Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * Fix WorkflowResponseTests: add missing padding field to stack component fixtures Stack component decoding requires a padding field; test JSON fixtures were missing it causing keyNotFound decode errors. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * Fix WorkflowResponseTests: add missing margin field to stack component fixtures Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * Replace ENABLE_WORKFLOWS_ENDPOINT compile flag with runtime argument check The -EnableWorkflowsEndpoint launch argument replaces the compile-time flag, allowing workflow resolution to be toggled without rebuilding. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * Add Workflow presentation mode to PaywallsTester Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * Remove offeringId shim, allow no-paywall offerings to open as workflows - Drop WorkflowScreen.offeringId / offering_id CodingKey (Android already uses offering_identifier exclusively — aligns both platforms) - Simplify PurchaseHandler to use screen.offeringIdentifier directly - No-paywall offerings in PaywallsTester now render as tappable rows (workflow mode) instead of plain read-only text, with a context menu restricted to the Workflow option Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * Rename WorkflowFetchResult to WorkflowDataResult, extract ProcessInfo extension Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix project --------- Co-authored-by: Cesar de la Vega <664544+vegaro@users.noreply.github.com> Co-authored-by: RevenueCat Git Bot <72824662+RCGitBot@users.noreply.github.com> Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com> |
||
|
|
0b65086ec2 |
Add workflows network layer for multipage paywalls (#6557)
* Add workflows network layer for multipage paywalls - Add `getWorkflows` and `getWorkflow` endpoint paths - Add `WorkflowsListResponse` and `PublishedWorkflow` response models - Add `WorkflowDetailProcessor` to handle `inline`/`use_cdn` response actions - Add `WorkflowCdnFetcher` protocol and `DirectWorkflowCdnFetcher` implementation - Add `GetWorkflowsOperation` and `GetWorkflowOperation` cacheable network operations - Add `WorkflowsAPI` facade and wire into `Backend` - Add unit tests for all new components iOS equivalent of https://github.com/RevenueCat/purchases-android/pull/3300 Made-with: Cursor * fix xcodeproj * reset Package.resolved * Update WorkflowStep models to match actual backend response - Add WorkflowTrigger struct (name, type, action_id, component_id) - Add triggers, outputs, and metadata fields to WorkflowStep - Add metadata field to PublishedWorkflow - Remove value field from WorkflowTriggerAction (backend uses step_id only) Made-with: Cursor * Update WorkflowResponseTests to match updated models - Replace value/resolvedTargetStepId assertions with stepId - Remove testDecodeWorkflowTriggerActionValueTakesPrecedence (value field removed) - Add testDecodeWorkflowTrigger for new WorkflowTrigger struct - Add testDecodePublishedWorkflowWithMetadata - Update testDecodeWorkflowStepDefaults to cover triggers, outputs, metadata - Add testDecodeWorkflowStepMatchingActualBackendResponse with real backend payload Made-with: Cursor * Disable file_length lint rule in HTTPRequestPath.swift Made-with: Cursor * Fix GetWorkflowOperation to compute result once before distributing to callbacks CDN fetch and JSON decoding were running once per deduplicated callback. Compute the Result<WorkflowFetchResult, BackendError> once outside the performOnAllItemsAndRemoveFromCache loop, matching the pattern used by GetOfferingsOperation and other operations in the codebase. Made-with: Cursor * Fix CDN fetcher: use URLSession instead of Data(contentsOf:), classify errors correctly - Replace Data(contentsOf:) with URLSession.shared.dataTask + DispatchSemaphore in DirectWorkflowCdnFetcher; gets URLSession timeout, HTTP status validation, and proper network stack semantics - Add WorkflowDetailProcessingError.cdnFetchFailed typed error so CDN I/O failures are distinguishable from envelope parsing failures - Catch cdnFetchFailed in GetWorkflowOperation and map to NetworkError.networkError instead of NetworkError.decoding, fixing misleading error classification - Update WorkflowDetailProcessorTests to assert the typed error is thrown Made-with: Cursor * Replace semaphore CDN fetch with async/await using withCheckedThrowingContinuation - Make WorkflowCdnFetcher.fetchCompiledWorkflowData async throws; use withCheckedThrowingContinuation to bridge URLSession.dataTask into async, avoiding any thread-blocking - Make WorkflowDetailProcessor.process async throws to propagate async - Bridge into async in GetWorkflowOperation via Task {}; completion() is called inside the Task after CDN fetch and decoding complete - Update WorkflowDetailProcessorTests to async throws with await Made-with: Cursor * Make CDN fetcher and processor completion-handler based for consistency Avoids Task{} in GetWorkflowOperation and keeps all operations calling completion() synchronously from within the HTTP callback, matching every other operation in the codebase. - WorkflowCdnFetcher.fetchCompiledWorkflowData now takes a completion handler; DirectWorkflowCdnFetcher uses URLSession.dataTask (non-blocking, no semaphore) - WorkflowDetailProcessor.process now takes a completion handler; inline action completes synchronously, use_cdn fans out to the fetcher callback - GetWorkflowOperation splits into getWorkflow/handleResponse/backendResult/ distribute helpers to stay within line-length limits - WorkflowDetailProcessorTests updated to use waitUntilValue pattern Made-with: Cursor * Fix ambiguous cache key delimiter in GetWorkflowOperation Space-separated appUserID+workflowId could collide (e.g. user 'a b' + workflow 'c' == user 'a' + workflow 'b c'). Use newline as delimiter, matching the precedent set by GetWebBillingProductsOperation. Made-with: Cursor * PR comments * remove cdn fetcher * fix response in BackendGetWorkflowsTests.swift * fix WorkflowResponseTests * fix error * [skip ci] Generating new test snapshots (#6584) * [skip ci] Generating new test snapshots (#6585) * [skip ci] Generating new test snapshots (#6586) * [skip ci] Generating new test snapshots (#6587) * [skip ci] Generating new test snapshots (#6588) * [skip ci] Generating new test snapshots (#6589) * [skip ci] Generating new test snapshots (#6590) * Test CDN mock is not re-assignable per test * [skip ci] Generating new test snapshots (#6597) * [skip ci] Generating new test snapshots (#6598) * [skip ci] Generating new test snapshots (#6599) * [skip ci] Generating new test snapshots (#6600) * [skip ci] Generating new test snapshots (#6601) * [skip ci] Generating new test snapshots (#6602) * [skip ci] Generating new test snapshots (#6603) * getWorkflow is signed * add type parameter * add response verification for CDN response * remove workflows list * add value to WorkflowTriggerAction * step id * hash and filerepo * missingCdnHash * linter and project * fix project * revert Package.resolved * @unchecked Sendable * use hash in generateOrGetCachedFileURL * fix compilation * skip responseVerificationMode in cdn * change basePath of caches * remove reserialization * Use explicit type in GetWorkflowOperation.createFactory for greppability Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * Fix swiftlint identifier_name violation in WorkflowDetailProcessorTests Rename short variable `d` to `doubleValue` in two `if case .double(let d)` patterns to satisfy the identifier_name rule requiring names >= 3 characters. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * remove default FileRepositoryType * fix project * fix test * test preserves camel case * Generating new test snapshots for `feat/workflows-network-layer` - ios-15 (#6654) * Generating new test snapshots for `feat/workflows-network-layer` - ios-14 (#6656) --------- Co-authored-by: RevenueCat Git Bot <72824662+RCGitBot@users.noreply.github.com> Co-authored-by: Facundo Menzella <facumenzella@gmail.com> Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com> Co-authored-by: Facundo Menzella <facumenzella@users.noreply.github.com> |
||
|
|
c23c3cdf40 |
UI events for paywall component interactions (#6523)
* Initial setup for paywall control interaction events for v2 paywalls (PWENG-9)
* Use tabs container name for component_name and tab name for component_value (PWENG-9)
* Use tabs container name for tab and toggle control interaction (PWENG-9)
* Introduce paywall control interaction events for legacy paywalls (PWENG-9)
* Align documented code (PWENG-9)
* Fix tvOS 13 build by replacing TapGesture with onTapGesture in FooterView (PWENG-9)
* Fix RevenueCatUITests build by inlining JSON in ToPresentedOverridesTests (PWENG-9)
* Nest paywall control interaction type as PaywallEvent.ControlType (PWENG-9)
* Add componentURL to paywall control interaction events (PWENG-9)
* Add text control type for paywall markdown link analytics (PWENG-9)
* Only track carousel interactions for user drags (PWENG-9)
* Open footer links via OpenURLAction with Link on macOS (PWENG-9)
* Updated documentation for the tab control type (PWENG-9)
* Omit nil name/transition when encoding ButtonComponent (PWENG-9)
* Fix lint errors (PWENG-9)
* Track footer restore tap before restore interceptor (PWENG-9)
* Add CodingKeys for componentUrl in control interaction events (PWENG-9)
* Revert "Add CodingKeys for componentUrl in control interaction events (PWENG-9)"
This reverts commit
|
||
|
|
13212e20f1 |
Track paywall source for Customer Center purchases (#5691)
* Track customer center purchases * simplified logic * add spiinternal * fixed tests * pod lib lint * Add PaywallSource.swift to RevenueCatUI target * Remove unnecessary `@_spi(Internal)` attributes * Simplified Customer Center paywall source to `customer_center` * Remove redundant `source` parameter from `track` method * Update snapshot files * Move PaywallSource to RevenueCat module as @_spi(Internal) enum PaywallSource is now a String-backed enum in the core SDK module (alongside PaywallViewMode), with PaywallEvent.Data.source typed as PaywallSource? instead of String?. Public inits without source are deprecated in favor of @_spi(Internal) inits that accept the typed source parameter. Made-with: Cursor * Fix: pass `paywallSource` to `PaywallEvent` creation * Cleanup unnecessary diff * Revert changes to Examples/rc-maestro/rc-maestro/Resources/StoreKit/StoreKitConfiguration.storekit * Remove redundant source parameter from trackPaywallImpression The source is already embedded in PaywallEvent.Data by createEventData(), so passing it separately was redundant. Callers no longer need to forward paywallSource explicitly. Made-with: Cursor * Undo changes to RevenueCatUI/Purchasing/MockPurchases.swift * Undo changes to RevenueCatUI/Purchasing/PurchaseHandler.swift * Remove presentedOfferingSource from receipt posting This top-level field will be added in a separate PR for the non-paywall Customer Center purchase path. For now, source is only sent inside the paywall object. Made-with: Cursor * Remove trailing newlines from snapshot JSON files Restores the original file endings to avoid potential test comparison issues when files are opened in Xcode. Made-with: Cursor * Simplification * Remove trailing newlines from OfferingContext snapshot files Restores the original file endings to match main and avoid spurious last-line diffs in the PR. Made-with: Cursor * Change Paywall.source type from String? to PaywallSource? Uses the strongly-typed enum directly instead of its raw value, letting Codable handle the string encoding automatically. Made-with: Cursor * Revert cosmetic changes to PurchaseHandler+TestData.swift Restores original formatting from main; the diff was purely noise (reformatting + redundant default parameter). Made-with: Cursor * Change EventsRequest.PaywallEvent.source type from String? to PaywallSource? Consistent with PostReceiptDataOperation.Paywall.source, uses the strongly-typed enum directly and lets Codable handle serialization. Made-with: Cursor * Cleanup * Revert cosmetic changes in AsyncPurchaseHandler init Restores original trailing closure syntax to reduce PR noise. Made-with: Cursor * Add snapshot test for PaywallEvent source serialization Verifies that the source field is correctly encoded as "customer_center" in the EventsRequest.PaywallEvent payload. Made-with: Cursor * Update deprecation messages for PaywallEvent.Data inits Replace implementation-specific deprecation messages with a generic "This initializer will be removed in a future version." message. Made-with: Cursor * Fix compilation error, add source: nil test coverage, fix snapshot newline - Add @_spi(Internal) import to PurchaseHandlerTests so the `source` parameter on the @_spi(Internal) PaywallEvent.Data init is visible. - Restore testPostsReceiptDataWithPresentedPaywall to use source: nil (the common case) so we verify the source key is absent from JSON. - Add testPostsReceiptDataWithPresentedPaywallAndSource for the source: .customerCenter case with its own snapshots. - Fix missing trailing newline in testImpressionEventWithSource snapshot. Made-with: Cursor * Fix snapshot test trailing newlines causing test failures Remove spurious trailing newlines from 11 snapshot JSON files. The snapshot library produces output without a final newline, so the extra byte caused every comparison to fail. Made-with: Cursor * Fix exhaustive switch on PaywallEvent in PurchaseHandlerTests Add missing cases for exitOffer, purchaseInitiated, and purchaseError. Made-with: Cursor * Fix PaywallSource being dropped by withPurchaseInfo withPurchaseInfo was creating a new PaywallEvent.Data without forwarding self.source, so the source was silently lost for purchaseInitiated, cancel, and purchaseError events. This meant the POST receipt paywall.source field was always nil even when the paywall was presented from Customer Center. Also remove the default nil for the source parameter in the internal init to prevent similar issues in the future. Made-with: Cursor * fix: Add missing `source` parameter to PaywallEvent.Data init in tests Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> --------- Co-authored-by: Antonio Pallares <antonio.pallares@revenuecat.com> Co-authored-by: Antonio Pallares <ajpallares@users.noreply.github.com> Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com> |
||
|
|
9356db1d42 | [CEC Mode]: introduce isPurchaseAllowedByRestoreBehavior() (#6192) | ||
|
|
b1df8c54e6 |
Add paywall_id to paywall events and POST /receipt requests (#6087)
* 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 |
||
|
|
507c9d42f7 |
Add payload_version to POST /receipt (#6130)
* 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 |
||
|
|
e43ac94d42 |
Fix CodingKeys to work correctly with snake_case key decoding strategies (#6134)
* Improve comment * Fix codable `CodingKeys` to not use snake case in rawValues As the `JSONEncoder.default` uses `convertToSnakeCase` `keyEncodingStrategy` This allows decoding using the `JSONDecoder.default (which uses `convertFromSnakeCase` `keyDecodingStrategy` to correctly decode the JSONs resulting from encoding with `JSONEncoder.default`. * Add tests * Improve comment |
||
|
|
a0636e36df |
Improve accuracy of transactions origin Part 7: add sdk_originated to POST /receipt (#6091)
|
||
|
|
0e10bc8720 |
Improve accuracy of transactions origin Part 6: add transaction_id to POST /receipt (#6023)
* 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
|
||
|
|
1569b69e58 |
Improve accuracy of transactions origin Part 3: remove PurchaseSource from PurchasedTransactionData and rename it to PostReceiptSource (#6076)
* 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
|
||
|
|
fa25f1edfe |
Improve accuracy of transactions origin Part 2: store and fetch transaction metadata (#6014)
* 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
|
||
|
|
0c3467ae78 |
Improve accuracy of transactions origin Part 1: refactor to allow caching transaction metadata (#5940)
* 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 * Undo `Codable` conformance of `EncodedAppleReceipt` Not needed for now |
||
|
|
a3df5dd229 |
Prevent duplicate post receipt requests (#5795)
* 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 |
||
|
|
510f4e73a4 |
Add internal Offerings source properties (#5749)
* Add `isLoadShedderResponse` and `isFallbackURLResponse` properties to `VerifiedHTTPResponse` Keeps the values also when returned by the ETagManager * lint * Add some tests for ETagManager * Rename `isFallbackURLResponse` to `isFallbackUrlResponse` to prevent decoding issues * Add tests for actual source of original cached response of ETagManager * Add tests to HTTPClientTests regarding the new booleans * lint * Add `originalSource` to `CustomerInfo` * Adds `isLoadedFromCache` boolean to CustomerInfo * Add docs * Unit tests for new customer info properties * Make `OriginalSource.CustomerInfo` a `String` enum * Add decoding tests for Customer Info with new original source field * Add `.loadShedder` check for Customer Info obtained in Load Shedder integration tests * Update fixture * improve internal docs * Add intermediate object `Offerings.Contents` to wrap `OfferingsResponse` + `OriginalSource` and `loadedFromCache` * Adapt existing unit tests to changes in `Offerings` underlying objects * Check Offerings original source in existing integration tests * Fix StoreKit unit tests * Fix RevenueCatUI tests * More Offerings.Contents tests * Fix test * Add more tests * Fix lint * [skip ci] Generating new test snapshots (#5741) * [skip ci] Generating new test snapshots (#5742) * [skip ci] Generating new test snapshots (#5743) * [skip ci] Generating new test snapshots (#5744) * [skip ci] Generating new test snapshots (#5745) * [skip ci] Generating new test snapshots (#5746) * [skip ci] Generating new test snapshots (#5747) * [skip ci] Generating new test snapshots (#5748) * Use enum for `HTTPResponseSource` * Update tests * Adapt to changes in `VerifiedHTTPResponse` * Lint * Adapt to changes in `VerifiedHTTPResponse` * update tests * Add log when a request to the fallback URL was handled by load shedder (which should never happen) * rename `HTTPResponseSource` to `HTTPResponseOriginalSource` * Adapt to changes * fix lint * Adapt to changes * Fix CustomerInfo's OriginalSource implementation + fix unit test * Fix tests compilation * Fix RCUI compilation * Move isFromCache boolean from `Offerings.Contents` to `Offerings` * Fix compilation of `DebugViewSwiftUITests` * Fix compilation of RevenueCatUI tests --------- Co-authored-by: RevenueCat Git Bot <72824662+RCGitBot@users.noreply.github.com> |
||
|
|
8b942f9fd9 |
Add Internal CustomerInfo source properties (#5737)
* Add `isLoadShedderResponse` and `isFallbackURLResponse` properties to `VerifiedHTTPResponse` Keeps the values also when returned by the ETagManager * lint * Add some tests for ETagManager * Rename `isFallbackURLResponse` to `isFallbackUrlResponse` to prevent decoding issues * Add tests for actual source of original cached response of ETagManager * Add tests to HTTPClientTests regarding the new booleans * lint * Add `originalSource` to `CustomerInfo` * Adds `isLoadedFromCache` boolean to CustomerInfo * Add docs * Unit tests for new customer info properties * Make `OriginalSource.CustomerInfo` a `String` enum * Add decoding tests for Customer Info with new original source field * Add `.loadShedder` check for Customer Info obtained in Load Shedder integration tests * Update fixture * improve internal docs * Fix test * Fix lint * Use enum for `HTTPResponseSource` * Update tests * Adapt to changes in `VerifiedHTTPResponse` * Lint * update tests * Add log when a request to the fallback URL was handled by load shedder (which should never happen) * rename `HTTPResponseSource` to `HTTPResponseOriginalSource` * Adapt to changes * fix lint * Fix CustomerInfo's OriginalSource implementation + fix unit test |
||
|
|
1c24d76d6f |
Update Xcode 16.0 to 16.4 in tests because of CircleCI's deprecation later this year (#5668)
* Update Xcode 16.0 to 16.4 in tests because of CircleCI's deprecation later this year * Updated test simulators for Xcode 16.4 * Fixed unused variable warning * [skip ci] Generating new test snapshots (#5670) * [skip ci] Generating new test snapshots (#5673) * [skip ci] Generating new test snapshots (#5672) * Updated iOS simulator version after rebase --------- Co-authored-by: RevenueCat Git Bot <72824662+RCGitBot@users.noreply.github.com> |
||
|
|
2bd289a993 |
Add GetWebBillingProductsOperation (#5419)
* Add validation logic for Test Store API keys under the TEST_STORE compiler flag * add unit test for test store api key validation * Move API key validation to `configure` and store validation result in `SystemInfo` * start new flow for purchase of `TestStoreProduct`s * WIP: implement alert for purchase of a Test Store product * Add file to Xcode workspace * WIP: implement fake purchase alert in all platforms and OSs * Enforce only one test purchases at once. Use constants for test purchase alert strings * Place new test store purchase APIs under the TEST_STORE compiler flag * fix lint * fix unit tests after change of internal APIs * PR comments * temporarily return hardcoded `CustomerInfo` on confirmed test purchase * tests: adapt unit tests to new `TestStorePurchaseHandler` dependency of `PurchasesOrchestrator` * fix lint * Fix backend integration tests compilation * Remove deprecation warning * remove unnecessary OSs checks Since the minimum supported versions of the SDK are iOS 13 and tvOS 13 * add `GetWebProductsOperation` * avoid duplicating decoding objects from WebOfferingProductsResponse and WebProductsResponse * add missing tests snapshots * Add `WebProductsResponse.ProductType` enum * Fix tests * add web products decoding tests * Rename WebProducts to WebBillingProducts * (more) rename WebProducts to WebBillingProducts * (even more) rename WebProducts to WebBillingProducts * Move `getWebBillingProducts` outside of `OfferingsAPI` into a new `WebBillingAPI` object * improve test * remove unnecessary `sorted()` of `Set` * fix lint * fix tests Since the GetWebBilling request path contains an unordered set of productIds, which the tests do not support, we're only doing snapshot tests with 1 product ID * fix more tests |
||
|
|
b7c98a0783 |
Rename internal WebProducts APIs to WebOfferingProducts (#5416)
* Rename existing WebProducts to WebOfferingProducts * fix lint * fix wrong file reference |
||
|
|
5bfbe3b703 | Fixes API tests after changes to public API (#5365) | ||
|
|
01a352d9e9 | Virtual Currency Support (#5108) | ||
|
|
39ad5519fd |
[DX-457] Re-apply Health SDK logging on app launch (#5360)
* Revert "Temporary revert of automatic health reporting in the SDK (#5353)"
This reverts commit
|
||
|
|
dd7086fd98 |
Temporary revert of automatic health reporting in the SDK (#5353)
* Revert "fix `Purchases` temporary leak when running SDK health check (#5350)" This reverts commit |
||
|
|
21f226de00 |
[DX-457] Log the SDK configuration report on every #DEBUG run (#5317)
* Creates a brand new SDK Manager to reuse logic across different classes * Updates to use the new manager * Adds a new method to log the health report to the console only for debug builds * Run health check for debug builds * Fixes sendable warnings that got introduced with new manager * Fixes swiftlint violations * Adds unit tests for the new SDK Health manager * Creates a new authorization provider * Injects the new authorization provider * Tidies and cleans up unit tests * Adds a new configuration value * Updates configuration unit tests * Makes sure that values are only set on DEBUG configuration * Reverts state of default value * Updates wording of output message * Fixes the wrong wording of error message * Logs the word RevenueCat along with report title * Fixes wording issues in error messages * Addresses PR comments * Adds extra checks * Renames configuration value * Removes unnecessary `Task` * Renames parameter * Calls the new endpoint in the BE to check if the app is available or not * Do not log or perform the report operations if it is not available * Missing xcodeproj changes * Adds unit test coverage * Turns validation on by default * Creates a new path for the report availability endpoint * We need to bypass auth but make sure that requests are still signed * Updates the name of the response field * Updates unit tests target with new response structure * Handles no credentials case more gracefully * Adds new unit tests for the new No ASC credentials warning * Updates the file's header comment * Adds documentation * Wraps calls in necessary compiler directives * Hide all report-related content behind compiler directives * Makes more health-related functionality DEBUG only * Addresses PR comments * Updates all unit test build failures * Gets it back to compile on `CUSTOM_ENTITLEMENT_COMPUTATION` * Removes configuration value * Updates logging to only include issues * Adds a mock method for the health report availability request |
||
|
|
46f2cd8c29 |
Change to use new endpoint to fetch web product info (#5135)
* Change to use new endpoint to fetch web product info * [skip ci] Generating new test snapshots (#5142) * [skip ci] Generating new test snapshots (#5141) * [skip ci] Generating new test snapshots (#5140) * [skip ci] Generating new test snapshots (#5139) * [skip ci] Generating new test snapshots (#5138) * [skip ci] Generating new test snapshots (#5137) * [skip ci] Generating new test snapshots (#5136) * Add small utility function to know if offerings has any web biling offering available --------- Co-authored-by: RevenueCat Git Bot <72824662+RCGitBot@users.noreply.github.com> |
||
|
|
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 |
||
|
|
47f40f5abe |
[DX-345] Update the testSDKHealth to use the new /health_report endpoint (#4979)
* Initial app creation * Initial screen * Installs and configures the SDK * Creates a full Swift 6 compliant ViewModel * Adds a demo customer center button * Fixes icon name * Updates a bit of the logic to fetch offerings * Updates initial layout by showing offerings * Makes more progress with a VERY DRAFT and VERY WIP screen * Creates a new network path and requests * Creates a new health API and adds it to the BE class * Creates a `checkOfferings` method * Pushes local changes * Moved viewmodels to separate files * Prepared some basic UI structure * Initial shell of the new method for testing * Initial implementation of `HealthReportOperation` from the SDK * Passes `appUserId` through to the internal API * Adds a new path called `appUserID` * Removes 'old' `appHealth` endpoint * Creates a new model for the network response * Report should be the response type * Updates backend request * Updates types and documentation for the new internal request * Moves validation into its own file * Replaces method for DebugViewModel * Updates error messages * Updates the `Status` section * Adds logic to show failure reason * Removes Sample App from PR * Resets `Package.resolved` * Availability checks * Check if device is authorized to make payments * Fixes all SwiftLint errors * Updates `ProductStatus` to not have a raw value of String * Minor style fixes * Removes warning * Adds more availability checks * Fixes comments Co-authored-by: Antonio Pallares <ajpallares@users.noreply.github.com> * Simplifies logic * Updates comment * Updates the comments to denote that this method is not intended for production use * Removes android-only invalid SDK version error * Adds an action button that the user can use to open the dashboard * Updates copy about non-actionable item * Prefers `Your RevenueCat Configuration` to `The Dashboard` * Removes `Sendable`-related warnings * Fixes unit tests on CI/CD * Fixes tests * Adds unit test for offering configuration error * Adds unit test for no packages in default offering * Adds unit test for generic offering configuration * Removes periods from the end of the error messages * Adds unit tests for all new errors * Fixes linter errors * Wrap in a `DEBUG` compiler directive * Includes all products in the healthy check status to be used in the Sample App * Updates report to include product and offering information * Moves logic over to the report rather than being exclusive to the Debug View * Fixes CI/CD failures * Updates the asset for the health check status --------- Co-authored-by: Hidde van der Ploeg <hiddevanderploeg@me.com> Co-authored-by: Antonio Pallares <ajpallares@users.noreply.github.com> |
||
|
|
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> |
||
|
|
72b06f6943 |
Introduce PurchaseParams to allow passing extra configuration info when making a purchase (#4400)
* Introduce PurchaseParams to allow passing extra configuration info when making a purchase * simplify naming * fallback to old method when flag is not enabled * Add $(inherited) to the test targets so they pick up compiler flags from Local.xcconfig * Include product/package inside PurchaseParams * fix tests * add compiler flag check * update purchase tester * fix docs * add tests * Update Sources/Networking/Operations/PostReceiptDataOperation.swift Co-authored-by: Toni Rico <toni.rico.diez@revenuecat.com> --------- Co-authored-by: Toni Rico <toni.rico.diez@revenuecat.com> |
||
|
|
6f8e53ac40 |
Fix Xcode 16 warnings (#4334)
When compiling with Xcode 16, we started throwing a bunch of new warnings. Here's a list and explanation of each: - The most common one was needing to explicitly restate the @unchecked Sendable conformance from a parent class. - There were a couple switches where a new entry was added in tvOS but only for iOS 18, and needed to add it to make it exhaustive. - There we a couple of places where we were adding a protocol conformance to a class from a different module e.g. extension DispatchTimeInterval: Comparable. This is considered unsafe now because the owners of the module could add the conformance later. Solved by removing the conformance which wasn't being used widely in the codebase, only in tests. - Finally there was a class where we were capturing SKRequest and SKProductsRequest instances in a sendable block, but only to either log something, or use them as dictionary key, so I just made the classes Sendable (with the new @retroactice attribute because they belong to a different module) |
||
|
|
dd0ee262ea |
[Customer Center] Initial implementation (#3967)
Alpha of the Customer Center. In this branch, it's been disabled behind a compilation flag. --------- Co-authored-by: Will Taylor <wtaylor151@gmail.com> Co-authored-by: James Borthwick <109382862+jamesrb1@users.noreply.github.com> Co-authored-by: RevenueCat Git Bot <72824662+RCGitBot@users.noreply.github.com> Co-authored-by: JayShortway <29483617+JayShortway@users.noreply.github.com> Co-authored-by: Toni Rico <toni.rico.diez@revenuecat.com> Co-authored-by: Toni Rico <antonio.rico.diez@revenuecat.com> Co-authored-by: Cody Kerns <44073103+codykerns@users.noreply.github.com> Co-authored-by: Andy Boedo <andresboedo@gmail.com> |
||
|
|
348258a41f | only retry post receipt for 429 status codes | ||
|
|
2bcd0ce328 | 5.0.0 changes: support SK2 by default | ||
|
|
a9737aa7fc |
Add targeting to PresentedOfferingContext (#3730)
### Motivation Get more context with targeting information ### Description - Parse targeting information into `Offerings` - Add targeting context into `Offerings.current` - Add targeting context into `getCurrentOffering(forPlacement: String)` - Post targeting context in post receipts --------- Co-authored-by: RevenueCat Git Bot <72824662+RCGitBot@users.noreply.github.com> |
||
|
|
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>
|
||
|
|
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> |
||
|
|
ed80980d08 |
HTTPClient: verbose logs for request IDs (#3320)
See https://github.com/RevenueCat/khepri/pull/6798. This can help debug issues as it helps us cross-reference requests on our end. ### Changes: - Logging `X-Request-ID` and `X-Amzn-Trace-ID` headers if present (only in verbose mode) - Logging these even for failed requests - Also logging `request_handled_by_load_shedder` for failed requests - To accomplish that. `HTTPURLResponse` now conforms to our `HTTPResponseType`, this allows us to extract headers in an uniform way ### Example: ``` [network] DEBUG: ℹ️ API request completed: GET '/v1/subscribers/$RCAnonymousID%3Ad9dc4f3b33054cf28c0f2ec9da0f48d5' (304) Request-ID: 'be534c50-5231-403b-be2f-7c077861751d'; Amzn-Trace-ID: 'Root=1-6532c6ca-7ef80f961ec9c4145484df13' ``` |
||
|
|
38b4e3b411 |
Paywalls: add identifier to events (#3332)
### Motivation Send a unique `id` on each paywall event ### Description - Added `id: ID?` to `PaywallEvent.Data` - Optional so its backwards compatible with events recorded before this change (see actual tests from below) - Add `withNewID()` function that will mutate the `id` when tracked for cancel and close #### Events recored before `id` changes but posted with `id` changes <img width="433" alt="Screenshot 2023-10-25 at 9 33 27 AM" src="https://github.com/RevenueCat/purchases-ios/assets/401294/b6c77f0c-7429-4638-a4ba-7d6068c80cb7"> #### Events recorded and posted with `id` changes <img width="479" alt="Screenshot 2023-10-25 at 9 37 53 AM" src="https://github.com/RevenueCat/purchases-ios/assets/401294/8cae5329-2f31-4f03-8997-69dcee609e1f"> --------- Co-authored-by: NachoSoto <ignaciosoto90@gmail.com> Co-authored-by: RevenueCat Git Bot <72824662+RCGitBot@users.noreply.github.com> Co-authored-by: Distiller <distiller@static.38.39.183.26.cyberlynk.net> Co-authored-by: Distiller <distiller@static.38.39.184.242.cyberlynk.net> Co-authored-by: distiller <distiller@static.38.23.41.34.cyberlynk.net> |
||
|
|
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> |
||
|
|
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`. |
||
|
|
56de917d02 |
Paywalls: removed presentedPaywallViewMode (#3109)
We're going to track this in a different way in the future. |
||
|
|
8ba761a23f |
Paywalls: send presented PaywallViewMode with purchases (#2859)
I've moved `PaywallViewMode` from `RevenueCatUI` to `RevenueCat`. This uses the same approach as #2645. This method is `public` so it can be used from `RevenueCatUI`, but the documentation stays its purpose. The behavior is covered with integration tests and a snapshot test. Depends on https://github.com/RevenueCat/khepri/pull/6364. |
||
|
|
0ea37c5dd8 |
Refactor: extract HealthOperation (#3154)
|
||
|
|
216f1e13c3 |
PurchasesOrchestrator: disambiguate transactions from the queue (#2890)
This adds tests and a fix for a scenario discovered by @aboedo and
@mcastany.
### Timeline:
- Make a purchase of product_1
- Close app
- Renewals happen in the background
- Subscription expires
- Open app, renewals are in the queue but not processed yet
- Start a purchase of `product_1` (`SKPaymentQueue.add`)
- StoreKit queue shows renewal from step 3 as finished, it's expired
- SDK sees finished transaction in the queue for `product_1`
- SDK sees completion block for `product_1`, considers it a successful
`.purchase`
- SDK posts receipt, `initiation_source: .purchase` 💥
- Backend replies with 422 because StoreKit hasn't processed the
transaction from 6
### Fix:
The fix looks at the date when the completion block was added (the time
`.purchase()` is called).
If the transaction was created _before_ this, we know it didn't come
from the actual purchase, and therefore it should be identified as
`.queue`.
|
||
|
|
cad8328a60 |
IntroEligibility: changed products to Set<String> (#2976)
Order is irrelevant when requesting intro eligibility. Note that I haven't changed the public API because that wouldn't be backwards compatible. |
||
|
|
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 |
||
|
|
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). |
||
|
|
01ca7ae635 |
PostReceiptDataOperation: add new testReceiptIdentifier parameter (#2749)
|
||
|
|
3f538fc6f9 |
Trusted Entitlements: update handling of 304 responses (#2698)
### Changes: - `ETagManager` no longer stores the verification mode - Created new `VerifiedHTTPResponse`, which has the `VerificationResult`, extracted from `HTTPResponse` - Removed `VerificationResult.from(cache:response:)` - Changed `SignatureVerificationHTTPClientTests` to test using the real `ETagManager`, so they become more "end to end" unit tests. A lot of them were relying on implementation details of the mock instead of the real `ETagManager`. - Removed several `HTTPClient` tests that were checking a behavior that was impossible (for example, no verification result despite it being enabled), or that checked behavior based on the cached `VerificationResult` |
||
|
|
2fc7725827 |
PostReceiptDataOperation: clean up cache key (#2628)
|