Sunday, March 10, 2019

facebook instant game custom server communication Nodejs, PHP

game.js

const BACKEND_URL = "https://localhost/quizbattle";
const BACKEND_URL_API = BACKEND_URL + "/api";
var game = {};
var gameData = {};
var gameOptions = {
    width: 0,
    height: 0,
    backgroundColor: 0xFFFFFF,
    localStorageName: "__ab__"
};
var gameStuff = {
    playerId: "",
    playerName: "",
    playerPic: "",
    locale: ""
};

window.onload = function () {
    $('#home-section').hide();

    FBInstant.initializeAsync().then(function () {
        FBInstant.startGameAsync().then(function () {
            gameStuff.playerId = FBInstant.player.getID();
            gameStuff.playerName = FBInstant.player.getName();
            gameStuff.playerPic = FBInstant.player.getPhoto();
            gameStuff.locale = FBInstant.getLocale();
            gameStuff.contextType = FBInstant.context.getType();

            gameOptions.height = window.innerHeight;
            gameOptions.width = window.innerWidth / 2;

            $('#u-photo-field').attr('src', gameStuff.playerPic);
            $('#u-name-field').html(gameStuff.playerName);
            $('#context-type').html(gameStuff.contextType);

            $('#home-section').show();
           
            loadingInitialData();
        });
    });
};

function getOrCreateContextId() {
    var contextType = FBInstant.context.getType();
    var contextId = FBInstant.context.getID();
   
    console.log(contextType);
    if (contextType == 'SOLO') {
        contextId = FBInstant.player.getID() + '_SOLO';
    }
    return contextId;
}

function showMessage(div, message) {
    document.getElementById(div).innerHTML = message;
}

function loadingInitialData() {
    var contextId = getOrCreateContextId();

    FBInstant.player.getSignedPlayerInfoAsync(contextId)
            .then(function (signedPlayerInfo) {
                // Requesting data from backend passing the signature as an argument
                // The backend APP can retrieve contextId from the signature
                var signature = signedPlayerInfo.getSignature();
                return new backendClient(BACKEND_URL_API).load(contextId, signature);
            })
            .then(function (response) {
                // Got successful response from backend
                console.log('Loaded from backend', response);
                if (response.empty) {
                    showMessage('test', 'No data');
                } else {
                    showMessage('test', JSON.stringify(response.data));
                }
            }.bind(this))
            .catch(function (error) {
                // Got error response from backend
                console.error('Not loaded from backend', error);
                showMessage('error-messages', 'Error loading backend data:' + error.message);
            }.bind(this));
}

function singlePlayBoot() {

}

function multiPlayBoot() {

}

function leaderBoardBoot() {

}

function helpBoot() {

}


node.js server side


var pg = require('pg');
var crypto = require('crypto-js');

module.exports = function(app) {

app.post('/get-match', function(request, response) {
        var signature = request.body.signature;
       
        var isValid = validate(signature);
       
        if (isValid) {
            var contextId = getEncodedData(signature);
            loadMatchDataAsync(contextId)
            .then(function(result){
                if (result) {
                    response.json({'success':true, 'contextId':contextId, 'empty': false, 'data':result});
                } else {
                    response.json({'success':true, 'contextId':contextId, 'empty': true});
                }
            })
            .catch(function(err){
                response.json({'success':false, 'error':err});
            });
        } else {
            console.log('encoded data', getEncodedData(signature));
            response.json({'success':false, 'error':'invalid signature'});
        }
       
    })

loadMatchDataAsync = function(contextId) {
        return new Promise(function(resolve, reject){
            pg.connect(process.env.DATABASE_URL, function(err, client, done) {
                client.query('SELECT * FROM matches WHERE context = $1::text', [contextId], function(err, result) {
                    done();
                    if (err) {
                        reject(err);
                    }
                    if (result.rows.length > 0) {
                        resolve(result.rows[0].data);
                    } else {
                        resolve();
                    }
                });
            });
        });
    };
   
    validate = function(signedRequest) {
        try{
           
            var firstpart = signedRequest.split('.')[0];
            var replaced = firstpart.replace(/-/g, '+').replace(/_/g, '/');
            var signature = crypto.enc.Base64.parse(replaced).toString();
            const dataHash = crypto.HmacSHA256(signedRequest.split('.')[1], process.env.APP_SECRET).toString();
            var isValid = signature === dataHash;
            if (!isValid) {
                console.log('Invalid signature');
                console.log('firstpart', firstpart);
                console.log('replaced ', replaced);
                console.log('Expected', dataHash);
                console.log('Actual', signature);
            }
           
            return isValid;
        } catch (e) {
            return false;
        }
    };
   
    getEncodedData = function(signedRequest) {
        try {
           
            const json = crypto.enc.Base64.parse(signedRequest.split('.')[1]).toString(crypto.enc.Utf8);
            const encodedData = JSON.parse(json);
           
            /*
            Here's an example of encodedData can look like
            {
                algorithm: 'HMAC-SHA256',
                issued_at: 1520009634,
                player_id: '123456789',
                request_payload: 'backend_save'
            }
            */
           
            return encodedData.request_payload;
        } catch (e) {
            return null;
        }
    };
}


PHP server side


private function _validate($signedRequest) {
        try {
            list($encoded_signature, $payload) = explode('.', $signedRequest, 2);

            // decode the data
            $signature = base64_url_decode($encoded_signature);
            //$data = json_decode(base64_url_decode($payload), true);
           
            // confirm the signature
            $expected_signature = hash_hmac('sha256', $payload, $this->facebook_config['app_secret'], $raw = true);
            $isValid = $signature === $expected_signature;

            if (!$isValid) {
                error_log('Bad Signed JSON signature!');
                return null;
            }

            return $isValid;
        } catch (Exception $ex) {
            return false;
        }
    }

    private function _getEncodedData($signedRequest) {
        try {
            $encodedData = json_decode(base64_url_decode(explode('.', $signedRequest)[1]), true);
            /*
            $encodedData = {
                "status":true,
                "data":{
                    "algorithm":"HMAC-SHA256",
                    "issued_at":1552157956,
                    "player_id":"2522121991191915",
                    "request_payload":"2522121991191915_SOLO"
                }
            }
             */

            return $encodedData;
        } catch (Exception $ex) {
            return null;
        }
    }


function load_post() {

$signature = $this->post('signature');

            $isValid = $this->_validate($signature);

            if ($isValid) {
                $edata = $this->_getEncodedData($signature);
                $this->success($edata->data);
            } else {
                $this->error('Invalid Request!');
            }
..........................
}

No comments: