Monday, November 18, 2019

app-debug.apk test using emulator avd command prompt

START EMULATOR

C:\Users\home>emulator -list-avds
Pixel_XL_API_22

C:\Users\home>emulator -avd Pixel_XL_API_22
emulator: WARNING: encryption is off
Warning: Quick Boot / Snapshots not supported on this machine. A CPU with EPT +
UG features is currently needed. We will address this in a future release.
emulator: WARNING: Not all modern X86 virtualization features supported, which i
ntroduces problems with slowdown when running Android on multicore vCPUs. Settin
g AVD to run with 1 vCPU core only.
emulator: WARNING: Your GPU drivers may have a bug. Switching to software render
ing.
HAX is working and emulator runs in fast virt mode.


RUN APK on EMULATOR

C:\Users\home>adb devices
List of devices attached
emulator-5554   device


C:\Users\home>adb -e install -r D:\app-debug.apk
Performing Push Install
D:\app-debug.apk: 1 file pushed. 35.9 MB/s (19761823 bytes in 0.525s)
        pkg: /data/local/tmp/app-debug.apk
Success

C:\Users\home>

Thursday, November 7, 2019

Security with HTTPS and SSL socket client creating with Trusted Certificate, KeyStore, bks type

private fun createHttpClient(): HttpClient {
        var httpClient: HttpClient? = null        try {
            val is_unsafe = AppConfig.current.getApiItem("unsafe").toBoolean()
            val requestConfig = RequestConfig.custom().setCircularRedirectsAllowed(true).build()

            var trustAllCerts = arrayOf<TrustManager>()
            val sslContext = SSLContext.getInstance(ConstVariables.SSLContext_TLS)
            if (is_unsafe) {
                trustAllCerts = arrayOf<TrustManager>(object : X509TrustManager {
                    override fun getAcceptedIssuers(): Array<X509Certificate> = arrayOf()
                    override fun checkClientTrusted(certs: Array<X509Certificate>, authType: String) = Unit
                    override fun checkServerTrusted(certs: Array<X509Certificate>, authType: String) = Unit
                })
                sslContext.init(null, trustAllCerts, SecureRandom())
            } else {
                var keyStore = App.getTrustedKeyStore()
                var trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm())
                trustManagerFactory.init(keyStore)
//                val keyManagerFactory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm())//                keyManagerFactory.init(keyStore, null)                sslContext.init(null, trustManagerFactory.trustManagers, SecureRandom())
            }

            var sslsf = SSLConnectionSocketFactory(sslContext, NoopHostnameVerifier.INSTANCE)
            var socketFactoryRegistry = RegistryBuilder.create<ConnectionSocketFactory>()
                    .register("https", sslsf).build()//.register("http", PlainConnectionSocketFactory())
            var connectionManager = BasicHttpClientConnectionManager(socketFactoryRegistry)
            httpClient = HttpClients.custom().setDefaultRequestConfig(requestConfig)
                    .setConnectionManager(connectionManager).build()
        } catch (e: Exception) {
            e.printStackTrace()
        }
        return httpClient as HttpClient
    }
**********************************************************************
private fun makeSocket(roomInfo: JSONObject): Socket? {
        if (mSocket != null)
            return mSocket        else {
            try {
                val is_unsafe = AppConfig.current.getApiItem("unsafe").toBoolean()
                val bot_timeout = AppConfig.current.getApiItem("bot_timeout").toLong()

                var trustAllCerts = arrayOf<TrustManager>()
                val sslContext = SSLContext.getInstance(ConstVariables.SSLContext_TLS)
                var trustManager: X509TrustManager? = null                if (is_unsafe) {
                    trustAllCerts = arrayOf<TrustManager>(object : X509TrustManager {
                        override fun getAcceptedIssuers(): Array<X509Certificate> = arrayOf()
                        override fun checkClientTrusted(certs: Array<X509Certificate>, authType: String) = Unit
                        override fun checkServerTrusted(certs: Array<X509Certificate>, authType: String) = Unit
                    })
                    sslContext.init(null, trustAllCerts, SecureRandom())

                    trustManager = trustAllCerts.get(0) as X509TrustManager
                } else {
                    var keyStore = App.getTrustedKeyStore()
                    var trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm())
                    trustManagerFactory.init(keyStore)
//                    val keyManagerFactory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm())//                    keyManagerFactory.init(keyStore, null)                    sslContext.init(null, trustManagerFactory.trustManagers, SecureRandom())

                    trustManager = trustManagerFactory.trustManagers.get(0) as X509TrustManager
                }

                //val trustManager = trustManagerFactory.trustManagers.get(0) as X509TrustManager                val okHttpClient = OkHttpClient.Builder().hostnameVerifier(NoopHostnameVerifier.INSTANCE)
                        .sslSocketFactory(sslContext.getSocketFactory(), trustManager)
                        .cookieJar(makeCookieJar())
                        .connectTimeout(CONNECT_TIMEOUT, TimeUnit.SECONDS)
                        .readTimeout(bot_timeout, TimeUnit.SECONDS).writeTimeout(bot_timeout, TimeUnit.SECONDS)
                        .build()

                val options = IO.Options()
                //options.query = ("authtoken=" + AppSharedPrefrence.getString(this, App..AUTH_TOKEN))                options.forceNew = AppConfig.current.getApiItem("bot_forceNew").toBoolean()
                options.secure = AppConfig.current.getApiItem("bot_secure").toBoolean()
                options.callFactory = okHttpClient
                options.webSocketFactory = okHttpClient
                options.transports = arrayOf(WebSocket.NAME, Polling.NAME)
                val botUrl = UrlHelper.botUrl("room-" + roomInfo.getInt("id"))
                mSocket = IO.socket(botUrl, options)
            } catch (e: Exception) {
                e.printStackTrace()
            }
        }
        return mSocket    }
