Skip to content

Instantly share code, notes, and snippets.

@tichif
Last active January 23, 2021 00:31
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save tichif/2a045112f863e19c05f526b022dfad9a to your computer and use it in GitHub Desktop.
Save tichif/2a045112f863e19c05f526b022dfad9a to your computer and use it in GitHub Desktop.
Utlisation de l'API Moncash dans un MERN environnement

Moncash API - Mern

Avant de commencer, je tiens à préciser que :

  • Ce code est open-source, n'importe quelle tiers personne peut l'utiliser ou suivre la logique.
  • L'authentification du client n'est pas pris en compte dans ce code, seulement la partie Moncash.
  • Cette version utilise la synthaxe then()/catch() pour résoudre les promesses (Promise), dans une version à venir, elle sera remplacée par la synthaxe Async/Await.
  • Ce code est destiné aux intermédiares et experts en MERN technologies.

Pour tout remarque ou suggestion, nous contacter par email : charleshebertdalzon@gmail.com

Ce code a été développé par Dalzon Charles-Hébert et par Fontin Eleazar Eliothson.

Partie Node JS

Après avoir créé un projet dans la partie Business dans le sandbox de moncash, deux clés son générées :

  • client id
  • client secret

En se référant à la documentation de Moncash, il faut d'abord installer le sdk:

npm install --save https://github.com/ecelestin/ecelestin-Moncash-sdk-nodejs

Ensuite, il faut créer deux fichiers. L'un dans le dossier controllers si vous en avez un, moncash.js (dans ce fichier, on ajoutera toute la logique de l'application pour la partie moncash) et l'autre dans le dossier routes, moncashRoutes.js (dans ce fichier, en fonction de l'url et la methode, le serveur choisira la méthode prédéfinie).

routes/moncashRoutes.js

const express = require('express');

const moncashController = require('./controllers/moncash');

const router = express.Router();

// @route   POST /api/moncash/payment/
// @desc    Procéder au payment
router.post('/payment', moncashController.processPayment);

// @route   GET /api/moncash/transaction/:transactionID
// @desc    Vérifier la transaction
router.get(
  '/transaction/:transactionID',
  moncashController.checkPayment
);

module.exports = router;

controllers/moncash.js

Tout d'abord il faut importer le sdk de Moncash dans le fichier. Nous aurons aussi besoin de générer des numéros uniques que nous aurons à envoyer vers les serveurs de Moncash.

const moncash = require('nodejs-moncash-sdk');
const { v4: uuidv4 } = require('uuid'); // générateur de numéro unique
require('dotenv').config(); // lire les variables dans le fichier .env

Il faut maintenant configurer moncash, en lui passant plusieurs options tel que (mode, les différentes clés réçues lors de la création du projet dans le sandbox de Moncash)

moncash.configure({
  mode: process.env.MONCASH_MODE, //sandbox or live
  client_id: process.env.MONCASH_CLIENT_ID,
  client_secret: process.env.MONCASH_CLIENT_SECRET,
});

NB : Petit conseil, mettez tous vos informations privées dans un fichier .env que vous ne publierez pas sur Github. Cela permettra non seulement de sécuriser vos infos et ensuite si vous devriez changer certaines informations, vous n'aurez qu'à le faire dans le fichier .env.

// Procéder à la transaction
exports.processPayment = (req, res, next) => {
  const create_payment_json = {
    amount: req.body.amount, // la quantité d'argent que Moncash aura à taxer au client
    orderId: uuidv4(), // définition d'un orderId unique via le package uuid que Moncash aura à sauvegarder dans sa base de données.
  };

  const payment_creator = moncash.payment;
  payment_creator.create(create_payment_json, function (err, payment) {
    if (err) {
      console.log(err);
      return res.status(500).json({
        error: err,
      });
    } else {
      res.status(200).json(payment_creator.redirect_uri(payment));
    }
  });
};

Si les informations données sont correctes, l'API de Moncash génerera comme réponse un lien. C'est à partir de ce lien que vous dirigerez l'utilisateur vers le site de moncash pour procéder au payment.

// Retourner les informations de la transaction via l'id de transaction
exports.checkPayment = (req, res, next) => {
  const transactionId = req.params.transactionID;

  moncash.capture.getByTransactionId(transactionId, function (err, data) {
    if (err) {
      return res.status(500).json({
        error: err,
      });
    } else {
      res.status(200).json(data);
    }
  });
};

Cette portion permet de vérifier s'il existe, dans les serveurs de Moncash, un payment relatif à l'id de transaction reçu. Si oui, elle retourne les informations sinon, elle retourne un message d'erreur.

Partie React JS

Si vous avez décidé d'intégrer l'API de Moncash dans votre application, c'est pour proposer un service payant. Dans votre partie cliente c'est-à-dire dans la partie React, vous disposez d'un endroit où le client puisse payer. Dans notre exemple, il s'agit d'un bouton sur lequel :

  • est écrit Payer avec Moncash
  • existe une action qui permet à notre partie cliente (React) de communiquer avec notre serveur (Node JS).

