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

iOS won't let me subscribe again, but works on Android #1514

Open caleb87 opened 3 months ago

caleb87 commented 3 months ago

My code works on Android, but not iOS.

On iOS, it won't pull up the window to subscribe. It did previously, but it looks like the status is owned so it won't do it again. The test subscription is expired though.

The docs (https://github.com/j3k0/cordova-plugin-purchase-documentation/blob/master/use-cases/subscription-ios.md) show to use store.order('subscription123') to place a subscription order; however, this creates an error: trying to purchase an unknown product. Right before I place the order, I use store.get('subscription123') and see it's there and says canPurchase: true.

Some issues/comments state that it should be done like this: store.get("subscription123").getOffer().order();

The getOffer method works on Android. On iOS, I'm never able to get the order screen up. Instead it seems to process the purchase all over again, but since it's an expired receipt, it won't won't load or allow me to resubscribe.

` const store = CdvPurchase.store; const ProductType = CdvPurchase.ProductType; const Platform = CdvPurchase.Platform; store.initialize([ Platform.APPLE_APPSTORE, Platform.GOOGLE_PLAY, { options: { applePay: { companyName: 'App Name', }, googlePay: { countryCode: 'US', googleMerchantName: 'App Name', },

            }
        }
    ])
    .then(() => {
        console.log('Store is ready!');
    });

if (device.platform == 'Android') {
    store.register([{
        id: 'subscription123',
        type: ProductType.PAID_SUBSCRIPTION,
        platform: Platform.GOOGLE_PLAY,
    }]);
} else {
    var productObject = {
        id: 'subscription123',
        type: ProductType.PAID_SUBSCRIPTION,
        platform: Platform.APPLE_APPSTORE,
    };
    console.log("REGISTER IS ", productObject); // shows the product type and platform values
    store.register([productObject]);
}

store.when()
    .approved(transaction => transaction.verify())
    .verified(function(receipt) {
        var receiptInfo = receipt.getNeededPlatformDataExample;
        checkReceipt(receiptInfo, function(result) {
            if (result['status'] == 'active') {
                // ACTIVE SUBSCRIPTION
                receipt.finish();
            } else {
                // NOT ACTIVE
            }

        });
    })
    .finished(transaction => console.log('Products owned: ' + transaction.products.map(p => p.id).join(',')))
    .receiptUpdated(r => updatePurchases(r))
    .productUpdated(p => updateUI(p));

function subscribe() { 
    // only using one of these, but put both in for examples i've used
    store.get("subscription123").getOffer().order().then(result => {
        // works on Android completely. on iOS, the subscribe screen wont show up even though subscription is expired
    });

    store.order("subscription123").then(result => {
        // trying to purchase an unknown product
    });
}

`

j3k0 commented 3 months ago

Do you get some logs in the console?

MarcelSchuermann commented 3 months ago

I had to do something like this for iOS order to work - maybe it helps:

//get IAP product
...

// get IAP offer
var IAPOffer1;
if (devicePlatform == 'iOS'){
     IAPOffer1 = IAPProduct1.getOffer('$'); // get default offer
     console.log(IAPOffer1);
} else {
    IAPOffer1 = IAPProduct1.getOffer();
}

// order IAP offer
IAPOffer1.order()
.then(error => {
   ...
}

For iOS to get the default offer: getOffer('$')

But it is more a workaround.

You will maybe have the same issue as me after reopening the app in iOS later: #1363

JumBay commented 3 months ago

I faced the same issue because I was filtering the .when().approve() and .when().verified() methods with my product ID as suggested here . But if you do that, you will never call verify() and finish() on you app's bundle ID. So I ended up doing this:

 CdvPurchase.store
      .when()
      .approved(async (transaction) => await transaction.verify())
      .verified(async (receipt) => await receipt.finish());

With this I can subscribe again. I don't know if this is the right way to do it, but it works, even if I notice a lot of logs (transaction finished) in Xcode with this.

I didn't publish the update yet because I'm not sure if this is the right way to do...

Edit: I've published it on TestFlight and now it doesn't seem to work anymore.

caleb87 commented 3 months ago

I'm swamped with work, so I just used AlexDisler's plugin for iOS and this one for Android. I'll figure it out when I have time and report back.

Thanks for the suggestions JumBay and MarcelSchuermann

JumBay commented 3 months ago

I think there's something wrong, cause now with the TestFlight version it works again, moreover when I try to subscribe it takes at least 5 sec before displaying the popup compare to 2 secs on v11 (tested with an old iphone 6s on iOS 15). Also sometimes I noticed a lot of logs in xcode, lots of finished transaction, maybe there's like a loop somewhere.

j3k0 commented 3 months ago

@JumBay this is the right way to do. Just finish all verified transactions (eventually do what you have to do before finishing, the concept here is that you shouldn't charge the user if there was an issue with processing the purchase)...

j3k0 commented 3 months ago

Please share logs if you need help understanding what is happening. Just notice many (thousands of) apps are online using this plugin, so it is expected to work.

7h4r05 commented 3 months ago

Hi @caleb87 looks like you are missing unverified. I believe you have to finish the unverified transaction if verification fails (for example subscription expires)