Guides
Log In
Guides

Instructions d’intégration

Vue d'ensemble

Ce scénario vous guide dans l’intégration des terminaux Moneris Go avec votre système de gestion de restaurant (RMS) afin de prendre en charge Pay-at-Table. L’objectif est de permettre aux serveurs de récupérer une liste de leurs tables ouvertes, de consulter les détails de table, d’accepter les paiements par carte (y compris les pourboires) à la table et de mettre automatiquement à jour le RMS. Cela améliore l’efficacité, élimine les erreurs manuelles et améliore l’expérience de paiement des clients.

À la fin, votre RMS sera en mesure de communiquer avec l’API RMS de Moneris et les terminaux Moneris Go pour compléter un flux de paiement en personne.


Compatibilité des appareils

Go Pay-at-Table est disponible sur les terminaux Moneris Go suivants:


📘

Remarque

Si vous n’avez pas accès à l’un des terminaux de test ci-dessus, cliquez ci-dessous pour commander un terminal de test Moneris Go et des cartes de test.

Commander un terminal


Aperçu du cas d’utilisation

Dans de nombreux restaurants, les serveurs doivent accepter les paiements directement à la table du client. Ce scénario utilise les appareils Android Moneris Go exécutant l’application Go Pay-at-Table pour rendre cela possible.

Le terminal Moneris Go agit comme un pont entre le RMS du marchand et l’infrastructure de paiement de Moneris :

  • Le RMS se connecte à l’API cloud RMS de Moneris pour envoyer et recevoir des données de tables/paiements.
  • L’application Moneris Go Pay-at-Table fonctionne sur le terminal Android et permet aux clients de finaliser leurs transactions par carte à la table.

📘

Note

Chaque message reçu ou envoyé par le RMS comporte un en-tête de deux octets devant la charge utile JSON, en ordre des octets réseau (big-endian), contenant la longueur du message.

Consultez twoBytesHeaderAppenderand twoBytesHeaderTrimmer dans la section de l’exemple de code complet.


Les taches clés que votre intégration doit prendre en charge :

  1. Connexion du RMS à Moneris (authenticate)
  2. Maintien de la connexion à l’aide d’Echo
  3. Récupération des tables du serveur (getTables)
  4. Obtention des informations de note pour une table sélectionnée (getTable)
  5. Application du paiement et mise à jour du RMS (applyPayment)

Prérequis

  • Un terminal Moneris Go avec Pay-at-Table installé
  • Votre merchantId (Identifiant du marchand) et apiToken (Jeton d’API) fournis par Moneris
  • terminalId (Identifiant du terminal) configuré pour votre appareil
  • Un serveur RMS qui implémente la structure de messages RMS
  • Une liste de tables ouvertes disponible dans votre RMS et interrogeable par serverId (Identifiant du serveur)

📘

Configuration de l’application

Pour télécharger et configurer l’application Pay-at-Table

cliquez ci-dessous

pour consulter l’article du Centre d’aide


1. Authentifier le RMS avec Moneris

Objectif:

Avant que toute donnée de table ou de paiement puisse être échangée, votre RMS doit s’authentifier auprès de Moneris. Cela se produit lorsqu’une connexion socket est ouverte entre votre implémentation RMS et le serveur cloud de Moneris. La requête authenticate (authentifier) enregistre vos informations d’identification marchand et établit une session sécurisée. Une seule connexion par MerchantId (Identifiant du marchandMID) est autorisée. Toutes les tentatives de connexion ultérieures seront rejetées s’il existe déjà une session active avec le même MID.


Entrées requises:

  • merchantId (Identifiant du marchand) (V13) : Votre numéro unique de marchand Moneris à 13 chiffres, commence par 0030
  • apiToken (Jeton d’API) (V50) : Identifiant sécurisé fourni par Moneris –
  • cloudApiVersion (Version API cloud) (V20) : Typiquement “1.0” –
  • requestId (Identifiant de requête) (V50) : Identifiant unique combinant ID du terminal, horodatage et numéro de séquence
  • requestTimestamp (Horodatage de requête) (F19) : Horodatage ISO de la requête (UTC)

Exemple de requête :


{
  "action": "authenticate",
  "apiToken": "P14i5WS4P0uhgbrnN7BZ",
  "cloudApiVersion": "1.0",
  "merchantId": "1234567890",
  "requestId": "I9000001-20250707-001",
  "requestTimestamp": "2025-07-07 10:00:00"
}

Sortie attendue:

Une réponse réussie retourne un status (Statut) de “200” avec votre merchantId (Identifiant du marchand), confirmant que vous êtes authentifié.

{
  "status": "200",
  "statusDesc": "OK"
}

📘

Commande authenticate (authentifier)

Afficher les spécifications complètes de l’API pour cette commande.

Afficher les spécifications


2. Maintenir la connexion en utilisant Echo (Écho)

Objectif:

Une fois authentifié, votre RMS doit maintenir la session active en envoyant un message echo (écho) toutes les 30 secondes. Si aucun écho n’est reçu, Moneris fermera la connexion.


Entrées requises:

Utilisez les mêmes en-têtes d’authentification que dans l’étape 1 :

  • merchantId (Identifiant du marchand)
  • apiToken (Jeton d’API)
  • cloudApiVersion (Version API cloud)
  • requestId (Identifiant de requête)
  • requestTimestamp (Horodatage de requête)

Exemple de requête:

{
  "action": "echo",
  "apiToken": "P14i5WS4P0uhgbrnN7BZ",
  "cloudApiVersion": "1.0",
  "merchantId": "1234567890",
  "requestId": "I9000001-20250707-002",
  "requestTimestamp": "2025-07-07 10:00:30"
}

Sortie attendue:

Même structure que la réponse authenticate (authentifier) — confirmant que la session est active.


📘

Commande echo (écho)

Afficher les spécifications complètes de l'AP| pour cette commande.

Afficher les spécifications


3. Récupérer une liste de tables ouvertes

Objectif:

Après que le serveur se soit connecté au terminal (par saisie d’ID ou balayage de carte), l’application Go Pay-at-Table envoie une requête getTables (obtenir les tables) au RMS pour récupérer les tables ouvertes de ce serveur. Ce sont les tables dont il est responsable, et chaque table inclut le total actuel et le solde restant.

Cet appel API est utilisé immédiatement après la connexion ou à tout moment où un serveur souhaite afficher les tables actives sous son profil. Il est généralement déclenché lors du chargement de l’écran principal de l’interface Go Pay-at-Table.


Entrées requises:

  • terminalId (Identifiant du terminal) (V8) : ID unique attribué au terminal
  • merchantId (Identifiant du marchand) (V13)
  • apiVersion (Version de l’API) (V20) : ex., “1.0”
  • requestId (Identifiant de requête)
  • requestTimestamp (Horodatage de requête)
  • serverId (Identifiant du serveur) (V50) : L’ID saisi ou balayé sur le terminal

Exemple de requête:


{
  "terminalId": "I9000001",
  "merchantId": "1234567890",
  "apiVersion": "1.0",
  "requestId": "I9000001-20250707-003",
  "requestTimestamp": "2025-07-07 10:01:00",
  "action": "getTables",
  "data": {
    "server": {
      "serverId": "1234"
    }
  }
}

Sortie attendue:

Un tableau d’objets table (table), chacun comprenant :

  • tableId (Identifiant de la table) – utilisé pour la recherche
  • tableName (Nom de la table) – affiché sur le terminal (ex., « Patio-4 », « Main Hall-7 »)
  • tableTotalAmount (Montant total de la table) – solde complet en cents
  • tableRemainingAmount (Montant restant de la table) – montant restant à payer en cents


{
  "status": "200",
  "data": {
    "tables": [
      {
        "tableId": "T123",
        "tableName": "Patio-4",
        "tableTotalAmount": 6400,
        "tableRemainingAmount": 6400
      },
      {
        "tableId": "T124",
        "tableName": "Main Hall-7",
        "tableTotalAmount": 8200,
        "tableRemainingAmount": 3200
      }
    ]
  }
}

📘

Commande getTables (obtenir les tables)

Afficher les spécifications complètes de l'AP| pour cette commande.

