React-native-iap: рдкреНрд░рдХрд╛рд░ '(рдЦрд░реАрдж: рдЙрддреНрдкрд╛рдж рдЦрд░реАрдж)' рдкреНрд░рдХрд╛рд░ 'рдЙрддреНрдкрд╛рдж рдЦрд░реАрдж' рд╕реЗ рдирд┐рдореНрдирд▓рд┐рдЦрд┐рдд рдЧреБрдг рдЧрд╛рдпрдм рд╣реИ: [...]

рдХреЛ рдирд┐рд░реНрдорд┐рдд 10 рдЬреВрди 2019  ┬╖  4рдЯрд┐рдкреНрдкрдгрд┐рдпрд╛рдБ  ┬╖  рд╕реНрд░реЛрдд: dooboolab/react-native-iap

рдкреНрд░рддрд┐рдХреНрд░рд┐рдпрд╛ рджреЗрд╢реА-рдЖрдИрдПрдкреА рдХрд╛ рд╕рдВрд╕реНрдХрд░рдг

3.0.0-рдЖрд░рд╕реА.1

рдкреНрд░рддрд┐рдХреНрд░рд┐рдпрд╛-рдореВрд▓ рдХрд╛ рд╕рдВрд╕реНрдХрд░рдг

0.59.9

рдЬрд┐рди рдкреНрд▓реЗрдЯрдлрд╛рд░реНрдореЛрдВ рдореЗрдВ рдЖрдкрдХреЛ рддреНрд░реБрдЯрд┐ рдХрд╛ рд╕рд╛рдордирд╛ рдХрд░рдирд╛ рдкрдбрд╝рд╛ (рдЖрдИрдУрдПрд╕ рдпрд╛ рдПрдВрдбреНрд░реЙрдЗрдб рдпрд╛ рджреЛрдиреЛрдВ?)

рдЖрдИрдУрдПрд╕

рдЕрдкреЗрдХреНрд╖рд┐рдд рд╡реНрдпрд╡рд╣рд╛рд░

рдХреЛрдИ рдкреНрд░рдХрд╛рд░ рдХреА рддреНрд░реБрдЯрд┐ рдирд╣реАрдВ

рд╡рд╛рд╕реНрддрд╡рд┐рдХ рд╡реНрдпрд╡рд╣рд╛рд░

Argument of type '(purchase: ProductPurchase) => Promise<void>' is not assignable to parameter of type 'ProductPurchase'.
  Type '(purchase: ProductPurchase) => Promise<void>' is missing the following properties from type 'ProductPurchase': productId, transactionId, transactionDate, transactionReceipt

рдкрд░реАрдХреНрд╖рдг рдХрд┐рдпрд╛ рдЧрдпрд╛ рд╡рд╛рддрд╛рд╡рд░рдг (рдПрдореБрд▓реЗрдЯрд░? рд░рд┐рдпрд▓ рдбрд┐рд╡рд╛рдЗрд╕?)

рд╡рд╛рд╕реНрддрд╡рд┐рдХ рдЙрдкрдХрд░рдг

рд╡реНрдпрд╡рд╣рд╛рд░ рдХреЛ рдкреБрди: рдкреЗрд╢ рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рдХрджрдо

import * as RNIap from 'react-native-iap';

export class Sample extends React.PureComponent {
  purchaseUpdateSubscription: any = null;

  componentDidMount() {
    this.purchaseUpdateSubscription = RNIap.purchaseUpdatedListener(async (purchase: RNIap.ProductPurchase) => { })
  }
}

Screenshot 2019-06-10 at 10 47 39

рдореИрдВ рдЗрд╕ рддреНрд░реБрдЯрд┐ рдХреЛ рдмрд┐рд▓реНрдХреБрд▓ рд╕рдордЭ рдирд╣реАрдВ рдкрд╛ рд░рд╣рд╛ рд╣реВрдВ, рдХреНрдпреЛрдВрдХрд┐ рдЬрд┐рд╕ рдкреНрд░рдХрд╛рд░ рдХрд╛ рдЯреАрдПрд╕ рд╢рд┐рдХрд╛рдпрдд рдХрд░ рд░рд╣рд╛ рд╣реИ рд╡рд╣ рд╡рд╣рд╛рдВ рдкреНрд░рддреАрдд рд╣реЛрддрд╛ рд╣реИ:

// RNIap.ProductPurchase
export interface ProductPurchase {
  productId: string;
  transactionId: string;
  transactionDate: number;
  transactionReceipt: string;
  signatureAndroid?: string;
  dataAndroid?: string;
  purchaseToken?: string;
}

рдФрд░ purchaseUpdatedListener рдкрд░рд┐рднрд╛рд╖рд╛ рд╕рдорд╛рди ProductPurchase рдЗрдВрдЯрд░рдлрд╝реЗрд╕ рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рддреА рд╣реИред

рдореИрдВ рдЦреЛ рдЧрдпрд╛ рд╣реВрдБ

тЭДя╕П types ЁЯРЫ bug

рд╕рдмрд╕реЗ рдЙрдкрдпреЛрдЧреА рдЯрд┐рдкреНрдкрдгреА

@jvandenaardweg рд╣реЗрдпрд╛ ! types рд▓рд┐рдП рдХреНрд╖рдорд╛ рдХрд░реЗрдВред рдореБрдЭрд╕реЗ рдПрдХ рдЫреЛрдЯреА рд╕реА рдЧрд▓рддреА рд╣реБрдИ рд╣реИ рдФрд░ рдЗрд╕реЗ ' [email protected] ' рдореЗрдВ рдареАрдХ рдХрд┐рдпрд╛ рдЧрдпрд╛ рд╣реИред

рдЯрд╛рдЗрдкрдкреНрд░рддрд┐ рдЙрдкрдпреЛрдЧрдХрд░реНрддрд╛ рдХреЗ рд▓рд┐рдП App.tsx рдХрд╛ рдирдореВрдирд╛ рднреА рдиреАрдЪреЗ рджрд┐рдпрд╛ рдЧрдпрд╛ рд╣реИред

import React, { Component } from 'react';
import {
  View,
  Text,
  Alert,
  Platform,
  ScrollView,
  StyleSheet,
  Button,
} from 'react-native';
import RNIap, {
  Product,
  ProductPurchase,
  Subscription,
  SubscriptionPurchase,
  purchaseUpdatedListener,
} from 'react-native-iap';

// App Bundle > com.dooboolab.test

const itemSkus = Platform.select({
  ios: [
    'com.cooni.point1000', 'com.cooni.point5000', // dooboolab
  ],
  android: [
    'android.test.purchased', 'android.test.canceled', 'android.test.refunded', 'android.test.item_unavailable',
    // 'point_1000', '5000_point', // dooboolab
  ],
});

const itemSubs = Platform.select({
  ios: [
    'com.cooni.point1000', 'com.cooni.point5000', // dooboolab
  ],
  android: [
    'test.sub1', // subscription
  ],
});

let purchaseUpdateSubscription: any;

interface IProps { };
interface IState {
  productList: Product<string>[] | Subscription<string>[] | any;
  receipt: string;
  availableItemsMessage: string;
};

class Page extends Component<IProps, IState> {
  constructor(props: IProps) {
    super(props);

    this.state = {
      productList: [],
      receipt: '',
      availableItemsMessage: '',
    };
  }

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

