hyochan / react-native-iap

In App Purchase module for React Native!
https://react-native-iap.hyo.dev
MIT License
2.86k stars 648 forks source link

RNIap.getProducts not returning promise on ios #341

Closed ZeroCool00 closed 5 years ago

ZeroCool00 commented 5 years ago

Version of react-native-iap

"react-native-iap": "^2.3.25"

Version of react-native

"react-native": "0.57.4",

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

IOS

Expected behavior

buyProduct should return promise ( .then() is not call ),

Actual behavior

buyProduct not returning any promise, in android its working fine, .then() call successfully, but in ios its not.

Tested environment (Emulator? Real Device?)

Real Device

Steps to reproduce the behavior

onBuyNowPress() {
    this.setState({ loading: true });
    RNIap.getProducts(['product_id']).then(success => { 
       RNIap.buyProduct('product_id').then(purchase => { 
        console.log("Purchase >>", purchase)
        this.setState({
          loading: false,
          receipt: purchase.transactionReceipt, 
        });
        Alert.alert('Purchase Successful!');
        this.props.getPurchase();
       }).catch(err => { 
         console.log(err.code, err.message);
          this.setState({ loading: false });
          Alert.alert(err.message);
          if(err.message === "You already own this item.") {
            this.props.getPurchase();
          }
        }) 
      }).catch(error => { 
        alert(error); 
        this.setState({ loading: false });
      })
  }

In ios i already buy product, so again when i press buy now, i get popup like you already purchase this product. but .then() or .catch() not getting call.

i also tried getAvailablePurchases() method, its always return []

hyochan commented 5 years ago

@LinusU I think @JJMoon was right. I've removed all the async and the problem comes up again. What do you think? @ZeroCool00 Would you downgrade to 2.3.24 for now?

hyochan commented 5 years ago

@ZeroCool00 Actually, would you please try 2.3.26 for us?

ZeroCool00 commented 5 years ago

@hyochan i'll test soon.. and let you know, and thank you for the quick reply.

ZeroCool00 commented 5 years ago

@hyochan i tried with 2.3.26, it not working? any solution?

hyochan commented 5 years ago

@ZeroCool00 Then it must be a linking problem. You should try to unlink and re-link again.

ZeroCool00 commented 5 years ago

@hyochan i tried, even i unlink and than uninstall and than install again, and link again. but it still not working

hyochan commented 5 years ago

@ZeroCool00 If you are in window, react-native link script is even more unstable. Did you try manually? Also please try 2.3.24 too. I think you are facing some config problem.

ZeroCool00 commented 5 years ago

@hyochan i m on mac, and i unlink and than uninstall and then install 2.3.24, and do manual setup, follow each step, it working with android, in ios its still not showing. process is working fine. i m getting popup and product also getting purchase, but no promise return.?

is there any other configuration for ios? i m stuck here, is there any other way to get that i already purchase.

hyochan commented 5 years ago

@JJMoon Could you help @ZeroCool00 ?

hyochan commented 5 years ago

@ZeroCool00 Also try removing Alert and use console to test instead. Because ios automatically generate native Alert when the purchase is being processed which may interrupt your code.

ZeroCool00 commented 5 years ago

@hyochan Plz reopen this.. i tried everything.. i also remove Alert. even i stop debugging and check.. its just not working. i wanted to publish my app, but i m stuck here. i even tried this lib https://github.com/chirag04/react-native-in-app-utils

this lib also has a same problem.. purchasing process working fine but not getting any promise.

is there any lifecycle method where i get event callback. i just want event callback where i can call server api.

hyochan commented 5 years ago

@ZeroCool00 Could you try to use async and await also? I'm also using this in our app and we are not facing this issue.

ZeroCool00 commented 5 years ago

@hyochan can you please provide your demo.. how are you using it.. it would be a great help.

ZeroCool00 commented 5 years ago
async forIOS() {
    let purchase =  await RNIap.buyProduct('product_id');
    console.log("BannerPurchase >>",purchase);  **<--- THIS IS NOT GETTING CALL**
    if(purchase) {
      console.log("Purchase >>", purchase)
      this.props.getPurchase();
      this.setState({
        loading: false,
        receipt: purchase.transactionReceipt, 
      });
      console.log('Purchase succesfully')
    }
  }

@hyochan I follow what you say, it seems this proccess is infinite, not returning anything. i dont even get the log.

hyochan commented 5 years ago

Since many others use our modules these days, your case looks quite weird. It'd be hard for us to figure out unless we have a full working code of yours.

IsaevTimur commented 5 years ago

@hyochan Having the same issue like @ZeroCool00 Just upgraded from 2.3.26 to 2.4.0-beta3 (made an unlink and link), the same story. Getting this in logs from device. Using real iPhone, development build via xCode (wire connected).

[IAPInfoManager]: Update operation failed, error: Error Domain=SSErrorDomain Code=109 "Cannot connect to iTunes Store" UserInfo={NSLocalizedDescription=Cannot connect to iTunes Store, SSErrorHTTPStatusCodeKey=401}

