IAP iOS some user fail to trigger onSuccess listener but get charged

Hi,

I received a lot feedback from user that they cant complete the non-consumable IAP but get charged on their payment method.

I cant reproduce the issue as tested on sandbox / download from AppStore.
Sometime it just has slightly delay but in the end still success trigger the listener.

There are still users that can complete the IAP without problem.

I try to suggest them with several way but non can solve the problem

  1. restart device
  2. restore purchase
  3. repurchase IAP
  4. remove app and reinstall

Before repurchase IAP, i have ask them to make sure have a stable and good connection.
So i have no clue on how to solve the issue, please help.

Thanks !

Project setup
Xcode 10.1
Cocos2d-JS 3.17

SDKBOX
v2.4.1.1 onesignal
v2.4.1.1 firebase
v2.4.1.1 iap
v2.4.1.1 googleplayservices

Device model : iPhone SE to iPhone XR.
iOS : 9 to 12.1

Current code init sequence as i read from other post

sdkbox.IAP.setListener({
			onSuccess : function (product) 
			{
				//Purchase success
			},
			onFailure : function (product, msg)
			{
				//Purchase failed
			},
			onCanceled : function (product)
			{
				//Purchase was canceled by user
			},
			onRestored : function (product)
			{
				//Purchase restored
			},
			onProductRequestSuccess : function (products) 
			{
				//Returns you the data for all the iap products
			},
			onProductRequestFailure : function (msg)
			{
				//When product refresh request fails.
			}
		});

sdkbox.IAP.init();
sdkbox.IAP.enableUserSideVerification(false);

sdkbox.PluginOneSignal.init();
sdkbox.firebase.Analytics.init();

did the user got crash or other something unusual, when purchasing

they didn’t mention about this, but they cant restore the purchase even after re-download.

what info do you need from the user? i try if i can get from them.

thanks.

the product is non-consume?

yes
“type”: “non_consumable”

so, the user purchase non-consumeable item, didn’t get success callback, but get charged on their payment, and they can’t restore by any way.

this is strange.

seem like APPLE take this purchase as failed.

yes, exactly the same issue reported by users.

i try get more details whether they can trigger the failed listener.

@htlxyz

I get some info from user.

The IAP purchase issue solved by contacting Apple Customer Service.

Apple verified the transaction exist but something go wrong on the system
Payment has been charged but status remain in "Pending"
After Apple canceled the transaction, user success complete the IAP

So this mean no problem on my code and SDKBOX IAP plugin?

Is there anything we can do to reduce / prevent this issue?
Does any developer facing the same issue on their app too?

Thanks

Hi @Zinitter did you solve the problem? i just release a game a week ago and a tiny percent of the buyers are telling me the same. I tell them to do the same you did but nothing works. There is a contradiction because if they try to restore the purchase, a message box appears telling them that there is nothing to restore. Also, If they try to buy the game again a messageBox tells them that the IAP was already buyed and ask if they want to buy it again for free. When the user accept this messageBox. The game stuck waiting for the onSuccess callback.

I still cant solve the problem.

My Apple id was stuck with the same issue with redeemed promo code and i contacted Apple Support, they cant help me clear the transaction.

But using another Apple id to purchase on same device do not has the issue.

You can ask your user to request refund and make the purchase with new / another Apple id.

Sorry for my bad english, this means that if Apple refund the purchase to the user and then, if he purchase again the problem isn´t fixed? He need a new Apple ID? Also promo code doesn´t work? That would be bad news;(

I found this here: (In the next comment he posted he claims that this solved his problem)

I have the same problem as described by @Seoptics with an app which is in production. Some users report to have paid for the premium feature, but not having these features unlocked. They can show us screenshot of the payment receipt in iTunes, but we don’t have any track of it in our system. I set up a lot of bug reporting regarding in-app purchase functions in my app, and everything seems to point to the fact that sometimes the buy function does not return any promise.

This bug is quite stressful as users can be very upset when they pay and don’t have their premium content unlocked. We received 5 complaints last week, for 40 sells, so this bug happens quite often !

As a workaround, I got inspired by @Seoptics’s idea. I validate the purchase in case I don’t have any news from iTunes, but unvalidate it in case I have an error a long time after that. This is necessary for instance if the user take a long time to type their iTunes password and decide to cancel the purchase. This way they could have their premium feature activated without any payment.

A simplified version of the code look like this :

`
solidBuy(productId:string):Promise< string >{

return new Promise((resolve,reject) => {

    let order =  this.inAppPurchase.buy(productId);

    order.then((data)=> {
            clearTimeout(this.orderTimeout);
            this.activateProduct(productId);
            resolve("Purchase complete");
    })
    .catch((err)=> {
            clearTimeout(this.orderTimeout);
            this.deActivateProduct(productId); // in case it would have been activated by the timeout below
            reject(err.errorMessage);
    });

    this.orderTimeout = setTimeout(() => {
        this.activateProduct(productId);
        resolve("Purchase complete (maybe)");
    },20000);

});
}

As from my user, the issue is fixed after contact Apple to refund and purchase again.
Promo code is working, its just so lucky that my Apple id get stuck with the same issue.

New Apple id is just my suggestion as temporarily solution since i cant fixed the issue.

So your workaround code is working?
I using Cocos2d-JS and need convert it to JS version.

No, I just found that post 2h ago. I´m using cocos2dx and I´m not a good developer (i learn by myself). Seems like he put a timer but i don´t know where and how we could integrate in sdkbox.

May be on the IAP listener or try ask SDKBox support.

@yinjimmy, can you please help me on this issue?

Recently i receive a lot users feedback and complain about this issue, they stuck on the IAP but get charged.

I ask them to contact Apple Support and sometime they refuse to help or decline the request of refund so user cant repurchase the IAP.

SDKBOX version that i using and have the issue
v2.4.1.1 onesignal
v2.4.1.1 firebase
v2.4.1.1 iap
v2.4.1.1 googleplayservices

 v2.4.3.3 onesignal
 v2.4.3.3 firebase
 v2.4.3.3 iap
 v2.4.3.3 googleplayservices

thanks.

let me check the IAP flow again.

Another IMPORTANT thing is: make sure IAP::init() is the first SDK initialization.

Did you use these function?
sdkbox::IAP::enableUserSideVerification(bool);
sdkbox::IAP::setAutoFinishTransaction(bool);
In my testing these function are buggy, use the default value will get less problem.

May be you can take a look in my question too.

yes, i do use

sdkbox::IAP::enableUserSideVerification(false);

but didnt use this

sdkbox::IAP::setAutoFinishTransaction(bool);

my iap is non-consumable, haven’t use subscriptions type yet

My current sdkbox init sequence

sdkbox.IAP.init();
sdkbox.IAP.enableUserSideVerification(false);

sdkbox.PluginOneSignal.init();

sdkbox.firebase.Analytics.init();

i suspect the issue may related to firebase analytics as before this, i was using google analytics and the issue less chance to happen.

Not sure is it about the internet connection / delay on the response of IAP and firebase after init.

My Apple id that get stuck with the same issue happen when i init firebase first over IAP, but the issue never happen on sandbox mode.

sdkbox.firebase.Analytics.init();
sdkbox.IAP.init();

if you still cant reproduce it, consider that some users may try to scam you.
(theyve received the items and got charged but want you to undo the payment so they didnt pay for it)