Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
# text
*.sh text eol=lf
2 changes: 1 addition & 1 deletion .version
Original file line number Diff line number Diff line change
@@ -1 +1 @@
8.2.4
9.0.0
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
{
"name": "revenuecat.purchases-unity.Editor",
"rootNamespace": "",
"name": "RevenueCat.Editor",
"rootNamespace": "RevenueCat.Editor",
"references": [],
"includePlatforms": [],
"includePlatforms": [
"Editor"
],
"excludePlatforms": [],
"allowUnsafeCode": false,
"overrideReferences": false,
Expand Down
22 changes: 8 additions & 14 deletions RevenueCat/Editor/RevenueCatPostInstall.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@

#if UNITY_EDITOR && (UNITY_IOS || UNITY_VISIONOS)

using System.IO;
#if UNITY_IOS || UNITY_VISIONOS
using UnityEngine;
using UnityEditor;
using UnityEditor.Callbacks;
Expand All @@ -13,21 +11,21 @@ public static class XcodeSwiftVersionPostProcess
[PostProcessBuild(999)]
public static void OnPostProcessBuild(BuildTarget buildTarget, string path)
{
if (buildTarget == BuildTarget.iOS)
if (buildTarget == BuildTarget.iOS || buildTarget == BuildTarget.VisionOS)
{
Debug.Log("Installing for iOS. Setting ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES and ENABLE_BITCODE to NO and adding StoreKit");
Debug.Log($"RevenueCat OnPostProcessBuild {buildTarget}: Setting ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES and ENABLE_BITCODE to NO and adding StoreKit");
ModifyFrameworks(path);
AddStoreKitFramework(path);
}
}

private static void ModifyFrameworks(string path)
{
string projPath = PBXProject.GetPBXProjectPath(path);
var projPath = PBXProject.GetPBXProjectPath(path);
var project = new PBXProject();
project.ReadFromFile(projPath);

string mainTargetGuid = project.GetUnityMainTargetGuid();
var mainTargetGuid = project.GetUnityMainTargetGuid();

foreach (var targetGuid in new[] { mainTargetGuid, project.GetUnityFrameworkTargetGuid() })
{
Expand All @@ -38,21 +36,17 @@ private static void ModifyFrameworks(string path)
}

project.SetBuildProperty(mainTargetGuid, "ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES", "YES");

project.WriteToFile(projPath);
}

private static void AddStoreKitFramework(string path)
{
string projPath = PBXProject.GetPBXProjectPath(path);
var projPath = PBXProject.GetPBXProjectPath(path);
var project = new PBXProject();
project.ReadFromFile(projPath);

string mainTargetGUID = project.GetUnityMainTargetGuid();
project.AddFrameworkToProject(mainTargetGUID, "StoreKit.framework", false);

project.AddFrameworkToProject(project.GetUnityMainTargetGuid(), "StoreKit.framework", false);
project.WriteToFile(projPath);
}

}
#endif
#endif // UNITY_IOS || UNITY_VISIONOS
547 changes: 270 additions & 277 deletions RevenueCat/Plugins/Android/PurchasesWrapper.java

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "revenuecat.purchases-unity",
"rootNamespace": "",
"name": "RevenueCat",
"rootNamespace": "RevenueCat",
"references": [],
"includePlatforms": [],
"excludePlatforms": [],
Expand Down
2 changes: 1 addition & 1 deletion RevenueCat/Scripts/AttributionNetwork.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
using System;
using System.Diagnostics.CodeAnalysis;