Pour faire plus simple, nous appelerons notre fameux bouton, Bouton Moncash 😁😁😁 et se trouve dans un composant (Component) appelé Boutique.

NB : Lors de la création du projet dans le sandbox de Moncash, il faut spécifier 3 éléments :

Nous créons 2 fichiers en React : api.js (facultatif), Boutique.js (obligatoire)

Afin de ne pas surcharger notre composant Boutique, nous implémenterons les méthodes, en utilisant fetch(), qui à travers d'api communiqueront avec notre server

api.js

const SERVER_API = 'http://localhost:5000/api'

export const payWithMoncash = (amount) => {
  return fetch(`${SERVER_API}/moncash/payment`, {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
    },
    body: JSON.stringify(amount),
  })
    .then((res) => res.json())
    .catch((err) => console.log(err));
};

export const checkTransactionId = (transactionId) => {
  return fetch(`${SERVER_API}/moncash/transaction/${transactionId}`, {
    method: 'GET',
    headers: {
      Accept: 'application/json',
    },
  })
    .then((res) => res.json())
    .catch((err) => console.log(err));

Boutique.js

Nous installons query-string qui nous permettra de retrouver les query strings dans les urls de notre application (par exemple: http://localhost:3000/boutique?transactionId=123456789)

import React, { useEffect, useState } from 'react';
import queryString from 'query-string';

import { payWithMoncash, checkTransactionId } from './api';

const Boutique = ({ location }) => {
  const [amount, setAmount] = useState(150); //150 gdes

  const checkMoncashTransactionId = () => {
    const parsedInfo = queryString.parse(location.search);
    const transactionId = parsedInfo.transactionId || undefined;

    if (transactionId) {
      checkTransactionId(transactionId)
        .then((res) => {
          if (res.error) {
            // Afficher erreur si le code est incorrect ou n'exite pas
            console.log(res.error);
          } else {
            // Verifier le message de retour est successful et le montant payé est égal au prix que le cient devait payer
            if (
              res.payment.message === 'successful' &&
              res.payment.cost === amount
            ) {
              //code
              // proceder a l'enregistrement dans la base de donnees
            } else {
              console.log('Erreur !!!');
            }
          }
        })
        .catch((err) => console.log(err));
    }
  };

  useEffect(() => {
    checkMoncashTransactionId();
  }, []);

  // Payer avec Moncash
  const pay = () => {
    payWithMoncash(amount)
      .then((res) => {
        if (res.error) {
          console.log(res.error);
        } else {
          window.location.href = res;
        }
      })
      .catch((err) => console.log(err));
  };
  return (
    <div>
      <button onClick={pay}>Payer with monCash</button>
    </div>
  );
};

export default Boutique;

Expliquons ce que nous faisons de faire.

Premièrement, nous avons créé une fonction qui vérifie, si il existe une paramètre dans l'url qui s'appelle transactionId. Si oui, la variable transactionId reçoit la valeur sinon la variable transactionId est non-défini. Ensuite, si la variable transactionId est définie, on passe cette valeur dans la fonction checkTransactionId() qui a été implémentée dans le fichier api.js, qui retourne soit une erreur, soit des informations relatives à la transaction via la variable transactionId.Si la fonction checkTransactionId() retourne une erreur, on affiche cette erreur dans la console. Ensuite, nous vérifions si le montant payé correspond au montant qui devait être payé et si la transaction a été un succès. Si oui, l'exécution du code continue, sinon on retourne un message d'erreur.

Pourquoi vérifier si la transaction a été un succès et si les montants correspondent ? C'est très simple, en fonction de l'addresse de retour que nous avions fourni lors de la création du projet dans le sandbox, Moncash utilise cette adresse et ajoute comme paramètre dans l'URL, transactionId ainsi que sa valeur.Le retour qui se fait est public, tout le monde peut y accéder et le modifier. En admettant, qu'une personne modifie la valeur du paramètre, et malheureusement ce code existe et le montant payé est inférieur ou supérieur au montant qu'elle devait payer, sans le vouloir, nous aurons autorisé cette personne à poursuivre son achat, etc. Donc il faut une double vérification.

Continuons, la fonction checkMoncashTansactionId est appelé dans useEffect() qui est une react hook qui s'exécute qu'une seule fois , en début de l'interprétation (render()) du composant Boutique, à cause du tableau vide passé en 2ème paramètre.

Dans le bouton Payer avec Moncash, nous avons inséré une action qui s'exécute à chaque fois que l'utilisateur clique sur le bouton. Cette action appelle la fonction payWithMoncash() qui accepte le montant à payer comme paramètre. En fonction du résultat, une erreur sera affichée dans la console ou l'utilisateur sera redirigé directement sur le site de Moncash.

@Eliothson
Copy link

for all comments and suggestions. contact us

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment