j3k0 / cordova-plugin-purchase

In-App Purchase for Cordova on iOS, Android and Windows
https://purchase.cordova.fovea.cc
1.29k stars 535 forks source link

V13 Android products array are empty. Capacitor Ionic VueJS #1437

Open v-trishyn opened 1 year ago

v-trishyn commented 1 year ago

Observed behavior

Android products are empty after registering the products, I haven't found any of these issues here.

Full log will be below

2023-07-07 14:11:54.654 30894-30894/io.my.app I/Capacitor/Console: File: http://localhost/js/app.30a84cbc.js - Line 1 - Msg: [CdvPurchase.Adapters] INFO: GooglePlay products: []

So, what I did:

  1. I have created subscriptions at Google Play Console, built a release, and pushed the app to internal tests.
  2. working with android v32
  3. have added into build.gradle dependencies
    implementation 'com.android.billingclient:billing:6.0.1'
  4. have added to AndroidManifest.xml(found that this is not required currently, but I did so)
    <uses-permission android:name="com.android.vending.BILLING" />
  5. I'm using product_id of subscriptions from Google Play to use them for the register method.
  6. have heard from the dev of this package that BILlING_KEY is not used anymore and ignored this.
  7. My code looks like this:
import "cordova-plugin-purchase";

const initStore = async () => {
  CdvPurchase.Logger.console = {
    error: (message: string | unknown) => logerror(message),
    warn: (message: string | unknown) => logwarn(message),
    log: (message: string | unknown) => loginfo(message)
  };

  CdvPurchase.store.verbosity = LogLevel.DEBUG;
  CdvPurchase.store.validator =
    process.env.VUE_APP_IAP_RECEIPT_VALIDATION_URL + `/${deviceid}`;
    CdvPurchase.store.validator_privacy_policy = ['analytics', 'support', 'tracking', 'fraud'];
    CdvPurchase.store.applicationUsername = () => deviceid;

  /** getting products from the backend */
  onPlansResult(async ({ data }) => {
    plans.value = data?.plans || [];
    CdvPurchase.store.ready(() => {
      products.value = CdvPurchase.store.products;
      console.log('STORE IS READY');
    });
    await registerProducts();
    setupListeners();
    await CdvPurchase.store.initialize([
      Platform.APPLE_APPSTORE,
      Platform.GOOGLE_PLAY,
    ]);
    console.log('store must be initialized');
  });
}

document.addEventListener('deviceready', initStore);