    purchaseUpdateSubscription = purchaseUpdatedListener((purchase: ProductPurchase) => {
      console.log('purchaseUpdatedListener', purchase);
      this.setState({ receipt: purchase.transactionReceipt }, () => this.goNext());
    });
  }

  componentWillMount() {
    if (purchaseUpdateSubscription) {
      purchaseUpdateSubscription.remove();
      purchaseUpdateSubscription = null;
    }
  }

  goNext = () => {
    Alert.alert('Receipt', this.state.receipt);
  }

  getItems = async() => {
    try {
      const products: Product<string>[] = await RNIap.getProducts(itemSkus);
      // const products = await RNIap.getSubscriptions(itemSkus);
      console.log('Products', products);
      this.setState({ productList: products });
    } catch (err) {
      console.warn(err.code, err.message);
    }
  }

  getSubscriptions = async() => {
    try {
      const products: Subscription<string>[] = await RNIap.getSubscriptions(itemSubs);
      console.log('Products', products);
      this.setState({ productList: products });
    } catch (err) {
      console.warn(err.code, err.message);
    }
  }

  getAvailablePurchases = async() => {
    try {
      console.info('Get available purchases (non-consumable or unconsumed consumable)');
      const purchases = await RNIap.getAvailablePurchases();
      console.info('Available purchases :: ', purchases);
      if (purchases && purchases.length > 0) {
        this.setState({
          availableItemsMessage: `Got ${purchases.length} items.`,
          receipt: purchases[0].transactionReceipt,
        });
      }
    } catch (err) {
      console.warn(err.code, err.message);
      Alert.alert(err.message);
    }
  }

  // Version 3 apis
  requestPurchase = async(sku: string) => {
    try {
      RNIap.requestPurchase(sku);
    } catch (err) {
      console.warn(err.code, err.message);
    }
  }

  requestSubscription = async(sku: string) => {
    try {
      RNIap.requestSubscription(sku);
    } catch (err) {
      Alert.alert(err.message);
    }
  }

  // Deprecated apis
  buyItem = async(sku: string) => {
    console.info('buyItem', sku);
    // const purchase = await RNIap.buyProduct(sku);
    // const products = await RNIap.buySubscription(sku);
    // const purchase = await RNIap.buyProductWithoutFinishTransaction(sku);
    try {
      const purchase: ProductPurchase = await RNIap.buyProduct(sku);
      // console.log('purchase', purchase);
      // await RNIap.consumePurchaseAndroid(purchase.purchaseToken);
      this.setState({ receipt: purchase.transactionReceipt }, () => this.goNext());
    } catch (err) {
      console.warn(err.code, err.message);
      const subscription = RNIap.addAdditionalSuccessPurchaseListenerIOS(async(purchase: ProductPurchase) => {
        this.setState({ receipt: purchase.transactionReceipt }, () => this.goNext());
        subscription.remove();
      });
    }
  }

  buySubscribeItem = async(sku: string) => {
    try {
      console.log('buySubscribeItem: ' + sku);
      const purchase = await RNIap.buySubscription(sku);
      console.info(purchase);
      this.setState({ receipt: purchase.transactionReceipt }, () => this.goNext());
    } catch (err) {
      console.warn(err.code, err.message);
      Alert.alert(err.message);
    }
  }

  render() {
    const { productList, receipt, availableItemsMessage } = this.state;
    const receipt100 = receipt.substring(0, 100);

    return (
      <View style={ styles.container }>
        <View style={ styles.header }>
          <Text style={ styles.headerTxt} >react-native-iap V3</Text>
        </View>
        <View style={ styles.content }>
          <ScrollView
            style={{ alignSelf: 'stretch' }}
          >
            <View style={{ height: 50 }} />
            <Button
              onPress={this.getAvailablePurchases}
              color='blue'
              title='Get available purchases'
            />

            <Text style={{ margin: 5, fontSize: 15, alignSelf: 'center' }} >{availableItemsMessage}</Text>

            <Text style={{ margin: 5, fontSize: 9, alignSelf: 'center' }} >{receipt100}</Text>

            <Button
              onPress={() => this.getItems()}
              color='green'
              title={`Get Products ({productList.length})`}
            />
            {
              productList.map((product: Product<string> | Subscription<string>, i: number) => {
                return (
                  <View key={i} style={{
                    flexDirection: 'column',
                  }}>
                    <Text style={{
                      marginTop: 20,
                      fontSize: 12,
                      color: 'black',
                      minHeight: 100,
                      alignSelf: 'center',
                      paddingHorizontal: 20,
                    }} >{JSON.stringify(product)}</Text>
                    <Button
                      onPress={() => this.requestPurchase(product.productId)}
                      // onPress={() => this.requestSubscription(product.productId)}
                      // onPress={() => this.buyItem(product.productId)}
                      // onPress={() => this.buySubscribeItem(product.productId)}
                      color='green'
                      title='Request purchase for above product'
                    />
                  </View>
                );
              })
            }
          </ScrollView>
        </View>
      </View>
    );
  }
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    flexDirection: 'column',
    justifyContent: 'center',
    alignItems: 'center',
    marginTop: Platform.select({
      ios: 0,
      android: 24,
    }),
    paddingTop: Platform.select({
      ios: 0,
      android: 24,
    }),
    backgroundColor: 'white',
  },
  header: {
    flex: 20,
    flexDirection: 'row',
    alignSelf: 'stretch',
    justifyContent: 'center',
    alignItems: 'center',
  },
  headerTxt: {
    fontSize: 26,
    color: 'green',
  },
  content: {
    flex: 80,
    flexDirection: 'column',
    justifyContent: 'center',
    alignSelf: 'stretch',
    alignItems: 'center',
  },
  title: {
    fontSize: 24,
    fontWeight: 'bold',
  },
  btn: {
    height: 48,
    width: 240,
    alignSelf: 'center',
    backgroundColor: '#00c40f',
    borderRadius: 0,
    borderWidth: 0,
  },
  txt: {
    fontSize: 16,
    color: 'white',
  },
});

