Refactor PayPal refund process by introducing a modular approach for refund context and rendering. This update consolidates refund logic into dedicated functions in inc_fx_paypal.php, enhancing maintainability and clarity. The commandes.php file is updated to utilize these new functions, improving the overall structure and readability of the refund handling code.
This commit is contained in:
@ -399,10 +399,7 @@ include('inc_header.php');
|
||||
|
||||
require_once $_SERVER["DOCUMENT_ROOT"] . "/superadm/php/inc_fx_paypal.php";
|
||||
// Jeton anti double clic / re-soumission
|
||||
if (empty($_SESSION['refund_token'])) {
|
||||
$_SESSION['refund_token'] = bin2hex(random_bytes(16));
|
||||
}
|
||||
$refundToken = $_SESSION['refund_token'];
|
||||
$refundToken = fxRefundEnsureToken();
|
||||
|
||||
// Récupération des infos commande (présentes dans ton contexte)
|
||||
$CAPTURE_ID = $recCommandes["TransactionID"] ?? null;
|
||||
@ -414,194 +411,24 @@ if (!isset($CAPTURE_ID) || !is_string($CAPTURE_ID) || $CAPTURE_ID === '') {
|
||||
return;
|
||||
}
|
||||
|
||||
// Charger ta classe PayPal
|
||||
require_once '../paypal_advanced/PaypalCheckout.class.php';
|
||||
$paypal = new PayPalCheckout(); // adapte si nécessaire
|
||||
|
||||
// Préparation
|
||||
$errors = [];
|
||||
$successMsg = null;
|
||||
|
||||
// Devise par défaut si jamais les détails PayPal n’en donnent pas
|
||||
$currencyUI = 'CAD';
|
||||
|
||||
// ====== Calculs initiaux côté serveur ======
|
||||
|
||||
// Solde dispo promoteur (ton helper existant)
|
||||
$solde_dispo_ms1 = statut_payement($recCommandes["eve_id"]);
|
||||
|
||||
// Récupérer les détails PayPal de la capture
|
||||
$paypaldetails = $paypal->paypaldetails($CAPTURE_ID);
|
||||
|
||||
// Harmoniser avec tes remboursements locaux si besoin
|
||||
$paypaldetails = reconcileRefundsWithLocal($paypaldetails);
|
||||
//print_rsl($paypaldetails);
|
||||
// Déterminer le max remboursable PayPal
|
||||
$max_remboursement_paypal = 0.0;
|
||||
//print_rsl($paypaldetails);
|
||||
// 1) S'il existe au moins un refund → on prend remaining (même si c’est 0)
|
||||
if (isset($paypaldetails['refunds']['totals']['remaining'])) {
|
||||
$max_remboursement_paypal = (float)$paypaldetails['refunds']['totals']['remaining'];
|
||||
}
|
||||
// 2) Sinon → fallback : montant total capturé
|
||||
else {
|
||||
if (!empty($paypaldetails['details']['amount']['value'])) {
|
||||
$max_remboursement_paypal = (float)$paypaldetails['details']['amount']['value'];
|
||||
} elseif (!empty($paypaldetails['delails']['amount']['value'])) {
|
||||
$max_remboursement_paypal = (float)$paypaldetails['delails']['amount']['value'];
|
||||
}
|
||||
}
|
||||
|
||||
// Déterminer la devise d’affichage si fournie par PayPal
|
||||
if (!empty($paypaldetails['refunds']['totals']['currency'])) {
|
||||
$currencyUI = (string)$paypaldetails['refunds']['totals']['currency'];
|
||||
} elseif (!empty($paypaldetails['details']['amount']['currency_code'])) {
|
||||
$currencyUI = (string)$paypaldetails['details']['amount']['currency_code'];
|
||||
} elseif (!empty($paypaldetails['delails']['amount']['currency_code'])) {
|
||||
$currencyUI = (string)$paypaldetails['delails']['amount']['currency_code'];
|
||||
}
|
||||
|
||||
// Frais MS1 (selon ta structure)
|
||||
$frais_ms1 = (float)($recCommandes['fra_total'] ?? 0);
|
||||
|
||||
// Max remboursable à l’écran (ta logique actuelle)
|
||||
$maxUI = $max_remboursement_paypal - $frais_ms1;
|
||||
|
||||
// maxblock = limite stricte d’input côté front, selon ton choix actuel
|
||||
if ($max_remboursement_paypal < $solde_dispo_ms1) {
|
||||
$maxblock = $max_remboursement_paypal;
|
||||
} else {
|
||||
$maxblock = 0;
|
||||
$maxUI = 0;
|
||||
}
|
||||
// ******************** variable info
|
||||
//print_rsl($recCommandes);
|
||||
echosl( "no_commande : ".$recCommandes["no_commande"]);
|
||||
echosl( "rem_ajoute_par : ".$_SESSION["usa_info"]["com_prenom"]." ".$_SESSION["usa_info"]["com_nom"]);
|
||||
echosl( "eve_id : ".$recCommandes["eve_id"]) ;
|
||||
echosl( "Montant remboursable : " . number_format($max_remboursement_paypal, 2) . " " . ($result['refunds']['totals']['currency'] ?? $result['delails']['amount']['currency_code'] ?? ''));
|
||||
|
||||
|
||||
|
||||
echo("solde dispo ms1 :");
|
||||
echosl($solde_dispo_ms1);
|
||||
echo("frais ms1 :");
|
||||
$frais_ms1=$recCommandes['fra_total'];
|
||||
echosl($frais_ms1);
|
||||
echo("max Remboursement :");
|
||||
|
||||
// $maxUI le max Remboursement si on veux pas renbourcer les frais ms1
|
||||
// $maxUI=$max_remboursement_paypal-$frais_ms1;
|
||||
// $maxUI le max Remboursement
|
||||
$maxUI=$max_remboursement_paypal;
|
||||
echosl($maxUI);
|
||||
$maxproposer = max(0, $maxUI - $frais_ms1);
|
||||
// Helpers UI
|
||||
// Contexte de remboursement mutualisé (calculs + détails PayPal)
|
||||
$refundCtx = fxRefundComputeContext($recCommandes);
|
||||
$paypaldetails = $refundCtx['paypaldetails'];
|
||||
|
||||
// Infos debug (Super Admin)
|
||||
echosl("no_commande : " . $recCommandes["no_commande"]);
|
||||
echosl("rem_ajoute_par : " . ($_SESSION["usa_info"]["com_prenom"] ?? '') . " " . ($_SESSION["usa_info"]["com_nom"] ?? ''));
|
||||
echosl("eve_id : " . $recCommandes["eve_id"]);
|
||||
echosl("Montant remboursable : " . number_format($refundCtx['max_paypal'], 2) . " " . $refundCtx['currency']);
|
||||
echo("solde dispo ms1 :");
|
||||
echosl($refundCtx['solde_dispo']);
|
||||
echo("frais ms1 :");
|
||||
echosl($refundCtx['frais_ms1']);
|
||||
echo("max Remboursement :");
|
||||
echosl($refundCtx['maxUI']);
|
||||
// Formulaire de remboursement (rendu mutualisé : voir inc_fx_paypal.php)
|
||||
echo fxRenderRefundForm($recCommandes, $refundToken, $refundCtx, $vDomaine);
|
||||
?>
|
||||
<!doctype html>
|
||||
<html lang="fr">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Remboursement PayPal</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
</head>
|
||||
<body style="font-family:system-ui,Arial,Helvetica;">
|
||||
|
||||
<div style="max-width:560px;border:1px solid #ddd;border-radius:10px;padding:14px;">
|
||||
<div style="font-weight:700;font-size:16px;margin-bottom:8px;">Remboursement PayPal (Checkout)</div>
|
||||
|
||||
<!-- Zone messages AJAX -->
|
||||
<div id="refund-msg" style="margin-bottom:10px;display:none;padding:10px;border-radius:8px;"></div>
|
||||
|
||||
<?php if ($maxUI > 0): ?>
|
||||
<div style="background:#f9fbff;border:1px solid #dbe7ff;padding:10px;border-radius:8px;margin-bottom:10px;">
|
||||
<div><strong>Capture:</strong> <?= h($CAPTURE_ID) ?></div>
|
||||
<div><strong>Max remboursable (affiché):</strong> <span id="pp_ref_max"><?= h(money2($maxUI)) ?></span> <?= h($currencyUI) ?></div>
|
||||
</div>
|
||||
|
||||
<form id="refund-form" method="post" style="display:grid;grid-template-columns:1fr 1fr;gap:10px;">
|
||||
<input type="hidden" name="refund_token" value="<?= h($refundToken) ?>">
|
||||
<input type="hidden" name="capture_id" value="<?= h($CAPTURE_ID) ?>">
|
||||
<input type="hidden" name="currency" value="<?= h($currencyUI) ?>">
|
||||
<input type="hidden" name="eve_id" value="<?= h($recCommandes["eve_id"]) ?>">
|
||||
<input type="hidden" name="no_commande" value="<?= h($recCommandes["no_commande"]) ?>">
|
||||
|
||||
<div style="grid-column:1/2;">
|
||||
<label style="display:block;font-size:12px;color:#333;margin-bottom:4px;">
|
||||
Montant à rembourser (<?= h($currencyUI) ?>)
|
||||
</label>
|
||||
<input id="pp_ref_amount" type="number" name="refund_amount" step="0.01" min="0.01"
|
||||
max="<?= h(money2($maxblock)) ?>"
|
||||
value="<?= h(money2($maxproposer)) ?>"
|
||||
style="width:100%;padding:8px;border:1px solid #ccc;border-radius:8px;">
|
||||
</div>
|
||||
|
||||
<div style="grid-column:2/3;">
|
||||
<label style="display:block;font-size:12px;color:#333;margin-bottom:4px;">Note (optionnelle)</label>
|
||||
<input type="text" name="refund_note" placeholder="Raison du remboursement"
|
||||
style="width:100%;padding:8px;border:1px solid #ccc;border-radius:8px;">
|
||||
</div>
|
||||
|
||||
<div style="grid-column:1/3;text-align:right;margin-top:6px;">
|
||||
<button type="submit"
|
||||
style="padding:10px 14px;border:1px solid #2d6cdf;border-radius:8px;background:#2d6cdf;color:#fff;cursor:pointer;">
|
||||
Rembourser
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
<?php else: ?>
|
||||
<div style="background:#fff8e6;border:1px solid #ffe1a6;padding:10px;border-radius:8px;">
|
||||
Aucun montant remboursable pour cette capture.
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
// Petit helper visuel
|
||||
function showMsg(text, ok) {
|
||||
const box = document.getElementById('refund-msg');
|
||||
box.style.display = 'block';
|
||||
box.style.background = ok ? '#e9f8ef' : '#feecec';
|
||||
box.style.border = ok ? '1px solid #b8e3c5' : '1px solid #f2b8b5';
|
||||
box.innerText = text;
|
||||
}
|
||||
|
||||
document.getElementById('refund-form')?.addEventListener('submit', async (e) => {
|
||||
e.preventDefault();
|
||||
|
||||
showMsg('⏳ Traitement en cours...', false);
|
||||
const data = new FormData(e.target);
|
||||
const res = await fetch( '<?php echo $vDomaine; ?>/superadm/ajax_refund.php', { method: 'POST', body: data });
|
||||
|
||||
// Défensif
|
||||
if (!res.ok) {
|
||||
showMsg('⚠️ Erreur réseau pendant le remboursement.', false);
|
||||
return;
|
||||
}
|
||||
|
||||
let json;
|
||||
try { json = await res.json(); } catch (_) {
|
||||
showMsg('⚠️ Réponse invalide du serveur.', false);
|
||||
return;
|
||||
}
|
||||
|
||||
if (json.success) {
|
||||
showMsg('✅ ' + (json.message || 'Remboursement effectué.'), true);
|
||||
|
||||
// 👉 Ici, tu peux déclencher des actions UI :
|
||||
// - recharger une grille des remboursements
|
||||
// - rafraîchir un solde
|
||||
// - etc.
|
||||
// refreshTableRemboursements();
|
||||
} else {
|
||||
showMsg('⚠️ ' + (json.message || 'Erreur remboursement.'), false);
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
|
||||
|
||||
|
||||
|
||||
@ -785,3 +785,223 @@ $qryInsert = $GLOBALS['db']->fxQuery($sqlInsert); // $stmt->execute([$capture_
|
||||
|
||||
}
|
||||
function h($s) { return htmlspecialchars((string)$s, ENT_QUOTES, 'UTF-8'); }
|
||||
|
||||
/* ==================================================================
|
||||
* Remboursement PayPal - briques mutualisees (Super Admin + fiche gestion)
|
||||
*
|
||||
* IMPORTANT : ces fonctions ne dependent QUE de ce fichier
|
||||
* (statut_payement, reconcileRefundsWithLocal, money2, h) + de la classe
|
||||
* PaypalCheckout. Elles n'incluent JAMAIS superadm/php/inc_functions.php
|
||||
* (le « U »), afin d'eviter tout conflit "Cannot redeclare" avec
|
||||
* php/inc_fonctions.php (le « O ») charge dans le contexte /compte.
|
||||
* ================================================================== */
|
||||
|
||||
/**
|
||||
* Garantit la presence d'un jeton anti double-clic en session.
|
||||
* @return string le jeton
|
||||
*/
|
||||
function fxRefundEnsureToken(): string
|
||||
{
|
||||
if (session_status() === PHP_SESSION_NONE) {
|
||||
session_start();
|
||||
}
|
||||
if (empty($_SESSION['refund_token'])) {
|
||||
$_SESSION['refund_token'] = bin2hex(random_bytes(16));
|
||||
}
|
||||
return $_SESSION['refund_token'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Calcule le contexte de remboursement d'une commande (capture PayPal).
|
||||
*
|
||||
* @param array $recCommandes Ligne commande (TransactionID, eve_id, no_commande, fra_total...)
|
||||
* @return array {
|
||||
* ok:bool, capture_id:string, currency:string, paypaldetails:array,
|
||||
* max_paypal:float, solde_dispo:float, frais_ms1:float,
|
||||
* maxUI:float, maxblock:float, maxproposer:float
|
||||
* }
|
||||
*/
|
||||
function fxRefundComputeContext(array $recCommandes): array
|
||||
{
|
||||
$out = [
|
||||
'ok' => false,
|
||||
'capture_id' => '',
|
||||
'currency' => 'CAD',
|
||||
'paypaldetails' => [],
|
||||
'max_paypal' => 0.0,
|
||||
'solde_dispo' => 0.0,
|
||||
'frais_ms1' => 0.0,
|
||||
'maxUI' => 0.0,
|
||||
'maxblock' => 0.0,
|
||||
'maxproposer' => 0.0,
|
||||
];
|
||||
|
||||
$captureId = $recCommandes['TransactionID'] ?? null;
|
||||
if (!is_string($captureId) || $captureId === '') {
|
||||
return $out; // pas de capture PayPal exploitable
|
||||
}
|
||||
$out['capture_id'] = $captureId;
|
||||
|
||||
require_once $_SERVER['DOCUMENT_ROOT'] . '/paypal_advanced/PaypalCheckout.class.php';
|
||||
$paypal = new PayPalCheckout();
|
||||
|
||||
// Solde disponible promoteur (helper de ce fichier)
|
||||
$out['solde_dispo'] = (float) statut_payement($recCommandes['eve_id'] ?? 0);
|
||||
|
||||
// Details PayPal de la capture + reconciliation locale
|
||||
$paypaldetails = $paypal->paypaldetails($captureId);
|
||||
$paypaldetails = reconcileRefundsWithLocal($paypaldetails);
|
||||
$out['paypaldetails'] = $paypaldetails;
|
||||
|
||||
// Max remboursable PayPal : remaining si dispo, sinon montant capture (tolere "delails")
|
||||
$maxPaypal = 0.0;
|
||||
if (isset($paypaldetails['refunds']['totals']['remaining'])) {
|
||||
$maxPaypal = (float) $paypaldetails['refunds']['totals']['remaining'];
|
||||
} elseif (!empty($paypaldetails['details']['amount']['value'])) {
|
||||
$maxPaypal = (float) $paypaldetails['details']['amount']['value'];
|
||||
} elseif (!empty($paypaldetails['delails']['amount']['value'])) {
|
||||
$maxPaypal = (float) $paypaldetails['delails']['amount']['value'];
|
||||
}
|
||||
$out['max_paypal'] = $maxPaypal;
|
||||
|
||||
// Devise d'affichage
|
||||
if (!empty($paypaldetails['refunds']['totals']['currency'])) {
|
||||
$out['currency'] = (string) $paypaldetails['refunds']['totals']['currency'];
|
||||
} elseif (!empty($paypaldetails['details']['amount']['currency_code'])) {
|
||||
$out['currency'] = (string) $paypaldetails['details']['amount']['currency_code'];
|
||||
} elseif (!empty($paypaldetails['delails']['amount']['currency_code'])) {
|
||||
$out['currency'] = (string) $paypaldetails['delails']['amount']['currency_code'];
|
||||
}
|
||||
|
||||
// Frais ms1
|
||||
$out['frais_ms1'] = (float) ($recCommandes['fra_total'] ?? 0);
|
||||
|
||||
// Plafonds (fidele a l'ecran Super Admin actuel) :
|
||||
// - maxUI affiche = max remboursable PayPal
|
||||
// - maxblock (input max) = max_paypal si max_paypal < solde dispo, sinon 0
|
||||
// - valeur proposee = max(0, maxUI - frais ms1)
|
||||
$out['maxUI'] = $maxPaypal;
|
||||
$out['maxblock'] = ($maxPaypal < $out['solde_dispo']) ? $maxPaypal : 0.0;
|
||||
$out['maxproposer'] = max(0.0, $out['maxUI'] - $out['frais_ms1']);
|
||||
$out['ok'] = true;
|
||||
|
||||
return $out;
|
||||
}
|
||||
|
||||
/**
|
||||
* Rend le formulaire de remboursement PayPal (carte + script AJAX).
|
||||
* Fragment HTML autonome (pas de <html>/<head>), reutilisable partout.
|
||||
*
|
||||
* @param array $recCommandes Ligne commande
|
||||
* @param string $refundToken Jeton anti double-clic
|
||||
* @param array $ctx Resultat de fxRefundComputeContext()
|
||||
* @param string $vDomaine Domaine de base (pour l'URL AJAX par defaut)
|
||||
* @param string|null $ajaxUrl URL de l'endpoint (def: $vDomaine.'/superadm/ajax_refund.php')
|
||||
* @return string HTML
|
||||
*/
|
||||
function fxRenderRefundForm(array $recCommandes, string $refundToken, array $ctx, string $vDomaine, ?string $ajaxUrl = null): string
|
||||
{
|
||||
if ($ajaxUrl === null) {
|
||||
$ajaxUrl = $vDomaine . '/superadm/ajax_refund.php';
|
||||
}
|
||||
|
||||
$captureId = (string) ($ctx['capture_id'] ?? '');
|
||||
$currencyUI = (string) ($ctx['currency'] ?? 'CAD');
|
||||
$maxUI = (float) ($ctx['maxUI'] ?? 0);
|
||||
$maxblock = (float) ($ctx['maxblock'] ?? 0);
|
||||
$maxproposer = (float) ($ctx['maxproposer'] ?? 0);
|
||||
|
||||
ob_start();
|
||||
?>
|
||||
<div class="ms1-refund" style="max-width:560px;border:1px solid #ddd;border-radius:10px;padding:14px;">
|
||||
<div style="font-weight:700;font-size:16px;margin-bottom:8px;">Remboursement PayPal (Checkout)</div>
|
||||
|
||||
<!-- Zone messages AJAX -->
|
||||
<div id="refund-msg" style="margin-bottom:10px;display:none;padding:10px;border-radius:8px;"></div>
|
||||
|
||||
<?php if ($maxUI > 0): ?>
|
||||
<div style="background:#f9fbff;border:1px solid #dbe7ff;padding:10px;border-radius:8px;margin-bottom:10px;">
|
||||
<div><strong>Capture:</strong> <?= h($captureId) ?></div>
|
||||
<div><strong>Max remboursable (affiché):</strong> <span id="pp_ref_max"><?= h(money2($maxUI)) ?></span> <?= h($currencyUI) ?></div>
|
||||
</div>
|
||||
|
||||
<form id="refund-form" method="post" style="display:grid;grid-template-columns:1fr 1fr;gap:10px;">
|
||||
<input type="hidden" name="refund_token" value="<?= h($refundToken) ?>">
|
||||
<input type="hidden" name="capture_id" value="<?= h($captureId) ?>">
|
||||
<input type="hidden" name="currency" value="<?= h($currencyUI) ?>">
|
||||
<input type="hidden" name="eve_id" value="<?= h($recCommandes['eve_id'] ?? '') ?>">
|
||||
<input type="hidden" name="no_commande" value="<?= h($recCommandes['no_commande'] ?? '') ?>">
|
||||
|
||||
<div style="grid-column:1/2;">
|
||||
<label style="display:block;font-size:12px;color:#333;margin-bottom:4px;">
|
||||
Montant à rembourser (<?= h($currencyUI) ?>)
|
||||
</label>
|
||||
<input id="pp_ref_amount" type="number" name="refund_amount" step="0.01" min="0.01"
|
||||
max="<?= h(money2($maxblock)) ?>"
|
||||
value="<?= h(money2($maxproposer)) ?>"
|
||||
style="width:100%;padding:8px;border:1px solid #ccc;border-radius:8px;">
|
||||
</div>
|
||||
|
||||
<div style="grid-column:2/3;">
|
||||
<label style="display:block;font-size:12px;color:#333;margin-bottom:4px;">Note (optionnelle)</label>
|
||||
<input type="text" name="refund_note" placeholder="Raison du remboursement"
|
||||
style="width:100%;padding:8px;border:1px solid #ccc;border-radius:8px;">
|
||||
</div>
|
||||
|
||||
<div style="grid-column:1/3;text-align:right;margin-top:6px;">
|
||||
<button type="submit"
|
||||
style="padding:10px 14px;border:1px solid #2d6cdf;border-radius:8px;background:#2d6cdf;color:#fff;cursor:pointer;">
|
||||
Rembourser
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
<?php else: ?>
|
||||
<div style="background:#fff8e6;border:1px solid #ffe1a6;padding:10px;border-radius:8px;">
|
||||
Aucun montant remboursable pour cette capture.
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
(function () {
|
||||
function showMsg(text, ok) {
|
||||
var box = document.getElementById('refund-msg');
|
||||
if (!box) return;
|
||||
box.style.display = 'block';
|
||||
box.style.background = ok ? '#e9f8ef' : '#feecec';
|
||||
box.style.border = ok ? '1px solid #b8e3c5' : '1px solid #f2b8b5';
|
||||
box.innerText = text;
|
||||
}
|
||||
var form = document.getElementById('refund-form');
|
||||
if (!form) return;
|
||||
form.addEventListener('submit', async function (e) {
|
||||
e.preventDefault();
|
||||
showMsg('⏳ Traitement en cours...', false);
|
||||
var data = new FormData(e.target);
|
||||
var res;
|
||||
try {
|
||||
res = await fetch(<?= json_encode($ajaxUrl) ?>, { method: 'POST', body: data });
|
||||
} catch (_) {
|
||||
showMsg('⚠️ Erreur réseau pendant le remboursement.', false);
|
||||
return;
|
||||
}
|
||||
if (!res.ok) {
|
||||
showMsg('⚠️ Erreur réseau pendant le remboursement.', false);
|
||||
return;
|
||||
}
|
||||
var json;
|
||||
try { json = await res.json(); } catch (_) {
|
||||
showMsg('⚠️ Réponse invalide du serveur.', false);
|
||||
return;
|
||||
}
|
||||
if (json.success) {
|
||||
showMsg('✅ ' + (json.message || 'Remboursement effectué.'), true);
|
||||
} else {
|
||||
showMsg('⚠️ ' + (json.message || 'Erreur remboursement.'), false);
|
||||
}
|
||||
});
|
||||
})();
|
||||
</script>
|
||||
<?php
|
||||
return ob_get_clean();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user