j3k0 / cordova-plugin-purchase

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

Issue on v13 with validator event returning outdated receipt on iOS #1466

Closed tomerdev closed 7 months ago

tomerdev commented 8 months ago

When a purchase is made on iOS using the v13 plugin, the validator event is returning an outdated receipt. Which makes the server side validation of the transaction impossible since the receipt doesn't contain the new transaction.

I was able to find the issue, it is here, the loadAppStoreReceipt is returning the cached receipt instead of loading the new one.

An easy fix would be to simply check that the forceReceiptReload property isn't true (before we return the cached receipt in the loadAppStoreReceipt method). And make sure we switch the forceReceiptReload property to false right after calling loadAppStoreReceipt here.

Would it be possible to ship the fix in a future version @j3k0? Thanks

j3k0 commented 8 months ago

It doesn't make validation impossible: Apple's API will return the history of all transactions (including newer ones).

On Sep 12, 2023 at 17:44:23, Tomer @.***> wrote:

When a purchase is made on iOS using the v13 plugin, the validator event is returning an outdated receipt. Which makes the server side validation of the transaction impossible since the receipt doesn't contain the new transaction.

I was able to find the issue, it is here https://github.com/j3k0/cordova-plugin-purchase/blob/533d06816006280512e80067f599c8089a869cb6/src/ts/platforms/apple-appstore/appstore-adapter.ts#L423, the loadAppStoreReceipt is returning the cached receipt instead of loading the new one.

An easy fix would be to simply check that the forceReceiptReload property isn't true (before we return the cached receipt in the loadAppStoreReceipt method). And make sure we switch the forceReceiptReload property to false right after calling loadAppStoreReceipt here https://github.com/j3k0/cordova-plugin-purchase/blob/533d06816006280512e80067f599c8089a869cb6/src/ts/platforms/apple-appstore/appstore-adapter.ts#L636 .

Would it be possible to ship the fix in a future version @j3k0 https://github.com/j3k0? Thanks

— Reply to this email directly, view it on GitHub https://github.com/j3k0/cordova-plugin-purchase/issues/1466, or unsubscribe https://github.com/notifications/unsubscribe-auth/AABO3CPVKXAITQSYOSGGR63X2BYMPANCNFSM6AAAAAA4U7BI3I . You are receiving this because you were mentioned.Message ID: @.***>

tomerdev commented 8 months ago

For subscriptions but not for consumables.

lilmnm-kamikaze- commented 8 months ago

I am having the same issue. I am able to validate one purchase then each purchase after with a different in app item it returns the previous appStoreReceipt that then gets sent to apple to validate but it is the first purchase that was already validated. Using V13.8.0 plugin

oduverne commented 8 months ago

Hello, same problem here. My solution is to restart the app after doing a restore or a purchase but it is not a good solution

heffthedev commented 8 months ago

This could explain why all our purchases (non_consumable) after an initial one do not get properly handled.

heffthedev commented 8 months ago

The changes @tomerdev suggested do not seem to fix the issue for us in ios for non_consumables. So far the only way I could find to make a purchase of 2 products (shopping cart style) get correctly validated with our own validator is by calling restorePurchases() after the 2 purchases are completed. Then the most up to date receipt gets then sent to the validator and it all works.

I have not managed to get the plugin to send the latest receipt (of a 2+ product purchase) without calling restorePurchase(). Only the first purchase is contained inside the receipts transactions, any further ones are not (until restorePurchase()).

lilmnm-kamikaze- commented 8 months ago

The same for me. Is there any insight on this issue? Even with the 13.8.1 update i am still getting this issue.

cmaas commented 8 months ago

I can confirm this issue, but in my case, loadAppStoreReceipt is called once only and never reaches the cached-check branch that @tomerdev mentioned.

The flow is as follows as far as I can tell: App starts → loads existing receipts via initializeAppReceipt() and caches the result. This happens every time the app starts, because on iOS, you have at least one transaction, which is the download of the app itself.

If you then buy an item (a consumable in my case), initializeAppReceipt() is called again, but this._receipt already exists and hence, the function exits here already: https://github.com/j3k0/cordova-plugin-purchase/blob/22ea0d6e8bffeed5f7219aa5af6fc88b39fdd3cf/src/ts/platforms/apple-appstore/appstore-adapter.ts#L382C30-L382C30

