logo oujood
🔍

Intégrer le système de paiement PayPal avec PHP et MySQL - Guide complet

Découvrez comment intégrer facilement PayPal dans votre site web PHP avec MySQL. Ce guide vous accompagne étape par étape, du compte Sandbox aux transactions sécurisées en production.
Le système de paiement est indispensable pour les sites Web qui ont quelque chose à vendre sur leur site et l'intégration du système de paiement dans votre site Web rend l'ensemble du processus si facile. Donc, dans ce tutoriel, nous allons vous montrer comment intégrer le système de paiement Paypal en utilisant PHP et MySQL

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é.

⚠️ Important : Ce tutoriel utilise l'ancienne API PayPal. Pour les nouveaux projets, considérez l'API PayPal Payments (v2) ou les solutions plus modernes comme Stripe.

🚀 É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.

  1. Rendez-vous sur https://developer.paypal.com/
  2. Créez un compte développeur (gratuit)
  3. 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

Configuration des comptes de test PayPal Sandbox

🔧 Étape 3 : Récupération des identifiants

Une fois vos comptes créés, vous devez récupérer les informations essentielles :

Comptes de test configurés

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
🛡️ Conseil sécurité : Ne jamais faire confiance aux données côté client. Validez toujours les montants côté serveur.

CODE SOURCE - Page "index.php" :


📋 Copier le code

<?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
⚠️ Attention : Les données GET peuvent être falsifiées. Implémentez toujours une vérification IPN (Instant Payment Notification) pour les transactions critiques.

CODE SOURCE - Page "success.php" sécurisée :


📋 Copier le code

<?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 :


📋 Copier le code

<?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 :

  1. Changez les URLs :
    https://www.sandbox.paypal.com/https://www.paypal.com/
  2. Utilisez votre vrai compte Business PayPal
  3. Configurez les URLs de production (sans localhost)
  4. Activez HTTPS sur tout votre site
  5. Testez avec de petits montants réels
  6. 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 :


📋 Copier le code

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 :


📋 Copier le code

<?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 :


📋 Copier le code

<?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 :


📋 Copier le code

<?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 :

Erreur : "IPN non reçu"
  • 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
Erreur : "Montant incorrect"
  • Vérifiez les décimales et le format (point vs virgule)
  • Contrôlez la devise configurée
  • Validez côté serveur avant PayPal
Erreur : "Transaction en double"
  • 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