diff --git a/.claude/skills/contract-conformance/extract.py b/.claude/skills/contract-conformance/extract.py index 84ee01b..ada0211 100644 --- a/.claude/skills/contract-conformance/extract.py +++ b/.claude/skills/contract-conformance/extract.py @@ -26,7 +26,11 @@ "CustomerIdentityParameters": "AdaptyCustomerIdentity", "AdaptyPaywallProduct.Response": "AdaptyPaywallProduct", "AdaptyPaywallProduct.Request": "AdaptyPaywallProductRequest", - "AdaptyPaywallProduct.Subscription": "AdaptySubscription", + # Renamed from AdaptyPaywallProduct.Subscription in contract 4.1.0, now shared with the + # promoted product. + "AdaptyProduct.Subscription": "AdaptySubscription", + "AdaptyPromotedProduct.Response": "AdaptyPromotedProduct", + "AdaptyPromotedProduct.Request": "AdaptyPromotedProductRequest", "AdaptySubscriptionOffer.Phase": "AdaptySubscriptionPhase", "AdaptyUI.FlowView": "AdaptyUIFlowView", "AdaptyUI.OnboardingView": "AdaptyUIOnboardingView", diff --git a/AGENTS.md b/AGENTS.md index 0d6ca0a..70d7ab5 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -131,7 +131,7 @@ Not every `[RuntimeInitializeOnLoadMethod]` here is a reset. `Adapty.InitializeT ### Platform conditionals -There are 25 `#if` in the package, and each belongs to one of five kinds. **Compilation boundary**, 8 — a native symbol exists only there: the `_Adapty` and `_AdaptyCallbackAction` aliases, everything under `Plugins/iOS` and `Plugins/Android`, the Kids Mode post-processor. **Wire contract**, 11 — the contract itself differs: a `[DataMember]` the schema marks platform-only, `offer_tags` read on Android alone, the offer id required on one more branch there. **Public API behaviour**, 3 — the iOS-only methods that on an Android device report `null`, meaning success. Their guard is `UNITY_IOS || UNITY_EDITOR` rather than `UNITY_IOS && !UNITY_EDITOR`, so the Editor reaches the no-op bridge like every other method instead of taking that branch; three `TransportTests` cases pin it. **Where a file actually is**, 2 — a `StreamingAssets` path is a different location on each platform, and the two places that resolve one do **not** agree about the Editor. `AdaptyCustomAssetPath.Resolve` has an `#else` and hands the path back unchanged. `Adapty.SetFallback` has none, so in the Editor it sends no `path` key at all — `transport-set-fallback.editor.approved.txt` is the whole request, `{"method": "set_fallback"}`. Nothing is lost by that: the Editor call reaches the no-op bridge and comes back with the not-supported error, having never needed a path. It is not an omission to repair. **The Kids Mode define**, 1 — `AdaptyConfiguration` forces `apple_idfa_collection_disabled` under `ADAPTY_KIDS_MODE && UNITY_IOS`, because the trait has compiled IDFA out of the binary and the request has to say so. +There are 26 `#if` in the package, and each belongs to one of five kinds. **Compilation boundary**, 8 — a native symbol exists only there: the `_Adapty` and `_AdaptyCallbackAction` aliases, everything under `Plugins/iOS` and `Plugins/Android`, the Kids Mode post-processor. **Wire contract**, 12 — the contract itself differs: a `[DataMember]` the schema marks platform-only (`is_family_shareable` on both product responses), `offer_tags` read on Android alone, the offer id required on one more branch there. **Public API behaviour**, 3 — the iOS-only methods that on an Android device report `null`, meaning success. Their guard is `UNITY_IOS || UNITY_EDITOR` rather than `UNITY_IOS && !UNITY_EDITOR`, so the Editor reaches the no-op bridge like every other method instead of taking that branch; three `TransportTests` cases pin it. **Where a file actually is**, 2 — a `StreamingAssets` path is a different location on each platform, and the two places that resolve one do **not** agree about the Editor. `AdaptyCustomAssetPath.Resolve` has an `#else` and hands the path back unchanged. `Adapty.SetFallback` has none, so in the Editor it sends no `path` key at all — `transport-set-fallback.editor.approved.txt` is the whole request, `{"method": "set_fallback"}`. Nothing is lost by that: the Editor call reaches the no-op bridge and comes back with the not-supported error, having never needed a path. It is not an omission to repair. **The Kids Mode define**, 1 — `AdaptyConfiguration` forces `apple_idfa_collection_disabled` under `ADAPTY_KIDS_MODE && UNITY_IOS`, because the trait has compiled IDFA out of the binary and the request has to say so. The last two kinds are not platform questions the layer above could have answered, which is why they are here rather than folded away. Nothing else qualifies. A constructor must not re-decide by define what the layer above already decided: `AdaptySubscriptionOffer` used to null `OfferTags` off Android although its only caller, the converter, passes null there anyway — the approved snapshots did not move when it went, which is what redundant means. @@ -162,6 +162,6 @@ When releasing a new version, update: Steps 1, 2 and 6 are the ones a release can get wrong without anything noticing — the tag and the artifact name are both derived from `package.json`, so a forgotten `SDKVersion` ships under a correct-looking name. `PackageManifestTests` compares all three against the manifest and fails the suite instead. -The `contract-conformance` skill compares the contract with the C# that restates it; run it whenever step 3 or step 4 moves. One key is worth naming here because a conformance run will keep reporting it: `CustomerIdentityParameters.obfuscated_profile_id` is declared in the contract, Android only, and implemented by nobody — not by this SDK, not by AdaptySDK-Android 4.0.1, and iOS has no such field at all. It was left unimplemented deliberately rather than guessed at. **Re-check it on every native bump**: the moment Android starts reading it, the Unity side has to carry it too, and that is a public API change to `AdaptyCustomerIdentity`, so it wants to land in a release that expects one. +The `contract-conformance` skill compares the contract with the C# that restates it; run it whenever step 3 or step 4 moves. One key is worth naming here because a conformance run will keep reporting it: `CustomerIdentityParameters.obfuscated_profile_id` is declared in the contract, Android only, and implemented by nobody — not by this SDK, not by AdaptySDK-Android 4.1.0, and iOS has no such field at all. It was left unimplemented deliberately rather than guessed at. **Re-check it on every native bump**: the moment Android starts reading it, the Unity side has to carry it too, and that is a public API change to `AdaptyCustomerIdentity`, so it wants to land in a release that expects one. `AdaptyErrorCode.unknownTransactionId` (1030) is the second such item, decided the same way. iOS 4.0.2 declares it, this enum does not name it, and no member is added — because nothing produces it: across the whole of `Sources/` at that tag the identifier appears only in `Sources/Errors/`, as a declaration, a description and a factory method with no call site. A code that cannot be raised needs no constant. **Re-check on every native bump** by grepping the tag for a throw site; the day one exists, the member is owed. diff --git a/Assets/Editor/AdaptyPlayModeSeed.cs b/Assets/Editor/AdaptyPlayModeSeed.cs index f5ac2d9..63ec78f 100644 --- a/Assets/Editor/AdaptyPlayModeSeed.cs +++ b/Assets/Editor/AdaptyPlayModeSeed.cs @@ -21,6 +21,8 @@ private sealed class Sink : IAdaptyEventListener { public void OnLoadLatestProfile(AdaptyProfile profile) { } + public void OnReceivePromotedPurchase(AdaptyPromotedProduct product) { } + public void OnInstallationDetailsSuccess(AdaptyInstallationDetails details) { } public void OnInstallationDetailsFail(AdaptyError error) { } diff --git a/Assets/Scenes/SampleScene.unity b/Assets/Scenes/SampleScene.unity index a976acd..5f0b74a 100644 --- a/Assets/Scenes/SampleScene.unity +++ b/Assets/Scenes/SampleScene.unity @@ -11528,7 +11528,7 @@ GameObject: - component: {fileID: 4323543924197680724} - component: {fileID: 911211715495459256} m_Layer: 5 - m_Name: Button Update Attribution + m_Name: Button Update External Attribution m_TagString: Untagged m_Icon: {fileID: 0} m_NavMeshLayer: 0 @@ -12917,7 +12917,7 @@ MonoBehaviour: m_OnCullStateChanged: m_PersistentCalls: m_Calls: [] - m_text: Update Attribution + m_text: Update External Attribution m_isRightToLeft: 0 m_fontAsset: {fileID: 11400000, guid: 8f586378b4e144a9851e7b34d9b748ee, type: 2} m_sharedMaterial: {fileID: 2180264, guid: 8f586378b4e144a9851e7b34d9b748ee, type: 2} @@ -13987,7 +13987,7 @@ MonoBehaviour: m_Calls: - m_Target: {fileID: 4053823967713777864} m_TargetAssemblyTypeName: ActionsSection, Assembly-CSharp - m_MethodName: UpdateAttributionPressed + m_MethodName: UpdateExternalAttributionPressed m_Mode: 1 m_Arguments: m_ObjectArgument: {fileID: 0} diff --git a/Assets/Scripts/AdaptyListener.cs b/Assets/Scripts/AdaptyListener.cs index 83bc97e..47a60ce 100644 --- a/Assets/Scripts/AdaptyListener.cs +++ b/Assets/Scripts/AdaptyListener.cs @@ -369,16 +369,16 @@ public void ReportTransaction(Action completionHandler) ); } - public void UpdateAttribution(Action completionHandler) + public void UpdateExternalAttribution(Action completionHandler) { - this.LogMethodRequest("UpdateAttribution"); + this.LogMethodRequest("UpdateExternalAttribution"); - Adapty.UpdateAttribution( + Adapty.UpdateExternalAttribution( "{\"test_key\": \"test_value\"}", "custom", (error) => { - this.LogMethodResult("UpdateAttribution", error); + this.LogMethodResult("UpdateExternalAttribution", error); completionHandler.Invoke(error); } ); @@ -531,6 +531,22 @@ public void OnLoadLatestProfile(AdaptyProfile profile) this.Router.SetProfile(profile); } + public void OnReceivePromotedPurchase(AdaptyPromotedProduct product) + { + Debug.Log( + "#AdaptyListener# OnReceivePromotedPurchase called, product = " + + product.VendorProductId + ); + + Adapty.MakePromotedPurchase( + product, + (result, error) => + { + this.LogMethodResult("MakePromotedPurchase", error); + } + ); + } + public void OnInstallationDetailsSuccess(AdaptyInstallationDetails details) { Debug.Log( diff --git a/Assets/Scripts/Sections/ActionsSection.cs b/Assets/Scripts/Sections/ActionsSection.cs index e9cd91f..6ee8447 100644 --- a/Assets/Scripts/Sections/ActionsSection.cs +++ b/Assets/Scripts/Sections/ActionsSection.cs @@ -75,10 +75,10 @@ public void SetIntegrationIdentifierPressed() ); } - public void UpdateAttributionPressed() + public void UpdateExternalAttributionPressed() { this.Router.SetIsLoading(true); - this.Listener.UpdateAttribution( + this.Listener.UpdateExternalAttribution( (error) => { this.Router.SetIsLoading(false); diff --git a/Assets/StreamingAssets/adapty_fallback_android.json b/Assets/StreamingAssets/adapty_fallback_android.json index c79332a..d8bdd5b 100755 --- a/Assets/StreamingAssets/adapty_fallback_android.json +++ b/Assets/StreamingAssets/adapty_fallback_android.json @@ -1 +1 @@ -{"data": {}, "meta": {"version": 10, "developer_ids": [], "response_created_at": 0}, "ui_builder": {}} \ No newline at end of file +{"data": {}, "meta": {"version": 11, "developer_ids": [], "response_created_at": 0}, "ui_builder": {}} \ No newline at end of file diff --git a/Assets/StreamingAssets/adapty_fallback_ios.json b/Assets/StreamingAssets/adapty_fallback_ios.json index c79332a..1517a9b 100755 --- a/Assets/StreamingAssets/adapty_fallback_ios.json +++ b/Assets/StreamingAssets/adapty_fallback_ios.json @@ -1 +1,9 @@ -{"data": {}, "meta": {"version": 10, "developer_ids": [], "response_created_at": 0}, "ui_builder": {}} \ No newline at end of file +{ + "data": {}, + "meta": { + "version": 11, + "developer_ids": [], + "response_created_at": 0 + }, + "ui_builder": {} +} diff --git a/MIGRATION-v4.0-to-v4.1.md b/MIGRATION-v4.0-to-v4.1.md new file mode 100644 index 0000000..9a67ef6 --- /dev/null +++ b/MIGRATION-v4.0-to-v4.1.md @@ -0,0 +1,122 @@ +# Migrate Adapty Unity SDK to v4.1 + +The good news first: this is a small migration, and the compiler does most of it for you. Plan for +three renames it will point out, one new interface method it will demand, and two things it cannot +know about: the fallback files both natives now reject, and the flag installation details now need. +Those two are the steps you can genuinely forget, so if you read just two sections, read +[Re-download your fallback files](#re-download-your-fallback-files) and +[Turn Adapty Attribution on if you read installation details](#turn-adapty-attribution-on-if-you-read-installation-details). + +Under the hood, v4.1 moves the native dependencies to AdaptySDK-iOS 4.1.0 and AdaptySDK-Android +4.1.0, renames the external attribution API to match them, makes Adapty Attribution opt-in, and +adds App Store promoted purchases. The why behind each change, and what was fixed along the way, is +in [CHANGELOG.md](Packages/com.adapty.unity-sdk/CHANGELOG.md). + +1. [Before you upgrade](#before-you-upgrade) +2. [Rename the attribution API](#rename-the-attribution-api) +3. [Implement the new listener method](#implement-the-new-listener-method) +4. [Re-download your fallback files](#re-download-your-fallback-files) +5. [Turn Adapty Attribution on if you read installation details](#turn-adapty-attribution-on-if-you-read-installation-details) +6. [Optional](#optional) + +## Before you upgrade + +If v4.0 built for you, v4.1 will too: the toolchain requirements did not move. Unity 2022.3 or +later, `com.unity.nuget.newtonsoft-json`, External Dependency Manager 1.2.188+ for iOS, Xcode 26 or +later, deployment target 15.0 or later — all exactly as in v4.0. + +Coming from 3.x? Take [MIGRATION-v3.17-to-v4.0.md](MIGRATION-v3.17-to-v4.0.md) first — it covers +the parts that actually hurt (the paywall-to-flow rename, the Newtonsoft dependency, the install +order). This guide starts where it ends. + +Both native dependencies move on their own. iOS is declared inside the package as a Swift package +and External Dependency Manager resolves it at build time; Android ships in the bundled +`.androidlib` that Unity adds to the Gradle build itself. There is nothing for you to update by +hand on either platform. + +## Rename the attribution API + +The native SDKs renamed their attribution APIs in 4.1, and the Unity SDK follows. There are no +deprecated aliases — deliberately: the old and new names would otherwise sit side by side in +autocomplete for a release cycle, and every existing call site is a two-word edit the compiler +finds for you anyway. + +| v4.0 | v4.1 | +|---|---| +| `Adapty.UpdateAttribution(jsonString, source, handler)` | `Adapty.UpdateExternalAttribution(jsonString, provider, handler)` | +| `Adapty.UpdateAttribution(dictionary, source, handler)` | `Adapty.UpdateExternalAttribution(dictionary, provider, handler)` | +| `AdaptyProfile.AppliedAttributionSources` | `AdaptyProfile.AppliedExternalAttributionProviders` | + +Only the names move. The provider is the same open `string` it always was (`"appsflyer"`, +`"adjust"`, `"branch"`, `"tenjin"`, `"apple_search_ads"`, `"custom"`), the profile member is still +an `IReadOnlyList`, and the data you were sending keeps working unchanged. + +## Implement the new listener method + +`IAdaptyEventListener` gained a member, so every class implementing it stops compiling until you +add: + +```csharp +public void OnReceivePromotedPurchase(AdaptyPromotedProduct product) +{ + // The user tapped one of your in-app purchases on your App Store product page. + // Hand it back to Adapty to complete the purchase: + Adapty.MakePromotedPurchase(product, (result, error) => { /* ... */ }); +} +``` + +Not sure whether you need this? Then you don't — promoted purchases are the ones you set up +manually in App Store Connect to appear on your App Store product page, and if you had, you would +know. An empty body is a perfectly honest implementation in that case, and the method is never +called on Android either way. + +One thing worth knowing before you rely on it: the pinned AdaptySDK-iOS 4.1.0 does not yet hand +promoted purchases to wrappers — it completes them by itself, natively, without telling anyone. And +because the native dependency is pinned to exactly 4.1.0 — deliberately, so native behaviour never +changes underneath a wrapper that was not built for it — a future native release will not slip into +your build on its own. The handler starts receiving purchases once a future Unity SDK release moves +that pin to a native that reports them; implementing it now means your code is ready the day you +take that update. + +## Re-download your fallback files + +This is the step the compiler cannot catch, and the symptom shows up at runtime looking like a +broken integration: right after upgrading, `Adapty.SetFallback` fails. On iOS it reports +`DecodingFailed` (`adapty_code: 2006`, *"The fallback paywalls version is not correct. Download a +new one from the Adapty Dashboard."*); on Android, `WrongParam` (`adapty_code: 3001`, *"The +fallback file version is not correct. Download a new one from the Adapty Dashboard."*). Your +integration is fine — the file is stale. + +Both natives reworked how fallback placements are read in 4.1 and now expect **fallback file format +11**; the format 10 files you exported for v4.0 no longer pass. The fix is exactly what the error +says: download fresh fallback files for both platforms from the Adapty Dashboard and replace the +ones in `Assets/StreamingAssets/`. + +## Turn Adapty Attribution on if you read installation details + +The other step nothing warns you about, and the one that looks least like a migration: in 4.1 the +natives collect installation details only when you ask them to. In v4.0 they always did. + +So if your app implements `IAdaptyEventListener.OnInstallationDetailsSuccess` or calls +`Adapty.GetCurrentInstallationStatus`, the callback stops arriving and the status stops reporting +`AdaptyInstallationStatusType.Determined` — on both platforms — until you activate the service: + +```csharp +var builder = new AdaptyConfiguration.Builder("PUBLIC_SDK_KEY") + .SetAdaptyAttributionEnabled(true); +``` + +Nothing else about it changed: the same details arrive in the same shape, on the same callback. If +your app never looked at installation details, there is nothing to do here — leaving the flag unset +is the same as before, minus the collection you were not using. + +## Optional + +- `AdaptyConfiguration.Builder.SetAdaptyAttributionEnabled(true)` turns on the + [Adapty Attribution](https://adapty.io/docs/attribution-integration) service. It is off by + default and not even sent unless you set it — the one thing that hangs off it is the installation + details covered above. +- That is the whole list. The v4.0 additions (`AdaptyUICreateFlowViewParameters.Locale`, + `AdaptyUIFlowView.Locale`) are now officially part of the cross-platform contract, and the rest + of the 4.1 wire-format changes — the nested offer identifier a purchase sends back, the + `ui_schema` a flow carries for the renderer — happen inside the SDK, where you never see them. diff --git a/Packages/com.adapty.unity-sdk/CHANGELOG.md b/Packages/com.adapty.unity-sdk/CHANGELOG.md index 9ef94ff..3ad328a 100644 --- a/Packages/com.adapty.unity-sdk/CHANGELOG.md +++ b/Packages/com.adapty.unity-sdk/CHANGELOG.md @@ -4,9 +4,69 @@ All notable changes to this package will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [4.1.0] - 2026-08-20 + +Upgrading from 4.0: see [MIGRATION-v4.0-to-v4.1.md](https://github.com/adaptyteam/AdaptySDK-Unity/blob/4.1.0/MIGRATION-v4.0-to-v4.1.md). + +### Breaking Changes + +- `Adapty.UpdateAttribution(...)` is renamed to `Adapty.UpdateExternalAttribution(...)`, with the + `source` parameter renamed to `provider`, matching the native 4.1 attribution API rename. The old + name is removed without a deprecated alias. +- `AdaptyProfile.AppliedAttributionSources` is renamed to + `AdaptyProfile.AppliedExternalAttributionProviders`. The wire key is unchanged. +- `IAdaptyEventListener` gained `OnReceivePromotedPurchase(AdaptyPromotedProduct)`; implementations + must add the method. +- The 4.1 natives read **fallback file format 11** and reject the format 10 files v4.0 shipped + against, on both platforms: `Adapty.SetFallback` reports `DecodingFailed` (`adapty_code: 2006`, + *"The fallback paywalls version is not correct"*) on iOS, and `WrongParam` (`adapty_code: 3001`, + *"The fallback file version is not correct"*) on Android. Re-download both fallback files from + the Adapty Dashboard. +- **Adapty Attribution is opt-in, and installation details come with it.** The 4.1 natives collect + installation details only when the configuration asks them to, where the 4.0 natives collected + them unconditionally: `IAdaptyEventListener.OnInstallationDetailsSuccess` stops firing and + `Adapty.GetCurrentInstallationStatus` stops reporting + `AdaptyInstallationStatusType.Determined` until the app activates with + `AdaptyConfiguration.Builder.SetAdaptyAttributionEnabled(true)`. Both platforms behave this way, + and nothing in the API changes shape — an app that reads installation details and does not set + the flag simply stops receiving them. + +### Added + +- `AdaptyConfiguration.Builder.SetAdaptyAttributionEnabled(bool)` — enables the Adapty Attribution + service. Not sent unless set, leaving the native default (off), which in 4.1 is also what turns + installation details off; see the breaking entry above. +- `Adapty.MakePromotedPurchase(AdaptyPromotedProduct, ...)` and + `IAdaptyEventListener.OnReceivePromotedPurchase` — App Store promoted in-app purchases (iOS only; + on other platforms the completion handler reports an error). At native iOS 4.1.0 the event is not + yet emitted — the native SDK still completes promoted purchases by itself, and the exact pin keeps + it that way — so the listener method starts firing only once a future SDK release moves the pin to + a native that reports them. +- `AdaptyFlow` carries the 4.1 `ui_schema` (custom flow layouts, UIBuilder 5.1) through to the + renderer. It is not public API: the schema is the renderer's own data, and the SDK only makes sure + a flow handed back to the native side keeps it. + +### Native and Protocol + +- The native dependencies are pinned to AdaptySDK-iOS 4.1.0, which includes the 4.0.3 hotfix, and + on Android to crossplatform 4.1.0 / android-sdk 4.1.0 / android-ui 4.1.0, with the bundled + unity-wrapper AAR rebuilt at 4.1.0 from `adaptyandroidwrapper/`. +- [Android] `Adapty.RestorePurchases` no longer reports `NoPurchasesToRestore` (1004): the 4.1 + native completes with the current profile when there is nothing to restore. iOS never produced + the code, so nothing sends it now; the `AdaptyErrorCode` member stays. +- The cross-platform contract is 4.1.0: `update_external_attribution_data`, + `make_promoted_purchase`, `did_receive_promoted_purchase`, `adapty_attribution_enabled` and + `ui_schema` are new, and the offer identifier of a product request now travels nested as + `subscription.offer.offer_identifier` — the natives read both forms, the flat + `subscription_offer_identifier` is no longer written. + ## [4.0.0-beta.2] - 2026-08-15 -Upgrading from 3.x: see [MIGRATION-v3.17-to-v4.0.md](https://github.com/adaptyteam/AdaptySDK-Unity/blob/4.0.0-beta.2/MIGRATION-v3.17-to-v4.0.md). +> Never published on its own: 4.1.0 is the first release to carry these changes. The section stays +> because the work of coming from 3.x is described here, and the 4.1.0 section above states only +> what moved since 4.0. + +Upgrading from 3.x: see [MIGRATION-v3.17-to-v4.0.md](https://github.com/adaptyteam/AdaptySDK-Unity/blob/4.1.0/MIGRATION-v3.17-to-v4.0.md). If you install from a `.unitypackage`, delete `Assets/AdaptySDK` and add `com.unity.nuget.newtonsoft-json` **before** importing. A `.unitypackage` never removes files, so the 62 sources this release drops would otherwise stay behind and compile alongside the new ones, which diff --git a/Packages/com.adapty.unity-sdk/Runtime/Adapty.Events.cs b/Packages/com.adapty.unity-sdk/Runtime/Adapty.Events.cs index 2aac135..3a73aa3 100644 --- a/Packages/com.adapty.unity-sdk/Runtime/Adapty.Events.cs +++ b/Packages/com.adapty.unity-sdk/Runtime/Adapty.Events.cs @@ -208,6 +208,17 @@ private static void Dispatch(string id, JObject parameters) ); return; } + case "did_receive_promoted_purchase": + { + if (!RequireEventListener(id)) + return; + var product = Required(parameters, "product"); + AdaptyCallbacks.InvokeSafe( + () => m_Listener.OnReceivePromotedPurchase(product), + "Failed to invoke IAdaptyEventListener.OnReceivePromotedPurchase(..)" + ); + return; + } case "on_installation_details_success": { if (!RequireEventListener(id)) diff --git a/Packages/com.adapty.unity-sdk/Runtime/Adapty.Overloads.cs b/Packages/com.adapty.unity-sdk/Runtime/Adapty.Overloads.cs index b2c0aea..39f531d 100644 --- a/Packages/com.adapty.unity-sdk/Runtime/Adapty.Overloads.cs +++ b/Packages/com.adapty.unity-sdk/Runtime/Adapty.Overloads.cs @@ -50,18 +50,18 @@ Action completionHandler ) => ReportTransaction(transactionId, null, completionHandler); /// - /// Updates attribution data for the profile to track user acquisition sources. + /// Updates external attribution data for the profile to track user acquisition sources. /// /// - /// This method allows you to send attribution data from various sources (e.g., AppsFlyer, Adjust, Branch) to Adapty. + /// This method allows you to send attribution data from external providers (e.g., AppsFlyer, Adjust, Branch) to Adapty. /// Read more on the Adapty Documentation /// /// A dictionary containing attribution (conversion) data from the attribution provider. - /// The source of attribution (e.g., "appsflyer", "adjust", "branch", "custom"). + /// The external attribution provider (e.g., "appsflyer", "adjust", "branch", "custom"). /// The action that will be called with the result. - public static void UpdateAttribution( + public static void UpdateExternalAttribution( IReadOnlyDictionary attribution, - string source, + string provider, Action completionHandler ) { @@ -77,14 +77,14 @@ Action completionHandler catch (Exception exception) { AdaptyRequest.FailEncoding( - "update_attribution_data", + "update_external_attribution_data", exception, completionHandler ); return; } - UpdateAttribution(json, source, completionHandler); + UpdateExternalAttribution(json, provider, completionHandler); } /// diff --git a/Packages/com.adapty.unity-sdk/Runtime/Adapty.cs b/Packages/com.adapty.unity-sdk/Runtime/Adapty.cs index cc2d632..50328e8 100644 --- a/Packages/com.adapty.unity-sdk/Runtime/Adapty.cs +++ b/Packages/com.adapty.unity-sdk/Runtime/Adapty.cs @@ -14,7 +14,7 @@ public static partial class Adapty /// /// The version of the Adapty SDK. /// - public static readonly string SDKVersion = "4.0.0-beta.2"; + public static readonly string SDKVersion = "4.1.0"; /// /// Use this method to initialize the Adapty SDK. @@ -458,6 +458,27 @@ Action completionHandler AdaptyRequest.Send("make_purchase", parameters, completionHandler); } + /// + /// Completes a promoted purchase the user started from the App Store product page. iOS only. + /// + /// + /// Promoted in-app purchases are an App Store feature; on other platforms the completion + /// handler reports an error. + /// Read more on the Adapty Documentation + /// + /// An object received through . + /// The action that will be called with the result. The result contains an object. + public static void MakePromotedPurchase( + AdaptyPromotedProduct product, + Action completionHandler + ) + { + var parameters = new JObject(); + parameters["product"] = AdaptyJson.ToNode(new AdaptyPromotedProductRequest(product)); + + AdaptyRequest.Send("make_promoted_purchase", parameters, completionHandler); + } + /// /// Presents the App Store code redemption sheet, enabling the user to redeem promotional codes. /// @@ -579,26 +600,26 @@ Action completionHandler } /// - /// Updates attribution data for the profile to track user acquisition sources. + /// Updates external attribution data for the profile to track user acquisition sources. /// /// - /// This method allows you to send attribution data from various sources (e.g., AppsFlyer, Adjust, Branch) to Adapty. + /// This method allows you to send attribution data from external providers (e.g., AppsFlyer, Adjust, Branch) to Adapty. /// Read more on the Adapty Documentation /// /// A serialized JSON string containing attribution (conversion) data from the attribution provider. - /// The source of attribution (e.g., "appsflyer", "adjust", "branch", "custom"). + /// The external attribution provider (e.g., "appsflyer", "adjust", "branch", "custom"). /// The action that will be called with the result. - public static void UpdateAttribution( + public static void UpdateExternalAttribution( string jsonString, - string source, + string provider, Action completionHandler ) { var parameters = new JObject(); parameters["attribution"] = jsonString; - parameters["source"] = source; + parameters["provider"] = provider; - AdaptyRequest.SendVoid("update_attribution_data", parameters, completionHandler); + AdaptyRequest.SendVoid("update_external_attribution_data", parameters, completionHandler); } /// diff --git a/Packages/com.adapty.unity-sdk/Runtime/Editor/AdaptySDKDependencies.xml b/Packages/com.adapty.unity-sdk/Runtime/Editor/AdaptySDKDependencies.xml index e7b5399..c3518a2 100644 --- a/Packages/com.adapty.unity-sdk/Runtime/Editor/AdaptySDKDependencies.xml +++ b/Packages/com.adapty.unity-sdk/Runtime/Editor/AdaptySDKDependencies.xml @@ -1,6 +1,6 @@ - + diff --git a/Packages/com.adapty.unity-sdk/Runtime/IAdaptyEventListener.cs b/Packages/com.adapty.unity-sdk/Runtime/IAdaptyEventListener.cs index b923f1a..aa2b22a 100644 --- a/Packages/com.adapty.unity-sdk/Runtime/IAdaptyEventListener.cs +++ b/Packages/com.adapty.unity-sdk/Runtime/IAdaptyEventListener.cs @@ -15,6 +15,16 @@ public interface IAdaptyEventListener /// The updated object. void OnLoadLatestProfile(AdaptyProfile profile); + /// + /// Called when the user starts a promoted in-app purchase from the App Store product page. iOS only. + /// + /// + /// Complete the purchase by passing the product to + /// . + /// + /// The the user chose. + void OnReceivePromotedPurchase(AdaptyPromotedProduct product); + /// /// Called when installation details are successfully retrieved. /// diff --git a/Packages/com.adapty.unity-sdk/Runtime/Models/AdaptyConfiguration.Builder.cs b/Packages/com.adapty.unity-sdk/Runtime/Models/AdaptyConfiguration.Builder.cs index 18f784b..696872c 100644 --- a/Packages/com.adapty.unity-sdk/Runtime/Models/AdaptyConfiguration.Builder.cs +++ b/Packages/com.adapty.unity-sdk/Runtime/Models/AdaptyConfiguration.Builder.cs @@ -27,6 +27,7 @@ internal AdaptyConfiguration(Builder builder) GoogleEnablePendingPrepaidPlans = builder.GoogleEnablePendingPrepaidPlans; GoogleLocalAccessLevelAllowed = builder.GoogleLocalAccessLevelAllowed; IpAddressCollectionDisabled = builder.IpAddressCollectionDisabled; + AdaptyAttributionEnabled = builder.AdaptyAttributionEnabled; AppleClearDataOnBackup = builder.AppleClearDataOnBackup; ServerCluster = builder.ServerCluster; BackendProxyHost = builder.BackendProxyHost; @@ -49,6 +50,7 @@ internal AdaptyConfiguration(Builder builder) /// /// Only the API key is required, but "not set" does not mean "not sent". The nullable /// members — , , + /// , /// , , /// , , /// and — are left out of @@ -114,6 +116,19 @@ public sealed class Builder /// public bool IpAddressCollectionDisabled; + /// + /// Enables the Adapty + /// Attribution service. Null leaves the native default, which is off. + /// + /// + /// Installation details hang off this flag: while it is off, the 4.1 natives collect + /// none, so + /// never fires and never reports + /// . The 4.0 natives collected + /// them unconditionally. + /// + public bool? AdaptyAttributionEnabled; + /// /// iOS only. Clears the SDK's stored data when the app is restored from an iCloud /// backup, so a restored device does not carry the previous one's profile. Null @@ -179,6 +194,7 @@ public override string ToString() => + $"{nameof(GoogleEnablePendingPrepaidPlans)}: {GoogleEnablePendingPrepaidPlans}, " + $"{nameof(GoogleLocalAccessLevelAllowed)}: {GoogleLocalAccessLevelAllowed}, " + $"{nameof(IpAddressCollectionDisabled)}: {IpAddressCollectionDisabled}, " + + $"{nameof(AdaptyAttributionEnabled)}: {AdaptyAttributionEnabled}, " + $"{nameof(AppleClearDataOnBackup)}: {AppleClearDataOnBackup}, " + $"{nameof(ServerCluster)}: {ServerCluster}, " + $"{nameof(BackendProxyHost)}: {BackendProxyHost}, " @@ -295,6 +311,16 @@ public Builder SetIPAddressCollectionDisabled(bool ipAddressCollectionDisabled) return this; } + /// + /// Sets . + /// + /// True to enable the Adapty Attribution service. + public Builder SetAdaptyAttributionEnabled(bool adaptyAttributionEnabled) + { + AdaptyAttributionEnabled = adaptyAttributionEnabled; + return this; + } + /// /// Sets . iOS only. /// diff --git a/Packages/com.adapty.unity-sdk/Runtime/Models/AdaptyConfiguration.cs b/Packages/com.adapty.unity-sdk/Runtime/Models/AdaptyConfiguration.cs index a658ee2..b767b7c 100644 --- a/Packages/com.adapty.unity-sdk/Runtime/Models/AdaptyConfiguration.cs +++ b/Packages/com.adapty.unity-sdk/Runtime/Models/AdaptyConfiguration.cs @@ -24,6 +24,8 @@ public sealed partial class AdaptyConfiguration private readonly bool? GoogleLocalAccessLevelAllowed; [DataMember(Name = "ip_address_collection_disabled")] private readonly bool? IpAddressCollectionDisabled; + [DataMember(Name = "adapty_attribution_enabled")] + private readonly bool? AdaptyAttributionEnabled; [DataMember(Name = "clear_data_on_backup")] private readonly bool? AppleClearDataOnBackup; [DataMember(Name = "server_cluster")] @@ -62,6 +64,7 @@ public override string ToString() => + $"{nameof(GoogleEnablePendingPrepaidPlans)}: {GoogleEnablePendingPrepaidPlans}, " + $"{nameof(GoogleLocalAccessLevelAllowed)}: {GoogleLocalAccessLevelAllowed}, " + $"{nameof(IpAddressCollectionDisabled)}: {IpAddressCollectionDisabled}, " + + $"{nameof(AdaptyAttributionEnabled)}: {AdaptyAttributionEnabled}, " + $"{nameof(AppleClearDataOnBackup)}: {AppleClearDataOnBackup}, " + $"{nameof(ServerCluster)}: {ServerCluster}, " + $"{nameof(BackendProxyHost)}: {BackendProxyHost}, " diff --git a/Packages/com.adapty.unity-sdk/Runtime/Models/AdaptyErrorCode.cs b/Packages/com.adapty.unity-sdk/Runtime/Models/AdaptyErrorCode.cs index 1657505..6e0e7dd 100644 --- a/Packages/com.adapty.unity-sdk/Runtime/Models/AdaptyErrorCode.cs +++ b/Packages/com.adapty.unity-sdk/Runtime/Models/AdaptyErrorCode.cs @@ -9,8 +9,10 @@ namespace AdaptySDK /// Almost every value is the native SDK's own, so a code always arrives whether or not this /// enum names it. Most of those are produced by one platform only, and each member says which; /// where both produce a number, both meanings are given, because they are not always the same - /// one. Verified against AdaptySDK-iOS 4.0.2 and AdaptySDK-Android 4.0.1, the pinned - /// dependencies. Two are different: and + /// one. Each was traced to its throw site in AdaptySDK-iOS 4.0.2 and AdaptySDK-Android 4.0.1; + /// the pinned 4.1.0 natives declare the same set of codes, with the one behavioural change + /// noted on . Two are different: + /// and /// are also raised by this SDK itself, on either platform, when a /// request cannot be encoded or a reply cannot be read — the native side is never reached in /// the first case and has already answered in the second. @@ -188,8 +190,10 @@ public enum AdaptyErrorCode CantMakePayments = 1003, /// - /// Android only. found nothing to restore. iOS does - /// not produce this code. + /// Nothing to restore. No pinned native produces it any more: iOS never did, and the + /// Android 4.1 native handles it internally — completes with the + /// current profile instead. The member stays because the code is still the native SDKs' + /// own and an older native, or a future one, may send it. /// NoPurchasesToRestore = 1004, diff --git a/Packages/com.adapty.unity-sdk/Runtime/Models/AdaptyFlow.cs b/Packages/com.adapty.unity-sdk/Runtime/Models/AdaptyFlow.cs index b3e3e6e..d0e0626 100644 --- a/Packages/com.adapty.unity-sdk/Runtime/Models/AdaptyFlow.cs +++ b/Packages/com.adapty.unity-sdk/Runtime/Models/AdaptyFlow.cs @@ -1,6 +1,7 @@ using System.Collections.Generic; using System.Collections.ObjectModel; using System.Runtime.Serialization; +using Newtonsoft.Json.Linq; using UnityEngine.Scripting; namespace AdaptySDK @@ -87,6 +88,13 @@ public AdaptyRemoteConfig RemoteConfig [Preserve] public IReadOnlyList Paywalls { get; private set; } + /// + /// The renderer's custom-layout schema. Opaque to the app: carried only so a flow handed + /// back to the native side keeps it. + /// + [DataMember(Name = "ui_schema")] + private readonly JObject _UiSchema; + [DataMember(Name = "response_created_at", IsRequired = true)] private readonly long _ResponseCreatedAt; [DataMember(Name = "payload_data")] @@ -163,6 +171,7 @@ public override string ToString() => + $"{nameof(FlowVersionId)}: {FlowVersionId}, " + $"{nameof(RemoteConfigs)}: {RemoteConfigs}, " + $"{nameof(Paywalls)}: {Paywalls}, " + + $"{nameof(_UiSchema)}: {_UiSchema?.ToString(Newtonsoft.Json.Formatting.None)}, " + $"{nameof(_ResponseCreatedAt)}: {_ResponseCreatedAt}, " + $"{nameof(_PayloadData)}: {_PayloadData}"; } diff --git a/Packages/com.adapty.unity-sdk/Runtime/Models/AdaptyInstallationStatusType.cs b/Packages/com.adapty.unity-sdk/Runtime/Models/AdaptyInstallationStatusType.cs index 66506c7..9eb4912 100644 --- a/Packages/com.adapty.unity-sdk/Runtime/Models/AdaptyInstallationStatusType.cs +++ b/Packages/com.adapty.unity-sdk/Runtime/Models/AdaptyInstallationStatusType.cs @@ -10,8 +10,10 @@ namespace AdaptySDK public enum AdaptyInstallationStatusType { /// - /// The details are not available. Reported when the platform has nothing to report, and on iOS - /// also when the install time or the launch count could not be obtained. + /// The details are not available. Reported when the platform has nothing to report — which + /// includes the case of Adapty Attribution being off, its default, since the 4.1 natives + /// collect nothing until it is on — and on iOS also when the install time or the launch + /// count could not be obtained. /// [EnumMember(Value = "not_available")] NotAvailable = 0, diff --git a/Packages/com.adapty.unity-sdk/Runtime/Models/AdaptyProfile.cs b/Packages/com.adapty.unity-sdk/Runtime/Models/AdaptyProfile.cs index e451045..19b6e0c 100644 --- a/Packages/com.adapty.unity-sdk/Runtime/Models/AdaptyProfile.cs +++ b/Packages/com.adapty.unity-sdk/Runtime/Models/AdaptyProfile.cs @@ -41,16 +41,16 @@ public sealed partial class AdaptyProfile internal readonly string SegmentId; /// - /// Identifiers of attribution sources applied to the profile. + /// Identifiers of external attribution providers applied to the profile. /// [DataMember(Name = "applied_attribution_sources")] - private readonly List _AppliedAttributionSources = new List(); + private readonly List _AppliedExternalAttributionProviders = new List(); /// - /// The attribution sources applied to this profile. + /// The external attribution providers applied to this profile. /// [Preserve] - public IReadOnlyList AppliedAttributionSources { get; private set; } + public IReadOnlyList AppliedExternalAttributionProviders { get; private set; } /// /// Previously set user custom attributes with method. @@ -132,7 +132,7 @@ public sealed partial class AdaptyProfile private void Freeze() { - AppliedAttributionSources = new ReadOnlyCollection(_AppliedAttributionSources); + AppliedExternalAttributionProviders = new ReadOnlyCollection(_AppliedExternalAttributionProviders); CustomAttributes = new ReadOnlyDictionary(_CustomAttributes); AccessLevels = new ReadOnlyDictionary(_AccessLevels); Subscriptions = new ReadOnlyDictionary(_Subscriptions); @@ -189,7 +189,7 @@ public override string ToString() return $"{nameof(ProfileId)}: {ProfileId}, " + $"{nameof(SegmentId)}: {SegmentId}, " + $"{nameof(CustomerUserId)}: {CustomerUserId}, " - + $"{nameof(AppliedAttributionSources)}: [{string.Join(", ", AppliedAttributionSources)}], " + + $"{nameof(AppliedExternalAttributionProviders)}: [{string.Join(", ", AppliedExternalAttributionProviders)}], " + $"{nameof(CustomAttributes)}: {customAttributesStr}, " + $"{nameof(AccessLevels)}: {accessLevelsStr}, " + $"{nameof(Subscriptions)}: {subscriptionsStr}, " diff --git a/Packages/com.adapty.unity-sdk/Runtime/Models/AdaptyPromotedProduct.cs b/Packages/com.adapty.unity-sdk/Runtime/Models/AdaptyPromotedProduct.cs new file mode 100644 index 0000000..eb048d3 --- /dev/null +++ b/Packages/com.adapty.unity-sdk/Runtime/Models/AdaptyPromotedProduct.cs @@ -0,0 +1,88 @@ +using System.Runtime.Serialization; +using UnityEngine.Scripting; + +namespace AdaptySDK +{ + /// + /// A product for an App Store promoted in-app purchase. iOS only. + /// + /// + /// Arrives through + /// when the user starts a purchase from the App Store product page. Hand it to + /// + /// to complete the purchase. + /// + [DataContract] + [Preserve] + public sealed class AdaptyPromotedProduct + { + private AdaptyPromotedProduct() { } + + /// + /// The unique identifier of the product in the App Store. + /// + [DataMember(Name = "vendor_product_id", IsRequired = true)] + public readonly string VendorProductId; + + /// + /// The description of the product, localized by the storefront the user's device is + /// connected to. + /// + [DataMember(Name = "localized_description", IsRequired = true)] + public readonly string LocalizedDescription; + + /// + /// The name of the product, localized by the storefront the user's device is connected to. + /// + [DataMember(Name = "localized_title", IsRequired = true)] + public readonly string LocalizedTitle; + + /// + /// Whether the product is available for family sharing in App Store Connect (iOS only). + /// +#if UNITY_IOS + [DataMember(Name = "is_family_shareable", IsRequired = true)] +#endif + public readonly bool IsFamilyShareable; + + /// + /// The region code of the locale used to format the price of the product. + /// + [DataMember(Name = "region_code")] + public readonly string RegionCode; + + /// + /// The object that represents the main price for the product. + /// + [DataMember(Name = "price", IsRequired = true)] + public readonly AdaptyPrice Price; + + /// + /// Detailed information about the subscription, including introductory offers, promotional offers, etc. + /// + /// + /// This is null for non-subscription products. + /// + [DataMember(Name = "subscription")] + public readonly AdaptySubscription Subscription; + + [DataMember(Name = "payload_data")] + private readonly string _PayloadData; + + internal string PayloadData => _PayloadData; + + /// + /// A description for logs and the debugger. The format is not part of the contract — + /// read the members rather than parsing it. + /// + public override string ToString() => + $"{nameof(VendorProductId)}: {VendorProductId}, " + + $"{nameof(LocalizedDescription)}: {LocalizedDescription}, " + + $"{nameof(LocalizedTitle)}: {LocalizedTitle}, " + + $"{nameof(IsFamilyShareable)}: {IsFamilyShareable}, " + + $"{nameof(RegionCode)}: {RegionCode}, " + + $"{nameof(Price)}: {Price}, " + + $"{nameof(Subscription)}: {Subscription}, " + + $"{nameof(_PayloadData)}: {_PayloadData}"; + } +} diff --git a/Packages/com.adapty.unity-sdk/Runtime/Models/AdaptyPromotedProduct.cs.meta b/Packages/com.adapty.unity-sdk/Runtime/Models/AdaptyPromotedProduct.cs.meta new file mode 100644 index 0000000..b72f48d --- /dev/null +++ b/Packages/com.adapty.unity-sdk/Runtime/Models/AdaptyPromotedProduct.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 9798abb672f547e4afbfe8685e11d065 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/com.adapty.unity-sdk/Runtime/Plugins/Android/AdaptySDKDependencies.androidlib/build.gradle b/Packages/com.adapty.unity-sdk/Runtime/Plugins/Android/AdaptySDKDependencies.androidlib/build.gradle index 4501638..7827fc2 100644 --- a/Packages/com.adapty.unity-sdk/Runtime/Plugins/Android/AdaptySDKDependencies.androidlib/build.gradle +++ b/Packages/com.adapty.unity-sdk/Runtime/Plugins/Android/AdaptySDKDependencies.androidlib/build.gradle @@ -3,9 +3,9 @@ apply plugin: 'com.android.library' def unityLibraryAndroid = project(':unityLibrary').android dependencies { - api 'io.adapty.internal:crossplatform:4.0.2' - api 'io.adapty:android-sdk:4.0.1' - api 'io.adapty:android-ui:4.0.1' + api 'io.adapty.internal:crossplatform:4.1.0' + api 'io.adapty:android-sdk:4.1.0' + api 'io.adapty:android-ui:4.1.0' } android { diff --git a/Packages/com.adapty.unity-sdk/Runtime/Plugins/Android/Local/io/adapty/internal/unity-wrapper/4.0.1.meta b/Packages/com.adapty.unity-sdk/Runtime/Plugins/Android/Local/io/adapty/internal/unity-wrapper/4.1.0.meta similarity index 100% rename from Packages/com.adapty.unity-sdk/Runtime/Plugins/Android/Local/io/adapty/internal/unity-wrapper/4.0.1.meta rename to Packages/com.adapty.unity-sdk/Runtime/Plugins/Android/Local/io/adapty/internal/unity-wrapper/4.1.0.meta diff --git a/Packages/com.adapty.unity-sdk/Runtime/Plugins/Android/Local/io/adapty/internal/unity-wrapper/4.0.1/unity-wrapper-4.0.1.aar b/Packages/com.adapty.unity-sdk/Runtime/Plugins/Android/Local/io/adapty/internal/unity-wrapper/4.1.0/unity-wrapper-4.1.0.aar similarity index 100% rename from Packages/com.adapty.unity-sdk/Runtime/Plugins/Android/Local/io/adapty/internal/unity-wrapper/4.0.1/unity-wrapper-4.0.1.aar rename to Packages/com.adapty.unity-sdk/Runtime/Plugins/Android/Local/io/adapty/internal/unity-wrapper/4.1.0/unity-wrapper-4.1.0.aar diff --git a/Packages/com.adapty.unity-sdk/Runtime/Plugins/Android/Local/io/adapty/internal/unity-wrapper/4.0.1/unity-wrapper-4.0.1.aar.meta b/Packages/com.adapty.unity-sdk/Runtime/Plugins/Android/Local/io/adapty/internal/unity-wrapper/4.1.0/unity-wrapper-4.1.0.aar.meta similarity index 100% rename from Packages/com.adapty.unity-sdk/Runtime/Plugins/Android/Local/io/adapty/internal/unity-wrapper/4.0.1/unity-wrapper-4.0.1.aar.meta rename to Packages/com.adapty.unity-sdk/Runtime/Plugins/Android/Local/io/adapty/internal/unity-wrapper/4.1.0/unity-wrapper-4.1.0.aar.meta diff --git a/Packages/com.adapty.unity-sdk/Runtime/Plugins/Android/Local/io/adapty/internal/unity-wrapper/4.0.1/unity-wrapper-4.0.1.pom b/Packages/com.adapty.unity-sdk/Runtime/Plugins/Android/Local/io/adapty/internal/unity-wrapper/4.1.0/unity-wrapper-4.1.0.pom similarity index 93% rename from Packages/com.adapty.unity-sdk/Runtime/Plugins/Android/Local/io/adapty/internal/unity-wrapper/4.0.1/unity-wrapper-4.0.1.pom rename to Packages/com.adapty.unity-sdk/Runtime/Plugins/Android/Local/io/adapty/internal/unity-wrapper/4.1.0/unity-wrapper-4.1.0.pom index 228e2bf..9f4ec4d 100644 --- a/Packages/com.adapty.unity-sdk/Runtime/Plugins/Android/Local/io/adapty/internal/unity-wrapper/4.0.1/unity-wrapper-4.0.1.pom +++ b/Packages/com.adapty.unity-sdk/Runtime/Plugins/Android/Local/io/adapty/internal/unity-wrapper/4.1.0/unity-wrapper-4.1.0.pom @@ -3,7 +3,7 @@ 4.0.0 io.adapty.internal unity-wrapper - 4.0.1 + 4.1.0 aar diff --git a/Packages/com.adapty.unity-sdk/Runtime/Plugins/Android/Local/io/adapty/internal/unity-wrapper/4.0.1/unity-wrapper-4.0.1.pom.meta b/Packages/com.adapty.unity-sdk/Runtime/Plugins/Android/Local/io/adapty/internal/unity-wrapper/4.1.0/unity-wrapper-4.1.0.pom.meta similarity index 100% rename from Packages/com.adapty.unity-sdk/Runtime/Plugins/Android/Local/io/adapty/internal/unity-wrapper/4.0.1/unity-wrapper-4.0.1.pom.meta rename to Packages/com.adapty.unity-sdk/Runtime/Plugins/Android/Local/io/adapty/internal/unity-wrapper/4.1.0/unity-wrapper-4.1.0.pom.meta diff --git a/Packages/com.adapty.unity-sdk/Runtime/Serialization/AdaptyPaywallProductRequest.cs b/Packages/com.adapty.unity-sdk/Runtime/Serialization/AdaptyPaywallProductRequest.cs index 9a8556c..586b862 100644 --- a/Packages/com.adapty.unity-sdk/Runtime/Serialization/AdaptyPaywallProductRequest.cs +++ b/Packages/com.adapty.unity-sdk/Runtime/Serialization/AdaptyPaywallProductRequest.cs @@ -5,7 +5,7 @@ namespace AdaptySDK.Serialization { /// /// What the native side expects when a product is handed back to it - a strict subset of what - /// it sent, with the subscription offer flattened to an identifier. + /// it sent, with the subscription reduced to its offer identifier. /// /// /// The model itself cannot express this: a member carries one contract name for both @@ -32,7 +32,7 @@ internal AdaptyPaywallProductRequest(AdaptyPaywallProduct product) var offer = product.Subscription?.Offer; if (offer != null) { - Offer = new OfferIdentifier(offer.Identifier, offer.Type); + Subscription = new AdaptySubscriptionOfferRequest(offer); } } @@ -76,26 +76,8 @@ internal AdaptyPaywallProductRequest(AdaptyPaywallProduct product) [Preserve] private string PayloadData { get; } - [DataMember(Name = "subscription_offer_identifier")] + [DataMember(Name = "subscription")] [Preserve] - private OfferIdentifier Offer { get; } - - [DataContract] - private sealed class OfferIdentifier - { - internal OfferIdentifier(string id, AdaptySubscriptionOfferType type) - { - Id = id; - Type = type; - } - - [DataMember(Name = "id")] - [Preserve] - private string Id { get; } - - [DataMember(Name = "type", IsRequired = true)] - [Preserve] - private AdaptySubscriptionOfferType Type { get; } - } + private AdaptySubscriptionOfferRequest Subscription { get; } } } diff --git a/Packages/com.adapty.unity-sdk/Runtime/Serialization/AdaptyPromotedProductRequest.cs b/Packages/com.adapty.unity-sdk/Runtime/Serialization/AdaptyPromotedProductRequest.cs new file mode 100644 index 0000000..b3b7440 --- /dev/null +++ b/Packages/com.adapty.unity-sdk/Runtime/Serialization/AdaptyPromotedProductRequest.cs @@ -0,0 +1,43 @@ +using System.Runtime.Serialization; +using UnityEngine.Scripting; + +namespace AdaptySDK.Serialization +{ + /// + /// What the native side expects when a promoted product is handed back to it - a strict subset + /// of what it sent, with the subscription reduced to its offer identifier. + /// + /// + /// The model itself cannot express this: a member carries one contract name for both + /// directions, so serializing as-is would return the whole + /// product, price and localization included. + /// + [DataContract] + [Preserve] + internal sealed class AdaptyPromotedProductRequest + { + internal AdaptyPromotedProductRequest(AdaptyPromotedProduct product) + { + VendorProductId = product.VendorProductId; + PayloadData = product.PayloadData; + + var offer = product.Subscription?.Offer; + if (offer != null) + { + Subscription = new AdaptySubscriptionOfferRequest(offer); + } + } + + [DataMember(Name = "vendor_product_id", IsRequired = true)] + [Preserve] + private string VendorProductId { get; } + + [DataMember(Name = "payload_data")] + [Preserve] + private string PayloadData { get; } + + [DataMember(Name = "subscription")] + [Preserve] + private AdaptySubscriptionOfferRequest Subscription { get; } + } +} diff --git a/Packages/com.adapty.unity-sdk/Runtime/Serialization/AdaptyPromotedProductRequest.cs.meta b/Packages/com.adapty.unity-sdk/Runtime/Serialization/AdaptyPromotedProductRequest.cs.meta new file mode 100644 index 0000000..a4dc1ea --- /dev/null +++ b/Packages/com.adapty.unity-sdk/Runtime/Serialization/AdaptyPromotedProductRequest.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: eb71db85af1a422780291f797583ac73 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/com.adapty.unity-sdk/Runtime/Serialization/AdaptySubscriptionOfferRequest.cs b/Packages/com.adapty.unity-sdk/Runtime/Serialization/AdaptySubscriptionOfferRequest.cs new file mode 100644 index 0000000..f494bee --- /dev/null +++ b/Packages/com.adapty.unity-sdk/Runtime/Serialization/AdaptySubscriptionOfferRequest.cs @@ -0,0 +1,55 @@ +using System.Runtime.Serialization; +using UnityEngine.Scripting; + +namespace AdaptySDK.Serialization +{ + /// + /// How a request writes a subscription offer back to the native side - reduced to its + /// identifier and nested as subscription.offer.offer_identifier, the same envelope for the + /// paywall and promoted product requests. + /// + [DataContract] + [Preserve] + internal sealed class AdaptySubscriptionOfferRequest + { + internal AdaptySubscriptionOfferRequest(AdaptySubscriptionOffer offer) + { + Offer = new OfferEnvelope(new OfferIdentifier(offer.Identifier, offer.Type)); + } + + [DataMember(Name = "offer", IsRequired = true)] + [Preserve] + private OfferEnvelope Offer { get; } + + [DataContract] + private sealed class OfferEnvelope + { + internal OfferEnvelope(OfferIdentifier identifier) + { + Identifier = identifier; + } + + [DataMember(Name = "offer_identifier", IsRequired = true)] + [Preserve] + private OfferIdentifier Identifier { get; } + } + + [DataContract] + private sealed class OfferIdentifier + { + internal OfferIdentifier(string id, AdaptySubscriptionOfferType type) + { + Id = id; + Type = type; + } + + [DataMember(Name = "id")] + [Preserve] + private string Id { get; } + + [DataMember(Name = "type", IsRequired = true)] + [Preserve] + private AdaptySubscriptionOfferType Type { get; } + } + } +} diff --git a/Packages/com.adapty.unity-sdk/Runtime/Serialization/AdaptySubscriptionOfferRequest.cs.meta b/Packages/com.adapty.unity-sdk/Runtime/Serialization/AdaptySubscriptionOfferRequest.cs.meta new file mode 100644 index 0000000..6077c08 --- /dev/null +++ b/Packages/com.adapty.unity-sdk/Runtime/Serialization/AdaptySubscriptionOfferRequest.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: a8a034ee22aa42e288b5225aba91f1fe +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/com.adapty.unity-sdk/package.json b/Packages/com.adapty.unity-sdk/package.json index 789dc0e..873fe66 100644 --- a/Packages/com.adapty.unity-sdk/package.json +++ b/Packages/com.adapty.unity-sdk/package.json @@ -1,11 +1,11 @@ { "name": "com.adapty.unity-sdk", - "version": "4.0.0-beta.2", + "version": "4.1.0", "displayName": "Adapty Unity SDK", "description": "Unity SDK for Adapty in-app purchases, subscriptions, paywalls, and onboarding flows.", "unity": "2022.3", "_upm": { - "changelog": "Upgrading from 3.x: see [MIGRATION-v3.17-to-v4.0.md](https://github.com/adaptyteam/AdaptySDK-Unity/blob/4.0.0-beta.2/MIGRATION-v3.17-to-v4.0.md). If you install from a `.unitypackage`, delete `Assets/AdaptySDK` and add `com.unity.nuget.newtonsoft-json` **before** importing: a `.unitypackage` never removes files, so the 62 sources this release drops would otherwise stay behind and collide with the new ones. Until Newtonsoft is there the SDK assembly is skipped, so code that calls Adapty will not compile — and once it does not, Unity stops loading the Editor assembly the installer menu lives in.\n\n### Added\n\n- **iOS Kids Mode** for the App Store Kids Category / COPPA: the `ADAPTY_KIDS_MODE` scripting define enables the `KidsMode` trait on the AdaptySDK-iOS Swift package (IDFA, AdSupport and AppTrackingTransparency are compiled out of the binary) and forces `apple_idfa_collection_disabled` in the runtime configuration. Swift package traits need Xcode 26, which v4 requires anyway. Set the define in Player Settings; a build profile's scripting defines work too, but only once Unity has recompiled the Editor assemblies for them, and the iOS build fails if the SDK detects that it is running against stale ones. `BuildPlayerOptions.extraScriptingDefines` is not supported at all.\n- `AdaptyUICreateFlowViewParameters.Locale` — the localization to render the flow with. A flow is localized when its view is built, so this is the only place that selects the localization. Requires the native iOS 4.0.2 and Android 4.0.1 releases, both of which are now the pinned dependencies.\n- `AdaptyUIFlowView.Locale` — the localization the view was actually built with.\n- `AdaptyUICreateFlowViewParameters.EnableSafeAreaPaddings` (Android only).\n- **Adapty SDK > Install Dependencies** — installs whichever of the SDK's package dependencies are missing: Newtonsoft.Json, and External Dependency Manager along with the OpenUPM scoped registry it is published on. A `.unitypackage` carries assets only and can bring neither, and External Dependency Manager has always had to be installed by hand even alongside Package Manager. Packages already in the project are left as they are, apart from an External Dependency Manager older than the SDK needs, which is upgraded.\n- `AdaptyErrorCode.NoPurchasesToRestore` (1004) — restored. The member was commented out in December 2024 while the native Android SDK kept sending the code, so `RestorePurchases` on a profile with nothing to restore returned an error that could only be matched against a literal `1004`. Nothing about the error changes; it now has its name back. The code is Android-only — iOS does not define it.\n- Seven more `AdaptyErrorCode` members the native SDKs declare but this enum never named: `UnidentifiedUserLogout` (3020, both platforms, from `Logout` on an unidentified profile), `PaymentPendingError` (1050, iOS), `BillingNetworkError` (112, Android), and `WrongAssetType` (4104), `JsException` (4105), `NavigatorNotFound` (4106), `InvalidActionUrl` (4107) — the four the Android flow renderer reports through `FlowViewDidReceiveError`. `AdaptyErrorCode` carries the native number, so these codes always arrived; they simply had no constant to match against. Each one was traced in the iOS 4.0.2 and Android 4.0.1 sources to the place the native SDK produces it — a throw site for all but 112, which comes out of the `fromBilling` mapping. `PaymentPendingError` is the one exception to \"already arrived\", and is named for completeness rather than to be handled: its only throw site is an iOS overload taking StoreKit's own purchase result, which the Unity bridge never calls. A pending purchase made through the SDK arrives as `AdaptyPurchaseResultType.Pending`.\n\n### Changed\n\n- **The minimum supported Unity version is now declared: 2022.3.** It was never stated before, in `package.json` or anywhere else, so Package Manager let any Editor install a package it might not be able to compile. Nothing was dropped — the floor is now stated. Installing on the floor is verified: a clean `.unitypackage` import and **Adapty SDK > Install Dependencies** were run end to end on 2022.3, and the SDK compiles afterwards. Player builds, device runs and the rest of the acceptance matrix were done on Unity 6, which is what the SDK is developed against.\n- **The JSON layer now uses Newtonsoft.Json instead of the bundled SimpleJSON.** The package depends on `com.unity.nuget.newtonsoft-json` 3.2.2, which Package Manager installs for you and **Adapty SDK > Install Dependencies** installs for everyone else. While Newtonsoft is absent the SDK assembly is skipped by a define constraint instead of failing to compile. The SDK reports the reason in the Editor console — as long as its Editor assembly loads, which it does not while your own scripts fail to compile. A second copy of Newtonsoft is reported the same way, since it makes its types ambiguous. The models, their members and every method signature are unchanged, so calling code is unaffected. **Breaking for anything that used `AdaptySDK.SimpleJSON` directly:** the namespace is gone, and its public types (`JSON`, `JSONNode`, `JSONObject`, `JSONArray` and the rest) went with it.\n- **Breaking:** migrated the paywall API to flows — `AdaptyPaywall` → `AdaptyFlow`, `GetPaywall` → `GetFlow`, `CreatePaywallView` → `CreateFlowView`, with the corresponding models, events and view controllers. Event listener interfaces now carry the `I` prefix.\n- The legacy onboarding API is deprecated in favor of flows, and `[Obsolete]` now covers the whole of it rather than only its entry points: `IAdaptyOnboardingsEventsListener`, `AdaptyOnboarding`, `AdaptyUIOnboardingView`, `AdaptyUIOnboardingMeta`, the `AdaptyOnboardingsAnalyticsEvent`, `AdaptyOnboardingsStateUpdatedParams` and `AdaptyOnboardingsInput` hierarchies, and the `AdaptyUI.ShowDialog` overload taking an `AdaptyUIOnboardingView`.\n- **Breaking:** `Adapty.GetLoglevel` is spelled `Adapty.GetLogLevel`. The typo was in the v3 surface too, out of step with its own `SetLogLevel` and with `get_log_level`, the operation the cross-platform contract names. Same signature, same wire method.\n- **Breaking:** removed two members that only forwarded to another one — `AdaptyProfile.NonSubscription.IsOneTime` (returned `IsConsumable` unchanged) and `AdaptyPlacement.GetIsTrackingPurchases` (wrapped the public `IsTrackingPurchases` field, whose `null` case cannot occur).\n- **Breaking:** a string the contract does not list now fails the read instead of degrading to `Unknown`, and the six members that existed only to catch one are gone: `AdaptyPurchaseResultType.Unknown`, `AdaptySubscriptionOfferType.Unknown`, `AdaptySubscriptionRenewalType.Unknown`, `AdaptyUIDialogActionType.Unknown`, `AdaptyUIUserActionType.Unknown` and `AdaptyWebPresentation.Unknown`. The SDK ships pinned to the native SDKs it is built against, so an unlisted value is a broken payload rather than one from the future. `AdaptyPaymentMode.Unknown` and `AdaptySubscriptionPeriodUnit.Unknown` stay, because the contract lists `\"unknown\"` among their values. No surviving member changed its numeric value, and a JSON number is no longer accepted for a string enum.\n- **iOS builds now require Xcode 26 or newer.** AdaptySDK-iOS 4.0 declares `swift-tools-version: 6.2`, where the 3.17.2 that v3 pinned declared 6.0, and Swift Package Manager refuses a package whose tools version is newer than the installed toolchain. On Xcode 16 the build fails while resolving the dependency, before anything is compiled. This is the floor for the whole SDK, not only for Kids Mode. Nothing in Unity can check it — the Editor never sees which Xcode will open the generated project.\n- Updated the cross-platform contract to 4.0.2 and the native SDK dependencies to iOS 4.0.2 and Android 4.0.1.\n- Errors returned by `flow_view_did_answer_permission` and by the observer-mode round trips are now logged instead of being swallowed.\n- **Breaking:** removed `AdaptyErrorCode.InvalidJson` (23) and `AdaptyErrorCode.PendingPurchase` (25). Neither native SDK declares these codes any more, so nothing can raise them; a pending purchase is reported as `AdaptyPurchaseResultType.Pending`.\n- **Breaking:** removed what the old JSON layer left behind — `AdaptyRefundPreferenceExtensions.ToJSONNode`, the last of the `ToJSONNode` extension classes, which the SDK does not call because the refund preference is serialized through its `[EnumMember]` mapping, and two constructors that only the hand-written parser ever called.\n- **Breaking:** the collections on a response model are read-only. `AdaptyProfile`, `AdaptyFlow`, `AdaptyFlowPaywall`, `AdaptySubscriptionOffer` and `AdaptyRemoteConfig` hand back `IReadOnlyList` and `IReadOnlyDictionary`, and `AdaptyProfile.NonSubscriptions` is read-only at both levels. A `readonly` field never made these models immutable: the reference could not be replaced, but the contents could. `GetPaywallProducts` reports an `IReadOnlyList` for the same reason. The deprecated onboarding API is the exception and keeps its old shapes — it is maintained rather than improved until it is removed.\n- **Breaking:** the parameter objects take the narrowest abstraction and copy it. `AdaptyUICreateFlowViewParameters.SetCustomTags`, `SetCustomTimers`, `SetCustomAssets` and `SetProductPurchaseParameters` accept an `IReadOnlyDictionary` and copy it; the four matching members are read-only properties rather than public fields. `UpdateAttribution` takes an `IReadOnlyDictionary`, and `AdaptyProfileParameters.CustomAttributes` exposes a view.\n- **Breaking, at the call site only:** the analytics-event parameter of `IAdaptyFlowsEventsListener.FlowViewDidReceiveAnalyticEvent` is named `parameters` rather than `@params`. The CLR signature is unchanged, so only a call passing it as a named argument has to be renamed.\n- **Breaking:** `IAdaptyFlowsEventsListener.FlowViewDidReceiveAnalyticEvent` and `IAdaptyUISystemRequestsHandler.FlowViewDidAskPermission` receive `IReadOnlyDictionary` instead of `IDictionary`. Implementations need the signature updated.\n- **Breaking:** `AdaptyPlacementFetchPolicy.Default`, `.ReloadRevalidatingCacheData` and `.ReturnCacheDataElseLoad` are `readonly`; they were public mutable statics.\n- Registered listeners no longer survive Play Mode. With Domain Reload disabled Unity keeps static fields between runs, so the listeners a previous run registered were still there for the next one. The SDK clears them at `RuntimeInitializeLoadType.SubsystemRegistration`.\n- **Breaking:** every concrete public class is now `sealed`; the four abstract roots the wire contract needs — `AdaptyCustomAsset` and the three legacy onboarding hierarchies — stay open. For most of them this states what was already true: a response model has no constructor reachable from outside the SDK — private, or `internal` as on `AdaptySubscriptionOffer` — so no type of yours could derive from one in the first place. Eleven could, all of them inputs rather than responses — the parameter objects, the two identity types, the three builders — and for those this is a real restriction. Nothing was designed for extension: no model declares a `virtual` or `protected` member.\n- **Breaking:** `AdaptyInstallationStatus` is one sealed type carrying a `Status` and a `Details`, instead of a base class and the three subclasses `AdaptyInstallationStatusNotAvailable`, `AdaptyInstallationStatusNotDetermined` and `AdaptyInstallationStatusDetermined`, which are gone along with their public constructors. The new `AdaptyInstallationStatusType` names the same three states the contract lists, so a caller switches on a value rather than testing for a type. `Details` is non-null exactly when `Status` is `Determined`. Nothing about the wire format changes.\n- **Breaking:** `AdaptyFlowPaywall.ProductReference` is now `internal`. Its constructor was private and every one of its members was already `internal`, so no instance of it could be obtained or read from outside the SDK.\n\n### Fixed\n\n- The `respond` delegate of `IAdaptyUISystemRequestsHandler.FlowViewDidAskPermission` and the report callbacks of `IAdaptyUIObserverModeResolver` are safe to invoke from any thread: the SDK now sends the request they produce from the Unity main thread. On Android the bridge is JNI, which a C# worker thread cannot enter on Unity 2022.3 (measured on 2022.3.62f3), so answering a permission request from one threw and left the flow waiting for an answer that never went out; Unity 6 attaches the thread on demand and happened to work. iOS was unaffected.\n- Requests call back on a device even when the app never sets an event listener. The platform callback bridge was registered by the four listener setters and by nothing else, so an app that subscribes to no events got no completion handler called at all — `Activate` included — on either platform, and every iOS request additionally leaked the handle meant to carry its reply. It is now registered at player startup, before the first scene loads, so every call made from the MonoBehaviour lifecycle onwards is covered. Present since 3.x.\n- An exception thrown by the completion handler passed to the deprecated `Adapty.GetOnboarding` now arrives with the name of the call that raised it, and the original as `InnerException`, the way every other Adapty call already reported one. Only `GetOnboarding` was affected.\n- `AdaptyProfileParameters.SetBirthday` now sends the date the contract asks for. The key is declared `YYYY-MM-dd` and was built by hand from the parts, without padding, so 7 March 1990 went out as `1990-3-7`. Every birthday whose month or day is below the tenth was affected, on every platform, since 3.x.\n- An offer whose branch of the contract requires an `id` is now rejected without one, at the point it is read: promotional and win-back everywhere, and introductory on Android. The converter read the identifier leniently for every branch, so a missing one became a null that `NullValueHandling` dropped from the purchase request, leaving the native side to fail the decode instead.\n- A subscription offer without `phases` is now rejected instead of being handed over half built. The contract requires the key, and the converter enforced `offer_identifier` and `type` but not this one, so a payload without it produced an offer whose phases were null. Neither native SDK can currently send such a payload.\n- Dates reach your code as local time again, as they did in 3.x. The wire is UTC and the public API is local — a subscription's expiry compares against `DateTime.Now` — but the payload from the native side was being turned into a document by a reader that recognises dates while it builds the tree, so every date arrived as `DateTimeKind.Utc`. Both the event callbacks and the reply to every method were affected; only 4.0.0-beta.1 ever behaved that way. Call `ToUniversalTime()` where you need the instant.\n- A date-looking string inside an untyped payload survives as it was sent. The same reader turned `params` of `FlowViewDidReceiveAnalyticEvent` into dates and back into strings, so `\"2026-07-30T10:00:00.000Z\"` reached the listener as `\"07/30/2026 10:00:00\"`.\n- `AdaptyPurchaseResult.ToString()` no longer throws. The contract carries `profile` in the success branch only, and the method dereferenced it unconditionally, so describing a pending or cancelled purchase raised a `NullReferenceException`.\n- `ReportTransaction` no longer reports `DecodingFailed` for transactions that were in fact reported successfully.\n- **The server cluster selected through the configuration builder now reaches the native SDK.** `ServerCluster` was the one builder field `AdaptyConfiguration`'s constructor did not copy, so `server_cluster` was never sent and every app ran against the default cluster whatever it chose. Selecting EU or CN did nothing in v3 and takes effect now, so an app that selected one starts talking to that region on upgrade. The builder field is `AdaptyServerCluster?` rather than `AdaptyServerCluster`, which keeps an unset cluster out of the request.\n- `AdaptyPlacementFetchPolicy.Default` is no longer null. It aliases `ReloadRevalidatingCacheData` but was declared above it, and static field initializers run in declaration order, so passing `Default` explicitly raised a `NullReferenceException` when the request was built.\n- `UpdateAttribution` sends `bool` and `DateTime` values instead of dropping them. The dictionary serializer had no branch for either, so an attribution carrying one went out without it.\n- `AdaptyCustomerIdentity.IsEmpty` reports an empty identity. `IosAppAccountToken` is a non-nullable `Guid`, so comparing it to null was always false and the property never returned true, which left the guard that keeps an empty identity out of the configuration dead.\n- A partially filled date in an onboarding `date_picker` event no longer throws. The helpers reading the optional `day`, `month` and `year` cast the nullable they received straight to `int`, which raises `InvalidOperationException` when the key is absent.\n- Custom linear gradient assets are now serialized from every color and alpha key of the Unity `Gradient`.\n- `AdaptyProductIdentifier` now implements value equality, so identifiers built from a flow work as keys in the dictionary passed to `AdaptyUICreateFlowViewParameters.SetProductPurchaseParameters`. An empty base plan id is now the same as none, at construction, so two identifiers that always went on the wire identically are equal and hash alike.\n- `AdaptyFlow.VendorProductIds` and `AdaptyFlow.ProductIdentifiers` no longer return duplicates across paywall variations.\n- Calling the SDK in the Editor now returns a readable \"not supported on this platform\" error. That holds for the whole surface now: `UpdateAppStoreCollectingRefundDataConsent`, `UpdateAppStoreRefundPreference` and `PresentCodeRedemptionSheet` were guarded so that the Editor took their off-iOS branch, which reports a null error — indistinguishable from success — so testing them in the Editor looked like they had worked. On an Android device they still report `null`, which is unchanged.\n- `UpdateAttribution` reports an attribution graph it cannot encode through the completion handler, as `EncodingFailed`, instead of throwing at the call site. The overload taking a dictionary is the only public method that has to encode an argument before it can build a request, so it was the only one whose failure escaped the transport's guard.\n- **Adapty SDK > Install Dependencies** stops on two loaded copies of Newtonsoft.Json, which is the state the SDK's own validator already reports as an error. It examined the first copy only, and the order loaded assemblies come back in is not specified, so the same project could be told its dependencies were complete on one run and be sent to fix them on the next.\n- **Adapty SDK > Install Dependencies** upgrades an External Dependency Manager older than the SDK needs, instead of reporting the project complete. It checked only that a copy was loaded, so a project coming from v3 — which declared 1.2.187 — kept it, and the iOS build resolved through a version that gets the Xcode project path wrong for the Swift project type. A copy installed from Google's own `.unitypackage` under `Assets/` has no version Package Manager can read, so it is reported rather than replaced.\n- `AdaptyConfiguration.Builder.ToString()` includes `GoogleEnablePendingPrepaidPlans`, the one member missing from the description.\n\n### Known issues\n\n- **Custom color and linear gradient assets are not rendered on iOS.** The pinned AdaptySDK-iOS 4.0.2 discards the values it receives and substitutes a transparent color and an empty gradient. Nothing on the Unity side is involved, and the same substitution is in the later 4.0.3 and 4.1.0 native releases, so there is no version to move the pin to. Custom image and video assets are unaffected. Whether Android is affected has not been established." + "changelog": "Upgrading from 4.0: see [MIGRATION-v4.0-to-v4.1.md](https://github.com/adaptyteam/AdaptySDK-Unity/blob/4.1.0/MIGRATION-v4.0-to-v4.1.md).\n\n### Breaking Changes\n\n- `Adapty.UpdateAttribution(...)` is renamed to `Adapty.UpdateExternalAttribution(...)`, with the `source` parameter renamed to `provider`, matching the native 4.1 attribution API rename. No deprecated alias.\n- `AdaptyProfile.AppliedAttributionSources` is renamed to `AdaptyProfile.AppliedExternalAttributionProviders`. The wire key is unchanged.\n- `IAdaptyEventListener` gained `OnReceivePromotedPurchase(AdaptyPromotedProduct)`; implementations must add the method.\n- Adapty Attribution is opt-in, and installation details come with it. The 4.1 natives collect installation details only when the configuration asks them to, where the 4.0 natives collected them unconditionally: `IAdaptyEventListener.OnInstallationDetailsSuccess` stops firing and `Adapty.GetCurrentInstallationStatus` stops reporting `Determined` until the app activates with `AdaptyConfiguration.Builder.SetAdaptyAttributionEnabled(true)`. Both platforms behave this way.\n- The 4.1 natives read fallback file format 11 and reject the format 10 files v4.0 shipped against, on both platforms: `Adapty.SetFallback` reports `DecodingFailed` (`adapty_code: 2006`) on iOS and `WrongParam` (`adapty_code: 3001`) on Android. Re-download both fallback files from the Adapty Dashboard.\n\n### Added\n\n- `AdaptyConfiguration.Builder.SetAdaptyAttributionEnabled(bool)` — enables the Adapty Attribution service, and with it the installation details.\n- `Adapty.MakePromotedPurchase(...)` and `IAdaptyEventListener.OnReceivePromotedPurchase` — App Store promoted in-app purchases (iOS only). At native iOS 4.1.0 the event is not yet emitted, and the exact pin keeps it that way; the listener method starts firing only once a future SDK release moves the pin to a native that reports them.\n- `AdaptyFlow` carries the 4.1 `ui_schema` (custom flow layouts, UIBuilder 5.1) through to the renderer; not public API.\n\n### Native and Protocol\n\n- The native dependencies are pinned to AdaptySDK-iOS 4.1.0, which includes the 4.0.3 hotfix, and on Android to crossplatform 4.1.0 / android-sdk 4.1.0 / android-ui 4.1.0, with the bundled unity-wrapper AAR rebuilt at 4.1.0.\n- [Android] `Adapty.RestorePurchases` no longer reports `NoPurchasesToRestore` (1004): the 4.1 native completes with the current profile when there is nothing to restore. iOS never produced the code; the `AdaptyErrorCode` member stays.\n- The cross-platform contract is 4.1.0: `update_external_attribution_data`, `make_promoted_purchase`, `did_receive_promoted_purchase`, `adapty_attribution_enabled` and `ui_schema` are new, and the offer identifier of a product request now travels nested as `subscription.offer.offer_identifier` — the flat form is no longer written." }, "dependencies": { "com.unity.nuget.newtonsoft-json": "3.2.2" diff --git a/README.md b/README.md index a6dd910..a1ee135 100755 --- a/README.md +++ b/README.md @@ -24,7 +24,7 @@ Adapty Unity SDK is a native wrapper around [Adapty iOS SDK](https://github.com/adaptyteam/AdaptySDK-iOS) and [Adapty Android SDK](https://github.com/adaptyteam/AdaptySDK-Android). Both SDKs are written in pure Swift/Kotlin, all wrapped into a C# lib. Requires Unity 2022.3 or newer and Android API 21 or newer. iOS builds require **Xcode 26 or newer** -and a deployment target of 15.0 or newer: AdaptySDK-iOS 4.0 is a `swift-tools-version: 6.2` package, +and a deployment target of 15.0 or newer: AdaptySDK-iOS 4.x is a `swift-tools-version: 6.2` package, and an older toolchain refuses to resolve it. Open the exported project as **`Unity-iPhone.xcworkspace`** — building `Unity-iPhone.xcodeproj` directly fails at link time with `ld: framework 'Pods_UnityFramework' not found`. @@ -95,10 +95,10 @@ so start new integrations on flows. does not sit at the repository root — and the version suffix, which pins the tag: ``` -https://github.com/adaptyteam/AdaptySDK-Unity.git?path=/Packages/com.adapty.unity-sdk#4.0.0-beta.2 +https://github.com/adaptyteam/AdaptySDK-Unity.git?path=/Packages/com.adapty.unity-sdk#4.1.0 ``` -Drop `#4.0.0-beta.2` and Package Manager resolves the default branch, which carries the previous +Drop `#4.1.0` and Package Manager resolves the default branch, which carries the previous major until a release merges this one into it — so an unpinned URL installs 3.17 and says nothing about it. @@ -132,6 +132,10 @@ Manager can read, so the menu item leaves it alone and warns instead; update tha Already on 3.x? [MIGRATION-v3.17-to-v4.0.md](MIGRATION-v3.17-to-v4.0.md) covers the move to 4.0 — the renamed paywall API, the new Newtonsoft.Json dependency, and the order to install things in. +Coming from 4.0? [MIGRATION-v4.0-to-v4.1.md](MIGRATION-v4.0-to-v4.1.md) covers the move to 4.1 — +the renamed attribution API, the new promoted-purchase listener method, and the fallback file the +native iOS 4.1 expects you to re-download. + Read the [release notes and known issues](Packages/com.adapty.unity-sdk/CHANGELOG.md) before you integrate: they carry the limitations of the pinned native SDKs, which no amount of configuration on your side will work around. @@ -184,7 +188,7 @@ version it was found in. - **Custom color and linear gradient assets are not rendered on iOS.** An asset built with `AdaptyCustomAsset.Color` or `AdaptyCustomAsset.LinearGradient` and passed through `AdaptyUICreateFlowViewParameters.SetCustomAssets` reaches the view as a transparent color and an - empty gradient: the pinned AdaptySDK-iOS 4.0.2 substitutes those for whatever it receives, and so - do 4.0.3 and 4.1.0, so there is no version to move the pin to. Custom image and video assets are - unaffected; whether Android is affected has not been established. Waiting on a native iOS + empty gradient: the pinned AdaptySDK-iOS 4.1.0 substitutes those for whatever it receives, exactly + as 4.0.2 and 4.0.3 did, so there is no version to move the pin to. Custom image and video assets + are unaffected; whether Android is affected has not been established. Waiting on a native iOS release, and on iOS acceptance after it. diff --git a/adaptyandroidwrapper/unitywrapper/build.gradle b/adaptyandroidwrapper/unitywrapper/build.gradle index 5a42ef3..a57eb49 100644 --- a/adaptyandroidwrapper/unitywrapper/build.gradle +++ b/adaptyandroidwrapper/unitywrapper/build.gradle @@ -34,8 +34,8 @@ dependencies { testImplementation 'junit:junit:4.13.2' androidTestImplementation 'androidx.test.ext:junit:1.1.3' androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0' - compileOnly 'io.adapty:android-sdk:4.0.1' - compileOnly 'io.adapty.internal:crossplatform:4.0.2' + compileOnly 'io.adapty:android-sdk:4.1.0' + compileOnly 'io.adapty.internal:crossplatform:4.1.0' compileOnly 'org.jetbrains.kotlin:kotlin-stdlib:1.4.32' compileOnly 'com.google.code.gson:gson:2.10' } diff --git a/cross_platform.yaml b/cross_platform.yaml index b38e9c0..28b3c18 100644 --- a/cross_platform.yaml +++ b/cross_platform.yaml @@ -1,5 +1,5 @@ $schema: "https://json-schema.org/draft/2020-12/schema" -$id: "https://adapty.io/crossPlatform/4.0.2/schema" +$id: "https://adapty.io/crossPlatform/4.1.0/schema" title: "Cross Platform Format" $requests: @@ -176,7 +176,8 @@ $requests: properties: method: { const: "adapty_ui_open_url" } url: { type: string } - open_in: { $ref: "#/$defs/AdaptyWebPresentation", default: "browser_out_app" } + open_in: + { $ref: "#/$defs/AdaptyWebPresentation", default: "browser_out_app" } AdaptyUIOpenUrl.Response: #response type: object @@ -331,7 +332,7 @@ $requests: properties: method: { const: "identify" } customer_user_id: { type: string } - parameters: { $ref: "#/$defs/CustomerIdentityParameters" } + parameters: { $ref: "#/$defs/CustomerIdentityParameters" } Identify.Response: #response type: object @@ -441,6 +442,24 @@ $requests: properties: success: { $ref: "#/$defs/AdaptyPurchaseResult" } + ### makePromotedPurchase ### + MakePromotedPurchase.Request: #request + type: object + required: [method, product] + properties: + method: { const: "make_promoted_purchase" } + product: { $ref: "#/$defs/AdaptyPromotedProduct.Request" } + + MakePromotedPurchase.Response: #response + type: object + oneOf: + - required: [error] + properties: + error: { $ref: "#/$defs/AdaptyError" } + - required: [success] + properties: + success: { $ref: "#/$defs/AdaptyPurchaseResult" } + OpenWebPaywall.Request: #request type: object required: [method] @@ -451,7 +470,8 @@ $requests: method: { const: "open_web_paywall" } product: { $ref: "#/$defs/AdaptyPaywallProduct.Request" } paywall: { $ref: "#/$defs/AdaptyFlowPaywall" } - open_in: { $ref: "#/$defs/AdaptyWebPresentation", default: "browser_out_app" } + open_in: + { $ref: "#/$defs/AdaptyWebPresentation", default: "browser_out_app" } OpenWebPaywall.Response: #response type: object @@ -609,16 +629,16 @@ $requests: properties: success: { const: true } - ### updateAttributionData ### - UpdateAttributionData.Request: #request + ### updateExternalAttributionData ### + UpdateExternalAttributionData.Request: #request type: object - required: [method, attribution, source] + required: [method, attribution, provider] properties: - method: { const: "update_attribution_data" } + method: { const: "update_external_attribution_data" } attribution: { type: string } - source: { type: string } + provider: { type: string } - UpdateAttributionData.Response: #response + UpdateExternalAttributionData.Response: #response type: object oneOf: - required: [error] @@ -827,6 +847,13 @@ $events: id: { const: "did_load_latest_profile" } profile: { $ref: "#/$defs/AdaptyProfile" } + Event.DidReceivePromotedPurchase: + type: object + required: [id, product] + properties: + id: { const: "did_receive_promoted_purchase" } + product: { $ref: "#/$defs/AdaptyPromotedProduct.Response" } + Event.OnInstallationDetailsSuccess: type: object required: [id, details] @@ -1100,13 +1127,15 @@ $defs: properties: api_key: { type: string } customer_user_id: { type: string } - customer_identity_parameters: { $ref: "#/$defs/CustomerIdentityParameters" } + customer_identity_parameters: + { $ref: "#/$defs/CustomerIdentityParameters" } observer_mode: { type: boolean, default: false } apple_idfa_collection_disabled: { type: boolean, default: false } google_adid_collection_disabled: { type: boolean, default: false } google_enable_pending_prepaid_plans: { type: boolean, default: false } google_local_access_level_allowed: { type: boolean, default: false } ip_address_collection_disabled: { type: boolean, default: false } + adapty_attribution_enabled: { type: boolean, default: false } clear_data_on_backup: { type: boolean, default: false } server_cluster: { type: string, enum: ["default", "eu", "cn"] } backend_proxy_host: { type: string } @@ -1128,7 +1157,7 @@ $defs: properties: app_account_token: { $ref: "#/$defs/UUID", description: "iOS Only" } obfuscated_account_id: { type: string, description: "Android Only" } - obfuscated_profile_id: { type: string, description: "Android Only" } + obfuscated_profile_id: { type: string, description: "Android Only" } AdaptyPaywallProduct.Response: #response type: object @@ -1162,7 +1191,7 @@ $defs: { type: boolean, default: false, description: "iOS Only" } region_code: { type: string } price: { $ref: "#/$defs/AdaptyPrice" } - subscription: { $ref: "#/$defs/AdaptyPaywallProduct.Subscription" } + subscription: { $ref: "#/$defs/AdaptyProduct.Subscription" } payload_data: { type: string } AdaptyPrice: #response @@ -1174,7 +1203,43 @@ $defs: currency_symbol: { type: string } localized_string: { type: string } - AdaptyPaywallProduct.Subscription: #response + AdaptyPromotedProduct.Request: #request + type: object + required: + - vendor_product_id + properties: + vendor_product_id: { type: string } + subscription: + type: object + properties: + offer: + type: object + required: [offer_identifier] + properties: + offer_identifier: + { $ref: "#/$defs/AdaptySubscriptionOffer.Identifier" } + payload_data: { type: string } + + AdaptyPromotedProduct.Response: #event + type: object + required: + - vendor_product_id + - localized_description + - localized_title + - is_family_shareable # iOS Only + - price + properties: + vendor_product_id: { type: string } + localized_description: { type: string } + localized_title: { type: string } + is_family_shareable: + { type: boolean, default: false, description: "iOS Only" } + region_code: { type: string } + price: { $ref: "#/$defs/AdaptyPrice" } + subscription: { $ref: "#/$defs/AdaptyProduct.Subscription" } + payload_data: { type: string } + + AdaptyProduct.Subscription: #event #response type: object required: - group_identifier # iOS Only @@ -1212,8 +1277,15 @@ $defs: access_level_id: { type: string } product_type: { type: string } paywall_product_index: { type: integer } - subscription_offer_identifier: - { $ref: "#/$defs/AdaptySubscriptionOffer.Identifier" } + subscription: + type: object + properties: + offer: + type: object + required: [offer_identifier] + properties: + offer_identifier: + { $ref: "#/$defs/AdaptySubscriptionOffer.Identifier" } paywall_variation_id: { type: string } paywall_ab_test_name: { type: string } paywall_name: { type: string } @@ -1289,13 +1361,55 @@ $defs: flow_id: { type: string } flow_name: { type: string } variation_id: { type: string } - remote_configs: + remote_configs: type: array items: { $ref: "#/$defs/AdaptyRemoteConfig" } flow_version_id: { type: string } variations: type: array items: { $ref: "#/$defs/AdaptyFlowPaywall" } + ui_schema: + type: object + required: [grids, layouts] + properties: + layouts: + type: array + items: + type: object + required: [flow_layout_id] + properties: + flow_layout_id: + type: string + grids: + type: array + items: + type: object + required: [cells] + properties: + platforms: + oneOf: + - const: "all" + - type: array + items: { enum: ["ios", "android"] } + default: [] + devices: + oneOf: + - const: "all" + - type: array + items: { enum: ["phone", "tab"] } + default: [] + custom_id: { type: string } + h_breakpoints: + type: array + items: { type: integer } + default: [] + v_breakpoints: + type: array + items: { type: integer } + default: [] + cells: + type: array + items: { type: integer } payload_data: { type: string } response_created_at: { type: integer } @@ -1583,7 +1697,6 @@ $defs: apple_jws_transaction: { type: string } google_purchase_token: { type: string } - AdaptyInstallationStatus: #response #event type: object oneOf: @@ -1597,12 +1710,12 @@ $defs: AdaptyInstallationDetails: #response #event type: object - required: [ install_time, app_launch_count] + required: [install_time, app_launch_count] properties: - install_id: { type: string } + install_id: { type: string } install_time: { $ref: "#/$defs/Date", nullable: false } app_launch_count: { type: integer, nullable: false } - payload: { type: string } + payload: { type: string } AdaptyUI.CustomAssets: type: array @@ -1657,9 +1770,10 @@ $defs: value: { type: string } AdaptyUI.Permission: #event - description: "Permission identifier requested by a flow. The values below are the + description: + 'Permission identifier requested by a flow. The values below are the known cross-platform set; any string is valid — unknown or Android-only ids - (e.g. \"phone\", \"sms\") and future values pass through unchanged." + (e.g. "phone", "sms") and future values pass through unchanged.' anyOf: - enum: - push @@ -1693,13 +1807,11 @@ $defs: type: object additionalProperties: { $ref: "#/$defs/Date", nullable: false } - AdaptyUI.ProductPurchaseParameters: #request + AdaptyUI.ProductPurchaseParameters: #request type: object propertyNames: { description: "key is adapty_product_id" } - additionalProperties: { - $ref: "#/$defs/AdaptyPurchaseParameters", - nullable: false - } + additionalProperties: + { $ref: "#/$defs/AdaptyPurchaseParameters", nullable: false } AdaptyUI.IOSPresentationStyle: type: string @@ -1727,8 +1839,8 @@ $defs: enum: - primary - secondary - - AdaptyPurchaseParameters: #request + + AdaptyPurchaseParameters: #request type: object properties: subscription_update_params: @@ -1737,7 +1849,6 @@ $defs: description: "Android Only", } is_offer_personalized: { type: boolean, description: "Android Only" } - AdaptySubscriptionUpdateParameters: #request # Android Only type: object diff --git a/tests/AdaptySDK.NextTests/AotContractTests.cs b/tests/AdaptySDK.NextTests/AotContractTests.cs index bf32af4..667c063 100644 --- a/tests/AdaptySDK.NextTests/AotContractTests.cs +++ b/tests/AdaptySDK.NextTests/AotContractTests.cs @@ -55,7 +55,7 @@ public void TheGuardStillFindsTheModelCollections() Assert.That( found, Has.Some.EqualTo(typeof(List)), - "AdaptyProfile.AppliedAttributionSources is no longer seen" + "AdaptyProfile.AppliedExternalAttributionProviders is no longer seen" ); // The models now hand out read-only views over concrete storage, so the interface diff --git a/tests/AdaptySDK.NextTests/ContractEnforcementTests.cs b/tests/AdaptySDK.NextTests/ContractEnforcementTests.cs index 2c284a6..c143d13 100644 --- a/tests/AdaptySDK.NextTests/ContractEnforcementTests.cs +++ b/tests/AdaptySDK.NextTests/ContractEnforcementTests.cs @@ -441,7 +441,7 @@ public void AResponseModelCannotBeMutatedThroughItsCollections() Throws.InstanceOf() ); Assert.That( - () => ((IList)profile.AppliedAttributionSources).Add("x"), + () => ((IList)profile.AppliedExternalAttributionProviders).Add("x"), Throws.InstanceOf() ); diff --git a/tests/AdaptySDK.NextTests/EventDispatchTests.cs b/tests/AdaptySDK.NextTests/EventDispatchTests.cs index 3c804ee..f91abb2 100644 --- a/tests/AdaptySDK.NextTests/EventDispatchTests.cs +++ b/tests/AdaptySDK.NextTests/EventDispatchTests.cs @@ -47,6 +47,21 @@ public void ProfileUpdatesReachTheListener() Assert.That(_listener.Profile.ProfileId, Is.Not.Null); } + [Test] + public void PromotedPurchasesReachTheListener() + { + Adapty.OnMessage( + "did_receive_promoted_purchase", + "{\"product\":" + Snapshots.LoadResponse("promoted-product-full") + "}" + ); + + Assert.That(_listener.PromotedProduct, Is.Not.Null); + Assert.That( + _listener.PromotedProduct.VendorProductId, + Is.EqualTo("com.adapty.sample.monthly") + ); + } + /// /// The wire is UTC and the public API is local, on this path too: an event is a document /// parsed before anything typed is built out of it. @@ -396,6 +411,7 @@ AdaptyError error private sealed class Listener : IAdaptyEventListener { internal AdaptyProfile Profile; + internal AdaptyPromotedProduct PromotedProduct; internal bool Throw; public void OnLoadLatestProfile(AdaptyProfile profile) @@ -408,6 +424,9 @@ public void OnLoadLatestProfile(AdaptyProfile profile) Profile = profile; } + public void OnReceivePromotedPurchase(AdaptyPromotedProduct product) => + PromotedProduct = product; + public void OnInstallationDetailsSuccess(AdaptyInstallationDetails details) { } public void OnInstallationDetailsFail(AdaptyError error) { } diff --git a/tests/AdaptySDK.NextTests/ParityTests.cs b/tests/AdaptySDK.NextTests/ParityTests.cs index b07933c..361b759 100644 --- a/tests/AdaptySDK.NextTests/ParityTests.cs +++ b/tests/AdaptySDK.NextTests/ParityTests.cs @@ -46,6 +46,16 @@ public void Flow(string fixture) => ModelSnapshot.Render(AdaptyJson.Deserialize(Snapshots.LoadResponse(fixture))) ); + [TestCase("promoted-product-full")] + [TestCase("promoted-product-minimal")] + public void PromotedProduct(string fixture) => + Snapshots.Matches( + fixture, + ModelSnapshot.Render( + AdaptyJson.Deserialize(Snapshots.LoadResponse(fixture)) + ) + ); + [TestCase("onboarding-full")] [TestCase("onboarding-minimal")] public void Onboarding(string fixture) => diff --git a/tests/AdaptySDK.NextTests/PlayModeStateTests.cs b/tests/AdaptySDK.NextTests/PlayModeStateTests.cs index 93561e2..3e9da8a 100644 --- a/tests/AdaptySDK.NextTests/PlayModeStateTests.cs +++ b/tests/AdaptySDK.NextTests/PlayModeStateTests.cs @@ -125,6 +125,8 @@ private sealed class Listener : IAdaptyEventListener public void OnLoadLatestProfile(AdaptyProfile profile) => Calls += 1; + public void OnReceivePromotedPurchase(AdaptyPromotedProduct product) { } + public void OnInstallationDetailsSuccess(AdaptyInstallationDetails details) { } public void OnInstallationDetailsFail(AdaptyError error) { } diff --git a/tests/AdaptySDK.NextTests/RequestParityTests.cs b/tests/AdaptySDK.NextTests/RequestParityTests.cs index 97071e5..cbf9d80 100644 --- a/tests/AdaptySDK.NextTests/RequestParityTests.cs +++ b/tests/AdaptySDK.NextTests/RequestParityTests.cs @@ -171,6 +171,22 @@ public void PaywallProduct(string fixture, int index, string snapshot) ); } + [TestCase("promoted-product-full", "request-promoted-product-with-offer")] + [TestCase("promoted-product-minimal", "request-promoted-product-plain")] + public void PromotedProduct(string fixture, string snapshot) => + Snapshots.Matches( + snapshot, + Snapshots.Canonical( + AdaptyJson.Serialize( + new AdaptyPromotedProductRequest( + AdaptyJson.Deserialize( + Snapshots.LoadResponse(fixture) + ) + ) + ) + ) + ); + [Test] public void ProductIdentifier() => Snapshots.Matches( diff --git a/tests/AdaptySDK.NextTests/TransportTests.cs b/tests/AdaptySDK.NextTests/TransportTests.cs index 5e99054..d2f7c9c 100644 --- a/tests/AdaptySDK.NextTests/TransportTests.cs +++ b/tests/AdaptySDK.NextTests/TransportTests.cs @@ -105,9 +105,9 @@ public void CreateFlowViewFlattensItsOptionalParameters() } [Test] - public void UpdateAttributionSendsEveryValueKind() + public void UpdateExternalAttributionSendsEveryValueKind() { - Adapty.UpdateAttribution( + Adapty.UpdateExternalAttribution( new Dictionary { { "status", "organic" }, @@ -123,8 +123,8 @@ public void UpdateAttributionSendsEveryValueKind() _ => { } ); - Assert.That(_method, Is.EqualTo("update_attribution_data")); - Snapshots.Matches("transport-update-attribution", Snapshots.Canonical(_request)); + Assert.That(_method, Is.EqualTo("update_external_attribution_data")); + Snapshots.Matches("transport-update-external-attribution", Snapshots.Canonical(_request)); } /// @@ -133,7 +133,7 @@ public void UpdateAttributionSendsEveryValueKind() /// transport's guard and be thrown at the caller instead of reported to the handler. /// [Test] - public void UpdateAttributionReportsAGraphItCannotEncode() + public void UpdateExternalAttributionReportsAGraphItCannotEncode() { var loop = new Dictionary(); loop["self"] = loop; @@ -141,7 +141,7 @@ public void UpdateAttributionReportsAGraphItCannotEncode() AdaptyError reported = null; Assert.DoesNotThrow( - () => Adapty.UpdateAttribution(loop, "custom", error => reported = error) + () => Adapty.UpdateExternalAttribution(loop, "custom", error => reported = error) ); Assert.Multiple(() => @@ -181,6 +181,9 @@ private static IEnumerable Requests() var paywall = AdaptyJson .Deserialize(Snapshots.LoadResponse("flow-full")) .Paywalls[0]; + var promoted = AdaptyJson.Deserialize( + Snapshots.LoadResponse("promoted-product-full") + ); var flowView = AdaptyJson.Deserialize( "{\"id\":\"view-1\",\"placement_id\":\"onboarding\",\"variation_id\":\"variation-0001\"}" ); @@ -200,6 +203,11 @@ TestCaseData Case(string name, string method, Action send) => "make_purchase", () => Adapty.MakePurchase(withoutOffer, (_, __) => { }) ); + yield return Case( + "make-promoted-purchase", + "make_promoted_purchase", + () => Adapty.MakePromotedPurchase(promoted, (_, __) => { }) + ); yield return Case( "create-web-paywall-url-product", "create_web_paywall_url", diff --git a/tests/shared/Fixtures/approved/flow-full.android.approved.txt b/tests/shared/Fixtures/approved/flow-full.android.approved.txt index 72c2346..0c89615 100644 --- a/tests/shared/Fixtures/approved/flow-full.android.approved.txt +++ b/tests/shared/Fixtures/approved/flow-full.android.approved.txt @@ -257,5 +257,6 @@ "Locale": "es" } ], - "_ResponseCreatedAt": 1753876800000 + "_ResponseCreatedAt": 1753876800000, + "_UiSchema": "{\"layouts\":[{\"flow_layout_id\":\"layout-0001\"}],\"grids\":[{\"platforms\":\"all\",\"devices\":[\"phone\"],\"custom_id\":\"grid-main\",\"h_breakpoints\":[320,768],\"v_breakpoints\":[568],\"cells\":[0,1,2]},{\"cells\":[3]}]}" } diff --git a/tests/shared/Fixtures/approved/flow-full.editor.approved.txt b/tests/shared/Fixtures/approved/flow-full.editor.approved.txt index 4a59173..2ae2628 100644 --- a/tests/shared/Fixtures/approved/flow-full.editor.approved.txt +++ b/tests/shared/Fixtures/approved/flow-full.editor.approved.txt @@ -257,5 +257,6 @@ "Locale": "es" } ], - "_ResponseCreatedAt": 1753876800000 + "_ResponseCreatedAt": 1753876800000, + "_UiSchema": "{\"layouts\":[{\"flow_layout_id\":\"layout-0001\"}],\"grids\":[{\"platforms\":\"all\",\"devices\":[\"phone\"],\"custom_id\":\"grid-main\",\"h_breakpoints\":[320,768],\"v_breakpoints\":[568],\"cells\":[0,1,2]},{\"cells\":[3]}]}" } diff --git a/tests/shared/Fixtures/approved/flow-full.ios.approved.txt b/tests/shared/Fixtures/approved/flow-full.ios.approved.txt index 08b0fc5..866eb43 100644 --- a/tests/shared/Fixtures/approved/flow-full.ios.approved.txt +++ b/tests/shared/Fixtures/approved/flow-full.ios.approved.txt @@ -257,5 +257,6 @@ "Locale": "es" } ], - "_ResponseCreatedAt": 1753876800000 + "_ResponseCreatedAt": 1753876800000, + "_UiSchema": "{\"layouts\":[{\"flow_layout_id\":\"layout-0001\"}],\"grids\":[{\"platforms\":\"all\",\"devices\":[\"phone\"],\"custom_id\":\"grid-main\",\"h_breakpoints\":[320,768],\"v_breakpoints\":[568],\"cells\":[0,1,2]},{\"cells\":[3]}]}" } diff --git a/tests/shared/Fixtures/approved/flow-minimal.android.approved.txt b/tests/shared/Fixtures/approved/flow-minimal.android.approved.txt index 1090e2b..f8bb79c 100644 --- a/tests/shared/Fixtures/approved/flow-minimal.android.approved.txt +++ b/tests/shared/Fixtures/approved/flow-minimal.android.approved.txt @@ -21,5 +21,6 @@ "_PayloadData": null, "_Paywalls": [], "_RemoteConfigs": [], - "_ResponseCreatedAt": 1753876800000 + "_ResponseCreatedAt": 1753876800000, + "_UiSchema": null } diff --git a/tests/shared/Fixtures/approved/flow-minimal.editor.approved.txt b/tests/shared/Fixtures/approved/flow-minimal.editor.approved.txt index 1090e2b..f8bb79c 100644 --- a/tests/shared/Fixtures/approved/flow-minimal.editor.approved.txt +++ b/tests/shared/Fixtures/approved/flow-minimal.editor.approved.txt @@ -21,5 +21,6 @@ "_PayloadData": null, "_Paywalls": [], "_RemoteConfigs": [], - "_ResponseCreatedAt": 1753876800000 + "_ResponseCreatedAt": 1753876800000, + "_UiSchema": null } diff --git a/tests/shared/Fixtures/approved/flow-minimal.ios.approved.txt b/tests/shared/Fixtures/approved/flow-minimal.ios.approved.txt index 1090e2b..f8bb79c 100644 --- a/tests/shared/Fixtures/approved/flow-minimal.ios.approved.txt +++ b/tests/shared/Fixtures/approved/flow-minimal.ios.approved.txt @@ -21,5 +21,6 @@ "_PayloadData": null, "_Paywalls": [], "_RemoteConfigs": [], - "_ResponseCreatedAt": 1753876800000 + "_ResponseCreatedAt": 1753876800000, + "_UiSchema": null } diff --git a/tests/shared/Fixtures/approved/profile-full.android.approved.txt b/tests/shared/Fixtures/approved/profile-full.android.approved.txt index b13a98c..c4f6e4b 100644 --- a/tests/shared/Fixtures/approved/profile-full.android.approved.txt +++ b/tests/shared/Fixtures/approved/profile-full.android.approved.txt @@ -24,7 +24,7 @@ "WillRenew": true } }, - "AppliedAttributionSources (property)": [ + "AppliedExternalAttributionProviders (property)": [ "appsflyer", "adjust" ], @@ -102,7 +102,7 @@ "WillRenew": true } }, - "_AppliedAttributionSources": [ + "_AppliedExternalAttributionProviders": [ "appsflyer", "adjust" ], diff --git a/tests/shared/Fixtures/approved/profile-full.editor.approved.txt b/tests/shared/Fixtures/approved/profile-full.editor.approved.txt index b13a98c..c4f6e4b 100644 --- a/tests/shared/Fixtures/approved/profile-full.editor.approved.txt +++ b/tests/shared/Fixtures/approved/profile-full.editor.approved.txt @@ -24,7 +24,7 @@ "WillRenew": true } }, - "AppliedAttributionSources (property)": [ + "AppliedExternalAttributionProviders (property)": [ "appsflyer", "adjust" ], @@ -102,7 +102,7 @@ "WillRenew": true } }, - "_AppliedAttributionSources": [ + "_AppliedExternalAttributionProviders": [ "appsflyer", "adjust" ], diff --git a/tests/shared/Fixtures/approved/profile-full.ios.approved.txt b/tests/shared/Fixtures/approved/profile-full.ios.approved.txt index b13a98c..c4f6e4b 100644 --- a/tests/shared/Fixtures/approved/profile-full.ios.approved.txt +++ b/tests/shared/Fixtures/approved/profile-full.ios.approved.txt @@ -24,7 +24,7 @@ "WillRenew": true } }, - "AppliedAttributionSources (property)": [ + "AppliedExternalAttributionProviders (property)": [ "appsflyer", "adjust" ], @@ -102,7 +102,7 @@ "WillRenew": true } }, - "_AppliedAttributionSources": [ + "_AppliedExternalAttributionProviders": [ "appsflyer", "adjust" ], diff --git a/tests/shared/Fixtures/approved/profile-minimal.android.approved.txt b/tests/shared/Fixtures/approved/profile-minimal.android.approved.txt index baf32c7..91df8e7 100644 --- a/tests/shared/Fixtures/approved/profile-minimal.android.approved.txt +++ b/tests/shared/Fixtures/approved/profile-minimal.android.approved.txt @@ -1,7 +1,7 @@ { "$type": "AdaptyProfile", "AccessLevels (property)": {}, - "AppliedAttributionSources (property)": [], + "AppliedExternalAttributionProviders (property)": [], "CustomAttributes (property)": {}, "CustomerUserId": null, "IsTestUser": false, @@ -11,7 +11,7 @@ "Subscriptions (property)": {}, "Version": 1753876800000, "_AccessLevels": {}, - "_AppliedAttributionSources": [], + "_AppliedExternalAttributionProviders": [], "_CustomAttributes": {}, "_NonSubscriptions": {}, "_Subscriptions": {} diff --git a/tests/shared/Fixtures/approved/profile-minimal.editor.approved.txt b/tests/shared/Fixtures/approved/profile-minimal.editor.approved.txt index baf32c7..91df8e7 100644 --- a/tests/shared/Fixtures/approved/profile-minimal.editor.approved.txt +++ b/tests/shared/Fixtures/approved/profile-minimal.editor.approved.txt @@ -1,7 +1,7 @@ { "$type": "AdaptyProfile", "AccessLevels (property)": {}, - "AppliedAttributionSources (property)": [], + "AppliedExternalAttributionProviders (property)": [], "CustomAttributes (property)": {}, "CustomerUserId": null, "IsTestUser": false, @@ -11,7 +11,7 @@ "Subscriptions (property)": {}, "Version": 1753876800000, "_AccessLevels": {}, - "_AppliedAttributionSources": [], + "_AppliedExternalAttributionProviders": [], "_CustomAttributes": {}, "_NonSubscriptions": {}, "_Subscriptions": {} diff --git a/tests/shared/Fixtures/approved/profile-minimal.ios.approved.txt b/tests/shared/Fixtures/approved/profile-minimal.ios.approved.txt index baf32c7..91df8e7 100644 --- a/tests/shared/Fixtures/approved/profile-minimal.ios.approved.txt +++ b/tests/shared/Fixtures/approved/profile-minimal.ios.approved.txt @@ -1,7 +1,7 @@ { "$type": "AdaptyProfile", "AccessLevels (property)": {}, - "AppliedAttributionSources (property)": [], + "AppliedExternalAttributionProviders (property)": [], "CustomAttributes (property)": {}, "CustomerUserId": null, "IsTestUser": false, @@ -11,7 +11,7 @@ "Subscriptions (property)": {}, "Version": 1753876800000, "_AccessLevels": {}, - "_AppliedAttributionSources": [], + "_AppliedExternalAttributionProviders": [], "_CustomAttributes": {}, "_NonSubscriptions": {}, "_Subscriptions": {} diff --git a/tests/shared/Fixtures/approved/promoted-product-full.android.approved.txt b/tests/shared/Fixtures/approved/promoted-product-full.android.approved.txt new file mode 100644 index 0000000..48b90dd --- /dev/null +++ b/tests/shared/Fixtures/approved/promoted-product-full.android.approved.txt @@ -0,0 +1,55 @@ +{ + "$type": "AdaptyPromotedProduct", + "IsFamilyShareable": false, + "LocalizedDescription": "Full access, billed monthly", + "LocalizedTitle": "Monthly", + "Price": { + "$type": "AdaptyPrice", + "Amount": 9.99, + "CurrencyCode": "USD", + "CurrencySymbol": "$", + "LocalizedString": "$9.99" + }, + "RegionCode": "US", + "Subscription": { + "$type": "AdaptySubscription", + "BasePlanId": "base-plan-1", + "GroupIdentifier": null, + "LocalizedPeriod": "1 month", + "Offer": { + "$type": "AdaptySubscriptionOffer", + "Identifier": "promo-1", + "OfferTags": null, + "Phases": [ + { + "$type": "AdaptySubscriptionPhase", + "LocalizedNumberOfPeriods": "1 time", + "LocalizedSubscriptionPeriod": "1 week", + "NumberOfPeriods": 1, + "PaymentMode": "FreeTrial (2)", + "Price": { + "$type": "AdaptyPrice", + "Amount": 0, + "CurrencyCode": "USD", + "CurrencySymbol": "$", + "LocalizedString": "Free" + }, + "SubscriptionPeriod": { + "$type": "AdaptySubscriptionPeriod", + "NumberOfUnits": 1, + "Unit": "Week (1)" + } + } + ], + "Type": "Promotional (1)" + }, + "Period": { + "$type": "AdaptySubscriptionPeriod", + "NumberOfUnits": 1, + "Unit": "Month (2)" + }, + "RenewalType": "Autorenewable (1)" + }, + "VendorProductId": "com.adapty.sample.monthly", + "_PayloadData": "{\"custom\":\"promoted payload\"}" +} diff --git a/tests/shared/Fixtures/approved/promoted-product-full.editor.approved.txt b/tests/shared/Fixtures/approved/promoted-product-full.editor.approved.txt new file mode 100644 index 0000000..06d3c25 --- /dev/null +++ b/tests/shared/Fixtures/approved/promoted-product-full.editor.approved.txt @@ -0,0 +1,55 @@ +{ + "$type": "AdaptyPromotedProduct", + "IsFamilyShareable": false, + "LocalizedDescription": "Full access, billed monthly", + "LocalizedTitle": "Monthly", + "Price": { + "$type": "AdaptyPrice", + "Amount": 9.99, + "CurrencyCode": "USD", + "CurrencySymbol": "$", + "LocalizedString": "$9.99" + }, + "RegionCode": "US", + "Subscription": { + "$type": "AdaptySubscription", + "BasePlanId": null, + "GroupIdentifier": null, + "LocalizedPeriod": "1 month", + "Offer": { + "$type": "AdaptySubscriptionOffer", + "Identifier": "promo-1", + "OfferTags": null, + "Phases": [ + { + "$type": "AdaptySubscriptionPhase", + "LocalizedNumberOfPeriods": "1 time", + "LocalizedSubscriptionPeriod": "1 week", + "NumberOfPeriods": 1, + "PaymentMode": "FreeTrial (2)", + "Price": { + "$type": "AdaptyPrice", + "Amount": 0, + "CurrencyCode": "USD", + "CurrencySymbol": "$", + "LocalizedString": "Free" + }, + "SubscriptionPeriod": { + "$type": "AdaptySubscriptionPeriod", + "NumberOfUnits": 1, + "Unit": "Week (1)" + } + } + ], + "Type": "Promotional (1)" + }, + "Period": { + "$type": "AdaptySubscriptionPeriod", + "NumberOfUnits": 1, + "Unit": "Month (2)" + }, + "RenewalType": "Autorenewable (1)" + }, + "VendorProductId": "com.adapty.sample.monthly", + "_PayloadData": "{\"custom\":\"promoted payload\"}" +} diff --git a/tests/shared/Fixtures/approved/promoted-product-full.ios.approved.txt b/tests/shared/Fixtures/approved/promoted-product-full.ios.approved.txt new file mode 100644 index 0000000..1273196 --- /dev/null +++ b/tests/shared/Fixtures/approved/promoted-product-full.ios.approved.txt @@ -0,0 +1,55 @@ +{ + "$type": "AdaptyPromotedProduct", + "IsFamilyShareable": true, + "LocalizedDescription": "Full access, billed monthly", + "LocalizedTitle": "Monthly", + "Price": { + "$type": "AdaptyPrice", + "Amount": 9.99, + "CurrencyCode": "USD", + "CurrencySymbol": "$", + "LocalizedString": "$9.99" + }, + "RegionCode": "US", + "Subscription": { + "$type": "AdaptySubscription", + "BasePlanId": null, + "GroupIdentifier": "group-1", + "LocalizedPeriod": "1 month", + "Offer": { + "$type": "AdaptySubscriptionOffer", + "Identifier": "promo-1", + "OfferTags": null, + "Phases": [ + { + "$type": "AdaptySubscriptionPhase", + "LocalizedNumberOfPeriods": "1 time", + "LocalizedSubscriptionPeriod": "1 week", + "NumberOfPeriods": 1, + "PaymentMode": "FreeTrial (2)", + "Price": { + "$type": "AdaptyPrice", + "Amount": 0, + "CurrencyCode": "USD", + "CurrencySymbol": "$", + "LocalizedString": "Free" + }, + "SubscriptionPeriod": { + "$type": "AdaptySubscriptionPeriod", + "NumberOfUnits": 1, + "Unit": "Week (1)" + } + } + ], + "Type": "Promotional (1)" + }, + "Period": { + "$type": "AdaptySubscriptionPeriod", + "NumberOfUnits": 1, + "Unit": "Month (2)" + }, + "RenewalType": "Autorenewable (1)" + }, + "VendorProductId": "com.adapty.sample.monthly", + "_PayloadData": "{\"custom\":\"promoted payload\"}" +} diff --git a/tests/shared/Fixtures/approved/promoted-product-minimal.android.approved.txt b/tests/shared/Fixtures/approved/promoted-product-minimal.android.approved.txt new file mode 100644 index 0000000..911b872 --- /dev/null +++ b/tests/shared/Fixtures/approved/promoted-product-minimal.android.approved.txt @@ -0,0 +1,17 @@ +{ + "$type": "AdaptyPromotedProduct", + "IsFamilyShareable": false, + "LocalizedDescription": "Everything, forever", + "LocalizedTitle": "Lifetime", + "Price": { + "$type": "AdaptyPrice", + "Amount": 49.99, + "CurrencyCode": "USD", + "CurrencySymbol": "$", + "LocalizedString": "$49.99" + }, + "RegionCode": null, + "Subscription": null, + "VendorProductId": "com.adapty.sample.lifetime", + "_PayloadData": null +} diff --git a/tests/shared/Fixtures/approved/promoted-product-minimal.editor.approved.txt b/tests/shared/Fixtures/approved/promoted-product-minimal.editor.approved.txt new file mode 100644 index 0000000..911b872 --- /dev/null +++ b/tests/shared/Fixtures/approved/promoted-product-minimal.editor.approved.txt @@ -0,0 +1,17 @@ +{ + "$type": "AdaptyPromotedProduct", + "IsFamilyShareable": false, + "LocalizedDescription": "Everything, forever", + "LocalizedTitle": "Lifetime", + "Price": { + "$type": "AdaptyPrice", + "Amount": 49.99, + "CurrencyCode": "USD", + "CurrencySymbol": "$", + "LocalizedString": "$49.99" + }, + "RegionCode": null, + "Subscription": null, + "VendorProductId": "com.adapty.sample.lifetime", + "_PayloadData": null +} diff --git a/tests/shared/Fixtures/approved/promoted-product-minimal.ios.approved.txt b/tests/shared/Fixtures/approved/promoted-product-minimal.ios.approved.txt new file mode 100644 index 0000000..911b872 --- /dev/null +++ b/tests/shared/Fixtures/approved/promoted-product-minimal.ios.approved.txt @@ -0,0 +1,17 @@ +{ + "$type": "AdaptyPromotedProduct", + "IsFamilyShareable": false, + "LocalizedDescription": "Everything, forever", + "LocalizedTitle": "Lifetime", + "Price": { + "$type": "AdaptyPrice", + "Amount": 49.99, + "CurrencyCode": "USD", + "CurrencySymbol": "$", + "LocalizedString": "$49.99" + }, + "RegionCode": null, + "Subscription": null, + "VendorProductId": "com.adapty.sample.lifetime", + "_PayloadData": null +} diff --git a/tests/shared/Fixtures/approved/public-surface.android.approved.txt b/tests/shared/Fixtures/approved/public-surface.android.approved.txt index d2e1cb7..e557826 100644 --- a/tests/shared/Fixtures/approved/public-surface.android.approved.txt +++ b/tests/shared/Fixtures/approved/public-surface.android.approved.txt @@ -3,6 +3,7 @@ protected AdaptySDK.AdaptyOnboardingsAnalyticsEvent.ctor() protected AdaptySDK.AdaptyOnboardingsInput.ctor() protected AdaptySDK.AdaptyOnboardingsStateUpdatedParams.ctor() public AdaptySDK.AdaptyConfiguration+Builder.ActivateUI : System.Boolean +public AdaptySDK.AdaptyConfiguration+Builder.AdaptyAttributionEnabled : System.Nullable public AdaptySDK.AdaptyConfiguration+Builder.AdaptyUIMediaCache : AdaptySDK.AdaptyUIMediaCacheConfiguration public AdaptySDK.AdaptyConfiguration+Builder.ApiKey : System.String public AdaptySDK.AdaptyConfiguration+Builder.AppleClearDataOnBackup : System.Nullable @@ -21,6 +22,7 @@ public AdaptySDK.AdaptyConfiguration+Builder.ObserverMode : System.Nullable public AdaptySDK.AdaptyConfiguration+Builder.SetAPIKey(System.String apiKey) : AdaptySDK.Builder public AdaptySDK.AdaptyConfiguration+Builder.SetActivateUI(System.Boolean activate) : AdaptySDK.Builder +public AdaptySDK.AdaptyConfiguration+Builder.SetAdaptyAttributionEnabled(System.Boolean adaptyAttributionEnabled) : AdaptySDK.Builder public AdaptySDK.AdaptyConfiguration+Builder.SetAdaptyUIMediaCache(System.Nullable memoryStorageTotalCostLimit, System.Nullable memoryStorageCountLimit, System.Nullable diskStorageSizeLimit) : AdaptySDK.Builder public AdaptySDK.AdaptyConfiguration+Builder.SetAppleClearDataOnBackup(System.Boolean appleClearDataOnBackup) : AdaptySDK.Builder public AdaptySDK.AdaptyConfiguration+Builder.SetAppleIDFACollectionDisabled(System.Boolean appleIdfaCollectionDisabled) : AdaptySDK.Builder @@ -73,7 +75,7 @@ public AdaptySDK.AdaptyOnboardingsTextInput.ctor(System.String value) public AdaptySDK.AdaptyPaymentMode.value__ : System.Int32 public AdaptySDK.AdaptyProductIdentifier.ctor(System.String vendorProductId, System.String adaptyProductId, System.String basePlanId) public AdaptySDK.AdaptyProfile.AccessLevels : System.Collections.Generic.IReadOnlyDictionary { get; } -public AdaptySDK.AdaptyProfile.AppliedAttributionSources : System.Collections.Generic.IReadOnlyList { get; } +public AdaptySDK.AdaptyProfile.AppliedExternalAttributionProviders : System.Collections.Generic.IReadOnlyList { get; } public AdaptySDK.AdaptyProfile.CustomAttributes : System.Collections.Generic.IReadOnlyDictionary { get; } public AdaptySDK.AdaptyProfile.NonSubscriptions : System.Collections.Generic.IReadOnlyDictionary> { get; } public AdaptySDK.AdaptyProfile.Subscriptions : System.Collections.Generic.IReadOnlyDictionary { get; } @@ -174,6 +176,7 @@ public AdaptySDK.AppTrackingTransparencyStatus.value__ : System.Int32 public abstract AdaptySDK.IAdaptyEventListener.OnInstallationDetailsFail(AdaptySDK.AdaptyError error) : System.Void public abstract AdaptySDK.IAdaptyEventListener.OnInstallationDetailsSuccess(AdaptySDK.AdaptyInstallationDetails details) : System.Void public abstract AdaptySDK.IAdaptyEventListener.OnLoadLatestProfile(AdaptySDK.AdaptyProfile profile) : System.Void +public abstract AdaptySDK.IAdaptyEventListener.OnReceivePromotedPurchase(AdaptySDK.AdaptyPromotedProduct product) : System.Void public abstract AdaptySDK.IAdaptyFlowsEventsListener.FlowViewDidAppear(AdaptySDK.AdaptyUIFlowView view) : System.Void public abstract AdaptySDK.IAdaptyFlowsEventsListener.FlowViewDidDisappear(AdaptySDK.AdaptyUIFlowView view) : System.Void public abstract AdaptySDK.IAdaptyFlowsEventsListener.FlowViewDidFailLoadingProducts(AdaptySDK.AdaptyUIFlowView view, AdaptySDK.AdaptyError error) : System.Void @@ -249,6 +252,7 @@ public override AdaptySDK.AdaptyProfile+NonSubscription.ToString() : System.Stri public override AdaptySDK.AdaptyProfile+Subscription.ToString() : System.String public override AdaptySDK.AdaptyProfile.ToString() : System.String public override AdaptySDK.AdaptyProfileParameters.ToString() : System.String +public override AdaptySDK.AdaptyPromotedProduct.ToString() : System.String public override AdaptySDK.AdaptyPurchaseParameters.ToString() : System.String public override AdaptySDK.AdaptyPurchaseResult.ToString() : System.String public override AdaptySDK.AdaptySubscription.ToString() : System.String @@ -378,6 +382,13 @@ public readonly AdaptySDK.AdaptyProfile+Subscription.VendorTransactionId : Syste public readonly AdaptySDK.AdaptyProfile+Subscription.WillRenew : System.Boolean public readonly AdaptySDK.AdaptyProfile.CustomerUserId : System.String public readonly AdaptySDK.AdaptyProfile.ProfileId : System.String +public readonly AdaptySDK.AdaptyPromotedProduct.IsFamilyShareable : System.Boolean +public readonly AdaptySDK.AdaptyPromotedProduct.LocalizedDescription : System.String +public readonly AdaptySDK.AdaptyPromotedProduct.LocalizedTitle : System.String +public readonly AdaptySDK.AdaptyPromotedProduct.Price : AdaptySDK.AdaptyPrice +public readonly AdaptySDK.AdaptyPromotedProduct.RegionCode : System.String +public readonly AdaptySDK.AdaptyPromotedProduct.Subscription : AdaptySDK.AdaptySubscription +public readonly AdaptySDK.AdaptyPromotedProduct.VendorProductId : System.String public readonly AdaptySDK.AdaptyPurchaseParameters.IsOfferPersonalized : System.Nullable public readonly AdaptySDK.AdaptyPurchaseParameters.SubscriptionUpdateParams : AdaptySDK.AdaptySubscriptionUpdateParameters public readonly AdaptySDK.AdaptyPurchaseResult.AppleJWSTransaction : System.String @@ -451,6 +462,7 @@ public sealed class AdaptySDK.AdaptyProfile+NonSubscription public sealed class AdaptySDK.AdaptyProfile+Subscription public sealed class AdaptySDK.AdaptyProfileParameters public sealed class AdaptySDK.AdaptyProfileParameters+Builder +public sealed class AdaptySDK.AdaptyPromotedProduct public sealed class AdaptySDK.AdaptyPurchaseParameters public sealed class AdaptySDK.AdaptyPurchaseParametersBuilder public sealed class AdaptySDK.AdaptyPurchaseResult @@ -491,6 +503,7 @@ public static AdaptySDK.Adapty.Identify(System.String customerUserId, System.Gui public static AdaptySDK.Adapty.IsActivated(System.Action completionHandler) : System.Void public static AdaptySDK.Adapty.LogShowFlow(AdaptySDK.AdaptyFlow flow, System.Action completionHandler) : System.Void public static AdaptySDK.Adapty.Logout(System.Action completionHandler) : System.Void +public static AdaptySDK.Adapty.MakePromotedPurchase(AdaptySDK.AdaptyPromotedProduct product, System.Action completionHandler) : System.Void public static AdaptySDK.Adapty.MakePurchase(AdaptySDK.AdaptyPaywallProduct product, AdaptySDK.AdaptyPurchaseParameters purchaseParameters, System.Action completionHandler) : System.Void public static AdaptySDK.Adapty.MakePurchase(AdaptySDK.AdaptyPaywallProduct product, System.Action completionHandler) : System.Void public static AdaptySDK.Adapty.OpenWebPaywall(AdaptySDK.AdaptyFlowPaywall paywall, AdaptySDK.AdaptyWebPresentation openIn, System.Action completionHandler) : System.Void @@ -511,8 +524,8 @@ public static AdaptySDK.Adapty.SetOnboardingsEventsListener(AdaptySDK.IAdaptyOnb public static AdaptySDK.Adapty.SetSystemRequestsHandler(AdaptySDK.IAdaptyUISystemRequestsHandler handler) : System.Void public static AdaptySDK.Adapty.UpdateAppStoreCollectingRefundDataConsent(System.Boolean consent, System.Action completionHandler) : System.Void public static AdaptySDK.Adapty.UpdateAppStoreRefundPreference(AdaptySDK.AdaptyRefundPreference refundPreference, System.Action completionHandler) : System.Void -public static AdaptySDK.Adapty.UpdateAttribution(System.Collections.Generic.IReadOnlyDictionary attribution, System.String source, System.Action completionHandler) : System.Void -public static AdaptySDK.Adapty.UpdateAttribution(System.String jsonString, System.String source, System.Action completionHandler) : System.Void +public static AdaptySDK.Adapty.UpdateExternalAttribution(System.Collections.Generic.IReadOnlyDictionary attribution, System.String provider, System.Action completionHandler) : System.Void +public static AdaptySDK.Adapty.UpdateExternalAttribution(System.String jsonString, System.String provider, System.Action completionHandler) : System.Void public static AdaptySDK.Adapty.UpdateProfile(AdaptySDK.AdaptyProfileParameters param, System.Action completionHandler) : System.Void public static AdaptySDK.AdaptyCustomAsset.Color(UnityEngine.Color color) : AdaptySDK.AdaptyCustomAsset public static AdaptySDK.AdaptyCustomAsset.LinearGradient(UnityEngine.Gradient gradient) : AdaptySDK.AdaptyCustomAsset diff --git a/tests/shared/Fixtures/approved/public-surface.editor.approved.txt b/tests/shared/Fixtures/approved/public-surface.editor.approved.txt index d2e1cb7..e557826 100644 --- a/tests/shared/Fixtures/approved/public-surface.editor.approved.txt +++ b/tests/shared/Fixtures/approved/public-surface.editor.approved.txt @@ -3,6 +3,7 @@ protected AdaptySDK.AdaptyOnboardingsAnalyticsEvent.ctor() protected AdaptySDK.AdaptyOnboardingsInput.ctor() protected AdaptySDK.AdaptyOnboardingsStateUpdatedParams.ctor() public AdaptySDK.AdaptyConfiguration+Builder.ActivateUI : System.Boolean +public AdaptySDK.AdaptyConfiguration+Builder.AdaptyAttributionEnabled : System.Nullable public AdaptySDK.AdaptyConfiguration+Builder.AdaptyUIMediaCache : AdaptySDK.AdaptyUIMediaCacheConfiguration public AdaptySDK.AdaptyConfiguration+Builder.ApiKey : System.String public AdaptySDK.AdaptyConfiguration+Builder.AppleClearDataOnBackup : System.Nullable @@ -21,6 +22,7 @@ public AdaptySDK.AdaptyConfiguration+Builder.ObserverMode : System.Nullable public AdaptySDK.AdaptyConfiguration+Builder.SetAPIKey(System.String apiKey) : AdaptySDK.Builder public AdaptySDK.AdaptyConfiguration+Builder.SetActivateUI(System.Boolean activate) : AdaptySDK.Builder +public AdaptySDK.AdaptyConfiguration+Builder.SetAdaptyAttributionEnabled(System.Boolean adaptyAttributionEnabled) : AdaptySDK.Builder public AdaptySDK.AdaptyConfiguration+Builder.SetAdaptyUIMediaCache(System.Nullable memoryStorageTotalCostLimit, System.Nullable memoryStorageCountLimit, System.Nullable diskStorageSizeLimit) : AdaptySDK.Builder public AdaptySDK.AdaptyConfiguration+Builder.SetAppleClearDataOnBackup(System.Boolean appleClearDataOnBackup) : AdaptySDK.Builder public AdaptySDK.AdaptyConfiguration+Builder.SetAppleIDFACollectionDisabled(System.Boolean appleIdfaCollectionDisabled) : AdaptySDK.Builder @@ -73,7 +75,7 @@ public AdaptySDK.AdaptyOnboardingsTextInput.ctor(System.String value) public AdaptySDK.AdaptyPaymentMode.value__ : System.Int32 public AdaptySDK.AdaptyProductIdentifier.ctor(System.String vendorProductId, System.String adaptyProductId, System.String basePlanId) public AdaptySDK.AdaptyProfile.AccessLevels : System.Collections.Generic.IReadOnlyDictionary { get; } -public AdaptySDK.AdaptyProfile.AppliedAttributionSources : System.Collections.Generic.IReadOnlyList { get; } +public AdaptySDK.AdaptyProfile.AppliedExternalAttributionProviders : System.Collections.Generic.IReadOnlyList { get; } public AdaptySDK.AdaptyProfile.CustomAttributes : System.Collections.Generic.IReadOnlyDictionary { get; } public AdaptySDK.AdaptyProfile.NonSubscriptions : System.Collections.Generic.IReadOnlyDictionary> { get; } public AdaptySDK.AdaptyProfile.Subscriptions : System.Collections.Generic.IReadOnlyDictionary { get; } @@ -174,6 +176,7 @@ public AdaptySDK.AppTrackingTransparencyStatus.value__ : System.Int32 public abstract AdaptySDK.IAdaptyEventListener.OnInstallationDetailsFail(AdaptySDK.AdaptyError error) : System.Void public abstract AdaptySDK.IAdaptyEventListener.OnInstallationDetailsSuccess(AdaptySDK.AdaptyInstallationDetails details) : System.Void public abstract AdaptySDK.IAdaptyEventListener.OnLoadLatestProfile(AdaptySDK.AdaptyProfile profile) : System.Void +public abstract AdaptySDK.IAdaptyEventListener.OnReceivePromotedPurchase(AdaptySDK.AdaptyPromotedProduct product) : System.Void public abstract AdaptySDK.IAdaptyFlowsEventsListener.FlowViewDidAppear(AdaptySDK.AdaptyUIFlowView view) : System.Void public abstract AdaptySDK.IAdaptyFlowsEventsListener.FlowViewDidDisappear(AdaptySDK.AdaptyUIFlowView view) : System.Void public abstract AdaptySDK.IAdaptyFlowsEventsListener.FlowViewDidFailLoadingProducts(AdaptySDK.AdaptyUIFlowView view, AdaptySDK.AdaptyError error) : System.Void @@ -249,6 +252,7 @@ public override AdaptySDK.AdaptyProfile+NonSubscription.ToString() : System.Stri public override AdaptySDK.AdaptyProfile+Subscription.ToString() : System.String public override AdaptySDK.AdaptyProfile.ToString() : System.String public override AdaptySDK.AdaptyProfileParameters.ToString() : System.String +public override AdaptySDK.AdaptyPromotedProduct.ToString() : System.String public override AdaptySDK.AdaptyPurchaseParameters.ToString() : System.String public override AdaptySDK.AdaptyPurchaseResult.ToString() : System.String public override AdaptySDK.AdaptySubscription.ToString() : System.String @@ -378,6 +382,13 @@ public readonly AdaptySDK.AdaptyProfile+Subscription.VendorTransactionId : Syste public readonly AdaptySDK.AdaptyProfile+Subscription.WillRenew : System.Boolean public readonly AdaptySDK.AdaptyProfile.CustomerUserId : System.String public readonly AdaptySDK.AdaptyProfile.ProfileId : System.String +public readonly AdaptySDK.AdaptyPromotedProduct.IsFamilyShareable : System.Boolean +public readonly AdaptySDK.AdaptyPromotedProduct.LocalizedDescription : System.String +public readonly AdaptySDK.AdaptyPromotedProduct.LocalizedTitle : System.String +public readonly AdaptySDK.AdaptyPromotedProduct.Price : AdaptySDK.AdaptyPrice +public readonly AdaptySDK.AdaptyPromotedProduct.RegionCode : System.String +public readonly AdaptySDK.AdaptyPromotedProduct.Subscription : AdaptySDK.AdaptySubscription +public readonly AdaptySDK.AdaptyPromotedProduct.VendorProductId : System.String public readonly AdaptySDK.AdaptyPurchaseParameters.IsOfferPersonalized : System.Nullable public readonly AdaptySDK.AdaptyPurchaseParameters.SubscriptionUpdateParams : AdaptySDK.AdaptySubscriptionUpdateParameters public readonly AdaptySDK.AdaptyPurchaseResult.AppleJWSTransaction : System.String @@ -451,6 +462,7 @@ public sealed class AdaptySDK.AdaptyProfile+NonSubscription public sealed class AdaptySDK.AdaptyProfile+Subscription public sealed class AdaptySDK.AdaptyProfileParameters public sealed class AdaptySDK.AdaptyProfileParameters+Builder +public sealed class AdaptySDK.AdaptyPromotedProduct public sealed class AdaptySDK.AdaptyPurchaseParameters public sealed class AdaptySDK.AdaptyPurchaseParametersBuilder public sealed class AdaptySDK.AdaptyPurchaseResult @@ -491,6 +503,7 @@ public static AdaptySDK.Adapty.Identify(System.String customerUserId, System.Gui public static AdaptySDK.Adapty.IsActivated(System.Action completionHandler) : System.Void public static AdaptySDK.Adapty.LogShowFlow(AdaptySDK.AdaptyFlow flow, System.Action completionHandler) : System.Void public static AdaptySDK.Adapty.Logout(System.Action completionHandler) : System.Void +public static AdaptySDK.Adapty.MakePromotedPurchase(AdaptySDK.AdaptyPromotedProduct product, System.Action completionHandler) : System.Void public static AdaptySDK.Adapty.MakePurchase(AdaptySDK.AdaptyPaywallProduct product, AdaptySDK.AdaptyPurchaseParameters purchaseParameters, System.Action completionHandler) : System.Void public static AdaptySDK.Adapty.MakePurchase(AdaptySDK.AdaptyPaywallProduct product, System.Action completionHandler) : System.Void public static AdaptySDK.Adapty.OpenWebPaywall(AdaptySDK.AdaptyFlowPaywall paywall, AdaptySDK.AdaptyWebPresentation openIn, System.Action completionHandler) : System.Void @@ -511,8 +524,8 @@ public static AdaptySDK.Adapty.SetOnboardingsEventsListener(AdaptySDK.IAdaptyOnb public static AdaptySDK.Adapty.SetSystemRequestsHandler(AdaptySDK.IAdaptyUISystemRequestsHandler handler) : System.Void public static AdaptySDK.Adapty.UpdateAppStoreCollectingRefundDataConsent(System.Boolean consent, System.Action completionHandler) : System.Void public static AdaptySDK.Adapty.UpdateAppStoreRefundPreference(AdaptySDK.AdaptyRefundPreference refundPreference, System.Action completionHandler) : System.Void -public static AdaptySDK.Adapty.UpdateAttribution(System.Collections.Generic.IReadOnlyDictionary attribution, System.String source, System.Action completionHandler) : System.Void -public static AdaptySDK.Adapty.UpdateAttribution(System.String jsonString, System.String source, System.Action completionHandler) : System.Void +public static AdaptySDK.Adapty.UpdateExternalAttribution(System.Collections.Generic.IReadOnlyDictionary attribution, System.String provider, System.Action completionHandler) : System.Void +public static AdaptySDK.Adapty.UpdateExternalAttribution(System.String jsonString, System.String provider, System.Action completionHandler) : System.Void public static AdaptySDK.Adapty.UpdateProfile(AdaptySDK.AdaptyProfileParameters param, System.Action completionHandler) : System.Void public static AdaptySDK.AdaptyCustomAsset.Color(UnityEngine.Color color) : AdaptySDK.AdaptyCustomAsset public static AdaptySDK.AdaptyCustomAsset.LinearGradient(UnityEngine.Gradient gradient) : AdaptySDK.AdaptyCustomAsset diff --git a/tests/shared/Fixtures/approved/public-surface.ios.approved.txt b/tests/shared/Fixtures/approved/public-surface.ios.approved.txt index d2e1cb7..e557826 100644 --- a/tests/shared/Fixtures/approved/public-surface.ios.approved.txt +++ b/tests/shared/Fixtures/approved/public-surface.ios.approved.txt @@ -3,6 +3,7 @@ protected AdaptySDK.AdaptyOnboardingsAnalyticsEvent.ctor() protected AdaptySDK.AdaptyOnboardingsInput.ctor() protected AdaptySDK.AdaptyOnboardingsStateUpdatedParams.ctor() public AdaptySDK.AdaptyConfiguration+Builder.ActivateUI : System.Boolean +public AdaptySDK.AdaptyConfiguration+Builder.AdaptyAttributionEnabled : System.Nullable public AdaptySDK.AdaptyConfiguration+Builder.AdaptyUIMediaCache : AdaptySDK.AdaptyUIMediaCacheConfiguration public AdaptySDK.AdaptyConfiguration+Builder.ApiKey : System.String public AdaptySDK.AdaptyConfiguration+Builder.AppleClearDataOnBackup : System.Nullable @@ -21,6 +22,7 @@ public AdaptySDK.AdaptyConfiguration+Builder.ObserverMode : System.Nullable public AdaptySDK.AdaptyConfiguration+Builder.SetAPIKey(System.String apiKey) : AdaptySDK.Builder public AdaptySDK.AdaptyConfiguration+Builder.SetActivateUI(System.Boolean activate) : AdaptySDK.Builder +public AdaptySDK.AdaptyConfiguration+Builder.SetAdaptyAttributionEnabled(System.Boolean adaptyAttributionEnabled) : AdaptySDK.Builder public AdaptySDK.AdaptyConfiguration+Builder.SetAdaptyUIMediaCache(System.Nullable memoryStorageTotalCostLimit, System.Nullable memoryStorageCountLimit, System.Nullable diskStorageSizeLimit) : AdaptySDK.Builder public AdaptySDK.AdaptyConfiguration+Builder.SetAppleClearDataOnBackup(System.Boolean appleClearDataOnBackup) : AdaptySDK.Builder public AdaptySDK.AdaptyConfiguration+Builder.SetAppleIDFACollectionDisabled(System.Boolean appleIdfaCollectionDisabled) : AdaptySDK.Builder @@ -73,7 +75,7 @@ public AdaptySDK.AdaptyOnboardingsTextInput.ctor(System.String value) public AdaptySDK.AdaptyPaymentMode.value__ : System.Int32 public AdaptySDK.AdaptyProductIdentifier.ctor(System.String vendorProductId, System.String adaptyProductId, System.String basePlanId) public AdaptySDK.AdaptyProfile.AccessLevels : System.Collections.Generic.IReadOnlyDictionary { get; } -public AdaptySDK.AdaptyProfile.AppliedAttributionSources : System.Collections.Generic.IReadOnlyList { get; } +public AdaptySDK.AdaptyProfile.AppliedExternalAttributionProviders : System.Collections.Generic.IReadOnlyList { get; } public AdaptySDK.AdaptyProfile.CustomAttributes : System.Collections.Generic.IReadOnlyDictionary { get; } public AdaptySDK.AdaptyProfile.NonSubscriptions : System.Collections.Generic.IReadOnlyDictionary> { get; } public AdaptySDK.AdaptyProfile.Subscriptions : System.Collections.Generic.IReadOnlyDictionary { get; } @@ -174,6 +176,7 @@ public AdaptySDK.AppTrackingTransparencyStatus.value__ : System.Int32 public abstract AdaptySDK.IAdaptyEventListener.OnInstallationDetailsFail(AdaptySDK.AdaptyError error) : System.Void public abstract AdaptySDK.IAdaptyEventListener.OnInstallationDetailsSuccess(AdaptySDK.AdaptyInstallationDetails details) : System.Void public abstract AdaptySDK.IAdaptyEventListener.OnLoadLatestProfile(AdaptySDK.AdaptyProfile profile) : System.Void +public abstract AdaptySDK.IAdaptyEventListener.OnReceivePromotedPurchase(AdaptySDK.AdaptyPromotedProduct product) : System.Void public abstract AdaptySDK.IAdaptyFlowsEventsListener.FlowViewDidAppear(AdaptySDK.AdaptyUIFlowView view) : System.Void public abstract AdaptySDK.IAdaptyFlowsEventsListener.FlowViewDidDisappear(AdaptySDK.AdaptyUIFlowView view) : System.Void public abstract AdaptySDK.IAdaptyFlowsEventsListener.FlowViewDidFailLoadingProducts(AdaptySDK.AdaptyUIFlowView view, AdaptySDK.AdaptyError error) : System.Void @@ -249,6 +252,7 @@ public override AdaptySDK.AdaptyProfile+NonSubscription.ToString() : System.Stri public override AdaptySDK.AdaptyProfile+Subscription.ToString() : System.String public override AdaptySDK.AdaptyProfile.ToString() : System.String public override AdaptySDK.AdaptyProfileParameters.ToString() : System.String +public override AdaptySDK.AdaptyPromotedProduct.ToString() : System.String public override AdaptySDK.AdaptyPurchaseParameters.ToString() : System.String public override AdaptySDK.AdaptyPurchaseResult.ToString() : System.String public override AdaptySDK.AdaptySubscription.ToString() : System.String @@ -378,6 +382,13 @@ public readonly AdaptySDK.AdaptyProfile+Subscription.VendorTransactionId : Syste public readonly AdaptySDK.AdaptyProfile+Subscription.WillRenew : System.Boolean public readonly AdaptySDK.AdaptyProfile.CustomerUserId : System.String public readonly AdaptySDK.AdaptyProfile.ProfileId : System.String +public readonly AdaptySDK.AdaptyPromotedProduct.IsFamilyShareable : System.Boolean +public readonly AdaptySDK.AdaptyPromotedProduct.LocalizedDescription : System.String +public readonly AdaptySDK.AdaptyPromotedProduct.LocalizedTitle : System.String +public readonly AdaptySDK.AdaptyPromotedProduct.Price : AdaptySDK.AdaptyPrice +public readonly AdaptySDK.AdaptyPromotedProduct.RegionCode : System.String +public readonly AdaptySDK.AdaptyPromotedProduct.Subscription : AdaptySDK.AdaptySubscription +public readonly AdaptySDK.AdaptyPromotedProduct.VendorProductId : System.String public readonly AdaptySDK.AdaptyPurchaseParameters.IsOfferPersonalized : System.Nullable public readonly AdaptySDK.AdaptyPurchaseParameters.SubscriptionUpdateParams : AdaptySDK.AdaptySubscriptionUpdateParameters public readonly AdaptySDK.AdaptyPurchaseResult.AppleJWSTransaction : System.String @@ -451,6 +462,7 @@ public sealed class AdaptySDK.AdaptyProfile+NonSubscription public sealed class AdaptySDK.AdaptyProfile+Subscription public sealed class AdaptySDK.AdaptyProfileParameters public sealed class AdaptySDK.AdaptyProfileParameters+Builder +public sealed class AdaptySDK.AdaptyPromotedProduct public sealed class AdaptySDK.AdaptyPurchaseParameters public sealed class AdaptySDK.AdaptyPurchaseParametersBuilder public sealed class AdaptySDK.AdaptyPurchaseResult @@ -491,6 +503,7 @@ public static AdaptySDK.Adapty.Identify(System.String customerUserId, System.Gui public static AdaptySDK.Adapty.IsActivated(System.Action completionHandler) : System.Void public static AdaptySDK.Adapty.LogShowFlow(AdaptySDK.AdaptyFlow flow, System.Action completionHandler) : System.Void public static AdaptySDK.Adapty.Logout(System.Action completionHandler) : System.Void +public static AdaptySDK.Adapty.MakePromotedPurchase(AdaptySDK.AdaptyPromotedProduct product, System.Action completionHandler) : System.Void public static AdaptySDK.Adapty.MakePurchase(AdaptySDK.AdaptyPaywallProduct product, AdaptySDK.AdaptyPurchaseParameters purchaseParameters, System.Action completionHandler) : System.Void public static AdaptySDK.Adapty.MakePurchase(AdaptySDK.AdaptyPaywallProduct product, System.Action completionHandler) : System.Void public static AdaptySDK.Adapty.OpenWebPaywall(AdaptySDK.AdaptyFlowPaywall paywall, AdaptySDK.AdaptyWebPresentation openIn, System.Action completionHandler) : System.Void @@ -511,8 +524,8 @@ public static AdaptySDK.Adapty.SetOnboardingsEventsListener(AdaptySDK.IAdaptyOnb public static AdaptySDK.Adapty.SetSystemRequestsHandler(AdaptySDK.IAdaptyUISystemRequestsHandler handler) : System.Void public static AdaptySDK.Adapty.UpdateAppStoreCollectingRefundDataConsent(System.Boolean consent, System.Action completionHandler) : System.Void public static AdaptySDK.Adapty.UpdateAppStoreRefundPreference(AdaptySDK.AdaptyRefundPreference refundPreference, System.Action completionHandler) : System.Void -public static AdaptySDK.Adapty.UpdateAttribution(System.Collections.Generic.IReadOnlyDictionary attribution, System.String source, System.Action completionHandler) : System.Void -public static AdaptySDK.Adapty.UpdateAttribution(System.String jsonString, System.String source, System.Action completionHandler) : System.Void +public static AdaptySDK.Adapty.UpdateExternalAttribution(System.Collections.Generic.IReadOnlyDictionary attribution, System.String provider, System.Action completionHandler) : System.Void +public static AdaptySDK.Adapty.UpdateExternalAttribution(System.String jsonString, System.String provider, System.Action completionHandler) : System.Void public static AdaptySDK.Adapty.UpdateProfile(AdaptySDK.AdaptyProfileParameters param, System.Action completionHandler) : System.Void public static AdaptySDK.AdaptyCustomAsset.Color(UnityEngine.Color color) : AdaptySDK.AdaptyCustomAsset public static AdaptySDK.AdaptyCustomAsset.LinearGradient(UnityEngine.Gradient gradient) : AdaptySDK.AdaptyCustomAsset diff --git a/tests/shared/Fixtures/approved/purchase-result-success.android.approved.txt b/tests/shared/Fixtures/approved/purchase-result-success.android.approved.txt index 15049fd..e997cfd 100644 --- a/tests/shared/Fixtures/approved/purchase-result-success.android.approved.txt +++ b/tests/shared/Fixtures/approved/purchase-result-success.android.approved.txt @@ -5,7 +5,7 @@ "Profile": { "$type": "AdaptyProfile", "AccessLevels (property)": {}, - "AppliedAttributionSources (property)": [], + "AppliedExternalAttributionProviders (property)": [], "CustomAttributes (property)": {}, "CustomerUserId": null, "IsTestUser": false, @@ -15,7 +15,7 @@ "Subscriptions (property)": {}, "Version": 1753876800000, "_AccessLevels": {}, - "_AppliedAttributionSources": [], + "_AppliedExternalAttributionProviders": [], "_CustomAttributes": {}, "_NonSubscriptions": {}, "_Subscriptions": {} diff --git a/tests/shared/Fixtures/approved/purchase-result-success.editor.approved.txt b/tests/shared/Fixtures/approved/purchase-result-success.editor.approved.txt index 15049fd..e997cfd 100644 --- a/tests/shared/Fixtures/approved/purchase-result-success.editor.approved.txt +++ b/tests/shared/Fixtures/approved/purchase-result-success.editor.approved.txt @@ -5,7 +5,7 @@ "Profile": { "$type": "AdaptyProfile", "AccessLevels (property)": {}, - "AppliedAttributionSources (property)": [], + "AppliedExternalAttributionProviders (property)": [], "CustomAttributes (property)": {}, "CustomerUserId": null, "IsTestUser": false, @@ -15,7 +15,7 @@ "Subscriptions (property)": {}, "Version": 1753876800000, "_AccessLevels": {}, - "_AppliedAttributionSources": [], + "_AppliedExternalAttributionProviders": [], "_CustomAttributes": {}, "_NonSubscriptions": {}, "_Subscriptions": {} diff --git a/tests/shared/Fixtures/approved/purchase-result-success.ios.approved.txt b/tests/shared/Fixtures/approved/purchase-result-success.ios.approved.txt index 15049fd..e997cfd 100644 --- a/tests/shared/Fixtures/approved/purchase-result-success.ios.approved.txt +++ b/tests/shared/Fixtures/approved/purchase-result-success.ios.approved.txt @@ -5,7 +5,7 @@ "Profile": { "$type": "AdaptyProfile", "AccessLevels (property)": {}, - "AppliedAttributionSources (property)": [], + "AppliedExternalAttributionProviders (property)": [], "CustomAttributes (property)": {}, "CustomerUserId": null, "IsTestUser": false, @@ -15,7 +15,7 @@ "Subscriptions (property)": {}, "Version": 1753876800000, "_AccessLevels": {}, - "_AppliedAttributionSources": [], + "_AppliedExternalAttributionProviders": [], "_CustomAttributes": {}, "_NonSubscriptions": {}, "_Subscriptions": {} diff --git a/tests/shared/Fixtures/approved/request-flow-full.android.approved.txt b/tests/shared/Fixtures/approved/request-flow-full.android.approved.txt index e5061ad..4daf928 100644 --- a/tests/shared/Fixtures/approved/request-flow-full.android.approved.txt +++ b/tests/shared/Fixtures/approved/request-flow-full.android.approved.txt @@ -22,6 +22,39 @@ } ], "response_created_at": 1753876800000, + "ui_schema": { + "grids": [ + { + "cells": [ + 0, + 1, + 2 + ], + "custom_id": "grid-main", + "devices": [ + "phone" + ], + "h_breakpoints": [ + 320, + 768 + ], + "platforms": "all", + "v_breakpoints": [ + 568 + ] + }, + { + "cells": [ + 3 + ] + } + ], + "layouts": [ + { + "flow_layout_id": "layout-0001" + } + ] + }, "variation_id": "variation-0001", "variations": [ { diff --git a/tests/shared/Fixtures/approved/request-flow-full.editor.approved.txt b/tests/shared/Fixtures/approved/request-flow-full.editor.approved.txt index 8832e57..a48bf95 100644 --- a/tests/shared/Fixtures/approved/request-flow-full.editor.approved.txt +++ b/tests/shared/Fixtures/approved/request-flow-full.editor.approved.txt @@ -22,6 +22,39 @@ } ], "response_created_at": 1753876800000, + "ui_schema": { + "grids": [ + { + "cells": [ + 0, + 1, + 2 + ], + "custom_id": "grid-main", + "devices": [ + "phone" + ], + "h_breakpoints": [ + 320, + 768 + ], + "platforms": "all", + "v_breakpoints": [ + 568 + ] + }, + { + "cells": [ + 3 + ] + } + ], + "layouts": [ + { + "flow_layout_id": "layout-0001" + } + ] + }, "variation_id": "variation-0001", "variations": [ { diff --git a/tests/shared/Fixtures/approved/request-flow-full.ios.approved.txt b/tests/shared/Fixtures/approved/request-flow-full.ios.approved.txt index 563819f..28cfa26 100644 --- a/tests/shared/Fixtures/approved/request-flow-full.ios.approved.txt +++ b/tests/shared/Fixtures/approved/request-flow-full.ios.approved.txt @@ -22,6 +22,39 @@ } ], "response_created_at": 1753876800000, + "ui_schema": { + "grids": [ + { + "cells": [ + 0, + 1, + 2 + ], + "custom_id": "grid-main", + "devices": [ + "phone" + ], + "h_breakpoints": [ + 320, + 768 + ], + "platforms": "all", + "v_breakpoints": [ + 568 + ] + }, + { + "cells": [ + 3 + ] + } + ], + "layouts": [ + { + "flow_layout_id": "layout-0001" + } + ] + }, "variation_id": "variation-0001", "variations": [ { diff --git a/tests/shared/Fixtures/approved/request-product-with-offer.android.approved.txt b/tests/shared/Fixtures/approved/request-product-with-offer.android.approved.txt index 16cf7d2..b7a3b41 100644 --- a/tests/shared/Fixtures/approved/request-product-with-offer.android.approved.txt +++ b/tests/shared/Fixtures/approved/request-product-with-offer.android.approved.txt @@ -7,9 +7,13 @@ "paywall_product_index": 0, "paywall_variation_id": "variation-0001", "product_type": "subscription", - "subscription_offer_identifier": { - "id": "intro-1", - "type": "introductory" + "subscription": { + "offer": { + "offer_identifier": { + "id": "intro-1", + "type": "introductory" + } + } }, "vendor_product_id": "com.adapty.sample.monthly", "web_purchase_url": "https://pay.adapty.io/checkout/abc" diff --git a/tests/shared/Fixtures/approved/request-product-with-offer.editor.approved.txt b/tests/shared/Fixtures/approved/request-product-with-offer.editor.approved.txt index 16cf7d2..b7a3b41 100644 --- a/tests/shared/Fixtures/approved/request-product-with-offer.editor.approved.txt +++ b/tests/shared/Fixtures/approved/request-product-with-offer.editor.approved.txt @@ -7,9 +7,13 @@ "paywall_product_index": 0, "paywall_variation_id": "variation-0001", "product_type": "subscription", - "subscription_offer_identifier": { - "id": "intro-1", - "type": "introductory" + "subscription": { + "offer": { + "offer_identifier": { + "id": "intro-1", + "type": "introductory" + } + } }, "vendor_product_id": "com.adapty.sample.monthly", "web_purchase_url": "https://pay.adapty.io/checkout/abc" diff --git a/tests/shared/Fixtures/approved/request-product-with-offer.ios.approved.txt b/tests/shared/Fixtures/approved/request-product-with-offer.ios.approved.txt index 16cf7d2..b7a3b41 100644 --- a/tests/shared/Fixtures/approved/request-product-with-offer.ios.approved.txt +++ b/tests/shared/Fixtures/approved/request-product-with-offer.ios.approved.txt @@ -7,9 +7,13 @@ "paywall_product_index": 0, "paywall_variation_id": "variation-0001", "product_type": "subscription", - "subscription_offer_identifier": { - "id": "intro-1", - "type": "introductory" + "subscription": { + "offer": { + "offer_identifier": { + "id": "intro-1", + "type": "introductory" + } + } }, "vendor_product_id": "com.adapty.sample.monthly", "web_purchase_url": "https://pay.adapty.io/checkout/abc" diff --git a/tests/shared/Fixtures/approved/request-promoted-product-plain.android.approved.txt b/tests/shared/Fixtures/approved/request-promoted-product-plain.android.approved.txt new file mode 100644 index 0000000..f69310b --- /dev/null +++ b/tests/shared/Fixtures/approved/request-promoted-product-plain.android.approved.txt @@ -0,0 +1,3 @@ +{ + "vendor_product_id": "com.adapty.sample.lifetime" +} diff --git a/tests/shared/Fixtures/approved/request-promoted-product-plain.editor.approved.txt b/tests/shared/Fixtures/approved/request-promoted-product-plain.editor.approved.txt new file mode 100644 index 0000000..f69310b --- /dev/null +++ b/tests/shared/Fixtures/approved/request-promoted-product-plain.editor.approved.txt @@ -0,0 +1,3 @@ +{ + "vendor_product_id": "com.adapty.sample.lifetime" +} diff --git a/tests/shared/Fixtures/approved/request-promoted-product-plain.ios.approved.txt b/tests/shared/Fixtures/approved/request-promoted-product-plain.ios.approved.txt new file mode 100644 index 0000000..f69310b --- /dev/null +++ b/tests/shared/Fixtures/approved/request-promoted-product-plain.ios.approved.txt @@ -0,0 +1,3 @@ +{ + "vendor_product_id": "com.adapty.sample.lifetime" +} diff --git a/tests/shared/Fixtures/approved/request-promoted-product-with-offer.android.approved.txt b/tests/shared/Fixtures/approved/request-promoted-product-with-offer.android.approved.txt new file mode 100644 index 0000000..b32b98e --- /dev/null +++ b/tests/shared/Fixtures/approved/request-promoted-product-with-offer.android.approved.txt @@ -0,0 +1,12 @@ +{ + "payload_data": "{\"custom\":\"promoted payload\"}", + "subscription": { + "offer": { + "offer_identifier": { + "id": "promo-1", + "type": "promotional" + } + } + }, + "vendor_product_id": "com.adapty.sample.monthly" +} diff --git a/tests/shared/Fixtures/approved/request-promoted-product-with-offer.editor.approved.txt b/tests/shared/Fixtures/approved/request-promoted-product-with-offer.editor.approved.txt new file mode 100644 index 0000000..b32b98e --- /dev/null +++ b/tests/shared/Fixtures/approved/request-promoted-product-with-offer.editor.approved.txt @@ -0,0 +1,12 @@ +{ + "payload_data": "{\"custom\":\"promoted payload\"}", + "subscription": { + "offer": { + "offer_identifier": { + "id": "promo-1", + "type": "promotional" + } + } + }, + "vendor_product_id": "com.adapty.sample.monthly" +} diff --git a/tests/shared/Fixtures/approved/request-promoted-product-with-offer.ios.approved.txt b/tests/shared/Fixtures/approved/request-promoted-product-with-offer.ios.approved.txt new file mode 100644 index 0000000..b32b98e --- /dev/null +++ b/tests/shared/Fixtures/approved/request-promoted-product-with-offer.ios.approved.txt @@ -0,0 +1,12 @@ +{ + "payload_data": "{\"custom\":\"promoted payload\"}", + "subscription": { + "offer": { + "offer_identifier": { + "id": "promo-1", + "type": "promotional" + } + } + }, + "vendor_product_id": "com.adapty.sample.monthly" +} diff --git a/tests/shared/Fixtures/approved/transport-create-web-paywall-url-product.editor.approved.txt b/tests/shared/Fixtures/approved/transport-create-web-paywall-url-product.editor.approved.txt index 32d2a06..16c55f1 100644 --- a/tests/shared/Fixtures/approved/transport-create-web-paywall-url-product.editor.approved.txt +++ b/tests/shared/Fixtures/approved/transport-create-web-paywall-url-product.editor.approved.txt @@ -9,9 +9,13 @@ "paywall_product_index": 0, "paywall_variation_id": "variation-0001", "product_type": "subscription", - "subscription_offer_identifier": { - "id": "intro-1", - "type": "introductory" + "subscription": { + "offer": { + "offer_identifier": { + "id": "intro-1", + "type": "introductory" + } + } }, "vendor_product_id": "com.adapty.sample.monthly", "web_purchase_url": "https://pay.adapty.io/checkout/abc" diff --git a/tests/shared/Fixtures/approved/transport-make-promoted-purchase.editor.approved.txt b/tests/shared/Fixtures/approved/transport-make-promoted-purchase.editor.approved.txt new file mode 100644 index 0000000..9d1574d --- /dev/null +++ b/tests/shared/Fixtures/approved/transport-make-promoted-purchase.editor.approved.txt @@ -0,0 +1,15 @@ +{ + "method": "make_promoted_purchase", + "product": { + "payload_data": "{\"custom\":\"promoted payload\"}", + "subscription": { + "offer": { + "offer_identifier": { + "id": "promo-1", + "type": "promotional" + } + } + }, + "vendor_product_id": "com.adapty.sample.monthly" + } +} diff --git a/tests/shared/Fixtures/approved/transport-make-purchase-with-offer.editor.approved.txt b/tests/shared/Fixtures/approved/transport-make-purchase-with-offer.editor.approved.txt index f85f885..efa692e 100644 --- a/tests/shared/Fixtures/approved/transport-make-purchase-with-offer.editor.approved.txt +++ b/tests/shared/Fixtures/approved/transport-make-purchase-with-offer.editor.approved.txt @@ -9,9 +9,13 @@ "paywall_product_index": 0, "paywall_variation_id": "variation-0001", "product_type": "subscription", - "subscription_offer_identifier": { - "id": "intro-1", - "type": "introductory" + "subscription": { + "offer": { + "offer_identifier": { + "id": "intro-1", + "type": "introductory" + } + } }, "vendor_product_id": "com.adapty.sample.monthly", "web_purchase_url": "https://pay.adapty.io/checkout/abc" diff --git a/tests/shared/Fixtures/approved/transport-open-web-paywall-product.editor.approved.txt b/tests/shared/Fixtures/approved/transport-open-web-paywall-product.editor.approved.txt index 963503f..3524790 100644 --- a/tests/shared/Fixtures/approved/transport-open-web-paywall-product.editor.approved.txt +++ b/tests/shared/Fixtures/approved/transport-open-web-paywall-product.editor.approved.txt @@ -10,9 +10,13 @@ "paywall_product_index": 0, "paywall_variation_id": "variation-0001", "product_type": "subscription", - "subscription_offer_identifier": { - "id": "intro-1", - "type": "introductory" + "subscription": { + "offer": { + "offer_identifier": { + "id": "intro-1", + "type": "introductory" + } + } }, "vendor_product_id": "com.adapty.sample.monthly", "web_purchase_url": "https://pay.adapty.io/checkout/abc" diff --git a/tests/shared/Fixtures/approved/transport-update-attribution.editor.approved.txt b/tests/shared/Fixtures/approved/transport-update-external-attribution.editor.approved.txt similarity index 74% rename from tests/shared/Fixtures/approved/transport-update-attribution.editor.approved.txt rename to tests/shared/Fixtures/approved/transport-update-external-attribution.editor.approved.txt index cdf1aaf..1f86490 100644 --- a/tests/shared/Fixtures/approved/transport-update-attribution.editor.approved.txt +++ b/tests/shared/Fixtures/approved/transport-update-external-attribution.editor.approved.txt @@ -1,5 +1,5 @@ { "attribution": "{\"status\":\"organic\",\"clicks\":3,\"cost\":1.5,\"is_retargeting\":false,\"install_time\":\"2026-07-30T10:00:00.000Z\",\"campaign\":null,\"tags\":[\"a\",\"b\"],\"nested\":{\"k\":\"v\"}}", - "method": "update_attribution_data", - "source": "appsflyer" + "method": "update_external_attribution_data", + "provider": "appsflyer" } diff --git a/tests/shared/Fixtures/responses/flow-full.json b/tests/shared/Fixtures/responses/flow-full.json index 1b2e4fb..2258f42 100644 --- a/tests/shared/Fixtures/responses/flow-full.json +++ b/tests/shared/Fixtures/responses/flow-full.json @@ -56,6 +56,39 @@ ] } ], + "ui_schema": { + "layouts": [ + { + "flow_layout_id": "layout-0001" + } + ], + "grids": [ + { + "platforms": "all", + "devices": [ + "phone" + ], + "custom_id": "grid-main", + "h_breakpoints": [ + 320, + 768 + ], + "v_breakpoints": [ + 568 + ], + "cells": [ + 0, + 1, + 2 + ] + }, + { + "cells": [ + 3 + ] + } + ] + }, "payload_data": "{\"custom\":\"payload\"}", "response_created_at": 1753876800000 } diff --git a/tests/shared/Fixtures/responses/promoted-product-full.json b/tests/shared/Fixtures/responses/promoted-product-full.json new file mode 100644 index 0000000..977f7a4 --- /dev/null +++ b/tests/shared/Fixtures/responses/promoted-product-full.json @@ -0,0 +1,48 @@ +{ + "vendor_product_id": "com.adapty.sample.monthly", + "localized_description": "Full access, billed monthly", + "localized_title": "Monthly", + "is_family_shareable": true, + "region_code": "US", + "price": { + "amount": 9.99, + "currency_code": "USD", + "currency_symbol": "$", + "localized_string": "$9.99" + }, + "subscription": { + "group_identifier": "group-1", + "period": { + "unit": "month", + "number_of_units": 1 + }, + "localized_period": "1 month", + "renewal_type": "autorenewable", + "base_plan_id": "base-plan-1", + "offer": { + "offer_identifier": { + "id": "promo-1", + "type": "promotional" + }, + "phases": [ + { + "price": { + "amount": 0.0, + "currency_code": "USD", + "currency_symbol": "$", + "localized_string": "Free" + }, + "number_of_periods": 1, + "payment_mode": "free_trial", + "subscription_period": { + "unit": "week", + "number_of_units": 1 + }, + "localized_subscription_period": "1 week", + "localized_number_of_periods": "1 time" + } + ] + } + }, + "payload_data": "{\"custom\":\"promoted payload\"}" +} diff --git a/tests/shared/Fixtures/responses/promoted-product-minimal.json b/tests/shared/Fixtures/responses/promoted-product-minimal.json new file mode 100644 index 0000000..ffc18a3 --- /dev/null +++ b/tests/shared/Fixtures/responses/promoted-product-minimal.json @@ -0,0 +1,12 @@ +{ + "vendor_product_id": "com.adapty.sample.lifetime", + "localized_description": "Everything, forever", + "localized_title": "Lifetime", + "is_family_shareable": false, + "price": { + "amount": 49.99, + "currency_code": "USD", + "currency_symbol": "$", + "localized_string": "$49.99" + } +} diff --git a/tests/shared/ModelSnapshot.cs b/tests/shared/ModelSnapshot.cs index 33ccdf9..f0ce060 100644 --- a/tests/shared/ModelSnapshot.cs +++ b/tests/shared/ModelSnapshot.cs @@ -89,6 +89,14 @@ private static void Write(StringBuilder builder, object value, int depth, HashSe return; } + // A JToken enumerates as its children, which the sequence walk below would render as + // nested empty arrays. It is already JSON - print it as itself, compactly. + if (value is Newtonsoft.Json.Linq.JToken token) + { + builder.Append(Quote(token.ToString(Newtonsoft.Json.Formatting.None))); + return; + } + if (depth >= MaxDepth) { builder.Append(Quote(""));