Android 应用内计费 - RestoreTransactionInformation

2023-11-27

我正在尝试使用应用内计费将免费应用程序升级到“付费”版本。我使用的代码来自本教程作为官方开发者处理计费 对于像我这样的简单流程来说,网站过于复杂和混乱。

我的升级代码工作正常。

当我尝试添加一些内容来检查用户是否已经购买但通过重新安装或清除数据(我不在乎是哪一种)丢失了购买数据时,问题就出现了。

在应用程序启动时,我检查第一次运行后设置的标志。如果该标志不存在,则会向用户显示一个对话框,警告他们应用程序将检查以前的购买情况,当他们单击“确定”时,将调用 RestoreTransactionInformation 方法。这会导致应用程序强制关闭。

由于应用程序内计费在调试时或在模拟器上不起作用,因此每次我想尝试代码时都必须发布应用程序的签名版本。当我尝试发出restoreTransactionInformation 请求时,我无法知道应用程序为何退出。有谁知道我如何诊断它,或者什么可能导致我的应用程序死机?或者如何使用restoreTransactionInformation方法的工作示例?

编辑:所以看起来 RESTORE_TRANSACTIONS 请求得到了正确的响应,并返回了我的测试购买的详细信息。不幸的是,在它可以对其进行任何操作之前,该应用程序已被强制关闭。以下是市场响应 RESTORE_TRANSACTIONS 请求后立即发生的情况的 logcat(没有混淆代码):

I/BillingService( 6484): confirmTransaction()
D/Finsky  ( 1884): [7] MarketBillingService.getPreferredAccount: com.hippypkg: Account from first account.
I/BillingService( 6484): current request is:**********
I/BillingService( 6484): RESTORE_TRANSACTIONS Sync Response code: RESULT_OK
D/WindowManagerImpl( 6484): finishRemoveViewLocked, mViews[0]: com.android.internal.policy.impl.PhoneWindow$DecorView@**********
W/InputManagerService( 1381): [unbindCurrentClientLocked] Disable input method client.
W/InputManagerService( 1381): [startInputLocked] Enable input method client.
D/NativeCrypto( 1884): returned from sslSelect() with result 1, error code 2
D/Finsky  ( 1884): [1] MarketBillingService.sendResponseCode: Sending response RESULT_OK for request ********** to com.hippypkg.
I/BillingService( 6484): Received action: com.android.vending.billing.PURCHASE_STATE_CHANGED
I/BillingService( 6484): purchaseStateChanged got signedData: {"nonce":**********,"orders":[{"orderId":"**********","packageName":"com.hippypkg","productId":"hippy_upgrade_free_to_full","purchaseTime":1331476540000,"purchaseState":0}]}
I/BillingService( 6484): purchaseStateChanged got signature: **********==
I/BillingService( 6484): signedData: {"nonce":**********,"orders":[{"orderId":"**********","packageName":"com.hippypkg","productId":"hippy_upgrade_free_to_full","purchaseTime":1331476540000,"purchaseState":0}]}
I/BillingService( 6484): signature: **********==
I/BillingService( 6484): confirmTransaction()
I/BillingService( 6484): makerequestbundle success
I/BillingService( 6484): putstringarray success
D/Finsky  ( 1884): [24] MarketBillingService.getPreferredAccount: com.hippypkg: Account from first account.
D/AndroidRuntime( 6484): Shutting down VM
W/dalvikvm( 6484): threadid=1: thread exiting with uncaught exception (group=0x4001d5a0)
E/AndroidRuntime( 6484): FATAL EXCEPTION: main
E/AndroidRuntime( 6484): java.lang.RuntimeException: Unable to start receiver com.hippypkg.BillingReceiver: java.lang.NullPointerException
E/AndroidRuntime( 6484):    at android.app.ActivityThread.handleReceiver(ActivityThread.java:2144)
E/AndroidRuntime( 6484):    at android.app.ActivityThread.access$2400(ActivityThread.java:135)
E/AndroidRuntime( 6484):    at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1114)
E/AndroidRuntime( 6484):    at android.os.Handler.dispatchMessage(Handler.java:99)
E/AndroidRuntime( 6484):    at android.os.Looper.loop(Looper.java:150)
E/AndroidRuntime( 6484):    at android.app.ActivityThread.main(ActivityThread.java:4385)
E/AndroidRuntime( 6484):    at java.lang.reflect.Method.invokeNative(Native Method)
E/AndroidRuntime( 6484):    at java.lang.reflect.Method.invoke(Method.java:507)
E/AndroidRuntime( 6484):    at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:849)
E/AndroidRuntime( 6484):    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:607)
E/AndroidRuntime( 6484):    at dalvik.system.NativeStart.main(Native Method)
E/AndroidRuntime( 6484): Caused by: java.lang.NullPointerException
E/AndroidRuntime( 6484):    at android.os.Parcel.readException(Parcel.java:1328)
E/AndroidRuntime( 6484):    at android.os.Parcel.readException(Parcel.java:1276)
E/AndroidRuntime( 6484):    at com.android.vending.billing.IMarketBillingService$Stub$Proxy.sendBillingRequest(IMarketBillingService.java:100)
E/AndroidRuntime( 6484):    at com.hippypkg.BillingHelper.confirmTransaction(BillingHelper.java:152)
E/AndroidRuntime( 6484):    at com.hippypkg.BillingHelper.verifyPurchase(BillingHelper.java:250)
E/AndroidRuntime( 6484):    at com.hippypkg.BillingReceiver.purchaseStateChanged(BillingReceiver.java:41)
E/AndroidRuntime( 6484):    at com.hippypkg.BillingReceiver.onReceive(BillingReceiver.java:23)
E/AndroidRuntime( 6484):    at android.app.ActivityThread.handleReceiver(ActivityThread.java:2103)
E/AndroidRuntime( 6484):    ... 10 more
W/ActivityManager( 1381):   Force finishing activity com.hippypkg/.Hippy

