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

[Android] Version 13.4.0 product always undefined #1414

Closed AlexLaLiga closed 10 months ago

AlexLaLiga commented 1 year ago

In my ionic app, I have imported the plugin version 13.4.0 and using it like this:

import 'cordova-plugin-purchase';
declare const CdvPurchase: any;

  constructor(private platform: Platform) {
    this.initialize();
  }

  private initialize(): void {

    if (!this.platform.is('cordova')) {
      return;
    }

    CdvPurchase.store.verbosity = CdvPurchase.LogLevel.DEBUG
    this.myProduct = CdvPurchase.store.get(this.globalProductID);
    this.setListeners()
    CdvPurchase.store.register([{
      type: CdvPurchase.ProductType.CONSUMABLE,
      id: this.globalProductID,
      platform: CdvPurchase.Platform.GOOGLE_PLAY
    }]);

    CdvPurchase.store.initialize([CdvPurchase.Platform.GOOGLE_PLAY]);
  }

  setListeners() {

    CdvPurchase.store.when().approved((p: any) => {
      console.log('PURCHASE SUCCESS');
    });
    CdvPurchase.store.when().updated((p: any) => {
      console.log('updated SUCCESS');
      this.myProduct = CdvPurchase.store.get(this.globalProductID);

    });
    CdvPurchase.store.when().verified((receipt: any) => {
      receipt.finish()
    });
    CdvPurchase.store.when().productUpdated(() => {
      console.log('PURCHASE ERROR');
    });
    CdvPurchase.store.when().finished(() => {
      console.log('FINISH');
    });
    CdvPurchase.store.when().receiptUpdated((receipt: any) => {
      if (CdvPurchase.store.owned(this.globalProductID)) {
        console.log("my-product is owned");
      }
    });
    CdvPurchase.store.error((err: any) => {
      console.error('Store Error ' + JSON.stringify(err));
    });
    CdvPurchase.store.ready(() => {
      console.log('Store is ready');
      console.log('Products: ' + JSON.stringify(CdvPurchase.store.products));
    });
}

Then I try to perform an order with the product offer:

  purchase(productId: string) {
    if (!this.platform.is('cordova')) {
      return;
    }
    this.myProduct = CdvPurchase.store.get(this.globalProductID);

    console.log("Init purchasing")
    if (this.myProduct)
      this.myProduct.getOffer().order()
    else
      console.log("No product")

  }

Log:

[CdvPurchase] INFO: initialize() store.js:319 [CdvPurchase.Adapters] INFO: Adding platforms: [{"platform":"android-playstore"}] store.js:319 [CdvPurchase.Adapters] INFO: store.js:319 [CdvPurchase.Adapters] INFO: GooglePlay initializing... store.js:319 [CdvPurchase.GooglePlay] INFO: Initialize store.js:319 [CdvPurchase.GooglePlay.Bridge] INFO: setup ok

No product

The problem is that product is always undefined and I don't have any additional log.

Versions info: "cordova-plugin-purchase": "13.4.0", "@angular/core": "^15.0.0", "@ionic/angular": "^7.0.0",

Related with the Google Play console, everything is well configured, app is published in Alpha channel, billing key is set, product is created and active, and I am testing it with the signed apk even with the GooglePlay apk. Also the tester group is ready.

Please, any help would be appreciated. Thanks in advance.

AlexLaLiga commented 1 year ago

With TEST Platform everything is working fine, but with GOOGLE_PLAY "store.ready" is never call. Last debug message is:

store.js:319 [CdvPurchase.GooglePlay.Bridge] INFO: setup ok

I have tried registering different products but it doesn't work. Please any help would be appreciated because I don't know what else I can try to make it works.

Thanks in advance.

j3k0 commented 1 year ago

Can you provide the full logs of that's shown when the app starts? (at least all that contains CdvPurchase).

AlexLaLiga commented 1 year ago

@j3k0 thanks for responding. Sure, here is the log:

Create CdvPurchase...
store.js:319 [CdvPurchase] INFO: initialize()
store.js:319 [CdvPurchase.Adapters] INFO: Adding platforms: [{"platform":"android-playstore"}]
store.js:319 [CdvPurchase.Adapters] INFO: 
store.js:319 [CdvPurchase.Adapters] INFO: GooglePlay initializing...
store.js:319 [CdvPurchase.GooglePlay] INFO: Initialize
store.js:319 [CdvPurchase.GooglePlay.Bridge] INFO: setup ok

Let me know if any additional information is needed.

Thanks in advance.

j3k0 commented 1 year ago

Maybe you can check with adb logcat to see what happens on the native side? Did you wait deviceready before initializing the plugin? Are you using capacitor?

AlexLaLiga commented 1 year ago

I don't see any additional log on the native side. Yes, I'm calling CdvPurchase.store.initialize() after deviceready. I am using Ionic with npm, not using capacitor. If it's needed I can provided you the full service code.

import { Injectable } from '@angular/core';
import { Platform } from '@ionic/angular';
import 'cordova-plugin-purchase/www/store.d';

@Injectable({
  providedIn: 'root'
})
export class PurchaseService {

  private globalProductID = "diet_shoot"
  private myProduct: any = null;

  constructor(private platform: Platform) {
    this.initialize();
  }

  private initialize(): void {

    if (!this.platform.is('cordova')) {
      return;
    }

    CdvPurchase.store.verbosity = CdvPurchase.LogLevel.DEBUG

    console.log("Register product")
    CdvPurchase.store.register([{
      type: CdvPurchase.ProductType.CONSUMABLE,
      id: this.globalProductID,
      platform: CdvPurchase.Platform.GOOGLE_PLAY
    }
    ]);
    console.log("Register product finish")

    CdvPurchase.store.when()
      .approved(transaction => transaction.verify())
      .verified(receipt => receipt.finish())
      .finished(transaction => console.log('Products owned: ' + transaction.products.map(p => p.id).join(',')))
      .receiptUpdated(r => console.log("receipt update"))
      .productUpdated(p => console.log("product update"));

    CdvPurchase.store.ready(function () {
      console.log("CdvPurchase is ready");
    });

    console.log("Lets initialize")
    CdvPurchase.store.initialize([CdvPurchase.Platform.GOOGLE_PLAY]).then((data) => {
      console.log('Store is ready!');
    });
    console.log("Finish initialize")
  }

  public getProduct(id: string): any {
    let temp = CdvPurchase.store.get(id)
    console.log(temp)
    return temp;
  }

  purchase(productId: string) {
    if (!this.platform.is('cordova')) {
      return;
    }

    this.myProduct = CdvPurchase.store.get(this.globalProductID, CdvPurchase.Platform.GOOGLE_PLAY);
    console.log(this.myProduct);
    console.log("Init purchasing")
    if (this.myProduct)
      this.myProduct.getOffer().order().then((result: any) => {
        if (result) {
          console.log("ERROR. Failed to place order. " + result.code + ": " + result.message);
        }
        else {
          console.log("subscription1 ordered successfully");
        }
      })
    else
      console.log("No product info")
  }
}
AlexLaLiga commented 12 months ago

Hi @j3k0 ,

Do you have any ideas? I have tried everything I've seen on forums and official documentation but I haven't been able to get it to work and I don't see any error messages either.

Thanks in advance.

BR

UzverNumber47 commented 12 months ago

Same for me. Everything worked on 11.0.0. Now I am interested in google play subscription offers and decided to update to 13.4.0 but can't even make old purchases to work. Don't see anything in logcat

UzverNumber47 commented 12 months ago

After store.register(...) I called store.update() and everything started to work.

store.register(...) before store.initialize() also works.

So In my case the problem was calling store.register(...) after store.initialize()

AlexLaLiga commented 12 months ago

@UzverNumber47 I am calling store.register(...) before store.initialize() and also I have tried calling store.update() but id doesn't work. Could you please share the code? I would appreciate it.

Thanks in advance.

UzverNumber47 commented 12 months ago

Could it be that you call store.get(id) before initialization is finished? I mean try CdvPurchase.store.initialize([CdvPurchase.Platform.GOOGLE_PLAY]).then((data) => { this.getId('Your id') console.log('Store is ready!'); });

