React-native-iap: Π’ Ρ‚ΠΈΠΏΠ΅ '(ΠΏΠΎΠΊΡƒΠΏΠΊΠ°: ProductPurchase)' ΠΎΡ‚ΡΡƒΡ‚ΡΡ‚Π²ΡƒΡŽΡ‚ ΡΠ»Π΅Π΄ΡƒΡŽΡ‰ΠΈΠ΅ свойства ΠΈΠ· Ρ‚ΠΈΠΏΠ° 'ProductPurchase': [...]

Π‘ΠΎΠ·Π΄Π°Π½Π½Ρ‹ΠΉ Π½Π° 10 июн. 2019  Β·  4ΠšΠΎΠΌΠΌΠ΅Π½Ρ‚Π°Ρ€ΠΈΠΈ  Β·  Π˜ΡΡ‚ΠΎΡ‡Π½ΠΈΠΊ: dooboolab/react-native-iap

ВСрсия react-native-iap

3.0.0-rc.1

ВСрсия react-native

0,59,9

ΠŸΠ»Π°Ρ‚Ρ„ΠΎΡ€ΠΌΡ‹, Π½Π° ΠΊΠΎΡ‚ΠΎΡ€Ρ‹Ρ… Π²Ρ‹ ΡΡ‚ΠΎΠ»ΠΊΠ½ΡƒΠ»ΠΈΡΡŒ с ошибкой (IOS ΠΈΠ»ΠΈ Android ΠΈΠ»ΠΈ ΠΎΠ±Π°?)

iOS

ОТидаСмоС повСдСниС

Ошибка Ρ‚ΠΈΠΏΠ°

ЀактичСскоС ΠΏΠΎΠ²Π΅Π΄Π΅Π½ΠΈΠ΅

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

Π― Π½Π΅ совсСм понимаю эту ΠΎΡˆΠΈΠ±ΠΊΡƒ, ΠΏΠΎΡ‚ΠΎΠΌΡƒ Ρ‡Ρ‚ΠΎ TS Ρ‚ΠΈΠΏΠ°, Π½Π° ΠΊΠΎΡ‚ΠΎΡ€Ρ‹ΠΉ ТалуСтся, ΠΏΠΎΡ…ΠΎΠΆΠ΅, Π΅ΡΡ‚ΡŒ:

// 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 Ρ€Π΅ΠΉΡ‚ΠΈΠ½Π³ΠΈ