**********************************************************************
fun getTrustedKeyStore(): KeyStore {
    if (trusted != null) return trusted as KeyStore
    val caInput = resource.openRawResource(R.raw.server)
    val cf = CertificateFactory.getInstance("X.509")
    val ca: Certificate
    try {
        ca = cf.generateCertificate(caInput)
        System.out.println("ca=" + (ca as X509Certificate).getSubjectDN())
    } finally {
        caInput.close()
    }

    // Create a KeyStore containing our trusted CAs    val keyStoreType = KeyStore.getDefaultType()
    val keyStore = KeyStore.getInstance(keyStoreType)
    keyStore.load(null, null)
    keyStore.setCertificateEntry("ca", ca)
    if (keyStoreType == ConstVariables.KeyStore_BKS) {
        return keyStore
    } else {
        var host = AppConfig.current.getApiItem("host")
        var hi = host.indexOf(':')
        var hip = host.substring(0, hi)
        var hips = hip.split('.')
        var ks = "ks_" + hips[2] + "_" + hips[3]
        var rid = ResourceHelper.getResourceId(ks, "raw")
        trusted = KeyStore.getInstance(ConstVariables.KeyStore_BKS)
        var ins = if (AppConfig.isApiRunatDevMode()) resource.openRawResource(rid)
        else resource.openRawResource(R.raw.key_store)//key_store.bks        try {
            trusted?.load(ins, null)
        } finally {
            ins.close()
            return trusted as KeyStore
        }
    }
}

Hide automatically the navigation bar in code behind using custom library with OnSystemUiVisibilityChangeListener

package mn.ho.mypoker.libraries.views

import android.os.Handler
import android.view.View
import android.view.Window


class SystemUiVisibilityHider(w: Window) : View.OnSystemUiVisibilityChangeListener {
    private var window: Window = w
    private var isFlagHideSystemUI = false    private var flagHideSystemUI = (View.SYSTEM_UI_FLAG_LAYOUT_STABLE            or View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION            or View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN            or View.SYSTEM_UI_FLAG_HIDE_NAVIGATION            or View.SYSTEM_UI_FLAG_FULLSCREEN            or View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY)

    init {
        this.window.decorView.setOnSystemUiVisibilityChangeListener(this)
    }

    override fun onSystemUiVisibilityChange(visibility: Int) {
        if (this.window.decorView.systemUiVisibility != flagHideSystemUI) {
            Handler().postDelayed({                isFlagHideSystemUI = false            }, 1000)
        }
    }

    fun run() {
        if (this.window.decorView.systemUiVisibility != flagHideSystemUI) {
            isFlagHideSystemUI = true            this.window.decorView.systemUiVisibility = flagHideSystemUI        }
    }
}

on Activity

override fun onTouchEvent(event: MotionEvent?): Boolean {
    sysNavHider?.run()
    return super.onTouchEvent(event)
}

how to install app-debug.apk file into android phone using command prompt shell

#-t is test only
#-r is reinstall
adb devices
List of devices attached
06157df6848f6a14        device
LGMS55017965c0d device
f89c9b8f        device

adb push app-debug.apk /tmp/
pm install /tmp/app-debug.apk
#if Failure [INSTALL_FAILED_INVALID_URI]

