问题描述
我已经完成了Unity IAP教程中的所有步骤。
但是我的游戏仍在Google Play控制台中进行审查。我知道我不需要等待内部测试的审查。但是我可以从互联网上获得的所有信息都告诉我,至少,我需要将我的应用置于Alpha状态。如果我错了,请告诉我。
我的代码是这样的:
using System;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Purchasing;
using Veewo.Systems.Base;
// Placing the Purchaser class in the CompleteProject namespace allows it to interact with ScoreManager,// one of the existing Survival Shooter scripts.
namespace Veewo.Systems
{
// Deriving the Purchaser class from IStoreListener enables it to receive messages from Unity Purchasing.
public class IAPSystem : SystemBase,IStoreListener
{
private static IStoreController m_StoreController; // The Unity Purchasing system.
private static IExtensionProvider m_StoreExtensionProvider; // The store-specific Purchasing subsystems.
// Product identifiers for all products capable of being purchased:
// "convenience" general identifiers for use with Purchasing,and their store-specific identifier
// counterparts for use with and outside of Unity Purchasing. Define store-specific identifiers
// also on each platform's publisher dashboard (iTunes Connect,Google Play Developer Console,etc.)
// General product identifiers for the consumable,non-consumable,and subscription products.
// Use these handles in the code to reference which product to purchase. Also use these values
// when defining the Product Identifiers on the store. Except,for illustration purposes,the
// kProductIDSubscription - it has custom Apple and Google identifiers. We declare their store-
// specific mapping to Unity Purchasing's AddProduct,below.
public static string kProductIDConsumable = "veewo.pi.diamond600";
public static string kProductIDNonConsumable = "veewo.pi.limited.goldheropiece";
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";
protected override void OnInitialize()
{
base.OnInitialize();
// 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()
{
Debug.Log("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.
ConfigurationBuilder builder;
// if (Application.platform == RuntimePlatform.Android
// && StandardPurchasingModule.Instance().appStore == AppStore.GooglePlay)
// {
// builder = ConfigurationBuilder.Instance(
// Google.Play.Billing.GooglePlayStoreModule.Instance());
// }
// else
// {
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(kProductIDNonConsumable,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);
Debug.Log("UnityPurchasing.Initialize");
}
private 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 BuyNonConsumable()
{
// Buy the non-consumable product using its general identifier. Expect a response either
// through ProcessPurchase or OnPurchaseFailed asynchronously.
BuyProductID(kProductIDNonConsumable);
}
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.
//TODO:
}
// Or ... a non-consumable product has been purchased by this user.
else if (String.Equals(args.purchasedProduct.definition.id,kProductIDNonConsumable,args.purchasedProduct.definition.id));
// TODO: The non-consumable item has been successfully purchased,grant this item to the player.
}
// Or ... a subscription product has been purchased by this user.
else if (String.Equals(args.purchasedProduct.definition.id,kProductIDSubscription,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 void OnPurchaseFailed(Product product,PurchaseFailureReason failureReason)
{
// A product purchase attempt did not succeed. Check failureReason for more detail. Consider sharing
// this reason with the user to guide their troubleshooting actions.
Debug.Log(string.Format("OnPurchaseFailed: FAIL. Product: '{0}',PurchaseFailureReason: {1}",product.definition.storeSpecificId,failureReason));
}
public IEnumerable<Product> GetAllProducts()
{
if (!IsInitialized()) yield break;
foreach (var product in m_StoreController.products.set)
{
yield return product;
}
}
}
}
我非常确定产品密钥正确。
但是我仍然无法完成这项工作。我在BlueStack4和Android9移动版上进行了测试,但都失败了。无法通过调用UnityPurchasing.Initialize
完成OnInitialized
。
bluestack中的日志为:
10-29 09:51:17.193 5183 5203 I Unity : UnityIAP Version: 2.1.1
10-29 09:51:17.219 5183 5203 I UnityIAP: Starting in-app billing setup.
10-29 09:51:17.221 5183 5183 I UnityIAP: Billing service connected.
10-29 09:51:17.222 5183 5203 I Unity : UnityPurchasing.Initialize
10-29 09:51:17.224 5183 5238 I UnityIAP: Billing Service Manager invoking callback 1 of 1
10-29 09:51:17.224 5183 5238 I UnityIAP: Checking for in-app billing 3 support.
10-29 09:51:17.224 4671 4683 W Finsky : [291] iec.a(16): com.veewo.alienland: No account found.
10-29 09:51:17.226 5183 5238 D UnityIAP: onIabSetupFinished: 3
10-29 09:51:17.226 5183 5238 I UnityIAP: Failed to setup IAB. Notifying Unity...
10-29 09:51:17.276 5183 5203 I Unity : OnInitializeFailed InitializationFailureReason:PurchasingUnavailable
10-29 09:51:17.276 5183 5203 I Unity : UnityEngine.Logger:Log(LogType,Object)
10-29 09:51:17.276 5183 5203 I Unity : Veewo.Systems.IAPSystem:OnInitializeFailed(InitializationFailureReason)
10-29 09:51:17.276 5183 5203 I Unity : UnityEngine.Purchasing.Extension.UnityUtil:Update()
10-29 09:51:17.276 5183 5203 I Unity :
10-29 09:51:17.276 5183 5203 I Unity : (Filename: ./Runtime/Export/Debug.bindings.h Line: 45)
10-29 09:51:17.276 5183 5203 I Unity :
10-29 09:51:30.322 1962 3360 I NetworkScheduler.Stats: Task com.google.android.gms/com.google.android.gms.auth.account.be.legacy.AuthCronService started execution. cause:4 exec_start_elapsed_seconds: 1056 [CONTEXT service_id=218 ]
10-29 09:51:32.652 4671 4707 E Finsky : [314] log.b(1): Error while refreshing device settings: network time: 32342,HTTP status code: na,exception com.android.volley.TimeoutError. Retrying.
10-29 09:51:32.652 4671 4707 E Finsky : network time: 32342,exception com.android.volley.TimeoutError
10-29 09:51:32.652 4671 4707 E Finsky : at vqf.hq(PG)
10-29 09:51:32.652 4671 4707 E Finsky : at czn.r(PG:2)
10-29 09:51:32.652 4671 4707 E Finsky : at exo.r(PG:3)
10-29 09:51:32.652 4671 4707 E Finsky : at czg.run(PG:3)
10-29 09:51:32.652 4671 4707 E Finsky : at android.os.Handler.handleCallback(Handler.java:751)
10-29 09:51:32.652 4671 4707 E Finsky : at android.os.Handler.dispatchMessage(Handler.java:95)
10-29 09:51:32.652 4671 4707 E Finsky : at android.os.Looper.loop(Looper.java:154)
10-29 09:51:32.652 4671 4707 E Finsky : at android.app.ActivityThread.main(ActivityThread.java:6138)
10-29 09:51:32.652 4671 4707 E Finsky : at java.lang.reflect.Method.invoke(Native Method)
10-29 09:51:32.652 4671 4707 E Finsky : at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:893)
10-29 09:51:32.652 4671 4707 E Finsky : at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:783)
10-29 09:51:32.653 4671 4704 E Finsky : [311] lon.b(-1): Error getting device settings.
10-29 09:51:32.653 4671 4704 E Finsky : network time: 32342,exception com.android.volley.TimeoutError
10-29 09:51:32.653 4671 4704 E Finsky : at vqf.hq(PG)
10-29 09:51:32.653 4671 4704 E Finsky : at czn.r(PG:2)
10-29 09:51:32.653 4671 4704 E Finsky : at exo.r(PG:3)
10-29 09:51:32.653 4671 4704 E Finsky : at czg.run(PG:3)
10-29 09:51:32.653 4671 4704 E Finsky : at android.os.Handler.handleCallback(Handler.java:751)
10-29 09:51:32.653 4671 4704 E Finsky : at android.os.Handler.dispatchMessage(Handler.java:95)
10-29 09:51:32.653 4671 4704 E Finsky : at android.os.Looper.loop(Looper.java:154)
10-29 09:51:32.653 4671 4704 E Finsky : at android.app.ActivityThread.main(ActivityThread.java:6138)
10-29 09:51:32.653 4671 4704 E Finsky : at java.lang.reflect.Method.invoke(Native Method)
10-29 09:51:32.653 4671 4704 E Finsky : at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:893)
10-29 09:51:32.653 4671 4704 E Finsky : at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:783)
10-29 09:51:32.653 4671 4707 I Finsky : [314] abij.n(4): SCH: jobFinished: 31-305419896. TimeElapsed: 32344ms.
10-29 09:51:32.653 4671 4671 I Finsky : [1] abgy.c(7): SCH: Job 31-305419896 finished. Rescheduling.
10-29 09:51:32.653 4671 4671 I Finsky : [1] abgx.handleMessage(60): SCH: RunningQueue size: 0,PendingQueue size: 0
10-29 09:51:32.654 4671 4671 I Finsky : [1] abgx.handleMessage(26): SCH: Executor finished
10-29 09:51:32.658 4671 4671 I Finsky : [1] abjk.b(5): SCH: Jobs in database: 1-1337 24-77777777 26-1414141414 31-305419896
10-29 09:51:32.659 4671 4671 I Finsky : [1] abhw.d(37): SCH: ConstraintMapping: 24-77777777,-> L: 0ms,D: 24834708ms,C: false,I: true,N: 0
10-29 09:51:32.660 4671 4671 I Finsky : [1] abhw.d(37): SCH: ConstraintMapping: 1-1337,31-305419896,-> L: 127994ms,D: 32437706ms,I: false,N: 1
10-29 09:51:32.660 4671 4671 I Finsky : [1] abhw.d(37): SCH: ConstraintMapping: 26-1414141414,-> L: 29458222ms,D: 30358222ms,N: 0
10-29 09:51:32.662 4671 4671 I Finsky : [1] abhw.f(7): SCH: Cancelling existing jobscheduler jobs: 9000 9005
10-29 09:51:32.662 4671 4671 I Finsky : [1] abhw.e(23): SCH: Scheduling job Id: 9001,L: 0,D: 24834708,N: 0
10-29 09:51:32.663 4671 4671 I Finsky : [1] abhw.e(23): SCH: Scheduling job Id: 9002,L: 127994,D: 32437706,N: 1
10-29 09:51:32.664 4671 4671 I Finsky : [1] abhw.e(23): SCH: Scheduling job Id: 9003,L: 29458222,D: 30358222,N: 0
已经4天了,我无法完成这项工作。我所有的猜测是:
- 网络问题,我在中国,虽然我使用的是VPN,但无法连接到Google Service,但它不稳定。
- 我没有正确设置Unity IAP
- 我需要等待Google Play控制台审核完成,然后等待其状态发布。
- 我没有在手机中使用正确的Google帐户登录信息。我将同一个帐户用于Google Play帐户,并且已经将其添加到测试帐户列表中,是否应该使用新帐户?
我现在很困惑,不知道下一步该怎么做。请帮助我。
解决方法
似乎您的代码是正确的。但是您的应用程序需要已经发布。如果正在审核中,它将无法正常工作。等待填充已批准。