我正处于完成我的第一个应用程序的边缘,剩下的最后一件事就是实施IAP计费,这就是为什么我目前正在阅读相关主题(包括加密,混淆和东西等安全问题)
我的应用程序是免费版本,可以通过IAP升级到完整版本,因此只有一个托管购买项目“溢价”。我对此有几个问题:
在Google IAP API示例(trivialdrivesample)中,总是会在MainActivity中检查IAP以查看用户是否购买了高级版本,通过
完成mHelper.queryInventoryAsync(mGotInventoryListener);
我的第一个担忧: 这意味着用户始终需要在应用启动时拥有互联网/数据连接,才能切换到高级版本吗?如果用户没有互联网连接怎么办?他会选择我猜的精简版,我觉得这很烦人。
所以我想到了如何在SharedPrefs或app数据库中本地保存isPremium状态。现在,我知道你无法阻止黑客对应用程序进行逆向工程,无论如何,即便如此,因为我没有服务器来进行服务器端验证。
然而,人们根本无法在某处保存“isPremium”标志,因为这太容易被发现。
所以我在考虑这样的事情:
- 用户购买Premium
- App获取IMEI / Device-ID,XOR使用硬编码的String键对其进行编码,并将其保存在应用数据库中。
现在,当用户再次启动应用时:
- App从数据库中获取编码的String,对其进行解码并检查decodeString == IMEI。如果是 - >溢价
- 如果不是,则会调用正常的queryInventoryAsync来查看用户是否购买了溢价。
您如何看待这种方法?我知道这不是超级固定,但对我而言,更重要的是用户不会烦恼(比如强制性的互联网连接),而不是应用程序将无法解决(无论如何这是不可能的)。 你有其他一些提示吗?
我目前还没有提到的另一件事是,当用户卸载/重新安装应用程序时,如何恢复事务状态。我知道API有一些机制,并且我的数据库可以通过应用程序导出和导入(因此编码的isPremium标志也可以导出/导入)。好的,我猜这将是另一个问题,当时机成熟时; - )
对此方法的任何想法和评论都是受欢迎的,您认为这是一个很好的解决方案吗?或者我错过了什么/走向了错误的方向?
答案 0 :(得分:17)
我也在做同样的调查,但在我的测试中我发现你不需要存储它,因为Google会做你需要的所有缓存,我怀疑(虽然我没有调查过)他们正在做尽可能安全(看到它们也符合他们的利益!)
所以这就是我做的事情
// Done in onCreate
mHelper = new IabHelper(this, getPublicKey());
mHelper.startSetup(new IabHelper.OnIabSetupFinishedListener() {
public void onIabSetupFinished(IabResult result) {
if (!result.isSuccess()) {
// Oh noes, there was a problem.
Log("Problem setting up In-app Billing: " + result);
} else {
Log("onIabSetupFinished " + result.getResponse());
mHelper.queryInventoryAsync(mGotInventoryListener);
}
}
});
// Called by button press
private void buyProUpgrade() {
mHelper.launchPurchaseFlow(this, "android.test.purchased", 10001,
mPurchaseFinishedListener, ((TelephonyManager)this.getSystemService(Context.TELEPHONY_SERVICE)).getDeviceId());
}
// Get purchase response
private IabHelper.OnIabPurchaseFinishedListener mPurchaseFinishedListener = new IabHelper.OnIabPurchaseFinishedListener() {
public void onIabPurchaseFinished(IabResult result, Purchase purchase)
{
if (result.isFailure()) {
Log("Error purchasing: " + result);
return;
}
else if (purchase.getSku().equals("android.test.purchased")) {
Log("onIabPurchaseFinished GOT A RESPONSE.");
mHelper.queryInventoryAsync(mGotInventoryListener);
}
}
};
// Get already purchased response
private IabHelper.QueryInventoryFinishedListener mGotInventoryListener = new IabHelper.QueryInventoryFinishedListener() {
public void onQueryInventoryFinished(IabResult result,
Inventory inventory) {
if (result.isFailure()) {
// handle error here
Log("Error checking inventory: " + result);
}
else {
// does the user have the premium upgrade?
mIsPremium = inventory.hasPurchase("android.test.purchased");
setTheme();
Log("onQueryInventoryFinished GOT A RESPONSE (" + mIsPremium + ").");
}
}
};
那么这里会发生什么?
IAB已设置并成功完成调用startSetup
(只要已通过互联网连接运行一次并且设置正确,它将始终成功)我们致电queryInventoryAsync
找出已经购买的东西(再次,如果在线时调用它,它总是在离线时工作)。
因此,如果购买成功完成(只能在线完成),我们会致电queryInventoryAsync
以确保在线时调用该商品。
现在没有必要存储任何与购买有关的内容,并且使您的应用程序不那么容易破解。
我已经测试了很多方法,飞行模式,再次打开设备,唯一令人困惑的是清除手机上某些Google应用中的数据(不太可能发生!)。
如果您有不同的经历,请为此做出贡献,我的应用程序仍处于早期测试阶段。
答案 1 :(得分:5)
我将ne0的答案重构为静态方法,包括来自snark的评论。
我的应用开始时调用此方法 - 您需要在@Path("/{manageElement}")
@POST
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
public Object update(@Context SecurityContext context,
@PathParam(MANAGED_ELEMENET) String manageElement, @QueryParam("prettify") boolean prettyOutput, String content) {
Request request = new Request();
request.setActionType(ActionType.UPDATE_ME);
request.setRequestBody(content);
request.setManageElement(manageElement);
request.setRequester(GENERIC_USER);
if(prettyOutput) {
Object response = service.service(request);
String resp = getPrettyGson().toJson(response);
return Response.status(Response.Status.OK).entity(resp).build();
}
else {
Object response = service.service(request);
String resp = getGson().toJson(response);
return Response.status(Response.Status.OK).entity(resp).build();
}
}
private Gson getPrettyGson() {
return new GsonBuilder()
.setPrettyPrinting()
.setDateFormat("yyyy-MM-dd HH:mm:ss")
.create();
}
private Gson getGson() {
return new GsonBuilder()
.setDateFormat("yyyy-MM-dd HH:mm:ss")
.create();
}
TODO
如果用户购买了该项目,这将在重新启动和没有网络连接的情况下工作。
答案 2 :(得分:3)
由于你已经知道使用这个系统不可能让它无法解决,我建议不要试图阻止黑客入侵。你提出的建议被称为“通过默默无闻的安全”,通常是一个坏主意。
我的建议是首先尝试queryInventoryAsync()
,如果没有互联网连接,只检查'isPremium'标志。
还有一些其他可能的解决方法,例如使用单独的免费和高级应用,而不是应用内购买。其他人如何处理此问题以及Google提供的工具可能需要进行调查。
queryInventoryAsync
会自动考虑卸载和重新安装,因为它会跟踪已登录用户的购买情况。
答案 3 :(得分:0)
是的,可以离线检索购买。此外,我正在考虑在显示计费 UI 之前计算用户打开应用的次数作为一种机制。