我开发了一个项目,它为订阅集成了应用内计费方法。它是非消耗品,每月有 3.99 美元的订阅项目。我使用了计费客户端方法 2.1.0 并在 Google 上配置了订阅项。当我通过内部测试测试应用订阅时,显示该项目已成功购买。但是,发布版本有很大的风险。所有用户订阅将在 3 天内自动退款。谷歌支持团队表示,确认所有购买必须在三天内完成。未能正确确认购买会导致这些购买被退款。
我集成了确认侦听器,但结果仍然相同。我将附上几个屏幕截图,显示该项目是如何退款的。
这是我处理过的代码。由于我的代码问题,是否有任何理由自动退款所有商品?感谢您的阅读和观看。
//build.gradle
implementation 'com.android.billingclient:billing:2.1.0'
implementation 'com.android.billingclient:billing-ktx:2.1.0'
//Androidmanifest.xml
<uses-permission android:name="com.android.vending.BILLING" />
//Subscription Purchase and Acknowledge code
class SubscriptionActivity : AppCompatActivity(), PurchasesUpdatedListener {
lateinit var billingClient: BillingClient
lateinit var skuList: MutableList<SkuDetails>
val acknowledgePurchaseResponseListener =
AcknowledgePurchaseResponseListener {
Toast.makeText(this@SubscriptionActivity, "Purchase acknowledged", Toast.LENGTH_SHORT).show() }
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_subscription)
setupBillingClient()
setupInitEvents()
}
private fun getAvailableProducts() {
if (billingClient.isReady)
{
val params = SkuDetailsParams.newBuilder()
.setSkusList(Arrays.asList(SubscriptionID))
.setType(BillingClient.SkuType.SUBS)
.build()
billingClient.querySkuDetailsAsync(params)
{
p0, skuDetailsList ->
if (p0.responseCode == BillingClient.BillingResponseCode.OK)
skuList = skuDetailsList as MutableList<SkuDetails>
//displayProduct(skuDetailsList)
else {
//Toast.makeText(this@SubscriptionActivity, "Cannot find product", Toast.LENGTH_SHORT).show()
}
}
}
else
{
//Toast.makeText(this@SubscriptionActivity, "Billing Client not ready", Toast.LENGTH_SHORT).show()
}
}
private fun setupBillingClient() {
//billingClient = BillingClient.newBuilder(this).setListener(this).build()
billingClient = BillingClient.newBuilder(this)
.enablePendingPurchases()
.setListener(this)
.build();
billingClient.startConnection(object:BillingClientStateListener{
override fun onBillingServiceDisconnected() {
//Toast.makeText(this@SubscriptionActivity, "Disconnected", Toast.LENGTH_SHORT).show()
}
override fun onBillingSetupFinished(p0: BillingResult) {
if (p0.responseCode == BillingClient.BillingResponseCode.OK) {
getAvailableProducts();
} else {
/*
Toast.makeText(
this@SubscriptionActivity,
"BillingClient connecting err : " + p0.responseCode,
Toast.LENGTH_SHORT
).show()
}
}
})
}
private fun setupInitEvents() {
buttonClose.setOnClickListener {
closeActivity()
}
buttonContainer.setOnClickListener {
purchase()
}
buttonRestorePurchase.setOnClickListener {
restorePurchase()
}
}
private fun closeActivity() {
setResult(Activity.RESULT_CANCELED, null);
finish()
}
private fun purchase() {
if (!SpLaunch.getPurchaseToken().isNullOrEmpty())
{
val builder = AlertDialog.Builder(this)
.setTitle(getString(R.string.restore_title))
.setMessage("Already Purchased")
.setPositiveButton(R.string.ok) { dialog, id ->
}
.create().show()
return
}
if (this::skuList.isInitialized && skuList.size > 0) {
var billingFlowParams = BillingFlowParams.newBuilder()
.setSkuDetails(skuList[0])
.build()
val responseCode = billingClient.launchBillingFlow(this, billingFlowParams).responseCode
} else {
//Toast.makeText(this@SubscriptionActivity, "No Subscription List", Toast.LENGTH_SHORT).show()
}
}
private fun restorePurchase() {
//Toast.makeText(this@SubscriptionActivity, "restorePurchase", Toast.LENGTH_SHORT).show()
if (SpLaunch.getPurchaseToken().isNullOrEmpty())
{
val builder = AlertDialog.Builder(this)
.setTitle(getString(R.string.restore_title))
.setMessage(getString(R.string.restore_message))
.setPositiveButton(R.string.ok) { dialog, id ->
}
.create().show()
} else {
val url = "https://www.googleapis.com/androidpublisher/v3/applications/" + packageName + "/purchases/subscriptions/" + SubscriptionID + "/tokens/" + SpLaunch.getPurchaseToken() + ":refund"
HttpTask({
if (it == null) {
//Toast.makeText(this@SubscriptionActivity, "Connection error", Toast.LENGTH_SHORT).show()
return@HttpTask
}
//Toast.makeText(this@SubscriptionActivity, "Refund Success", Toast.LENGTH_SHORT).show()
SpLaunch.setPurchaseToken("")
}).execute("POST", url, "")
}
}
override fun onPurchasesUpdated(p0: BillingResult, p1: MutableList<Purchase>?) {
if (p0.responseCode == BillingClient.BillingResponseCode.OK && p1 != null) {
//Toast.makeText(this@SubscriptionActivity, "Purchase Success", Toast.LENGTH_SHORT).show()
for (_pur in p1) {
var packageName: String = _pur.packageName
var purchaseToken: String = _pur.purchaseToken
var purchaseTime: Long = _pur.purchaseTime
var purchaseState: Int = _pur.purchaseState
SpLaunch.setPurchasePackage(packageName)
SpLaunch.setPurchaseToken(purchaseToken)
SpLaunch.setPurchaseTime(purchaseTime)
SpLaunch.setPurchaseState(purchaseState)
handlePurchase(_pur)
}
setResult(Activity.RESULT_OK, null)
finish()
} else if (p0.responseCode == BillingClient.BillingResponseCode.USER_CANCELED) {
//Toast.makeText(this@SubscriptionActivity, "Purchase canceled by User", Toast.LENGTH_SHORT).show()
setResult(Activity.RESULT_CANCELED, null)
finish()
} else {
//Toast.makeText(this@SubscriptionActivity, "Purchase canceled by Error: " + p0.responseCode, Toast.LENGTH_SHORT).show()
setResult(Activity.RESULT_CANCELED, null)
finish()
}
}
private fun handlePurchase(purchase : Purchase)
{
//in handlePurchase()
if (purchase.purchaseState === Purchase.PurchaseState.PURCHASED) {
/*
val acknowledgePurchaseParams: AcknowledgePurchaseParams =
AcknowledgePurchaseParams.newBuilder()
.setPurchaseToken(purchase.purchaseToken)
.build()
billingClient.acknowledgePurchase(
acknowledgePurchaseParams,
acknowledgePurchaseResponseListener
)*/
val params = AcknowledgePurchaseParams.newBuilder()
.setPurchaseToken(purchase.purchaseToken)
.build()
billingClient.acknowledgePurchase(params) { billingResult ->
val responseCode = billingResult.responseCode
val debugMessage = billingResult.debugMessage
}
}
}
override fun onDestroy() {
super.onDestroy()
billingClient.endConnection()
}
}