I have a problem purchasing subsciption items with the new InAppBilling V3
Here's my IABHelper's handleActivityResult method:
public boolean handleActivityResult(int requestCode, int resultCode, Intent data) {
IabResult result;
if (requestCode != mRequestCode)
return false;
checkSetupDone("handleActivityResult");
// end of async purchase operation
flagEndAsync();
if (data == null) {
Log.e(TAG + "Null data in IAB activity result.");
result = new IabResult(IABHELPER_BAD_RESPONSE, "Null data in IAB result");
if (mPurchaseListener != null)
mPurchaseListener.onIabPurchaseFinished(result, null);
return true;
}
int responseCode = getResponseCodeFromIntent(data);
String purchaseData = data.getStringExtra(RESPONSE_INAPP_PURCHASE_DATA);
String dataSignature = data.getStringExtra(RESPONSE_INAPP_SIGNATURE);\
...
purchaseData and dataSignature are null after that code, and as a result, the purchase flow can't be completed.
When Im trying to purchase in-app items, all works well. purchaseData and dataSignature aren't null.
This guy seem to had the same problem:
Google Play In-App Purchase returns error code -1008: null puchaseData or dataSignature
but it doesn't work for me, Im still getting null for the both fields.
What am I doing wrong? thanks in advance!
I only solved this problem, testing in REAL case. I published the app and sign with one test account, and get valid responses.
Related
I am implementing the new billing API 2.0 which has this purchase acknowledge method.
Earlier i was using AIDL for my purchase and i had following use case is that i used to pass developer payload during initiating purchase and used to get back my developer payload in the response as a part of purchase object.
Bundle bundle = mService.getBuyIntent(3, "com.example.myapp",
MY_SKU, "subs", developerPayload);
PendingIntent pendingIntent = bundle.getParcelable(RESPONSE_BUY_INTENT);
if (bundle.getInt(RESPONSE_CODE) == BILLING_RESPONSE_RESULT_OK) {
// Start purchase flow (this brings up the Google Play UI).
// Result will be delivered through onActivityResult().
startIntentSenderForResult(pendingIntent, RC_BUY, new Intent(),
Integer.valueOf(0), Integer.valueOf(0), Integer.valueOf(0));
}
and in my on on activity result i used to get purchase object
#Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == 1001) {
int responseCode = data.getIntExtra("RESPONSE_CODE", 0);
String purchaseData = data.getStringExtra("INAPP_PURCHASE_DATA");
String dataSignature = data.getStringExtra("INAPP_DATA_SIGNATURE");
if (resultCode == RESULT_OK) {
try {
JSONObject jo = new JSONObject(purchaseData);
String sku = jo.getString("productId");
alert("You have bought the " + sku + ". Excellent choice,
adventurer!");
}
catch (JSONException e) {
alert("Failed to parse purchase data.");
e.printStackTrace();
}
}
}
}
This purchase object has a developer payload which i send to the server for verification.
Now in the new API the developer payload can be added only after purchase is completed or when we consume a purchase, so the problem is after purchase is acknowledged i need the updated purchase object but how to get it?
if (!purchase.isAcknowledged()) {
AcknowledgePurchaseParams acknowledgePurchaseParams =
AcknowledgePurchaseParams.newBuilder()
.setPurchaseToken(purchase.getPurchaseToken())
.setDeveloperPayload(/* my payload */)
.build();
client.acknowledgePurchase(acknowledgePurchaseParams, acknowledgePurchaseResponseListener);
}
In acknowledgePurchaseResponseListener i get only the response code whether it is success or failure. But i need to updated purchase object object with developerPayload and isAcknowledged flag true.
Is there a way to do so? Could not find anything in documentation.
The local cache of purchases is updated by the time your acknowledgePurchaseResponseListener is called so you can query the purchase from the cache using https://developer.android.com/reference/com/android/billingclient/api/BillingClient.html#querypurchases. We will consider adding the update purchase to the listener for a future library release to make this more convenient.
I feel like I’m missing something really silly here!
After much confusion, reading and printing off seemingly endless contradictory information, I finally dived in and started testing in app billing. The activity is connecting ok to the Google Play server because it is returning the sku and price ok when calling getSkuDetails() in OnCreate().
I have set up product Id’s on the developer console, and also a test user. I uploaded an APK onto Alpha Testing a few days ago. On my device, signed in to Google Play Store with my developer account and ran the app in debug mode, setting android.test.cancelled as the product id in the launchPurchaseFlow () (in IabHelper).
The Google Payment dialog pops up, I click BUY and then unexpectedly get a Payment Successful message.
In the LogCat, the billing response code is 0 (BILLING_RESPONSE_RESULT_OK), causing handleActivityResult() to check the returned signature and purchasedata, which aren’t there ( makes sense? - because a purchase has not been requested in the first place). mPurchaseFinishedListener then treats this as an unknown error –1008.
public void onButtonClicked(View view) {
// Call BILLING API:
//
if (!billingStartedOk) {
Toast.makeText(context, "Purchase requires Google Play Store (billing) on your Android.", Toast.LENGTH_LONG).show();
return;
}
String payload = GameVariables.genPayLoad(); //
try {
mHelper.launchPurchaseFlow(activity, reservedProductIdCancelled, RC_REQUEST, mPurchaseFinishedListener, payload);
} catch (IabHelper.IabAsyncInProgressException e) {
e.printStackTrace();
}
}
D/GWL: in handleActivityResult, got responseCode = 0
D/GWL: in handleActivityResult, got resultCode = -1
E/IabHelper: In-app billing error: BUG: either purchaseData or dataSignature is null.
D/GWL: in handleActivityResult, got purchaseData == null || dataSignature == null
D/GWL: In mPurchaseFinishedListener - returned result.getResponse() = -1008
D/GWL: **** TrivialDrive Error: Error purchasing: IAB returned null purchaseData or dataSignature (response: -1008:Unknown error)
D/GWL: In mPurchaseFinishedListener - got !result.isSuccess
Following the docs on developer site , i have implemented InAppBilling v3 in my app recently . I have used the classes in the utils package provided in the TRIVIAL DRIVE sample.
The problem i am facing is if a user has purchased an in app product already on launching purchse flow again on another device the play store dialog shows ITEM ALREADY OWNED but the response code returned by IabResult does not match to the constant IabHelper.BILLING_RESPONSE_RESULT_ITEM_ALREADY_OWNED . The response code returned is actually one of the error codes in IabHelper class (-1005 User cancelled).
I would really like to know how can i get the actual response code instead of error code. Any help would be appreciated.
Below is the code for callback
// Callback for when a purchase is finished
IabHelper.OnIabPurchaseFinishedListener mPurchaseFinishedListener =
new IabHelper.OnIabPurchaseFinishedListener() {
public void onIabPurchaseFinished(IabResult result, Purchase purchase) {
if (result.isFailure()) {
if (result.getResponse() ==
IabHelper.BILLING_RESPONSE_RESULT_ITEM_ALREADY_OWNED) {
//already owned
boolean isPremium = true;
SharedPrefsUtils.setPremium(BaseActivity.this, isPremium);
EventBus.getDefault().post(new InAppBillingUiUpdateEvent(isPremium));
//setWaitScreen(false);
return;
}
//handle error
complain(result.getResponse() + " " + "Error purchasing: " + result);
//setWaitScreen(false);
return;
}
if (!verifyDeveloperPayload(purchase)) {
//corrupted
complain("Error purchasing. Authenticity verification failed.");
//setWaitScreen(false);
return;
}
//successful
if (purchase.getSku().equals(NO_ADS_PRODUCT_ID)) {
// bought the premium upgrade!
alert("Thank you for upgrading to premium!");
boolean isPremium = true;
SharedPrefsUtils.setPremium(BaseActivity.this, isPremium);
EventBus.getDefault().post(new InAppBillingUiUpdateEvent(isPremium));
//setWaitScreen(false);
}
}
};
I finally managed to find the problem in the IabHelper code , So here it goes whenever an Activity.RESULT_CANCELED result code is returned in handleActivityResult method the IabResult for all such cases is fixed with user cancelled (-1005) no matter what the reason is. So in order to get the correct actual response code replace the following code in handleActivityResult
else if (resultCode == Activity.RESULT_CANCELED) {
logDebug("Purchase canceled - Response: " + getResponseDesc(responseCode));
result = new IabResult(IABHELPER_USER_CANCELLED, "User canceled.");
if (mPurchaseListener != null) mPurchaseListener.onIabPurchaseFinished(result, null);
}
with this
else if (resultCode == Activity.RESULT_CANCELED) {
logDebug("Purchase canceled - Response: " + getResponseDesc(responseCode));
result = new IabResult(responseCode, null);
if (mPurchaseListener != null) mPurchaseListener.onIabPurchaseFinished(result, null);
}
hope it will saves someones time
I want to establish a content provider server whose different contents have to be bought using the Android InAppBilling API. If a mobile user buys an item he gets a unique purchase token. If the user wants to download some content from my server I can use that token to check if the purchase is valid and thus the user is allowed to download the content.
But how do I check if that purchase token really belongs to the user my server is talking to? Is there an existing authentication mechanism?
as you can see at the end of image there are "response code" you can use it like developer docs
#Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == 1001) {
int responseCode = data.getIntExtra("RESPONSE_CODE", 0);
String purchaseData = data.getStringExtra("INAPP_PURCHASE_DATA");
String dataSignature = data.getStringExtra("INAPP_DATA_SIGNATURE");
if (resultCode == RESULT_OK) {
try {
JSONObject jo = new JSONObject(purchaseData);
String sku = jo.getString("productId");
alert("You have bought the " + sku + ". Excellent choice,
adventurer!");
}
catch (JSONException e) {
alert("Failed to parse purchase data.");
e.printStackTrace();
}
}
}
}
if not enough also you can check In-app Billing Reference (IAB Version 3)
well I am trying to help not a prof..
I am testing In-App Purchase using Static References.
I am using the Static Reference android.test.purchased but i can't figure out how to get the response of the Test Purchase in my onActivityResult(...) Method.
Here is my onActivityResult(....) Method's Body:
#Override
protected void onActivityResult(int requestCode, int resultCode, Intent data)
{
// TODO Auto-generated method stub
super.onActivityResult(requestCode, resultCode, data);
Log.d("In-App", "Data = "+data.toString());
Log.d("In-App", "Request Code = "+requestCode);
Log.d("In-App", "Result Code = "+resultCode);
}
My Logcat says:
08-16 10:48:10.114: D/In-App(3889): Data = Intent { (has extras) }
08-16 10:48:10.114: D/In-App(3889): Request Code = 666
08-16 10:48:10.114: D/In-App(3889): Result Code = -1
Screen Shots of my Test Purchase:
1.
2.
The question is that, what extras should i extract from the Data Object in my onActivityResult(....) method, to detect the status of the current test purchase (Canceled, already purchased, Successfully accepted, etc) and the information about the product (JSON respnse)?
Any help is highly appreciated, thanks.
I have updated my onActivityResult(...) Method
int responseCode = data.getIntExtra("RESPONSE_CODE", 0);
String purchaseData = data.getStringExtra("INAPP_PURCHASE_DATA");
String dataSignature = data.getStringExtra("INAPP_DATA_SIGNATURE");
if (resultCode == RESULT_OK) {
//Purchased Status = Purchased
try {
//Product Details JSON
JSONObject jo = new JSONObject(purchaseData);
//Purchased Product ID
String sku = jo.getString("productId");
}
catch (JSONException e) {
e.printStackTrace();
}
}
else {
//Purchased Status = Not Purchased
}
This way i have got the purchase's status and the purchased product's details JSON.
First of all, you need a database to persist the orders. Second of all, when the user install the your app. You have to issue a RESTORE_TRANSACTIONS what the user has bought and insert the result into the local database. you can read more HERE.
EDITED:
For more details check out In-App Billing Integration