所以我终于弄清楚了。

如果您查看 Google 文档中的应用内结算概述,它会指出:

RESTORE_TRANSACTIONS 请求类型还会触发 PURCHASE_STATE_CHANGED 广播意图,其中包含与购买请求期间发送的相同类型的交易信息,尽管您不需要使用 CONFIRM_NOTIFICATIONS 消息来响应此意图。

在正常的购买确认交易周期中,当您请求购买应用内结算产品时,Google 会发回带有一堆字段的 JSON。这些字段之一是“notification_id”。当谷歌发送 PURCHASE_STATE_CHANGED 意图时,它期望来自应用程序的 CONFIRM_NOTIFICATIONS 响应,其中包含一堆信息,包括 notification_id 。这里一切都很好。

当您从 Google 收到来自应用的 RESTORE_TRANSACTIONS 请求的 PURCHASE_STATE_CHANGED 时,问题就开始了。此 JSON 不包含 notification_id 字段。该库仍然以 CONFIRM_NOTIFICATIONS 进行响应,将 notification_id 数组添加到捆绑包中,在本例中为 null。这就是导致 NullPointerException 的原因。

解决方案:我修改了 BillingHelper.java 类,添加了一个布尔值来跟踪用户何时进行正常购买以及何时想要恢复交易。 如果它是一个restoreTransactions 请求,我会向处理程序发送一条消息并跳过confirmNotifications 步骤。

编辑: 上述修复的代码位于 BillingHelper.java 中 我使用布尔标志来跟踪用户是否进行了 RESTORE_TRANSACTIONS 调用 (isRestoreTransactions)。

在 BillingHelper.java 的 'verifyPurchase' 方法中,我将代码更改如下:

protected static void verifyPurchase(String signedData, String signature) {
        ArrayList<VerifiedPurchase> purchases = BillingSecurity.verifyPurchase(signedData, signature);

        if(isRestoreTransaction)
        {
            /*
            *
            *Add some logic to retrieve the restored purchase product ID's from the 'purchases' array
            *
            */

            //Set the boolean to false
            isRestoreTranscation = false;

            //Send a message to the handler, informing it that purchases were restored
            if(mCompletedHandler != null){
                mCompletedHandler.sendEmptyMessage(0);
            } else {
                Log.e(TAG, "verifyPurchase error. Handler not instantiated. Have you called setCompletedHandler()?");
            }
        }
        else
        {
            /*
            *......
            *......
            *......
            *Original method body here
            *......
            *......
            *......
            */
        }
    }
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Android 应用内计费 - RestoreTransactionInformation 的相关文章

