我正在我的统一游戏中实现IAP,并且当我在PC 上测试IAP时,如您在屏幕截图中所见,IAP正常运行,但是当我在具有测试产品的真实Android设备上对其进行测试来自Google public string NO_ADS = "android.test.purchased";
的ID名称,它在真正的android设备上给我这个错误,但在PC上运行正常,所以为什么这是我丢失的东西呢?这是我的代码
IAPManager
public class IAPManager : Singleton<IAPManager> , IStoreListener
{
private static IStoreController m_StoreController;
private static IExtensionProvider m_StoreExtensionProvider;
//public static string kProductIDConsumable = "consumable";
public string NO_ADS = "android.test.purchased"; // Non consumable
//public static string kProductIDSubscription = "subscription";
//// Apple App Store-specific product identifier for the subscription product.
//private static string kProductNameAppleSubscription = "com.unity3d.subscription.new";
//// Google Play Store-specific product identifier subscription product.
//private static string kProductNameGooglePlaySubscription =
"com.unity3d.subscription.original";
void Start()
{
// If we haven't set up the Unity Purchasing reference
if (m_StoreController == null)
{
// Begin to configure our connection to Purchasing
InitializePurchasing();
}
}
public void InitializePurchasing()
{
// If we have already connected to Purchasing ...
if (IsInitialized())
{
// ... we are done here.
return;
}
// Create a builder, first passing in a suite of Unity provided stores.
var builder = ConfigurationBuilder.Instance(StandardPurchasingModule.Instance());
// Add a product to sell / restore by way of its identifier, associating the general identifier
// with its store-specific identifiers.
//builder.AddProduct(kProductIDConsumable, ProductType.Consumable);
// Continue adding the non-consumable product.
builder.AddProduct(NO_ADS, ProductType.NonConsumable);
// And finish adding the subscription product. Notice this uses store-specific IDs, illustrating
// if the Product ID was configured differently between Apple and Google stores. Also note that
// one uses the general kProductIDSubscription handle inside the game - the store-specific IDs
// must only be referenced here.
//builder.AddProduct(kProductIDSubscription, ProductType.Subscription, new IDs(){
// { kProductNameAppleSubscription, AppleAppStore.Name },
// { kProductNameGooglePlaySubscription, GooglePlay.Name },
// });
// Kick off the remainder of the set-up with an asynchrounous call, passing the configuration
// and this class' instance. Expect a response either in OnInitialized or OnInitializeFailed.
UnityPurchasing.Initialize(this, builder);
}
public bool IsInitialized()
{
// Only say we are initialized if both the Purchasing references are set.
return m_StoreController != null && m_StoreExtensionProvider != null;
}
//public void BuyConsumable()
//{
// Buy the consumable product using its general identifier.Expect a response either
// through ProcessPurchase or OnPurchaseFailed asynchronously.
// BuyProductID(kProductIDConsumable);
//}
public void BuyNoAds()
{
// Buy the non-consumable product using its general identifier. Expect a response either
// through ProcessPurchase or OnPurchaseFailed asynchronously.
BuyProductID(NO_ADS);
}
public string GetProductPriceFromStore(string id)
{
if(m_StoreController!=null && m_StoreController.products != null)
{
return m_StoreController.products.WithID(id).metadata.localizedPriceString;
}
else
{
return "";
}
}
//public void BuySubscription()
//{
// // Buy the subscription product using its the general identifier. Expect a response either
// // through ProcessPurchase or OnPurchaseFailed asynchronously.
// // Notice how we use the general product identifier in spite of this ID being mapped to
// // custom store-specific identifiers above.
// BuyProductID(kProductIDSubscription);
//}
void BuyProductID(string productId)
{
// If Purchasing has been initialized ...
if (IsInitialized())
{
// ... look up the Product reference with the general product identifier and the Purchasing
// system's products collection.
Product product = m_StoreController.products.WithID(productId);
// If the look up found a product for this device's store and that product is ready to be sold ...
if (product != null && product.availableToPurchase)
{
Debug.Log(string.Format("Purchasing product asychronously: '{0}'", product.definition.id));
// ... buy the product. Expect a response either through ProcessPurchase or OnPurchaseFailed
// asynchronously.
m_StoreController.InitiatePurchase(product);
}
// Otherwise ...
else
{
// ... report the product look-up failure situation
Debug.Log("BuyProductID: FAIL. Not purchasing product, either is not found or is not available for purchase");
}
}
// Otherwise ...
else
{
// ... report the fact Purchasing has not succeeded initializing yet. Consider waiting longer or
// retrying initiailization.
Debug.Log("BuyProductID FAIL. Not initialized.");
}
}
// Restore purchases previously made by this customer. Some platforms automatically restore purchases, like Google.
// Apple currently requires explicit purchase restoration for IAP, conditionally displaying a password prompt.
public void RestorePurchases()
{
// If Purchasing has not yet been set up ...
if (!IsInitialized())
{
// ... report the situation and stop restoring. Consider either waiting longer, or retrying initialization.
Debug.Log("RestorePurchases FAIL. Not initialized.");
return;
}
// If we are running on an Apple device ...
if (Application.platform == RuntimePlatform.IPhonePlayer ||
Application.platform == RuntimePlatform.OSXPlayer)
{
// ... begin restoring purchases
Debug.Log("RestorePurchases started ...");
// Fetch the Apple store-specific subsystem.
var apple = m_StoreExtensionProvider.GetExtension<IAppleExtensions>();
// Begin the asynchronous process of restoring purchases. Expect a confirmation response in
// the Action<bool> below, and ProcessPurchase if there are previously purchased products to
restore.
apple.RestoreTransactions((result) => {
// The first phase of restoration. If no more responses are received on ProcessPurchase
then
// no purchases are available to be restored.
Debug.Log("RestorePurchases continuing: " + result + ". If no further messages, no
purchases available to restore.");
});
}
// Otherwise ...
else
{
// We are not running on an Apple device. No work is necessary to restore purchases.
Debug.Log("RestorePurchases FAIL. Not supported on this platform. Current = " +
Application.platform);
}
}
//
// --- IStoreListener
//
public void OnInitialized(IStoreController controller, IExtensionProvider extensions)
{
// Purchasing has succeeded initializing. Collect our Purchasing references.
Debug.Log("OnInitialized: PASS");
// Overall Purchasing system, configured with products for this application.
m_StoreController = controller;
// Store specific subsystem, for accessing device-specific store features.
m_StoreExtensionProvider = extensions;
}
public void OnInitializeFailed(InitializationFailureReason error)
{
// Purchasing set-up has not succeeded. Check error for reason. Consider sharing this reason
with the user.
Debug.Log("OnInitializeFailed InitializationFailureReason:" + error);
}
public PurchaseProcessingResult ProcessPurchase(PurchaseEventArgs args)
{
// A consumable product has been purchased by this user.
//if (String.Equals(args.purchasedProduct.definition.id, kProductIDConsumable,
StringComparison.Ordinal))
//{
// Debug.Log(string.Format("ProcessPurchase: PASS. Product: '{0}'",
args.purchasedProduct.definition.id));
// // The consumable item has been successfully purchased, add 100 coins to the player's in-
game score.
//}
// Or ... a non-consumable product has been purchased by this user.
if (String.Equals(args.purchasedProduct.definition.id, NO_ADS, StringComparison.Ordinal))
{
Debug.Log(string.Format("ProcessPurchase: PASS. Product: '{0}'",
args.purchasedProduct.definition.id));
// TODO: The non-consumable item has been successfully purchased, grant this item to the
player.
DataManager.instance.RemoveAds();
}
// Or ... a subscription product has been purchased by this user.
//else if (String.Equals(args.purchasedProduct.definition.id, kProductIDSubscription,
StringComparison.Ordinal))
//{
// Debug.Log(string.Format("ProcessPurchase: PASS. Product: '{0}'",
args.purchasedProduct.definition.id));
// // TODO: The subscription item has been successfully purchased, grant this to the player.
//}
// Or ... an unknown product has been purchased by this user. Fill in additional products
here....
else
{
Debug.Log(string.Format("ProcessPurchase: FAIL. Unrecognized product: '{0}'",
args.purchasedProduct.definition.id));
}
// Return a flag indicating whether this product has completely been received, or if the
application needs
// to be reminded of this purchase at next app launch. Use PurchaseProcessingResult.Pending when
still
// saving purchased products to the cloud, and when that save is delayed.
return PurchaseProcessingResult.Complete;
}
}
单班
public class Singleton<T> : MonoBehaviour where T: MonoBehaviour
{
public bool dontDestroy = false;
static T m_instance;
public static T instance
{
get
{
if(m_instance == null)
{
m_instance = GameObject.FindObjectOfType<T>();
if(m_instance == null)
{
GameObject singleton = new GameObject(typeof(T).Name);
m_instance = singleton.AddComponent<T>();
}
}
return m_instance;
}
}
public virtual void Awake()
{
if(m_instance == null)
{
m_instance = this as T;
if (dontDestroy)
{
transform.parent = null;
DontDestroyOnLoad(this.gameObject);
}
}
else
{
Destroy(gameObject);
}
}
}
DataManager
public GameObject noAdsButton;
//public GameObject AdsManager;
bool noAds = false;
public void RemoveAds()
{
noAds = true;
noAdsButton.SetActive(false);
//AdsManager.GetComponent<AdsManager>().enabled = false;
//AdsManager.SetActive(false);
}
被调用进行购买的功能
public void ClickBuy()
{
switch (itemType)
{
case ItemType.noAds:
IAPManager.instance.BuyNoAds();
break;
}
}