export default Page;

рд╕рднреА 4 рдЯрд┐рдкреНрдкрдгрд┐рдпрд╛рдБ

рдпрджрд┐ рдЖрдк рдиреАрдЪреЗ рдХреА рддрд░рд╣ рдмрддрд╛рддреЗ рд╣реИрдВ рддреЛ рдХреНрдпрд╛ рд╣реЛрдЧрд╛?

purchaseUpdateSubscription = purchaseUpdatedListener((purchase: ProductPurchase | SubscriptionPurchase) => {
  console.log('purchaseUpdatedListener', purchase);
  this.setState({ receipt: purchase.transactionReceipt }, () => this.goNext());
});

рдпрд╣ рд╡рд╣реА...

Argument of type '(purchase: ProductPurchase | SubscriptionPurchase) => Promise<void>' is not assignable to parameter of type 'ProductPurchase'.
  Type '(purchase: ProductPurchase | SubscriptionPurchase) => Promise<void>' is missing the following properties from type 'ProductPurchase': productId, transactionId, transactionDate, transactionReceipt

@jvandenaardweg рд╣реЗрдпрд╛ ! types рд▓рд┐рдП рдХреНрд╖рдорд╛ рдХрд░реЗрдВред рдореБрдЭрд╕реЗ рдПрдХ рдЫреЛрдЯреА рд╕реА рдЧрд▓рддреА рд╣реБрдИ рд╣реИ рдФрд░ рдЗрд╕реЗ ' [email protected] ' рдореЗрдВ рдареАрдХ рдХрд┐рдпрд╛ рдЧрдпрд╛ рд╣реИред

рдЯрд╛рдЗрдкрдкреНрд░рддрд┐ рдЙрдкрдпреЛрдЧрдХрд░реНрддрд╛ рдХреЗ рд▓рд┐рдП App.tsx рдХрд╛ рдирдореВрдирд╛ рднреА рдиреАрдЪреЗ рджрд┐рдпрд╛ рдЧрдпрд╛ рд╣реИред

import React, { Component } from 'react';
import {
  View,
  Text,
  Alert,
  Platform,
  ScrollView,
  StyleSheet,
  Button,
} from 'react-native';
import RNIap, {
  Product,
  ProductPurchase,
  Subscription,
  SubscriptionPurchase,
  purchaseUpdatedListener,
} from 'react-native-iap';

// App Bundle > com.dooboolab.test

const itemSkus = Platform.select({
  ios: [
    'com.cooni.point1000', 'com.cooni.point5000', // dooboolab
  ],
  android: [
    'android.test.purchased', 'android.test.canceled', 'android.test.refunded', 'android.test.item_unavailable',
    // 'point_1000', '5000_point', // dooboolab
  ],
});

