According to Android developer guide. Our server will receive Pub/Sub notification when a user buy or cancel a subscription.
But it seems we don't have any information to identify the user. How can I modify the user's data in my server accordingly?
Below is the notification I received
{"version":"1.0","packageName":"com.diamondtech.streammap","eventTimeMillis":"1557833856021","subscriptionNotification":{"version":"1.0","notificationType":2,"purchaseToken":"celgdoceflgifibejhfgafpd.AO-J1OxoputCKFIjQt7QgnF5hSg8jiqDCNEiBM-8kBEOTwXr4oy-33LGr8l6h2Ih1GXX7yjgcptIaatVFN5EHT4_sywD5-qruOhOJZVdwBnGM71-8N-xxxxxxxxxxx","subscriptionId":"premium"}}
And the response of the google verification API
{ "kind": "androidpublisher#subscriptionPurchase",
"startTimeMillis": "1557832655139", "expiryTimeMillis":
"1557834750372", "autoRenewing": false, "priceCurrencyCode": "TWD",
"priceAmountMicros": "160000000", "countryCode": "TW",
"developerPayload": "", "cancelReason": 1, "orderId":
"GPA.3318-4223-0272-90442..6", "linkedPurchaseToken":
"cjnnjplpglkmdmdhbhaecobo.AO-J1OzD_JJr1Z8pzSDvJSjujfHVp9EQUsUnBv2kf5JocYsXpT-rX954WIuOSAtmLbMzqJmHudnwvsJll1GnXThj6JoDFSAR2E01fa4eZ7lUIozMOh4a_xxxxxxxxxxxx",
"purchaseType": 0 }
Very late answer but since there are no answers maybe this can help someone at least.
You only get personal information if the customer used "Subscribe with Google":
https://developers.google.com/news/subscribe/
If you have an app where the user buys a subscription, you get a receipt from Google with a purchaseToken. And since you know which user it is in the app, save the user information together with the receipt (most importantly the purchaseToken) in your database.
When you get a notification, match the purchaseToken in the notification with your database to determine which user it belongs to.
Do note that if a user cancels and then resubscribes they will get a new purchaseToken and the original one will be in the receipt from Google under "linkedPurchaseToken":
https://developers.google.com/android-publisher/api-ref/purchases/subscriptions#resource
Related
The application I am developing is a credit scoring application. This software should prevent unauthorized access. In order to implement this, I've used user and password-based authentication with two roles available - standard user and administrator.
This was implemented entirely in Python by using SQLAlchemy for interacting with the database, and Android for the user interface. For authentication, I am calling User Management System to get a token:
Request Body:
{
"username": "credit_scoring",
"password": "132465"
}
Response Body:
{
"apiData": {
"navigationList": [],
"userId": "credit_scoring",
"userName": "credit_scoring",
"email": "credit_scoring#erainfotechbd.com",
"cellNo": "01255",
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJVc2VySWQiOiJjcyIsIm5iZiI6MTY1OTUyNzcxOCwiZXhwIjoxNjYwMTMyNTE4LCJpYXQiOjE2NTk1Mjc3MThasdasd9.4lkEv-TpwIyYcldXfKucy3ynewhbuUFgjtdnKm7ymjE",
"page": 0,
"pageSize": 0,
"searchString": null,
"skip": 0
},
"message": "Record has been saved.",
"isExecute": true,
"totalRecord": 0
}
For security purposes, I have inserted some letters in the token. So currently, the token in this data is invalid.
Now, I want to save this token in the Android application and if the token is expired, then the user should be logged out from the application. How can I do that? Any help will be highly appreciated.
First of all, you must answer a question what does it mean for you, that the user is logged out from a mobile app? This might mean:
deleting the access token from the app's memory
deleting any data about the user from memory (e.g., username, profile picture, etc.)
maybe deleting the entries mentioned above from persistent storage, if these were not only kept in the memory, etc.
Once you have the logout logic implemented, you need a trigger for the logout itself. The best solution (in my opinion) will be to listen to 401 responses from your APIs and call logout when you get such a response. Another way is to check the expiration time of the access token (the exp claim contains expiration) and call logout when the token is expired. You can make such checks e.g., every time you make a call to the API, or set up an interval function (as you know the expiration time upfront you can set the logout event to trigger at the given time). Even if you decide to go with the second option, you will still need a mechanism to catch 401 responses from the API — responses from the backend should be your source of truth, not anything you calculate on the frontend (e.g., there might be clock skews present on the device, or the token might get revoked, etc.)
After setting up Android real-time developer notifications (RTDN) as a way to receive IAP subscription state changes to my web server, I only actually receive a certain push from Google's RTDN webhook that never includes the subscription details. Below is the payload structure that gets delivered to my server every time a purchase subscription event happens from my app:
"message": {
"data": "longstringofcharacters",
"messageId": "604411111111111",
"message_Id": "60442222222222",
"publishTime": "2019-07-03T11:03:34.076Z",
"publish_time": "2019-07-03T11:03:34.076Z",
},
"subscription": "projects/api-keyname/subscriptions/my-project-name"
According to Google's RTDN setup guide (https://developer.android.com/google/play/billing/realtime_developer_notifications.html), the below format is what I should expect to receive anytime a new subscription is purchased, cancelled, restored, or undergoes any other related state change from a user in my app:
{
"version": string,
"packageName": string
"eventTimeMillis": long
"subscriptionNotification": SubscriptionNotification
"testNotification": TestNotification
}
I have gone through Google's RTDN setup guide a number of times, and made sure my topic has granted publisher permissions for "Pub/Sub Publisher" using "google-play-developer-notifications#system.gserviceaccount.com" which Google says is a necessary step; doing this I believe is the reason why I'm able to receive webhook transmissions, but for some reason I don't understand why subscriptions events aren't getting transmitted.
Ultimately, my goal is to receive the correct payload which contains IAP state change details so that automatically syncs with my user database on my server.
Has anyone experienced this with RTDN when attempting to receive IAP push notifications?
I figured it out: I missed in Google's guide that the payload I was expecting was base64 encoded in the "data": "longstringofcharacters" portion I pasted above. Once I realized that, I decoded one from my logs, and found the IAP subscription details I was expecting.
I have an android app with renewable monthly subscriptions. In this app I want to notify user some info when his subcription continues in next month.
As I can see renewals in merchant center(orderId ends with eg. ..0, ..1), but when querying the inventory my purchase orderId is same as befor eq.
{
"orderId": "GPA.XXXX-YYYY-XXXX-ZZZZZ",
"packageName": "my.packageName",
"productId": "my.sku",
"purchaseTime": 1456398623654,
"purchaseState": 0,
"developerPayload": "mypayload",
"purchaseToken": "token",
"autoRenewing": true
}
What bothers me more is that purchaseTime also doesn't change.
So my question is: If there is any way to detect in app that renewal occured?
Edit:
I'm using Google Play Developer API to get subscription info and then calculate number of renewals myself.
Order id for all recurrences are returned in orderId field of the INAPP_PURCHASE_DATA JSON field (in V3) with each recurring transaction appended by an integer.
Subscription order numbers
To help you track transactions relating to a given subscription,
Google payments provides a base Merchant Order Number for all
recurrences of the subscription and denotes each recurring transaction
by appending an integer as follows:
GPA.1234-5678-9012-34567 (base order number)
GPA.1234-5678-9012-34567..0 (first recurrence orderID)
GPA.1234-5678-9012-34567..1 (second recurrence orderID)
GPA.1234-5678-9012-34567..2 (third recurrence orderID) ...
But due to local caching you might not get the latest information. So try clearing cache from application manager to first see if you get correct purchase information.
Since purchase query this way is not reliable, it makes more sense to call Google Play Developer Purchases.subscriptions: get API from a backend to get Purchases.subscriptions resource which will return expiryTimeMillis of current subscription.
{
"kind": "androidpublisher#subscriptionPurchase",
"startTimeMillis": long,
"expiryTimeMillis": long,
"autoRenewing": boolean,
"priceCurrencyCode": string,
"priceAmountMicros": long,
"countryCode": string,
"developerPayload": string,
"paymentState": integer,
"cancelReason": integer
}
The purchase data for subscriptions is returned only when the subscription is active.
If the subscription expires then you won't get this purchase data when you query the inventory.
Excerpt from the link
If a recurring payment fails (for example, because the customer’s
credit card has become invalid), the subscription does not renew. The
getPurchases() method does not return failed or expired subscriptions.
You can serve in your DB the expiration date, and every time, when you are getting the data, you can check the expiration date with your db's value, and if it is later, then the subscription was renewed))
I struggled to find a solution for the exact implementation of #random's suggestion. It seems to indeed be the only way to have a solid implementation for renewal tracking on Android, but I couldn't find a good approach online on how to do it. For those who want to save some time (cost me 6 hours today), please find my answer below:
1. First step is to add the dependencies:
implementation "com.google.apis:google-api-services-androidpublisher:v3-rev142-1.25.0" // Update based on latest release
implementation "com.google.auth:google-auth-library-oauth2-http:1.12.1" // Update based on latest release
2. Follow these steps to link the Google Play Console with Google Play Developer API (choose the "Use a service account", not "Use OAuth clients" and follow until "Additional information").
3. Download the services JSON file from your Google Cloud service account (click on the account that you set up in the previous step). You can find/create this file under the "Manage Keys" action or the "Keys" tab. Add the exported JSON file in your assets folder in Android
4. Then you can call the Google Play Developer API to query subscriptions like this (important to call from a Thread, didn't work from the UI thread, not sure why):
new Thread(() -> {
InputStream inputStream = context.getAssets().open("service_account_google_play.json"); // JSON file from step 3
GoogleCredentials credentials = GoogleCredentials.fromStream(inputStream)
.createScoped(AndroidPublisherScopes.ANDROIDPUBLISHER);
AndroidPublisher androidPublisher = new AndroidPublisher(
new NetHttpTransport(),
JacksonFactory.getDefaultInstance(),
new HttpCredentialsAdapter(credentials)
);
SubscriptionPurchase purchase = androidPublisher.purchases().subscriptions().get(
context.getPackageName(), subscriptionId, purchaseToken
).execute();
// Check the orderId or check the expiryTimeMillis for renewal check, e.g. purchase.getOrderId();
}).start();
At the risk of being overly descriptive, the subscriptionId is the ID of your subscription in the Play Console (e.g. subscription_monthly or whatever you called it), and the purchaseToken is the token you get from the Purchase token after querying the BillingClient (querying subscriptions is explained in detail here).
From Google Developer website, I found that GCM Token may be changed after period of time:
The Instance ID service initiates callbacks periodically (for example,
every 6 months), requesting that your app refreshes its tokens. It may
also initiate callbacks when:
There are security issues; for example, SSL or platform issues.
Device information is no longer valid; for example, backup and restore.
The Instance ID service is otherwise affected.
I have an android app using GCM Topic subscriptions to send push notification, in this case, when token has been refreshed, do I need to re-subscribe all topics again or GCM server will automatically do it ?
Thank you in advance.
I have tested that when token is refreshed (you receive a new token) you have to register all topics again.
Also checkout this SO
Sample test:
Get token ("...b43sCSdoEDkU54SIWll3hbDVsd7E1UdwlAvp4LP")
Register for topic.
Send notification for topic
Works!
Restart few times app and still get notification for topic
Force call
instanceID.getToken(defaultSenderId, GoogleCloudMessaging.INSTANCE_ID_SCOPE);
Token refreshed ("...XVT_pZq7fy_vKmskiGpDXMyqdAF6ODl_46JMdi5")
Send notification for topic. I don't get it!
More details:
Tool #1
Use this to check google gcm data
Reinstall app
Get new token ("")
Response from tool #1
{
"applicationVersion": "39",
"connectDate": "2016-01-12",
"application": "com.esportlivescore.develop.debug",
"authorizedEntity": "11443413691531",
"connectionType": "MOBILE",
"appSigner": ".................",
"platform": "ANDROID"
}
Subscribe for topic
Response from tool #1
{
"applicationVersion": "39",
"connectDate": "2016-01-12",
"application": "com.esportlivescore.develop.debug",
"authorizedEntity": "11443413691531",
"rel": {
"topics": {
"match-28388-start": {
"addDate": "2016-01-13"
}
}
},
"connectionType": "MOBILE",
"appSigner": ".................",
"platform": "ANDROID"
}
Messages for topic are working
Request new token (refresh)
Google resend me old token :)
Again...
Now i don't have any subscriptions :(
I had subscribed to topics from GCM and when I removed all app data by android settings, the GCM token is the same and GCM notification on topics are still available, so I get notifications which I don't want to receive.
My questions are:
How can I get list all of subscribed topics from GCM?
How can I remove all subscribed topics without knowing their names?
Should the GCM token be changed after clearing app data or should all subscribed topics be removed automatically in this case?
You can't only you can use tool for debug i suggest :)
You have to save subscribed topics for example in sharedprefs. If you don't have token in sharedpreferences you should call instanceId.deleteInstanceID()
Simply call instanceId.deleteInstanceID()
Token will change rarely but when it changes you must resubscribe all your topics.
Also checkout this question on SO
TOOL
You can use this tool to debug :)
When i don't subscribe any topic i get something like this:
{
"applicationVersion": "39",
"connectDate": "2016-01-12",
"application": "com.esportlivescore.develop.debug",
"authorizedEntity": "114434000000000",
"connectionType": "MOBILE",
"appSigner": ".................",
"platform": "ANDROID"
}
After i subscribe some topic:
{
"applicationVersion": "39",
"connectDate": "2016-01-12",
"application": "com.esportlivescore.develop.debug",
"authorizedEntity": "11443413691531",
"rel": {
"topics": {
"match-28388-start": {
"addDate": "2016-01-13"
}
}
},
"connectionType": "MOBILE",
"appSigner": ".................",
"platform": "ANDROID"
}
So somple usage. I use Advanced REST Client (plugin for Chrome)
HTTP GET Request
https://iid.googleapis.com/iid/info/<TOKEN>?details=true
Content-Type:application/json
Authorization:key=AIzaSyZ-1u...0GBYzPu7Udno5aA
TOKEN in url : token obtainet from google
key : can be found in Google Developer Console / GCM Console
How can I get list all of subscribing topics from gcm?
The current version of GCM doesn't provide a method to do this.
How can I remove all of subcribing topics without knowing their names?
The documentation indicates InstanceId.deleteInstanceID() will do this.
Should GCM token be changed after clear app data or removed all
subscribing topics in this case automatically?
Although the documentation implies that token registrations and subscriptions are removed if the user clears app data, that is not true in the current version of GCM. The issue is discussed in the answer to this related question.