I sent my game to the app store and got the response:
When validating receipts on your server, your server needs to be able to handle a production-signed app getting its receipts from Apple’s test environment. The recommended approach is for your production server to always validate receipts against the production App Store first. If validation fails with the error code “Sandbox receipt used in production,” you should validate against the test environment instead.
I don’t understand what they want me to do?
I am validating the receipt in the IAP and the purchase works fine in the TestFlights, has anyone stumble on this issue? what did you do?
A sample for my code:
public class IAPManagerScript : MonoBehaviour, IStoreListener
{
public Text message;
public static IAPManagerScript instance;
private static IStoreController m_StoreController;
private static IExtensionProvider m_StoreExtensionProvider;
//Step 1 create your products
private string buyProduct = "my id for the iap";
//************************** Adjust these methods **************************************
public void InitializePurchasing()
{
if (IsInitialized()) { return; }
var builder = ConfigurationBuilder.Instance(StandardPurchasingModule.Instance());
//Step 2 choose if your product is a consumable or non consumable
builder.AddProduct(buyProduct,ProductType.Consumable);
UnityPurchasing.Initialize(this, builder);
}
private bool IsInitialized()
{
return m_StoreController != null && m_StoreExtensionProvider != null;
}
//Step 3 Create methods
public void buyPoructMethod()
{
BuyProductID(buyProduct);
}
public static void SavePurchase(bool purchased,string path)
{
BinaryFormatter bf = new BinaryFormatter();
//Application.persistentDataPath is a string, so if you wanted you can put that into debug.log if you want to know where save games are located
FileStream file = File.Create(Application.persistentDataPath + path); //you can call it anything you want
bf.Serialize(file, purchased);
file.Close();
}
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)
{
Debug.Log(productReceipt.productID);
Debug.Log(productReceipt.purchaseDate);
Debug.Log(productReceipt.transactionID);
}
}
catch (IAPSecurityException)
{
Debug.Log("Invalid receipt, not unlocking content");
validPurchase = false;
// message.text = message.text + "Failed to Purchase \n";
Debug.Log("Purchase Failed");
}
#endif
if (validPurchase)
{
SceneManager.LoadScene("InfoScene");
}
return PurchaseProcessingResult.Complete;
}
//**************************** Dont worry about these methods ***********************************
private void Awake()
{
TestSingleton();
}
void Start()
{
if (m_StoreController == null) { InitializePurchasing(); }
}
private void TestSingleton()
{
if (instance != null) { Destroy(gameObject); return; }
instance = this;
DontDestroyOnLoad(gameObject);
}
void BuyProductID(string productId)
{
if (IsInitialized())
{
Product product = m_StoreController.products.WithID(productId);
if (product != null && product.availableToPurchase)
{
Debug.Log(string.Format("Purchasing product asychronously: '{0}'", product.definition.id));
m_StoreController.InitiatePurchase(product);
}
else
{
Debug.Log("BuyProductID: FAIL. Not purchasing product, either is not found or is not available for purchase");
}
}
else
{
Debug.Log("BuyProductID FAIL. Not initialized.");
}
}
public void RestorePurchases()
{
if (!IsInitialized())
{
Debug.Log("RestorePurchases FAIL. Not initialized.");
return;
}
if (Application.platform == RuntimePlatform.IPhonePlayer ||
Application.platform == RuntimePlatform.OSXPlayer)
{
Debug.Log("RestorePurchases started ...");
var apple = m_StoreExtensionProvider.GetExtension<IAppleExtensions>();
apple.RestoreTransactions((result) => {
Debug.Log("RestorePurchases continuing: " + result + ". If no further messages, no purchases available to restore.");
});
}
else
{
Debug.Log("RestorePurchases FAIL. Not supported on this platform. Current = " + Application.platform);
}
}
public void OnInitialized(IStoreController controller, IExtensionProvider extensions)
{
Debug.Log("OnInitialized: PASS");
m_StoreController = controller;
m_StoreExtensionProvider = extensions;
}
public void OnInitializeFailed(InitializationFailureReason error)
{
Debug.Log("OnInitializeFailed InitializationFailureReason:" + error);
}
public void OnPurchaseFailed(Product product, PurchaseFailureReason failureReason)
{
Debug.Log(string.Format("OnPurchaseFailed: FAIL. Product: '{0}', PurchaseFailureReason: {1}", product.definition.storeSpecificId, failureReason));
}