#then use android phone shell command
adb -s 06157df6848f6a14 shell
pm install -t -r /sdcard/Download/app-debug.apk

or using Terminal Emulator App


Monday, July 29, 2019

Making Node.js service always alive on Ubuntu Server

Making file /etc/systemd/system/hello_world.service , this would be the main file as a configuration to SystemD service
[Unit]
Description=Node.js Hello World Http Server
[Service]
PIDFile=/tmp/hello_world-99.pid
User=<Your Username>
Group=<Your User Group>
Restart=always
KillSignal=SIGQUIT
WorkingDirectory=/home/<username>/hello_world/
ExecStart=/usr/bin/node /home/<username>/hello_world/server.js
[Install]
WantedBy=multi-user.target

sudo systemctl enable hello_world.service
sudo systemctl start|stop|status hello_world.service

Friday, July 26, 2019

boroo\.AndroidStudio3.4\config\studio64.exe.vmoptions

-server
-Xms1024m
-Xmx2048m
-XX:MaxPermSize=250m
-XX:ReservedCodeCacheSize=240m
-XX:+UseConcMarkSweepGC
-XX:SoftRefLRUPolicyMSPerMB=50
-Dsun.io.useCanonCaches=false
-Djava.net.preferIPv4Stack=true
-Djdk.http.auth.tunneling.disabledSchemes=""
-Djna.nosys=true
-Djna.boot.library.path=
-da

Friday, July 19, 2019

How to Install CodeIgniter PHP Framework on Ubuntu 18.04 LTS

<VirtualHost *:80>
 #ServerAdmin admin@yourdomain.com
 #ServerName yourdomain.com
 DocumentRoot /home/boroo/ftp/projects/codeigniter
 <Directory /home/boroo/ftp/projects/codeigniter/>
   Options +FollowSymLinks
   AllowOverride All
   Order allow,deny
   allow from all
 </Directory>
 ErrorLog /var/log/apache2/codeigniter-error_log
 CustomLog /var/log/apache2/codeigniter-access_log common
</VirtualHost>
linux server deer apache garaar tohiruulj baihad rewrite mod ajillahgui tohioldol baival htacces doorhi tohirgood hiigeerei, support ssl+rewrite mod. result: https://domain/subdir/api
#on apache2
#Options Indexes FollowSymLinks Includes ExecCGI
#AllowOverride All
<IfModule mod_rewrite.c>
RewriteEngine on
# Redirect Trailing Slashes If Not A Folder...
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(.*)/$ /$1 [L,R=301]
RewriteCond %{HTTPS} off
RewriteRule ^(.*)$ https://%{HTTP_HOST}%{REQUEST_URI} [L,R=301]
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^(.*)$ index.php/$1 [L,QSA]
</IfModule>

Sunday, March 24, 2019

nodejs require('net') cluster server emit connection to express app with http, https modules website chat bot

app.js



var cluster = require('cluster'), net = require('net');
var port = process.env.PORT || 8080;
var num_processes = process.env.WORK || require('os').cpus().length;
var fs = require('fs');
var options = {
pauseOnConnect: true,
key: fs.readFileSync(__dirname + '/private/server.key', 'utf8'),
cert: fs.readFileSync(__dirname + '/private/server.crt', 'utf8'),
requestCert: true,
rejectUnauthorized: false
}
if (cluster.isMaster) {
// This stores our workers. We need to keep them to be able to reference
// them based on source IP address. It's also useful for auto-restart, for example.
var workers = [];

// Helper function for spawning worker at index 'i'.
var spawn = function (i) {
workers[i] = cluster.fork();

// Optional: Restart worker on exit
workers[i].on('exit', function (code, signal) {
console.log('respawning worker', i);
spawn(i);
});
};

// Spawn workers.
for (var i = 0; i < num_processes; i++) {
spawn(i);
}

// Helper function for getting a worker index based on IP address.
// This is a hot path so it should be really fast. The way it works
// is by converting the IP address to a number by removing non numeric
// characters, then compressing it to the number of slots we have.
//
// Compared against "real" hashing (from the sticky-session code) and
// "real" IP number conversion, this function is on par in terms of
// worker index distribution only much faster.
var worker_index = function (ip, len) {
var s = '';
for (var i = 0, _len = ip.length; i < _len; i++) {
if (!isNaN(ip[i])) {
s += ip[i];
}
}
return Number(s) % len;
};

// Create the outside facing server listening on our port.
var server = net.createServer({ pauseOnConnect: true }, function (connection) {
// We received a connection and need to pass it to the appropriate
// worker. Get the worker for this connection's source IP and pass
// it the connection.
var worker = workers[worker_index(connection.remoteAddress, num_processes)];
worker.send('sticky-session:connection', connection);
}).listen(port);

console.log('Master server running on port: ' + port);
} else {
/*nodepro process running...*/
var core = require('./nodepro/core/start');
//if not use options with ssl then ERR_SSL_PROTOCOL_ERROR
var server = core.run(options);

// Listen to messages sent from the master. Ignore everything else.
process.on('message', function (message, connection) {
if (message !== 'sticky-session:connection') {
return;
}
//console.log(message, connection.remoteAddress);

// Emulate a connection event on the server by emitting the
// event with the connection the master sent us.
server.emit('connection', connection);

connection.resume();
});

console.log('ProcId : ' + process.pid + ' is listening to all incoming requests...');
}



nodepro/core/start.js



/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/


exports.run = function (credentials) {
// don't use yet in BOT
//var loader = require('./loader').instance();
// don't use yet
/*
var app = require('express')();
var server = require('http').Server(app);
var io = require('socket.io')(server);
var redis = require('socket.io-redis');
var http = app.listen(80, function () {
console.log('ProcId : ' + process.pid + ' is listening to all incoming requests...');
});
*/

// Note we don't use a port here because the master listens on it for us.
var express = require('express'), path = require('path'),
http = require('http'), https = require('https');
//var app = express.createServer(credentials);
var app = new express();
// Don't expose our internal server to the outside.
var server = null;
if (credentials == undefined)
server = http.createServer(app);
else
server = https.createServer(credentials, app);
//var server = app.listen();

/*app use modules[web]*/
//var web_core = loader.module('web');
//web_core.init(express, app);
//or
app.use(express.static('public'));

/*server use modules[chat]*/
/*
if (loader.options.modules.chat.enable) {
var chat = loader.module('chat');
chat.init(server, app);
}
*/
/*server use modules[gamebot]*/
//var bot = loader.module('gamebot');
var bot = require('../modules/gamebot');
bot.init(server, app);

return server;
};



nodepro/modules/web.js


exports.init = function (express, app) {
var loader_package = require('../core/loader');
var loader = loader_package.instance();
var cookieParser = require('cookie-parser');
var bodyParser = require('body-parser');
var sys_config = loader.config('sys.config');
var _opt = loader.options;

/*app.configuring*/
//set NODE_ENV=production&&node app.js
function debuggerModule(req, res, next) {
return next();
}
// debugging middleware in development only
if ('development' === app.get('env')) {
process.env.NODE_TLS_REJECT_UNAUTHORIZED = "0";
app.use(debuggerModule);
}
// process.env['CONSUMER_KEY'] = ""
// process.env['CONSUMER_SECRET'] = ""
// process.env['ACCESS_TOKEN_KEY'] = ""
// process.env['ACCESS_TOKEN_SECRET'] = ""

app.use(bodyParser.urlencoded({ extended: _opt.fw.urlencoded_extended }));// parse application/x-www-form-urlencoded
app.use(bodyParser.json());// parse application/json
app.set('json spaces', 1);
app.use(cookieParser());
app.use(_opt.res.static_path, express.static(loader.static_dir()));

if (_opt.fw.jade_engine) {
app.engine('jade', require('jade').__express);
app.set('view engine', 'jade');
}
if (_opt.fw.disable_view_cache) {
app.disable('view cache');
} else {
app.set('view cache', true);
}
if (_opt.fw.disable_etag) {
app.disable('etag');
}
if (_opt.fw.use_compression) {
var compression = require('compression');
app.use(compression());
}
if (_opt.fw.use_helmet) {
var helmet = require('helmet');
app.use(helmet());
app.use(helmet.noCache());
app.use(helmet.frameguard());
} else {
app.disable('x-powered-by');
}
if (_opt.fw.use_session) {
var sessionLib = loader.lib('session/' + _opt.fw.session_lib);
sessionLib.configure(express, app, loader);
}

/*app.routing*/
function init_loader_base(area_item, app_area) {
var new_loader = loader_package.instance();
var area_name = new_loader._comm.trim(app_area.mountpath, '/');
var base = {};
base.bag = {};
base.loader = new_loader;
base.area = area_name;
base.area_object = area_item;
base.app = app_area;
new_loader._base = base;
return new_loader._base;
}
function parallel(middlewares) {
var async = require("async");
return function (req, res, next) {
async.each(middlewares, function (mw, cb) {
mw(req, res, cb);
}, next);
};
}
function headers(req, res, next) {
res.header('Access-Control-Allow-Credentials', true);
res.header('Access-Control-Allow-Origin', req.headers.origin);
res.header('Access-Control-Allow-Methods', 'GET,PUT,POST,DELETE');
res.header('Access-Control-Allow-Headers', 'X-Requested-With, X-HTTP-Method-Override, Content-Type, Accept');
}
function routing(req, res, next) {
console.log('routing..................');
headers(req, res, next);
var base = init_loader_base(undefined, app);
var router_package = base.loader.core('router');
var instance = router_package();
instance.route(base, req, res, next);
}
// Router middleware, mentioned it before defining routes.
/*areas*/
sys_config.areas.forEach(function (area_item) {
var app_area = new express();
app.use(area_item.uri, app_area);
app_area.use(function (req, res, next) {
headers(req, res, next);
var base = init_loader_base(area_item, app_area);
var router_package = base.loader.core('router');
var instance = router_package();
instance.route(base, req, res, next);
});
});
/*main*/
if (_opt.fw.use_parallel) {
app.use(parallel([routing]));
} else {
app.use(routing);
}
};



