You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Google应用内购买72小时自动退款问题及代码排查咨询

Why Your Google Play In-App Purchases Are Auto-Refunding After 72 Hours

Hey there! The issue you're facing is a super common gotcha with Google Play Billing, and it’s directly tied to a missing critical step in your code: acknowledging purchases.

The Core Problem

Google Play requires that you explicitly acknowledge every non-consumable in-app purchase (like your remove_ads item) within 72 hours of the purchase being made. If you skip this step, the system automatically refunds the user—exactly what you’re seeing right now.

Looking at your onPurchasesUpdated method, you’re only showing a toast when a purchase succeeds, but you never tell Google Play that you’ve delivered the product (or applied the ad-free status) to the user. That’s why the platform is reversing the payment.

Fixing the Code

Let’s update your code to add purchase acknowledgment, plus handle edge cases like missed purchases if the app crashed right after payment.

Step 1: Update onPurchasesUpdated to Acknowledge Purchases

First, add a helper method to handle purchase confirmation, then integrate it into your update handler:

@Override
public void onPurchasesUpdated(BillingResult billingResult, @Nullable List<Purchase> list) {
    if (billingResult.getResponseCode() == BillingClient.BillingResponseCode.OK && list != null) {
        for (Purchase purchase : list) {
            handlePurchase(purchase);
        }
    } else if (billingResult.getResponseCode() == BillingClient.BillingResponseCode.USER_CANCELED) {
        Toast.makeText(this, "İptal edildi", Toast.LENGTH_SHORT).show();
    } else if(billingResult.getResponseCode() == BillingClient.BillingResponseCode.ITEM_ALREADY_OWNED) {
        Toast.makeText(this, "already owned", Toast.LENGTH_LONG).show();
        // Don't forget to acknowledge existing owned purchases too!
        List<Purchase> existingPurchases = mBillingClient.queryPurchases(BillingClient.SkuType.INAPP).getPurchasesList();
        for (Purchase p : existingPurchases) {
            if (!p.isAcknowledged()) {
                handlePurchase(p);
            }
        }
    } else {
        Toast.makeText(this, "Error: " + billingResult.getDebugMessage(), Toast.LENGTH_SHORT).show();
    }
}

// Helper method to handle purchase acknowledgment and benefit delivery
private void handlePurchase(Purchase purchase) {
    if (purchase.getPurchaseState() == Purchase.PurchaseState.PURCHASED) {
        if (!purchase.isAcknowledged()) {
            AcknowledgePurchaseParams acknowledgeParams = AcknowledgePurchaseParams.newBuilder()
                    .setPurchaseToken(purchase.getPurchaseToken())
                    .build();
            mBillingClient.acknowledgePurchase(acknowledgeParams, new AcknowledgePurchaseResponseListener() {
                @Override
                public void onAcknowledgePurchaseResponse(BillingResult billingResult) {
                    if (billingResult.getResponseCode() == BillingClient.BillingResponseCode.OK) {
                        // Here's where you should apply the user's purchase (e.g., disable ads)
                        Toast.makeText(MainActivity.this, purchase.getSku() + " successfully activated!", Toast.LENGTH_SHORT).show();
                    } else {
                        Toast.makeText(MainActivity.this, "Failed to confirm purchase: " + billingResult.getDebugMessage(), Toast.LENGTH_SHORT).show();
                    }
                }
            });
        } else {
            // Purchase already acknowledged, just apply the benefit
            Toast.makeText(MainActivity.this, purchase.getSku() + " is already active!", Toast.LENGTH_SHORT).show();
        }
    }
}

Step 2: Check for Unacknowledged Purchases on App Start

If the app crashes or closes right after a purchase, you’ll need to catch those unconfirmed orders when the app restarts. Add this logic to your onBillingSetupFinished method:

@Override
public void onBillingSetupFinished(BillingResult billingResult) {
    if (billingResult.getResponseCode() == BillingClient.BillingResponseCode.OK) {
        // First, check for any existing unacknowledged purchases
        QueryPurchasesParams queryParams = QueryPurchasesParams.newBuilder()
                .setProductType(BillingClient.SkuType.INAPP)
                .build();
        mBillingClient.queryPurchasesAsync(queryParams, (billingResult1, purchases) -> {
            if (billingResult1.getResponseCode() == BillingClient.BillingResponseCode.OK) {
                for (Purchase purchase : purchases) {
                    handlePurchase(purchase);
                }
            }
        });

        // Then continue with your existing SKU query logic
        final List<String> skuList = new ArrayList<>();
        skuList.add("remove_ads");
        final SkuDetailsParams.Builder params = SkuDetailsParams.newBuilder();
        params.setSkusList(skuList).setType(BillingClient.SkuType.INAPP);
        mBillingClient.querySkuDetailsAsync(params.build(), new SkuDetailsResponseListener() {
            @Override
            public void onSkuDetailsResponse(BillingResult billingResult2, List<SkuDetails> list) {
                if (billingResult2.getResponseCode() == BillingClient.BillingResponseCode.OK && list != null) {
                    BillingFlowParams flowParams = BillingFlowParams.newBuilder()
                            .setSkuDetails(list.get(0))
                            .build();
                    mBillingClient.launchBillingFlow(MainActivity.this, flowParams);
                }
            }
        });
    } else {
        Toast.makeText(MainActivity.this, "Billing setup error: " + billingResult.getDebugMessage(), Toast.LENGTH_SHORT).show();
    }
}

Key Takeaways

  • Always acknowledge non-consumable purchases: This tells Google Play you’ve delivered the product, preventing automatic refunds.
  • Handle edge cases: Check for unacknowledged purchases on app startup to cover scenarios where the app closed unexpectedly.
  • Log errors: Include debug messages to troubleshoot any acknowledgment failures.

Once you implement these changes, your purchases should stop getting automatically refunded after 72 hours.

内容的提问来源于stack exchange,提问作者Serhat Hacıoğlu

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.07 11:22:56