Android InApp Purchase - How to handle Pending Status? - java

I am integrating Android In-App purchase with Android Native application. I used following code to start In-App purchase intent!
Bundle buyIntentBundle = mService.getBuyIntent(3, getPackageName(),”product_sku”, "inapp",”Some Developer Payload”);
PendingIntent pendingIntent = buyIntentBundle.getParcelable("BUY_INTENT");
startIntentSenderForResult(pendingIntent.getIntentSender(),REQUEST_CODE, new Intent(),Integer.valueOf(0), Integer.valueOf(0), Integer.valueOf(0));
Above code is working with and open Google Play for In-app purchase. I overridden the onActivityResult for getting the result of In-App Purchase (following is code)
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == 1001) {
if (resultCode == RESULT_OK) {
try {
final JSONObject jo = new JSONObject(purchaseData);
String orderId = jo.getString("orderId");
String productId = jo.getString("productId");
String state = String.valueOf(jo.getInt("purchaseState"));
//POST purchase result to my server for my record!
//Finally Consume the product
} catch (JSONException e) {
e.printStackTrace();
}
}
}
}
Following is response we received from InApp purchase
'{
"orderId":"12999763169054705758.1371079406387615",
"packageName":"com.example.app",
"productId":"exampleSku",
"purchaseTime":1345678900000,
"purchaseState":0,
"developerPayload":"bGoa+V7g/yqDXvKRqq+JTFn4uQZbPiQJo4pf9RzJ",
"purchaseToken":"rojeslcdyyiapnqcynkjyyjh"
}'
My questions is that how to identify that the in-app purchase transaction is in “Pending” status because "purchaseState" (Integer value in JSON) having only three values “purchased”,” canceled” and “refunded”.
I am from india and google wallet is accepting any transaction immediately after inapp purchase completed.
It is always in Pending status first and then takes upto 48 hours to accept or reject the transaction!
I want only allow my customer to use In app purchased product only after successfully payment in Google waltet.
I faced that My one customer purchased inapp product from my application but somehow their data is not posted on server. Customer told me that that purchase transaction was in pending status initially and completed after two days, even I got that payment in my Merchant account so I concluded that Android is not responding resultCode == RESULT_OK in onActivityResult is purchased is in pending.
So If anyone faced same problem then please explain me the solution or flow to handle the pending status! Should I have to use APN for pending status? But I need that pending status order details to store on my server!
Thanks in Advance!

I know this question was asked a long time ago. But I am facing the very same issue (with an App for the Brazilian market).
I noticed that purchases which got processed right away, everything works fine.
But, some purchases get into the 'pending payment' status and it can take up to 48 hours to change status. But, my server never gets called - as OP stated, I suspect Google play server returns a different status (other than RESULT_OK) and my app doesn't capture that.
Then, my user gets charged, but has no access to the PREMIUM features - since my server never got notified.
So, as of 2018, I read that we might have an alternative. The following link has some information on how to deal with subscriptions
https://developer.android.com/google/play/billing/billing_subscriptions
.. and points to the following:
https://developer.android.com/google/play/billing/realtime_developer_notifications
"Realtime Developer Notifications"
this allows Google play to send your server a notification, every time a user subscription status changes. So, if your user's payment took 48h to complete, your server would receive a notification and you could make the user PREMIUM or whatever logic you need.
Hope it helps.

Related

Google Billing Library : QueryPurchasesAsync : Subscription resource of cancelled purchase not returned

I am working on integrating Google Billing Library into my application for making purchases from my app. I have created a 1-month test subscription from the application. Every time a subscription is auto-renewed, a subscription resource is returned then i am using the value of "IsAutorenewing" for processing of the purchase in my app
After few renewals of the existing subscription, i have cancelled the subscription and tried to query the purchases using querypurchasesasync but this time the subscription resource is not returned after the subscription is cancelled to check the value of IsAutoRenewing
Can you please suggest why cancelled purchase is not returned & what needs to be done in order to get the cancelled purchase also?

How to handle onNotificationReceived from Firebase every 10 minutes while app is in background. Android 9.0

