I have android app with subscription in-app purchase. Because I am using the subscription to deliver data from online service, I am using verification of purchase on server side via google play API (with purchase token the app send me after purchase). Majority of purchase tokens in my database are long alphanumeric strings, something like this :
djcbhbiertdkkotyuupnlmioppb.AO-J1Ozg0oasdfB3MAlWy-PihFE_nPVRMMfTW2_VPJt5KTKQA3CXNwyqweJAtUdIGTuOW9zEIIy-XS_4Un-a-Co6aEs__Adj1rZ4GtRxPKr04ph-l6nP2sU-w6e500YfTj5l0O8WEXF37yt
and these are verified OK. But from time to time I receive purchase tokens containing just 15 digits, like this :
781871156762279
And for these the result it always:
Google.GoogleApiException Google.Apis.Requests.RequestError
The purchase token was not found. [404]
Errors [
Message[The purchase token was not found.] Location[token - parameter] Reason[purchaseTokenNotFound] Domain[global]
]
I did not find anything about this in documentation. Am I missing something? Or could it be these are "fake" purchases from some cracked version of my app? Thanks.
I have received short purchase tokens in the same 15-digit format, and I believe that these are, in fact, attempts at fraudulent purchases.
It is not your app that is cracked. Rather, a user installs a special app onto a rooted device that performs a man-in-the-middle attack against your app, emulating the legitimate In-App Billing Service. When your app begins a purchase flow, this fraudulent app intercepts the purchase request, and returns a fake purchase token.
Apps that verify the token locally are vulnerable, because the same fraudulent app is used to verify the token.
Apps that send the token to the backend are probably safer, because the backend can make a request to the in-app billing API to verify the token independently. However, the app must wait for the backend verification to succeed before granting the user the purchased privileges.
Please see my other answer for more details on this attack.
Related
I want to store in Android app data a token (a random number, e.g. 128 bit long) unique for each install. I also want to deliver this token to the backend after confirmed purchase. That is, the backend should reject hacker's attempts to store the token, if it is not after an app purchase.
In other words, I need to implement app "activation" so, that the activation is possible only after a genuine Google Play purchase.
An explanation why I need this:
Suppose I resell a $1 product for $2 in a $10 paid app.
The right to use this service by the user appears when he purchases my paid app.
If the user can fake app install, then he may obtain 1000 products for $10 and thus I would have $990 loss.
Thus I need to store on server side a token generated on confirmed (on the backend side) purchase.
Is this possible with Play Market?
In Flutter (and I believe, in plain Android, too) it can be done this way:
Every purchase needs to be "acknowledged".
After verifying the purchase receipt and the delivering the content to the user it is important to call InAppPurchase.completePurchase to tell the underlying store that the purchase has been completed. Calling InAppPurchase.completePurchase will inform the underlying store that the app verified and processed the purchase and the store can proceed to finalize the transaction and bill the end user's payment account.
Warning: Failure to call InAppPurchase.completePurchase and get a successful response within 3 days of the purchase will result a refund.
So, the task can be done this way:
The purchase is created (on the client app side, by contacting Play Store).
The app contacts my server with purchase token (and possibly other info).
During the request from the app the server retrieves the purchase by purchases.products.get to verify that the purchase is not forged by a hacker and increases users in-app funds.
App, after receiving a successful reply from the server, acknowledges the purchase by InAppPurchase.completePurchase.
If the app fails to acknowledge, the purchase is refunded in 3 days.
I'm researching for developing an open source app that will get a sync feature in future.
This will be realized with Firebase Firestore. This feature should only be available to users subscribing to an abonnement via Google Play Billing.
Now, if I upload my google-services.json to my open source repo, anyone can compile the app and remove the check, whether there's an active subscription or not.
But if I don't upload my google-services.json, CircleCI can't build my project, since the file is missing.
Does anyone have experience in this matter, or some tips?
Thanks!
you should have a server for this. firebase or custom, doesn't matter, but the purchase, subscription, and renewal business rules should not be in your app providing the services based on the subscription status.
If all of the logic is in your app, without server, then you're bound to have issues with this. There's no way around that.
You should also assume that somebody will break your subscription logic (to provide free access) and the apk will be listed forever on something like apkmirror for anyone to get it.
Here's a suggested proper flow.
Server:
users need to have a login
have a list of products a user can subscribe to, with SKUs
that exist in the google play store, under your app's products
App:
app calls your server to get a list of products available for this user. you really care about getting the SKUs for this.
make request to google's billing client to get pricing for the list of SKUs
when customer purchases something using google's billing sdk, you'll get a purchase receipt object
send your purchase receipt to your server
Server:
the server will validate the purchase receipt with Google, where the server itself makes an API call to google to verify the authenticity of the receipt
if the receipt succeeds, the server returns a success code
App:
if the response from sending the receipt succeeds, then you make a new request to the billing SDK, this time to fulfill the purchase (close the transaction)
when fulfillment succeeds, call your server to notify that fulfillment is completed. send the lenght of the subscription (monthly, yearly...)
Server:
grant access to the subscription content the user just purchased
when the renewal time is up (which came from the fulfillment call), your server wakes up and calls google to renew the subscription
if renewal succeeds, user continues to have access to the subscription content
Yes, this is A LOT more work, but it is also dramatically more resilient and future proof than having a true/false flag in your app.
With that being said, if your subscriptions are $1 a year, and you expect to have no more than 10 users...then do the quick and easy way.
I'm trying to implement in-app purchases (consumable) using the Google Play Billing Library.(which is proving less straightforward that I would like...).
In order to plan the workflow I'd like to know what information is available to a backend server (or to the app, and then can be verified by the server) about an app's purchases. For example: can I get the date and time of purchase?
Yes, you definitely can get the date and time of purchase, order id, state, purchase token and so on. All information about purchase data is described here:
(Table 7) https://developer.android.com/google/play/billing/billing_reference
Concerning server verification, official doc says
Successful purchases also generate a purchase token, which is a unique identifier representing the user and the product ID for the in-app product they purchased. Your apps can store the purchase token locally or, ideally, pass it to your secure backend server where it can be used to verify the purchase and protect against fraud.
The process steps of server verification you need to implement:
https://developer.android.com/google/play/billing/billing_library_overview#Verify-purchase
Google Play In-app Billing samples for more clarity:
https://github.com/googlesamples/android-play-billing
I've implemented a recurring subscription for Android In App Billing, but I'm wondering how the apps knows that the subscription is renewed. In testing, at least, the subscription is ended after 1 day. Will it be continued when the app is published on the store?
On iOS the testing subscription is renewed a couple of times. Enough to let you test when the subscription is renewed, but what is the best way to do this on Android?
Can I use the purchaseToken to let my server query Google Play API or do ever renewed subscription get a new purchaseToken?
In android IAP, The purchase token expires when the user manually cancels the subscription or disables the auto-renew. Otherwise, you will get a valid purchase token every time. In test mode, the tester's subscription automatically gets expired after 5 minutes but in production, it will work fine. So don't get worried about the production behavior. If you are running the app as a tester, you will get the below payment modes on the payment flow start.
You will have 2 options to test your implementation
Test Card(Always Approves)
Test Card(Always Declines)
By testing both of the cards, you can be sure about the implementation. Your app should be capable of handling both responses from IAP. If both flows goes well, You shouldn't be worried about it. You are ready to roll-out it on production. Here is the Official Docs, may help you to understand the entire flow.
If you designed custom flow to manage IAP, You can verify the IAP Token from backend using the Google Play Developer API and allow the user to consume the feature accordingly. For the custom flow, You can send custom JWT token from backend according to IAP Token expiry and set custom JWT Token exp claim. By using this method you can check the custom token is expired or not in the client-side, If it's happened so, You can fetch the new IAP token and send it to your backend. Your backend should validate that IAP token and issue new JWT token to user and cycle goes on. Let me know if you want the custom flow, I'll post it here.
You simply periodically query the Google Play server to check the items that the user own, if it is a subscription, it will reply that he/she owns it while it is active and therefore has not expired.
Do not forget to verify the signature of the received data, and much better using a server side verification
Android - protecting in app purchases with server side verification
I have implemented server-side verification Google IAP purchase tokens. My mobile app send me this token as get it from Google.
A regular token looks like
minodojglppganfbiedlabed.AO-J1OyNtpooSraUdtKlZ_9gYs0o20ZF_0ryTNACmvaaaG5EwPX0hPruUdGbE3XejoXYCYzJA2xjjAxrDLFhmu9WC4fvTDNL-RDXCWjlHKpzLOigxCr1QhScXR8uXtX8R94iV6MmMHqD
but sometimes I get a short token like this
korpimulxmslxissnschtkdb
When I verify this token via Google Play Developer API: https://www.googleapis.com/androidpublisher/v2/applications/packageName/purchases/subscriptions/subscriptionId/tokens/token, for the short token I get a 404 error.
Where is the problem? Is it possible that this short token represents real transactions?
I have been receiving these same invalid tokens in our app with no idea of the reason for a while. The tokens have come in various formats, including 24 alpha characters (eg. glvnqnpjqslcagyimgxeuybk), 15 digits (eg. 781871156762279, see this question), and even tokens of proper length that have a slightly different format from valid ones (eg. xdavcuvdnniwwrhwemleqjdz.rSQozm... see this question).
These are the error messages I have received from the in-app billing API for these various tokens at one time or another:
"code": 404, "message": "The purchase token was not found."
"code": 400, "message": "Invalid Value"
"code": 400, "message": "Your request is invalid for this subscription purchase."
The answer given by Marc Greenstock gave me an idea to try to reproduce the issue.
Making a fraudulent purchase
I tested two apps that claim to hack in-app purchases: Freedom, and Lucky Patcher, on a rooted device. The former did not work: though it detected that our app can make purchases, when I tried to make a fake one it told me that "this app's purchases cannot be faked". The latter one did work after some fiddling, however, and generated a short purchase token exactly as in the question. When I tried to verify the token via the in-app billing API, I received the same exact "invalid token" message as before.
I also started logging the root status of devices generating invalid tokens using this method. While this is not proof of anything, the fact that nearly all invalid tokens originated from rooted devices made me suspect foul play.
The attack
I believe the attack works as follows. Anyone who knows more about this please chime in!
User installs one of the hacking apps that claims to make free in-app purchases onto a rooted device
The hacking app either patches the legitimate In-App Billing Service on the device, or emulates it
During a purchase flow the hacking app intercepts the purchase Intent which is meant for the legitimate service
The hacking app processes the purchase request and generates a response the same way the legitimate service would, but the purchase request never reaches Google's servers
An app that relies on local token validation will request purchases from the In-App Billing Service. This request is also intercepted by the hacking app, which claims that the purchase is valid
An app that relies on server token validation sends the purchase token to a server, which makes a call to the in-app billing API, which has never seen the token, and therefore returns an "invalid token" response
Mitigation
Apps that rely purely on the In-App Billing Service are vulnerable! The purchase and the purchase validation requests are both intercepted by the same fraudulent app. There is no defense.
Apps that rely on a server backend should send the purchase token to the backend to be verified through the publisher API. These apps must not credit the user with the purchase until the backend verifies it and returns a positive result to the app. The backend should probably follow the security recommendations for In-App Billing. These apps are probably safer from fraudulent purchases, though they generate a lot of invalid purchases.
I don't think that it is safe to rely on the length or format of the token, order id, or other data for determining validity of the purchase. These tokens are probably only malformed because they were emulating a previous format. Presumably the authors of the hacking app will eventually release a version to emulate any format that Google cares to devise. The only safe means is to verify the purchase via the in-app billing API on a device that you control, ie. a server.
Did you end up solving this?
The only reason I can suggest is that the token was generated by an In-app Purchase Cracker like the "Freedom in-app purchases for Android" app that can be installed on rooted devices.
I'm interested to see if you have received a short token for any test purchases that you have made yourself.
Another indication that the token is fake is format of the orderId that you get after purchase on the app.
If it doesn't follow the format indicated in the Administering In-app Billing docs then it's most likely a fraudulent purchase.
I've found a partial mitigation that works works with some fake IAP providers: recheck the digital signature manually. Whatever the IAP simulator does, they don't have a copy of Google's private RSA key. I've rolled my own signature check, and it catches at least some of those fake transactions.
The check code is a gist.
We got error like:
"code": 400, "message": "Your request is invalid for this subscription purchase."
while trying to verify the subscription purchase with v1 instead of v2.
v1 -> androidpublisher/v3/applications/{packageName}/subscriptions/{productId}
v2 -> androidpublisher/v3/applications/{packageName}/purchases/subscriptionsv2/tokens/{token}