cordova-plugin-purchase icon indicating copy to clipboard operation
cordova-plugin-purchase copied to clipboard

anyone have tested the library with ios 17.4?

Open luk156 opened this issue 1 year ago • 12 comments

anyone have tested the library with ios 17.4?

luk156 avatar Feb 15 '24 17:02 luk156

I'm not able to fetch the products on iOS 17.3, it's throwing error as Invalid parameters passed to "register". Complete IAP flow got broken. Can anyone facing the same?

pravinkumarputta avatar Feb 20 '24 16:02 pravinkumarputta

We also have the same problem. Our monthly and yearly abos are broken.. Android works just fine. But iOS 17.4 seems broken :(.

jhasenfuss avatar Mar 08 '24 15:03 jhasenfuss

Hello. Please, do you resolve this issue ? I have the same... Thanks a lot

guenolefr avatar Apr 03 '24 16:04 guenolefr

@guenolefr @jhasenfuss The method implementations have been updated, please check the new documentation update accordingly. This was the fix in my case.

pravinkumarputta avatar Apr 07 '24 04:04 pravinkumarputta

@pravinkumarputta Could you please be a little more specific about which method in particular you had to update in order to make this work? That would be greatly appreciated. Many thanks!

vesper8 avatar Apr 08 '24 19:04 vesper8

@pravinkumarputta please be more precise. Like @vesper8, I have no idea which docu you mean.

jhasenfuss avatar Apr 24 '24 19:04 jhasenfuss

  1. Product Pricing
// Old
IAP_PRODUCT['currency']
IAP_PRODUCT['price']
IAP_PRODUCT['priceMicros']

// New
IAP_PRODUCT['pricing']['currency']
IAP_PRODUCT['pricing']['price']
IAP_PRODUCT['pricing']['priceMicros']
  1. IAP initialisation
// Old
const store = (<any>window).CdvPurchase.store;
store.register([{
			id: IAP_PRODUCT,
			type: store.CONSUMABLE,
		}]);

        store.when(IAP_PRODUCT).updated(async () => {
			////console.log('purchase updated----');
			this.IAP_PRODUCT = store.get(IAP_PRODUCT) || {};
			if (this.IAP_PRODUCT.finished) {
				status = 'Purchased';
			} else if (this.IAP_PRODUCT.state === 'approved') {
				status = 'Processing...';
				await (() => {
					this.IAP_PRODUCT.finish();//we need to await for it to finish
				})();//we need to call this here also
				//await this._updatePurchaseOnServer(this.IAP_PRODUCT);
			}
		});
store.when(IAP_PRODUCT).finished(async (product) => {
    const transactionId = product['transaction']['id'];
});
store.refresh(); // Was used to initialise IAP


// New
const store = (<any>window).CdvPurchase.store;
store.register([{
			id: IAP_PRODUCT,
			type: store.CONSUMABLE,
			platform: this.platform.is("ios") ? (<any>window).CdvPurchase.Platform.APPLE_APPSTORE : (<any>window).CdvPurchase.Platform.GOOGLE_PLAY,
		}]);
        store.when(IAP_PRODUCT).updated(async () => {
			////console.log('purchase updated----');
			this.IAP_PRODUCT = store.get(IAP_PRODUCT) || {};
			if (this.IAP_PRODUCT.finished) {
				status = 'Purchased';
			} else if (this.IAP_PRODUCT.state === 'approved') {
				status = 'Processing...';
				await (() => {
					this.IAP_PRODUCT.finish();//we need to await for it to finish
				})();//we need to call this here also
				//await this._updatePurchaseOnServer(this.IAP_PRODUCT);
			}
		});
store.when(IAP_PRODUCT).finished(async (product) => {
    const transactionId = product['transactionId'];
    // log the product to get clear idea
});
store.initialize(); // initialise IAP
  1. Callbacks no longer accessible

    • refunded
    • error
    • cancelled
    • refresh
  2. Make Purchase

// Old
let response = await store.order(product); // the response was getting through the callbacks


// New
try {
    let response = await this.IAP_PRODUCT.getOffer().order(); // I get response here only
if (response && response.isError) {
				if (response.message.includes('cancelled')) {
					this._iap_purchase.next({ state: PURCHASE_STATUS.CANCELLED, product: product });
				} else {
					this._iap_purchase.next({ state: PURCHASE_STATUS.FAILED, product: product });
				}
				return;
			}
} catch(err) {
    // handle error
}

@jhasenfuss @vesper8 I hope this comparision will help you.

pravinkumarputta avatar Apr 29 '24 04:04 pravinkumarputta

@pravinkumarputta thanks for your reply. But sadly, not really..

Maybe you can help me if i share my code:

AppComponent:


    constructor(...) {
        this.platform.ready().then(async () => {
            await this.initializeApp();
        });
   }

    async initializeApp() {
        ...

        if (this.platform.is('mobile')) {
            this.store = CdvPurchase.store;

            if (!environment.production) {
                // this.store.verbosity = LogLevel.DEBUG;
            }

            this.registerProducts();

            this.store.error((err) => {
                console.error('Store Error ' + JSON.stringify(err));
                this.tracker.trackEvent('Store Error', err.message, JSON.stringify(err));
                throw new Error('Store Error ' + JSON.stringify(err));
            });

            this.store.when().approved((transaction) => {
                console.log('transaction approved', transaction);
                transaction.verify();
            }).verified((receipt) => {
                console.log('receipt verified', receipt);
                receipt.finish();
            }).finished(transaction => {
                console.log('Products owned: ' + transaction.products.map(p => p.id).join(','));
            }).receiptUpdated(receipt => {
                console.log('receiptUpdated');
                receipt.transactions.forEach(transaction => {
                    transaction.products.forEach(trProduct => {
                        console.log(`product owned: ${trProduct.id}`);
                    });
                });
            }).productUpdated(t => {
                console.log('productUpdated', t);
                this.updateProVersion();
            }).unverified((receipt) => {
                console.log(`Receipt cannot be verified: ${receipt && receipt.payload && receipt.payload.message}`);
                if (receipt.payload.code === CdvPurchase.ErrorCode.COMMUNICATION) {
                    console.log('HTTP ERROR: ' + receipt.payload.status);
                }
            }).receiptsVerified(() => {
                console.log('receiptsVerified');
            }).receiptsReady(() => {
                console.log('All platforms are done loading their local receipts');
            });

            await this.store.initialize([
                CdvPurchase.Platform.GOOGLE_PLAY,
                CdvPurchase.Platform.APPLE_APPSTORE
            ]);
            await this.store.update();
            await this.store.restorePurchases();

            console.log('STORE INIT DONE');

            this.updateProVersion();
        }

        await this.checkTerms();
    }


    private registerProducts() {
        this.store.register([
            {
                id      : IOS_PRO_MONTH,
                type    : ProductType.PAID_SUBSCRIPTION,
                platform: CdvPlatform.APPLE_APPSTORE,
                group   : 'pro_version_group'
            }, {
                id      : IOS_PRO_YEAR,
                type    : ProductType.PAID_SUBSCRIPTION,
                platform: CdvPlatform.APPLE_APPSTORE,
                group   : 'pro_version_group'
            }, {
                id      : ANDROID_PRO_MONTH,
                type    : ProductType.PAID_SUBSCRIPTION,
                platform: CdvPlatform.GOOGLE_PLAY,
                group   : 'pro_version_group'
            }, {
                id      : ANDROID_PRO_YEAR,
                type    : ProductType.PAID_SUBSCRIPTION,
                platform: CdvPlatform.GOOGLE_PLAY,
                group   : 'pro_version_group'
            }
        ]);
    }

    private updateProVersion() {
        const pro = this.store.get(this.gameInstanceService.storeProMonth);
        const pro12 = this.store.get(this.gameInstanceService.storeProYear);
        console.log(pro.owned, pro12.owned);
        console.log(pro, pro12);
        this.gameInstanceService.proWithAbo = pro.owned || pro12.owned;
        console.log('-> this.gameInstanceService.proWithAbo', this.gameInstanceService.proWithAbo);
    }

As you can see, i tried several events with console logs, but with no result.. Can you say what i am doing wrong?

jhasenfuss avatar Apr 29 '24 05:04 jhasenfuss

@jhasenfuss Code looks okay, but what exactly is the problem you are facing? Are you getting some kind of error on the console? or is nothing happening?

Please share the code for purchase/order.

pravinkumarputta avatar May 01 '24 04:05 pravinkumarputta

Thanks @pravinkumarputta. The problem is, users which purchased a monthly or yearly subscription can't use the pro version after a restart of the app. So the app doesn't load the purchased products or i don't really know, what the problem is. The purchase itself works, because they can't repurchase a subscription. Maybe something since 17.4? Android works just fine.

The purchase function:

    proVersion: CdvPurchase.Product;
    proVersion12: CdvPurchase.Product;

    constructor(...){
        this.proVersion = this.store.get(gameInstanceService.storeProMonth);
        this.proVersion12 = this.store.get(gameInstanceService.storeProYear);
    }

    async subscribeProVersion() {
        try {
            let err: CdvPurchase.IError;
            if (this.selPeriod === 1) {
                err = await this.proVersion.getOffer().order();

            } else if (this.selPeriod === 12) {
                err = await this.proVersion12.getOffer().order();
            }
            console.log(err);
        } catch (e) {
            console.error(e);
        }
    }

jhasenfuss avatar May 01 '24 18:05 jhasenfuss

Hi, meanwhile i switched to https://www.revenuecat.com and it works just fine. Even on the latest iOS version.. So from my side, this issue can be closed.

jhasenfuss avatar May 28 '24 20:05 jhasenfuss

Hi, meanwhile i switched to https://www.revenuecat.com and it works just fine. Even on the latest iOS version..

So from my side, this issue can be closed.

RevenueCat will stop Cordova support.

hooliapps avatar Jun 29 '24 07:06 hooliapps