0

我开发了一个项目,它为订阅集成了应用内计费方法。它是非消耗品,每月有 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()
    }

}
4

0 回答 0