Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

type(fix): fixes in-app-purchases-2 store.ready not getting fired #2043

Merged
merged 1 commit into from
May 20, 2018

Conversation

bshafiee
Copy link
Contributor

Fixed the interface which should have been a callback not a promise (resulting in promise never gets resolved).

@bshafiee
Copy link
Contributor Author

why nobody looks at this PR?

@joelbrewer
Copy link

Has this issue been addressed?

@bshafiee
Copy link
Contributor Author

nope, as you can see the PR is still open and no one bothers merging it!

@ihadeed
Copy link
Collaborator

ihadeed commented Nov 20, 2017

A promise should work fine. The @Cordova decorator will pass the resolve callback of the promise to the plugin as a callback parameter. It should work as if you passed an actual function.

Can you confirm that this works:

import { InAppPurchase2 } from '@ionic-native/in-app-purchase2';

InAppPurchase2.getPlugin().ready(() => console.log('Callback called'));

and this doesn't:

import { InAppPurchase2 } from '@ionic-native/in-app-purchase2';
...
constructor(private iap: InAppPurchase2) {}
...
this.iap.ready()
  .then(() => console.log('Ready resolved'));

@bshafiee
Copy link
Contributor Author

@ihadeed Nope, it doesn't work. I did try your sample and it doesn't work. That's why I opened this PR. Also unlike other ionic-native wrappers this one misses a check like if (platform.is('cordova')) (but a separate issue)

@ihadeed
Copy link
Collaborator

ihadeed commented Nov 21, 2017

@bshafiee
Please note that I just edited the code samples. I misplaced one line of code.

If both of the code samples I provided don't work, then the issue is not from the wrapper.

@ihadeed
Copy link
Collaborator

ihadeed commented Nov 21, 2017

Make sure you're waiting for deviceready to fire (via an event listener, or use Platform.ready) .. if you call the ready method of the plugin before the plugin is fully loaded, then the callback will not be registered.

Example:

import { InAppPurchase2 } from '@ionic-native/in-app-purchase2';
import { Platform } from 'ionic-framework';

class MyComponentOrService {

  constructor(private iap: InAppPurchase2, private platform: Platform) {}

  async someMethod() {
    await this.platform.ready();
    await this.iap.ready();
    console.log('Ready resolved')
  }
}

@bshafiee
Copy link
Contributor Author

bshafiee commented Nov 22, 2017

