Open vanessag opened 2 years ago
Update: I was able to reproduce the issue on a development environment by creating a new iOS sandbox tester and then click 'Restore Purchases' button and login with the new iOS sandbox tester. It seems if the user has not previously made any purchases on the account that '.finished' is never triggered.
@vanessag Hey man! have u found any solution? It seems I'm having the same issue.
@bhaskar-se No I have no found any solution to this, unfortunately. I think this a bug with the plugin.
Maybe @j3k0 can shed some light on if this is a bug.
Hi guys, do you have any workaround for this issue? I'm having the same issue for IOS on Android it is working fine.
Still waiting for a proper fix for this too. Note that this is a duplicate of https://github.com/j3k0/cordova-plugin-purchase/issues/1050
If I'm correct, on iOS, refresh.finished()
will be called when the application receipt is validated. If there is no purchase, no validation take place, BUT the plugin will validate the application receipt to get the status of purchases (the pseudo-product which type is 'application' on iOS).
In case there's no validator setup, it'll use the old way of refreshing purchases provided by the StoreKit SDK, which is to replay all transactions. However, this method has no real "DONE" event, to refresh.finished()
might not trigger (I need to check).
A simple fix should be to make sure you validate application receipts in your application.
Thanks for the answer. Not sure I fully understand it though.
updated
events with event.alias === 'application'
, is that what you refer to? I might have missed it but I don't recall reading about that in the documentation.My current workaround is to trigger my finished callback when enough
Edit: this workaround doesn't actually work as expected but I found a better one, see my next messages.
My full code is below.
Thank you!loaded
events have been fired.
// a global variable created by the cordova plugin
this.purchases.store = window.store
this.purchases.store.validator = config.storeValidator
// 'product' matches both products and subscriptions.
this.purchases.store.when('product')
// approved and verified are the only really required listeners as they handle
// subscription
.approved(product => {
product.verify()
})
.verified(product => {
product.finish()
})
// `updated` is called several times for the same
// product at initialization, once for every step of the life cycle
// https://github.com/j3k0/cordova-plugin-purchase/blob/master/doc/api.md#life-cycle),
// and we can't trust its data as only the last `updated` event we get will
// have the right data. So we only take into account the `updated` events
// that might happen after initialization.
.updated(product => {
if(product.state !== 'registered' && product.alias !== 'application') {
this.purchasesProductUpdated(product)
}
})
// `loaded` happens when we have the product info, but it doesn't tell us
// if the user owns it yet (it seems that its `state` and `owned` properties are
// always 'valid' and `false`). We use the `finished` event
// of the store refresh instead further below, which does happen once we have
// what we need (currently not working on iOS though)
.loaded(product => {
// iOS bug fix, since `finished` is never triggered on iOS, we need to trigger
// it ourselves once we got this number of products
if (this.cordova.device.platform === 'iOS') {
loadedProductsCount++
if (loadedProductsCount === Object.keys(config.offer.product).length) {
setTimeout(() => this.purchasesRefreshFinished(), 500)
}
}
})
this.purchases.store.error(error => {
/*
switch (error.code) {
case 6777002: // server unreachable. will try to reach again in a few moments
case 3777003: // ITEM_ALREADY_PURCHASED
}
*/
this.syslog('warning', new Error('APPLICATION STORE ERROR ' + error.code + ': ' + error.message))
})
// register products/subscriptions
const products = new Array
Object.entries(config.offer.product).forEach(entry => {
products.push({
id: entry[1].productId,
type: this.purchases.store[entry[1].productType],
alias: entry[0]
})
})
this.purchases.store.register(products)
const refresh = this.purchases.store.refresh()
// `failed` seems useless as the lib will make new attempts later and errors
// are caught by our error callback anyway
// refresh.failed(() => {})
// never called on iOS after initial load
refresh.finished(this.purchasesRefreshFinished)
Update: interestingly, when clicking the Refresh Purchase button for the first time, the finished
event is actually fired, but twice!
And if I click on it again, the next times it's fired only once.
So I guess the initial finished
event of the initial refresh that we all want in this thread is somehow "queued" and is fired only when a second refresh happens.
It would be great if you could take a look. Thank you
Lol, thanks to this discovery, I stopped using the loaded
event as my workaround (it didn't fully work anyway) and I wrote a new workaround which actually works now: refresh twice in a row at start, and it actually has the expected result: the finished
event is triggered once, and subsequent calls to refresh()
also trigger a single finished
event.
const refresh = store.refresh()
if (cordova.device.platform === 'iOS') {
store.refresh()
}
Jeez, maybe Apple will publish my app now :/
The second call to "refresh" might prompt for the login to the app store which is not a nice startup experience. As I understand the second "refresh" will automatically try to restore previous purchases.
System Info
Expected behavior
I expect store.refresh().finished to be triggered properly.
Observed behavior
I have a 'Restore Purchases' button. When the button is pressed I display a loading indicator and then trigger a
store.refresh()
. I then listen for the .finished event to hide my loading indicator. However, in a version released on the App Store the .finished event never gets triggered and loading indicator gets stuck. Everything works fine in Test Flight but not on the App Store released version so this is very hard to debug. Any help would be very much appreciated!Relevant code: