OUJOOD.COM
Système de paiement PayPal avec PHP et MySQL : Implémentation complète
PayPal reste l'une des solutions de paiement les plus fiables pour les sites e-commerce. Dans ce tutoriel détaillé, nous allons créer un système de paiement robuste utilisant l'API PayPal, PHP et MySQL.
Vous apprendrez à configurer l'environnement de test PayPal Sandbox, traiter les paiements en toute sécurité, et gérer les retours de transaction. Ce guide inclut la gestion d'erreurs et les bonnes pratiques de sécurité.
🚀 Étape 1 : Configuration du compte PayPal Sandbox
Le PayPal Sandbox est un environnement de test gratuit qui simule les vraies transactions sans traiter d'argent réel.
- Rendez-vous sur https://developer.paypal.com/
- Créez un compte développeur (gratuit)
- Connectez-vous au tableau de bord développeur
💼 Étape 2 : Création des comptes de test
Vous devez créer deux types de comptes pour tester les transactions :
- Compte Personnel : Simule l'acheteur
- Compte Business : Simule votre boutique (vendeur)
1. Accédez à Sandbox → Comptes de test
2. Cliquez sur "Créer un compte"
3. Configurez vos comptes avec des données réalistes

🔧 Étape 3 : Récupération des identifiants
Une fois vos comptes créés, vous devez récupérer les informations essentielles :

