-
Notifications
You must be signed in to change notification settings - Fork 243
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
12a2846
commit a1cd11a
Showing
391 changed files
with
81,401 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
{ | ||
"version": "1.0", | ||
"components": [ | ||
"Microsoft.VisualStudio.Workload.ManagedGame" | ||
] | ||
} |
285 changes: 285 additions & 0 deletions
285
Samples/Unity/PlayFabEconomyV2/Assets/AndroidIAPExample.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,285 @@ | ||
using Mono.Cecil.Cil; | ||
using PlayFab; | ||
using PlayFab.ClientModels; | ||
using PlayFab.EconomyModels; | ||
using System; | ||
using System.Collections.Generic; | ||
using System.Linq; | ||
using System.Threading.Tasks; | ||
using Unity.Burst.Intrinsics; | ||
using Unity.VisualScripting; | ||
using UnityEngine; | ||
using UnityEngine.Purchasing; | ||
using UnityEngine.Purchasing.Extension; | ||
using UnityEngine.Purchasing.Security; | ||
|
||
public class AndroidIAPExample : MonoBehaviour, IDetailedStoreListener | ||
{ | ||
/** | ||
* True if the Store Controller, extensions, and Catalog are set. | ||
*/ | ||
public bool IsInitialized | ||
{ | ||
get | ||
{ | ||
return m_StoreController != null && Catalog != null; | ||
} | ||
} | ||
|
||
/** | ||
* Returns false as this is just sample code. | ||
* | ||
* @todo Implement this functionality for your game. | ||
*/ | ||
public bool UserHasExistingSave | ||
{ | ||
get | ||
{ | ||
return false; | ||
} | ||
} | ||
|
||
// Items list, configurable via inspector. | ||
private List<PlayFab.EconomyModels.CatalogItem> Catalog; | ||
|
||
private static IStoreController m_StoreController; | ||
|
||
public void BuyProductByID(string productID) | ||
{ | ||
if (!IsInitialized) throw new Exception("IAP Service is not initialized!"); | ||
|
||
m_StoreController.InitiatePurchase(productID); | ||
} | ||
|
||
private void InitializePurchasing() | ||
{ | ||
if (IsInitialized) return; | ||
|
||
var builder = ConfigurationBuilder.Instance(StandardPurchasingModule.Instance(AppStore.GooglePlay)); | ||
|
||
foreach (var item in Catalog) | ||
{ | ||
var googlePlayItemId = item.AlternateIds.FirstOrDefault(item => item.Type == "GooglePlay").Value; | ||
if (googlePlayItemId.IsUnityNull()) { | ||
builder.AddProduct(googlePlayItemId, ProductType.Consumable); | ||
} | ||
} | ||
|
||
UnityPurchasing.Initialize(this, builder); | ||
} | ||
|
||
/** | ||
* Attempts to log the player in via the Android Device ID. | ||
*/ | ||
private void Login() | ||
{ | ||
// Best practice is to soft-login with a unique ID, then prompt the player to finish | ||
// creating a PlayFab account in order to retrive cross-platform saves or other benefits. | ||
if (UserHasExistingSave) | ||
{ | ||
// @todo Integrate this with the save system. | ||
LoginWithPlayFabRequest loginWithPlayFabRequest = new() | ||
{ | ||
Username = "", | ||
Password = "" | ||
}; | ||
PlayFabClientAPI.LoginWithPlayFab(loginWithPlayFabRequest, OnRegistration, OnPlayFabError); | ||
return; | ||
} | ||
|
||
// AndroidDeviceID will prompt for permissions on newer devices. | ||
// Using a non-device specific GUID and saving to a local file | ||
// is a better approach. PlayFab does allow you to link multiple | ||
// Android device IDs to a single PlayFab account. | ||
PlayFabClientAPI.LoginWithAndroidDeviceID(new LoginWithAndroidDeviceIDRequest() | ||
{ | ||
CreateAccount = true, | ||
AndroidDeviceId = SystemInfo.deviceUniqueIdentifier | ||
}, result => { | ||
RefreshIAPItems(); | ||
}, error => Debug.LogError(error.GenerateErrorReport())); | ||
} | ||
|
||
public void OnGUI() | ||
{ | ||
// Support high-res devices. | ||
GUI.matrix = Matrix4x4.TRS(new Vector3(0, 0, 0), Quaternion.identity, new Vector3(3, 3, 3)); | ||
|
||
if (!IsInitialized) | ||
{ | ||
GUILayout.Label("Initializing IAP and logging in..."); | ||
return; | ||
} | ||
|
||
// Draw a purchase menu for each catalog item. | ||
foreach (var item in Catalog) | ||
{ | ||
if (GUILayout.Button("Buy " + item.Description)) | ||
{ | ||
BuyProductByID(item.AlternateIds.FirstOrDefault(item => item.Type == "GooglePlay").Value); | ||
} | ||
} | ||
} | ||
|
||
private void OnRegistration(LoginResult result) | ||
{ | ||
PlayFabSettings.staticPlayer.ClientSessionTicket = result.SessionTicket; | ||
} | ||
|
||
public void OnInitialized(IStoreController controller, IExtensionProvider extensions) | ||
{ | ||
m_StoreController = controller; | ||
} | ||
|
||
public void OnInitializeFailed(InitializationFailureReason error) | ||
{ | ||
Debug.Log("OnInitializeFailed InitializationFailureReason:" + error); | ||
} | ||
|
||
public void OnInitializeFailed(InitializationFailureReason error, string message) | ||
{ | ||
Debug.Log("OnInitializeFailed InitializationFailureReason:" + error + message); | ||
} | ||
|
||
private void OnPlayFabError(PlayFabError error) | ||
{ | ||
Debug.LogError(error.GenerateErrorReport()); | ||
} | ||
|
||
public void OnPurchaseFailed(UnityEngine.Purchasing.Product product, PurchaseFailureReason failureReason) | ||
{ | ||
Debug.Log(string.Format("OnPurchaseFailed: FAIL. Product: '{0}', PurchaseFailureReason: {1}", product.definition.storeSpecificId, failureReason)); | ||
} | ||
|
||
public void OnPurchaseFailed(UnityEngine.Purchasing.Product product, PurchaseFailureDescription failureDescription) | ||
{ | ||
Debug.Log(string.Format("OnPurchaseFailed: FAIL. Product: '{0}', PurchaseFailureReason: {1}", product.definition.storeSpecificId, failureDescription)); | ||
} | ||
|
||
/** | ||
* Callback for Store purchases. | ||
* | ||
* @note This code does not account for purchases that were pending and are | ||
* delivered on application start. Production code should account for these | ||
* cases. | ||
* | ||
* @see https://docs.unity3d.com/Packages/[email protected]/api/UnityEngine.Purchasing.PurchaseProcessingResult.html | ||
*/ | ||
public PurchaseProcessingResult ProcessPurchase(PurchaseEventArgs purchaseEvent) | ||
{ | ||
if (!IsInitialized) | ||
{ | ||
return PurchaseProcessingResult.Complete; | ||
} | ||
|
||
if (purchaseEvent.purchasedProduct == null) | ||
{ | ||
Debug.LogWarning("Attempted to process purchase with unknown product. Ignoring."); | ||
return PurchaseProcessingResult.Complete; | ||
} | ||
|
||
if (string.IsNullOrEmpty(purchaseEvent.purchasedProduct.receipt)) | ||
{ | ||
Debug.LogWarning("Attempted to process purchase with no receipt. Ignoring."); | ||
return PurchaseProcessingResult.Complete; | ||
} | ||
|
||
RedeemGooglePlayInventoryItemsRequest request = new() | ||
{ | ||
Purchases = new List<GooglePlayProductPurchase> { | ||
new GooglePlayProductPurchase() { | ||
ProductId = purchaseEvent.purchasedProduct.receipt, | ||
Token = GooglePurchase.FromJson(purchaseEvent.purchasedProduct.receipt).PayloadData.signature | ||
} | ||
} | ||
}; | ||
PlayFabEconomyAPI.RedeemGooglePlayInventoryItems(request, result => Debug.Log("Validation successful!"), | ||
error => Debug.Log("Validation failed: " + error.GenerateErrorReport())); | ||
|
||
return PurchaseProcessingResult.Complete; | ||
} | ||
|
||
private async void RefreshIAPItems() | ||
{ | ||
SearchItemsRequest request = new() | ||
{ | ||
Count = 50, | ||
Store = new StoreReference() { Id = "Default" } | ||
}; | ||
SearchItemsResponse response = new(); | ||
PlayFabEconomyAPIAsync asyncAPI = new(); | ||
do | ||
{ | ||
response = await asyncAPI.searchItemsAsync(request); | ||
Catalog.AddRange(response.Items); | ||
} while (response.ContinuationToken != ""); | ||
InitializePurchasing(); | ||
} | ||
|
||
// Start is called before the first frame update | ||
public void Start() | ||
{ | ||
Login(); | ||
} | ||
|
||
// Update is called once per frame | ||
public void Update() { } | ||
} | ||
|
||
// Utility classes for the sample. | ||
|
||
public class PlayFabEconomyAPIAsync | ||
{ | ||
private TaskCompletionSource<SearchItemsResponse> searchItemsAsyncTaskSource = new(); | ||
|
||
public void OnSearchItemsRequestComplete(SearchItemsResponse response) | ||
{ | ||
searchItemsAsyncTaskSource.SetResult(response); | ||
} | ||
|
||
public Task<SearchItemsResponse> searchItemsAsync(SearchItemsRequest request) { | ||
PlayFabEconomyAPI.SearchItems(request, OnSearchItemsRequestComplete, error => Debug.LogError(error.GenerateErrorReport())); | ||
return searchItemsAsyncTaskSource.Task; | ||
} | ||
} | ||
|
||
public class PurchaseJsonData | ||
{ | ||
public string orderId; | ||
public string packageName; | ||
public string productId; | ||
public long purchaseTime; | ||
public int purchaseState; | ||
public string purchaseToken; | ||
} | ||
|
||
public class PurchasePayloadData | ||
{ | ||
public PurchaseJsonData JsonData; | ||
|
||
public string signature; | ||
public string json; | ||
|
||
public static PurchasePayloadData FromJson(string json) | ||
{ | ||
var payload = JsonUtility.FromJson<PurchasePayloadData>(json); | ||
payload.JsonData = JsonUtility.FromJson<PurchaseJsonData>(json); | ||
return payload; | ||
} | ||
} | ||
|
||
public class GooglePurchase | ||
{ | ||
public PurchasePayloadData PayloadData; | ||
|
||
public string Store; | ||
public string TransactionID; | ||
public string Payload; | ||
|
||
public static GooglePurchase FromJson(string json) | ||
{ | ||
var purchase = JsonUtility.FromJson<GooglePurchase>(json); | ||
purchase.PayloadData = PurchasePayloadData.FromJson(purchase.Payload); | ||
return purchase; | ||
} | ||
} |
11 changes: 11 additions & 0 deletions
11
Samples/Unity/PlayFabEconomyV2/Assets/AndroidIAPExample.cs.meta
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
9 changes: 9 additions & 0 deletions
9
Samples/Unity/PlayFabEconomyV2/Assets/PlayFabEditorExtensions.meta
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
9 changes: 9 additions & 0 deletions
9
Samples/Unity/PlayFabEconomyV2/Assets/PlayFabEditorExtensions/Editor.meta
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
Oops, something went wrong.