随机推荐

  • SonarQube 5.3 后台任务失败且未登录仪表板

    我知道这类似于sonarqube 5 2 后台任务有时会失败且没有日志 但是我无法发表评论 由于缺乏声誉点 来添加更多信息 因此尝试添加这篇文章作为答案 但它被版主删除了 我遇到了问题SonarQube 5 2 昨天升级后现在是 5 3 我
  • 使用 Obj-C 重命名现有文件

    我已经多次看到这个问题 但到目前为止我无法使用任何后期解决方案取得成功 我想做的是重命名应用程序本地存储中的文件 对于 Obj c 来说也是一种新功能 我能够检索旧路径并创建新路径 但是我必须写什么才能真正更改文件名 到目前为止我所拥有的是
  • 无法通过反射设置布尔值

    我无法设置Boolean使用 Java 反射将值赋给字段 字段数据类型为java lang Boolean 但是 如果数据类型是原始类型 我可以设置该值 即boolean 这是一个简单的 VOBoolean类型和原始类型 public cl
  • ASP.NET MVC CMS 数据库中的动态路由

    基本上 我有一个使用 ASP NET MVC 构建的 CMS 后端 现在我正在转向前端站点 并且需要能够根据输入的路由从我的 CMS 数据库加载页面 所以如果用户输入example com students information MVC将
  • Remote_addr 不返回 IPv4 地址

    我在本地主机上使用 xampp 当我使用 SERVER REMOTE ADDR 它返回 1 也在phpinfo 为什么要这样做 我希望它返回一个正常的 IP 地址 如 127 0 0 1 我的操作系统是windows vista 1 is
  • 如何将 Scala 数组传递给 Scala vararg 方法?

    考虑下面的代码 private def test some String private def call val some Array asd zxc test some 它打印expect String found Array Stri
  • userinterfaceonly:=true 似乎不允许 VBA 更改条件格式?

    我正在通过 Worksheet Change 事件运行一段代码 并且让它在事件开始时调用 unprotect sub 并在事件结束时匹配调用 protected sub 这按预期工作 我正在尝试在工作簿打开事件中将保护设置为 userint
  • 如何使用 Rails Clockwork gem 运行 rake 任务?

    从 Clockwork 调用 rake 任务的语法是什么 我尝试了各种语法 但似乎没有任何效果 我对发条特别感兴趣 因为 Heroku 支持它 这是我的clock rb 使用与每当gem使用相同的语法 module Clockwork pu
  • Angular2(或 TypeScript)中的“预期声明或声明”错误

    我对 Angular2 和 TypeScript 完全陌生 我正在关注教程 但我一直在处理这个错误 是编译器的错误还是什么原因导致的 看起来您正在使用旧版本的编译器 您需要下载并安装TypeScript 1 5 测试版 或更新版本 以便使用
  • 我们可以在.NETironpython中加载pandasDataFrame吗?

    我们可以使用iron python在 NET空间中加载pandas DataFrame吗 如果不是 我正在考虑将 pandas df 转换为 csv 文件 然后在 net 空间中读取 不 Pandas 与 CPython 紧密相连 就像你说
  • 读取 Filter 中的 Response.Body 流

    我编写了在服务器方法调用后运行的过滤器 并将其内容打印到控制台 代码是用ASP NET core v2 1编写的 public class MyCustomFilter ActionFilterAttribute public overri
  • C#:文本文件的尾部程序

    我有一个不断记录短行的日志文件 我需要开发一个对添加到该文件的新行做出反应 或轮询或侦听 的服务 这是一种 unix 的尾部程序 以便我的服务始终保持最新状态以保护该文件 我认为打开读取流并保持打开状态不是一个好主意 也许我应该使用 Fil
  • 使用Javascript获取Json对象的最大值

    这应该是一件容易的事 我就是想不通 如何使用 javascript 从这段 JSON 中获取最大值 data one 21 two 35 three 24 four 2 five 18 meta title Happy with the s
  • 有没有其他方法可以改变脚手架抽屉的宽度?

    目前Flutter Material不直接支持编辑Scaffold抽屉控制器 他们的图书馆打开了设置抽屉宽度的问题 我们可以访问和更改宽度吗 或者是否存在自定义脚手架实现来改变行为 只需复制抽屉源码 to a MyDrawer阶级与变革 k
  • 为什么流行的Java Base64编码库使用OutputStreams进行编码,使用InputStreams进行编码?

    我一直在尝试解决 Java 程序中的内存问题 其中我们将整个文件加载到内存中 对其进行 base64 编码 然后将其用作发布请求中的表单参数 这是由于文件太大而导致 OOME 我正在开发一种解决方案 可以通过 Base64 编码器将文件流式
  • Gradle 与文件目录的依赖关系

    我正在为 jboss Ear 应用程序编写 EJB jar 模块 我正在使用 gradle 我不想尝试声明 jar 在运行时从 JBoss 依赖的所有不同依赖项 有没有办法声明目录树中所有文件的依赖关系 在您的依赖项部分build grad
  • 如何创建文件格式?

    我一直在阅读一些有关文件格式的文章 并且对它们非常感兴趣 我想知道创建格式的过程是什么 例如 jpeg gif 或音频格式 您会使用什么编程语言 如果您使用某种编程语言 该网站警告我 这个问题可能会被关闭 但这只是我在追求知识时要冒的风险
  • wxPython:BoxSizer中的项目不水平扩展,仅垂直扩展

    我有几个不同大小的按钮 它们按照我想要的方式扩展 但是 当我将父级添加到新的 wx BoxSizer 用于在框架中的所有元素周围添加边框 时 已添加的 sizer 可以在垂直方向上正确运行 但不能在水平方向上运行 下面的代码演示了这个问题
  • 服务器上 WCF(双工)内存泄漏

    Hi have quite a problem with an Service running WCF in duplex mode It leaks memory not much but it s about 80MB a day an
  • Android 应用内计费 - RestoreTransactionInformation

    我正在尝试使用应用内计费将免费应用程序升级到 付费 版本 我使用的代码来自本教程作为官方开发者处理计费 对于像我这样的简单流程来说 网站过于复杂和混乱 我的升级代码工作正常 当我尝试添加一些内容来检查用户是否已经购买但通过重新安装或清除数据