我有一个新的 Android 应用程序,其中添加了应用程序内计费功能,我感到沮丧得抓狂。
我已上传签名的 APK 并发布到 alpha。我创建了一组应用内产品并将其全部激活。我创建了一个新的 Gmail 帐户,并将其定义为应用程序 apk 页面上该应用程序的测试人员。
我已将 Android 手机恢复出厂设置并使用新的 Gmail 帐户对其进行初始化。我已在 chrome 中输入 /apps/testing 链接并注册为测试人员。然后我下载并安装了我的应用程序。在我的应用程序中,我询问了可用的应用程序内产品,并显示了我上面创建的集合。我选择了购买并完成了以下购买流程。
1. 屏幕显示要购买的产品和价格,并请求按“继续”,我这样做
2. 屏幕显示付款方式,我选择兑换代码
3. 屏幕显示兑换您的代码,我输入之前在开发者控制台中设置的促销代码之一(上面未提及 - 抱歉),然后按兑换
4. 屏幕再次显示产品,这次价格被划掉,并提供添加我选择的商品的选项(很奇怪被要求再次添加购买嘿嘿)
5. 屏幕显示已添加的项目
6. 几秒钟后,屏幕显示错误,您已经拥有该物品。
怎么可能,这个用户在十分钟前并不存在,并且如上所述只使用过这个应用程序一次。
我在堆栈溢出和其他类似的地方看到了很多问题,并尝试了一切,清除谷歌游戏商店缓存,清除谷歌游戏商店数据等。上面描述的这个序列是我在完全干净的手机上使用完全干净的用户进行的最新尝试。
我可以上传我使用的应用程序代码,但这没有抓住重点,即当这个 gmail 帐户以前从未从任何人那里购买过任何东西时,这个 gmail 帐户怎么可能已经拥有一个项目。这肯定是一个错误。
关于如何进行的所有线索都非常受欢迎。现在添加了代码,请注意,这是一个混合 Android 应用程序,用户购买决策代码位于 javascript/html 中,应用程序内操作位于下面的包装器代码中
private void processCommand(JSONObject commandJSON) throws JSONException
{
String command = commandJSON.getString("method");
if ("GetInAppProducts".equals(command))
{
Log.d(TAG, "Querying Inventory");
InAppPurchaseSkuString = null ; // clear the purchased sku. Note this is tested in mConsumeFinishedListener
mHelper.queryInventoryAsync(true, itemSkus, new IabHelper.QueryInventoryFinishedListener()
{
@Override
public void onQueryInventoryFinished(IabResult iabResult, Inventory inventory)
{
InventoryRecord = inventory ;
if (iabResult.isFailure())
{
Log.d(TAG, "Query inventory failed");
SendEndItemsToApp ();
}
else
{
Log.d(TAG, "Query inventory was successful.");
InventoryCheckCount = 0 ; // seems that we cannot just fire off a whole lot of these checks at the same time, so do them in sequence
if (itemSkus.size()>0) { CheckForOwnedItems (); } else { SendEndItemsToApp (); }
}
}
});
}
else if ("BuyInAppProduct".equals(command))
{
JSONArray params = commandJSON.getJSONArray("parameters");
InAppPurchaseSkuString = params.getString(0);
Log.d(TAG, "User decision to purchase " + InAppPurchaseSkuString);
mHelper.launchPurchaseFlow( MainActivity.this, InAppPurchaseSkuString, InAppPurchaseActivityCode, mPurchaseFinishedListener, "mypurchasetoken"); // consider putting the user email address in the last field - need to get from app
};
}//end of ProcessCommand
public void CheckForOwnedItems ()
{
Log.d(TAG, "Pre Purchase Inventory Processing Started");
String sku = itemSkus.get(InventoryCheckCount);
if (InventoryRecord.getSkuDetails(sku) != null)
{
if (InventoryRecord.hasPurchase(sku))
{
consumeItem ();
}
else
{
SendItemToApp ();
InventoryCheckCount++;
if (InventoryCheckCount < itemSkus.size()) { CheckForOwnedItems (); } else { SendEndItemsToApp (); }
};
};
}//end of CheckForOwnedItems
public void SendItemToApp ()
{
String sku = itemSkus.get(InventoryCheckCount);
String priceString = InventoryRecord.getSkuDetails(sku).getPrice().replaceAll("[^\\d.]+", ""); // RegExp removes all characters except digits and periods
String infoString = "InAppProductDetails('" + sku + "','" + "dummy" + "','" + priceString + "');"; // dummy is a placeholder for product description which is not (yet?) used in the app
Log.d(TAG, infoString);
mWebView.evaluateJavascript (infoString, new ValueCallback<String>()
{
@Override
public void onReceiveValue(String s)
{
//Log.d(TAG,"Returned from InAppProductDetails:");
}
}
);
}
public void SendEndItemsToApp ()
{
String endString = "InAppProductsEnd();"; // name is a placeholder for now
Log.d(TAG, endString);
mWebView.evaluateJavascript(endString, new ValueCallback<String>()
{
@Override
public void onReceiveValue(String s)
{
//Log.d(TAG,"Returned from InAppProductsEnd:");
}
}
);
}
public void consumeItem()
{
Log.d(TAG,"Pre Purchase Inventory Query Started");
String sku = itemSkus.get(InventoryCheckCount);
mHelper.consumeAsync(InventoryRecord.getPurchase(sku), mConsumeFinishedListener);
}
IabHelper.OnConsumeFinishedListener mConsumeFinishedListener = new IabHelper.OnConsumeFinishedListener()
{
public void onConsumeFinished (Purchase purchase, IabResult result)
{
if (result.isSuccess())
{
Log.d(TAG, "Pre Purchase Consume Item Completed");
SendItemToApp ();
InventoryCheckCount++;
if (InventoryCheckCount < itemSkus.size()) { CheckForOwnedItems (); } else { SendEndItemsToApp (); }
}
else
{
Log.d(TAG,"Pre Purchase Consume Item Failed");
}
}
};
IabHelper.OnIabPurchaseFinishedListener mPurchaseFinishedListener = new IabHelper.OnIabPurchaseFinishedListener()
{
public void onIabPurchaseFinished (IabResult result, Purchase purchase)
{
if (result.isFailure())
{
Log.d(TAG,"Purchase Scenario Failed");
}
else if (purchase.getSku().equals(InAppPurchaseSkuString))
{
Log.d(TAG,"Purchase Scenario Completed");
String evalString = "InAppProductPurchased('" + InAppPurchaseSkuString + "');";
Log.d(TAG, evalString);
mWebView.evaluateJavascript (evalString, new ValueCallback<String>()
{
@Override
public void onReceiveValue(String s)
{
Log.d(TAG, "Returned from InAppProductPurchased:");
}
}
);
}
}
};