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

iOS in-app purchase failure when payment method added live when there is not payment method already present #307

Closed
anandwahed opened this issue Oct 31, 2018 · 27 comments
Labels
🙏 help wanted Extra attention is needed 📱 iOS Related to iOS

Comments

@anandwahed
Copy link

anandwahed commented Oct 31, 2018

Version of react-native-iap

react-native-iap": "^2.3.17

Platforms you faced the error (IOS or Android or both?)

iOS

Expected behavior

Payment should go through only when finishTransaction is being called

Actual behavior

The amount is being detected after a payment method like credit card is added.
But the RNIap.buyProductWithoutFinishTransaction(sku) method reached.

Tested environment (Emulator? Real Device?)

Real Device - iPhone 6s

Steps to reproduce the behavior

  • Check to ensure the Apple account in the device the payments method is set to none or some invalid payment details
  • Make the in-app purchase through the app
  • User is taken to the Account settings page to add a valid payment method
  • Payment method is charged but the transaction receipt is not received in the below mentioned code
ComponentDidMount(){
     await RNIap.initConnection();
     await RNIap.consumeAllItems();
     const prod = await RNIap.getProducts(product);

} 

  async componentWillUnmount() {
       RNIap.endConnection()
}

buyProduct(sku){
await RNIap.clearTransaction();

RNIap.buyProductWithoutFinishTransaction(sku)
.then(purchase => {
  // not reached
 if(calltoserverisSuccess){
    RNIap.finishTransaction();
 }
})
.catch(error => {
 // code enters catch case if ever
}}
@hyochan hyochan added 🙏 help wanted Extra attention is needed 📱 iOS Related to iOS labels Oct 31, 2018
@hyochan
Copy link
Member

hyochan commented Oct 31, 2018

Sorry to say this but your code seem to have many syntax error. Please refer to our example project in our repo and compare with your code first.

@hyochan hyochan closed this as completed Oct 31, 2018
@zohaibahmed-22
Copy link

@dooboolab
Thanks for the reply, the code that is posted over here is just for an example, this is not the exact that is being used in the real application.
i will try to explain what is the issue

  1. componentDidMount

    • connection in initiated by calling RNIap.initConnection();
    • then products are fetched and store in state by calling RNIap.getProducts(product)
  2. when user click on purchase button

    • RNIap.clearTransaction(); is called just to make sure no pending transactions left.
    • RNIap.buyProductWithoutFinishTransaction(sku) is called in success case a call to application server is triggered if server gives success case true then RNIap.finishTransaction(); is called to complete the payment.
  3. componentWillUnmount()

    • RNIap.endConnection() to end connection.

This process works fine if user have already payment method added. When user don't have a payment method added it takes to adding payment method page and amount is charged before reaching the RNIap.finishTransaction();

@hyochan
Copy link
Member

hyochan commented Nov 1, 2018

Thanks for the detail. Your issue looks clear now. cc @JJMoon

@hyochan hyochan reopened this Nov 1, 2018
@anandwahed
Copy link
Author

@JJMoon

@JJMoon
Copy link
Contributor

JJMoon commented Nov 1, 2018

@zohaibahmed-22 Is this sandbox test case ?

@zohaibahmed-22
Copy link

@JJMoon No, its a real environment case.

@JJMoon
Copy link
Contributor

JJMoon commented Nov 1, 2018

I understand this error happens when the user is in logged out state, or no credit card info.
If the methods work fine, the cause of this error lies somewhere else.
We need to prepare this action, which leaves the app, viewDidDisappear etc.
In sandbox mode, this symptom happens somewhat different way.
When I make a new sandbox account, and the first purchasing doesn't work.
At the second time, the device is logged in state, and works fine.
I don't have a clue for this error.

@zohaibahmed-22
Copy link

zohaibahmed-22 commented Nov 1, 2018

@dooboolab @JJMoon have a look at this one
https://forums.developer.apple.com/thread/6431
https://forums.developer.apple.com/thread/64489

@anandwahed
Copy link
Author

anandwahed commented Nov 1, 2018

@dooboolab and @JJMoon also i think i found the issue when going through our iOS code. In the updatedTransactions method when we receive a failure message in the SKPaymentTransactionStateDeferred or SKPaymentTransactionStateFailed we are not suppose to finishTransaction.

Since in many cases the SKPaymentTransactionStateFailed result is followed by a SKPaymentTransactionStatePurchased as mentioned in the above thread 6431. I am not sure about the Objective-C code so kindly check and confirm if we do clear the transaction on failure.

@JJMoon
Copy link
Contributor

JJMoon commented Nov 2, 2018

@anandwahed I agree on you. It's my fault. Sorry for that.
I will look over the Apple's thread and take care of the issue.

@JJMoon
Copy link
Contributor

JJMoon commented Nov 5, 2018

@anandwahed I guess it's right way to finish transaction when it's failed. Please look up this thread. https://stackoverflow.com/questions/11008636/inapp-purchase-skpaymentqueue-finish-transaction-doesnt-work
When it fails, there will be no receipt, so you don't apply the purchase product. And the finishing transaction doesn't always mean 'purchasing'.
Meanwhile, I will investigate thread 6431.

@JJMoon
Copy link
Contributor

JJMoon commented Nov 8, 2018

I just read the thread 6431 (https://forums.developer.apple.com/thread/6431#14562)
The bottom line. Problem not solved since 2015. Wow..
There are two different ways to handle this 'store kit flow' effect.

  1. Do not show any user alert.
  2. Show the result.

And I learned two things.
A. We have to finishTransaction when it failed. (I guess with or without option, always)
B. Storekit flow makes failed and success with receipt both. (it's bad)

I suggest you all to read this thread, and return to this issue.

I guess, which way you choose (1 or 2), it's on your own.
We might need a call back for failure response.

@maxs15
Copy link
Contributor

maxs15 commented Dec 3, 2018

@JJMoon does it mean we shouldn't use buyProductWithoutFinishTransaction ?

@JJMoon
Copy link
Contributor

JJMoon commented Dec 15, 2018

@maxs15 I didn't mean that. Sorry for confusing.
The StoreKit flow can happen at any purchasing. It's iOS issue, not this module.
For now, I don't have any clue, either. This thing happens in any native iOS apps. Right?
We will dig more in our free time.

@hyochan
Copy link
Member

hyochan commented Dec 15, 2018

Today, I've tried to debug this issue because I have generated this problem. The payment finishTransanction and is completed but not getting the callback when payMethod has changed. I've tried to debug writing some console.log but I couldn't test real billing in dev environment. Could anyone suggest me how to debug this process so I can debug this for real purchase? Should I have to use this in sandbox mode? It is working perfectly in sandbox so I have no idea how to debug this. This is very reluctant.

Let's gather some ideas because I think this is very important to be fixed.

I am always getting this error when I attempt the purchase with non sandbox user.

@hyochan hyochan added ☠️ wontfix This will not be worked on 🏃🏻‍♀️ in progress Currently working on labels Dec 15, 2018
@JJMoon
Copy link
Contributor

JJMoon commented Dec 17, 2018

@hyochan If you connect the real server (yours), whether you are in debug mode or release mode doesn't matter. I guess you have 2 options.

  1. You run on the real device in debug mode in Xcode. Use JS console log.
  2. You run on the real device in release mode in Xcode. Use NSLog in objective-c code.
    Both methods should work.

@hyochan
Copy link
Member

hyochan commented Dec 17, 2018

@JJMoon Yeah, I've figured that out already but still I couldn't make live purchase testing in ios. Is this stackoverflow true? Then how can I solve this problem? We have to test the live purchase.

@hyochan
Copy link
Member

hyochan commented Dec 17, 2018

Anyone who is facing this problem, I am sure all of them do, please give us an idea of how to encounter this problem. in-app purchase fail when payment method added live as described in issue title. How could I debug this? @anandwahed Did you ever contact apple about this problem?

@anandwahed
Copy link
Author

@hyochan No we didn't contact Apple support.

@hyochan
Copy link
Member

hyochan commented Dec 17, 2018

I think we need to contact apple for this and this looks really terrible that there is a different test case which is not reproducible in sandbox environment. For those who want to understand the problem, I've recorded the screen and you can see the clip here. @anandwahed Could you also contact apple for this? Because I know they aren't that supportive so it is better for more people to have contact with them. Let's gather up to solve this issue.

@hyochan hyochan removed the ☠️ wontfix This will not be worked on label Dec 20, 2018
@hyochan
Copy link
Member

hyochan commented Dec 21, 2018

Today we received answer from Apple. There may be callback returned again after failure. We are working on workaround solution in #348 but I am afraid this could be very nasty.

@hyochan
Copy link
Member

hyochan commented Dec 23, 2018

I've released to 2.4.0-beta1, trying to make a workaround for this problem. The PR #348 has been added to this version and you can also see the readme of this feature. Note that this is under testing.

@hyochan
Copy link
Member

hyochan commented Dec 31, 2018

I've tested this in a live purchase and seems to be working. However, keep in mind that you should only add listener when there was a failure or it may be duplicated with a successful result.

@Edgpaez
Copy link
Contributor

Edgpaez commented Feb 27, 2019

Guys, thank you for investing time in this problem! 🙏

I think the current documentation needs to be clearer around the usage of addAdditionalSuccessPurchaseListenerIOS. That's something I tried to address at #414.

But I also think this leaves room from improvement to the API. Would you guys be open to discussing a change in the API that can better accommodate the so called 'store kit flow'? Something maybe using RxJS, for example:

const observable = RNIap.buyProduct('com.example.coins100')
						.subscribe(
							purchase => console.log(purchase), // successful payment
							err => console.log(err) // err.code and err.message are available
						)
						

Or something different that better hides the additional subscription for iOS?

@hyochan
Copy link
Member

hyochan commented Mar 7, 2019

@Edgpaez That's great but that will change the behavior of the purchase being done in android. I hope I can investigate version 3 of this module in 2019.

@Edgpaez
Copy link
Contributor

Edgpaez commented Mar 14, 2019

hi @hyochan, AFAICT, we don't need to change the internal behavior of the package, only the public facing interface. We can keep the buyItemByType method resolving promises and simply add a bit of Rx on top of that on index.js.

for example, we'd have this in index.js:

export const buyProduct = (sku) => Platform.select({
  android: () => Observable.of(RNIapModule.buyItemByType(ANDROID_ITEM_TYPE_IAP, sku, null, 0)), // returns an observable that emits when the RNIapModule.buyItemByType promise resolves
  ios: ... // ios would do the same but taking into account the usage of addAdditionalSuccessPurchaseListenerIOS
})();

My goal is not this specific implementation but to hide details specific to the 'store kit flow'.
Do you think this is a good idea?
Would you be interested in a PR?

@hyochan
Copy link
Member

hyochan commented Mar 14, 2019

@Edgpaez Ok. I understand the detail now. However, I feel adding RxJS is bit too much for implementing the feature since I think this can be covered without it.

Also, I feel the below would be something different.

RNIap.buyProduct('com.example.coins100')
						.subscribe(
							purchase => console.log(purchase), // successful payment
							err => console.log(err) // err.code and err.message are available
						)

If you call buyProduct for two items like below,

RNIap.buyProduct('com.example.coins100');
RNIap.buyProduct('com.example.coins200');

We can't guarantee which one would finish first so I think we should handle this in native to sendEvent out to JS.

I feel like implementation should look like

RNIap.buyProduct('com.example.coins100');
RNIap.buyProduct('com.example.coins200');

// receiving events
const subs =  RNIap.purchaseUpdateListener(purchase => {
  ...
});

Please tell me if there is something I've missed.

Lets handle further disccustin in #423

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
🙏 help wanted Extra attention is needed 📱 iOS Related to iOS
Projects
None yet
Development

No branches or pull requests

6 participants