One thing though: This has nothing to do with the validator event. It happens during the entire flow of the transaction. The receipt is simply never updated.

oduverne commented 7 months ago

I did it this way by replacing the deprecated verifyReceipt by App Store Server API and it works great. Thank you!

It doesn't make validation impossible: Apple's API will return the history of all transactions (including newer ones). On Sep 12, 2023 at 17:44:23, Tomer @.> wrote: When a purchase is made on iOS using the v13 plugin, the validator event is returning an outdated receipt. Which makes the server side validation of the transaction impossible since the receipt doesn't contain the new transaction. I was able to find the issue, it is here https://github.com/j3k0/cordova-plugin-purchase/blob/533d06816006280512e80067f599c8089a869cb6/src/ts/platforms/apple-appstore/appstore-adapter.ts#L423, the loadAppStoreReceipt is returning the cached receipt instead of loading the new one. An easy fix would be to simply check that the forceReceiptReload property isn't true (before we return the cached receipt in the loadAppStoreReceipt method). And make sure we switch the forceReceiptReload property to false right after calling loadAppStoreReceipt here https://github.com/j3k0/cordova-plugin-purchase/blob/533d06816006280512e80067f599c8089a869cb6/src/ts/platforms/apple-appstore/appstore-adapter.ts#L636 . Would it be possible to ship the fix in a future version @j3k0 https://github.com/j3k0? Thanks — Reply to this email directly, view it on GitHub <#1466>, or unsubscribe https://github.com/notifications/unsubscribe-auth/AABO3CPVKXAITQSYOSGGR63X2BYMPANCNFSM6AAAAAA4U7BI3I . You are receiving this because you were mentioned.Message ID: @.>

lilmnm-kamikaze- commented 7 months ago

I did it this way by replacing the deprecated verifyReceipt by App Store Server API and it works great. Thank you!

It doesn't make validation impossible: Apple's API will return the history of all transactions (including newer ones). On Sep 12, 2023 at 17:44:23, Tomer @.> wrote: When a purchase is made on iOS using the v13 plugin, the validator event is returning an outdated receipt. Which makes the server side validation of the transaction impossible since the receipt doesn't contain the new transaction. I was able to find the issue, it is here https://github.com/j3k0/cordova-plugin-purchase/blob/533d06816006280512e80067f599c8089a869cb6/src/ts/platforms/apple-appstore/appstore-adapter.ts#L423, the loadAppStoreReceipt is returning the cached receipt instead of loading the new one. An easy fix would be to simply check that the forceReceiptReload property isn't true (before we return the cached receipt in the loadAppStoreReceipt method). And make sure we switch the forceReceiptReload property to false right after calling loadAppStoreReceipt here https://github.com/j3k0/cordova-plugin-purchase/blob/533d06816006280512e80067f599c8089a869cb6/src/ts/platforms/apple-appstore/appstore-adapter.ts#L636 . Would it be possible to ship the fix in a future version @j3k0 https://github.com/j3k0? Thanks — Reply to this email directly, view it on GitHub <#1466>, or unsubscribe https://github.com/notifications/unsubscribe-auth/AABO3CPVKXAITQSYOSGGR63X2BYMPANCNFSM6AAAAAA4U7BI3I . You are receiving this because you were mentioned.Message ID: @.>

@oduverne would you be able to make a pull request for the fix to help others?

j3k0 commented 7 months ago

The fix is server side, note that verifyReceipt will also return the list of newer transactions in the receipt. (check the difference between the in_app and latest_receipt fields)... Maybe you are using a (very) outdated library to handle this server side? In doubt, you can test validation with https://www.iaptic.com, AFAIK you should get the correct result.

ersingencturk commented 3 months ago

i was able to get the refreshed appstorereceipt with this way:

store.when().approved(verifyPurchase);  

verifyPurchase function:

switch (platform) {
      case "iOS":
        postData.type="ios-appstore";
        store.getAdapter("ios-appstore").refreshReceipt().then(function (s) {
          postData.appStoreReceipt=product.parentReceipt.nativeData.appStoreReceipt;
          serverValidateTransaction(product,postData);
        });

for me 13.10.1 version of this plugin is broken for ios , custom server validation.

i was using store.validator function to validate in app purchase but that seems to never receive in app purcahses only app itself with product.type="application"