集成Android Billing 4.0.0致React Native IAP失效求助
问题描述
在React Native安卓应用中集成Android Billing 4.0.0时遇到版本冲突问题:
- 在
app/build.gradle中添加implementation 'com.android.billingclient:billing:4.0.0'后,RNIAP组件的const products = await RNIap.getProducts(itemSkus);语句卡住无返回,后续代码无法执行 - 移除该依赖后,内购功能恢复正常,但Google Play控制台强制要求升级到Billing Library 4或更高版本,导致无法上传应用新版本
- 已排除模拟器环境问题,线上发布后内购仍无响应;
AndroidManifest.xml已添加<uses-permission android:name="com.android.vending.BILLING" />权限,iOS端内购功能完全正常
相关代码
iap.js
import React, { Component } from "react"; import { View, Text, StyleSheet, Platform, PixelRatio, } from "react-native"; import {Header,Left,Right,Icon,Content,Grid,Row,Col,Container,H1,Button,Footer} from 'native-base'; import LinearGradient from 'react-native-linear-gradient'; //Redux imports import { connect } from "react-redux"; import { toggle } from '../../Reducers/toggle'; //In-app Purchase import import * as RNIap from 'react-native-iap'; const itemSkus = Platform.select({ ios: [ 'example.for_stackoverflow.ios', ], android: [ 'example.for_stackoverflow.android', ], }); class Iap extends Component { constructor(props) { console.log(itemSkus); super(props); this.state = { productList: [], receipt: '', availableItemsMessage: '', }; } state = { toggle: this.props.toggle.togglePremium || false, }; handleChange = ( toggle ) => { this.setState({ toggle }); }; async componentDidMount() { try { const result = await RNIap.initConnection(); console.log('result', result); } catch (err) { console.warn(err.code, err.message); } this.getItems(); } //after purcahse to to the finish transaction page. goToNext = () => { this.props.navigation.navigate('Logout', { receipt: this.state.receipt, }); } getItems = async() => { try { console.log(itemSkus + ' check itemSkus'); const products = await RNIap.getProducts(itemSkus); //const subscriptions = await RNIap.getSubscriptions(itemSkus); // const products = await RNIap.getSubscriptions(itemSkus); console.log(products + 'check Products'); this.setState({ productList: products }); } catch (err) { console.log(err.message + ' err.message'); console.warn(err.code, err.message); } } buyItem = async(sku) => { //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: any = await RNIap.buyProduct(sku); this.setState({ receipt: purchase.transactionReceipt }, () => this.goToNext()); } catch (err) { //console.warn(err.code, err.message); const subscription = RNIap.addAdditionalSuccessPurchaseListenerIOS(async (purchase) => { this.setState({ receipt: purchase.transactionReceipt }, () => this.goToNext()); subscription.remove(); }); } } getPurchases = async() => { try { console.log('get hit, and when??'); const purchases = await RNIap.getAvailablePurchases(); let restoredTitles = ''; let coins = CoinStore.getCount(); purchases.forEach(purchase => { if (purchase.productId == '4dot6OVER01') { this.setState({ togglePremium: true }); restoredTitles += 'Premium Version'; } else if (purchase.productId == 'android.test.purchased') { console.log(purchase.productId); this.setState({ togglePremium: true }); restoredTitles += 'Premium Version'; } }) Alert.alert('Restore Successful', 'You successfully restored the following purchases: ' + restoredTitles); } catch(err) { //console.warn(err); // standardized err.code and err.message available Alert.alert(err.message); } } static navigationOptions = { drawerIcon : ({tintColor}) => ( <Icon name="ios-star-outline" style={{fontSize: 24, color: tintColor}} /> ) } getPrice = (priceString) => { const price = JSON.parse(priceString); //return (<H1 style={styles.textHeaderNumber}>${price}</H1>) return(<Text style={styles.buttonTextBackWhite}>${price}</Text>) } getTitle = (titleString) => { const title = JSON.parse(titleString); return(<Row><H1 style={styles.textHeader}>{title}</H1></Row>) } getDescription = (descString) => { const desc = JSON.parse(descString); return(<Row><Text style={styles.textDesc}>{desc}</Text></Row>) } render() { const purchase = this.props.toggle.togglePremium; const { productList, receipt, availableItemsMessage } = this.state; console.log(productList + ' check productList'); const receipt100 = receipt.substring(0, 100); return ( <Col size={1}> <Row><Text style={styles.textHeader}>Cricket Over Counter Premium</Text></Row> <Row><Text style={styles.textDesc}>Cricket Umpire Over and Ball Counter upgrade to premium to allow all features for the entire innings!</Text></Row> { productList.map((product, i) => { console.log(product + ' check product.'); return ( <Row key={i} style={{ flexDirection: 'column', alignItems: 'center', }}> <Button rounded large warning style={styles.largeButtonGreen} onPress={() => this.buyItem(product.productId)} > {this.getPrice(JSON.stringify(product.price))} </Button> </Row> ); }) } </Col> ); } } const mapStateToProps = state => ({ toggle: state.toggle, }); export default connect(mapStateToProps)(Iap);
package.json
{ "name": "ExampleForStackOverflow", "version": "0.0.1", "private": true, "scripts": { "android": "react-native run-android", "ios": "react-native run-ios", "start": "react-native start", "test": "jest", "lint": "eslint ." }, "dependencies": { "@invertase/react-native-apple-authentication": "^2.1.2", "@react-native-async-storage/async-storage": "^1.13.4", "@react-native-firebase/app": "^10.8.0", "@react-native-firebase/auth": "^10.8.0", "@react-native-firebase/firestore": "^10.8.0", "@react-native-google-signin/google-signin": "^7.0.0-alpha.3", "@react-native-picker/picker": "^1.16.7", "@react-navigation/drawer": "^5.12.3", "@react-navigation/native": "^5.9.2", "native-base": "^2.15.2", "react": "16.13.1", "react-compose": "^2.0.0", "react-native": "0.63.5", "react-native-animatable": "^1.3.3", "react-native-apple-authentication": "^2.0.0", "react-native-cli": "^2.0.1", "react-native-dotenv": "^3.3.1", "react-native-draggable-flatlist": "^3.0.7", "react-native-elements": "^3.2.0", "react-native-gesture-handler": "^1.10.1", "react-native-haptic-feedback": "^1.11.0", "react-native-iap": "^5.2.14", "react-native-keep-awake": "^4.0.0", "react-native-linear-gradient": "^2.5.6", "react-native-reanimated": "^1.13.4", "react-native-restart": "0.0.24", "react-native-safe-area-context": "^3.1.9", "react-native-safe-area-view": "^1.1.1", "react-native-screens": "^2.17.1", "react-native-scroll-into-view": "^2.0.2", "react-native-splash-screen": "^3.2.0", "react-native-swipe-list-view": "^3.2.9", "react-native-swipeable-item": "^2.0.2", "react-native-switch": "^2.0.0", "react-native-vector-icons": "^8.0.0", "react-native-webview": "^11.2.3", "react-navigation": "^4.4.3", "react-navigation-drawer": "^2.6.0", "react-redux": "^7.2.2", "redux": "^4.0.5", "redux-devtools-extension": "^2.13.8", "redux-logger": "^3.0.6", "redux-persist": "^6.0.0", "redux-thunk": "^2.3.0", "tcomb-form-native": "^0.6.20" }, "devDependencies": { "@babel/core": "^7.8.4", "@babel/runtime": "^7.8.4", "@react-native-community/eslint-config": "^1.1.0", "babel-jest": "^25.1.0", "eslint": "^6.5.1", "jest": "^25.1.0", "metro-react-native-babel-preset": "^0.59.0", "react-test-renderer": "16.13.1" }, "jest": { "preset": "react-native" } }
解决方案
核心原因
你当前使用的react-native-iap@5.2.14内部依赖的Billing Library版本低于4.0.0,手动添加更高版本的Billing Library会触发版本冲突,导致Native层与JS层通信阻塞,出现调用卡住的问题。
具体修复步骤
移除手动添加的Billing Library依赖
从app/build.gradle中删除implementation 'com.android.billingclient:billing:4.0.0'这一行。升级react-native-iap到兼容Billing Library 4+的版本
react-native-iap从6.0.0版本开始正式支持Billing Library 4.x,执行以下命令升级:npm install react-native-iap@^6.0.0 --save # 或使用yarn yarn add react-native-iap@^6.0.0适配新版本API变更
6.x版本有部分API调整,需要修改代码适配:initConnection不再返回结果,直接处理Promise状态即可getProducts参数格式保持兼容,但返回的商品对象字段有微调- 购买流程的错误处理逻辑需要对应更新
示例修改
componentDidMount:async componentDidMount() { try { await RNIap.initConnection(); console.log('内购连接初始化成功'); } catch (err) { console.warn(err.code, err.message); } this.getItems(); }验证依赖版本
执行cd android && ./gradlew app:dependencies查看依赖树,确认com.android.billingclient:billing的版本已自动升级到4.x或更高。清理并重建项目
# 清理安卓构建缓存 cd android && ./gradlew clean # 重新构建应用 react-native run-android
额外注意事项
- 升级后需在Google Play控制台确认内购商品状态正常,避免因版本变更导致的商品拉取失败
- 测试时使用搭载最新Google Play服务的真机,模拟器可能存在兼容性问题
- 若遇到其他API适配问题,可参考
react-native-iap版本变更日志调整代码
内容的提问来源于stack exchange,提问作者Andrew Oxley
相关产品推荐
相关产品推荐