InAppPurchase2.getPlugin().ready(... works. Not sure what getPlugin does but still if you call it on a instance like iap.ready() it won't work. This is quite confusing and needs to be fixed IMHO. At the very least the documentation needs to point this out.

@ihadeed
Copy link
Collaborator

ihadeed commented Nov 22, 2017

  • InAppPurchase2.getPlugin() gets the original plugin object without wrapping it with Ionic Native.
  • Did you try this.iap.ready() AFTER deviceready event has fired? (or this.platform.ready() resolved)

@bshafiee
Copy link
Contributor Author

  • Makes sense, that's why it works

  • Yes, AFTER deviceready!

@blueromans
Copy link

`[Error] ERROR – Error: Uncaught (in promise): function (cb, altCb) {

var ret = cb;

if (cb instanceof store.Error) {
    store.error.callbacks.trigger(cb);
}
else if (typeof cb === "function") {
    store.error.callbacks.push(cb);
}

/// ### alternative usage
///
///  - `store.error(code, callback)`
///    - only call the callback for errors with the given error code.
///    - **example**: `store.error(store.ERR_SETUP, function() { ... });`
else if (typeof altCb === "function") {
    ret = function(err) {
        if (err.code === cb)
            altCb();
    };
    store.error(ret);
}
else if (cb.code && cb.message) {
    store.error.callbacks.trigger(new store.Error(cb));
}
else if (cb.code) {
    // error message is null(unknown error)
    store.error.callbacks.trigger(new store.Error(cb));
}
///

return ret;
} — polyfills.js:2` ı m getting this error when ı call store.ready arter device.ready

@tobika
Copy link
Contributor

tobika commented Dec 5, 2017

Nice, didn't know there was an PR already. Just to link to the ionic forum discussion.
https://forum.ionicframework.com/t/in-app-purchase-android-ios/103346/9

If InAppPurchase2.getPlugin() works it should be at least documented somewhere. Will check also tomorrow.

@tobika
Copy link
Contributor

tobika commented Dec 8, 2017

Looking at the code I guess I found out why ready() doesn't work as a promise.

Usually you add the @Cordova decorator in front of a function with callback so it's automatically turned into a promised version. @Cordova expects (succesCallback, errorCallback). But the original ready function of the plugin only accepts one callback function that returns a boolean with status true or false.

So promisifying the function doesn't work in this way. Any idea how to encapsulate it correctly?

@ihadeed
Copy link
Collaborator

ihadeed commented Dec 8, 2017

@tobika passing extra parameters to a function doesn't hurt. If the original ready function is only expecting one param, then the second one will be omitted.

@tobika
Copy link
Contributor

tobika commented Dec 11, 2017

@ihadeed thanks for this clarification.
Nonetheless it's a fact that the plugin doesn't work in the current state if you try to use a promise like explained in the documentation. For me everything works great once I change it to a callback style. Even without .getPlugin().

When I try it with a promise I have the same error as @blueromans

It's a pitty that people who try to use this plugin but don't know that they can change the typing of the lib won't be able to use it.

Unfortunately I don't know how to fix the promis problem so maybe for the time being it would be better to accept this PR and change the documentation?

@NodeKing
Copy link

NodeKing commented Dec 22, 2017

Has anyone managed to get this working? I'm experiencing exactly the same issue as @blueromans . I'm unsure how to implement the code fix.

@tobika
Copy link
Contributor

tobika commented Dec 22, 2017

@NodeKing as a workaround open the file node_modules/@ionic-native/in-app-purchase-2/index.d.ts
and change Line 270 (should be) to:
ready(callback: Function): any;

Then you can use the this.iap.ready(() => {}) function with a callback

Update:
Here is even a nicer writeup how to workaround the problem :)
#1869 (comment)

@NodeKing
Copy link

Thanks @tobika . I implemented the changes, but am still experiencing the same issue. I ended bypassing ionic native and just using cordova. It seems to be working but one thing I noticed is that the store.ready() method isn't fired until store.refresh() is called, so this could be my underlying issue.

@bshafiee
Copy link
Contributor Author

bshafiee commented Feb 11, 2018

@ihadeed this is causing everyone trouble and I really don't understand why the PR is not getting merged. Sure, not the most elegant solution but it sure does work. Please either provide a proper fix or merge the PR.

@diegodem
Copy link

@bshafiee What changes should I make to my project in order for this to work? @tobika 's solution did not work for me, still the event ready is never getting fired.

@tobika
Copy link
Contributor

tobika commented Feb 20, 2018

@diegodem did you call this.iap.refresh();
I think @NodeKing is right when he said that ready is not fired until this.iap.refresh(); is called.

@diegodem
Copy link

@tobika When I call this.iap.refresh(), either before or after this.iap.ready(), I get an error with code 6777001 and message "Init failed - String resource ID #0x0"

@tobika
Copy link
Contributor

tobika commented Feb 20, 2018

@diegodem I think you should check in the repo of the plugin and create an issue there https://github.com/j3k0/cordova-plugin-purchase

@ihadeed any news on if this PR will get merged or not? If you don't have the time maybe ask someone else or give merge permissions to someone else? or if there is a problem with the PR itself I'm sure we can think of another solution but we need some feedback to do this.

@diegodem
Copy link

@tobika I did create an issue already, but nobody answered

@bshafiee
Copy link
Contributor Author

bshafiee commented Feb 20, 2018 via email

@diegodem
Copy link

@bshafiee I don't find the file src/@ionic-native/plugins/in-app-purchase-2/index.ts in my ionic project. Where should it be?

@bshafiee
Copy link
Contributor Author

bshafiee commented Feb 20, 2018 via email

@steve-allan
Copy link

From Tobika's comment above
as a workaround open the file node_modules/@ionic-native/in-app-purchase-2/index.d.ts
and change Line 270 (should be) to:
ready(callback: Function): any;

