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
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.
Perhaps I am simply not looking in the correct places, but I am surprised at the lack of documentation around cross-platform apps incorporating renewing subscriptions.
I have a cross-platform (iOS and Android) Ionic app with a Rails API for the back-end. I am using https://github.com/AlexDisler/cordova-plugin-inapppurchase as a wrapper around the iOS/Google store SDKs.
My question revolves around subscription renewal:
From what I gather, it is recommended that I have a daily job checking each of my subscriptions for their expiration date. If a subscription is about to expire, I can send the receipt stored in my database to either Google/Apple and check if the subscription has been renewed. If it has, I update the expiration date and keep the subscription active. If the subscription has not been renewed, I mark the subscription as inactive.
Now, what happens if a user renews a subscription? How will I know that a subscription has been renewed? And when do I check for subscription renewal? Seems like my options are:
Every day, check every expired subscription for renewal. Problems with this: 1) as the number of users grows, so will the number of users with expired subscriptions. Eventually, this will result in checking many, many expired subscriptions every night. 2) A user will be unable to access subscription-only content between the time they renew their subscription through the app/play store and the nightly subscription-checking job is run.
Every time a user with an expired subscription logs into the app, check to see if they have renewed their subscription. Problems with this: 1) My app uses token authentication. Therefore, users are not required to login with each use. So, if a user renews their subscription, but their token is still valid, the app will have no way of knowing that their subscription is up-to-date.
Is there another option? What am I missing here?
I would suggest using a combination of both options.
When a user opens your app you should send a separate request to your backend that will fetch the receipt a check if its renewed and cache the expiration date and the time it was checked. Then I would have another job run nightly that doesn't check all the expired receipts, but clears the expiration date cache if it hasn't been checked within a certain period. Then, the next time a user opens the app, the cache will be empty and and will be fetched.
I'm working on adding Android subscriptions to RevenueCat but it won't be ready for a couple months.
I am currently working on an application that incorporates google in-app subscriptions. It seems like google would handle most of billing and transactions, but the questions are:
1. if user's credit card is expired while the app is trying to renew a subscription, do I need to handle this case on the device or my backend server?
2. if no, would google sent notification to the user?
3. if yes, how do I check the state of user's credit card????(hopefully i don't need to)
As per the documentation:
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.
Recommendation: Include business logic in your app to notify your
backend servers of subscription purchases, tokens, and any billing
errors that may occur. Your backend servers can use the server-side
API to query and update your records and follow up with customers
directly, if needed.
1.Looks like it should be your server's responsibility to check with Google on daily basis or so and disable the content if subscription is not active. If payment fails due to what ever reason, Google doesn't renew the subscription automatically again, so it's as good as saying that subscription is canceled.
2.Google does notify to user.
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.