React-native-iap: O tipo '(compra: Compra de Produto)' não contém as seguintes propriedades do tipo 'Compra de Produto': [...]

Criado em 10 jun. 2019  ·  4Comentários  ·  Fonte: dooboolab/react-native-iap

Versão do react-native-iap

3.0.0-rc.1

Versão do react-native

0,59,9

Plataformas em que você enfrentou o erro (IOS ou Android ou ambos?)

iOS

Comportamento esperado

Sem erro de tipo

Comportamento real

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

Ambiente testado (emulador? Dispositivo real?)

Dispositivo real

Passos para reproduzir o comportamento

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

Não entendi muito bem esse erro, porque o TS do tipo está reclamando de que parece estar lá:

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

E a definição purchaseUpdatedListener usa a mesma interface ProductPurchase .

Estou perdida 🤔

❄️ types 🐛 bug

Comentários muito úteis

@jvandenaardweg Heya! Desculpe pelo types . Tive um pequeno erro que foi corrigido em `[email protected] '.

Também abaixo está um exemplo de App.tsx para o usuário datilografado.

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;

Todos 4 comentários

O que acontece se você afirmar como abaixo?

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

O mesmo...

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 Heya! Desculpe pelo types . Tive um pequeno erro que foi corrigido em `[email protected] '.

Também abaixo está um exemplo de App.tsx para o usuário datilografado.

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;

Obrigado @hyochan pela solução rápida, funciona! :-)

Esta página foi útil?
0 / 5 - 0 avaliações