Then you can use the this.iap.ready(() => {}) function with a callback

This just caused an error which building the app as I mentioned in the post just above.
I either get errors or the refresh method simply does nothing.

@aabdolla
Copy link

aabdolla commented Mar 8, 2018

So you tried the callback method, that replaces the promise and no dice? I will try it too and let you know if I get the error. Did you try using the .getPlugin()?

@aabdolla
Copy link

aabdolla commented Mar 8, 2018

So I didn't get an error. It just doesn't work still. Same behavior as before.

Update: So I wait until the platform is ready, call the configuration to set the handlers and the store ready is still never called with the changes @tobika suggested. Is there something else I am missing?

@steve-allan
Copy link

This is using the .getPlugin() method?

Does anyone know of any IAP modules for Angular 4 that do work? I really like Angular but frustrations like this make me consider switching.

@aabdolla
Copy link

aabdolla commented Mar 8, 2018

Oh to me it seemed like you did this.iap.ready after the index.d.ts change or InAppPurchase2.getPlugin().ready.

@aabdolla
Copy link

aabdolla commented Mar 8, 2018

Well for some reason nothing in here has worked for me. My store.ready never gets called and my handlers for the store just loop forever. I tired using the .getPlugin() and also modified the index.d.ts with the suggestions making it a callback instead of promise.

@steve-allan If you switch to something that works or find a solution to this, please let me know if you remember :)

@NodeKing
Copy link

NodeKing commented Mar 9, 2018

For those of you still having issues, just install the ionic plugin package and don't make the ionic-native package changes as making them is only a temporary fix and will be overwritten every time you rebuild your node_modules. Better off waiting for the fix to be implemented. While your waiting, use the following work around. Only ran basic testing on ios that worked fine. Works perfectly on Android (using in production). Code below

Install the plugin as normal via the ionic docs, then use the following code.

`
declare var store: any;

export class IapService {

// Type definition for anyone interested
// type Product = {
// additionalData?: any,
// alias: string,
// canPurchase: boolean,
// currency: string,
// description: string,
// downloaded: boolean,
// downloading: boolean,
// id: string,
// loaded: boolean,
// owned: boolean,
// price: string,
// priceMicros: number,
// state: string,
// title: string,
// transaction?: any,
// type: string,
// valid: boolean,
// }

productArr: Array = [];

constructor(public platform: Platform) {
platform.ready().then(
() => {
if (store) {
this.initProducts();
}
}
)

}

initProducts() {

var products: any = {}

products['productId'] = {
id: 'productId',
alias: "Product Name",
type: store.NON_CONSUMABLE
}

store.error(
  (err) => {
    // do stuff
  })

store.when("yourProductIdorAlias").approved((order) => {
  // Product has been purchased.
  this.updateProducts();
  order.finish();
});

store.when("yourProductIdorAlias").updated((data) => {
  this.updateProducts();
});

store.when("all_features").owned((data) => {
  // Product owned
  this.updateProducts();
});

store.when("all_features").refunded((data) => {
  // Do stuff. I don't think refunds work properly tho :(
  this.updateProducts();


});

store.when("all_features").cancelled((data) => {
  // Do stuff
  this.updateProducts();
});

///////////////////////////////////////////////////////////////

store.register(products.productId);
this.initStore();

}

initStore() {
// store.verbosity = store.DEBUG;
store.ready(() => {
store.get("yourProductIdorAlias");
this.updateProducts();
});
store.refresh();
}

updateProducts() {
if (store) {
if (store.hasOwnProperty('products')) {
// update your own object to track product status.
// If you want to do it that way, or just use store.products directly.
// e.g.
this.productArr = store.products;
}
}
}
}`

Note you will need a 'Restore Purchases' button feature on ios. Just call a store.refresh method followed by an updateProducts(). Android should restore purchases itself with the above code.

@steve-allan
Copy link

Thank you NodeKing,
I'll try this over the weekend.

@aabdolla
Copy link

aabdolla commented Mar 9, 2018

@NodeKing Thanks for swinging by and providing help! What exactly is it you are doing? You are not using the InAppPurchase2 module? You are using your own store? Sorry for the noobish question.

@NodeKing
Copy link

NodeKing commented Mar 9, 2018

@aabdolla Still using the InAppPurchase2 module, just bypassing the problem ionic native code. You can use any cordova plugin directly, even if ionic doesn't provide it. You just need to implement it differently.

I provided a full working example. You can use the whole lot, or just take what you need.

@aabdolla
Copy link

aabdolla commented Mar 9, 2018

@NodeKing Ah okay I see. Can my purchase code can still be the same using the store directly?

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

    let productId;

    if (this.platform.is('ios')) {
      productId = this.product.appleProductId;
    } else if (this.platform.is('android')) {
      productId = this.product.googleProductId;
    }

	try {
		console.log(productId);
		let product = store.get(productId);

		let order = await store.order(productId);
	} catch (err) {
		console.log('Error Ordering ' + JSON.stringify(err));
	}
}

With how it is currently I am getting this error:

Error Ordering {"line":441,"column":28,"sourceURL":"http://localhost:8080/var/containers/Bundle/Application/A66850AB-2460-4B28-BB0A-D46E6752D5A7/MyApp.app/www/plugins/cc.fovea.cordova.purchase/www/store-ios.js"}

@aabdolla
Copy link

aabdolla commented Mar 9, 2018

@NodeKing You are amazing! I am on to my next round of issues with purchasing in app purchases but this is no longer a hang up. Thank you so much! That bug above was an easy fix. You had "products.productId" and I just had to change it to "products[productId".

What is the benefit of updateProducts()? Like keeping our own version of the products array?

@NodeKing
Copy link

NodeKing commented Mar 9, 2018

@aabdolla You're welcome. Apologies for the typo, I had to strip out most of my code to simplify for the example. RE: Products array: Yes, I just like storing the data in my own object to be accessed the same as the rest of my data.

@aabdolla
Copy link

@NodeKing Oh okay cool. Yeah, it is working great for me. I just need to put the correct logic now in them. It's all new to me but at least I know the base of the purchasing is going to work.

So is there a point to ever switching back to the plugin now that you enabled us to do it ourselves?

@aabdolla
Copy link

My last hiccup now is my restore button. Using this code it looks like it never completes as the console log and alert at the end is never fired and I get this error:

Error Ordering {"line":699,"column":96,"sourceURL":"http://localhost:8080/var/containers/Bundle/Application/49B5DA21-D504-46C3-90D4-DD550A1ACBFF/MyApp.app/www/build/main.js"}

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

	this.restoreClicked = true;

	let productId;

	if (this.platform.is('ios')) {
		productId = this.product.appleProductId;
	} else if (this.platform.is('android')) {
		productId = this.product.googleProductId;
	}

	console.log('Products: ' + JSON.stringify(store.products));

	console.log('Refreshing Store: ' + productId);

	try {
		let product = store.get(productId);

		console.log('Product Info: ' + JSON.stringify(product));

		store.refresh();

		console.log("refreshed");
		this.restoreClicked = false;

		console.log("refresh " + productId + " owned: " + store.products[productId].owned);

		alert('Finished Restore');
	} catch (err) {
		console.log('Error Ordering ' + JSON.stringify(err));

		this.removeAdsClicked = false;
		this.restoreClicked = false;
	}
}

Any thoughts @NodeKing? Sorry to ping you but you have been the man with the answers.

@NodeKing
Copy link

@aabdolla I don't have any IOS apps, so I haven't got any working code I can share. I was playing around with IOS and had it working tho. I believe I just had a button click() event linked to a refresh() method. The button would only be show on IOS (not required on android). All the refresh method contained was store.refresh() from memory.

You won't need to do anything anything with store.get() etc, as this should occur at startup/initialization in the above code.

Maybe double check you have done everything correctly via the cordova documentation
https://github.com/j3k0/cordova-plugin-purchase/wiki/HOWTO

@aabdolla
Copy link

@NodeKing Oh okay, not necessary for Android, good to know. Thanks for checking it. I will double check the cordova documentation again. Thanks!