Afficher les spécifications


4. Récupérer les détails de l’addition d’une table

Objectif:

Une fois qu’une table est sélectionnée, l’application Go Pay-at-Table envoie une requête getTable (obtenir la table) au RMS pour récupérer tous les détails de paiement pour cette table.


Entrées requises:

  • tableId (Identifiant de la table) de l’étape précédente
  • serverId (Identifiant du serveur) (facultatif si balayé)
  • terminalId (Identifiant du terminal), merchantId (Identifiant du marchand), apiVersion (Version API), etc.

REMARQUE : Les méthodes de division par montant et par addition/place ne sont prises en charge que si elles sont activées via le menu Paramètres de l’application Go Pay-at-Table.


Exemple de requête:


{
  "terminalId": "I9000001",
  "merchantId": "1234567890",
  "apiVersion": "1.0",
  "requestId": "I9000001-20250707-004",
  "requestTimestamp": "2025-07-07 10:01:10",
  "action": "getTable",
  "data": {
    "server": {
      "serverId": "1234"
    },
    "tableId": "T123"
  }
}

Sortie attendue:

Includes:

Comprend :

  1. splitMethod (Méthode de division) : 1 = addition complète, 2 = division par montant, 3 = division par addition/place
  2. masterCheck (Addition principale) : solde total
  3. checks[] (Additions séparées) optionnelles : si le RMS a déjà divisé l’addition par invité


{
  "status": "200",
  "data": {
    "table": {
      "tableName": "Patio-4",
      "tableTotalAmount": 6400,
      "splitMethod": 1,
      "masterCheck": {
        "checkId": "1",
        "checkName": "Full Table",
        "totalAmount": 6400,
        "remainingAmount": 6400
      }
    }
  }
}

📘

Commande getTable (obtenir la table)

Afficher les spécifications complètes de l'AP| pour cette commande.

Afficher les spécifications


Gestion des paiements divisés

Le flux Pay-at-Table prend en charge trois modes de paiement, déterminés par le champ splitMethod (Méthode de division) renvoyé dans la réponse getTable (obtenir la table) :


splitMethod (Méthode de division)Description
1Addition complète (par défaut)
2Division par montant
3Division par addition/place

Division par montant (splitMethod = 2)

Division par addition/place (splitMethod = 3)

Ce mode permet à plusieurs invités de payer des montants personnalisés sur une addition partagée. L’application Go Pay-at-Table affiche un écran permettant aux serveurs ou aux clients de saisir le montant partiel du paiement.

Étapes :

  1. Recevoir splitMethod = 2 dans la réponse getTable (obtenir la table)

  2. Inviter l’utilisateur à saisir un montant partiel de paiement.

  3. Pour chaque paiement :

    1. Définir checkId (Identifiant de l’addition) sur l’addition principale (ex. « 1 »)
    2. Utiliser paidAmount (Montant payé) pour indiquer la portion réglée
    3. Envoyer une requête applyPayment (appliquerPaiement) comme d’habitude
  4. Répéter jusqu’à ce que remainingAmount (Montant restant) et tableRemainingAmount (Montant restant de la table) atteignent 0.

Ce mode est utilisé lorsque la table est divisée en additions séparées pour chaque invité/place (ex. « Addition 1 – Jean », « Addition 2 – Marie »).

Étapes :

  1. Recevoir splitMethod = 3 et un tableau checks[] (Additions séparées) dans la réponse getTable (obtenir la table)

  2. Afficher la liste des additions au serveur ou au client.

  3. Pour chaque addition :

    1. Utiliser l’checkId (Identifiant de l’addition) approprié
    2. Appliquer un paiement en utilisant applyPayment (appliquerPaiement) uniquement pour cette addition
  4. Répéter le processus pour chaque addition impayée.


Important:

Une addition/place est considérée comme réglée lorsque son remainingAmount (Montant restant) est 0. La table complète est fermée lorsque toutes les additions/places sont payées.

Cette gestion supplémentaire garantit que votre RMS prend en charge toute la gamme des scénarios réels de restauration — des factures de groupe aux additions séparées — tout en maintenant une intégration cohérente avec la plateforme Moneris Go.