const itemSubs = Platform.select({
  ios: [
    'com.cooni.point1000', 'com.cooni.point5000', // dooboolab
  ],
  android: [
    'test.sub1', // subscription
  ],
});

let purchaseUpdateSubscription: any;

interface IProps { };
interface IState {
  productList: Product<string>[] | Subscription<string>[] | any;
  receipt: string;
  availableItemsMessage: string;
};

class Page extends Component<IProps, IState> {
  constructor(props: IProps) {
    super(props);

    this.state = {
      productList: [],
      receipt: '',
      availableItemsMessage: '',
    };
  }

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

    purchaseUpdateSubscription = purchaseUpdatedListener((purchase: ProductPurchase) => {
      console.log('purchaseUpdatedListener', purchase);
      this.setState({ receipt: purchase.transactionReceipt }, () => this.goNext());
    });
  }

  componentWillMount() {
    if (purchaseUpdateSubscription) {
      purchaseUpdateSubscription.remove();
      purchaseUpdateSubscription = null;
    }
  }

  goNext = () => {
    Alert.alert('Receipt', this.state.receipt);
  }

  getItems = async() => {
    try {
      const products: Product<string>[] = await RNIap.getProducts(itemSkus);
      // const products = await RNIap.getSubscriptions(itemSkus);
      console.log('Products', products);
      this.setState({ productList: products });
    } catch (err) {
      console.warn(err.code, err.message);
    }
  }

  getSubscriptions = async() => {
    try {
      const products: Subscription<string>[] = await RNIap.getSubscriptions(itemSubs);
      console.log('Products', products);
      this.setState({ productList: products });
    } catch (err) {
      console.warn(err.code, err.message);
    }
  }

  getAvailablePurchases = async() => {
    try {
      console.info('Get available purchases (non-consumable or unconsumed consumable)');
      const purchases = await RNIap.getAvailablePurchases();
      console.info('Available purchases :: ', purchases);
      if (purchases && purchases.length > 0) {
        this.setState({
          availableItemsMessage: `Got ${purchases.length} items.`,
          receipt: purchases[0].transactionReceipt,
        });
      }
    } catch (err) {
      console.warn(err.code, err.message);
      Alert.alert(err.message);
    }
  }

  // Version 3 apis
  requestPurchase = async(sku: string) => {
    try {
      RNIap.requestPurchase(sku);
    } catch (err) {
      console.warn(err.code, err.message);
    }
  }

  requestSubscription = async(sku: string) => {
    try {
      RNIap.requestSubscription(sku);
    } catch (err) {
      Alert.alert(err.message);
    }
  }

  // Deprecated apis
  buyItem = async(sku: string) => {
    console.info('buyItem', sku);
    // const purchase = await RNIap.buyProduct(sku);
    // const products = await RNIap.buySubscription(sku);
    // const purchase = await RNIap.buyProductWithoutFinishTransaction(sku);
    try {
      const purchase: ProductPurchase = await RNIap.buyProduct(sku);
      // console.log('purchase', purchase);
      // await RNIap.consumePurchaseAndroid(purchase.purchaseToken);
      this.setState({ receipt: purchase.transactionReceipt }, () => this.goNext());
    } catch (err) {
      console.warn(err.code, err.message);
      const subscription = RNIap.addAdditionalSuccessPurchaseListenerIOS(async(purchase: ProductPurchase) => {
        this.setState({ receipt: purchase.transactionReceipt }, () => this.goNext());
        subscription.remove();
      });
    }
  }

  buySubscribeItem = async(sku: string) => {
    try {
      console.log('buySubscribeItem: ' + sku);
      const purchase = await RNIap.buySubscription(sku);
      console.info(purchase);
      this.setState({ receipt: purchase.transactionReceipt }, () => this.goNext());
    } catch (err) {
      console.warn(err.code, err.message);
      Alert.alert(err.message);
    }
  }

  render() {
    const { productList, receipt, availableItemsMessage } = this.state;
    const receipt100 = receipt.substring(0, 100);

    return (
      <View style={ styles.container }>
        <View style={ styles.header }>
          <Text style={ styles.headerTxt} >react-native-iap V3</Text>
        </View>
        <View style={ styles.content }>
          <ScrollView
            style={{ alignSelf: 'stretch' }}
          >
            <View style={{ height: 50 }} />
            <Button
              onPress={this.getAvailablePurchases}
              color='blue'
              title='Get available purchases'
            />

            <Text style={{ margin: 5, fontSize: 15, alignSelf: 'center' }} >{availableItemsMessage}</Text>

            <Text style={{ margin: 5, fontSize: 9, alignSelf: 'center' }} >{receipt100}</Text>

            <Button
              onPress={() => this.getItems()}
              color='green'
              title={`Get Products ({productList.length})`}
            />
            {
              productList.map((product: Product<string> | Subscription<string>, i: number) => {
                return (
                  <View key={i} style={{
                    flexDirection: 'column',
                  }}>
                    <Text style={{
                      marginTop: 20,
                      fontSize: 12,
                      color: 'black',
                      minHeight: 100,
                      alignSelf: 'center',
                      paddingHorizontal: 20,
                    }} >{JSON.stringify(product)}</Text>
                    <Button
                      onPress={() => this.requestPurchase(product.productId)}
                      // onPress={() => this.requestSubscription(product.productId)}
                      // onPress={() => this.buyItem(product.productId)}
                      // onPress={() => this.buySubscribeItem(product.productId)}
                      color='green'
                      title='Request purchase for above product'
                    />
                  </View>
                );
              })
            }
          </ScrollView>
        </View>
      </View>
    );
  }
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    flexDirection: 'column',
    justifyContent: 'center',
    alignItems: 'center',
    marginTop: Platform.select({
      ios: 0,
      android: 24,
    }),
    paddingTop: Platform.select({
      ios: 0,
      android: 24,
    }),
    backgroundColor: 'white',
  },
  header: {
    flex: 20,
    flexDirection: 'row',
    alignSelf: 'stretch',
    justifyContent: 'center',
    alignItems: 'center',
  },
  headerTxt: {
    fontSize: 26,
    color: 'green',
  },
  content: {
    flex: 80,
    flexDirection: 'column',
    justifyContent: 'center',
    alignSelf: 'stretch',
    alignItems: 'center',
  },
  title: {
    fontSize: 24,
    fontWeight: 'bold',
  },
  btn: {
    height: 48,
    width: 240,
    alignSelf: 'center',
    backgroundColor: '#00c40f',
    borderRadius: 0,
    borderWidth: 0,
  },
  txt: {
    fontSize: 16,
    color: 'white',
  },
});