@aabdolla
Copy link

On iOS for some reason during the purchase and refresh I get prompted multiple times to authenticate. During purchase, I have to put in my details three times. Same for restore. I am using the handlers above that @NodeKing provided. My purchase function looks like this:

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

	let productId;

    if (this.platform.is('ios')) {
      productId = this.product.appleProductId;
    } else if (this.platform.is('android')) {
      productId = this.product.googleProductId;
    }

	try {
		console.log("purchase: " + productId);

		let order = await store.order(productId);
	} catch (err) {
		console.log('Error Ordering ' + JSON.stringify(err));
	}
}

@steve-allan Did you get around to implementing the recommendation?

@danielsogl danielsogl added this to the 5.0.0 milestone Mar 19, 2018
@paulstelzer
Copy link
Contributor

@danielsogl could you please merge this pull request? It's not nice to make this change every time because the current implementation is not working

@ihadeed ihadeed merged commit a018381 into danielsogl:master May 20, 2018
@himalay-chauhan
Copy link

On iOS for some reason during the purchase and refresh I get prompted multiple times to authenticate. During purchase, I have to put in my details three times. Same for restore. I am using the handlers above that @NodeKing provided. My purchase function looks like this:

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

	let productId;

    if (this.platform.is('ios')) {
      productId = this.product.appleProductId;
    } else if (this.platform.is('android')) {
      productId = this.product.googleProductId;
    }

	try {
		console.log("purchase: " + productId);

		let order = await store.order(productId);
	} catch (err) {
		console.log('Error Ordering ' + JSON.stringify(err));
	}
}

@steve-allan Did you get around to implementing the recommendation?

I'm receiving Update callback after product is registered saying Product is registered. But I'm not getting any response after store.order(productId). Just nothing happens. No error, nothing. Do you have any idea what could be the issue here? I tried the same async code block you have provided here. I would be very much grateful. I'm stuck at this since a week.

Thanks in advance.

@himalay-chauhan
Copy link

@aabdolla Still using the InAppPurchase2 module, just bypassing the problem ionic native code. You can use any cordova plugin directly, even if ionic doesn't provide it. You just need to implement it differently.

I provided a full working example. You can use the whole lot, or just take what you need.

Hi @NodeKing
First of many thanks for your example. I'm receiving registered callback when product is registered but I'm not receiving any update callback or error after "store.order(productId);" line.
I've tried the same source provided by @aabdolla . Please see my above comment. Can you please help to identify the possible cause?

Thank you!

@CraigRBI
Copy link

@aabdolla Still using the InAppPurchase2 module, just bypassing the problem ionic native code. You can use any cordova plugin directly, even if ionic doesn't provide it. You just need to implement it differently.
I provided a full working example. You can use the whole lot, or just take what you need.

Hi @NodeKing
First of many thanks for your example. I'm receiving registered callback when product is registered but I'm not receiving any update callback or error after "store.order(productId);" line.
I've tried the same source provided by @aabdolla . Please see my above comment. Can you please help to identify the possible cause?

Thank you!

@himalay-chauhan - did you get to the bottom of your problem?

@himalay-chauhan
Copy link

@aabdolla Still using the InAppPurchase2 module, just bypassing the problem ionic native code. You can use any cordova plugin directly, even if ionic doesn't provide it. You just need to implement it differently.
I provided a full working example. You can use the whole lot, or just take what you need.

Hi @NodeKing
First of many thanks for your example. I'm receiving registered callback when product is registered but I'm not receiving any update callback or error after "store.order(productId);" line.
I've tried the same source provided by @aabdolla . Please see my above comment. Can you please help to identify the possible cause?
Thank you!

@himalay-chauhan - did you get to the bottom of your problem?

@CraigRBI yes, it's working fine for me now. Thank you for the response, little late though :D

@mayankkataria
Copy link

In my app it was working earlier but not working now. Has anybody found the solution for it?

@mayankkataria
Copy link

My all methods of store were not working. Uninstalling and re-installing app worked for me.

1 similar comment
@mayankkataria
Copy link

My all methods of store were not working. Uninstalling and re-installing app worked for me.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.