How to handle native modal cancel / error event (cordova-plugin-purchase)?

441 Views Asked by At

Problem

When user opens a subscription page, native iTunes user / password modal appears. No matter what action does user next. Enter password or cancel it, my code does not handle this event. And loading never stops and purchase button never appears.

So, app rejected every time with reason: app loaded indefinitely in the subscription page

Any ideas how to solve the problem? I can't get it. .cancelled() does not handle native events.

Env:

Ionic 5.1.1
Capacitor 2.3.0
Device: iPhone 6S iOS 13.5.1
cordova-plugin-purchase: 10.2.0

App initialization:

this.platform.ready().then(() => {
    this.registerProducts()
 })

registerProducts() {
    this.store.register([
        {
            id: 'premium1',
            alias: 'premium',
            type: this.store.PAID_SUBSCRIPTION,
        },
        {
            id: 'product1',
            alias: 'product1',
            type: this.store.CONSUMABLE,
        },
        {
            id: 'product2',
            alias: 'product2',
            type: this.store.CONSUMABLE,
        },
    ])
    this.store.refresh()
}

First page:

this.platform.ready().then(() => {
    let m = this.store.get('premium')
        if (!m.owned) {
            this.setPremium(false)
            this.cdref.detectChanges() // Angular ChangeDetectorRef
        }
})

Then subscription page:

this.platform.ready().then(() => {
    this.registerProducts()
    this.setupListeners()
    this.store.update()
 })

registerProducts() {
    let product = this.store.get('premium')
    
    if (!product) {
        this.loading = false
        this.productError = true
    } else if (product.state === this.store.INVALID) {
        this.loading = false
        this.productError = true
    } else {
        this.loading = false
        this.product = product
    }

    this.cdref.detectChanges()
}

setupListeners() {
    this.store.when('premium')
        .error(e => {
            this.dissmissAlert()
            this.presentAlert(e.code, e.message)
        })
        .finished(() => {
            this.loading = false
            this.restoreLoading = false
        })
        .expired(() => {
            this.loading = false
            this.productError = true
        })
        .approved((p: IAPProduct) => {
            p.verify()
            this.cdref.detectChanges()
        })
        .verified((p: IAPProduct) => {
            this.setPremium(true)
            p.finish()
            this.cdref.detectChanges()
        })
}

purchase() {
    this.btnLoading = true
    
    try {
        this.store.order('premium').then(() => {
            this.btnLoading = false
        }).catch((err) => {
            this.presentAlert('', err)
        })
    } catch (error) {
        this.presentAlert('', error)
    }
}

restore() {
        this.restoreLoading = true
        this.store.refresh()
        this.cdref.detectChanges()
}
<ng-container *ngIf="productError">
    <div class="wait">
            <span class="text">Error with Subcription. Try again later.</span>
    </div>
</ng-container>

 <ng-container *ngIf="!productError">
    <ng-container *ngIf="loading">
        <div class="wait">
            <span class="text">Loading...</span>
            <ion-spinner class="spinner" name="crescent"></ion-spinner>
        </div>
    </ng-container>

    <ng-container *ngIf="!loading">
        <p class="act" *ngIf="product.owned">Subscribed</p>

        <button class="sub-btn" (click)="purchase()" *ngIf="!product.owned">
            <ion-spinner class="spinner" name="crescent" *ngIf="btnLoading"></ion-spinner>

            <ng-container *ngIf="!btnLoading">
                <span class="text">Subscribe {{ product.price }} / {{ product.billingPeriod }} мес.</span>
                <ion-icon class="icon" name="arrow-forward-circle" slot="end"></ion-icon>
            </ng-container>
        </button>
    </ng-container>
</ng-container>
1

There are 1 best solutions below

0
Roman Khegay On BEST ANSWER

registerProducts() and setupListeners() should be called ONCE upon app initialization.