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 doesn't recognise purchase after closing/reopening the app #1606

Open kreso22 opened 1 week ago

kreso22 commented 1 week ago

Observed behavior

Making a purchase works (product is owned). Next app load - receipt shows product is not owned. Only after hitting restorePurchase does the receipt show the product as owned

Not using validation. App has one single product.

Expected behavior

Product owned persists.

Code

Currently forcing the restorePurchases() on random event (when app starts). Otherwise, product will be shown as not owned.


        // Register the product
        CdvPurchase.store.register([{
            id: PROD_ID,
            platform: PLATFORM,
            type: CdvPurchase.ProductType.NON_CONSUMABLE
        }]);

        // Handle product updates
        CdvPurchase.store.when().productUpdated(product => {
            if(product.id == PROD_ID) {
                processIAP(product.owned);          
            }
        });

        CdvPurchase.store.when().receiptUpdated(localReceipt => {
            const product = CdvPurchase.store.get(PROD_ID);

            if (product) {
                processIAP(product.owned);
            }
        });

        CdvPurchase.store.when().receiptsReady(() => {
            MyDebug.log("STORE", "receiptsReady! ...");         
            CdvPurchase.store.restorePurchases();
        });

        CdvPurchase.store.when().approved(transaction => {
            processIAP(true);
            transaction.finish();
        });

        CdvPurchase.store.initialize([
            {
                platform: PLATFORM,
                options: { needAppReceipt: false }
            }
        ]);

    }

initiatePurchase() {
        CdvPurchase.store.get(PROD_ID).getOffer().order().then(error => {
                if (error) {
                    // failed to buy
                    alert("Purchase not successful. Error:" + error.code);
                }
            });
    }
j3k0 commented 1 day ago

I understand it's a workaround but do not do this:

CdvPurchase.store.when().receiptsReady(() => {
    CdvPurchase.store.restorePurchases();
});

As mentioned in the documentation, "restorePurchases" should only be called when the user clicks the "restore purchases" button. This call will ask for the user appstore password (except if they already logged in in the last 15 minutes, like just after downloading the app).

Can you share the startup logs when the non consumable is owned (without that call)?

kreso22 commented 11 hours ago

I understand it's a workaround but do not do this:

CdvPurchase.store.when().receiptsReady(() => {
  CdvPurchase.store.restorePurchases();
});

As mentioned in the documentation, "restorePurchases" should only be called when the user clicks the "restore purchases" button. This call will ask for the user appstore password (except if they already logged in in the last 15 minutes, like just after downloading the app).

Can you share the startup logs when the non consumable is owned (without that call)?

Sure!

Here is the related Xcode startup log:

[CDVTimer][console] 0.012994ms
[CDVTimer][handleopenurl] 0.012040ms
[CDVTimer][intentandnavigationfilter] 0.587940ms
[CDVTimer][gesturehandler] 0.012994ms
[CDVTimer][applovinmax] 0.067949ms
[CDVTimer][inappbrowser] 0.017047ms
[CdvPurchase.AppleAppStore.objc] Initialized.
[CDVTimer][inapppurchase] 10.787964ms
[CDVTimer][screenedgesplugin] 0.082016ms
[CDVTimer][cdvwkwebviewfilexhr] 49.003005ms
[CDVTimer][socialsharing] 0.018001ms
[CDVTimer][TotalPluginStartup] 60.752034ms
[CdvPurchase.AppleAppStore.objc] (before init): WARNING: Your app should be single page to use in-app-purchases. onReset is not supported.
Create CdvPurchase...

And from inside the app once started:

[STORE] - "initializeStore" - "CVD Platform:" - "ios-appstore" - "Product name:" XXXX
[STORE] - "[productUpdated]. product:" - SKProduct {className: "Product", title: "Remove ads", description: "No more ads", ...
[STORE] - "Processing ownership. Owned:" - false
[STORE] - "[receiptsReady]."
[STORE] - "[receiptUpdated]. localreceipt:" - SKApplicationReceipt {className: "Receipt", transactions: [1, platform: "ios-appstore", ...}
[STORE] - "Processing ownership. Owned:" - false
[STORE] - "[receiptUpdated]. localreceipt:" - Receipt {className:
"Receipt", transactions: [1, platform: "ios-appstore", ...}
[STORE] - "Processing ownership. Owned: " - false

The first receiptUpdated is the app. Second receiptUpdate looks empty (no transactions, nothing ...).

On each receiptUpdate I check if it is for desired product (I have only 1 in my app) like so:


CdvPurchase.store.when().receiptUpdated(localReceipt => {

     log("STORE", "[receiptUpdated]. localreceipt:", localReceipt);

     const product = CdvPurchase.store.get(PROD_ID);

     if (product) {
          processOwnership(product.owned);
     }
});