cancel
Showing results for 
Search instead for 
Did you mean: 

IAP Implementation for Durable Items

AyodhyanandanB
Honored Guest

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.

0 REPLIES 0