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

anyone have tested the library with ios 17.4? #1532

Open luk156 opened 2 months ago

luk156 commented 2 months ago

anyone have tested the library with ios 17.4?

pravinkumarputta commented 2 months ago

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?

jhasenfuss commented 1 month ago

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

guenolefr commented 1 month ago

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

pravinkumarputta commented 4 weeks ago

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

vesper8 commented 3 weeks ago

@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!

jhasenfuss commented 1 week ago

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

pravinkumarputta commented 1 week ago
  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']


2. IAP initialisation

// Old const store = (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 = (window).CdvPurchase.store; store.register([{ id: IAP_PRODUCT, type: store.CONSUMABLE, platform: this.platform.is("ios") ? (window).CdvPurchase.Platform.APPLE_APPSTORE : (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


3. Callbacks no longer accessible
    - `refunded`
    - `error`
    - `cancelled`
    - `refresh`

4. 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.
jhasenfuss commented 1 week ago

@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?

pravinkumarputta commented 5 days ago

@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.

jhasenfuss commented 4 days ago

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);
        }
    }