Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Verify a Purchase using Firebase Functions

I want to verify purchases in my app using Firebase Functions and Purchases.products: get But I don't know how to use the authorization Scope from the link or how to build the request in Firebase Functions. This is what I have so far:

const functions = require('firebase-functions');
const admin = require('firebase-admin');
const google = require("googleapis");
const publisher = google.androidpublisher('v2');
admin.initializeApp(functions.config().firebase);

exports.validatePurchases = functions.database
    .ref('/purchases/{uId}/{orderId}')
    .onWrite((event) => {
        const purchase = event.data.val();
        const token = purchase.token;
        const packageName = purchase.package_name;
        const sku = purchase.sku;
        const signature = purchase.signature;
        const uri = "https://www.googleapis.com/androidpublisher/v2/applications/" + packageName + "/purchases/products/" + sku + "/tokens/" + token;

        return TODO;
    });

I have setup mostly everything but my JavaScript knowledge is very limited and don't know how to build the Request and get the Result in Firebase Functions

like image 926
Guanaco Devs Avatar asked Mar 18 '18 09:03

Guanaco Devs


1 Answers

I have not much knowledge with JavaScript, I got to this function partly guessing from all that I have read, I will appreciate corrections, I know there is room for improvement but it does the validation.

This is the function I'm using to validate my purchases:

const functions = require('firebase-functions');
const admin = require('firebase-admin');
const {google} = require("googleapis");
const publisher = google.androidpublisher('v2');
const authClient = new google.auth.JWT({
    email: 'Service Account Email',
    key: '-----BEGIN PRIVATE KEY-----\n**********************************************************************************==\n-----END PRIVATE KEY-----\n',
    scopes: ['https://www.googleapis.com/auth/androidpublisher']
});
admin.initializeApp();

exports.validatePurchases = functions.database
    .ref('/purchases/{uId}/{orderId}')
    .onCreate((event, context) => {
        const purchase = event.val();
        if (purchase.is_processed === true) {
            console.log('Purchase already processed!, exiting');
            return null;
        }
        const orderId = context.params.orderId;
        const dbRoot = event.ref.root;
        const package_name = purchase.package_name;
        const sku = purchase.sku;
        const my_token = purchase.token;

        authClient.authorize((err, result) => {
            if (err) {
                console.log(err);
            }
            publisher.purchases.products.get({
                auth: authClient,
                packageName: package_name,
                productId: sku,
                token: my_token
            }, (err, response) => {
                if (err) {
                    console.log(err);
                }
                // Result Status must be equals to 200 so that the purchase is valid
                if (response.status === 200) {
                    return event.ref.child('is_validated').set(true);
                } else {
                    return event.ref.child('is_validated').set(false);
                }
            });
        });
        return null;
    });

UPDATE: I just found out that when using Promo Codes this will fail as orderId is empty for Promo Codes.

Using Promises

return authClient.authorize()
        // authClient.authorize() returns a credentials Object
        .then(credentials => {
            return publisher.purchases.products.get({
                auth: authClient,
                packageName: packageName,
                productId: sku,
                token: token
            });
        })
        // publisher.purchases.products.get() Returns a axiosResponse object with Purchase data within and the status that should be enough for the validation
        .then(axiosResponse => {
                if (axiosResponse.status === 200 && axiosResponse.data.purchaseState === 0) {
                // Your purchase is valid, do your thing
                } else {
                    return event.ref.set(null);
                }
            })
            .catch(reason => {
                console.log(`Rejection Code: ${reason.code}`);
                console.log(`Rejection Message: ${reason.message}`);
                return event.ref.set(null);
            });

It is my understanding that axiosResponse.status === 200 should be enough for verifying a purchase, note however that axiosResponse.data holds the data from the purchase Schema$ProductPurchase where you can check other values from the Purchase. Which I find interesting if you are using "Licensing Testing" or "Promo Codes". In this sample I'm using axiosResponse.data.purchaseState to check that the purchase is still valid(maybe unnecesary...)

like image 198
Guanaco Devs Avatar answered Oct 19 '22 15:10

Guanaco Devs