Exemple de requête:


{
  "terminalId": "I9000001",
  "merchantId": "1234567890",
  "apiVersion": "1.0",
  "requestId": "I9000001-20250707-004",
  "requestTimestamp": "2025-07-07 10:01:10",
  "action": "getTable",
  "data": {
    "server": {
      "serverId": "1234"
    },
    "tableId": "T123"
  }
}

Sortie attendue:


{
  "terminalId": "I4123456",
  "merchantId": "00312312312312",
  "configCode": "C12345678SI",
  "requestId": "I4123456-2025-08-18-002",
  "responseTimestamp": "2025-08-18 10:01:00",
  "status": "200",
  "statusDesc": "OK",
  "data": {
    "table": {
      "tableId": "01",
      "tableName": "Table 1",
      "tableTotalAmount": 4200,
      "tableRemainingAmount": 4200,
      "splitMethod": 3,
      "masterCheck": {
        "checkId": "1",
        "checkName": "Full Table",
        "totalAmount": 4200,
        "preTaxAmount": 0,
        "remainingAmount": 4200,
        "receipt": {
          "taxes": [],
          "discounts": [],
          "lineItems": []
        }
      },
      "checks": [
        {
          "checkId": "1",
          "checkName": "Check 1",
          "totalAmount": 2000,
          "preTaxAmount": 0,
          "gratuity": 0,
          "remainingAmount": 2000,
          "receipt": {
            "taxes": [],
            "discounts": [],
            "lineItems": []
          }
        },
        {
          "checkId": "2",
          "checkName": "Check 2",
          "totalAmount": 2200,
          "preTaxAmount": 0,
          "remainingAmount": 2200,
          "receipt": {
            "taxes": [],
            "discounts": [],
            "lineItems": []
          }
        }
      ]
    }
  }
}

5. Appliquer un paiement au PDV (POS)

Objectif:

Après que le client a effectué un paiement par carte (pourboire inclus) à l’aide du terminal Moneris Go, l’application Go Pay-at-Table envoie une requête applyPayment (appliquerPaiement) afin de finaliser la transaction et de mettre à jour le total de l’addition.


Entrées requises:

  • tableId (Identifiant de la table), checkId (Identifiant de l’addition) : de l’étape
  • tenderType (Type de paiement) : “CARD” ou “CASH”
  • Si CARD : vous devez inclure :
  • cardType (Type de carte) (VISA, MC, AMEX, etc.)
  • authNumber (Numéro d’autorisation) : code d’approbation
  • referenceNumber (Numéro de référence) : ID de transaction
  • lastDigits (Derniers chiffres) : 4 derniers chiffres du PAN
  • paidAmount (Montant payé) : montant approuvé en cents
  • tipAmount (Montant du pourboire) : pourboire optionnel en cents

Exemple de requête:

Remarque : paidAmount (Montant payé) inclut tipAmount (Montant du pourboire).


{
  "terminalId": "I9000001",
  "merchantId": "1234567890",
  "apiVersion": "1.0",
  "requestId": "I9000001-20250707-005",
  "idempotencyKey": "74ae1696-b1e3-4328-af6d-f1e04d947a13",
  "requestTimestamp": "2025-07-07 10:02:00",
  "action": "applyPayment",
  "data": {
    "server": {
      "serverId": "1234"
    },
    "check": {
      "tableId": "T123",
      "checkId": "1"
    },
    "payment": {
      "tenderType": "CARD",
      "card": {
        "cardType": "VISA",
        "authNumber": "183235",
        "referenceNumber": "1020293",
        "lastDigits": "1234"
      },
      "paidAmount": 5400,
      "tipAmount": 1000
    }
  }
}

Sortie attendue:

remainingAmount (Montant restant) et tableRemainingAmount (Montant restant de la table) doivent diminuer en conséquence. Lorsque les deux atteignent 0, la table est considérée comme fermée.


{
  "status": "200",
  "data": {
    "table": {
      "tableId": "T123",
      "tableRemainingAmount": 0
    },
    "check": {
      "checkId": "1",
      "remainingAmount": 0
    }
  }
}

📘

Commande applyPayment (appliquer un paiement)

Afficher les spécifications complètes de l'AP| pour cette commande.

Afficher les spécifications


Additional Notes

  • Tous les champs monétaires (paidAmount, tipAmount, totalAmount) sont exprimés en cents.
  • Si tableRemainingAmount (Montant restant de la table) > 0 après paiement, le même flux peut être répété jusqu’à règlement complet.
  • Les erreurs (ex. : ID de table invalide ou champ manquant) sont renvoyées avec un status (Statut) et un statusDesc (Description du statut).

6. Exemple de code complet

Ci-dessous se trouve l’exemple complet de code en JavaScript


import tls from 'tls';

let displayecho = true;

let intervalConnect = null;
let cloud_sock = null;
const ActionType = {
    echo: 'echo',
    authenticate: 'authenticate',
    getTables: 'getTables',
    getTable: 'getTable',
    applyPayment: 'applyPayment',
};

const errCodesMsgsMap = {
    '218': 'Unsupported action\tAction not supported.',
    '600': 'Internal Error',         // 600
    '601': 'Incorrect Msg Length',   // 601
    '602': 'JSON Parse Error',       // 602
    '603': 'Missing Server ID',      // 603
    '604': 'Invalid Server ID',      // 604
    '605': 'Invalid Server Tracks',  // 605
    '606': 'Missing Table ID',       // 606
    '607': 'Invalid Table ID',       // 607
    '608': 'Missing Check ID',       // 608
    '609': 'Invalid Check ID',       // 609
    '610': 'Missing Payment Obj',    // 610
    '611': 'Invalid Payment Obj',    // 611
    '612': 'Missing IdempotencyKey', // 612
    '613': 'Invalid split method',   // 613
    '614': 'Server Not Found',       // 614
    '615': 'No Table Found',         // 615
    '616': 'No Check Found'          // 616
};
const options = {
    autoconnect: true,
    reconnection: true,
    reconnectionDelay: 1000,
    reconnectionDelayMax: 2000,
    reconnectionAttempts: Infinity
};
const cloudSever = {
    host: 'patposct.moneris.com',
    port: 443
}


const getMID = function () {
    return '0030128923365';  // votre MID (identifiant marchand)
};

const getCfgCode = function () {
    return "C000497ECP";
};

const getApiToken = function () {
    return 'C7jZF1ad7IIGCoEIMGoD';
}
let getRequestId = function () {
    let now = new Date();
    return getMID() + '-' + now.toISOString();
};
let getDateTime = function () {
    const now = new Date();
    return now.toISOString().replace('T', '-').substring(0, 19);
};
// gérer "echo" (écho) toutes les 30 secondes
function startEchoInterval(cloud_sock) {
   
    // S'assurer de ne pas démarrer plusieurs intervalles
    if (cloud_sock.echoInterval) {
        clearInterval(cloud_sock.echoInterval);
    }

    cloud_sock.echoInterval = setInterval(() => {
        if (cloud_sock.gotEchoNoResponse === 0) {
            if (!cloud_sock.echoTimeout) {
                // définir 10 s comme délai d’attente pour recevoir la réponse echo (réponse d’écho)
                cloud_sock.echoTimeout = setTimeout(() => {
                    console.info('Timeout, closing connection....');
                    clearInterval(cloud_sock.echoInterval);
                    clearTimeout(cloud_sock.echoTimeout);
                    cloud_sock.echoTimeout = null;
                    cloud_sock.end();
                }, 10000);  
            }
            return;
        }

        // Construire la requête echo (écho)
        let echoRequest = {
            action: 'echo',
            apiToken: getApiToken(),
            cloudApiVersion: '1.0',
            merchantId: getMID(),
            requestId: getRequestId(),
            requestTimestamp: getDateTime()
        };

        console.info(
            '\x1b[32m',
            'Sending Echo Request: ' + new Date().toString() +
            '\n' + JSON.stringify(echoRequest, null, 2)
        );

        cloud_sock.gotEchoNoResponse = 0;
        let sendbuf = twoBytesHeaderAppender(echoRequest);
        cloud_sock.write(sendbuf);  // envoyer echo (écho)

    }, 30000); // toutes les 30 s
}

