I'm having trouble figuring out how to detect when a refund has been issued for a managed (uncomsumable) in-app product in Android using com.android.billingclient:billing:2.0.3
. The problem seems fairly deep though maybe I'm making it more complicated than it ought to be.
To begin, I've made a test purchase which has been acknowledged AND refunded:
Looking at the logs of my app I see the following:
D/BillingManager: Got a verified purchase: Purchase. Json: {"orderId":"GPA.3362-7185-5389-78416","packageName":"com.glan.input","productId":"pro","purchaseTime":1567672759460,"purchaseState":0,"purchaseToken":"pbkpcaadklleoecegjfjdpbl.AO-J1OwsR6WVaVZCCYOU6JyYN1r0qJsrwitIPZfhc3jX4yketRUwNzKqwMgYx0TgZ2GebEGbXDL0RlMyogwtSKSPsaHCJ4RA4MPlIGay-aM1-QhmnqwjXjQ","acknowledged":true}
I/BillingManager: purchase pbkpcaadklleoecegjfjdpbl.AO-J1OwsR6WVaVZCCYOU6JyYN1r0qJsrwitIPZfhc3jX4yketRUwNzKqwMgYx0TgZ2GebEGbXDL0RlMyogwtSKSPsaHCJ4RA4MPlIGay-aM1-QhmnqwjXjQ is in 1 state
There's something funny going on here:
Log.d(TAG, "Got a verified purchase: " + purchase);
which is printing the underlying JSON which represents the purchase
. "purchaseState":0
Log.i(TAG, "purchase " + purchase.getPurchaseToken() + " is in " + purchase.getPurchaseState() + " state");
. purchase.getPurchaseState()
is resulting in a value of 1
If I look at the implementation of getPurchaseState
in Android Studio I see the following:
public @PurchaseState int getPurchaseState() {
switch (mParsedJson.optInt("purchaseState", PurchaseState.PURCHASED)) {
case 4:
return PurchaseState.PENDING;
default:
return PurchaseState.PURCHASED;
}
}
Earlier in the file the PurchaseState
interface is declared as:
@Retention(SOURCE)
public @interface PurchaseState {
// Purchase with unknown state.
int UNSPECIFIED_STATE = 0;
// Purchase is completed.
int PURCHASED = 1;
// Purchase is waiting for payment completion.
int PENDING = 2;
}
It seems like getPurchaseState
never returns PurchaseState.UNSPECIFIED_STATE
and only returns PENDING
which the JSON comes with a value of 4
. I've confirmed that a state of PENDING
is correctly returned when the purchase is performed with a payment method that takes a while to approve.
I've found posts like In-App Billing v3 - Don't detect refund which suggest that Play Services are caching purchases but I'm not convinced that's causing this problem because if I modify my code betweens runs of my app to acknowledge/consume the purchase those get state changes get immediately reflected in the JSON of the purchase.
How am I supposed to detect a refunded managed product?
When you are testing in app purchases on Android, you must use a signed version of the app. Your Google Play email must be listed in the Google Play Console as a tester. The easiest way to do this is to publish your app to a Beta release with open testing.
User returns a paid app: After purchasing a paid app, a user has up to 2 hours to return it for a full refund. They can only return an app once. If they purchase the same app again, they won't be able to return it a second time. User requests a refund: Users can request a refund on Google Play.
You can return most books bought on Google Play for a full refund within 7 days of purchase. If they are defective, unavailable, or don't perform as stated, you can request a refund at any time. If you return a book it may be removed from your library and you may not be able to read it. Single issues.
I have one purchase (SkuType.INAPP) in my application. I make a test purchase and then make a refund.
Problem:
purchase.getOriginalJson() // contains "purchaseState":0
purchase.getPurchaseState() // returns 1
Inside com.android.billingclient.api.Purchase:
public int getPurchaseState() {
switch(this.zzc.optInt("purchaseState", 1)) {
case 4:
return 2;
default:
return 1;
}
}
//...
public @interface PurchaseState {
int UNSPECIFIED_STATE = 0;
int PURCHASED = 1;
int PENDING = 2;
}
Hacky way to check purchaseState from original json:
purchase.getOriginalJson().contains(String.format("\"purchaseState\":%s", Purchase.PurchaseState.PURCHASED))
Unfortunately, this problem still exists!
More details here.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With