export default Page;

рдзрдиреНрдпрд╡рд╛рдж @hyochan рддреНрд╡рд░рд┐рдд рд╕реБрдзрд╛рд░ рдХреЗ рд▓рд┐рдП, рдпрд╣ рдХрд╛рдо рдХрд░рддрд╛ рд╣реИ! :-)

рдХреНрдпрд╛ рдпрд╣ рдкреГрд╖реНрда рдЙрдкрдпреЛрдЧреА рдерд╛?
0 / 5 - 0 рд░реЗрдЯрд┐рдВрдЧреНрд╕

рд╕рдВрдмрдВрдзрд┐рдд рдореБрджреНрджреЛрдВ

fergalindez picture fergalindez  ┬╖  5рдЯрд┐рдкреНрдкрдгрд┐рдпрд╛рдБ

bakedbean picture bakedbean  ┬╖  5рдЯрд┐рдкреНрдкрдгрд┐рдпрд╛рдБ

MacMillan13 picture MacMillan13  ┬╖  3рдЯрд┐рдкреНрдкрдгрд┐рдпрд╛рдБ

lc3t35 picture lc3t35  ┬╖  4рдЯрд┐рдкреНрдкрдгрд┐рдпрд╛рдБ

coldfins picture coldfins  ┬╖  3рдЯрд┐рдкреНрдкрдгрд┐рдпрд╛рдБ