I need to be able to send my GPS-location to a server every time I receive a silent-push notification from Firebase. The timer for sending a notification is currently set to every 10 minutes. When the phone is charging this is not a problem, but when it's idle and the application is in the background, the onMessageReceived from FirebaseMessagingService is only called every few hours. This leads me to believe it has something to do with the new power management rules for Android 9.0. But for my application to work I need to be able to send my location every 10 minutes. Not just sometimes.
I have tried to set the battery optimisation to 'not optimised' using Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS
This does not seem to have any effect.
I also tried setting the priority of the Firebase notification to High. This works, but only for 10 messages per day, which means it's not a solution for my application. (Source: Power Management restrictions)
I am now trying to always have the application in the 'Active' bucket by opening a foregroundService. This should make sure the app is never in any other bucket, where the notifications can be deferred for a few hours.
An app is in the active bucket if the user is currently using the app, for example:
The app has launched an activity
The app is running a foreground service
The app has a sync adapter associated with a content provider used by a foreground app
The user clicks on a notification from the app
If an app is in the active bucket, the system does not place any restrictions on the app's jobs, alarms, or FCM messages.
(Source: Power Buckets Android9).
This does not seem like a solution I should want, though, since it might not be best practice. And it doesn't seem to work either anyways.
This is my onMessageReceived function:
#Override
public void onMessageReceived(RemoteMessage remoteMessage) {
Log.d(TAG, "From: " + remoteMessage.getFrom());
if (remoteMessage.getData().size() > 0) {
Log.d(TAG, "Message data payload: " + remoteMessage.getData());
Map<String, String> params = remoteMessage.getData();
JSONObject object = new JSONObject(params);
PushNotificationManager.getInstance().handlePushNotification(object, getApplicationContext());
}
}
Right now I am clueless as to why the Firebase messages do not enter the onMessageReceived function.
Tim,
I think that the main problem is with how you send the push notification and what is its content.
If you check here - https://firebase.google.com/docs/cloud-messaging/android/receive
you will see that if the message contains notification data and the app is at background then the push notification goes to the Notification area.
You need to send a push notification that contains only data:
(Here you can find more information about the different types - https://firebase.google.com/docs/cloud-messaging/concept-options#notifications_and_data_messages)
Here is what is the explanation how to trigger it-
In a trusted environment such as Cloud Functions or your app server, use the Admin SDK or the FCM Server Protocols: Set the data key only.
Please note that if you need to do a long running operation, then you will need to start a separate service (the service should present a notification in the notification area that it is running, otherwise you will get a crash on the newer Android versions)
Fixed by using AlarmManager. Schedule an alarm in 10 minutes, and when completing the code within the alarm, schedule a new alarm in 10 minutes.
AlarmManager can wake the phone if it is in doze mode, which means all code inside the trigger will be executed properly.

Android 8.0 Oreo - Accounts

In my app, I need to known if there is any Google account or any Samsung account.
Up to Android 7 it was easy to get this information with something like:
Account[] accounts = AccountManager.get(getContext())
.getAccountsByType("com.google")
But with the event of Oreo this does not work anymore.
EDIT: see official information on this subject:
In Android 8.0 (API level 26), apps can no longer get access to user accounts unless the authenticator owns the accounts or the user grants that access. The GET_ACCOUNTS permission is no longer sufficient. To be granted access to an account, apps should either use AccountManager.newChooseAccountIntent() or an authenticator-specific method. After getting access to accounts, an app can can call AccountManager.getAccounts() to access them.
Android 8.0 deprecates LOGIN_ACCOUNTS_CHANGED_ACTION. Apps should instead use addOnAccountsUpdatedListener() to get updates about accounts during runtime.
For information about new APIs and methods added for account access and discoverability, see Account Access and Discoverability in the New APIs section of this document
I spent half a day to find a solution to my need, without success.
I've found information claiming that now the only way to access to accounts is to use AccountPicker like this:
AccountPicker.newChooseAccountIntent(null, null, new String[]{"com.google"},true, null, null, null, null);
But this does respond to my problem. To be clear I only need to know if an account exists for a certain type (Google, Samsung...) I do not need to know how much if so and do not need accounts information.
Using "android.permission.READ_CONTACTS" permission, and
Account[] accounts = AccountManager.get(getContext())
.getAccountsByType("com.google")
working again in android Oreo
As you already said, there's no way to read other accounts if the user didn't give you the permission to do so. The permission now is provided not only with the run-time permission but even with the account picker, i.e. an account is visible to your app only if the user selected the account after you called the account picker. This new restriction is exactly to avoid what you are trying to do: read all user accounts. There's no solution to your problem, the only thing you can do is to present the picker to the user and let him select all the accounts, not the best user experience however.
Edit: starting from Google Play Services 11.6 there's now a new method requestGoogleAccountsAccess() to get all Google accounts.
To get the installed google accounts on a device running Oreo+ (8+) with this code
Account[] accounts = AccountManager.get(getContext()).getAccountsByType("com.google")
You need to first call
https://developers.google.com/android/reference/com/google/android/gms/auth/GoogleAuthUtil.html#requestGoogleAccountsAccess(android.content.Context)
Please add the following dependency first
com.google.android.gms:play-services-auth:16.0.0
The call requestGoogleAccountsAccess() throws an exception which you can cast (after checking) to UserRecoverableAuthException and get an intent from it to start with startActivityForResult
Here is some example code, working on Android Oreo
// call this on a background thread!
private void requestGoogleAccountAccess() throws Exception
{
googleAccountAccessGranted = GoogleAuthUtil.requestGoogleAccountsAccess(this);
Log.i(TAG, "googleAccountAccessGranted: " + googleAccountAccessGranted);
}
// exception handler after calling method above
private void handleAuthResult(Throwable e)
{
if (e instanceof UserRecoverableAuthException)
{
UserRecoverableAuthException authException = (UserRecoverableAuthException) e;
startActivityForResult(authException.getIntent(), AUTH_PERMISSION_REQUEST);
}
else
{
Log.e(TAG, "Cannot request Google Account Access", e);
}
}
#Override
protected void onActivityResult(int requestCode, int resultCode, Intent data)
{
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == AUTH_PERMISSION_REQUEST)
{
Log.i(TAG, "Google Auth Permission Result");
if (resultCode == Activity.RESULT_CANCELED)
{
Log.w(TAG, "User Cancelled Play Services Auth Request.")
}
else if (resultCode == Activity.RESULT_OK)
{
Log.d(TAG, "User accepted Play Services Auth Request.");
// call the following line again on a background thread. the call now returns a boolean instead of throwing an exception
// googleAccountAccessGranted = GoogleAuthUtil.requestGoogleAccountsAccess(this);
}
}
}
It's a bit strange why Google decided themselves for this "architecture". Why not return a Task, etc.
But this is how you get it working.
Of course this code needs proper exception handling which I left out for readability.

