I have an inapp subscription. I am trying to insure the re-installation of the app on the same or a new will recognize that the user has a valid subscription. The "already owned" response is supposed to have a value of "7", which works fine for consumable, managed products. For subscriptions, however, I do not get a "7" response. The messages are different, too. For a managed, consumable product the message is "Item already owned" with a "7" response. For subscriptions the message is "You already own this item", with NO "7" response, and the IAB result is "-1005:User cancelled". The subscription is active in Google Wallet, has not been cancelled. I get the response, I believe, from the alert box dismissal.
The question is, how do I recognize this response to the IABsetup? I have tried if request.mResponse = 1, but that does not work. I aparently do not get a useful response code for subscriptions. During testing, I have to turn off debug to upload the .apk so this is even more difficult to follow.
Why would the subscription response from the server differ from the managed product response for already owned items?
I need to be able to activate the app based on the already owned response.
Thanks.
I discovered the IabHelper.java has this:
else if (resultCode == Activity.RESULT_CANCELED) {
logDebug("Purchase canceled - Response: " + getResponseDesc(responseCode));
result = new IabResult(IABHELPER_USER_CANCELED, "User canceled.");
if (mPurchaseListener != null) mPurchaseListener.onIabPurchaseFinished(result, null);
}
I changed to this:
else if (resultCode == Activity.RESULT_CANCELED) {
logDebug("Purchase canceled - Response: " + getResponseDesc(responseCode));
result = new IabResult(responseCode, "User canceled.");
if (mPurchaseListener != null) mPurchaseListener.onIabPurchaseFinished(result, null);
}
Note the change from (IABHELPER_USER_CANCELED, "User canceled.") to (responseCode, "User Canceled")
Now the responseCode is passing to the
public void onIabPurchaseFinished(IabResult result, Purchase info)
so I can determine if the response is a dialog cancel and allow the active subscription to re-install and contact my server. At that point I am doing the 0Auth API call to Google to verify the installation and active subscription.
There may be alternate ways to do this, but this worked.
Hope this helps the 3 people in the world who are doing Android inapp subscriptions.
Related
I’m a newb building an Android app using HiveMQ to communicate with an IoT broker.
My stand-alone unit test works great: I can subscribe to a topic, publish a message, and see the response come into my subscription callback:
Mqtt3AsyncClient mqttClient = ...
mqttClient.subscribeWith()
.topicFilter(this.clientResponseTopic)
.callback(published -> {
Log.d(TAG, "subscribe: callback: enter");
String string = new String(published.getPayloadAsBytes(), StandardCharsets.UTF_8);
Log.d(TAG, "subscribe: callback: topic=" + published.getTopic() + "\n" + string);
})
.send()
.whenComplete((ack, exception) -> {
if(exception == null) {
Log.d(TAG, "subscribe: success");
}
else {
Log.w(TAG, "subscribe: failure", exception);
}
});
// ... on to publishing a message to broker
// ... that causes it to publish a message back to my subscription
And the unit test logs show the output:
subscribe: success
subscribe: callback: enter
subscribe: callback: topic=/app/2208401-0744fb78a23042d1fada1647c1a8576e/subscribe
However, when I run my App, I can see the subscription request sent successfully, but I never ever hear any messages in my callback:
subscribe: success
... (crickets) ...
I’ve tested both in an emulator, and on my personal device.
I’ve read the HiveMQ Andriod Guide, and I’ve checked all of that twice.
I feel like something is either blocking responses from coming in, or Android is killing whatever it is HiveMQ stands up to listen for responses.
If someone can suggest what I might be doing wrong, I’d appreciate it. If not, I’d also appreciate any HiveMQ/Android debug/diagnostic/triage tips. I’ve already added org.slf4j:slf4j-android:1.7.30 to my Gradle project in hopes of getting HiveMQ’s logging to show up, but it seems that is flawed.
The Firestore database set() does not call OnSuccessListener or OnFailureListener and don't writes the data to the database online!
As you see in the code first I get the instanceID via getInstanceId() and this is working because I see the log statements.
But the Firestore database is never executed. I don't see any data in my database and the both methods are not called either.
No error message in the log.
I don't get it what's the problem here without any error message :-(
FirebaseInstanceId.getInstance().getInstanceId().addOnSuccessListener(instanceIdResult -> {
if(instanceIdResult == null) {
Log.e(TAG, "Firebase Instance Id Result is null!");
HyperLog.e(TAG, "Firebase Instance Id Result is null!");
return;
}
String mToken = instanceIdResult.getToken();
HyperLog.e(TAG, mToken);
Log.e(TAG,mToken);
Log.e(TAG,"ID is" + instanceIdResult.getId());
db.collection(C_USERS).document(eMail).collection(C_DEVICES).document(instanceIdResult.getId())
.set(deviceInfo)
.addOnSuccessListener(aVoid -> {
Log.e(TAG, "Success!");
callBack.onAddUpdateTokenSuccess();
})
.addOnFailureListener(e -> {
//noinspection Convert2MethodRef
Log.e(TAG, "Failure!");
callBack.onAddUpdateTokenFailure(e);
});
I test this on a real device with debug build and this device has internet (Mobile and WiFi).
Seems the problem is a not allowed API in the Google Cloud Console.
After digging deeper into the logs I have seen that "FireStore" writes this line:
Requests to this API securetoken.googleapis.com method google.identity.securetoken.v1.SecureToken.GrantToken are blocked.
Now I have added Token Service API to the allowed API's and it is working.
Both callbacks are now called successfully and the document is also written.
Please check that your API is allowed or not in the Google CLoud Console
I'm using the Fovea.cc purchase plugin for an Android Cordova app. The plugin initializes, but cannot find my in-app purchase products - I get the same "Product could not be found" error I would if the product didn't exist or if the app was not published. I'm looking for more troubleshooting steps.
Here's the relevant code, which is called on app launch:
store.register({
id: "com.ineptech.wn.unlockgame",
alias: "unlockgame",
type: store.NON_CONSUMABLE
});
store.ready(function() {
console.log("\\o/ STORE READY \\o/"); // This does get called
});
store.when("unlockgame").approved(function (order) {
UnlockContent();
order.finish();
});
store.refresh();
var p = store.get("unlockgame");
popup("Title = " + p.title);
// this displays "Title = null"
And here's the code for the purchase button:
store.order("unlockgame");
// Results in "The item you were attempting to purchase
// could not be found" error from Play Store
Here's what a purchase attempt displays in logcat:
I/ActivityManager(424): Displayed com.android.vending/com.google.android.finsky.billing.lightpurchase.IabV3Activity: +80ms
E/Volley(22062): [1009] BasicNetwork.performRequest: Unexpected response code 500 for https://android.clients.google.com/fdfe/preparePurchase
And here's what I've already double-checked:
"unlockgame" is the correct product ID
The product is defined in the Play console and is Active
The app is published and active
The app is built in release mode and running on a real device
The device can reach the Play store okay
My app's license key is stored in res/values/billing_key.xml
What else might be keeping the plugin from getting the products?
The issue is most probably with your configuration, but first I see is that you're trying to access the product title right after doing store.refresh()
store.refresh();
var p = store.get("unlockgame");
However, store.refresh() method is asynchronous and will just trigger a request to the server. You can monitor changes to your product this way:
store.when("unlockgame").updated(function(p) {
alert("product is " + p.state + ", title is " + p.title);
});
Then call:
store.refresh();
Also check that the product ID on the play console is com.ineptech.wn.unlockgame, not just unlockgame, and that it's of type Managed.
I refered google play android api to check the purchase and consumption status of an in-app item.
For some orders, I can get right result,but some return the error as below:
error: {
errors: [
{
domain: "global",
reason: "purchaseTokenNotFound",
message: "The purchase token was not found.",
locationType: "parameter",
location: "token"
}
],
code: 404,
message: "The purchase token was not found."
}
Purchase token is provided by google, does it can be faked?
I found if I cancel the google order, then check the purchase status,it will return the purchase token was not found. if not, i will get the right purchase status.
Hope somebody can help.
If you are selling the same INAPP product to the same user muliple times within a short period, then it's very likely that all access tokens except the last purchase will return a 404 code.
For example:
john#example.com went to your app and purchased com.example.test.product a few times, you will probaly notice within your records (or Google Wallet Merchant account) that it's the same user buying the product.
When you go to check the last purchase from this user, then the following is likely to appear
{
kind: "androidpublisher#inappPurchase",
purchaseTime: "1409823171827",
purchaseState: "0",
consumptionState: "1",
developerPayload: "My Product | Ref | 1409823162466"
}
and yet if you were to check his previous tokens, then it's very likely that his purchases will return 404!
I had read somewhere (can't remember where) that the purchase token created for each purchase is basically based on the inapp product and google user. Therefore, it's very likely that each purchase will "destroy" any previous purchase token created for the same user.
Hope this explanation helps. I am constantly having this problem everyday when my server is attempting to connect to the Google API and check the transactions. Perhaps one day somebody will read this and provide a solution :)
The documents are misleading. You don't need to use this API to verify purchases.
Mobile app have INAPP_PURCHASE_DATA and INAPP_DATA_SIGNATURE from getBuyIntent method.
You can verify the purchase with the signature and your public key.
https://developer.android.com/google/play/billing/billing_reference.html#getBuyIntent
You can find the public key on Google Play Developer Console -> YOUR_APP -> service and API
package main
import (
"crypto"
"crypto/rsa"
"crypto/sha1"
"crypto/x509"
"encoding/base64"
"encoding/pem"
"fmt"
)
//replace const below with your own.
const (
pubKeyPEM=`-----BEGIN PUBLIC KEY-----
Some thing like this
-----END PUBLIC KEY-----`
data = `purchase data from getBuyIntent API`
sign = `purchase data signature from getBuyIntent API`
)
func Panic(err error) {
if err != nil {
panic(err)
}
}
func main() {
PEMBlock, _ := pem.Decode([]byte(pubKeyPEM))
if PEMBlock == nil {
Panic(fmt.Errorf("Could not parse Public Key PEM"))
}
if PEMBlock.Type != "PUBLIC KEY" {
Panic(fmt.Errorf("Found wrong key type"))
}
pubkey, err := x509.ParsePKIXPublicKey(PEMBlock.Bytes)
if err != nil {
Panic(err)
}
// compute the sha1
h := sha1.New()
h.Write([]byte(data))
// decode b64 signature
signature, err := base64.StdEncoding.DecodeString(sign)
Panic(err)
// Verify
err = rsa.VerifyPKCS1v15(pubkey.(*rsa.PublicKey), crypto.SHA1, h.Sum(nil), signature)
Panic(err)
// It verified!
fmt.Println("OK")
}
I am working on in app billing of android..i follow tutorial of android and currently i am testing for test app.(android.test.purchased)
I create app on google console
I sign apk and upload it to google console than i copy public key and paste it into my code and sign apk again and install it on phone than i tried to buy test purchased id It display me purchased successful but in my Log value i display the purchaseddata and datasignature and i got datasignature NULL (empty)
The fun part is in handleActivityResult method there is one if condition which checks weather datasignature or purchaseddata is Null or not and in my code it does not execute if skips it ? how it is possible?
Here i pur log but in my logcat i cannot see "In BUG Null value"
if (purchaseData == null || dataSignature == null) {
logError("BUG: either purchaseData or dataSignature is null.");
Log.e("Inapp", "In BUG Null value");
logDebug("Extras: " + data.getExtras().toString());
result = new IabResult(IABHELPER_UNKNOWN_ERROR, "IAB returned null purchaseData or dataSignature");
if (mPurchaseListener != null) mPurchaseListener.onIabPurchaseFinished(result, null);
return true;
}
I had this problem myself. After a while I found what I did wrong. I was calling the wrong method on the IABHelper.
If you call mHelper.launchPurchaseFlow(...) with an SKU that is registered as a subscription on Google Developer Console it will result in the error: IAB returned null purchaseData or dataSignature (response -1008:Unknown error).
If you have a SKU that is registered as an subscription you have to use the method: mHelper.launchSubscriptionPurchaseFlow(...) instead.
Hope this helps.