function stopEchoInterval(cloud_sock) {
    if (cloud_sock.echoInterval) {
        clearInterval(cloud_sock.echoInterval);
        cloud_sock.echoInterval = null;
    }
    if (cloud_sock.echoTimeout) {
        clearTimeout(cloud_sock.echoTimeout);
        cloud_sock.echoTimeout = null;
    }
}
// construire la réponse gettables (getTables)
function getTables(request) {

    let requestId = request.requestId;
    let session = request.session;
    let requestServerId = request.data.server.serverId; // serverId (identifiant du serveur)
    let requestTrack2;
    let tId = request.terminalId;
    let mId = request.merchantId;
    let response = {};
    response = {
        'terminalId': tId,
        'merchantId': mId,
        'configCode': getCfgCode(),
        'requestId': requestId,
        'responseTimestamp': getDateTime(),
        'session': session
    }
    if (request.data.server.serverId != undefined)
        requestServerId = request.data.server.serverId;

    if (request.data.server.trackData != undefined)
        requestTrack2 = request.data.server.trackData.track2;

    if (requestServerId === undefined && requestTrack2 === undefined) {
        response.status = "603";
        response.statusDesc = errCodesMsgsMap[response.status];
        console.info("unable to determine the serverId", request.data.server.serverId);

        return response;
    }

    // par exemple, votre serverId (identifiant du serveur) = 1234, et il y a 3 tables pour ce serverId (identifiant du serveur)
    if (requestServerId == '1234') {
        response = {
            ...response,
            'data': {
                'tables': [
                    {
                        'tableId': '00',
                        'tableName': '0020',
                        'tableTotalAmount': 15000,
                        'tableRemainingAmount': 15000
                    },
                    {
                        'tableId': '01',
                        'tableName': 'Alphabets',
                        'tableTotalAmount': 9500,
                        'tableRemainingAmount': 9500
                    },
                    {
                        'tableId': '02',
                        'tableName': 'DiningRoom7',
                        'tableTotalAmount': 24000,
                        'tableRemainingAmount': 24000
                    }
                ]
            },
            'status': '200',
            'statusDesc': 'OK'
        }
    } else {
        response.status = '614';
        response.statusDesc = errCodesMsgsMap[response.status];
    }
    return response;
}
// construire la réponse gettable (getTable)
function getTable(request) {
    let requestId = request.requestId;
    let session = request.session;
    let requestServerId = request.data.server.serverId; // serverId (identifiant du serveur)
    let tableId;
    let tId = request.terminalId;
    let mId = request.merchantId;
    let response = {};

    response = {
        'terminalId': tId,
        'merchantId': mId,
        'configCode': getCfgCode(),
        'requestId': requestId,
        'responseTimestamp': getDateTime(),
        'session': session
    }
    if (request.data.server.serverId != undefined) {
        requestServerId = request.data.server.serverId;
    } else {
        response.status = "603";
        response.statusDesc = errCodesMsgsMap[response.status];
        console.info("unable to determine the serverId", request.data.server.serverId);

        return response;
    }
    if (request.data.tableId != undefined) {
        tableId = request.data.tableId;
    } else {
        response.status = "606";
        response.statusDesc = errCodesMsgsMap[response.status];
        return response;
    }

    // par exemple, votre serverId (identifiant du serveur) = 1234, et il y a 3 tables pour ce serverId (identifiant du serveur)
    if (requestServerId == '1234') {
        // dans cet exemple de code, retourner les mêmes détails pour tableId (identifiant de table) '00' et '01' ; un code d’erreur 615 sera retourné pour tableId (identifiant de table) '03'.
        if ((tableId == '00') || (tableId == '01')) {
            response = {
                ...response,
                'data': {
                    'table': {
                        'masterCheck': {
                            'receipt': {
                                'taxes': [],
                                'discounts': [],
                                'lineItems': []
                            },
                            'checkId': '1',
                            'checkName': 'Full Table',
                            'totalAmount': 15000,
                            'preTaxAmount': 14000,
                            'remainingAmount': 15000
                        },
                        'checks': [],
                        'tableId': '00',
                        'tableName': '0020',
                        'tableTotalAmount': 15000,
                        'tableRemainingAmount': 15000,
                        'splitMethod': 1
                    }
                },
                'status': '200',
                'statusDesc': 'OK'
            }
        }
        else {
            response.status = '615';
            response.statusDesc = errCodesMsgsMap[response.status];
        }

    } else {
        response.status = '614';
        response.statusDesc = errCodesMsgsMap[response.status];
    }
    return response;
}
// traiter le paiement, mettre à jour les informations de paiement dans votre base de données
function processPayment(request) {
    let requestId = request.requestId;
    let session = request.session;
    let requestServerId = request.data.server.serverId; // serverId (identifiant du serveur)
    let tableId, checkId; // tableId (identifiant de table), checkId (identifiant de l’addition)
    let tId = request.terminalId;
    let mId = request.merchantId;
    let response = {};

    response = {
        'terminalId': tId,
        'merchantId': mId,
        'configCode': getCfgCode(),
        'requestId': requestId,
        'responseTimestamp': getDateTime(),
        'session': session
    }
    if (request.data.server.serverId != undefined) {
        requestServerId = request.data.server.serverId;
    } else {
        response.status = "603";
        response.statusDesc = errCodesMsgsMap[response.status];
        console.info("unable to determine the serverId", request.data.server.serverId);

        return response;
    }
    if (request.data.check.tableId != undefined) {
        tableId = request.data.check.tableId;
    } else {
        response.status = "606";
        response.statusDesc = errCodesMsgsMap[response.status];
        return response;
    }
    if (request.data.check.checkId != undefined) {
        checkId = request.data.check.checkId;
    } else {
        response.status = "608";
        response.statusDesc = errCodesMsgsMap[response.status];
        return response;
    }
    if (request.data.payment != undefined) {
        // traiter l’objet de paiement (payment)
    } else {
        response.status = "611";
        response.statusDesc = errCodesMsgsMap[response.status];
        return response;
    }
    // tous les montants doivent être vos montants réels
    response = {
        ...response,
        'status': '200',
        'statusDesc': 'OK',
        'data': {
            'check': {
                'checkId': checkId,
                'checkName': 'checkName',
                'totalAmount': 15000,
                'preTaxAmount': 14000,
                'remainingAmount': 10000
            },
            'table': {
                'tableId': tableId,
                'tableName': '0020',
                'tableTotalAmount': 15000,
                'tableRemainingAmount': 10000
            }
        }
    }
    return response;
}
let connectToRMSCloud = function (cloudSever, options) {

    console.info('connectToRMSCloud');
    // connexion TLS au cloud RMS de Moneris
    cloud_sock = tls.connect(cloudSever, options, function () {
        console.info('connected to options: ', cloudSever.host, ' port: ', cloudSever.port);
        // requête d’authentification
        let authReq = {
            action: 'authenticate',
            apiToken: getApiToken(), // votre apiToken (jeton d’API)
            cloudApiVersion: '1.0',
            merchantId: getMID(),
            requestId: getRequestId(),
            requestTimestamp: getDateTime()
        };
        console.info('send to cloud:\n ', JSON.stringify(authReq, null, 2));
        let sendbuf = twoBytesHeaderAppender(authReq);
        cloud_sock.write(sendbuf);

        cloud_sock.on('close', onClose);
        cloud_sock.on('data', onData);
        cloud_sock.on('error', onError);
        cloud_sock.on('timeout', onTimeout);
    });
}

let onClose = function () {
    // traiter si la connexion est fermée
    console.info('cloud onClose ', new Date().toString());
    stopEchoInterval(cloud_sock);
    
};


let onData = async function (dataWith2Bytes) {
    //
    let response = [], sendbuf = [];
    let [isErrored, data] = twoBytesHeaderTrimmer(dataWith2Bytes);  // convertir uniquement en JSON
    if (isErrored) {
        cloud_sock.end();
        return;
    }

    if (data.action == ActionType.echo) {
        console.info('\x1b[32m', ' rev echo:\n' + JSON.stringify(data, null, 2)); // rev echo (réception d’écho)
    } else {
        console.info('rev:\n' + JSON.stringify(data, null, 2)); // rev (réception)
    }
    let status = parseInt(data.status);
    switch (data.action) {
        case ActionType.authenticate:  // traiter la réponse à l’action authenticate (authentifier)
            let status = parseInt(data.status);
            if (status === 200) { // bon statut
                console.info('Authenticate response OK, SetInterval for sending Echo Request');
                // définir l’intervalle d’écho toutes les 30 s
                startEchoInterval(cloud_sock);
             
            } else {
                console.info('\x1b[31m', 'Authenticate response failed: ' + data.status + ' ' + 'Desc: ', data.statusDesc);
                cloud_sock.end();

            }
            break;
        case ActionType.echo:  // traiter la réponse à l’action authenticate (authentifier)

            if (cloud_sock.echoTimeout != undefined) {

                clearTimeout(cloud_sock.echoTimeout);
            }
            cloud_sock.gotEchoNoResponse = 1;
            break;
        case ActionType.getTables:  // traiter la requête d’action getTables (obtenir les tables)

            response = getTables(data);
            sendbuf = twoBytesHeaderAppender(response);
            cloud_sock.write(sendbuf);
            console.info('response on getTables:' + new Date().toString() + '\n' + JSON.stringify(response, null, 2));
            break;
        case ActionType.getTable:// traiter la requête d’action getTable (obtenir la table)

            response = getTable(data);
            sendbuf = twoBytesHeaderAppender(response);
            cloud_sock.write(sendbuf);
            console.info('response on getTables:' + new Date().toString() + '\n' + JSON.stringify(response, null, 2));
            break;
        case ActionType.applyPayment: // traiter la requête d’action applyPayment (appliquer le paiement)
            response = processPayment(data);
            sendbuf = twoBytesHeaderAppender(response);
            cloud_sock.write(sendbuf);
            console.info('response on getTables:' + new Date().toString() + '\n' + JSON.stringify(response, null, 2));
            break;
        default:

            response = {
                'terminalId': data.terminalId,
                'merchantId': data.merchantId,
                'configCode': getCfgCode(),
                'requestId': data.requestId,
                'responseTimestamp': getDateTime(),
                'session': data.session,
                'status': '218',
                'statusDesc': errCodesMsgsMap['218']
            }
            sendbuf = twoBytesHeaderAppender(response);
            cloud_sock.write(sendbuf);
            console.info('unkonwn action: \n', JSON.stringify(response, null, 2));
            break;
    }
};

let onError = function (e) {
    console.info('cloud onError', e);
    cloud_sock.destroy();
 
};


let onTimeout = function () {
    console.info('onTimeout, end session');
    cloud_sock.end();
 
};


// ajouter 2 octets de longueur de message au début
const twoBytesHeaderAppender = function (dataObject) {

    let bufContent;

    bufContent = Buffer.from(JSON.stringify(dataObject), 'utf8');

    let bufTwoBytesHeader = Buffer.alloc(2);
    bufTwoBytesHeader.writeUInt16BE(bufContent.length, 0);

    let buf = Buffer.concat([bufTwoBytesHeader, bufContent]);

    return buf;
};

// obtenir le message du cloud Moneris
const twoBytesHeaderTrimmer = function (data) {

    let isErrored = true;
    let message = '';
    if (data.length <= 2) {

        return [isErrored, message];
    }

    let dataLen = data.readUInt16BE(0);
    if (dataLen !== data.length - 2) {
        console.log(dataLen);
        console.log(data.toString());

        return [isErrored, message];
    }

    try {
        message = JSON.parse(data.slice(2).toString());
        isErrored = false;

        return [isErrored, message];
    } catch (e) {
        console.error('JSON parse error', e);
        console.error('JSON onParseError', data.toString());

        return [isErrored, message];
    }
};


/***************************************************************************
 *                              Se connecter au cloud RMS                  *
 ***************************************************************************/
connectToRMSCloud(cloudSever, options);
/*************************************************************************
 *                       Configuration des gestionnaires d’événements     *
 *************************************************************************/