Informations à noter :
- Email du compte Business (ID marchand)
- Email du compte Personnel
- Mots de passe des deux comptes
📄 Étape 4 : Architecture du système
Notre système de paiement se compose de trois pages principales :
- 📦 index.php : Page produit avec bouton PayPal
- ✅ success.php : Gestion du retour après paiement réussi
- ❌ cancel.php : Gestion de l'annulation de paiement
📦 Page principale "index.php"
Cette page affiche votre produit et gère l'envoi vers PayPal. Éléments clés :
- Affichage du produit (image, nom, prix)
- Formulaire sécurisé vers PayPal
- Configuration des URLs de retour
- Paramètres cachés pour la transaction
CODE SOURCE - Page "index.php" :
<?php
// Configuration PayPal Sandbox (à modifier pour la production)
$paypal_url = 'https://www.sandbox.paypal.com/cgi-bin/webscr';
$paypal_id = 'EQ7UGS7DEHSFL'; // Remplacez par votre Business ID
$base_url = 'http://localhost/php-paypal'; // Adaptez à votre domaine
// Données du produit (en production, récupérez depuis la base de données)
$produit = [
'id' => 1,
'nom' => 'Formation PHP Avancée - OUJOOD',
'prix' => 90.00,
'devise' => 'USD',
'description' => 'Formation complète PHP et MySQL'
];
$client = "Visiteur"; // Récupérez le nom depuis la session utilisateur
?>
<!DOCTYPE html>
<html lang="fr">
<head>
<meta charset="UTF-8">
<title>Boutique - Paiement PayPal</title>
<style>
.product-card {
border: 1px solid #ddd;
border-radius: 8px;
padding: 20px;
max-width: 400px;
margin: 20px auto;
text-align: center;
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
}
.price {
font-size: 24px;
color: #2c5aa0;
font-weight: bold;
margin: 10px 0;
}
.security-notice {
background: #f0f7ff;
border: 1px solid #b3d7ff;
padding: 10px;
border-radius: 4px;
margin: 15px 0;
font-size: 12px;
}
</style>
</head>
<body>
<div class="container">
<h2>🛒 Boutique en ligne</h2>
<h4>Bienvenue, <?php echo htmlspecialchars($client); ?> !</h4>
<div class="product-card">
<div class="image">
<a href="https://www.oujood.com/" title="Formation développement Web" rel="nofollow">
<img src="https://www.oujood.com/images/logo.png" alt="Logo OUJOOD" style="max-width: 200px;" />
</a>
</div>
<div class="name">
<h3><?php echo htmlspecialchars($produit['nom']); ?></h3>
<p><?php echo htmlspecialchars($produit['description']); ?></p>
</div>
<div class="price">
Prix : <?php echo number_format($produit['prix'], 2); ?> <?php echo $produit['devise']; ?>
</div>
<div class="security-notice">
🔒 Paiement sécurisé par PayPal
</div>
<div class="btn">
<form action="<?php echo $paypal_url; ?>" method="post" name="frmPayPal">
<!-- Informations PayPal obligatoires -->
<input type="hidden" name="business" value="<?php echo $paypal_id; ?>">
<input type="hidden" name="cmd" value="_xclick">
<!-- Détails du produit -->
<input type="hidden" name="item_name" value="<?php echo htmlspecialchars($produit['nom']); ?>">
<input type="hidden" name="item_number" value="<?php echo $produit['id']; ?>">
<input type="hidden" name="amount" value="<?php echo $produit['prix']; ?>">
<input type="hidden" name="currency_code" value="<?php echo $produit['devise']; ?>">
<!-- Paramètres additionnels -->
<input type="hidden" name="no_shipping" value="1">
<input type="hidden" name="handling" value="0">
<input type="hidden" name="cpp_header_image" value="https://www.oujood.com/images/logo.png">
<!-- URLs de retour sécurisées -->
<input type="hidden" name="return" value="<?php echo $base_url; ?>/success.php">
<input type="hidden" name="cancel_return" value="<?php echo $base_url; ?>/cancel.php">
<input type="hidden" name="notify_url" value="<?php echo $base_url; ?>/ipn.php">
<!-- Bouton PayPal officiel -->
<input type="image"
src="https://www.sandbox.paypal.com/fr_FR/i/btn/btn_buynowCC_LG.gif"
border="0"
name="submit"
alt="Payer avec PayPal"
title="Procéder au paiement sécurisé">
<img alt="" border="0" src="https://www.sandbox.paypal.com/fr_FR/i/scr/pixel.gif" width="1" height="1">
</form>
</div>
</div>
</div>
</body>
</html>
✅ Page de succès "success.php"
Page cruciale qui traite le retour après un paiement réussi. Elle doit :
- Vérifier l'authenticité des données reçues
- Valider le montant et la devise
- Enregistrer la transaction en base de données
- Afficher la confirmation à l'utilisateur
CODE SOURCE - Page "success.php" sécurisée :
<?php
session_start();
// Configuration de sécurité
$prix_attendu = 90.00;
$devise_attendue = 'USD';
// Récupération sécurisée des paramètres PayPal
$item_number = filter_input(INPUT_GET, 'item_number', FILTER_SANITIZE_NUMBER_INT);
$transaction_id = filter_input(INPUT_GET, 'tx', FILTER_SANITIZE_STRING);
$montant_recu = filter_input(INPUT_GET, 'amt', FILTER_SANITIZE_NUMBER_FLOAT, FILTER_FLAG_ALLOW_FRACTION);
$devise_recue = filter_input(INPUT_GET, 'cc', FILTER_SANITIZE_STRING);
$statut_paiement = filter_input(INPUT_GET, 'st', FILTER_SANITIZE_STRING);
// Fonction de validation
function validerTransaction($montant, $devise, $prix_attendu, $devise_attendue, $statut) {
return ($montant >= $prix_attendu &&
$devise === $devise_attendue &&
$statut === 'Completed');
}
// Log des transactions (recommandé pour la production)
function logTransaction($data) {
$log = date('Y-m-d H:i:s') . " - Transaction: " . json_encode($data) . "\n";
file_put_contents('transactions.log', $log, FILE_APPEND | LOCK_EX);
}
?>
<!DOCTYPE html>
<html lang="fr">
<head>
<meta charset="UTF-8">
<title>Paiement Confirmé</title>
<style>
.success-container {
max-width: 600px;
margin: 50px auto;
text-align: center;
padding: 30px;
border: 2px solid #28a745;
border-radius: 10px;
background: #f8fff9;
}
.error-container {
max-width: 600px;
margin: 50px auto;
text-align: center;
padding: 30px;
border: 2px solid #dc3545;
border-radius: 10px;
background: #fff5f5;
}
.transaction-details {
background: #e9f4ff;
padding: 15px;
border-radius: 5px;
margin: 20px 0;
text-align: left;
}
.btn-return {
display: inline-block;
padding: 10px 20px;
background: #007bff;
color: white;
text-decoration: none;
border-radius: 5px;
margin-top: 20px;
}
</style>
</head>
<body>
<?php
// Validation et traitement
if ($transaction_id && validerTransaction($montant_recu, $devise_recue, $prix_attendu, $devise_attendue, $statut_paiement)) {
// Log de la transaction réussie
logTransaction([
'id' => $transaction_id,
'montant' => $montant_recu,
'devise' => $devise_recue,
'statut' => $statut_paiement,
'timestamp' => date('Y-m-d H:i:s')
]);
// TODO: Enregistrer en base de données
// INSERT INTO transactions (transaction_id, montant, devise, statut, date_creation) VALUES (...)
echo '<div class="success-container">';
echo '<h1>🎉 Paiement Confirmé !</h1>';
echo '<h2>Merci pour votre achat</h2>';
echo '<p>Votre transaction a été traitée avec succès.</p>';
echo '<div class="transaction-details">';
echo '<h3>📋 Détails de la transaction</h3>';
echo '<p><strong>ID Transaction :</strong> ' . htmlspecialchars($transaction_id) . '</p>';
echo '<p><strong>Montant :</strong> ' . htmlspecialchars($montant_recu) . ' ' . htmlspecialchars($devise_recue) . '</p>';
echo '<p><strong>Statut :</strong> ' . htmlspecialchars($statut_paiement) . '</p>';
echo '<p><strong>Date :</strong> ' . date('d/m/Y à H:i') . '</p>';
echo '</div>';
echo '<p><small>Un email de confirmation vous sera envoyé sous peu.</small></p>';
} else {
// Transaction échouée ou falsifiée
echo '<div class="error-container">';
echo '<h1>❌ Paiement Refusé</h1>';
echo '<p>Une erreur s\'est produite lors du traitement de votre paiement.</p>';
echo '<p>Veuillez contacter notre support client.</p>';
// Log de l'erreur
logTransaction([
'erreur' => 'Validation échouée',
'montant_recu' => $montant_recu,
'devise_recue' => $devise_recue,
'statut' => $statut_paiement,
'timestamp' => date('Y-m-d H:i:s')
]);
}
?>
<div>
<div class="image">
<a href="index.php" class="btn-return">
🏠 Retour à la boutique
</a>
</div>
</div>
</body>
</html>
❌ Page d'annulation "cancel.php"
Gère les cas où l'utilisateur annule le paiement sur PayPal.
CODE SOURCE - Page "cancel.php" améliorée :
<?php
// Log des annulations pour statistiques
$log_data = [
'action' => 'paiement_annule',
'timestamp' => date('Y-m-d H:i:s'),
'ip' => $_SERVER['REMOTE_ADDR'] ?? 'inconnue',
'user_agent' => $_SERVER['HTTP_USER_AGENT'] ?? 'inconnu'
];
file_put_contents('cancellations.log', json_encode($log_data) . "\n", FILE_APPEND | LOCK_EX);
?>
<!DOCTYPE html>
<html lang="fr">
<head>
<meta charset="UTF-8">
<title>Paiement Annulé</title>
<style>
.cancel-container {
max-width: 500px;
margin: 100px auto;
text-align: center;
padding: 40px;
border: 2px solid #ffc107;
border-radius: 10px;
background: #fffbf0;
}
.btn-retry {
display: inline-block;
padding: 12px 25px;
background: #28a745;
color: white;
text-decoration: none;
border-radius: 5px;
margin: 10px;
font-weight: bold;
}
.btn-home {
display: inline-block;
padding: 12px 25px;
background: #6c757d;
color: white;
text-decoration: none;
border-radius: 5px;
margin: 10px;
}
.help-section {
background: #f8f9fa;
padding: 20px;
border-radius: 5px;
margin-top: 30px;
}
</style>
</head>
<body>
<div class="cancel-container">
<h1>⚠️ Paiement Annulé</h1>
<h2>Aucune transaction n'a été effectuée</h2>
<p>Vous avez annulé le processus de paiement PayPal.<br>
Aucun montant n'a été débité de votre compte.</p>
<div class="help-section">
<h3>🤔 Besoin d'aide ?</h3>
<p>Si vous rencontrez des difficultés avec le paiement :</p>
<ul style="text-align: left; display: inline-block;">
<li>Vérifiez que votre compte PayPal est actif</li>
<li>Assurez-vous d'avoir suffisamment de fonds</li>
<li>Contactez notre support client</li>
</ul>
</div>
<div>
<a href="index.php" class="btn-retry">
🔄 Réessayer le paiement
</a>
<a href="../index.php" class="btn-home">
🏠 Retour à l'accueil
</a>
</div>
<p><small>Votre panier a été conservé pour 24h.</small></p>
</div>
</body>
</html>
🔐 Étape 5 : Sécurisation avancée (Recommandée)
🛡️ Mise en place de l'IPN (Instant Payment Notification)
L'IPN permet de vérifier côté serveur que les transactions sont authentiques :
- Validation automatique des paiements
- Protection contre la falsification
- Traitement asynchrone des notifications
🔒 Autres mesures de sécurité :
- HTTPS obligatoire en production
- Validation côté serveur de tous les montants
- Logs détaillés des transactions
- Tokens CSRF pour les formulaires
- Limitation du taux de requêtes
🚀 Étape finale : Passage en production
✅ Checklist de déploiement :
- Changez les URLs :
https://www.sandbox.paypal.com/→https://www.paypal.com/ - Utilisez votre vrai compte Business PayPal
- Configurez les URLs de production (sans localhost)
- Activez HTTPS sur tout votre site
- Testez avec de petits montants réels
- Configurez la surveillance des transactions
🆕 Alternatives modernes recommandées
Pour de nouveaux projets, considérez ces solutions plus récentes :
- PayPal Payments API v2 : Plus moderne et sécurisé
- Stripe : Interface développeur excellente
- Square : Simple et efficace
- Mollie : Support européen excellent
💾 Étape bonus : Intégration avec MySQL
Pour une application complète, voici comment intégrer une base de données MySQL :
📋 Structure de base de données recommandée :
Table des produits :
CREATE TABLE produits (
id INT AUTO_INCREMENT PRIMARY KEY,
nom VARCHAR(255) NOT NULL,
description TEXT,
prix DECIMAL(10,2) NOT NULL,
devise VARCHAR(3) DEFAULT 'USD',
actif BOOLEAN DEFAULT TRUE,
date_creation TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
CREATE TABLE transactions (
id INT AUTO_INCREMENT PRIMARY KEY,
produit_id INT,
transaction_paypal VARCHAR(50) UNIQUE,
montant DECIMAL(10,2) NOT NULL,
devise VARCHAR(3) NOT NULL,
statut ENUM('pending', 'completed', 'failed', 'refunded') DEFAULT 'pending',
email_acheteur VARCHAR(255),
date_transaction TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
ip_acheteur VARCHAR(45),
FOREIGN KEY (produit_id) REFERENCES produits(id)
);
CREATE TABLE logs_paiement (
id INT AUTO_INCREMENT PRIMARY KEY,
transaction_id VARCHAR(50),
action VARCHAR(50),
donnees_brutes TEXT,
ip_address VARCHAR(45),
user_agent TEXT,
date_log TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
🔧 Classe de gestion des paiements :
<?php
class PaymentManager {
private $pdo;
private $config;
public function __construct($database_config, $paypal_config) {
$this->config = $paypal_config;
try {
$this->pdo = new PDO(
"mysql:host={$database_config['host']};dbname={$database_config['dbname']};charset=utf8",
$database_config['username'],
$database_config['password'],
[PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION]
);
} catch (PDOException $e) {
error_log("Erreur connexion DB: " . $e->getMessage());
throw new Exception("Erreur de connexion à la base de données");
}
}
/**
* Récupère un produit par son ID
*/
public function getProduit($id) {
$stmt = $this->pdo->prepare("SELECT * FROM produits WHERE id = ? AND actif = 1");
$stmt->execute([$id]);
return $stmt->fetch(PDO::FETCH_ASSOC);
}
/**
* Enregistre une nouvelle transaction
*/
public function creerTransaction($produit_id, $transaction_paypal, $montant, $devise, $email = null) {
$stmt = $this->pdo->prepare("
INSERT INTO transactions (produit_id, transaction_paypal, montant, devise, email_acheteur, ip_acheteur)
VALUES (?, ?, ?, ?, ?, ?)
");
return $stmt->execute([
$produit_id,
$transaction_paypal,
$montant,
$devise,
$email,
$_SERVER['REMOTE_ADDR'] ?? null
]);
}
/**
* Met à jour le statut d'une transaction
*/
public function updateStatutTransaction($transaction_paypal, $nouveau_statut) {
$stmt = $this->pdo->prepare("
UPDATE transactions
SET statut = ?, date_transaction = CURRENT_TIMESTAMP
WHERE transaction_paypal = ?
");
return $stmt->execute([$nouveau_statut, $transaction_paypal]);
}
/**
* Vérifie si une transaction existe déjà
*/
public function transactionExiste($transaction_paypal) {
$stmt = $this->pdo->prepare("SELECT COUNT(*) FROM transactions WHERE transaction_paypal = ?");
$stmt->execute([$transaction_paypal]);
return $stmt->fetchColumn() > 0;
}
/**
* Log des événements de paiement
*/
public function logEvenement($transaction_id, $action, $donnees_brutes = null) {
$stmt = $this->pdo->prepare("
INSERT INTO logs_paiement (transaction_id, action, donnees_brutes, ip_address, user_agent)
VALUES (?, ?, ?, ?, ?)
");
return $stmt->execute([
$transaction_id,
$action,
$donnees_brutes ? json_encode($donnees_brutes) : null,
$_SERVER['REMOTE_ADDR'] ?? null,
$_SERVER['HTTP_USER_AGENT'] ?? null
]);
}
/**
* Génère le formulaire PayPal pour un produit
*/
public function genererFormulairePayPal($produit_id) {
$produit = $this->getProduit($produit_id);
if (!$produit) {
throw new Exception("Produit introuvable");
}
$form_data = [
'business' => $this->config['business_id'],
'cmd' => '_xclick',
'item_name' => $produit['nom'],
'item_number' => $produit['id'],
'amount' => $produit['prix'],
'currency_code' => $produit['devise'],
'return' => $this->config['return_url'],
'cancel_return' => $this->config['cancel_url'],
'notify_url' => $this->config['notify_url']
];
return $form_data;
}
}
// Configuration
$db_config = [
'host' => 'localhost',
'dbname' => 'ma_boutique',
'username' => 'username',
'password' => 'password'
];
$paypal_config = [
'sandbox' => true, // false pour la production
'business_id' => 'votre-business-id',
'return_url' => 'https://votresite.com/success.php',
'cancel_url' => 'https://votresite.com/cancel.php',
'notify_url' => 'https://votresite.com/ipn.php'
];
// Utilisation
try {
$paymentManager = new PaymentManager($db_config, $paypal_config);
$produit = $paymentManager->getProduit(1);
if ($produit) {
$form_data = $paymentManager->genererFormulairePayPal($produit['id']);
// Générer le formulaire avec $form_data
}
} catch (Exception $e) {
error_log("Erreur PaymentManager: " . $e->getMessage());
// Afficher message d'erreur utilisateur
}
?>
🔔 Implémentation de l'IPN (Instant Payment Notification)
L'IPN est crucial pour la sécurité. Voici un exemple d'implémentation :
<?php
/**
* Gestionnaire IPN PayPal
* Ce fichier doit être accessible publiquement via HTTPS
*/
// Configuration
$paypal_url = 'https://www.sandbox.paypal.com/cgi-bin/webscr'; // Sandbox
// $paypal_url = 'https://www.paypal.com/cgi-bin/webscr'; // Production
// Log toutes les notifications reçues
file_put_contents('ipn.log', date('Y-m-d H:i:s') . " - IPN reçu: " . file_get_contents('php://input') . "\n", FILE_APPEND);
// Préparer la requête de vérification
$req = 'cmd=_notify-validate';
foreach ($_POST as $key => $value) {
$value = urlencode(stripslashes($value));
$req .= "&$key=$value";
}
// Envoyer la requête de vérification à PayPal
$ch = curl_init($paypal_url);
curl_setopt($ch, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, $req);
curl_setopt($ch, CURLOPT_SSLVERSION, 6);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 1);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 2);
curl_setopt($ch, CURLOPT_FORBID_REUSE, 1);
curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 30);
curl_setopt($ch, CURLOPT_HTTPHEADER, array('Connection: Close'));
$res = curl_exec($ch);
if (!$res) {
error_log('Erreur cURL IPN: ' . curl_error($ch));
curl_close($ch);
exit;
}
curl_close($ch);
// Vérifier la réponse de PayPal
if (strcmp($res, "VERIFIED") == 0) {
// L'IPN est vérifié, traiter les données
$transaction_id = $_POST['txn_id'] ?? '';
$payment_status = $_POST['payment_status'] ?? '';
$payment_amount = $_POST['mc_gross'] ?? '';
$payment_currency = $_POST['mc_currency'] ?? '';
$receiver_email = $_POST['receiver_email'] ?? '';
$payer_email = $_POST['payer_email'] ?? '';
$item_number = $_POST['item_number'] ?? '';
// Inclure votre classe PaymentManager
include 'PaymentManager.php';
try {
$paymentManager = new PaymentManager($db_config, $paypal_config);
// Vérifications de sécurité
if ($receiver_email !== $paypal_config['business_email']) {
throw new Exception("Email receveur incorrect: $receiver_email");
}
// Éviter les doublons
if ($paymentManager->transactionExiste($transaction_id)) {
$paymentManager->logEvenement($transaction_id, 'doublon_ipn', $_POST);
exit;
}
// Traitement selon le statut
switch ($payment_status) {
case 'Completed':
// Paiement réussi
$paymentManager->creerTransaction(
$item_number,
$transaction_id,
$payment_amount,
$payment_currency,
$payer_email
);
$paymentManager->updateStatutTransaction($transaction_id, 'completed');
// Actions supplémentaires (email, activation produit, etc.)
envoyerEmailConfirmation($payer_email, $transaction_id);
activerProduit($payer_email, $item_number);
break;
case 'Pending':
$paymentManager->creerTransaction(
$item_number,
$transaction_id,
$payment_amount,
$payment_currency,
$payer_email
);
$paymentManager->updateStatutTransaction($transaction_id, 'pending');
break;
case 'Failed':
case 'Denied':
case 'Expired':
$paymentManager->updateStatutTransaction($transaction_id, 'failed');
break;
case 'Refunded':
case 'Reversed':
$paymentManager->updateStatutTransaction($transaction_id, 'refunded');
// Traitement du remboursement
desactiverProduit($payer_email, $item_number);
break;
}
// Log du succès
$paymentManager->logEvenement($transaction_id, 'ipn_traite', $_POST);
} catch (Exception $e) {
error_log("Erreur IPN: " . $e->getMessage());
// Log de l'erreur
if (isset($paymentManager)) {
$paymentManager->logEvenement($transaction_id, 'erreur_ipn', [
'erreur' => $e->getMessage(),
'donnees' => $_POST
]);
}
}
} else if (strcmp($res, "INVALID") == 0) {
// L'IPN n'est pas valide
error_log("IPN INVALIDE reçu: " . print_r($_POST, true));
file_put_contents('ipn_invalid.log', date('Y-m-d H:i:s') . " - IPN INVALIDE: " . json_encode($_POST) . "\n", FILE_APPEND);
}
/**
* Fonctions auxiliaires
*/
function envoyerEmailConfirmation($email, $transaction_id) {
$sujet = "Confirmation de votre achat";
$message = "Votre paiement a été confirmé. ID de transaction: $transaction_id";
$headers = "From: noreply@votresite.com\r\nContent-Type: text/html; charset=UTF-8";
mail($email, $sujet, $message, $headers);
}
function activerProduit($email, $produit_id) {
// Logique d'activation du produit/service
// Par exemple: activer l'accès à un cours, envoyer un lien de téléchargement, etc.
}
function desactiverProduit($email, $produit_id) {
// Logique de désactivation en cas de remboursement
}
// Réponse HTTP 200 pour confirmer la réception
http_response_code(200);
echo "IPN traité";
?>
📊 Tableau de bord d'administration
Un tableau de bord simple pour suivre vos transactions :
<?php
// admin.php - Tableau de bord simple
session_start();
// Authentification simple (à améliorer en production)
if (!isset($_SESSION['admin']) || $_SESSION['admin'] !== true) {
if ($_POST['password'] ?? '' === 'votre_mot_de_passe_admin') {
$_SESSION['admin'] = true;
} else {
echo '<form method="post"><input type="password" name="password" placeholder="Mot de passe admin"><button>Connexion</button></form>';
exit;
}
}
include 'PaymentManager.php';
$paymentManager = new PaymentManager($db_config, $paypal_config);
// Récupérer les statistiques
$stmt = $paymentManager->pdo->query("
SELECT
COUNT(*) as total_transactions,
SUM(CASE WHEN statut = 'completed' THEN montant ELSE 0 END) as revenus_total,
COUNT(CASE WHEN statut = 'completed' THEN 1 END) as paiements_reussis,
COUNT(CASE WHEN statut = 'pending' THEN 1 END) as paiements_en_attente,
COUNT(CASE WHEN statut = 'failed' THEN 1 END) as paiements_echoues
FROM transactions
");
$stats = $stmt->fetch(PDO::FETCH_ASSOC);
// Dernières transactions
$stmt = $paymentManager->pdo->query("
SELECT t.*, p.nom as produit_nom
FROM transactions t
LEFT JOIN produits p ON t.produit_id = p.id
ORDER BY t.date_transaction DESC
LIMIT 20
");
$dernieres_transactions = $stmt->fetchAll(PDO::FETCH_ASSOC);
?>
<!DOCTYPE html>
<html>
<head>
<title>Administration - Paiements</title>
<style>
body { font-family: Arial, sans-serif; margin: 20px; }
.stats { display: flex; gap: 20px; margin-bottom: 30px; }
.stat-card {
background: #f8f9fa;
padding: 20px;
border-radius: 8px;
text-align: center;
flex: 1;
}
.stat-number { font-size: 2em; font-weight: bold; color: #28a745; }
table { width: 100%; border-collapse: collapse; }
th, td { padding: 10px; border: 1px solid #ddd; text-align: left; }
th { background: #f8f9fa; }
.status-completed { color: green; font-weight: bold; }
.status-pending { color: orange; font-weight: bold; }
.status-failed { color: red; font-weight: bold; }
</style>
</head>
<body>
<h1>📊 Tableau de bord PayPal</h1>
<div class="stats">
<div class="stat-card">
<div class="stat-number"><?= number_format($stats['revenus_total'], 2) ?> €</div>
<div>Revenus total</div>
</div>
<div class="stat-card">
<div class="stat-number"><?= $stats['paiements_reussis'] ?></div>
<div>Paiements réussis</div>
</div>
<div class="stat-card">
<div class="stat-number"><?= $stats['paiements_en_attente'] ?></div>
<div>En attente</div>
</div>
<div class="stat-card">
<div class="stat-number"><?= $stats['paiements_echoues'] ?></div>
<div>Échecs</div>
</div>
</div>
<h2>🕒 Dernières transactions</h2>
<table>
<thead>
<tr>
<th>Date</th>
<th>Transaction ID</th>
<th>Produit</th>
<th>Email</th>
<th>Montant</th>
<th>Statut</th>
</tr>
</thead>
<tbody>
<?php foreach ($dernieres_transactions as $transaction): ?>
<tr>
<td><?= date('d/m/Y H:i', strtotime($transaction['date_transaction'])) ?></td>
<td><?= htmlspecialchars($transaction['transaction_paypal']) ?></td>
<td><?= htmlspecialchars($transaction['produit_nom']) ?></td>
<td><?= htmlspecialchars($transaction['email_acheteur']) ?></td>
<td><?= number_format($transaction['montant'], 2) ?> <?= $transaction['devise'] ?></td>
<td><span class="status-<?= $transaction['statut'] ?>"><?= ucfirst($transaction['statut']) ?></span></td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
<p><a href="?logout=1">Déconnexion</a></p>
<?php
if (isset($_GET['logout'])) {
session_destroy();
header('Location: admin.php');
exit;
}
?>
</body>
</html>
🎯 Bonnes pratiques et optimisations
⚡ Performance :
- Cache des produits : Utilisez Redis ou Memcached
- Index de base de données : Sur transaction_paypal, email_acheteur
- Pagination : Pour l'admin des transactions
- CDN : Pour les images de produits
🔍 Monitoring :
- Alertes automatiques : Sur les échecs de paiement
- Rapports quotidiens : Revenus et transactions
- Surveillance des logs : Détection des anomalies
- Backup automatique : Base de données quotidien
📧 Communication client :
- Emails transactionnels : Confirmation, reçu, relance
- Templates responsive : Compatibles mobiles
- Support multilingue : Selon la localisation
- Statut de commande : Suivi en temps réel
🚨 Gestion des erreurs communes
❌ Problèmes fréquents et solutions :
- Vérifiez que l'URL IPN est accessible en HTTPS
- Testez avec l'outil PayPal IPN Simulator
- Vérifiez les logs d'erreur du serveur
- Vérifiez les décimales et le format (point vs virgule)
- Contrôlez la devise configurée
- Validez côté serveur avant PayPal
- Implémentez une vérification d'unicité
- Utilisez des tokens uniques par session
- Loggez tous les doublons pour analyse
🎯 Conclusion et ressources
Vous disposez maintenant d'un système PayPal complet et sécurisé ! Ce tutoriel couvre :
- ✅ Configuration PayPal Sandbox et production
- ✅ Intégration PHP/MySQL robuste
- ✅ Sécurisation avancée avec IPN
- ✅ Interface d'administration
- ✅ Gestion d'erreurs et monitoring
📚 Ressources utiles :
- Documentation PayPal : https://developer.paypal.com/docs/
- PayPal Sandbox : https://sandbox.paypal.com/
- IPN Simulator : https://developer.paypal.com/developer/ipnSimulator
- Guide sécurité : https://developer.paypal.com/docs/nvp-soap-api/
💡 Conseil final : Commencez simple, testez exhaustivement, puis ajoutez progressivement les fonctionnalités avancées. La sécurité doit toujours être votre priorité #1 !
Bon développement et bonnes ventes ! 🚀💰
Retour à l'accueil du site
Par carabde le 24/Septembre/2021 - Mis à jour le 05/09/2025