Intégrer MonCash avec Flask
Une application Flask complète : créer un paiement, vérifier le statut sur la page de retour et recevoir les confirmations par webhook — le tout sans dépendances tierces pour la partie MonCash.
Commencez en Sandbox
Avant d'implémenter le code ci-dessous avec votre clé live (sk_proj_…), nous recommandons de tester d'abord en mode Sandbox avec sk_test_proj_…. Le code est identique — vous ne changez que la valeur de la variable d'environnement. Lisez le Guide Sandbox avant de continuer.
Prérequis
- Python 3.9+
- pip ou uv
- Un projet MonCashConnect avec clé secrète — créer un projet
Installation
pip install flask moncashconnect python-dotenvOu avec uv : uv add flask moncashconnect python-dotenv
Variables d'environnement
Créez un fichier .env à la racine :
MCC_SECRET_KEY=sk_proj_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
MCC_WEBHOOK_SECRET=whsec_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
APP_URL=https://votresite.com
FLASK_ENV=developmentApplication complète (app.py)
Application Flask fonctionnelle couvrant les trois routes essentielles. La clé est request.get_data() dans la route webhook — elle retourne les octets bruts sans consommer le stream.
from flask import Flask, request, redirect, jsonify, abort
from moncashconnect import MonCashClient, construct_event, MonCashError
from dotenv import load_dotenv
import os
load_dotenv()
app = Flask(__name__)
client = MonCashClient(os.environ["MCC_SECRET_KEY"])
# ── POST /pay ────────────────────────────────────────────────────────
# Corps attendu : {"orderId": "...", "amount": 500}
@app.post("/pay")
def initiate_payment():
data = request.get_json(force=True)
order_id = data.get("orderId")
amount = data.get("amount")
if not order_id or not amount:
abort(400, description="orderId et amount sont requis.")
try:
payment = client.create_payment(
amount=int(amount),
reference_id=str(order_id),
return_url=f"{os.environ['APP_URL']}/pay/return",
)
except MonCashError as exc:
return jsonify({"error": str(exc)}), exc.status_code
return redirect(payment["paymentUrl"])
# ── GET /pay/return ──────────────────────────────────────────────────
# Page de retour — vérifier le statut réel via l'API
@app.get("/pay/return")
def payment_return():
ref = request.args.get("ref")
if not ref:
abort(400, description="Référence manquante.")
try:
tx = client.get_payment_status(ref)
except MonCashError as exc:
return jsonify({"error": str(exc)}), exc.status_code
# Remplacez par render_template("return.html", tx=tx) si vous utilisez Jinja2
return jsonify(tx)
# ── POST /webhooks/moncash ────────────────────────────────────────────
# Pas de @csrf — Flask n'a pas de protection CSRF native sur les routes POST
# La sécurité est assurée par la vérification HMAC-SHA256
@app.post("/webhooks/moncash")
def moncash_webhook():
raw_body = request.get_data() # bytes — AVANT tout parsing
signature = request.headers.get("X-MCC-Signature", "")
timestamp = request.headers.get("X-MCC-Timestamp", "")
try:
event = construct_event(
raw_body,
signature,
timestamp,
os.environ["MCC_WEBHOOK_SECRET"],
)
except MonCashError as exc:
return str(exc), exc.status_code
if event["event"] == "payment.completed":
# Mettez à jour votre BDD ici
print(f"✅ Paiement confirmé : {event['reference']} — {event['amount']} HTG")
elif event["event"] == "payment.failed":
print(f"❌ Paiement échoué : {event['reference']}")
return "OK", 200
if __name__ == "__main__":
app.run(debug=os.environ.get("FLASK_ENV") == "development")Structure avec Blueprints (application plus grande)
Pour une application Flask de taille réelle, organisez les routes MonCash dans un Blueprint :
# payments/blueprint.py
from flask import Blueprint, request, redirect, jsonify
from moncashconnect import MonCashClient, construct_event, MonCashError
import os
payments_bp = Blueprint("payments", __name__, url_prefix="/payments")
client = MonCashClient(os.environ["MCC_SECRET_KEY"])
@payments_bp.post("/pay")
def pay(): ...
@payments_bp.get("/return")
def return_page(): ...
@payments_bp.post("/webhook")
def webhook(): ...
# app.py — enregistrement du blueprint
from flask import Flask
from payments.blueprint import payments_bp
app = Flask(__name__)
app.register_blueprint(payments_bp)Avec cette structure, l'URL webhook devient /payments/webhook. Pensez à mettre à jour la configuration dans le tableau de bord MonCashConnect.
Liste de contrôle avant mise en production
MCC_SECRET_KEY et MCC_WEBHOOK_SECRET sont dans les variables d'environnement du serveur (pas dans le code ou le dépôt Git)
request.get_data() est utilisé dans la route webhook — pas request.json ou request.data après get_json()
FLASK_ENV=production (désactive le mode debug)
Utilisez un serveur WSGI en production : gunicorn app:app (pas flask run)
La route webhook répond toujours 200 après traitement réussi
Votre handler webhook est idempotent — vérifiez si la commande est déjà traitée avant de la modifier
L'URL https://votresite.com/webhooks/moncash est configurée dans le tableau de bord MonCashConnect
flask run en production — c'est le serveur de développement. Utilisez gunicorn app:app ou uvicorn (avec ASGI via Quart si vous passez à async).Autres guides d'intégration