nodepro/modules/gamebot.js


exports.init = function (server, app) {
//var loader_package = require('../core/loader');
//var loader = loader_package.instance();
var socketio = require('socket.io');
//var socket_redis = require('socket.io-redis');
var io = socketio(server);
//var _opt = loader.options;
// Tell Socket.IO to use the redis adapter. By default, the redis
// server is assumed to be on localhost:6379. You don't have to
// specify them explicitly unless you want to change them.
//io.adapter(socket_redis({ host: 'localhost', port: 6379 }));
var botio = io.of('/bot'/*_opt.modules.gamebot.uri*/);

var uuidv4 = require('uuid/v4');
var rooms = {};
var sockets = {};
botio.on('connection', function (socket) {
//console.log('- io.sockets.server.engine.clientsCount = ', io.sockets.server.engine.clientsCount, ' -> ', sockets, ';');

socket.emit('detect', /* send to client */
{ uuid: uuidv4() }
); // emit an event to the socket
socket.on('context', function (data) { /* */
if (rooms[data.context] == undefined) {
rooms[data.context] = {};
}
rooms[data.context][data.player_id] = socket;
sockets[socket.id] = [data.context, data.player_id];
console.log('Connected:: ', data.context, ', ', data.player_id);
});
socket.on('message', function (data) { /* */
var room = rooms[data.context];
var players = Object.keys(room);
players.forEach(function (player_id) {
var friend_socket = room[player_id];
friend_socket.emit('message', data);
});
//var sockets_keys = Object.keys(sockets);
//console.log('sockets.length is ', sockets_keys.length);
//botio.emit('message', data);
// emit an event to all connected sockets
}); // listen to the event

socket.on('disconnect', function () {
var meta = sockets[socket.id];
console.log('(meta != undefined) is ', (meta != undefined));
if (meta != undefined) {
var context = meta[0];
var player_id = meta[1];
console.log('context.length is ', context, ', ', Object.keys(rooms[context]).length);
delete sockets[socket.id];
delete rooms[context][player_id];
console.log('context.length is ', context, ', ', Object.keys(rooms[context]).length);
}
var sockets_keys = Object.keys(sockets);
console.log('sockets.length is ', sockets_keys.length);
console.log('Disconnected:: ', socket.id);
});
});
};



public/index.html
.

<!DOCTYPE html>
<html>

<head>
<script src="https://code.jquery.com/jquery-1.12.4.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/socket.io/2.2.0/socket.io.js"></script>
</head>

<body>
<script>
$(function () {
function getOrContextId() {
return 'room1';
}
$('#txt').val(window.location);
var my_id = undefined;
var socket = io(window.location + 'bot');
socket.on('detect', function (data) {
my_id = "ID:" + data.uuid;
socket.emit('context', { context: getOrContextId(), player_id: my_id });
$('ul').append($('<li>').html('Me-' + my_id + ': i connected'));
});
socket.on('message', function (data) {
if (data.text !== undefined) {
if (my_id !== data.player_id)
$('ul').append($('<li>').html('Friend-' + data.player_id + ': ' + data.text));
else
$('ul').append($('<li>').html('Me-' + my_id + ': ' + data.text));
}
});
$('#btn').click(function () {
socket.emit('message', {
context: getOrContextId(),
player_id: my_id,
text: $('#txt').val()
});
});
});
</script>
<input type="text" id="txt">
<a id="btn" href="javascript:void(0);">Send</a>
<ul>
<li></li>
</ul>
</body>

</html>


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!');
            }
..........................
}