- Implemented PayPal webhook for handling payment notifications, including signature verification and transaction updates. - Created invoice generation and license management for software upgrades upon successful payment. - Added comprehensive logging for debugging purposes. - Introduced new CSS styles for the marketing file management system, including layout, toolbar, breadcrumb navigation, search filters, and file management UI components.
425 lines
20 KiB
PHP
425 lines
20 KiB
PHP
<?php
|
|
defined(page_security_key) or exit;
|
|
|
|
if (debug && debug_id == $_SESSION['id']){
|
|
ini_set('display_errors', '1');
|
|
ini_set('display_startup_errors', '1');
|
|
error_reporting(E_ALL);
|
|
}
|
|
|
|
$page = 'softwaretool';
|
|
//Check if allowed
|
|
if (isAllowed($page,$_SESSION['profile'],$_SESSION['permission'],'R') === 0){
|
|
header('location: index.php');
|
|
exit;
|
|
}
|
|
$bearertoken = createCommunicationToken($_SESSION['userkey']);
|
|
|
|
//+++++++++++++++++++++++++++++++++++++++++++++++++++++
|
|
// PAYMENT RETURN DETECTION
|
|
//+++++++++++++++++++++++++++++++++++++++++++++++++++++
|
|
$payment_return = isset($_GET['order_id']) ? $_GET['order_id'] : null;
|
|
$payment_return_status = isset($_GET['payment_return']) ? $_GET['payment_return'] : null;
|
|
$paypal_token = isset($_GET['token']) ? $_GET['token'] : null; // PayPal returns with ?token=
|
|
|
|
// Handle PayPal return - capture the order directly
|
|
if ($paypal_token && $payment_return) {
|
|
try {
|
|
// Get PayPal access token
|
|
$ch = curl_init(PAYPAL_URL . '/v1/oauth2/token');
|
|
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
|
curl_setopt($ch, CURLOPT_POST, true);
|
|
curl_setopt($ch, CURLOPT_POSTFIELDS, 'grant_type=client_credentials');
|
|
curl_setopt($ch, CURLOPT_USERPWD, PAYPAL_CLIENT_ID . ':' . PAYPAL_CLIENT_SECRET);
|
|
curl_setopt($ch, CURLOPT_HTTPHEADER, ['Content-Type: application/x-www-form-urlencoded']);
|
|
$response = curl_exec($ch);
|
|
curl_close($ch);
|
|
$token_data = json_decode($response, true);
|
|
$access_token = $token_data['access_token'] ?? '';
|
|
|
|
if ($access_token) {
|
|
// Capture the PayPal order
|
|
$capture_url = PAYPAL_URL . "/v2/checkout/orders/{$paypal_token}/capture";
|
|
$ch = curl_init($capture_url);
|
|
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
|
curl_setopt($ch, CURLOPT_POST, true);
|
|
curl_setopt($ch, CURLOPT_HTTPHEADER, [
|
|
'Content-Type: application/json',
|
|
'Authorization: Bearer ' . $access_token
|
|
]);
|
|
$response = curl_exec($ch);
|
|
$http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
|
curl_close($ch);
|
|
|
|
if (debug) {
|
|
debuglog("PayPal Capture: HTTP $http_code - $response");
|
|
}
|
|
|
|
// Update transaction status based on capture result
|
|
if ($http_code == 200 || $http_code == 201) {
|
|
$capture_result = json_decode($response, true);
|
|
$capture_status = $capture_result['status'] ?? '';
|
|
|
|
$payment_status = null;
|
|
if ($capture_status === 'COMPLETED') {
|
|
$payment_status = 1; // Paid
|
|
} elseif ($capture_status === 'PENDING') {
|
|
$payment_status = 101; // Pending
|
|
}
|
|
|
|
if ($payment_status !== null) {
|
|
$pdo = dbConnect($dbname);
|
|
$sql = 'UPDATE transactions SET payment_status = ? WHERE txn_id = ?';
|
|
$stmt = $pdo->prepare($sql);
|
|
$stmt->execute([$payment_status, $payment_return]);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Redirect to clean URL
|
|
header("Location: ?page=softwaretool&payment_return=1&order_id={$payment_return}");
|
|
exit;
|
|
|
|
} catch (Exception $e) {
|
|
if (debug) {
|
|
debuglog("PayPal Capture Error: " . $e->getMessage());
|
|
}
|
|
}
|
|
}
|
|
|
|
template_header('Softwaretool', 'softwaretool','view');
|
|
|
|
// Show payment return message if returning from payment
|
|
$view = '';
|
|
$payment_modal = '';
|
|
if ($payment_return && $payment_return_status) {
|
|
// Check actual payment status in database
|
|
$pdo = dbConnect($dbname);
|
|
$sql = 'SELECT payment_status FROM transactions WHERE txn_id = ?';
|
|
$stmt = $pdo->prepare($sql);
|
|
$stmt->execute([$payment_return]);
|
|
$transaction = $stmt->fetch(PDO::FETCH_ASSOC);
|
|
|
|
if ($transaction) {
|
|
if ($transaction['payment_status'] == 1) {
|
|
// Payment confirmed as paid
|
|
$payment_modal = '
|
|
<div id="paymentModal" class="modal" style="display: flex; position: fixed; top: 0; left: 0; width: 100%; height: 100%; background: rgba(0,0,0,0.5); z-index: 1000; align-items: center; justify-content: center;">
|
|
<div class="modal-content" style="background: white; border-radius: 12px; max-width: 500px; margin: 20px; box-shadow: 0 10px 40px rgba(0,0,0,0.3); position: relative;">
|
|
<span class="close" onclick="closePaymentModal()" style="position: absolute; top: 15px; right: 20px; font-size: 28px; font-weight: bold; color: #999; cursor: pointer;">×</span>
|
|
<div style="text-align: center; padding: 40px 30px;">
|
|
<i class="fa-solid fa-check-circle" style="font-size: 64px; color: #28a745; margin-bottom: 20px;"></i>
|
|
<h2 style="color: #155724; margin-bottom: 15px;">Payment Successful!</h2>
|
|
<p style="margin-bottom: 10px; color: #333;">Your payment has been processed. Please reconnect your device to apply the software upgrade.</p>
|
|
<p style="font-size: 12px; color: #666; margin-bottom: 25px;">Order ID: '.htmlspecialchars($payment_return).'</p>
|
|
<button onclick="closePaymentModal()" class="btn" style="padding: 12px 30px;">Continue</button>
|
|
</div>
|
|
</div>
|
|
</div>';
|
|
} else if ($transaction['payment_status'] == 0 || $transaction['payment_status'] == 101) {
|
|
// Payment pending
|
|
$payment_modal = '
|
|
<div id="paymentModal" class="modal" style="display: flex; position: fixed; top: 0; left: 0; width: 100%; height: 100%; background: rgba(0,0,0,0.5); z-index: 1000; align-items: center; justify-content: center;">
|
|
<div class="modal-content" style="background: white; border-radius: 12px; max-width: 500px; margin: 20px; box-shadow: 0 10px 40px rgba(0,0,0,0.3);">
|
|
<div style="text-align: center; padding: 40px 30px;">
|
|
<i class="fa-solid fa-clock" style="font-size: 64px; color: #ffc107; margin-bottom: 20px;"></i>
|
|
<h2 style="color: #856404; margin-bottom: 15px;">Payment Processing...</h2>
|
|
<p style="margin-bottom: 10px; color: #333;">Your payment is being processed. This page will update automatically when confirmed.</p>
|
|
<p style="font-size: 12px; color: #666; margin-bottom: 25px;">Order ID: '.htmlspecialchars($payment_return).'</p>
|
|
<i class="fa-solid fa-spinner fa-spin" style="font-size: 32px; color: #ffc107;"></i>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<script>
|
|
// Auto-refresh every 3 seconds to check payment status
|
|
setTimeout(function() { location.reload(); }, 3000);
|
|
</script>';
|
|
} else {
|
|
// Payment failed/cancelled
|
|
$payment_modal = '
|
|
<div id="paymentModal" class="modal" style="display: flex; position: fixed; top: 0; left: 0; width: 100%; height: 100%; background: rgba(0,0,0,0.5); z-index: 1000; align-items: center; justify-content: center;">
|
|
<div class="modal-content" style="background: white; border-radius: 12px; max-width: 500px; margin: 20px; box-shadow: 0 10px 40px rgba(0,0,0,0.3); position: relative;">
|
|
<span class="close" onclick="closePaymentModal()" style="position: absolute; top: 15px; right: 20px; font-size: 28px; font-weight: bold; color: #999; cursor: pointer;">×</span>
|
|
<div style="text-align: center; padding: 40px 30px;">
|
|
<i class="fa-solid fa-exclamation-circle" style="font-size: 64px; color: #dc3545; margin-bottom: 20px;"></i>
|
|
<h2 style="color: #721c24; margin-bottom: 15px;">Payment Failed</h2>
|
|
<p style="margin-bottom: 10px; color: #333;">Your payment could not be processed. Please try again.</p>
|
|
<p style="font-size: 12px; color: #666; margin-bottom: 25px;">Order ID: '.htmlspecialchars($payment_return).'</p>
|
|
<button onclick="closePaymentModal()" class="btn" style="padding: 12px 30px;">Close</button>
|
|
</div>
|
|
</div>
|
|
</div>';
|
|
}
|
|
}
|
|
}
|
|
|
|
$view .= '
|
|
<div class="content-title">
|
|
<div class="title">
|
|
<i class="fa-solid fa-box-open"></i>
|
|
<div class="txt">
|
|
<h2>'.$softwaretool_h2 .'</h2>
|
|
<p>'.$softwaretool_p.'</p>
|
|
</div>
|
|
</div>';
|
|
|
|
|
|
if (isset($_GET['equipmentID'])){$returnpage = 'equipment&equipmentID='.$_GET['equipmentID']; } else {$returnpage = 'dashboard';}
|
|
|
|
|
|
//SHOW BACK BUTTON ONLY FOR PORTAL USERS
|
|
if (isAllowed('dashboard',$_SESSION['profile'],$_SESSION['permission'],'R') != 0){
|
|
$view .= '
|
|
<div class="title-actions">
|
|
<a href="index.php?page='.$returnpage.'" class="btn alt mar-right-2"><i class="fa-solid fa-arrow-left"></i></a>
|
|
<button class="btn" onclick="showInstructions()" style="">
|
|
<i class="fa-solid fa-circle-question"></i>
|
|
</button>
|
|
</div>
|
|
';
|
|
}
|
|
|
|
$view .= '
|
|
</div>';
|
|
|
|
$view .= '<div class="content-block">
|
|
|
|
<p id="servicetoken" value="" hidden>'.$bearertoken.'</p>
|
|
|
|
<div id="connectdevice" style="display:flex; gap: 10px; margin-bottom: 20px;">
|
|
<button class="btn" id="connectButton" onClick="connectDeviceForSoftware()" style="min-width: 150px;">
|
|
<i class="fa-solid fa-plug"></i> Connect
|
|
</button>
|
|
<div id="readStatus" style="flex: 1; background-color: #f1f1f1; border-radius: 4px; overflow: hidden;">
|
|
<div id="readBar" style="height: 100%; transition: width 0.3s ease;"></div>
|
|
</div>
|
|
</div>
|
|
|
|
<div id="Device_output" style="display:none;">
|
|
<div id="serialResults" style="font-family: monospace;white-space: pre;padding: 10px;background-color:#f1f1f1;display:none;"></div>
|
|
|
|
<div id="softwareCheckStatus" style="margin: 20px 0; padding: 20px; background-color:#f8f9fa; border-radius: 8px; text-align: center; display:none;">
|
|
<i class="fa-solid fa-spinner fa-spin" style="font-size: 24px; color: #04AA6D; margin-bottom: 10px;"></i>
|
|
<p id="checkingMessage" style="margin: 0; font-size: 16px;">'.$softwaretool_checking.'</p>
|
|
</div>
|
|
|
|
<div id="softwareOptions" style="margin-top: 20px; display:none;">
|
|
<h3 style="margin-bottom: 20px; color: #333;">'.$softwaretool_select_upgrade.'</h3>
|
|
<div id="softwareOptionsGrid" style="display: grid; grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); gap: 20px; justify-content: center; max-width: 1200px; margin: 0 auto;">
|
|
</div>
|
|
</div>
|
|
|
|
<div id="noUpdatesMessage" style="margin: 20px 0; padding: 30px; background: linear-gradient(135deg, #f8f9fa 0%, #e9ecef 100%); border-radius: 3px; text-align: center; display:none; box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);">
|
|
<i class="fa-solid fa-check-circle" style="font-size: 48px; margin-bottom: 15px; color: #28a745;"></i>
|
|
<p style="margin: 0; font-size: 18px; font-weight: 600; color: #155724;"><strong>'.$softwaretool_no_updates.'</strong></p>
|
|
<p style="margin: 10px 0 0 0; color: #6c757d; font-size: 14px;">Your device is up to date</p>
|
|
</div>
|
|
|
|
<div id="uploadSection" style="margin-top: 10px; display:none;">
|
|
<button class="btn" id="uploadSoftware" style="display:none;"
|
|
emergencyPlug-update verify
|
|
board="emergencyPlug"
|
|
port-filters= "[{usbVendorId: 1027, usbProductId: 24597}];"
|
|
disabled>Install Software
|
|
<span class="upload-progress"></span>
|
|
</button>
|
|
<!-- Hidden checkbox that upload.js expects -->
|
|
<input type="checkbox" id="action_firmware" style="display:none;">
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Help Modal -->
|
|
<div id="helpModal" style="display:none; position: fixed; top: 0; left: 0; width: 100%; height: 100%; background: rgba(0,0,0,0.5); z-index: 1000; align-items: center; justify-content: center;">
|
|
<div style="background: white; border-radius: 12px; max-width: 600px; max-height: 80vh; overflow-y: auto; margin: 20px; box-shadow: 0 10px 40px rgba(0,0,0,0.3);">
|
|
<div style="padding: 25px; border-bottom: 1px solid #e0e0e0; display: flex; justify-content: space-between; align-items: center;">
|
|
<h3 style="margin: 0; color: #333;"><i class="fa-solid fa-circle-question"></i> '.$softwaretool_step.'</h3>
|
|
<button onclick="closeInstructions()" style="background: transparent; border: none; font-size: 20px; cursor: pointer; color: #666;"><i class="fa-solid fa-times"></i></button>
|
|
</div>
|
|
<div style="padding: 25px;">
|
|
<ol style="line-height: 1.8; color: #555;">
|
|
<li style="margin-bottom: 15px;">'.$softwaretool_step_1.'</li>
|
|
<li style="margin-bottom: 15px;">'.$softwaretool_step_2.'</li>
|
|
<li style="margin-bottom: 15px;">'.$softwaretool_step_3.'</li>
|
|
<li style="margin-bottom: 15px;">'.$softwaretool_step_4.'</li>
|
|
<li style="margin-bottom: 15px;">'.$softwaretool_step_5.'</li>
|
|
<li style="margin-bottom: 15px;">'.$softwaretool_step_6.'</li>
|
|
<li style="margin-bottom: 15px;">'.$softwaretool_step_7.'</li>
|
|
<li style="margin-bottom: 15px;">'.$softwaretool_step_8.'</li>
|
|
</ol>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
';
|
|
|
|
$view .= '
|
|
</div>
|
|
</div>
|
|
';
|
|
|
|
$view .= '</div>';
|
|
//OUTPUT
|
|
echo $view;
|
|
|
|
// Output payment modal if exists
|
|
echo $payment_modal;
|
|
|
|
|
|
echo '
|
|
<script src="assets/upload.js?'.script_version.'"></script>
|
|
<script src="assets/softwaretool.js?'.script_version.'"></script>
|
|
<script>
|
|
var link = "'.$baseurl.'";
|
|
var DEBUG = '.(debug ? 'true' : 'false').';
|
|
var MOLLIE_ENABLED = '.(mollie_enabled ? 'true' : 'false').';
|
|
var PAYPAL_ENABLED = '.(paypal_enabled ? 'true' : 'false').';
|
|
var PAY_ON_DELIVERY_ENABLED = '.(pay_on_delivery_enabled ? 'true' : 'false').';
|
|
var port, textEncoder, writableStreamClosed, writer, historyIndex = -1;
|
|
const lineHistory = [];
|
|
|
|
// Modal functions - defined globally for inline onclick
|
|
window.showInstructions = function() {
|
|
const modal = document.getElementById("helpModal");
|
|
if (modal) {
|
|
modal.style.display = "flex";
|
|
}
|
|
};
|
|
|
|
window.closeInstructions = function() {
|
|
const modal = document.getElementById("helpModal");
|
|
if (modal) {
|
|
modal.style.display = "none";
|
|
}
|
|
};
|
|
|
|
// Payment modal functions
|
|
window.closePaymentModal = function() {
|
|
const modal = document.getElementById("paymentModal");
|
|
if (modal) {
|
|
modal.style.display = "none";
|
|
// Clean URL by removing payment_return and order_id parameters
|
|
const url = new URL(window.location);
|
|
url.searchParams.delete("payment_return");
|
|
url.searchParams.delete("order_id");
|
|
window.history.replaceState({}, document.title, url);
|
|
}
|
|
};
|
|
|
|
// Close modal on background click
|
|
document.addEventListener("click", function(e) {
|
|
const helpModal = document.getElementById("helpModal");
|
|
if (helpModal && e.target === helpModal) {
|
|
closeInstructions();
|
|
}
|
|
const paymentModal = document.getElementById("paymentModal");
|
|
if (paymentModal && e.target === paymentModal) {
|
|
closePaymentModal();
|
|
}
|
|
});
|
|
|
|
// Monitor upload completion
|
|
const observer = new MutationObserver(function(mutations) {
|
|
mutations.forEach(function(mutation) {
|
|
if (mutation.type === "attributes" && mutation.attributeName === "checked") {
|
|
const actionFirmware = document.getElementById("action_firmware");
|
|
if (actionFirmware && actionFirmware.checked) {
|
|
console.log("Upload completion detected!");
|
|
// Upload completed successfully
|
|
handleUploadCompletion(true);
|
|
}
|
|
}
|
|
});
|
|
});
|
|
|
|
// Also monitor for readBar changes to detect completion
|
|
const readBarObserver = new MutationObserver(function(mutations) {
|
|
mutations.forEach(function(mutation) {
|
|
if (mutation.type === "childList" || mutation.type === "characterData") {
|
|
const readBar = document.getElementById("readBar");
|
|
if (readBar && readBar.innerHTML && readBar.innerHTML.includes("Firmware update completed")) {
|
|
console.log("Firmware update completed detected via readBar!");
|
|
setTimeout(() => handleUploadCompletion(true), 500);
|
|
}
|
|
}
|
|
});
|
|
});
|
|
|
|
// Start observing when upload section is visible
|
|
function startUploadMonitoring() {
|
|
const actionFirmware = document.getElementById("action_firmware");
|
|
const readBar = document.getElementById("readBar");
|
|
|
|
if (actionFirmware) {
|
|
observer.observe(actionFirmware, {
|
|
attributes: true,
|
|
attributeFilter: ["checked"]
|
|
});
|
|
console.log("Started monitoring action_firmware checkbox");
|
|
}
|
|
|
|
if (readBar) {
|
|
readBarObserver.observe(readBar, {
|
|
childList: true,
|
|
subtree: true,
|
|
characterData: true
|
|
});
|
|
console.log("Started monitoring readBar for completion");
|
|
}
|
|
}
|
|
|
|
// Handle upload completion (success or failure)
|
|
window.handleUploadCompletion = function(success) {
|
|
console.log("handleUploadCompletion called with success:", success);
|
|
|
|
const installStatus = document.getElementById("installationStatus");
|
|
const readBar = document.getElementById("readBar");
|
|
|
|
if (success) {
|
|
// Success handling
|
|
console.log("Updating UI for successful completion");
|
|
if (installStatus) {
|
|
installStatus.innerHTML = \'\' +
|
|
\'<i class="fa-solid fa-check-circle" style="font-size: 48px; color: #28a745; margin-bottom: 15px;"></i>\' +
|
|
\'<p style="margin: 0; font-size: 18px; font-weight: 600; color: #155724;">Installation Complete!</p>\' +
|
|
\'<p style="margin: 5px 0 0 0; color: #666; font-size: 14px;">Your device has been successfully updated</p>\';
|
|
}
|
|
// Ensure progress bar is at 100%
|
|
if (readBar) {
|
|
readBar.style.width = "100%";
|
|
readBar.style.background = "#04AA6D";
|
|
readBar.innerHTML = "Firmware update completed - 100%";
|
|
console.log("Updated readBar to 100%");
|
|
}
|
|
} else {
|
|
// Failure handling
|
|
console.log("Updating UI for failed completion");
|
|
if (installStatus) {
|
|
installStatus.innerHTML = \'\' +
|
|
\'<i class="fa-solid fa-exclamation-circle" style="font-size: 48px; color: #dc3545; margin-bottom: 15px;"></i>\' +
|
|
\'<p style="margin: 0; font-size: 18px; font-weight: 600; color: #721c24;">Installation Failed</p>\' +
|
|
\'<p style="margin: 5px 0 0 0; color: #666; font-size: 14px;">Please try again or contact support</p>\';
|
|
}
|
|
if (readBar) {
|
|
readBar.style.width = "100%";
|
|
readBar.style.background = "#dc3545";
|
|
readBar.innerHTML = "Installation failed";
|
|
}
|
|
}
|
|
|
|
// Stop monitoring
|
|
observer.disconnect();
|
|
readBarObserver.disconnect();
|
|
console.log("Stopped monitoring observers");
|
|
};
|
|
|
|
// Monitor for upload errors
|
|
window.addEventListener("error", function(e) {
|
|
if (e.message && e.message.includes("upload")) {
|
|
handleUploadCompletion(false);
|
|
}
|
|
});
|
|
</script>';
|
|
|
|
template_footer();
|
|
?>
|