- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
我正在尝试启用应用内购买,但无法弄清楚为什么我的 skuDetails 查询总是返回 0 个项目。
我已将该应用程序公开到我的内部测试并将我的邮件地址添加到许可证测试(尝试了我的开发人员邮件和 2. 邮件地址),然后我在手机上下载了该应用程序,等待了几个小时,然后尝试进行购买。它接受我的连接,但在我查询 SKU 详细信息时总是在我的 skuDetailsList 中返回 0 个元素。请帮忙?
list :
<uses-permission android:name="com.android.vending.BILLING" />
build.gradle:
def billing_version = "3.0.0"
implementation "com.android.billingclient:billing-ktx:$billing_version"
querySkuDetails 方法:
suspend fun querySkuDetails() {
val skuList = ArrayList<String>()
skuList.add(PREMIUM_UPGRADE_ID)
val params = SkuDetailsParams.newBuilder()
params.setSkusList(skuList).setType(BillingClient.SkuType.INAPP)
withContext(Dispatchers.IO) {
billingClient.querySkuDetailsAsync(params.build()) { billingResult, skuDetailsList ->
// Process the result.
if (billingResult.responseCode == BillingClient.BillingResponseCode.OK) {
Log.v(TAG, "queryInventoryAsync - skuDetailsList:")
if (!skuDetailsList.isNullOrEmpty()) {
for (item in skuDetailsList) {
Log.v(TAG, item.toString())
skuDetails = item
}
} else {
Log.v(TAG, "skuDetailsList - is empty")
}
return@querySkuDetailsAsync
}
谷歌游戏控制台:
import android.util.Log
import android.widget.Toast
import com.alexco.habitrack.activities.main.MainActivity
import com.alexco.habitrack.utilities.Persistence
import com.android.billingclient.api.*
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
class BillingCli(private val activity: MainActivity, private val database: Persistence) {
companion object {
private const val TAG = "InAppBilling"
private const val PREMIUM_UPGRADE_ID = "noaddsplease"
}
private var purchases: Purchase.PurchasesResult? = null
private var skuDetails: SkuDetails? = null
private val purchasesUpdateListener =
PurchasesUpdatedListener { billingResult, purchases ->
if (billingResult.responseCode == BillingClient.BillingResponseCode.OK && purchases != null) {
for (purchase in purchases) {
handlePurchase(purchase)
}
} else if (billingResult.responseCode == BillingClient.BillingResponseCode.USER_CANCELED) {
// Handle an error caused by a user cancelling the purchase flow.
} else {
// Handle any other error codes.
}
}
private var billingClient: BillingClient = BillingClient.newBuilder(activity)
.setListener(purchasesUpdateListener)
.enablePendingPurchases()
.build()
init {
connect()
}
private fun handlePurchase(purchase: Purchase) {
if (purchase.purchaseState == Purchase.PurchaseState.PURCHASED) {
Log.v(TAG, "purchase approved")
database.setPremium(activity, true)
activity.newHabitHandler.openAddHabitActivity(activity.tutorial.enabled)
Toast.makeText(activity, "welcome to premium", Toast.LENGTH_LONG).show()
if (!purchase.isAcknowledged) {
val acknowledgePurchaseParams = AcknowledgePurchaseParams.newBuilder()
.setPurchaseToken(purchase.purchaseToken)
suspend {
val ackPurchaseResult = withContext(Dispatchers.IO) {
billingClient.acknowledgePurchase(
acknowledgePurchaseParams.build()
) {
Log.v(TAG, "purchase acknowledged")
}
}
}
}
}
if (purchase.purchaseState == Purchase.PurchaseState.PENDING) {//pending
Log.v(TAG, "purchased pending")
return
}
if (purchase.purchaseState == Purchase.PurchaseState.UNSPECIFIED_STATE) {//pending
Log.v(TAG, "purchase unspecified state")
return
}
}
private fun connect() {
billingClient.startConnection(object : BillingClientStateListener {
override fun onBillingSetupFinished(billingResult: BillingResult) {
if (billingResult.responseCode == BillingClient.BillingResponseCode.OK) {
Log.v(TAG, "service connected")
// The BillingClient is ready. You can query purchases here.
suspend {
querySkuDetails()
}
suspend {
purchases = billingClient.queryPurchases(BillingClient.SkuType.INAPP)
Log.v(TAG, "onBillingSetUpFinished, purchases: ${purchases.toString()}")
}
} else {
Log.v(TAG, "service did not connect")
}
}
override fun onBillingServiceDisconnected() {
Log.v(TAG, "service disconnected")
}
})
}
suspend fun querySkuDetails() {
val skuList = ArrayList<String>()
skuList.add(PREMIUM_UPGRADE_ID)
val params = SkuDetailsParams.newBuilder()
params.setSkusList(skuList).setType(BillingClient.SkuType.INAPP)
withContext(Dispatchers.IO) {
billingClient.querySkuDetailsAsync(params.build()) { billingResult, skuDetailsList ->
// Process the result.
if (billingResult.responseCode == BillingClient.BillingResponseCode.OK) {
Log.v(TAG, "queryInventoryAsync - skuDetailsList:")
if (!skuDetailsList.isNullOrEmpty()) {
for (item in skuDetailsList) {
Log.v(TAG, item.toString())
skuDetails = item
}
} else {
Log.v(TAG, "skuDetailsList - is empty")
}
return@querySkuDetailsAsync
}
if (billingResult.responseCode == 1) {
//user cancel
return@querySkuDetailsAsync
}
if (billingResult.responseCode == 2) {
Toast.makeText(activity, "Internet required for purchase", Toast.LENGTH_LONG)
.show()
return@querySkuDetailsAsync
}
if (billingResult.responseCode == 3) {
Toast.makeText(
activity,
"Incompatible Google Play Billing Version",
Toast.LENGTH_LONG
).show()
return@querySkuDetailsAsync
}
if (billingResult.responseCode == 7) {
Toast.makeText(activity, "you already own Premium", Toast.LENGTH_LONG)
.show()
return@querySkuDetailsAsync
}
Toast.makeText(
activity,
"no skuDetails sorry",
Toast.LENGTH_LONG
)
.show()
Log.v(TAG, "responseCode: ${billingResult.responseCode}")
Log.v(TAG, "debug: ${billingResult.debugMessage}")
}
}
}
fun makePurchase() {
if (purchases != null) {
if (purchases!!.purchasesList != null) {
if (purchases!!.purchasesList!!.size > 0) {
Toast.makeText(
activity,
"you already owns premium\n-premium activated-",
Toast.LENGTH_LONG
).show()
database.setPremium(activity, true)
activity.newHabitHandler.openAddHabitActivity(activity.tutorial.enabled)
}
}
}
// Retrieve a value for "skuDetails" by calling querySkuDetailsAsync().
if (skuDetails != null) {
val flowParams = BillingFlowParams.newBuilder()
.setSkuDetails(skuDetails!!)
.build()
val responseCode = billingClient.launchBillingFlow(activity, flowParams).responseCode
Log.v(TAG, "makePurchase - responseCode:$responseCode")
} else {
Log.v(TAG, "makePurchase - no skuDetails")
Toast.makeText(activity, "Google didn't accept the purchase attempt", Toast.LENGTH_LONG)
.show()
}
}
最佳答案
我在 google play 控制台中删除了该项目并获得了一个具有新 id 的新项目,并且它工作了¯_(ツ)_/¯
关于android billing querySkuDetailsAsync 总是返回 0 skuDetails,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/65457918/
我不明白 int 63823 为何比 double 1.0 占用更少的空间。在这个特定实例中,int 中是否没有存储更多信息? 最佳答案 I don't understand how an int 6
这可能不是一个直接的代码问题,但它是一个经常出现在 SO 上的问题,我发现阅读它非常有用。 App Store - Help answering “Missing Compliance” (using
我在我们的应用程序中使用 syncfusion 寻呼机和下拉列表请打开以下链接。 https://stackblitz.com/edit/angular-nv6myv?file=src%2Fapp%2
以便解释指针和引用in this question我写了这段代码。 MyClass& MyClass::MyInstance() { static MyClass & myLoca
在 C 和 C++ 中,assert 是一个非常 重量级例程,将错误写入 stdout 并终止程序。在我们的应用程序中,我们实现了一个更强大的 assert 替代品,并为其提供了自己的宏。已尽一切努力
我已经创建了一个 MVC webApi 项目,现在我想使用身份验证和授权。我想我已经实现了这种安全措施,但由于某种原因,有些事情变糟了,当我编写我的凭据并尝试调用一些 webApi 方法时,显示消息“
我发现自己使用一种奇怪的方式向我的函数添加回调函数,我想知道是否有更通用的方式向函数添加回调函数,最好的情况是我的所有函数都检查最后给定的作为函数的参数,如果是,则将其用作回调。 我以前是这样的: v
几乎从来没有我只想获取某个 Remote 的情况;我总是想要所有的 Remote 。我认为这将是一个足够常见的用例,git 会考虑它(与他们有 pull.rebase true 的方式相同)。 那么,
我正在尝试使用 inarray 但它总是返回 true?有任何想法吗? (所有 li 均已显示) $("#select-by-color-list li").hide(); // get the se
我正在尝试为我公司的开发环境设置过期网址。我们使用 lighttpd在此环境中提供上传的文件,我发现 these docs这似乎相当有希望。 问题是我似乎根本无法让它工作,而且我有点不知所措,试图找出
我无法让“文件夹”外部变量工作。我总是得到[:]。 我正在 Windows 下的 Grails 上进行开发(这就是为什么外部配置文件看起来像 file:C:\path\to/file)。 我在另一个项
这个问题是出于对 PL 如何工作的好奇,而不是其他任何事情。 (它实际上是在查看与 Haskell 不同的 SML 时想到的,因为前者使用按值调用 - 但我的问题是关于 Haskell。) Haske
我有一个高速缓存内存模块,我希望它是可字寻址的,但有字节的写使能信号。 always @ (posedge clk) begin //stuff... if(write) begin
我正在处理一些代码,其中一个对象“foo”正在创建另一个对象对象“bar”,并向其传递一个Callable。之后 foo 将返回bar,然后我希望 foo 变得无法访问(即:可用于垃圾收集)。 我最初
我已将我的程序与此方法相关联: public static void CreateFileAssociation(string extension, string key, string descri
所以我正在进行目录遍历,但我无法让 opendir 按照我想要的方式工作。它总是无法打开我发送的目录,它给出了一些未知的错误。我通常传入 argv[1],但我放弃了,只是开始硬编码路径。 char *
这个问题在这里已经有了答案: How do I compare strings in Java? (23 个回答) 关闭 9 年前。 出于某种原因,我的(基本)程序总是打印我为 else 语句保留的
我不想冒为此提出破解的风险,因为它涉及 datetime 对象。基本上,我想按如下方式进行转换: 2010-04-21 06:37:53 -> 2010-04-21 06:40:00 2010-08-
我正在用 C 语言玩文件 I/O。我正在尝试使用 fgets 从一个文件中读取数据并将其输出到另一个文件。问题是它总是返回 NULL,因此没有任何内容被复制到输出文件中。这是我的代码: #includ
class MyClass { // empty class with no base class }; int main() { MyClass* myClass = new MyC
我是一名优秀的程序员,十分优秀!