j3k0 / cordova-plugin-purchase

In-App Purchase for Cordova on iOS, Android and Windows
https://purchase.cordova.fovea.cc
1.3k stars 537 forks source link

iOS multiple purchases always same appStoreReceipt #193

Closed joscarras closed 7 years ago

joscarras commented 9 years ago

Hello guys,

I have been working for the first time with iOS / Android in-app and this plugin for the first time and I am having an issue, which I am not able to track or solve with iOS.

Whenever I do a purchase in iOS I am getting back always the same appStoreReceipt although the purchase appears to complete successfully. In android this is not the case and each purchase returns back a new receipt which I am able to validate and assig the correct credits. I need the server validation in order to update the user Credits

Since this is the first time I am doing this, I am not sure if this has to do something with iOS Sandbox or a possible bug in the plugin, my app or Cordova.

The products I have in the iOS store are not yet submitted (waiting for Screenshot) since I want to make sure the application is working before I submit it and the user used to make the purchases was created using the sandbox in iTunesconnect.

Here is the scenario in more details: 1: Place an order. 2: Prompt to enter the sandbox user details. 3: Order completes successfully. 4: Plugin send receipt to the server app backend. 5: Create the request with the store secrete and send it to the sandbox iOS service. 6: Get back success with the in_app containing the correct product and transaction id. 7: Send back to the app a success response. 8: Call product finish. 9: Place a new order. 10: Order completes successfully ( Since I was just prompted app store does not ask me again for credentials ) 11: Plugin sends receipt to the server app backend with exactly the SAME appStoreReceipt, however this time the id and the transactionReceipt (which is depreciated) has changed compare to the first validation. 12: Create the request with the store secret and send it to the sandbox iOS service. 13: Get back success with the in_app containing only the original product and transaction id (exactly same order) and nothing else added. 14: Send back to the app a response. 15: Call product finish however the user never received his credits.

No mater how many orders its always the same steps from 10 to 15. The plugin version I use for iOS is 3.10.1 and Cordova version is 3.8.0.

I have plenty of source to show and even some debugging url in the back end that shows all the iOS validation requests decoded.

Can you provide me some help please?

butlimous commented 9 years ago

I have read from iOS7 and up, Apple introduced something called "grand unified receipt". You can read more about it at: http://stackoverflow.com/questions/20173491/implementing-the-grand-unified-receipt-on-ios . So, the receipt should be the same each time but with a different transaction id.

I have a question for you if you don't mind, I have tested the purchase process and get the successful message and the "Approved" state is fired but I'm getting only the Transaction ID and no receipt. However, in Android I get the receipt so I can validate It. I'm using PhoneGap Build. Using also version 3.10.1 for iOS

joscarras commented 9 years ago

Well I am not doing anything special...

I have registered a validator function like this: store.validator = validator;

and when the product is approved I call product.verify();

And in the validator

function validator( product, callback ) { /* CODE */ }

In the product parameter you can find transaction variable ( product.transaction ) which has all the data such as appStoreReceipt and transactionReceipt which if you decode using iOS services you can find all the details.

Now you said phonegap... I have given up on phonegap a long time ago and started using cordova instead. Phonegap uses cordova under it, so I though why go through another layer and not just use cordova directly.

The code I have is huge in for this part. If that above does not help let me know and I will take your email and send you some of the code I am working right now to help you out.

butlimous commented 9 years ago

THANKS VERY MUCH! I didn't know that I need to run the validator inorder to get the receipt data. For Android, the receipt would just return in the "product" when "Approved" state fires. However, for iOS, you need to run product.verify() to and get the receipt through the validator.

Now I'm facing your problem, when validating the receipt, transaction ID is always the same. The only fields which change are request_date_ms and request_date_pst in the receipt response. The receipt itself is always the same but that's not the problem. The problem now is that its response is always the same except for the 2 fields I stated above. Transaction ID should be different each time. Transaction ID returned by the plugin is different but It's not reliable as a hacker can easily spoof the "POST" items. So there should be a way to get it from the response of the Apple service and reward the user accordingly.

butlimous commented 9 years ago

The correct transaction receipt is found in the "transactionReceipt". When you send the value of this for validation, it will respond with the details of this individual transaction. Strange enough, transactionReceipt is actually deprecated!

"in_app" array found in "appStoreReceipt" is supposed to have all the IAPs but for some reason, It's showing only the first transaction. All other transactions are not there. I don't know if the plugin needs to b e updated or what

butlimous commented 9 years ago

If the transaction is stuck like this, It's very likely It's pending finishing. The problem for me is that I deleted the Sandbox user so the transaction won't finish and I'm stuck :(

joscarras commented 9 years ago

Hey! Yes I know about the transaction Receipt and that it contains the correct data but will be soon removed. Probably with IOS 9 but have not found any details about that.

For now I use the transaction receipt to validate the back and give the user credits however I store all the data and I will monitor it when the app goes live.

As far as not calling product.finsh that is not the case for me! I am defenetely doing it and I can see it in the logs of the app.

Also have tested with multiple devices and sandbox users.

butlimous commented 9 years ago

I don't really know but I read several questions on stackoverflow and they say that It's because of pending transactions. However, I read this from apple docs:

"The in-app purchase receipt for a consumable product is added to the receipt when the purchase is made. It is kept in the receipt until your app finishes that transaction. After that point, it is removed from the receipt the next time the receipt is updated—for example, when the user makes another purchase or if your app explicitly refreshes the receipt.

The in-app purchase receipt for a non-consumable product, auto-renewable subscription, non-renewing subscription, or free subscription remains in the receipt indefinitely."

It's getting more confusing and the move from individual receipts to this BS unified receipts is the most stupid move EVER! What about if 2 users buy at the same tim? How can I know which one made the purchase? The transaction receipt would work well and is secure in this case.

Do you get prompted to login to sandbox users when you open the app? I keep getting those and I assume they are unfinished transactions. Do you know how can I log unfinished transactions? I check for approved state and call product finish from there. Nothing happens, however, when I delete and reinstall the app, I find the approved state gets fired with 3 transactions but It seems they won't finish for some reason :(

joscarras commented 9 years ago

When i have unfished transactions I do get the loging which is normal procedure. However if I don't log the transactions which are unfinished still fire and finshed as per documentation.

I suggest you do what I did, log all the post data in a table and validate based on transaction receipt.

When this is solved the change the validator to work with App Store receipt. Eventually we will find what's the cause of this and solve it. If I had the time I would debug the iOS sources of the pluging and see what's going on, unfortunately I am being pressured so badly from my boss that he rather realeases a bad product at this state than spend any more time to solve this the right way

butlimous commented 9 years ago

The trasnaction receipt would just make the life A LOT EASIER! However, I'm afraid Apple could just make it stop working suddenly as It's deprecated. That's what I'm worried about

joscarras commented 9 years ago

Yes that is why if that happens and the field is missing I return back an invalid response and in the application I never call product.finsh so the item never gets consumed and inform the user that something serious went wrong and call customer support.

This way at-least the users are not being charged and not receive their credits. This also gives the time to solve this problem in the future when it does indeed happen.

butlimous commented 9 years ago

Do you get asked to login to old Sandbox test accounts when opening the app? Like ones that were deleted?