But pop-ups with purchase show up, with success story. But none reaction in Javascript Thread on it :( Promise is not resolved or rejected in iOS module.

try {
      let purchase = false;

      console.log('BEFORE BUY');
      if (Platform.OS === 'ios') {
        purchase = await RNIap.buyProduct(selected);
      } else {
        purchase = await RNIap.buySubscription(selected);
      }
      console.log('AFTER BUY');

      await RNIap.finishTransaction();
      console.info('purchase >', purchase);
      setSubscription({type: selected, purchaseData: purchase});
    } catch (err) {
      console.log(err); // TODO add something went wrong
    }

AFTER BUY Don't even shows up. The same story without async/await, with resolving .then promise.

Any ideas what might cause this problem?

hyochan commented 5 years ago

Could you try 2.4.0-beta4?

IsaevTimur commented 5 years ago

@hyochan Thanks a lot 👍 Issue resolved on my side.

ZeroCool00 commented 5 years ago

@IsaevTimur how did you solve it? its still not working.

ZeroCool00 commented 5 years ago

@hyochan Here is the Full code of my inapp purchase module

import React, { Component } from 'react';
import { View, ImageBackground, TouchableOpacity, Image, Alert, 
  ActivityIndicator, Platform } from 'react-native';
import {inapp, btnBuy, purple} from '../helper/constants';
import ImageResizeMode from 'react-native/Libraries/Image/ImageResizeMode'
import { connect } from 'react-redux';
import { Icon } from 'native-base';
import { Actions } from 'react-native-router-flux';
import * as RNIap from 'react-native-iap';
import { getItems, getPurchase, fetchCategory } from '../action';

const itemSkus = Platform.select({
  ios: [
    'max_asl_unlimited_access'
  ],
  android: [
    'max_asl_unlimited_access'
  ]
});

class Banner extends Component {

  constructor(props){
    super(props);
    this.onBuyNowPress = this.onBuyNowPress.bind(this);
    this.forAndroid = this.forAndroid.bind(this);
    this.forIOS = this.forIOS.bind(this);
  }

  state = {
    loading: false,
  }

  async componentDidMount() {
    try {
      const result = await RNIap.initConnection();
      console.log('result', result);
      this.props.getItems();
    } catch (err) {
      console.warn(err.code, err.message);
    }
}

  componentDidUpdate(prevProps) {
    if(prevProps.purchase !== this.props.purchase) {
      this.props.fetchCategory();
    } 
  }

  onBuyNowPress() {
    this.setState({ loading: true });
    RNIap.getProducts(itemSkus).then(success => { 
      if(Platform.OS == "ios") {
        this.forIOS()
      } else {
        this.forAndroid()
      }
    }).catch(error => { 
      alert(error); 
      this.setState({ loading: false });
    })
  }

  async forIOS() {
    console.log('BannerCall');
    try {
      const purchase = await RNIap.buyProduct('max_asl_unlimited_access');
      console.log("BannerPurchase >>",purchase);
      if(purchase) {
        this.props.getPurchase();
        this.setState({
          loading: false,
        });
        console.log('Purchase succesfully')
      }
    } catch (err) {
      console.log("BannerError >> ", err);
    }
  }

  forAndroid() {
    RNIap.buyProduct('max_asl_unlimited_access').then(purchase => { 
      this.props.getPurchase();
      this.setState({
        loading: false,
        receipt: purchase.transactionReceipt, 
      });
      console.log('Purchase succesfully')
      Alert.alert("Purchase succesfully")
     }).catch(err => { 
       console.log(err.code, err.message);
        this.setState({ loading: false });
        if(err.message === "You already own this item.") {
          this.props.getPurchase();
          Alert.alert("You already own this product, we are restoring your purchase.")
        }
      }) 
  }

  componentWillUnmount() {
    RNIap.endConnection();
  }

  renderSpinner() {
    if(this.state.loading) {
      return (
        <ActivityIndicator size="large" color="#F7CD52" />
      )
    }
    return null
}

  render() {
    const { container, bannerStyle, btnContainer, iconClose } = styles;
    return (
      <View style={container}>
          <ImageBackground source={inapp} style={bannerStyle} resizeMode={ImageResizeMode.contain}>
              <Icon name="close" style={iconClose} type="FontAwesome" onPress={() => {Actions.pop()}}/>
              <View style={btnContainer}>
                {this.renderSpinner()}
                <TouchableOpacity onPress={() => this.onBuyNowPress()}>
                    <Image source={btnBuy}  />
                </TouchableOpacity>
              </View>
          </ImageBackground>
      </View>
    );
  }
}

const styles = {
  container: {
    flex: 1,  
  },
  iconClose: {
    color: '#FFF',
    fontSize: 30,
    alignSelf: 'flex-end',
    margin: 20,
    fontWeight: 'bold',
  },
  bannerStyle: {
    flex: 1,
    width: '100%',
    height: '100%',
    backgroundColor: purple
  }, 
  btnBuy: {
    marginRight: 20,
    marginTop: 160,
    height: 85,
    width: 215,
  },
  btnContainer: {
    flex: 1,
    marginTop: 50,
    justifyContent: 'center',
    alignItems: 'center'
  }
};

function mapStateToProps({ purchase }) {
    return { purchase };
}

export default connect(mapStateToProps, { getItems, getPurchase, fetchCategory })(Banner);
IsaevTimur commented 5 years ago

@ZeroCool00

Make sure that you are using - 2.4.0-beta4.

Also, make sure that you are using SANDBOX user (under development). Which can be configured in iTunes connect. These users created here - https://appstoreconnect.apple.com/access/users, under Sandbox section.

Hope it helps :)

ZeroCool00 commented 5 years ago

@IsaevTimur
but my app store purchase process is working fine that meant everything is configured, and yes i m using 2.4.0-beta4.

here is my entry on package.json file

"react-native-iap": "^2.4.0-beta4",

ZeroCool00 commented 5 years ago

@hyochan i finally solved the issue.. problem is when i first test i didnt write this line await RNIap.finishTransaction(); in .then() method. i added it afterward. that was the problem, solution is i need to clear transaction(await RNIap.clearTransaction();), and it work.

Thanks again for your support n for this great library.

hyochan commented 5 years ago

@ZeroCool00 Congrat for success!! Yeah, those kinds of the problem should be solved better on your side because we need some traction on what've you've done wrong. Thanks for coming back again for those who might be suffering on the same thing.