Check the status of In-App Subscription in Android

I have created an application in which there is in-app purchases which has monthly auto renewal.. on first time successful payment i have called a web-service in which i have made the user to premium class.
Now if the user has cancelled the payment manually from the google server, how could i know that user has cancelled his/her subscription.
Is there is some PHP code query or from android i have to called something in background to check the status??
Thank you in advace
I haven't tested this, but could you use the autoRenewing field in INAPP_PURCHASE_DATA?
If true, the subscription is active, and will automatically renew on the next billing date. If false, indicates that the user has canceled the subscription.
http://developer.android.com/google/play/billing/billing_reference.html#getBuyIntent
Apparently, the autoRenewing field was added or at least documented in the beginning of 2015.
You can check the purchased subscriptions inside the app via
Edit: For subscriptions use "subs", for inapp-purchases use "inapp" e.g.:
Inapp-Purchases: mService.getPurchases(3, getPackageName(), "inapp", null);
Subscriptions: mService.getPurchases(3, getPackageName(), "subs", null);
See also Querying for Purchased Items at http://developer.android.com/google/play/billing/billing_integrate.html
So you can implement a task in your app where you check if the user still has a subscription. If not, you can remove the premium status. Moreover this information could be useful for you:
When the user cancels a subscription, Google Play does not offer a refund for the current billing cycle. Instead, it allows the user to have access to the canceled subscription until the end of the current billing cycle, at which time it terminates the subscription. For example, if a user purchases a monthly subscription and cancels it on the 15th day of the cycle, Google Play will consider the subscription valid until the end of the 30th day (or other day, depending on the month).
source: http://developer.android.com/google/play/billing/billing_subscriptions.html under subscription cancelled
Hope this could help you
Edit2: Because the AIDL library which is mentioned in my answer is deprecated and deactivated in the future, it is recommended to switch to the new Google Play Billing Library.
Check the getPurchases() method here. Or if you use Android Studio and have the source code downloaded just press Ctrl + Q on IInAppBillingService.getPurchases method

