123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549 |
- using System;
- using System.Collections.Generic;
- using System.Reflection;
- using System.Text;
- using System.Text.RegularExpressions;
- using UnityEngine;
- using UnityEngine.Networking;
- using UnityEngine.Purchasing;
-
- namespace UnityEditor.Purchasing
- {
- [Obsolete("UDP support will be removed in the next major update of In-App Purchasing. Right now, the UDP SDK will still function normally in tandem with IAP.")]
- /// <summary>
- /// Synchronize store data from UDP and IAP
- /// </summary>
- public static class UdpSynchronizationApi
- {
- internal const string kOAuthClientId = "channel_editor";
-
- // Although a client secret is here, it doesn't matter
- // because the user information is also secured by user's token
- private const string kOAuthClientSecret = "B63AFB324DE3D12A13827340019D1EE3";
-
- private const string kHttpVerbGET = "GET";
- private const string kHttpVerbPOST = "POST";
- private const string kHttpVerbPUT = "PUT";
-
- private const string kContentType = "Content-Type";
- private const string kApplicationJson = "application/json";
- private const string kAuthHeader = "Authorization";
-
- private static void CheckUdpBuildConfig()
- {
- var udpBuildConfig = BuildConfigInterface.GetClassType();
- if (udpBuildConfig == null)
- {
- Debug.LogError("Cannot Retrieve Build Config Endpoints for UDP. Please make sure the UDP package is installed");
- throw new NotImplementedException();
- }
- }
-
- /// <summary>
- /// Get Access Token according to authCode.
- /// </summary>
- /// <param name="authCode"> Acquired by UnityOAuth</param>
- /// <returns></returns>
- [Obsolete("Internal API, it will be removed soon.")]
- public static object GetAccessToken(string authCode)
- {
- return CreateGetAccessTokenRequest(authCode);
- }
-
- /// <summary>
- /// Create a Web Request to get the UDP Access Token according to authCode.
- /// </summary>
- /// <param name="authCode">Acquired by UnityOAuth</param>
- /// <returns></returns>
- internal static UnityWebRequest CreateGetAccessTokenRequest(string authCode)
- {
- CheckUdpBuildConfig();
-
- var req = new TokenRequest
- {
- code = authCode,
- client_id = kOAuthClientId,
- client_secret = kOAuthClientSecret,
- grant_type = "authorization_code",
- redirect_uri = BuildConfigInterface.GetIdEndpoint()
- };
- return AsyncRequest(kHttpVerbPOST, BuildConfigInterface.GetApiEndpoint(), "/v1/oauth2/token", null, req);
- }
-
- /// <summary>
- /// Call UDP store asynchronously to retrieve the Organization Identifier.
- /// </summary>
- /// <param name="accessToken">The bearer token to UDP.</param>
- /// <param name="projectGuid">The project id.</param>
- /// <returns>The HTTP GET Request to get the organization identifier.</returns>
- [Obsolete("Internal API, it will be removed soon.")]
- public static object GetOrgId(string accessToken, string projectGuid)
- {
- return CreateGetOrgIdRequest(accessToken, projectGuid);
- }
-
- /// <summary>
- /// Call UDP store asynchronously to retrieve the Organization Identifier.
- /// </summary>
- /// <param name="accessToken">The bearer token to UDP.</param>
- /// <param name="projectGuid">The project id.</param>
- /// <returns>The HTTP GET Request to get the organization identifier.</returns>
- internal static UnityWebRequest CreateGetOrgIdRequest(string accessToken, string projectGuid)
- {
- CheckUdpBuildConfig();
-
- var api = "/v1/core/api/projects/" + projectGuid;
- return AsyncRequest(kHttpVerbGET, BuildConfigInterface.GetApiEndpoint(), api, accessToken, null);
- }
-
- /// <summary>
- /// Call UDP store asynchronously to create a store item.
- /// </summary>
- /// <param name="accessToken">The bearer token to UDP.</param>
- /// <param name="orgId">The organization identifier to create the store item under.</param>
- /// <param name="iapItem">The store item to create.</param>
- /// <returns>The HTTP POST Request to create a store item.</returns>
- [Obsolete("Internal API, it will be removed soon.")]
- public static object CreateStoreItem(string accessToken, string orgId, IapItem iapItem)
- {
- return CreateAddStoreItemRequest(accessToken, orgId, iapItem);
- }
-
- /// <summary>
- /// Call UDP store asynchronously to create a store item.
- /// </summary>
- /// <param name="accessToken">The bearer token to UDP.</param>
- /// <param name="orgId">The organization identifier to create the store item under.</param>
- /// <param name="iapItem">The store item to create.</param>
- /// <returns>The HTTP POST Request to create a store item.</returns>
- internal static UnityWebRequest CreateAddStoreItemRequest(string accessToken, string orgId, IapItem iapItem)
- {
- CheckUdpBuildConfig();
-
- var api = "/v1/store/items";
- iapItem.ownerId = orgId;
- return AsyncRequest(kHttpVerbPOST, BuildConfigInterface.GetUdpEndpoint(), api, accessToken, iapItem);
- }
-
- /// <summary>
- /// Call UDP store asynchronously to update a store item.
- /// </summary>
- /// <param name="accessToken">The bearer token to UDP.</param>
- /// <param name="iapItem">The updated store item.</param>
- /// <returns>The HTTP PUT Request to update a store item.</returns>
- [Obsolete("Internal API, it will be removed soon.")]
- public static object UpdateStoreItem(string accessToken, IapItem iapItem)
- {
- return CreateUpdateStoreItemRequest(accessToken, iapItem);
- }
-
- /// <summary>
- /// Call UDP store asynchronously to update a store item.
- /// </summary>
- /// <param name="accessToken">The bearer token to UDP.</param>
- /// <param name="iapItem">The updated store item.</param>
- /// <returns>The HTTP PUT Request to update a store item.</returns>
- internal static UnityWebRequest CreateUpdateStoreItemRequest(string accessToken, IapItem iapItem)
- {
- CheckUdpBuildConfig();
-
- var api = "/v1/store/items/" + iapItem.id;
- return AsyncRequest(kHttpVerbPUT, BuildConfigInterface.GetUdpEndpoint(), api, accessToken, iapItem);
- }
-
- /// <summary>
- /// Call UDP store asynchronously to search for a store item.
- /// </summary>
- /// <param name="accessToken">The bearer token to UDP.</param>
- /// <param name="orgId">The organization identifier where to find the store item.</param>
- /// <param name="appItemSlug">The store item slug name.</param>
- /// <returns>The HTTP GET Request to update a store item.</returns>
- [Obsolete("Internal API, it will be removed soon.")]
- public static object SearchStoreItem(string accessToken, string orgId, string appItemSlug)
- {
- return CreateSearchStoreItemRequest(accessToken, orgId, appItemSlug);
- }
-
- /// <summary>
- /// Call UDP store asynchronously to search for a store item.
- /// </summary>
- /// <param name="accessToken">The bearer token to UDP.</param>
- /// <param name="orgId">The organization identifier where to find the store item.</param>
- /// <param name="appItemSlug">The store item slug name.</param>
- /// <returns>The HTTP GET Request to update a store item.</returns>
- internal static UnityWebRequest CreateSearchStoreItemRequest(string accessToken, string orgId, string appItemSlug)
- {
- CheckUdpBuildConfig();
-
- var api = "/v1/store/items/search?ownerId=" + orgId +
- "&ownerType=ORGANIZATION&start=0&count=20&type=IAP&masterItemSlug=" + appItemSlug;
- return AsyncRequest(kHttpVerbGET, BuildConfigInterface.GetUdpEndpoint(), api, accessToken, null);
- }
-
- // Return UnityWebRequest instance
- static UnityWebRequest AsyncRequest(string method, string url, string api, string token, object postObject)
- {
- var request = new UnityWebRequest(url + api, method);
-
- if (postObject != null)
- {
- var postData = HandlePostData(JsonUtility.ToJson(postObject));
- var postDataBytes = Encoding.UTF8.GetBytes(postData);
-
- request.uploadHandler = new UploadHandlerRaw(postDataBytes);
- }
-
- request.downloadHandler = new DownloadHandlerBuffer();
-
- request.SetRequestHeader(kContentType, kApplicationJson);
- if (token != null)
- {
- request.SetRequestHeader(kAuthHeader, "Bearer " + token);
- }
-
- request.SendWebRequest();
-
- return request;
- }
-
- internal static bool CheckUdpAvailability()
- {
- return true;
- }
-
- internal static bool CheckUdpCompatibility()
- {
- var udpBuildConfig = BuildConfigInterface.GetClassType();
- if (udpBuildConfig == null)
- {
- Debug.LogError("Cannot Retrieve Build Config Endpoints for UDP. Please make sure the UDP package is installed");
- return false;
- }
-
- var udpVersion = BuildConfigInterface.GetVersion();
- int.TryParse(udpVersion.Split('.')[0], out var majorVersion);
-
- return majorVersion >= 2;
- }
-
- // A very tricky way to deal with the json string, need to be improved
- // en-US and zh-CN will appear in the JSON and Unity JsonUtility cannot
- // recognize them to variables. So we change this to a string (remove "-").
- private static string HandlePostData(string oldData)
- {
- var newData = oldData.Replace("thisShouldBeENHyphenUS", "en-US");
- newData = newData.Replace("thisShouldBeZHHyphenCN", "zh-CN");
- var re = new Regex("\"\\w+?\":\"\",");
- newData = re.Replace(newData, "");
- re = new Regex(",\"\\w+?\":\"\"");
- newData = re.Replace(newData, "");
- re = new Regex("\"\\w+?\":\"\"");
- newData = re.Replace(newData, "");
- return newData;
- }
- }
-
- #region model
-
- /// <summary>
- /// This class is used to authenticate the API call to UDP. In OAuth2.0 authentication format.
- /// </summary>
- [Serializable]
- public class TokenRequest
- {
- /// <summary>
- /// The access token. Acquired by UnityOAuth
- /// </summary>
- public string code;
- /// <summary>
- /// The client identifier
- /// </summary>
- public string client_id;
- /// <summary>
- /// The client secret key
- /// </summary>
- public string client_secret;
- /// <summary>
- /// The type of OAuth2.0 code granting.
- /// </summary>
- public string grant_type;
- /// <summary>
- /// Redirect use after a successful authorization.
- /// </summary>
- public string redirect_uri;
- /// <summary>
- /// When the access token is expire. This token is used to renew it.
- /// </summary>
- public string refresh_token;
- }
-
- /// <summary>
- /// PriceSets holds the PurchaseFee. Used for IapItem.
- /// </summary>
- [Serializable]
- public class PriceSets
- {
- /// <summary>
- /// Get the PurchaseFee
- /// </summary>
- public PurchaseFee PurchaseFee = new PurchaseFee();
- }
-
- /// <summary>
- /// A PurchaseFee contains the PriceMap which contains the prices and currencies.
- /// </summary>
- [Serializable]
- public class PurchaseFee
- {
- /// <summary>
- /// The PurchaseFee type
- /// </summary>
- public string priceType = "CUSTOM";
- /// <summary>
- /// Holds a list of prices with their currencies
- /// </summary>
- public PriceMap priceMap = new PriceMap();
- }
-
- /// <summary>
- /// PriceMap hold a list of PriceDetail.
- /// </summary>
- [Serializable]
- public class PriceMap
- {
- /// <summary>
- /// List of prices with their currencies.
- /// </summary>
- public List<PriceDetail> DEFAULT = new List<PriceDetail>();
- }
-
- /// <summary>
- /// Price and the currency of a IAPItem.
- /// </summary>
- [Serializable]
- public class PriceDetail
- {
- /// <summary>
- /// Price of a IAPItem.
- /// </summary>
- public string price;
- /// <summary>
- /// Currency of the price.
- /// </summary>
- public string currency = "USD";
- }
-
- /// <summary>
- /// The Response from and HTTP response converted into an object.
- /// </summary>
- [Serializable]
- public class GeneralResponse
- {
- /// <summary>
- /// The body from the HTTP response.
- /// </summary>
- public string message;
- }
-
- /// <summary>
- /// The properties of a IAPItem.
- /// </summary>
- [Serializable]
- public class Properties
- {
- /// <summary>
- /// The description of a IAPItem.
- /// </summary>
- public string description;
- }
-
- /// <summary>
- /// The response used when creating/updating IAP item succeeds
- /// </summary>
- [Serializable]
- public class IapItemResponse : GeneralResponse
- {
- /// <summary>
- /// The IapItem identifier.
- /// </summary>
- public string id;
- }
-
- /// <summary>
- /// IapItem is the representation of a purchasable product from the UDP store
- /// </summary>
- [Serializable]
- public class IapItem
- {
- /// <summary>
- /// A unique identifier to identify the product.
- /// </summary>
- public string id;
- /// <summary>
- /// The product url stripped of all unsafe characters.
- /// </summary>
- public string slug;
- /// <summary>
- /// The product name.
- /// </summary>
- public string name;
- /// <summary>
- /// The organization url stripped of all unsafe characters.
- /// </summary>
- public string masterItemSlug;
- /// <summary>
- /// Is product a consumable type. If set to false it is a subscriptions.
- /// Consumables may be purchased more than once.
- /// Subscriptions have a finite window of validity.
- /// </summary>
- public bool consumable = true;
- /// <summary>
- /// The product type.
- /// </summary>
- public string type = "IAP";
- /// <summary>
- /// The product status.
- /// </summary>
- public string status = "STAGE";
- /// <summary>
- /// The organization id.
- /// </summary>
- public string ownerId;
- /// <summary>
- /// The organization type.
- /// </summary>
- public string ownerType = "ORGANIZATION";
-
- /// <summary>
- /// The product's prices with currencies.
- /// </summary>
- public PriceSets priceSets = new PriceSets();
-
- /// <summary>
- /// The properties of the product.
- /// </summary>
- public Properties properties = new Properties();
-
- /// <summary>
- /// Validates that the IapItem has at least the minimum amount of information set.
- /// </summary>
- /// <returns>A string error of missing information to the IapItem.</returns>
- public string ValidationCheck()
- {
- if (string.IsNullOrEmpty(slug))
- {
- return "Please fill in the ID";
- }
-
- if (string.IsNullOrEmpty(name))
- {
- return "Please fill in the title";
- }
-
- if (properties == null || string.IsNullOrEmpty(properties.description))
- {
- return "Please fill in the description";
- }
-
- return "";
- }
- }
-
- /// <summary>
- /// TokenInfo holds all the authentication token required to authenticate the API call.
- /// </summary>
- [Serializable]
- public class TokenInfo : GeneralResponse
- {
- /// <summary>
- /// The OAuth2.0 access token.
- /// </summary>
- public string access_token;
- /// <summary>
- /// The OAuth2.0 refresh token.
- /// </summary>
- public string refresh_token;
- }
-
- /// <summary>
- /// The response used when searching for IAP item.
- /// </summary>
- [Serializable]
- public class IapItemSearchResponse : GeneralResponse
- {
- /// <summary>
- /// The total amount of IAP item found.
- /// </summary>
- public int total;
- /// <summary>
- /// The list of IAP item found.
- /// </summary>
- public List<IapItem> results;
- }
-
- struct ReqStruct
- {
- public UnityWebRequest request;
- public GeneralResponse resp;
- public ProductCatalogEditor.ProductCatalogItemEditor itemEditor;
- public IapItem iapItem;
- }
-
- /// <summary>
- /// The response used when searching for Organization identifier.
- /// </summary>
- [Serializable]
- public class OrgIdResponse : GeneralResponse
- {
- /// <summary>
- /// The organization identifier.
- /// </summary>
- public string org_foreign_key;
- }
-
- /// <summary>
- /// The response used when searching for Organization roles.
- /// </summary>
- [Serializable]
- public class OrgRoleResponse : GeneralResponse
- {
- /// <summary>
- /// The organization roles.
- /// </summary>
- public List<string> roles;
- }
-
- /// <summary>
- /// The response used when getting an error.
- /// </summary>
- [Serializable]
- public class ErrorResponse : GeneralResponse
- {
- /// <summary>
- /// The http error code.
- /// </summary>
- public string code;
- /// <summary>
- /// The details of an error.
- /// </summary>
- public ErrorDetail[] details;
- }
-
- /// <summary>
- /// The details of an error return from the api.
- /// </summary>
- [Serializable]
- public class ErrorDetail
- {
- /// <summary>
- /// The error context where it occured.
- /// </summary>
- public string field;
- /// <summary>
- /// The error message reason.
- /// </summary>
- public string reason;
- }
-
- #endregion
- }
|