UzverNumber47 commented 12 months ago

My code is very simple

// Works
this.store = CdvPurchase.store;   
this.registerSubscriptions();
this.store.initialize().then((err) => {
  const product = this.store.get(MY_ID); // product
});

// Does NOT work
this.store = CdvPurchase.store; 
this.store.initialize().then((err) => {
  this.registerSubscriptions();
  const product = this.store.get(MY_ID); // undefined
});

registerSubscriptions() {
    const type = CdvPurchase.ProductType.PAID_SUBSCRIPTION;
    this.store.register([
      {
        id: GrechaStoreService.MONTH_1,
        type,
        platform: this.purchasePlatform
      }
    ]);
  }
AlexLaLiga commented 12 months ago

@UzverNumber47 thanks for your response. The problem is that .initialize promise result is never call, so console.log('Store ir ready!") is never printed. If I use CdvPurchase.Platform.TEST it works but not with CdvPurchase.Platform.GOOGLE_PLAY. I have check everything and the app is published in a private channel, the product is active...

As you can see, I am doing the same:

    CdvPurchase.store.register([{
      type: CdvPurchase.ProductType.CONSUMABLE,
      id: this.globalProductID,
      platform: CdvPurchase.Platform.GOOGLE_PLAY
    }
    ]);
    CdvPurchase.store.initialize([CdvPurchase.Platform.GOOGLE_PLAY]).then((data) => {
      console.log('Store is ready!');
    });
    CdvPurchase.store.when()
    .approved(transaction => transaction.verify())
    .verified(receipt => receipt.finish())
    .finished(transaction => console.log('Products owned: ' + transaction.products.map(p => p.id).join(',')))
    .receiptUpdated(r => console.log("receipt update"))
    .productUpdated(p => console.log("product update"));

And this is the result:

store.js:319 [CdvPurchase] INFO: initialize()
store.js:319 [CdvPurchase.Adapters] INFO: Adding platforms: [{"platform":"android-playstore"}]
store.js:319 [CdvPurchase.Adapters] INFO: 
store.js:319 [CdvPurchase.Adapters] INFO: GooglePlay initializing...
store.js:319 [CdvPurchase.GooglePlay] INFO: Initialize
store.js:319 [CdvPurchase.GooglePlay.Bridge] INFO: setup ok

But "Store is ready!" is never printed.

UzverNumber47 commented 12 months ago

Do you run your code after deviceready event? As far as I can see you start initializing in the constructor which may be called before deviceready

A little extended version of my code

this.platform.ready().then(() => {
  this.store = CdvPurchase.store;   
  this.registerSubscriptions();
  this.store.initialize().then((err) => {
    const product = this.store.get(MY_ID); // product
  });
})

this.platform in my case is from '@ionic/angular'

UzverNumber47 commented 12 months ago

Try instead of this.initialize();

this.platform.ready().then(() => {
  this.initialize();
})

in your constructor

j3k0 commented 11 months ago

@AlexLaLiga You should register the events handler (store.when()...) before calling initialize() -- that's probably the reason why ready is not called, the pending purchases are not handled.

EYALIN commented 10 months ago

@j3k0 i just upgraded to 13 from 11, and gets undefined when calling product.get. using the latest version of the plugin

also, i support you monthly. and thanks for all your work

EYALIN commented 10 months ago

@j3k0 It was my mistake, sent the incorrect object to register. but I think it was needed to throw an error. the error handling of this version is very low. it could help us to implement things correctly.

again, thank you for all the efforts, I'm highly appreciate your work and support you in Patreon

AlexLaLiga commented 10 months ago

I have finally resolved the issue updating android cordova version. It seems there was a problem with app ready event. Now it works. Thank you all.

j3k0 commented 10 months ago

@AlexLaLiga - I just went through your messages. Nice you got it sorted out. I'll make sure the plugin improve on the "error reporting" front. Thanks for your support

danieldanielecki commented 8 months ago

I had very trivial experience, instead of registering products with just an ID, e.g., scorex2, I tend to do it with a full path, e.g., com.company.application.scorex2. The latter is way for iOS, but for Android we just need to register ID.