CONNECTION_FAILED while creating room Google Play Services

This is my function to quickGame
Bundle autoMatchCriteria = RoomConfig.createAutoMatchCriteria(1, 1, 0);
RoomConfig.Builder rtmConfigBuilder = RoomConfig.builder(this);
rtmConfigBuilder.setMessageReceivedListener(this);
rtmConfigBuilder.setRoomStatusUpdateListener(this);
rtmConfigBuilder.setAutoMatchCriteria(autoMatchCriteria);
switchToScreen(R.id.screen_wait);
//keepScreenOn();
//resetGameVars();
getGamesClient().createRoom(rtmConfigBuilder.build());
and onRoomCreated where game stops
#Override
public void onRoomCreated(int statusCode, Room room) {
Log.d(TAG, "onRoomCreated(" + statusCode + ", " + room + ")");
if (statusCode != GamesClient.STATUS_OK) {
Log.e(TAG, "*** Error: onRoomCreated, status " + statusCode);
showGameError();
return;
}
showWaitingRoom(room);
}
Im getting statusCode = 7000 which is STATUS_REAL_TIME_CONNECTION_FAILED, and room = null so i think problem is in quickGame function.
D/Multiplayer(24221): Sign-in succeeded.
D/Multiplayer(24221): GameHelper: onActivityResult: req=10001, resp=RESULT_CANCELED
D/Multiplayer(24221): GameHelper: onActivityResult: request code not meant for us. Ignoring.
W/Multiplayer(24221): *** invitation inbox UI cancelled, 0
D/Multiplayer(24221): GameHelper: onActivityResult: req=10000, resp=RESULT_OK
D/Multiplayer(24221): GameHelper: onActivityResult: request code not meant for us. Ignoring.
D/Multiplayer(24221): Select players UI succeeded.
D/Multiplayer(24221): Invitee count: 1
D/Multiplayer(24221): Creating room...
D/Multiplayer(24221): Room created, waiting for it to be ready...
D/Multiplayer(24221): onRoomCreated(7000, null)
E/Multiplayer(24221): *** Error: onRoomCreated, status 7000
W/ResourceType(24221): getEntry failing because entryIndex 13 is beyond type entryCount 1
W/ResourceType(24221): Failure getting entry for 0x7f0b000d (t=10 e=13) in package 0 (error -2147483647)
E/GooglePlayServicesUtil(24221): The Google Play services resources were not found. Check your project configuration to ensure that the resources are included.
W/PicturePileLayerContent(24221): Warning: painting PicturePile without content!
W/PicturePileLayerContent(24221): Warning: painting PicturePile without content!
W/PicturePileLayerContent(24221): Warning: painting PicturePile without content!
Im comparing my source code with google example, but no effects
I've Google Play Services 4.0 on tablet and latest sdk
Did you turn on multiplayer in the Google Play Developer Console?
Enabling multiplayer support
To integrate game services multiplayer support, such as player matchmaking, you must enable multiplayer settings. Currently, only real-time multiplayer for Android is supported.
To enable multiplayer:
Open the Games with Game Services page from the Google Play Developer Console, and select your game.
Open the Linked apps page for your game, then switch the multplayer setting to ON.
Click Save as draft to store your settings.
Source: https://developers.google.com/games/services/console/configuring#enabling_multiplayer_support
Just to let you know - i figured out what my problem is .. well sort of..
It turns out the problem is AdMob in my case - it was initially integrated as a separate library and when i added GooglePlus to the game started seeing lots of stuff in the console.
I was confused that the problem was GooglePlus (throwing the messages you are seeing about missing resources) - but it is the AdMob part that is doing that.
Nevertheless even after figuring this out GooglePlus operations do not function.
The strangest thing is that they work under an old HTC Sense (2.2) but not on any 4.x device..
I just ripped the whole thing out - lost two days in it .. for a single +1 BUTTON.
I installed google play services.apk from internet and that was the reason. I wait for update from Google Play and everything is working now :)

Categories