public partial class Purchases
namespace RevenueCat
{
[SuppressMessage("ReSharper", "InconsistentNaming")]
[SuppressMessage("ReSharper", "UnusedMember.Global")]
Expand Down
6 changes: 1 addition & 5 deletions RevenueCat/Scripts/BillingFeature.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
public partial class Purchases
namespace RevenueCat
{
/// <summary>
/// Enum for billing features.
Expand All @@ -11,22 +11,18 @@ public enum BillingFeature
/// Purchase/query for subscriptions
/// </summary>
Subscriptions = 0,

/// <summary>
/// Subscriptions update/replace
/// </summary>
SubscriptionsUpdate = 1,

/// <summary>
/// Purchase/query for in-app items on VR
/// </summary>
InAppItemsOnVR = 2,

/// <summary>
/// Purchase/query for subscriptions on VR
/// </summary>
SubscriptionsOnVR = 3,

/// <summary>
/// Launch a price change confirmation flow
/// </summary>
Expand Down
191 changes: 109 additions & 82 deletions RevenueCat/Scripts/CustomerInfo.cs
Original file line number Diff line number Diff line change
@@ -1,109 +1,136 @@
using JetBrains.Annotations;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using JetBrains.Annotations;
using RevenueCat.SimpleJSON;
using static RevenueCat.Utilities;

public partial class Purchases
namespace RevenueCat
{
///
/// <summary>
/// CustomerInfo encapsulates the current status of subscriber.
/// Use it to determine which entitlements to unlock, typically by checking
/// ActiveSubscriptions or via LatestExpirationDate.
/// </summary>
///
/// <remarks>
/// All DateTimes are in UTC, be sure to compare them with <c>DateTime.UtcNow</c>
/// </remarks>
///
[SuppressMessage("ReSharper", "UnusedMember.Global")]
public class CustomerInfo
public sealed class CustomerInfo
{
public EntitlementInfos Entitlements;
public List<string> ActiveSubscriptions;
public List<string> AllPurchasedProductIdentifiers;
public DateTime? LatestExpirationDate;
public DateTime FirstSeen;
public string OriginalAppUserId;
public DateTime RequestDate;
public DateTime? OriginalPurchaseDate;
public Dictionary<string, DateTime?> AllExpirationDates;
public Dictionary<string, DateTime?> AllPurchaseDates;
[CanBeNull] public string OriginalApplicationVersion;
[CanBeNull] public string ManagementURL;
public List<StoreTransaction> NonSubscriptionTransactions;
public Dictionary<string, SubscriptionInfo> SubscriptionsByProductIdentifier;

public CustomerInfo(JSONNode response)
[JsonProperty("entitlements")]
public EntitlementInfos Entitlements { get; }

[JsonProperty("activeSubscriptions")]
public IReadOnlyList<string> ActiveSubscriptions { get; }

[JsonProperty("allPurchasedProductIdentifiers")]
public IReadOnlyList<string> AllPurchasedProductIdentifiers { get; }

[JsonProperty("latestExpirationDateMillis")]
public long? LatestExpirationDateMillis { get; }

[JsonIgnore]
public DateTime? LatestExpirationDate
=> FromOptionalUnixTimeInMilliseconds(LatestExpirationDateMillis);

[JsonProperty("firstSeenMillis")]
public long FirstSeenMillis { get; }

[JsonIgnore]
public DateTime FirstSeen
=> FromUnixTimeInMilliseconds(FirstSeenMillis);

[JsonProperty("originalAppUserId")]
public string OriginalAppUserId { get; }

[JsonProperty("requestDateMillis")]
public long RequestDateMillis { get; }

[JsonIgnore]
public DateTime RequestDate
=> FromUnixTimeInMilliseconds(RequestDateMillis);

[JsonProperty("originalPurchaseDateMillis")]
public long? OriginalPurchaseDateMillis { get; }

[JsonIgnore]
public DateTime? OriginalPurchaseDate
=> FromOptionalUnixTimeInMilliseconds(OriginalPurchaseDateMillis);

[JsonProperty("allExpirationDatesMillis")]
public IReadOnlyDictionary<string, long?> AllExpirationDatesMillis { get; }

[JsonIgnore]
public IReadOnlyDictionary<string, DateTime?> AllExpirationDates { get; }

[JsonProperty("allPurchaseDatesMillis")]
public IReadOnlyDictionary<string, long?> AllPurchaseDatesMillis { get; }

[JsonIgnore]
public IReadOnlyDictionary<string, DateTime?> AllPurchaseDates { get; }

[CanBeNull]
[JsonProperty("originalApplicationVersion")]
public string OriginalApplicationVersion { get; }

[CanBeNull]
[JsonProperty("managementURL")]
public string ManagementURL { get; }

[JsonProperty("nonSubscriptionTransactions")]
public IReadOnlyList<StoreTransaction> NonSubscriptionTransactions { get; }

[JsonProperty("subscriptionsByProductIdentifier")]
public IReadOnlyDictionary<string, SubscriptionInfo> SubscriptionsByProductIdentifier { get; }

[JsonConstructor]
internal CustomerInfo(
[JsonProperty("entitlements")] EntitlementInfos entitlements,
[JsonProperty("activeSubscriptions")] List<string> activeSubscriptions,
[JsonProperty("allPurchasedProductIdentifiers")] List<string> allPurchasedProductIdentifiers,
[JsonProperty("latestExpirationDateMillis")] long? latestExpirationDateMillis,
[JsonProperty("firstSeenMillis")] long firstSeenMillis,
[JsonProperty("originalAppUserId")] string originalAppUserId,
[JsonProperty("requestDateMillis")] long requestDateMillis,
[JsonProperty("originalPurchaseDateMillis")] long? originalPurchaseDateMillis,
[JsonProperty("allExpirationDatesMillis")] Dictionary<string, long?> allExpirationDatesMillis,
[JsonProperty("allPurchaseDatesMillis")] Dictionary<string, long?> allPurchaseDatesMillis,
[JsonProperty("originalApplicationVersion")] string originalApplicationVersion,
[JsonProperty("managementURL")] string managementURL,
[JsonProperty("nonSubscriptionTransactions")] List<StoreTransaction> nonSubscriptionTransactions,
[JsonProperty("subscriptionsByProductIdentifier")] Dictionary<string, SubscriptionInfo> subscriptionsByProductIdentifier)
{
Entitlements = new EntitlementInfos(response["entitlements"]);
ActiveSubscriptions = new List<string>();
foreach (JSONNode subscription in response["activeSubscriptions"])
{
ActiveSubscriptions.Add(subscription);
}
Entitlements = entitlements;
ActiveSubscriptions = activeSubscriptions;
AllPurchasedProductIdentifiers = allPurchasedProductIdentifiers;
LatestExpirationDateMillis = latestExpirationDateMillis;
FirstSeenMillis = firstSeenMillis;
OriginalAppUserId = originalAppUserId;
RequestDateMillis = requestDateMillis;
OriginalPurchaseDateMillis = originalPurchaseDateMillis;
AllExpirationDatesMillis = allExpirationDatesMillis;

AllPurchasedProductIdentifiers = new List<string>();
foreach (JSONNode productIdentifier in response["allPurchasedProductIdentifiers"])
{
AllPurchasedProductIdentifiers.Add(productIdentifier);
}
var allExpirationDates = new Dictionary<string, DateTime?>();

FirstSeen = FromUnixTimeInMilliseconds(response["firstSeenMillis"].AsLong);
OriginalAppUserId = response["originalAppUserId"];
RequestDate = FromUnixTimeInMilliseconds(response["requestDateMillis"].AsLong);
OriginalPurchaseDate =
FromOptionalUnixTimeInMilliseconds(response["originalPurchaseDateMillis"].AsLong);
LatestExpirationDate =
FromOptionalUnixTimeInMilliseconds(response["latestExpirationDateMillis"].AsLong);
ManagementURL = response["managementURL"];
AllExpirationDates = new Dictionary<string, DateTime?>();
foreach (var keyValue in response["allExpirationDatesMillis"])
foreach (var (productId, value) in allExpirationDatesMillis)
{
var productID = keyValue.Key;
var expirationDateJSON = keyValue.Value;
if (expirationDateJSON != null && !expirationDateJSON.IsNull && expirationDateJSON.AsLong != 0L)
{
AllExpirationDates.Add(productID, FromUnixTimeInMilliseconds(expirationDateJSON.AsLong));
}
else
{
AllExpirationDates.Add(productID, null);
}
allExpirationDates.Add(productId, FromOptionalUnixTimeInMilliseconds(value));
}

AllPurchaseDates = new Dictionary<string, DateTime?>();
foreach (var keyValue in response["allPurchaseDatesMillis"])
{
var productID = keyValue.Key;
var purchaseDateJSON = keyValue.Value;
if (purchaseDateJSON != null && !purchaseDateJSON.IsNull && purchaseDateJSON.AsLong != 0L)
{
AllPurchaseDates.Add(productID, FromUnixTimeInMilliseconds(purchaseDateJSON.AsLong));
}
else
{
AllPurchaseDates.Add(productID, null);
}
}
AllExpirationDates = allExpirationDates;

OriginalApplicationVersion = response["originalApplicationVersion"];

NonSubscriptionTransactions = new List<StoreTransaction>();
foreach (JSONNode transactionResponse in response["nonSubscriptionTransactions"])
{
NonSubscriptionTransactions.Add(new StoreTransaction(transactionResponse));
}
var allPurchaseDates = new Dictionary<string, DateTime?>();
AllPurchaseDatesMillis = allPurchaseDatesMillis;

SubscriptionsByProductIdentifier = new Dictionary<string, SubscriptionInfo>();
foreach (var keyValue in response["subscriptionsByProductIdentifier"])
foreach (var (productId, value) in allPurchaseDatesMillis)
{
var productID = keyValue.Key;
var subscriptionInfoJSON = keyValue.Value;
SubscriptionsByProductIdentifier.Add(productID, new SubscriptionInfo(subscriptionInfoJSON));
allExpirationDates.Add(productId, FromOptionalUnixTimeInMilliseconds(value));
}

AllExpirationDates = allPurchaseDates;
OriginalApplicationVersion = originalApplicationVersion;
ManagementURL = managementURL;
NonSubscriptionTransactions = nonSubscriptionTransactions;
SubscriptionsByProductIdentifier = subscriptionsByProductIdentifier;
}

public override string ToString()
Expand Down
21 changes: 7 additions & 14 deletions RevenueCat/Scripts/DangerousSettings.cs
Original file line number Diff line number Diff line change
@@ -1,28 +1,21 @@
using System;
using RevenueCat.SimpleJSON;
using Newtonsoft.Json;

public partial class Purchases
namespace RevenueCat
{
/// <summary>
/// Advanced settings. Use only after contacting RevenueCat support and making sure you understand them.
/// </summary>
[Serializable]
public class DangerousSettings
public sealed class DangerousSettings
{
public readonly bool AutoSyncPurchases;
[JsonProperty]
public bool AutoSyncPurchases { get; }

public DangerousSettings(bool autoSyncPurchases)
[JsonConstructor]
public DangerousSettings(bool autoSyncPurchases = true)
{
AutoSyncPurchases = autoSyncPurchases;
}

public JSONNode Serialize()
{
var n = new JSONObject();
n["AutoSyncPurchases"] = AutoSyncPurchases;
return n;
}

public override string ToString()
{
return $"{nameof(AutoSyncPurchases)}: {AutoSyncPurchases}";
Expand Down
Loading