Google Play In-App Purchase Verification Using Firebase Functions
Validating transaction receipts through your server is essential for ensuring the authenticity of each purchase and protecting against fraud. This step confirms that transactions are legitimate, maintains app security, and complies with Google Play’s guidelines, providing a trustworthy user experience.
In this guide, we’re using React Native for building our app (client) and the react-native-iap
module for handling in-app purchases. This module allows us to make payments and retrieve receipts efficiently. We'll then validate these receipts on our server using Firebase Functions to ensure secure and legitimate transactions.
1. Set Up React Native IAP Locally
- First, you need to install the
react-native-iap
module in your React Native project. This module will help you handle in-app purchases and get transaction receipts.
2. Create a Google Cloud Project
- If you don’t have a Google Cloud project, create one. This project will be used to manage your google play console transaction. You can link this to your firebase project too.
3. Create a Service Account in Google Cloud
- In your Google Cloud project, create a service account. Generate a new key for this account and download the JSON file containing your credentials. In google cloud project search “Service Account” this is how you can find and create it.
4. Enable “Google Play Android Developer API” in your google cloud project
- In your Google Cloud project, search for “Google Play Android Developer API” and then open and enable it.
5. Add Service Account to Google Play Console
- Copy the email address from your service account. Go to your Google Play Console, navigate to the Users & Permissions tab, click on Invite new users, and enter copied email address with the below permissions in a Account Permission tab and then also select the app from the app permissions tab must!
6. Add Code to Firebase Function
- Upload the downloaded JSON file to your Firebase project. In your Firebase Functions code. Write a function to handle the receipt verification process.
import admin from "firebase-admin";
import { onCall } from "firebase-functions/v2/https";
import { CallableRequest } from "firebase-functions/v2/https";
import { google } from "googleapis";
import * as key from "../Keys/service-account-key.json"; // Your Downloaded JSON File
admin.initializeApp();
const authClient = new google.auth.JWT({
email: key.client_email,
key: key.private_key,
scopes: ["https://www.googleapis.com/auth/androidpublisher"],
});
const playDeveloperApiClient = google.androidpublisher({
version: "v3",
auth: authClient,
});
interface Data {
subscriptionId: string;
purchaseToken: string;
packageName: string;
}
export const handlePurchases = onCall((async (request: CallableRequest<Data>)) => {
const subscriptionId: string = request.data?.subscriptionId; // send from client
const purchaseToken: string = request.data?.purchaseToken; // send from client
const packageName: string = request.data?.packageName; // send from client
if (!subscriptionId || !purchaseToken || !packageName) {
return {
status: 400,
message: "Please pass all the data",
};
}
try {
await authClient.authorize();
const res = await playDeveloperApiClient.purchases.subscriptions.get({
packageName: packageName,
subscriptionId,
token: purchaseToken,
});
const currentTime = new Date().getTime();
if (res?.data?.expiryTimeMillis && currentTime < parseInt(res.data.expiryTimeMillis)) {
return {
status: 200,
message: "Verification Successful!",
};
}
} catch (error) {
// eslint-disable-next-line no-console
console.log(error);
}
return {
status: 400,
message: "Failed to verify subscription, Try again!",
};
})
7. Call firebase function from your client (React Native) and you’re done
- From your React Native app, send the necessary data to your Firebase Function. This data includes the subscription ID, package name, and purchase token.
try {
const validatePurchasesCallable =
functions().httpsCallable("handlePurchases");
const response = await validatePurchasesCallable({
subscriptionId: monthlyNotepadSubscription[0],
purchaseToken: purchase.purchaseToken,
packageName: "com.techbuzz.notepad",
});
if (response.data.status != 200) {
return;
}
if (purchase.purchaseStateAndroid === 1 && !purchase.isAcknowledgedAndroid) {
await acknowledgePurchaseAndroid({
token: purchase.purchaseToken,
});
}
if (purchase.transactionReceipt) {
await finishTransaction({
purchase: purchase,
isConsumable: false,
});
}
} catch (_) {
console.log("Something went wrong")
}
NOTE: You may get this error on your server side “The current user has insufficient permissions to perform the requested operation.”
Solution:
You may also check the whole page to find many other solutions too!
If it helped you then don’t forget to clap :)