123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308 |
- using System;
- using System.Collections.Generic;
- using System.IO;
- using System.Net;
- using System.Net.Http;
- using System.Text;
- using System.Threading.Tasks;
- using SingularityGroup.HotReload.DTO;
- using SingularityGroup.HotReload.Newtonsoft.Json;
- using UnityEditor;
- using UnityEngine;
- namespace SingularityGroup.HotReload.Editor {
- internal enum RedeemStage {
- None,
- Registration,
- Redeem,
- Login
- }
- // IMPORTANT: don't rename
- internal enum RegistrationOutcome {
- None,
- Indie,
- Business,
- }
- internal class RedeemLicenseHelper {
- public static readonly RedeemLicenseHelper I = new RedeemLicenseHelper();
- private string _pendingCompanySize;
- private string _pendingInvoiceNumber;
- private string _pendingRedeemEmail;
- private const string registerFlagPath = PackageConst.LibraryCachePath + "/registerFlag.txt";
- public const string registerOutcomePath = PackageConst.LibraryCachePath + "/registerOutcome.txt";
- public RedeemStage RedeemStage { get; private set; }
- public RegistrationOutcome RegistrationOutcome { get; private set; }
- public bool RegistrationRequired => RedeemStage != RedeemStage.None;
- private string status;
- private string error;
- const string statusSuccess = "success";
- const string statusAlreadyClaimed = "already redeemed by this user/device";
- const string unknownError = "We apologize, an error happened while redeeming your license. Please reach out to customer support for assistance.";
- private GUILayoutOption[] secondaryButtonLayoutOptions = new[] { GUILayout.MaxWidth(100) };
- private bool requestingRedeem;
- private HttpClient redeemClient;
- const string redeemUrl = "https://vmhzj6jonn3qy7hk7tx7levpli0bstpj.lambda-url.us-east-1.on.aws/redeem";
- public RedeemLicenseHelper() {
- if (File.Exists(registerFlagPath)) {
- RedeemStage = RedeemStage.Registration;
- }
- try {
- if (File.Exists(registerOutcomePath)) {
- RegistrationOutcome outcome;
- if (Enum.TryParse(File.ReadAllText(registerOutcomePath), out outcome)) {
- RegistrationOutcome = outcome;
- }
- }
- } catch (Exception e) {
- Log.Warning($"Failed determining registration outcome with {e.GetType().Name}: {e.Message}");
- }
- }
- public void RenderStage(HotReloadRunTabState state) {
- if (state.redeemStage == RedeemStage.Registration) {
- RenderRegistration();
- } else if (state.redeemStage == RedeemStage.Redeem) {
- RenderRedeem();
- } else if (state.redeemStage == RedeemStage.Login) {
- RenderLogin(state);
- }
- }
- private void RenderRegistration() {
- var message = PackageConst.IsAssetStoreBuild
- ? "Unity Pro users are required to obtain an additional license. You are eligible to redeem one if your company has ten or fewer employees. Please enter your company details below."
- : "The licensing model for Unity Pro users varies depending on the number of employees in your company. Please enter your company details below.";
- if (error != null) {
- EditorGUILayout.HelpBox(error, MessageType.Warning);
- } else {
- EditorGUILayout.HelpBox(message, MessageType.Info);
- }
- EditorGUILayout.Space();
- EditorGUILayout.Space();
- EditorGUILayout.LabelField("Company size (number of employees)");
- GUI.SetNextControlName("company_size");
- _pendingCompanySize = EditorGUILayout.TextField(_pendingCompanySize)?.Trim();
- EditorGUILayout.Space();
- if (GUILayout.Button("Proceed")) {
- int companySize;
- if (!int.TryParse(_pendingCompanySize, out companySize)) {
- error = "Please enter a number.";
- } else {
- error = null;
- HandleRegistration(companySize);
- }
- }
- }
- void HandleRegistration(int companySize) {
- RequestHelper.RequestEditorEvent(new Stat(StatSource.Client, StatLevel.Debug, StatFeature.Licensing, StatEventType.Register), new EditorExtraData { { StatKey.CompanySize, companySize } });
- if (companySize > 10) {
- FinishRegistration(RegistrationOutcome.Business);
- EditorCodePatcher.DownloadAndRun().Forget();
- } else if (PackageConst.IsAssetStoreBuild) {
- SwitchToStage(RedeemStage.Redeem);
- } else {
- FinishRegistration(RegistrationOutcome.Indie);
- EditorCodePatcher.DownloadAndRun().Forget();
- }
- }
- private void RenderRedeem() {
- if (error != null) {
- EditorGUILayout.HelpBox(error, MessageType.Warning);
- } else {
- EditorGUILayout.HelpBox("To enable us to verify your purchase, please enter your invoice number/order ID. Additionally, provide the email address that you intend to use for managing your credentials.", MessageType.Info);
- }
- EditorGUILayout.Space();
- EditorGUILayout.Space();
- EditorGUILayout.LabelField("Invoice number/Order ID");
- GUI.SetNextControlName("invoice_number");
- _pendingInvoiceNumber = EditorGUILayout.TextField(_pendingInvoiceNumber ?? HotReloadPrefs.RedeemLicenseInvoice)?.Trim();
- EditorGUILayout.Space();
- EditorGUILayout.LabelField("Email");
- GUI.SetNextControlName("email_redeem");
- _pendingRedeemEmail = EditorGUILayout.TextField(_pendingRedeemEmail ?? HotReloadPrefs.RedeemLicenseEmail);
- EditorGUILayout.Space();
- using (new EditorGUI.DisabledScope(requestingRedeem)) {
- if (GUILayout.Button("Redeem", HotReloadRunTab.bigButtonHeight)) {
- RedeemLicense(email: _pendingRedeemEmail, invoiceNumber: _pendingInvoiceNumber).Forget();
- }
- }
- EditorGUILayout.Space();
- EditorGUILayout.Space();
- using (new EditorGUILayout.HorizontalScope()) {
- GUILayout.FlexibleSpace();
- if (GUILayout.Button("Skip", secondaryButtonLayoutOptions)) {
- SwitchToStage(RedeemStage.Login);
- }
- GUILayout.FlexibleSpace();
- }
- }
- async Task RedeemLicense(string email, string invoiceNumber) {
- string validationError;
- if (string.IsNullOrEmpty(invoiceNumber)) {
- validationError = "Please enter invoice number / order ID.";
- } else {
- validationError = HotReloadRunTab.ValidateEmail(email);
- }
- if (validationError != null) {
- error = validationError;
- return;
- }
- var resp = await RequestRedeem(email: email, invoiceNumber: invoiceNumber);
- status = resp?.status;
- if (status != null) {
- if (status != statusSuccess && status != statusAlreadyClaimed) {
- Log.Error("Redeeming license failed: unknown status received");
- error = unknownError;
- } else {
- HotReloadPrefs.RedeemLicenseEmail = email;
- HotReloadPrefs.RedeemLicenseInvoice = invoiceNumber;
- // prepare data for login screen
- HotReloadPrefs.LicenseEmail = email;
- HotReloadPrefs.LicensePassword = null;
- SwitchToStage(RedeemStage.Login);
- }
- } else if (resp?.error != null) {
- Log.Warning($"Redeeming a license failed with error: {resp.error}");
- error = GetPrettyError(resp);
- } else {
- Log.Warning("Redeeming a license failed: uknown error encountered");
- error = unknownError;
- }
- }
- string GetPrettyError(RedeemResponse response) {
- var err = response?.error;
- if (err == null) {
- return unknownError;
- }
- if (err.Contains("Invalid email")) {
- return "Please enter a valid email address.";
- } else if (err.Contains("License invoice already redeemed")) {
- return "The invoice number/order ID you're trying to use has already been applied to redeem a license. Please enter a different invoice number/order ID. If you have already redeemed a license for another email, you may proceed to the next step.";
- } else if (err.Contains("Different license already redeemed by given email")) {
- return "The provided email has already been used to redeem a license. If you have previously redeemed a license, you can proceed to the next step and use your existing credentials. If not, please input a different email address.";
- } else if (err.Contains("Invoice not found")) {
- return "The invoice was not found. Please ensure that you've entered the correct invoice number/order ID.";
- } else if (err.Contains("Invoice refunded")) {
- return "The purchase has been refunded. Please enter a different invoice number/order ID.";
- } else {
- return unknownError;
- }
- }
- async Task<RedeemResponse> RequestRedeem(string email, string invoiceNumber) {
- requestingRedeem = true;
- await ThreadUtility.SwitchToThreadPool();
- try {
- redeemClient = redeemClient ?? (redeemClient = HttpClientUtils.CreateHttpClient());
- var input = new Dictionary<string, string> {
- { "email", email },
- { "invoice", invoiceNumber }
- };
- var content = new StringContent(JsonConvert.SerializeObject(input), Encoding.UTF8, "application/json");
- using (var resp = await redeemClient.PostAsync(redeemUrl, content, HotReloadWindow.Current.cancelToken).ConfigureAwait(false)) {
- if (resp.StatusCode != HttpStatusCode.OK) {
- return new RedeemResponse(null, $"Redeem request failed. Status code: {(int)resp.StatusCode}, reason: {resp.ReasonPhrase}");
- }
- var str = await resp.Content.ReadAsStringAsync().ConfigureAwait(false);
- try {
- return JsonConvert.DeserializeObject<RedeemResponse>(str);
- } catch (Exception ex) {
- return new RedeemResponse(null, $"Failed deserializing redeem response with exception: {ex.GetType().Name}: {ex.Message}");
- }
- }
- } catch (WebException ex) {
- return new RedeemResponse(null, $"Redeeming license failed: WebException encountered {ex.Message}");
- } finally {
- requestingRedeem = false;
- }
- }
- private class RedeemResponse {
- public string status;
- public string error;
- public RedeemResponse(string status, string error) {
- this.status = status;
- this.error = error;
- }
- }
- private void RenderLogin(HotReloadRunTabState state) {
- if (status == statusSuccess) {
- EditorGUILayout.HelpBox("Success! You will receive an email containing your license password shortly. Once you receive it, please enter the received password in the designated field below to complete your registration.", MessageType.Info);
- } else if (status == statusAlreadyClaimed) {
- EditorGUILayout.HelpBox("Your license has already been redeemed. Please enter your existing password below.", MessageType.Info);
- }
- EditorGUILayout.Space();
- EditorGUILayout.Space();
- HotReloadRunTab.RenderLicenseInnerPanel(state, renderLogout: false);
- EditorGUILayout.Space();
- EditorGUILayout.Space();
- using (new EditorGUILayout.HorizontalScope()) {
- GUILayout.FlexibleSpace();
- if (GUILayout.Button("Go Back", secondaryButtonLayoutOptions)) {
- SwitchToStage(RedeemStage.Redeem);
- }
- GUILayout.FlexibleSpace();
- }
- }
- public void StartRegistration() {
- // ReSharper disable once AssignNullToNotNullAttribute
- Directory.CreateDirectory(Path.GetDirectoryName(registerFlagPath));
- using (File.Create(registerFlagPath)) {
- }
- RedeemStage = RedeemStage.Registration;
- RegistrationOutcome = RegistrationOutcome.None;
- }
- public void FinishRegistration(RegistrationOutcome outcome) {
- // ReSharper disable once AssignNullToNotNullAttribute
- Directory.CreateDirectory(Path.GetDirectoryName(registerFlagPath));
- File.WriteAllText(registerOutcomePath, outcome.ToString());
- File.Delete(registerFlagPath);
- RegistrationOutcome = outcome;
- SwitchToStage(RedeemStage.None);
- Cleanup();
- }
- void SwitchToStage(RedeemStage stage) {
- // remove focus so that the input field re-renders
- GUI.FocusControl(null);
- RedeemStage = stage;
- }
- void Cleanup() {
- redeemClient?.Dispose();
- redeemClient = null;
- _pendingCompanySize = null;
- _pendingInvoiceNumber = null;
- _pendingRedeemEmail = null;
- status = null;
- error = null;
- }
- }
- }
|