Démarrage rapide
Ce guide est conçu pour les développeurs afin d'intégrer Papi à votre site ou application. Suivez ces étapes pour mettre en place un système de paiement qui fonctionne de manière fluide pour vos clients.
Prérequis
- Votre application est en ligne et prête à accepter les paiements.
- Vous disposez de votre clé API depuis le tableau de bord Papi (voir Où trouver votre clé API ci-dessous).
- Choisissez une URL de redirection après un paiement réussi (
successUrl). - Choisissez une URL de redirection après un paiement échoué (
failureUrl). - Choisissez une URL sur votre serveur qui recevra les notifications de paiement de Papi (
notificationUrl).
Où trouver votre clé API
- Connectez-vous à votre tableau de bord : https://dashboard.papi.mg.
- En haut à droite, cliquez sur l'icône de votre avatar (photo ou initiales).
- Cliquez sur Boutiques dans le menu déroulant.
- Cliquez sur l'application que vous souhaitez utiliser.
- Dans le tableau de bord de l'application, ouvrez l'onglet Développeur.
- Sous Clé API, vous verrez votre clé (une longue chaîne de caractères).
Important : Gardez votre clé API secrète. Toute personne qui la possède peut créer des paiements en votre nom.
Vue d'ensemble du flux
Avant d'entrer dans le détail des étapes, voici comment le flux s'articule :
- Un client passe commande sur votre site.
- Vous créez un lien de paiement sécurisé via Papi.
- Le client est redirigé vers la page de paiement pour finaliser le paiement.
- Papi envoie le résultat du paiement à votre système via votre URL de notification (requête POST).
- Votre système vérifie la notification et met à jour le statut de la commande (succès ou échec).
- Le client voit le résultat sur votre site (succès ou échec).
Étape 1 : Générer un lien de paiement
Pour permettre aux clients de payer, vous devez créer un lien sécurisé qui les envoie vers une page de paiement. Ce lien contient le montant, les informations client et l'URL à laquelle Papi notifiera votre système du résultat du paiement.
Ce que vous devez faire
Envoyez une requête POST à l'endpoint suivant :
POST https://app.papi.mg/dashboard/api/payment-links
Authentification
Chaque requête doit inclure votre clé API dans les en-têtes :
Content-Type: application/json
Token: <VOTRE_CLÉ_API>
Corps de la requête
Envoyez un corps JSON avec les champs requis et optionnels. Exemple :
{
"amount": 15000.0,
"clientName": "Nom du client",
"reference": "ORDER-123",
"description": "Paiement pour la commande #123",
"successUrl": "https://yourapp.com/payment-success",
"failureUrl": "https://yourapp.com/payment-failure",
"notificationUrl": "https://yourapp.com/payment-notify",
"validDuration": 60,
"provider": "MVOLA",
"payerEmail": "customer@example.com",
"payerPhone": "+261340000000",
"testReason": "Tests internes",
"isTestMode": false
}
| Nom du champ | Type | Requis | Description |
|---|---|---|---|
| clientName | string | ✓ | Nom du client. |
| amount | number | ✓ | Montant du paiement (>= 300). |
| reference | string | ✓ | Votre référence unique pour ce paiement. |
| description | string | ✓ | Description du paiement (max 255 caractères). |
| successUrl | string | × | URL de redirection après succès (http(s)://). |
| failureUrl | string | × | URL de redirection après échec (http(s)://). |
| notificationUrl | string | ✓ | URL qui recevra les notifications de paiement (http(s)://). |
| validDuration | integer | × | Durée de validité en minutes (>0). Par défaut : 1. |
| provider | string | × | Un parmi : MVOLA, ARTEL_MONEY, ORANGE_MONEY, BRED. |
| payerEmail | string | × | Adresse e-mail du client. |
| payerPhone | string | × | Numéro de téléphone du client (ex. +261340000000). |
| testReason | string | × | Motif du mode test (si isTestMode=true). |
| isTestMode | boolean | × | Mettre à true pour activer le mode test. |
Réponse en cas de succès
Si la requête est valide, vous recevez :
{
"data": {
"amount": 15000.0,
"currency": "MGA",
"linkCreationDateTime": 1723850012,
"linkExpirationDateTime": 1723853612,
"paymentLink": "https://pay.papi.mg/payment/abc123",
"clientName": "Nom du client",
"paymentReference": "ORDER-123",
"description": "Paiement pour la commande #123",
"successUrl": "https://yourapp.com/payment-success",
"failureUrl": "https://yourapp.com/payment-failure",
"notificationUrl": "https://yourapp.com/payment-notify",
"payerEmail": "customer@example.com",
"payerPhone": "+261340000000",
"notificationToken": "xyz789",
"testReason": "Tests internes",
"isTestMode": false
}
}
paymentLink— Redirigez le client vers cette URL pour qu'il effectue le paiement.notificationToken— Conservez-le pour vérifier ensuite que les notifications sont authentiques.
Réponse en cas d'erreur
En cas de problème, vous pouvez recevoir :
{
"error": {
"code": "VALIDATION_ERROR",
"message": "Le montant est requis"
}
}
Exemples
- PHP
- JavaScript
- Python
- Java
- Go
- NestJS
- React
<?php
$apiUrl = "https://app.papi.mg/dashboard/api/payment-links";
$apiKey = "<VOTRE_CLÉ_API>";
$data = [
"amount" => 15000.0,
"clientName" => "Nom du client",
"reference" => "ORDER-123",
"description" => "Paiement pour la commande #123",
"successUrl" => "https://yourapp.com/payment-success",
"failureUrl" => "https://yourapp.com/payment-failure",
"notificationUrl" => "https://yourapp.com/payment-notify",
"validDuration" => 60,
"provider" => "MVOLA",
"payerEmail" => "customer@example.com",
"payerPhone" => "+261340000000",
];
$options = [
"http" => [
"header" => "Content-Type: application/json\r\nToken: $apiKey\r\n",
"method" => "POST",
"content" => json_encode($data),
],
];
$context = stream_context_create($options);
$response = file_get_contents($apiUrl, false, $context);
if ($response !== false) {
$result = json_decode($response, true);
if (isset($result["data"]["paymentLink"])) {
$paymentLink = $result["data"]["paymentLink"];
$notificationToken = $result["data"]["notificationToken"] ?? null;
// Stocker $notificationToken pour vérifier les notifications plus tard
echo "Lien de paiement : $paymentLink\n";
} else {
echo "Erreur : " . json_encode($result["error"] ?? $result) . "\n";
}
} else {
echo "Échec de la génération du lien de paiement.\n";
}
?>
const response = await fetch("https://app.papi.mg/dashboard/api/payment-links", {
method: "POST",
headers: {
"Content-Type": "application/json",
"Token": "<VOTRE_CLÉ_API>",
},
body: JSON.stringify({
amount: 15000.0,
clientName: "Nom du client",
reference: "ORDER-123",
description: "Paiement pour la commande #123",
successUrl: "https://yourapp.com/payment-success",
failureUrl: "https://yourapp.com/payment-failure",
notificationUrl: "https://yourapp.com/payment-notify",
validDuration: 60,
provider: "MVOLA",
payerEmail: "customer@example.com",
payerPhone: "+261340000000",
}),
});
const data = await response.json();
if (data.data?.paymentLink) {
const paymentLink = data.data.paymentLink;
const notificationToken = data.data.notificationToken;
// Stocker notificationToken pour vérifier les notifications plus tard
console.log("Lien de paiement :", paymentLink);
} else {
console.error("Erreur :", data.error);
}
import requests
url = "https://app.papi.mg/dashboard/api/payment-links"
headers = {
"Content-Type": "application/json",
"Token": "<VOTRE_CLÉ_API>",
}
payload = {
"amount": 15000.0,
"clientName": "Nom du client",
"reference": "ORDER-123",
"description": "Paiement pour la commande #123",
"successUrl": "https://yourapp.com/payment-success",
"failureUrl": "https://yourapp.com/payment-failure",
"notificationUrl": "https://yourapp.com/payment-notify",
"validDuration": 60,
"provider": "MVOLA",
"payerEmail": "customer@example.com",
"payerPhone": "+261340000000",
}
response = requests.post(url, headers=headers, json=payload)
data = response.json()
if "data" in data and "paymentLink" in data["data"]:
payment_link = data["data"]["paymentLink"]
notification_token = data["data"].get("notificationToken")
# Stocker notification_token pour vérifier les notifications plus tard
print("Lien de paiement :", payment_link)
else:
print("Erreur :", data.get("error"))
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
public class PapiPayment {
public static void main(String[] args) throws Exception {
String apiUrl = "https://app.papi.mg/dashboard/api/payment-links";
String apiKey = "<VOTRE_CLÉ_API>";
String body = """
{
"amount": 15000.0,
"clientName": "Nom du client",
"reference": "ORDER-123",
"description": "Paiement pour la commande #123",
"successUrl": "https://yourapp.com/payment-success",
"failureUrl": "https://yourapp.com/payment-failure",
"notificationUrl": "https://yourapp.com/payment-notify",
"validDuration": 60,
"provider": "MVOLA",
"payerEmail": "customer@example.com",
"payerPhone": "+261340000000"
}
""";
HttpClient client = HttpClient.newHttpClient();
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create(apiUrl))
.header("Content-Type", "application/json")
.header("Token", apiKey)
.POST(HttpRequest.BodyPublishers.ofString(body))
.build();
HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
System.out.println("Réponse : " + response.body());
// Parser avec une bibliothèque JSON (ex. Jackson, Gson) pour extraire paymentLink et notificationToken
}
}
package main
import (
"bytes"
"encoding/json"
"fmt"
"io"
"net/http"
)
func main() {
apiURL := "https://app.papi.mg/dashboard/api/payment-links"
apiKey := "<VOTRE_CLÉ_API>"
payload := map[string]interface{}{
"amount": 15000.0,
"clientName": "Nom du client",
"reference": "ORDER-123",
"description": "Paiement pour la commande #123",
"successUrl": "https://yourapp.com/payment-success",
"failureUrl": "https://yourapp.com/payment-failure",
"notificationUrl": "https://yourapp.com/payment-notify",
"validDuration": 60,
"provider": "MVOLA",
"payerEmail": "customer@example.com",
"payerPhone": "+261340000000",
}
jsonBody, _ := json.Marshal(payload)
req, _ := http.NewRequest("POST", apiURL, bytes.NewBuffer(jsonBody))
req.Header.Set("Content-Type", "application/json")
req.Header.Set("Token", apiKey)
client := &http.Client{}
resp, err := client.Do(req)
if err != nil {
fmt.Println("Erreur :", err)
return
}
defer resp.Body.Close()
body, _ := io.ReadAll(resp.Body)
fmt.Println("Réponse :", string(body))
// Utiliser json.Unmarshal pour extraire paymentLink et notificationToken
}
import { Injectable } from '@nestjs/common';
import { HttpService } from '@nestjs/axios';
import { firstValueFrom } from 'rxjs';
@Injectable()
export class PaymentService {
constructor(private readonly httpService: HttpService) {}
async createPaymentLink(): Promise<{ paymentLink: string; notificationToken: string }> {
const { data } = await firstValueFrom(
this.httpService.post(
'https://app.papi.mg/dashboard/api/payment-links',
{
amount: 15000.0,
clientName: 'Nom du client',
reference: 'ORDER-123',
description: 'Paiement pour la commande #123',
successUrl: 'https://yourapp.com/payment-success',
failureUrl: 'https://yourapp.com/payment-failure',
notificationUrl: 'https://yourapp.com/payment-notify',
validDuration: 60,
provider: 'MVOLA',
payerEmail: 'customer@example.com',
payerPhone: '+261340000000',
},
{
headers: {
'Content-Type': 'application/json',
Token: '<VOTRE_CLÉ_API>',
},
},
),
);
// Stocker data.data.notificationToken pour vérifier les notifications plus tard
return {
paymentLink: data.data.paymentLink,
notificationToken: data.data.notificationToken,
};
}
}
// React (frontend) doit appeler votre propre backend — n'exposez jamais votre clé API dans le navigateur.
async function initierPaiement(commande: { montant: number; reference: string }) {
const response = await fetch('/api/creer-paiement', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
amount: commande.montant,
clientName: 'Nom du client',
reference: commande.reference,
description: `Paiement pour la commande ${commande.reference}`,
}),
});
const { paymentLink } = await response.json();
// Rediriger le client vers la page de paiement Papi
window.location.href = paymentLink;
}
Étape 2 : Rediriger le client vers la page de paiement
Une fois le lien de paiement généré, le client doit finaliser le paiement via ce lien.
Ce que vous devez faire
- Extraire
paymentLinkde la réponse de l'étape 1. - Rediriger le client vers cette URL (même onglet, nouvel onglet ou WebView dans une app mobile).
Applications mobiles : Utilisez une WebView ou le navigateur par défaut. Certaines plateformes réinitialisent la WebView lorsque l'app passe en arrière-plan ; gérez ce cas pour ne pas perdre l'état.
Exemples
- PHP
- JavaScript (Express)
- Python (Flask)
- Java (Servlet)
- Go
- NestJS
- React
<?php
// Après avoir obtenu $paymentLink à l'étape 1 :
header("Location: " . $paymentLink);
exit();
?>
// Dans votre route Express, après avoir obtenu paymentLink à l'étape 1 :
app.post('/checkout', async (req, res) => {
const { paymentLink } = await createPaymentLink(req.body); // votre fonction de l'étape 1
res.redirect(302, paymentLink);
});
from flask import redirect
@app.route('/checkout', methods=['POST'])
def checkout():
payment_link = create_payment_link() # votre fonction de l'étape 1
return redirect(payment_link, code=302)
// Dans votre servlet ou contrôleur Spring MVC, après avoir obtenu paymentLink à l'étape 1 :
response.sendRedirect(paymentLink);
// Dans votre gestionnaire HTTP, après avoir obtenu paymentLink à l'étape 1 :
http.Redirect(w, r, paymentLink, http.StatusFound)
import { Controller, Post, Res } from '@nestjs/common';
import { Response } from 'express';
@Controller('checkout')
export class CheckoutController {
constructor(private readonly paymentService: PaymentService) {}
@Post()
async checkout(@Res() res: Response) {
const { paymentLink } = await this.paymentService.createPaymentLink();
return res.redirect(302, paymentLink);
}
}
// Après avoir reçu paymentLink depuis votre backend (étape 1) :
function redirigerVersPaiement(paymentLink: string) {
window.location.href = paymentLink;
// Pour ouvrir dans un nouvel onglet :
// window.open(paymentLink, '_blank');
}
Étape 3 : Mettre en place l'endpoint de notification
Après que le client a terminé le paiement sur la page Papi, Papi envoie une requête POST à votre notificationUrl pour informer votre système du résultat.
Ce que vous devez faire
- Créer un endpoint qui accepte les requêtes POST et lit le corps JSON (l'URL que vous avez fournie comme
notificationUrlà l'étape 1). - Dans ce script, vérifier la notification puis mettre à jour votre système (ex. marquer la commande comme payée ou échouée).
Exemple de payload de notification
Papi envoie un corps JSON du type :
{
"paymentStatus": "SUCCESS",
"paymentMethod": "MVOLA",
"currency": "MGA",
"amount": 15000,
"fee": 500,
"clientName": "Nom du client",
"description": "Paiement pour la commande #123",
"merchantPaymentReference": "MERCHANT-0001",
"paymentReference": "ORDER-123",
"notificationToken": "xyz789",
"message": "Paiement effectué avec succès.",
"payerEmail": "customer@example.com",
"payerPhone": "+261340000000"
}
| Nom du champ | Type | Description |
|---|---|---|
| paymentStatus | string | SUCCESS, PENDING ou FAILED. |
| paymentMethod | string | Méthode utilisée (ex. MVOLA). |
| currency | string | Code devise. |
| amount | integer | Montant payé. |
| fee | integer | Frais de transaction. |
| clientName | string | Nom du client. |
| description | string | Votre description. |
| merchantPaymentReference | string | Référence du système de paiement. |
| paymentReference | string | Votre référence (identique à l'étape 1). |
| notificationToken | string | Permet de vérifier l'authenticité. |
| message | string | Détails supplémentaires. |
| payerEmail | string | E-mail du client. |
| payerPhone | string | Téléphone du client. |
Comment vérifier la notification
- Vérifier que
paymentReferencecorrespond à la référence envoyée lors de la création du lien de paiement. - Vérifier que
notificationTokencorrespond au token reçu dans la réponse de l'étape 1.
Si les deux correspondent, considérez la notification comme authentique et mettez à jour vos enregistrements.
Exemple d'endpoint de notification en PHP
<?php
// payment-notify.php (ou le chemin correspondant à votre notificationUrl)
$data = json_decode(file_get_contents("php://input"), true);
if (!$data) {
http_response_code(400);
exit();
}
$paymentReference = $data["paymentReference"] ?? "";
$notificationToken = $data["notificationToken"] ?? "";
$paymentStatus = $data["paymentStatus"] ?? "";
$amount = $data["amount"] ?? 0;
// Vérification : comparer avec la référence et le notificationToken stockés à la création du lien
$expectedToken = "xyz789"; // Récupérer le token stocké à l'étape 1 pour ce paiement
$expectedReference = "ORDER-123"; // Ou depuis votre base pour cette commande
if ($paymentReference !== $expectedReference || $notificationToken !== $expectedToken) {
http_response_code(403);
exit();
}
if ($paymentStatus === "SUCCESS") {
file_put_contents("log.txt", "Paiement $paymentReference : SUCCÈS (montant : $amount)\n", FILE_APPEND);
// Mettre à jour votre base pour marquer la commande comme payée
} elseif ($paymentStatus === "FAILED") {
file_put_contents("log.txt", "Paiement $paymentReference : ÉCHEC\n", FILE_APPEND);
// Gérer l'échec (ex. notifier le client)
} else {
// PENDING ou autre
file_put_contents("log.txt", "Paiement $paymentReference : $paymentStatus\n", FILE_APPEND);
}
http_response_code(200); // Indiquer à Papi que la notification a été reçue
?>
Étape 4 : Créer les pages de résultat du paiement
À la fin du processus de paiement, Papi affiche un message de succès ou d'échec à l'utilisateur, puis le redirige vers les URLs fournies à l'étape 1 (successUrl et failureUrl).
Ce que vous devez faire
Option 1 : Utiliser une seule URL pour le succès et l'échec (ex. retour à l'accueil ou à la page de détail de commande).
Option 2 : Utiliser deux pages distinctes :
- Une page pour les paiements réussis, à l'URL définie comme
successUrl(ex. prochaines étapes, livraison). - Une page pour les paiements échoués, à l'URL définie comme
failureUrl(ex. « Paiement échoué », « Réessayer » ou contacter le support).
Mode test
Pour tester votre intégration :
isTestMode=true— Marque la transaction comme test (attention : selon la configuration, des mouvements réels peuvent tout de même avoir lieu).- Mode test de l'application (cartes uniquement) — Dans le tableau de bord, activez le mode test dans les paramètres de l'application. Vous pouvez utiliser cette carte de test :
- Numéro : 4000 0000 0000 5126
- Date d'expiration : 01/2028
- CVV : 123
Remarque : Le mobile money ne permet pas de transactions de test non réelles.