05-12-2024 11:53 PM
Hi,
I want to implement Meta Quest IAP workflow with following steps.
1.User will purchase the IAP durable item
2.Durable item is Asset bundle of a scene as in different levels
3.Once purchase is completed i want to start downloading Assets Bundles attached to that IAP as DLC .
4. While downloading i want to see the download progress
5.Once downloaded it should get stored locally
6.It should load the scene on button click.
I have tried to implement this flow here is the code
using UnityEngine;
using Oculus.Platform;
using Oculus.Platform.Models;
using System.Collections.Generic;
using TMPro;
using System;
using UnityEngine.SceneManagement;
using System.Collections;
using System.Linq;
using System.IO;
public class IAPManager : MonoBehaviour
{
public TextMeshPro debug, purchaseData;
private void Start()
{
// Initialize the Oculus Platform SDK
Core.Initialize();
// Fetch available IAPs
FetchIAPs();
// Fetch purchased IAPs
FetchPurchasedIAPs();
}
private void FetchIAPs()
{
IAP.GetProductsBySKU(new List<string>() {"arcade_range" }.ToArray()).OnComplete(GetProductsCallback);
}
private void FetchPurchasedIAPs()
{
Entitlements.IsUserEntitledToApplication().OnComplete(r =>
{
if (r.IsError)
{
Debug.Log("Error entitling user to the application: " + r.GetError().Message);
}
});
IAP.GetViewerPurchases().OnComplete(GetPurchasedItemsCallback);
}
private void GetProductsCallback(Message<ProductList> message)
{
if (message.IsError)
{
Debug.LogError("Failed to fetch IAP products: " + message.GetError().Message);
// debug.text += "\n"+"Failed to fetch IAP products: " + message.GetError().Message;
return;
}
Debug.Log("Available IAPs:");
foreach (var product in message.Data)
{
Debug.Log($"Product: {product.Name}, Price: {product.FormattedPrice}");
// debug.text += "\n" + $"Product: {product.Name}, Price: {product.FormattedPrice}";
}
}
private void GetPurchasedItemsCallback(Message<PurchaseList> message)
{
if (message.IsError)
{
Debug.LogError("Failed to fetch purchased IAPs: " + message.GetError().Message);
purchaseData.text += "\n" + "Failed to fetch purchased IAPs: " + message.GetError().Message;
return;
}
else
{
OnPurchasesRetrieved();
// foreach (var purchase in message.Data)
//{
// Debug.Log($"Purchase: {purchase.Sku}, GrantTime: {purchase.GrantTime}");
// purchaseData.text += "\n" + $"Purchase: {purchase.Sku},Purchase: {purchase.ID}, GrantTime: {purchase.GrantTime}";
// if (String.Compare(purchase.Sku, "arcade_range") == 0)
// {
// purchaseData.text += "\n Downloading" + $"Purchase: {purchase.Sku}";
// AssetFile.DownloadByName(purchase.Sku).OnComplete(getDownloadedItemsCallBack);
// }
// }
}
//Debug.Log("Purchased IAPs:");
//foreach (var purchase in message.Data)
//{
// Debug.Log($"Purchase: {purchase.Sku}, GrantTime: {purchase.GrantTime}");
// purchaseData.text += "\n" + $"Purchase: {purchase.Sku},Purchase: {purchase.ID}, GrantTime: {purchase.GrantTime}";
// // AssetFile.Download(ulong.Parse(purchase.ID)).OnComplete(getDownloadedItemsCallBack);
//}
}
void OnPurchasesRetrieved()
{
Debug.Log("Finished checking purchased DLC. Retrieving downloadable assets");
GetOculusAssetFileList();
}
public void GetOculusAssetFileList()
{
AssetFile.GetList().OnComplete((Message<AssetDetailsList> msg) =>
{
if (msg.IsError)
{
Debug.LogError("Error retrieving DLC information: " + msg.GetError().Message);
purchaseData.text += "\n" + "Error retrieving DLC information";
return;
}
List<ulong> assetIds = new List<ulong>();
Dictionary<string, string> newSkuToFilepathMap = new Dictionary<string, string>();
FetchPurchasedProducts((purchasedSkus) =>
{
foreach (var assetDetail in msg.Data)
{
Debug.Log($"AssetID: {assetDetail.AssetId}, Filepath: {assetDetail.Filepath}");
purchaseData.text += "\n" + $"AssetID: {assetDetail.AssetId}, Filepath: {assetDetail.AssetId}";
// AssetFile.Download(assetDetail.AssetId).OnComplete(getDownloadedItemsCallBack);
purchaseData.text += "\n" + Path.GetFileNameWithoutExtension(assetDetail.Filepath);
StartCoroutine(LoadSceneFromBundle(assetDetail.Filepath, "ProShooterVR_ArcadeMode"));
//if (!string.IsNullOrEmpty(assetDetail.Filepath))
//{
// var filepathParts = assetDetail.Filepath.Split('/');
// var filename = filepathParts.get
// var sku = Path.GetFileNameWithoutExtension(filename);
// if (thumbnailSKUs.Contains(sku))
// {
// newSkuToFilepathMap[sku] = assetDetail.Filepath;
// bool isDownloaded = File.Exists(assetDetail.Filepath);
// skuDownloadStatus[sku] = isDownloaded;
// assetIds.Add(assetDetail.AssetId);
// skuToAssetIdMap[sku] = assetDetail.AssetId;
// skuPurchaseStatus[sku] = purchasedSkus.Contains(sku);
// }
//}
}
//skuToFilepathMap = newSkuToFilepathMap;
//dlcAssetIds = assetIds.ToArray();
});
});
}
IEnumerator LoadSceneFromBundle(string bundlePath, string sceneName)
{
if (!System.IO.File.Exists(bundlePath))
{
Debug.LogError("Bundle file does not exist: " + bundlePath);
purchaseData.text += "\n" + "Bundle file does not exist:";
yield break;
}
// Load the asset bundle asynchronously
AssetBundleCreateRequest bundleLoadRequest = AssetBundle.LoadFromFileAsync(bundlePath);
yield return bundleLoadRequest;
AssetBundle bundle = bundleLoadRequest.assetBundle;
if (bundle == null)
{
Debug.LogError("Failed to load AssetBundle!");
purchaseData.text += "\n" + "Failed to load AssetBundle!";
yield break;
}
// Check if the scene is in the bundle
if (!bundle.Contains(sceneName))
{
Debug.LogError("Scene not found in the bundle!");
purchaseData.text += "\n" + "Scene not found in the bundle!";
yield break;
}
// Load the scene asynchronously
string scenePath = bundle.GetAllScenePaths()[0]; // Assuming the scene is the first one listed in the asset bundle
AsyncOperation asyncLoad = SceneManager.LoadSceneAsync(scenePath);
// Wait until the scene has loaded
while (!asyncLoad.isDone)
{
yield return null;
}
// Optionally unload the asset bundle to free memory
// bundle.Unload(false);
}
private void FetchPurchasedProducts(Action<List<string>> onComplete)
{
IAP.GetViewerPurchases().OnComplete((Message<PurchaseList> msg) =>
{
if (msg.IsError)
{
Debug.LogError("Error checking purchased DLC: " + msg.GetError().Message);
onComplete(new List<string>());
return;
}
List<string> purchasedSkus = msg.Data.Select(p => p.Sku).ToList();
onComplete(purchasedSkus);
});
}
/// <summary>
/// //////////////////////////////////////////////////////////////
/// </summary>
/// <param name="message"></param>
private void getDownloadedItemsCallBack(Message<AssetFileDownloadResult> message)
{
if(message.IsError)
{
purchaseData.text += "\n" + "Failed to download purchased IAPs: " + message.GetError().Message;
return;
}
else
{
purchaseData.text += "\n" + "Downloading waait";
purchaseData.text += "\n" + message.GetAppDownloadProgressResult().StatusCode.ToString();
var asset = message.GetAssetDetails();
LoadSceneFromAssetBundle(asset.Filepath);
}
}
/// <summary>
/// This Method will call the purchase flow API of Meta
/// </summary>
public void BuyItem()
{
IAP.LaunchCheckoutFlow(sku: "arcade_range").OnComplete(BuyItemCallback);
}
private void BuyItemCallback(Message<Purchase> message)
{
if (message.IsError)
{
return;
}
else
{
}
Purchase p = message.GetPurchase();
Debug.Log("purchased " + p.ID);
ulong assetFileId = ulong.Parse(p.ID); // Replace with your actual asset file ID
DownloadAssetById(assetFileId);
// purchaseData.text = string.Empty;
// FetchPurchasedIAPs();
}
private void DownloadAssetById(ulong assetFileId)
{
AssetFile.DownloadById(assetFileId).OnComplete(AssetDownloadComplete);
}
private void AssetDownloadComplete(Message<AssetFileDownloadResult> message)
{
if (message.IsError)
{
Debug.LogError("Failed to download asset file: " + message.GetError().Message);
return;
}
else
{
purchaseData.text += "\n" + "Downloading waait";
var asset = message.GetAssetDetails();
LoadSceneFromAssetBundle(asset.Filepath);
}
}
private void LoadSceneFromAssetBundle(string assetBundleUri)
{
StartCoroutine(LoadYourAsyncScene(assetBundleUri));
}
IEnumerator LoadYourAsyncScene(string assetBundleUri)
{
// Load the AssetBundle file from cache if it exists with the specified version number
AssetBundle myLoadedAssetBundle = AssetBundle.LoadFromFile(assetBundleUri);
if (myLoadedAssetBundle == null)
{
Debug.Log("Failed to load AssetBundle!");
purchaseData.text += "\n" + "Failed to load AssetBundle";
yield break;
}
string[] scenePaths = myLoadedAssetBundle.GetAllScenePaths();
string sceneName = System.IO.Path.GetFileNameWithoutExtension(scenePaths[0]);
Debug.Log("Loading scene " + sceneName);
purchaseData.text += "\n" + "Loading";
// Load the scene asynchronously in the background
AsyncOperation asyncLoad = SceneManager.LoadSceneAsync(sceneName);
while (!asyncLoad.isDone)
{
yield return null;
}
Debug.Log("Scene loaded");
// Optionally, unload the asset bundle to free up memory after the scene has loaded
myLoadedAssetBundle.Unload(false);
}
}
//void OnPurchasesRetrieved()
//{
// Debug.Log("Finished checking purchased DLC. Retrieving downloadable assets");
// GetOculusAssetFileList();
//}
//public void GetOculusAssetFileList()
//{
// AssetFile.GetList().OnComplete((Message<AssetDetailsList> msg) =>
// {
// if (msg.IsError)
// {
// Debug.LogError("Error retrieving DLC information: " + msg.GetError().Message);
// loadStatus.text = "Error retrieving DLC information";
// return;
// }
// List<ulong> assetIds = new List<ulong>();
// Dictionary<string, string> newSkuToFilepathMap = new Dictionary<string, string>();
// FetchPurchasedProducts((purchasedSkus) =>
// {
// foreach (var assetDetail in msg.Data)
// {
// Debug.Log($"AssetID: {assetDetail.AssetId}, Filepath: {assetDetail.Filepath}");
// if (!string.IsNullOrEmpty(assetDetail.Filepath))
// {
// var filepathParts = assetDetail.Filepath.Split('/');
// var filename = filepathParts.LastOrDefault();
// var sku = Path.GetFileNameWithoutExtension(filename);
// if (thumbnailSKUs.Contains(sku))
// {
// newSkuToFilepathMap[sku] = assetDetail.Filepath;
// bool isDownloaded = File.Exists(assetDetail.Filepath);
// skuDownloadStatus[sku] = isDownloaded;
// assetIds.Add(assetDetail.AssetId);
// skuToAssetIdMap[sku] = assetDetail.AssetId;
// skuPurchaseStatus[sku] = purchasedSkus.Contains(sku);
// }
// }
// }
// skuToFilepathMap = newSkuToFilepathMap;
// dlcAssetIds = assetIds.ToArray();
// });
// });
//}
//private void FetchPurchasedProducts(Action<List<string>> onComplete)
//{
// IAP.GetViewerPurchases().OnComplete((Message<PurchaseList> msg) =>
// {
// if (msg.IsError)
// {
// Debug.LogError("Error checking purchased DLC: " + msg.GetError().Message);
// onComplete(new List<string>());
// return;
// }
// List<string> purchasedSkus = msg.Data.Select(p => p.Sku).ToList();
// onComplete(purchasedSkus);
// });
//}
Please Help Us implement this.