c# - IAP 错误导致拒绝,但我不明白问题是什么
问题描述
以下是我收到的拒绝消息:“我们发现,在通过 Wi-Fi 运行 iOS 13.2 的 iPad 上审查时,您的应用内购买产品出现一个或多个错误。
具体来说,当我们在您的商店点击购买按钮时,没有进行任何购买活动。
下一步
在您的服务器上验证收据时,您的服务器需要能够处理从 Apple 测试环境获取其收据的生产签名应用程序。推荐的方法是让您的生产服务器始终首先根据生产 App Store 验证收据。如果验证失败并出现错误代码“生产中使用的沙盒收据”,则应改为针对测试环境进行验证。
我无法完全理解问题所在。IAP 在沙盒中对我来说工作得很好,所以我猜我在某个地方遗漏了一些东西。任何帮助表示赞赏。
public class IAPManager : MonoBehaviour, IStoreListener
{
IStoreController controller;
IExtensionProvider extensions;
public GameObject iapButton;
const string gameID = "com.thegame.";
void Start()
{
InitializePurchasing();
CheckIfNoAdsPurchased();
}
public void CheckIfNoAdsPurchased()
{
Product product = controller.products.WithID(gameID + "noads");
if (product != null && product.hasReceipt)
{
// Owned Non Consumables and Subscriptions should always have receipts.
// So here the Non Consumable product has already been bought.
TheGameCtrl.instance.showAds = false;
iapButton.gameObject.SetActive(false);
}
else
{
TheGameCtrl.instance.showAds = true;
iapButton.gameObject.SetActive(true);
}
}
public void InitializePurchasing()
{
// If we have already connected to Purchasing ...
if (IsInitialized())
{
// ... we are done here.
return;
}
var builder = ConfigurationBuilder.Instance(StandardPurchasingModule.Instance());
// Use your own products
builder.AddProduct(gameID + "noads", ProductType.NonConsumable);
UnityPurchasing.Initialize(this, builder);
}
bool IsInitialized()
{
// Only say we are initialized if both the Purchasing references are set.
return controller != null && extensions != null;
}
/// <summary>
/// Called when Unity IAP is ready to make purchases.
/// </summary>
public void OnInitialized(IStoreController controller, IExtensionProvider extensions)
{
Debug.Log("IAP: Initialized successfully");
this.controller = controller;
this.extensions = extensions;
}
public void BuyNoAds()
{
BuyProductID("noads");
}
/// <summary>
/// Called when Unity IAP encounters an unrecoverable initialization error.
///
/// Note that this will not be called if Internet is unavailable; Unity IAP
/// will attempt initialization until it becomes available.
/// </summary>
public void OnInitializeFailed(InitializationFailureReason error)
{
Debug.Log("OnInitializeFailed InitializationFailureReason:" + error);
}
public void BuyProductID(string productId)
{
productId = gameID + productId;
if (IsInitialized())
{
Product product = controller.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.
controller.InitiatePurchase(product);
#if UNITY_EDITOR
StartCoroutine(DisablePurchaseButton());
#else
//hide IAP button
iapButton.SetActive(false);
#endif
//Do your product logic
Debug.Log("Ads removed");
TheGameCtrl.instance.showAds = false;
}
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");
}
}
else
{
// ... report the fact Purchasing has not succeeded initializing yet. Consider waiting longer or
// retrying initiailization.
Debug.Log("BuyProductID FAIL. Not initialized.");
}
}
/// <summary>
/// Called when a purchase completes.
///
/// May be called at any time after OnInitialized().
/// </summary>
public PurchaseProcessingResult ProcessPurchase(PurchaseEventArgs e)
{
bool validPurchase = true; // Presume valid for platforms with no R.V.
// Unity IAP's validation logic is only included on these platforms.
#if UNITY_ANDROID || UNITY_IOS || UNITY_STANDALONE_OSX
// Prepare the validator with the secrets we prepared in the Editor
// obfuscation window.
var validator = new CrossPlatformValidator(GooglePlayTangle.Data(),
AppleTangle.Data(), Application.identifier);
try
{
// On Google Play, result has a single product ID.
// On Apple stores, receipts contain multiple products.
var result = validator.Validate(e.purchasedProduct.receipt);
// For informational purposes, we list the receipt(s)
Debug.Log("Receipt is valid. Contents:");
foreach (IPurchaseReceipt productReceipt in result)
{
if (productReceipt.productID != e.purchasedProduct.definition.id)
{
Debug.Log("Invalid receipt data");
validPurchase = false;
}
}
}
catch (IAPSecurityException)
{
Debug.Log("Invalid receipt, not unlocking content");
validPurchase = false;
#if UNITY_EDITOR
validPurchase = true;
#endif
}
#endif
//apply the purchasing in case if the transaction is valid
if (validPurchase)
{
switch (e.purchasedProduct.definition.id)
{
case (gameID + "noads"):
Debug.Log("YOU BOUGHT THE NON CONSUMIBLE");
TheGameCtrl.instance.showAds = false;
#if UNITY_EDITOR
StartCoroutine(DisablePurchaseButton());
#else
//hide IAP button
iapButton.SetActive(false);
#endif
//Do your product logic
break;
}
}
return PurchaseProcessingResult.Complete;
}
/// <summary>
/// Called when a purchase fails.
/// </summary>
public void OnPurchaseFailed(Product p, PurchaseFailureReason r)
{
// 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}", p.definition.storeSpecificId, r));
}
// 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 (!IsInitialized())
{
Debug.Log("RestorePurchases FAIL. Not initialized.");
return;
}
// If we are running on an Apple device ...
if (Application.platform == RuntimePlatform.IPhonePlayer
|| Application.platform == RuntimePlatform.OSXPlayer
|| Application.platform == RuntimePlatform.tvOS)
{
Debug.Log("RestorePurchases started ...");
var apple = extensions.GetExtension<IAppleExtensions>();
// Begin the asynchronous process of restoring purchases. Expect a confirmation response in
// the Action below, and ProcessPurchase if there are previously purchased products to restore.
apple.RestoreTransactions(OnTransactionsRestored);
}
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);
}
}
void OnTransactionsRestored(bool success)
{
Debug.Log("Transactions restored " + success.ToString());
}
private IEnumerator DisablePurchaseButton()
{
yield return new WaitForEndOfFrame();
iapButton.SetActive(false);
}
}
解决方案
推荐阅读
- flutter - 飞镖和粒子云
- python - 禁用部分 tkinter 文本小部件
- asterisk - 转移呼叫时的星号通道变量持久性
- swift - 我想在字符串类型变量中添加多个变量,并且每个值都将从 swift 4 的下一行开始
- ios - 使用带有 NavigationView 的 ScrollView 我得到空白的灰色空间
- google-maps - 如何在 Flutter 中为滑动面板提供可滚动的列表视图手势优先级?
- reactjs - How to get queryString from url?
- css - Laravel:文本换行 Laravel 从 DB 中提取信息
- angular - StackBlitz 和 raw-loader
- lua - 如何在对象上设置 onCollisionEnter?