Purchases always successful even when I delete postback script - postback

I'm using Google Wallet for Digital Goods (in Sandbox Mode) and it worked fine for month or two, and information on the database was updated accordingly. Recently, however, the purchases were completing, however the database was not updated.
For debugging, I deleted the postback script from the server, but very strangely, Google keeps completing the purchases even though the postback script no longer exists!!
So now every purchase goes through no matter what!
Google claims it was successful within the iframe (which pops up on the client to initiate and complete a purchase).
I'm using the sandbox credit card.
Is there a bug in Google's code?

I found the problem.
Somehow, the postback url field inside the sandbox settings page was deleted.
I had set it correctly some time ago but for some unknown reason it was missing.
It seems from this that Google completes purchases when no postback is specified.
-- dragonfire

Related

Receipt validation in iOS returns incorrect info during sandbox testing

I am implementing receipt validation for my app, because it was paid and will be free with in app purchases going forward.
I have setup my server and everything, and I have sent the receipt data. However, when I get the response, no matter what, the response JSON always says that the original_application_version was 1.0. The idea behind me validating receipts is that if your original application version was before 1.x, then you can automatically unlock the premium version.
However, even for brand new beta testers, who have never before installed the app, the original_application_version in the JSON is returning a value of 1.0.
the URL on my server is https://sandbox.itunes.apple.com/verifyReceipt. When I changed it to the production URL, I got a response of 21007 (which means I should change to the test environment).
Has anyone experienced this? I highly doubt it's going to just magically start returning the correct values in production, yet it's completely broken for testing purposes. It is returning the incorrect info on both TestFlight builds and when building directly from Xcode.
What you're getting in the sandbox environment is consistent as per the documentation:
Original Application Version
The version of the app that was originally purchased.
This corresponds to the value of CFBundleVersion (in iOS) or
CFBundleShortVersionString (in macOS) in the Info.plist file when the
purchase was originally made.
In the sandbox environment, the value of this field is always “1.0”.
Ref: Receipt Fields: Original Application Version
In sandbox environment, the value of this field is always 1.0 whereas in the production environment, it will be the CFBundleVersion of when the user first installed this app.
Based on this, most people use the following solution:
They change CFBundleVersion aka Build (not Version) to a new style
Check this value to differentiate between the old paid app and the newer free app (with iAP)
WWDC2013 Session 308: Using Receipts to Protect Your Digital Sales spoke about this exact scenario, so what you're doing is recommended by Apple anyways.
Excerpt:
So for everyone that's got a paid app on the store today, and you want
to make the transition to being a free app with In-App Purchases,
previously that used to be quite a challenge for you, because if you
simply switch to being a free app with In-App Purchases, your
customers would have to go and buy all those In-App Purchases again,
but they've already paid for it, and they're not going to like that.
So now in the receipt itself we have the date, when the user first
purchased your app, and the version that it was at that time.
So you can use this to make a really informed, intelligent decision
about what features and content to grant this user into, so if your
app looks at the receipt and inspects it and sees this user bought my
app before I made the switch to being free with In-App Purchases,
grant them into what they've paid for, but if they purchased your app
after you've made the transition to being free with In-App Purchases,
you know then not too unlock features and content until they make the
purchase and you verify that transaction with the receipt itself.
Ref: Using Receipts to Protect Your Digital Sales
Summary:
But you already seem to be following this approach.
The only thing is that in sandbox environment, it's always 1.0 because every install is treated as a fresh install, no carry-forward information.
So you can't really test it until it goes to production, which is scary.
Viable Solution:
So how would one test the following scenarios?
User installed app when it was paid
User installed app after it became free
Hm... I would use environment alongwith original_application_version... and 2 TestFlight builds:
Case 1: User installs app after it became free but has paid for it previously
If environment shows Sandbox then I would mock the original_application_version to an old app build number and check the flow
Else environment shows Production and I would take the original_application_version from the receipt
Provide TestFlight build with release notes indicating the case
Case 2: User installs app after it became free but has not paid for it
If environment shows Sandbox then I would mock the original_application_version to new app build number and check the flow
Else environment shows Production and I would take the original_application_version from the receipt
Provide TestFlight build with release notes indicating the case
In either case I would get the proper receipt ofcourse, and note that both TestFlight builds are releasable if QA passes it, as long as our else condition is guaranteed to work: The part which takes the original_application_version from the receipt.
PS: The solution was the best I could think of right now
Ok, I finally have this sorted out. As others have previously stated in other stack overflow threads, original_application_version is completely useless. In sandbox, it always returns 1.0. In production, it doesn't return the version number, but rather the build number of your app--so the naming is bad too.
In the end, my solution (probably a very bad one, but the most straight forward ones and it does work) was to use original_purchase_date_ms. So from my server, I load the date (a Unix timestamp in milliseconds) that my app officially went live as free and not paid. So I just compare this number to the original_purchase_date_ms, and if the original purchase date was before, then it validates as them already having bought the app.
Might be a SandBox issue.
How about capture the production replies and verify if the wrong AppVersion is there as well. (Assuming you have a 1.1+ already in store) This would be a BugReport if so, but I doubt it that the field is incorrect.
However if the field is incorrect there as well, how about validating the purchase date which was correct last time I was testing.

