I'm trying to add In App Purchases (or as Google likes to call it; "In App Billing") to my React Native App.
What I've done:
Created a product in Google Play Console (with identifier "unlock_premium"
yarn add react-native-iap (this library)
Added the code below to a component in my app
Tested both with react-native run-android on a physical device, and through publishing it to an Alpha test from Google Play Console
Notes: The app is signed and billing permissions are enabled in the manifest file, and the version of react-native-iap is 0.3.23.
The issue:
When running a debug build the console.log() just prints that the product is undefined, and the productInfo does not display on screen when running the Alpha deployed version (so the product was also undefined there). The products variable is just an empty array.
(The try-statement seems to succeed since I see no errors printed from it.)
import InAppPurchase from "react-native-iap";
const itemSKUs = Platform.select({
android: [
"com.kimer.unlock_premium" // I've also tried just "unlock_premium"
]
})
...
constructor(props) {
super(props);
this.state = {
modalVisible: false,
premiumProduct: undefined
};
}
...
async componentDidMount() {
try {
await InAppPurchase.prepare();
const products = await InAppPurchase.getProducts(itemSKUs);
console.log("finished call to get products");
console.log(products[0]);
this.setState({premiumProduct: products[0]});
} catch (err) {
console.warn(err);
}
}
...
render() {
let productInfo;
if (this.state.premiumProduct !== undefined) {
productInfo = (
<Text>{this.state.premiumProduct}
{this.state.premiumProduct.localizedPrice}
{this.state.premiumProduct.currency} {this.state.premiumProduct.productID}
{this.state.premiumProduct.title}
{this.state.premiumProduct.description}</Text>
);
}
return (
<View>
...
{productInfo}
...
</View>
);
}
SOLVED:
It is now working for me 😄! I tried a couple of things but I'm not sure what was key to getting it to work 🤔 This is anyways what I did:
Reinstalled the package
Changed my import statement from import InAppPurchase from "react-native-iap;" to "import * as InAppPurchase from 'react-native-iap';"
Changed my SKU in the code from "com.kimer.unlock_premium" to "unlock_premium"
Related
I need to install downloaded .apk file from within the Expo app (it's for update functionality). This is my code:
import React from "react";
import { Button, View } from "react-native";
import * as FileSystem from "expo-file-system";
import { startActivityAsync } from "expo-intent-launcher";
export function Updater() {
async function updateApk() {
const uri = "https://expo.dev/artifacts/eas/my_apk_name.apk";
const localUri = FileSystem.documentDirectory + "test.apk";
try {
await FileSystem.downloadAsync(uri, localUri);
await startActivityAsync("android.intent.action.INSTALL_PACKAGE", {
data: localUri,
flags: 1,
});
} catch (error) {
alert(`Error during installing APK: ${error}`);
}
}
return (
<View>
<Button title="Reset APK" onPress={updateApk} />
</View>
);
}
It downloads the file, stores it, but then there is an error during startActivityAsync:
Encountered an exception while calling native method:
Exception occurred while executing exported method startActivity on module ExpoIntentLauncher:
file://data/user/0/com.my.app.id/files/test.apk exposed beyond app through Intent.getData()
I tried passing uri first to FileSystem.getContentUriAsync() but then there is no error, the intent result is 0 but nothing happens.
My permissions in app.json:
"permissions": [
"READ_EXTERNAL_STORAGE",
"WRITE_EXTERNAL_STORAGE",
"CAMERA"
]
Do I need any additional permissions to get it to work? Or is it completely impossible with Expo? Maybe I should save the file to different location to be able to use this intent?
I also tried android.intent.action.VIEW with no luck.
I test it on Android 13, on physical device. App is built with EAS.
Maybe you can use this command to build release build.
expo:build android
For that you have to signup in Expo's website.
After that you can get apk in Expo's server.
I finally got it to work. The funny part is that I got the answer from the AI that is now banned here. But I just tested this solution on a real android device and it works. Anyway there are two changes needed:
REQUEST_INSTALL_PACKAGES must be added to app.json file.
Uri for intent must be a content uri so: localUri = await FileSystem.getContentUriAsync(localUri)
I am out of ideas right now why my app does not pass app check verifications. I am building a React-Native app with Firebase using react-native-firebase. It throws this error:
[firestore/permission-denied]
I have installed the app-check package for react native. I have added these lines to app/build.gradle:
implementation 'com.google.firebase:firebase-appcheck-safetynet'
implementation 'com.google.firebase:firebase-appcheck-debug'
I have enabled App Check in Firebase console and added the SHA-256 certificate fingerprint to it.
I have added this flag to firebase.json:
"automaticResourceManagement": true,
and finally, the initialization of the app check in index.js:
import { firebase } from '#react-native-firebase/app-check';
try {
firebase.appCheck().setTokenAutoRefreshEnabled(true);
firebase.appCheck().activate('ignored', true);
const appchecktoken = firebase.appCheck().getToken(true);
console.log("app check success, appchecktoken: " + JSON.stringify(appchecktoken));
} catch (e) {
console.log("Failed to initialize appCheck:", e);
}
When I print the appchecktoken it seems empty:
{"_U":0,"_V":0,"_W":null,"_X":null}
What am I missing here? Please remember that I am using the react-native-firebase package and not the native packages.
I need to add those to Android files:
android:usesCleartextTraffic="true" and <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
But I'm using managed workflow and I don't know how to add those lines to app.json file.
I did this plugin which seems to work:
const { createRunOncePlugin, withAndroidManifest } = require('#expo/config-plugins');
const withAndroidManifestHavingBetterSecuritySettings = config => {
return withAndroidManifest(config, config => {
const androidManifest = config.modResults.manifest;
const mainApplication = androidManifest.application[0];
if(process.env.CHANNEL !== 'dev') {
androidManifest.$ = {
...androidManifest.$,
'xmlns:tools': 'http://schemas.android.com/tools',
};
mainApplication.$['tools:replace'] = 'android:usesCleartextTraffic';
mainApplication.$['android:usesCleartextTraffic'] = 'false';
}
return config;
});
};
module.exports = createRunOncePlugin(
withAndroidManifestHavingBetterSecuritySettings,
'withAndroidManifestHavingBetterSecuritySettings',
'1.0.0'
);
I had many issues related to merging of AndroidManifest files when "developmentClient": true in my eas.json file (related to me dev eas profile). I believe that it's related to the fact that the debug/AndroidManifest is a higher priority manifest than main/AndroidManifest (not sure though). So my solution was not to ignore the changes when building the dev profile. Hardening security settings in development builds do not seem useful anyhow.
So I struggled with this problem for a while now and the only solution I could come up with was setting the minimum sdk version of the android app from 21 to 28. This is not ideal as my application now does not support old android devices, but doing this defaults the usesClearTextTraffic flag to false.
If your app works fine while developing in expo, but after generating the APK some functions don't work, try this. In my case the APK crashed on login, but building in development with expo was working fine. The problem was that traffic is encrypted so that's why I ended up here trying to set clear text traffic. The problem in my case was with expoPushToken, in the APK it throws an exception I wasn't catching (building with expo worked fine as I said before, no exception). So, if the exception happens just catch it and set the token to empty string.
So, I had this:
import * as Notifications from "expo-notifications";
export async function getDevicePushTokenForAPP() {
const pushToken = await Notifications.getExpoPushTokenAsync();
return pushToken.data;
}
So then, I added the try and catch:
export async function getDevicePushTokenForAPP() {
try {
const pushToken = await Notifications.getExpoPushTokenAsync();
return pushToken.data;
} catch (e) {
return "";
}
}
Now if you build the APK again (expo build:android) it should work fine, in my case login worked. But please note this is for testing purposes only, I needed the APK to quickly show it to the client. (Note that you will need the bundle, not the apk, when uploading to the Playstore). This is a quick fix for you to test the APK; but with no token, push notifications won't work. The final fix is to add firebase to your project, it's mandatory now, so add firebase and with the firebase unique ID, your push notification will work in your APK.
My conclusion is that expo uses its own ID to communicate with firebase, that's why it works while developing but the APK doesn't go through expo and tries to connect to firebase directly, but crashes because there's no ID.
You should update your app.json like that:
"android": {
"usesCleartextTraffic": true,
uses-permission android:name
},
I'm new to React Native/Firebase. I followed these instructions to setup #react-native-firebase/auth for Android.
I have enabled Email/Password based login from my Firebase web console and have created a user for testing. I am calling Firebase auth using the following code snippet.
auth()
.signInWithEmailAndPassword(userNameText, passwordText)
.then(() => {
navigation.navigate("ClientMainScreen");
})
.catch((error) => {
setSnackBarVisible(true);
});
The thing is that this seems to be running without errors on my Android emulator on my Mac but when testing this out on a physical android device using expo publish, the app crashes when trying to execute this code snippet with the following error log in the Expo Client. What am I missing ?
For context, my index.js looks like this
import { registerRootComponent } from "expo";
import App from "./App";
registerRootComponent(App);
and I'm using react-native": "~0.62.2"
native. I want to active StackNavigator in App.js file. Please note my App.js file in different folder named 'app' and under app there is components folder under this I want to make all component file. here is my App.js
import React, { Component } from "react";
import { StackNavigator } from "react-navigation";
import Attendence from "./components/Attendence";
const Application = StackNavigator(
{
Home: { screen: Attendence }
},
{
navigationOptions: {
header: false
}
}
);
export default class App extends React.Component {
render() {
return <Application />;
}
}
As you said you have downloaded an old project ! it means you are about to tackle different node dependency packages errors on the route to get it running!
My tips to get your self saved from errors are like this
1: go into the package.json
2: note down every dependency names and versions
(the reason why to do that is simple ! you gonna need some awesome code that were in old version is now deprecated in new versions of those libs running!) so installing old ones would help you with that
3: if the point 2 does not work! please do check the version number of react native that you have and the one the real coder of the app used! and install the older version of react native in newer version there are some new things than old versions!
PRO TIP::: USE YARN
instead of NPM to save your self from the problems!