const registerProducts = async () => {
  const productsForRegister = [];
  plans.value.forEach((plan) => {
    /** I'm absolutely certain that I'm getting the ID of backend product, have seen it within the console.log()
    const id = getPlanId(plan) || '';
    const iapProduct = {
      id: id,
      type: ProductType.PAID_SUBSCRIPTION,
      plantorm: getStorePlatform(),
      // group: 'default'
    };
    productsForRegister.push(iapProduct);
  });
  console.log(productsForRegister);
  CdvPurchase.store.register(productsForRegister);
  console.log('products constructed send to register');
};

const setupListeners = async () => {
  CdvPurchase.store.when().approved(approvedEvent);
  CdvPurchase.store.when().verified(verifiedEvent);
  CdvPurchase.store.when().finished(ownedEvent);
  CdvPurchase.store.when().receiptUpdated(updatedEvent);

  CdvPurchase.store.error(errorEvent);
};

Any advice?

2023-07-07 14:06:12.493 23242-23242/? D/CdvPurchase: onStart()
2023-07-07 14:06:12.493 23242-23242/? D/CdvPurchase: queryPurchases()
2023-07-07 14:06:12.503 23242-23242/? D/CdvPurchase: executeServiceRequest() -> OK
2023-07-07 14:06:12.795 23242-27751/? I/CdvPurchase: queryPurchases(SUBS) -> Elapsed time: 292ms
2023-07-07 14:06:12.836 23242-27749/? I/CdvPurchase: queryPurchases(INAPP) -> Elapsed time: 333ms
2023-07-07 14:06:12.836 23242-27749/? D/CdvPurchase: sendToListener() -> setPurchases
2023-07-07 14:06:12.836 23242-27749/? D/CdvPurchase:             data -> {"purchases":[]}
2023-07-07 14:06:14.288 23242-23242/? D/CdvPurchase: onStop()
2023-07-07 14:07:01.370 28582-28672/? D/CdvPurchase: sendToListener() -> ready
2023-07-07 14:07:01.372 28582-28672/? D/CdvPurchase:             data -> {}
2023-07-07 14:07:01.372 28582-28672/? D/CdvPurchase: init()
2023-07-07 14:07:01.384 28582-28672/? D/CdvPurchase: startServiceConnection()
2023-07-07 14:07:01.410 28582-28719/? D/CdvPurchase: startServiceConnection() -> Success
2023-07-07 14:07:01.410 28582-28719/? D/CdvPurchase: init() -> Success
2023-07-07 14:09:25.438 28582-28582/? D/CdvPurchase: onStop()
2023-07-07 14:11:11.164 30894-30894/io.my.app I/Capacitor/Console: File: http://localhost/ - Line 4362 - Msg: Create CdvPurchase...
2023-07-07 14:11:11.705 30894-30894/io.my.app I/Capacitor/Console: File: http://localhost/js/app.30a84cbc.js - Line 1 - Msg: Create CdvPurchase...
2023-07-07 14:11:54.484 30894-30894/io.my.app I/Capacitor/Console: File: http://localhost/js/app.30a84cbc.js - Line 1 - Msg: [CdvPurchase] INFO: initialize()
2023-07-07 14:11:54.484 30894-30894/io.my.app I/Capacitor/Console: File: http://localhost/js/app.30a84cbc.js - Line 1 - Msg: [CdvPurchase.Adapters] INFO: Adding platforms: [{"platform":"ios-appstore"},{"platform":"android-playstore"}]
2023-07-07 14:11:54.484 30894-30894/io.my.app I/Capacitor/Console: File: http://localhost/js/app.30a84cbc.js - Line 1 - Msg: [CdvPurchase.Adapters] INFO: 
2023-07-07 14:11:54.485 30894-30894/io.my.app I/Capacitor/Console: File: http://localhost/js/app.30a84cbc.js - Line 1 - Msg: [CdvPurchase.Adapters] INFO: 
2023-07-07 14:11:54.485 30894-30894/io.my.app I/Capacitor/Console: File: http://localhost/js/app.30a84cbc.js - Line 1 - Msg: [CdvPurchase.Adapters] INFO: AppStore initializing...
2023-07-07 14:11:54.486 30894-30894/io.my.app I/Capacitor/Console: File: http://localhost/js/app.30a84cbc.js - Line 1 - Msg: [CdvPurchase.Adapters] INFO: AppStore is not supported.
2023-07-07 14:11:54.487 30894-30894/io.my.app I/Capacitor/Console: File: http://localhost/js/app.30a84cbc.js - Line 1 - Msg: [CdvPurchase.Adapters] INFO: GooglePlay initializing...
2023-07-07 14:11:54.488 30894-30894/io.my.app I/Capacitor/Console: File: http://localhost/js/app.30a84cbc.js - Line 1 - Msg: [CdvPurchase.GooglePlay] INFO: Initialize
2023-07-07 14:11:54.488 30894-30894/io.my.app I/Capacitor/Console: File: http://localhost/js/app.30a84cbc.js - Line 1 - Msg: [CdvPurchase.GooglePlay.Bridge] INFO: setup ok
2023-07-07 14:11:54.491 30894-31009/io.my.app D/CdvPurchase: sendToListener() -> ready
2023-07-07 14:11:54.491 30894-31009/io.my.app D/CdvPurchase:             data -> {}
2023-07-07 14:11:54.492 30894-31009/io.my.app D/CdvPurchase: init()
2023-07-07 14:11:54.517 30894-30894/io.my.app I/Capacitor/Console: File: http://localhost/js/app.30a84cbc.js - Line 1 - Msg: [CdvPurchase.GooglePlay.Bridge] INFO: listener: {"type":"ready","data":{}}
2023-07-07 14:11:54.531 30894-31009/io.my.app D/CdvPurchase: startServiceConnection()
2023-07-07 14:11:54.613 30894-31626/io.my.app D/CdvPurchase: startServiceConnection() -> Success
2023-07-07 14:11:54.613 30894-31626/io.my.app D/CdvPurchase: init() -> Success
2023-07-07 14:11:54.629 30894-30894/io.my.app I/Capacitor/Console: File: http://localhost/js/app.30a84cbc.js - Line 1 - Msg: [CdvPurchase.GooglePlay] DEBUG: Ready
2023-07-07 14:11:54.630 30894-30894/io.my.app I/Capacitor/Console: File: http://localhost/js/app.30a84cbc.js - Line 1 - Msg: [CdvPurchase.Adapters] INFO: GooglePlay initialized. 
2023-07-07 14:11:54.654 30894-30894/io.my.app I/Capacitor/Console: File: http://localhost/js/app.30a84cbc.js - Line 1 - Msg: [CdvPurchase.Adapters] INFO: GooglePlay products: []
2023-07-07 14:11:54.655 30894-30894/io.my.app I/Capacitor/Console: File: http://localhost/js/app.30a84cbc.js - Line 1 - Msg: [CdvPurchase.AdapterListener] DEBUG: setSupportedPlatforms: android-playstore
2023-07-07 14:11:54.655 30894-30894/io.my.app I/Capacitor/Console: File: http://localhost/js/app.30a84cbc.js - Line 1 - Msg: [CdvPurchase] DEBUG: Calling callback: type=ready() name=
fyxtc commented 1 year ago

Same as what I encountered. Capacitor Ionic ReactJS. @j3k0 Hope to help us

Kamsou commented 1 year ago

Same, works with ios but not android. Ping @j3k0 🙏🏽]

Resolved :)

v-trishyn commented 1 year ago

@j3k0 Hello. Any advice, please?

j3k0 commented 1 year ago

I apologize, I'm in vacation (thus the delay).

Adding implementation 'com.android.billingclient:billing:6.0.1' is a mistake for 2 reasons:

  1. the plugin uses version 5
  2. npx cap sync should add that dependency to your build.gradle and the permission to the android manifest file.

Make sure you configured the licensed testers, check this doc: https://developer.android.com/google/play/billing/test

v-trishyn commented 1 year ago

I apologize, I'm in vacation (thus the delay).

Adding implementation 'com.android.billingclient:billing:6.0.1' is a mistake for 2 reasons:

  1. the plugin uses version 5
  2. npx cap sync should add that dependency to your build.gradle and the permission to the android manifest file.

Make sure you configured the licensed testers, check this doc: https://developer.android.com/google/play/billing/test

@j3k0 Thank you for the answer.

I have licensed testers.

npx cap sync did work and set the correct version of the billing client, but I still have the same output after I rebuilt the Android app and reuploaded it to Google Play Console.

2023-07-13 13:14:22.549 17624-17624/io.my.app I/Capacitor/Console: File: http://localhost/ - Line 4362 - Msg: Create CdvPurchase...
2023-07-13 13:14:23.072 17624-17624/io.my.app I/Capacitor/Console: File: http://localhost/js/app.f622b1fd.js - Line 1 - Msg: Create CdvPurchase...
2023-07-13 13:14:29.141 17624-17624/io.my.app I/Capacitor/Console: File: http://localhost/js/app.f622b1fd.js - Line 1 - Msg: [CdvPurchase] INFO: initialize()
2023-07-13 13:14:29.150 17624-17624/io.my.app I/Capacitor/Console: File: http://localhost/js/app.f622b1fd.js - Line 1 - Msg: [CdvPurchase.Adapters] INFO: Adding platforms: [{"platform":"android-playstore"}]
2023-07-13 13:14:29.151 17624-17624/io.my.app I/Capacitor/Console: File: http://localhost/js/app.f622b1fd.js - Line 1 - Msg: [CdvPurchase.Adapters] INFO: 
2023-07-13 13:14:29.151 17624-17624/io.sdcn.app I/Capacitor/Console: File: http://localhost/js/app.f622b1fd.js - Line 1 - Msg: [CdvPurchase.Adapters] INFO: GooglePlay initializing...
2023-07-13 13:14:29.151 17624-17624/io.my.app I/Capacitor/Console: File: http://localhost/js/app.f622b1fd.js - Line 1 - Msg: [CdvPurchase.GooglePlay] INFO: Initialize
2023-07-13 13:14:29.151 17624-17624/io.my.app I/Capacitor/Console: File: http://localhost/js/app.f622b1fd.js - Line 1 - Msg: [CdvPurchase.GooglePlay.Bridge] INFO: setup ok
2023-07-13 13:14:29.158 17624-17704/io.my.app D/CdvPurchase: sendToListener() -> ready
2023-07-13 13:14:29.158 17624-17704/io.my.app D/CdvPurchase:             data -> {}
2023-07-13 13:14:29.159 17624-17704/io.my.app D/CdvPurchase: init()
2023-07-13 13:14:29.168 17624-17624/io.my.app I/Capacitor/Console: File: http://localhost/js/app.f622b1fd.js - Line 1 - Msg: [CdvPurchase.GooglePlay.Bridge] INFO: listener: {"type":"ready","data":{}}
2023-07-13 13:14:29.174 17624-17704/io.my.app D/CdvPurchase: startServiceConnection()
2023-07-13 13:14:29.192 17624-17844/io.my.app D/CdvPurchase: startServiceConnection() -> Success
2023-07-13 13:14:29.192 17624-17844/io.my.app D/CdvPurchase: init() -> Success
2023-07-13 13:14:29.195 17624-17624/io.my.app I/Capacitor/Console: File: http://localhost/js/app.f622b1fd.js - Line 1 - Msg: [CdvPurchase.GooglePlay] DEBUG: Ready
2023-07-13 13:14:29.235 17624-17624/io.my.app I/Capacitor/Console: File: http://localhost/js/app.f622b1fd.js - Line 1 - Msg: [CdvPurchase.Adapters] INFO: GooglePlay initialized. 
2023-07-13 13:14:29.235 17624-17624/io.my.app I/Capacitor/Console: File: http://localhost/js/app.f622b1fd.js - Line 1 - Msg: [CdvPurchase.Adapters] INFO: GooglePlay products: []
2023-07-13 13:14:29.235 17624-17624/io.my.app I/Capacitor/Console: File: http://localhost/js/app.f622b1fd.js - Line 1 - Msg: [CdvPurchase.AdapterListener] DEBUG: setSupportedPlatforms: android-playstore
2023-07-13 13:14:29.235 17624-17624/io.my.app I/Capacitor/Console: File: http://localhost/js/app.f622b1fd.js - Line 1 - Msg: [CdvPurchase] DEBUG: Calling callback: type=ready() name=
sajjadalis commented 1 year ago

I have a similar issue with Framework7, Capacitor, Vue3 app. The PAID_SUBSCRIPTION product is not getting registered while NON_CONSUMABLE product is working fine.

async initPro() {
  const { store, ProductType, Platform } = CdvPurchase;

  this.refreshProduct();
  this.refreshSubscriptionProduct();

  // This registration works fine
  store.register({
    type: CdvPurchase.NON_CONSUMABLE,
    id: "pro",
    platform: Platform.GOOGLE_PLAY,
  });

  // Not working. "app.subscription" is ID for Subscription. "monthly-plan" is ID for base plan. 
  store.register({
    type: CdvPurchase.PAID_SUBSCRIPTION,
    id: "app.subscription.monthly-plan",
    platform: Platform.GOOGLE_PLAY,
  });

  store
    .when("pro")
    .productUpdated(this.refreshProduct)
    .approved(this.finishPurchase);

  store
    .when("app.subscription.monthly-plan")
    .productUpdated(this.refreshSubscriptionProduct)
    .approved(this.finishSubscriptionPurchase);

  this.products = store.products;

  store.error(error => {
    console.log("ERROR " + error.code + ": " + error.message);
  });

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

orderPro() {
  if (this.product && this.product.canPurchase) {
    await this.product.getOffer().order();
  }
},

orderSubscription() {
  const { store } = CdvPurchase;
  await store.order("app.subscription.monthly-plan");
}

orderSubscription returns this error. Store Error 6777003: Product not registered: null.

And this.products array returns only a single NON_CONSUMABLE product. Why subscription product is not getting registered? I would appreciate any help. Thanks

chuchuva commented 12 months ago

@v-trishyn Double-check what you pass to the store.register method. The platform field for at least some products should be CdvPurchase.Platform.GOOGLE_PLAY, for example:

    const ProductType = CdvPurchase.ProductType;
    const Platform = CdvPurchase.Platform;

    store.register([{
        id: 'subscription1',
        type: ProductType.PAID_SUBSCRIPTION,
        platform: Platform.APPLE_APPSTORE,
    }, {
        id: 'subscription1',
        type: ProductType.PAID_SUBSCRIPTION,
        platform: Platform.GOOGLE_PLAY,
    }, {
        id: 'consumable1',
        type: ProductType.CONSUMABLE,
        platform: Platform.BRAINTREE,
    }]);
idesignzone commented 11 months ago

For PAID_SUBSCRIPTION you should register like:

  store.register({
    type: ProductType.PAID_SUBSCRIPTION,
    id: "subscription_product_id",
    platform: Platform.GOOGLE_PLAY,
  });

"CdvPurchase" namespace does not work for PAID_SUBSCRIPTION type. No idea why!

MarcelSchuermann commented 9 months ago

For PAID_SUBSCRIPTION you should register like:

  store.register({
    type: ProductType.PAID_SUBSCRIPTION,
    id: "subscription_product_id",
    platform: Platform.GOOGLE_PLAY,
  });

"CdvPurchase" namespace does not work for PAID_SUBSCRIPTION type. No idea why!

ProductType.PAID_SUBSCRIPTION does not work for me. I get following error then: custom.js:19560 Uncaught ReferenceError: ProductType is not defined

If I use window.CdvPurchase.ProductType.PAID_SUBSCRIPTION instead, I do not get the error but then I get IAP error code:6777003 Product not registered: null

I use following code to register:

window.CdvPurchase.store.register([{
                id:    inAppPurch_PRO,
                type:   window.CdvPurchase.ProductType.PAID_SUBSCRIPTION,
                platform: devicePlatformCdvPurchase
            }]);

id is refering the the product id (it worked on earlier versions <13) devicePlatformCdvPurchase is refering to window.CdvPurchase.Platform.GOOGLE_PLAY for Android.

And following code to order() the subscription:

const offer1 = product1.offers[0].id;
window.CdvPurchase.store.order(offer1)

The store object is available with the right product and the right offer (subscription).

Any ideas?

PS. I also migrated to the iapic v3 API according to this migration documentations: https://www.iaptic.com/documentation/information-fovea-billing https://www.iaptic.com/documentation/setup/cordova

MarcelSchuermann commented 9 months ago

any ideas @j3k0 ? Any help is highly appreciated.

idesignzone commented 9 months ago

@MarcelSchuermann are you defining the ProductType? you should define it like

import "cordova-plugin-purchase/www/store";
const { store, ProductType, Platform } = CdvPurchase;

Here is a working composable I use

https://gist.github.com/idesignzone/962746e23f967286f0608f0b7cc276c2

MarcelSchuermann commented 9 months ago

@idesignzone Thanks for the input.

Yes, exactly like you mentioned (without the import command as I am using vanilla .js). const { store, ProductType, Platform } = CdvPurchase;

I am also using the Iaptic backend like this:

const iaptic = new CdvPurchase.Iaptic({
            appName: "com.xxx.xxx",
            apiKey: "xxx",
        });
store.validator = iaptic.validator;

Any other ideas?

MarcelSchuermann commented 9 months ago

Nice, it worked - I looked at your example @idesignzone thx a lot!

I changed: store.order(IAPProduct1.offers[0].id) To: iapProduct.getOffer().order();

I think that was the main change. I also improved the store.get() function parameters according to your example.