Does apple issue a receipt of the app when user purchase and download it?

As title said, I am implementing a flow to fetch a local receipt then try to validate it from our backend server. As what Apple suggested, if the receipt tis nil or invalid. I need to do a receipt refresh request, the problem is, this requires network as well as user login.
So the actual issue we are facing is, from the dev build and test flight build, when we build a fresh new app that has not opened before. Once open, it will show a popup to ask for user login (obviously it's because of my logic - if receipt nil then do receipt refresh request). But we don't want to spam user with this dialog box every time and we have the business need to validate the user's receipt at app start up.
So back to the title question, since the dev and test flight build isn't working as expected (show login dialog because receipt is nil). Does the prod build - the app download from App Store, actually issues a receipt from App Store? In a WWDC video they said it should, but not very specific, so I am here to get some confirmation or some other thoughts from you guys.
Thanks!!
It's been a long time since I've used receipt validation (I used for auto renewable subscription) so what I'm going to tell you could have been changed.
In my experience it never happen to receive a nil receipt in production since the first receipt is downloaded from the App Store along with the application even for free apps. nil receipt happens in sandbox and in adhoc (don't know which should be the right behavior from from TestFlight) and in this case the best way to replicate production is to "restore purchases" or make a refresh request.
There is an old discussion about it on Apple dev forum where an employee clarifies that(probably 2015), I'm not able to find it again, but maybe you can also make a search there.

iOS in-app purchase transactionReceipt vs. appStoreReceiptURL

We've been happily processing in-app purchases in our iOS app since the beginning of time. Because the code has been around a while, it uses the now-deprecated transactionReceipt property on the transaction we get when our paymentQueue:updatedTransactions: method is called by Store Kit. We send that receipt to our server, which validates it through a post to Apple's server, does what we need to do on the server, and reports back "success" to our app. Works great.
Now we want to add a subscription-based product so I'm looking at having to re-implement IAP using the appStoreReceiptURL property on the main bundle and loading my app receipt from there. There are a couple things I don't get.
First and most obvious: I get the same SKPaymentTransactionStatePurchased state when paymentQueue:updatedTransactions: is called. For now, when I submit it to Apple I'm just displaying the JSON I get back. Since the status is non-zero I'm also submitting to the sandbox server and displaying what I get back from it, too. Problem is in both cases I get this:
{"status":21002, "exception":"com.apple.jingle.mzfairplay.validators.DrmInvalidArgumentException"}
I suspect this has something to do with the fact that I'm running a debug build, though not in the sim -- it's running on an actual device. To get around that, I tried uploading a build to the App Store so I could download through TestFlight, but since I'm using my sandbox Apple ID, TestFlight refuses to install it.
So the first question is why am I getting this "DrmInvalidArgumentException", and am I configured correctly to test (debug build on real device, using my sandbox Apple ID to make purchases).
Second question is more baffling to me. As I understand it, I will still get notified via paymentQueue:updatedTransactions, and I will iterate over the transactions I get there (?) but then instead of submitting the receipt in the transaction, I'll submit the app receipt from the URL in the main bundle. It will contain ALL IAP PURCHASES EVER and I will have to iterate over all those to figure out what's new and what I'm interested in, right?
The flow doesn't seem right. I'm getting a notification based on a transaction, but then looking at a dump of ALL my IAP transactions that are contained in the app receipt. So I can't possibly be understanding the flow correctly.
Status 21002 is The data in the receipt-data property was malformed or missing.. The body of the POST to the verify endpoint has to be a JSON dict containing a receipt-data key and a password key that contains your app-specific shared secret that you can get from iTunes Connect.
You might try using this tool to test your receipts. You don't need to use TestFlight, everything should work fine on a debug build as long as you are using a Sandbox iTunes account, which you can create in iTunes Connect.
Your explanation is correct, you will want to send the whole receipt for each purchased transaction. This is redundant, you could potentially keep a app side cache so you don't send the same receipt data each time but it is possible for the contents of appStoreReceiptURL to change at any time.
There are many other tricky edge cases implementing subscriptions. I built RevenueCat because of all these crappy things with subscriptions. Apple really bolted it onto the existing IAP stuff poorly in my opinion.

Is it possible to see if an in app purchase was made in sandbox mode

I am working on an iOS library that records a successful in app purchase to our API for later processing. What I would like to be able to do is log to our api if the IAP was made in sandbox (with a test user) or not.
That information is in the receipt. You can:
1) grab the receipt and submit it to the Apple servers (sandbox or production). It will come back either valid or with a 21007/21008 error revealing its environment.
2) grab the receipt and decode it. There is an undocumented field that indicates whether or not it is from the sandbox.
I'd go with #1
After a while of hunting I came across this post which seems to be what I am trying to do. Check if iOS app is live in app store I will update the post once I am fully deployed and tested but it seems to be working for now.

How can I validate purchases with Amazon IAP API

In an IOS app, I normally validate that purchases have succeeded and been processed by my application by comparing Apple's list of receipts with one that I add to locally when I've processed the purchase myself. This safeguards against things like app crashes during purchase, bugs, etc that can make things get out of sync. I'm trying to do something similar with Amazon.
I have come across statements like this repeatedly in Amazon docs about purchase validation/restoration:
It is possible for a purchase transaction to have more than one Purchase Token representation, so they should not be used as transaction IDs.
This was a huge help as I thought the exact opposite. Unfortunately, I haven't found the rest of the thought: "The transaction ID can be found here:...". I'm starting to think that their transactions don't offer a stable ID...! Please tell me what I'm missing or, if it really doesn't exist, how I can safeguard against the problems I mentioned above?
The initiatePurchaseUpdatesRequest call under Amazon IAP takes an offset parameter which is a permanent identifier for a restore point. Each time you start up you issue an initiatePurchaseUpdatesRequest starting from the last offset you saw (or Offset.BEGINNING the first time). When the response comes in, you save off the offset from the response for the next startup. In this way, any new but unrecorded sales (due to bugs or multiple device issues) will get caught on the next startup. One, minor, impact of this is that you can actually get notified of a purchase twice - once when it occurs and once on the next onPurchaseUpdatesResponse.
As usual you also have the ability for a user to manually request a "restore purchases" that issues the initiatePurchaseUpdatesRequest with the Offset.BEGINNING parameter in case all else fails.
Ps. The actual receipt validation should be performed by calling an Amazon web service from your own server using the receipt values from either the restore or the purchase responses.
With the current Amazon IAP api, keep track of whether you've fulfilled the IAP item by the "requestId" in the PurchaseResponse object. This "requestId" can be used as a sort of pseudo transaction id. You should save this requestId to a server or to the device's local storage.
See the quick start link below for more details:
https://developer.amazon.com/appsandservices/apis/earn/in-app-purchasing/docs/quick-start#7

Resources